figmatk 0.3.0 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +49 -14
  4. package/cli.mjs +2 -0
  5. package/commands/render.mjs +56 -0
  6. package/lib/fig-deck.mjs +4 -4
  7. package/lib/rasterizer/deck-rasterizer.mjs +228 -0
  8. package/lib/rasterizer/download-font.mjs +57 -0
  9. package/lib/rasterizer/font-resolver.mjs +602 -0
  10. package/lib/rasterizer/fonts/DarkerGrotesque-400.ttf +0 -0
  11. package/lib/rasterizer/fonts/DarkerGrotesque-500.ttf +0 -0
  12. package/lib/rasterizer/fonts/DarkerGrotesque-600.ttf +0 -0
  13. package/lib/rasterizer/fonts/Inter-Regular.ttf +0 -0
  14. package/lib/rasterizer/fonts/Inter-Variable.ttf +0 -0
  15. package/lib/rasterizer/fonts/Inter.var.woff2 +0 -0
  16. package/lib/rasterizer/fonts/darker-grotesque-patched-400-normal.woff2 +0 -0
  17. package/lib/rasterizer/fonts/darker-grotesque-patched-500-normal.woff2 +0 -0
  18. package/lib/rasterizer/fonts/darker-grotesque-patched-600-normal.woff2 +0 -0
  19. package/lib/rasterizer/fonts/darker-grotesque-patched-700-normal.woff2 +0 -0
  20. package/lib/rasterizer/fonts/inter-v3-400-italic.woff2 +0 -0
  21. package/lib/rasterizer/fonts/inter-v3-400-normal.woff2 +0 -0
  22. package/lib/rasterizer/fonts/inter-v3-500-normal.woff2 +0 -0
  23. package/lib/rasterizer/fonts/inter-v3-600-normal.woff2 +0 -0
  24. package/lib/rasterizer/fonts/inter-v3-700-italic.woff2 +0 -0
  25. package/lib/rasterizer/fonts/inter-v3-700-normal.woff2 +0 -0
  26. package/lib/rasterizer/fonts/inter-v3-meta.json +6 -0
  27. package/lib/rasterizer/render-report-lib.mjs +127 -0
  28. package/lib/rasterizer/render-report.mjs +25 -0
  29. package/lib/rasterizer/svg-builder.mjs +571 -0
  30. package/lib/rasterizer/test-render.mjs +63 -0
  31. package/lib/template-deck.mjs +573 -148
  32. package/manifest.json +21 -0
  33. package/mcp-server.mjs +184 -20
  34. package/package.json +17 -2
  35. package/skills/figma-slides-creator/SKILL.md +79 -172
  36. package/skills/figma-template-builder/SKILL.md +158 -0
@@ -6,10 +6,10 @@ description: >
6
6
  clone or remove slides, or produce a .deck file for Figma Slides.
7
7
  Powered by FigmaTK under the hood.
8
8
  metadata:
9
- version: "0.3.0"
9
+ version: "0.3.7"
10
10
  ---
11
11
 
12
- # Figma Slides Creator
12
+ # FigmaTK Skill
13
13
 
14
14
  ## ⚠️ Never open .deck files directly
15
15
 
@@ -23,132 +23,39 @@ To let the user view the result: tell them to **open the file in Figma Desktop**
23
23
 
24
24
  | Task | Approach |
25
25
  |------|----------|
26
- | Create from scratch | **Path A** `figmatk_create_deck` MCP tool |
27
- | Create from a `.deck` template | **Path B** `figmatk_list_template_layouts` + `figmatk_create_from_template` |
28
- | Edit text or images in an existing deck | `figmatk_update_text`, `figmatk_insert_image` |
29
- | Clone, remove, or restructure slides | `figmatk_clone_slide`, `figmatk_remove_slide` |
30
- | Inspect structure or read content | `figmatk_inspect`, `figmatk_list_text` |
26
+ | Create a new deck from scratch | Use the high-level JS API (`lib/api.mjs`) |
27
+ | Edit text or images in an existing deck | Use MCP tools (`figmatk_update_text`, `figmatk_insert_image`) |
28
+ | Clone, remove, or restructure slides | Use MCP tools (`figmatk_clone_slide`, `figmatk_remove_slide`) |
29
+ | Inspect structure or read content | Use MCP tools (`figmatk_inspect`, `figmatk_list_text`) |
31
30
 
32
31
  ---
33
32
 
34
- ## File locationsalways use /tmp
33
+ ## Path ACreate from Scratch (High-Level API)
35
34
 
36
- **All files go in `/tmp/`** — scripts, output decks, images, everything. Never write to the Desktop, Documents, Downloads, or any user directory. Never create intermediate notes or reference markdown files. Just build and save the deck.
35
+ Use this when the user wants a new presentation. Write a Node.js script and execute it.
37
36
 
38
- ---
39
-
40
- ## Path B Create from a Template (preferred when user provides a .deck file)
41
-
42
- Use this path when the user provides a `.deck` template file. The output deck inherits all fonts, colors, spacing, and visual design from the template verbatim.
43
-
44
- ### Step 1 — Inspect the template
45
-
46
- ```
47
- figmatk_list_template_layouts("/path/to/template.deck")
48
- ```
49
-
50
- Returns a catalog of all available slide layouts. Each entry includes:
51
- - `slideId` — the ID to reference this layout
52
- - Text fields — editable TEXT nodes with their names and current content
53
- - Image placeholders — FRAME nodes with IMAGE fill (these need a real image)
54
-
55
- **Read the catalog carefully before picking layouts:**
56
- - Match each slide's purpose to your content (the existing text in the template is a strong hint — e.g. "Use this slide to introduce the big problem" → use for your problem statement)
57
- - Slides with image placeholders need an appropriate image — the surrounding text should describe what's shown in that image
58
- - Slides with `SHAPE_WITH_TEXT` pill labels (MONTH XX YEAR, TAGLINE, CONFIDENTIAL) cannot be changed programmatically — tell the user to update those in Figma
59
-
60
- ### Step 2 — Create the deck
61
-
62
- ```
63
- figmatk_create_from_template({
64
- template: "/path/to/template.deck",
65
- output: "/tmp/my-deck.deck",
66
- slides: [
67
- { slideId: "1:74", text: { "Title": "My Company" } },
68
- { slideId: "1:112", text: { "Header 1": "The problem.", "Body 1": "Description here." } },
69
- { slideId: "1:643", text: { "Thank you": "Thank you!" } }
70
- ]
71
- })
72
- ```
73
-
74
- Only pass text fields that exist in the layout's catalog — extra fields are silently ignored.
75
-
76
- ---
77
-
78
- ## Path A — Create from Scratch (MCP tool — no template)
79
-
80
- **Always use this path.** No npm install, no scripts, no workspace setup.
81
-
82
- Call `figmatk_create_deck` with a structured slide description:
83
-
84
- ```json
85
- {
86
- "output": "/tmp/my-deck.deck",
87
- "title": "My Presentation",
88
- "theme": "midnight",
89
- "slides": [
90
- { "type": "title", "title": "My Presentation", "subtitle": "A subtitle" },
91
- { "type": "bullets", "title": "Key Points", "bullets": ["Point one", "Point two", "Point three"] },
92
- { "type": "two-column", "title": "Comparison", "leftText": "Left side content", "rightText": "Right side content" },
93
- { "type": "stat", "title": "By the numbers", "stat": "42%", "caption": "of users prefer this" },
94
- { "type": "image-full", "image": "/tmp/photo.jpg", "title": "Caption text" },
95
- { "type": "closing", "title": "Thank you", "subtitle": "Questions?" }
96
- ]
97
- }
98
- ```
99
-
100
- ### Slide types
101
-
102
- | Type | Fields |
103
- |------|--------|
104
- | `title` | `title`, `subtitle` |
105
- | `bullets` | `title`, `bullets` (array) |
106
- | `two-column` | `title`, `leftText`, `rightText`, `image` (right side) |
107
- | `stat` | `title`, `stat` (big number), `caption` |
108
- | `image-full` | `image` (path), `title`, `body` (overlay text) |
109
- | `closing` | `title`, `subtitle` |
110
-
111
- ### Themes
112
-
113
- `midnight` · `ocean` · `forest` · `coral` · `terracotta` · `minimal`
114
-
115
- Each theme handles backgrounds, accent colors, and text colors automatically.
116
-
117
- ---
118
-
119
- ## Path A2 — Create from Scratch (Node.js script fallback)
120
-
121
- Only use this if `figmatk_create_deck` is unavailable or you need layout control beyond what the MCP tool offers.
122
-
123
- ### Step 1 — Set up workspace (MANDATORY — never skip)
124
-
125
- ```bash
126
- [ -d /tmp/figmatk-ws/node_modules ] || (mkdir -p /tmp/figmatk-ws && cd /tmp/figmatk-ws && npm init -y && npm install figmatk)
127
- ```
128
-
129
- ### Step 2 — Write script to `/tmp/figmatk-ws/deck.mjs`
130
-
131
- **Always use bare specifier** `import { Deck } from 'figmatk'` — never a file path.
37
+ > **Import path:** `figmatk` is an npm package. Import from the installed package:
38
+ > ```javascript
39
+ > import { Deck } from 'figmatk';
40
+ > ```
132
41
 
133
42
  ```javascript
134
43
  import { Deck } from 'figmatk';
135
44
 
136
- function hex(h) {
137
- return { r: parseInt(h.slice(1,3),16)/255, g: parseInt(h.slice(3,5),16)/255, b: parseInt(h.slice(5,7),16)/255 };
138
- }
139
-
140
45
  const deck = await Deck.create('My Presentation');
141
- const slide = deck.addBlankSlide();
142
- slide.setBackground('Black');
143
- slide.addText('Slide Title', { style: 'Title', color: 'White', x: 64, y: 80, width: 1792, align: 'LEFT' });
144
- await deck.save('/tmp/my-presentation.deck');
145
- console.log('Done');
146
- ```
147
46
 
148
- ### Step 3 Run
149
-
150
- ```bash
151
- node /tmp/figmatk-ws/deck.mjs
47
+ const slide = deck.addBlankSlide(); // template blank slide auto-removed
48
+ slide.setBackground('Black'); // named color — see list below
49
+ slide.addText('Slide Title', {
50
+ style: 'Title', color: 'White',
51
+ x: 64, y: 80, width: 1792, align: 'LEFT'
52
+ });
53
+ slide.addText('Subtitle', {
54
+ style: 'Body 1', color: 'Grey',
55
+ x: 64, y: 240, width: 1200, align: 'LEFT'
56
+ });
57
+
58
+ await deck.save('/path/to/output.deck');
152
59
  ```
153
60
 
154
61
  ### ⚠️ Critical gotchas
@@ -158,11 +65,9 @@ node /tmp/figmatk-ws/deck.mjs
158
65
  | `setBackground` with hex | `s.setBackground('#1A1A1A')` | `s.setBackground('Black')` |
159
66
  | `setBackground` with raw RGB | `s.setBackground({ r:0.1, g:0.1, b:0.1 })` | `s.setBackground('Black')` — raw RGB silently renders white |
160
67
  | Shape method signature | `s.addRectangle({ x:0, y:0, width:100 })` | `s.addRectangle(0, 0, 100, 100, opts)` |
161
- | Shape fill color | `{ fill: { r:1, g:0, b:0 } }` | `{ fill: '#F4900C' }` or `{ fill: 'Red' }` — hex strings and named colors work directly |
68
+ | Shape fill color | `{ fill: '#F4900C' }` | `{ fill: hex('#F4900C') }` — use the hex() helper |
162
69
  | `addLine` options | `{ strokeColor: ..., strokeWeight: 2 }` | `{ color: 'Black', weight: 2 }` |
163
70
  | `align` value | `align: 'left'` | `align: 'LEFT'` (uppercase) |
164
- | `addImage` without await | `slide.addImage('/tmp/photo.jpg')` | `await slide.addImage('/tmp/photo.jpg')` — async, images silently missing without await |
165
- | `addImage` old signature | `await slide.addImage(x, y, w, h, path)` | `await slide.addImage(path, { x, y, width, height })` — path is first arg now |
166
71
 
167
72
  ### Hex color helper (for shape fills)
168
73
 
@@ -186,17 +91,13 @@ function hex(h) {
186
91
  | `Body 3` | 24pt | Regular | Captions, labels |
187
92
  | `Note` | 20pt | Regular | Footnotes, sources |
188
93
 
189
- ### Colors for `setBackground()`
190
-
191
- Accepts named colors, hex strings, or designer aliases — **when using figmatk 0.0.12+ from the workspace install**.
94
+ ### Named colors for `setBackground()`
192
95
 
193
- **Named colors** (case-sensitive, from the Light Slides theme):
96
+ > **Case-sensitive.** `'Black'` works, `'black'` does not.
194
97
 
195
98
  `'Black'`, `'White'`, `'Grey'`, `'Blue'`, `'Red'`, `'Yellow'`, `'Green'`, `'Orange'`, `'Pink'`, `'Purple'`, `'Teal'`, `'Violet'`, `'Persimmon'`, `'Pale Pink'`, `'Pale Blue'`, `'Pale Green'`, `'Pale Teal'`, `'Pale Purple'`, `'Pale Persimmon'`, `'Pale Violet'`, `'Pale Red'`, `'Pale Yellow'`
196
99
 
197
- **Hex strings** (0.0.12+): `slide.setBackground('#C8102E')`
198
-
199
- **Designer aliases** (0.0.12+): `slide.setBackground('navy')`, `slide.setBackground('coral')`, `slide.setBackground('terracotta')` etc.
100
+ Use `'Black'` for dark backgrounds, `'White'` for light. For custom slide backgrounds, use the closest named color — **not hex**.
200
101
 
201
102
  ### Slide dimensions
202
103
 
@@ -214,8 +115,8 @@ slide.addDiamond(x, y, width, height, opts)
214
115
  slide.addTriangle(x, y, width, height, opts)
215
116
  slide.addStar(x, y, width, height, opts)
216
117
  slide.addLine(x1, y1, x2, y2, opts) // opts: color, weight
217
- await slide.addImage(pathOrBuf, opts) // ⚠️ ASYNC — must use await; opts: x, y, width, height (default: full slide 1920×1080), cornerRadius, opacity
218
- slide.addTable(x, y, data, opts) // 2D string array; opts: width, colWidths, rowHeight
118
+ slide.addImage(path, opts) // opts: x, y, width, height
119
+ slide.addTable(data, opts) // 2D string array; opts: x, y, width, colWidths, rowHeight
219
120
  slide.addSVG(x, y, width, svgPathOrBuf, opts)
220
121
  ```
221
122
 
@@ -240,7 +141,6 @@ Use this when the user provides a `.deck` file to modify.
240
141
 
241
142
  | Tool | Purpose |
242
143
  |------|---------|
243
- | `figmatk_create_deck` | **Create a new deck from scratch** — no npm install needed |
244
144
  | `figmatk_inspect` | Node hierarchy tree — structure, node IDs, slide count |
245
145
  | `figmatk_list_text` | All text strings and image hashes per slide |
246
146
  | `figmatk_list_overrides` | Editable override keys per symbol (component) |
@@ -249,37 +149,55 @@ Use this when the user provides a `.deck` file to modify.
249
149
  | `figmatk_clone_slide` | Deep-clone a slide with new text and images |
250
150
  | `figmatk_remove_slide` | Mark slides as REMOVED (never deleted) |
251
151
  | `figmatk_roundtrip` | Decode + re-encode for pipeline validation |
152
+ | `figmatk_render_slide` | Render a slide to image (inline WebP or saved PNG) |
252
153
 
253
154
  ---
254
155
 
255
- ## Design Philosophy
256
-
257
- Think like a **professional PowerPoint designer**, not an AI generating slides. Every deck must feel like it was made by a human who spent a day on it.
156
+ ## Path C — Visual QA (Render + Inspect)
258
157
 
259
- ### Deck structure use this template every time
158
+ After creating or modifying a deck, **always render and visually inspect** the output. This catches issues that text inspection misses: overflowing text, broken layouts, wrong colors, misaligned elements.
260
159
 
261
- A proper deck has a clear spine. Follow this slide order:
160
+ ### Workflow
262
161
 
263
- | # | Slide type | Purpose |
264
- |---|-----------|---------|
265
- | 1 | **Title** | Dark bg, big title, subtitle, presenter name |
266
- | 2 | **Agenda / Overview** | 3–5 bullet topics, light bg |
267
- | 3–N | **Content slides** | Vary layout each slide — see below |
268
- | N+1 | **Section divider** (optional) | Bold colour block to signal a new chapter |
269
- | Last | **Closing / CTA** | Dark bg mirrors title slide — "Thank you", next steps, contact |
162
+ 1. Render each slide at preview size (returns inline WebP image):
163
+ ```
164
+ figmatk_render_slide(path: "/tmp/my-deck.deck", slide: 1)
165
+ ```
166
+ 2. Inspect the returned image for:
167
+ - Text overflowing its bounding box or clipped
168
+ - Layout misalignment or overlapping elements
169
+ - Wrong colors or missing backgrounds
170
+ - Missing images or broken fills
171
+ 3. If issues are found, fix them and re-render
172
+ 4. For full-resolution export:
173
+ ```
174
+ figmatk_render_slide(path: "/tmp/my-deck.deck", slide: 1, output: "/tmp/slide-1.png")
175
+ ```
176
+
177
+ ### Render options
178
+
179
+ | Option | Example | Effect |
180
+ |--------|---------|--------|
181
+ | (none) | `slide: 1` | Inline WebP at 800px wide (for QA) |
182
+ | `width` | `width: 400` | Resize to 400px wide (proportional) |
183
+ | `scale` | `scale: "50%"` | Half size (960×540) |
184
+ | `output` | `output: "/tmp/s.png"` | Save full PNG to disk |
185
+
186
+ ### CLI alternative
270
187
 
271
- The title and closing slides must use the **same dark background** — this creates the "sandwich" effect that makes decks feel complete.
188
+ ```bash
189
+ figmatk render my-deck.deck -o /tmp/renders/ # all slides
190
+ figmatk render my-deck.deck -o /tmp/renders/ --slide 3 # single slide
191
+ figmatk render my-deck.deck -o /tmp/renders/ --width 400 # thumbnail size
192
+ ```
272
193
 
273
- ### Consistent visual motif pick one and use it on every slide
194
+ **Important:** Always run visual QA on every deck you create or modify. Do not skip this step.
274
195
 
275
- Choose one repeating element and place it consistently across all content slides:
196
+ ---
276
197
 
277
- - **Top accent bar**: `addRectangle(0, 0, 1920, 8, { fill: hex('#...') })` — full-width coloured strip at top
278
- - **Left colour panel**: tall rectangle on the left third, text floats right
279
- - **Corner badge**: small filled circle or square in bottom-right with slide number or logo
280
- - **Bottom rule**: thin full-width line at y=1040
198
+ ## Design Philosophy
281
199
 
282
- Without a motif, slides look unrelated. With one, the deck feels designed.
200
+ Every deck must look **intentionally designed**, not AI-generated.
283
201
 
284
202
  ### Colour
285
203
 
@@ -298,29 +216,18 @@ Without a motif, slides look unrelated. With one, the deck feels designed.
298
216
  | Ocean | `'Blue'` | `hex('#21295C')` | `'White'` |
299
217
  | Minimal | `'White'` | `hex('#36454F')` | `'Black'` |
300
218
 
301
- ### Layout — vary every slide
302
-
303
- Each content slide should use a **different layout type**. Never repeat the same structure back-to-back.
219
+ ### Layout
304
220
 
305
- | Layout | When to use |
306
- |--------|------------|
307
- | Two-column | Comparison, pros/cons, text + image |
308
- | 2×2 or 2×3 grid | Features, icons, categories |
309
- | Large stat callout | One big number + explanation |
310
- | Half-background image | Photo-rich slides |
311
- | Timeline / steps | Process, history, roadmap |
312
- | Icon + text rows | Lists that need visual weight |
313
- | Full-bleed image | Impact moment, section break |
221
+ - Every slide needs at least **one visual element** — shape, image, SVG, or table.
222
+ - **Vary layouts** — never repeat the same structure slide after slide.
223
+ - Carry one visual motif through every slide (coloured accent bar, icon circles, etc.).
314
224
 
315
- Every slide needs at least **one visual element** shape, image, SVG, or table. No text-only slides.
225
+ **Layout options:** two-column, icon+text rows, 2×2/2×3 grid, large stat callout, half-background image, timeline/steps.
316
226
 
317
227
  ### Typography
318
228
 
319
- - Left-align body text. Centre only titles on title/closing slides.
229
+ - Left-align body text. Centre only titles.
320
230
  - Minimum 64px margin from slide edges. 24–48px between content blocks.
321
- - Use `Header 2` or `Header 3` for slide titles on content slides (not `Title` — that's for the title slide only).
322
- - **Body text: max 2 sentences per text block.** Text boxes have fixed heights — overflow gets clipped. If you have more to say, use a bullet list or split across slides.
323
- - **Bullets: max 6 items, max 8 words per bullet.** Longer bullets wrap and push content off-slide.
324
231
 
325
232
  ### Never do
326
233
 
@@ -328,17 +235,17 @@ Every slide needs at least **one visual element** — shape, image, SVG, or tabl
328
235
  - Centre body text
329
236
  - Use accent lines under slide titles (hallmark of AI-generated slides)
330
237
  - Text-only slides
331
- - Low-contrast text against background — **match image tone to slide palette**: dark/moody images on light-background slides make text unreadable; pick a bright image or switch to a dark-background layout
332
- - Skip the closing slide — it makes the deck feel unfinished
333
- - Put long paragraphs in body/caption fields — text overflows the container
238
+ - Low-contrast text against background
334
239
 
335
240
  ---
336
241
 
337
242
  ## QA
338
243
 
339
244
  1. Self-check: no placeholder text (`lorem ipsum`, `[title here]`) remains
340
- 2. Tell the user to open the `.deck` in Figma Desktop to catch rendering issues
341
- 3. Offer to fix anything they report
245
+ 2. **Render every slide** using `figmatk_render_slide` and visually inspect for overflows, clipping, alignment, and color issues
246
+ 3. Fix any issues found and re-render to confirm
247
+ 4. Tell the user to open the `.deck` in Figma Desktop for final review
248
+ 5. Offer to fix anything they report
342
249
 
343
250
  ---
344
251
 
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: figma-template-builder
3
+ description: >
4
+ Author reusable Figma Slides templates as .deck files. Use when the user
5
+ wants to build a template from reference images or examples, derive a new
6
+ template from an existing deck, define reusable layouts, mark editable
7
+ text/image slots, or prepare a draft template for later instantiation.
8
+ metadata:
9
+ version: "0.3.7"
10
+ ---
11
+
12
+ # Figma Template Builder
13
+
14
+ Use this skill when the goal is to build or refine the template itself. For the common workflow of taking an existing template and producing a new presentation, use `skills/figma-slides-creator/SKILL.md`.
15
+
16
+ ## MCP First
17
+
18
+ In Claude Cowork, keep template authoring inside the MCP plugin.
19
+
20
+ - Do not inspect the installed `figmatk` package to discover template features.
21
+ - Do not write direct Node.js scripts for draft creation, annotation, wrapping, or instantiation when MCP tools exist for those steps.
22
+ - Prefer `figmatk_create_template_draft`, `figmatk_annotate_template_layout`, `figmatk_publish_template_draft`, and `figmatk_list_template_layouts`.
23
+
24
+ Only fall back to direct library code if the MCP server is unavailable or the required capability is missing from the MCP surface.
25
+
26
+ ## Skill Boundary
27
+
28
+ Use this skill when the deliverable is a reusable template, not a one-off deck.
29
+
30
+ Stay here when the user wants to:
31
+
32
+ - translate reference images or screenshots into template layouts
33
+ - create a new layout family
34
+ - define placeholder semantics for later sessions
35
+ - turn an ordinary deck into a reusable template
36
+
37
+ Hand off to `skills/figma-slides-creator/SKILL.md` once the template exists and the task becomes simple content population.
38
+
39
+ ## Core Model
40
+
41
+ Template work happens in two states:
42
+
43
+ - Draft template: `SLIDE_ROW -> SLIDE -> ...`
44
+ - Published or publish-like template: `SLIDE_ROW -> MODULE -> SLIDE -> ...`
45
+
46
+ Draft templates are easier to author. Publish-like wrapping is the final step before later instantiation.
47
+
48
+ ## Design-First Workflow
49
+
50
+ When the user provides reference images, screenshots, or example decks:
51
+
52
+ 1. Read the references first and infer the layout family before touching the `.deck`.
53
+ 2. Decide which parts are reusable structure versus one-off sample content.
54
+ 3. Create only the smallest set of layouts that captures the system.
55
+ 4. Use semantic slot names so later sessions can populate them without re-reading the design intent.
56
+
57
+ Do not mirror every visual variation as its own layout unless the content structure changes materially.
58
+
59
+ ## Naming Conventions
60
+
61
+ Always use explicit metadata when authoring reusable layouts:
62
+
63
+ - Layouts: `layout:<name>`
64
+ - Text slots: `slot:text:<name>`
65
+ - Image slots: `slot:image:<name>`
66
+ - Decorative fixed imagery: `fixed:image:<name>`
67
+
68
+ These conventions are how later sessions discover what is editable.
69
+
70
+ Prefer semantic names over visual or auto-generated names.
71
+
72
+ Good:
73
+
74
+ - `title`
75
+ - `subtitle`
76
+ - `hero_image`
77
+ - `device_screen_primary`
78
+ - `quote_author`
79
+
80
+ Bad:
81
+
82
+ - `text1`
83
+ - `frame183`
84
+ - `image-left`
85
+ - `rectangle2`
86
+
87
+ ## Recommended Flow
88
+
89
+ ### 1. Create or inspect a draft template
90
+
91
+ - New draft from scratch: `figmatk_create_template_draft`
92
+ - Existing deck/template: `figmatk_list_template_layouts`
93
+ - Structural inspection: `figmatk_inspect`
94
+
95
+ ### 2. Annotate reusable layouts
96
+
97
+ Use `figmatk_annotate_template_layout` to:
98
+
99
+ - rename a slide as a layout
100
+ - mark text nodes as editable text slots
101
+ - mark image-bearing nodes as editable image slots
102
+ - mark decorative imagery as fixed
103
+
104
+ The tool accepts node ID maps, so inspect first if you need the raw node IDs.
105
+
106
+ While annotating:
107
+
108
+ - rename the slide itself to the stable layout name
109
+ - mark only true placeholders as `slot:*`
110
+ - mark decorative or sample imagery as `fixed:image:*`
111
+ - prefer stable semantic names over spatial names like `left_box`
112
+
113
+ ### 3. Publish-wrap when the template is ready
114
+
115
+ Use `figmatk_publish_template_draft` to add publish-like `MODULE` wrappers while preserving the slide subtree and internal assets.
116
+
117
+ ### 4. Verify the result
118
+
119
+ - `figmatk_list_template_layouts`
120
+ - `figmatk_list_text`
121
+ - `figmatk_roundtrip` if you want a conservative encode/decode check
122
+ - open the wrapped template in Figma Desktop when validating real upload behavior
123
+
124
+ ## Practical Rules
125
+
126
+ - Prefer explicit slot names over heuristic placeholders.
127
+ - Do not assume every image fill is editable content.
128
+ - Preserve `Internal Only Canvas` assets.
129
+ - Preserve special nodes such as device mockups and interactive slide elements; do not try to recreate them from scratch unless necessary.
130
+
131
+ ## Template Authoring Heuristics
132
+
133
+ - Start with 4-8 reusable layouts, not an exhaustive library.
134
+ - Reuse one layout when only copy length changes; create a new layout when hierarchy or media structure changes.
135
+ - Separate content slots from chrome. For device mockups, the screen is usually the slot and the hardware frame is usually fixed.
136
+ - If a layout has explicit slot metadata, do not rely on heuristic image placeholders for that layout.
137
+ - After publish-wrapping, re-run `figmatk_list_template_layouts` and confirm the layout names and slot names survived unchanged.
138
+
139
+ ## Example
140
+
141
+ ```json
142
+ {
143
+ "path": "/tmp/draft-template.deck",
144
+ "output": "/tmp/draft-template-annotated.deck",
145
+ "slideId": "1:42",
146
+ "layoutName": "cover",
147
+ "textSlots": {
148
+ "1:120": "title",
149
+ "1:121": "subtitle"
150
+ },
151
+ "imageSlots": {
152
+ "1:144": "hero_image"
153
+ },
154
+ "fixedImages": {
155
+ "1:199": "background_texture"
156
+ }
157
+ }
158
+ ```