@hypersocial/decks-cli 0.2.1 → 0.3.0

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 CHANGED
@@ -2,12 +2,16 @@
2
2
 
3
3
  Command-line client for Hypersocial Decks live analytics and share management.
4
4
 
5
- Most users should install the friendlier package name:
5
+ Most users should start with the friendlier package name:
6
6
 
7
7
  ```bash
8
- npx hyperdecks whoami
8
+ npx hyperdecks init
9
9
  ```
10
10
 
11
+ `init` signs you in, installs the skill into your agents, sets up a voice,
12
+ builds and narrates a three-slide hello deck and prints its link. Re-running it
13
+ resumes where it stopped; `--json --yes` is the agent form.
14
+
11
15
  This package targets production by default:
12
16
 
13
17
  ```bash
@@ -108,7 +112,7 @@ credits per second, refunded if fal fails to produce the clip.
108
112
  ## Narration
109
113
 
110
114
  `narrate` turns a deck's `script.md` into voiced clips in your own voice, word-timed
111
- captions, 60 fps mouth frames and a deck manifest. Rendering runs on the Hyperdecks
115
+ captions and a deck manifest (the player's live head lip-syncs from the audio). Rendering runs on the Hyperdecks
112
116
  backend with the platform's ElevenLabs key; the CLI only sends the script and polls.
113
117
  Each line is cached by a hash of its spoken text and voice, so after an edit only the
114
118
  slides whose words changed re-render.
@@ -128,7 +132,7 @@ normalize: hyper desk=hyperdecks
128
132
  ---
129
133
 
130
134
  ## hello
131
- hold: 1.1
135
+ hold: 1.5
132
136
  presenter: wave
133
137
 
134
138
  Hi, I'm Selçuk's mini avatar. [wave] This is Hyperdecks. [mood: excited]
@@ -137,6 +141,7 @@ Hi, I'm Selçuk's mini avatar. [wave] This is Hyperdecks. [mood: excited]
137
141
  - `## <slide-id>` is the slide's `<section id>` in `index.html`. Text before the first
138
142
  heading is notes for people and is ignored.
139
143
  - `hold`, `presenter` and `voice` lines directly under a heading are slide options.
144
+ `hold` is the pause in seconds after the line before the next slide (0.4 by default).
140
145
  - Sentences are the caption units, 4–85 characters each.
141
146
  - Stage tags (`[wave]`, `[mood: x]`, `[emote: x]`, `[reveal: x]`, `[play: x]`,
142
147
  `[pause: s]`, `[custom: name …]`) are not spoken. Each becomes a cue at the start
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypersocial/decks-cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Command-line client for Hypersocial Decks live analytics and share management.",
5
5
  "type": "module",
6
6
  "main": "./src/commands.mjs",
@@ -11,6 +11,32 @@ Hyperdecks is the CLI and MCP surface for Hypersocial Decks live analytics,
11
11
  recipient links, and deck activity. Treat real recipient activity, live deck
12
12
  stats, customer-facing links, and "what happened" questions as production work.
13
13
 
14
+ ## First run
15
+
16
+ When `hyperdecks whoami` says not authenticated, or `hyperdecks voice list`
17
+ shows no ready voice, run the first-run command instead of working around it:
18
+
19
+ ```bash
20
+ npx hyperdecks init --json --yes [--voice dash] [--name "<name>" --about "<one line>" --ask "<the ask>"]
21
+ ```
22
+
23
+ It signs in, installs this skill, scaffolds a three-slide hello deck from the
24
+ three answers, narrates it, releases one QA link and prints one JSON object
25
+ (`status`, `steps`, `slug`, `deckDir`, `shareUrl`, `next`). Re-running it
26
+ resumes where it stopped. Signed out, it first opens the sign-in page in the
27
+ user's browser and waits up to 3 minutes: tell them to finish signing in there
28
+ (a new account can be made on that page) before you run it, or have them run
29
+ `npx hyperdecks login` first. Inside a git repository it puts the deck in
30
+ `~/hyperdecks` rather than committing into that repository; `--dir` picks the
31
+ folder. Relay every step whose `status` is `needs-human` to the user, word for
32
+ word, and wait for them:
33
+
34
+ - deck: it needs `--name`, `--about` and `--ask`; ask the user, do not invent them;
35
+ - voice: cloning needs them to type their consent in a terminal (`hyperdecks
36
+ voice clone <file> --name <name>`), or to agree to the statement before you
37
+ call `clone_voice`. Never type or confirm consent for them. `--voice dash`
38
+ lets D.A.S.H., a character voice, present instead.
39
+
14
40
  ## Target Selection
15
41
 
16
42
  - Prefer production for real-world questions and actions. The published CLI is
@@ -53,13 +79,18 @@ Hyperdecks server; use the CLI where you have a shell and the tool otherwise.
53
79
 
54
80
  | Step | CLI | MCP tool |
55
81
  |---|---|---|
56
- | Scaffold | `hyperdecks deck new <slug> "<title>" --narrated` | `new_deck` |
82
+ | Scaffold | `hyperdecks deck new <slug> "<title>" --narrated [--layouts hero,product,number,closer]` | `new_deck` (`layouts`) |
57
83
  | Images | `hyperdecks gen-image "<prompt>" --slug <slug>` | `generate_image` |
58
84
  | Video clips | `hyperdecks gen-video "<prompt>" --slug <slug>` | `generate_video` |
85
+ | Product screenshot | `hyperdecks screenshot <url> --slug <slug>` | `capture_url` |
86
+ | Chart from numbers | `hyperdecks chart <spec.json> --slug <slug>` | `render_chart` |
87
+ | Icon set | `hyperdecks icons <subject...> --style "<style>" --slug <slug>` | `generate_icon_set` |
88
+ | Outside review (required before release) | `hyperdecks review <slug> --script decks/<slug>/script.md` | `review_deck` |
59
89
  | Your voice (once) | `hyperdecks voice clone <file...> --name <name>` | `clone_voice` |
60
90
  | Narrate | `hyperdecks narrate decks/<slug>` | `narrate_deck` |
61
91
  | Check | `hyperdecks check decks/<slug>` | `check_deck` |
62
92
  | Ship | `hyperdecks release decks/<slug> --to "<name>" --email <e>` | `release_deck` |
93
+ | Share-link poster | `hyperdecks poster <slug>` | `generate_deck_poster` |
63
94
  | Import only | `hyperdecks import <file> --slug <slug>` | `import_deck_from_url` |
64
95
  | One more link | `hyperdecks share new <slug> "<name>"` | `create_share` |
65
96
  | Who watched | `hyperdecks deck stats <slug>` | `deck_stats` |
@@ -76,18 +107,31 @@ Write `decks/<slug>/evidence.md` as you go: one row per fact with the slide, the
76
107
  claim, the source (file path, thread, doc title) and its date. Never put a
77
108
  number on a slide that has no row.
78
109
 
110
+ <!-- topic: format -->
79
111
  ### 2. Scaffold
80
112
 
81
113
  ```bash
82
- hyperdecks deck new <slug> "<title>" --narrated # writes decks/<slug>/, registers a draft you own
114
+ hyperdecks deck new <slug> "<title>" --narrated --layouts hero,features,product,number,closer
115
+ # one slide per layout, from the layout library
116
+ hyperdecks deck new <slug> "<title>" --narrated [--presenter-style stage|bubble|glass|corner]
117
+ # the plain eight-slide outline
83
118
  ```
84
119
 
120
+ Both write `decks/<slug>/` and register a draft you own. Prefer `--layouts`:
121
+ pick the layouts from the story (see **Design** below), in order; repeat one
122
+ for more of the same (`number,number` gives slides `number` and `number-2`).
123
+
85
124
  Files, all in `decks/<slug>/`:
86
125
 
87
- - `index.html`: eight `<section class="slide" id="…">` slides (hello, problem,
88
- why-now, idea, how, proof, ask, bye) and the hosted player script. Keep, cut or
89
- add slides; keep the contract below.
90
- - `script.md`: the narration, one `## <slide-id>` block per slide.
126
+ - `index.html`: the `<section class="slide" id="…">` slides and the hosted
127
+ player script. With `--layouts` each slide is a layout's section (ids like
128
+ `features`, inner ids like `features-f1`) and its CSS sits once in `<head>`;
129
+ without, eight outline slides (hello, problem, why-now, idea, how, proof, ask,
130
+ bye). Keep, cut or add slides; keep the contract below.
131
+ - `script.md`: the narration, one `## <slide-id>` block per slide. With
132
+ `--layouts` each block starts as the layout's starter line with its `[go]` /
133
+ `[look]` cues already pointing at the slide's elements: rewrite the words,
134
+ keep the cues at the words they belong to.
91
135
  - `evidence.md`: the sources table. Private: release never uploads it.
92
136
 
93
137
  The deck contract (`check` enforces each line):
@@ -100,13 +144,186 @@ The deck contract (`check` enforces each line):
100
144
  - A title (first `<h1>`/`<h2>`) over 28 characters carries
101
145
  `data-title-mobile="<shorter>"`. The scaffold puts one on every title.
102
146
  - Each slide keeps its `<div class="narration-slot"></div>` (where the
103
- presenter's face and caption bubble sit); the first slide has `data-intro`.
147
+ presenter's face and captions sit; the player glides the presenter there,
148
+ so put slots on different sides across slides, as the scaffold's
149
+ `.slot-left` does); the first slide has `data-intro`.
104
150
  - The player script stays
105
151
  `<script type="module" src="https://decks.hypersocial.ai/player/v1/player.js" data-manifest="…">`.
106
152
  `narrate` fills `data-manifest`; do not edit it by hand.
107
153
  - Local images and files sit inside the deck folder and are referenced
108
154
  relatively (`img/chart.png`). Hosted images (`gen-image` URLs) are fine too.
109
155
 
156
+ <!-- topic: presenter -->
157
+ #### Which presenter style
158
+
159
+ Set the deck's on the player tag (`data-presenter-style="stage"`) or in
160
+ script.md's front matter (`presenterStyle: stage`); override one slide with
161
+ `data-presenter-style` on its `<section>` or `presenter-style: corner` under
162
+ its `##` heading.
163
+
164
+ | Style | Pick it when |
165
+ |---|---|
166
+ | `stage` (default with the live head) | the presenter carries the deck: a founder pitch, a walkthrough. Large unframed head, lower-third captions; leave ~500 px beside the text for it |
167
+ | `bubble` (default otherwise) | a light aside; the face in a circle with a speech bubble |
168
+ | `glass` | dark or busy slides: head and captions on one frosted panel |
169
+ | `corner` | the slide is the point (a product shot, a chart, a demo): small head bottom-right, subtitles along the bottom |
170
+
171
+ Below 650 px stage and glass become corner on their own. Run
172
+ `hyperdecks check <deck> --layout` to be warned about any slide whose presenter
173
+ would cover its title.
174
+
175
+ <!-- topic: transitions -->
176
+ #### Transitions
177
+
178
+ Every slide enters with `drift` unless it says otherwise. Use another only
179
+ where the slide's job matches its "use for"; never hand-write slide-change
180
+ animation in the deck's own JS (it fights the player's timing).
181
+
182
+ | Transition | Use for | What it does |
183
+ |---|---|---|
184
+ | `drift` (default) | most slides | the old slide drifts out, the new one in, its parts arriving title first |
185
+ | `cut` | a punchline, a fast beat | instant |
186
+ | `push` | the next step in a sequence | the new slide slides over; the old one sinks back and darkens |
187
+ | `wipe` | a new section | a band in the deck's second colour, then one in ink, sweeps across and leaves the new slide |
188
+ | `zoom <selector>` | going into the detail of one row, bar or card | the new slide opens out of that element on the slide before |
189
+ | `morph` (carry over) | a number or title on both slides | the element travels into its new place and size |
190
+ | `fly` | the deck's one big reveal | the presenter flies across and the new slide appears behind it |
191
+
192
+ Set the deck's in script.md's front matter (`transition: drift`) or on the
193
+ player tag (`data-transition`); a slide's under its `##` heading
194
+ (`transition: push`) or as `data-transition` on its `<section>`, which wins.
195
+ A slide's transition is how it enters; going back plays it in reverse.
196
+ A `transition:` in script.md reaches the player through the manifest, so it
197
+ takes effect after `narrate` (release runs it; nothing re-renders);
198
+ `data-transition` in index.html takes effect at once. Zoom
199
+ names its target on the slide before: `transition: zoom #dash-row`. Morph
200
+ pairs elements by name: put `data-morph="saved"` on the element on both
201
+ slides. Reduced motion turns every one into a short crossfade, and the
202
+ narration never waits for a transition.
203
+
204
+ `check` fails an unknown name, a `zoom` with no selector or one that matches
205
+ nothing on the slide before, a `morph` with no `data-morph` name on both
206
+ slides, and a second `fly`. It warns when a deck uses more than two kinds
207
+ besides its default: pick one or two and keep them for the same job.
208
+
209
+ Viewers can poke the live head: a click or tap makes it react (an "oof", a
210
+ flinch) and a drag pulls it a little before it springs back, without pausing
211
+ the narration.
212
+
213
+ <!-- topic: design -->
214
+ ### Design: make it look good
215
+
216
+ A narrated deck is only as convincing as it looks. Design before you write the
217
+ narration, and check the result at both widths (step 6).
218
+
219
+ **One idea per slide.** The title states the idea as a sentence a listener
220
+ could repeat ("Narration renders in two minutes", not "Speed"). Everything
221
+ else on the slide supports that sentence; if it needs a second idea, it is a
222
+ second slide. Five to nine slides carry a three-minute deck.
223
+
224
+ **Type scale.** The layout shell defines it in `:root`; use the tokens, not
225
+ new sizes:
226
+
227
+ | Token | Stage (1600×900) | Phone | For |
228
+ |---|---|---|---|
229
+ | `--t-display` | 128px | 54px | one hero line or a closing word, at most once per slide |
230
+ | `--t-title` | 84px | 38px | the slide's title |
231
+ | `--t-sub` | 56px | 28px | a title beside a visual, a number's label |
232
+ | `--t-lead` | 30px | 17px | one supporting sentence |
233
+ | `--t-body` | 24px | 15px | list items, card text |
234
+ | `--t-label` | 17px | 12px | kickers, sources, captions (mono, uppercase) |
235
+
236
+ Nothing on a slide below 17px at stage size (a screenshot's frame chrome aside). A title over 28 characters needs
237
+ `data-title-mobile`; write the short version as a real sentence.
238
+
239
+ **Colour and contrast.** Three colours do the work: `--ink`, `--paper` and one
240
+ `--accent` (a second, `--accent-2`, only for a chart's other series, an
241
+ illustration or a placeholder face). Text is ink on paper or accent, or paper on ink: never accent
242
+ on paper, never grey on a colour. Alternate slide grounds (`on-accent`,
243
+ plain, `on-ink`) so the slide changes read, and keep every number, source and
244
+ label at full contrast. Change the palette and fonts once, in `:root`; every
245
+ layout reads them.
246
+
247
+ **Pick the layout from what the slide says.** Each layout's file in
248
+ `templates/layouts/` opens with its slot side, presenter and the cues that
249
+ suit it.
250
+
251
+ | Layout | Use it for | Presenter, moves |
252
+ |---|---|---|
253
+ | `hero` | the opening, a section opener; the presenter is the visual, no `.art` unless it shows the real subject | stage, bottom-right; `[wave]`, `[look: h1]` |
254
+ | `number` | one number with its source: proof, traction, a delta | stage; `[look: #<id>-figure]` as it is said |
255
+ | `product` | a screenshot in a browser frame (`.frame.phone` for a phone shot) with the point beside it | stage; `[look: #<id>-frame]`, `[reveal: #<id>-callout]` |
256
+ | `quote` | a customer or user in their own words, quoted exactly | stage, bottom-left; `[look: #<id>-quote]` |
257
+ | `compare` | before/after, them/us, three parallel lines each | stage; `[look: #<id>-before]`, `[go: #<id>-after]` |
258
+ | `timeline` | four milestones: history, a roadmap, a process | stage; `[go: #<id>-m1]` … `m4`, `[go: slot]` |
259
+ | `features` | three things it does, an icon each | stage; `[go: #<id>-f1]` … `f3`, `[go: slot]` |
260
+ | `chart` | real numbers drawn with `chart`, the takeaway beside | stage; `[look: #<id>-chart]`, `[reveal: #<id>-takeaway]` |
261
+ | `team` | up to four people, faces only with permission | stage; `[go: #<id>-p1]` … `p4`, `[go: slot]` |
262
+ | `closer` | the end: one line and one action (a command, a link) | stage, bottom-left; `[go: #<id>-cta]`, `[go: slot]`, `[wave]` |
263
+
264
+ Presenter style follows the deck (step 2, *Which presenter style*): `stage`
265
+ when the presenter carries the story, which is what the layouts are drawn for
266
+ (they keep the slot's corner clear); `corner` on a slide where a big
267
+ visual is the point and moves are not needed; `glass` on a dark or busy
268
+ picture. Two layouts (`quote`, `closer`) sit left; put a `slot-left` on others
269
+ where there is room so slide changes move the presenter.
270
+
271
+ **Visuals: real first, generated second.**
272
+
273
+ - Product: `hyperdecks screenshot <url>` (`capture_url`) into `product`'s
274
+ `.shot`; crop to what the narration talks about. Never mock a UI that
275
+ exists.
276
+ - Numbers: `hyperdecks chart <spec.json>` (`render_chart`) in the deck's
277
+ palette into `chart`'s `.chart`, the source in `data-note` and evidence.md.
278
+ Never a generated picture of a chart.
279
+ - **No decorative imagery.** Generic illustration (stages, speech bubbles,
280
+ floating shapes, abstract "tech" art, clip-art metaphors) makes a deck look
281
+ cheap. A visual earns its place only if it shows something real from the
282
+ story: the product, the data, a concrete situation or object. When in doubt,
283
+ leave it out: one big statement in type beats a picture of nothing. The
284
+ presenter is the hero of the opening slide; don't put art behind it.
285
+ - Icons: only when they label something the viewer must tell apart at a
286
+ glance. Three short statements set large usually beat three icon cards.
287
+ - Images: `gen-image` (`generate_image`) only for a concrete subject the
288
+ deck is about (a product in use, a place, a real object), never a metaphor.
289
+ One style per deck, described the same way every time (subject, medium,
290
+ palette hexes, "no text"). `--model nano-banana-2.1` when the image must
291
+ carry legible type.
292
+ - Video: `gen-video` for a short loop behind a hero or a product moment, with
293
+ `[play: video]` at its word; never needed for a deck to work.
294
+ - People: real photos only with permission; initials otherwise.
295
+
296
+ **Motion.** The presenter's moves are the deck's motion; keep everything else
297
+ still.
298
+
299
+ - `[go]` when the narration walks through items (cards, milestones, people,
300
+ the action on the closer): one `go` per item at the word that starts it,
301
+ then `[go: slot]` before the slide ends. Never more than one `go` per
302
+ sentence; never a `go` on a slide in `corner` style.
303
+ - `[look]` for a glance: the title as it is read, a number as it is said, a
304
+ screenshot as the narration turns to it.
305
+ - `[reveal]` for the one detail that should not be read before it is said (a
306
+ takeaway, a callout); not for every line.
307
+ - `[wave]` once at the start and once at the end.
308
+ - No CSS animation on slides beyond `.reveal`; the player animates the slide
309
+ change.
310
+
311
+ **Review before release (required).** After `check` passes and the deck is
312
+ imported, run `hyperdecks review <slug> --script decks/<slug>/script.md`
313
+ (`review_deck`, then poll `check_review_job`). Act on every finding:
314
+
315
+ - `fix`: change the slide or the script, re-import (or `release --qa`), and
316
+ review again until no `fix` is left. Do not release with a `fix` open.
317
+ - `consider`: apply it when it serves the slide's one idea; otherwise note in
318
+ your reply to the user why you left it.
319
+ - A finding you disagree with (a deliberate choice, a misread screenshot):
320
+ say so to the user with the reason instead of silently ignoring it.
321
+
322
+ If review cannot run (no credits, the service is down), say so to the user and
323
+ do the step 6 screenshot pass yourself as the reviewer: the same desktop and
324
+ phone screenshots, the same `fix` bar.
325
+
326
+ <!-- topic: script -->
110
327
  ### 3. Write slides and script
111
328
 
112
329
  - One idea per slide; the title states it.
@@ -115,9 +332,32 @@ The deck contract (`check` enforces each line):
115
332
  - Narration: short phrases, each sentence one caption of 4 to 85 characters
116
333
  (two lines in the bubble). Aim for 2.5 to 3 minutes in all; `maxTotalSeconds`
117
334
  in the front matter is the cap check uses (180 in the scaffold).
118
- - Holds: 1.1 s by default; `hold: 2` under the heading on interactive slides.
335
+ - Holds: 0.4 s by default. Give a slide `hold: 1.5` when the viewer needs time to read a number, a chart or a reveal, and `hold: 2` on interactive slides.
119
336
  - Cues: `[reveal: #id]` reveals `.reveal#id` at the word before it; also
120
337
  `[wave]`, `[mood: excited]`, `[pause: 0.5]`, `[custom: name]`.
338
+ - Moves, to point at part of a slide: `[go: <selector>]` flies the presenter
339
+ beside that element (the side with room) until the next `go`, the slide's
340
+ end, or `[go: slot]`; `[look: <selector>]` turns the head toward it without
341
+ moving. One selector each, matched inside that slide; check fails one that
342
+ matches nothing. Use `go` when the narration walks through a list or chart,
343
+ `look` for a glance at a title or number. Not in `corner` style. The best
344
+ showcase is a slide of stacked cards, one point each, with a `go` to each
345
+ card at the word that starts it: `Write [go: #step-1] the slides, write
346
+ [go: #step-2] what you'd say`. Give each card an id.
347
+
348
+ | Cue | Effect |
349
+ |---|---|
350
+ | `[wave]` | the presenter greets (the live head's joy emote) |
351
+ | `[mood: excited]` / `[emote: name]` | the head's mood or emote |
352
+ | `[reveal: #id]` | adds `is-revealed` to `#id` |
353
+ | `[pause: 0.5]` | a beat of silence (≤ 5 s) |
354
+ | `[play: video]` / `[pause: video]` | play or pause the slide's video |
355
+ | `[go: .list]` / `[go: slot]` | fly beside `.list` / back to the slot |
356
+ | `[look: h2]` | look at the title |
357
+ | `[custom: name]` | only the slide's controller sees it |
358
+ - `voice: dash` narrates with D.A.S.H.'s own robot voice, a synthetic
359
+ character voice (not a clone of anyone) any account may use, for decks that
360
+ D.A.S.H. presents. It works once the deployment sets `CHARACTER_VOICE_DASH`.
121
361
  - No `voice:` in the front matter means the user's first ready voice. If
122
362
  `hyperdecks voice list` shows none, ask the user to record one and run
123
363
  `voice clone` (it asks them for consent; never clone anyone else's voice).
@@ -133,12 +373,18 @@ A slide fails when the transcript does not match the script: read the error,
133
373
  reword the sentence (spell numbers and names the way they are said, or add a
134
374
  `normalize:` entry), and narrate again; only that slide re-renders.
135
375
 
376
+ <!-- topic: check -->
136
377
  ### 5. Check
137
378
 
138
379
  ```bash
139
- hyperdecks check decks/<slug> [--forbid "Other Co,codename"]
380
+ hyperdecks check decks/<slug> [--forbid "Other Co,codename"] [--layout]
140
381
  ```
141
382
 
383
+ `--layout` lays each slide out in Chromium (needs `hyperdecks preview setup`).
384
+ At the stage size it warns, without failing, when a slide's presenter would
385
+ overlap its title. On a 390×844 phone (the share player's 374×736 slide card)
386
+ it fails any slide whose content overflows: the player never scrolls a slide.
387
+
142
388
  `--forbid` lists phrases that must not reach this audience (another customer,
143
389
  an internal codename). Messages and what to do:
144
390
 
@@ -152,25 +398,48 @@ an internal codename). Messages and what to do:
152
398
  | `narration runs …s, over the …s cap` | shorten the script |
153
399
  | `clip <id>: audio …` / `clip <id>: timeline …` | narrate again; the asset is missing or wrong |
154
400
  | `clip <id>: caption N runs` | narrate that slide again (`--slide <id>`) |
155
- | `clip <id>: caption words do not match` | narrate that slide again |
401
+ | `clip <id>: captions do not cover the clip text` | narrate that slide again |
156
402
  | `clip <id>: caption N is … characters` | split or join sentences in that block |
157
403
  | `local reference does not resolve` / `leaves the deck folder` | move the file into the deck folder or fix the path |
158
404
  | `player script must be` / `no player script` | restore the player/v1 script tag |
159
405
  | `the player script has no data-manifest` | run `narrate` |
406
+ | `no loading state` | copy the template's `<style data-hd-loading>` block (and the preload lines above it) into `<head>`; set `--hd-ground` / `--hd-ground-ink` to the first slide's colours |
160
407
  | `forbidden phrase` | remove it from the page, script or narration |
161
408
  | `slide <id>: needs a data-note` | add the source |
162
409
  | `slide <id>: title … needs a data-title-mobile` | add a shorter title |
410
+ | `… presenter style "…" is not one of` | use stage, bubble, glass or corner |
411
+ | `script.md line N: [go: …] matches nothing in slide <id>` | fix the selector, or add the element to that slide |
412
+ | `… transition "…" is not one of` | use drift, cut, push, wipe, zoom `<selector>`, morph or fly |
413
+ | `slide <id>: zoom target "…" matches nothing on the slide before it` | point the zoom at an element of the previous slide, or give that element the id |
414
+ | `slide <id>: morph needs an element with the same data-morph` | put `data-morph="<name>"` on the carried element on both slides, or use drift |
415
+ | `script.md says transition "…" but the manifest has "…"` | run `narrate` (no audio re-renders), or release, which runs it |
416
+ | `fly is for the deck's one big moment` | keep `fly` on one slide; use drift or push on the others |
417
+ | ⚠ `N kinds of transition besides the default` | use one or two kinds, each for its own job |
418
+ | `slide <id>: its content runs …px past the slide on a 390×844 phone` | trim that slide on phones: smaller type, fewer items, or hide decoration in its `@media (max-width: 650px)` rules (an `#id` rule outranks them; restate it there) |
419
+ | ⚠ `slide <id>: the … presenter would overlap the title` | move that slide's `.narration-slot`, pad the text away from it, or use `corner` |
163
420
 
164
421
  ### 6. Look at it, at both widths
165
422
 
166
423
  Serve the folder (`npx serve decks/<slug>` or `python3 -m http.server -d decks/<slug>`)
167
424
  and screenshot it with Playwright at 1440×900 and 390×844, every slide
168
- (`#scene=<id>`). Look at both: nothing clipped, the bubble clear of the text, the
169
- mobile titles in use. Fix and repeat until both read well.
425
+ (`#scene=<id>`). Look at both: nothing clipped, the presenter and its captions
426
+ clear of the text, the mobile titles in use. Fix and repeat until both read well.
427
+
428
+ Then the required review (see **Design**, *Review before release*): once the
429
+ deck is imported, `hyperdecks review <slug> --script decks/<slug>/script.md`
430
+ (`review_deck`, then `check_review_job`) has GPT-6 Luna look at every slide of
431
+ the deck's QA link on desktop and phone and list each problem as `fix` or
432
+ `consider` with a concrete suggestion. Release only when no `fix` is left.
433
+
434
+ Pick image models by job: `gen-image` defaults to GPT Image 2.5; `--model
435
+ nano-banana-2.1` is stronger on typography and photoreal scenes. `gen-video`
436
+ defaults to MiniMax H3 Max (`--model kling-v3` is the fallback). Use `chart` for
437
+ real numbers instead of a generated picture of a chart, and `icons` with the
438
+ deck's palette for a set that matches.
170
439
 
171
440
  ### 7. Release
172
441
 
173
- Commit the deck folder first: release refuses uncommitted changes so every
442
+ Only after `check` passes and the last review has no `fix` finding. Commit the deck folder first: release refuses uncommitted changes so every
174
443
  release matches a commit.
175
444
 
176
445
  ```bash
@@ -187,6 +456,11 @@ references must be tracked by git (an ignored `*.mp4` is refused). Then it impor
187
456
  (each `--email` pairs with the `--to` before it). Confirm recipients with the
188
457
  user before sending real links; send the links yourself only if asked.
189
458
 
459
+ Release also makes the deck's share-link poster (its first slide with a Play
460
+ badge, shown when a link is pasted into Slack, iMessage or email, and on the
461
+ dashboard card) within a minute. When the first slide changed look, check it
462
+ with `hyperdecks poster <slug>` (`generate_deck_poster`), which prints its URL.
463
+
190
464
  ### 8. Report who watched
191
465
 
192
466
  ```bash
@@ -197,6 +471,16 @@ hyperdecks activity --deck <slug> --limit 10
197
471
  Tell the user who opened it, how far they got and whether the narration
198
472
  finished. QA links never count.
199
473
 
474
+ ### Hosted source (chat agents and the connector)
475
+
476
+ A deck can also live on Hyperdecks with no folder: chat agents build it through
477
+ the hosted MCP (`deck_guide`, `new_deck`, `get_deck_source`, `write_deck_source`,
478
+ `upload_deck_asset`, `check_deck`, `release_deck`; see `chat.md`). Move between
479
+ the two with `hyperdecks source pull <slug> [dir]` (the hosted draft into a
480
+ folder) and `hyperdecks source push <slug> [dir]` (a folder's files back to the
481
+ draft), `hyperdecks asset add <slug> <file|url>` for an image, `hyperdecks guide
482
+ [topic]` for these sections, and `--hosted` on `deck new`, `check` and `release`.
483
+
200
484
  ## Safety
201
485
 
202
486
  - For read-only inspection, run commands directly and summarize the important
@@ -0,0 +1,49 @@
1
+ # Hyperdecks from a chat app (hosted MCP)
2
+
3
+ You are building a narrated deck through the Hyperdecks connector: no terminal,
4
+ no git, no folder. The deck's files (`index.html`, `script.md`, `evidence.md`)
5
+ live on Hyperdecks as a draft; you read and edit them with tools, and nothing
6
+ reaches a recipient until `release_deck`. Topics: call `deck_guide` with
7
+ `format`, `script`, `design`, `layouts`, `presenter`, `transitions` or `check`
8
+ before the step that needs it.
9
+
10
+ ## The loop
11
+
12
+ 1. `deck_guide` (this page), then `deck_guide { topic: "layouts" }` to pick slides.
13
+ 2. `new_deck { slug, title, layouts }`: registers your draft and returns the
14
+ full text of every file plus a `previewUrl` (the draft, untracked).
15
+ 3. Images: `generate_image`, `render_chart`, `capture_url`, or
16
+ `upload_deck_asset` for a picture the user gave you (a URL, or a file they
17
+ uploaded). Each returns a hosted URL: put that URL in the HTML. A hosted deck
18
+ has no folder, so a relative path like `img/a.png` is an error.
19
+ 4. `write_deck_source` to change a file: `edits: [{ find, replace }]` with
20
+ exact text that occurs once (copy it from `get_deck_source`), or `content`
21
+ to replace the file. Pass `baseRev` (the `rev` you last read) so a
22
+ concurrent edit fails instead of being overwritten.
23
+ 5. `check_deck { slug }` until it passes (the narration errors clear in step 6).
24
+ 6. `narrate_deck { slug }` with no `script` narrates the stored `script.md`;
25
+ poll `check_narration_job` until `done`. The draft's player tag then has its
26
+ `data-manifest`. No voice of their own yet? Pass `voice: "dash"`.
27
+ 7. `check_deck { slug }` again, then `review_deck { slug, draft: true }` and
28
+ `check_review_job`: it shoots desktop and 390×844 phone frames, which is how
29
+ you catch a slide that overflows a phone (the hosted check cannot measure
30
+ layout).
31
+ 8. `release_deck { slug, qa: true }` gives the user a QA link to watch first.
32
+ `release_deck { slug, recipients: [{ recipient, email }] }` makes one
33
+ tracked link each. Release creates live links: confirm the recipients with
34
+ the user before you call it.
35
+ 9. Later: `list_shares`, `deck_stats`, `activity_overview`.
36
+
37
+ ## Rules that matter
38
+
39
+ - Every slide is `<section class="slide" id="…" data-note="<source of its facts>">`,
40
+ in the same order as the `## <id>` blocks in `script.md`.
41
+ - Keep the player script tag and the `<style data-hd-loading>` block the
42
+ scaffold wrote; never write `data-manifest` yourself.
43
+ - Edit the draft freely: recipients keep seeing the last release until you call
44
+ `release_deck` again. A release is refused when the check fails, when
45
+ narration is stale (narrate again first), or when the draft changed after
46
+ the check.
47
+ - Files are capped at 800 KB each: use hosted image URLs, never inline base64.
48
+ - Facts come from the user's material; put the source of each in `evidence.md`
49
+ and the slide's `data-note`.