🌐
index.html
Back
📝 Html ⚡ Executable Ctrl+S: Save • Ctrl+R: Run • Ctrl+F: Find
<?php // Simple helper for cache-busting local assets based on last modified time function bust($path) { $abs = __DIR__ . '/' . $path; $ts = @filemtime($abs); return htmlspecialchars($path . '?v=' . ($ts ? $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 (already versioned by CDN) --> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/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 with cache busting --> <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"> <!-- Chat column only --> <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 --> <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="downloadStitched" class="text-xs px-2 py-1 rounded-md bg-emerald-600 text-white hover:bg-emerald-700">Download</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 start building your complete file. </div> </div> <div class="mt-4 p-3 rounded-lg bg-zinc-50 dark:bg-zinc-800"> <label class="text-sm block mb-2">Filename for download:</label> <input id="stitcherFilename" type="text" class="w-full rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 px-2 py-1 text-sm" placeholder="my-code.html" value="stitched-code.txt"> </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);"> <!-- Settings body is dynamically rendered by settings.js --> <div id="settingsBody"></div> </div> </div> </div> </div> <!-- Local JS with cache busting --> <script src="<?= bust('settings.js') ?>"></script> <script src="<?= bust('stitcher.js') ?>"></script> <script src="<?= bust('chat.js') ?>"></script> </body> </html>