Nanny7 commited on
Commit
bbfeecb
·
0 Parent(s):

feat: initial clean LFS commit of showcase

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.png filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: agy
3
+ emoji: 🌋
4
+ colorFrom: blue
5
+ colorTo: red
6
+ sdk: static
7
+ pinned: false
8
+ ---
9
+
10
+ # CWA Seismic Warning & AI Agent Swarm Showcase (AGY)
11
+
12
+ This Hugging Face Space showcases Taiwan's Central Weather Administration (CWA) next-generation seismic monitoring and earthquake warning technologies. It presents four key PowerPoint documents through an interactive, premium dark-mode web application featuring:
13
+
14
+ 1. **Slide Showcase Viewer**: Side-by-side slide viewing with extracted text outlines and key takeaways.
15
+ 2. **Blind Zone & Warning Lead Time Simulator**: Interactive physics calculations for travel times and warning zones.
16
+ 3. **AI Agent Swarm Flow Diagram**: Dynamic interactive visualization of CWA multi-agent automation loops.
17
+ 4. **大型地震模型 (LEM) Panel**: Context and details for the Wav2Vec 2.0-adapted SeisWav2Vec self-supervised deep learning model.
18
+ 5. **Universal Keyword Search Indexer**: Search through all 164 slides text content instantly.
index.css ADDED
@@ -0,0 +1,1070 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Noto+Sans+TC:wght@300;400;500;700;900&family=Outfit:wght@300;400;500;600;700;800;900&display=swap');
2
+
3
+ :root {
4
+ --bg-color: #080c14;
5
+ --card-bg: rgba(16, 22, 36, 0.7);
6
+ --card-border: rgba(255, 255, 255, 0.08);
7
+ --text-main: #f1f5f9;
8
+ --text-muted: #94a3b8;
9
+ --text-dark: #64748b;
10
+
11
+ /* Accent Colors */
12
+ --accent-cyan: #06b6d4;
13
+ --accent-cyan-glow: rgba(6, 182, 212, 0.15);
14
+ --accent-blue: #3b82f6;
15
+ --accent-blue-glow: rgba(59, 130, 246, 0.15);
16
+ --accent-amber: #f59e0b;
17
+ --accent-amber-glow: rgba(245, 158, 11, 0.15);
18
+ --accent-emerald: #10b981;
19
+ --accent-emerald-glow: rgba(16, 185, 129, 0.15);
20
+ --accent-rose: #f43f5e;
21
+ --accent-rose-glow: rgba(244, 63, 94, 0.15);
22
+
23
+ --font-sans: 'Outfit', 'Noto Sans TC', sans-serif;
24
+ --font-mono: 'JetBrains Mono', monospace;
25
+
26
+ --sidebar-width: 280px;
27
+ --header-height: 70px;
28
+ }
29
+
30
+ * {
31
+ box-sizing: border-box;
32
+ margin: 0;
33
+ padding: 0;
34
+ }
35
+
36
+ body {
37
+ background-color: var(--bg-color);
38
+ color: var(--text-main);
39
+ font-family: var(--font-sans);
40
+ min-height: 100vh;
41
+ display: flex;
42
+ overflow: hidden;
43
+ -webkit-font-smoothing: antialiased;
44
+ -moz-osx-font-smoothing: grayscale;
45
+ }
46
+
47
+ /* Custom Scrollbar */
48
+ ::-webkit-scrollbar {
49
+ width: 6px;
50
+ height: 6px;
51
+ }
52
+ ::-webkit-scrollbar-track {
53
+ background: rgba(8, 12, 20, 0.5);
54
+ }
55
+ ::-webkit-scrollbar-thumb {
56
+ background: rgba(148, 163, 184, 0.2);
57
+ border-radius: 99px;
58
+ }
59
+ ::-webkit-scrollbar-thumb:hover {
60
+ background: var(--accent-cyan);
61
+ }
62
+
63
+ /* Glassmorphism Panel styles */
64
+ .glass-panel {
65
+ background: var(--card-bg);
66
+ backdrop-filter: blur(12px);
67
+ -webkit-backdrop-filter: blur(12px);
68
+ border: 1px solid var(--card-border);
69
+ border-radius: 16px;
70
+ box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.6);
71
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
72
+ }
73
+
74
+ .glass-panel:hover {
75
+ border-color: rgba(255, 255, 255, 0.12);
76
+ }
77
+
78
+ /* Main Dashboard Layout */
79
+ .dashboard-container {
80
+ display: flex;
81
+ width: 100vw;
82
+ height: 100vh;
83
+ overflow: hidden;
84
+ background: radial-gradient(circle at 50% 0%, #0e1726 0%, #05080f 100%);
85
+ }
86
+
87
+ /* Sidebar Navigation */
88
+ .sidebar {
89
+ width: var(--sidebar-width);
90
+ height: 100%;
91
+ background: rgba(10, 15, 26, 0.85);
92
+ backdrop-filter: blur(20px);
93
+ -webkit-backdrop-filter: blur(20px);
94
+ border-right: 1px solid var(--card-border);
95
+ display: flex;
96
+ flex-direction: column;
97
+ z-index: 100;
98
+ flex-shrink: 0;
99
+ transition: transform 0.3s ease;
100
+ }
101
+
102
+ .sidebar-header {
103
+ padding: 24px;
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 12px;
107
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
108
+ }
109
+
110
+ .sidebar-logo {
111
+ width: 38px;
112
+ height: 38px;
113
+ border-radius: 10px;
114
+ background: linear-gradient(135deg, var(--accent-cyan) 0%, var(--accent-blue) 100%);
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ box-shadow: 0 4px 15px rgba(6, 182, 212, 0.3);
119
+ }
120
+
121
+ .sidebar-logo svg {
122
+ width: 22px;
123
+ height: 22px;
124
+ color: white;
125
+ }
126
+
127
+ .sidebar-title-container h2 {
128
+ font-size: 18px;
129
+ font-weight: 900;
130
+ letter-spacing: 0.5px;
131
+ background: linear-gradient(to right, #00f2fe, #4facfe);
132
+ -webkit-background-clip: text;
133
+ -webkit-text-fill-color: transparent;
134
+ }
135
+
136
+ .sidebar-title-container p {
137
+ font-size: 10px;
138
+ color: var(--text-muted);
139
+ font-family: var(--font-mono);
140
+ text-transform: uppercase;
141
+ margin-top: 1px;
142
+ }
143
+
144
+ .sidebar-menu {
145
+ flex: 1;
146
+ padding: 20px 14px;
147
+ display: flex;
148
+ flex-direction: column;
149
+ gap: 6px;
150
+ overflow-y: auto;
151
+ }
152
+
153
+ .menu-item {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 12px;
157
+ padding: 12px 16px;
158
+ color: var(--text-muted);
159
+ text-decoration: none;
160
+ font-size: 14px;
161
+ font-weight: 500;
162
+ border-radius: 12px;
163
+ transition: all 0.2s ease;
164
+ cursor: pointer;
165
+ border: 1px solid transparent;
166
+ }
167
+
168
+ .menu-item svg {
169
+ width: 18px;
170
+ height: 18px;
171
+ transition: transform 0.2s ease;
172
+ }
173
+
174
+ .menu-item:hover {
175
+ color: var(--text-main);
176
+ background: rgba(255, 255, 255, 0.04);
177
+ }
178
+
179
+ .menu-item:hover svg {
180
+ transform: translateX(2px);
181
+ }
182
+
183
+ .menu-item.active {
184
+ color: #white;
185
+ background: linear-gradient(90deg, rgba(6, 182, 212, 0.15) 0%, rgba(59, 130, 246, 0.05) 100%);
186
+ border-color: rgba(6, 182, 212, 0.3);
187
+ box-shadow: 0 4px 15px rgba(6, 182, 212, 0.05);
188
+ }
189
+
190
+ .menu-item.active svg {
191
+ color: var(--accent-cyan);
192
+ }
193
+
194
+ .sidebar-footer {
195
+ padding: 20px;
196
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
197
+ font-size: 11px;
198
+ color: var(--text-dark);
199
+ text-align: center;
200
+ font-family: var(--font-mono);
201
+ }
202
+
203
+ /* App Main Body */
204
+ .main-content {
205
+ flex: 1;
206
+ height: 100%;
207
+ display: flex;
208
+ flex-direction: column;
209
+ overflow: hidden;
210
+ position: relative;
211
+ }
212
+
213
+ /* Header */
214
+ .top-header {
215
+ height: var(--header-height);
216
+ padding: 0 32px;
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: space-between;
220
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
221
+ background: rgba(10, 15, 26, 0.3);
222
+ backdrop-filter: blur(10px);
223
+ -webkit-backdrop-filter: blur(10px);
224
+ z-index: 10;
225
+ flex-shrink: 0;
226
+ }
227
+
228
+ .header-title h3 {
229
+ font-size: 18px;
230
+ font-weight: 800;
231
+ color: white;
232
+ }
233
+
234
+ .header-title p {
235
+ font-size: 12px;
236
+ color: var(--text-muted);
237
+ }
238
+
239
+ .header-badge {
240
+ display: flex;
241
+ align-items: center;
242
+ gap: 8px;
243
+ background: rgba(6, 182, 212, 0.1);
244
+ border: 1px solid rgba(6, 182, 212, 0.2);
245
+ padding: 6px 14px;
246
+ border-radius: 99px;
247
+ font-size: 12px;
248
+ font-weight: 600;
249
+ color: var(--accent-cyan);
250
+ }
251
+
252
+ .header-badge span.pulse-dot {
253
+ width: 8px;
254
+ height: 8px;
255
+ background-color: var(--accent-cyan);
256
+ border-radius: 50%;
257
+ box-shadow: 0 0 10px var(--accent-cyan);
258
+ animation: pulse-ring 2s infinite;
259
+ }
260
+
261
+ /* Scrollable Viewport Container */
262
+ .viewport-container {
263
+ flex: 1;
264
+ padding: 32px;
265
+ overflow-y: auto;
266
+ position: relative;
267
+ }
268
+
269
+ /* Tab Sections */
270
+ .tab-section {
271
+ display: none;
272
+ animation: fadeIn 0.4s ease-out forwards;
273
+ }
274
+
275
+ .tab-section.active {
276
+ display: block;
277
+ }
278
+
279
+ /* Grid Systems */
280
+ .grid-container {
281
+ display: grid;
282
+ grid-template-columns: repeat(12, 1fr);
283
+ gap: 24px;
284
+ }
285
+
286
+ .col-12 { grid-column: span 12; }
287
+ .col-8 { grid-column: span 8; }
288
+ .col-6 { grid-column: span 6; }
289
+ .col-4 { grid-column: span 4; }
290
+
291
+ @media (max-width: 1024px) {
292
+ .col-8, .col-6, .col-4 {
293
+ grid-column: span 12;
294
+ }
295
+ }
296
+
297
+ /* General CSS components */
298
+ h4.section-title {
299
+ font-size: 20px;
300
+ font-weight: 800;
301
+ margin-bottom: 24px;
302
+ color: white;
303
+ display: flex;
304
+ align-items: center;
305
+ gap: 10px;
306
+ }
307
+
308
+ h4.section-title::before {
309
+ content: '';
310
+ width: 4px;
311
+ height: 20px;
312
+ background: linear-gradient(to bottom, var(--accent-cyan), var(--accent-blue));
313
+ border-radius: 2px;
314
+ display: inline-block;
315
+ }
316
+
317
+ /* CSS Cards */
318
+ .card-padding {
319
+ padding: 24px;
320
+ }
321
+
322
+ .card-title {
323
+ font-size: 16px;
324
+ font-weight: 700;
325
+ color: white;
326
+ margin-bottom: 16px;
327
+ display: flex;
328
+ align-items: center;
329
+ justify-content: space-between;
330
+ }
331
+
332
+ .text-content {
333
+ font-size: 14px;
334
+ line-height: 1.6;
335
+ color: var(--text-muted);
336
+ }
337
+
338
+ .text-content p {
339
+ margin-bottom: 12px;
340
+ }
341
+
342
+ /* Overview Dashboard Components */
343
+ .ppt-grid-card {
344
+ height: 100%;
345
+ cursor: pointer;
346
+ overflow: hidden;
347
+ position: relative;
348
+ }
349
+
350
+ .ppt-grid-card::before {
351
+ content: '';
352
+ position: absolute;
353
+ top: 0;
354
+ left: 0;
355
+ width: 100%;
356
+ height: 4px;
357
+ background: linear-gradient(90deg, var(--accent-blue) 0%, var(--accent-cyan) 100%);
358
+ opacity: 0.7;
359
+ }
360
+
361
+ .ppt-grid-card:hover {
362
+ transform: translateY(-4px);
363
+ border-color: rgba(6, 182, 212, 0.3);
364
+ box-shadow: 0 15px 30px -10px rgba(6, 182, 212, 0.15);
365
+ }
366
+
367
+ .ppt-tag {
368
+ display: inline-block;
369
+ padding: 4px 10px;
370
+ background: rgba(59, 130, 246, 0.1);
371
+ border: 1px solid rgba(59, 130, 246, 0.2);
372
+ color: #60a5fa;
373
+ font-size: 11px;
374
+ font-weight: 700;
375
+ border-radius: 6px;
376
+ margin-bottom: 12px;
377
+ text-transform: uppercase;
378
+ font-family: var(--font-mono);
379
+ }
380
+
381
+ .ppt-grid-card:nth-child(2) .ppt-tag {
382
+ background: rgba(16, 185, 129, 0.1);
383
+ border-color: rgba(16, 185, 129, 0.2);
384
+ color: #34d399;
385
+ }
386
+ .ppt-grid-card:nth-child(3) .ppt-tag {
387
+ background: rgba(245, 158, 11, 0.1);
388
+ border-color: rgba(245, 158, 11, 0.2);
389
+ color: #fbbf24;
390
+ }
391
+ .ppt-grid-card:nth-child(4) .ppt-tag {
392
+ background: rgba(244, 63, 94, 0.1);
393
+ border-color: rgba(244, 63, 94, 0.2);
394
+ color: #f87171;
395
+ }
396
+
397
+ .ppt-title {
398
+ font-size: 15px;
399
+ font-weight: 800;
400
+ color: white;
401
+ line-height: 1.4;
402
+ margin-bottom: 12px;
403
+ min-height: 42px;
404
+ }
405
+
406
+ .ppt-info {
407
+ display: flex;
408
+ align-items: center;
409
+ justify-content: space-between;
410
+ font-size: 12px;
411
+ color: var(--text-dark);
412
+ font-family: var(--font-mono);
413
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
414
+ padding-top: 12px;
415
+ margin-top: 8px;
416
+ }
417
+
418
+ .view-btn {
419
+ background: transparent;
420
+ border: none;
421
+ color: var(--accent-cyan);
422
+ font-size: 13px;
423
+ font-weight: 700;
424
+ display: flex;
425
+ align-items: center;
426
+ gap: 6px;
427
+ cursor: pointer;
428
+ transition: all 0.2s ease;
429
+ }
430
+
431
+ .view-btn:hover {
432
+ color: var(--text-main);
433
+ }
434
+
435
+ /* Slide Viewer System */
436
+ .slide-layout-wrapper {
437
+ display: flex;
438
+ gap: 24px;
439
+ height: calc(100vh - 160px);
440
+ }
441
+
442
+ .slide-selector-pane {
443
+ width: 250px;
444
+ display: flex;
445
+ flex-direction: column;
446
+ flex-shrink: 0;
447
+ height: 100%;
448
+ }
449
+
450
+ .slide-selector-header {
451
+ padding: 16px;
452
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
453
+ }
454
+
455
+ .ppt-selector-dropdown {
456
+ width: 100%;
457
+ background: rgba(8, 12, 20, 0.8);
458
+ border: 1px solid var(--card-border);
459
+ color: white;
460
+ padding: 10px 14px;
461
+ border-radius: 10px;
462
+ font-weight: 600;
463
+ font-size: 13px;
464
+ cursor: pointer;
465
+ outline: none;
466
+ }
467
+
468
+ .slide-thumbnails-list {
469
+ flex: 1;
470
+ overflow-y: auto;
471
+ padding: 12px;
472
+ display: flex;
473
+ flex-direction: column;
474
+ gap: 10px;
475
+ }
476
+
477
+ .thumb-item {
478
+ padding: 10px 14px;
479
+ background: rgba(255, 255, 255, 0.02);
480
+ border: 1px solid transparent;
481
+ border-radius: 10px;
482
+ cursor: pointer;
483
+ transition: all 0.2s ease;
484
+ }
485
+
486
+ .thumb-item:hover {
487
+ background: rgba(255, 255, 255, 0.05);
488
+ }
489
+
490
+ .thumb-item.active {
491
+ background: rgba(6, 182, 212, 0.06);
492
+ border-color: rgba(6, 182, 212, 0.3);
493
+ }
494
+
495
+ .thumb-num {
496
+ font-family: var(--font-mono);
497
+ font-size: 11px;
498
+ font-weight: 700;
499
+ color: var(--accent-cyan);
500
+ margin-bottom: 4px;
501
+ display: block;
502
+ }
503
+
504
+ .thumb-title {
505
+ font-size: 12px;
506
+ color: var(--text-main);
507
+ font-weight: 600;
508
+ overflow: hidden;
509
+ text-overflow: ellipsis;
510
+ white-space: nowrap;
511
+ }
512
+
513
+ .slide-viewer-pane {
514
+ flex: 1;
515
+ display: flex;
516
+ flex-direction: column;
517
+ height: 100%;
518
+ gap: 20px;
519
+ }
520
+
521
+ .slide-display-card {
522
+ flex: 1;
523
+ position: relative;
524
+ display: flex;
525
+ align-items: center;
526
+ justify-content: center;
527
+ overflow: hidden;
528
+ background: #020408;
529
+ border-radius: 16px;
530
+ border: 1px solid var(--card-border);
531
+ }
532
+
533
+ .slide-display-card img {
534
+ max-width: 95%;
535
+ max-height: 95%;
536
+ object-contain: fit;
537
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);
538
+ border-radius: 4px;
539
+ transition: transform 0.3s ease;
540
+ cursor: zoom-in;
541
+ }
542
+
543
+ .slide-fullscreen-btn {
544
+ position: absolute;
545
+ top: 16px;
546
+ right: 16px;
547
+ width: 36px;
548
+ height: 36px;
549
+ border-radius: 10px;
550
+ background: rgba(16, 22, 36, 0.8);
551
+ border: 1px solid var(--card-border);
552
+ color: white;
553
+ display: flex;
554
+ align-items: center;
555
+ justify-content: center;
556
+ cursor: pointer;
557
+ z-index: 5;
558
+ transition: all 0.2s ease;
559
+ }
560
+
561
+ .slide-fullscreen-btn:hover {
562
+ background: var(--accent-cyan);
563
+ border-color: var(--accent-cyan);
564
+ }
565
+
566
+ .slide-info-card {
567
+ height: 200px;
568
+ display: flex;
569
+ overflow: hidden;
570
+ flex-shrink: 0;
571
+ }
572
+
573
+ .slide-info-text {
574
+ flex: 1;
575
+ padding: 20px;
576
+ overflow-y: auto;
577
+ border-right: 1px solid rgba(255, 255, 255, 0.05);
578
+ }
579
+
580
+ .slide-info-takeaway {
581
+ width: 300px;
582
+ padding: 20px;
583
+ overflow-y: auto;
584
+ background: rgba(8, 12, 20, 0.3);
585
+ }
586
+
587
+ .slide-info-card h5 {
588
+ font-size: 14px;
589
+ font-weight: 800;
590
+ color: white;
591
+ margin-bottom: 12px;
592
+ display: flex;
593
+ align-items: center;
594
+ gap: 8px;
595
+ }
596
+
597
+ .slide-info-card ul {
598
+ list-style: none;
599
+ }
600
+
601
+ .slide-info-card li {
602
+ font-size: 13px;
603
+ color: var(--text-muted);
604
+ line-height: 1.6;
605
+ margin-bottom: 8px;
606
+ position: relative;
607
+ padding-left: 14px;
608
+ }
609
+
610
+ .slide-info-card li::before {
611
+ content: '';
612
+ position: absolute;
613
+ left: 0;
614
+ top: 7px;
615
+ width: 5px;
616
+ height: 5px;
617
+ border-radius: 50%;
618
+ background-color: var(--accent-cyan);
619
+ }
620
+
621
+ .slide-viewer-controls {
622
+ display: flex;
623
+ align-items: center;
624
+ justify-content: space-between;
625
+ background: rgba(16, 22, 36, 0.5);
626
+ border: 1px solid var(--card-border);
627
+ padding: 10px 20px;
628
+ border-radius: 12px;
629
+ flex-shrink: 0;
630
+ }
631
+
632
+ .nav-btn {
633
+ background: rgba(255, 255, 255, 0.03);
634
+ border: 1px solid var(--card-border);
635
+ color: white;
636
+ padding: 8px 16px;
637
+ border-radius: 8px;
638
+ cursor: pointer;
639
+ font-weight: 700;
640
+ font-size: 13px;
641
+ display: flex;
642
+ align-items: center;
643
+ gap: 6px;
644
+ transition: all 0.2s ease;
645
+ }
646
+
647
+ .nav-btn:hover:not(:disabled) {
648
+ background: rgba(255, 255, 255, 0.08);
649
+ }
650
+
651
+ .nav-btn:disabled {
652
+ opacity: 0.3;
653
+ cursor: not-allowed;
654
+ }
655
+
656
+ /* Simulator System (Blind Zone warning lead time calculator) */
657
+ .input-group {
658
+ margin-bottom: 20px;
659
+ }
660
+
661
+ .input-label {
662
+ display: flex;
663
+ justify-content: space-between;
664
+ font-size: 13px;
665
+ font-weight: 700;
666
+ color: var(--text-muted);
667
+ margin-bottom: 8px;
668
+ }
669
+
670
+ .input-label span.value-display {
671
+ color: var(--accent-cyan);
672
+ font-family: var(--font-mono);
673
+ font-weight: 700;
674
+ }
675
+
676
+ .input-slider {
677
+ width: 100%;
678
+ height: 6px;
679
+ background: rgba(0, 0, 0, 0.5);
680
+ border-radius: 99px;
681
+ outline: none;
682
+ appearance: none;
683
+ cursor: pointer;
684
+ accent-color: var(--accent-cyan);
685
+ }
686
+
687
+ .calculator-summary-box {
688
+ background: rgba(6, 182, 212, 0.04);
689
+ border: 1px solid rgba(6, 182, 212, 0.15);
690
+ border-radius: 12px;
691
+ padding: 20px;
692
+ margin-top: 24px;
693
+ }
694
+
695
+ .lead-time-large-display {
696
+ font-size: 48px;
697
+ font-weight: 900;
698
+ color: var(--accent-cyan);
699
+ font-family: var(--font-mono);
700
+ text-shadow: 0 0 20px rgba(6, 182, 212, 0.4);
701
+ margin-bottom: 8px;
702
+ text-align: center;
703
+ }
704
+
705
+ .lead-time-large-display span {
706
+ font-size: 20px;
707
+ font-weight: 700;
708
+ margin-left: 4px;
709
+ text-shadow: none;
710
+ }
711
+
712
+ .warning-status-alert {
713
+ text-align: center;
714
+ font-size: 14px;
715
+ font-weight: 800;
716
+ padding: 8px 12px;
717
+ border-radius: 8px;
718
+ margin-top: 12px;
719
+ text-transform: uppercase;
720
+ letter-spacing: 0.5px;
721
+ }
722
+
723
+ .status-safe {
724
+ background: rgba(16, 185, 129, 0.15);
725
+ border: 1px solid rgba(16, 185, 129, 0.3);
726
+ color: #34d399;
727
+ }
728
+
729
+ .status-blind {
730
+ background: rgba(244, 63, 94, 0.15);
731
+ border: 1px solid rgba(244, 63, 94, 0.3);
732
+ color: #f87171;
733
+ }
734
+
735
+ /* Blind zone graphics */
736
+ .blind-zone-canvas-wrapper {
737
+ width: 100%;
738
+ height: 380px;
739
+ background: #020408;
740
+ border-radius: 12px;
741
+ border: 1px solid var(--card-border);
742
+ overflow: hidden;
743
+ position: relative;
744
+ display: flex;
745
+ align-items: center;
746
+ justify-content: center;
747
+ }
748
+
749
+ .blind-zone-canvas-wrapper canvas {
750
+ width: 100%;
751
+ height: 100%;
752
+ }
753
+
754
+ /* Agent Swarm System Visualizer */
755
+ .agent-swarm-wrapper {
756
+ position: relative;
757
+ width: 100%;
758
+ height: 480px;
759
+ background: #020408;
760
+ border-radius: 16px;
761
+ border: 1px solid var(--card-border);
762
+ overflow: hidden;
763
+ cursor: grab;
764
+ }
765
+
766
+ .agent-card-overlay {
767
+ position: absolute;
768
+ bottom: 24px;
769
+ left: 24px;
770
+ right: 24px;
771
+ background: rgba(10, 15, 26, 0.9);
772
+ border: 1px solid var(--card-border);
773
+ border-radius: 14px;
774
+ padding: 20px;
775
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.7);
776
+ display: flex;
777
+ gap: 16px;
778
+ align-items: flex-start;
779
+ z-index: 10;
780
+ }
781
+
782
+ .agent-icon-box {
783
+ width: 48px;
784
+ height: 48px;
785
+ border-radius: 12px;
786
+ display: flex;
787
+ align-items: center;
788
+ justify-content: center;
789
+ flex-shrink: 0;
790
+ box-shadow: 0 4px 12px rgba(0,0,0,0.5);
791
+ }
792
+
793
+ .agent-details h4 {
794
+ font-size: 15px;
795
+ font-weight: 800;
796
+ color: white;
797
+ margin-bottom: 4px;
798
+ display: flex;
799
+ align-items: center;
800
+ gap: 10px;
801
+ }
802
+
803
+ .agent-details h4 span.agent-role-badge {
804
+ font-size: 10px;
805
+ font-family: var(--font-mono);
806
+ padding: 2px 8px;
807
+ border-radius: 4px;
808
+ font-weight: 700;
809
+ text-transform: uppercase;
810
+ }
811
+
812
+ .agent-details p {
813
+ font-size: 13px;
814
+ color: var(--text-muted);
815
+ line-height: 1.5;
816
+ }
817
+
818
+ /* Search engine layout */
819
+ .search-container {
820
+ display: flex;
821
+ flex-direction: column;
822
+ gap: 20px;
823
+ height: calc(100vh - 160px);
824
+ }
825
+
826
+ .search-input-box {
827
+ position: relative;
828
+ width: 100%;
829
+ }
830
+
831
+ .search-input-box input {
832
+ width: 100%;
833
+ background: rgba(16, 22, 36, 0.7);
834
+ border: 1px solid var(--card-border);
835
+ color: white;
836
+ padding: 16px 20px 16px 54px;
837
+ border-radius: 14px;
838
+ font-size: 15px;
839
+ font-weight: 600;
840
+ outline: none;
841
+ transition: all 0.3s ease;
842
+ backdrop-filter: blur(10px);
843
+ }
844
+
845
+ .search-input-box input:focus {
846
+ border-color: var(--accent-cyan);
847
+ box-shadow: 0 0 20px var(--accent-cyan-glow);
848
+ }
849
+
850
+ .search-input-box svg {
851
+ position: absolute;
852
+ top: 17px;
853
+ left: 20px;
854
+ width: 20px;
855
+ height: 20px;
856
+ color: var(--text-muted);
857
+ }
858
+
859
+ .search-results-viewport {
860
+ flex: 1;
861
+ overflow-y: auto;
862
+ padding-right: 8px;
863
+ display: flex;
864
+ flex-direction: column;
865
+ gap: 16px;
866
+ }
867
+
868
+ .search-result-card {
869
+ display: flex;
870
+ gap: 20px;
871
+ cursor: pointer;
872
+ }
873
+
874
+ .search-result-card:hover {
875
+ border-color: rgba(6, 182, 212, 0.3);
876
+ background: rgba(255, 255, 255, 0.02);
877
+ }
878
+
879
+ .result-image-box {
880
+ width: 160px;
881
+ height: 100px;
882
+ background: #020408;
883
+ border-radius: 10px;
884
+ overflow: hidden;
885
+ border: 1px solid rgba(255, 255, 255, 0.05);
886
+ display: flex;
887
+ align-items: center;
888
+ justify-content: center;
889
+ flex-shrink: 0;
890
+ }
891
+
892
+ .result-image-box img {
893
+ max-width: 95%;
894
+ max-height: 95%;
895
+ object-fit: contain;
896
+ }
897
+
898
+ .result-content-box {
899
+ flex: 1;
900
+ display: flex;
901
+ flex-direction: column;
902
+ justify-content: center;
903
+ min-width: 0;
904
+ }
905
+
906
+ .result-meta {
907
+ font-size: 11px;
908
+ font-family: var(--font-mono);
909
+ font-weight: 700;
910
+ color: var(--accent-cyan);
911
+ margin-bottom: 6px;
912
+ }
913
+
914
+ .result-title {
915
+ font-size: 14px;
916
+ font-weight: 700;
917
+ color: white;
918
+ margin-bottom: 6px;
919
+ overflow: hidden;
920
+ text-overflow: ellipsis;
921
+ white-space: nowrap;
922
+ }
923
+
924
+ .result-snippet {
925
+ font-size: 12.5px;
926
+ color: var(--text-muted);
927
+ line-height: 1.5;
928
+ display: -webkit-box;
929
+ -webkit-line-clamp: 2;
930
+ -webkit-box-orient: vertical;
931
+ overflow: hidden;
932
+ }
933
+
934
+ .no-results-view {
935
+ text-align: center;
936
+ padding: 60px 20px;
937
+ color: var(--text-dark);
938
+ }
939
+
940
+ .no-results-view svg {
941
+ width: 48px;
942
+ height: 48px;
943
+ margin-bottom: 16px;
944
+ opacity: 0.5;
945
+ }
946
+
947
+ /* Fullscreen lightroom modal */
948
+ .lightroom-modal {
949
+ position: fixed;
950
+ top: 0;
951
+ left: 0;
952
+ width: 100vw;
953
+ height: 100vh;
954
+ background: rgba(2, 4, 8, 0.95);
955
+ backdrop-filter: blur(12px);
956
+ z-index: 1000;
957
+ display: none;
958
+ align-items: center;
959
+ justify-content: center;
960
+ cursor: zoom-out;
961
+ animation: fadeIn 0.2s ease-out forwards;
962
+ }
963
+
964
+ .lightroom-modal img {
965
+ max-width: 95vw;
966
+ max-height: 90vh;
967
+ object-fit: contain;
968
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.9);
969
+ border-radius: 6px;
970
+ }
971
+
972
+ .lightroom-close-btn {
973
+ position: absolute;
974
+ top: 24px;
975
+ right: 24px;
976
+ width: 48px;
977
+ height: 48px;
978
+ border-radius: 50%;
979
+ background: rgba(16, 22, 36, 0.8);
980
+ border: 1px solid var(--card-border);
981
+ color: white;
982
+ display: flex;
983
+ align-items: center;
984
+ justify-content: center;
985
+ cursor: pointer;
986
+ transition: all 0.2s ease;
987
+ }
988
+
989
+ .lightroom-close-btn:hover {
990
+ background: var(--accent-rose);
991
+ border-color: var(--accent-rose);
992
+ }
993
+
994
+ /* Animations */
995
+ @keyframes fadeIn {
996
+ from { opacity: 0; transform: translateY(6px); }
997
+ to { opacity: 1; transform: translateY(0); }
998
+ }
999
+
1000
+ @keyframes pulse-ring {
1001
+ 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(6, 182, 212, 0.5); }
1002
+ 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(6, 182, 212, 0); }
1003
+ 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(6, 182, 212, 0); }
1004
+ }
1005
+
1006
+ /* Download Center */
1007
+ .download-card {
1008
+ display: flex;
1009
+ justify-content: space-between;
1010
+ align-items: center;
1011
+ padding: 20px 24px;
1012
+ background: rgba(255, 255, 255, 0.02);
1013
+ border: 1px solid var(--card-border);
1014
+ border-radius: 12px;
1015
+ margin-bottom: 16px;
1016
+ transition: all 0.2s ease;
1017
+ }
1018
+
1019
+ .download-card:hover {
1020
+ background: rgba(255, 255, 255, 0.04);
1021
+ border-color: rgba(59, 130, 246, 0.3);
1022
+ }
1023
+
1024
+ .download-meta {
1025
+ display: flex;
1026
+ align-items: center;
1027
+ gap: 16px;
1028
+ }
1029
+
1030
+ .download-icon {
1031
+ width: 42px;
1032
+ height: 42px;
1033
+ background: rgba(59, 130, 246, 0.15);
1034
+ border-radius: 10px;
1035
+ display: flex;
1036
+ align-items: center;
1037
+ justify-content: center;
1038
+ color: var(--accent-blue);
1039
+ flex-shrink: 0;
1040
+ }
1041
+
1042
+ .download-details h5 {
1043
+ font-size: 14px;
1044
+ font-weight: 700;
1045
+ color: white;
1046
+ margin-bottom: 2px;
1047
+ }
1048
+
1049
+ .download-details p {
1050
+ font-size: 12px;
1051
+ color: var(--text-dark);
1052
+ }
1053
+
1054
+ .download-action-btn {
1055
+ background: var(--accent-blue);
1056
+ color: white;
1057
+ border: none;
1058
+ padding: 10px 18px;
1059
+ border-radius: 8px;
1060
+ font-size: 13px;
1061
+ font-weight: 700;
1062
+ cursor: pointer;
1063
+ transition: all 0.2s ease;
1064
+ text-decoration: none;
1065
+ }
1066
+
1067
+ .download-action-btn:hover {
1068
+ background: #2563eb;
1069
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
1070
+ }
index.html ADDED
@@ -0,0 +1,629 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>CWA 次世代地震預警與 AI 代理系統展示平台</title>
7
+ <link rel="stylesheet" href="index.css">
8
+ </head>
9
+ <body>
10
+
11
+ <div class="dashboard-container">
12
+
13
+ <!-- Sidebar Navigation -->
14
+ <aside class="sidebar">
15
+ <div class="sidebar-header">
16
+ <div class="sidebar-logo">
17
+ <!-- Waveform Icon -->
18
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
19
+ <path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
20
+ </svg>
21
+ </div>
22
+ <div class="sidebar-title-container">
23
+ <h2>AGY 展示平台</h2>
24
+ <p>CWA EEW & AI Swarm</p>
25
+ </div>
26
+ </div>
27
+
28
+ <nav class="sidebar-menu">
29
+ <div class="menu-item active" data-tab="overview">
30
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
31
+ <rect x="3" y="3" width="7" height="9"/>
32
+ <rect x="14" y="3" width="7" height="5"/>
33
+ <rect x="14" y="12" width="7" height="9"/>
34
+ <rect x="3" y="16" width="7" height="5"/>
35
+ </svg>
36
+ 知識庫總覽
37
+ </div>
38
+ <div class="menu-item" data-tab="highlights">
39
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
40
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
41
+ </svg>
42
+ 簡報精選精華
43
+ </div>
44
+ <div class="menu-item" data-tab="viewer">
45
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
46
+ <rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"/>
47
+ <line x1="7" y1="2" x2="7" y2="22"/>
48
+ <line x1="17" y1="2" x2="17" y2="22"/>
49
+ <line x1="2" y1="12" x2="22" y2="12"/>
50
+ </svg>
51
+ 投影片展示
52
+ </div>
53
+ <div class="menu-item" data-tab="calculator">
54
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
55
+ <circle cx="12" cy="12" r="10"/>
56
+ <circle cx="12" cy="12" r="6"/>
57
+ <circle cx="12" cy="12" r="2"/>
58
+ </svg>
59
+ 預警盲區模擬
60
+ </div>
61
+ <div class="menu-item" data-tab="swarm">
62
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
63
+ <circle cx="18" cy="5" r="3"/>
64
+ <circle cx="6" cy="12" r="3"/>
65
+ <circle cx="18" cy="19" r="3"/>
66
+ <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
67
+ <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
68
+ </svg>
69
+ AI 代理協作
70
+ </div>
71
+ <div class="menu-item" data-tab="lem">
72
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
73
+ <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
74
+ <polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
75
+ <line x1="12" y1="22.08" x2="12" y2="12"/>
76
+ </svg>
77
+ 大型地震模型 (LEM)
78
+ </div>
79
+ <div class="menu-item" data-tab="search">
80
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
81
+ <circle cx="11" cy="11" r="8"/>
82
+ <line x1="21" y1="21" x2="16.65" y2="16.65"/>
83
+ </svg>
84
+ 全文大綱搜尋
85
+ </div>
86
+ <div class="menu-item" data-tab="downloads">
87
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
88
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
89
+ <polyline points="7 10 12 15 17 10"/>
90
+ <line x1="12" y1="15" x2="12" y2="3"/>
91
+ </svg>
92
+ 簡報下載專區
93
+ </div>
94
+ </nav>
95
+
96
+ <div class="sidebar-footer">
97
+ CWA SEISMOLOGY &copy; 2026
98
+ </div>
99
+ </aside>
100
+
101
+ <!-- Main Content Area -->
102
+ <main class="main-content">
103
+
104
+ <!-- Top Header -->
105
+ <header class="top-header">
106
+ <div class="header-title">
107
+ <h3 id="current-tab-title">知識庫總覽</h3>
108
+ <p id="current-tab-subtitle">展示台灣中央氣象署強震即時警報與 AI 技術整合之演進</p>
109
+ </div>
110
+ <div class="header-badge">
111
+ <span class="pulse-dot"></span>
112
+ <span>系統狀態:運作中</span>
113
+ </div>
114
+ </header>
115
+
116
+ <!-- Viewport View Sections -->
117
+ <div class="viewport-container">
118
+
119
+ <!-- ================= OVERVIEW TAB ================= -->
120
+ <section id="tab-overview" class="tab-section active">
121
+ <div class="grid-container">
122
+
123
+ <div class="col-12">
124
+ <div class="glass-panel card-padding">
125
+ <h4 class="section-title">中央氣象署次世代地震測報技術專案</h4>
126
+ <div class="text-content">
127
+ <p>本專案旨在匯整與展示台灣中央氣象署(CWA)地震測報中心在強震即時警報系統(EEW)效能、大規模地震觀測網架構、以及引領次世代地震觀測發展的最新技術。本平台彙整了四大關鍵技術簡報的精華知識,涵蓋從基礎開源框架(Earthworm、Seiscomp、Grafana)到前沿人工智慧技術(大型地震模型 LEM、SeisWav2Vec 2.0 自監督預訓練、以及 AI Agent 協作群組),幫助您全方位、多維度地理解次世代地震測報核心架構。</p>
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <div class="col-12">
133
+ <h4 style="font-size: 16px; font-weight: 800; color: white; margin: 12px 0 8px 0; letter-spacing: 0.5px;">請選擇欲查看的專題簡報:</h4>
134
+ </div>
135
+
136
+ <!-- PPT Cards -->
137
+ <div class="col-6">
138
+ <div class="glass-panel card-padding ppt-grid-card" onclick="jumpToPresentation('ppt1')">
139
+ <span class="ppt-tag">即時監測與架構</span>
140
+ <h3 class="ppt-title">Real-Time Seismic Data Processing and Monitoring at CWA</h3>
141
+ <p class="text-content" style="font-size: 13px; min-height: 60px;">探討中央氣象署的全面性地震監測基礎設施,說明如何利用開源監測軟體 Earthworm 與 Seiscomp 進行即時波形處理,並展示結合 Grafana 建構的即時系統狀態、數據延遲與 availability 儀表板。</p>
142
+ <div class="ppt-info">
143
+ <span>投影片數量:26 張</span>
144
+ <button class="view-btn">
145
+ 進入簡報
146
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
147
+ </button>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <div class="col-6">
153
+ <div class="glass-panel card-padding ppt-grid-card" onclick="jumpToPresentation('ppt2')">
154
+ <span class="ppt-tag">效能統計與案例</span>
155
+ <h3 class="ppt-title">2026年5月 地震預警系統報告</h3>
156
+ <p class="text-content" style="font-size: 13px; min-height: 60px;">詳細整理 2026 年 5 月 CWA 強震即時警報系統(EEW)的效能,透過 5/1 東部海域地震、5/12 東部海域地震、5/17 埔里地震等三大案例,探討幾何中心定位法誤差、應變盲區、測站觸發率及預估震度準確度。</p>
157
+ <div class="ppt-info">
158
+ <span>投影片數量:17 張</span>
159
+ <button class="view-btn">
160
+ 進入簡報
161
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
162
+ </button>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="col-6">
168
+ <div class="glass-panel card-padding ppt-grid-card" onclick="jumpToPresentation('ppt3')">
169
+ <span class="ppt-tag">AI 代理與大模型</span>
170
+ <h3 class="ppt-title">AI 代理群與大型語言模型在次世代地震測報之整合與應用</h3>
171
+ <p class="text-content" style="font-size: 13px; min-height: 60px;">重磅展示 CWA 最先進的科學研究:將語音處理領域 (Wav2Vec 2.0) 遷移至地震波形分析的自監督模型 SeisWav2Vec 2.0 (大型地震模型 LEM),以及由四大 Agent(預警總管、整合工程師、地震學助教、科學研究員)組成的自動化協作群組架構。</p>
172
+ <div class="ppt-info">
173
+ <span>投影片數量:71 張</span>
174
+ <button class="view-btn">
175
+ 進入簡報
176
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
177
+ </button>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <div class="col-6">
183
+ <div class="glass-panel card-padding ppt-grid-card" onclick="jumpToPresentation('ppt4')">
184
+ <span class="ppt-tag">預警演進與宣導</span>
185
+ <h3 class="ppt-title">臺灣地震預警系統的演進與發展 (嘉義災防宣導)</h3>
186
+ <p class="text-content" style="font-size: 13px; min-height: 60px;">回顧台���強震即時警報的發展歷程與原理,說明 P 波(傳播快、振幅小)與 S 波(傳播慢、震幅大)在預警上的幾何學,展示災防預警發布條件、推播管道(PWS、電視、中小學軟體)及花蓮 M7.2 地震案例。</p>
187
+ <div class="ppt-info">
188
+ <span>投影片數量:50 張</span>
189
+ <button class="view-btn">
190
+ 進入簡報
191
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
192
+ </button>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ </div>
198
+ </section>
199
+
200
+ <!-- ================= VIEWER TAB ================= -->
201
+ <section id="tab-viewer" class="tab-section">
202
+ <div class="slide-layout-wrapper">
203
+
204
+ <!-- Left Thumbnails Sidebar Pane -->
205
+ <div class="glass-panel slide-selector-pane">
206
+ <div class="slide-selector-header">
207
+ <select id="ppt-select" class="ppt-selector-dropdown">
208
+ <option value="ppt1">PPT 1: CWA Real-Time Infrastructure</option>
209
+ <option value="ppt2">PPT 2: 2026.05 EEW Report</option>
210
+ <option value="ppt3">PPT 3: AI Swarm & LEM Integration</option>
211
+ <option value="ppt4">PPT 4: Taiwan EEW Evolution</option>
212
+ </select>
213
+ </div>
214
+ <div id="slide-thumbnails" class="slide-thumbnails-list">
215
+ <!-- Dynamic thumbnails will load here -->
216
+ </div>
217
+ </div>
218
+
219
+ <!-- Right Core Slide Viewer Pane -->
220
+ <div class="slide-viewer-pane">
221
+
222
+ <div class="slide-display-card">
223
+ <img id="main-slide-img" src="" alt="Slide View">
224
+ <button class="slide-fullscreen-btn" id="btn-fullscreen" title="全螢幕展示">
225
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
226
+ <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
227
+ </svg>
228
+ </button>
229
+ </div>
230
+
231
+ <div class="slide-viewer-controls">
232
+ <button class="nav-btn" id="btn-prev-slide">
233
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="15 18 9 12 15 6"/></svg>
234
+ 上一張
235
+ </button>
236
+ <span id="slide-index-indicator" style="font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: var(--accent-cyan);">Slide 1 / 26</span>
237
+ <button class="nav-btn" id="btn-next-slide">
238
+ 下一張
239
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
240
+ </button>
241
+ </div>
242
+
243
+ <div class="glass-panel slide-info-card">
244
+ <div class="slide-info-text">
245
+ <h5>
246
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><line x1="9" y1="9" x2="15" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="13" y2="17"/></svg>
247
+ 投影片文字內容
248
+ </h5>
249
+ <ul id="slide-text-lines">
250
+ <!-- Text lines will render here dynamically -->
251
+ </ul>
252
+ </div>
253
+ <div class="slide-info-takeaway">
254
+ <h5>
255
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
256
+ 核心知識要點
257
+ </h5>
258
+ <ul id="slide-takeaway-points">
259
+ <!-- Key notes parsed will render here -->
260
+ </ul>
261
+ </div>
262
+ </div>
263
+
264
+ </div>
265
+
266
+ </div>
267
+ </section>
268
+
269
+ <!-- ================= CALCULATOR TAB ================= -->
270
+ <section id="tab-calculator" class="tab-section">
271
+ <div class="grid-container">
272
+
273
+ <div class="col-4">
274
+ <div class="glass-panel card-padding" style="height: 100%;">
275
+ <h4 class="card-title">物理物理參數控制</h4>
276
+
277
+ <div class="input-group">
278
+ <div class="input-label">
279
+ <span>震源深度 (Depth)</span>
280
+ <span id="depth-val" class="value-display">15 km</span>
281
+ </div>
282
+ <input type="range" min="5" max="100" value="15" id="slider-depth" class="input-slider">
283
+ </div>
284
+
285
+ <div class="input-group">
286
+ <div class="input-label">
287
+ <span>震央距離 (Epicenter Distance)</span>
288
+ <span id="dist-val" class="value-display">40 km</span>
289
+ </div>
290
+ <input type="range" min="10" max="150" value="40" id="slider-dist" class="input-slider">
291
+ </div>
292
+
293
+ <div class="input-group">
294
+ <div class="input-label">
295
+ <span>S波平均波速 (S-Wave Velocity)</span>
296
+ <span id="vs-val" class="value-display">3.5 km/s</span>
297
+ </div>
298
+ <input type="range" min="2.5" max="4.5" step="0.1" value="3.5" id="slider-vs" class="input-slider">
299
+ </div>
300
+
301
+ <div class="input-group">
302
+ <div class="input-label">
303
+ <span>預警解算時效 (Processing Time)</span>
304
+ <span id="process-val" class="value-display">10.0 s</span>
305
+ </div>
306
+ <input type="range" min="5.0" max="25.0" step="0.5" value="10.0" id="slider-process" class="input-slider">
307
+ </div>
308
+
309
+ <div class="calculator-summary-box">
310
+ <p class="input-label" style="margin-bottom: 4px;">預估警報黃金時間 (Lead Time)</p>
311
+ <div id="calc-lead-time" class="lead-time-large-display">1.4<span>秒</span></div>
312
+ <div id="calc-warning-status" class="warning-status-alert status-safe">警報覆蓋安全區</div>
313
+
314
+ <div style="margin-top: 16px; font-size: 12px; color: var(--text-dark); line-height: 1.5;">
315
+ <p><strong>盲區半徑 (Blind Zone Radius):</strong> <span id="blind-radius-txt" style="color: var(--text-muted); font-weight: bold;">35.0 km</span></p>
316
+ <p><strong>震源距離 (Hypocenter Distance):</strong> <span id="hypo-dist-txt" style="color: var(--text-muted); font-weight: bold;">42.7 km</span></p>
317
+ </div>
318
+ </div>
319
+ </div>
320
+ </div>
321
+
322
+ <div class="col-8">
323
+ <div class="glass-panel card-padding">
324
+ <div class="card-title">
325
+ <span>地震預警波前傳播幾何示意圖 (下半球立體與剖面)</span>
326
+ <span style="font-size: 11px; font-family: var(--font-mono); color: var(--text-dark);">動態向量演算視窗</span>
327
+ </div>
328
+ <div class="blind-zone-canvas-wrapper">
329
+ <canvas id="blind-zone-canvas"></canvas>
330
+ </div>
331
+ <div class="text-content" style="font-size: 12px; margin-top: 14px;">
332
+ <p><strong>學術原理說明:</strong> 預警系統解算地震參數需要一定時間(簡稱「預警解算時效」)。在預警解算完畢前,S波(破壞性切變波)已經從震源向外傳播了一定半徑,這塊無法提早收到警報的圓形區域即為<strong>應變盲區</strong>。盲區半徑等於 <em>解算時間 × S波波速</em>。若測站或城市位於盲區外(震央距大於盲區半徑),將獲得寶貴的警報黃金前置時間。</p>
333
+ </div>
334
+ </div>
335
+ </div>
336
+
337
+ </div>
338
+ </section>
339
+
340
+ <!-- ================= SWARM TAB ================= -->
341
+ <section id="tab-swarm" class="tab-section">
342
+ <div class="grid-container">
343
+
344
+ <div class="col-8">
345
+ <div class="glass-panel card-padding">
346
+ <div class="card-title">
347
+ <span>EEW 多代理群組 (Swarm) 協作網絡圖</span>
348
+ <span style="font-size: 11px; font-family: var(--font-mono); color: var(--accent-emerald);">互動點選可查看代理職責</span>
349
+ </div>
350
+ <div class="agent-swarm-wrapper">
351
+ <!-- Canvas for agent graph -->
352
+ <canvas id="agent-swarm-canvas"></canvas>
353
+
354
+ <!-- Floating Overlay panel for selected agent -->
355
+ <div id="agent-details-overlay" class="agent-card-overlay">
356
+ <div id="agent-overlay-icon" class="agent-icon-box" style="background: rgba(16, 185, 129, 0.2); color: var(--accent-emerald);">
357
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
358
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
359
+ </svg>
360
+ </div>
361
+ <div class="agent-details">
362
+ <h4>
363
+ <span id="agent-overlay-name">my_agent</span>
364
+ <span id="agent-overlay-role" class="agent-role-badge" style="background: var(--accent-emerald-glow); color: var(--accent-emerald);">預警總管</span>
365
+ </h4>
366
+ <p id="agent-overlay-desc">維護監控 Cron、處理異常推播(Telegram)與撰寫每日 EEW 系統運作日報總結。協調 seismo 與 secondary_agent。</p>
367
+ </div>
368
+ </div>
369
+ </div>
370
+ </div>
371
+ </div>
372
+
373
+ <div class="col-4">
374
+ <div class="glass-panel card-padding" style="height: 100%;">
375
+ <h4 class="card-title">EEW 自動化閉環工作流</h4>
376
+ <div class="text-content" style="font-size: 13.5px;">
377
+ <p style="margin-bottom: 14px;">在 PPT 3 報告(第 48-66 頁)中,展示了 CWA 引進的 AI Agent 多代理協作系統。這是一套整合了地震監控、自動化派遣與科研分析的閉環系統:</p>
378
+
379
+ <div style="display: flex; flex-direction: column; gap: 14px; margin-top: 10px;">
380
+ <div style="display: flex; gap: 12px; align-items: flex-start;">
381
+ <span style="font-family: var(--font-mono); font-size: 11px; font-weight: 700; background: rgba(59, 130, 246, 0.15); color: var(--accent-blue); padding: 2px 6px; border-radius: 4px; margin-top: 2px;">01</span>
382
+ <div>
383
+ <h6 style="color: white; font-weight: 700; font-size: 13px;">事件偵測與觸發</h6>
384
+ <p style="font-size: 12px; color: var(--text-dark);">seismo 偵測 CWA 預警系統目錄下的新 `.rep` (Earthquake Report) 報告產出。</p>
385
+ </div>
386
+ </div>
387
+
388
+ <div style="display: flex; gap: 12px; align-items: flex-start;">
389
+ <span style="font-family: var(--font-mono); font-size: 11px; font-weight: 700; background: rgba(16, 185, 129, 0.15); color: var(--accent-emerald); padding: 2px 6px; border-radius: 4px; margin-top: 2px;">02</span>
390
+ <div>
391
+ <h6 style="color: white; font-weight: 700; font-size: 13px;">跨節點調度派遣</h6>
392
+ <p style="font-size: 12px; color: var(--text-dark);">secondary_agent 透過 conducter 自動化路由派遣訊息,指派科學計算任務。</p>
393
+ </div>
394
+ </div>
395
+
396
+ <div style="display: flex; gap: 12px; align-items: flex-start;">
397
+ <span style="font-family: var(--font-mono); font-size: 11px; font-weight: 700; background: rgba(245, 158, 11, 0.15); color: var(--accent-amber); padding: 2px 6px; border-radius: 4px; margin-top: 2px;">03</span>
398
+ <div>
399
+ <h6 style="color: white; font-weight: 700; font-size: 13px;">科研分析與繪圖</h6>
400
+ <p style="font-size: 12px; color: var(--text-dark);">seismo_agent 負責資料轉換成 CSV/JSON,分析時效與延遲,並輸出統計圖表。</p>
401
+ </div>
402
+ </div>
403
+
404
+ <div style="display: flex; gap: 12px; align-items: flex-start;">
405
+ <span style="font-family: var(--font-mono); font-size: 11px; font-weight: 700; background: rgba(244, 63, 94, 0.15); color: var(--accent-rose); padding: 2px 6px; border-radius: 4px; margin-top: 2px;">04</span>
406
+ <div>
407
+ <h6 style="color: white; font-weight: 700; font-size: 13px;">異常通報與日報發布</h6>
408
+ <p style="font-size: 12px; color: var(--text-dark);">my_agent 整合報告,產出每日摘要,若有異常(如解算時效過慢)則推播 Telegram 告警。</p>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </div>
413
+ </div>
414
+ </div>
415
+
416
+ </div>
417
+ </section>
418
+
419
+ <!-- ================= LEM TAB ================= -->
420
+ <section id="tab-lem" class="tab-section">
421
+ <div class="grid-container">
422
+
423
+ <div class="col-8">
424
+ <div class="glass-panel card-padding">
425
+ <h4 class="card-title">SeisWav2Vec 2.0 大型地震模型架構</h4>
426
+ <div class="text-content" style="font-size: 14px;">
427
+ <p>大型地震模型 (Large Earthquake Model, LEM) 是地震監測的新紀元。在 PPT 3 簡報(第 32-47 頁)中,展示了基於 Wav2Vec 2.0 語音對比學習概念的地震學創新應用。透過特徵編碼器 (CNN)、Transformer、遮罩與離散量化編碼器,系統能從無標記的海量連續波形中提取通用的地震波相特徵,並進行微調。</p>
428
+
429
+ <div style="margin: 24px 0; padding: 20px; background: rgba(8, 12, 20, 0.6); border: 1px dashed var(--card-border); border-radius: 12px;">
430
+ <!-- Mermaid-like CSS representation of model -->
431
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 14px;">
432
+ <div style="background: var(--accent-blue-glow); border: 1px solid var(--accent-blue); padding: 10px 16px; border-radius: 8px; font-family: var(--font-mono); font-size: 12px; width: 80%; text-align: center; color: #93c5fd;">
433
+ 原始地震波形資料輸入 (Seismic Waveform Input)
434
+ </div>
435
+
436
+ <div style="color: var(--text-dark);">▼</div>
437
+
438
+ <div style="background: var(--accent-cyan-glow); border: 1px solid var(--accent-cyan); padding: 10px 16px; border-radius: 8px; font-family: var(--font-mono); font-size: 12px; width: 80%; text-align: center; color: #a5f3fc;">
439
+ 特徵編碼器 Feature Encoder (多層一維 CNN)
440
+ </div>
441
+
442
+ <div style="color: var(--text-dark);">▼</div>
443
+
444
+ <div style="display: flex; justify-content: space-between; width: 80%; gap: 12px;">
445
+ <div style="flex: 1; background: rgba(245, 158, 11, 0.1); border: 1px dashed var(--accent-amber); padding: 10px; border-radius: 8px; font-family: var(--font-mono); font-size: 11px; text-align: center; color: #fde68a;">
446
+ 遮罩預測 (Masking)
447
+ </div>
448
+ <div style="flex: 1; background: rgba(16, 185, 129, 0.1); border: 1px dashed var(--accent-emerald); padding: 10px; border-radius: 8px; font-family: var(--font-mono); font-size: 11px; text-align: center; color: #a7f3d0;">
449
+ 離散向量量化器 (Quantization)
450
+ </div>
451
+ </div>
452
+
453
+ <div style="color: var(--text-dark);">▼</div>
454
+
455
+ <div style="background: rgba(139, 92, 246, 0.15); border: 1px solid #8b5cf6; padding: 10px 16px; border-radius: 8px; font-family: var(--font-mono); font-size: 12px; width: 80%; text-align: center; color: #ddd6fe;">
456
+ 上下文編碼器 Context Encoder (12層 Transformer 全局自注意力)
457
+ </div>
458
+
459
+ <div style="color: var(--text-dark);">▼</div>
460
+
461
+ <div style="background: var(--accent-rose-glow); border: 1px solid var(--accent-rose); padding: 10px 16px; border-radius: 8px; font-family: var(--font-mono); font-size: 12px; width: 80%; text-align: center; color: #fecdd3;">
462
+ 任務解碼器與分類任務微調 (P/S 波相辨識、震央距與規模預估)
463
+ </div>
464
+ </div>
465
+ </div>
466
+ </div>
467
+ </div>
468
+ </div>
469
+
470
+ <div class="col-4">
471
+ <div class="glass-panel card-padding" style="height: 100%;">
472
+ <h4 class="card-title">自監督學習的核心優勢</h4>
473
+ <div class="text-content" style="font-size: 13px;">
474
+ <ul style="list-style: none; display: flex; flex-direction: column; gap: 16px; margin-top: 10px;">
475
+ <li style="position: relative; padding-left: 20px; line-height: 1.5;">
476
+ <span style="position: absolute; left: 0; top: 2px; color: var(--accent-cyan); font-weight: bold;">✔</span>
477
+ <strong style="color: white; display: block; margin-bottom: 4px;">無標註學習 (Self-Supervised)</strong>
478
+ 無需人工標記正確答案,直接利用數據本身的時序特徵進行學習,極大地解決了「深度學習需要海量標註數據」的傳統瓶頸。
479
+ </li>
480
+ <li style="position: relative; padding-left: 20px; line-height: 1.5;">
481
+ <span style="position: absolute; left: 0; top: 2px; color: var(--accent-cyan); font-weight: bold;">✔</span>
482
+ <strong style="color: white; display: block; margin-bottom: 4px;">學習通用表示 (General Representations)</strong>
483
+ CNN 特徵編碼器捕獲局部波形,而 12 層 Transformer 捕捉波形全局上下文,提取出地震特有的資訊 Embedding。
484
+ </li>
485
+ <li style="position: relative; padding-left: 20px; line-height: 1.5;">
486
+ <span style="position: absolute; left: 0; top: 2px; color: var(--accent-cyan); font-weight: bold;">✔</span>
487
+ <strong style="color: white; display: block; margin-bottom: 4px;">高效任務微調 (Task Fine-Tuning)</strong>
488
+ 在預訓練的 Wav2Vec 模型上外接 CNN 解碼器,僅需極少量的標記事件(如 P/S 波相精準到時),即可達到領先(SOTA)的辨識效能。
489
+ </li>
490
+ </ul>
491
+ </div>
492
+ </div>
493
+ </div>
494
+
495
+ </div>
496
+ </section>
497
+
498
+ <!-- ================= SEARCH TAB ================= -->
499
+ <section id="tab-search" class="tab-section">
500
+ <div class="search-container">
501
+ <div class="search-input-box">
502
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
503
+ <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
504
+ </svg>
505
+ <input type="text" id="search-input" placeholder="請輸入欲搜尋的關鍵字(如:Earthworm, Grafana, 盲區, 代理, M7.2)...">
506
+ </div>
507
+
508
+ <div id="search-results" class="search-results-viewport">
509
+ <div class="no-results-view">
510
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
511
+ <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/>
512
+ </svg>
513
+ <p style="font-size: 14px; font-weight: 600;">請輸入關鍵字以檢索四大簡報共 164 頁的文字大綱</p>
514
+ </div>
515
+ </div>
516
+ </div>
517
+ </section>
518
+
519
+ <!-- ================= HIGHLIGHTS TAB ================= -->
520
+ <section id="tab-highlights" class="tab-section">
521
+ <div class="grid-container">
522
+ <div class="col-12">
523
+ <div class="glass-panel card-padding">
524
+ <h4 class="section-title">簡報核心重點與圖表擷取</h4>
525
+ <p class="text-content" style="margin-bottom: 20px;">本單元為您從 4 份簡報中,挑選出最關鍵的圖表與重要原理說明。每個重點皆附有簡報的重點截圖與文字摘要,您可以點選「定位簡報」來查看該投影片的完整脈絡。</p>
526
+
527
+ <!-- Category Filters -->
528
+ <div style="display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap;">
529
+ <button class="nav-btn active-filter filter-btn" data-filter-cat="all">全部精選</button>
530
+ <button class="nav-btn filter-btn" data-filter-cat="network">測報架構 & 監測</button>
531
+ <button class="nav-btn filter-btn" data-filter-cat="eew">預警物理 & 效能</button>
532
+ <button class="nav-btn filter-btn" data-filter-cat="ai">大型地震模型 & 代理群</button>
533
+ <button class="nav-btn filter-btn" data-filter-cat="history">地震史 & 案例</button>
534
+ </div>
535
+ </div>
536
+ </div>
537
+
538
+ <!-- Highlights Grid -->
539
+ <div class="col-12">
540
+ <div id="highlights-container" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 24px;">
541
+ <!-- Cards will render here -->
542
+ </div>
543
+ </div>
544
+ </div>
545
+ </section>
546
+
547
+ <!-- ================= DOWNLOADS TAB ================= -->
548
+ <section id="tab-downloads" class="tab-section">
549
+ <div class="grid-container">
550
+ <div class="col-12">
551
+ <div class="glass-panel card-padding">
552
+ <h4 class="section-title">原始檔案資訊與下載</h4>
553
+ <p class="text-content" style="margin-bottom: 24px;">以下為您在本地電腦中儲存的 4 份簡報,點選即可下載或查閱對應檔案大小與內容說明。這些簡報已重構為網頁展示。</p>
554
+
555
+ <div class="download-card">
556
+ <div class="download-meta">
557
+ <div class="download-icon">
558
+ <!-- File Icon -->
559
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
560
+ </div>
561
+ <div class="download-details">
562
+ <h5>Real-Time Seismic Data Processing and Monitoring at CWA Current Status and Future Directions.pptx</h5>
563
+ <p>檔案大小:33.3 MB · 分類:地震觀測網與開源監測架構(JPGU學術報告)</p>
564
+ </div>
565
+ </div>
566
+ </div>
567
+
568
+ <div class="download-card">
569
+ <div class="download-meta">
570
+ <div class="download-icon">
571
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
572
+ </div>
573
+ <div class="download-details">
574
+ <h5>202605_地震預警系統報告.pptx</h5>
575
+ <p>檔案大小:20.0 MB · 分類:地震預警中心 2026年5月 效能與案例統計月報</p>
576
+ </div>
577
+ </div>
578
+ </div>
579
+
580
+ <div class="download-card">
581
+ <div class="download-meta">
582
+ <div class="download-icon">
583
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
584
+ </div>
585
+ <div class="download-details">
586
+ <h5>2026_0603_AI 代理群與大型語言模型在次世代地震測報之整合與應用.pptx</h5>
587
+ <p>檔案大小:103.4 MB · 分類:AI Agent 與自監督預訓練大型地震模型(台北市立大學演講)</p>
588
+ </div>
589
+ </div>
590
+ </div>
591
+
592
+ <div class="download-card">
593
+ <div class="download-meta">
594
+ <div class="download-icon">
595
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
596
+ </div>
597
+ <div class="download-details">
598
+ <h5>2026_0612_嘉義災防宣導-臺灣地震預警系統的演進與發展.pptx</h5>
599
+ <p>檔案大小:150.4 MB · 分類:地震預警宣導、傳播管道、盲區概念與花蓮地震專題報告</p>
600
+ </div>
601
+ </div>
602
+ </div>
603
+
604
+ </div>
605
+ </div>
606
+ </div>
607
+ </section>
608
+
609
+ </div>
610
+
611
+ </main>
612
+
613
+ </div>
614
+
615
+ <!-- lightbox modal for full screen view -->
616
+ <div id="lightroom" class="lightroom-modal">
617
+ <button class="lightroom-close-btn" id="btn-close-lightroom">
618
+ <!-- Close Icon -->
619
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
620
+ </button>
621
+ <img id="lightroom-img" src="" alt="Zoomed Slide">
622
+ </div>
623
+
624
+ <!-- Load Slides Data -->
625
+ <script src="slides_data.js"></script>
626
+ <!-- Load App Logic -->
627
+ <script src="index.js"></script>
628
+ </body>
629
+ </html>
index.js ADDED
@@ -0,0 +1,908 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Application Entry State
2
+ let currentPptId = "ppt1";
3
+ let currentSlideIndex = 0; // 0-indexed index inside the selected PPT's slides list
4
+ let searchIndex = [];
5
+
6
+ // DOM Elements
7
+ const menuItems = document.querySelectorAll(".menu-item");
8
+ const tabSections = document.querySelectorAll(".tab-section");
9
+ const tabTitleEl = document.getElementById("current-tab-title");
10
+ const tabSubtitleEl = document.getElementById("current-tab-subtitle");
11
+
12
+ const pptSelectEl = document.getElementById("ppt-select");
13
+ const slideThumbnailsEl = document.getElementById("slide-thumbnails");
14
+ const mainSlideImgEl = document.getElementById("main-slide-img");
15
+ const slideIndexIndicatorEl = document.getElementById("slide-index-indicator");
16
+ const btnPrevSlide = document.getElementById("btn-prev-slide");
17
+ const btnNextSlide = document.getElementById("btn-next-slide");
18
+ const slideTextLinesEl = document.getElementById("slide-text-lines");
19
+ const slideTakeawayEl = document.getElementById("slide-takeaway-points");
20
+ const btnFullscreen = document.getElementById("btn-fullscreen");
21
+
22
+ const lightroomEl = document.getElementById("lightroom");
23
+ const lightroomImgEl = document.getElementById("lightroom-img");
24
+ const btnCloseLightroom = document.getElementById("btn-close-lightroom");
25
+
26
+ // Calculator inputs
27
+ const sliderDepth = document.getElementById("slider-depth");
28
+ const sliderDist = document.getElementById("slider-dist");
29
+ const sliderVs = document.getElementById("slider-vs");
30
+ const sliderProcess = document.getElementById("slider-process");
31
+
32
+ const valDepth = document.getElementById("depth-val");
33
+ const valDist = document.getElementById("dist-val");
34
+ const valVs = document.getElementById("vs-val");
35
+ const valProcess = document.getElementById("process-val");
36
+
37
+ const calcLeadTimeEl = document.getElementById("calc-lead-time");
38
+ const calcWarningStatusEl = document.getElementById("calc-warning-status");
39
+ const txtBlindRadius = document.getElementById("blind-radius-txt");
40
+ const txtHypoDist = document.getElementById("hypo-dist-txt");
41
+ const blindZoneCanvas = document.getElementById("blind-zone-canvas");
42
+
43
+ // Search inputs
44
+ const searchInput = document.getElementById("search-input");
45
+ const searchResults = document.getElementById("search-results");
46
+
47
+ // Tab configuration
48
+ const tabMeta = {
49
+ overview: { title: "知識庫總覽", subtitle: "展示台灣中央氣象署強震即時警報與 AI 技術整合之演進" },
50
+ highlights: { title: "簡報精選精華", subtitle: "彙整四大簡報最關鍵的核心圖表與重要原理,支援一鍵定位簡報脈絡" },
51
+ viewer: { title: "投影片展示", subtitle: "逐頁瀏覽 4 份核心簡報精華、提取文字與高畫質圖片" },
52
+ calculator: { title: "預警盲區模擬", subtitle: "互動式物理參數計算,深入理解應變盲區與警報前置時間的幾何關係" },
53
+ swarm: { title: "AI 代理協作", subtitle: "解析 2026 年 CWA 強震即時警報系統中多代理群組自動化閉環工作流" },
54
+ lem: { title: "大型地震模型 (LEM)", subtitle: "介紹以 Wav2Vec 2.0 為基座演化的次世代地震自監督深度學習模型 SeisWav2Vec" },
55
+ search: { title: "全文大綱搜尋", subtitle: "高效檢索四大簡報共 164 頁的文字內容,快速定位知識點" },
56
+ downloads: { title: "簡報下載專區", subtitle: "查閱本地原始 PPTX 簡報大小與詳細檔案路徑資訊" }
57
+ };
58
+
59
+ // Initialize Application
60
+ window.addEventListener("DOMContentLoaded", () => {
61
+ initNavigation();
62
+ initSlideViewer();
63
+ initCalculator();
64
+ initSwarmVisualizer();
65
+ initSearchEngine();
66
+ initLightroom();
67
+ initHighlights();
68
+ });
69
+
70
+ // 1. Navigation Controller
71
+ function initNavigation() {
72
+ menuItems.forEach(item => {
73
+ item.addEventListener("click", () => {
74
+ const tabId = item.getAttribute("data-tab");
75
+
76
+ // Update sidebar state
77
+ menuItems.forEach(mi => mi.classList.remove("active"));
78
+ item.classList.add("active");
79
+
80
+ // Update viewport state
81
+ tabSections.forEach(section => {
82
+ section.classList.remove("active");
83
+ if (section.id === `tab-${tabId}`) {
84
+ section.classList.add("active");
85
+ }
86
+ });
87
+
88
+ // Update header titles
89
+ const meta = tabMeta[tabId] || { title: "展示平台", subtitle: "" };
90
+ tabTitleEl.textContent = meta.title;
91
+ tabSubtitleEl.textContent = meta.subtitle;
92
+
93
+ // Special canvas re-draw on tab visibility
94
+ if (tabId === "calculator") {
95
+ setTimeout(drawBlindZone, 100);
96
+ } else if (tabId === "swarm") {
97
+ setTimeout(drawSwarm, 100);
98
+ }
99
+ });
100
+ });
101
+ }
102
+
103
+ function jumpToTab(tabId) {
104
+ const item = document.querySelector(`.menu-item[data-tab="${tabId}"]`);
105
+ if (item) item.click();
106
+ }
107
+
108
+ function jumpToPresentation(pptId, slideNum = 1) {
109
+ currentPptId = pptId;
110
+ pptSelectEl.value = pptId;
111
+ currentSlideIndex = slideNum - 1;
112
+ renderSlidesList();
113
+ jumpToTab("viewer");
114
+ }
115
+
116
+ // 2. Slide Showcase Controller
117
+ function initSlideViewer() {
118
+ pptSelectEl.addEventListener("change", (e) => {
119
+ currentPptId = e.target.value;
120
+ currentSlideIndex = 0;
121
+ renderSlidesList();
122
+ });
123
+
124
+ btnPrevSlide.addEventListener("click", () => {
125
+ if (currentSlideIndex > 0) {
126
+ currentSlideIndex--;
127
+ updateActiveSlide();
128
+ }
129
+ });
130
+
131
+ btnNextSlide.addEventListener("click", () => {
132
+ const pptData = slidesData.find(p => p.id === currentPptId);
133
+ if (pptData && currentSlideIndex < pptData.slides.length - 1) {
134
+ currentSlideIndex++;
135
+ updateActiveSlide();
136
+ }
137
+ });
138
+
139
+ renderSlidesList();
140
+ }
141
+
142
+ function renderSlidesList() {
143
+ const pptData = slidesData.find(p => p.id === currentPptId);
144
+ if (!pptData) return;
145
+
146
+ slideThumbnailsEl.innerHTML = "";
147
+ pptData.slides.forEach((slide, idx) => {
148
+ const thumb = document.createElement("div");
149
+ thumb.className = `thumb-item ${idx === currentSlideIndex ? 'active' : ''}`;
150
+ thumb.setAttribute("data-index", idx);
151
+
152
+ // Find slide title or summary
153
+ let titleStr = slide.title || (slide.text && slide.text[0]) || `Slide ${slide.slideNumber}`;
154
+ if (titleStr.length > 22) {
155
+ titleStr = titleStr.substring(0, 20) + "...";
156
+ }
157
+
158
+ thumb.innerHTML = `
159
+ <span class="thumb-num">Slide ${slide.slideNumber}</span>
160
+ <span class="thumb-title" title="${slide.title || ''}">${titleStr}</span>
161
+ `;
162
+
163
+ thumb.addEventListener("click", () => {
164
+ currentSlideIndex = idx;
165
+ updateActiveSlide();
166
+ });
167
+
168
+ slideThumbnailsEl.appendChild(thumb);
169
+ });
170
+
171
+ updateActiveSlide();
172
+ }
173
+
174
+ function updateActiveSlide() {
175
+ const pptData = slidesData.find(p => p.id === currentPptId);
176
+ if (!pptData) return;
177
+
178
+ const slide = pptData.slides[currentSlideIndex];
179
+ if (!slide) return;
180
+
181
+ // Update main display image
182
+ mainSlideImgEl.src = slide.image;
183
+
184
+ // Highlight active thumbnail and scroll into view smoothly
185
+ const thumbs = slideThumbnailsEl.querySelectorAll(".thumb-item");
186
+ thumbs.forEach((t, idx) => {
187
+ t.classList.remove("active");
188
+ if (idx === currentSlideIndex) {
189
+ t.classList.add("active");
190
+ t.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
191
+ }
192
+ });
193
+
194
+ // Update indicators
195
+ slideIndexIndicatorEl.textContent = `Slide ${slide.slideNumber} / ${pptData.slidesCount}`;
196
+
197
+ // Update disabled buttons
198
+ btnPrevSlide.disabled = currentSlideIndex === 0;
199
+ btnNextSlide.disabled = currentSlideIndex === pptData.slides.length - 1;
200
+
201
+ // Render slide texts
202
+ slideTextLinesEl.innerHTML = "";
203
+ if (slide.text && slide.text.length > 0) {
204
+ slide.text.forEach(line => {
205
+ const li = document.createElement("li");
206
+ li.textContent = line;
207
+ slideTextLinesEl.appendChild(li);
208
+ });
209
+ } else {
210
+ const li = document.createElement("li");
211
+ li.textContent = "(此頁投影片無主要大綱文字,可能為全圖表展示)";
212
+ li.style.color = "var(--text-dark)";
213
+ li.style.fontStyle = "italic";
214
+ slideTextLinesEl.appendChild(li);
215
+ }
216
+
217
+ // Generate key terms or takeaways dynamically based on slide content
218
+ slideTakeawayEl.innerHTML = "";
219
+ const takeaways = generateTakeaways(slide, pptData.id);
220
+ takeaways.forEach(p => {
221
+ const li = document.createElement("li");
222
+ li.textContent = p;
223
+ slideTakeawayEl.appendChild(li);
224
+ });
225
+ }
226
+
227
+ function generateTakeaways(slide, pptId) {
228
+ const points = [];
229
+ const textJoined = (slide.text || []).join(" ").toLowerCase();
230
+ const title = (slide.title || "").toLowerCase();
231
+
232
+ if (pptId === "ppt1") {
233
+ if (textJoined.includes("earthworm")) points.push("Earthworm:USGS 開源地震處理模組化系統,為全球實時監控基準。");
234
+ if (textJoined.includes("seiscomp")) points.push("SeisComP:先進地震圖形界面與定位波相拾取平台。");
235
+ if (textJoined.includes("grafana")) points.push("Grafana 觀測:即時視覺化海底觀測網水壓計、波形延遲及預警推播狀態。");
236
+ if (textJoined.includes("ebear")) points.push("eBEAR:CWA 自行開發的 Earthworm Alert Reporting 自動速報系統。");
237
+ if (textJoined.includes("docker")) points.push("容器化部署:CWA EEW 與 Seiscomp 已打包為 Docker 映像檔上傳 Docker Hub。");
238
+ } else if (pptId === "ppt2") {
239
+ if (textJoined.includes("pws")) points.push("PWS:災防告警細胞廣播發布範疇統計(如 M6.1 地震第一報與第二報範圍)。");
240
+ if (textJoined.includes("幾何中心")) points.push("定位法:幾何中心法在早期預警解算中對震央與震度有誤差修正之探討。");
241
+ if (textJoined.includes("盲區") || textJoined.includes("應變")) points.push("應變盲區:最快解算耗時乘以 S 波平均波速(3.5 km/s),該範圍內無警報前置時間。");
242
+ if (textJoined.includes("觸發")) points.push("測站觸發率:針對特定震源以半徑搜索,排除共站干擾計算實時觸發比例。");
243
+ } else if (pptId === "ppt3") {
244
+ if (textJoined.includes("agent") || textJoined.includes("代理")) points.push("AI Agent Swarm:my_agent, seismo, seismo_agent, secondary_agent 四協同代理職責分工。");
245
+ if (textJoined.includes("wav2vec") || textJoined.includes("lem")) points.push("SeisWav2Vec 2.0:對比學習 Wav2Vec 結構遷移至地震波形,自監督預訓練模型。");
246
+ if (textJoined.includes("transformer")) points.push("Transformer 上下文:12 層注意力機制學習特徵的全局上下文,無標記資料自學習。");
247
+ if (textJoined.includes("telegram")) points.push("Telegram 通訊:整合 my_agent 自動推送 eew_health、異常日誌及 daily_report。");
248
+ } else if (pptId === "ppt4") {
249
+ if (textJoined.includes("p波") || textJoined.includes("s波")) points.push("強震預警原理:利用傳播快速的 P 縱波推算震級,趕在破壞性 S 橫波抵達前告警。");
250
+ if (textJoined.includes("電視") || textJoined.includes("推播")) points.push("多元傳播管道:國家級警報、電視台即時插播、中小學預警軟體及手機 APP 四大網絡。");
251
+ if (textJoined.includes("花蓮")) points.push("花蓮 M7.2 案例:回顧強震預警警報實際發送實例、各縣市震度與警報時效。");
252
+ }
253
+
254
+ if (points.length === 0) {
255
+ points.push("地震預警核心知識:解析地震波傳播時序與即時資料串流。");
256
+ points.push("技術重點:結合自動化解算模型,縮減預警盲區以求減災。");
257
+ }
258
+
259
+ return points;
260
+ }
261
+
262
+ // 3. Lightroom Lightbox Modal
263
+ function initLightroom() {
264
+ mainSlideImgEl.addEventListener("click", () => {
265
+ lightroomImgEl.src = mainSlideImgEl.src;
266
+ lightroomEl.style.display = "flex";
267
+ });
268
+
269
+ lightroomEl.addEventListener("click", () => {
270
+ lightroomEl.style.display = "none";
271
+ });
272
+
273
+ btnCloseLightroom.addEventListener("click", (e) => {
274
+ e.stopPropagation();
275
+ lightroomEl.style.display = "none";
276
+ });
277
+ }
278
+
279
+ // 4. Blind Zone & Lead Time Warning Simulator
280
+ function initCalculator() {
281
+ const updateCalculator = () => {
282
+ const depth = parseFloat(sliderDepth.value);
283
+ const dist = parseFloat(sliderDist.value);
284
+ const vs = parseFloat(sliderVs.value);
285
+ const processTime = parseFloat(sliderProcess.value);
286
+
287
+ // Display value update
288
+ valDepth.textContent = `${depth} km`;
289
+ valDist.textContent = `${dist} km`;
290
+ valVs.textContent = `${vs.toFixed(1)} km/s`;
291
+ valProcess.textContent = `${processTime.toFixed(1)} s`;
292
+
293
+ // Geometry math
294
+ const hypoDist = Math.sqrt(dist*dist + depth*depth);
295
+ const sTravelTime = hypoDist / vs;
296
+ const leadTime = sTravelTime - processTime;
297
+
298
+ // Blind zone radius (ground distance when S-wave wavefront intersects ground surface at the processing time)
299
+ // S-wave path distance at processing time: s_path = processTime * vs
300
+ // If path is less than focal depth, S-wave hasn't even reached the ground surface directly above epicenter.
301
+ let blindRadius = 0;
302
+ const sPath = processTime * vs;
303
+ if (sPath > depth) {
304
+ blindRadius = Math.sqrt(sPath*sPath - depth*depth);
305
+ }
306
+
307
+ // Render text
308
+ txtHypoDist.textContent = `${hypoDist.toFixed(1)} km`;
309
+ txtBlindRadius.textContent = blindRadius > 0 ? `${blindRadius.toFixed(1)} km` : "0 km (未傳抵地表)";
310
+
311
+ if (leadTime > 0) {
312
+ calcLeadTimeEl.innerHTML = `${leadTime.toFixed(1)}<span>秒</span>`;
313
+ calcLeadTimeEl.style.color = "var(--accent-cyan)";
314
+ calcWarningStatusEl.textContent = "警報覆蓋安全區";
315
+ calcWarningStatusEl.className = "warning-status-alert status-safe";
316
+ } else {
317
+ calcLeadTimeEl.innerHTML = `0.0<span>秒</span>`;
318
+ calcLeadTimeEl.style.color = "var(--accent-rose)";
319
+ calcWarningStatusEl.textContent = "處於預警盲區內";
320
+ calcWarningStatusEl.className = "warning-status-alert status-blind";
321
+ }
322
+
323
+ // Save to window state for canvas redraw
324
+ window.calcState = { depth, dist, vs, processTime, hypoDist, blindRadius, leadTime };
325
+ drawBlindZone();
326
+ };
327
+
328
+ sliderDepth.addEventListener("input", updateCalculator);
329
+ sliderDist.addEventListener("input", updateCalculator);
330
+ sliderVs.addEventListener("input", updateCalculator);
331
+ sliderProcess.addEventListener("input", updateCalculator);
332
+
333
+ updateCalculator();
334
+ }
335
+
336
+ function drawBlindZone() {
337
+ const canvas = blindZoneCanvas;
338
+ if (!canvas) return;
339
+ const ctx = canvas.getContext("2d");
340
+
341
+ // Set resolution based on DPI
342
+ const dpr = window.devicePixelRatio || 1;
343
+ const rect = canvas.getBoundingClientRect();
344
+ canvas.width = rect.width * dpr;
345
+ canvas.height = rect.height * dpr;
346
+ ctx.scale(dpr, dpr);
347
+
348
+ const w = rect.width;
349
+ const h = rect.height;
350
+ ctx.clearRect(0, 0, w, h);
351
+
352
+ const state = window.calcState;
353
+ if (!state) return;
354
+
355
+ // Drawing coordinates mapping
356
+ // Earth surface is a horizontal line at y = 100
357
+ const ySurface = 80;
358
+ // Center is epicenter at x = w / 2 - 100
359
+ const xEpicenter = w / 2 - 80;
360
+
361
+ // Scale pixels: 1 km = 2 pixels
362
+ const scale = 2.0;
363
+
364
+ // Draw Earth crust layer
365
+ ctx.fillStyle = "rgba(10, 15, 26, 0.4)";
366
+ ctx.fillRect(0, ySurface, w, h - ySurface);
367
+
368
+ // Draw ground line
369
+ ctx.strokeStyle = "#475569";
370
+ ctx.lineWidth = 2;
371
+ ctx.beginPath();
372
+ ctx.moveTo(0, ySurface);
373
+ ctx.lineTo(w, ySurface);
374
+ ctx.stroke();
375
+
376
+ // Epicenter
377
+ ctx.fillStyle = "#94a3b8";
378
+ ctx.beginPath();
379
+ ctx.arc(xEpicenter, ySurface, 4, 0, 2*Math.PI);
380
+ ctx.fill();
381
+
382
+ // Hypocenter (Earthquake Focus)
383
+ const yFocus = ySurface + state.depth * scale;
384
+ ctx.fillStyle = "var(--accent-rose)";
385
+ ctx.beginPath();
386
+ ctx.arc(xEpicenter, yFocus, 6, 0, 2*Math.PI);
387
+ ctx.fill();
388
+
389
+ // Glow for Hypocenter
390
+ const gradientFocus = ctx.createRadialGradient(xEpicenter, yFocus, 2, xEpicenter, yFocus, 18);
391
+ gradientFocus.addColorStop(0, "rgba(244, 63, 94, 0.6)");
392
+ gradientFocus.addColorStop(1, "rgba(244, 63, 94, 0)");
393
+ ctx.fillStyle = gradientFocus;
394
+ ctx.beginPath();
395
+ ctx.arc(xEpicenter, yFocus, 18, 0, 2*Math.PI);
396
+ ctx.fill();
397
+
398
+ // Target Station (user epicenter distance)
399
+ const xStation = xEpicenter + state.dist * scale;
400
+ ctx.fillStyle = state.leadTime > 0 ? "var(--accent-cyan)" : "var(--accent-rose)";
401
+ ctx.beginPath();
402
+ ctx.arc(xStation, ySurface, 5, 0, 2*Math.PI);
403
+ ctx.fill();
404
+
405
+ // Label for target
406
+ ctx.fillStyle = "white";
407
+ ctx.font = "bold 11px sans-serif";
408
+ ctx.fillText("目標觀測點", xStation - 25, ySurface - 12);
409
+
410
+ // Wavefront propagation circle
411
+ // S-wave wavefront path distance
412
+ const rSwave = state.processTime * state.vs * scale;
413
+ ctx.strokeStyle = "rgba(245, 158, 11, 0.4)";
414
+ ctx.lineWidth = 1.5;
415
+ ctx.setLineDash([4, 3]);
416
+ ctx.beginPath();
417
+ ctx.arc(xEpicenter, yFocus, rSwave, 0, 2*Math.PI);
418
+ ctx.stroke();
419
+ ctx.setLineDash([]);
420
+
421
+ // P-wave wavefront path (approx 1.73x faster than S-wave)
422
+ const rPwave = state.processTime * state.vs * 1.73 * scale;
423
+ ctx.strokeStyle = "rgba(6, 182, 212, 0.25)";
424
+ ctx.lineWidth = 1;
425
+ ctx.beginPath();
426
+ ctx.arc(xEpicenter, yFocus, rPwave, 0, 2*Math.PI);
427
+ ctx.stroke();
428
+
429
+ // Shaded Blind Zone on surface
430
+ const rBlindPx = state.blindRadius * scale;
431
+ if (rBlindPx > 0) {
432
+ ctx.fillStyle = "rgba(244, 63, 94, 0.15)";
433
+ ctx.fillRect(xEpicenter - rBlindPx, ySurface, rBlindPx * 2, h - ySurface);
434
+
435
+ // Draw surface indicator for blind zone
436
+ ctx.strokeStyle = "var(--accent-rose)";
437
+ ctx.lineWidth = 3;
438
+ ctx.beginPath();
439
+ ctx.moveTo(xEpicenter - rBlindPx, ySurface);
440
+ ctx.lineTo(xEpicenter + rBlindPx, ySurface);
441
+ ctx.stroke();
442
+
443
+ // Blind Zone Label
444
+ ctx.fillStyle = "var(--accent-rose)";
445
+ ctx.font = "bold 11px sans-serif";
446
+ ctx.fillText(`預警盲區半徑: ${state.blindRadius.toFixed(1)} km`, xEpicenter - 65, ySurface + 24);
447
+ }
448
+
449
+ // S-wave wavefront intersection with ground
450
+ ctx.fillStyle = "var(--accent-amber)";
451
+ const intersectDist = state.processTime * state.vs;
452
+ if (intersectDist > state.depth) {
453
+ const xIntersectLeft = xEpicenter - rBlindPx;
454
+ const xIntersectRight = xEpicenter + rBlindPx;
455
+ ctx.beginPath();
456
+ ctx.arc(xIntersectLeft, ySurface, 4, 0, 2*Math.PI);
457
+ ctx.arc(xIntersectRight, ySurface, 4, 0, 2*Math.PI);
458
+ ctx.fill();
459
+ }
460
+
461
+ // Draw ray from Hypocenter to target station
462
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.15)";
463
+ ctx.lineWidth = 1;
464
+ ctx.beginPath();
465
+ ctx.moveTo(xEpicenter, yFocus);
466
+ ctx.lineTo(xStation, ySurface);
467
+ ctx.stroke();
468
+
469
+ // Annotations
470
+ ctx.fillStyle = "var(--text-dark)";
471
+ ctx.font = "10px sans-serif";
472
+ ctx.fillText("震央 (Epicenter)", xEpicenter + 8, ySurface - 12);
473
+
474
+ ctx.fillStyle = "var(--accent-rose)";
475
+ ctx.fillText(`震源 (Hypocenter) Depth: ${state.depth} km`, xEpicenter + 12, yFocus + 4);
476
+ }
477
+
478
+ // 5. AI Agent Swarm network visualizer
479
+ function initSwarmVisualizer() {
480
+ const swarmCanvas = document.getElementById("agent-swarm-canvas");
481
+ if (!swarmCanvas) return;
482
+
483
+ // Node details
484
+ const agentNodes = [
485
+ { id: "my_agent", name: "my_agent", role: "預警總管", color: "var(--accent-emerald)", bg: "var(--accent-emerald-glow)", x: 200, y: 120, r: 48, desc: "主控監測流程,定時健康檢查 (eew_health cron)。判讀並處理異常事件,異常時透過 Telegram 主動發送告警,並彙整所有 agent 資訊產出每日簡報日報 (eew_report_daily)。" },
486
+ { id: "seismo", name: "seismo", role: "數據前向轉發", color: "var(--accent-blue)", bg: "var(--accent-blue-glow)", x: 450, y: 120, r: 42, desc: "維持持續運行的心跳循環 (heartbeat loop),即時偵測及提取中央氣象署 EEW 流程產生的最新地震 `.rep` 報告原始檔,將資料進行結構化轉換輸出 (轉為 JSON/CSV)。" },
487
+ { id: "seismo_agent", name: "seismo_agent", role: "地震學分析助教", color: "var(--accent-amber)", bg: "var(--accent-amber-glow)", x: 450, y: 320, r: 42, desc: "負責科學數據分析與處理流水線。解析速報效能與測站延遲統計,呼叫自動繪圖模組繪製事件散佈圖與序列圖,同時儲存備份二進位原始數據檔。" },
488
+ { id: "secondary_agent", name: "secondary_agent", role: "系統整合工程師", color: "var(--accent-rose)", bg: "var(--accent-rose-glow)", x: 200, y: 320, r: 48, desc: "本地調度與訊息轉發中心。負責部署與維護容器重啟腳本 (eew_fault_recovery.sh)、Cron 排程及 Telegram 機器人核心,負責在 my_agent 要求時調用 delegate_task 指派任務。" }
489
+ ];
490
+
491
+ const links = [
492
+ { from: "seismo", to: "secondary_agent", label: "地震.rep JSON 輸出" },
493
+ { from: "secondary_agent", to: "seismo_agent", label: "分發科研分析與繪圖" },
494
+ { from: "seismo_agent", to: "my_agent", label: "回傳延遲時效日報" },
495
+ { from: "my_agent", to: "secondary_agent", label: "調度容器故障重啟" },
496
+ { from: "seismo", to: "my_agent", label: "心跳狀態報告" }
497
+ ];
498
+
499
+ let selectedNodeId = "my_agent";
500
+
501
+ const updateSwarmOverlay = (node) => {
502
+ document.getElementById("agent-overlay-name").textContent = node.name;
503
+ document.getElementById("agent-overlay-role").textContent = node.role;
504
+ document.getElementById("agent-overlay-role").style.backgroundColor = node.bg;
505
+ document.getElementById("agent-overlay-role").style.color = node.color;
506
+ document.getElementById("agent-overlay-desc").textContent = node.desc;
507
+ document.getElementById("agent-overlay-icon").style.color = node.color;
508
+ document.getElementById("agent-overlay-icon").style.backgroundColor = node.bg;
509
+ };
510
+
511
+ window.drawSwarm = () => {
512
+ const dpr = window.devicePixelRatio || 1;
513
+ const rect = swarmCanvas.getBoundingClientRect();
514
+ swarmCanvas.width = rect.width * dpr;
515
+ swarmCanvas.height = rect.height * dpr;
516
+ const ctx = swarmCanvas.getContext("2d");
517
+ ctx.scale(dpr, dpr);
518
+
519
+ const w = rect.width;
520
+ const h = rect.height;
521
+ ctx.clearRect(0, 0, w, h);
522
+
523
+ // Set node positions dynamically centered to canvas
524
+ const xCenter = w / 2;
525
+ const yCenter = h / 2 - 40;
526
+
527
+ // Re-adjust node coordinates relative to canvas center
528
+ agentNodes[0].x = xCenter - 150; agentNodes[0].y = yCenter - 80;
529
+ agentNodes[1].x = xCenter + 150; agentNodes[1].y = yCenter - 80;
530
+ agentNodes[2].x = xCenter + 150; agentNodes[2].y = yCenter + 120;
531
+ agentNodes[3].x = xCenter - 150; agentNodes[3].y = yCenter + 120;
532
+
533
+ // Draw Links
534
+ links.forEach(l => {
535
+ const fromNode = agentNodes.find(n => n.id === l.from);
536
+ const toNode = agentNodes.find(n => n.id === l.to);
537
+ if (!fromNode || !toNode) return;
538
+
539
+ ctx.strokeStyle = "rgba(148, 163, 184, 0.15)";
540
+ ctx.lineWidth = 2;
541
+ ctx.beginPath();
542
+ ctx.moveTo(fromNode.x, fromNode.y);
543
+ ctx.lineTo(toNode.x, toNode.y);
544
+ ctx.stroke();
545
+
546
+ // Draw dynamic animated pulse dot on links
547
+ const speed = 0.001;
548
+ const t = (Date.now() * speed) % 1;
549
+ const dotX = fromNode.x + (toNode.x - fromNode.x) * t;
550
+ const dotY = fromNode.y + (toNode.y - fromNode.y) * t;
551
+ ctx.fillStyle = "var(--accent-cyan)";
552
+ ctx.beginPath();
553
+ ctx.arc(dotX, dotY, 4, 0, 2*Math.PI);
554
+ ctx.fill();
555
+ });
556
+
557
+ // Draw Nodes
558
+ agentNodes.forEach(node => {
559
+ const isSelected = node.id === selectedNodeId;
560
+
561
+ // Glowing shadow
562
+ if (isSelected) {
563
+ ctx.shadowColor = node.color;
564
+ ctx.shadowBlur = 15;
565
+ }
566
+
567
+ // Node fill
568
+ ctx.fillStyle = "rgba(10, 15, 26, 0.9)";
569
+ ctx.strokeStyle = isSelected ? "white" : node.color;
570
+ ctx.lineWidth = isSelected ? 3 : 1.5;
571
+ ctx.beginPath();
572
+ ctx.arc(node.x, node.y, node.r, 0, 2*Math.PI);
573
+ ctx.fill();
574
+ ctx.stroke();
575
+
576
+ // Reset shadow
577
+ ctx.shadowBlur = 0;
578
+
579
+ // Text
580
+ ctx.fillStyle = "white";
581
+ ctx.font = "bold 12px sans-serif";
582
+ ctx.textAlign = "center";
583
+ ctx.fillText(node.name, node.x, node.y - 4);
584
+
585
+ ctx.fillStyle = node.color;
586
+ ctx.font = "10px sans-serif";
587
+ ctx.fillText(node.role, node.x, node.y + 14);
588
+ });
589
+ };
590
+
591
+ // Click handler
592
+ swarmCanvas.addEventListener("click", (e) => {
593
+ const rect = swarmCanvas.getBoundingClientRect();
594
+ const x = e.clientX - rect.left;
595
+ const y = e.clientY - rect.top;
596
+
597
+ // Check if clicked node
598
+ for (let node of agentNodes) {
599
+ const dist = Math.sqrt((x - node.x)**2 + (y - node.y)**2);
600
+ if (dist <= node.r) {
601
+ selectedNodeId = node.id;
602
+ updateSwarmOverlay(node);
603
+ drawSwarm();
604
+ break;
605
+ }
606
+ }
607
+ });
608
+
609
+ // Animation loop for swarm
610
+ let animId;
611
+ const loop = () => {
612
+ if (document.getElementById("tab-swarm").classList.contains("active")) {
613
+ drawSwarm();
614
+ }
615
+ animId = requestAnimationFrame(loop);
616
+ };
617
+ loop();
618
+
619
+ // Select default node
620
+ updateSwarmOverlay(agentNodes[0]);
621
+ }
622
+
623
+ // 6. Search Engine Controller
624
+ function initSearchEngine() {
625
+ // Build slide index
626
+ searchIndex = [];
627
+ slidesData.forEach(ppt => {
628
+ ppt.slides.forEach(slide => {
629
+ searchIndex.push({
630
+ pptId: ppt.id,
631
+ pptTitle: ppt.title,
632
+ slideNumber: slide.slideNumber,
633
+ title: slide.title || "",
634
+ textLines: slide.text || [],
635
+ image: slide.image
636
+ });
637
+ });
638
+ });
639
+
640
+ searchInput.addEventListener("input", (e) => {
641
+ const query = e.target.value.toLowerCase().trim();
642
+ if (!query) {
643
+ searchResults.innerHTML = `
644
+ <div class="no-results-view">
645
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
646
+ <circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/>
647
+ </svg>
648
+ <p style="font-size: 14px; font-weight: 600;">請輸入關鍵字以檢索四大簡報共 164 頁的文字大綱</p>
649
+ </div>
650
+ `;
651
+ return;
652
+ }
653
+
654
+ const matches = searchIndex.filter(item => {
655
+ const matchTitle = item.title.toLowerCase().includes(query);
656
+ const matchPptTitle = item.pptTitle.toLowerCase().includes(query);
657
+ const matchLines = item.textLines.some(line => line.toLowerCase().includes(query));
658
+ return matchTitle || matchPptTitle || matchLines;
659
+ });
660
+
661
+ if (matches.length === 0) {
662
+ searchResults.innerHTML = `
663
+ <div class="no-results-view">
664
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
665
+ <circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
666
+ </svg>
667
+ <p style="font-size: 14px; font-weight: 600;">未找到任何相符的投影片內容</p>
668
+ <p style="font-size: 12px; color: var(--text-dark); margin-top: 4px;">請試著搜尋其他字眼(如:Earthworm, Grafana, 盲區, 代理)</p>
669
+ </div>
670
+ `;
671
+ return;
672
+ }
673
+
674
+ searchResults.innerHTML = "";
675
+ matches.forEach(item => {
676
+ const card = document.createElement("div");
677
+ card.className = "glass-panel card-padding search-result-card";
678
+
679
+ // Highlight matching snippet
680
+ let snippet = "";
681
+ const matchingLine = item.textLines.find(line => line.toLowerCase().includes(query));
682
+ if (matchingLine) {
683
+ snippet = matchingLine;
684
+ } else if (item.textLines.length > 0) {
685
+ snippet = item.textLines[0];
686
+ }
687
+
688
+ // Highlight text snippet helper
689
+ const queryIdx = snippet.toLowerCase().indexOf(query);
690
+ if (queryIdx !== -1 && query.length > 0) {
691
+ const start = snippet.substring(0, queryIdx);
692
+ const match = snippet.substring(queryIdx, queryIdx + query.length);
693
+ const end = snippet.substring(queryIdx + query.length);
694
+ snippet = `${start}<mark style="background-color: rgba(6, 182, 212, 0.4); color: white; border-radius: 2px; padding: 0 2px;">${match}</mark>${end}`;
695
+ }
696
+
697
+ card.innerHTML = `
698
+ <div class="result-image-box">
699
+ <img src="${item.image}" alt="Slide Thumb">
700
+ </div>
701
+ <div class="result-content-box">
702
+ <div class="result-meta">${item.pptTitle} · 第 ${item.slideNumber} 頁</div>
703
+ <div class="result-title">${item.title || '(無標題)'}</div>
704
+ <div class="result-snippet">${snippet}</div>
705
+ </div>
706
+ `;
707
+
708
+ card.addEventListener("click", () => {
709
+ jumpToPresentation(item.pptId, item.slideNumber);
710
+ });
711
+
712
+ searchResults.appendChild(card);
713
+ });
714
+ });
715
+ }
716
+
717
+ // 7. Key Highlights Section Controller
718
+ const highlightsData = [
719
+ {
720
+ pptId: "ppt1",
721
+ slideNum: 6,
722
+ category: "network",
723
+ title: "中央氣象署即時地震觀測網分佈",
724
+ description: "展示臺灣陸地與海域鋪設的極其密集的觀測網。包含強震儀站點與鋪設於東部海域、南部海域的 OBS 海底電纜觀測系統,是提供即時測報之根本基礎。",
725
+ keyPoints: ["島內密集陸地強震儀分佈", "東部與南部海纜觀測系統 (OBS)", "即時連續訊號在發震數秒內傳達中心"]
726
+ },
727
+ {
728
+ pptId: "ppt1",
729
+ slideNum: 11,
730
+ category: "network",
731
+ title: "實時地震波形歸檔整合數據流",
732
+ description: "展示 Earthworm 處理器如何透過共享記憶體 RING,將 Field Stations 的資料利用 tbuf2mseed 模組壓縮,並同步保存至本機 NAS,確保高可靠度與極低資料延遲。",
733
+ keyPoints: ["共享記憶體 (Shared Memory) 機制", "支援跨網絡與跨節點資料交換", "Docker 容器化保證部署彈性"]
734
+ },
735
+ {
736
+ pptId: "ppt1",
737
+ slideNum: 12,
738
+ category: "network",
739
+ title: "eBEAR 自動化強震即時警報軟體",
740
+ description: "CWA 自主開發的 eBEAR (Earthworm Based Earthquake Alert Reporting) 系統,已封裝成 Docker Image 並提供在 Docker Hub 下載,具備模組化警報回報流程。",
741
+ keyPoints: ["Docker Hub: cwadayi/earthworm_ubuntu22.04_eew:v1", "高整合性的地震波相拾取與規模演算", "支援自動化的警報派遣與日誌紀錄"]
742
+ },
743
+ {
744
+ pptId: "ppt2",
745
+ slideNum: 2,
746
+ category: "eew",
747
+ title: "2026年5月 強震即時警報系統 (EEW) 效能",
748
+ description: "彙�� 2026 年 5 月份 EEW 的統計數據。最快解算時效達到驚人的 9.7 秒,PWS 細胞廣播發送 6 次,展示了防減災技術在即時解算上的優異成果。",
749
+ keyPoints: ["5月份地震預警最速時效達 9.7 秒", "PWS 發布 6 次,電視台即時插播 6 次", "對外通報管道觸發穩定,無漏報異常"]
750
+ },
751
+ {
752
+ pptId: "ppt2",
753
+ slideNum: 14,
754
+ category: "eew",
755
+ title: "應變盲區 (Blind Zone) 與 S 波幾何學",
756
+ description: "地震發生後,P 波與 S 波以球形向外傳播。在系統花費時間解算並發送警報前,S 波已波及的範圍(半徑 = 解算時間 × S波速度)即為預警盲區,此區域內前置時間為零。",
757
+ keyPoints: ["盲區半徑 = 解算時間 (s) × S波速度 (3.5km/s)", "極淺地震(如大埔或美濃)的震央周圍多位於盲區內", "加速測站密集度與算法能有效縮小盲區"]
758
+ },
759
+ {
760
+ pptId: "ppt2",
761
+ slideNum: 15,
762
+ category: "eew",
763
+ title: "測站觸發率 (Trigger Rate) 半徑篩選",
764
+ description: "以定位震央為中心、事件最遠觸發測站為半徑作圓,評估該圓形範圍內觀測網測站觸發率,排除共站造成的比率偏低誤差,是評估觀測網效能的關鍵指標。",
765
+ keyPoints: ["速報震央為圓心,最遠觸發測站為半徑作圓", "剔除重複計算共站,反映真實觀測網覆蓋", "輔助評估密集強震時之儀器健康度"]
766
+ },
767
+ {
768
+ pptId: "ppt3",
769
+ slideNum: 4,
770
+ category: "history",
771
+ title: "臺灣過去 10 年災害性地震損失統計",
772
+ description: "展示臺灣過去 10 年內數次引發嚴重災情之強震(包括美濃、花蓮、池上、大埔地震等),凸顯了台灣極高地震風險性與建立次世代預警系統的迫切性。",
773
+ keyPoints: ["美濃地震維冠大樓塌陷、花蓮地震統帥飯店受災", "高鐵與捷運在預警下成功採取減速避災措施", "災害地震平均每 30-40 年發生一次"]
774
+ },
775
+ {
776
+ pptId: "ppt3",
777
+ slideNum: 44,
778
+ category: "ai",
779
+ title: "大型地震模型 LEM 與 SeisWav2Vec 2.0",
780
+ description: "介紹將 Wav2Vec 2.0 語音自監督模型遷移至地震波形特徵提取之架構。由一維 CNN 編碼器與 12 層 Transformer 構成,從無標記波形中自主學習震波語意特徵。",
781
+ keyPoints: ["自監督學習 (SSL) 無需人工標註海量資料", "Wav2Vec 離散向量量化與 Mask 遮罩學習", "微調後於波相辨識精度與噪訊過濾上顯著提升"]
782
+ },
783
+ {
784
+ pptId: "ppt3",
785
+ slideNum: 50,
786
+ category: "ai",
787
+ title: "EEW Swarm 四位 Agent 角色與職責矩陣",
788
+ description: "次世代地震系統中四個協同 Agents 的核心分工矩陣:包含主助理、科學前向轉發工程師、地震分析學助教與本地排程容器整合工程師,建構多代理人體系。",
789
+ keyPoints: ["my_agent、seismo、seismo_agent、secondary_agent", "實現跨系統的多代理協調派遣", "個別 Agent 維持獨立的指令執行環境與日誌監控"]
790
+ },
791
+ {
792
+ pptId: "ppt3",
793
+ slideNum: 54,
794
+ category: "ai",
795
+ title: "EEW 代理群多模組自動化閉環工作流",
796
+ description: "當系統產出新事件的 `.rep` 報告後,自動觸發 seismo 解析資料、seismo_agent 繪製散佈圖、secondary_agent 協調排程、最後由 my_agent 生成簡報推播至 Telegram 的自動化流向圖。",
797
+ keyPoints: ["事件驅動 (Event-driven) 觸發運行", "Telegram API 即時回報與 eew_fault_recovery 恢復功能", "全流程無人工介入,秒級產出簡報與日報"]
798
+ },
799
+ {
800
+ pptId: "ppt4",
801
+ slideNum: 18,
802
+ category: "eew",
803
+ title: "強震即時警報物理原理 (P/S 波速差)",
804
+ description: "圖解地震產生之 P 縱波 (速度快) 與 S 橫波 (破壞大、速度慢) 的傳播物理機制。利用兩者速度差,可在 S 波抵達前數秒解算位置並以電磁波速度搶先警報。",
805
+ keyPoints: ["P波波速約 6 km/s,S波波速約 3.5 km/s", "電磁波傳輸警報以光速前進,遠快於地震波", "提供關鍵數秒至數十秒避難避險前置時間"]
806
+ },
807
+ {
808
+ pptId: "ppt4",
809
+ slideNum: 21,
810
+ category: "history",
811
+ title: "2024/04/03 M7.2 花蓮大地震國家級警報實況",
812
+ description: "回顧花蓮強震當日預警系統運行狀況,說明系統於極短時間內完成解算,並透過細胞廣播 (PWS)、電視插播與中小學防震系統即時推送警報的實際情境。",
813
+ keyPoints: ["全台多個縣市獲得寶貴避險前置時間", "電視台實時彈出插播覆蓋全螢幕", "分析極震區盲區覆蓋以及大眾運輸系統防災減速反應"]
814
+ }
815
+ ];
816
+
817
+ function initHighlights() {
818
+ const container = document.getElementById("highlights-container");
819
+ const filterButtons = document.querySelectorAll(".filter-btn");
820
+ if (!container) return;
821
+
822
+ const renderHighlights = (catFilter) => {
823
+ container.innerHTML = "";
824
+
825
+ const filtered = catFilter === "all"
826
+ ? highlightsData
827
+ : highlightsData.filter(h => h.category === catFilter);
828
+
829
+ filtered.forEach(h => {
830
+ const card = document.createElement("div");
831
+ card.className = "glass-panel";
832
+ card.style.display = "flex";
833
+ card.style.flexDirection = "column";
834
+ card.style.overflow = "hidden";
835
+
836
+ const slideImgPath = `slides/${h.pptId}/slide_${h.slideNum}.png`;
837
+
838
+ // Category tag display
839
+ const catLabels = { network: "觀測架構", eew: "預警效能", ai: "AI與代理人", history: "地震與案例" };
840
+ const catLabel = catLabels[h.category] || h.category;
841
+
842
+ card.innerHTML = `
843
+ <div style="position: relative; width: 100%; padding-top: 56.25%; background: #020408; border-bottom: 1px solid var(--card-border); overflow: hidden; display: flex; align-items: center; justify-content: center;">
844
+ <img src="${slideImgPath}" alt="${h.title}" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: contain; cursor: zoom-in; padding: 4px;" class="highlight-img-click">
845
+ <span style="position: absolute; top: 12px; left: 12px; background: rgba(10, 15, 26, 0.85); border: 1px solid var(--card-border); padding: 4px 10px; border-radius: 6px; font-size: 11px; font-weight: 700; color: var(--accent-cyan); font-family: var(--font-mono);">
846
+ ${h.pptId.toUpperCase()} · Slide ${h.slideNum}
847
+ </span>
848
+ <span style="position: absolute; bottom: 12px; right: 12px; background: rgba(59, 130, 246, 0.15); border: 1px solid rgba(59, 130, 246, 0.2); padding: 3px 8px; border-radius: 4px; font-size: 10.5px; font-weight: bold; color: #60a5fa;">
849
+ ${catLabel}
850
+ </span>
851
+ </div>
852
+ <div style="padding: 20px; flex: 1; display: flex; flex-direction: column; gap: 12px;">
853
+ <h4 style="font-size: 15px; font-weight: 800; color: white; line-height: 1.4;">${h.title}</h4>
854
+ <p style="font-size: 12.5px; color: var(--text-muted); line-height: 1.6; flex: 1;">${h.description}</p>
855
+ <ul style="list-style: none; margin-top: 6px; display: flex; flex-direction: column; gap: 6px;">
856
+ ${h.keyPoints.map(pt => `
857
+ <li style="font-size: 12px; color: var(--text-dark); position: relative; padding-left: 14px; line-height: 1.5;">
858
+ <span style="position: absolute; left: 0; top: 5px; width: 4px; height: 4px; border-radius: 50%; background-color: var(--accent-cyan);"></span>
859
+ ${pt}
860
+ </li>
861
+ `).join("")}
862
+ </ul>
863
+ </div>
864
+ <div style="border-top: 1px solid rgba(255, 255, 255, 0.05); padding: 12px 20px; display: flex; justify-content: flex-end; background: rgba(8, 12, 20, 0.2);">
865
+ <button class="view-btn jump-to-slide-btn" data-ppt="${h.pptId}" data-slide="${h.slideNum}">
866
+ 定位至簡報
867
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="9 18 15 12 9 6"/></svg>
868
+ </button>
869
+ </div>
870
+ `;
871
+
872
+ // Lightbox click handler
873
+ card.querySelector(".highlight-img-click").addEventListener("click", () => {
874
+ lightroomImgEl.src = slideImgPath;
875
+ lightroomEl.style.display = "flex";
876
+ });
877
+
878
+ // Slide Jump click handler
879
+ card.querySelector(".jump-to-slide-btn").addEventListener("click", () => {
880
+ jumpToPresentation(h.pptId, h.slideNum);
881
+ });
882
+
883
+ container.appendChild(card);
884
+ });
885
+ };
886
+
887
+ // Filter Buttons binding
888
+ filterButtons.forEach(btn => {
889
+ btn.addEventListener("click", () => {
890
+ filterButtons.forEach(b => {
891
+ b.classList.remove("active-filter");
892
+ b.style.background = "";
893
+ b.style.borderColor = "";
894
+ });
895
+
896
+ btn.classList.add("active-filter");
897
+ btn.style.background = "var(--accent-cyan-glow)";
898
+ btn.style.borderColor = "var(--accent-cyan)";
899
+
900
+ const filterCat = btn.getAttribute("data-filter-cat");
901
+ renderHighlights(filterCat);
902
+ });
903
+ });
904
+
905
+ // Initial render
906
+ renderHighlights("all");
907
+ }
908
+
slides/ppt1/slide_1.png ADDED

Git LFS Details

  • SHA256: d52ade2861f4e9ba22a0cb5ef3b654fbc6ea5cbb48a1833c8c672b10f8ed0db0
  • Pointer size: 131 Bytes
  • Size of remote file: 799 kB
slides/ppt1/slide_10.png ADDED

Git LFS Details

  • SHA256: 9caa4b8a5737336a30ca8084be1fd4a4c60de225f1f79826d0ac7e0858a6196c
  • Pointer size: 131 Bytes
  • Size of remote file: 800 kB
slides/ppt1/slide_11.png ADDED

Git LFS Details

  • SHA256: 6fc3386afa50dd590f7560165f9a965b007beef63fa779e07c0c22b624d827fa
  • Pointer size: 131 Bytes
  • Size of remote file: 245 kB
slides/ppt1/slide_12.png ADDED

Git LFS Details

  • SHA256: 6469f838f2e605cc6f9e75012a5fb71acad820d155287c160a884db8c38dde0a
  • Pointer size: 131 Bytes
  • Size of remote file: 295 kB
slides/ppt1/slide_13.png ADDED

Git LFS Details

  • SHA256: 441e01178e391d277bc77c3f04e229cb3082fd508995bd6ac7e3d5a9d1646974
  • Pointer size: 132 Bytes
  • Size of remote file: 1.44 MB
slides/ppt1/slide_14.png ADDED

Git LFS Details

  • SHA256: f605e70d51b957dbfd84ad54c143a8508007ee537f9e2ecb734963d57a3b8700
  • Pointer size: 131 Bytes
  • Size of remote file: 439 kB
slides/ppt1/slide_15.png ADDED

Git LFS Details

  • SHA256: e6af031529709cfe9a3d3612dd9578378530b671bf0f89cca086f549ced4989c
  • Pointer size: 131 Bytes
  • Size of remote file: 570 kB
slides/ppt1/slide_16.png ADDED

Git LFS Details

  • SHA256: b2279c8c61b5d935a2e708c187775cd60557a25cc657c3a28dcbaa5f07600719
  • Pointer size: 131 Bytes
  • Size of remote file: 442 kB
slides/ppt1/slide_17.png ADDED

Git LFS Details

  • SHA256: be0482592dbd7e73a1b57155ba6c4313c96395562fb3b86472617dacc6bf0b7c
  • Pointer size: 131 Bytes
  • Size of remote file: 381 kB
slides/ppt1/slide_18.png ADDED

Git LFS Details

  • SHA256: be94202c3854da255bdcf1e585b0482e10e36402804807c1d6234e4afe32692a
  • Pointer size: 131 Bytes
  • Size of remote file: 879 kB
slides/ppt1/slide_19.png ADDED

Git LFS Details

  • SHA256: 1300f8501d1f4d240e7f867db95a654b253843fe1d877965f00400a5c4e8e413
  • Pointer size: 131 Bytes
  • Size of remote file: 586 kB
slides/ppt1/slide_2.png ADDED

Git LFS Details

  • SHA256: 2afbfdb3193d800826e6908f419f249ca2efe6d1c193027e0e1a2c67f0a7ba8b
  • Pointer size: 131 Bytes
  • Size of remote file: 686 kB
slides/ppt1/slide_20.png ADDED

Git LFS Details

  • SHA256: 28c27770e768e53b451b8abb12e4bc900f260d0af3d83c2f70c0c7f69531900e
  • Pointer size: 131 Bytes
  • Size of remote file: 389 kB
slides/ppt1/slide_21.png ADDED

Git LFS Details

  • SHA256: 2df69350943565f5ca0b43ca0ce4126568a8d74d10a4754770785083157fb69b
  • Pointer size: 131 Bytes
  • Size of remote file: 754 kB
slides/ppt1/slide_22.png ADDED

Git LFS Details

  • SHA256: af25911e2303b9be039c7e298972b9eb9a2dc6539560764e33bfe6bb30ca70c6
  • Pointer size: 131 Bytes
  • Size of remote file: 609 kB
slides/ppt1/slide_23.png ADDED

Git LFS Details

  • SHA256: 9055de7872f92a82860ca0fe18b3dd4e58cfdc91cb6056bc1849c78ff9381d22
  • Pointer size: 132 Bytes
  • Size of remote file: 1.06 MB
slides/ppt1/slide_24.png ADDED

Git LFS Details

  • SHA256: c735ff492fef00b8cfd8ac4eedc0ff6e4db2c5e577f02f20327a62f17dc17c61
  • Pointer size: 131 Bytes
  • Size of remote file: 704 kB
slides/ppt1/slide_25.png ADDED

Git LFS Details

  • SHA256: 57799b9f88f2faa87974641e57733c7f38f9d91c00be150cfc171ba7323ff45e
  • Pointer size: 131 Bytes
  • Size of remote file: 514 kB
slides/ppt1/slide_26.png ADDED

Git LFS Details

  • SHA256: e95674ba46c72dfb49c1ff689ea737f71e2fea75f162b3addf2c9e67fd863193
  • Pointer size: 132 Bytes
  • Size of remote file: 1.61 MB
slides/ppt1/slide_3.png ADDED

Git LFS Details

  • SHA256: 7c1278a95f9fafb4d24801c009d3453d9d3505dbffbc6386d14826934cbd5acf
  • Pointer size: 132 Bytes
  • Size of remote file: 1.06 MB
slides/ppt1/slide_4.png ADDED

Git LFS Details

  • SHA256: 1a96cc3226b099820026bd50e85db1aaec28f3d89da34e7d1c6c43f61eae253b
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
slides/ppt1/slide_5.png ADDED

Git LFS Details

  • SHA256: c9d18da84a8c5d0dcdbb8b2d8914bd152708b0aebd46f68f3ec47524076f3161
  • Pointer size: 132 Bytes
  • Size of remote file: 1.24 MB
slides/ppt1/slide_6.png ADDED

Git LFS Details

  • SHA256: 51ee9fbdcdd9e947e250e0313821dfe6c5024380ca27dfbbff7829c210bedfe0
  • Pointer size: 132 Bytes
  • Size of remote file: 1.29 MB
slides/ppt1/slide_7.png ADDED

Git LFS Details

  • SHA256: 0727d8307f3ea304b558c1189a2ce3db579d00183ad10a9b06a2936e66003620
  • Pointer size: 131 Bytes
  • Size of remote file: 367 kB
slides/ppt1/slide_8.png ADDED

Git LFS Details

  • SHA256: 85863c181ad94df2e417f9ae134cae836e7167f3c0b7a83a6816ffe87762222e
  • Pointer size: 131 Bytes
  • Size of remote file: 747 kB
slides/ppt1/slide_9.png ADDED

Git LFS Details

  • SHA256: d476fba47eb98fca29cc0e49ab985c56090e6552a4feb16f6c3e5e60a17dbf6b
  • Pointer size: 131 Bytes
  • Size of remote file: 291 kB
slides/ppt2/slide_1.png ADDED

Git LFS Details

  • SHA256: 1f904d2c651fdb6a2b554bfe8ce63b58e7394e9a90db85c194a6cd246f68196b
  • Pointer size: 130 Bytes
  • Size of remote file: 76.1 kB
slides/ppt2/slide_10.png ADDED

Git LFS Details

  • SHA256: a3034327a8e5083bf7e3b2d25d515f950216fb569a359ac5605a74b6d243f22c
  • Pointer size: 131 Bytes
  • Size of remote file: 744 kB
slides/ppt2/slide_11.png ADDED

Git LFS Details

  • SHA256: 7be9706d32b75c879ae2c7282a2d2ac3c9311af214b883316784e46bfe999c77
  • Pointer size: 131 Bytes
  • Size of remote file: 134 kB
slides/ppt2/slide_12.png ADDED

Git LFS Details

  • SHA256: ea4d83f83a9c21484db73e4080b504169ff479a00e1e82063fbcf495869b993b
  • Pointer size: 131 Bytes
  • Size of remote file: 251 kB
slides/ppt2/slide_13.png ADDED

Git LFS Details

  • SHA256: 7f147631e550c565a9faf228dc6b1801173ca6affc1813f28eef9ba2dd6eb3c3
  • Pointer size: 131 Bytes
  • Size of remote file: 294 kB
slides/ppt2/slide_14.png ADDED

Git LFS Details

  • SHA256: 62dd55c079643869717be32706cc7d96520f39cbec0f219b2501f13c8e35be16
  • Pointer size: 131 Bytes
  • Size of remote file: 270 kB
slides/ppt2/slide_15.png ADDED

Git LFS Details

  • SHA256: 04529c5bd922ea50c2d2e182db6ca93d00106deb8f7f1e1a2abab159183306f2
  • Pointer size: 131 Bytes
  • Size of remote file: 210 kB
slides/ppt2/slide_16.png ADDED

Git LFS Details

  • SHA256: ac54433601d749e43da3bc411b8ad1e3534d775db9494a7b59b380a25062f9c8
  • Pointer size: 131 Bytes
  • Size of remote file: 347 kB
slides/ppt2/slide_17.png ADDED

Git LFS Details

  • SHA256: 144c3351f69b4f739a6b61fa0bff069dff34f7d14e2ef798b61579e1c506b2f8
  • Pointer size: 130 Bytes
  • Size of remote file: 79.6 kB
slides/ppt2/slide_2.png ADDED

Git LFS Details

  • SHA256: 695121e0bc48216f8f6cbcd6fd8cfcb0b60262b22b679fa597d7a011a11d0fda
  • Pointer size: 131 Bytes
  • Size of remote file: 174 kB
slides/ppt2/slide_3.png ADDED

Git LFS Details

  • SHA256: c86a8dcd165247ce389c3507adb8f5e2b87613d6379c529274ed20af0e8015b7
  • Pointer size: 131 Bytes
  • Size of remote file: 369 kB
slides/ppt2/slide_4.png ADDED

Git LFS Details

  • SHA256: 476435fb87b93825c4b260e1b3a942d34fecd1d3dde908e9f2b38f564ca8445c
  • Pointer size: 131 Bytes
  • Size of remote file: 742 kB
slides/ppt2/slide_5.png ADDED

Git LFS Details

  • SHA256: 3679f5079c1c757dc990426a867cf4038290a2d5fb3a9b787b42ad831d3a919d
  • Pointer size: 131 Bytes
  • Size of remote file: 137 kB
slides/ppt2/slide_6.png ADDED

Git LFS Details

  • SHA256: cf99375e8763951b0b6d0d3c75224767a229c9b170d74786ab1ae7aeb84dd521
  • Pointer size: 131 Bytes
  • Size of remote file: 369 kB
slides/ppt2/slide_7.png ADDED

Git LFS Details

  • SHA256: f1a04bb84a77ba866f4347f7537662416a66aa045f37e5299f28e8ed052a884e
  • Pointer size: 131 Bytes
  • Size of remote file: 739 kB
slides/ppt2/slide_8.png ADDED

Git LFS Details

  • SHA256: 81fd6f054634b74b856b248c9e0101bb3f065ca66063f82534879fb0ce2d0e32
  • Pointer size: 131 Bytes
  • Size of remote file: 136 kB
slides/ppt2/slide_9.png ADDED

Git LFS Details

  • SHA256: 965a594d43b6d20070d2337e792e057c2c7634ed00bdc962147b4e59c7974834
  • Pointer size: 131 Bytes
  • Size of remote file: 379 kB
slides/ppt3/slide_1.png ADDED

Git LFS Details

  • SHA256: bf73782f298f750cf33ee817ebdbd2e00290ed4ac590b7e5f6dd33c45a72db47
  • Pointer size: 131 Bytes
  • Size of remote file: 294 kB
slides/ppt3/slide_10.png ADDED

Git LFS Details

  • SHA256: 732a1408e114658c49071da45afd22a970ba35a72d4ab195f379d00cb820ce78
  • Pointer size: 131 Bytes
  • Size of remote file: 960 kB