@seanyao/roll 0.5.0 β†’ 2.602.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 (181) hide show
  1. package/CHANGELOG.md +736 -0
  2. package/LICENSE +21 -0
  3. package/README.md +65 -165
  4. package/bin/dream-test-quality-scan +110 -0
  5. package/bin/roll +15030 -814
  6. package/conventions/config.yaml +17 -1
  7. package/conventions/global/AGENTS.md +146 -100
  8. package/conventions/global/CLAUDE.md +1 -21
  9. package/conventions/global/GEMINI.md +8 -22
  10. package/conventions/global/project_rules.md +9 -0
  11. package/conventions/templates/backend-service/AGENTS.md +30 -81
  12. package/conventions/templates/backend-service/GEMINI.md +3 -3
  13. package/conventions/templates/backend-service/project_rules.md +16 -0
  14. package/conventions/templates/cli/AGENTS.md +31 -58
  15. package/conventions/templates/cli/CLAUDE.md +3 -5
  16. package/conventions/templates/cli/GEMINI.md +3 -3
  17. package/conventions/templates/cli/project_rules.md +16 -0
  18. package/conventions/templates/frontend-only/AGENTS.md +29 -64
  19. package/conventions/templates/frontend-only/GEMINI.md +3 -3
  20. package/conventions/templates/frontend-only/project_rules.md +14 -0
  21. package/conventions/templates/fullstack/AGENTS.md +31 -79
  22. package/conventions/templates/fullstack/CLAUDE.md +1 -1
  23. package/conventions/templates/fullstack/GEMINI.md +3 -3
  24. package/conventions/templates/fullstack/project_rules.md +15 -0
  25. package/lib/README.md +42 -0
  26. package/lib/__pycache__/github_sync.cpython-314.pyc +0 -0
  27. package/lib/__pycache__/loop-fmt.cpython-314.pyc +0 -0
  28. package/lib/__pycache__/loop_result_eval.cpython-314.pyc +0 -0
  29. package/lib/__pycache__/loop_unstick.cpython-314.pyc +0 -0
  30. package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
  31. package/lib/__pycache__/prices_fetcher.cpython-314.pyc +0 -0
  32. package/lib/__pycache__/roll-home.cpython-314.pyc +0 -0
  33. package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
  34. package/lib/__pycache__/roll_git.cpython-314.pyc +0 -0
  35. package/lib/__pycache__/roll_render.cpython-314.pyc +0 -0
  36. package/lib/__pycache__/slides-render.cpython-314.pyc +0 -0
  37. package/lib/agent_usage/README.md +49 -0
  38. package/lib/agent_usage/__init__.py +108 -0
  39. package/lib/agent_usage/__pycache__/__init__.cpython-314.pyc +0 -0
  40. package/lib/agent_usage/__pycache__/gemini.cpython-314.pyc +0 -0
  41. package/lib/agent_usage/__pycache__/kimi.cpython-314.pyc +0 -0
  42. package/lib/agent_usage/__pycache__/openai.cpython-314.pyc +0 -0
  43. package/lib/agent_usage/__pycache__/pi.cpython-314.pyc +0 -0
  44. package/lib/agent_usage/__pycache__/pi_emit.cpython-314.pyc +0 -0
  45. package/lib/agent_usage/__pycache__/qwen.cpython-314.pyc +0 -0
  46. package/lib/agent_usage/gemini.py +127 -0
  47. package/lib/agent_usage/kimi.py +278 -0
  48. package/lib/agent_usage/kimi_emit.py +123 -0
  49. package/lib/agent_usage/openai.py +126 -0
  50. package/lib/agent_usage/pi.py +200 -0
  51. package/lib/agent_usage/pi_emit.py +135 -0
  52. package/lib/agent_usage/qwen.py +128 -0
  53. package/lib/backfill-pi-usage.py +243 -0
  54. package/lib/changelog_audit.py +155 -0
  55. package/lib/changelog_generate.py +263 -0
  56. package/lib/context_feed_budget.sh +194 -0
  57. package/lib/github_sync.py +876 -0
  58. package/lib/i18n/README.md +54 -0
  59. package/lib/i18n/agent.sh +75 -0
  60. package/lib/i18n/alert.sh +20 -0
  61. package/lib/i18n/backlog.sh +96 -0
  62. package/lib/i18n/brief.sh +5 -0
  63. package/lib/i18n/changelog.sh +5 -0
  64. package/lib/i18n/ci.sh +15 -0
  65. package/lib/i18n/debug.sh +0 -0
  66. package/lib/i18n/doctor.sh +44 -0
  67. package/lib/i18n/dream.sh +0 -0
  68. package/lib/i18n/init.sh +91 -0
  69. package/lib/i18n/lang.sh +10 -0
  70. package/lib/i18n/loop.sh +140 -0
  71. package/lib/i18n/migrate.sh +74 -0
  72. package/lib/i18n/offboard.sh +31 -0
  73. package/lib/i18n/onboard.sh +0 -0
  74. package/lib/i18n/peer.sh +41 -0
  75. package/lib/i18n/peer_help.sh +25 -0
  76. package/lib/i18n/peer_reset.sh +7 -0
  77. package/lib/i18n/peer_status.sh +5 -0
  78. package/lib/i18n/prices.sh +3 -0
  79. package/lib/i18n/prices_refresh.sh +17 -0
  80. package/lib/i18n/prices_show.sh +7 -0
  81. package/lib/i18n/propose.sh +0 -0
  82. package/lib/i18n/release.sh +0 -0
  83. package/lib/i18n/research.sh +0 -0
  84. package/lib/i18n/review_pr.sh +0 -0
  85. package/lib/i18n/sentinel.sh +0 -0
  86. package/lib/i18n/setup.sh +3 -0
  87. package/lib/i18n/shared.sh +157 -0
  88. package/lib/i18n/skills/roll-brief.sh +47 -0
  89. package/lib/i18n/skills/roll-build.sh +97 -0
  90. package/lib/i18n/skills/roll-design.sh +18 -0
  91. package/lib/i18n/skills/roll-fix.sh +53 -0
  92. package/lib/i18n/skills/roll-loop.sh +28 -0
  93. package/lib/i18n/skills/roll-onboard.sh +33 -0
  94. package/lib/i18n/skills_catalog.sh +30 -0
  95. package/lib/i18n/slides.sh +3 -0
  96. package/lib/i18n/slides_build.sh +38 -0
  97. package/lib/i18n/slides_delete.sh +19 -0
  98. package/lib/i18n/slides_list.sh +14 -0
  99. package/lib/i18n/slides_logs.sh +12 -0
  100. package/lib/i18n/slides_new.sh +15 -0
  101. package/lib/i18n/slides_preview.sh +14 -0
  102. package/lib/i18n/slides_templates.sh +7 -0
  103. package/lib/i18n/status.sh +21 -0
  104. package/lib/i18n/update.sh +24 -0
  105. package/lib/i18n.sh +211 -0
  106. package/lib/loop-exit-summary.py +393 -0
  107. package/lib/loop-fmt.py +589 -0
  108. package/lib/loop_pick_agent.py +316 -0
  109. package/lib/loop_result_eval.py +469 -0
  110. package/lib/loop_unstick.py +180 -0
  111. package/lib/model_prices.py +194 -0
  112. package/lib/prices/README.md +35 -0
  113. package/lib/prices/snapshot-2026-05-22.json +22 -0
  114. package/lib/prices/snapshot-2026-05-23-deepseek.json +15 -0
  115. package/lib/prices/snapshot-2026-05-23-kimi.json +15 -0
  116. package/lib/prices_fetcher.py +285 -0
  117. package/lib/roll-backlog.py +225 -0
  118. package/lib/roll-brief.py +286 -0
  119. package/lib/roll-help.py +158 -0
  120. package/lib/roll-home.py +556 -0
  121. package/lib/roll-init.py +156 -0
  122. package/lib/roll-loop-status.py +1683 -0
  123. package/lib/roll-loop-story.py +191 -0
  124. package/lib/roll-onboard-render.py +378 -0
  125. package/lib/roll-peer.py +252 -0
  126. package/lib/roll-plan-validate.py +386 -0
  127. package/lib/roll-setup.py +102 -0
  128. package/lib/roll-status.py +367 -0
  129. package/lib/roll_git.py +41 -0
  130. package/lib/roll_render.py +414 -0
  131. package/lib/slides/components/README.md +123 -0
  132. package/lib/slides/components/cards-2.html +9 -0
  133. package/lib/slides/components/cards-3.html +9 -0
  134. package/lib/slides/components/cards-4.html +9 -0
  135. package/lib/slides/components/compare.html +22 -0
  136. package/lib/slides/components/highlight.html +9 -0
  137. package/lib/slides/components/pipeline.html +12 -0
  138. package/lib/slides/components/plain.html +7 -0
  139. package/lib/slides/components/quote.html +4 -0
  140. package/lib/slides/components/timeline.html +9 -0
  141. package/lib/slides/templates/introduction-v3.html +571 -0
  142. package/lib/slides/templates/pitch.html +0 -0
  143. package/lib/slides-render.py +778 -0
  144. package/lib/slides-validate.py +357 -0
  145. package/lib/test_quality_gate.py +143 -0
  146. package/package.json +8 -7
  147. package/skills/roll-.changelog/SKILL.md +406 -33
  148. package/skills/roll-.clarify/SKILL.md +5 -2
  149. package/skills/roll-.dream/SKILL.md +374 -0
  150. package/skills/roll-.echo/SKILL.md +5 -2
  151. package/skills/roll-.qa/SKILL.md +57 -3
  152. package/skills/roll-.review/SKILL.md +42 -3
  153. package/skills/roll-brief/SKILL.md +209 -0
  154. package/skills/roll-build/SKILL.md +308 -63
  155. package/skills/roll-debug/SKILL.md +341 -162
  156. package/skills/roll-debug/injectable-bb.js +263 -0
  157. package/skills/roll-deck/SKILL.md +296 -0
  158. package/skills/roll-design/ENGINEERING_CHECKLIST.md +1 -1
  159. package/skills/roll-design/SKILL.md +733 -94
  160. package/skills/roll-doc/SKILL.md +595 -0
  161. package/skills/roll-doctor/SKILL.md +192 -0
  162. package/skills/roll-fix/SKILL.md +149 -32
  163. package/skills/{roll-jot β†’ roll-idea}/SKILL.md +18 -10
  164. package/skills/roll-loop/SKILL.md +579 -0
  165. package/skills/roll-notes/SKILL.md +103 -0
  166. package/skills/roll-onboard/SKILL.md +234 -0
  167. package/skills/roll-peer/SKILL.md +336 -0
  168. package/skills/roll-propose/SKILL.md +157 -0
  169. package/skills/roll-review-pr/SKILL.md +58 -0
  170. package/skills/roll-sentinel/SKILL.md +11 -2
  171. package/skills/roll-spar/SKILL.md +8 -6
  172. package/template/.github/workflows/ci.yml +5 -2
  173. package/template/AGENTS.md +20 -74
  174. package/skills/roll-research/SKILL.md +0 -307
  175. package/skills/roll-research/references/schema.json +0 -162
  176. package/skills/roll-research/scripts/md_to_pdf.py +0 -289
  177. package/tools/roll-fetch/SKILL.md +0 -182
  178. package/tools/roll-fetch/package.json +0 -15
  179. package/tools/roll-fetch/smart-web-fetch.js +0 -558
  180. package/tools/roll-probe/SKILL.md +0 -84
  181. /package/template/{BACKLOG.md β†’ .roll/backlog.md} +0 -0
@@ -0,0 +1,571 @@
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