octocode-cli 1.3.1 → 1.5.1
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.
- package/README.md +193 -34
- package/out/chunks/chunk-7476PETK.js +309 -0
- package/out/chunks/chunk-CVNNNSMQ.js +26 -0
- package/out/chunks/chunk-OQBJTZWK.js +60 -0
- package/out/chunks/chunk-UCZCF3BQ.js +9 -0
- package/out/chunks/command-help-specs-JZXVSLZ5.js +8 -0
- package/out/chunks/commands-XBFPLHSQ.js +8 -0
- package/out/chunks/help-P7TCOYAJ.js +10 -0
- package/out/chunks/main-help-ULF5PAQY.js +10 -0
- package/out/chunks/prompts-5E6VKRX5.js +8 -0
- package/out/chunks/spinner-URV2OX6O.js +8 -0
- package/out/chunks/tool-command-M6VA7P2F.js +8 -0
- package/out/octocode-cli.js +1 -1
- package/package.json +5 -3
- package/skills/README.md +13 -1
- package/skills/agentic-flow-best-practices/SKILL.md +280 -0
- package/skills/agentic-flow-best-practices/references/agent-collaboration-patterns.md +75 -0
- package/skills/agentic-flow-best-practices/references/pr-review-agent-example.md +47 -0
- package/skills/agentic-flow-best-practices/references/resources.md +112 -0
- package/skills/octocode-chrome-devtools/README.md +541 -0
- package/skills/octocode-chrome-devtools/SKILL.md +197 -0
- package/skills/octocode-chrome-devtools/agents/openai.yaml +7 -0
- package/skills/octocode-chrome-devtools/references/CDP_AGENT_REFERENCE.md +401 -0
- package/skills/octocode-chrome-devtools/references/CHROME_FLAGS.md +234 -0
- package/skills/octocode-chrome-devtools/references/INTENTS.md +108 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTH.md +179 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_AUTOMATION.md +214 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_DEBUG.md +329 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_ENVIRONMENT.md +237 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_INSPECT.md +263 -0
- package/skills/octocode-chrome-devtools/references/INTENTS_STORAGE_CONSENT.md +214 -0
- package/skills/octocode-chrome-devtools/references/RECOVERY.md +39 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS.md +43 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_ASYNC_WORKERS.md +345 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_BROWSER.md +403 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_OBSERVE.md +275 -0
- package/skills/octocode-chrome-devtools/references/SCRIPT_PATTERNS_SPECIAL.md +18 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +503 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +123 -0
- package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +81 -0
- package/skills/octocode-chrome-devtools/scripts/octocode-chrome-devtools.vpn.example.json +8 -0
- package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +362 -0
- package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +193 -0
- package/skills/octocode-chrome-devtools/scripts/undercover.mjs +226 -0
- package/skills/octocode-design/README.md +2 -2
- package/skills/octocode-documentation-writer/README.md +1 -1
- package/skills/octocode-engineer/README.md +1 -1
- package/skills/octocode-engineer/SKILL.md +4 -2
- package/skills/octocode-engineer/references/output-files.md +3 -3
- package/skills/octocode-engineer/scripts/run.js +136 -136
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +48 -0
- package/skills/octocode-engineer/src/reporting/summary-md.ts +43 -2
- package/skills/octocode-install/SKILL.md +1 -1
- package/skills/octocode-pull-request-reviewer/README.md +5 -5
- package/skills/octocode-pull-request-reviewer/SKILL.md +1 -1
- package/skills/octocode-research/AGENTS.md +1 -1
- package/skills/octocode-research/README.md +2 -2
- package/skills/octocode-research/docs/ARCHITECTURE.md +1 -1
- package/skills/octocode-research/scripts/server.js +191 -246
- package/skills/octocode-research/src/routes/github.ts +4 -21
- package/skills/octocode-research/src/routes/local.ts +4 -21
- package/skills/octocode-research/src/utils/fileContentTransform.ts +44 -0
- package/skills/octocode-search-skill/SKILL.md +245 -229
- package/skills/octocode-search-skill/references/agent-skills-guide.md +177 -0
- package/skills/octocode-search-skill/references/discovery-surfaces.md +162 -0
- package/skills/octocode-search-skill/references/fetch-and-create-locally.md +57 -0
- package/skills/octocode-search-skill/references/install-reference.md +130 -0
- package/skills/octocode-search-skill/references/references-template.md +27 -0
- package/skills/octocode-search-skill/references/references.md +62 -0
- package/skills/octocode-slides/README.md +307 -0
- package/skills/octocode-slides/SKILL.md +410 -0
- package/skills/octocode-slides/references/01-brief.md +156 -0
- package/skills/octocode-slides/references/02-research.md +149 -0
- package/skills/octocode-slides/references/03-outline.md +172 -0
- package/skills/octocode-slides/references/04-design.md +301 -0
- package/skills/octocode-slides/references/05-implementation.md +213 -0
- package/skills/octocode-slides/references/06-review.md +258 -0
- package/skills/octocode-slides/references/animation.md +281 -0
- package/skills/octocode-slides/references/design-system.md +316 -0
- package/skills/octocode-slides/references/html-templates.md +673 -0
- package/skills/octocode-slides/references/image-generation.md +448 -0
- package/skills/octocode-slides/references/resources.md +840 -0
- package/skills/octocode-slides/references/slide-rules.md +541 -0
- package/skills/octocode-slides/references/wireframes.md +727 -0
- package/skills/octocode-slides/scripts/animation.js +182 -0
- package/skills/octocode-slides/scripts/base.css +353 -0
- package/skills/octocode-slides/scripts/base.html +655 -0
- package/skills/octocode-slides/scripts/generate_image.py +221 -0
- package/skills/octocode-slides/scripts/navbridge.js +79 -0
- package/skills/octocode-slides/scripts/presenter.js +316 -0
- package/skills/octocode-slides/scripts/slide.html +248 -0
- package/skills/octocode-stats/SKILL.md +73 -0
- package/skills/octocode-stats/assets/template.html +1332 -0
- package/skills/octocode-stats/scripts/build_dashboard.mjs +407 -0
- package/assets/example.png +0 -0
- package/out/chunks/chunk-QCY7Q7YW.js +0 -389
- package/out/chunks/command-help-specs-CQ3RBLP6.js +0 -8
- package/out/chunks/commands-OCTZP2TO.js +0 -51
- package/out/chunks/help-XPXP46ZT.js +0 -10
- package/out/chunks/main-help-35HX2UDQ.js +0 -10
- package/out/chunks/tool-command-HOSMVLNK.js +0 -8
- package/skills/octocode-search-skill/INSTALL_REFERENCE.md +0 -112
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
# HTML Templates Reference — Octocode Slides
|
|
2
|
+
|
|
3
|
+
Read during Phase 5 implementation, and consult during Phase 4 when a design choice depends on available layout patterns.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Canonical slide skeleton
|
|
8
|
+
|
|
9
|
+
A slide can use up to **four regions** in this order. Only `.slide-content` is required — every other region is opt-in per slide. The skeleton is a contract for **where things go when present**, not a recipe forcing every slide into the same shape.
|
|
10
|
+
|
|
11
|
+
| Region | Class | Optional? | Purpose |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| Logo | `.slide-logo` | Optional | Brand mark, anchored top-right (absolute) |
|
|
14
|
+
| Header | `.slide-header` | Optional | `.title` + `.description` (muted subtitle) |
|
|
15
|
+
| Content | `.slide-content` | **Required** | Smart flex body — bullets, grid, chart, image, etc. |
|
|
16
|
+
| Footer | `.slide-footer` | Optional | Source line, page number, link, attribution |
|
|
17
|
+
|
|
18
|
+
**Use what serves the slide; omit the rest.** A title hero may only need `.slide-content`. A data slide may use all four. Variety across slides is good — what matters is that *when* a region appears, it sits in the same place and uses the same class.
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<div class="slide slide--{{TYPE}}">
|
|
22
|
+
<!-- optional: brand mark, top-right -->
|
|
23
|
+
<header class="slide-logo">
|
|
24
|
+
<img src="../assets/logo.svg" alt="{{Brand}}">
|
|
25
|
+
</header>
|
|
26
|
+
|
|
27
|
+
<!-- optional: title + description -->
|
|
28
|
+
<header class="slide-header">
|
|
29
|
+
<h2 class="title">{{Slide title — claim sentence}}</h2>
|
|
30
|
+
<p class="description">{{One-line description (optional)}}</p>
|
|
31
|
+
</header>
|
|
32
|
+
|
|
33
|
+
<!-- required: the body. Smart flex column by default. -->
|
|
34
|
+
<main class="slide-content">
|
|
35
|
+
{{body markup — see per-type templates below}}
|
|
36
|
+
</main>
|
|
37
|
+
|
|
38
|
+
<!-- optional: source / context / page / link -->
|
|
39
|
+
<footer class="slide-footer">
|
|
40
|
+
<span>{{Source or context}}</span>
|
|
41
|
+
<span>{{Page or link}}</span>
|
|
42
|
+
</footer>
|
|
43
|
+
</div>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`.slide-content` defaults to `display: flex; flex-direction: column; flex: 1; gap: var(--sp-3)`. Use these modifier classes when the default isn't right:
|
|
47
|
+
|
|
48
|
+
| Modifier | Effect | Use for |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `slide-content--center` | Center along both axes + text-align center | Single number, single sentence, hero block |
|
|
51
|
+
| `slide-content--middle` | Center vertically only | Tall body that should sit in the middle |
|
|
52
|
+
| `slide-content--row` | Switch to flex-row, align stretched | Text + image side-by-side |
|
|
53
|
+
| `slide-content--grid-2` | Two equal columns | Comparisons, two-up cards |
|
|
54
|
+
| `slide-content--grid-3` | Three equal columns | KPI grid, three icons + labels |
|
|
55
|
+
|
|
56
|
+
Centered slide types (`title`, `section`, `quote`, `closing`) automatically center the entire stack — no modifier needed.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Naming convention — classes and IDs
|
|
61
|
+
|
|
62
|
+
Every element targeted by animation, JS control, or external overrides must have a precise, unambiguous selector. Vague class names collide across slides when scripts run in `index.html` context or when a future deck reuses a component.
|
|
63
|
+
|
|
64
|
+
### Class rules for repetitive components
|
|
65
|
+
|
|
66
|
+
| Pattern | Use | Example |
|
|
67
|
+
|---------|-----|---------|
|
|
68
|
+
| `{component}-list` | Wrapping container for a group | `bullets`, `agenda-list`, `stat-grid`, `timeline` |
|
|
69
|
+
| `{component}-item` | Individual member of a list | `stat-item`, `tl-item`, `bar-row` |
|
|
70
|
+
| `{component}-{part}` | Named sub-part of an item | `stat-value`, `stat-label`, `tl-dot`, `tl-label`, `bar-label` |
|
|
71
|
+
| `{context}-col {context}-{role}` | Layout column scoped to a slide type | `cmp-col cmp-before` · `col two-col-left` · `col two-col-right` |
|
|
72
|
+
| `{slide-type}-{role}` | Unique element within one slide type | `closing-links`, `image-caption`, `chart-insight`, `code-caption` |
|
|
73
|
+
|
|
74
|
+
**Never use bare, context-free class names** — `col`, `item`, `panel`, `row` alone — as the sole identifier. Always pair them with a context qualifier (e.g. `col two-col-left`) so animation selectors stay unambiguous across a multi-slide deck.
|
|
75
|
+
|
|
76
|
+
### ID rules
|
|
77
|
+
|
|
78
|
+
Assign `id` only when:
|
|
79
|
+
|
|
80
|
+
- An animation counter needs a direct DOM target (`document.getElementById(...)` in `onUpdate`)
|
|
81
|
+
- A chart or diagram library initializes by element ID
|
|
82
|
+
- JS needs a reliable, unique anchor (focus management, presenter note jump target)
|
|
83
|
+
|
|
84
|
+
**Format: `{slide-slug}-{role}`** — e.g. `id="stats-kpi-1"`, `id="perf-chart"`, `id="timeline-root"`.
|
|
85
|
+
|
|
86
|
+
Never use bare numeric IDs (`id="1"`) or generic IDs (`id="chart"`, `id="kpi"`). They collide when a slide is scripted from the parent `index.html` or reused across decks.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Slide file shell
|
|
91
|
+
|
|
92
|
+
Every `slides/slug.html` uses this shell. Slides fill the iframe 100%×100% — the stage in `index.html` handles all scaling. Add CDN `<link>` / `<script>` tags inside `<head>` only when the slide needs them.
|
|
93
|
+
|
|
94
|
+
**`js/navbridge.js` is required in every slide.** It propagates arrow-key events from the iframe back to the parent navigation controller via `postMessage`, so keyboard navigation keeps working after the user clicks inside a slide. The `<script>` tag is already included at the end of `scripts/slide.html` — do not remove it.
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!DOCTYPE html>
|
|
98
|
+
<html lang="en">
|
|
99
|
+
<head>
|
|
100
|
+
<meta charset="UTF-8">
|
|
101
|
+
<title>{{Slide Title}}</title>
|
|
102
|
+
<link rel="stylesheet" href="../css/base.css">
|
|
103
|
+
<link rel="stylesheet" href="../css/theme.css">
|
|
104
|
+
<!-- Optional: add CDN libraries only when the slide needs them -->
|
|
105
|
+
</head>
|
|
106
|
+
<body>
|
|
107
|
+
<div class="slide slide--{{TYPE}}">
|
|
108
|
+
<!-- Up to four regions: .slide-logo, .slide-header, .slide-content, .slide-footer -->
|
|
109
|
+
<!-- Only .slide-content is required. -->
|
|
110
|
+
</div>
|
|
111
|
+
<aside class="speaker-notes">{{Speaker notes}}</aside>
|
|
112
|
+
<script src="../js/navbridge.js"></script>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Slide type templates
|
|
120
|
+
|
|
121
|
+
All templates below use the **canonical 4-region skeleton**. Logo and footer regions are shown commented-out: include them when the deck calls for them and stay consistent about it. Description lines are shown as `{{Description (optional)}}` — fill them when the title needs reinforcement, leave the line out when it doesn't. Don't pad slides with descriptions just because the slot exists.
|
|
122
|
+
|
|
123
|
+
### title
|
|
124
|
+
|
|
125
|
+
Centered hero. The deck's first impression. `.slide-header` carries the display heading; `.slide-content` carries supporting copy.
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<div class="slide slide--title">
|
|
129
|
+
<!-- <header class="slide-logo"><img src="../assets/logo.svg" alt="Brand"></header> -->
|
|
130
|
+
<header class="slide-header">
|
|
131
|
+
<p class="eyebrow fade-in">{{Event or tagline}}</p>
|
|
132
|
+
<h1 class="display slide-up">{{Main Title}}</h1>
|
|
133
|
+
<p class="description slide-up delay-1">{{Subtitle / description}}</p>
|
|
134
|
+
</header>
|
|
135
|
+
<main class="slide-content">
|
|
136
|
+
<p class="meta fade-in delay-2">{{Author}} · {{Date}}</p>
|
|
137
|
+
</main>
|
|
138
|
+
</div>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### agenda
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<div class="slide slide--agenda">
|
|
145
|
+
<header class="slide-header">
|
|
146
|
+
<h2 class="title fade-in">{{Agenda}}</h2>
|
|
147
|
+
<!-- optional: <p class="description">{{One-line promise}}</p> -->
|
|
148
|
+
</header>
|
|
149
|
+
<main class="slide-content">
|
|
150
|
+
<ol class="agenda-list">
|
|
151
|
+
<li class="slide-up delay-1"><span class="num">01</span>{{Topic One}}</li>
|
|
152
|
+
<li class="slide-up delay-2"><span class="num">02</span>{{Topic Two}}</li>
|
|
153
|
+
<li class="slide-up delay-3"><span class="num">03</span>{{Topic Three}}</li>
|
|
154
|
+
</ol>
|
|
155
|
+
</main>
|
|
156
|
+
</div>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### section-header
|
|
160
|
+
|
|
161
|
+
```html
|
|
162
|
+
<div class="slide slide--section">
|
|
163
|
+
<header class="slide-header">
|
|
164
|
+
<p class="section-num fade-in">{{01}}</p>
|
|
165
|
+
<h2 class="display slide-up">{{Section Title}}</h2>
|
|
166
|
+
<p class="description slide-up delay-1">{{One supporting line}}</p>
|
|
167
|
+
</header>
|
|
168
|
+
</div>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`.slide-header` is the only region used; the centered slide type vertically centers it.
|
|
172
|
+
|
|
173
|
+
### content
|
|
174
|
+
|
|
175
|
+
Header above, smart-flex body below.
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<div class="slide slide--content">
|
|
179
|
+
<header class="slide-header">
|
|
180
|
+
<h2 class="title fade-in">{{Action title}}</h2>
|
|
181
|
+
<!-- optional: <p class="description">{{Why this matters}}</p> -->
|
|
182
|
+
</header>
|
|
183
|
+
<main class="slide-content">
|
|
184
|
+
<ul class="bullets">
|
|
185
|
+
<li class="slide-up delay-1">{{Point one — max 12 words}}</li>
|
|
186
|
+
<li class="slide-up delay-2">{{Point two}}</li>
|
|
187
|
+
<li class="slide-up delay-3">{{Point three}}</li>
|
|
188
|
+
<!-- max 4 bullets -->
|
|
189
|
+
</ul>
|
|
190
|
+
</main>
|
|
191
|
+
</div>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### two-column
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<div class="slide slide--two-col">
|
|
198
|
+
<header class="slide-header">
|
|
199
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
200
|
+
</header>
|
|
201
|
+
<main class="slide-content slide-content--grid-2">
|
|
202
|
+
<!-- `col` keeps base.css styling; `two-col-left` gives animation/JS a precise target -->
|
|
203
|
+
<div class="col two-col-left slide-up delay-1">
|
|
204
|
+
<h3 class="col-heading two-col-heading">{{Left heading}}</h3>
|
|
205
|
+
<ul class="two-col-bullets"><li>{{Point}}</li><li>{{Point}}</li></ul>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="col two-col-right slide-up delay-2">
|
|
208
|
+
<h3 class="col-heading two-col-heading">{{Right heading}}</h3>
|
|
209
|
+
<ul class="two-col-bullets"><li>{{Point}}</li><li>{{Point}}</li></ul>
|
|
210
|
+
</div>
|
|
211
|
+
</main>
|
|
212
|
+
</div>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### quote
|
|
216
|
+
|
|
217
|
+
```html
|
|
218
|
+
<div class="slide slide--quote">
|
|
219
|
+
<main class="slide-content">
|
|
220
|
+
<p class="quote-mark fade-in" aria-hidden="true">"</p>
|
|
221
|
+
<blockquote class="quote-text slide-up">{{Quote — max 30 words}}</blockquote>
|
|
222
|
+
<cite class="quote-attr fade-in delay-2">— {{Name, Title}}</cite>
|
|
223
|
+
</main>
|
|
224
|
+
</div>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### code (with highlight.js)
|
|
228
|
+
|
|
229
|
+
```html
|
|
230
|
+
<!DOCTYPE html>
|
|
231
|
+
<html lang="en">
|
|
232
|
+
<head>
|
|
233
|
+
<meta charset="UTF-8">
|
|
234
|
+
<title>{{Slide Title}}</title>
|
|
235
|
+
<link rel="stylesheet" href="../css/base.css">
|
|
236
|
+
<link rel="stylesheet" href="../css/theme.css">
|
|
237
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github-dark.min.css">
|
|
238
|
+
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>
|
|
239
|
+
</head>
|
|
240
|
+
<body>
|
|
241
|
+
<div class="slide slide--code">
|
|
242
|
+
<header class="slide-header">
|
|
243
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
244
|
+
</header>
|
|
245
|
+
<main class="slide-content">
|
|
246
|
+
<pre class="code-block slide-up delay-1"><code class="language-{{js|python|typescript|bash|go}}">{{
|
|
247
|
+
// code here — max 20 lines
|
|
248
|
+
}}</code></pre>
|
|
249
|
+
<p class="code-caption fade-in delay-2">{{One-line insight}}</p>
|
|
250
|
+
</main>
|
|
251
|
+
</div>
|
|
252
|
+
<aside class="speaker-notes">{{Speaker notes}}</aside>
|
|
253
|
+
<script>hljs.highlightAll();</script>
|
|
254
|
+
<script src="../js/navbridge.js"></script>
|
|
255
|
+
</body>
|
|
256
|
+
</html>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### chart (HTML+CSS bar chart — no JS library)
|
|
260
|
+
|
|
261
|
+
```html
|
|
262
|
+
<div class="slide slide--chart">
|
|
263
|
+
<header class="slide-header">
|
|
264
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
265
|
+
</header>
|
|
266
|
+
<main class="slide-content">
|
|
267
|
+
<div class="bar-chart slide-up delay-1" role="img" aria-label="{{description}}">
|
|
268
|
+
<div class="bar-row">
|
|
269
|
+
<span class="bar-label">{{Label A}}</span>
|
|
270
|
+
<div class="bar" style="--pct:80%"><span>{{Value}}</span></div>
|
|
271
|
+
</div>
|
|
272
|
+
<div class="bar-row">
|
|
273
|
+
<span class="bar-label">{{Label B}}</span>
|
|
274
|
+
<div class="bar" style="--pct:55%"><span>{{Value}}</span></div>
|
|
275
|
+
</div>
|
|
276
|
+
<div class="bar-row">
|
|
277
|
+
<span class="bar-label">{{Label C}}</span>
|
|
278
|
+
<div class="bar" style="--pct:35%"><span>{{Value}}</span></div>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
<p class="chart-insight fade-in delay-3">{{Key takeaway}}</p>
|
|
282
|
+
</main>
|
|
283
|
+
</div>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### timeline
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<div class="slide slide--timeline">
|
|
290
|
+
<header class="slide-header">
|
|
291
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
292
|
+
</header>
|
|
293
|
+
<main class="slide-content">
|
|
294
|
+
<ol class="timeline">
|
|
295
|
+
<li class="tl-item slide-up delay-1">
|
|
296
|
+
<span class="tl-dot"></span>
|
|
297
|
+
<div><strong class="tl-label">{{2022 / Q1}}</strong> {{Description}}</div>
|
|
298
|
+
</li>
|
|
299
|
+
<li class="tl-item slide-up delay-2"><!-- repeat --></li>
|
|
300
|
+
</ol>
|
|
301
|
+
</main>
|
|
302
|
+
</div>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### comparison
|
|
306
|
+
|
|
307
|
+
```html
|
|
308
|
+
<div class="slide slide--comparison">
|
|
309
|
+
<header class="slide-header">
|
|
310
|
+
<h2 class="title fade-in">{{Before / After}}</h2>
|
|
311
|
+
</header>
|
|
312
|
+
<main class="slide-content">
|
|
313
|
+
<div class="comparison">
|
|
314
|
+
<div class="cmp-col cmp-before slide-up delay-1">
|
|
315
|
+
<h3>{{Before}}</h3>
|
|
316
|
+
<ul><li>{{Point}}</li><li>{{Point}}</li></ul>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="cmp-divider fade-in delay-2"></div>
|
|
319
|
+
<div class="cmp-col cmp-after slide-up delay-3">
|
|
320
|
+
<h3>{{After}}</h3>
|
|
321
|
+
<ul><li>{{Point}}</li><li>{{Point}}</li></ul>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
</main>
|
|
325
|
+
</div>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### closing
|
|
329
|
+
|
|
330
|
+
```html
|
|
331
|
+
<div class="slide slide--closing">
|
|
332
|
+
<header class="slide-header">
|
|
333
|
+
<h2 class="display slide-up">{{Thank You / Key Takeaway}}</h2>
|
|
334
|
+
<p class="description fade-in delay-1">{{Call to action}}</p>
|
|
335
|
+
</header>
|
|
336
|
+
<main class="slide-content">
|
|
337
|
+
<div class="closing-links fade-in delay-2">
|
|
338
|
+
<a href="{{URL}}">{{Link text}}</a>
|
|
339
|
+
</div>
|
|
340
|
+
</main>
|
|
341
|
+
</div>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### stats (KPI / big numbers)
|
|
345
|
+
|
|
346
|
+
Use for 1–3 key metrics. Add a Motion counter animation when numbers should count up.
|
|
347
|
+
|
|
348
|
+
```html
|
|
349
|
+
<div class="slide slide--stats">
|
|
350
|
+
<header class="slide-header">
|
|
351
|
+
<h2 class="title fade-in">{{Heading — claim sentence}}</h2>
|
|
352
|
+
</header>
|
|
353
|
+
<main class="slide-content">
|
|
354
|
+
<div class="stat-grid">
|
|
355
|
+
<!-- id="{slide-slug}-kpi-{n}" — required for animation counter targeting -->
|
|
356
|
+
<div class="stat-item pop-in delay-1">
|
|
357
|
+
<span class="stat-value" id="{{slide-slug}}-kpi-1">{{Number or symbol}}</span>
|
|
358
|
+
<span class="stat-label">{{Label}}</span>
|
|
359
|
+
</div>
|
|
360
|
+
<div class="stat-item pop-in delay-2">
|
|
361
|
+
<span class="stat-value" id="{{slide-slug}}-kpi-2">{{Number or symbol}}</span>
|
|
362
|
+
<span class="stat-label">{{Label}}</span>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="stat-item pop-in delay-3">
|
|
365
|
+
<span class="stat-value" id="{{slide-slug}}-kpi-3">{{Number or symbol}}</span>
|
|
366
|
+
<span class="stat-label">{{Label}}</span>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
<p class="stat-caption fade-in delay-4">{{Source or context}}</p>
|
|
370
|
+
</main>
|
|
371
|
+
</div>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### image (full-bleed background)
|
|
375
|
+
|
|
376
|
+
Full-bleed slides are special: they bypass the standard regions and use absolute layering.
|
|
377
|
+
|
|
378
|
+
```html
|
|
379
|
+
<div class="slide slide--image">
|
|
380
|
+
<img src="{{path/to/image.jpg}}" alt="{{Descriptive alt text}}">
|
|
381
|
+
<div class="image-overlay" aria-hidden="true"></div>
|
|
382
|
+
<div class="image-caption slide-up delay-1">
|
|
383
|
+
<p class="display">{{Short headline}}</p>
|
|
384
|
+
<p class="description">{{Supporting line}}</p>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### image-placeholder (inline — user will provide image later)
|
|
390
|
+
|
|
391
|
+
Use for `content`, `two-col`, or `chart` slides that need an inline image the user hasn't provided yet. Replace `.image-ph` with `<img>` once the file is available.
|
|
392
|
+
|
|
393
|
+
```html
|
|
394
|
+
<div class="slide slide--content">
|
|
395
|
+
<header class="slide-header">
|
|
396
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
397
|
+
</header>
|
|
398
|
+
<main class="slide-content">
|
|
399
|
+
<!-- IMAGE PLACEHOLDER — replace with <img src="PATH" alt="ALT"> when provided -->
|
|
400
|
+
<div class="image-ph slide-up delay-1" data-expected="{{image description}}">
|
|
401
|
+
<div class="image-ph-inner">
|
|
402
|
+
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true" style="opacity:0.5">
|
|
403
|
+
<rect x="1" y="1" width="42" height="42" rx="5" stroke="currentColor" stroke-width="1.5" stroke-dasharray="5 3"/>
|
|
404
|
+
<circle cx="16" cy="17" r="4" stroke="currentColor" stroke-width="1.5"/>
|
|
405
|
+
<path d="M3 33l10-8 7 6 7-7 14 9" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
|
406
|
+
</svg>
|
|
407
|
+
<p class="image-ph-label">PLACEHOLDER: {{image description}}</p>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</main>
|
|
411
|
+
</div>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### image-placeholder (full-bleed — for slide--image type when image not yet provided)
|
|
415
|
+
|
|
416
|
+
```html
|
|
417
|
+
<div class="slide slide--image">
|
|
418
|
+
<div class="image-ph-bleed" aria-label="Image placeholder: {{image description}}">
|
|
419
|
+
[ PLACEHOLDER: {{image description}} ]
|
|
420
|
+
</div>
|
|
421
|
+
<div class="image-overlay" aria-hidden="true"></div>
|
|
422
|
+
<div class="image-caption slide-up delay-1">
|
|
423
|
+
<p class="display">{{Short headline}}</p>
|
|
424
|
+
<p class="description">{{Supporting line}}</p>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### markdown-content (renders .md source via marked.js)
|
|
430
|
+
|
|
431
|
+
```html
|
|
432
|
+
<!DOCTYPE html>
|
|
433
|
+
<html lang="en">
|
|
434
|
+
<head>
|
|
435
|
+
<meta charset="UTF-8">
|
|
436
|
+
<title>{{Slide Title}}</title>
|
|
437
|
+
<link rel="stylesheet" href="../css/base.css">
|
|
438
|
+
<link rel="stylesheet" href="../css/theme.css">
|
|
439
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
|
|
440
|
+
<style>
|
|
441
|
+
.md-body h1, .md-body h2 { font-family: var(--font-head); color: var(--accent); margin-bottom: var(--sp-3); }
|
|
442
|
+
.md-body h1 { font-size: var(--t-title); }
|
|
443
|
+
.md-body h2 { font-size: var(--t-sub); }
|
|
444
|
+
.md-body p { font-size: var(--t-body); color: var(--text); margin-bottom: var(--sp-2); }
|
|
445
|
+
.md-body ul, .md-body ol { padding-left: var(--sp-4); font-size: var(--t-body); }
|
|
446
|
+
.md-body li { margin-bottom: var(--sp-1); }
|
|
447
|
+
.md-body code { font-family: var(--font-mono); font-size: var(--t-code); background: var(--code-bg); padding: 0.1em 0.4em; border-radius: var(--r-sm); }
|
|
448
|
+
.md-body blockquote { border-left: 3px solid var(--accent); padding-left: var(--sp-3); color: var(--muted); font-style: italic; }
|
|
449
|
+
.md-body strong { color: var(--accent); font-weight: 700; }
|
|
450
|
+
</style>
|
|
451
|
+
</head>
|
|
452
|
+
<body>
|
|
453
|
+
<div class="slide slide--content">
|
|
454
|
+
<header class="slide-header">
|
|
455
|
+
<h2 class="title fade-in">{{Heading}}</h2>
|
|
456
|
+
</header>
|
|
457
|
+
<main class="slide-content">
|
|
458
|
+
<div class="md-body slide-up delay-1" data-md>
|
|
459
|
+
{{Paste raw Markdown content here — agent fills this from source files}}
|
|
460
|
+
</div>
|
|
461
|
+
</main>
|
|
462
|
+
</div>
|
|
463
|
+
<aside class="speaker-notes">{{Speaker notes}}</aside>
|
|
464
|
+
<script>
|
|
465
|
+
marked.setOptions({ breaks: true, gfm: true });
|
|
466
|
+
document.querySelectorAll('[data-md]').forEach(el => {
|
|
467
|
+
el.innerHTML = marked.parse(el.textContent.trim());
|
|
468
|
+
});
|
|
469
|
+
</script>
|
|
470
|
+
<script src="../js/navbridge.js"></script>
|
|
471
|
+
</body>
|
|
472
|
+
</html>
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Region usage — guidelines, not rules
|
|
478
|
+
|
|
479
|
+
- **Logo / footer rhythm is usually deck-wide, but exceptions are fine.** Pick a posture in Phase 4 (always-on, always-off, or "everywhere except hero/section breaks") and stick to it. Don't sprinkle them onto random slides — that creates noise rather than rhythm.
|
|
480
|
+
- **Header is per-slide.** Use `.display` for hero/section/closing slides, `.title` for content/code/chart/etc. If the slide's *whole point* is the body — a single quote, one giant number, a full-bleed image — omit the header.
|
|
481
|
+
- **Description is optional.** Reach for it when the title needs a "why this matters" line. Skip it when the title already lands.
|
|
482
|
+
- **Footer carries facts about the slide, not the message of the slide.** Sources, page numbers, links, attribution — yes. Extra bullets or claims — no.
|
|
483
|
+
- **The four classes are how you stay consistent. The *content* of each region is up to the slide.** Two slides using `.slide-content--grid-2` can look completely different — different visuals, different rhythm, different focus. Same skeleton, different shape.
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Motion animation patterns
|
|
488
|
+
|
|
489
|
+
Use these `<script type="module">` blocks at the bottom of a slide's `<body>`. They replace or augment the CSS animation classes from `base.css`.
|
|
490
|
+
|
|
491
|
+
### Staggered entrance (lists, bullets, cards)
|
|
492
|
+
```html
|
|
493
|
+
<script type="module">
|
|
494
|
+
import { animate, stagger } from "https://cdn.jsdelivr.net/npm/motion@latest/+esm";
|
|
495
|
+
animate(
|
|
496
|
+
'.bullets li, .agenda-list li, .stat-item, .tl-item',
|
|
497
|
+
{ opacity: [0, 1], y: [16, 0] },
|
|
498
|
+
{ delay: stagger(0.12), duration: 0.45, easing: [0.22, 1, 0.36, 1] }
|
|
499
|
+
);
|
|
500
|
+
</script>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Title + description sequence
|
|
504
|
+
```html
|
|
505
|
+
<script type="module">
|
|
506
|
+
import { timeline } from "https://cdn.jsdelivr.net/npm/motion@latest/+esm";
|
|
507
|
+
timeline([
|
|
508
|
+
['.display', { opacity: [0, 1], y: [-14, 0] }, { duration: 0.5 }],
|
|
509
|
+
['.description', { opacity: [0, 1] }, { duration: 0.35, at: '+0.1' }],
|
|
510
|
+
['.meta', { opacity: [0, 1] }, { duration: 0.3, at: '+0.08' }],
|
|
511
|
+
]);
|
|
512
|
+
</script>
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Animated counter (stat / KPI slides)
|
|
516
|
+
```html
|
|
517
|
+
<script type="module">
|
|
518
|
+
import { animate } from "https://cdn.jsdelivr.net/npm/motion@latest/+esm";
|
|
519
|
+
// Use the slide-slug-scoped ID from the stats template (e.g. 'results-kpi-1')
|
|
520
|
+
animate(0, {{TARGET_NUMBER}}, {
|
|
521
|
+
duration: 1.4,
|
|
522
|
+
easing: [0.22, 1, 0.36, 1],
|
|
523
|
+
onUpdate(v) {
|
|
524
|
+
document.getElementById('{{slide-slug}}-kpi-1').textContent =
|
|
525
|
+
Math.round(v).toLocaleString();
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
</script>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Bar chart fill animation (augments CSS .bar::after)
|
|
532
|
+
```html
|
|
533
|
+
<script type="module">
|
|
534
|
+
import { animate, stagger } from "https://cdn.jsdelivr.net/npm/motion@latest/+esm";
|
|
535
|
+
document.querySelectorAll('.bar').forEach((bar, i) => {
|
|
536
|
+
const target = bar.style.getPropertyValue('--pct');
|
|
537
|
+
bar.style.setProperty('--pct', '0%');
|
|
538
|
+
setTimeout(() => {
|
|
539
|
+
animate(0, parseFloat(target), {
|
|
540
|
+
duration: 0.8,
|
|
541
|
+
delay: i * 0.1,
|
|
542
|
+
easing: [0.22, 1, 0.36, 1],
|
|
543
|
+
onUpdate(v) { bar.style.setProperty('--pct', v + '%'); }
|
|
544
|
+
});
|
|
545
|
+
}, 200);
|
|
546
|
+
});
|
|
547
|
+
</script>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### In-view reveal (for timelines / diagrams that still fit on one slide)
|
|
551
|
+
```html
|
|
552
|
+
<script type="module">
|
|
553
|
+
import { animate, inView } from "https://cdn.jsdelivr.net/npm/motion@latest/+esm";
|
|
554
|
+
inView('.tl-item', ({ target }) => {
|
|
555
|
+
animate(target, { opacity: [0, 1], x: [-20, 0] }, { duration: 0.4 });
|
|
556
|
+
}, { margin: '-10% 0px' });
|
|
557
|
+
</script>
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Rules:**
|
|
561
|
+
- Motion loads from ESM CDN — always `type="module"`.
|
|
562
|
+
- Prefer `timeline()` for multi-step sequences; `animate()` + `stagger()` for lists.
|
|
563
|
+
- Don't combine Motion entrance animations with CSS `.fade-in`/`.slide-up` on the same element — pick one.
|
|
564
|
+
- Respect `prefers-reduced-motion` — wrap Motion calls in `if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches)`.
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
## `css/base.css` source
|
|
570
|
+
|
|
571
|
+
**Copy `scripts/base.css` verbatim to `css/base.css` at the deck root.** Do not paraphrase, do not regenerate from memory — `scripts/base.css` is the canonical source of truth for layout primitives, type scale, slide-type rules, component classes, animations, and the print/PDF media query.
|
|
572
|
+
|
|
573
|
+
Override the override surface in `css/theme.css` only — colors, fonts, and the type-scale `clamp()` ranges if the design calls for it. Never edit `css/base.css` after the copy.
|
|
574
|
+
|
|
575
|
+
**The override surface (defined in `:root` in `scripts/base.css`):**
|
|
576
|
+
|
|
577
|
+
```css
|
|
578
|
+
/* css/theme.css overrides any of these; do not redefine layout rules. */
|
|
579
|
+
:root {
|
|
580
|
+
/* Colors */
|
|
581
|
+
--bg --surface --border --accent --text --muted --code-bg --code-text
|
|
582
|
+
/* Fonts */
|
|
583
|
+
--font-head --font-body --font-mono
|
|
584
|
+
/* Type scale (clamp ranges) */
|
|
585
|
+
--t-display --t-title --t-sub --t-body --t-small --t-code
|
|
586
|
+
/* Spacing (8px grid) */
|
|
587
|
+
--sp-1 --sp-2 --sp-3 --sp-4 --sp-6 --sp-8 --pad
|
|
588
|
+
/* Radius */
|
|
589
|
+
--r-sm --r-md --r-lg
|
|
590
|
+
/* Motion */
|
|
591
|
+
--fast --base --slow --ease
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**What `scripts/base.css` provides (do not duplicate elsewhere):**
|
|
596
|
+
- Reset + slide canvas (1280×720, `overflow: hidden`)
|
|
597
|
+
- Canonical 4-region skeleton (`.slide-logo`, `.slide-header`, `.slide-content`, `.slide-footer`)
|
|
598
|
+
- Smart-flex modifiers (`--center`, `--middle`, `--row`, `--grid-2`, `--grid-3`)
|
|
599
|
+
- Typography classes (`.display`, `.title`, `.description`, `.subtitle`, `.eyebrow`, `.meta`)
|
|
600
|
+
- Slide-type rules (`.slide--title`, `.slide--section`, `.slide--quote`, `.slide--closing`, `.slide--stats`, `.slide--agenda`, `.slide--image`)
|
|
601
|
+
- Component classes (`.bullets`, `.agenda-list`, `.code-block`, `.bar-chart`, `.timeline`, `.comparison`, `.stat-grid`, `.image-ph`, `.image-ph-bleed`, `.image-overlay`, `.image-caption`)
|
|
602
|
+
- Animations (`.fade-in`, `.slide-up`, `.pop-in`, `.delay-1` … `.delay-4`) + `prefers-reduced-motion`
|
|
603
|
+
- `@media print` for 1280×720 PDF export
|
|
604
|
+
|
|
605
|
+
If a class is missing for a one-off layout, add it to the slide's local `<style>` — do not edit `scripts/base.css` or `css/base.css` after the copy.
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## `index.html` — navigation controller
|
|
610
|
+
|
|
611
|
+
**Always copy from `scripts/base.html`** — it is the canonical source of truth. The condensed reference below shows the key patterns; use the full template for actual implementation.
|
|
612
|
+
|
|
613
|
+
### Slide manifest format
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
const slides = [
|
|
617
|
+
// Each entry: { path, hidden, name }
|
|
618
|
+
// path – slide HTML file relative to index.html
|
|
619
|
+
// name – unique slug for URL hash (e.g. 'problem' → #problem)
|
|
620
|
+
// Do NOT use numbers — playback order is controlled by this array.
|
|
621
|
+
// hidden – true = skip during playback AND hide from overview grid
|
|
622
|
+
{ path: 'slides/title.html', hidden: false, name: 'title' },
|
|
623
|
+
{ path: 'slides/problem.html', hidden: false, name: 'problem' },
|
|
624
|
+
{ path: 'slides/solution.html', hidden: false, name: 'solution' },
|
|
625
|
+
{ path: 'slides/closing.html', hidden: false, name: 'closing' },
|
|
626
|
+
];
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Key rules for the manifest:
|
|
630
|
+
- **Filename numbers do NOT control order** — the array position does. Files can be named `slides/title.html` without a numeric prefix.
|
|
631
|
+
- **`name` must be unique** across all visible (non-hidden) slides.
|
|
632
|
+
- **`hidden: true`** skips the slide during playback and hides it from the overview grid, but keeps the file in the deck (useful for draft slides or extended-edition content).
|
|
633
|
+
- **Name-based hash navigation**: `#problem` jumps to the slide where `name === 'problem'`. Legacy numeric hashes (`#5`) still work for backwards compatibility.
|
|
634
|
+
|
|
635
|
+
### Navbridge integration
|
|
636
|
+
|
|
637
|
+
The parent `index.html` uses a **single `handleKey()` function** as the sole navigation handler:
|
|
638
|
+
|
|
639
|
+
- When the **parent window** has focus → `document.addEventListener('keydown', handleKey, true)` fires directly. Step-aware nav keys (`→`, `↓`, Space, `←`, `↑`) are first posted into the active iframe as `{ type: 'octocode-slides:key', key }`, so `animation.js` can reveal or hide a step before slide navigation happens.
|
|
640
|
+
- When the **iframe** has focus (user clicked inside a slide) → `js/navbridge.js` inside the slide posts `{ type: 'octocode-slides:nav', key }` only after the slide has no step left to consume in that direction.
|
|
641
|
+
- The parent's message listener calls `handleKey({ key, passthrough: true })`; `passthrough:true` means the key already passed through the active iframe and should now advance or retreat the deck.
|
|
642
|
+
|
|
643
|
+
Do NOT attach a second `keydown` listener to the iframe — that would double-fire and advance two slides per key press.
|
|
644
|
+
|
|
645
|
+
```javascript
|
|
646
|
+
window.addEventListener('message', function (event) {
|
|
647
|
+
var data = event.data;
|
|
648
|
+
if (!data || typeof data !== 'object') return;
|
|
649
|
+
if (data.type === 'octocode-slides:nav' && data.key) {
|
|
650
|
+
handleKey({ key: data.key, passthrough: true, preventDefault: function () {} });
|
|
651
|
+
} else if (data.type === 'octocode-slides:activity') {
|
|
652
|
+
showHud();
|
|
653
|
+
} else if (data.type === 'octocode-slides:presenter-goto') {
|
|
654
|
+
go(clampIndex(data.index));
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### HUD (keyboard hint pill)
|
|
660
|
+
|
|
661
|
+
The HUD fades in on mouse move / key press, fades out after 1.6 s:
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
let hudTimer;
|
|
665
|
+
function showHud() {
|
|
666
|
+
hud.classList.add('show');
|
|
667
|
+
clearTimeout(hudTimer);
|
|
668
|
+
hudTimer = setTimeout(() => hud.classList.remove('show'), 1600);
|
|
669
|
+
}
|
|
670
|
+
window.addEventListener('mousemove', showHud);
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**After generating all slides:** fill `const slides = [...]` with every `{ path, hidden, name }` entry in presentation order.
|