@diagrammo/dgmo 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/advanced.cjs CHANGED
@@ -105,7 +105,7 @@ function vennAliasKeywordRemovedMessage(args) {
105
105
  return `Venn 'alias' keyword was removed. Use 'as' instead \u2014 '${args.name} as ${args.alias}'.`;
106
106
  }
107
107
  function pipeOperatorRemovedMessage() {
108
- return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'`;
108
+ return `'|' is no longer metadata syntax \u2014 write metadata inline as 'key: value' (e.g. "Name color: red"), or remove the '|'.`;
109
109
  }
110
110
  function ganttBarePercentRemovedMessage(percent) {
111
111
  return `Bare-percent progress shorthand '| ${percent}' was removed. Use 'progress: ${percent.replace(/%$/, "")}' (integer 0\u2013100, no '%').`;
@@ -13658,7 +13658,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13658
13658
  solid: parsed.solidFill === true
13659
13659
  }),
13660
13660
  borderColor: catColor,
13661
- borderWidth: CHART_BORDER_WIDTH
13661
+ borderWidth: CHART_BORDER_WIDTH,
13662
+ opacity: SCATTER_FILL_OPACITY
13662
13663
  },
13663
13664
  label: labelConfig,
13664
13665
  emphasis: emphasisConfig,
@@ -13685,6 +13686,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13685
13686
  {
13686
13687
  type: "scatter",
13687
13688
  data,
13689
+ itemStyle: { opacity: SCATTER_FILL_OPACITY },
13688
13690
  label: labelConfig,
13689
13691
  emphasis: emphasisConfig,
13690
13692
  blur: blurConfig
@@ -14990,7 +14992,7 @@ async function renderExtendedChartForExport(content, theme, palette) {
14990
14992
  chart.dispose();
14991
14993
  }
14992
14994
  }
14993
- var echarts, import_charts, import_components, import_renderers, EMPHASIS_SELF, EMPHASIS_SERIES, BLUR_DIM, EMPHASIS_LINE, CHART_BASE, CHART_BORDER_WIDTH, VALID_EXTENDED_TYPES, KNOWN_EXTENDED_OPTIONS, EXTENDED_CHART_DOORS, FIT_PADDING, OFFSCREEN, fitStackedLabelToSegment, ECHART_EXPORT_WIDTH, ECHART_EXPORT_HEIGHT, STANDARD_CHART_TYPES;
14995
+ var echarts, import_charts, import_components, import_renderers, EMPHASIS_SELF, EMPHASIS_SERIES, BLUR_DIM, EMPHASIS_LINE, CHART_BASE, CHART_BORDER_WIDTH, SCATTER_FILL_OPACITY, VALID_EXTENDED_TYPES, KNOWN_EXTENDED_OPTIONS, EXTENDED_CHART_DOORS, FIT_PADDING, OFFSCREEN, fitStackedLabelToSegment, ECHART_EXPORT_WIDTH, ECHART_EXPORT_HEIGHT, STANDARD_CHART_TYPES;
14994
14996
  var init_echarts = __esm({
14995
14997
  "src/echarts.ts"() {
14996
14998
  "use strict";
@@ -15048,6 +15050,7 @@ var init_echarts = __esm({
15048
15050
  tooltip: { show: false }
15049
15051
  };
15050
15052
  CHART_BORDER_WIDTH = 2;
15053
+ SCATTER_FILL_OPACITY = 0.65;
15051
15054
  VALID_EXTENDED_TYPES = /* @__PURE__ */ new Set([
15052
15055
  "sankey",
15053
15056
  "chord",
@@ -15323,8 +15326,61 @@ function parseEventLine(content, palette) {
15323
15326
  EVENT_LINE_DIAGNOSTIC_CODES.NO_EVENTS
15324
15327
  );
15325
15328
  }
15329
+ if (options.scale) {
15330
+ validateEraDateOrder(result.events, result.eras, pushWarning);
15331
+ }
15326
15332
  return result;
15327
15333
  }
15334
+ function validateEraDateOrder(events, eras, pushWarning) {
15335
+ if (eras.length < 2) return;
15336
+ const order = new Map(eras.map((e, i) => [e.name, i]));
15337
+ const dated = events.filter(
15338
+ (e) => e.dateValue !== null && e.era !== null && order.has(e.era)
15339
+ );
15340
+ if (dated.length === 0) return;
15341
+ const perEra = /* @__PURE__ */ new Map();
15342
+ for (const ev of dated) {
15343
+ const idx = order.get(ev.era);
15344
+ const cur = perEra.get(idx);
15345
+ if (!cur) {
15346
+ perEra.set(idx, { min: ev, max: ev });
15347
+ } else {
15348
+ if (ev.dateValue < cur.min.dateValue) cur.min = ev;
15349
+ if (ev.dateValue > cur.max.dateValue) cur.max = ev;
15350
+ }
15351
+ }
15352
+ const n = eras.length;
15353
+ const prefixMax = new Array(n).fill(null);
15354
+ const suffixMin = new Array(n).fill(null);
15355
+ for (let k = 1; k < n; k++) {
15356
+ const prev = perEra.get(k - 1)?.max ?? null;
15357
+ const carried = prefixMax[k - 1] ?? null;
15358
+ prefixMax[k] = prev && (!carried || prev.dateValue > carried.dateValue) ? prev : carried;
15359
+ }
15360
+ for (let k = n - 2; k >= 0; k--) {
15361
+ const next = perEra.get(k + 1)?.min ?? null;
15362
+ const carried = suffixMin[k + 1] ?? null;
15363
+ suffixMin[k] = next && (!carried || next.dateValue < carried.dateValue) ? next : carried;
15364
+ }
15365
+ for (const ev of dated) {
15366
+ const k = order.get(ev.era);
15367
+ const ahead = suffixMin[k];
15368
+ const behind = prefixMax[k];
15369
+ if (ahead && ev.dateValue > ahead.dateValue) {
15370
+ pushWarning(
15371
+ ev.lineNumber,
15372
+ `"${ev.label}" (${ev.date}) is in era "${ev.era}" but dated after era "${ahead.era}" begins (${ahead.date}). event-line eras run left-to-right by date \u2014 fix the date or move it to the right era, or their brackets will overlap.`,
15373
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15374
+ );
15375
+ } else if (behind && ev.dateValue < behind.dateValue) {
15376
+ pushWarning(
15377
+ ev.lineNumber,
15378
+ `"${ev.label}" (${ev.date}) is in era "${ev.era}" but dated before era "${behind.era}" ends (${behind.date}). event-line eras run left-to-right by date \u2014 fix the date or move it to the right era, or their brackets will overlap.`,
15379
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15380
+ );
15381
+ }
15382
+ }
15383
+ }
15328
15384
  function parseEventHeader(trimmed, lineNumber, era, aliasMap, diagnostics, pushWarning) {
15329
15385
  let date = null;
15330
15386
  let dateValue = null;
@@ -15391,7 +15447,8 @@ var init_parser13 = __esm({
15391
15447
  EVENT_LINE_DIAGNOSTIC_CODES = {
15392
15448
  NO_EVENTS: "E_EVENT_LINE_NO_EVENTS",
15393
15449
  BAD_DATE: "E_EVENT_LINE_BAD_DATE",
15394
- UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED"
15450
+ UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED",
15451
+ ERA_DATE_ORDER: "E_EVENT_LINE_ERA_DATE_ORDER"
15395
15452
  };
15396
15453
  NON_ISO_DATE_RE = /^\d{1,4}[/.]\d/;
15397
15454
  SIDE_RE = /^side\s+(above|below|alternate)\b/i;
@@ -26327,9 +26384,6 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26327
26384
  const hasExpandedEra = parsed.events.some(
26328
26385
  (e) => e.era && eraByName.has(e.era) && !collapsedSet.has(e.era)
26329
26386
  );
26330
- const anyCollapsed = parsed.events.some(
26331
- (e) => e.era && collapsedSet.has(e.era)
26332
- );
26333
26387
  const sideOpt = parsed.options.side;
26334
26388
  const alternate = sideOpt === "alternate";
26335
26389
  const eraSide = alternate ? "below" : sideOpt === "above" ? "below" : "above";
@@ -26396,7 +26450,8 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26396
26450
  x: 0,
26397
26451
  side,
26398
26452
  lane: 0,
26399
- left: 0
26453
+ left: 0,
26454
+ spanHalf: 0
26400
26455
  };
26401
26456
  }
26402
26457
  const era = slot.era;
@@ -26409,19 +26464,19 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26409
26464
  palette.textOnFillLight,
26410
26465
  palette.textOnFillDark
26411
26466
  );
26412
- const overflow = slot.members.length > ERA_MEMBER_MAX;
26413
- const shown = overflow ? slot.members.slice(0, ERA_MEMBER_MAX - 1) : slot.members;
26414
- const memberStrs = shown.map(
26467
+ let spanLo = null;
26468
+ let spanHi = null;
26469
+ for (const m of slot.members) {
26470
+ if (m.dateValue === null) continue;
26471
+ if (spanLo === null || m.dateValue < spanLo) spanLo = m.dateValue;
26472
+ if (spanHi === null || m.dateValue > spanHi) spanHi = m.dateValue;
26473
+ }
26474
+ const repDateValue = spanLo !== null && spanHi !== null ? (spanLo + spanHi) / 2 : null;
26475
+ const memberStrs = slot.members.map(
26415
26476
  (m) => `\u2022 ${m.date ? `${m.date} ` : ""}${m.label}`
26416
26477
  );
26417
- const bulletColors = shown.map(eventColor);
26478
+ const bulletColors = slot.members.map(eventColor);
26418
26479
  const lines = wrapDescription(memberStrs, charsPerLine);
26419
- if (overflow) {
26420
- lines.push({
26421
- text: `+${slot.members.length - (ERA_MEMBER_MAX - 1)} more`,
26422
- kind: "plain"
26423
- });
26424
- }
26425
26480
  return {
26426
26481
  kind: "era",
26427
26482
  event: null,
@@ -26429,7 +26484,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26429
26484
  members: slot.members,
26430
26485
  label: era.name,
26431
26486
  date: null,
26432
- dateValue: null,
26487
+ dateValue: repDateValue,
26433
26488
  lineNumber: era.lineNumber,
26434
26489
  eraName: era.name,
26435
26490
  color: solid,
@@ -26441,87 +26496,189 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26441
26496
  x: 0,
26442
26497
  side,
26443
26498
  lane: 0,
26444
- left: 0
26499
+ left: 0,
26500
+ spanHalf: 0
26445
26501
  };
26446
26502
  });
26447
- const scaled = parsed.options.scale && !anyCollapsed && placed.every((p) => p.dateValue !== null);
26503
+ const scaled = parsed.options.scale && placed.every((p) => p.dateValue !== null);
26448
26504
  const innerW = width - H_MARGIN * 2;
26449
- if (scaled) {
26450
- const vals = placed.map((p) => p.dateValue);
26451
- const lo = Math.min(...vals);
26452
- const hi = Math.max(...vals);
26453
- placed.forEach((p) => {
26454
- p.x = H_MARGIN + (hi > lo ? (p.dateValue - lo) / (hi - lo) * innerW : innerW / 2);
26505
+ const MIN_DOT_GAP = 16;
26506
+ const STRETCH_CAP = 8;
26507
+ const COLLAPSE_W = 64;
26508
+ const SEG_GAP = 16;
26509
+ const segments = [];
26510
+ let run = [];
26511
+ const flushRun = () => {
26512
+ if (!run.length) return;
26513
+ const ds = run.map((e) => e.dateValue);
26514
+ segments.push({
26515
+ kind: "run",
26516
+ events: run,
26517
+ lo: Math.min(...ds),
26518
+ hi: Math.max(...ds)
26455
26519
  });
26456
- const MIN_DOT_GAP = 16;
26457
- let prevX = -Infinity;
26458
- let prevVal = null;
26459
- for (const p of [...placed].sort((a, b) => a.x - b.x)) {
26460
- if (p.dateValue === prevVal) {
26461
- p.x = prevX;
26462
- continue;
26520
+ run = [];
26521
+ };
26522
+ for (const p of placed) {
26523
+ if (p.kind === "era") {
26524
+ flushRun();
26525
+ segments.push({ kind: "capsule", p });
26526
+ } else {
26527
+ run.push(p);
26528
+ }
26529
+ }
26530
+ flushRun();
26531
+ const totalRunTime = segments.reduce(
26532
+ (s, seg) => s + (seg.kind === "run" ? seg.hi - seg.lo : 0),
26533
+ 0
26534
+ );
26535
+ const place = (stretch) => {
26536
+ if (scaled) {
26537
+ const pxPerUnit = totalRunTime > 0 ? innerW * stretch / totalRunTime : 0;
26538
+ let cursor = H_MARGIN;
26539
+ for (const seg of segments) {
26540
+ if (seg.kind === "capsule") {
26541
+ seg.p.x = cursor + COLLAPSE_W / 2;
26542
+ seg.p.spanHalf = COLLAPSE_W / 2;
26543
+ cursor += COLLAPSE_W + SEG_GAP;
26544
+ } else {
26545
+ for (const e of seg.events) {
26546
+ e.x = cursor + (e.dateValue - seg.lo) * pxPerUnit;
26547
+ e.spanHalf = 0;
26548
+ }
26549
+ cursor += (seg.hi - seg.lo) * pxPerUnit + SEG_GAP;
26550
+ }
26551
+ }
26552
+ let prevX = -Infinity;
26553
+ let prevVal = null;
26554
+ for (const p of [...placed].sort((a, b) => a.x - b.x)) {
26555
+ if (p.kind === "era") {
26556
+ prevX = p.x + p.spanHalf;
26557
+ prevVal = null;
26558
+ continue;
26559
+ }
26560
+ if (p.dateValue === prevVal) {
26561
+ p.x = prevX;
26562
+ continue;
26563
+ }
26564
+ if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26565
+ prevX = p.x;
26566
+ prevVal = p.dateValue;
26567
+ }
26568
+ } else {
26569
+ placed.forEach((p) => {
26570
+ p.spanHalf = 0;
26571
+ });
26572
+ const n = placed.length;
26573
+ const spacing = n > 1 ? Math.max(MIN_SPACING, innerW / (n - 1)) : 0;
26574
+ placed.forEach((p, i) => {
26575
+ p.x = H_MARGIN + i * spacing;
26576
+ });
26577
+ }
26578
+ for (const side of ["above", "below"]) {
26579
+ const arr = placed.filter((p) => p.side === side).sort((a, b) => a.x - b.x);
26580
+ const laneRight = [];
26581
+ for (let i = 0; i < arr.length; i++) {
26582
+ const p = arr[i];
26583
+ const next = arr[i + 1];
26584
+ const crowdedRight = !!next && next.x - p.x < CARD_W + FAN_GAP;
26585
+ const preferred = crowdedRight ? p.x - CARD_W + CARD_INSET : p.x - CARD_W / 2;
26586
+ const maxLeft = p.x - CARD_INSET;
26587
+ const minLeft = p.x - CARD_W + CARD_INSET;
26588
+ let lane = laneRight.length;
26589
+ let left = Math.max(preferred, minLeft);
26590
+ for (let l = 0; l < laneRight.length; l++) {
26591
+ const want = Math.max(preferred, laneRight[l] + FAN_GAP, minLeft);
26592
+ if (want <= maxLeft) {
26593
+ lane = l;
26594
+ left = want;
26595
+ break;
26596
+ }
26597
+ }
26598
+ p.lane = lane;
26599
+ p.left = Math.max(6, left);
26600
+ laneRight[lane] = p.left + CARD_W;
26601
+ }
26602
+ }
26603
+ };
26604
+ const derive = () => {
26605
+ const contentW2 = Math.max(
26606
+ width,
26607
+ Math.max(...placed.map((p) => p.x + p.spanHalf)) + H_MARGIN,
26608
+ Math.max(...placed.map((p) => p.left + CARD_W)) + 6
26609
+ );
26610
+ const laneOffset = { above: [], below: [] };
26611
+ for (const side of ["above", "below"]) {
26612
+ const laneH = [];
26613
+ for (const p of placed) {
26614
+ if (p.side !== side) continue;
26615
+ laneH[p.lane] = Math.max(laneH[p.lane] ?? 0, p.cardH);
26463
26616
  }
26464
- if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26465
- prevX = p.x;
26466
- prevVal = p.dateValue;
26617
+ const off = [];
26618
+ let acc = 0;
26619
+ for (let l = 0; l < laneH.length; l++) {
26620
+ off[l] = acc;
26621
+ acc += (laneH[l] ?? 0) + LANE_GAP;
26622
+ }
26623
+ laneOffset[side] = off;
26467
26624
  }
26625
+ const laneNear2 = (p) => (p.side === "above" ? LEADER_ABOVE : LEADER_BELOW) + (laneOffset[p.side][p.lane] ?? 0);
26626
+ const ext = (side) => Math.max(
26627
+ 0,
26628
+ ...placed.filter((p) => p.side === side).map((p) => laneNear2(p) + p.cardH)
26629
+ );
26630
+ const dateAbove = placed.some((p) => p.side === "below" && p.date);
26631
+ const dateBelow = placed.some((p) => p.side === "above" && p.date);
26632
+ const contentAbove = Math.max(
26633
+ ext("above"),
26634
+ dateAbove ? DATE_OFFSET + 10 : 0
26635
+ );
26636
+ const contentBelow = Math.max(
26637
+ ext("below"),
26638
+ dateBelow ? DATE_OFFSET + 10 : 0
26639
+ );
26640
+ const aboveExt = contentAbove + (hasExpandedEra && eraSide === "above" ? ERA_BLOCK : 0);
26641
+ const belowExt = contentBelow + (hasExpandedEra && eraSide === "below" ? ERA_BLOCK : 0);
26642
+ const TOP_PAD = 14;
26643
+ const BOT_PAD = 14;
26644
+ const spineY2 = topUsed + TOP_PAD + aboveExt;
26645
+ const eraBaseY2 = eraSide === "above" ? spineY2 - (contentAbove + 14) : spineY2 + (contentBelow + 14);
26646
+ const contentH = spineY2 + belowExt + BOT_PAD;
26647
+ const totalH2 = Math.max(heightHint, contentH);
26648
+ return { contentW: contentW2, contentH, totalH: totalH2, spineY: spineY2, eraBaseY: eraBaseY2, laneNear: laneNear2 };
26649
+ };
26650
+ const envStretch = Number(
26651
+ globalThis.process?.env?.["DGMO_EVT_STRETCH"]
26652
+ );
26653
+ let layout;
26654
+ if (Number.isFinite(envStretch) && envStretch > 0) {
26655
+ place(envStretch);
26656
+ layout = derive();
26468
26657
  } else {
26469
- const n = placed.length;
26470
- const spacing = n > 1 ? Math.max(MIN_SPACING, innerW / (n - 1)) : 0;
26471
- placed.forEach((p, i) => {
26472
- p.x = H_MARGIN + i * spacing;
26473
- });
26474
- }
26475
- for (const side of ["above", "below"]) {
26476
- const arr = placed.filter((p) => p.side === side).sort((a, b) => a.x - b.x);
26477
- const laneRight = [];
26478
- for (let i = 0; i < arr.length; i++) {
26479
- const p = arr[i];
26480
- const next = arr[i + 1];
26481
- const crowdedRight = !!next && next.x - p.x < CARD_W + FAN_GAP;
26482
- const preferred = crowdedRight ? p.x - CARD_W + CARD_INSET : p.x - CARD_W / 2;
26483
- const maxLeft = p.x - CARD_INSET;
26484
- const minLeft = p.x - CARD_W + CARD_INSET;
26485
- let lane = laneRight.length;
26486
- let left = Math.max(preferred, minLeft);
26487
- for (let l = 0; l < laneRight.length; l++) {
26488
- const want = Math.max(preferred, laneRight[l] + FAN_GAP, minLeft);
26489
- if (want <= maxLeft) {
26490
- lane = l;
26491
- left = want;
26492
- break;
26658
+ place(1);
26659
+ layout = derive();
26660
+ const panelAspect = width / heightHint;
26661
+ const aspectOf = (l) => l.contentW / l.contentH;
26662
+ if (scaled && layout.contentH > heightHint && aspectOf(layout) < panelAspect) {
26663
+ place(STRETCH_CAP);
26664
+ const capped = derive();
26665
+ if (aspectOf(capped) <= panelAspect) {
26666
+ layout = capped;
26667
+ } else {
26668
+ let loF = 1;
26669
+ let hiF = STRETCH_CAP;
26670
+ for (let it = 0; it < 8; it++) {
26671
+ const mid = (loF + hiF) / 2;
26672
+ place(mid);
26673
+ if (aspectOf(derive()) >= panelAspect) hiF = mid;
26674
+ else loF = mid;
26493
26675
  }
26676
+ place(hiF);
26677
+ layout = derive();
26494
26678
  }
26495
- p.lane = lane;
26496
- p.left = Math.max(6, left);
26497
- laneRight[lane] = p.left + CARD_W;
26498
26679
  }
26499
26680
  }
26500
- const contentW = Math.max(
26501
- width,
26502
- Math.max(...placed.map((p) => p.x)) + H_MARGIN,
26503
- Math.max(...placed.map((p) => p.left + CARD_W)) + 6
26504
- );
26505
- const rowGap = (side) => Math.max(0, ...placed.filter((p) => p.side === side).map((p) => p.cardH)) + LANE_GAP;
26506
- const rowGapA = rowGap("above");
26507
- const rowGapB = rowGap("below");
26508
- const ext = (side) => Math.max(
26509
- 0,
26510
- ...placed.filter((p) => p.side === side).map(
26511
- (p) => (side === "above" ? LEADER_ABOVE : LEADER_BELOW) + p.lane * (side === "above" ? rowGapA : rowGapB) + p.cardH
26512
- )
26513
- );
26514
- const dateAbove = placed.some((p) => p.side === "below" && p.date);
26515
- const dateBelow = placed.some((p) => p.side === "above" && p.date);
26516
- const contentAbove = Math.max(ext("above"), dateAbove ? DATE_OFFSET + 10 : 0);
26517
- const contentBelow = Math.max(ext("below"), dateBelow ? DATE_OFFSET + 10 : 0);
26518
- const aboveExt = contentAbove + (hasExpandedEra && eraSide === "above" ? ERA_BLOCK : 0);
26519
- const belowExt = contentBelow + (hasExpandedEra && eraSide === "below" ? ERA_BLOCK : 0);
26520
- const TOP_PAD = 14;
26521
- const BOT_PAD = 14;
26522
- const spineY = topUsed + TOP_PAD + aboveExt;
26523
- const eraBaseY = eraSide === "above" ? spineY - (contentAbove + 14) : spineY + (contentBelow + 14);
26524
- const totalH = Math.max(heightHint, spineY + belowExt + BOT_PAD);
26681
+ const { contentW, totalH, spineY, eraBaseY, laneNear } = layout;
26525
26682
  const svg = d3Selection6.select(container).append("svg").attr("width", contentW).attr("height", totalH).attr("viewBox", `0 0 ${contentW} ${totalH}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY);
26526
26683
  svg.append("rect").attr("width", contentW).attr("height", totalH).attr("fill", palette.bg);
26527
26684
  if (showTitle) {
@@ -26549,12 +26706,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26549
26706
  activeGroup
26550
26707
  });
26551
26708
  }
26552
- const xs = placed.map((p) => p.x);
26553
- const x0 = Math.min(...xs);
26554
- const x1 = Math.max(...xs);
26709
+ const x0 = Math.min(...placed.map((p) => p.x - p.spanHalf));
26710
+ const x1 = Math.max(...placed.map((p) => p.x + p.spanHalf));
26555
26711
  svg.append("line").attr("x1", x0 - 20).attr("y1", spineY).attr("x2", x1 + 20).attr("y2", spineY).attr("stroke", palette.text).attr("stroke-width", 2.5).attr("stroke-linecap", "round");
26556
26712
  const geo = placed.map((p) => {
26557
- const near = p.side === "above" ? spineY - LEADER_ABOVE - p.lane * rowGapA : spineY + LEADER_BELOW + p.lane * rowGapB;
26713
+ const near = p.side === "above" ? spineY - laneNear(p) : spineY + laneNear(p);
26558
26714
  const top = p.side === "above" ? near - p.cardH : near;
26559
26715
  return { p, near, top };
26560
26716
  });
@@ -26666,7 +26822,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26666
26822
  for (const p of placed) {
26667
26823
  if (p.kind === "era") {
26668
26824
  const col = p.color;
26669
- const half = Math.min(46, 16 + (p.members.length - 1) * 9);
26825
+ const half = Math.max(16, p.spanHalf);
26670
26826
  const spineLeft = x0 - 20;
26671
26827
  const spineRight = x1 + 20;
26672
26828
  const bx0 = Math.max(spineLeft, p.x - half);
@@ -26677,6 +26833,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26677
26833
  "d",
26678
26834
  `M${bx0},${spineY} L${bx0},${barY} L${bx1},${barY} L${bx1},${spineY}`
26679
26835
  ).attr("fill", "none").attr("stroke", col).attr("stroke-width", 1.5).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
26836
+ const breakCx = (bx0 + bx1) / 2;
26837
+ eg.append("line").attr("x1", breakCx - BREAK_GAP / 2).attr("y1", spineY).attr("x2", breakCx + BREAK_GAP / 2).attr("y2", spineY).attr("stroke", palette.bg).attr("stroke-width", 4).attr("stroke-linecap", "butt");
26838
+ for (const d of axisBreakPaths(breakCx, spineY)) {
26839
+ eg.append("path").attr("d", d).attr("fill", "none").attr("stroke", col).attr("stroke-width", 1.25).attr("stroke-linecap", "round").attr("stroke-linejoin", "round").attr("stroke-opacity", 0.85);
26840
+ }
26680
26841
  if (onClickItem) {
26681
26842
  const ln = p.lineNumber;
26682
26843
  eg.style("cursor", "pointer").on("click", () => onClickItem(ln));
@@ -26710,13 +26871,23 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26710
26871
  }
26711
26872
  const clampX = (v) => Math.max(4, Math.min(contentW - 4, v));
26712
26873
  const cap = eraSide === "above" ? 1 : -1;
26713
- for (const r of runs) {
26714
- if (r.collapsed) continue;
26874
+ const drawn = runs.filter((r) => !r.collapsed).map((r) => ({
26875
+ run: r,
26876
+ x0: clampX(r.firstX - ERA_BRACKET_PAD),
26877
+ x1: clampX(r.lastX + ERA_BRACKET_PAD)
26878
+ })).sort((a, b) => a.run.firstX - b.run.firstX);
26879
+ for (let i = 1; i < drawn.length; i++) {
26880
+ const prev = drawn[i - 1];
26881
+ const cur = drawn[i];
26882
+ if (prev.x1 <= cur.x0) continue;
26883
+ const mid = (prev.run.lastX + cur.run.firstX) / 2;
26884
+ prev.x1 = Math.max(prev.run.lastX, Math.min(prev.x1, mid - ERA_SEAM_GAP));
26885
+ cur.x0 = Math.min(cur.run.firstX, Math.max(cur.x0, mid + ERA_SEAM_GAP));
26886
+ }
26887
+ for (const { run: r, x0: x02, x1: x12 } of drawn) {
26715
26888
  const neutral = !r.era.color;
26716
26889
  const col = neutral ? palette.text : resolveColor(r.era.color, palette) ?? palette.text;
26717
26890
  const op = neutral ? 0.5 : 0.85;
26718
- const x02 = clampX(r.firstX - 14);
26719
- const x12 = clampX(r.lastX + 14);
26720
26891
  const y = eraBaseY;
26721
26892
  const eg = svg.append("g").attr("data-era", r.era.name).attr("data-era-collapsed", String(r.collapsed)).attr("data-line-number", r.era.lineNumber);
26722
26893
  eg.append("path").attr(
@@ -26742,6 +26913,22 @@ function renderEventLineForExport(container, parsed, palette, isDark, exportDims
26742
26913
  tagOverride
26743
26914
  );
26744
26915
  }
26916
+ function axisBreakPaths(cx, cy) {
26917
+ const HUMPS = 2;
26918
+ const STEPS = 14;
26919
+ const wave = (x0) => {
26920
+ let d = "";
26921
+ for (let i = 0; i <= STEPS; i++) {
26922
+ const t = i / STEPS;
26923
+ const y = cy - BREAK_HALF_H + t * 2 * BREAK_HALF_H;
26924
+ const x = x0 + BREAK_AMP * Math.sin(t * Math.PI * HUMPS);
26925
+ d += `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`;
26926
+ if (i < STEPS) d += " ";
26927
+ }
26928
+ return d;
26929
+ };
26930
+ return [wave(cx - BREAK_GAP / 2), wave(cx + BREAK_GAP / 2)];
26931
+ }
26745
26932
  function wrapDescription(lines, charsPerLine) {
26746
26933
  const out = [];
26747
26934
  for (const line11 of lines) {
@@ -26771,7 +26958,7 @@ function renderBody(cardG, lines, bodyColor, palette, startBaseline = CARD_BODY_
26771
26958
  y += DESC_LINE_H;
26772
26959
  }
26773
26960
  }
26774
- var d3Selection6, CARD_W, H_MARGIN, TITLE_AREA, LEGEND_BAND, LEADER_ABOVE, LEADER_BELOW, DESC_FONT, DESC_LINE_H, CARD_PAD, CARD_BODY_TOP, DOT_R, MIN_SPACING, LANE_GAP, NEUTRAL_TAG, CARD_INSET, FAN_GAP, DATE_OFFSET, ERA_BLOCK, ERA_BRACKET_CAP, ERA_LEG, ERA_LABEL_FONT, ERA_MEMBER_MAX;
26961
+ var d3Selection6, CARD_W, H_MARGIN, TITLE_AREA, LEGEND_BAND, LEADER_ABOVE, LEADER_BELOW, DESC_FONT, DESC_LINE_H, CARD_PAD, CARD_BODY_TOP, DOT_R, MIN_SPACING, LANE_GAP, NEUTRAL_TAG, CARD_INSET, FAN_GAP, DATE_OFFSET, ERA_BLOCK, ERA_BRACKET_CAP, ERA_BRACKET_PAD, ERA_SEAM_GAP, BREAK_HALF_H, BREAK_AMP, BREAK_GAP, ERA_LEG, ERA_LABEL_FONT;
26775
26962
  var init_renderer = __esm({
26776
26963
  "src/event-line/renderer.ts"() {
26777
26964
  "use strict";
@@ -26806,9 +26993,13 @@ var init_renderer = __esm({
26806
26993
  DATE_OFFSET = 28;
26807
26994
  ERA_BLOCK = 30;
26808
26995
  ERA_BRACKET_CAP = 8;
26996
+ ERA_BRACKET_PAD = 14;
26997
+ ERA_SEAM_GAP = 3;
26998
+ BREAK_HALF_H = 6;
26999
+ BREAK_AMP = 2.2;
27000
+ BREAK_GAP = 5;
26809
27001
  ERA_LEG = 13;
26810
27002
  ERA_LABEL_FONT = 11.5;
26811
- ERA_MEMBER_MAX = 6;
26812
27003
  }
26813
27004
  });
26814
27005
 
@@ -53279,6 +53470,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53279
53470
  y,
53280
53471
  score,
53281
53472
  palette,
53473
+ isDark,
53282
53474
  SCORE_LABEL_R
53283
53475
  );
53284
53476
  if (!exportDims) {
@@ -53310,7 +53502,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53310
53502
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
53311
53503
  for (const pt of layout.curvePoints) {
53312
53504
  const step = allSteps[pt.stepIndex];
53313
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53505
+ const faceG = renderScoreFace(
53506
+ curveG,
53507
+ pt.x,
53508
+ pt.y,
53509
+ pt.score,
53510
+ palette,
53511
+ isDark
53512
+ );
53314
53513
  addEmotionLabel(faceG, pt, palette);
53315
53514
  if (step) {
53316
53515
  faceG.attr("data-line-number", step.lineNumber);
@@ -53336,7 +53535,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53336
53535
  const pt = layout.curvePoints[0];
53337
53536
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
53338
53537
  const step = allSteps[pt.stepIndex];
53339
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53538
+ const faceG = renderScoreFace(
53539
+ curveG,
53540
+ pt.x,
53541
+ pt.y,
53542
+ pt.score,
53543
+ palette,
53544
+ isDark
53545
+ );
53340
53546
  addEmotionLabel(faceG, pt, palette);
53341
53547
  if (step) {
53342
53548
  faceG.attr("data-line-number", step.lineNumber);
@@ -53433,6 +53639,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53433
53639
  faceCy,
53434
53640
  step.score,
53435
53641
  palette,
53642
+ isDark,
53436
53643
  COLLAPSED_FACE_R
53437
53644
  );
53438
53645
  faceG.attr("data-line-number", step.lineNumber);
@@ -53536,7 +53743,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53536
53743
  const fcy = parseFloat(el.attr("data-cy") ?? "0");
53537
53744
  el.attr(
53538
53745
  "transform",
53539
- `translate(${fcx},${fcy}) scale(1.5) translate(${-fcx},${-fcy})`
53746
+ `translate(${fcx},${fcy}) scale(${FACE_HOVER_SCALE}) translate(${-fcx},${-fcy})`
53540
53747
  );
53541
53748
  } else {
53542
53749
  el.attr("transform", null);
@@ -53582,12 +53789,12 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53582
53789
  PADDING2,
53583
53790
  Math.min(fcx - bw / 2, layout.totalWidth - PADDING2 - bw)
53584
53791
  );
53585
- const by = Math.max(PADDING2, fcy - FACE_RADIUS - THOUGHT_GAP - bh);
53792
+ const by = Math.max(PADDING2, fcy - FACE_HOVER_R - THOUGHT_GAP - bh);
53586
53793
  const scoreColor = scoreToColor(score, palette);
53587
53794
  const tintedBg = mix(scoreColor, palette.surface, 20);
53588
53795
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
53589
53796
  g.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", CARD_RADIUS).attr("fill", tintedBg).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
53590
- g.append("line").attr("x1", fcx).attr("y1", by + bh).attr("x2", fcx).attr("y2", fcy - FACE_RADIUS - 1).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
53797
+ g.append("line").attr("x1", fcx).attr("y1", by + bh).attr("x2", fcx).attr("y2", fcy - FACE_HOVER_R - 1).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
53591
53798
  const centerX = bx + bw / 2;
53592
53799
  for (let i = 0; i < lines.length; i++) {
53593
53800
  g.append("text").attr("x", centerX).attr("y", by + THOUGHT_PAD_Y + (i + 1) * THOUGHT_LINE_H - 2).attr("text-anchor", "middle").attr("font-size", THOUGHT_FONT).attr("font-style", "italic").attr("fill", palette.textMuted).text(lines[i]);
@@ -53830,24 +54037,23 @@ function renderPersonaSilhouette(parent, cx, cy, color, palette, scale = 1) {
53830
54037
  `M ${cx} ${cy - 12 * s} C ${cx + 10 * s} ${cy - 12 * s}, ${cx + 9 * s} ${cy + 2 * s}, ${cx + 6 * s} ${cy + 6 * s} Q ${cx} ${cy + 9 * s}, ${cx - 6 * s} ${cy + 6 * s} C ${cx - 9 * s} ${cy + 2 * s}, ${cx - 10 * s} ${cy - 12 * s}, ${cx} ${cy - 12 * s} Z`
53831
54038
  ).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", 1.2);
53832
54039
  }
53833
- function renderScoreFace(parent, cx, cy, score, palette, radius) {
54040
+ function renderScoreFace(parent, cx, cy, score, palette, isDark, radius) {
53834
54041
  const r = radius ?? FACE_RADIUS;
53835
54042
  const color = scoreToColor(score, palette);
53836
54043
  const g = parent.append("g").attr("class", "journey-face").attr("data-cx", cx).attr("data-cy", cy);
53837
- g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", color).attr("stroke", palette.bg).attr("stroke-width", 1.5);
53838
- const eyeColor = contrastText(
53839
- color,
53840
- palette.textOnFillLight,
53841
- palette.textOnFillDark
53842
- );
54044
+ const faceFill = shapeFill(palette, color, isDark);
54045
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r + 1).attr("fill", palette.bg);
54046
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", faceFill).attr("stroke", color).attr("stroke-width", 2);
54047
+ const eyeColor = color;
53843
54048
  const eyeY = cy - r * 0.15;
53844
54049
  const eyeSpacing = r * 0.32;
53845
54050
  const eyeR = r * 0.12;
53846
54051
  g.append("circle").attr("cx", cx - eyeSpacing).attr("cy", eyeY).attr("r", eyeR).attr("fill", eyeColor);
53847
54052
  g.append("circle").attr("cx", cx + eyeSpacing).attr("cy", eyeY).attr("r", eyeR).attr("fill", eyeColor);
53848
- const mouthY = cy + r * 0.25;
53849
- const mouthW = r * 0.45;
53850
- const curve = (score - 3) / 2 * r * 0.35;
54053
+ const mouthW = r * 0.46;
54054
+ const curve = (score - 3) / 2 * r * 0.42;
54055
+ const apexY = cy + r * 0.32;
54056
+ const mouthY = apexY - curve / 2;
53851
54057
  g.append("path").attr(
53852
54058
  "d",
53853
54059
  `M ${cx - mouthW} ${mouthY} Q ${cx} ${mouthY + curve} ${cx + mouthW} ${mouthY}`
@@ -53918,7 +54124,7 @@ function renderJourneyMapForExport(content, theme, palette) {
53918
54124
  }
53919
54125
  return svgEl.outerHTML;
53920
54126
  }
53921
- var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
54127
+ var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, FACE_HOVER_SCALE, FACE_HOVER_R, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
53922
54128
  var init_renderer17 = __esm({
53923
54129
  "src/journey-map/renderer.ts"() {
53924
54130
  "use strict";
@@ -53954,6 +54160,8 @@ var init_renderer17 = __esm({
53954
54160
  GRID_LINE_OPACITY = 0.15;
53955
54161
  CURVE_STROKE_WIDTH = 2.5;
53956
54162
  FACE_RADIUS = 14;
54163
+ FACE_HOVER_SCALE = 1.5;
54164
+ FACE_HOVER_R = (FACE_RADIUS + 1) * FACE_HOVER_SCALE;
53957
54165
  DIM_HOVER = 0.25;
53958
54166
  TITLE_LINE_HEIGHT = 16;
53959
54167
  EMOTION_LABEL_MAX_WIDTH = 180;