@seanyao/roll 3.609.2 β†’ 3.610.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/README.md +5 -6
  3. package/dist/roll.mjs +16146 -16186
  4. package/package.json +3 -2
  5. package/skills/README.md +14 -1
  6. package/skills/docs/skill-authoring.md +66 -0
  7. package/skills/reports/skill-audit-summary.md +53 -0
  8. package/skills/roll-.changelog/SKILL.md +25 -443
  9. package/skills/roll-.changelog/references/full-contract.md +462 -0
  10. package/skills/roll-.clarify/SKILL.md +6 -4
  11. package/skills/roll-.dream/SKILL.md +26 -353
  12. package/skills/roll-.dream/references/full-contract.md +365 -0
  13. package/skills/roll-.echo/SKILL.md +6 -4
  14. package/skills/roll-.qa/SKILL.md +25 -236
  15. package/skills/roll-.qa/references/full-contract.md +256 -0
  16. package/skills/roll-.review/SKILL.md +6 -2
  17. package/skills/roll-brief/SKILL.md +6 -8
  18. package/skills/roll-build/SKILL.md +28 -864
  19. package/skills/roll-build/references/full-contract.md +883 -0
  20. package/skills/roll-debug/SKILL.md +26 -585
  21. package/skills/roll-debug/references/full-contract.md +607 -0
  22. package/skills/roll-design/SKILL.md +28 -903
  23. package/skills/roll-design/references/full-contract.md +923 -0
  24. package/skills/roll-doc/SKILL.md +25 -574
  25. package/skills/roll-doc/references/full-contract.md +594 -0
  26. package/skills/roll-doctor/SKILL.md +21 -2
  27. package/skills/roll-fix/SKILL.md +28 -621
  28. package/skills/roll-fix/references/full-contract.md +640 -0
  29. package/skills/roll-idea/SKILL.md +6 -2
  30. package/skills/roll-loop/SKILL.md +27 -543
  31. package/skills/roll-loop/references/full-contract.md +555 -0
  32. package/skills/roll-notes/SKILL.md +6 -2
  33. package/skills/roll-onboard/SKILL.md +6 -2
  34. package/skills/roll-peer/SKILL.md +27 -316
  35. package/skills/roll-peer/references/full-contract.md +329 -0
  36. package/skills/roll-propose/SKILL.md +6 -8
  37. package/skills/roll-review-pr/SKILL.md +6 -2
  38. package/skills/roll-sentinel/SKILL.md +26 -344
  39. package/skills/roll-sentinel/references/full-contract.md +363 -0
  40. package/skills/roll-spar/SKILL.md +27 -269
  41. package/skills/roll-spar/references/full-contract.md +288 -0
  42. package/skills/route-cases/skills.json +235 -0
  43. package/skills/scripts/audit-skills.mjs +272 -0
  44. package/skills/scripts/test-audit-skills.mjs +39 -0
  45. package/skills/tests/fixtures/skill-audit/block-skill/SKILL.md +12 -0
  46. package/skills/tests/fixtures/skill-audit/minimal-skill/SKILL.md +8 -0
  47. package/skills/tests/fixtures/skill-audit/quoted-skill/SKILL.md +10 -0
  48. package/skills/tests/fixtures/skill-audit/route-cases.json +21 -0
  49. package/skills/tests/fixtures/skill-audit/spoke-skill/SKILL.md +12 -0
  50. package/skills/tests/fixtures/skill-audit/spoke-skill/references/runbook.md +3 -0
  51. package/bin/roll +0 -15361
  52. package/lib/backfill-pi-usage.py +0 -243
  53. package/lib/changelog_audit.py +0 -149
  54. package/lib/changelog_generate.py +0 -470
  55. package/lib/consistency_check.py +0 -409
  56. package/lib/context_feed_budget.sh +0 -194
  57. package/lib/github_sync.py +0 -876
  58. package/lib/i18n/slides.sh +0 -3
  59. package/lib/i18n/slides_build.sh +0 -38
  60. package/lib/i18n/slides_delete.sh +0 -19
  61. package/lib/i18n/slides_list.sh +0 -14
  62. package/lib/i18n/slides_logs.sh +0 -12
  63. package/lib/i18n/slides_new.sh +0 -15
  64. package/lib/i18n/slides_preview.sh +0 -14
  65. package/lib/i18n/slides_templates.sh +0 -7
  66. package/lib/i18n.sh +0 -211
  67. package/lib/loop-exit-summary.py +0 -393
  68. package/lib/loop-fmt.py +0 -589
  69. package/lib/loop_pick_agent.py +0 -316
  70. package/lib/loop_result_eval.py +0 -469
  71. package/lib/loop_unstick.py +0 -180
  72. package/lib/model_prices.py +0 -194
  73. package/lib/prices_fetcher.py +0 -534
  74. package/lib/roll-backlog.py +0 -225
  75. package/lib/roll-brief.py +0 -286
  76. package/lib/roll-help.py +0 -158
  77. package/lib/roll-home.py +0 -556
  78. package/lib/roll-init.py +0 -156
  79. package/lib/roll-loop-status.py +0 -1691
  80. package/lib/roll-loop-story.py +0 -191
  81. package/lib/roll-peer.py +0 -252
  82. package/lib/roll-setup.py +0 -102
  83. package/lib/roll-status.py +0 -367
  84. package/lib/roll_git.py +0 -41
  85. package/lib/roll_render.py +0 -414
  86. package/lib/slides/components/README.md +0 -123
  87. package/lib/slides/components/cards-2.html +0 -9
  88. package/lib/slides/components/cards-3.html +0 -9
  89. package/lib/slides/components/cards-4.html +0 -9
  90. package/lib/slides/components/compare.html +0 -22
  91. package/lib/slides/components/highlight.html +0 -9
  92. package/lib/slides/components/pipeline.html +0 -12
  93. package/lib/slides/components/plain.html +0 -7
  94. package/lib/slides/components/quote.html +0 -4
  95. package/lib/slides/components/timeline.html +0 -9
  96. package/lib/slides/templates/introduction-v3.html +0 -571
  97. package/lib/slides/templates/pitch.html +0 -0
  98. package/lib/slides-render.py +0 -778
  99. package/lib/slides-validate.py +0 -357
  100. package/lib/test_quality_gate.py +0 -143
  101. package/skills/roll-deck/SKILL.md +0 -296
  102. /package/skills/roll-debug/{injectable-bb.js β†’ assets/injectable-bb.js} +0 -0
  103. /package/skills/roll-design/{ENGINEERING_CHECKLIST.md β†’ references/engineering-checklist.md} +0 -0
@@ -1,571 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" data-theme="dark" data-lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
- <title>{{title_en}}</title>
7
- <meta name="title-zh" content="{{title_zh}}">
8
- <meta name="deck-slug" content="{{slug}}">
9
- <style>
10
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=Noto+Sans+SC:wght@300;400;500;600;700;900&display=swap');
11
-
12
- :root {
13
- --bg: #0a0a0f;
14
- --bg-card: #12121a;
15
- --bg-card-hover: #1a1a28;
16
- --bg-control: rgba(18,18,26,0.92);
17
- --text: #e8e8ef;
18
- --text-dim: #8888a0;
19
- --text-bright: #ffffff;
20
- --accent: #6366f1;
21
- --accent-light: #818cf8;
22
- --green: #22c55e;
23
- --green-text: #4ade80;
24
- --orange: #f59e0b;
25
- --orange-text: #fbbf24;
26
- --red: #ef4444;
27
- --red-text: #f87171;
28
- --cyan: #06b6d4;
29
- --cyan-text: #22d3ee;
30
- --border: rgba(255,255,255,0.06);
31
- --border-strong: rgba(255,255,255,0.12);
32
- --shadow: 0 4px 24px rgba(0,0,0,0.3);
33
- --radius: 16px;
34
- }
35
-
36
- [data-theme="light"] {
37
- --bg: #fafafc;
38
- --bg-card: #ffffff;
39
- --bg-card-hover: #f3f4f8;
40
- --bg-control: rgba(255,255,255,0.95);
41
- --text: #1f2937;
42
- --text-dim: #6b7280;
43
- --text-bright: #0a0a0f;
44
- --accent: #6366f1;
45
- --accent-light: #4f46e5;
46
- --green: #16a34a;
47
- --green-text: #15803d;
48
- --orange: #d97706;
49
- --orange-text: #b45309;
50
- --red: #dc2626;
51
- --red-text: #b91c1c;
52
- --cyan: #0891b2;
53
- --cyan-text: #0e7490;
54
- --border: rgba(15,23,42,0.08);
55
- --border-strong: rgba(15,23,42,0.14);
56
- --shadow: 0 4px 24px rgba(15,23,42,0.06);
57
- }
58
-
59
- * { margin: 0; padding: 0; box-sizing: border-box; }
60
-
61
- html, body {
62
- font-family: 'Inter', 'Noto Sans SC', system-ui, sans-serif;
63
- background: var(--bg);
64
- color: var(--text);
65
- height: 100%;
66
- width: 100%;
67
- overflow: hidden;
68
- transition: background 0.3s ease, color 0.3s ease;
69
- }
70
-
71
- /* Language switching: only show the active language */
72
- [data-lang="en"] .lang-zh { display: none !important; }
73
- [data-lang="zh"] .lang-en { display: none !important; }
74
-
75
- #progress-bar {
76
- position: fixed; top: 0; left: 0; height: 3px;
77
- background: linear-gradient(90deg, var(--accent), var(--cyan));
78
- z-index: 1000; transition: width 0.4s ease;
79
- }
80
-
81
- .slides-container { position: relative; width: 100vw; height: 100vh; }
82
-
83
- .slide {
84
- position: absolute; inset: 0;
85
- display: flex; flex-direction: column;
86
- justify-content: center; align-items: center;
87
- padding: 64px 80px 96px;
88
- opacity: 0; transform: translateY(30px);
89
- transition: opacity 0.5s ease, transform 0.5s ease;
90
- pointer-events: none; overflow-y: auto;
91
- }
92
- .slide.active { opacity: 1; transform: translateY(0); pointer-events: auto; }
93
- .slide.exit-up { opacity: 0; transform: translateY(-30px); }
94
-
95
- .slide-number {
96
- position: absolute; top: 28px; right: 24px;
97
- font-size: 13px; color: var(--text-dim); font-weight: 500;
98
- }
99
-
100
- .tag {
101
- display: inline-block; padding: 5px 14px;
102
- background: rgba(99,102,241,0.12); color: var(--accent-light);
103
- border-radius: 20px; font-size: 12px; font-weight: 600;
104
- letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 16px;
105
- }
106
- [data-theme="light"] .tag { background: rgba(99,102,241,0.1); }
107
-
108
- h1 {
109
- font-size: 52px; font-weight: 800; line-height: 1.15;
110
- color: var(--text-bright); text-align: center; margin-bottom: 12px;
111
- }
112
- h1 .gradient {
113
- background: linear-gradient(135deg, var(--accent-light), var(--cyan));
114
- -webkit-background-clip: text; -webkit-text-fill-color: transparent;
115
- }
116
- h2 {
117
- font-size: 34px; font-weight: 700; line-height: 1.25;
118
- color: var(--text-bright); margin-bottom: 12px; text-align: center;
119
- }
120
- h3 { font-size: 18px; font-weight: 600; color: var(--text-bright); margin-bottom: 6px; }
121
- strong { color: var(--text-bright); }
122
-
123
- .subtitle {
124
- font-size: 18px; color: var(--text-dim); line-height: 1.6;
125
- text-align: center; max-width: 720px;
126
- }
127
- .section-desc {
128
- font-size: 15px; color: var(--text-dim); line-height: 1.7;
129
- max-width: 820px; margin-bottom: 24px; text-align: center;
130
- }
131
-
132
- .cards { display: grid; gap: 14px; width: 100%; max-width: 1100px; }
133
- .cards-2 { grid-template-columns: 1fr 1fr; }
134
- .cards-3 { grid-template-columns: 1fr 1fr 1fr; }
135
- .cards-4 { grid-template-columns: repeat(4, 1fr); }
136
-
137
- .card {
138
- background: var(--bg-card); border: 1px solid var(--border);
139
- border-radius: var(--radius); padding: 20px 20px;
140
- transition: all 0.3s ease;
141
- box-shadow: var(--shadow);
142
- }
143
- .card:hover { background: var(--bg-card-hover); transform: translateY(-2px); }
144
- .card p { font-size: 13px; color: var(--text-dim); line-height: 1.6; }
145
- .card .small { font-size: 11px; }
146
-
147
- .metric-row {
148
- display: flex; gap: 14px; width: 100%; max-width: 1000px;
149
- justify-content: center; margin-top: 8px;
150
- }
151
- .metric {
152
- text-align: center; padding: 20px 14px;
153
- background: var(--bg-card); border: 1px solid var(--border);
154
- border-radius: var(--radius); flex: 1;
155
- box-shadow: var(--shadow);
156
- }
157
- .metric .number { font-size: 32px; font-weight: 800; line-height: 1; margin-bottom: 6px; }
158
- .metric .label { font-size: 12px; color: var(--text-dim); font-weight: 500; }
159
-
160
- .compare {
161
- display: grid; grid-template-columns: 1fr 50px 1fr;
162
- gap: 0; width: 100%; max-width: 1000px; align-items: start;
163
- }
164
- .compare-col { padding: 22px 20px; border-radius: var(--radius); }
165
- .compare-before { background: rgba(239,68,68,0.06); border: 1px solid rgba(239,68,68,0.18); }
166
- .compare-after { background: rgba(34,197,94,0.06); border: 1px solid rgba(34,197,94,0.18); }
167
- [data-theme="light"] .compare-before { background: rgba(239,68,68,0.04); }
168
- [data-theme="light"] .compare-after { background: rgba(34,197,94,0.04); }
169
- .compare-arrow {
170
- display: flex; align-items: center; justify-content: center;
171
- font-size: 24px; color: var(--text-dim); padding-top: 36px;
172
- }
173
- .compare-col h3 { margin-bottom: 14px; font-size: 15px; }
174
- .compare-item {
175
- display: flex; align-items: flex-start; gap: 8px;
176
- margin-bottom: 10px; font-size: 13px; line-height: 1.5; color: var(--text);
177
- }
178
- .compare-item .icon { flex-shrink: 0; font-size: 13px; margin-top: 2px; }
179
-
180
- .pipeline-bar {
181
- display: flex; gap: 4px; width: 100%; max-width: 1000px; margin-top: 8px;
182
- }
183
- .pipe-stage {
184
- flex: 1; text-align: center; padding: 14px 8px; border-radius: 12px;
185
- }
186
- .pipe-stage h4 { font-size: 16px; font-weight: 700; }
187
- .pipe-stage p { font-size: 11px; color: var(--text-dim); margin-top: 4px; line-height: 1.4; }
188
- .pipe-idea, .pipe-backlog { background: rgba(99,102,241,0.08); border: 1.5px solid rgba(99,102,241,0.3); }
189
- .pipe-idea h4, .pipe-backlog h4 { color: var(--accent-light); }
190
- .pipe-build { background: rgba(34,197,94,0.08); border: 1.5px solid rgba(34,197,94,0.3); }
191
- .pipe-build h4 { color: var(--green-text); }
192
- .pipe-verify { background: rgba(245,158,11,0.08); border: 1.5px solid rgba(245,158,11,0.3); }
193
- .pipe-verify h4 { color: var(--orange-text); }
194
- .pipe-release { background: rgba(6,182,212,0.08); border: 1.5px solid rgba(6,182,212,0.3); }
195
- .pipe-release h4 { color: var(--cyan-text); }
196
- .pipe-arrow { display: flex; align-items: center; justify-content: center; font-size: 16px; color: var(--text-dim); padding: 0 2px; }
197
-
198
- /* Human-AI bar */
199
- .hai-bar { display: flex; gap: 4px; width: 100%; max-width: 1000px; margin-top: 8px; }
200
- .hai-zone { text-align: center; padding: 14px 10px; border-radius: 12px; }
201
- .hai-zone .emoji { font-size: 20px; }
202
- .hai-zone h4 { font-size: 14px; font-weight: 700; margin-top: 4px; }
203
- .hai-zone p { font-size: 11px; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
204
- .hai-human { flex: 2; background: rgba(99,102,241,0.06); border: 1px solid rgba(99,102,241,0.18); }
205
- .hai-human h4 { color: var(--accent-light); }
206
- .hai-ai { flex: 2; background: rgba(34,197,94,0.06); border: 1px solid rgba(34,197,94,0.18); }
207
- .hai-ai h4 { color: var(--green-text); }
208
- .hai-push { flex: 1; background: rgba(245,158,11,0.06); border: 1px solid rgba(245,158,11,0.18); }
209
- .hai-push h4 { color: var(--orange-text); }
210
-
211
- .quote-block {
212
- border-left: 3px solid var(--accent);
213
- padding: 14px 22px; margin: 14px 0;
214
- background: rgba(99,102,241,0.06);
215
- border-radius: 0 10px 10px 0; max-width: 720px;
216
- }
217
- .quote-block p { font-size: 15px; font-style: italic; color: var(--text); line-height: 1.6; }
218
-
219
- .highlight-box {
220
- background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(6,182,212,0.1));
221
- border: 1px solid rgba(99,102,241,0.2);
222
- border-radius: var(--radius); padding: 20px 24px; max-width: 820px; text-align: center;
223
- }
224
- .highlight-box p { font-size: 14px; color: var(--text); line-height: 1.6; }
225
-
226
- .analogy {
227
- background: rgba(245,158,11,0.06); border: 1px solid rgba(245,158,11,0.18);
228
- border-radius: var(--radius); padding: 14px 20px; max-width: 820px; margin-top: 14px;
229
- }
230
- .analogy-label {
231
- font-size: 11px; font-weight: 700; color: var(--orange-text);
232
- text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 5px;
233
- }
234
- .analogy p { font-size: 13px; line-height: 1.6; color: var(--text); }
235
-
236
- .flow-row {
237
- display: flex; align-items: center; gap: 8px;
238
- flex-wrap: wrap; justify-content: center;
239
- }
240
- .flow-step {
241
- padding: 10px 16px; border-radius: 10px;
242
- font-size: 13px; font-weight: 600;
243
- }
244
- .flow-arrow { font-size: 16px; color: var(--text-dim); }
245
- .flow-branch { display: flex; flex-direction: column; gap: 6px; align-items: center; }
246
-
247
- .timeline {
248
- width: 100%; max-width: 880px; position: relative; padding-left: 36px;
249
- }
250
- .timeline::before {
251
- content: ''; position: absolute; left: 12px; top: 0; bottom: 0;
252
- width: 2px; background: var(--border-strong);
253
- }
254
- .timeline-item { position: relative; margin-bottom: 14px; padding-left: 24px; }
255
- .timeline-item::before {
256
- content: ''; position: absolute; left: -30px; top: 5px;
257
- width: 10px; height: 10px; border-radius: 50%;
258
- background: var(--accent); box-shadow: 0 0 10px rgba(99,102,241,0.3);
259
- }
260
- .timeline-item h4 { font-size: 14px; font-weight: 700; color: var(--text-bright); margin-bottom: 3px; }
261
- .timeline-item p { font-size: 12.5px; color: var(--text-dim); line-height: 1.5; }
262
-
263
- /* Skill table */
264
- .skill-table {
265
- width: 100%; max-width: 1060px; border-collapse: collapse; margin-top: 8px;
266
- }
267
- .skill-table th {
268
- text-align: left; font-size: 11px; font-weight: 700; color: var(--text-dim);
269
- text-transform: uppercase; letter-spacing: 0.06em;
270
- padding: 6px 10px; border-bottom: 1px solid var(--border-strong);
271
- }
272
- .skill-table td {
273
- padding: 7px 10px; font-size: 12.5px; color: var(--text);
274
- border-bottom: 1px solid var(--border); vertical-align: top;
275
- }
276
- [data-theme="dark"] .skill-table tr:hover td { background: rgba(255,255,255,0.02); }
277
- [data-theme="light"] .skill-table tr:hover td { background: rgba(15,23,42,0.02); }
278
- .skill-name {
279
- font-family: 'SF Mono', 'Fira Code', monospace; font-weight: 600;
280
- font-size: 12px; white-space: nowrap;
281
- }
282
- .phase-tag {
283
- font-size: 10px; padding: 2px 7px; border-radius: 4px; font-weight: 600;
284
- display: inline-block;
285
- }
286
- .phase-design { background: rgba(99,102,241,0.15); color: var(--accent-light); }
287
- .phase-build { background: rgba(34,197,94,0.15); color: var(--green-text); }
288
- .phase-check { background: rgba(245,158,11,0.15); color: var(--orange-text); }
289
- .phase-auto { background: rgba(6,182,212,0.15); color: var(--cyan-text); }
290
- .phase-support { background: rgba(255,255,255,0.06); color: var(--text-dim); }
291
- [data-theme="light"] .phase-support { background: rgba(15,23,42,0.06); }
292
-
293
- /* Autonomous layers */
294
- .auto-layers {
295
- display: flex; gap: 12px; width: 100%; max-width: 1000px; margin-top: 8px;
296
- }
297
- .auto-layer {
298
- flex: 1; text-align: center; padding: 18px 14px; border-radius: var(--radius);
299
- background: var(--bg-card); border: 1px solid var(--border);
300
- box-shadow: var(--shadow);
301
- }
302
- .auto-layer .al-icon { font-size: 28px; }
303
- .auto-layer h4 { font-size: 14px; font-weight: 700; color: var(--text-bright); margin-top: 6px; }
304
- .auto-layer p { font-size: 12px; color: var(--text-dim); margin-top: 6px; line-height: 1.5; }
305
- .auto-layer .al-skills {
306
- font-size: 10px; color: var(--text-dim); margin-top: 8px;
307
- font-family: 'SF Mono', 'Fira Code', monospace;
308
- }
309
-
310
- .tree {
311
- font-family: 'SF Mono', 'Fira Code', monospace;
312
- font-size: 13px; line-height: 1.7; color: var(--text);
313
- background: var(--bg-card); border: 1px solid var(--border);
314
- border-radius: var(--radius); padding: 20px 24px;
315
- max-width: 720px; width: 100%;
316
- box-shadow: var(--shadow);
317
- }
318
- .tree .comment { color: var(--text-dim); }
319
- .tree .file { color: var(--cyan-text); }
320
- .tree .dir { color: var(--accent-light); }
321
-
322
- .skill-tag {
323
- font-size: 11px; padding: 4px 8px; border-radius: 6px;
324
- font-weight: 600; font-family: 'SF Mono', 'Fira Code', monospace;
325
- background: rgba(99,102,241,0.15); color: var(--accent-light);
326
- }
327
-
328
- /* Loop overview pills */
329
- .loops-row {
330
- display: flex; gap: 4px; width: 100%; max-width: 1000px; margin-top: 12px;
331
- }
332
- .loop-pill {
333
- flex: 1; text-align: center; padding: 8px 6px; border-radius: 8px;
334
- border: 1px dashed;
335
- }
336
- .loop-pill.la { border-color: rgba(99,102,241,0.4); background: rgba(99,102,241,0.04); }
337
- .loop-pill.lb { border-color: rgba(34,197,94,0.4); background: rgba(34,197,94,0.04); }
338
- .loop-pill.lc { border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.04); }
339
- .loop-pill h5 { font-size: 12px; font-weight: 700; }
340
- .loop-pill.la h5 { color: var(--accent-light); }
341
- .loop-pill.lb h5 { color: var(--green-text); }
342
- .loop-pill.lc h5 { color: var(--orange-text); }
343
- .loop-pill p { font-size: 10px; color: var(--text-dim); margin-top: 2px; }
344
-
345
- /* Controls (theme + lang) */
346
- .controls {
347
- position: fixed; top: 18px; left: 18px;
348
- display: flex; gap: 8px; z-index: 200;
349
- }
350
- .ctrl-btn {
351
- background: var(--bg-control); backdrop-filter: blur(20px);
352
- border: 1px solid var(--border-strong);
353
- color: var(--text); font-size: 12px; font-weight: 600;
354
- padding: 8px 14px; border-radius: 24px; cursor: pointer;
355
- display: flex; align-items: center; gap: 6px;
356
- transition: all 0.2s;
357
- min-height: 36px;
358
- }
359
- .ctrl-btn:hover { transform: translateY(-1px); border-color: var(--accent-light); }
360
- .ctrl-btn svg { width: 14px; height: 14px; }
361
- .ctrl-btn .label { font-size: 12px; }
362
-
363
- /* Nav */
364
- .nav {
365
- position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
366
- display: flex; align-items: center; gap: 14px; z-index: 100;
367
- background: var(--bg-control); backdrop-filter: blur(20px);
368
- padding: 8px 18px; border-radius: 40px; border: 1px solid var(--border-strong);
369
- }
370
- .nav-btn {
371
- width: 36px; height: 36px; border-radius: 50%;
372
- background: rgba(127,127,127,0.08); border: 1px solid var(--border-strong);
373
- color: var(--text); font-size: 18px; cursor: pointer;
374
- display: flex; align-items: center; justify-content: center; transition: all 0.2s;
375
- }
376
- .nav-btn:hover { background: rgba(127,127,127,0.16); }
377
- .nav-btn:disabled { opacity: 0.3; cursor: default; }
378
- .nav-info { font-size: 12px; color: var(--text-dim); font-weight: 500; min-width: 50px; text-align: center; }
379
-
380
- .key-hint {
381
- position: fixed; bottom: 32px; right: 24px;
382
- font-size: 11px; color: var(--text-dim); display: flex; gap: 10px; z-index: 100;
383
- }
384
- .key-hint kbd {
385
- background: rgba(127,127,127,0.1); border: 1px solid var(--border-strong);
386
- padding: 1px 6px; border-radius: 3px; font-family: inherit;
387
- }
388
-
389
- .fade-in { opacity: 0; transform: translateY(16px); }
390
- .slide.active .fade-in { animation: fadeUp 0.5s ease forwards; }
391
- .slide.active .fade-in:nth-child(2) { animation-delay: 0.06s; }
392
- .slide.active .fade-in:nth-child(3) { animation-delay: 0.12s; }
393
- .slide.active .fade-in:nth-child(4) { animation-delay: 0.18s; }
394
- .slide.active .fade-in:nth-child(5) { animation-delay: 0.24s; }
395
- .slide.active .fade-in:nth-child(6) { animation-delay: 0.30s; }
396
- @keyframes fadeUp { to { opacity: 1; transform: translateY(0); } }
397
-
398
- .slide::-webkit-scrollbar { width: 4px; }
399
- .slide::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 2px; }
400
-
401
- /* Mobile adaptation */
402
- @media (max-width: 900px) {
403
- .slide { padding: 64px 18px 110px; justify-content: flex-start; }
404
- h1 { font-size: 30px; }
405
- h2 { font-size: 22px; line-height: 1.3; }
406
- h3 { font-size: 15px; }
407
- .subtitle { font-size: 15px; }
408
- .section-desc { font-size: 13px; margin-bottom: 16px; }
409
- .tag { font-size: 11px; padding: 4px 11px; margin-bottom: 12px; }
410
- .cards-2, .cards-3, .cards-4 { grid-template-columns: 1fr; }
411
- .compare { grid-template-columns: 1fr; gap: 12px; }
412
- .compare-arrow { transform: rotate(90deg); padding: 0; }
413
- .pipeline-bar { flex-direction: column; }
414
- .pipe-arrow { transform: rotate(90deg); padding: 4px 0; }
415
- .hai-bar { flex-direction: column; }
416
- .auto-layers { flex-direction: column; }
417
- .loops-row { flex-direction: column; }
418
- .metric-row { flex-direction: column; }
419
- .timeline { padding-left: 28px; }
420
- .timeline-item { padding-left: 16px; }
421
- .skill-table { font-size: 11px; }
422
- .skill-table th:nth-child(2), .skill-table td:nth-child(2) { display: none; }
423
- .slide-number { top: 16px; right: 16px; font-size: 11px; }
424
- .controls { top: 12px; left: 12px; gap: 6px; }
425
- .ctrl-btn { padding: 6px 10px; font-size: 11px; min-height: 32px; }
426
- .ctrl-btn .label { display: none; }
427
- .key-hint { display: none; }
428
- .nav { bottom: 16px; padding: 6px 14px; }
429
- .nav-btn { width: 32px; height: 32px; font-size: 16px; }
430
- .tree { font-size: 11px; padding: 14px 16px; overflow-x: auto; white-space: pre; }
431
- .quote-block { padding: 12px 16px; }
432
- .quote-block p { font-size: 13px; }
433
- .highlight-box { padding: 14px 16px; }
434
- .highlight-box p { font-size: 13px; }
435
- .flow-step { font-size: 12px; padding: 8px 12px; }
436
- .auto-layer { padding: 14px 12px; }
437
- .auto-layer .al-icon { font-size: 24px; }
438
- .card { padding: 16px; }
439
- }
440
-
441
- @media (max-width: 480px) {
442
- h1 { font-size: 26px; }
443
- h2 { font-size: 19px; }
444
- .slide { padding: 60px 14px 100px; }
445
- }
446
- </style>
447
- </head>
448
- <body>
449
-
450
- <div id="progress-bar"></div>
451
-
452
- <!-- Controls -->
453
- <div class="controls">
454
- <button class="ctrl-btn" id="btn-theme" onclick="toggleTheme()" aria-label="Toggle theme">
455
- <span id="theme-icon">πŸŒ™</span>
456
- <span class="label" id="theme-label">Dark</span>
457
- </button>
458
- <button class="ctrl-btn" id="btn-lang" onclick="toggleLang()" aria-label="Toggle language">
459
- <span>🌐</span>
460
- <span class="label" id="lang-label">EN</span>
461
- </button>
462
- </div>
463
-
464
- <div class="slides-container" id="slides">
465
- {{#slides}}
466
- <!-- {{number}}: {{title_en}} -->
467
- <div class="slide{{#is_cover}} active{{/is_cover}}" data-slide="{{number}}">
468
- {{{slide_inner_html}}}
469
- {{^is_cover}}<span class="slide-number">{{number_padded}} / {{total_slides}}</span>{{/is_cover}}
470
- </div>
471
- {{/slides}}
472
- </div>
473
-
474
- <!-- Nav -->
475
- <div class="nav">
476
- <button class="nav-btn" id="btn-prev" onclick="navigate(-1)" aria-label="Previous slide">β€Ή</button>
477
- <span class="nav-info" id="nav-info">1 / {{total_slides}}</span>
478
- <button class="nav-btn" id="btn-next" onclick="navigate(1)" aria-label="Next slide">β€Ί</button>
479
- </div>
480
- <div class="key-hint">
481
- <span><kbd>←</kbd> <kbd>β†’</kbd> <span class="lang-en">Navigate</span><span class="lang-zh">ηΏ»ι‘΅</span></span>
482
- <span><kbd>F</kbd> <span class="lang-en">Fullscreen</span><span class="lang-zh">全屏</span></span>
483
- <span><kbd>T</kbd> <span class="lang-en">Theme</span><span class="lang-zh">主钘</span></span>
484
- <span><kbd>L</kbd> <span class="lang-en">Lang</span><span class="lang-zh">语言</span></span>
485
- </div>
486
-
487
- <script>
488
- const slides = document.querySelectorAll('.slide');
489
- const total = slides.length;
490
- let current = 0;
491
-
492
- function showSlide(i) {
493
- slides.forEach((s, idx) => {
494
- s.classList.remove('active', 'exit-up');
495
- if (idx < i) s.classList.add('exit-up');
496
- });
497
- slides[i].classList.add('active');
498
- document.getElementById('nav-info').textContent = `${i + 1} / ${total}`;
499
- document.getElementById('btn-prev').disabled = i === 0;
500
- document.getElementById('btn-next').disabled = i === total - 1;
501
- document.getElementById('progress-bar').style.width = `${((i + 1) / total) * 100}%`;
502
- }
503
-
504
- function navigate(dir) {
505
- const next = current + dir;
506
- if (next >= 0 && next < total) { current = next; showSlide(current); }
507
- }
508
-
509
- // Theme toggle
510
- function applyTheme(theme) {
511
- document.documentElement.setAttribute('data-theme', theme);
512
- document.getElementById('theme-icon').textContent = theme === 'dark' ? 'πŸŒ™' : 'β˜€οΈ';
513
- document.getElementById('theme-label').textContent = theme === 'dark' ? 'Dark' : 'Light';
514
- try { localStorage.setItem('roll-slides-theme', theme); } catch (e) {}
515
- }
516
- function toggleTheme() {
517
- const cur = document.documentElement.getAttribute('data-theme') || 'dark';
518
- applyTheme(cur === 'dark' ? 'light' : 'dark');
519
- }
520
-
521
- // Language toggle
522
- function applyLang(lang) {
523
- document.documentElement.setAttribute('data-lang', lang);
524
- document.documentElement.setAttribute('lang', lang === 'zh' ? 'zh-CN' : 'en');
525
- document.getElementById('lang-label').textContent = lang === 'en' ? 'EN' : 'δΈ­ζ–‡';
526
- try { localStorage.setItem('roll-slides-lang', lang); } catch (e) {}
527
- }
528
- function toggleLang() {
529
- const cur = document.documentElement.getAttribute('data-lang') || 'en';
530
- applyLang(cur === 'en' ? 'zh' : 'en');
531
- }
532
-
533
- // Restore preferences
534
- try {
535
- const savedTheme = localStorage.getItem('roll-slides-theme');
536
- if (savedTheme === 'light' || savedTheme === 'dark') applyTheme(savedTheme);
537
- const savedLang = localStorage.getItem('roll-slides-lang');
538
- if (savedLang === 'en' || savedLang === 'zh') applyLang(savedLang);
539
- else {
540
- // Auto-detect: prefer ZH if browser is Chinese
541
- const nav = (navigator.language || '').toLowerCase();
542
- if (nav.startsWith('zh')) applyLang('zh');
543
- }
544
- } catch (e) {}
545
-
546
- document.addEventListener('keydown', (e) => {
547
- if (e.key === 'ArrowRight' || e.key === 'ArrowDown' || e.key === ' ') { e.preventDefault(); navigate(1); }
548
- else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); navigate(-1); }
549
- else if (e.key === 'f' || e.key === 'F') {
550
- document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen();
551
- }
552
- else if (e.key === 't' || e.key === 'T') { toggleTheme(); }
553
- else if (e.key === 'l' || e.key === 'L') { toggleLang(); }
554
- });
555
-
556
- // Touch swipe
557
- let tx = 0, ty = 0;
558
- document.addEventListener('touchstart', e => {
559
- tx = e.touches[0].clientX; ty = e.touches[0].clientY;
560
- }, { passive: true });
561
- document.addEventListener('touchend', e => {
562
- const dx = tx - e.changedTouches[0].clientX;
563
- const dy = ty - e.changedTouches[0].clientY;
564
- // Only treat as swipe when horizontal movement dominates and is significant
565
- if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) navigate(dx > 0 ? 1 : -1);
566
- }, { passive: true });
567
-
568
- showSlide(0);
569
- </script>
570
- </body>
571
- </html>
File without changes