Commit ·
bbfeecb
0
Parent(s):
feat: initial clean LFS commit of showcase
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- README.md +18 -0
- index.css +1070 -0
- index.html +629 -0
- index.js +908 -0
- slides/ppt1/slide_1.png +3 -0
- slides/ppt1/slide_10.png +3 -0
- slides/ppt1/slide_11.png +3 -0
- slides/ppt1/slide_12.png +3 -0
- slides/ppt1/slide_13.png +3 -0
- slides/ppt1/slide_14.png +3 -0
- slides/ppt1/slide_15.png +3 -0
- slides/ppt1/slide_16.png +3 -0
- slides/ppt1/slide_17.png +3 -0
- slides/ppt1/slide_18.png +3 -0
- slides/ppt1/slide_19.png +3 -0
- slides/ppt1/slide_2.png +3 -0
- slides/ppt1/slide_20.png +3 -0
- slides/ppt1/slide_21.png +3 -0
- slides/ppt1/slide_22.png +3 -0
- slides/ppt1/slide_23.png +3 -0
- slides/ppt1/slide_24.png +3 -0
- slides/ppt1/slide_25.png +3 -0
- slides/ppt1/slide_26.png +3 -0
- slides/ppt1/slide_3.png +3 -0
- slides/ppt1/slide_4.png +3 -0
- slides/ppt1/slide_5.png +3 -0
- slides/ppt1/slide_6.png +3 -0
- slides/ppt1/slide_7.png +3 -0
- slides/ppt1/slide_8.png +3 -0
- slides/ppt1/slide_9.png +3 -0
- slides/ppt2/slide_1.png +3 -0
- slides/ppt2/slide_10.png +3 -0
- slides/ppt2/slide_11.png +3 -0
- slides/ppt2/slide_12.png +3 -0
- slides/ppt2/slide_13.png +3 -0
- slides/ppt2/slide_14.png +3 -0
- slides/ppt2/slide_15.png +3 -0
- slides/ppt2/slide_16.png +3 -0
- slides/ppt2/slide_17.png +3 -0
- slides/ppt2/slide_2.png +3 -0
- slides/ppt2/slide_3.png +3 -0
- slides/ppt2/slide_4.png +3 -0
- slides/ppt2/slide_5.png +3 -0
- slides/ppt2/slide_6.png +3 -0
- slides/ppt2/slide_7.png +3 -0
- slides/ppt2/slide_8.png +3 -0
- slides/ppt2/slide_9.png +3 -0
- slides/ppt3/slide_1.png +3 -0
- slides/ppt3/slide_10.png +3 -0
.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 © 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
|
slides/ppt1/slide_10.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_11.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_12.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_13.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_14.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_15.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_16.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_17.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_18.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_19.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_2.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_20.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_21.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_22.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_23.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_24.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_25.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_26.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_3.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_4.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_5.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_6.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_7.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_8.png
ADDED
|
Git LFS Details
|
slides/ppt1/slide_9.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_1.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_10.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_11.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_12.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_13.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_14.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_15.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_16.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_17.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_2.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_3.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_4.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_5.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_6.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_7.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_8.png
ADDED
|
Git LFS Details
|
slides/ppt2/slide_9.png
ADDED
|
Git LFS Details
|
slides/ppt3/slide_1.png
ADDED
|
Git LFS Details
|
slides/ppt3/slide_10.png
ADDED
|
Git LFS Details
|