Spaces:
Sleeping
Sleeping
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Code Snapshot Studio - 代码截图工坊</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script> | |
| <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> | |
| <!-- Default Theme --> | |
| <link id="prism-theme" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css"> | |
| <!-- Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <!-- Custom Styles --> | |
| <link rel="stylesheet" href="/static/style.css"> | |
| </head> | |
| <body class="h-screen flex flex-col overflow-hidden"> | |
| {% raw %} | |
| <div id="app" class="flex-1 flex flex-col h-full"> | |
| <!-- Header --> | |
| <header class="bg-white border-b border-gray-200 px-4 lg:px-6 py-3 flex items-center justify-between shadow-sm z-30"> | |
| <div class="flex items-center gap-3"> | |
| <button @click="toggleSidebar" class="lg:hidden text-gray-600 hover:text-indigo-600 transition-colors"> | |
| <i class="fa-solid fa-bars text-xl"></i> | |
| </button> | |
| <div class="w-8 h-8 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center text-white font-bold"> | |
| <i class="fa-solid fa-camera"></i> | |
| </div> | |
| <h1 class="text-lg lg:text-xl font-bold text-gray-800 tracking-tight truncate">Code Snapshot Studio</h1> | |
| </div> | |
| <div class="flex items-center gap-4"> | |
| <button @click="copyImage" :disabled="isExporting" | |
| class="bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 px-4 py-2 rounded-lg font-medium transition-colors flex items-center gap-2 shadow-sm whitespace-nowrap hidden sm:flex"> | |
| <i class="fa-regular fa-copy"></i> | |
| <span>复制图片</span> | |
| </button> | |
| <button @click="exportImage" :disabled="isExporting" | |
| class="bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400 text-white px-4 py-2 rounded-lg font-medium transition-colors flex items-center gap-2 shadow-sm whitespace-nowrap"> | |
| <i v-if="!isExporting" class="fa-solid fa-download"></i> | |
| <i v-else class="fa-solid fa-spinner fa-spin"></i> | |
| <span>{{ isExporting ? '导出中...' : '导出 PNG' }}</span> | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="flex-1 flex overflow-hidden relative"> | |
| <!-- Mobile Backdrop --> | |
| <div v-if="showSidebar" @click="showSidebar = false" class="fixed inset-0 bg-black/20 z-20 lg:hidden backdrop-blur-sm transition-opacity"></div> | |
| <!-- Sidebar Controls --> | |
| <aside :class="['w-80 bg-white border-r border-gray-200 overflow-y-auto p-6 flex flex-col gap-6 shadow-[4px_0_24px_rgba(0,0,0,0.02)] z-30 absolute inset-y-0 left-0 lg:static sidebar-transition transform', showSidebar ? 'translate-x-0' : '-translate-x-full lg:translate-x-0']"> | |
| <!-- Code Input --> | |
| <div class="flex flex-col gap-2"> | |
| <label class="text-sm font-semibold text-gray-700 flex justify-between items-center"> | |
| 代码内容 | |
| <button @click="clearCode" class="text-xs text-red-500 hover:text-red-700 font-normal hover:underline" title="清空代码"> | |
| 清空 | |
| </button> | |
| </label> | |
| <textarea v-model="code" class="w-full h-48 p-3 border border-gray-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none resize-none bg-gray-50" placeholder="在此粘贴你的代码..."></textarea> | |
| </div> | |
| <!-- Language & Theme --> | |
| <div class="grid grid-cols-2 gap-4"> | |
| <div class="flex flex-col gap-2"> | |
| <label class="text-sm font-semibold text-gray-700 flex justify-between items-center"> | |
| 语言 | |
| <button @click="loadExample" class="text-xs text-indigo-600 hover:text-indigo-800 font-normal hover:underline" title="加载示例代码"> | |
| 示例 | |
| </button> | |
| </label> | |
| <select v-model="language" @change="onLanguageChange" class="w-full p-2 border border-gray-300 rounded-lg text-sm bg-white focus:ring-2 focus:ring-indigo-500 outline-none"> | |
| <option value="javascript">JavaScript</option> | |
| <option value="typescript">TypeScript</option> | |
| <option value="python">Python</option> | |
| <option value="java">Java</option> | |
| <option value="go">Go</option> | |
| <option value="rust">Rust</option> | |
| <option value="html">HTML</option> | |
| <option value="css">CSS</option> | |
| <option value="sql">SQL</option> | |
| <option value="bash">Bash</option> | |
| <option value="json">JSON</option> | |
| </select> | |
| </div> | |
| <div class="flex flex-col gap-2"> | |
| <label class="text-sm font-semibold text-gray-700">主题</label> | |
| <select v-model="theme" @change="changeTheme" class="w-full p-2 border border-gray-300 rounded-lg text-sm bg-white focus:ring-2 focus:ring-indigo-500 outline-none"> | |
| <option value="tomorrow">Dark (Tomorrow)</option> | |
| <option value="okaidia">Okaidia</option> | |
| <option value="solarizedlight">Solarized Light</option> | |
| <option value="twilight">Twilight</option> | |
| <option value="default">Default</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Background --> | |
| <div class="flex flex-col gap-2"> | |
| <label class="text-sm font-semibold text-gray-700">背景风格</label> | |
| <div class="grid grid-cols-5 gap-2"> | |
| <button v-for="(bg, index) in backgrounds" :key="index" | |
| @click="currentBg = bg" | |
| :class="['w-8 h-8 rounded-full border-2 transition-all', currentBg.value === bg.value ? 'border-indigo-600 scale-110 shadow-md' : 'border-transparent hover:scale-105']" | |
| :style="{ background: bg.value }" | |
| :title="bg.name"> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Settings --> | |
| <div class="space-y-4 border-t border-gray-100 pt-4"> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-sm font-medium text-gray-700">窗口控件</label> | |
| <input type="checkbox" v-model="showWindowControls" class="w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500"> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-sm font-medium text-gray-700">行号</label> | |
| <input type="checkbox" v-model="showLineNumbers" class="w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500"> | |
| </div> | |
| <div class="flex items-center justify-between"> | |
| <label class="text-sm font-medium text-gray-700">窗口阴影</label> | |
| <input type="checkbox" v-model="showShadow" class="w-4 h-4 text-indigo-600 rounded focus:ring-indigo-500"> | |
| </div> | |
| <div class="flex flex-col gap-2"> | |
| <div class="flex justify-between"> | |
| <label class="text-sm font-medium text-gray-700">内边距 (Padding)</label> | |
| <span class="text-xs text-gray-500">{{ padding }}px</span> | |
| </div> | |
| <input type="range" v-model="padding" min="16" max="128" step="8" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-indigo-600"> | |
| </div> | |
| <div class="flex flex-col gap-2"> | |
| <label class="text-sm font-medium text-gray-700">窗口标题</label> | |
| <input type="text" v-model="windowTitle" class="w-full p-2 border border-gray-300 rounded-lg text-sm outline-none focus:ring-2 focus:ring-indigo-500" placeholder="例如: app.py"> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- Preview Area --> | |
| <section class="flex-1 bg-gray-100 flex items-center justify-center p-4 lg:p-8 overflow-auto relative w-full"> | |
| <!-- Grid Background Pattern --> | |
| <div class="absolute inset-0 opacity-[0.03]" | |
| style="background-image: radial-gradient(#000 1px, transparent 1px); background-size: 20px 20px;"> | |
| </div> | |
| <!-- The Snapshot Card --> | |
| <div ref="snapshotCard" class="preview-container relative flex items-center justify-center min-w-[300px] lg:min-w-[400px]" | |
| :style="{ background: currentBg.value, padding: padding + 'px' }"> | |
| <!-- Window --> | |
| <div class="bg-[#1e1e1e] rounded-xl overflow-hidden min-w-[300px] max-w-[90vw] lg:max-w-4xl w-auto" | |
| :class="{'shadow-2xl': showShadow, 'shadow-none': !showShadow}" | |
| :style="{ backgroundColor: themeBgColor }"> | |
| <!-- Window Header --> | |
| <div v-if="showWindowControls || windowTitle" class="px-4 py-3 flex items-center relative bg-white/5 border-b border-white/5"> | |
| <!-- Controls --> | |
| <div v-if="showWindowControls" class="flex gap-2 absolute left-4"> | |
| <div class="w-3 h-3 rounded-full bg-[#ff5f56] border border-[#e0443e]"></div> | |
| <div class="w-3 h-3 rounded-full bg-[#ffbd2e] border border-[#dea123]"></div> | |
| <div class="w-3 h-3 rounded-full bg-[#27c93f] border border-[#1aab29]"></div> | |
| </div> | |
| <!-- Title --> | |
| <div class="w-full text-center text-xs font-medium text-gray-400 font-sans select-none truncate px-12"> | |
| {{ windowTitle }} | |
| </div> | |
| </div> | |
| <!-- Code Content --> | |
| <div class="p-0 overflow-hidden overflow-x-auto"> | |
| <pre class="!m-0 !p-6 !bg-transparent text-sm leading-relaxed outline-none code-font" | |
| :class="{'line-numbers': showLineNumbers}"><code :class="'language-' + language" ref="codeBlock">{{ code }}</code></pre> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| {% endraw %} | |
| <!-- App Logic --> | |
| <script src="/static/script.js"></script> | |
| </body> | |
| </html> |