slidemind 0.3.13 → 0.3.14
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 +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +86 -21
- package/README.md +14 -17
- package/bin/slidemind.mjs +65 -4969
- package/package.json +1 -1
- package/skills/create-slides/SKILL.md +113 -74
- package/skills/search/SKILL.md +25 -7
- package/src/bridge/build-and-push.mjs +108 -15
- package/src/bridge/bundle.mjs +69 -0
- package/src/bridge/capture.mjs +19 -0
- package/src/bridge/client/brands.mjs +622 -0
- package/src/bridge/client/engine-routing.mjs +237 -0
- package/src/bridge/client/engine-search.mjs +310 -0
- package/src/bridge/client/engine-slides.mjs +435 -0
- package/src/bridge/client/pane-ops.mjs +335 -0
- package/src/bridge/client/presentations.mjs +587 -0
- package/src/bridge/client/render.mjs +317 -0
- package/src/bridge/client/transport.mjs +695 -0
- package/src/bridge/client.mjs +80 -3452
- package/src/bridge/config.mjs +1 -1
- package/src/bridge/engine-download.mjs +15 -2
- package/src/builders/build.mjs +11 -804
- package/src/builders/draft-check.mjs +376 -0
- package/src/builders/lint.mjs +38 -2
- package/src/builders/preview.html +799 -0
- package/src/builders/review.mjs +23 -4
- package/src/builders/visual-families.mjs +6 -8
- package/src/cli/common.mjs +179 -0
- package/src/cli/concepts.mjs +310 -0
- package/src/cli/connection.mjs +147 -0
- package/src/cli/deck.mjs +608 -0
- package/src/cli/designs.mjs +113 -0
- package/src/cli/diagnose.mjs +141 -0
- package/src/cli/download-bundle.mjs +144 -0
- package/src/cli/engine-helpers.mjs +429 -0
- package/src/cli/engine-results.mjs +320 -0
- package/src/cli/help.mjs +201 -0
- package/src/cli/install.mjs +257 -0
- package/src/cli/read.mjs +120 -0
- package/src/cli/ready.mjs +805 -0
- package/src/cli/search.mjs +608 -0
- package/src/cli/session.mjs +177 -0
- package/src/cli/setup.mjs +282 -0
- package/src/cli/slide-reuse.mjs +334 -0
- package/src/cli/status.mjs +118 -0
- package/src/cli/workspace.mjs +302 -0
- package/src/installers/codex.mjs +1 -1
- package/src/update-check.mjs +231 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slidemind",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Connect Claude Code, Codex & opencode to the slidemind PowerPoint add-in — let your coding agent build and edit native, on-brand PowerPoint decks against your active corporate design, from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,30 +43,29 @@ later stages (above all the review gate) from being skipped. The stages, as they
|
|
|
43
43
|
host, so it can run while `ready` executes)
|
|
44
44
|
3. Plan sign-off — `slidemind plan <deck>` (every from-scratch request — one slide included)
|
|
45
45
|
4. Concept mockups — `slidemind concepts <deck>`, then immediately inspect every mockup (anything
|
|
46
|
-
authored from an approved plan, a single slide included; see *The create flow* below).
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
double evidence**: `asset-plan.md` may say "no mockups" ONLY when BOTH the re-run's output
|
|
54
|
-
printed zero paths AND `ls decks/<slug>/concepts/` is empty — quote both in the file.
|
|
55
|
-
Authoring a concept-seeded deck without having Read the concept PNGs that exist on disk is the
|
|
56
|
-
single largest fidelity failure this workflow has; nothing about a truncated stdout excuses it.
|
|
46
|
+
authored from an approved plan, a single slide included; see *The create flow* below). The
|
|
47
|
+
command **always ends on one line, `CONCEPTS: <n>/<planned> <status>`** (`--json` for the
|
|
48
|
+
payload). Generation takes 1–2 minutes; if your harness cut the command off before that line
|
|
49
|
+
appeared, re-run it — mockups already on disk come back as the path list instantly. Only a
|
|
50
|
+
`CONCEPTS: 0/…` line means there are no mockups; a truncated output never does. Authoring a
|
|
51
|
+
concept-seeded deck without having Read the concept PNGs on disk is the single largest fidelity
|
|
52
|
+
failure this workflow has.
|
|
57
53
|
**User-provided images** go into this command, classified from the user's words: a sketch or
|
|
58
54
|
mockup to build ("build it like this") → `--concept <file>[@N,M]` (it becomes the mockup for
|
|
59
55
|
those slides, no generation); a look to emulate ("in this style") → `--style <file>[@N,M]`
|
|
60
56
|
(leads the generation; repeatable; `@` omitted = all slides). A photo or logo to place on a slide
|
|
61
57
|
is not a concept input: copy it into `decks/<slug>/assets/` and list it in `asset-plan.md`.
|
|
62
|
-
5. Source assets —
|
|
63
|
-
|
|
58
|
+
5. Source assets — fill the `decks/<slug>/asset-plan.md` form `slidemind concepts` seeded (regions /
|
|
59
|
+
organisms / connectors / icons per slide, each with its share of the content area), then search + download
|
|
60
|
+
every icon row in it (*The create flow* steps 2–3). **This stage is not optional and not mergeable
|
|
64
61
|
into authoring**: it is done when every plan item is checked off with a filename or marked
|
|
65
62
|
`(no result)` after its search ran. A deck with concept mockups and an empty `assets/` beyond
|
|
66
63
|
the brand chrome means this stage was skipped, not completed.
|
|
67
64
|
6. Author the slides
|
|
68
|
-
7. Draft check — `slidemind view-draft`
|
|
69
|
-
|
|
65
|
+
7. Draft check — `slidemind view-draft`, until it reports `DRAFT CHECK: no measured defects` AND each
|
|
66
|
+
concept/render pair matches region by region
|
|
67
|
+
8. Review gate — `slidemind review`, the moment the deck builds clean (it refuses to open while the
|
|
68
|
+
last draft check's measured defects stand)
|
|
70
69
|
9. Deliver — `slidemind push` or `slidemind build-pptx`, whichever target was locked in stage 2
|
|
71
70
|
|
|
72
71
|
**Changing slides that already exist runs a shorter list.** When the deck is on disk and the
|
|
@@ -115,22 +114,28 @@ Two gate invariants you must never break — one per gate, equally binding:
|
|
|
115
114
|
|
|
116
115
|
## After a context summarization (compaction recovery)
|
|
117
116
|
|
|
118
|
-
Long deck runs
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
When you notice you are running from a summary (or after any interruption
|
|
122
|
-
improvise from the summary and do NOT restart finished work.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
117
|
+
Long deck runs exceed the context window mid-task; the harness then replaces the transcript with a
|
|
118
|
+
summary. **Everything you only held in chat — viewed concept images, search results, the asset
|
|
119
|
+
inventory, the workflow rules you read from the bundle, this skill's own steps — is gone; the files
|
|
120
|
+
on disk are not.** When you notice you are running from a summary (or after any interruption or
|
|
121
|
+
continuation), do NOT improvise from the summary and do NOT restart finished work. Run
|
|
122
|
+
`slidemind ready <deck>` first: it re-materializes and re-binds (idempotent) and ends with a
|
|
123
|
+
**`stage:` line read from disk** (plan / concepts / asset plan / slides / draft check / review) and
|
|
124
|
+
a **`next:` line naming the command or file that is due** (`stage` in `--json`). Resume there, after
|
|
125
|
+
re-reading the set that stage works from:
|
|
126
|
+
|
|
127
|
+
- **plan gate** → *Two UX rules* and the `slidemind plan` row below.
|
|
128
|
+
- **concepts / sourcing** → `<bundle>/workflow/create.md` steps 1–2, `asset-plan.md`, every
|
|
129
|
+
`concepts/concept-slide-NN.png` at full detail.
|
|
130
|
+
- **authoring** → `<bundle>/workflow/create.md` + `authoring.md`, `design.md`,
|
|
131
|
+
`corporate-design.css`, the `layouts/<Layout>.jsx` the plan names, `asset-plan.md`, `plan.md`;
|
|
132
|
+
then, per batch of ≤3, the matching concept PNGs immediately before each write.
|
|
133
|
+
- **draft check** → *The create flow* step 4; **review** → step 5 + *Two UX rules*; **deliver** →
|
|
134
|
+
the deliver row. An **edit** to existing slides → `<bundle>/workflow/edit.md` + the cited files.
|
|
135
|
+
|
|
136
|
+
Two rules bind: the stage todo list still binds after a summary — "mostly done" is never a reason
|
|
137
|
+
to skip the remaining stages — and never author, judge, or deliver from a remembered image; reopen
|
|
138
|
+
the PNG from its path. `<bundle>` is `bundleDir` from `slidemind ready <deck> --json`.
|
|
134
139
|
|
|
135
140
|
## Bootstrap (set yourself up — don't make the user do it)
|
|
136
141
|
|
|
@@ -148,6 +153,8 @@ binds the deck to it.
|
|
|
148
153
|
no local server. Patch a missing CLI yourself; **connecting** needs the user once (they hold the
|
|
149
154
|
add-in's identity):
|
|
150
155
|
|
|
156
|
+
- **Flags you are unsure of:** `slidemind <command> --help` prints that command's usage block; the
|
|
157
|
+
deck commands take `--json`.
|
|
151
158
|
- **CLI missing** (`slidemind` not on `PATH`): `npm install -g slidemind` — that alone is the fix.
|
|
152
159
|
Only run `slidemind init --for <claude|codex|opencode|all>` ((re)deploys this skill) when this
|
|
153
160
|
skill did **not** come from a plugin: if the `SKILL.md` you are reading sits under a harness
|
|
@@ -302,7 +309,7 @@ next to the deck (`paneSuspended` + `fallbackFile` in the result) — nothing is
|
|
|
302
309
|
once a window exists. Reserve the full "open PowerPoint…" instruction for when a command actually
|
|
303
310
|
reports it as the blocker.
|
|
304
311
|
|
|
305
|
-
`
|
|
312
|
+
`view-draft` and `review` run offline, but only against a **previously
|
|
306
313
|
materialized** bundle — so `ready` (or a prior `ready`) must have succeeded at least once.
|
|
307
314
|
`build-pptx` additionally needs the backend reachable (the OOXML assembly is server-side), but no
|
|
308
315
|
PowerPoint window for a bound deck — so *"a `.pptx` file to send around"* is deliverable end-to-end
|
|
@@ -319,7 +326,7 @@ with PowerPoint closed.
|
|
|
319
326
|
insert, `insert-slide`, `read <presId>`) can't reach the add-in (a connection blocker, a pane that
|
|
320
327
|
never wakes, the *click the slidemind pane* line with no effect), **stop the workflow** — don't
|
|
321
328
|
skip the step, work around it, or retry silently. Ask the user to go back into the PowerPoint tab
|
|
322
|
-
of presentation **<title/fileName>** (name it from the command output or `slidemind
|
|
329
|
+
of presentation **<title/fileName>** (name it from the command output or `slidemind ready --diagnose`
|
|
323
330
|
— never a raw id), then re-run the failed command once they have. Everything before delivery keeps
|
|
324
331
|
running meanwhile — never park the whole task on a window it doesn't need.
|
|
325
332
|
|
|
@@ -497,16 +504,24 @@ brand/layout/composition detail until create/edit.**
|
|
|
497
504
|
|
|
498
505
|
## The create flow
|
|
499
506
|
|
|
507
|
+
Every deck command (`view-draft`, `review`, `concepts`, `build-pptx`, `push`) re-syncs the deck's
|
|
508
|
+
bound design before it runs, so a corporate design that changes mid-session is picked up by itself —
|
|
509
|
+
it prints `design bundle updated` when that happens, and you re-read the bundle files it names. You
|
|
510
|
+
never have to re-run `ready` to get a fresh design.
|
|
511
|
+
|
|
500
512
|
`slidemind ready` already resolved the design source and materialized its bundle; the deck was bound
|
|
501
513
|
to it by `slidemind ready <deck>` (continuing) or by `slidemind new <slug>`'s auto-bind (new work).
|
|
502
514
|
From here:
|
|
503
515
|
|
|
504
|
-
1. **Load create context, then inspect the concepts.** Read
|
|
505
|
-
`workflow/
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
516
|
+
1. **Load create context, then inspect the concepts.** Read `workflow/create.md` (the procedure),
|
|
517
|
+
`workflow/authoring.md` (the portability contract — **required BEFORE the first slide write**,
|
|
518
|
+
never deferred to a lint repair: it names what CSS ports, the slide file shape, transforms,
|
|
519
|
+
rules and dividers, painted nesting, decorative layers, the Arrow contract), the deck's
|
|
520
|
+
`design.md`, `corporate-design.css`, and the specific `layouts/*.jsx` the plan uses. Nothing
|
|
521
|
+
else up front: `workflow/composition.md` and `workflow/layouts.md` are for slides WITHOUT a
|
|
522
|
+
mockup and for `[STYLE]` questions — open them when a lint message or a missing mockup sends
|
|
523
|
+
you there, not before (the read set is what `workflow/create.md` prescribes; `workflow/flow.md`
|
|
524
|
+
you already read after `ready`). The approved `plan.md` is a **content contract, not a rendering
|
|
510
525
|
spec** — you drew its storyboards yourself, but approval covered the story and content, not the
|
|
511
526
|
sketch's geometry; how to interpret the `storyboard-canvas` blocks at author time (design
|
|
512
527
|
hypothesis: reinterpret freely, never transcribe coordinates, block geometry, or block labels)
|
|
@@ -519,22 +534,24 @@ From here:
|
|
|
519
534
|
search.** First list the directory and keep the literal repo-relative mapping in the task context:
|
|
520
535
|
concept `NN` maps to `decks/<slug>/concepts/concept-slide-NN.png` and later to the `NN-*.jsx`
|
|
521
536
|
slide. These workspace files are the durable source of truth across turns and compaction — never
|
|
522
|
-
rely on an earlier rendered attachment.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
537
|
+
rely on an earlier rendered attachment. **The mockup is painted onto the layout's own chrome:
|
|
538
|
+
the title, section label, footer and page marks you see are chrome that travels through the
|
|
539
|
+
layout's props; everything else is the body and belongs in the layout's `[CONTENT]` slot**
|
|
540
|
+
(its geometry is in the layout's `PLACEHOLDERS` metadata). Note each region's share of that
|
|
541
|
+
area in percentages as you inspect. `slidemind concepts` seeded
|
|
542
|
+
`decks/<slug>/asset-plan.md` as a form with one section per slide (`regions:` / `organisms:` /
|
|
543
|
+
`connectors:` / `icons:`); **fill every row the moment you finish inspecting a
|
|
544
|
+
mockup, before any slide is written** — one bullet per region, per organism, per arrow or line
|
|
545
|
+
(`from → to, straight|elbow|curved, arrow end|both|none, solid|dashed`), per icon (subject,
|
|
546
|
+
prominence, one engine query). The draft check COUNTS these rows: connectors listed but not drawn,
|
|
547
|
+
an inventory left empty, and fetched assets no slide references each come back as an
|
|
548
|
+
`[inventory:warn]`. (If the form is missing — an older add-in — write it in that shape yourself.)
|
|
533
549
|
|
|
534
550
|
Chat context is evicted when the conversation is summarized; this file is not. Every icon
|
|
535
551
|
decision you make only in your head is lost at the next compaction — the file is the contract
|
|
536
|
-
step 2 executes and step 3 authors from.
|
|
537
|
-
as downloads land, so the file always shows what is still
|
|
552
|
+
step 2 executes and step 3 authors from. Append the fetched filename to each icon row
|
|
553
|
+
(`… — assets/engine-<id>-<n>.svg`) as downloads land, so the file always shows what is still
|
|
554
|
+
missing. This first pass is
|
|
538
555
|
deliberately deck-wide and search-oriented; it is not the fresh authoring read required in
|
|
539
556
|
step 3. Rebuild each mockup under your constraints
|
|
540
557
|
— the rebuild rule is owned by
|
|
@@ -559,12 +576,11 @@ From here:
|
|
|
559
576
|
is discovery, not sourcing: this step is done only when every `asset-plan.md` item is checked
|
|
560
577
|
off with a downloaded filename or marked `(no result)` after its search actually ran. Four
|
|
561
578
|
mechanics that waste rounds when gotten wrong:
|
|
562
|
-
- **Icon results have no preview
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
downloads = mark it `(no result)` and move on.
|
|
579
|
+
- **Icon results have no preview — the download is the preview.** Pick from the `search`
|
|
580
|
+
output's titles/scores, `download-result` the picks (it prints each asset's size + recolor
|
|
581
|
+
info), judge from the saved file, re-search once if it was wrong. At most two search rounds
|
|
582
|
+
per need; two failed downloads = mark it `(no result)` and move on. (`view-slide` is for
|
|
583
|
+
slide-library results only.)
|
|
568
584
|
- **Batch the downloads:** one `download-result --search <id> --pick <n,n,…>` per search id —
|
|
569
585
|
never one call per icon.
|
|
570
586
|
- **Quote hex colors:** `--icon-color "#<hex>"` (a value resolved from a corporate-design.css token) — an unquoted `#` makes the shell drop the
|
|
@@ -598,23 +614,46 @@ From here:
|
|
|
598
614
|
4. **Look at your own draft first (you, the agent).** This is the **draft check** in
|
|
599
615
|
`workflow/build-and-export.md` — your pre-check, *before* any human sees the deck. Two rules
|
|
600
616
|
moved early on purpose — do them NOW, not at the review gate when the run is long and attention
|
|
601
|
-
is spent:
|
|
602
|
-
mechanical pass on files you just wrote
|
|
617
|
+
is spent: `slidemind view-draft` lints every slide before it renders, so fix every `[lint:warn]`
|
|
618
|
+
it prints (a mechanical pass on files you just wrote — `--fix` applies the deterministic ones);
|
|
619
|
+
and after any fix round below, one more `slidemind
|
|
603
620
|
view-draft` over the changed slides is mandatory — the verification must appear in your command
|
|
604
621
|
sequence, not only in prose. Run `slidemind view-draft <deck>`: it renders each authored `slides/*.jsx` to a per-slide PNG and hands the
|
|
605
|
-
images back to you — **nothing is pushed, no `.pptx` is written, no review gate opens
|
|
606
|
-
|
|
607
|
-
`
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
622
|
+
images back to you — **nothing is pushed, no `.pptx` is written, no review gate opens** — and
|
|
623
|
+
runs the captured slides through the server's export gate (slot-fit, diagram geometry,
|
|
624
|
+
connector endpoints) so the errors `build-pptx` would stop on surface HERE, before the review,
|
|
625
|
+
not as a 422 after the user approved (skipped with a note when the backend is unreachable;
|
|
626
|
+
`--lint-only` for a fast lint loop, `--json` for a payload whose `reviewCanOpen` says whether
|
|
627
|
+
the gate will open). Two things come back, and both bind:
|
|
628
|
+
- **Measured lines** under each slide — `[fill:warn]` (a stretched region whose organisms pool
|
|
629
|
+
at its top, with the region and the dead band) and `[inventory:warn]` (connectors listed in
|
|
630
|
+
`asset-plan.md` but not drawn, an empty inventory, a fetched asset no slide references) —
|
|
631
|
+
and a closing `DRAFT CHECK: N measured defects` line. Fix every one in the JSX and re-run;
|
|
632
|
+
the review gate refuses to open while they stand for an unchanged slide. A fill band is
|
|
633
|
+
fixed by distributing the organisms (`grid-template-rows` with one track per organism,
|
|
634
|
+
`justify-content: space-between`, a figure that grows), never by deleting content.
|
|
635
|
+
- **The pair**: for every concept-seeded slide, Read its anchored
|
|
636
|
+
`decks/<slug>/concepts/concept-slide-NN.png` and matching
|
|
637
|
+
`decks/<slug>/.draft-preview/<slide-basename>.png` **together in the same visual inspection**,
|
|
638
|
+
in batches of at most 3 pairs, and compare **region by region**: same organisms at the
|
|
639
|
+
same relative positions and shares, every connector present, no region emptier than in the
|
|
640
|
+
concept, nothing clipped, overlapping, or buried. A loop, a hub, a flywheel, a timeline in the
|
|
641
|
+
concept that became a row of cards in the render is a failed rebuild, not a simplification —
|
|
642
|
+
re-express the mechanism. Under the slide's section in `asset-plan.md` write `draft-check:` and
|
|
643
|
+
then one tick line per `organisms:` row: `- [x] <organism>` when it is in the render at the
|
|
644
|
+
concept's position and share, `- [ ] <organism> — <what differs>` when it is not. A bare
|
|
645
|
+
`matches` is not a verdict; the review gate refuses to open while any concept-seeded slide
|
|
646
|
+
has fewer ticks than organisms or a `[ ]` standing. A draft-only inspection does not satisfy
|
|
647
|
+
this check. The measured lines also include `[type:warn]` — most of the text one rung below
|
|
648
|
+
`--fs-body` next to free space; fix by raising the ramp, never by shrinking type to fit.
|
|
649
|
+
Fix anything wrong **in the JSX** (re-express, never delete), render again, and reopen the
|
|
650
|
+
affected concept/draft pairs until each slide matches its image and the measured line reads
|
|
651
|
+
zero. Do not open the review gate from a summary or a memory of an earlier look. It runs
|
|
613
652
|
locally against the materialized bundle, so it needs no awake pane and no open host once the
|
|
614
653
|
bundle has been downloaded once. Two faithful limits to know: a slide on a **purpose-built (Mode
|
|
615
654
|
A) layout** (cover/divider/agenda) and a **reused native `.pptx`** slide have no freeform region
|
|
616
655
|
to capture, so they come back as a status line, not an image — judge those at the full-deck
|
|
617
|
-
`review
|
|
656
|
+
`review` step below. Do this self-check before the human review gate; don't push a draft
|
|
618
657
|
you haven't looked at.
|
|
619
658
|
5. **The review gate — the moment the deck builds clean.** This is the **review gate** in
|
|
620
659
|
`workflow/build-and-export.md`; the CLI's gate command is `slidemind review <deck>`. Run it the
|
|
@@ -740,7 +779,7 @@ data table per chart; `slidemind read <file.pptx>` (host-free) reports text only
|
|
|
740
779
|
|
|
741
780
|
Finding and fetching engine imagery is `slidemind-search`'s job (`slidemind search` → `slidemind
|
|
742
781
|
download-result`). What follows is **create-only**: how to size and colour a fetched asset so it
|
|
743
|
-
reads on-brand in your JSX, honoured in both the
|
|
782
|
+
reads on-brand in your JSX, honoured in both the local draft preview and the render.
|
|
744
783
|
|
|
745
784
|
Engine results land as files referenced with `<img src="./assets/<file>">`. These facts are specific
|
|
746
785
|
to the local harness (the bundle's `workflow/*.md` stay harness-neutral):
|
|
@@ -757,7 +796,7 @@ to the local harness (the bundle's `workflow/*.md` stay harness-neutral):
|
|
|
757
796
|
- **Never open the SVG to find out whether it recolours.** `slidemind download-result` prints a
|
|
758
797
|
`recolor` descriptor for each asset — `{ recolorable, mode, applied, note }`, where `mode` is
|
|
759
798
|
`currentColor`, `mono`, `multicolor`, or `raster`. Read it; that *is* the answer.
|
|
760
|
-
- **Two ways to colour an icon, both honoured in the
|
|
799
|
+
- **Two ways to colour an icon, both honoured in the local draft preview *and* the render:**
|
|
761
800
|
- **Bake it in (prefer this):** pass `--icon-color <hex>` — a hex you resolve from a token in the
|
|
762
801
|
bundle's `corporate-design.css` — to `slidemind download-result`. Works for any `recolorable`
|
|
763
802
|
glyph and the colour is in the saved file, so preview and render always agree. The tint is
|
|
@@ -787,7 +826,7 @@ Vocabulary. You are the local harness — this is your mapping:
|
|
|
787
826
|
| concept mockups (mandatory for every from-scratch set, after plan sign-off) | `slidemind concepts <deck>` — renders one image mockup per approved-plan slide into the durable repo-relative path `decks/<slug>/concepts/concept-slide-NN.png`; immediately Read all of them for image/content search, then after searches reopen them in batches of at most 3 immediately before writing the matching `NN-*.jsx`. Re-list and reopen those paths after any interruption or continuation; never rely on an earlier chat image. The rebuild rule is owned by `workflow/create.md` → Preconditions. It never hard-fails: on an empty result (exit 0 — the add-in can't generate them) author from the storyboards alone, but for anything authored from a plan — one slide included — you always run it. Skip only for targeted work on slides that already exist (edit/rework) |
|
|
788
827
|
| **read** / **list** the workspace | read the files from the materialized bundle dir (`<bundle>/…`) and your `decks/<slug>/` |
|
|
789
828
|
| **create / update a slide file** | write `decks/<slug>/slides/NN-<topic>.jsx` on disk with a **zero-padded numeric prefix** (`01-cover.jsx`, `02-agenda.jsx`, …) — over the CLI, "the ordered set you hand to build & push" is expressed as **numeric-aware filename order**; there is no ordered-list channel, so reordering = renaming files. For a concept-seeded create or structural rewrite, the immediately preceding tool action must Read the matching anchored concept path(s) at full detail |
|
|
790
|
-
| **draft check** | `slidemind view-draft <deck>` — renders each `slides/*.jsx` to `decks/<slug>/.draft-preview/<slide-basename>.png` and returns the images to **you**; **no push, no `.pptx`, no review gate**. Read each concept/draft path pair together, fix the JSX, render, and pair again. A draft-only inspection does not count. Host-free once the bundle is materialized. Freeform slides only — purpose-built (Mode A) and reused native `.pptx` slides report as a status line (judge those at `review`/`build`). This is your self-check; `slidemind review` is the *human* gate |
|
|
829
|
+
| **draft check** | `slidemind view-draft <deck>` — lints every slide (fix each `[lint:warn]`; `--fix` applies the deterministic ones), then renders each `slides/*.jsx` to `decks/<slug>/.draft-preview/<slide-basename>.png` (clean, no overlays), prints the measured defects (`[fill:warn]`, `[inventory:warn]`, closing `DRAFT CHECK:` line) and returns the images to **you**; **no push, no `.pptx`, no review gate**. Read each concept/draft path pair together, fix the JSX, render, and pair again until the measured count is zero and the pairs match. A draft-only inspection does not count; `slidemind review` refuses to open while measured defects stand for an unchanged slide. Host-free once the bundle is materialized. Freeform slides only — purpose-built (Mode A) and reused native `.pptx` slides report as a status line (judge those at `review`/`build`). This is your self-check; `slidemind review` is the *human* gate |
|
|
791
830
|
| **build & push** | `slidemind push <deck>` (into the open PowerPoint — the insert needs a window) · `slidemind build-pptx <deck> [--out <file>]` (to a `.pptx` file — **host-free** for a bound deck: no PowerPoint window at all) — deliver whichever target was locked at intake; both render server-side via the bridge and for a bound deck the render itself is pane-free |
|
|
792
831
|
| **the review gate** | `slidemind review <deck>` — builds the HTML preview (lint-clean or it doesn't open), serves it with a spatial comment overlay and **blocks until the user decides**, then prints `DECISION: approved` or `DECISION: comments` (each comment cited to `slides/<file>.jsx:line:col` + the round it was dropped on). On approve → deliver (`slidemind push` / `build-pptx`); on comments → edit the cited files and re-run `slidemind review` in the same turn (addressed pins show as ghost pins); the user pins as many comments as they like and sends them as one batch (Approve is off while unsent pins exist); expect several comment rounds. CLI delivers happen out of the user's sight, so **every deliver follows an approved review of what it delivers — re-run `review` after any slide change**. `push`/`build-pptx` never pause; only `review` does. |
|
|
793
832
|
| **inspect the deck** | `slidemind read [presId\|file.pptx] [--mode summary\|raw]` — one verb, two sources: pull the current content of an open presentation (a presId; **host-required** — it reads the live, possibly unsaved file) **or** read a local `.pptx` on disk (a file path; host-free — no PowerPoint, no pairing). Prefer the file path when the deck is saved. `summary` = per-slide outline; `raw` = each slide's plain text — to inspect what's there before editing. Plain text, not authoring markup |
|
|
@@ -813,8 +852,8 @@ the rules may have changed.
|
|
|
813
852
|
## Failure modes the render lint can't catch
|
|
814
853
|
|
|
815
854
|
Render-time lint rejects contract violations (unsupported CSS, bad imports, remote `<img>` URLs —
|
|
816
|
-
the add-in's capability-table engine; `slidemind
|
|
817
|
-
bundle). It can't catch content-shaped failures; check for these **at the draft check** (step 4,
|
|
855
|
+
the add-in's capability-table engine; `slidemind view-draft` runs the same engine locally from the
|
|
856
|
+
bundle before it renders). It can't catch content-shaped failures; check for these **at the draft check** (step 4,
|
|
818
857
|
before the review gate) — not after delivery:
|
|
819
858
|
|
|
820
859
|
- Layout-default / placeholder text leaking through an unfilled prop.
|
package/skills/search/SKILL.md
CHANGED
|
@@ -96,8 +96,8 @@ commands then carry its `--to`), and a command that can't be re-routed tells you
|
|
|
96
96
|
**Add-in unreachable → block and name the tab.** When a host-required command can't reach the add-in
|
|
97
97
|
(a connection blocker, a pane that never wakes, the *click the slidemind pane* line with no effect),
|
|
98
98
|
**stop** — don't skip the step, work around it, or retry silently. Ask the user to go back into the
|
|
99
|
-
PowerPoint tab of presentation **<title/fileName>** (name it from the command output or `slidemind
|
|
100
|
-
|
|
99
|
+
PowerPoint tab of presentation **<title/fileName>** (name it from the command output or `slidemind ready
|
|
100
|
+
--diagnose` — never a raw id), then re-run the failed command once they have.
|
|
101
101
|
|
|
102
102
|
## Discover → search → pick → reuse
|
|
103
103
|
|
|
@@ -145,11 +145,12 @@ Follow `workflow/search.md` for the discipline; this is the CLI shape of its loo
|
|
|
145
145
|
path, Read that ONE file — every candidate is on it with its selection number burned in. With no
|
|
146
146
|
contact sheet, previews depend on the result type: **slide-library results** support `slidemind
|
|
147
147
|
view-slide --search <searchId> --pick <n>` (writes one result's preview to a file you can Read);
|
|
148
|
-
**image/icon results have NO preview on this path** — `view-slide`
|
|
149
|
-
for them, so never call it on an image search
|
|
148
|
+
**image/icon results have NO preview on this path** (the search says so itself) — `view-slide`
|
|
149
|
+
answers "no image returned" for them, so never call it on an image search.
|
|
150
150
|
For icons, pick by title + score, `download-result` the pick (downloads are cheap), Read the
|
|
151
151
|
saved file, and re-search once if it was wrong — two failed downloads for the same need means
|
|
152
|
-
mark it unresolved and move on. Pick by **selection number(s)
|
|
152
|
+
mark it unresolved and move on. Pick by **selection number(s)**, and **cite the sources** (see
|
|
153
|
+
the rule below) whenever you report results back.
|
|
153
154
|
5. **Read the source deck when the story matters.** A found slide came out of a real presentation,
|
|
154
155
|
and often the answer the user needs is in the *rest* of it — how the slide was framed, what came
|
|
155
156
|
before and after, whether that deck already covers more of the ask. `slidemind read-source
|
|
@@ -160,6 +161,14 @@ Follow `workflow/search.md` for the discipline; this is the CLI shape of its loo
|
|
|
160
161
|
deck — image and collection results do not.
|
|
161
162
|
6. **Reuse.** Insert live or fold into a deck — see the next section.
|
|
162
163
|
|
|
164
|
+
> **Always provide sources.** Every time you report search results to the user — a shortlist, a
|
|
165
|
+
> single pick, a "found it / didn't find it" answer — list, per result, the **presentation it came
|
|
166
|
+
> from** (its title and slide number as printed) and its **link** when the CLI printed a `source:`
|
|
167
|
+
> line (`sourceUrl` in `--json`; SharePoint-indexed results carry the file's browser URL). The user
|
|
168
|
+
> must be able to open the original presentation without asking you for it. A result printed
|
|
169
|
+
> without a `source:` line has no link on record — say so; **never invent, guess, or reconstruct a
|
|
170
|
+
> URL**. This is not optional and does not depend on whether the user asked for sources.
|
|
171
|
+
|
|
163
172
|
## Reuse targets — insert live, or fold into a deck
|
|
164
173
|
|
|
165
174
|
A found slide can be reused two ways. Both extract the picked result(s) as native PowerPoint slides
|
|
@@ -168,7 +177,7 @@ A found slide can be reused two ways. Both extract the picked result(s) as nativ
|
|
|
168
177
|
- **Insert LIVE into an open PowerPoint** — `slidemind insert-slide --search <searchId> --pick
|
|
169
178
|
<n[,n,…]> --to <presId> [--at <N>]`. **This is the one host-required step:** the picked slides
|
|
170
179
|
land in that open window (at position `--at`, else appended), so PowerPoint must be open with the
|
|
171
|
-
slidemind pane. Name the target window from `slidemind
|
|
180
|
+
slidemind pane. Name the target window from `slidemind ready --diagnose` (or, on the relayed path,
|
|
172
181
|
use the window the search ran on). A window that reconnects mid-run gets a **new** id; the CLI
|
|
173
182
|
re-finds it and retries once by itself, so a stale id is not your problem. If it still reports the
|
|
174
183
|
pane asleep, **don't loop** — relay its line, or fold the same pick into a deck (below) and push
|
|
@@ -239,7 +248,7 @@ insert live, fold into a deck, engine fetch). You are the local harness — this
|
|
|
239
248
|
| Verb (`workflow/search.md`) | Your equivalent (local harness) |
|
|
240
249
|
|---|---|
|
|
241
250
|
| **discover engines** ("list what you can reach") | `slidemind ls engines [--type collection\|search\|scope] [--subtype …]` — returns the engine catalog **plus the search scopes** (pick an `id`; never guess one). The catalog is backend/user-scoped; `--to` is accepted but never needed. No window needed where the add-in serves the pane-free routes. |
|
|
242
|
-
| **engine search** (with `intent`) | `slidemind search "<query>" --engine <id> --intent <auto\|content\|visual> [--top N] [--deck <deck>]` — leave `--to` off → a `searchId` + numbered results, plus the exact follow-up commands for the path it took (`--deck` routes by that deck's design when several environments are connected) |
|
|
251
|
+
| **engine search** (with `intent`) | `slidemind search "<query>" --engine <id> --intent <auto\|content\|visual> [--top N] [--deck <deck>]` — leave `--to` off → a `searchId` + numbered results, plus the exact follow-up commands for the path it took (`--deck` routes by that deck's design when several environments are connected); each result may carry a `source:` link — **always cite it back** |
|
|
243
252
|
| **search a scope** (unfiltered corpus, no engine) | `slidemind search "<query>" --engine <scope:all\|scope:sharepoint> --result-type <slide\|image> [--intent …]` — `--result-type` is required for a scope (defaults to `slide`). Results reuse identically to engine results. |
|
|
244
253
|
| **pull an on-demand thumbnail** for a stored result | `slidemind view-slide --search <searchId> --pick <n>` — SLIDE-library results only; image/icon results have no preview ("no image returned") — download and Read the file instead (add `--to <searchWindow>` only on the relayed path) · or `slidemind search … --json` for the structured payload. |
|
|
245
254
|
| **read a result's SOURCE deck** (the presentation it came from) | `slidemind read-source --search <searchId> --pick <n> [--from-slide N] [--to-slide N] [--out <file.md>]` — Markdown of the whole source presentation, one section per slide (add `--to <searchWindow>` only on the relayed path). Indexed text, not the live file. Slide results only. |
|
|
@@ -259,6 +268,15 @@ from `slidemind ls engines` (live discovery — the bundle carries no engine lis
|
|
|
259
268
|
search/reuse craft is in `workflow/search.md`. Read them and follow them. If the corporate design
|
|
260
269
|
moves mid-task, re-list engines before searching again, since the catalog may have changed.
|
|
261
270
|
|
|
271
|
+
**Where the bundle is, and what to do when there isn't one.** Searching itself needs no bundle, but
|
|
272
|
+
`workflow/search.md` lives in one, so a machine that has never materialized a design has nothing to
|
|
273
|
+
read. `slidemind ready --json` prints the materialized bundle's directory as `bundleDir` (run it
|
|
274
|
+
once — it resolves the design and materializes it); for a deck you are reusing into,
|
|
275
|
+
`slidemind ready <deck> --json` uses that deck's own binding. When several designs are at hand and
|
|
276
|
+
nothing says which, `slidemind ls designs` lists every one (cached here and listed by the add-in)
|
|
277
|
+
so the user can choose, and `slidemind download-bundle --brand <presFP>` materializes it without a
|
|
278
|
+
PowerPoint window.
|
|
279
|
+
|
|
262
280
|
`workflow/search.md` also owns the judgement this skill deliberately does **not** restate: what an
|
|
263
281
|
engine actually is (a saved filter over a shared corpus, not a library of its own), when to reach
|
|
264
282
|
for a scope instead, and why zero results are not evidence that the content doesn't exist. Read it
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
materializeBundle,
|
|
34
34
|
readDeckBundleRef,
|
|
35
35
|
resolveBundleDirForDeck,
|
|
36
|
+
resolveWindowlessBrand,
|
|
36
37
|
writeDeckBundleRef,
|
|
37
38
|
} from "./bundle.mjs";
|
|
38
39
|
import { bundleHasCaptureEngine, captureDeckLocally } from "./capture.mjs";
|
|
@@ -471,7 +472,10 @@ function mapCapturedSlide(fileName, cap, warnings) {
|
|
|
471
472
|
for (const w of cap.render?.warnings ?? []) warnings.push(`${fileName}: ${w}`);
|
|
472
473
|
|
|
473
474
|
if (cap.previewDataUrl) {
|
|
474
|
-
|
|
475
|
+
// The serialised boxes ride along so view-draft can run the measured
|
|
476
|
+
// checks (fill band, content grid) the add-in's see_draft runs.
|
|
477
|
+
const { root = null, stage = null, boxes = null } = cap.render ?? {};
|
|
478
|
+
return { fileName, status: "ok", dataUri: cap.previewDataUrl, render: { root, stage, boxes } };
|
|
475
479
|
}
|
|
476
480
|
// A degraded render is a non-editable raster; push/build-pptx will REJECT it.
|
|
477
481
|
// Attach it anyway (so the agent isn't blind) but flag the named cause.
|
|
@@ -515,7 +519,7 @@ function mapCapturedSlide(fileName, cap, warnings) {
|
|
|
515
519
|
// note?}], warnings: [] }.
|
|
516
520
|
export async function viewDraft(
|
|
517
521
|
deckDir,
|
|
518
|
-
{ presentationId = null, onProgress = null } = {},
|
|
522
|
+
{ presentationId = null, onProgress = null, only = null, translate = false } = {},
|
|
519
523
|
) {
|
|
520
524
|
if (!deckDir) throw new Error("viewDraft: deckDir is required");
|
|
521
525
|
const slidesDir = path.join(deckDir, "slides");
|
|
@@ -525,12 +529,19 @@ export async function viewDraft(
|
|
|
525
529
|
// Authored JSX + reused native .pptx share one play-order namespace, exactly
|
|
526
530
|
// as the render path sees them — walk them in that order so the draft view
|
|
527
531
|
// lines up with the deck the user will get.
|
|
528
|
-
|
|
532
|
+
let all = sortSlideFilenames(
|
|
529
533
|
fs.readdirSync(slidesDir).filter((f) => f.endsWith(".jsx") || f.endsWith(".pptx")),
|
|
530
534
|
);
|
|
531
535
|
if (all.length === 0) {
|
|
532
536
|
throw new Error(`No .jsx or .pptx slides under ${slidesDir}`);
|
|
533
537
|
}
|
|
538
|
+
if (Array.isArray(only) && only.length > 0) {
|
|
539
|
+
const missing = only.filter((f) => !all.includes(f));
|
|
540
|
+
if (missing.length > 0) {
|
|
541
|
+
throw new Error(`no such slide file${missing.length > 1 ? "s" : ""} under ${slidesDir}: ${missing.join(", ")}`);
|
|
542
|
+
}
|
|
543
|
+
all = all.filter((f) => only.includes(f));
|
|
544
|
+
}
|
|
534
545
|
const warnings = [];
|
|
535
546
|
// Same identity reconcile as the render path — a reorder renames every file,
|
|
536
547
|
// and a stale sidecar here would mislabel renamed converted slides (Chrome
|
|
@@ -553,6 +564,22 @@ export async function viewDraft(
|
|
|
553
564
|
// cached engine.
|
|
554
565
|
let ref = readDeckBundleRef(deckDir);
|
|
555
566
|
let bundleDir = resolveBundleDirForDeck(deckDir);
|
|
567
|
+
if (hasJsx && !presentationId && (!bundleDir || !bundleHasCaptureEngine(bundleDir))) {
|
|
568
|
+
// Window-free first: the deck's binding or the one design at hand syncs
|
|
569
|
+
// over the brand route; only a miss (old add-in, revoked design) goes on
|
|
570
|
+
// to the registry below.
|
|
571
|
+
const brandId = await resolveWindowlessBrand(deckDir);
|
|
572
|
+
if (brandId) {
|
|
573
|
+
try {
|
|
574
|
+
const mat = await materializeBundle(null, { brandId });
|
|
575
|
+
writeDeckBundleRef(deckDir, { presFP: mat.presFP, etag: mat.etag });
|
|
576
|
+
ref = readDeckBundleRef(deckDir);
|
|
577
|
+
bundleDir = resolveBundleDirForDeck(deckDir);
|
|
578
|
+
} catch (err) {
|
|
579
|
+
if (!isBrandRouteMiss(err)) throw err;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
556
583
|
if (hasJsx && (!bundleDir || !bundleHasCaptureEngine(bundleDir))) {
|
|
557
584
|
const registry = await requireRenderHost();
|
|
558
585
|
const entry = pickPresentationEntry(registry, presentationId, {
|
|
@@ -587,19 +614,19 @@ export async function viewDraft(
|
|
|
587
614
|
throw new Error(
|
|
588
615
|
"draft preview needs the corporate design's capture engine, which ships in the " +
|
|
589
616
|
"materialized bundle. Run `slidemind download-bundle <deck>` against an open " +
|
|
590
|
-
"PowerPoint host once, then retry — or use `slidemind
|
|
617
|
+
"PowerPoint host once, then retry — or use `slidemind view-draft <deck>` for a " +
|
|
591
618
|
"host-free HTML preview.",
|
|
592
619
|
);
|
|
593
620
|
}
|
|
594
621
|
|
|
595
622
|
let captured;
|
|
596
623
|
try {
|
|
597
|
-
captured = await captureDeckLocally({ deckDir, bundleDir, onProgress });
|
|
624
|
+
captured = await captureDeckLocally({ deckDir, bundleDir, onProgress, only });
|
|
598
625
|
} catch (err) {
|
|
599
626
|
throw new Error(
|
|
600
627
|
`local draft capture failed (${err instanceof Error ? err.message : String(err)}). ` +
|
|
601
|
-
"It runs a local headless Chrome; if Chrome can't launch here,
|
|
602
|
-
"`slidemind
|
|
628
|
+
"It runs a local headless Chrome; if Chrome can't launch here, " +
|
|
629
|
+
"`slidemind review <deck>` still builds and serves the HTML preview without it." +
|
|
603
630
|
(Object.keys(conversions).length > 0 ? ` ${convertedNote}.` : ""),
|
|
604
631
|
);
|
|
605
632
|
}
|
|
@@ -619,7 +646,67 @@ export async function viewDraft(
|
|
|
619
646
|
return mapCapturedSlide(fileName, byFile.get(fileName), warnings);
|
|
620
647
|
});
|
|
621
648
|
|
|
622
|
-
|
|
649
|
+
// --translate: run the captured slides through the server's translate gate
|
|
650
|
+
// (slot-fit, diagram-geometry, connector-endpoints — the checks the local
|
|
651
|
+
// capture cannot make) and hand its verdict back per slide. The returned
|
|
652
|
+
// .pptx is discarded: this is validation, not delivery, so no review gate
|
|
653
|
+
// stands in front of it and a per-slide builder can prove its ONE slide
|
|
654
|
+
// buildable before reporting. Pane-free over the deck's bound design.
|
|
655
|
+
// `translate: "auto"` (the default) runs the gate when it can and reports
|
|
656
|
+
// why not otherwise — an unbound deck or a dead backend must not turn a
|
|
657
|
+
// host-free draft check into a failure; `true` (--translate) insists.
|
|
658
|
+
let translateErrors = null;
|
|
659
|
+
let translateWarnings = [];
|
|
660
|
+
let translateRan = false;
|
|
661
|
+
if (translate && captured?.slides?.length) {
|
|
662
|
+
const skip = (why) => {
|
|
663
|
+
if (translate !== "auto") throw new Error(why);
|
|
664
|
+
warnings.push(`export gate skipped: ${why}`);
|
|
665
|
+
};
|
|
666
|
+
if (!ref?.presFP) {
|
|
667
|
+
skip("the deck is not bound to a design (run `slidemind ready <deck>` first)");
|
|
668
|
+
} else {
|
|
669
|
+
let mat = null;
|
|
670
|
+
try {
|
|
671
|
+
mat = await materializeBundle(null, { brandId: ref.presFP });
|
|
672
|
+
} catch (err) {
|
|
673
|
+
skip(`the design's backend did not answer (${err?.message ?? err})`);
|
|
674
|
+
}
|
|
675
|
+
if (mat) writeDeckBundleRef(deckDir, { presFP: mat.presFP, etag: mat.etag });
|
|
676
|
+
if (mat && !mat.backendURL) {
|
|
677
|
+
skip("the bound design's backend could not be resolved (brand route unavailable)");
|
|
678
|
+
} else if (mat) {
|
|
679
|
+
try {
|
|
680
|
+
const res = await translateDeck({
|
|
681
|
+
brandId: ref.presFP,
|
|
682
|
+
backendURL: mat.backendURL,
|
|
683
|
+
deckName: path.basename(deckDir),
|
|
684
|
+
captured,
|
|
685
|
+
etag: mat.etag,
|
|
686
|
+
});
|
|
687
|
+
translateErrors = new Map();
|
|
688
|
+
translateWarnings = Array.isArray(res?.warnings) ? res.warnings : [];
|
|
689
|
+
translateRan = true;
|
|
690
|
+
} catch (err) {
|
|
691
|
+
if (err?.status === 422) {
|
|
692
|
+
const { parseTranslateErrors } = await import("../builders/draft-check.mjs");
|
|
693
|
+
translateErrors = parseTranslateErrors(err.message);
|
|
694
|
+
translateRan = true;
|
|
695
|
+
} else {
|
|
696
|
+
skip(`the export gate did not answer (${err?.message ?? err})`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const slide of slides) {
|
|
703
|
+
if (translateErrors && slide.status === "ok") {
|
|
704
|
+
slide.translateErrors = translateErrors.get(slide.fileName) ?? [];
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (translateErrors?.has("")) warnings.push(...translateErrors.get(""));
|
|
708
|
+
|
|
709
|
+
return { slides, warnings, translateWarnings, translateRan };
|
|
623
710
|
}
|
|
624
711
|
|
|
625
712
|
// A brand-route miss is definitive for the pane-free path but NOT for the
|
|
@@ -631,7 +718,9 @@ function isBrandRouteMiss(err) {
|
|
|
631
718
|
err?.code === "brand_route_unavailable" ||
|
|
632
719
|
err?.code === "unknown_brand" ||
|
|
633
720
|
err?.code === "no_active_cd" ||
|
|
634
|
-
|
|
721
|
+
// A 404 WITHOUT a machine code is Express saying "no such route" (an old
|
|
722
|
+
// add-in); a coded 404 is the route itself talking and must surface.
|
|
723
|
+
(err?.status === 404 && !err?.code)
|
|
635
724
|
);
|
|
636
725
|
}
|
|
637
726
|
|
|
@@ -776,8 +865,9 @@ export async function buildAndPush({
|
|
|
776
865
|
// backgrounded during the render); the pane matters only for the insert. ---
|
|
777
866
|
let result = null;
|
|
778
867
|
let renderEntry = null;
|
|
779
|
-
|
|
780
|
-
|
|
868
|
+
const brandId = presentationId ? null : await resolveWindowlessBrand(deckDir);
|
|
869
|
+
if (brandId) {
|
|
870
|
+
const detached = await tryDetachedRender(deckDir, { presFP: brandId }, onProgress, reconcile);
|
|
781
871
|
if (detached) result = detached.result;
|
|
782
872
|
}
|
|
783
873
|
if (!result) {
|
|
@@ -983,10 +1073,13 @@ export async function buildPptxFile({
|
|
|
983
1073
|
// folded or reported.
|
|
984
1074
|
const reconcile = collectReconcileInputs({ deckDir, outFile: out, force });
|
|
985
1075
|
|
|
986
|
-
// Pane-free path
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
|
|
1076
|
+
// Pane-free path: the deck's binding, else the one design at hand (cache /
|
|
1077
|
+
// brand list). `--to <presId>` names a window explicitly and therefore keeps
|
|
1078
|
+
// the registry path — reading the design off an open presentation is the one
|
|
1079
|
+
// thing a render legitimately needs a window for.
|
|
1080
|
+
const brandId = presentationId ? null : await resolveWindowlessBrand(deckDir);
|
|
1081
|
+
if (brandId) {
|
|
1082
|
+
const detached = await tryDetachedRender(deckDir, { presFP: brandId }, onProgress, reconcile);
|
|
990
1083
|
if (detached) {
|
|
991
1084
|
fs.writeFileSync(out, Buffer.from(detached.result.base64, "base64"));
|
|
992
1085
|
return {
|