@bendyline/squisq 2.4.3 → 2.4.4

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.
@@ -2,7 +2,7 @@ import {
2
2
  assertMarkdownDocumentWithinLimits,
3
3
  parseMarkdown,
4
4
  toMdast
5
- } from "./chunk-7TJJA2RI.js";
5
+ } from "./chunk-YQW6AQBV.js";
6
6
  import {
7
7
  formatFrontmatterYaml,
8
8
  getChildren,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  expectedSyllablesAt,
3
3
  wordPosAtExpectedSyllables
4
- } from "./chunk-2HY2ZA7U.js";
4
+ } from "./chunk-OIIIHI3Y.js";
5
5
 
6
6
  // src/narration/types.ts
7
7
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -23,7 +23,7 @@ import {
23
23
  templateRegistry,
24
24
  writeCustomTemplatesToFrontmatter,
25
25
  writeCustomThemesToFrontmatter
26
- } from "./chunk-2HY2ZA7U.js";
26
+ } from "./chunk-OIIIHI3Y.js";
27
27
  import {
28
28
  ASCII_TREE_VOCAB,
29
29
  ASCII_VOCAB,
@@ -58,7 +58,7 @@ import {
58
58
  } from "./chunk-BAOV476U.js";
59
59
  import {
60
60
  parseMarkdown
61
- } from "./chunk-7TJJA2RI.js";
61
+ } from "./chunk-YQW6AQBV.js";
62
62
  import {
63
63
  KNOWN_BLOCK_META_KEYS,
64
64
  TEMPLATE_ALIASES,
@@ -66,7 +66,7 @@ import {
66
66
  isContainerTemplate,
67
67
  resolveTemplateName,
68
68
  serializeAnnotation
69
- } from "./chunk-D6YTLQCL.js";
69
+ } from "./chunk-O3LLD7HG.js";
70
70
  import {
71
71
  extractPlainText,
72
72
  getChildren,
@@ -56,7 +56,7 @@ import {
56
56
  sanitizeUrl,
57
57
  splitKeyValueToken,
58
58
  tokenizeAttrTokens
59
- } from "./chunk-D6YTLQCL.js";
59
+ } from "./chunk-O3LLD7HG.js";
60
60
  import {
61
61
  extractPlainText,
62
62
  readFrontmatterThemeId,
@@ -2414,7 +2414,7 @@ function renderInlineHtml(nodes) {
2414
2414
  case "mention":
2415
2415
  return escapeInlineHtml(`@${node.displayName}`);
2416
2416
  case "inlineIcon":
2417
- return escapeInlineHtml(`{[${node.token}]}`);
2417
+ return `<i class="fa-${node.family} fa-${node.name}" aria-hidden="true"></i>`;
2418
2418
  case "htmlInline":
2419
2419
  return escapeInlineHtml(node.rawHtml);
2420
2420
  }
@@ -2525,10 +2525,16 @@ function extractEmbeddedVideos(contents, limit = Infinity) {
2525
2525
  const attrs = n.attributes;
2526
2526
  const src = attrs?.src || nestedSource(n.children);
2527
2527
  if (src) {
2528
+ const startAt = attrs?.["data-squisq-video-start-at"] ? parseTimeSeconds(attrs["data-squisq-video-start-at"]) : null;
2529
+ const clipStart = attrs?.["data-squisq-video-clip-start"] ? parseTimeSeconds(attrs["data-squisq-video-clip-start"]) : null;
2530
+ const clipEnd = attrs?.["data-squisq-video-clip-end"] ? parseTimeSeconds(attrs["data-squisq-video-clip-end"]) : null;
2528
2531
  add({
2529
2532
  src,
2530
2533
  ...attrs?.poster ? { posterSrc: attrs.poster } : {},
2531
- alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || ""
2534
+ alt: attrs?.["aria-label"] || attrs?.title || attrs?.alt || "",
2535
+ ...startAt != null ? { startAt } : {},
2536
+ ...clipStart != null ? { clipStart } : {},
2537
+ ...clipEnd != null ? { clipEnd } : {}
2532
2538
  });
2533
2539
  }
2534
2540
  } else if ((n.type === "link" || n.type === "image") && typeof n.url === "string" && VIDEO_FILE_RE.test(n.url)) {
@@ -8125,7 +8131,10 @@ function collectRichMediaItems(layers, block) {
8125
8131
  src: video.src,
8126
8132
  ...video.posterSrc ? { posterSrc: video.posterSrc } : {},
8127
8133
  alt: video.alt,
8128
- aspectRatio: 16 / 9
8134
+ aspectRatio: 16 / 9,
8135
+ ...video.startAt != null ? { startAt: video.startAt } : {},
8136
+ ...video.clipStart != null ? { clipStart: video.clipStart } : {},
8137
+ ...video.clipEnd != null ? { clipEnd: video.clipEnd } : {}
8129
8138
  })
8130
8139
  ),
8131
8140
  ...sources.map((source) => ({ kind: "mermaid", source, aspectRatio: 16 / 9 })),
@@ -8181,6 +8190,7 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
8181
8190
  ];
8182
8191
  }
8183
8192
  if (item.kind === "video") {
8193
+ const clipStart = Math.max(0, item.clipStart ?? 0);
8184
8194
  return [
8185
8195
  {
8186
8196
  id: `${block.id}-embedded-video-${kindIndex}`,
@@ -8191,8 +8201,9 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
8191
8201
  ...item.posterSrc ? { posterSrc: item.posterSrc } : {},
8192
8202
  alt: item.alt || block.title || "Embedded video",
8193
8203
  fit: "contain",
8194
- clipStart: 0,
8195
- clipEnd: Math.max(0, block.duration)
8204
+ clipStart,
8205
+ clipEnd: Math.max(clipStart, item.clipEnd ?? clipStart + Math.max(0, block.duration)),
8206
+ ...item.startAt != null ? { startAt: Math.max(0, item.startAt) } : {}
8196
8207
  }
8197
8208
  }
8198
8209
  ];
@@ -8233,11 +8244,36 @@ function richMediaLayers(item, position, block, theme, kindIndex) {
8233
8244
  `${block.id}-embedded-${item.template}-${kindIndex}`
8234
8245
  );
8235
8246
  }
8247
+ function applyEmbeddedVideoTiming(layers, block) {
8248
+ const timedBySrc = new Map(
8249
+ extractEmbeddedVideos(block.contents).filter((video) => video.startAt != null || video.clipStart != null || video.clipEnd != null).map((video) => [video.src, video])
8250
+ );
8251
+ if (timedBySrc.size === 0) return layers;
8252
+ return layers.map((layer) => {
8253
+ if (layer.type !== "video") return layer;
8254
+ const video = timedBySrc.get(layer.content.src);
8255
+ if (!video) return layer;
8256
+ const clipStart = Math.max(0, video.clipStart ?? layer.content.clipStart);
8257
+ return {
8258
+ ...layer,
8259
+ content: {
8260
+ ...layer.content,
8261
+ clipStart,
8262
+ clipEnd: Math.max(
8263
+ clipStart,
8264
+ video.clipEnd ?? (video.clipStart != null ? clipStart + Math.max(0, block.duration) : layer.content.clipEnd)
8265
+ ),
8266
+ ...video.startAt != null ? { startAt: Math.max(0, video.startAt) } : {}
8267
+ }
8268
+ };
8269
+ });
8270
+ }
8236
8271
  function appendRichContentLayers(layers, block, theme, viewport) {
8237
- const items = collectRichMediaItems(layers, block);
8238
- if (items.length === 0) return layers;
8272
+ const timedLayers = applyEmbeddedVideoTiming(layers, block);
8273
+ const items = collectRichMediaItems(timedLayers, block);
8274
+ if (items.length === 0) return timedLayers;
8239
8275
  const layout = resolveSupplementalMediaLayout(
8240
- layers,
8276
+ timedLayers,
8241
8277
  block.template ? resolveTemplateName(block.template) : void 0,
8242
8278
  viewport,
8243
8279
  items.length,
@@ -6,7 +6,7 @@ import {
6
6
  quoteAttrValue,
7
7
  splitKeyValueToken,
8
8
  tokenizeAttrTokens
9
- } from "./chunk-D6YTLQCL.js";
9
+ } from "./chunk-O3LLD7HG.js";
10
10
  import {
11
11
  resolveIcon
12
12
  } from "./chunk-7N4G32LG.js";
@@ -1763,6 +1763,11 @@ interface EmbeddedVideo {
1763
1763
  src: string;
1764
1764
  posterSrc?: string;
1765
1765
  alt: string;
1766
+ /** Delay within the owning block before playback begins. */
1767
+ startAt?: number;
1768
+ /** Source-media in/out points carried by inline video data attributes. */
1769
+ clipStart?: number;
1770
+ clipEnd?: number;
1766
1771
  }
1767
1772
  /** Plain text of a block's body contents (excluding the heading). */
1768
1773
  declare function extractBodyPlainText(contents?: MarkdownBlockNode[]): string;
package/dist/doc/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  sectionExtractors,
27
27
  validateMarkdownDoc,
28
28
  validateMarkdownSource
29
- } from "../chunk-AQF5ZYDI.js";
29
+ } from "../chunk-GXUFHFYD.js";
30
30
  import {
31
31
  ASCII_CHAR_H,
32
32
  ASCII_CHAR_W,
@@ -144,7 +144,7 @@ import {
144
144
  wrapWithPersistentLayers,
145
145
  writeCustomTemplatesToFrontmatter,
146
146
  writeCustomThemesToFrontmatter
147
- } from "../chunk-2HY2ZA7U.js";
147
+ } from "../chunk-OIIIHI3Y.js";
148
148
  import {
149
149
  PATH_SHAPE_KINDS,
150
150
  anchorPoint,
@@ -200,13 +200,13 @@ import {
200
200
  isTemplateBlock,
201
201
  scaledFontSize2 as scaledFontSize
202
202
  } from "../chunk-BAOV476U.js";
203
- import "../chunk-7TJJA2RI.js";
203
+ import "../chunk-YQW6AQBV.js";
204
204
  import {
205
205
  CONTAINER_TEMPLATES,
206
206
  TABLE_FED_TEMPLATES,
207
207
  isContainerTemplate,
208
208
  resolveTemplateName
209
- } from "../chunk-D6YTLQCL.js";
209
+ } from "../chunk-O3LLD7HG.js";
210
210
  import "../chunk-7N4G32LG.js";
211
211
  import "../chunk-O7JILDEF.js";
212
212
  import "../chunk-4VOD55SX.js";
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  reanchorSession,
24
24
  traceWordPosAt,
25
25
  vadStep
26
- } from "./chunk-EMXYZLRH.js";
26
+ } from "./chunk-GGTDYLW4.js";
27
27
  import {
28
28
  DEFAULT_TRANSFORM_STYLE_ID,
29
29
  analyzeBlocks,
@@ -165,7 +165,7 @@ import {
165
165
  sectionExtractors,
166
166
  validateMarkdownDoc,
167
167
  validateMarkdownSource
168
- } from "./chunk-AQF5ZYDI.js";
168
+ } from "./chunk-GXUFHFYD.js";
169
169
  import {
170
170
  ASCII_CHAR_H,
171
171
  ASCII_CHAR_W,
@@ -291,7 +291,7 @@ import {
291
291
  wrapWithPersistentLayers,
292
292
  writeCustomTemplatesToFrontmatter,
293
293
  writeCustomThemesToFrontmatter
294
- } from "./chunk-2HY2ZA7U.js";
294
+ } from "./chunk-OIIIHI3Y.js";
295
295
  import {
296
296
  PATH_SHAPE_KINDS,
297
297
  anchorPoint,
@@ -434,7 +434,7 @@ import {
434
434
  stringifyMarkdown,
435
435
  unwrapMarkdownSource,
436
436
  wrapMarkdownSource
437
- } from "./chunk-JUAC2QWP.js";
437
+ } from "./chunk-CYRIVZTR.js";
438
438
  import {
439
439
  DEFAULT_MARKDOWN_SAFETY_LIMITS,
440
440
  MarkdownLimitError,
@@ -446,7 +446,7 @@ import {
446
446
  resolveMarkdownSafetyLimits,
447
447
  serializePandocAttributes,
448
448
  toMdast
449
- } from "./chunk-7TJJA2RI.js";
449
+ } from "./chunk-YQW6AQBV.js";
450
450
  import {
451
451
  BLOCK_META_KEY_DESCRIPTORS,
452
452
  CONTAINER_TEMPLATES,
@@ -464,7 +464,7 @@ import {
464
464
  splitKeyValueToken,
465
465
  tokenizeAttrTokens,
466
466
  unquoteAttrValue
467
- } from "./chunk-D6YTLQCL.js";
467
+ } from "./chunk-O3LLD7HG.js";
468
468
  import {
469
469
  ICONS,
470
470
  canonicalIconToken,
@@ -14,7 +14,7 @@ import {
14
14
  stringifyMarkdown,
15
15
  unwrapMarkdownSource,
16
16
  wrapMarkdownSource
17
- } from "../chunk-JUAC2QWP.js";
17
+ } from "../chunk-CYRIVZTR.js";
18
18
  import {
19
19
  DEFAULT_MARKDOWN_SAFETY_LIMITS,
20
20
  MarkdownLimitError,
@@ -26,7 +26,7 @@ import {
26
26
  resolveMarkdownSafetyLimits,
27
27
  serializePandocAttributes,
28
28
  toMdast
29
- } from "../chunk-7TJJA2RI.js";
29
+ } from "../chunk-YQW6AQBV.js";
30
30
  import {
31
31
  BLOCK_META_KEY_DESCRIPTORS,
32
32
  KNOWN_BLOCK_META_KEYS,
@@ -40,7 +40,7 @@ import {
40
40
  splitKeyValueToken,
41
41
  tokenizeAttrTokens,
42
42
  unquoteAttrValue
43
- } from "../chunk-D6YTLQCL.js";
43
+ } from "../chunk-O3LLD7HG.js";
44
44
  import "../chunk-7N4G32LG.js";
45
45
  import {
46
46
  countNodes,
@@ -23,7 +23,7 @@ import {
23
23
  reanchorSession,
24
24
  traceWordPosAt,
25
25
  vadStep
26
- } from "../chunk-EMXYZLRH.js";
26
+ } from "../chunk-GGTDYLW4.js";
27
27
  import {
28
28
  buildNarrationScript,
29
29
  buildNarrationTimingJson,
@@ -33,12 +33,12 @@ import {
33
33
  wordIndexAtChar,
34
34
  wordIndexAtTime,
35
35
  wordPosAtExpectedSyllables
36
- } from "../chunk-2HY2ZA7U.js";
36
+ } from "../chunk-OIIIHI3Y.js";
37
37
  import "../chunk-PUS54YU6.js";
38
38
  import "../chunk-CUYHFOFL.js";
39
39
  import "../chunk-SBAX4ZPO.js";
40
40
  import "../chunk-BAOV476U.js";
41
- import "../chunk-D6YTLQCL.js";
41
+ import "../chunk-O3LLD7HG.js";
42
42
  import "../chunk-O7JILDEF.js";
43
43
  import "../chunk-4VOD55SX.js";
44
44
  import "../chunk-XOFT65EX.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -70,6 +70,180 @@ function isReservedAnnotationToken(token) {
70
70
  return TEMPLATE_TOKEN_NAMES.has(resolveTemplateName(token));
71
71
  }
72
72
 
73
+ // src/markdown/annotationCoercion.ts
74
+ var KNOWN_BLOCK_META_KEYS = {
75
+ x: "number",
76
+ y: "number",
77
+ startTime: "time",
78
+ duration: "time",
79
+ connectsTo: "connectionList",
80
+ transition: "transition",
81
+ transitionDuration: "time",
82
+ transitionDirection: "transitionDirection"
83
+ };
84
+ var BLOCK_META_KEY_DESCRIPTORS = [
85
+ {
86
+ key: "transition",
87
+ description: "Block-to-block transition effect",
88
+ values: TRANSITION_TYPES
89
+ },
90
+ {
91
+ key: "transitionDuration",
92
+ description: "How long the transition lasts",
93
+ valueHint: "seconds \u2014 e.g. 0.7, 700ms"
94
+ },
95
+ {
96
+ key: "transitionDirection",
97
+ description: "Directional variant for the transition",
98
+ values: TRANSITION_DIRECTIONS
99
+ },
100
+ {
101
+ key: "startTime",
102
+ description: "Timeline start time of this block",
103
+ valueHint: "mm:ss or seconds \u2014 e.g. 01:30, 5, 1500ms"
104
+ },
105
+ {
106
+ key: "duration",
107
+ description: "How long this block lasts",
108
+ valueHint: "mm:ss or seconds \u2014 e.g. 45, 1500ms"
109
+ },
110
+ {
111
+ key: "x",
112
+ description: "Horizontal position on the diagram canvas",
113
+ valueHint: "number \u2014 e.g. 600"
114
+ },
115
+ {
116
+ key: "y",
117
+ description: "Vertical position on the diagram canvas",
118
+ valueHint: "number \u2014 e.g. 300"
119
+ },
120
+ {
121
+ key: "connectsTo",
122
+ description: "Diagram connections to other blocks",
123
+ valueHint: "comma-separated target or target:type \u2014 e.g. foo,bar:flow"
124
+ }
125
+ ];
126
+ function coerceAnnotationValues(params) {
127
+ const blockMeta = {};
128
+ const metadata = {};
129
+ const warnings = [];
130
+ let transitionType = null;
131
+ let transitionDuration;
132
+ let transitionDirection;
133
+ for (const [key, raw] of Object.entries(params)) {
134
+ const kind = KNOWN_BLOCK_META_KEYS[key];
135
+ if (!kind) {
136
+ metadata[key] = raw;
137
+ continue;
138
+ }
139
+ if (kind === "number") {
140
+ const n = parseNumber(raw);
141
+ if (n == null) {
142
+ warnings.push(`Invalid number for "${key}": ${JSON.stringify(raw)}`);
143
+ } else {
144
+ blockMeta[key] = n;
145
+ }
146
+ } else if (kind === "time") {
147
+ const s = parseTimeSeconds(raw);
148
+ if (s == null) {
149
+ warnings.push(`Invalid time for "${key}": ${JSON.stringify(raw)}`);
150
+ } else if (key === "transitionDuration") {
151
+ transitionDuration = s;
152
+ } else {
153
+ blockMeta[key] = s;
154
+ }
155
+ } else if (kind === "connectionList") {
156
+ const { list, warning } = parseConnectionList(raw);
157
+ if (warning) warnings.push(`"${key}": ${warning}`);
158
+ blockMeta.connectsTo = list;
159
+ } else if (kind === "transition") {
160
+ const transition = normalizeTransitionType(raw);
161
+ if (transition == null) {
162
+ warnings.push(`Invalid transition for "${key}": ${JSON.stringify(raw)}`);
163
+ } else {
164
+ transitionType = transition;
165
+ }
166
+ } else if (kind === "transitionDirection") {
167
+ const direction = normalizeTransitionDirection(raw);
168
+ if (direction == null) {
169
+ warnings.push(`Invalid transition direction for "${key}": ${JSON.stringify(raw)}`);
170
+ } else {
171
+ transitionDirection = direction;
172
+ }
173
+ }
174
+ }
175
+ if (transitionType) {
176
+ blockMeta.transition = {
177
+ type: transitionType,
178
+ ...transitionDuration !== void 0 ? { duration: transitionDuration } : {},
179
+ ...transitionDirection !== void 0 ? { direction: transitionDirection } : {}
180
+ };
181
+ }
182
+ return { blockMeta, metadata, warnings };
183
+ }
184
+ function parseNumber(raw) {
185
+ if (raw.trim() === "") return null;
186
+ const n = Number(raw);
187
+ return Number.isFinite(n) ? n : null;
188
+ }
189
+ var TIME_BARE_RE = /^\d+(?:\.\d+)?$/;
190
+ var TIME_MS_RE = /^(\d+(?:\.\d+)?)ms$/;
191
+ var TIME_MMSS_RE = /^(\d+):(\d{1,2})(?:\.(\d+))?$/;
192
+ function parseTimeSeconds(raw) {
193
+ const trimmed = raw.trim();
194
+ if (!trimmed) return null;
195
+ if (TIME_BARE_RE.test(trimmed)) {
196
+ const n = Number(trimmed);
197
+ return Number.isFinite(n) ? n : null;
198
+ }
199
+ const msMatch = trimmed.match(TIME_MS_RE);
200
+ if (msMatch) {
201
+ const n = Number(msMatch[1]);
202
+ return Number.isFinite(n) ? n / 1e3 : null;
203
+ }
204
+ const mmssMatch = trimmed.match(TIME_MMSS_RE);
205
+ if (mmssMatch) {
206
+ const mins = Number(mmssMatch[1]);
207
+ const secs = Number(mmssMatch[2]);
208
+ const frac = mmssMatch[3] ? Number(`0.${mmssMatch[3]}`) : 0;
209
+ if (!Number.isFinite(mins) || !Number.isFinite(secs) || !Number.isFinite(frac)) {
210
+ return null;
211
+ }
212
+ if (secs >= 60) return null;
213
+ return mins * 60 + secs + frac;
214
+ }
215
+ return null;
216
+ }
217
+ function parseConnectionList(raw) {
218
+ const trimmed = raw.trim();
219
+ if (!trimmed) return { list: [], warning: null };
220
+ const list = [];
221
+ let droppedEmpty = false;
222
+ for (const part of trimmed.split(",")) {
223
+ const entry = part.trim();
224
+ if (!entry) {
225
+ droppedEmpty = true;
226
+ continue;
227
+ }
228
+ const colonIdx = entry.indexOf(":");
229
+ if (colonIdx < 0) {
230
+ list.push({ target: entry });
231
+ } else {
232
+ const target = entry.slice(0, colonIdx).trim();
233
+ const type = entry.slice(colonIdx + 1).trim();
234
+ if (!target) {
235
+ droppedEmpty = true;
236
+ continue;
237
+ }
238
+ list.push(type ? { target, type } : { target });
239
+ }
240
+ }
241
+ return {
242
+ list,
243
+ warning: droppedEmpty ? "dropped empty connection entries" : null
244
+ };
245
+ }
246
+
73
247
  // src/markdown/sanitize.ts
74
248
  var SAFE_LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
75
249
  var SAFE_MEDIA_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "blob"]);
@@ -313,180 +487,6 @@ function stripUrlSchemeNoise(value) {
313
487
  return out;
314
488
  }
315
489
 
316
- // src/markdown/annotationCoercion.ts
317
- var KNOWN_BLOCK_META_KEYS = {
318
- x: "number",
319
- y: "number",
320
- startTime: "time",
321
- duration: "time",
322
- connectsTo: "connectionList",
323
- transition: "transition",
324
- transitionDuration: "time",
325
- transitionDirection: "transitionDirection"
326
- };
327
- var BLOCK_META_KEY_DESCRIPTORS = [
328
- {
329
- key: "transition",
330
- description: "Block-to-block transition effect",
331
- values: TRANSITION_TYPES
332
- },
333
- {
334
- key: "transitionDuration",
335
- description: "How long the transition lasts",
336
- valueHint: "seconds \u2014 e.g. 0.7, 700ms"
337
- },
338
- {
339
- key: "transitionDirection",
340
- description: "Directional variant for the transition",
341
- values: TRANSITION_DIRECTIONS
342
- },
343
- {
344
- key: "startTime",
345
- description: "Timeline start time of this block",
346
- valueHint: "mm:ss or seconds \u2014 e.g. 01:30, 5, 1500ms"
347
- },
348
- {
349
- key: "duration",
350
- description: "How long this block lasts",
351
- valueHint: "mm:ss or seconds \u2014 e.g. 45, 1500ms"
352
- },
353
- {
354
- key: "x",
355
- description: "Horizontal position on the diagram canvas",
356
- valueHint: "number \u2014 e.g. 600"
357
- },
358
- {
359
- key: "y",
360
- description: "Vertical position on the diagram canvas",
361
- valueHint: "number \u2014 e.g. 300"
362
- },
363
- {
364
- key: "connectsTo",
365
- description: "Diagram connections to other blocks",
366
- valueHint: "comma-separated target or target:type \u2014 e.g. foo,bar:flow"
367
- }
368
- ];
369
- function coerceAnnotationValues(params) {
370
- const blockMeta = {};
371
- const metadata = {};
372
- const warnings = [];
373
- let transitionType = null;
374
- let transitionDuration;
375
- let transitionDirection;
376
- for (const [key, raw] of Object.entries(params)) {
377
- const kind = KNOWN_BLOCK_META_KEYS[key];
378
- if (!kind) {
379
- metadata[key] = raw;
380
- continue;
381
- }
382
- if (kind === "number") {
383
- const n = parseNumber(raw);
384
- if (n == null) {
385
- warnings.push(`Invalid number for "${key}": ${JSON.stringify(raw)}`);
386
- } else {
387
- blockMeta[key] = n;
388
- }
389
- } else if (kind === "time") {
390
- const s = parseTimeSeconds(raw);
391
- if (s == null) {
392
- warnings.push(`Invalid time for "${key}": ${JSON.stringify(raw)}`);
393
- } else if (key === "transitionDuration") {
394
- transitionDuration = s;
395
- } else {
396
- blockMeta[key] = s;
397
- }
398
- } else if (kind === "connectionList") {
399
- const { list, warning } = parseConnectionList(raw);
400
- if (warning) warnings.push(`"${key}": ${warning}`);
401
- blockMeta.connectsTo = list;
402
- } else if (kind === "transition") {
403
- const transition = normalizeTransitionType(raw);
404
- if (transition == null) {
405
- warnings.push(`Invalid transition for "${key}": ${JSON.stringify(raw)}`);
406
- } else {
407
- transitionType = transition;
408
- }
409
- } else if (kind === "transitionDirection") {
410
- const direction = normalizeTransitionDirection(raw);
411
- if (direction == null) {
412
- warnings.push(`Invalid transition direction for "${key}": ${JSON.stringify(raw)}`);
413
- } else {
414
- transitionDirection = direction;
415
- }
416
- }
417
- }
418
- if (transitionType) {
419
- blockMeta.transition = {
420
- type: transitionType,
421
- ...transitionDuration !== void 0 ? { duration: transitionDuration } : {},
422
- ...transitionDirection !== void 0 ? { direction: transitionDirection } : {}
423
- };
424
- }
425
- return { blockMeta, metadata, warnings };
426
- }
427
- function parseNumber(raw) {
428
- if (raw.trim() === "") return null;
429
- const n = Number(raw);
430
- return Number.isFinite(n) ? n : null;
431
- }
432
- var TIME_BARE_RE = /^\d+(?:\.\d+)?$/;
433
- var TIME_MS_RE = /^(\d+(?:\.\d+)?)ms$/;
434
- var TIME_MMSS_RE = /^(\d+):(\d{1,2})(?:\.(\d+))?$/;
435
- function parseTimeSeconds(raw) {
436
- const trimmed = raw.trim();
437
- if (!trimmed) return null;
438
- if (TIME_BARE_RE.test(trimmed)) {
439
- const n = Number(trimmed);
440
- return Number.isFinite(n) ? n : null;
441
- }
442
- const msMatch = trimmed.match(TIME_MS_RE);
443
- if (msMatch) {
444
- const n = Number(msMatch[1]);
445
- return Number.isFinite(n) ? n / 1e3 : null;
446
- }
447
- const mmssMatch = trimmed.match(TIME_MMSS_RE);
448
- if (mmssMatch) {
449
- const mins = Number(mmssMatch[1]);
450
- const secs = Number(mmssMatch[2]);
451
- const frac = mmssMatch[3] ? Number(`0.${mmssMatch[3]}`) : 0;
452
- if (!Number.isFinite(mins) || !Number.isFinite(secs) || !Number.isFinite(frac)) {
453
- return null;
454
- }
455
- if (secs >= 60) return null;
456
- return mins * 60 + secs + frac;
457
- }
458
- return null;
459
- }
460
- function parseConnectionList(raw) {
461
- const trimmed = raw.trim();
462
- if (!trimmed) return { list: [], warning: null };
463
- const list = [];
464
- let droppedEmpty = false;
465
- for (const part of trimmed.split(",")) {
466
- const entry = part.trim();
467
- if (!entry) {
468
- droppedEmpty = true;
469
- continue;
470
- }
471
- const colonIdx = entry.indexOf(":");
472
- if (colonIdx < 0) {
473
- list.push({ target: entry });
474
- } else {
475
- const target = entry.slice(0, colonIdx).trim();
476
- const type = entry.slice(colonIdx + 1).trim();
477
- if (!target) {
478
- droppedEmpty = true;
479
- continue;
480
- }
481
- list.push(type ? { target, type } : { target });
482
- }
483
- }
484
- return {
485
- list,
486
- warning: droppedEmpty ? "dropped empty connection entries" : null
487
- };
488
- }
489
-
490
490
  // src/markdown/attrTokens.ts
491
491
  var DQ_RUN = `"(?:[^"\\\\]|\\\\.)*"`;
492
492
  var SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
@@ -589,13 +589,13 @@ export {
589
589
  CONTAINER_TEMPLATES,
590
590
  isContainerTemplate,
591
591
  isReservedAnnotationToken,
592
- sanitizeUrl,
593
- sanitizeHtmlNodes,
594
592
  KNOWN_BLOCK_META_KEYS,
595
593
  BLOCK_META_KEY_DESCRIPTORS,
596
594
  coerceAnnotationValues,
597
595
  parseNumber,
598
596
  parseTimeSeconds,
597
+ sanitizeUrl,
598
+ sanitizeHtmlNodes,
599
599
  matchTrailingTemplateAnnotation,
600
600
  matchTrailingPandocAttr,
601
601
  tokenizeAttrTokens,