Ansible: Temporary Media Links
---
import Base from '../layouts/Base.astro';
import { getPaste } from '../lib/r2';

const { id } = Astro.params;

if (!id) {
  return Astro.redirect('/');
}

const runtime = Astro.locals.runtime as { env: { BUCKET: import('@cloudflare/workers-types').R2Bucket } };
const bucket = runtime?.env?.BUCKET;

if (!bucket) {
  return new Response('Storage not configured', { status: 500 });
}

const paste = await getPaste(bucket, id);

if (!paste) {
  return new Response('Paste not found', { status: 404 });
}

const { metadata } = paste;

let textContent = '';
let imageDataUrl = '';
// Convert stored OKLCH values back to CSS, with fallback for legacy hex values
const storedColor = metadata.bgColor;
let bgColor = 'oklch(98% 0.01 90)';
if (storedColor) {
  if (storedColor.startsWith('#')) {
    // Legacy hex color - use as-is for backwards compatibility
    bgColor = storedColor;
  } else {
    // OKLCH stored format: "L C H"
    const [L, C, h] = storedColor.split(' ');
    bgColor = `oklch(${L}% ${C} ${h})`;
  }
}

// Generate OG image URL
const siteUrl = Astro.url.origin;
const ogImage = metadata.type === 'image' 
  ? `${siteUrl}/raw/${id}`
  : `${siteUrl}/og/${id}.svg`;

// Generate description for text pastes
let description = 'Shared via Ansible';

if (metadata.type === 'text' && paste.body) {
  const reader = paste.body.getReader();
  const decoder = new TextDecoder();
  let result = '';
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    result += decoder.decode(value, { stream: true });
  }
  result += decoder.decode();
  textContent = result;
  
  // Set description to first 150 chars of text
  description = textContent.slice(0, 150).replace(/\n/g, ' ');
  if (textContent.length > 150) description += '...';
} else if (metadata.type === 'image' && paste.body) {
  const reader = paste.body.getReader();
  const chunks: Uint8Array[] = [];
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  
  const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
  const combined = new Uint8Array(totalLength);
  let offset = 0;
  for (const chunk of chunks) {
    combined.set(chunk, offset);
    offset += chunk.length;
  }
  
  // Convert to base64
  let binary = '';
  for (let i = 0; i < combined.length; i++) {
    binary += String.fromCharCode(combined[i]);
  }
  const base64 = btoa(binary);
  imageDataUrl = `data:${metadata.contentType};base64,${base64}`;
}
---

<Base title="Ansible" description={description} ogImage={ogImage} ogType="article">
  <header class="header">
    <a href="/" class="title">Ansible: Quickly share temporary text or images</a>
    <button id="copy-btn">Copy Link</button>
  </header>
  <div class="content-box" style={`background: ${bgColor};`}>
    {metadata.type === 'text' ? (
      <pre>{textContent}</pre>
    ) : (
      <img src={imageDataUrl} alt="Shared image" class="paste-image" />
    )}
  </div>
</Base>

<script>
  const copyBtn = document.getElementById('copy-btn') as HTMLButtonElement;

  copyBtn.addEventListener('click', async () => {
    try {
      await navigator.clipboard.writeText(window.location.href);
      copyBtn.textContent = 'Copied!';
      setTimeout(() => {
        copyBtn.textContent = 'Copy Link';
      }, 2000);
    } catch (error) {
      console.error('Failed to copy:', error);
      // Fallback for older browsers
      const textArea = document.createElement('textarea');
      textArea.value = window.location.href;
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand('copy');
      document.body.removeChild(textArea);
      copyBtn.textContent = 'Copied!';
      setTimeout(() => {
        copyBtn.textContent = 'Copy Link';
      }, 2000);
    }
  });
</script>