<?php
// Cache-busting helper for local assets
function bust($path) {
$abs = __DIR__ . '/' . $path;
$ts = @file_exists($abs) ? @filemtime($abs) : 1;
return htmlspecialchars($path . '?v=' . ($ts ?: 1), ENT_QUOTES, 'UTF-8');
}
?>
<!doctype html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>Unified Chat (DeepSeek · Grok · OpenAI)</title>
<!-- Third-party libs -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
<script>
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.toggle('dark', prefersDark);
</script>
<script src="https://cdn.tailwindcss.com"></script>
<!-- Local CSS (cache-busted) -->
<link rel="stylesheet" href="<?= bust('styles.css') ?>" />
</head>
<body class="bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-zinc-100 h-full overflow-x-hidden">
<div class="min-h-screen grid grid-rows-[auto,1fr]">
<!-- Header -->
<header class="border-b border-zinc-200/80 dark:border-zinc-800/80 bg-white/80 dark:bg-zinc-950/80 backdrop-blur sticky top-0 z-10">
<div class="max-w-5xl mx-auto px-2 sm:px-4 py-3 flex items-center justify-between gap-2 sm:gap-3">
<div class="flex items-center gap-2 sm:gap-3">
<div class="h-6 w-6 sm:h-8 sm:w-8 rounded-xl bg-gradient-to-br from-indigo-500 via-sky-500 to-emerald-500"></div>
<h1 class="text-sm sm:text-lg font-semibold">Unified Chat</h1>
<span class="hidden sm:inline text-xs text-zinc-500">DeepSeek · xAI Grok · OpenAI</span>
</div>
<div class="flex items-center gap-2">
<button id="openStitcher" class="text-xs px-2 py-1 sm:px-3 sm:py-1.5 rounded-lg border border-emerald-300 dark:border-emerald-700 bg-emerald-50 dark:bg-emerald-900 hover:bg-emerald-100 dark:hover:bg-emerald-800 text-emerald-700 dark:text-emerald-300">
Stitcher <span id="stitcherCount" class="hidden ml-1 px-1 bg-emerald-600 text-white rounded-full text-xs">0</span>
</button>
<button id="openSettings" class="text-xs px-2 py-1 sm:px-3 sm:py-1.5 rounded-lg border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800">
Settings
</button>
</div>
</div>
</header>
<!-- Main -->
<main class="max-w-5xl mx-auto w-full px-2 sm:px-4 py-3 sm:py-6">
<section class="flex flex-col min-h-[70vh]">
<!-- Transcript -->
<div id="transcript" class="flex-1 space-y-3 sm:space-y-4 overflow-y-auto pr-1 scrollbar-thin min-w-0">
<!-- messages will render here -->
</div>
<!-- Debug -->
<details id="debugWrap" class="mt-4 hidden">
<summary class="cursor-pointer text-sm text-zinc-600 dark:text-zinc-300">Debug (request / response)</summary>
<pre id="debugArea" class="mt-2 p-3 rounded-xl bg-zinc-100 dark:bg-zinc-900 text-xs overflow-x-auto"></pre>
</details>
<!-- Composer -->
<div class="mt-3 sm:mt-4">
<div class="rounded-2xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-2 shadow-sm">
<label class="sr-only" for="question">Your message</label>
<div class="flex items-end gap-2 min-w-0">
<textarea id="question" rows="2"
class="flex-1 min-w-0 min-h-[48px] sm:min-h-[72px] rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 px-2 sm:px-3 py-2 text-sm"
placeholder="Ask me anything…"></textarea>
<div class="flex flex-col items-stretch gap-1 shrink-0">
<button id="send" class="h-8 sm:h-10 px-2 sm:px-4 rounded-xl bg-indigo-600 text-white hover:bg-indigo-500 disabled:opacity-50 text-xs sm:text-sm">
Send
</button>
<div id="status" class="text-[9px] sm:text-[11px] text-zinc-500 text-center"></div>
</div>
</div>
</div>
</div>
</section>
</main>
</div>
<!-- Stitcher Overlay (textarea version) -->
<div id="stitcherOverlay" class="fixed inset-0 z-40 hidden" aria-hidden="true">
<div id="stitcherBackdrop" class="absolute inset-0 bg-black/40 backdrop-blur-sm"></div>
<div class="absolute inset-0 flex items-start justify-center p-2 sm:p-4">
<div role="dialog" aria-modal="true" aria-labelledby="stitcherTitle"
class="w-full max-w-4xl mt-4 sm:mt-10 rounded-2xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 shadow-xl max-h-[90vh] overflow-hidden">
<div class="p-3 sm:p-4 border-b border-zinc-200 dark:border-zinc-800 flex items-center justify-between">
<h2 id="stitcherTitle" class="font-semibold text-sm sm:text-base">Code Stitcher</h2>
<div class="flex items-center gap-2">
<button id="clearStitcher" class="text-xs px-2 py-1 rounded-md border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800">Clear All</button>
<button id="copyStitched" class="text-xs px-2 py-1 rounded-md bg-indigo-600 text-white hover:bg-indigo-700">Copy All</button>
<button id="closeStitcher" class="text-xs px-2 py-1 rounded-md border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800">Close</button>
</div>
</div>
<div class="p-3 sm:p-4 overflow-y-auto" style="max-height: calc(90vh - 60px);">
<div id="stitcherContent" class="space-y-3">
<div class="text-center text-zinc-500 text-sm" id="stitcherEmpty">
No code chunks added yet. Use the "Add to Stitcher" button on code blocks to add editable chunks.
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Settings Overlay -->
<div id="settingsOverlay" class="fixed inset-0 z-40 hidden" aria-hidden="true">
<div id="overlayBackdrop" class="absolute inset-0 bg-black/40 backdrop-blur-sm"></div>
<div class="absolute inset-0 flex items-start justify-center p-2 sm:p-4">
<div role="dialog" aria-modal="true" aria-labelledby="settingsTitle"
class="w-full max-w-md mt-4 sm:mt-10 rounded-2xl border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 shadow-xl max-h-[90vh] overflow-hidden">
<div class="p-3 sm:p-4 border-b border-zinc-200 dark:border-zinc-800 flex items-center justify-between">
<h2 id="settingsTitle" class="font-semibold text-sm sm:text-base">Settings</h2>
<div class="flex items-center gap-2">
<button id="clearChat" class="text-xs px-2 py-1 rounded-md border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800">Clear</button>
<button id="closeSettings" class="text-xs px-2 py-1 rounded-md border border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800">Close</button>
</div>
</div>
<div class="p-3 sm:p-4 space-y-3 overflow-y-auto" style="max-height: calc(90vh - 60px);">
<div id="settingsBody"></div>
</div>
</div>
</div>
</div>
<!-- Local JS (cache-busted). stitcher.js contains its own constructable stylesheet. -->
<script src="<?= bust('settings.js') ?>"></script>
<script src="<?= bust('stitcher.js') ?>"></script>
<script src="<?= bust('chat.js') ?>"></script>
</body>
</html>