slidemind 0.3.19 → 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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +591 -15
- package/README.md +202 -7
- package/bin/slidemind.mjs +50 -2
- package/package.json +2 -2
- package/skills/create-slides/SKILL.md +484 -886
- package/skills/search/SKILL.md +46 -23
- package/src/bridge/build-and-push.mjs +548 -73
- package/src/bridge/bundle.mjs +100 -31
- package/src/bridge/capture.mjs +14 -17
- package/src/bridge/client/brands.mjs +157 -18
- package/src/bridge/client/engine-routing.mjs +48 -13
- package/src/bridge/client/engine-search.mjs +24 -1
- package/src/bridge/client/engine-slides.mjs +1 -1
- package/src/bridge/client/render.mjs +27 -1
- package/src/bridge/client/transport.mjs +499 -23
- package/src/bridge/client.mjs +10 -0
- package/src/bridge/config.mjs +67 -6
- package/src/bridge/engine-download.mjs +0 -10
- package/src/bridge/graph-auth.mjs +55 -3
- package/src/bridge/image-intent-envelope.mjs +239 -0
- package/src/bridge/package-preview.mjs +67 -0
- package/src/bridge/sharepoint.mjs +914 -0
- package/src/builders/asset-fetch.mjs +203 -0
- package/src/builders/block-dividers.mjs +83 -0
- package/src/builders/build.mjs +171 -87
- package/src/builders/card-height.mjs +294 -0
- package/src/builders/chart-hole.mjs +183 -0
- package/src/builders/chart-jsx.mjs +410 -0
- package/src/builders/chart-labels.mjs +326 -0
- package/src/builders/concept-geometry.mjs +370 -0
- package/src/builders/concept-intent.mjs +200 -0
- package/src/builders/concept-layout.mjs +79 -0
- package/src/builders/concept-lines.mjs +509 -0
- package/src/builders/concept-lists.mjs +270 -0
- package/src/builders/concept-marks.mjs +84 -0
- package/src/builders/concept-relationships.mjs +12 -0
- package/src/builders/concept-spec.mjs +645 -0
- package/src/builders/concept-text.mjs +140 -0
- package/src/builders/concept-waves.mjs +57 -0
- package/src/builders/copy-fit.mjs +139 -0
- package/src/builders/design-tokens.mjs +347 -0
- package/src/builders/draft-check.mjs +552 -340
- package/src/builders/draft-lines.mjs +369 -0
- package/src/builders/edit-route.mjs +29 -0
- package/src/builders/figure-sketch.mjs +375 -0
- package/src/builders/fold-sets.mjs +639 -0
- package/src/builders/gate-server.mjs +9 -4
- package/src/builders/gate-session.js +273 -12
- package/src/builders/gate-shell.js +1494 -0
- package/src/builders/image-intent-check.mjs +308 -0
- package/src/builders/image-use-reconcile.mjs +107 -0
- package/src/builders/kit-helpers.mjs +121 -0
- package/src/builders/layout-names.mjs +32 -0
- package/src/builders/legacy-reference-uses.mjs +179 -0
- package/src/builders/line-fit.mjs +224 -0
- package/src/builders/line-native.mjs +895 -0
- package/src/builders/line-relations.mjs +230 -0
- package/src/builders/lint-fix.mjs +117 -9
- package/src/builders/lint.mjs +25 -20
- package/src/builders/list-jsx.mjs +88 -0
- package/src/builders/placement.mjs +133 -0
- package/src/builders/plan-checks.mjs +135 -0
- package/src/builders/plan-comments.mjs +19 -0
- package/src/builders/plan-handlers.mjs +95 -0
- package/src/builders/plan-markdown-edit.mjs +3 -3
- package/src/builders/plan-overlay.js +1530 -1608
- package/src/builders/plan.mjs +689 -186
- package/src/builders/preset-definitions.json +1 -0
- package/src/builders/preset-eval.mjs +244 -0
- package/src/builders/preview.html +51 -120
- package/src/builders/red-thread-backbone.mjs +135 -36
- package/src/builders/repair.mjs +205 -0
- package/src/builders/repeat-groups.mjs +540 -0
- package/src/builders/resolve-skill.mjs +5 -4
- package/src/builders/review-overlay.js +296 -293
- package/src/builders/review.mjs +305 -84
- package/src/builders/rule-shift.mjs +96 -0
- package/src/builders/seat.mjs +354 -0
- package/src/builders/settle-driver.mjs +236 -0
- package/src/builders/settle.mjs +1448 -0
- package/src/builders/slide-prep.mjs +67 -0
- package/src/builders/spec-assets.mjs +236 -0
- package/src/builders/spec-match.mjs +56 -0
- package/src/builders/stack.mjs +413 -0
- package/src/builders/storyboard-markdown.mjs +70 -27
- package/src/builders/storyboard-schema.mjs +54 -114
- package/src/builders/text-blocks.mjs +212 -0
- package/src/builders/text-card-fit.mjs +552 -0
- package/src/builders/text-stack.mjs +276 -0
- package/src/builders/transcribe.mjs +1069 -0
- package/src/cli/assets.mjs +164 -0
- package/src/cli/common.mjs +173 -12
- package/src/cli/concept-spec-support.mjs +76 -0
- package/src/cli/concepts.mjs +1002 -103
- package/src/cli/deck.mjs +401 -65
- package/src/cli/diagnose.mjs +4 -2
- package/src/cli/engine-helpers.mjs +17 -10
- package/src/cli/engine-results.mjs +76 -3
- package/src/cli/help.mjs +232 -28
- package/src/cli/install.mjs +9 -0
- package/src/cli/maintenance.mjs +104 -0
- package/src/cli/read.mjs +2 -1
- package/src/cli/ready.mjs +6 -3
- package/src/cli/record.mjs +522 -0
- package/src/cli/remote.mjs +818 -0
- package/src/cli/revision.mjs +686 -0
- package/src/cli/search.mjs +30 -11
- package/src/cli/session.mjs +56 -13
- package/src/cli/setup.mjs +46 -2
- package/src/cli/status.mjs +142 -33
- package/src/cli/tasks.mjs +246 -0
- package/src/cli/transcribe.mjs +854 -0
- package/src/cli/workspace-ui.mjs +260 -0
- package/src/cli/workspace.mjs +212 -34
- package/src/install-channel.mjs +64 -7
- package/src/lib/adopt.mjs +346 -25
- package/src/lib/concept-spec-binding.mjs +96 -0
- package/src/lib/conversions.mjs +70 -6
- package/src/lib/convert-folded.mjs +107 -0
- package/src/lib/deck-paths.mjs +418 -0
- package/src/lib/durable-ids.mjs +47 -0
- package/src/lib/edit-operations.mjs +316 -0
- package/src/lib/edit-state.mjs +1244 -0
- package/src/lib/edit-transitions.mjs +120 -0
- package/src/lib/extensions.mjs +501 -0
- package/src/lib/image-assets.mjs +465 -0
- package/src/lib/image-derivatives.mjs +234 -0
- package/src/lib/image-intent-fingerprint.mjs +70 -0
- package/src/lib/image-intent-tags.mjs +176 -0
- package/src/lib/image-intent.mjs +802 -0
- package/src/lib/image-use-overrides.mjs +279 -0
- package/src/lib/inline-components.mjs +613 -0
- package/src/lib/lint-converted.mjs +68 -4
- package/src/lib/merge-json.mjs +163 -0
- package/src/lib/overwrite-guard.mjs +3 -3
- package/src/lib/presentation-context.mjs +531 -0
- package/src/lib/preservation-tags.mjs +157 -0
- package/src/lib/preservation-verdict.mjs +1342 -0
- package/src/lib/publish.mjs +1557 -0
- package/src/lib/rebase.mjs +945 -0
- package/src/lib/reconcile-ack.mjs +4 -6
- package/src/lib/reconcile.mjs +62 -24
- package/src/lib/record-client.mjs +175 -0
- package/src/lib/record-state.mjs +151 -0
- package/src/lib/remote.mjs +661 -0
- package/src/lib/revision.mjs +1210 -0
- package/src/lib/slide-guids.mjs +4 -6
- package/src/lib/slide-preservation.mjs +423 -0
- package/src/lib/slide-selection.mjs +9 -0
- package/src/lib/workspace-transaction.mjs +117 -0
- package/src/pptx/content-digest.mjs +334 -0
- package/src/pptx/deck-assemble.mjs +1278 -0
- package/src/pptx/fill/engine.mjs +2 -3
- package/src/pptx/fill/freeform.mjs +6 -3
- package/src/pptx/ooxml.mjs +20 -0
- package/src/pptx/package-evidence.mjs +383 -0
- package/src/pptx/tags.mjs +163 -11
- package/src/update-check.mjs +22 -4
- package/src/workspace/adapters.mjs +222 -0
- package/src/workspace/auth.mjs +256 -0
- package/src/workspace/autostart.mjs +204 -0
- package/src/workspace/daemon.mjs +360 -0
- package/src/workspace/discovery.mjs +180 -0
- package/src/workspace/edit.mjs +167 -0
- package/src/workspace/events.mjs +52 -0
- package/src/workspace/gate-client.mjs +290 -0
- package/src/workspace/handback.mjs +73 -0
- package/src/workspace/lifecycle.mjs +635 -0
- package/src/workspace/operations.mjs +200 -0
- package/src/workspace/pick-folder.mjs +152 -0
- package/src/workspace/registry.mjs +359 -0
- package/src/workspace/remote-links.mjs +397 -0
- package/src/workspace/remote-status.mjs +359 -0
- package/src/workspace/routes.mjs +1331 -0
- package/src/workspace/server.mjs +159 -0
- package/src/workspace/sync.mjs +1119 -0
- package/src/workspace/task-actions.mjs +215 -0
- package/src/workspace/tasks.mjs +507 -0
- package/src/workspace/ui/app.js +1886 -0
- package/src/workspace/ui/index.html +141 -0
- package/src/workspace/ui/plan-bridge.js +120 -0
- package/src/workspace/ui/sharepoint.svg +59 -0
- package/src/workspace/ui/styles.css +423 -0
- 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
|
+
}
|