@bendyline/squisq 2.13.10 → 2.13.11

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.
@@ -7,6 +7,7 @@ import {
7
7
  extractPlainText,
8
8
  findBlockVideoEmbed,
9
9
  findNodesByType,
10
+ isHtmlLineBreak,
10
11
  isSvgIslandLang,
11
12
  isVideoEmbedUrl,
12
13
  matchTrailingTemplateAnnotation,
@@ -3219,6 +3220,7 @@ function renderInlineHtml(nodes) {
3219
3220
  case "inlineIcon":
3220
3221
  return `<i class="fa-${node.family} fa-${node.name}" aria-hidden="true"></i>`;
3221
3222
  case "htmlInline":
3223
+ if (isHtmlLineBreak(node.rawHtml)) return "<br>";
3222
3224
  return INLINE_MEDIA_TAG_RE.test(node.rawHtml.trim()) ? "" : escapeInlineHtml(node.rawHtml);
3223
3225
  }
3224
3226
  }).join("");
@@ -9,7 +9,7 @@ import {
9
9
  expectedSyllablesAt,
10
10
  flattenRenderableBlocks,
11
11
  wordPosAtExpectedSyllables
12
- } from "./chunk-LWNT6M4N.js";
12
+ } from "./chunk-SKGMNBQX.js";
13
13
 
14
14
  // src/narration/types.ts
15
15
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-HKHOFTGM.js";
4
4
  import {
5
5
  applySavedPresentation
6
- } from "./chunk-KRMAEYNM.js";
6
+ } from "./chunk-N7I6JP6Q.js";
7
7
  import {
8
8
  SHAPE_NAMES,
9
9
  buildChartData,
@@ -31,7 +31,7 @@ import {
31
31
  templateRegistry,
32
32
  writeCustomTemplatesToFrontmatter,
33
33
  writeCustomThemesToFrontmatter
34
- } from "./chunk-LWNT6M4N.js";
34
+ } from "./chunk-SKGMNBQX.js";
35
35
  import {
36
36
  ASCII_TREE_VOCAB,
37
37
  ASCII_VOCAB,
@@ -54,7 +54,7 @@ import {
54
54
  toCells,
55
55
  toGrid,
56
56
  traceBoxes
57
- } from "./chunk-DIY3NBIS.js";
57
+ } from "./chunk-3WUI2N6C.js";
58
58
  import {
59
59
  iconMarker
60
60
  } from "./chunk-7ZAAICW4.js";
@@ -4312,6 +4312,103 @@ function materializeFlashcards(doc, options = {}) {
4312
4312
  };
4313
4313
  }
4314
4314
 
4315
+ // src/narration/reanchor.ts
4316
+ var MIN_SHARED_WORDS = 0.9;
4317
+ var MAX_WORD_EDITS = 500;
4318
+ function words(text) {
4319
+ const found = [];
4320
+ const tokenRe = /\S+/g;
4321
+ let match;
4322
+ while ((match = tokenRe.exec(text)) !== null) {
4323
+ found.push({ text: match[0], start: match.index, end: match.index + match[0].length });
4324
+ }
4325
+ return found;
4326
+ }
4327
+ function sharedWords(a, b, maxEdits) {
4328
+ const n = a.length;
4329
+ const m = b.length;
4330
+ const max = Math.min(n + m, maxEdits);
4331
+ const mid = max + 1;
4332
+ const v = new Int32Array(2 * max + 3);
4333
+ const trace = [];
4334
+ for (let d = 0; d <= max; d++) {
4335
+ trace.push(v.slice(mid - d - 1, mid + d + 2));
4336
+ for (let k = -d; k <= d; k += 2) {
4337
+ let x = k === -d || k !== d && v[mid + k - 1] < v[mid + k + 1] ? v[mid + k + 1] : v[mid + k - 1] + 1;
4338
+ let y = x - k;
4339
+ while (x < n && y < m && a[x] === b[y]) {
4340
+ x++;
4341
+ y++;
4342
+ }
4343
+ v[mid + k] = x;
4344
+ if (x >= n && y >= m) return walkBack(trace, n, m);
4345
+ }
4346
+ }
4347
+ return null;
4348
+ }
4349
+ function walkBack(trace, n, m) {
4350
+ const pairs = [];
4351
+ let x = n;
4352
+ let y = m;
4353
+ for (let d = trace.length - 1; d >= 0; d--) {
4354
+ const row = trace[d];
4355
+ const at = (k2) => row[k2 + d + 1];
4356
+ const k = x - y;
4357
+ const previous = k === -d || k !== d && at(k - 1) < at(k + 1) ? k + 1 : k - 1;
4358
+ const previousX = at(previous);
4359
+ const previousY = previousX - previous;
4360
+ while (x > previousX && y > previousY) {
4361
+ x--;
4362
+ y--;
4363
+ pairs.push([x, y]);
4364
+ }
4365
+ x = previousX;
4366
+ y = previousY;
4367
+ }
4368
+ return pairs.reverse();
4369
+ }
4370
+ function reanchorNarrationText(recorded, current) {
4371
+ if (recorded === current) return (offset) => offset;
4372
+ const from = words(recorded);
4373
+ const to = words(current);
4374
+ if (!from.length || !to.length) return null;
4375
+ const budget = Math.min(
4376
+ MAX_WORD_EDITS,
4377
+ Math.ceil((1 - MIN_SHARED_WORDS) * (from.length + to.length))
4378
+ );
4379
+ const fromText = from.map((word) => word.text);
4380
+ const toText = to.map((word) => word.text);
4381
+ let head = 0;
4382
+ while (head < from.length && head < to.length && fromText[head] === toText[head]) head++;
4383
+ let tail = 0;
4384
+ while (tail < from.length - head && tail < to.length - head && fromText[from.length - 1 - tail] === toText[to.length - 1 - tail])
4385
+ tail++;
4386
+ const middle = sharedWords(
4387
+ fromText.slice(head, from.length - tail),
4388
+ toText.slice(head, to.length - tail),
4389
+ budget
4390
+ );
4391
+ if (!middle) return null;
4392
+ const shared = head + middle.length + tail;
4393
+ if (shared < MIN_SHARED_WORDS * from.length || shared < MIN_SHARED_WORDS * to.length) return null;
4394
+ const target = new Int32Array(from.length).fill(-1);
4395
+ for (let i = 0; i < head; i++) target[i] = i;
4396
+ for (const [i, j] of middle) target[head + i] = head + j;
4397
+ for (let i = 1; i <= tail; i++) target[from.length - i] = to.length - i;
4398
+ return (offset) => {
4399
+ let low = 0;
4400
+ let high = from.length;
4401
+ while (low < high) {
4402
+ const probe = low + high >> 1;
4403
+ if (from[probe].end <= offset) low = probe + 1;
4404
+ else high = probe;
4405
+ }
4406
+ const j = low < from.length ? target[low] : -1;
4407
+ if (j < 0) return void 0;
4408
+ return to[j].start + Math.max(0, offset - from[low].start);
4409
+ };
4410
+ }
4411
+
4315
4412
  // src/doc/applyNarrationTiming.ts
4316
4413
  var MIN_BLOCK_SIMILARITY = 0.5;
4317
4414
  var MIN_V1_SCRIPT_SIMILARITY = 0.8;
@@ -4492,12 +4589,15 @@ async function applyNarrationTiming(doc, container) {
4492
4589
  const blocks = retimeBlocks(doc.blocks, ctx);
4493
4590
  const duration = Math.max(clip.startAt + playedDuration, ctx.cursor);
4494
4591
  const captions = narrationCaptions(doc, clip, timing, timeMap, playedDuration);
4592
+ const currentText = buildNarrationScript(doc).sourceText;
4593
+ const reanchor = reanchorNarrationText(timing.sourceText, currentText);
4594
+ const mapOffset = reanchor ?? ((offset) => offset);
4495
4595
  const retimed = {
4496
4596
  ...doc,
4497
4597
  blocks,
4498
4598
  duration,
4499
4599
  presentationNarration: {
4500
- sourceText: timing.sourceText,
4600
+ sourceText: reanchor ? currentText : timing.sourceText,
4501
4601
  src: clip.src,
4502
4602
  startTime: clip.startAt,
4503
4603
  duration: playedDuration,
@@ -4508,11 +4608,13 @@ async function applyNarrationTiming(doc, container) {
4508
4608
  // A slide shown inside a trimmed span appears where playback resumes.
4509
4609
  slides: timing.slides?.flatMap((mark) => {
4510
4610
  const time = timeMap ? timeMap.snapSourceToPlayed(mark.time) : mark.time;
4511
- return time >= 0 && time <= playedDuration ? [{ charOffset: mark.charOffset, time: clip.startAt + time }] : [];
4611
+ const charOffset = mapOffset(mark.charOffset);
4612
+ return charOffset !== void 0 && time >= 0 && time <= playedDuration ? [{ charOffset, time: clip.startAt + time }] : [];
4512
4613
  }),
4513
4614
  bookmarks: timing.bookmarks.flatMap((bookmark) => {
4514
4615
  const time = timeMap ? timeMap.sourceToPlayed(bookmark.time) : bookmark.time;
4515
- return time !== null && time >= 0 && time <= playedDuration ? [{ ...bookmark, time: clip.startAt + time }] : [];
4616
+ const charOffset = mapOffset(bookmark.charOffset);
4617
+ return charOffset !== void 0 && time !== null && time >= 0 && time <= playedDuration ? [{ ...bookmark, charOffset, time: clip.startAt + time }] : [];
4516
4618
  })
4517
4619
  },
4518
4620
  ...captions ? { captions } : {},
@@ -4541,8 +4643,8 @@ function parseTimingJson(data) {
4541
4643
  }
4542
4644
  }
4543
4645
  function normalizeToWords(text) {
4544
- const words = text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2);
4545
- return new Set(words);
4646
+ const words2 = text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 2);
4647
+ return new Set(words2);
4546
4648
  }
4547
4649
  function scoreTextSimilarity(a, b) {
4548
4650
  const wordsA = normalizeToWords(a);
@@ -4,7 +4,7 @@ import {
4
4
  flattenRenderableBlocks,
5
5
  getPinnedBlockMeta,
6
6
  markdownToDoc
7
- } from "./chunk-LWNT6M4N.js";
7
+ } from "./chunk-SKGMNBQX.js";
8
8
  import {
9
9
  deriveTemplateInputs,
10
10
  extractEmbeddedVideos,
@@ -19,7 +19,7 @@ import {
19
19
  isStandaloneHtmlVideo,
20
20
  isTreeFence,
21
21
  matchNumberHighlight
22
- } from "./chunk-DIY3NBIS.js";
22
+ } from "./chunk-3WUI2N6C.js";
23
23
  import {
24
24
  extractContent,
25
25
  mapElementToBlock,
@@ -2078,7 +2078,7 @@ function compilePresentationPlan(doc, plan) {
2078
2078
  );
2079
2079
  if (narration && narration.sourceText !== checked.sourceText)
2080
2080
  throw new Error(
2081
- "The narration no longer matches the document. Generate narration again before presenting."
2081
+ "The narration no longer matches the document. Record or generate narration again before presenting."
2082
2082
  );
2083
2083
  if (narration && flattenRenderableBlocks(doc.blocks).some((block) => {
2084
2084
  const pin = getPinnedBlockMeta(block);
@@ -32,7 +32,7 @@ import {
32
32
  profileBlockContents,
33
33
  renderMarkdownBlocksHtml,
34
34
  treeToTemplateData
35
- } from "./chunk-DIY3NBIS.js";
35
+ } from "./chunk-3WUI2N6C.js";
36
36
  import {
37
37
  anchorPoint,
38
38
  base64ToUtf8,
package/dist/doc/index.js CHANGED
@@ -76,9 +76,9 @@ import {
76
76
  validateMarkdownSource,
77
77
  wordRevealDelay,
78
78
  writeDashboardLayoutsToFrontmatter
79
- } from "../chunk-WK6LEZ3U.js";
79
+ } from "../chunk-ESGKI6CD.js";
80
80
  import "../chunk-HKHOFTGM.js";
81
- import "../chunk-KRMAEYNM.js";
81
+ import "../chunk-N7I6JP6Q.js";
82
82
  import {
83
83
  BASE_INPUT_DESCRIPTORS,
84
84
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -199,7 +199,7 @@ import {
199
199
  wrapWithPersistentLayers,
200
200
  writeCustomTemplatesToFrontmatter,
201
201
  writeCustomThemesToFrontmatter
202
- } from "../chunk-LWNT6M4N.js";
202
+ } from "../chunk-SKGMNBQX.js";
203
203
  import {
204
204
  ASCII_CHAR_H,
205
205
  ASCII_CHAR_W,
@@ -254,7 +254,7 @@ import {
254
254
  treeFromMarkdownList,
255
255
  treeFromTemplateData,
256
256
  treeToTemplateData
257
- } from "../chunk-DIY3NBIS.js";
257
+ } from "../chunk-3WUI2N6C.js";
258
258
  import "../chunk-SVN7T6BK.js";
259
259
  import {
260
260
  PATH_SHAPE_KINDS,
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  traceWordPosAt,
32
32
  transcriptWordKeys,
33
33
  vadStep
34
- } from "./chunk-FXQ7C2FF.js";
34
+ } from "./chunk-65WL5ILB.js";
35
35
  import {
36
36
  fenceRendererLangs
37
37
  } from "./chunk-PGI7HSWE.js";
@@ -235,7 +235,7 @@ import {
235
235
  validateMarkdownSource,
236
236
  wordRevealDelay,
237
237
  writeDashboardLayoutsToFrontmatter
238
- } from "./chunk-WK6LEZ3U.js";
238
+ } from "./chunk-ESGKI6CD.js";
239
239
  import {
240
240
  buildNarrationTimingJson,
241
241
  parseNarrationTimingJson,
@@ -272,7 +272,7 @@ import {
272
272
  serializePresentationPlan,
273
273
  transformNarratedSegment,
274
274
  usesDynamicPresentation
275
- } from "./chunk-KRMAEYNM.js";
275
+ } from "./chunk-N7I6JP6Q.js";
276
276
  import {
277
277
  BASE_INPUT_DESCRIPTORS,
278
278
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -399,7 +399,7 @@ import {
399
399
  wrapWithPersistentLayers,
400
400
  writeCustomTemplatesToFrontmatter,
401
401
  writeCustomThemesToFrontmatter
402
- } from "./chunk-LWNT6M4N.js";
402
+ } from "./chunk-SKGMNBQX.js";
403
403
  import {
404
404
  ASCII_CHAR_H,
405
405
  ASCII_CHAR_W,
@@ -456,7 +456,7 @@ import {
456
456
  treeFromMarkdownList,
457
457
  treeFromTemplateData,
458
458
  treeToTemplateData
459
- } from "./chunk-DIY3NBIS.js";
459
+ } from "./chunk-3WUI2N6C.js";
460
460
  import {
461
461
  extractContent,
462
462
  mapElementToBlock,
@@ -31,7 +31,7 @@ import {
31
31
  traceWordPosAt,
32
32
  transcriptWordKeys,
33
33
  vadStep
34
- } from "../chunk-FXQ7C2FF.js";
34
+ } from "../chunk-65WL5ILB.js";
35
35
  import "../chunk-NEWNNLWN.js";
36
36
  import {
37
37
  buildNarrationTimingJson,
@@ -45,8 +45,8 @@ import {
45
45
  wordIndexAtChar,
46
46
  wordIndexAtTime,
47
47
  wordPosAtExpectedSyllables
48
- } from "../chunk-LWNT6M4N.js";
49
- import "../chunk-DIY3NBIS.js";
48
+ } from "../chunk-SKGMNBQX.js";
49
+ import "../chunk-3WUI2N6C.js";
50
50
  import "../chunk-FUBMT6SJ.js";
51
51
  import "../chunk-WNEH3BZP.js";
52
52
  import "../chunk-L7QZORLB.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  profileBlockContents,
3
3
  recommendTemplatesForBlock
4
- } from "../chunk-DIY3NBIS.js";
4
+ } from "../chunk-3WUI2N6C.js";
5
5
  import "../chunk-L7QZORLB.js";
6
6
  import "../chunk-L3KSFLTP.js";
7
7
  import "../chunk-P53Z4RYC.js";
@@ -29,9 +29,9 @@ import {
29
29
  serializePresentationPlan,
30
30
  transformNarratedSegment,
31
31
  usesDynamicPresentation
32
- } from "../chunk-KRMAEYNM.js";
33
- import "../chunk-LWNT6M4N.js";
34
- import "../chunk-DIY3NBIS.js";
32
+ } from "../chunk-N7I6JP6Q.js";
33
+ import "../chunk-SKGMNBQX.js";
34
+ import "../chunk-3WUI2N6C.js";
35
35
  import "../chunk-SVN7T6BK.js";
36
36
  import "../chunk-FUBMT6SJ.js";
37
37
  import "../chunk-WNEH3BZP.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.13.10",
3
+ "version": "2.13.11",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",