@bendyline/squisq 2.13.8 → 2.13.9

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.
@@ -9,7 +9,7 @@ import {
9
9
  expectedSyllablesAt,
10
10
  flattenRenderableBlocks,
11
11
  wordPosAtExpectedSyllables
12
- } from "./chunk-5C6ZO5FV.js";
12
+ } from "./chunk-L3VQQ3WT.js";
13
13
 
14
14
  // src/narration/types.ts
15
15
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -32,7 +32,7 @@ import {
32
32
  profileBlockContents,
33
33
  renderMarkdownBlocksHtml,
34
34
  treeToTemplateData
35
- } from "./chunk-HGXZMB7K.js";
35
+ } from "./chunk-R2J7CQJZ.js";
36
36
  import {
37
37
  anchorPoint,
38
38
  base64ToUtf8,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-FTN6SGW5.js";
4
4
  import {
5
5
  applySavedPresentation
6
- } from "./chunk-CS3MAZZX.js";
6
+ } from "./chunk-VMKRN7TB.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-5C6ZO5FV.js";
34
+ } from "./chunk-L3VQQ3WT.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-HGXZMB7K.js";
57
+ } from "./chunk-R2J7CQJZ.js";
58
58
  import {
59
59
  iconMarker
60
60
  } from "./chunk-7ZAAICW4.js";
@@ -5282,12 +5282,13 @@ function renderAsciiDiagram(diagram2, options = {}) {
5282
5282
  const vocab = (options.style ?? diagram2.style) === "ascii" ? ASCII_VOCAB : UNICODE_VOCAB;
5283
5283
  if (diagram2.nodes.length === 0) return "";
5284
5284
  const placed = layoutNodes(diagram2.nodes, diagram2.edges);
5285
+ const routes = planRoutesWithRoom(diagram2.edges, placed);
5285
5286
  const canvas = new Canvas();
5286
5287
  const drawOrder = [...placed.values()].sort(
5287
5288
  (a, b) => a.depth - b.depth || a.rect.r0 - b.rect.r0 || a.rect.c0 - b.rect.c0
5288
5289
  );
5289
5290
  for (const p of drawOrder) drawBox(canvas, p, vocab);
5290
- drawEdges(canvas, diagram2.edges, placed, vocab);
5291
+ drawEdges(canvas, routes, placed, vocab);
5291
5292
  return canvas.emit();
5292
5293
  }
5293
5294
  function computeSideDegrees(nodes, edges) {
@@ -5527,7 +5528,40 @@ function drawBox(canvas, p, vocab) {
5527
5528
  });
5528
5529
  }
5529
5530
  }
5530
- function drawEdges(canvas, edges, placed, vocab) {
5531
+ var OPPOSITE_SIDE = {
5532
+ top: "bottom",
5533
+ bottom: "top",
5534
+ left: "right",
5535
+ right: "left"
5536
+ };
5537
+ function planRoutesWithRoom(edges, placed) {
5538
+ const maxInserts = 4 * edges.length + 8;
5539
+ for (let inserted = 0; ; inserted++) {
5540
+ const { routes, room } = planRoutes(edges, placed);
5541
+ if (!room || inserted >= maxInserts) return routes;
5542
+ insertLine(placed, room);
5543
+ }
5544
+ }
5545
+ function insertLine(placed, { axis, after }) {
5546
+ const bump = (v) => v > after ? v + 1 : v;
5547
+ for (const p of placed.values()) {
5548
+ const r = p.rect;
5549
+ p.rect = axis === "row" ? { ...r, r0: bump(r.r0), r1: bump(r.r1) } : { ...r, c0: bump(r.c0), c1: bump(r.c1) };
5550
+ }
5551
+ }
5552
+ function roomBeside(side, rect) {
5553
+ switch (side) {
5554
+ case "bottom":
5555
+ return { axis: "row", after: rect.r1 };
5556
+ case "top":
5557
+ return { axis: "row", after: rect.r0 - 1 };
5558
+ case "right":
5559
+ return { axis: "col", after: rect.c1 };
5560
+ case "left":
5561
+ return { axis: "col", after: rect.c0 - 1 };
5562
+ }
5563
+ }
5564
+ function planRoutes(edges, placed) {
5531
5565
  const sorted = [...edges].sort(
5532
5566
  (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || (a.label ?? "").localeCompare(b.label ?? "")
5533
5567
  );
@@ -5561,6 +5595,20 @@ function drawEdges(canvas, edges, placed, vocab) {
5561
5595
  });
5562
5596
  }
5563
5597
  const attachment = assignAttachments(plans, placed);
5598
+ const straight = resolveStraightRuns(plans, placed, attachment);
5599
+ const jogs = assignJogStaggers(plans, placed, attachment, straight);
5600
+ let room = jogs.room;
5601
+ plans.forEach((plan, i) => {
5602
+ if (room || routeAxis(plan) === null) return;
5603
+ const s = placed.get(plan.sourceId).rect;
5604
+ const t = placed.get(plan.targetId).rect;
5605
+ const lane = jogLane(plan, s, t);
5606
+ const needed = straight[i] ? plan.biDir ? 2 : 1 : 2;
5607
+ if (freeLines(lane) < needed) room = roomBeside(plan.exitSide, s);
5608
+ });
5609
+ return { routes: { plans, attachment, straight, staggers: jogs.staggers }, room };
5610
+ }
5611
+ function drawEdges(canvas, { plans, attachment, straight, staggers }, placed, vocab) {
5564
5612
  const sideLabelRequests = [];
5565
5613
  plans.forEach(
5566
5614
  (plan, i) => routeEdge(
@@ -5569,6 +5617,7 @@ function drawEdges(canvas, edges, placed, vocab) {
5569
5617
  i,
5570
5618
  placed,
5571
5619
  attachment,
5620
+ { straight: straight[i], stagger: staggers[i] },
5572
5621
  vocab,
5573
5622
  (candidates, label) => sideLabelRequests.push({ candidates, label })
5574
5623
  )
@@ -5645,12 +5694,14 @@ function assignAttachments(plans, placed) {
5645
5694
  for (const [sideKey, list2] of [...bySide.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
5646
5695
  const [nodeId, side] = sideKey.split("|");
5647
5696
  const node = placed.get(nodeId);
5697
+ const horizontal = side === "top" || side === "bottom";
5648
5698
  list2.sort((a, b) => {
5649
5699
  const ra = placed.get(a.otherId).rect;
5650
5700
  const rb = placed.get(b.otherId).rect;
5651
- return ra.r0 - rb.r0 || ra.c0 - rb.c0 || a.otherId.localeCompare(b.otherId);
5701
+ const along = horizontal ? ra.c0 + ra.c1 - (rb.c0 + rb.c1) : ra.r0 + ra.r1 - (rb.r0 + rb.r1);
5702
+ const across = horizontal ? ra.r0 - rb.r0 : ra.c0 - rb.c0;
5703
+ return along || across || a.otherId.localeCompare(b.otherId);
5652
5704
  });
5653
- const horizontal = side === "top" || side === "bottom";
5654
5705
  const from = horizontal ? node.rect.c0 + 1 : node.rect.r0 + 1;
5655
5706
  const to = horizontal ? node.rect.c1 - 1 : node.rect.r1 - 1;
5656
5707
  const inner = Math.max(1, to - from + 1);
@@ -5685,15 +5736,128 @@ function assignAttachments(plans, placed) {
5685
5736
  }
5686
5737
  return result;
5687
5738
  }
5688
- function routeEdge(canvas, plan, planIdx, placed, attachment, vocab, deferSideLabel) {
5739
+ function routeAxis(plan) {
5740
+ if (plan.enterSide !== OPPOSITE_SIDE[plan.exitSide]) return null;
5741
+ return plan.exitSide === "bottom" || plan.exitSide === "top" ? "vertical" : "horizontal";
5742
+ }
5743
+ function jogLane(plan, s, t) {
5744
+ const [exitLine, enterLine] = plan.exitSide === "bottom" ? [s.r1, t.r0] : plan.exitSide === "top" ? [s.r0, t.r1] : plan.exitSide === "right" ? [s.c1, t.c0] : [s.c0, t.c1];
5745
+ const dir = plan.exitSide === "bottom" || plan.exitSide === "right" ? 1 : -1;
5746
+ return {
5747
+ dir,
5748
+ mid: Math.floor((exitLine + enterLine) / 2),
5749
+ firstFree: exitLine + dir,
5750
+ lastFree: enterLine - dir
5751
+ };
5752
+ }
5753
+ function freeLines(lane) {
5754
+ return (lane.lastFree - lane.firstFree) * lane.dir + 1;
5755
+ }
5756
+ function straightRunFits(s, t, exitPos, vertical) {
5757
+ return vertical ? exitPos >= t.c0 + 1 && exitPos <= t.c1 - 1 && exitPos >= s.c0 + 1 && exitPos <= s.c1 - 1 : exitPos >= t.r0 + 1 && exitPos <= t.r1 - 1 && exitPos >= s.r0 + 1 && exitPos <= s.r1 - 1;
5758
+ }
5759
+ function resolveStraightRuns(plans, placed, attachment) {
5760
+ const rectOf = (id) => placed.get(id).rect;
5761
+ const slot = (i, end) => attachment.get(`${i}:${end}`);
5762
+ const axes = plans.map(routeAxis);
5763
+ const straight = plans.map(
5764
+ (plan, i) => axes[i] !== null && straightRunFits(
5765
+ rectOf(plan.sourceId),
5766
+ rectOf(plan.targetId),
5767
+ slot(i, "exit").pos,
5768
+ axes[i] === "vertical"
5769
+ )
5770
+ );
5771
+ const cellOn = (j, side) => {
5772
+ const plan = plans[j];
5773
+ if (axes[j] === null) return null;
5774
+ if (`${plan.sourceId}|${plan.exitSide}` === side) return slot(j, "exit").pos;
5775
+ if (`${plan.targetId}|${plan.enterSide}` !== side) return null;
5776
+ const t = rectOf(plan.targetId);
5777
+ const lane = jogLane(plan, rectOf(plan.sourceId), t);
5778
+ if (!straight[j] && freeLines(lane) >= 2) return slot(j, "enter").pos;
5779
+ return axes[j] === "vertical" ? clamp2(slot(j, "exit").pos, t.c0 + 1, t.c1 - 1) : clamp2(slot(j, "exit").pos, t.r0 + 1, t.r1 - 1);
5780
+ };
5781
+ const clashes = (side, cell, self) => plans.some((_, j) => {
5782
+ const other = j === self ? null : cellOn(j, side);
5783
+ return other !== null && Math.abs(other - cell) <= 1;
5784
+ });
5785
+ for (let changed = true; changed; ) {
5786
+ changed = false;
5787
+ plans.forEach((plan, i) => {
5788
+ if (!straight[i]) return;
5789
+ const targetSide = `${plan.targetId}|${plan.enterSide}`;
5790
+ if (!clashes(targetSide, slot(i, "exit").pos, i)) return;
5791
+ const s = rectOf(plan.sourceId);
5792
+ const t = rectOf(plan.targetId);
5793
+ const port = slot(i, "enter").pos;
5794
+ const realign = straightRunFits(s, t, port, axes[i] === "vertical") && !clashes(`${plan.sourceId}|${plan.exitSide}`, port, i) && !clashes(targetSide, port, i);
5795
+ if (realign) slot(i, "exit").pos = port;
5796
+ else straight[i] = false;
5797
+ changed = true;
5798
+ });
5799
+ }
5800
+ return straight;
5801
+ }
5802
+ function assignJogStaggers(plans, placed, attachment, straight) {
5803
+ const staircases = /* @__PURE__ */ new Map();
5804
+ const staggers = plans.map((plan, i) => {
5805
+ const exit = attachment.get(`${i}:exit`);
5806
+ const enter = attachment.get(`${i}:enter`);
5807
+ const basis = enter.count > exit.count ? enter : exit;
5808
+ const s = placed.get(plan.sourceId).rect;
5809
+ const t = placed.get(plan.targetId).rect;
5810
+ const lane = jogLane(plan, s, t);
5811
+ if (routeAxis(plan) !== null && !straight[i] && freeLines(lane) >= 2) {
5812
+ const fanIn = basis === enter;
5813
+ const heading = Math.sign(enter.pos - exit.pos);
5814
+ const key = fanIn ? `${plan.targetId}|${plan.enterSide}|${heading}` : `${plan.sourceId}|${plan.exitSide}|${heading}`;
5815
+ const jog = {
5816
+ planIdx: i,
5817
+ outerness: fanIn ? -enter.pos * heading : exit.pos * heading,
5818
+ toward: fanIn ? lane.dir : lane.dir === 1 ? -1 : 1,
5819
+ lane,
5820
+ fanIn,
5821
+ room: fanIn ? roomBeside(plan.enterSide, t) : roomBeside(plan.exitSide, s)
5822
+ };
5823
+ staircases.set(key, [...staircases.get(key) ?? [], jog]);
5824
+ }
5825
+ return basis.index - Math.floor((basis.count - 1) / 2);
5826
+ });
5827
+ let room = null;
5828
+ for (const jogs of staircases.values()) {
5829
+ if (jogs.length < 2) continue;
5830
+ jogs.sort((a, b) => b.outerness - a.outerness || a.planIdx - b.planIdx);
5831
+ const half = Math.floor((jogs.length - 1) / 2);
5832
+ const lines = jogs.map((jog, rank) => jog.lane.mid + jog.toward * (half - rank));
5833
+ const shift = jogs[0].fanIn ? staircaseShift(jogs, lines, { spareArrowLine: true, spareExitLine: true }) : staircaseShift(jogs, lines, { spareArrowLine: true }) ?? staircaseShift(jogs, lines, {});
5834
+ if (shift === null) {
5835
+ room ?? (room = jogs[0].room);
5836
+ continue;
5837
+ }
5838
+ jogs.forEach((jog, k) => {
5839
+ staggers[jog.planIdx] = lines[k] + shift - jog.lane.mid;
5840
+ });
5841
+ }
5842
+ return { staggers, room };
5843
+ }
5844
+ function staircaseShift(jogs, lines, spare) {
5845
+ let lo = -Infinity;
5846
+ let hi = Infinity;
5847
+ jogs.forEach(({ lane }, k) => {
5848
+ const first = spare.spareExitLine ? lane.firstFree + lane.dir : lane.firstFree;
5849
+ const last = spare.spareArrowLine ? lane.lastFree - lane.dir : lane.lastFree;
5850
+ lo = Math.max(lo, Math.min(first, last) - lines[k]);
5851
+ hi = Math.min(hi, Math.max(first, last) - lines[k]);
5852
+ if ((last - first) * lane.dir < 0) hi = -Infinity;
5853
+ });
5854
+ return lo <= hi ? clamp2(0, lo, hi) : null;
5855
+ }
5856
+ function routeEdge(canvas, plan, planIdx, placed, attachment, shape, vocab, deferSideLabel) {
5689
5857
  const s = placed.get(plan.sourceId).rect;
5690
5858
  const t = placed.get(plan.targetId).rect;
5691
- const exitSlot = attachment.get(`${planIdx}:exit`);
5692
- const enterSlot = attachment.get(`${planIdx}:enter`);
5693
- const exitPos = exitSlot.pos;
5694
- const enterPos = enterSlot.pos;
5695
- const basis = enterSlot.count > exitSlot.count ? enterSlot : exitSlot;
5696
- const stagger = basis.index - Math.floor((basis.count - 1) / 2);
5859
+ const exitPos = attachment.get(`${planIdx}:exit`).pos;
5860
+ const enterPos = attachment.get(`${planIdx}:enter`).pos;
5697
5861
  const junction = (r, c, side) => {
5698
5862
  const ch = side === "top" ? vocab.stemUp : side === "bottom" ? vocab.stemDown : side === "left" ? vocab.stemLeft : vocab.stemRight;
5699
5863
  canvas.set(r, c, ch, "junction");
@@ -5705,7 +5869,8 @@ function routeEdge(canvas, plan, planIdx, placed, attachment, vocab, deferSideLa
5705
5869
  label: normalizeEdgeLabel(plan.edge.label),
5706
5870
  vocab,
5707
5871
  junction,
5708
- stagger,
5872
+ straight: shape.straight,
5873
+ stagger: shape.stagger,
5709
5874
  deferSideLabel
5710
5875
  };
5711
5876
  if (plan.exitSide === "bottom" && plan.enterSide === "top") {
@@ -5743,8 +5908,7 @@ function drawVertical(args, s, t, exitCol, enterCol, dir) {
5743
5908
  const hi = Math.max(rA, rB);
5744
5909
  for (let r = lo; r <= hi; r++) canvas.drawEdgeCell(r, col, vocab.v, "line-v", vocab);
5745
5910
  };
5746
- const straightOk = exitCol >= t.c0 + 1 && exitCol <= t.c1 - 1 && exitCol >= s.c0 + 1 && exitCol <= s.c1 - 1;
5747
- if (straightOk || gap < 2) {
5911
+ if (args.straight || gap < 2) {
5748
5912
  const sCol = clamp2(exitCol, s.c0 + 1, s.c1 - 1);
5749
5913
  const tCol = clamp2(exitCol, t.c0 + 1, t.c1 - 1);
5750
5914
  junction(exitRow, sCol, dir === 1 ? "bottom" : "top");
@@ -5877,8 +6041,7 @@ function drawHorizontal(args, s, t, exitRow, enterRow, dir) {
5877
6041
  canvas.drawEdgeCell(row, lo + i, ch, ch === vocab.h ? "line-h" : "label", vocab);
5878
6042
  });
5879
6043
  };
5880
- const straightOk = exitRow >= t.r0 + 1 && exitRow <= t.r1 - 1 && exitRow >= s.r0 + 1 && exitRow <= s.r1 - 1;
5881
- if (straightOk || gap < 2) {
6044
+ if (args.straight || gap < 2) {
5882
6045
  const sRow = clamp2(exitRow, s.r0 + 1, s.r1 - 1);
5883
6046
  const tRow = clamp2(exitRow, t.r0 + 1, t.r1 - 1);
5884
6047
  junction(sRow, exitCol, dir === 1 ? "right" : "left");
@@ -880,6 +880,7 @@ function findRailLabels(grid, boxes) {
880
880
  return out;
881
881
  }
882
882
  function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
883
+ var _a;
883
884
  const H = grid.length;
884
885
  const W = H > 0 ? grid[0].length : 0;
885
886
  const cells = /* @__PURE__ */ new Map();
@@ -950,7 +951,7 @@ function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
950
951
  const isCrossCell = (idx) => CROSS_CHARS.has(grid[Math.floor(idx / W)][idx % W]);
951
952
  const stateKey = (idx, channel) => idx * 2 + channel;
952
953
  const visited = /* @__PURE__ */ new Set();
953
- const components = [];
954
+ const found = [];
954
955
  const seeds = [];
955
956
  for (const idx of cells.keys()) {
956
957
  seeds.push([idx, 0]);
@@ -959,11 +960,13 @@ function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
959
960
  for (const [seedIdx, seedChannel] of seeds) {
960
961
  if (visited.has(stateKey(seedIdx, seedChannel))) continue;
961
962
  const componentCells = /* @__PURE__ */ new Set();
963
+ const states = [];
962
964
  const queue = [[seedIdx, seedChannel]];
963
965
  visited.add(stateKey(seedIdx, seedChannel));
964
966
  while (queue.length > 0) {
965
967
  const [idx, channel] = queue.pop();
966
968
  componentCells.add(idx);
969
+ states.push(stateKey(idx, channel));
967
970
  const r = Math.floor(idx / W);
968
971
  const c = idx % W;
969
972
  const neighbors = isCrossCell(idx) && channel === 1 ? [
@@ -990,15 +993,19 @@ function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
990
993
  }
991
994
  }
992
995
  }
993
- components.push([...componentCells].sort((a, b) => a - b));
994
- }
995
- components.sort((a, b) => a[0] - b[0]);
996
- const edges = [];
997
- const seen = /* @__PURE__ */ new Set();
998
- let mergedParallel = false;
999
- const looseComponents = /* @__PURE__ */ new Set();
1000
- const sideLabelCells = /* @__PURE__ */ new Set();
1001
- for (const component of components) {
996
+ found.push({ cells: [...componentCells].sort((a, b) => a - b), states });
997
+ }
998
+ found.sort((a, b) => a.cells[0] - b.cells[0]);
999
+ const componentOfState = /* @__PURE__ */ new Map();
1000
+ found.forEach(({ states }, i) => states.forEach((key) => componentOfState.set(key, i)));
1001
+ const nearestOwner = (r, c) => {
1002
+ const owners = perimeterOwners.get(r * W + c) ?? [];
1003
+ if (owners.length === 0) return null;
1004
+ return [...owners].sort(
1005
+ (a, b) => boxes[a].r0 - boxes[b].r0 || boxes[a].c0 - boxes[b].c0 || a - b
1006
+ )[0];
1007
+ };
1008
+ const attachmentsOf = (component) => {
1002
1009
  const inComponent = new Set(component);
1003
1010
  const endpoints = [];
1004
1011
  for (const idx of component) {
@@ -1034,13 +1041,6 @@ function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
1034
1041
  attachmentsByBox.set(boxIdx, { boxIdx, isArrowEnd: info.arrow !== null });
1035
1042
  }
1036
1043
  }
1037
- const nearestOwner = (r, c) => {
1038
- const owners = perimeterOwners.get(r * W + c) ?? [];
1039
- if (owners.length === 0) return null;
1040
- return [...owners].sort(
1041
- (a, b) => boxes[a].r0 - boxes[b].r0 || boxes[a].c0 - boxes[b].c0 || a - b
1042
- )[0];
1043
- };
1044
1044
  for (const idx of component) {
1045
1045
  const r = Math.floor(idx / W);
1046
1046
  const c = idx % W;
@@ -1089,6 +1089,53 @@ function extractEdges(grid, mask, perimeterOwners, boxes, warnings) {
1089
1089
  }
1090
1090
  }
1091
1091
  }
1092
+ return { byBox: attachmentsByBox, dangling };
1093
+ };
1094
+ const components = found.map(({ cells: componentCells }) => componentCells);
1095
+ const infos = components.map(attachmentsOf);
1096
+ const mergedInto = components.map((_, i) => i);
1097
+ for (const idx of cells.keys()) {
1098
+ if (!isCrossCell(idx)) continue;
1099
+ const across = componentOfState.get(stateKey(idx, 0));
1100
+ const along = componentOfState.get(stateKey(idx, 1));
1101
+ if (across === void 0 || along === void 0 || across === along) continue;
1102
+ const r = Math.floor(idx / W);
1103
+ const c = idx % W;
1104
+ for (const [trunk, bar, vertical] of [
1105
+ [along, across, true],
1106
+ [across, along, false]
1107
+ ]) {
1108
+ if (mergedInto[trunk] !== trunk || mergedInto[bar] !== bar) continue;
1109
+ const trunkEnds = [...infos[trunk].byBox.values()];
1110
+ const barEnds = [...infos[bar].byBox.values()];
1111
+ const source = trunkEnds.find((a) => !a.isArrowEnd);
1112
+ const target = trunkEnds.find((a) => a.isArrowEnd);
1113
+ if (trunkEnds.length !== 2 || !source || !target || barEnds.length === 0) continue;
1114
+ const sideOf = (boxIdx) => {
1115
+ const b = boxes[boxIdx];
1116
+ const [lo, hi, at] = vertical ? [b.r0, b.r1, r] : [b.c0, b.c1, c];
1117
+ return hi < at ? -1 : lo > at ? 1 : 0;
1118
+ };
1119
+ const sourceSide = sideOf(source.boxIdx);
1120
+ if (sourceSide === 0 || sideOf(target.boxIdx) !== -sourceSide) continue;
1121
+ const fanOut = barEnds.every((a) => a.isArrowEnd && sideOf(a.boxIdx) === -sourceSide);
1122
+ const fanIn = barEnds.every((a) => !a.isArrowEnd && sideOf(a.boxIdx) === sourceSide);
1123
+ if (!fanOut && !fanIn) continue;
1124
+ for (const a of barEnds) infos[trunk].byBox.set(a.boxIdx, a);
1125
+ (_a = infos[trunk]).dangling || (_a.dangling = infos[bar].dangling);
1126
+ components[trunk] = [...components[trunk], ...components[bar]].sort((x, y) => x - y);
1127
+ mergedInto[bar] = trunk;
1128
+ break;
1129
+ }
1130
+ }
1131
+ const edges = [];
1132
+ const seen = /* @__PURE__ */ new Set();
1133
+ let mergedParallel = false;
1134
+ const looseComponents = /* @__PURE__ */ new Set();
1135
+ const sideLabelCells = /* @__PURE__ */ new Set();
1136
+ for (const [componentIdx, component] of components.entries()) {
1137
+ if (mergedInto[componentIdx] !== componentIdx) continue;
1138
+ const { byBox: attachmentsByBox, dangling } = infos[componentIdx];
1092
1139
  const attachments = [...attachmentsByBox.values()].sort(
1093
1140
  (a, b) => boxes[a.boxIdx].r0 - boxes[b.boxIdx].r0 || boxes[a.boxIdx].c0 - boxes[b.boxIdx].c0
1094
1141
  );
@@ -4,7 +4,7 @@ import {
4
4
  flattenRenderableBlocks,
5
5
  getPinnedBlockMeta,
6
6
  markdownToDoc
7
- } from "./chunk-5C6ZO5FV.js";
7
+ } from "./chunk-L3VQQ3WT.js";
8
8
  import {
9
9
  deriveTemplateInputs,
10
10
  extractEmbeddedVideos,
@@ -18,7 +18,7 @@ import {
18
18
  isPictureParagraph,
19
19
  isStandaloneHtmlVideo,
20
20
  isTreeFence
21
- } from "./chunk-HGXZMB7K.js";
21
+ } from "./chunk-R2J7CQJZ.js";
22
22
  import {
23
23
  extractContent,
24
24
  mapElementToBlock,
package/dist/doc/index.js CHANGED
@@ -75,9 +75,9 @@ import {
75
75
  validateMarkdownSource,
76
76
  wordRevealDelay,
77
77
  writeDashboardLayoutsToFrontmatter
78
- } from "../chunk-PJHMZ26J.js";
78
+ } from "../chunk-QVNMPBS3.js";
79
79
  import "../chunk-FTN6SGW5.js";
80
- import "../chunk-CS3MAZZX.js";
80
+ import "../chunk-VMKRN7TB.js";
81
81
  import {
82
82
  BASE_INPUT_DESCRIPTORS,
83
83
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -198,7 +198,7 @@ import {
198
198
  wrapWithPersistentLayers,
199
199
  writeCustomTemplatesToFrontmatter,
200
200
  writeCustomThemesToFrontmatter
201
- } from "../chunk-5C6ZO5FV.js";
201
+ } from "../chunk-L3VQQ3WT.js";
202
202
  import {
203
203
  ASCII_CHAR_H,
204
204
  ASCII_CHAR_W,
@@ -253,7 +253,7 @@ import {
253
253
  treeFromMarkdownList,
254
254
  treeFromTemplateData,
255
255
  treeToTemplateData
256
- } from "../chunk-HGXZMB7K.js";
256
+ } from "../chunk-R2J7CQJZ.js";
257
257
  import "../chunk-SVN7T6BK.js";
258
258
  import {
259
259
  PATH_SHAPE_KINDS,
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  traceWordPosAt,
30
30
  transcriptWordKeys,
31
31
  vadStep
32
- } from "./chunk-NROIOJKY.js";
32
+ } from "./chunk-3BIKSQTM.js";
33
33
  import {
34
34
  fenceRendererLangs
35
35
  } from "./chunk-PGI7HSWE.js";
@@ -232,7 +232,7 @@ import {
232
232
  validateMarkdownSource,
233
233
  wordRevealDelay,
234
234
  writeDashboardLayoutsToFrontmatter
235
- } from "./chunk-PJHMZ26J.js";
235
+ } from "./chunk-QVNMPBS3.js";
236
236
  import {
237
237
  buildNarrationTimingJson,
238
238
  parseNarrationTimingJson,
@@ -265,7 +265,7 @@ import {
265
265
  serializePresentationPlan,
266
266
  transformNarratedSegment,
267
267
  usesDynamicPresentation
268
- } from "./chunk-CS3MAZZX.js";
268
+ } from "./chunk-VMKRN7TB.js";
269
269
  import {
270
270
  BASE_INPUT_DESCRIPTORS,
271
271
  BLOCK_MEDIA_LAYOUT_POLICIES,
@@ -392,7 +392,7 @@ import {
392
392
  wrapWithPersistentLayers,
393
393
  writeCustomTemplatesToFrontmatter,
394
394
  writeCustomThemesToFrontmatter
395
- } from "./chunk-5C6ZO5FV.js";
395
+ } from "./chunk-L3VQQ3WT.js";
396
396
  import {
397
397
  ASCII_CHAR_H,
398
398
  ASCII_CHAR_W,
@@ -449,7 +449,7 @@ import {
449
449
  treeFromMarkdownList,
450
450
  treeFromTemplateData,
451
451
  treeToTemplateData
452
- } from "./chunk-HGXZMB7K.js";
452
+ } from "./chunk-R2J7CQJZ.js";
453
453
  import {
454
454
  extractContent,
455
455
  mapElementToBlock,
@@ -29,7 +29,7 @@ import {
29
29
  traceWordPosAt,
30
30
  transcriptWordKeys,
31
31
  vadStep
32
- } from "../chunk-NROIOJKY.js";
32
+ } from "../chunk-3BIKSQTM.js";
33
33
  import "../chunk-NEWNNLWN.js";
34
34
  import {
35
35
  buildNarrationTimingJson,
@@ -43,8 +43,8 @@ import {
43
43
  wordIndexAtChar,
44
44
  wordIndexAtTime,
45
45
  wordPosAtExpectedSyllables
46
- } from "../chunk-5C6ZO5FV.js";
47
- import "../chunk-HGXZMB7K.js";
46
+ } from "../chunk-L3VQQ3WT.js";
47
+ import "../chunk-R2J7CQJZ.js";
48
48
  import "../chunk-FUBMT6SJ.js";
49
49
  import "../chunk-WNEH3BZP.js";
50
50
  import "../chunk-L7QZORLB.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  profileBlockContents,
3
3
  recommendTemplatesForBlock
4
- } from "../chunk-HGXZMB7K.js";
4
+ } from "../chunk-R2J7CQJZ.js";
5
5
  import "../chunk-L7QZORLB.js";
6
6
  import "../chunk-L3KSFLTP.js";
7
7
  import "../chunk-P53Z4RYC.js";
@@ -25,9 +25,9 @@ import {
25
25
  serializePresentationPlan,
26
26
  transformNarratedSegment,
27
27
  usesDynamicPresentation
28
- } from "../chunk-CS3MAZZX.js";
29
- import "../chunk-5C6ZO5FV.js";
30
- import "../chunk-HGXZMB7K.js";
28
+ } from "../chunk-VMKRN7TB.js";
29
+ import "../chunk-L3VQQ3WT.js";
30
+ import "../chunk-R2J7CQJZ.js";
31
31
  import "../chunk-SVN7T6BK.js";
32
32
  import "../chunk-FUBMT6SJ.js";
33
33
  import "../chunk-WNEH3BZP.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq",
3
- "version": "2.13.8",
3
+ "version": "2.13.9",
4
4
  "description": "Headless utilities for doc/block rendering, spatial math, Markdown, and storage",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",