aied-lab commited on
Commit
60df2b7
·
1 Parent(s): 5032d19

Add light theme with dark mode toggle

Browse files
Files changed (1) hide show
  1. index.html +168 -25
index.html CHANGED
@@ -4,15 +4,68 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Gemma 多模態影像分析助手</title>
 
 
 
 
 
 
 
 
 
 
 
 
7
  <style>
8
- :root {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  --bg-color: #0b0f19;
 
 
10
  --card-bg: rgba(17, 24, 39, 0.75);
 
11
  --border-glow: rgba(0, 240, 255, 0.3);
12
  --neon-cyan: #00f0ff;
13
  --neon-purple: #b026ff;
14
  --text-main: #f3f4f6;
15
  --text-muted: #9ca3af;
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
  body {
@@ -21,11 +74,12 @@
21
  margin: 40px auto;
22
  padding: 20px;
23
  background-color: var(--bg-color);
24
- background-image:
25
- radial-gradient(circle at 10% 20%, rgba(176, 38, 255, 0.1) 0%, transparent 40%),
26
- radial-gradient(circle at 90% 80%, rgba(0, 240, 255, 0.1) 0%, transparent 40%);
27
  color: var(--text-main);
28
  min-height: 100vh;
 
29
  }
30
 
31
  .card {
@@ -34,10 +88,11 @@
34
  -webkit-backdrop-filter: blur(16px);
35
  padding: 30px;
36
  border-radius: 16px;
37
- border: 1px solid var(--border-glow);
38
- box-shadow: 0 0 30px rgba(0, 240, 255, 0.1), inset 0 0 15px rgba(176, 38, 255, 0.05);
39
  position: relative;
40
  overflow: hidden;
 
41
  }
42
 
43
  .card::before {
@@ -48,6 +103,7 @@
48
  width: 100%;
49
  height: 2px;
50
  background: linear-gradient(90deg, transparent, var(--neon-cyan), transparent);
 
51
  animation: scanline 4s linear infinite;
52
  }
53
 
@@ -61,6 +117,7 @@
61
  font-size: 1.8rem;
62
  background: linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
63
  -webkit-background-clip: text;
 
64
  -webkit-text-fill-color: transparent;
65
  letter-spacing: 0.5px;
66
  }
@@ -90,23 +147,23 @@
90
  textarea {
91
  width: 100%;
92
  padding: 12px 14px;
93
- background: rgba(3, 7, 18, 0.6);
94
- border: 1px solid rgba(255, 255, 255, 0.1);
95
  border-radius: 8px;
96
  box-sizing: border-box;
97
  color: var(--text-main);
98
  font-size: 0.95rem;
99
- transition: all 0.3s ease;
100
  }
101
 
102
  input:focus {
103
  outline: none;
104
  border-color: var(--neon-cyan);
105
- box-shadow: 0 0 10px rgba(0, 240, 255, 0.3);
106
  }
107
 
108
  input[type="file"]::file-selector-button {
109
- background: rgba(0, 240, 255, 0.1);
110
  color: var(--neon-cyan);
111
  border: 1px solid var(--neon-cyan);
112
  padding: 6px 12px;
@@ -117,12 +174,12 @@
117
 
118
  input[type="file"]::file-selector-button:hover {
119
  background: var(--neon-cyan);
120
- color: #000;
121
  }
122
 
123
  button {
124
  background: linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
125
- color: #000;
126
  border: none;
127
  padding: 12px 20px;
128
  font-weight: 700;
@@ -142,13 +199,53 @@
142
  }
143
 
144
  button:disabled {
145
- background: #374151;
146
- color: #9ca3af;
147
  cursor: not-allowed;
148
  box-shadow: none;
149
  transform: none;
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  #preview-container {
153
  margin-top: 12px;
154
  text-align: center;
@@ -160,33 +257,61 @@
160
  border-radius: 8px;
161
  display: none;
162
  border: 1px solid var(--border-glow);
163
- box-shadow: 0 4px 12px rgba(0,0,0,0.5);
164
  }
165
 
166
  #result {
167
  margin-top: 12px;
168
  padding: 16px;
169
- background: rgba(3, 7, 18, 0.8);
170
- border: 1px solid rgba(255, 255, 255, 0.08);
171
  border-radius: 8px;
172
  white-space: pre-wrap;
173
  min-height: 60px;
174
  font-size: 0.95rem;
175
- color: #e5e7eb;
176
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.4);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
  </style>
179
  </head>
180
  <body>
 
 
 
 
 
 
 
 
 
 
 
181
  <div class="card">
182
  <h2>Gemma 多模態影像分析助手</h2>
183
  <p>透過 Hugging Face Router 呼叫 gemma-4-31B-it 模型進行高精度影像辨識與分析。</p>
184
-
185
  <div class="form-group">
186
  <label for="apiKey">Hugging Face Token (HF_TOKEN)</label>
187
  <input type="password" id="apiKey" placeholder="請輸入您的 HF_TOKEN">
188
  </div>
189
-
190
  <div class="form-group">
191
  <label for="imageInput">上傳圖片</label>
192
  <input type="file" id="imageInput" accept="image/*" onchange="previewSelectedImage(event)">
@@ -194,14 +319,14 @@
194
  <img id="imagePreview" alt="圖片預覽">
195
  </div>
196
  </div>
197
-
198
  <div class="form-group">
199
  <label for="promptInput">輸入提示詞 (Prompt)</label>
200
  <input type="text" id="promptInput" value="Describe this image in one sentence in chinese">
201
  </div>
202
-
203
  <button id="submitBtn" onclick="analyzeImage()">啟動分析</button>
204
-
205
  <div class="form-group" style="margin-top: 24px;">
206
  <label>模型回應</label>
207
  <div id="result">系統待命,尚未開始分析...</div>
@@ -209,6 +334,24 @@
209
  </div>
210
 
211
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  // 選擇圖片時即時顯示預覽
213
  function previewSelectedImage(event) {
214
  const file = event.target.files[0];
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Gemma 多模態影像分析助手</title>
7
+ <script>
8
+ (function () {
9
+ var theme;
10
+ try {
11
+ theme = localStorage.getItem('theme');
12
+ } catch (e) {}
13
+ if (!theme) {
14
+ theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
15
+ }
16
+ document.documentElement.dataset.theme = theme;
17
+ })();
18
+ </script>
19
  <style>
20
+ :root,
21
+ :root[data-theme="light"] {
22
+ --bg-color: #ffffff;
23
+ --bg-glow-1: rgba(124, 58, 237, 0.07);
24
+ --bg-glow-2: rgba(8, 145, 178, 0.07);
25
+ --card-bg: rgba(255, 255, 255, 0.9);
26
+ --card-border: rgba(15, 23, 42, 0.08);
27
+ --border-glow: rgba(8, 145, 178, 0.35);
28
+ --neon-cyan: #0891b2;
29
+ --neon-purple: #7c3aed;
30
+ --text-main: #0f172a;
31
+ --text-muted: #64748b;
32
+ --input-bg: #f8fafc;
33
+ --input-border: rgba(15, 23, 42, 0.12);
34
+ --result-bg: #f8fafc;
35
+ --result-text: #1e293b;
36
+ --result-shadow: inset 0 1px 3px rgba(15, 23, 42, 0.06);
37
+ --card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08), inset 0 0 15px rgba(124, 58, 237, 0.03);
38
+ --preview-shadow: 0 4px 12px rgba(15, 23, 42, 0.15);
39
+ --focus-shadow: rgba(8, 145, 178, 0.25);
40
+ --btn-text: #ffffff;
41
+ --btn-disabled-bg: #e2e8f0;
42
+ --btn-disabled-text: #94a3b8;
43
+ --scanline-opacity: 0.6;
44
+ }
45
+
46
+ :root[data-theme="dark"] {
47
  --bg-color: #0b0f19;
48
+ --bg-glow-1: rgba(176, 38, 255, 0.1);
49
+ --bg-glow-2: rgba(0, 240, 255, 0.1);
50
  --card-bg: rgba(17, 24, 39, 0.75);
51
+ --card-border: rgba(0, 240, 255, 0.3);
52
  --border-glow: rgba(0, 240, 255, 0.3);
53
  --neon-cyan: #00f0ff;
54
  --neon-purple: #b026ff;
55
  --text-main: #f3f4f6;
56
  --text-muted: #9ca3af;
57
+ --input-bg: rgba(3, 7, 18, 0.6);
58
+ --input-border: rgba(255, 255, 255, 0.1);
59
+ --result-bg: rgba(3, 7, 18, 0.8);
60
+ --result-text: #e5e7eb;
61
+ --result-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
62
+ --card-shadow: 0 0 30px rgba(0, 240, 255, 0.1), inset 0 0 15px rgba(176, 38, 255, 0.05);
63
+ --preview-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
64
+ --focus-shadow: rgba(0, 240, 255, 0.3);
65
+ --btn-text: #000000;
66
+ --btn-disabled-bg: #374151;
67
+ --btn-disabled-text: #9ca3af;
68
+ --scanline-opacity: 1;
69
  }
70
 
71
  body {
 
74
  margin: 40px auto;
75
  padding: 20px;
76
  background-color: var(--bg-color);
77
+ background-image:
78
+ radial-gradient(circle at 10% 20%, var(--bg-glow-1) 0%, transparent 40%),
79
+ radial-gradient(circle at 90% 80%, var(--bg-glow-2) 0%, transparent 40%);
80
  color: var(--text-main);
81
  min-height: 100vh;
82
+ transition: background-color 0.35s ease, color 0.35s ease;
83
  }
84
 
85
  .card {
 
88
  -webkit-backdrop-filter: blur(16px);
89
  padding: 30px;
90
  border-radius: 16px;
91
+ border: 1px solid var(--card-border);
92
+ box-shadow: var(--card-shadow);
93
  position: relative;
94
  overflow: hidden;
95
+ transition: background-color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
96
  }
97
 
98
  .card::before {
 
103
  width: 100%;
104
  height: 2px;
105
  background: linear-gradient(90deg, transparent, var(--neon-cyan), transparent);
106
+ opacity: var(--scanline-opacity);
107
  animation: scanline 4s linear infinite;
108
  }
109
 
 
117
  font-size: 1.8rem;
118
  background: linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
119
  -webkit-background-clip: text;
120
+ background-clip: text;
121
  -webkit-text-fill-color: transparent;
122
  letter-spacing: 0.5px;
123
  }
 
147
  textarea {
148
  width: 100%;
149
  padding: 12px 14px;
150
+ background: var(--input-bg);
151
+ border: 1px solid var(--input-border);
152
  border-radius: 8px;
153
  box-sizing: border-box;
154
  color: var(--text-main);
155
  font-size: 0.95rem;
156
+ transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
157
  }
158
 
159
  input:focus {
160
  outline: none;
161
  border-color: var(--neon-cyan);
162
+ box-shadow: 0 0 10px var(--focus-shadow);
163
  }
164
 
165
  input[type="file"]::file-selector-button {
166
+ background: color-mix(in srgb, var(--neon-cyan) 12%, transparent);
167
  color: var(--neon-cyan);
168
  border: 1px solid var(--neon-cyan);
169
  padding: 6px 12px;
 
174
 
175
  input[type="file"]::file-selector-button:hover {
176
  background: var(--neon-cyan);
177
+ color: var(--btn-text);
178
  }
179
 
180
  button {
181
  background: linear-gradient(135deg, var(--neon-cyan), var(--neon-purple));
182
+ color: var(--btn-text);
183
  border: none;
184
  padding: 12px 20px;
185
  font-weight: 700;
 
199
  }
200
 
201
  button:disabled {
202
+ background: var(--btn-disabled-bg);
203
+ color: var(--btn-disabled-text);
204
  cursor: not-allowed;
205
  box-shadow: none;
206
  transform: none;
207
  }
208
 
209
+ .theme-toggle {
210
+ position: fixed;
211
+ top: 20px;
212
+ right: 20px;
213
+ z-index: 10;
214
+ width: auto;
215
+ display: inline-flex;
216
+ align-items: center;
217
+ gap: 6px;
218
+ padding: 8px 14px;
219
+ border-radius: 999px;
220
+ border: 1px solid var(--input-border);
221
+ background: var(--card-bg);
222
+ color: var(--text-muted);
223
+ font-size: 0.8rem;
224
+ font-weight: 600;
225
+ letter-spacing: 0;
226
+ box-shadow: 0 4px 14px rgba(15, 23, 42, 0.12);
227
+ backdrop-filter: blur(8px);
228
+ -webkit-backdrop-filter: blur(8px);
229
+ }
230
+
231
+ .theme-toggle:hover {
232
+ opacity: 1;
233
+ transform: none;
234
+ color: var(--text-main);
235
+ box-shadow: 0 4px 14px rgba(15, 23, 42, 0.18);
236
+ }
237
+
238
+ .theme-toggle svg {
239
+ width: 16px;
240
+ height: 16px;
241
+ flex-shrink: 0;
242
+ }
243
+
244
+ :root[data-theme="light"] .icon-sun,
245
+ :root[data-theme="dark"] .icon-moon {
246
+ display: none;
247
+ }
248
+
249
  #preview-container {
250
  margin-top: 12px;
251
  text-align: center;
 
257
  border-radius: 8px;
258
  display: none;
259
  border: 1px solid var(--border-glow);
260
+ box-shadow: var(--preview-shadow);
261
  }
262
 
263
  #result {
264
  margin-top: 12px;
265
  padding: 16px;
266
+ background: var(--result-bg);
267
+ border: 1px solid var(--input-border);
268
  border-radius: 8px;
269
  white-space: pre-wrap;
270
  min-height: 60px;
271
  font-size: 0.95rem;
272
+ color: var(--result-text);
273
+ box-shadow: var(--result-shadow);
274
+ transition: background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease;
275
+ }
276
+
277
+ @media (max-width: 520px) {
278
+ body {
279
+ margin: 20px auto;
280
+ padding: 16px;
281
+ }
282
+
283
+ .theme-toggle {
284
+ top: 12px;
285
+ right: 12px;
286
+ }
287
+
288
+ .card {
289
+ padding: 22px;
290
+ }
291
  }
292
  </style>
293
  </head>
294
  <body>
295
+ <button class="theme-toggle" id="themeToggle" type="button" aria-label="切換背景樣式" onclick="toggleTheme()">
296
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
297
+ <circle cx="12" cy="12" r="4"></circle>
298
+ <path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"></path>
299
+ </svg>
300
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
301
+ <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"></path>
302
+ </svg>
303
+ <span id="themeLabel">深色</span>
304
+ </button>
305
+
306
  <div class="card">
307
  <h2>Gemma 多模態影像分析助手</h2>
308
  <p>透過 Hugging Face Router 呼叫 gemma-4-31B-it 模型進行高精度影像辨識與分析。</p>
309
+
310
  <div class="form-group">
311
  <label for="apiKey">Hugging Face Token (HF_TOKEN)</label>
312
  <input type="password" id="apiKey" placeholder="請輸入您的 HF_TOKEN">
313
  </div>
314
+
315
  <div class="form-group">
316
  <label for="imageInput">上傳圖片</label>
317
  <input type="file" id="imageInput" accept="image/*" onchange="previewSelectedImage(event)">
 
319
  <img id="imagePreview" alt="圖片預覽">
320
  </div>
321
  </div>
322
+
323
  <div class="form-group">
324
  <label for="promptInput">輸入提示詞 (Prompt)</label>
325
  <input type="text" id="promptInput" value="Describe this image in one sentence in chinese">
326
  </div>
327
+
328
  <button id="submitBtn" onclick="analyzeImage()">啟動分析</button>
329
+
330
  <div class="form-group" style="margin-top: 24px;">
331
  <label>模型回應</label>
332
  <div id="result">系統待命,尚未開始分析...</div>
 
334
  </div>
335
 
336
  <script>
337
+ function toggleTheme() {
338
+ const root = document.documentElement;
339
+ const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
340
+ root.dataset.theme = next;
341
+ try {
342
+ localStorage.setItem('theme', next);
343
+ } catch (e) {}
344
+ updateThemeLabel();
345
+ }
346
+
347
+ function updateThemeLabel() {
348
+ const isDark = document.documentElement.dataset.theme === 'dark';
349
+ document.getElementById('themeLabel').textContent = isDark ? '淺色' : '深色';
350
+ document.getElementById('themeToggle').setAttribute('aria-label', isDark ? '切換為淺色背景' : '切換為深色背景');
351
+ }
352
+
353
+ updateThemeLabel();
354
+
355
  // 選擇圖片時即時顯示預覽
356
  function previewSelectedImage(event) {
357
  const file = event.target.files[0];