slidemind 0.3.20 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/AGENTS.md +591 -15
  4. package/README.md +202 -7
  5. package/bin/slidemind.mjs +50 -2
  6. package/package.json +2 -2
  7. package/skills/create-slides/SKILL.md +484 -886
  8. package/skills/search/SKILL.md +46 -23
  9. package/src/bridge/build-and-push.mjs +548 -73
  10. package/src/bridge/bundle.mjs +100 -31
  11. package/src/bridge/capture.mjs +14 -17
  12. package/src/bridge/client/brands.mjs +157 -18
  13. package/src/bridge/client/engine-routing.mjs +48 -13
  14. package/src/bridge/client/engine-search.mjs +24 -1
  15. package/src/bridge/client/render.mjs +27 -1
  16. package/src/bridge/client/transport.mjs +499 -23
  17. package/src/bridge/client.mjs +10 -0
  18. package/src/bridge/config.mjs +67 -6
  19. package/src/bridge/engine-download.mjs +0 -10
  20. package/src/bridge/graph-auth.mjs +55 -3
  21. package/src/bridge/image-intent-envelope.mjs +239 -0
  22. package/src/bridge/package-preview.mjs +67 -0
  23. package/src/bridge/sharepoint.mjs +914 -0
  24. package/src/builders/asset-fetch.mjs +203 -0
  25. package/src/builders/block-dividers.mjs +83 -0
  26. package/src/builders/build.mjs +171 -87
  27. package/src/builders/card-height.mjs +294 -0
  28. package/src/builders/chart-hole.mjs +183 -0
  29. package/src/builders/chart-jsx.mjs +410 -0
  30. package/src/builders/chart-labels.mjs +326 -0
  31. package/src/builders/concept-geometry.mjs +370 -0
  32. package/src/builders/concept-intent.mjs +200 -0
  33. package/src/builders/concept-layout.mjs +79 -0
  34. package/src/builders/concept-lines.mjs +509 -0
  35. package/src/builders/concept-lists.mjs +270 -0
  36. package/src/builders/concept-marks.mjs +84 -0
  37. package/src/builders/concept-relationships.mjs +12 -0
  38. package/src/builders/concept-spec.mjs +645 -0
  39. package/src/builders/concept-text.mjs +140 -0
  40. package/src/builders/concept-waves.mjs +57 -0
  41. package/src/builders/copy-fit.mjs +139 -0
  42. package/src/builders/design-tokens.mjs +347 -0
  43. package/src/builders/draft-check.mjs +552 -340
  44. package/src/builders/draft-lines.mjs +369 -0
  45. package/src/builders/edit-route.mjs +29 -0
  46. package/src/builders/figure-sketch.mjs +375 -0
  47. package/src/builders/fold-sets.mjs +639 -0
  48. package/src/builders/gate-server.mjs +9 -4
  49. package/src/builders/gate-session.js +273 -12
  50. package/src/builders/gate-shell.js +1494 -0
  51. package/src/builders/image-intent-check.mjs +308 -0
  52. package/src/builders/image-use-reconcile.mjs +107 -0
  53. package/src/builders/kit-helpers.mjs +121 -0
  54. package/src/builders/layout-names.mjs +32 -0
  55. package/src/builders/legacy-reference-uses.mjs +179 -0
  56. package/src/builders/line-fit.mjs +224 -0
  57. package/src/builders/line-native.mjs +895 -0
  58. package/src/builders/line-relations.mjs +230 -0
  59. package/src/builders/lint-fix.mjs +117 -9
  60. package/src/builders/lint.mjs +25 -20
  61. package/src/builders/list-jsx.mjs +88 -0
  62. package/src/builders/placement.mjs +133 -0
  63. package/src/builders/plan-checks.mjs +135 -0
  64. package/src/builders/plan-comments.mjs +19 -0
  65. package/src/builders/plan-handlers.mjs +95 -0
  66. package/src/builders/plan-markdown-edit.mjs +3 -3
  67. package/src/builders/plan-overlay.js +1530 -1608
  68. package/src/builders/plan.mjs +689 -186
  69. package/src/builders/preset-definitions.json +1 -0
  70. package/src/builders/preset-eval.mjs +244 -0
  71. package/src/builders/preview.html +51 -120
  72. package/src/builders/red-thread-backbone.mjs +135 -36
  73. package/src/builders/repair.mjs +205 -0
  74. package/src/builders/repeat-groups.mjs +540 -0
  75. package/src/builders/resolve-skill.mjs +5 -4
  76. package/src/builders/review-overlay.js +296 -293
  77. package/src/builders/review.mjs +305 -84
  78. package/src/builders/rule-shift.mjs +96 -0
  79. package/src/builders/seat.mjs +354 -0
  80. package/src/builders/settle-driver.mjs +236 -0
  81. package/src/builders/settle.mjs +1448 -0
  82. package/src/builders/slide-prep.mjs +67 -0
  83. package/src/builders/spec-assets.mjs +236 -0
  84. package/src/builders/spec-match.mjs +56 -0
  85. package/src/builders/stack.mjs +413 -0
  86. package/src/builders/storyboard-markdown.mjs +70 -27
  87. package/src/builders/storyboard-schema.mjs +54 -114
  88. package/src/builders/text-blocks.mjs +212 -0
  89. package/src/builders/text-card-fit.mjs +552 -0
  90. package/src/builders/text-stack.mjs +276 -0
  91. package/src/builders/transcribe.mjs +1069 -0
  92. package/src/cli/assets.mjs +164 -0
  93. package/src/cli/common.mjs +173 -12
  94. package/src/cli/concept-spec-support.mjs +76 -0
  95. package/src/cli/concepts.mjs +1002 -103
  96. package/src/cli/deck.mjs +401 -65
  97. package/src/cli/diagnose.mjs +4 -2
  98. package/src/cli/engine-helpers.mjs +17 -10
  99. package/src/cli/engine-results.mjs +76 -3
  100. package/src/cli/help.mjs +232 -28
  101. package/src/cli/install.mjs +9 -0
  102. package/src/cli/maintenance.mjs +104 -0
  103. package/src/cli/read.mjs +2 -1
  104. package/src/cli/ready.mjs +6 -3
  105. package/src/cli/record.mjs +522 -0
  106. package/src/cli/remote.mjs +818 -0
  107. package/src/cli/revision.mjs +686 -0
  108. package/src/cli/search.mjs +30 -11
  109. package/src/cli/session.mjs +56 -13
  110. package/src/cli/setup.mjs +46 -2
  111. package/src/cli/status.mjs +142 -33
  112. package/src/cli/tasks.mjs +246 -0
  113. package/src/cli/transcribe.mjs +854 -0
  114. package/src/cli/workspace-ui.mjs +260 -0
  115. package/src/cli/workspace.mjs +212 -34
  116. package/src/install-channel.mjs +64 -7
  117. package/src/lib/adopt.mjs +346 -25
  118. package/src/lib/concept-spec-binding.mjs +96 -0
  119. package/src/lib/conversions.mjs +70 -6
  120. package/src/lib/convert-folded.mjs +107 -0
  121. package/src/lib/deck-paths.mjs +418 -0
  122. package/src/lib/durable-ids.mjs +47 -0
  123. package/src/lib/edit-operations.mjs +316 -0
  124. package/src/lib/edit-state.mjs +1244 -0
  125. package/src/lib/edit-transitions.mjs +120 -0
  126. package/src/lib/extensions.mjs +501 -0
  127. package/src/lib/image-assets.mjs +465 -0
  128. package/src/lib/image-derivatives.mjs +234 -0
  129. package/src/lib/image-intent-fingerprint.mjs +70 -0
  130. package/src/lib/image-intent-tags.mjs +176 -0
  131. package/src/lib/image-intent.mjs +802 -0
  132. package/src/lib/image-use-overrides.mjs +279 -0
  133. package/src/lib/inline-components.mjs +613 -0
  134. package/src/lib/lint-converted.mjs +68 -4
  135. package/src/lib/merge-json.mjs +163 -0
  136. package/src/lib/overwrite-guard.mjs +3 -3
  137. package/src/lib/presentation-context.mjs +531 -0
  138. package/src/lib/preservation-tags.mjs +157 -0
  139. package/src/lib/preservation-verdict.mjs +1342 -0
  140. package/src/lib/publish.mjs +1557 -0
  141. package/src/lib/rebase.mjs +945 -0
  142. package/src/lib/reconcile-ack.mjs +4 -6
  143. package/src/lib/reconcile.mjs +62 -24
  144. package/src/lib/record-client.mjs +175 -0
  145. package/src/lib/record-state.mjs +151 -0
  146. package/src/lib/remote.mjs +661 -0
  147. package/src/lib/revision.mjs +1210 -0
  148. package/src/lib/slide-guids.mjs +4 -6
  149. package/src/lib/slide-preservation.mjs +423 -0
  150. package/src/lib/slide-selection.mjs +9 -0
  151. package/src/lib/workspace-transaction.mjs +117 -0
  152. package/src/pptx/content-digest.mjs +334 -0
  153. package/src/pptx/deck-assemble.mjs +1278 -0
  154. package/src/pptx/fill/engine.mjs +2 -3
  155. package/src/pptx/fill/freeform.mjs +6 -3
  156. package/src/pptx/ooxml.mjs +20 -0
  157. package/src/pptx/package-evidence.mjs +383 -0
  158. package/src/pptx/tags.mjs +163 -11
  159. package/src/update-check.mjs +22 -4
  160. package/src/workspace/adapters.mjs +222 -0
  161. package/src/workspace/auth.mjs +256 -0
  162. package/src/workspace/autostart.mjs +204 -0
  163. package/src/workspace/daemon.mjs +360 -0
  164. package/src/workspace/discovery.mjs +180 -0
  165. package/src/workspace/edit.mjs +167 -0
  166. package/src/workspace/events.mjs +52 -0
  167. package/src/workspace/gate-client.mjs +290 -0
  168. package/src/workspace/handback.mjs +73 -0
  169. package/src/workspace/lifecycle.mjs +635 -0
  170. package/src/workspace/operations.mjs +200 -0
  171. package/src/workspace/pick-folder.mjs +152 -0
  172. package/src/workspace/registry.mjs +359 -0
  173. package/src/workspace/remote-links.mjs +397 -0
  174. package/src/workspace/remote-status.mjs +359 -0
  175. package/src/workspace/routes.mjs +1331 -0
  176. package/src/workspace/server.mjs +159 -0
  177. package/src/workspace/sync.mjs +1119 -0
  178. package/src/workspace/task-actions.mjs +215 -0
  179. package/src/workspace/tasks.mjs +507 -0
  180. package/src/workspace/ui/app.js +1886 -0
  181. package/src/workspace/ui/index.html +141 -0
  182. package/src/workspace/ui/plan-bridge.js +120 -0
  183. package/src/workspace/ui/sharepoint.svg +59 -0
  184. package/src/workspace/ui/styles.css +423 -0
  185. package/src/builders/visual-families.mjs +0 -46
@@ -0,0 +1,236 @@
1
+ // The first-write settle, driven the same way by both clients: the CLI's
2
+ // `transcribe` and the add-in's slide preparation run THIS loop and hand in only
3
+ // their renderer and their file access. Mirrored into the add-in by
4
+ // scripts/sync-transcription-runtime.mjs — no Node or CLI imports here.
5
+ import { fitTextToImage, fitConnectors, measuredFromRender } from "./settle.mjs";
6
+ import { fitTextToCard } from "./text-card-fit.mjs";
7
+ import { translateElement, writtenTop } from "./concept-geometry.mjs";
8
+ import { foldSets } from "./fold-sets.mjs";
9
+ import { applyCopyFit, slideContext, textOverruns } from "./copy-fit.mjs";
10
+ import { typeLadder } from "./design-tokens.mjs";
11
+
12
+ export const SETTLE_PASSES = 4;
13
+
14
+ /**
15
+ * The last render's measurements with a pass's changes applied, or null when
16
+ * that pass needs a new render: a pass that only moved elements and gave cards
17
+ * new heights leaves every text as it was set (its lines, its ink), so the
18
+ * next pass can run on the moved rects. A text's width, a wrap, a scaled disc,
19
+ * anything else changes what a render measures — as does a move the writer
20
+ * does not make one to one (a text on the slot's floor, writtenTop).
21
+ */
22
+ export function movedMeasurements(measured, before, after) {
23
+ if (after.elements.length !== before.elements.length) return null;
24
+ const slot = after.slot;
25
+ const was = new Map(before.elements.map((e) => [e.id, e]));
26
+ const out = new Map(measured);
27
+ for (const e of after.elements) {
28
+ const o = was.get(e.id);
29
+ if (!o) return null;
30
+ const [dx, dy, dw, dh] = [e.x - o.x, e.y - o.y, e.w - o.w, e.h - o.h];
31
+ // The element as it was, moved (and, a shape, resized): anything else differs.
32
+ const text = e.text != null;
33
+ const as = structuredClone(o);
34
+ translateElement(as, dx, dy);
35
+ if (!text) Object.assign(as, { w: e.w, h: e.h });
36
+ if (JSON.stringify(as, near) !== JSON.stringify(e, near)) return null;
37
+ if (Math.abs(writtenTop(e, slot) - writtenTop(o, slot) - dy) > 1e-6) return null;
38
+ const m = measured.get(e.id);
39
+ if (!m || !(dx || dy || dw || dh)) continue;
40
+ out.set(e.id, {
41
+ ...m,
42
+ x: m.x + dx,
43
+ y: m.y + dy,
44
+ ...(Number.isFinite(m.inkY) ? { inkY: m.inkY + dy } : {}),
45
+ ...(text ? {} : { w: m.w + dw, h: m.h + dh }),
46
+ });
47
+ }
48
+ return out;
49
+ }
50
+ // Moves add up in floats: equal to a micro-pixel.
51
+ const near = (_k, v) => (typeof v === "number" ? Math.round(v * 1e6) / 1e6 : v);
52
+
53
+ /**
54
+ * Render the machine-written slides, fit each text's box to the room the image
55
+ * gives it, rewrite, re-render — until nothing changes or the passes run out. Then fit
56
+ * every connector once, on the final geometry.
57
+ *
58
+ * - `slides`: `[{ key, spec }]` — `spec` is replaced as the passes adjust it.
59
+ * - `capture(keys)` → `Map<key, render>` (a render carries `boxes`); throw when
60
+ * the renderer is unavailable. `null` = no renderer: connectors are fitted
61
+ * against the spec's own rectangles.
62
+ * - `rescaffold(slide, spec)` rewrites that slide's source from the adjusted spec.
63
+ * - `readSource(key)` / `writeSource(key, src)` for the connector fit and the
64
+ * set fold.
65
+ * - `ramp` (design-tokens.mjs parseTypeRamp): a slide whose text still runs
66
+ * past its room moves its whole type ladder one step down, once.
67
+ * - `fitCopy({ key, items, context, plan })` → `[{ id, text }]`: the words of
68
+ * what still does not fit, one call per slide (copy-fit.mjs copyFitPrompt);
69
+ * a slide's `plan` is its storyboard beat.
70
+ */
71
+ function createSession({ capture, rescaffold, readSource, writeSource, ramp = null, fitCopy = null, passes = SETTLE_PASSES, onPass = (_pass, _count) => {}, onError = (_stage, _error) => {} }) {
72
+ const result = { passes: 0, changes: 0, bySlide: new Map() };
73
+ const note = (key, lines) => {
74
+ if (!lines?.length) return;
75
+ result.bySlide.set(key, [...(result.bySlide.get(key) ?? []), ...lines]);
76
+ };
77
+ let rendered = Boolean(capture);
78
+ // The measurements of a slide's final geometry (a render whose pass changed
79
+ // nothing): the connector fit reads them instead of capturing the slide again.
80
+ const finals = new Map();
81
+ // Each slide's last rendered measurements, final or not.
82
+ const latest = new Map();
83
+ const settlePasses = async (list) => {
84
+ let live = list;
85
+ for (let pass = 1; pass <= passes && live.length; pass++) {
86
+ onPass(pass, live.length);
87
+ let renders;
88
+ try {
89
+ renders = await capture(live.map((s) => s.key));
90
+ } catch (err) {
91
+ onError("settle", err);
92
+ if (pass === 1) rendered = false;
93
+ break;
94
+ }
95
+ result.passes = Math.max(result.passes, pass);
96
+ const next = [];
97
+ for (const s of live) {
98
+ const render = renders.get(s.key);
99
+ if (!render?.boxes) { onError("render", new Error(`${s.key}: no render in settle pass ${pass} — left as written`)); continue; }
100
+ let measured = measuredFromRender(render, s.spec.slot);
101
+ latest.set(s.key, measured);
102
+ const texts = [...measured.values()].filter((m) => m.text);
103
+ if (texts.length && texts.every((m) => !(m.inkWidth > 0))) onError("render", new Error(`${s.key}: the render carried no text measurements in settle pass ${pass} — its type was not fitted`));
104
+ // Ink first (a word wider than its frame), then a text past its card
105
+ // or grown into its stack, in the same pass: the card levers leave
106
+ // alone what the ink fit just re-wrapped (`stale`), which waits for
107
+ // the render of this pass; everything else is independent of it. A
108
+ // pass that only moved things runs again on the moved measurements
109
+ // (movedMeasurements) instead of waiting for a render; the state it
110
+ // ends in is predicted, so it gets one render of its own to confirm
111
+ // it (a moved text wraps or lands differently than predicted) and
112
+ // never stands as final measurements. A wrap that leaves its stack
113
+ // to the next render is not made on the last pass (`last`).
114
+ for (let rigid = 0; ; rigid++) {
115
+ const ink = fitTextToImage(s.spec, measured, { last: pass === passes });
116
+ const card = fitTextToCard(ink.spec, measured, { stale: ink.stale });
117
+ const changes = [...ink.changes, ...card.changes];
118
+ if (!changes.length) {
119
+ if (rigid) next.push(s);
120
+ else finals.set(s.key, measured);
121
+ break;
122
+ }
123
+ const before = s.spec;
124
+ s.spec = card.spec;
125
+ result.changes += changes.length;
126
+ note(s.key, changes);
127
+ await rescaffold(s, card.spec);
128
+ const moved = movedMeasurements(measured, before, card.spec);
129
+ if (!moved) { next.push(s); break; }
130
+ measured = moved;
131
+ // Moves that keep answering each other settle nothing a render would.
132
+ if (rigid + 1 >= passes) { next.push(s); break; }
133
+ }
134
+ }
135
+ live = next;
136
+ }
137
+ };
138
+ const overruns = (s, tighten) => {
139
+ const measured = finals.get(s.key) ?? latest.get(s.key);
140
+ return measured ? textOverruns(s.spec, measured, { tighten }) : [];
141
+ };
142
+ const rewrite = async (s, spec, lines) => {
143
+ s.spec = spec;
144
+ result.changes += lines.length;
145
+ note(s.key, lines);
146
+ finals.delete(s.key);
147
+ await rescaffold(s, spec);
148
+ };
149
+ // A slide keeps ONE type hierarchy. Text that still runs past its room
150
+ // after the fit moves the whole ladder one step down (every text alike,
151
+ // once); what still does not fit then gets shorter words, never a size.
152
+ const fitType = async (list) => {
153
+ const down = typeLadder(ramp, -1);
154
+ if (down && down.body !== typeLadder(ramp, 0).body) {
155
+ const tight = list.filter((s) => !s.spec.typeStepOffset && overruns(s).length);
156
+ for (const s of tight) {
157
+ const n = overruns(s).length;
158
+ await rewrite(s, { ...s.spec, typeStepOffset: -1 }, [`type one step down (${down.body} body): ${n} text${n === 1 ? "" : "s"} ran past ${n === 1 ? "its" : "their"} room`]);
159
+ }
160
+ await settlePasses(tight);
161
+ }
162
+ if (!fitCopy) return;
163
+ for (const tighten of [1, 0.85]) {
164
+ const fitted = [];
165
+ await Promise.all(list.map(async (s) => {
166
+ const items = overruns(s, tighten).filter((i) => !i.why);
167
+ if (!items.length) return;
168
+ try {
169
+ const answers = await fitCopy({ key: s.key, items, context: slideContext(s.spec), plan: s.plan ?? null });
170
+ const applied = applyCopyFit(s.spec, items, answers);
171
+ if (!applied.changes.length) return;
172
+ await rewrite(s, applied.spec, applied.changes);
173
+ fitted.push(s);
174
+ } catch (err) {
175
+ onError("copy-fit", err);
176
+ }
177
+ }));
178
+ if (!fitted.length) return;
179
+ await settlePasses(fitted);
180
+ }
181
+ };
182
+ // Lines, once, on the final geometry: every glued connector is re-solved
183
+ // within its own preset against the measured nodes and the path the image
184
+ // draws (crossings count against it) — never anything but a native line.
185
+ const fitConnectorsOn = async (list) => {
186
+ const linked = list.filter((s) => s.spec?.connectors?.length);
187
+ if (!linked.length) return;
188
+ let renders = null;
189
+ const moved = linked.filter((s) => !finals.has(s.key));
190
+ if (rendered && moved.length) {
191
+ try { renders = await capture(moved.map((s) => s.key)); }
192
+ catch (err) { onError("connectors", err); }
193
+ }
194
+ for (const s of linked) {
195
+ const render = renders?.get(s.key);
196
+ if (!finals.has(s.key) && render?.boxes) finals.set(s.key, measuredFromRender(render, s.spec.slot));
197
+ const measured = finals.get(s.key) ?? new Map((s.spec.elements ?? []).map((e) => [e.id, { x: e.x, y: e.y, w: e.w, h: e.h, text: e.text != null, painted: Boolean(e.fill && e.fill !== "none") }]));
198
+ const src = await readSource(s.key);
199
+ const fitted = fitConnectors(src, s.spec, measured);
200
+ if (fitted.src !== src) await writeSource(s.key, fitted.src);
201
+ s.routeChanges = fitted.changes;
202
+ s.fitted = fitted.fitted;
203
+ }
204
+ };
205
+ // Last: every built set becomes one local component (fold-sets.mjs). Every
206
+ // pass before reads and rewrites the flat lines, so this runs after them.
207
+ const foldSetsOn = async (list) => {
208
+ for (const s of list) {
209
+ if (!s.spec?.repeats?.some((g) => g.built)) continue;
210
+ const src = await readSource(s.key);
211
+ const folded = foldSets(src, s.spec, finals.get(s.key) ?? latest.get(s.key) ?? null);
212
+ if (folded.src !== src) await writeSource(s.key, folded.src);
213
+ s.foldedSets = folded.folded;
214
+ }
215
+ };
216
+ return { result, settlePasses, fitType, fitConnectorsOn, foldSetsOn };
217
+ }
218
+
219
+ /**
220
+ * Render the machine-written slides, fit each text's box to the room the image
221
+ * gives it and inside the card it sits on (its size stays the design's ramp),
222
+ * rewrite, re-render — until nothing changes or the passes run out. Text that
223
+ * still does not fit steps the slide's ladder down, then shortens its words.
224
+ * Then fit every connector once, on the final geometry, and fold each built
225
+ * set into one component.
226
+ */
227
+ export async function settleWrittenSlides(opts) {
228
+ const session = createSession(opts);
229
+ if (opts.capture) {
230
+ await session.settlePasses(opts.slides);
231
+ await session.fitType(opts.slides);
232
+ }
233
+ await session.fitConnectorsOn(opts.slides);
234
+ await session.foldSetsOn(opts.slides);
235
+ return session.result;
236
+ }