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>