Spaces:
Sleeping
Sleeping
File size: 12,116 Bytes
031a9c5 96b445e 031a9c5 8661be0 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 8661be0 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 8661be0 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 96b445e 031a9c5 8661be0 031a9c5 96b445e 031a9c5 96b445e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | <!DOCTYPE html>
<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> |