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.
Files changed (186) 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/engine-slides.mjs +1 -1
  16. package/src/bridge/client/render.mjs +27 -1
  17. package/src/bridge/client/transport.mjs +499 -23
  18. package/src/bridge/client.mjs +10 -0
  19. package/src/bridge/config.mjs +67 -6
  20. package/src/bridge/engine-download.mjs +0 -10
  21. package/src/bridge/graph-auth.mjs +55 -3
  22. package/src/bridge/image-intent-envelope.mjs +239 -0
  23. package/src/bridge/package-preview.mjs +67 -0
  24. package/src/bridge/sharepoint.mjs +914 -0
  25. package/src/builders/asset-fetch.mjs +203 -0
  26. package/src/builders/block-dividers.mjs +83 -0
  27. package/src/builders/build.mjs +171 -87
  28. package/src/builders/card-height.mjs +294 -0
  29. package/src/builders/chart-hole.mjs +183 -0
  30. package/src/builders/chart-jsx.mjs +410 -0
  31. package/src/builders/chart-labels.mjs +326 -0
  32. package/src/builders/concept-geometry.mjs +370 -0
  33. package/src/builders/concept-intent.mjs +200 -0
  34. package/src/builders/concept-layout.mjs +79 -0
  35. package/src/builders/concept-lines.mjs +509 -0
  36. package/src/builders/concept-lists.mjs +270 -0
  37. package/src/builders/concept-marks.mjs +84 -0
  38. package/src/builders/concept-relationships.mjs +12 -0
  39. package/src/builders/concept-spec.mjs +645 -0
  40. package/src/builders/concept-text.mjs +140 -0
  41. package/src/builders/concept-waves.mjs +57 -0
  42. package/src/builders/copy-fit.mjs +139 -0
  43. package/src/builders/design-tokens.mjs +347 -0
  44. package/src/builders/draft-check.mjs +552 -340
  45. package/src/builders/draft-lines.mjs +369 -0
  46. package/src/builders/edit-route.mjs +29 -0
  47. package/src/builders/figure-sketch.mjs +375 -0
  48. package/src/builders/fold-sets.mjs +639 -0
  49. package/src/builders/gate-server.mjs +9 -4
  50. package/src/builders/gate-session.js +273 -12
  51. package/src/builders/gate-shell.js +1494 -0
  52. package/src/builders/image-intent-check.mjs +308 -0
  53. package/src/builders/image-use-reconcile.mjs +107 -0
  54. package/src/builders/kit-helpers.mjs +121 -0
  55. package/src/builders/layout-names.mjs +32 -0
  56. package/src/builders/legacy-reference-uses.mjs +179 -0
  57. package/src/builders/line-fit.mjs +224 -0
  58. package/src/builders/line-native.mjs +895 -0
  59. package/src/builders/line-relations.mjs +230 -0
  60. package/src/builders/lint-fix.mjs +117 -9
  61. package/src/builders/lint.mjs +25 -20
  62. package/src/builders/list-jsx.mjs +88 -0
  63. package/src/builders/placement.mjs +133 -0
  64. package/src/builders/plan-checks.mjs +135 -0
  65. package/src/builders/plan-comments.mjs +19 -0
  66. package/src/builders/plan-handlers.mjs +95 -0
  67. package/src/builders/plan-markdown-edit.mjs +3 -3
  68. package/src/builders/plan-overlay.js +1530 -1608
  69. package/src/builders/plan.mjs +689 -186
  70. package/src/builders/preset-definitions.json +1 -0
  71. package/src/builders/preset-eval.mjs +244 -0
  72. package/src/builders/preview.html +51 -120
  73. package/src/builders/red-thread-backbone.mjs +135 -36
  74. package/src/builders/repair.mjs +205 -0
  75. package/src/builders/repeat-groups.mjs +540 -0
  76. package/src/builders/resolve-skill.mjs +5 -4
  77. package/src/builders/review-overlay.js +296 -293
  78. package/src/builders/review.mjs +305 -84
  79. package/src/builders/rule-shift.mjs +96 -0
  80. package/src/builders/seat.mjs +354 -0
  81. package/src/builders/settle-driver.mjs +236 -0
  82. package/src/builders/settle.mjs +1448 -0
  83. package/src/builders/slide-prep.mjs +67 -0
  84. package/src/builders/spec-assets.mjs +236 -0
  85. package/src/builders/spec-match.mjs +56 -0
  86. package/src/builders/stack.mjs +413 -0
  87. package/src/builders/storyboard-markdown.mjs +70 -27
  88. package/src/builders/storyboard-schema.mjs +54 -114
  89. package/src/builders/text-blocks.mjs +212 -0
  90. package/src/builders/text-card-fit.mjs +552 -0
  91. package/src/builders/text-stack.mjs +276 -0
  92. package/src/builders/transcribe.mjs +1069 -0
  93. package/src/cli/assets.mjs +164 -0
  94. package/src/cli/common.mjs +173 -12
  95. package/src/cli/concept-spec-support.mjs +76 -0
  96. package/src/cli/concepts.mjs +1002 -103
  97. package/src/cli/deck.mjs +401 -65
  98. package/src/cli/diagnose.mjs +4 -2
  99. package/src/cli/engine-helpers.mjs +17 -10
  100. package/src/cli/engine-results.mjs +76 -3
  101. package/src/cli/help.mjs +232 -28
  102. package/src/cli/install.mjs +9 -0
  103. package/src/cli/maintenance.mjs +104 -0
  104. package/src/cli/read.mjs +2 -1
  105. package/src/cli/ready.mjs +6 -3
  106. package/src/cli/record.mjs +522 -0
  107. package/src/cli/remote.mjs +818 -0
  108. package/src/cli/revision.mjs +686 -0
  109. package/src/cli/search.mjs +30 -11
  110. package/src/cli/session.mjs +56 -13
  111. package/src/cli/setup.mjs +46 -2
  112. package/src/cli/status.mjs +142 -33
  113. package/src/cli/tasks.mjs +246 -0
  114. package/src/cli/transcribe.mjs +854 -0
  115. package/src/cli/workspace-ui.mjs +260 -0
  116. package/src/cli/workspace.mjs +212 -34
  117. package/src/install-channel.mjs +64 -7
  118. package/src/lib/adopt.mjs +346 -25
  119. package/src/lib/concept-spec-binding.mjs +96 -0
  120. package/src/lib/conversions.mjs +70 -6
  121. package/src/lib/convert-folded.mjs +107 -0
  122. package/src/lib/deck-paths.mjs +418 -0
  123. package/src/lib/durable-ids.mjs +47 -0
  124. package/src/lib/edit-operations.mjs +316 -0
  125. package/src/lib/edit-state.mjs +1244 -0
  126. package/src/lib/edit-transitions.mjs +120 -0
  127. package/src/lib/extensions.mjs +501 -0
  128. package/src/lib/image-assets.mjs +465 -0
  129. package/src/lib/image-derivatives.mjs +234 -0
  130. package/src/lib/image-intent-fingerprint.mjs +70 -0
  131. package/src/lib/image-intent-tags.mjs +176 -0
  132. package/src/lib/image-intent.mjs +802 -0
  133. package/src/lib/image-use-overrides.mjs +279 -0
  134. package/src/lib/inline-components.mjs +613 -0
  135. package/src/lib/lint-converted.mjs +68 -4
  136. package/src/lib/merge-json.mjs +163 -0
  137. package/src/lib/overwrite-guard.mjs +3 -3
  138. package/src/lib/presentation-context.mjs +531 -0
  139. package/src/lib/preservation-tags.mjs +157 -0
  140. package/src/lib/preservation-verdict.mjs +1342 -0
  141. package/src/lib/publish.mjs +1557 -0
  142. package/src/lib/rebase.mjs +945 -0
  143. package/src/lib/reconcile-ack.mjs +4 -6
  144. package/src/lib/reconcile.mjs +62 -24
  145. package/src/lib/record-client.mjs +175 -0
  146. package/src/lib/record-state.mjs +151 -0
  147. package/src/lib/remote.mjs +661 -0
  148. package/src/lib/revision.mjs +1210 -0
  149. package/src/lib/slide-guids.mjs +4 -6
  150. package/src/lib/slide-preservation.mjs +423 -0
  151. package/src/lib/slide-selection.mjs +9 -0
  152. package/src/lib/workspace-transaction.mjs +117 -0
  153. package/src/pptx/content-digest.mjs +334 -0
  154. package/src/pptx/deck-assemble.mjs +1278 -0
  155. package/src/pptx/fill/engine.mjs +2 -3
  156. package/src/pptx/fill/freeform.mjs +6 -3
  157. package/src/pptx/ooxml.mjs +20 -0
  158. package/src/pptx/package-evidence.mjs +383 -0
  159. package/src/pptx/tags.mjs +163 -11
  160. package/src/update-check.mjs +22 -4
  161. package/src/workspace/adapters.mjs +222 -0
  162. package/src/workspace/auth.mjs +256 -0
  163. package/src/workspace/autostart.mjs +204 -0
  164. package/src/workspace/daemon.mjs +360 -0
  165. package/src/workspace/discovery.mjs +180 -0
  166. package/src/workspace/edit.mjs +167 -0
  167. package/src/workspace/events.mjs +52 -0
  168. package/src/workspace/gate-client.mjs +290 -0
  169. package/src/workspace/handback.mjs +73 -0
  170. package/src/workspace/lifecycle.mjs +635 -0
  171. package/src/workspace/operations.mjs +200 -0
  172. package/src/workspace/pick-folder.mjs +152 -0
  173. package/src/workspace/registry.mjs +359 -0
  174. package/src/workspace/remote-links.mjs +397 -0
  175. package/src/workspace/remote-status.mjs +359 -0
  176. package/src/workspace/routes.mjs +1331 -0
  177. package/src/workspace/server.mjs +159 -0
  178. package/src/workspace/sync.mjs +1119 -0
  179. package/src/workspace/task-actions.mjs +215 -0
  180. package/src/workspace/tasks.mjs +507 -0
  181. package/src/workspace/ui/app.js +1886 -0
  182. package/src/workspace/ui/index.html +141 -0
  183. package/src/workspace/ui/plan-bridge.js +120 -0
  184. package/src/workspace/ui/sharepoint.svg +59 -0
  185. package/src/workspace/ui/styles.css +423 -0
  186. package/src/builders/visual-families.mjs +0 -46
@@ -0,0 +1,244 @@
1
+ // ECMA-376 preset geometry evaluator: the one place that knows what a
2
+ // `prstGeom` looks like and where its connection sites are.
3
+ //
4
+ // `preset-definitions.json` (generated by scripts/convert-preset-definitions.mjs
5
+ // from the POI/ECMA `presetShapeDefinitions.xml`) carries all 187 presets:
6
+ // adjust defaults, guide formulas, path commands and the `cxnLst`. This module
7
+ // binds the guides for one box and returns
8
+ //
9
+ // - `paths`: per `<a:path>` the evaluated ops (shape-local px, arc angles in
10
+ // degrees — the add-in's `PathOp` shape) and the outline sampled into a
11
+ // polyline in slide space (arcs and Béziers flattened);
12
+ // - `sites`: the connection sites in slide space, in `cxnLst` order, so a
13
+ // site's array index IS the `stCxn`/`endCxn` idx PowerPoint glues to.
14
+ //
15
+ // Canonical in the CLI; the add-in carries a byte-identical mirror
16
+ // (scripts/sync-transcription-runtime.mjs). Nothing here throws: an unknown
17
+ // preset evaluates to no paths and no sites.
18
+
19
+ import definitions from "./preset-definitions.json" with { type: "json" };
20
+
21
+ const PRESETS = definitions.presets;
22
+
23
+ /** Every preset the definitions carry — the full ECMA-376 set. */
24
+ export const PRESET_NAMES = Object.keys(PRESETS);
25
+
26
+ /** OOXML angle unit: 60000ths of a degree. */
27
+ const DEG = 60000;
28
+ const CIRCLE = 360 * DEG;
29
+
30
+ // Flattening density: an arc gets one chord per ARC_STEP of sweep (never
31
+ // fewer than 4), a Bézier BEZ_STEPS chords. Both keep the chord error well
32
+ // under 0.5 px for any box a slide can hold.
33
+ const ARC_STEP = Math.PI / 64;
34
+ const BEZ_STEPS = 24;
35
+
36
+ /** ECMA-376 §20.1.9.11 built-in guides, for a box whose top-left is the origin. */
37
+ function builtin(name, w, h) {
38
+ switch (name) {
39
+ case "w": case "r": return w;
40
+ case "h": case "b": return h;
41
+ case "l": case "t": return 0;
42
+ case "hc": return w / 2;
43
+ case "vc": return h / 2;
44
+ case "ss": return Math.min(w, h);
45
+ case "ls": return Math.max(w, h);
46
+ case "3cd4": return 270 * DEG;
47
+ case "3cd8": return 135 * DEG;
48
+ case "5cd8": return 225 * DEG;
49
+ case "7cd8": return 315 * DEG;
50
+ }
51
+ const m = /^(wd|hd|ssd|cd)(\d+)$/.exec(name);
52
+ if (!m) return undefined;
53
+ const n = Number(m[2]);
54
+ if (n === 0) return 0;
55
+ return { wd: w, hd: h, ssd: Math.min(w, h), cd: CIRCLE }[m[1]] / n;
56
+ }
57
+
58
+ const toRad = (a) => (a / DEG) * Math.PI / 180;
59
+
60
+ /**
61
+ * Guide scope for one box: adjusts first (the caller's `av` by name, else the
62
+ * definition's default), then `gdLst` in document order. An unknown name reads
63
+ * as 0 rather than throwing.
64
+ */
65
+ function bindGuides(def, w, h, av = {}) {
66
+ const values = new Map();
67
+ const value = (token) => {
68
+ if (token === undefined || token === "") return 0;
69
+ const literal = Number(token);
70
+ if (Number.isFinite(literal)) return literal;
71
+ return values.get(token) ?? builtin(token, w, h) ?? 0;
72
+ };
73
+ const formula = (fmla) => {
74
+ const [op, a, b, c] = String(fmla).trim().split(/\s+/);
75
+ const x = value(a), y = value(b), z = value(c);
76
+ switch (op) {
77
+ case "*/": return z === 0 ? 0 : (x * y) / z;
78
+ case "+-": return x + y - z;
79
+ case "+/": return z === 0 ? 0 : (x + y) / z;
80
+ case "?:": return x > 0 ? y : z;
81
+ case "val": return x;
82
+ case "abs": return Math.abs(x);
83
+ case "sqrt": return Math.sqrt(x);
84
+ case "max": return Math.max(x, y);
85
+ case "min": return Math.min(x, y);
86
+ case "pin": return Math.max(x, Math.min(y, z));
87
+ case "mod": return Math.sqrt(x * x + y * y + z * z);
88
+ case "at2": return Math.atan2(y, x) * 180 / Math.PI * DEG;
89
+ case "sin": return x * Math.sin(toRad(y));
90
+ case "cos": return x * Math.cos(toRad(y));
91
+ case "tan": return x * Math.tan(toRad(y));
92
+ // The arctan here is a plain radian angle, not an OOXML one.
93
+ case "cat2": return x * Math.cos(Math.atan2(z, y));
94
+ case "sat2": return x * Math.sin(Math.atan2(z, y));
95
+ default: return 0;
96
+ }
97
+ };
98
+ // An adjust may be passed as a number (the OOXML value) or as a fmla string.
99
+ const adjust = (v) => (typeof v === "number" ? v : formula(v));
100
+ for (const [name, fmla] of def.av ?? []) values.set(name, name in av ? adjust(av[name]) : formula(fmla));
101
+ for (const [name, v] of Object.entries(av)) if (!values.has(name)) values.set(name, adjust(v));
102
+ for (const [name, fmla] of def.gd ?? []) values.set(name, formula(fmla));
103
+ return value;
104
+ }
105
+
106
+ /**
107
+ * `arcTo` angles are VISUAL angles on the ellipse (the direction from the
108
+ * centre); the ellipse is traced by its parametric angle. This maps one onto
109
+ * the other continuously, so a sweep past ±90° keeps its sign and length
110
+ * (Apache POI's ArcToCommand does the same).
111
+ */
112
+ function parametric(theta, rx, ry) {
113
+ if (!(rx > 0 && ry > 0)) return theta;
114
+ const t = Math.atan2(rx * Math.sin(theta), ry * Math.cos(theta));
115
+ return t + 2 * Math.PI * Math.round((theta - t) / (2 * Math.PI));
116
+ }
117
+
118
+ /** Shape-local ops for one path, `PathOp`-shaped (arc angles in degrees). */
119
+ function pathOps(path, value, w, h) {
120
+ const sx = path.w ? w / path.w : 1;
121
+ const sy = path.h ? h / path.h : 1;
122
+ const pt = (x, y) => ({ x: value(x) * sx, y: value(y) * sy });
123
+ return path.ops.map(([code, ...a]) => {
124
+ switch (code) {
125
+ case "m": return { op: "moveTo", ...pt(a[0], a[1]) };
126
+ case "l": return { op: "lnTo", ...pt(a[0], a[1]) };
127
+ case "c": return { op: "cubicBezTo", c1: pt(a[0], a[1]), c2: pt(a[2], a[3]), end: pt(a[4], a[5]) };
128
+ case "q": return { op: "quadBezTo", c: pt(a[0], a[1]), end: pt(a[2], a[3]) };
129
+ case "a": return { op: "arcTo", wR: value(a[0]) * sx, hR: value(a[1]) * sy, stAng: value(a[2]) / DEG, swAng: value(a[3]) / DEG };
130
+ default: return { op: "close" };
131
+ }
132
+ });
133
+ }
134
+
135
+ /** Flatten ops into polylines (one per moveTo), shape-local. */
136
+ function flatten(ops) {
137
+ const lines = [];
138
+ let line = null;
139
+ let cur = { x: 0, y: 0 };
140
+ const push = (p) => {
141
+ if (!line) { line = { points: [{ ...cur }], closed: false }; lines.push(line); }
142
+ line.points.push(p);
143
+ cur = p;
144
+ };
145
+ for (const o of ops) {
146
+ if (o.op === "moveTo") {
147
+ cur = { x: o.x, y: o.y };
148
+ line = { points: [{ ...cur }], closed: false };
149
+ lines.push(line);
150
+ } else if (o.op === "lnTo") push({ x: o.x, y: o.y });
151
+ else if (o.op === "cubicBezTo" || o.op === "quadBezTo") {
152
+ const p0 = cur;
153
+ const cs = o.op === "cubicBezTo" ? [o.c1, o.c2] : [o.c];
154
+ for (let i = 1; i <= BEZ_STEPS; i++) {
155
+ const t = i / BEZ_STEPS, u = 1 - t;
156
+ const at = cs.length === 2
157
+ ? (k) => u * u * u * p0[k] + 3 * u * u * t * cs[0][k] + 3 * u * t * t * cs[1][k] + t * t * t * o.end[k]
158
+ : (k) => u * u * p0[k] + 2 * u * t * cs[0][k] + t * t * o.end[k];
159
+ push({ x: at("x"), y: at("y") });
160
+ }
161
+ } else if (o.op === "arcTo") {
162
+ const st = o.stAng * Math.PI / 180;
163
+ const t0 = parametric(st, o.wR, o.hR);
164
+ const t1 = parametric(st + o.swAng * Math.PI / 180, o.wR, o.hR);
165
+ const cx = cur.x - o.wR * Math.cos(t0);
166
+ const cy = cur.y - o.hR * Math.sin(t0);
167
+ const n = Math.max(4, Math.ceil(Math.abs(t1 - t0) / ARC_STEP));
168
+ for (let i = 1; i <= n; i++) {
169
+ const t = t0 + (t1 - t0) * i / n;
170
+ push({ x: cx + o.wR * Math.cos(t), y: cy + o.hR * Math.sin(t) });
171
+ }
172
+ } else if (o.op === "close" && line) {
173
+ line.closed = true;
174
+ const first = line.points[0];
175
+ cur = { ...first };
176
+ line = null;
177
+ }
178
+ }
179
+ return lines;
180
+ }
181
+
182
+ /**
183
+ * Local box point -> slide point. OOXML order: flip inside the box, rotate
184
+ * clockwise about the box centre, then offset. `rot` is in 60000ths of a degree.
185
+ */
186
+ function transformer(w, h, { x = 0, y = 0, rot = 0, flipH = false, flipV = false } = {}) {
187
+ const a = toRad(rot), cos = Math.cos(a), sin = Math.sin(a);
188
+ const cx = w / 2, cy = h / 2;
189
+ const point = (p) => {
190
+ const dx = (flipH ? w - p.x : p.x) - cx;
191
+ const dy = (flipV ? h - p.y : p.y) - cy;
192
+ return { x: x + cx + dx * cos - dy * sin, y: y + cy + dx * sin + dy * cos };
193
+ };
194
+ // A site's outward angle: a flip mirrors it, rot turns it (degrees, 0 = +x, clockwise).
195
+ const angle = (deg) => {
196
+ let d = flipH ? 180 - deg : deg;
197
+ if (flipV) d = -d;
198
+ return ((d + rot / DEG) % 360 + 360) % 360;
199
+ };
200
+ return { point, angle };
201
+ }
202
+
203
+ /**
204
+ * Evaluate a preset for a `w` x `h` box.
205
+ *
206
+ * av: adjust overrides by name — `{ adj1: 25000 }` (OOXML value) or a fmla
207
+ * string (`"val 25000"`); anything omitted keeps the definition default.
208
+ * xf: optional `{ x, y, rot, flipH, flipV }` placing the box on the slide;
209
+ * `rot` in 60000ths of a degree. Without it the box sits at the origin.
210
+ *
211
+ * Returns `{ paths: [{ ops, polylines, fill, stroke }], sites: [{ idx, x, y, angle }] }`.
212
+ * `ops` stay box-local and untransformed; `polylines` and `sites` are placed by `xf`.
213
+ * A site's `angle` is the outward direction in degrees (0 = +x, 90 = +y/down).
214
+ */
215
+ export function evalPreset(prst, w, h, av = {}, xf = {}) {
216
+ const def = Object.hasOwn(PRESETS, prst) ? PRESETS[prst] : null;
217
+ if (!def) return { paths: [], sites: [] };
218
+ const value = bindGuides(def, w, h, av ?? {});
219
+ const { point, angle } = transformer(w, h, xf ?? {});
220
+ const paths = def.paths.map((path) => {
221
+ const ops = pathOps(path, value, w, h);
222
+ const polylines = flatten(ops).map((l) => ({ points: l.points.map(point), closed: l.closed }));
223
+ return { ops, polylines, fill: path.fill !== false, stroke: path.stroke !== false };
224
+ });
225
+ const sites = (def.cxn ?? []).map(([ang, px, py], idx) => ({
226
+ idx,
227
+ ...point({ x: value(px), y: value(py) }),
228
+ angle: angle(value(ang) / DEG),
229
+ }));
230
+ return { paths, sites };
231
+ }
232
+
233
+ /** A preset's adjust handles and their defaults, `{ adj1: 50000, … }` in `av` order ({} for none or an unknown preset). */
234
+ export function presetAdjusts(prst) {
235
+ const def = Object.hasOwn(PRESETS, prst) ? PRESETS[prst] : null;
236
+ if (!def) return {};
237
+ const value = bindGuides(def, 100, 100);
238
+ return Object.fromEntries((def.av ?? []).map(([name]) => [name, value(name)]));
239
+ }
240
+
241
+ /** The connection sites alone (same placement and indices as `evalPreset`). */
242
+ export function presetSites(prst, w, h, av, xf) {
243
+ return evalPreset(prst, w, h, av, xf).sites;
244
+ }
@@ -13,8 +13,12 @@
13
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
15
15
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400..700;1,400..700&display=swap" />
16
- <link rel="stylesheet" href="./corporate-design.css" />
17
- <link rel="stylesheet" href="./layouts.css" />
16
+ <!-- The page lives in <workspace>/preview/, the deck's own media in
17
+ <workspace>/assets/. One <base> keeps every "./assets/…" a slide author
18
+ writes resolving to the deck's assets dir, off disk and over the review
19
+ server alike. -->
20
+ <base href="../" />
21
+ <style id="design-system">__DESIGN_CSS__</style>
18
22
  <style>
19
23
  /* Compatibility aliases for decks/slides authored against the older
20
24
  statworx token names. New materialized bundles expose semantic tokens
@@ -40,6 +44,7 @@
40
44
  bundle's corporate-design.css carries; repeated here so a preview
41
45
  against an older bundle lays out like the render will. */
42
46
  [data-connector] { position: absolute; width: 0; height: 0; padding: 0; margin: 0; border-color: transparent; pointer-events: none; }
47
+ [data-sm-line] { border-color: transparent !important; background: none !important; pointer-events: none; }
43
48
  [data-sm-connector-overlay] { position: absolute; left: 0; top: 0; width: 1280px; height: 720px; overflow: visible; pointer-events: none; z-index: 2147483000; }
44
49
  </style>
45
50
  <style>
@@ -197,6 +202,24 @@
197
202
  text-overflow: ellipsis;
198
203
  white-space: nowrap;
199
204
  }
205
+ /* Small screens: the rail becomes a horizontal filmstrip along the bottom
206
+ so the slide keeps the full width; fit() reads the rail's live size. */
207
+ @media (max-width: 760px) {
208
+ :root { --deck-rail-w: 0px; }
209
+ .deck { inset: 0 0 var(--deck-rail-h, 112px) 0; }
210
+ .deck__rail {
211
+ top: auto;
212
+ right: 0;
213
+ width: 100%;
214
+ height: var(--deck-rail-h, 112px);
215
+ border-right: 0;
216
+ border-top: 1px solid var(--deck-chrome-border);
217
+ }
218
+ .deck__thumbs { display: flex; gap: 10px; padding: 10px 12px; overflow-x: auto; overflow-y: hidden; }
219
+ .deck__thumb { width: 132px; flex: none; margin: 0; }
220
+ .deck__thumb-preview, .deck__thumb-frame { width: 114px; height: 64px; }
221
+ .deck__nav { bottom: calc(var(--deck-rail-h, 112px) + 10px); }
222
+ }
200
223
  .deck__error {
201
224
  padding: 32px;
202
225
  color: #ff6b6b;
@@ -484,7 +507,13 @@ __RUNTIME_APPLIER__
484
507
  var Chart = window.SlidemindChart
485
508
  ? window.SlidemindChart.createComponent(React)
486
509
  : function () { return null; };
487
- var slideScope = Object.assign({ Chart: Chart }, LAYOUTS);
510
+ // <Figure>: a drawn shape as a structural sketch, compiled to its exact
511
+ // outline by the add-in's figure runtime (the same module this CLI's
512
+ // transcriber expands sketches with), inlined with the chart scripts.
513
+ var Figure = window.SlidemindFigure
514
+ ? window.SlidemindFigure.createComponent(React)
515
+ : function () { return null; };
516
+ var slideScope = Object.assign({ Chart: Chart, Figure: Figure }, LAYOUTS);
488
517
 
489
518
  var slides = [];
490
519
  document.querySelectorAll('script[data-kind="slide"]').forEach(function (s) {
@@ -517,125 +546,20 @@ __RUNTIME_APPLIER__
517
546
 
518
547
  smInlineSvgAssets();
519
548
 
520
- // Connector overlay — the preview's stand-in for the native connectors
521
- // the render derives from every data-connector directive (the capture
522
- // engine injects an equivalent SVG before its screenshot). Straight,
523
- // elbow and arc routes, side pins, arrowheads, dash and colour follow
524
- // the same rules as the exporter's connector-derive; the exporter's
525
- // measured output at view-draft / in the .pptx stays the truth.
549
+ // Native line overlay — the preview's stand-in for the native lines the
550
+ // render exports from every data-connector / data-sm-line directive. The
551
+ // bundle's capture engine draws them with the exporter's own preset
552
+ // evaluator (drawLineDirectives), so the preview shows what PowerPoint
553
+ // draws; view-draft / the .pptx stay the truth.
526
554
  function smDrawConnectors(container) {
527
555
  if (!container) return;
528
- var old = container.querySelector(":scope > svg[data-sm-connector-overlay]");
529
- if (old) old.remove();
530
- var dirs = container.querySelectorAll("[data-connector]");
531
- if (!dirs.length) return;
532
- var derive = window.SlidemindConnectorDerive;
533
- if (!derive || typeof derive.resolveConnector !== "function") {
534
- console.warn("connector overlay skipped: the bundle's capture engine exports no connector geometry — the connected add-in predates it; view-draft / build-pptx still derive the real connectors server-side");
556
+ var d = window.SlidemindConnectorDerive;
557
+ if (!d || typeof d.drawLineDirectives !== "function") {
558
+ console.warn("line overlay skipped: the bundle's capture engine has no drawLineDirectives — the connected add-in predates native lines; view-draft / build-pptx still export them");
535
559
  return;
536
560
  }
537
- var cRect = container.getBoundingClientRect();
538
- var scale = cRect.width / 1280 || 1;
539
- var NS = "http://www.w3.org/2000/svg";
540
- var svg = document.createElementNS(NS, "svg");
541
- svg.setAttribute("data-sm-connector-overlay", "1");
542
- svg.setAttribute("viewBox", "0 0 1280 720");
543
- var defs = document.createElementNS(NS, "defs");
544
- svg.appendChild(defs);
545
- var rectOf = function (el) {
546
- var r = el.getBoundingClientRect();
547
- return { x: (r.left - cRect.left) / scale, y: (r.top - cRect.top) / scale, w: r.width / scale, h: r.height / scale };
548
- };
549
- var resolveRef = function (ref) {
550
- if (!ref) return null;
551
- try { return container.querySelector(ref.charAt(0) === "#" ? ref : "#" + ref); } catch (e) { return null; }
552
- };
553
- var sideOrNull = function (raw) {
554
- var s = (raw || "").toLowerCase();
555
- return (s === "top" || s === "bottom" || s === "left" || s === "right") ? s : null;
556
- };
557
- // Computed CSS colour ("rgb(a)(...)") → { hex, alpha } as the serialiser reports it.
558
- var colorOf = function (css) {
559
- var m = /rgba?(([^)]+))/.exec(css || "");
560
- if (!m) return { hex: "000000", alpha: 1 };
561
- var p = m[1].split(",").map(function (v) { return parseFloat(v); });
562
- var hex = p.slice(0, 3).map(function (v) {
563
- var h = Math.max(0, Math.min(255, Math.round(v))).toString(16);
564
- return h.length < 2 ? "0" + h : h;
565
- }).join("").toUpperCase();
566
- return { hex: hex, alpha: p.length > 3 && isFinite(p[3]) ? p[3] : 1 };
567
- };
568
- dirs.forEach(function (el, idx) {
569
- var fromEl = resolveRef(el.getAttribute("data-from"));
570
- var toEl = resolveRef(el.getAttribute("data-to"));
571
- var route = (el.getAttribute("data-route") || "straight").toLowerCase();
572
- var centerEl = route === "arc" ? resolveRef(el.getAttribute("data-center")) : null;
573
- var arrowRaw = (el.getAttribute("data-arrow") || "none").toLowerCase();
574
- var cs = getComputedStyle(el);
575
- var stroke = colorOf(cs.color);
576
- // The same payload the serialiser hands the exporter; resolveConnector
577
- // derives endpoints, route, bends, arc, and arrowhead size from it.
578
- var r = derive.resolveConnector({
579
- from: el.getAttribute("data-from") || "",
580
- to: el.getAttribute("data-to") || "",
581
- arrow: (arrowRaw === "start" || arrowRaw === "end" || arrowRaw === "both") ? arrowRaw : "none",
582
- route: route,
583
- fromSide: sideOrNull(el.getAttribute("data-from-side")),
584
- toSide: sideOrNull(el.getAttribute("data-to-side")),
585
- center: el.getAttribute("data-center") || null,
586
- fromRect: fromEl ? rectOf(fromEl) : null,
587
- toRect: toEl ? rectOf(toEl) : null,
588
- centerRect: centerEl ? rectOf(centerEl) : null,
589
- strokeHex: stroke.hex,
590
- strokeAlpha: stroke.alpha,
591
- strokeWidthPx: parseFloat(cs.borderTopWidth) || 0,
592
- dashed: /dashed|dotted/.test(cs.borderTopStyle || ""),
593
- missing: [],
594
- });
595
- if (!r) return;
596
- var color = "#" + r.strokeHex;
597
- var d;
598
- if (r.arc) {
599
- var large = Math.abs(r.arc.sweepDeg) > 180 ? 1 : 0;
600
- var sweep = r.arc.sweepDeg > 0 ? 1 : 0;
601
- d = "M" + r.start.x + " " + r.start.y + " A" + r.arc.r + " " + r.arc.r + " 0 " + large + " " + sweep + " " + r.end.x + " " + r.end.y;
602
- } else {
603
- d = r.points.map(function (p, i) { return (i ? "L" : "M") + p.x + " " + p.y; }).join(" ");
604
- }
605
- var path = document.createElementNS(NS, "path");
606
- path.setAttribute("d", d);
607
- path.setAttribute("fill", "none");
608
- path.setAttribute("stroke", color);
609
- path.setAttribute("stroke-opacity", String(r.strokeAlpha));
610
- path.setAttribute("stroke-width", String(r.strokeWidthPx));
611
- if (r.dashed) {
612
- var dash = Math.max(4, r.strokeWidthPx * 3);
613
- path.setAttribute("stroke-dasharray", dash + " " + dash);
614
- }
615
- if (r.arrowStart || r.arrowEnd) {
616
- // Marker sized to the head PowerPoint renders (r.arrowheadPx).
617
- var markerId = "sm-conn-arrow-" + idx;
618
- var marker = document.createElementNS(NS, "marker");
619
- marker.setAttribute("id", markerId);
620
- marker.setAttribute("viewBox", "0 0 10 10");
621
- marker.setAttribute("refX", "9");
622
- marker.setAttribute("refY", "5");
623
- marker.setAttribute("markerWidth", String(r.arrowheadPx));
624
- marker.setAttribute("markerHeight", String(r.arrowheadPx));
625
- marker.setAttribute("orient", "auto-start-reverse");
626
- marker.setAttribute("markerUnits", "userSpaceOnUse");
627
- var tri = document.createElementNS(NS, "path");
628
- tri.setAttribute("d", "M0,0 L10,5 L0,10 z");
629
- tri.setAttribute("fill", color);
630
- tri.setAttribute("fill-opacity", String(r.strokeAlpha));
631
- marker.appendChild(tri);
632
- defs.appendChild(marker);
633
- if (r.arrowStart) path.setAttribute("marker-start", "url(#" + markerId + ")");
634
- if (r.arrowEnd) path.setAttribute("marker-end", "url(#" + markerId + ")");
635
- }
636
- svg.appendChild(path);
637
- });
638
- if (svg.childNodes.length > 1) container.appendChild(svg);
561
+ var r = d.drawLineDirectives(container);
562
+ r.refused.forEach(function (x) { console.warn("line " + (x.id || "(no id)") + ": " + x.message); });
639
563
  }
640
564
  function smScheduleConnectors(container) {
641
565
  requestAnimationFrame(function () { requestAnimationFrame(function () { smDrawConnectors(container); }); });
@@ -727,6 +651,7 @@ __RUNTIME_APPLIER__
727
651
  } else if (bottom > thumbs.scrollTop + thumbs.clientHeight) {
728
652
  thumbs.scrollTop = bottom - thumbs.clientHeight + 12;
729
653
  }
654
+ if (btn.scrollIntoView) btn.scrollIntoView({ block: "nearest", inline: "nearest" });
730
655
  }
731
656
  });
732
657
  }
@@ -764,9 +689,15 @@ __RUNTIME_APPLIER__
764
689
  stage.style.transform = "none";
765
690
  return;
766
691
  }
767
- var railW = rail && getComputedStyle(rail).display !== "none" ? rail.offsetWidth : 0;
692
+ // The rail sits left (its width) or, on small screens, along the
693
+ // bottom (its height) — read whichever edge it occupies.
694
+ var railShown = rail && getComputedStyle(rail).display !== "none";
695
+ var railBottom = railShown && rail.offsetWidth >= window.innerWidth - 1;
696
+ var railW = railShown && !railBottom ? rail.offsetWidth : 0;
697
+ var railH = railBottom ? rail.offsetHeight : 0;
698
+ var chromeH = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--deck-chrome-h")) || 0;
768
699
  var sx = Math.max(1, window.innerWidth - railW) / 1280;
769
- var sy = window.innerHeight / 720;
700
+ var sy = Math.max(1, window.innerHeight - railH - (railBottom ? chromeH : 0)) / 720;
770
701
  var s = Math.min(sx, sy) * (thumbMode ? 1 : 0.95);
771
702
  stage.style.transform = "scale(" + s + ")";
772
703
  }