@seanyao/roll 2026.522.1 → 2026.522.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.
@@ -0,0 +1,576 @@
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
+ <div class="lang-en">
469
+ {{{body_en}}}
470
+ </div>
471
+ <div class="lang-zh">
472
+ {{{body_zh}}}
473
+ </div>
474
+ {{^is_cover}}<span class="slide-number">{{number_padded}} / {{total_slides}}</span>{{/is_cover}}
475
+ </div>
476
+ {{/slides}}
477
+ </div>
478
+
479
+ <!-- Nav -->
480
+ <div class="nav">
481
+ <button class="nav-btn" id="btn-prev" onclick="navigate(-1)" aria-label="Previous slide">‹</button>
482
+ <span class="nav-info" id="nav-info">1 / {{total_slides}}</span>
483
+ <button class="nav-btn" id="btn-next" onclick="navigate(1)" aria-label="Next slide">›</button>
484
+ </div>
485
+ <div class="key-hint">
486
+ <span><kbd>←</kbd> <kbd>→</kbd> <span class="lang-en">Navigate</span><span class="lang-zh">翻页</span></span>
487
+ <span><kbd>F</kbd> <span class="lang-en">Fullscreen</span><span class="lang-zh">全屏</span></span>
488
+ <span><kbd>T</kbd> <span class="lang-en">Theme</span><span class="lang-zh">主题</span></span>
489
+ <span><kbd>L</kbd> <span class="lang-en">Lang</span><span class="lang-zh">语言</span></span>
490
+ </div>
491
+
492
+ <script>
493
+ const slides = document.querySelectorAll('.slide');
494
+ const total = slides.length;
495
+ let current = 0;
496
+
497
+ function showSlide(i) {
498
+ slides.forEach((s, idx) => {
499
+ s.classList.remove('active', 'exit-up');
500
+ if (idx < i) s.classList.add('exit-up');
501
+ });
502
+ slides[i].classList.add('active');
503
+ document.getElementById('nav-info').textContent = `${i + 1} / ${total}`;
504
+ document.getElementById('btn-prev').disabled = i === 0;
505
+ document.getElementById('btn-next').disabled = i === total - 1;
506
+ document.getElementById('progress-bar').style.width = `${((i + 1) / total) * 100}%`;
507
+ }
508
+
509
+ function navigate(dir) {
510
+ const next = current + dir;
511
+ if (next >= 0 && next < total) { current = next; showSlide(current); }
512
+ }
513
+
514
+ // Theme toggle
515
+ function applyTheme(theme) {
516
+ document.documentElement.setAttribute('data-theme', theme);
517
+ document.getElementById('theme-icon').textContent = theme === 'dark' ? '🌙' : '☀️';
518
+ document.getElementById('theme-label').textContent = theme === 'dark' ? 'Dark' : 'Light';
519
+ try { localStorage.setItem('roll-slides-theme', theme); } catch (e) {}
520
+ }
521
+ function toggleTheme() {
522
+ const cur = document.documentElement.getAttribute('data-theme') || 'dark';
523
+ applyTheme(cur === 'dark' ? 'light' : 'dark');
524
+ }
525
+
526
+ // Language toggle
527
+ function applyLang(lang) {
528
+ document.documentElement.setAttribute('data-lang', lang);
529
+ document.documentElement.setAttribute('lang', lang === 'zh' ? 'zh-CN' : 'en');
530
+ document.getElementById('lang-label').textContent = lang === 'en' ? 'EN' : '中文';
531
+ try { localStorage.setItem('roll-slides-lang', lang); } catch (e) {}
532
+ }
533
+ function toggleLang() {
534
+ const cur = document.documentElement.getAttribute('data-lang') || 'en';
535
+ applyLang(cur === 'en' ? 'zh' : 'en');
536
+ }
537
+
538
+ // Restore preferences
539
+ try {
540
+ const savedTheme = localStorage.getItem('roll-slides-theme');
541
+ if (savedTheme === 'light' || savedTheme === 'dark') applyTheme(savedTheme);
542
+ const savedLang = localStorage.getItem('roll-slides-lang');
543
+ if (savedLang === 'en' || savedLang === 'zh') applyLang(savedLang);
544
+ else {
545
+ // Auto-detect: prefer ZH if browser is Chinese
546
+ const nav = (navigator.language || '').toLowerCase();
547
+ if (nav.startsWith('zh')) applyLang('zh');
548
+ }
549
+ } catch (e) {}
550
+
551
+ document.addEventListener('keydown', (e) => {
552
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown' || e.key === ' ') { e.preventDefault(); navigate(1); }
553
+ else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); navigate(-1); }
554
+ else if (e.key === 'f' || e.key === 'F') {
555
+ document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen();
556
+ }
557
+ else if (e.key === 't' || e.key === 'T') { toggleTheme(); }
558
+ else if (e.key === 'l' || e.key === 'L') { toggleLang(); }
559
+ });
560
+
561
+ // Touch swipe
562
+ let tx = 0, ty = 0;
563
+ document.addEventListener('touchstart', e => {
564
+ tx = e.touches[0].clientX; ty = e.touches[0].clientY;
565
+ }, { passive: true });
566
+ document.addEventListener('touchend', e => {
567
+ const dx = tx - e.changedTouches[0].clientX;
568
+ const dy = ty - e.changedTouches[0].clientY;
569
+ // Only treat as swipe when horizontal movement dominates and is significant
570
+ if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) navigate(dx > 0 ? 1 : -1);
571
+ }, { passive: true });
572
+
573
+ showSlide(0);
574
+ </script>
575
+ </body>
576
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.522.1",
3
+ "version": "2026.522.2",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "bash tests/run.sh"
@@ -47,6 +47,8 @@ $roll-deck "How TCR keeps us honest"
47
47
 
48
48
  Read in this order, skipping files that don't exist:
49
49
 
50
+ 0. `package.json` — **project identity**: extract `name`, `homepage`, `repository.url`, `author`. These are hard facts that MUST NOT be guessed or inferred from memory.
51
+ `package.json` — **项目身份**: 提取 `name`、`homepage`、`repository.url`、`author`。这些是硬事实,禁止猜测。
50
52
  1. `README.md` — top-level pitch and entry points.
51
53
  2. `AGENTS.md` — communication style, conventions, where to look.
52
54
  3. `.roll/backlog.md` — recently Done Stories and the top of the Todo queue.
@@ -72,8 +74,15 @@ Default count is `18` (or the count the template prescribes). Each slide MUST co
72
74
  - `body_zh` — Chinese body, concise, ≤ 200 chars.
73
75
  - `evidence` — list of `<path>:<line>` references that ground the claim.
74
76
 
75
- **Grounding threshold**: every 3 consecutive slides MUST contain at least 1 evidence citation. If you cannot ground a slide, label it `⚠️ unverified` in the body and explain why in one line.
76
- **Grounding 阈值**:每 3 张 slide 至少 1 条 evidence。无法取证时打 `⚠️ unverified` 并一行说明。
77
+ **Grounding rules** (two tiers):
78
+
79
+ - **Hard facts** (URLs, package names, version numbers, person names, quotes with attribution): MUST cite the source file and line (e.g. `package.json:2`, `README.md:15`). If no source can be found, mark the claim as `⚠️ unverified` — never guess.
80
+ - **Narrative / opinions**: every 3 consecutive slides MUST contain at least 1 evidence citation. If you cannot ground a slide, label it `⚠️ unverified` and explain why in one line.
81
+
82
+ **Grounding 规则**(两级):
83
+
84
+ - **硬事实**(URL、包名、版本号、人名、带归属的引言):MUST 引用源文件及行号(如 `package.json:2`)。找不到来源则标 `⚠️ unverified`——禁止猜测。
85
+ - **叙述/观点**:每 3 张 slide 至少 1 条 evidence。无法取证时打 `⚠️ unverified` 并一行说明。
77
86
 
78
87
  Bilingual rule: English and Chinese MUST be on separate lines in the rendered body — never inline on the same line.
79
88
 
@@ -89,6 +98,10 @@ title_en: "<topic in English>"
89
98
  title_zh: "<topic in Chinese>"
90
99
  total_slides: 18
91
100
  created: <YYYY-MM-DD>
101
+ pkg_name: "<from package.json name>"
102
+ repo_url: "<from package.json repository.url>"
103
+ site_url: "<from package.json homepage>"
104
+ author: "<from package.json author>"
92
105
  ---
93
106
 
94
107
  ## Slide 1
@@ -108,25 +121,20 @@ evidence:
108
121
 
109
122
  `total_slides` MUST match the number of `## Slide N` blocks. The validator (run by `roll slides build`) will reject mismatches.
110
123
 
111
- ### 5. Prompt the user 提示用户
112
-
113
- After writing, print a single bilingual block:
124
+ Project identity fields (`pkg_name`, `repo_url`, `site_url`, `author`) are copied verbatim from `package.json` never infer or guess these values. `roll slides build` injects them into the HTML template (cover, back cover, footer).
125
+ 项目身份字段从 `package.json` 原样复制——禁止推测。`roll slides build` 会把它们注入 HTML 模板(封面、封底、页脚)。
114
126
 
115
- ```
116
- deck.md written → .roll/slides/<slug>/deck.md
117
- deck.md 已写入 → .roll/slides/<slug>/deck.md
118
-
119
- Next: roll slides build <slug>
120
- 下一步:roll slides build <slug>
121
- ```
127
+ ### 5. Stop after writing 写完即停
122
128
 
123
- Do not run `roll slides build` yourself — that is a deliberate human gate.
129
+ Once `deck.md` is written, stop. Do not run `roll slides build` yourself — that
130
+ is a deliberate human gate. The CLI prints the bilingual "Next" hint after the
131
+ agent exits, so the agent should not duplicate it.
124
132
 
125
133
  ## Output format expectations
126
134
 
127
135
  - One file written: `.roll/slides/<slug>/deck.md`.
128
- - Final agent message ends with the bilingual "Next" hint above.
129
136
  - No HTML, no edits to any other file.
137
+ - No "Next" hint in the agent's final message (the CLI prints it).
130
138
 
131
139
  ## Rules
132
140