HelloSun commited on
Commit
02bc2f8
·
verified ·
1 Parent(s): 33c8390

Create index.html_v1

Browse files
Files changed (1) hide show
  1. index.html_v1 +723 -0
index.html_v1 ADDED
@@ -0,0 +1,723 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-TW">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>單牌占卜法 - One Card Tarot</title>
7
+ <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
+ <style>
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
19
+ min-height: 100vh;
20
+ display: flex;
21
+ justify-content: center;
22
+ align-items: center;
23
+ padding: 20px;
24
+ }
25
+
26
+ .container {
27
+ background: rgba(255, 255, 255, 0.95);
28
+ border-radius: 20px;
29
+ padding: 40px;
30
+ max-width: 800px;
31
+ width: 100%;
32
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
33
+ }
34
+
35
+ h1 {
36
+ text-align: center;
37
+ color: #2a5298;
38
+ margin-bottom: 10px;
39
+ font-size: 2.5em;
40
+ }
41
+
42
+ .subtitle {
43
+ text-align: center;
44
+ color: #666;
45
+ margin-bottom: 30px;
46
+ }
47
+
48
+ .input-section {
49
+ margin-bottom: 30px;
50
+ }
51
+
52
+ label {
53
+ display: block;
54
+ margin-bottom: 10px;
55
+ color: #333;
56
+ font-weight: 600;
57
+ }
58
+
59
+ textarea {
60
+ width: 100%;
61
+ padding: 15px;
62
+ border: 2px solid #ddd;
63
+ border-radius: 10px;
64
+ font-size: 16px;
65
+ resize: vertical;
66
+ min-height: 100px;
67
+ transition: border-color 0.3s;
68
+ }
69
+
70
+ textarea:focus {
71
+ outline: none;
72
+ border-color: #2a5298;
73
+ }
74
+
75
+ .btn {
76
+ width: 100%;
77
+ padding: 15px;
78
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
79
+ color: white;
80
+ border: none;
81
+ border-radius: 10px;
82
+ font-size: 18px;
83
+ font-weight: 600;
84
+ cursor: pointer;
85
+ transition: transform 0.2s, box-shadow 0.2s;
86
+ }
87
+
88
+ .btn:hover {
89
+ transform: translateY(-2px);
90
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
91
+ }
92
+
93
+ .btn:active {
94
+ transform: translateY(0);
95
+ }
96
+
97
+ .btn:disabled {
98
+ background: #ccc;
99
+ cursor: not-allowed;
100
+ transform: none;
101
+ }
102
+
103
+ .result-section {
104
+ margin-top: 30px;
105
+ display: none;
106
+ }
107
+
108
+ .result-section.show {
109
+ display: block;
110
+ animation: fadeIn 0.5s;
111
+ }
112
+
113
+ @keyframes fadeIn {
114
+ from { opacity: 0; transform: translateY(20px); }
115
+ to { opacity: 1; transform: translateY(0); }
116
+ }
117
+
118
+ .card-display {
119
+ display: flex;
120
+ flex-direction: column;
121
+ align-items: center;
122
+ margin-bottom: 30px;
123
+ }
124
+
125
+ .card-container {
126
+ position: relative;
127
+ width: 300px;
128
+ height: 500px;
129
+ margin-bottom: 20px;
130
+ }
131
+
132
+ .card {
133
+ width: 100%;
134
+ height: 100%;
135
+ border-radius: 15px;
136
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
137
+ overflow: hidden;
138
+ transition: transform 0.6s;
139
+ transform-style: preserve-3d;
140
+ background: #000;
141
+ }
142
+
143
+ .card.reversed {
144
+ transform: rotate(180deg);
145
+ }
146
+
147
+ .card img {
148
+ width: 100%;
149
+ height: 100%;
150
+ object-fit: cover;
151
+ }
152
+
153
+ .card-info {
154
+ text-align: center;
155
+ padding: 20px;
156
+ background: #f8f9fa;
157
+ border-radius: 10px;
158
+ width: 100%;
159
+ }
160
+
161
+ .card-name {
162
+ font-size: 24px;
163
+ color: #2a5298;
164
+ font-weight: bold;
165
+ margin-bottom: 10px;
166
+ }
167
+
168
+ .card-position {
169
+ font-size: 18px;
170
+ color: #666;
171
+ font-style: italic;
172
+ }
173
+
174
+ .interpretation {
175
+ background: #f8f9fa;
176
+ padding: 25px;
177
+ border-radius: 10px;
178
+ border-left: 5px solid #667eea;
179
+ }
180
+
181
+ .interpretation h3 {
182
+ color: #2a5298;
183
+ margin-bottom: 15px;
184
+ font-size: 20px;
185
+ }
186
+
187
+ /* Markdown 渲染樣式 */
188
+ .markdown-body {
189
+ line-height: 1.8;
190
+ color: #333;
191
+ word-wrap: break-word;
192
+ }
193
+
194
+ .markdown-body h1,
195
+ .markdown-body h2,
196
+ .markdown-body h3,
197
+ .markdown-body h4,
198
+ .markdown-body h5,
199
+ .markdown-body h6 {
200
+ margin-top: 20px;
201
+ margin-bottom: 12px;
202
+ font-weight: 600;
203
+ line-height: 1.4;
204
+ color: #2a5298;
205
+ }
206
+
207
+ .markdown-body h1 { font-size: 1.8em; border-bottom: 2px solid #eee; padding-bottom: 8px; }
208
+ .markdown-body h2 { font-size: 1.5em; border-bottom: 1px solid #eee; padding-bottom: 6px; }
209
+ .markdown-body h3 { font-size: 1.3em; }
210
+ .markdown-body h4 { font-size: 1.15em; }
211
+
212
+ .markdown-body p {
213
+ margin-bottom: 12px;
214
+ }
215
+
216
+ .markdown-body ul,
217
+ .markdown-body ol {
218
+ margin-bottom: 12px;
219
+ padding-left: 2em;
220
+ }
221
+
222
+ .markdown-body li {
223
+ margin-bottom: 6px;
224
+ }
225
+
226
+ .markdown-body strong {
227
+ color: #2a5298;
228
+ font-weight: 700;
229
+ }
230
+
231
+ .markdown-body em {
232
+ color: #555;
233
+ font-style: italic;
234
+ }
235
+
236
+ .markdown-body code {
237
+ background: #f0f0f0;
238
+ padding: 2px 6px;
239
+ border-radius: 4px;
240
+ font-family: 'Courier New', Courier, monospace;
241
+ font-size: 0.9em;
242
+ color: #c7254e;
243
+ }
244
+
245
+ .markdown-body pre {
246
+ background: #282c34;
247
+ color: #abb2bf;
248
+ padding: 15px;
249
+ border-radius: 8px;
250
+ overflow-x: auto;
251
+ margin-bottom: 15px;
252
+ }
253
+
254
+ .markdown-body pre code {
255
+ background: transparent;
256
+ color: inherit;
257
+ padding: 0;
258
+ font-size: 0.9em;
259
+ }
260
+
261
+ .markdown-body blockquote {
262
+ border-left: 4px solid #667eea;
263
+ padding: 10px 15px;
264
+ margin: 15px 0;
265
+ background: #f9f9ff;
266
+ color: #555;
267
+ border-radius: 0 8px 8px 0;
268
+ }
269
+
270
+ .markdown-body a {
271
+ color: #667eea;
272
+ text-decoration: none;
273
+ border-bottom: 1px dashed #667eea;
274
+ }
275
+
276
+ .markdown-body a:hover {
277
+ color: #764ba2;
278
+ border-bottom-style: solid;
279
+ }
280
+
281
+ .markdown-body hr {
282
+ border: none;
283
+ border-top: 1px solid #ddd;
284
+ margin: 20px 0;
285
+ }
286
+
287
+ .markdown-body table {
288
+ border-collapse: collapse;
289
+ width: 100%;
290
+ margin-bottom: 15px;
291
+ }
292
+
293
+ .markdown-body table th,
294
+ .markdown-body table td {
295
+ border: 1px solid #ddd;
296
+ padding: 8px 12px;
297
+ text-align: left;
298
+ }
299
+
300
+ .markdown-body table th {
301
+ background: #f0f0f0;
302
+ font-weight: 600;
303
+ }
304
+
305
+ .markdown-body table tr:nth-child(even) {
306
+ background: #f9f9f9;
307
+ }
308
+
309
+ .section-divider {
310
+ border: none;
311
+ border-top: 1px dashed #ccc;
312
+ margin: 20px 0;
313
+ }
314
+
315
+ .reasoning-section {
316
+ background: #fff9e6;
317
+ padding: 15px;
318
+ border-radius: 8px;
319
+ border-left: 4px solid #ffc107;
320
+ margin-bottom: 20px;
321
+ }
322
+
323
+ .reasoning-section summary {
324
+ cursor: pointer;
325
+ font-weight: 600;
326
+ color: #856404;
327
+ user-select: none;
328
+ }
329
+
330
+ .reasoning-section[open] summary {
331
+ margin-bottom: 10px;
332
+ }
333
+
334
+ .loading {
335
+ text-align: center;
336
+ padding: 40px;
337
+ display: none;
338
+ }
339
+
340
+ .loading.show {
341
+ display: block;
342
+ }
343
+
344
+ .spinner {
345
+ border: 4px solid #f3f3f3;
346
+ border-top: 4px solid #667eea;
347
+ border-radius: 50%;
348
+ width: 50px;
349
+ height: 50px;
350
+ animation: spin 1s linear infinite;
351
+ margin: 0 auto 20px;
352
+ }
353
+
354
+ @keyframes spin {
355
+ 0% { transform: rotate(0deg); }
356
+ 100% { transform: rotate(360deg); }
357
+ }
358
+
359
+ .error {
360
+ background: #fee;
361
+ color: #c33;
362
+ padding: 15px;
363
+ border-radius: 10px;
364
+ margin-top: 20px;
365
+ display: none;
366
+ }
367
+
368
+ .error.show {
369
+ display: block;
370
+ }
371
+
372
+ .status {
373
+ text-align: center;
374
+ padding: 10px;
375
+ margin-bottom: 20px;
376
+ border-radius: 5px;
377
+ display: none;
378
+ }
379
+
380
+ .status.show {
381
+ display: block;
382
+ }
383
+
384
+ .status.connecting {
385
+ background: #fff3cd;
386
+ color: #856404;
387
+ }
388
+
389
+ .status.connected {
390
+ background: #d4edda;
391
+ color: #155724;
392
+ }
393
+
394
+ .status.error {
395
+ background: #f8d7da;
396
+ color: #721c24;
397
+ }
398
+
399
+ .img-debug {
400
+ font-size: 12px;
401
+ color: #999;
402
+ margin-top: 5px;
403
+ word-break: break-all;
404
+ }
405
+ </style>
406
+ </head>
407
+ <body>
408
+ <div class="container">
409
+ <h1>🔮 ���牌占卜法</h1>
410
+ <p class="subtitle">One Card Tarot Spread</p>
411
+
412
+ <div id="status" class="status">準備連接...</div>
413
+
414
+ <div class="input-section">
415
+ <label for="question">請輸入您的問題:</label>
416
+ <textarea id="question" placeholder="例如:我最近的工作運勢如何?我應該如何面對這段感情?"></textarea>
417
+ </div>
418
+
419
+ <button class="btn" id="drawBtn" onclick="drawCard()">抽取塔羅牌</button>
420
+
421
+ <div class="loading" id="loading">
422
+ <div class="spinner"></div>
423
+ <p>正在抽取塔羅牌並進行解讀...</p>
424
+ </div>
425
+
426
+ <div class="error" id="error"></div>
427
+
428
+ <div class="result-section" id="result">
429
+ <div class="card-display">
430
+ <div class="card-container">
431
+ <div class="card" id="cardImage">
432
+ <img src="" alt="Tarot Card" id="cardImg">
433
+ </div>
434
+ </div>
435
+ <div class="card-info">
436
+ <div class="card-name" id="cardName"></div>
437
+ <div class="card-position" id="cardPosition"></div>
438
+ <div class="img-debug" id="imgDebug"></div>
439
+ </div>
440
+ </div>
441
+
442
+ <div class="interpretation">
443
+ <h3>📖 牌義解讀</h3>
444
+ <div id="reasoningContainer"></div>
445
+ <div class="markdown-body" id="interpretation"></div>
446
+ </div>
447
+ </div>
448
+ </div>
449
+
450
+ <script>
451
+ // 配置 marked.js
452
+ marked.setOptions({
453
+ breaks: true, // 支持換行符轉換為 <br>
454
+ gfm: true, // 啟用 GitHub Flavored Markdown
455
+ headerIds: true, // 為標題添加 id
456
+ mangle: false // 不轉義郵箱
457
+ });
458
+
459
+ // 配置
460
+ const UNIQUE_PREFIX = 'my_secure_hy3_test_8f7a';
461
+ const MQTT_BROKER = 'wss://broker.emqx.io:8084/mqtt';
462
+ const TAROT_IMAGES_URL = 'https://raw.githubusercontent.com/metabismuth/tarot-json/refs/heads/master/tarot-images.json';
463
+ const CARDS_BASE_URL = 'https://raw.githubusercontent.com/metabismuth/tarot-json/refs/heads/master/cards/';
464
+
465
+ // 全局變量
466
+ let client = null;
467
+ let clientId = null;
468
+ let tarotCards = [];
469
+ let isConnected = false;
470
+
471
+ // 初始化
472
+ window.onload = function() {
473
+ initMQTT();
474
+ loadTarotData();
475
+ };
476
+
477
+ // 加載塔羅牌數據(從 tarot-images.json)
478
+ async function loadTarotData() {
479
+ try {
480
+ console.log('正在加載塔羅牌圖片數據...');
481
+ const response = await fetch(TAROT_IMAGES_URL);
482
+ if (!response.ok) {
483
+ throw new Error(`HTTP error! status: ${response.status}`);
484
+ }
485
+
486
+ const data = await response.json();
487
+
488
+ if (Array.isArray(data)) {
489
+ tarotCards = data;
490
+ } else if (data.cards && Array.isArray(data.cards)) {
491
+ tarotCards = data.cards;
492
+ } else {
493
+ throw new Error('數據格式不正確');
494
+ }
495
+
496
+ console.log('塔羅牌數據加載成功,共', tarotCards.length, '張牌');
497
+ console.log('第一張牌示例:', tarotCards[0]);
498
+ } catch (error) {
499
+ console.error('加載塔羅牌數據失敗:', error);
500
+ showError('加載塔羅牌數據失敗,請刷新頁面重試');
501
+ }
502
+ }
503
+
504
+ // 初始化 MQTT 連接
505
+ function initMQTT() {
506
+ clientId = 'client_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
507
+
508
+ const options = {
509
+ clientId: clientId,
510
+ clean: true,
511
+ connectTimeout: 4000,
512
+ reconnectPeriod: 1000,
513
+ };
514
+
515
+ updateStatus('正在連接占卜伺服器...', 'connecting');
516
+
517
+ try {
518
+ client = mqtt.connect(MQTT_BROKER, options);
519
+
520
+ client.on('connect', function() {
521
+ console.log('MQTT 連接成功');
522
+ isConnected = true;
523
+ updateStatus('已連接占卜伺服器', 'connected');
524
+
525
+ const subscribeTopic = `${UNIQUE_PREFIX}/hy3/response/${clientId}`;
526
+ client.subscribe(subscribeTopic, function(err) {
527
+ if (!err) {
528
+ console.log('訂閱主題成功:', subscribeTopic);
529
+ } else {
530
+ console.error('訂閱失敗:', err);
531
+ }
532
+ });
533
+ });
534
+
535
+ client.on('message', function(topic, message) {
536
+ try {
537
+ const data = JSON.parse(message.toString());
538
+ console.log('收到回應:', data);
539
+
540
+ if (data.client_id === clientId) {
541
+ handleLLMResponse(data);
542
+ }
543
+ } catch (error) {
544
+ console.error('解析消息失敗:', error);
545
+ }
546
+ });
547
+
548
+ client.on('error', function(err) {
549
+ console.error('MQTT 錯誤:', err);
550
+ updateStatus('連接錯誤: ' + err.message, 'error');
551
+ isConnected = false;
552
+ });
553
+
554
+ client.on('close', function() {
555
+ console.log('MQTT 連接關閉');
556
+ isConnected = false;
557
+ updateStatus('連接已關閉', 'error');
558
+ });
559
+
560
+ } catch (error) {
561
+ console.error('MQTT 初始化失敗:', error);
562
+ updateStatus('初始化失敗: ' + error.message, 'error');
563
+ }
564
+ }
565
+
566
+ // 更新狀態顯示
567
+ function updateStatus(message, type) {
568
+ const statusEl = document.getElementById('status');
569
+ statusEl.textContent = message;
570
+ statusEl.className = 'status show ' + type;
571
+ }
572
+
573
+ // 抽取塔羅牌
574
+ async function drawCard() {
575
+ const question = document.getElementById('question').value.trim();
576
+
577
+ if (!question) {
578
+ alert('請先輸入您的問題');
579
+ return;
580
+ }
581
+
582
+ if (!isConnected) {
583
+ alert('尚未連接到占卜伺服器,請等待連接完成後再試');
584
+ return;
585
+ }
586
+
587
+ if (tarotCards.length === 0) {
588
+ alert('塔羅牌數據尚未加載完成,請稍後再試');
589
+ return;
590
+ }
591
+
592
+ // 顯示加載動畫
593
+ document.getElementById('loading').classList.add('show');
594
+ document.getElementById('result').classList.remove('show');
595
+ document.getElementById('error').classList.remove('show');
596
+ document.getElementById('drawBtn').disabled = true;
597
+ document.getElementById('reasoningContainer').innerHTML = '';
598
+ document.getElementById('interpretation').innerHTML = '';
599
+
600
+ try {
601
+ const randomIndex = Math.floor(Math.random() * tarotCards.length);
602
+ const selectedCard = tarotCards[randomIndex];
603
+ const isUpright = Math.random() > 0.5;
604
+
605
+ console.log('選中的牌:', selectedCard);
606
+ console.log('圖片文件名:', selectedCard.img);
607
+
608
+ displayCard(selectedCard, isUpright);
609
+
610
+ const position = isUpright ? '正位' : '逆位';
611
+ const requestData = {
612
+ client_id: clientId,
613
+ message: `問題:${question}\n\n抽到的牌:${selectedCard.name} (${position})\n牌組:${selectedCard.arcana}\n牌號:${selectedCard.number}\n\n請為這張牌進行詳細的占卜解讀,結合使用者的問題給出具體建議。請使用 Markdown 格式輸出,包含標題、重點標註、條列式建議等。`
614
+ };
615
+
616
+ console.log('發送請求:', requestData);
617
+
618
+ const publishTopic = `${UNIQUE_PREFIX}/hy3/request`;
619
+ client.publish(publishTopic, JSON.stringify(requestData), { qos: 1 }, function(err) {
620
+ if (err) {
621
+ console.error('發布失敗:', err);
622
+ showError('發送請求失敗,請稍後重試');
623
+ document.getElementById('loading').classList.remove('show');
624
+ document.getElementById('drawBtn').disabled = false;
625
+ }
626
+ });
627
+
628
+ } catch (error) {
629
+ console.error('抽牌錯誤:', error);
630
+ showError('抽牌過程發生錯誤: ' + error.message);
631
+ document.getElementById('loading').classList.remove('show');
632
+ document.getElementById('drawBtn').disabled = false;
633
+ }
634
+ }
635
+
636
+ // 顯示牌面
637
+ function displayCard(card, isUpright) {
638
+ const cardImg = document.getElementById('cardImg');
639
+ const cardName = document.getElementById('cardName');
640
+ const cardPosition = document.getElementById('cardPosition');
641
+ const cardElement = document.getElementById('cardImage');
642
+ const imgDebug = document.getElementById('imgDebug');
643
+
644
+ const imgFileName = card.img;
645
+ const imgUrl = CARDS_BASE_URL + imgFileName;
646
+
647
+ console.log('圖片文件名:', imgFileName);
648
+ console.log('完整圖片 URL:', imgUrl);
649
+
650
+ cardImg.src = imgUrl;
651
+ cardImg.alt = card.name;
652
+
653
+ cardImg.onerror = function() {
654
+ console.error('圖片加載失敗:', imgUrl);
655
+ showError('圖片加載失敗,請檢查網絡連接');
656
+ };
657
+
658
+ cardName.textContent = card.name;
659
+ cardPosition.textContent = isUpright ? '✨ 正位 (Upright)' : '🔄 逆位 (Reversed)';
660
+
661
+ imgDebug.textContent = `Image: ${imgFileName}`;
662
+
663
+ if (!isUpright) {
664
+ cardElement.classList.add('reversed');
665
+ } else {
666
+ cardElement.classList.remove('reversed');
667
+ }
668
+ }
669
+
670
+ // 處理 LLM 回應(使用 Markdown 解析)
671
+ function handleLLMResponse(data) {
672
+ document.getElementById('loading').classList.remove('show');
673
+ document.getElementById('drawBtn').disabled = false;
674
+
675
+ if (data.status === 'success') {
676
+ const interpretationEl = document.getElementById('interpretation');
677
+ const reasoningContainer = document.getElementById('reasoningContainer');
678
+
679
+ // 處理思考過程(可折疊)
680
+ if (data.reasoning && data.reasoning.trim()) {
681
+ const reasoningHtml = marked.parse(data.reasoning);
682
+ reasoningContainer.innerHTML = `
683
+ <details class="reasoning-section">
684
+ <summary>💭 查看 AI 思考過程</summary>
685
+ <div class="markdown-body">${reasoningHtml}</div>
686
+ </details>
687
+ `;
688
+ } else {
689
+ reasoningContainer.innerHTML = '';
690
+ }
691
+
692
+ // 解析主要結果為 Markdown
693
+ const resultHtml = marked.parse(data.result || '');
694
+ interpretationEl.innerHTML = resultHtml;
695
+
696
+ document.getElementById('result').classList.add('show');
697
+ } else if (data.status === 'error') {
698
+ showError('解讀失敗: ' + (data.error || '未知錯誤'));
699
+ } else {
700
+ showError('未知回應狀態: ' + data.status);
701
+ }
702
+ }
703
+
704
+ // 顯示錯誤
705
+ function showError(message) {
706
+ const errorEl = document.getElementById('error');
707
+ errorEl.textContent = message;
708
+ errorEl.classList.add('show');
709
+
710
+ setTimeout(() => {
711
+ errorEl.classList.remove('show');
712
+ }, 5000);
713
+ }
714
+
715
+ // 頁面卸載時清理
716
+ window.onbeforeunload = function() {
717
+ if (client) {
718
+ client.end();
719
+ }
720
+ };
721
+ </script>
722
+ </body>
723
+ </html>