@ztffn/presentation-generator-plugin 1.3.3 → 1.3.5
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/.claude-plugin/plugin.json +4 -3
- package/README.md +27 -17
- package/agents/presentation-content.md +1 -2
- package/agents/presentation-design.md +9 -5
- package/agents/presentation-narrative.md +4 -4
- package/agents/presentation-style.md +196 -0
- package/hooks/enforce-style-schema.sh +70 -0
- package/hooks/hooks.json +49 -0
- package/hooks/pre-validate-presentation-json.sh +62 -0
- package/hooks/validate-presentation-json.sh +64 -0
- package/package.json +1 -1
- package/scripts/outline_to_graph.py +413 -0
- package/scripts/validate_draft.py +2 -1
- package/skills/graph-json-spec/SKILL.md +73 -620
- package/skills/presentation-generator/SKILL.md +80 -33
- package/skills/presentation-generator/presentation-guide.md +2 -2
- package/skills/slide-recipes/SKILL.md +326 -0
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: graph-json-spec
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Node schema, edge wiring, and positioning grid for generating valid
|
|
5
|
+
graph-based presentation JSON. Structural specification —
|
|
6
|
+
see slide-recipes for design decisions and patterns.
|
|
7
7
|
user-invocable: false
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Graph JSON Specification
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
Derived from the renderer at `src/types/presentation.ts` and validated demo JSON.
|
|
12
|
+
Structural reference for producing valid presentation graph JSON.
|
|
14
13
|
|
|
15
14
|
## Quick Reference
|
|
16
15
|
|
|
17
|
-
The most common errors and their fixes — read this first.
|
|
18
|
-
|
|
19
16
|
| Rule | Correct | Wrong |
|
|
20
17
|
|---|---|---|
|
|
21
18
|
| Slide title field | `data.label` | ~~title~~, ~~headline~~, ~~heading~~ |
|
|
@@ -31,14 +28,14 @@ The most common errors and their fixes — read this first.
|
|
|
31
28
|
| Node size | `{ width: 180, height: 70 }` in both `style` and `measured` | any other size |
|
|
32
29
|
| Edge pairs | Every forward edge needs a return edge | one-way edges |
|
|
33
30
|
|
|
34
|
-
**Banned data field names** —
|
|
31
|
+
**Banned data field names** — silently ignored by the renderer:
|
|
35
32
|
`headline`, `subheadline`, `bullets`, `speakerNote`, `speakerNotes`, `visualHint`, `theme`, `title`, `body`, `text`, `background`, `showLogo`, `keyMessage`, `claim`, `hook`, `description`, `summary`
|
|
36
33
|
|
|
37
34
|
---
|
|
38
35
|
|
|
39
36
|
## Node Structure
|
|
40
37
|
|
|
41
|
-
Every node
|
|
38
|
+
Every node wrapper:
|
|
42
39
|
|
|
43
40
|
```json
|
|
44
41
|
{
|
|
@@ -51,12 +48,11 @@ Every node in the graph has this wrapper:
|
|
|
51
48
|
}
|
|
52
49
|
```
|
|
53
50
|
|
|
54
|
-
- `id`: Kebab-case slug (e.g. `"cover"`, `"problem-detail"
|
|
55
|
-
- `type`: Always `"huma"`
|
|
56
|
-
- `position`: `{ x, y }` in
|
|
57
|
-
- `style
|
|
58
|
-
-
|
|
59
|
-
- Optional: `style.backgroundColor` — hex string for node background in the graph editor (also applied as slide background)
|
|
51
|
+
- `id`: Kebab-case slug (e.g. `"cover"`, `"problem-detail"`)
|
|
52
|
+
- `type`: Always `"huma"`
|
|
53
|
+
- `position`: `{ x, y }` in canvas coordinates (see Positioning Grid)
|
|
54
|
+
- `style` / `measured`: Always `{ width: 180, height: 70 }`
|
|
55
|
+
- Optional: `style.backgroundColor` — hex string for node/slide background
|
|
60
56
|
|
|
61
57
|
### SlideNodeData Fields
|
|
62
58
|
|
|
@@ -64,27 +60,27 @@ Every node in the graph has this wrapper:
|
|
|
64
60
|
|
|
65
61
|
| Field | Type | Default | Description |
|
|
66
62
|
|---|---|---|---|
|
|
67
|
-
| `label` | `string?` | — | Slide title
|
|
68
|
-
| `topic` | `string?` | — | Section badge
|
|
63
|
+
| `label` | `string?` | — | Slide title |
|
|
64
|
+
| `topic` | `string?` | — | Section badge (e.g. `"01 / Problem"`) |
|
|
69
65
|
| `content` | `string?` | — | Markdown body. Supports headings, bullets, bold, code, tables, `[chart:name]` embeds, `` images/videos, `[text](#nodeId)` navigation links |
|
|
70
|
-
| `notes` | `string?` | — | Speaker notes
|
|
66
|
+
| `notes` | `string?` | — | Speaker notes (presenter panel only) |
|
|
71
67
|
|
|
72
68
|
#### Slide Type
|
|
73
69
|
|
|
74
70
|
| Field | Type | Default | Description |
|
|
75
71
|
|---|---|---|---|
|
|
76
|
-
| `type` | `"content" \| "r3f" \| "chart" \| "custom"` | `"content"` | Slide renderer
|
|
72
|
+
| `type` | `"content" \| "r3f" \| "chart" \| "custom"` | `"content"` | Slide renderer |
|
|
77
73
|
|
|
78
74
|
#### Layout & Display
|
|
79
75
|
|
|
80
76
|
| Field | Type | Default | Description |
|
|
81
77
|
|---|---|---|---|
|
|
82
78
|
| `centered` | `boolean?` | `true` | Center content vertically and horizontally |
|
|
83
|
-
| `layout` | `"single" \| "two-column"` | `"single"` |
|
|
84
|
-
| `lightText` | `boolean?` | `false` |
|
|
85
|
-
| `brandFont` | `boolean?` | `false` |
|
|
79
|
+
| `layout` | `"single" \| "two-column"` | `"single"` | `"two-column"` splits on `---` delimiter |
|
|
80
|
+
| `lightText` | `boolean?` | `false` | White text for dark backgrounds |
|
|
81
|
+
| `brandFont` | `boolean?` | `false` | Display font for the title |
|
|
86
82
|
| `showBranding` | `boolean?` | `true` | Show branding overlay |
|
|
87
|
-
| `brandingText` | `string?` | — | Bottom-left branding label
|
|
83
|
+
| `brandingText` | `string?` | — | Bottom-left branding label |
|
|
88
84
|
|
|
89
85
|
#### Background Media
|
|
90
86
|
|
|
@@ -92,7 +88,7 @@ Every node in the graph has this wrapper:
|
|
|
92
88
|
|---|---|---|---|
|
|
93
89
|
| `backgroundImage` | `string?` | — | URL to background image |
|
|
94
90
|
| `backgroundImageFit` | `"cover" \| "contain"` | `"cover"` | How image fills the slide |
|
|
95
|
-
| `backgroundImageOverlay` | `boolean?` | `false` | Dark scrim
|
|
91
|
+
| `backgroundImageOverlay` | `boolean?` | `false` | Dark scrim for text readability |
|
|
96
92
|
| `backgroundVideo` | `string?` | — | URL to background video |
|
|
97
93
|
| `backgroundVideoFit` | `"cover" \| "contain"` | `"cover"` | How video fills the slide |
|
|
98
94
|
| `backgroundVideoLoop` | `boolean?` | `true` | Loop background video |
|
|
@@ -101,7 +97,7 @@ Every node in the graph has this wrapper:
|
|
|
101
97
|
|
|
102
98
|
| Field | Type | Default | Description |
|
|
103
99
|
|---|---|---|---|
|
|
104
|
-
| `inlineVideoControls` | `boolean?` | `true` | Show controls on inline videos
|
|
100
|
+
| `inlineVideoControls` | `boolean?` | `true` | Show controls on inline videos |
|
|
105
101
|
| `inlineVideoAutoplay` | `boolean?` | `true` | Autoplay inline videos |
|
|
106
102
|
| `inlineVideoLoop` | `boolean?` | `true` | Loop inline videos |
|
|
107
103
|
|
|
@@ -111,56 +107,40 @@ Every node in the graph has this wrapper:
|
|
|
111
107
|
|---|---|---|---|
|
|
112
108
|
| `scene.component` | `string` | — | Registry key (e.g. `"rotating-cube"`, `"particle-field"`) |
|
|
113
109
|
| `scene.props` | `Record<string, unknown>?` | — | Props passed to the scene component |
|
|
114
|
-
| `scene.controls` | `boolean?` | — | Enable OrbitControls
|
|
110
|
+
| `scene.controls` | `boolean?` | — | Enable OrbitControls |
|
|
115
111
|
| `scene.background` | `string?` | — | Scene background hex color |
|
|
116
112
|
|
|
117
113
|
#### Charts
|
|
118
114
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
|
122
|
-
|
|
123
|
-
| `chart.chartType` | `"bar" \| "line" \| "area" \| "pie" \| "radar"` | — | Chart renderer |
|
|
124
|
-
| `chart.data` | `Array<Record<string, unknown>>` | — | Data array |
|
|
125
|
-
| `chart.config.xKey` | `string?` | — | X-axis data key |
|
|
126
|
-
| `chart.config.yKeys` | `string[]?` | — | Y-axis data keys |
|
|
127
|
-
| `chart.config.colors` | `string[]?` | — | Series colors |
|
|
128
|
-
| `chart.config.showGrid` | `boolean?` | — | Show grid lines |
|
|
129
|
-
| `chart.config.showLegend` | `boolean?` | — | Show legend |
|
|
130
|
-
|
|
131
|
-
**Inline charts** (referenced via `[chart:name]` in content):
|
|
132
|
-
|
|
133
|
-
| Field | Type | Default | Description |
|
|
134
|
-
|---|---|---|---|
|
|
135
|
-
| `charts` | `Record<string, ChartConfig>` | — | Named chart configurations. Key is referenced in content as `[chart:keyname]` |
|
|
115
|
+
| Field | Type | Description |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `chart` | `ChartConfig` | Full-viewport chart (when `type: "chart"`) |
|
|
118
|
+
| `charts` | `Record<string, ChartConfig>` | Named inline charts, referenced via `[chart:keyname]` in content |
|
|
136
119
|
|
|
137
|
-
Each
|
|
120
|
+
Each ChartConfig: `{ chartType, data, config: { xKey, yKeys, colors, showGrid, showLegend } }`
|
|
138
121
|
|
|
139
122
|
#### Other
|
|
140
123
|
|
|
141
|
-
| Field | Type |
|
|
142
|
-
|
|
143
|
-
| `sceneGroup` | — |
|
|
144
|
-
| `focus` | — |
|
|
124
|
+
| Field | Type | Description |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `sceneGroup` | — | Scene group reference |
|
|
127
|
+
| `focus` | — | Focus state |
|
|
145
128
|
|
|
146
129
|
### Content Markdown Features
|
|
147
130
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- `## Heading` — headings (ATX style only, no setext)
|
|
151
|
-
- `- bullet` or `* bullet` — unordered lists
|
|
131
|
+
- `## Heading` — ATX headings
|
|
132
|
+
- `- bullet` / `* bullet` — unordered lists
|
|
152
133
|
- `1. item` — ordered lists
|
|
153
134
|
- `` ```language ``` `` — syntax-highlighted code blocks
|
|
154
135
|
- `> blockquote` — styled blockquotes
|
|
155
|
-
- `**bold**` and `*italic*`
|
|
136
|
+
- `**bold**` and `*italic*`
|
|
156
137
|
- `| col | col |` — GFM tables
|
|
157
138
|
- `` — images; `.mp4/.webm/.mov` URLs render as inline video
|
|
158
|
-
- `[text](url)` — external links
|
|
139
|
+
- `[text](url)` — external links
|
|
159
140
|
- `[text](#nodeId)` — navigation links to other slides
|
|
160
|
-
- `[chart:name]` — inline chart embed (
|
|
141
|
+
- `[chart:name]` — inline chart embed (own line)
|
|
161
142
|
- `---` — column delimiter when `layout: "two-column"`
|
|
162
|
-
- Single newline = visible line break
|
|
163
|
-
- Multiple blank lines = visible vertical spacing
|
|
143
|
+
- Single newline = visible line break
|
|
164
144
|
|
|
165
145
|
---
|
|
166
146
|
|
|
@@ -170,95 +150,65 @@ Edges define valid navigation paths — without correct edges, arrow keys won't
|
|
|
170
150
|
|
|
171
151
|
### Handle IDs
|
|
172
152
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
|
176
|
-
|
|
177
|
-
| `s-
|
|
178
|
-
| `s-
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `t-
|
|
182
|
-
| `t-
|
|
183
|
-
| `t-bottom` | target | Bottom | Arrived via **up arrow** from source |
|
|
184
|
-
| `t-top` | target | Top | Arrived via **down arrow** from source |
|
|
153
|
+
| Handle ID | Type | Navigation |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `s-right` | source | Right arrow follows this edge |
|
|
156
|
+
| `s-left` | source | Left arrow follows this edge |
|
|
157
|
+
| `s-bottom` | source | Down arrow follows this edge |
|
|
158
|
+
| `s-top` | source | Up arrow follows this edge |
|
|
159
|
+
| `t-right` | target | Arrived via left arrow |
|
|
160
|
+
| `t-left` | target | Arrived via right arrow |
|
|
161
|
+
| `t-bottom` | target | Arrived via up arrow |
|
|
162
|
+
| `t-top` | target | Arrived via down arrow |
|
|
185
163
|
|
|
186
164
|
### Bidirectional Pair Rule
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
If the user can press right to go from A to B, they must be able to press left to go from B back to A.
|
|
166
|
+
Every forward edge must have a return edge with swapped source/target and swapped handles.
|
|
191
167
|
|
|
192
168
|
### Standard Edge Pairs
|
|
193
169
|
|
|
194
|
-
**Horizontal
|
|
195
|
-
|
|
170
|
+
**Horizontal (spine):**
|
|
196
171
|
```json
|
|
197
172
|
{ "id": "e-a-b", "source": "a", "target": "b", "sourceHandle": "s-right", "targetHandle": "t-left" }
|
|
198
173
|
{ "id": "e-b-a", "source": "b", "target": "a", "sourceHandle": "s-left", "targetHandle": "t-right" }
|
|
199
174
|
```
|
|
200
175
|
|
|
201
|
-
**Drill-
|
|
202
|
-
|
|
176
|
+
**Drill-down / return:**
|
|
203
177
|
```json
|
|
204
178
|
{ "id": "e-parent-child", "source": "parent", "target": "child", "sourceHandle": "s-bottom", "targetHandle": "t-top" }
|
|
205
179
|
{ "id": "e-child-parent", "source": "child", "target": "parent", "sourceHandle": "s-top", "targetHandle": "t-bottom" }
|
|
206
180
|
```
|
|
207
181
|
|
|
208
|
-
**
|
|
209
|
-
|
|
210
|
-
```json
|
|
211
|
-
{ "id": "e-child1-child2", "source": "child1", "target": "child2", "sourceHandle": "s-right", "targetHandle": "t-left" }
|
|
212
|
-
{ "id": "e-child2-child1", "source": "child2", "target": "child1", "sourceHandle": "s-left", "targetHandle": "t-right" }
|
|
213
|
-
```
|
|
182
|
+
**Siblings within a drill-down branch:** same horizontal pattern as spine.
|
|
214
183
|
|
|
215
184
|
### Edge ID Convention
|
|
216
185
|
|
|
217
|
-
Pattern: `e-{source}-{target}` using
|
|
218
|
-
|
|
219
|
-
Examples: `e-cover-problem`, `e-problem-cover`, `e-problem-detail`, `e-detail-problem`
|
|
220
|
-
|
|
221
|
-
### Edge Object Structure
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"id": "e-cover-problem",
|
|
226
|
-
"source": "cover",
|
|
227
|
-
"target": "problem",
|
|
228
|
-
"sourceHandle": "s-right",
|
|
229
|
-
"targetHandle": "t-left"
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
All four fields (`source`, `target`, `sourceHandle`, `targetHandle`) are required. Always set handles explicitly.
|
|
186
|
+
Pattern: `e-{source}-{target}` using node IDs.
|
|
234
187
|
|
|
235
188
|
### Validation Checklist
|
|
236
189
|
|
|
237
|
-
1. Every node has at least one outgoing edge
|
|
238
|
-
2. Every forward edge has a paired return edge
|
|
239
|
-
3. All handle IDs
|
|
240
|
-
4. Source handles start with `s
|
|
241
|
-
5.
|
|
242
|
-
6.
|
|
243
|
-
7. Drill-down children always have `s-top`/`t-bottom` return
|
|
190
|
+
1. Every node has at least one outgoing edge
|
|
191
|
+
2. Every forward edge has a paired return edge
|
|
192
|
+
3. All handle IDs from the valid set of 8
|
|
193
|
+
4. Source handles start with `s-`, target handles with `t-`
|
|
194
|
+
5. First spine node: no incoming `s-right` edge
|
|
195
|
+
6. Last spine node: no outgoing `s-right` edge (or loops to cover)
|
|
196
|
+
7. Drill-down children always have `s-top`/`t-bottom` return to parent
|
|
244
197
|
8. No duplicate edge IDs
|
|
245
198
|
|
|
246
199
|
---
|
|
247
200
|
|
|
248
201
|
## Positioning Grid
|
|
249
202
|
|
|
250
|
-
### Grid Constants
|
|
251
|
-
|
|
252
203
|
| Parameter | Value |
|
|
253
204
|
|---|---|
|
|
254
205
|
| Horizontal spacing | 240px |
|
|
255
206
|
| Vertical spacing | 150px |
|
|
256
|
-
| Node
|
|
257
|
-
| Node height | 70px (in `style` and `measured`) |
|
|
207
|
+
| Node size | 180 x 70 (in `style` and `measured`) |
|
|
258
208
|
|
|
259
209
|
### Spine Row
|
|
260
210
|
|
|
261
|
-
All spine nodes
|
|
211
|
+
All spine nodes at `y: 0`, starting `x: 0`, incrementing by 240:
|
|
262
212
|
|
|
263
213
|
```
|
|
264
214
|
x: 0 240 480 720 960 1200
|
|
@@ -267,23 +217,8 @@ y: 0 [Cover] [Problem] [Solution] [Value] [Features] [CTA]
|
|
|
267
217
|
|
|
268
218
|
### Drill-Down Rows
|
|
269
219
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
- First level: `y: 150`
|
|
273
|
-
- Second level: `y: 300`
|
|
274
|
-
|
|
275
|
-
The first child inherits the parent's `x` position. Additional siblings at the same depth increment `x` by 240.
|
|
276
|
-
|
|
277
|
-
```
|
|
278
|
-
x: 720 960
|
|
279
|
-
y:0 [Value]
|
|
280
|
-
|
|
|
281
|
-
y:150 [Case A] [Case B]
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
- First child: same `x` as parent (720)
|
|
285
|
-
- Second child: parent `x` + 240 (960)
|
|
286
|
-
- Third child: parent `x` + 480 (1200)
|
|
220
|
+
Children placed below parent: first level `y: 150`, second level `y: 300`.
|
|
221
|
+
First child inherits parent's `x`. Siblings increment `x` by 240.
|
|
287
222
|
|
|
288
223
|
### Position Quick Reference
|
|
289
224
|
|
|
@@ -293,131 +228,32 @@ y:150 [Case A] [Case B]
|
|
|
293
228
|
| problem | 240 | 0 | Spine 2 |
|
|
294
229
|
| problem-detail | 240 | 150 | Drill-down under problem |
|
|
295
230
|
| solution | 480 | 0 | Spine 3 |
|
|
296
|
-
| solution-how | 480 | 150 | Drill-down under solution |
|
|
297
231
|
| value | 720 | 0 | Spine 4 |
|
|
298
|
-
| value-case | 720 | 150 | Drill-down under value |
|
|
299
232
|
| cta | 960 | 0 | Spine 5 |
|
|
300
233
|
|
|
301
234
|
### Rules
|
|
302
235
|
|
|
303
|
-
1. Position reflects visual layout in the editor —
|
|
304
|
-
2. Navigation is determined solely by edges and
|
|
305
|
-
3. Spine nodes
|
|
306
|
-
4.
|
|
307
|
-
5. No two nodes should overlap (maintain at least 240px horizontal, 150px vertical separation)
|
|
236
|
+
1. Position reflects visual layout in the editor — no effect on navigation
|
|
237
|
+
2. Navigation is determined solely by edges and handle assignments
|
|
238
|
+
3. Spine nodes at `y: 0`; drill-downs below parent
|
|
239
|
+
4. No two nodes should overlap (240px horizontal, 150px vertical minimum)
|
|
308
240
|
|
|
309
241
|
---
|
|
310
242
|
|
|
311
|
-
##
|
|
312
|
-
|
|
313
|
-
### Slide Type Selection
|
|
314
|
-
|
|
315
|
-
| Content Signal | `type` | Key Fields |
|
|
316
|
-
|---|---|---|
|
|
317
|
-
| Standard text, bullets, headings | `"content"` (default) | `content`, `layout` |
|
|
318
|
-
| Full-viewport data visualization | `"chart"` | `chart` (ChartConfig) |
|
|
319
|
-
| Interactive 3D scene | `"r3f"` | `scene` (R3FSceneConfig) |
|
|
320
|
-
|
|
321
|
-
Most slides are `"content"`. Use `"chart"` or `"r3f"` only when the content is primarily a visualization.
|
|
322
|
-
|
|
323
|
-
### Layout Decisions
|
|
324
|
-
|
|
325
|
-
**When to use `two-column`** — set `layout: "two-column"` and split content on `---`:
|
|
326
|
-
|
|
327
|
-
- Comparison: before/after, old/new, us/them
|
|
328
|
-
- Pros/cons: advantages on left, considerations on right
|
|
329
|
-
- Text + chart: explanation on left, `[chart:name]` on right
|
|
330
|
-
- Text + media: bullets on left, `` on right
|
|
331
|
-
- Dual evidence: two independent supporting points side by side
|
|
332
|
-
|
|
333
|
-
Do not force two-column when content is naturally sequential.
|
|
243
|
+
## Chart Config Structure
|
|
334
244
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
- Cover slides: Title + subtitle, branded
|
|
338
|
-
- Call-to-action slides: Single message, end of presentation
|
|
339
|
-
- Single-message impact slides: One powerful statement or quote
|
|
340
|
-
- Transition slides: Brief pause between major sections
|
|
341
|
-
|
|
342
|
-
Set `centered: false` for content-heavy slides, data slides, comparison slides.
|
|
343
|
-
|
|
344
|
-
**When to use brand font** — set `brandFont: true` for:
|
|
345
|
-
|
|
346
|
-
- Cover slide (first slide)
|
|
347
|
-
- Closing/CTA slide (last slide)
|
|
348
|
-
- High-impact single-message slides
|
|
349
|
-
|
|
350
|
-
Do not use on content-heavy or data slides — brand fonts are display fonts, not body fonts.
|
|
351
|
-
|
|
352
|
-
**When to show branding** — set `showBranding: true` and `brandingText` for:
|
|
353
|
-
|
|
354
|
-
- Cover slide, closing slide, slides likely to be screenshotted
|
|
355
|
-
|
|
356
|
-
Set `showBranding: false` for immersive slides (R3F, full-bleed video) where the overlay is distracting.
|
|
357
|
-
|
|
358
|
-
### Background Treatment
|
|
359
|
-
|
|
360
|
-
**Background image** — use when the outline indicates mood, atmosphere, visual evidence, or impact:
|
|
361
|
-
|
|
362
|
-
- Set `backgroundImageOverlay: true` and `lightText: true` when text appears over the image
|
|
363
|
-
- Image URLs: use Unsplash with query params `?w=1920&q=80`
|
|
364
|
-
|
|
365
|
-
**Background video** — use for cinematic section openers or demo context:
|
|
366
|
-
|
|
367
|
-
- Set `backgroundVideo` to a placeholder: `"PLACEHOLDER: [description of needed video]"`
|
|
368
|
-
- Set `backgroundVideoFit: "cover"` and `backgroundVideoLoop: true`
|
|
369
|
-
- Add to delivery summary for manual upload
|
|
370
|
-
|
|
371
|
-
**Inline video** — for videos embedded via `` in content:
|
|
372
|
-
|
|
373
|
-
- Set URL to placeholder, configure `inlineVideoControls`, `inlineVideoAutoplay`, `inlineVideoLoop`
|
|
374
|
-
- Add to delivery summary
|
|
375
|
-
|
|
376
|
-
**Text contrast** — set `lightText: true` whenever background is dark:
|
|
377
|
-
|
|
378
|
-
- Dark `style.backgroundColor`
|
|
379
|
-
- `backgroundImage` with `backgroundImageOverlay: true`
|
|
380
|
-
- `backgroundVideo` slides
|
|
381
|
-
- `type: "r3f"` with dark scene background
|
|
382
|
-
|
|
383
|
-
### Chart Decisions
|
|
384
|
-
|
|
385
|
-
**Full-viewport chart** (`type: "chart"`) — when the data IS the slide:
|
|
245
|
+
Charts always use the `config` wrapper: `{ chartType, data, config: { xKey, yKeys, ... } }`.
|
|
386
246
|
|
|
247
|
+
**Full-viewport** (`type: "chart"`) — the chart IS the slide:
|
|
387
248
|
```json
|
|
388
|
-
{
|
|
389
|
-
"type": "chart",
|
|
390
|
-
"chart": {
|
|
391
|
-
"chartType": "bar",
|
|
392
|
-
"data": [...],
|
|
393
|
-
"config": { "xKey": "quarter", "yKeys": ["revenue", "cost"], "showGrid": true, "showLegend": true }
|
|
394
|
-
},
|
|
395
|
-
"content": "Optional caption below the chart"
|
|
396
|
-
}
|
|
249
|
+
{ "type": "chart", "chart": { "chartType": "bar", "data": [...], "config": { "xKey": "quarter", "yKeys": ["revenue"], "showGrid": true } } }
|
|
397
250
|
```
|
|
398
251
|
|
|
399
|
-
**Inline
|
|
400
|
-
|
|
252
|
+
**Inline** (`[chart:name]` in content) — data supports a text argument:
|
|
401
253
|
```json
|
|
402
|
-
{
|
|
403
|
-
"content": "## Why this approach wins\n\n[chart:comparison]\n\nClosing statement.",
|
|
404
|
-
"charts": {
|
|
405
|
-
"comparison": {
|
|
406
|
-
"chartType": "radar",
|
|
407
|
-
"data": [...],
|
|
408
|
-
"config": { "xKey": "axis", "yKeys": ["ours", "theirs"], "showLegend": true }
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
254
|
+
{ "content": "## Title\n\n[chart:comparison]", "charts": { "comparison": { "chartType": "radar", "data": [...], "config": { "xKey": "axis", "yKeys": ["ours", "theirs"] } } } }
|
|
412
255
|
```
|
|
413
256
|
|
|
414
|
-
**Chart title rule** — `label` states the insight, not the data category:
|
|
415
|
-
|
|
416
|
-
| Weak | Strong |
|
|
417
|
-
|---|---|
|
|
418
|
-
| "Revenue Data" | "Revenue grew 40% QoQ after the pricing change" |
|
|
419
|
-
| "Response Time" | "Response time dropped 60% after caching rollout" |
|
|
420
|
-
|
|
421
257
|
**Chart type selection:**
|
|
422
258
|
|
|
423
259
|
| Data Pattern | Chart Type |
|
|
@@ -427,386 +263,3 @@ Set `showBranding: false` for immersive slides (R3F, full-bleed video) where the
|
|
|
427
263
|
| Multi-axis comparison | `"radar"` |
|
|
428
264
|
| Part-of-whole | `"pie"` |
|
|
429
265
|
| Volume trend | `"area"` |
|
|
430
|
-
|
|
431
|
-
### R3F Scene Decisions
|
|
432
|
-
|
|
433
|
-
Available scenes:
|
|
434
|
-
- `"rotating-cube"` — interactive cube, good for tech demos. Set `scene.controls: true`.
|
|
435
|
-
- `"particle-field"` — atmospheric particle cloud. Set `scene.controls: false`.
|
|
436
|
-
|
|
437
|
-
Always set `lightText: true` for R3F slides (dark backgrounds).
|
|
438
|
-
|
|
439
|
-
### Topic Badge Conventions
|
|
440
|
-
|
|
441
|
-
Format: `"NN / Section Name"` for numbered sections:
|
|
442
|
-
- `"01 / Problem"`, `"02 / Solution"`, `"03 / Value"`, `"04 / Evidence"`
|
|
443
|
-
|
|
444
|
-
Or plain text: `"Huma Showcase"`, `"Technical Deep Dive"`.
|
|
445
|
-
|
|
446
|
-
---
|
|
447
|
-
|
|
448
|
-
## Visual Intent Mapping
|
|
449
|
-
|
|
450
|
-
The narrative agent annotates each slide with a `**Visual intent:**` label. Map them to JSON treatments:
|
|
451
|
-
|
|
452
|
-
| Visual Intent | type | centered | layout | brandFont | Background | lightText | Notes |
|
|
453
|
-
|---|---|---|---|---|---|---|---|
|
|
454
|
-
| `bookend` | content | true | single | true | optional image/color | if dark bg | Cover and CTA slides. showBranding: true |
|
|
455
|
-
| `chapter-opener` | content | false or true | single | false | backgroundImage + overlay | true | Full-bleed image, sets section mood. Max 3 bullet points. |
|
|
456
|
-
| `impact` | content | true | single | false | optional dark color | if dark bg | Single statement, no bullets. Let whitespace do the work. |
|
|
457
|
-
| `workhorse` | content | false | single or two-column | false | none | false | Standard bullets/content. The default treatment. |
|
|
458
|
-
| `evidence` | content or chart | false | single or two-column | false | none | false | Data-forward: chart, table, or comparison. |
|
|
459
|
-
| `breathing-room` | content | true | single | false | backgroundImage + overlay OR style.backgroundColor | true | Minimal text. Visual pause. Resets audience attention. |
|
|
460
|
-
|
|
461
|
-
When the outline lacks visual intent annotations, infer them:
|
|
462
|
-
- First slide → bookend
|
|
463
|
-
- Last slide → bookend
|
|
464
|
-
- Slide after 2+ consecutive bullet-heavy slides → breathing-room or impact
|
|
465
|
-
- Slide introducing a new topic section → chapter-opener
|
|
466
|
-
- Slide with chart data or comparison → evidence
|
|
467
|
-
- Everything else → workhorse
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## Slide Recipes
|
|
472
|
-
|
|
473
|
-
Complete `data` field patterns for common slide types.
|
|
474
|
-
|
|
475
|
-
### Cover / Call to Action
|
|
476
|
-
|
|
477
|
-
```json
|
|
478
|
-
{
|
|
479
|
-
"label": "[Title or Ask]",
|
|
480
|
-
"topic": "[Company or Meeting Context]",
|
|
481
|
-
"content": "One sentence: what this is and for whom. (Cover) — or — what happens next and when. (CTA)",
|
|
482
|
-
"centered": true,
|
|
483
|
-
"brandFont": true,
|
|
484
|
-
"showBranding": true,
|
|
485
|
-
"brandingText": "[company.domain]"
|
|
486
|
-
}
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
### Standard Bullets
|
|
490
|
-
|
|
491
|
-
```json
|
|
492
|
-
{
|
|
493
|
-
"label": "Headline claim that makes the point",
|
|
494
|
-
"topic": "01 / Problem",
|
|
495
|
-
"content": "## Headline claim in one sentence\n\n- Specific point with a number or named entity\n- Second point — consequence or contrast\n- Third point — the implication\n\nClosing sentence bridging to the next slide.",
|
|
496
|
-
"notes": "Talking point not on screen. Objection signal: 'If they ask X, navigate to [node-id].' Time: 2 minutes max.",
|
|
497
|
-
"centered": false
|
|
498
|
-
}
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
### Impact Statement
|
|
502
|
-
|
|
503
|
-
```json
|
|
504
|
-
{
|
|
505
|
-
"label": "The Shift",
|
|
506
|
-
"topic": "01 / Problem",
|
|
507
|
-
"content": "The market has permanently changed.\n\n**Batch scheduling is structurally incompatible with same-day delivery expectations.**",
|
|
508
|
-
"notes": "Pause. Do not advance immediately.",
|
|
509
|
-
"centered": true
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
### Two-Column Comparison
|
|
514
|
-
|
|
515
|
-
```json
|
|
516
|
-
{
|
|
517
|
-
"label": "Old Way vs. New Way",
|
|
518
|
-
"topic": "02 / Solution",
|
|
519
|
-
"content": "## Today\n- 3 systems, no shared state\n- 11 hours/week reconciling exports\n\n---\n\n## With [Product]\n- Single live record across all systems\n- Reports update in real time",
|
|
520
|
-
"layout": "two-column",
|
|
521
|
-
"centered": false
|
|
522
|
-
}
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
### Text + Inline Chart
|
|
526
|
-
|
|
527
|
-
```json
|
|
528
|
-
{
|
|
529
|
-
"label": "The Data Confirms It",
|
|
530
|
-
"topic": "03 / Evidence",
|
|
531
|
-
"content": "## Continuous monitoring outperforms batch on every metric\n\nThree field studies. Same result each time.\n\n---\n\n[chart:comparison]",
|
|
532
|
-
"charts": {
|
|
533
|
-
"comparison": {
|
|
534
|
-
"chartType": "bar",
|
|
535
|
-
"data": [{ "metric": "Uptime %", "ours": 98.2, "baseline": 91.5 }],
|
|
536
|
-
"config": { "xKey": "metric", "yKeys": ["ours", "baseline"], "showGrid": true, "showLegend": true }
|
|
537
|
-
}
|
|
538
|
-
},
|
|
539
|
-
"layout": "two-column",
|
|
540
|
-
"centered": false
|
|
541
|
-
}
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
### Proof Point / Customer Quote
|
|
545
|
-
|
|
546
|
-
```json
|
|
547
|
-
{
|
|
548
|
-
"label": "Summit Health: Year One",
|
|
549
|
-
"topic": "04 / Proof",
|
|
550
|
-
"content": "## 2.3 hours of admin time eliminated per nurse per shift\n\n> \"The conflicts surfaced automatically.\"\n> — VP Operations, Summit Health (42 facilities)\n\n- 38% reduction in shift coverage failures\n- Full rollout across 6 units in 11 weeks",
|
|
551
|
-
"notes": "If they ask about rollout timeline, the drill-down below has the full breakdown.",
|
|
552
|
-
"centered": false
|
|
553
|
-
}
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Section Opener with Background Image
|
|
557
|
-
|
|
558
|
-
```json
|
|
559
|
-
{
|
|
560
|
-
"label": "The Opportunity",
|
|
561
|
-
"topic": "03 / Opportunity",
|
|
562
|
-
"content": "## A $40B market with no dominant platform\n\nEvery competitor is solving scheduling.\nNobody is solving the coordination layer.",
|
|
563
|
-
"backgroundImage": "https://images.unsplash.com/photo-XXXXX?w=1920&q=80",
|
|
564
|
-
"backgroundImageFit": "cover",
|
|
565
|
-
"backgroundImageOverlay": true,
|
|
566
|
-
"lightText": true,
|
|
567
|
-
"centered": false
|
|
568
|
-
}
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
### Styled Background Color Slide
|
|
572
|
-
|
|
573
|
-
```json
|
|
574
|
-
{
|
|
575
|
-
"label": "Styling & Branding",
|
|
576
|
-
"topic": "04 / Features",
|
|
577
|
-
"content": "## Per-slide styling\n\n- Custom background colors\n- Light text mode for dark backgrounds\n- Brand font for titles",
|
|
578
|
-
"lightText": true,
|
|
579
|
-
"brandFont": true,
|
|
580
|
-
"centered": true,
|
|
581
|
-
"showBranding": true,
|
|
582
|
-
"brandingText": "huma.energy"
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
With `style.backgroundColor` set at the node level:
|
|
587
|
-
|
|
588
|
-
```json
|
|
589
|
-
"style": { "width": 180, "height": 70, "backgroundColor": "#1a1a2e" }
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
## Complete Reference Example
|
|
595
|
-
|
|
596
|
-
A 7-node presentation demonstrating key slide types. This is the minimum viable structure the design agent should know how to produce.
|
|
597
|
-
|
|
598
|
-
```json
|
|
599
|
-
{
|
|
600
|
-
"meta": {
|
|
601
|
-
"name": "Graph-Based Presentations"
|
|
602
|
-
},
|
|
603
|
-
"nodes": [
|
|
604
|
-
{
|
|
605
|
-
"id": "cover",
|
|
606
|
-
"type": "huma",
|
|
607
|
-
"position": { "x": 0, "y": 0 },
|
|
608
|
-
"data": {
|
|
609
|
-
"label": "Graph-Based Presentations",
|
|
610
|
-
"topic": "Huma Showcase",
|
|
611
|
-
"content": "Non-linear storytelling for technical teams.\n\nNavigate with arrow keys. Press **down** to drill into any topic.",
|
|
612
|
-
"centered": true,
|
|
613
|
-
"brandFont": true,
|
|
614
|
-
"showBranding": true,
|
|
615
|
-
"brandingText": "huma.energy"
|
|
616
|
-
},
|
|
617
|
-
"style": { "width": 180, "height": 70 },
|
|
618
|
-
"measured": { "width": 180, "height": 70 }
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
"id": "problem",
|
|
622
|
-
"type": "huma",
|
|
623
|
-
"position": { "x": 240, "y": 0 },
|
|
624
|
-
"data": {
|
|
625
|
-
"label": "The Problem",
|
|
626
|
-
"topic": "01 / Problem",
|
|
627
|
-
"content": "## Linear tools break complex stories\n\n- Cannot branch based on audience questions\n- Skipping sections breaks the flow\n- Presenters get lost navigating dense topics\n- No private view for speaker notes during screen share\n\nEvery meeting is different. The presentation should adapt.",
|
|
628
|
-
"notes": "Pause here and ask: 'Does this match what you experience with your current tooling?' Key talking point: the fourth bullet resonates most with technical presenters.",
|
|
629
|
-
"centered": false
|
|
630
|
-
},
|
|
631
|
-
"style": { "width": 180, "height": 70 },
|
|
632
|
-
"measured": { "width": 180, "height": 70 }
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
"id": "problem-detail",
|
|
636
|
-
"type": "huma",
|
|
637
|
-
"position": { "x": 240, "y": 150 },
|
|
638
|
-
"data": {
|
|
639
|
-
"label": "Value Breakdown",
|
|
640
|
-
"topic": "03 / Value",
|
|
641
|
-
"content": "## Flexible narratives\nAdapt flow to audience type and questions in real time\n\n---\n\n## Consistent style\nReuse front-end web styling and components directly\n\n## Professional delivery\nSpeaker notes and timing without the audience seeing them",
|
|
642
|
-
"layout": "two-column",
|
|
643
|
-
"centered": false
|
|
644
|
-
},
|
|
645
|
-
"style": { "width": 180, "height": 70 },
|
|
646
|
-
"measured": { "width": 180, "height": 70 }
|
|
647
|
-
},
|
|
648
|
-
{
|
|
649
|
-
"id": "value",
|
|
650
|
-
"type": "huma",
|
|
651
|
-
"position": { "x": 480, "y": 0 },
|
|
652
|
-
"data": {
|
|
653
|
-
"label": "The Value",
|
|
654
|
-
"topic": "03 / Value",
|
|
655
|
-
"content": "## Why this approach wins\n\n[chart:value]\n\nAdapt to the audience. Reuse web components. Deliver with confidence.",
|
|
656
|
-
"charts": {
|
|
657
|
-
"value": {
|
|
658
|
-
"chartType": "radar",
|
|
659
|
-
"data": [
|
|
660
|
-
{ "axis": "Flexibility", "graph": 95, "traditional": 30 },
|
|
661
|
-
{ "axis": "Visual Quality", "graph": 90, "traditional": 50 },
|
|
662
|
-
{ "axis": "Interactivity", "graph": 85, "traditional": 20 },
|
|
663
|
-
{ "axis": "Ease of Use", "graph": 60, "traditional": 90 },
|
|
664
|
-
{ "axis": "Consistency", "graph": 85, "traditional": 40 },
|
|
665
|
-
{ "axis": "Tech Signal", "graph": 95, "traditional": 15 }
|
|
666
|
-
],
|
|
667
|
-
"config": {
|
|
668
|
-
"xKey": "axis",
|
|
669
|
-
"yKeys": ["graph", "traditional"],
|
|
670
|
-
"showGrid": true,
|
|
671
|
-
"showLegend": true
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
},
|
|
675
|
-
"centered": false
|
|
676
|
-
},
|
|
677
|
-
"style": { "width": 180, "height": 70 },
|
|
678
|
-
"measured": { "width": 180, "height": 70 }
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
"id": "feat-3d",
|
|
682
|
-
"type": "huma",
|
|
683
|
-
"position": { "x": 720, "y": 0 },
|
|
684
|
-
"data": {
|
|
685
|
-
"label": "3D Scenes",
|
|
686
|
-
"topic": "04 / Features",
|
|
687
|
-
"type": "r3f",
|
|
688
|
-
"scene": {
|
|
689
|
-
"component": "rotating-cube",
|
|
690
|
-
"controls": true,
|
|
691
|
-
"background": "#1a1a2e"
|
|
692
|
-
},
|
|
693
|
-
"content": "Interactive React Three Fiber scene.\n\nDrag to rotate. Scroll to zoom. Scenes load from a component registry.",
|
|
694
|
-
"lightText": true
|
|
695
|
-
},
|
|
696
|
-
"style": { "width": 180, "height": 70 },
|
|
697
|
-
"measured": { "width": 180, "height": 70 }
|
|
698
|
-
},
|
|
699
|
-
{
|
|
700
|
-
"id": "feat-bgimage",
|
|
701
|
-
"type": "huma",
|
|
702
|
-
"position": { "x": 960, "y": 0 },
|
|
703
|
-
"data": {
|
|
704
|
-
"label": "Background Image",
|
|
705
|
-
"topic": "04 / Features",
|
|
706
|
-
"content": "## Full-bleed imagery\n\nUpload any image as a slide background. Toggle overlay for text readability.\n\n- Cover/contain fit modes\n- Dark overlay toggle\n- Light text auto-detection",
|
|
707
|
-
"backgroundImage": "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1920&q=80",
|
|
708
|
-
"backgroundImageFit": "cover",
|
|
709
|
-
"backgroundImageOverlay": true,
|
|
710
|
-
"lightText": true,
|
|
711
|
-
"centered": false
|
|
712
|
-
},
|
|
713
|
-
"style": { "width": 180, "height": 70 },
|
|
714
|
-
"measured": { "width": 180, "height": 70 }
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
"id": "end",
|
|
718
|
-
"type": "huma",
|
|
719
|
-
"position": { "x": 1200, "y": 0 },
|
|
720
|
-
"data": {
|
|
721
|
-
"label": "Get Started",
|
|
722
|
-
"content": "Fork this template to build your own.\n\nEvery slide, chart, and scene is editable in the graph editor.",
|
|
723
|
-
"centered": true,
|
|
724
|
-
"brandFont": true,
|
|
725
|
-
"showBranding": true,
|
|
726
|
-
"brandingText": "huma.energy"
|
|
727
|
-
},
|
|
728
|
-
"style": { "width": 180, "height": 70 },
|
|
729
|
-
"measured": { "width": 180, "height": 70 }
|
|
730
|
-
}
|
|
731
|
-
],
|
|
732
|
-
"edges": [
|
|
733
|
-
{ "id": "e-cover-problem", "source": "cover", "target": "problem", "sourceHandle": "s-right", "targetHandle": "t-left" },
|
|
734
|
-
{ "id": "e-problem-cover", "source": "problem", "target": "cover", "sourceHandle": "s-left", "targetHandle": "t-right" },
|
|
735
|
-
{ "id": "e-problem-value", "source": "problem", "target": "value", "sourceHandle": "s-right", "targetHandle": "t-left" },
|
|
736
|
-
{ "id": "e-value-problem", "source": "value", "target": "problem", "sourceHandle": "s-left", "targetHandle": "t-right" },
|
|
737
|
-
{ "id": "e-problem-detail", "source": "problem", "target": "problem-detail", "sourceHandle": "s-bottom", "targetHandle": "t-top" },
|
|
738
|
-
{ "id": "e-detail-problem", "source": "problem-detail", "target": "problem", "sourceHandle": "s-top", "targetHandle": "t-bottom" },
|
|
739
|
-
{ "id": "e-value-3d", "source": "value", "target": "feat-3d", "sourceHandle": "s-right", "targetHandle": "t-left" },
|
|
740
|
-
{ "id": "e-3d-value", "source": "feat-3d", "target": "value", "sourceHandle": "s-left", "targetHandle": "t-right" },
|
|
741
|
-
{ "id": "e-3d-bgimage", "source": "feat-3d", "target": "feat-bgimage", "sourceHandle": "s-right", "targetHandle": "t-left" },
|
|
742
|
-
{ "id": "e-bgimage-3d", "source": "feat-bgimage", "target": "feat-3d", "sourceHandle": "s-left", "targetHandle": "t-right" },
|
|
743
|
-
{ "id": "e-bgimage-end", "source": "feat-bgimage", "target": "end", "sourceHandle": "s-right", "targetHandle": "t-left" },
|
|
744
|
-
{ "id": "e-end-bgimage", "source": "end", "target": "feat-bgimage", "sourceHandle": "s-left", "targetHandle": "t-right" }
|
|
745
|
-
]
|
|
746
|
-
}
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
### Additional Patterns from the Demo
|
|
750
|
-
|
|
751
|
-
**Styled background color node** (node-level `style.backgroundColor`):
|
|
752
|
-
|
|
753
|
-
```json
|
|
754
|
-
{
|
|
755
|
-
"id": "feat-style",
|
|
756
|
-
"type": "huma",
|
|
757
|
-
"position": { "x": 1920, "y": 150 },
|
|
758
|
-
"data": {
|
|
759
|
-
"label": "Styling & Branding",
|
|
760
|
-
"topic": "04 / Features",
|
|
761
|
-
"content": "## Per-slide styling\n\n- Custom background colors\n- Light text mode for dark backgrounds\n- Brand font for titles",
|
|
762
|
-
"lightText": true,
|
|
763
|
-
"brandFont": true,
|
|
764
|
-
"centered": true,
|
|
765
|
-
"showBranding": true,
|
|
766
|
-
"brandingText": "huma.energy"
|
|
767
|
-
},
|
|
768
|
-
"style": { "width": 180, "height": 70, "backgroundColor": "#1a1a2e" },
|
|
769
|
-
"measured": { "width": 180, "height": 70 }
|
|
770
|
-
}
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
**Second-level drill-down** (y:300, child of a y:150 node):
|
|
774
|
-
|
|
775
|
-
```json
|
|
776
|
-
{
|
|
777
|
-
"id": "feat-tables",
|
|
778
|
-
"type": "huma",
|
|
779
|
-
"position": { "x": 960, "y": 300 },
|
|
780
|
-
"data": { "label": "Tables", "topic": "04 / Features", "content": "...", "centered": false },
|
|
781
|
-
"style": { "width": 180, "height": 70 },
|
|
782
|
-
"measured": { "width": 180, "height": 70 }
|
|
783
|
-
}
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
Edges: `feat-markdown` (y:150) → `feat-tables` (y:300) via `s-bottom`/`t-top`.
|
|
787
|
-
|
|
788
|
-
**Sibling drill-downs connected horizontally** (same y-level, `s-right`/`t-left`):
|
|
789
|
-
|
|
790
|
-
```json
|
|
791
|
-
{ "id": "e-md-charts", "source": "feat-markdown", "target": "feat-charts", "sourceHandle": "s-right", "targetHandle": "t-left" }
|
|
792
|
-
{ "id": "e-charts-md", "source": "feat-charts", "target": "feat-markdown", "sourceHandle": "s-left", "targetHandle": "t-right" }
|
|
793
|
-
```
|
|
794
|
-
|
|
795
|
-
Both at y:150, connected horizontally within the drill-down branch.
|
|
796
|
-
|
|
797
|
-
---
|
|
798
|
-
|
|
799
|
-
## Media Delivery Summary Format
|
|
800
|
-
|
|
801
|
-
After generating the JSON, list any slides with placeholder media:
|
|
802
|
-
|
|
803
|
-
```
|
|
804
|
-
Slides requiring manual media upload:
|
|
805
|
-
- "Cover" — background video: [description]
|
|
806
|
-
- "Demo" — inline video: [description]
|
|
807
|
-
|
|
808
|
-
Upload via POST /api/slide-images/upload (multipart/form-data, field: "file")
|
|
809
|
-
Returns { ok: true, key, url } — replace placeholder with returned url
|
|
810
|
-
Accepted: all image/*, video/mp4, video/webm, video/quicktime
|
|
811
|
-
Max size: 50MB
|
|
812
|
-
```
|