@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/auto.mjs CHANGED
@@ -103,7 +103,7 @@ function vennAliasKeywordRemovedMessage(args) {
103
103
  return `Venn 'alias' keyword was removed. Use 'as' instead \u2014 '${args.name} as ${args.alias}'.`;
104
104
  }
105
105
  function pipeOperatorRemovedMessage() {
106
- return `'|' removed \u2014 'Node | c: red' \u2192 'Node c: red'`;
106
+ return `'|' is no longer metadata syntax \u2014 write metadata inline as 'key: value' (e.g. "Name color: red"), or remove the '|'.`;
107
107
  }
108
108
  function ganttBarePercentRemovedMessage(percent) {
109
109
  return `Bare-percent progress shorthand '| ${percent}' was removed. Use 'progress: ${percent.replace(/%$/, "")}' (integer 0\u2013100, no '%').`;
@@ -13629,7 +13629,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13629
13629
  solid: parsed.solidFill === true
13630
13630
  }),
13631
13631
  borderColor: catColor,
13632
- borderWidth: CHART_BORDER_WIDTH
13632
+ borderWidth: CHART_BORDER_WIDTH,
13633
+ opacity: SCATTER_FILL_OPACITY
13633
13634
  },
13634
13635
  label: labelConfig,
13635
13636
  emphasis: emphasisConfig,
@@ -13656,6 +13657,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13656
13657
  {
13657
13658
  type: "scatter",
13658
13659
  data,
13660
+ itemStyle: { opacity: SCATTER_FILL_OPACITY },
13659
13661
  label: labelConfig,
13660
13662
  emphasis: emphasisConfig,
13661
13663
  blur: blurConfig
@@ -14961,7 +14963,7 @@ async function renderExtendedChartForExport(content, theme, palette) {
14961
14963
  chart.dispose();
14962
14964
  }
14963
14965
  }
14964
- var 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;
14966
+ var 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;
14965
14967
  var init_echarts = __esm({
14966
14968
  "src/echarts.ts"() {
14967
14969
  "use strict";
@@ -15015,6 +15017,7 @@ var init_echarts = __esm({
15015
15017
  tooltip: { show: false }
15016
15018
  };
15017
15019
  CHART_BORDER_WIDTH = 2;
15020
+ SCATTER_FILL_OPACITY = 0.65;
15018
15021
  VALID_EXTENDED_TYPES = /* @__PURE__ */ new Set([
15019
15022
  "sankey",
15020
15023
  "chord",
@@ -15290,8 +15293,61 @@ function parseEventLine(content, palette) {
15290
15293
  EVENT_LINE_DIAGNOSTIC_CODES.NO_EVENTS
15291
15294
  );
15292
15295
  }
15296
+ if (options.scale) {
15297
+ validateEraDateOrder(result.events, result.eras, pushWarning);
15298
+ }
15293
15299
  return result;
15294
15300
  }
15301
+ function validateEraDateOrder(events, eras, pushWarning) {
15302
+ if (eras.length < 2) return;
15303
+ const order = new Map(eras.map((e, i) => [e.name, i]));
15304
+ const dated = events.filter(
15305
+ (e) => e.dateValue !== null && e.era !== null && order.has(e.era)
15306
+ );
15307
+ if (dated.length === 0) return;
15308
+ const perEra = /* @__PURE__ */ new Map();
15309
+ for (const ev of dated) {
15310
+ const idx = order.get(ev.era);
15311
+ const cur = perEra.get(idx);
15312
+ if (!cur) {
15313
+ perEra.set(idx, { min: ev, max: ev });
15314
+ } else {
15315
+ if (ev.dateValue < cur.min.dateValue) cur.min = ev;
15316
+ if (ev.dateValue > cur.max.dateValue) cur.max = ev;
15317
+ }
15318
+ }
15319
+ const n = eras.length;
15320
+ const prefixMax = new Array(n).fill(null);
15321
+ const suffixMin = new Array(n).fill(null);
15322
+ for (let k = 1; k < n; k++) {
15323
+ const prev = perEra.get(k - 1)?.max ?? null;
15324
+ const carried = prefixMax[k - 1] ?? null;
15325
+ prefixMax[k] = prev && (!carried || prev.dateValue > carried.dateValue) ? prev : carried;
15326
+ }
15327
+ for (let k = n - 2; k >= 0; k--) {
15328
+ const next = perEra.get(k + 1)?.min ?? null;
15329
+ const carried = suffixMin[k + 1] ?? null;
15330
+ suffixMin[k] = next && (!carried || next.dateValue < carried.dateValue) ? next : carried;
15331
+ }
15332
+ for (const ev of dated) {
15333
+ const k = order.get(ev.era);
15334
+ const ahead = suffixMin[k];
15335
+ const behind = prefixMax[k];
15336
+ if (ahead && ev.dateValue > ahead.dateValue) {
15337
+ pushWarning(
15338
+ ev.lineNumber,
15339
+ `"${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.`,
15340
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15341
+ );
15342
+ } else if (behind && ev.dateValue < behind.dateValue) {
15343
+ pushWarning(
15344
+ ev.lineNumber,
15345
+ `"${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.`,
15346
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15347
+ );
15348
+ }
15349
+ }
15350
+ }
15295
15351
  function parseEventHeader(trimmed, lineNumber, era, aliasMap, diagnostics, pushWarning) {
15296
15352
  let date = null;
15297
15353
  let dateValue = null;
@@ -15358,7 +15414,8 @@ var init_parser13 = __esm({
15358
15414
  EVENT_LINE_DIAGNOSTIC_CODES = {
15359
15415
  NO_EVENTS: "E_EVENT_LINE_NO_EVENTS",
15360
15416
  BAD_DATE: "E_EVENT_LINE_BAD_DATE",
15361
- UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED"
15417
+ UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED",
15418
+ ERA_DATE_ORDER: "E_EVENT_LINE_ERA_DATE_ORDER"
15362
15419
  };
15363
15420
  NON_ISO_DATE_RE = /^\d{1,4}[/.]\d/;
15364
15421
  SIDE_RE = /^side\s+(above|below|alternate)\b/i;
@@ -26295,9 +26352,6 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26295
26352
  const hasExpandedEra = parsed.events.some(
26296
26353
  (e) => e.era && eraByName.has(e.era) && !collapsedSet.has(e.era)
26297
26354
  );
26298
- const anyCollapsed = parsed.events.some(
26299
- (e) => e.era && collapsedSet.has(e.era)
26300
- );
26301
26355
  const sideOpt = parsed.options.side;
26302
26356
  const alternate = sideOpt === "alternate";
26303
26357
  const eraSide = alternate ? "below" : sideOpt === "above" ? "below" : "above";
@@ -26364,7 +26418,8 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26364
26418
  x: 0,
26365
26419
  side,
26366
26420
  lane: 0,
26367
- left: 0
26421
+ left: 0,
26422
+ spanHalf: 0
26368
26423
  };
26369
26424
  }
26370
26425
  const era = slot.era;
@@ -26377,19 +26432,19 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26377
26432
  palette.textOnFillLight,
26378
26433
  palette.textOnFillDark
26379
26434
  );
26380
- const overflow = slot.members.length > ERA_MEMBER_MAX;
26381
- const shown = overflow ? slot.members.slice(0, ERA_MEMBER_MAX - 1) : slot.members;
26382
- const memberStrs = shown.map(
26435
+ let spanLo = null;
26436
+ let spanHi = null;
26437
+ for (const m of slot.members) {
26438
+ if (m.dateValue === null) continue;
26439
+ if (spanLo === null || m.dateValue < spanLo) spanLo = m.dateValue;
26440
+ if (spanHi === null || m.dateValue > spanHi) spanHi = m.dateValue;
26441
+ }
26442
+ const repDateValue = spanLo !== null && spanHi !== null ? (spanLo + spanHi) / 2 : null;
26443
+ const memberStrs = slot.members.map(
26383
26444
  (m) => `\u2022 ${m.date ? `${m.date} ` : ""}${m.label}`
26384
26445
  );
26385
- const bulletColors = shown.map(eventColor);
26446
+ const bulletColors = slot.members.map(eventColor);
26386
26447
  const lines = wrapDescription(memberStrs, charsPerLine);
26387
- if (overflow) {
26388
- lines.push({
26389
- text: `+${slot.members.length - (ERA_MEMBER_MAX - 1)} more`,
26390
- kind: "plain"
26391
- });
26392
- }
26393
26448
  return {
26394
26449
  kind: "era",
26395
26450
  event: null,
@@ -26397,7 +26452,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26397
26452
  members: slot.members,
26398
26453
  label: era.name,
26399
26454
  date: null,
26400
- dateValue: null,
26455
+ dateValue: repDateValue,
26401
26456
  lineNumber: era.lineNumber,
26402
26457
  eraName: era.name,
26403
26458
  color: solid,
@@ -26409,87 +26464,189 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26409
26464
  x: 0,
26410
26465
  side,
26411
26466
  lane: 0,
26412
- left: 0
26467
+ left: 0,
26468
+ spanHalf: 0
26413
26469
  };
26414
26470
  });
26415
- const scaled = parsed.options.scale && !anyCollapsed && placed.every((p) => p.dateValue !== null);
26471
+ const scaled = parsed.options.scale && placed.every((p) => p.dateValue !== null);
26416
26472
  const innerW = width - H_MARGIN * 2;
26417
- if (scaled) {
26418
- const vals = placed.map((p) => p.dateValue);
26419
- const lo = Math.min(...vals);
26420
- const hi = Math.max(...vals);
26421
- placed.forEach((p) => {
26422
- p.x = H_MARGIN + (hi > lo ? (p.dateValue - lo) / (hi - lo) * innerW : innerW / 2);
26473
+ const MIN_DOT_GAP = 16;
26474
+ const STRETCH_CAP = 8;
26475
+ const COLLAPSE_W = 64;
26476
+ const SEG_GAP = 16;
26477
+ const segments = [];
26478
+ let run2 = [];
26479
+ const flushRun = () => {
26480
+ if (!run2.length) return;
26481
+ const ds = run2.map((e) => e.dateValue);
26482
+ segments.push({
26483
+ kind: "run",
26484
+ events: run2,
26485
+ lo: Math.min(...ds),
26486
+ hi: Math.max(...ds)
26423
26487
  });
26424
- const MIN_DOT_GAP = 16;
26425
- let prevX = -Infinity;
26426
- let prevVal = null;
26427
- for (const p of [...placed].sort((a, b) => a.x - b.x)) {
26428
- if (p.dateValue === prevVal) {
26429
- p.x = prevX;
26430
- continue;
26488
+ run2 = [];
26489
+ };
26490
+ for (const p of placed) {
26491
+ if (p.kind === "era") {
26492
+ flushRun();
26493
+ segments.push({ kind: "capsule", p });
26494
+ } else {
26495
+ run2.push(p);
26496
+ }
26497
+ }
26498
+ flushRun();
26499
+ const totalRunTime = segments.reduce(
26500
+ (s, seg) => s + (seg.kind === "run" ? seg.hi - seg.lo : 0),
26501
+ 0
26502
+ );
26503
+ const place = (stretch) => {
26504
+ if (scaled) {
26505
+ const pxPerUnit = totalRunTime > 0 ? innerW * stretch / totalRunTime : 0;
26506
+ let cursor = H_MARGIN;
26507
+ for (const seg of segments) {
26508
+ if (seg.kind === "capsule") {
26509
+ seg.p.x = cursor + COLLAPSE_W / 2;
26510
+ seg.p.spanHalf = COLLAPSE_W / 2;
26511
+ cursor += COLLAPSE_W + SEG_GAP;
26512
+ } else {
26513
+ for (const e of seg.events) {
26514
+ e.x = cursor + (e.dateValue - seg.lo) * pxPerUnit;
26515
+ e.spanHalf = 0;
26516
+ }
26517
+ cursor += (seg.hi - seg.lo) * pxPerUnit + SEG_GAP;
26518
+ }
26519
+ }
26520
+ let prevX = -Infinity;
26521
+ let prevVal = null;
26522
+ for (const p of [...placed].sort((a, b) => a.x - b.x)) {
26523
+ if (p.kind === "era") {
26524
+ prevX = p.x + p.spanHalf;
26525
+ prevVal = null;
26526
+ continue;
26527
+ }
26528
+ if (p.dateValue === prevVal) {
26529
+ p.x = prevX;
26530
+ continue;
26531
+ }
26532
+ if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26533
+ prevX = p.x;
26534
+ prevVal = p.dateValue;
26535
+ }
26536
+ } else {
26537
+ placed.forEach((p) => {
26538
+ p.spanHalf = 0;
26539
+ });
26540
+ const n = placed.length;
26541
+ const spacing = n > 1 ? Math.max(MIN_SPACING, innerW / (n - 1)) : 0;
26542
+ placed.forEach((p, i) => {
26543
+ p.x = H_MARGIN + i * spacing;
26544
+ });
26545
+ }
26546
+ for (const side of ["above", "below"]) {
26547
+ const arr = placed.filter((p) => p.side === side).sort((a, b) => a.x - b.x);
26548
+ const laneRight = [];
26549
+ for (let i = 0; i < arr.length; i++) {
26550
+ const p = arr[i];
26551
+ const next = arr[i + 1];
26552
+ const crowdedRight = !!next && next.x - p.x < CARD_W + FAN_GAP;
26553
+ const preferred = crowdedRight ? p.x - CARD_W + CARD_INSET : p.x - CARD_W / 2;
26554
+ const maxLeft = p.x - CARD_INSET;
26555
+ const minLeft = p.x - CARD_W + CARD_INSET;
26556
+ let lane = laneRight.length;
26557
+ let left = Math.max(preferred, minLeft);
26558
+ for (let l = 0; l < laneRight.length; l++) {
26559
+ const want = Math.max(preferred, laneRight[l] + FAN_GAP, minLeft);
26560
+ if (want <= maxLeft) {
26561
+ lane = l;
26562
+ left = want;
26563
+ break;
26564
+ }
26565
+ }
26566
+ p.lane = lane;
26567
+ p.left = Math.max(6, left);
26568
+ laneRight[lane] = p.left + CARD_W;
26569
+ }
26570
+ }
26571
+ };
26572
+ const derive = () => {
26573
+ const contentW2 = Math.max(
26574
+ width,
26575
+ Math.max(...placed.map((p) => p.x + p.spanHalf)) + H_MARGIN,
26576
+ Math.max(...placed.map((p) => p.left + CARD_W)) + 6
26577
+ );
26578
+ const laneOffset = { above: [], below: [] };
26579
+ for (const side of ["above", "below"]) {
26580
+ const laneH = [];
26581
+ for (const p of placed) {
26582
+ if (p.side !== side) continue;
26583
+ laneH[p.lane] = Math.max(laneH[p.lane] ?? 0, p.cardH);
26431
26584
  }
26432
- if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26433
- prevX = p.x;
26434
- prevVal = p.dateValue;
26585
+ const off = [];
26586
+ let acc = 0;
26587
+ for (let l = 0; l < laneH.length; l++) {
26588
+ off[l] = acc;
26589
+ acc += (laneH[l] ?? 0) + LANE_GAP;
26590
+ }
26591
+ laneOffset[side] = off;
26435
26592
  }
26593
+ const laneNear2 = (p) => (p.side === "above" ? LEADER_ABOVE : LEADER_BELOW) + (laneOffset[p.side][p.lane] ?? 0);
26594
+ const ext = (side) => Math.max(
26595
+ 0,
26596
+ ...placed.filter((p) => p.side === side).map((p) => laneNear2(p) + p.cardH)
26597
+ );
26598
+ const dateAbove = placed.some((p) => p.side === "below" && p.date);
26599
+ const dateBelow = placed.some((p) => p.side === "above" && p.date);
26600
+ const contentAbove = Math.max(
26601
+ ext("above"),
26602
+ dateAbove ? DATE_OFFSET + 10 : 0
26603
+ );
26604
+ const contentBelow = Math.max(
26605
+ ext("below"),
26606
+ dateBelow ? DATE_OFFSET + 10 : 0
26607
+ );
26608
+ const aboveExt = contentAbove + (hasExpandedEra && eraSide === "above" ? ERA_BLOCK : 0);
26609
+ const belowExt = contentBelow + (hasExpandedEra && eraSide === "below" ? ERA_BLOCK : 0);
26610
+ const TOP_PAD = 14;
26611
+ const BOT_PAD = 14;
26612
+ const spineY2 = topUsed + TOP_PAD + aboveExt;
26613
+ const eraBaseY2 = eraSide === "above" ? spineY2 - (contentAbove + 14) : spineY2 + (contentBelow + 14);
26614
+ const contentH = spineY2 + belowExt + BOT_PAD;
26615
+ const totalH2 = Math.max(heightHint, contentH);
26616
+ return { contentW: contentW2, contentH, totalH: totalH2, spineY: spineY2, eraBaseY: eraBaseY2, laneNear: laneNear2 };
26617
+ };
26618
+ const envStretch = Number(
26619
+ globalThis.process?.env?.["DGMO_EVT_STRETCH"]
26620
+ );
26621
+ let layout;
26622
+ if (Number.isFinite(envStretch) && envStretch > 0) {
26623
+ place(envStretch);
26624
+ layout = derive();
26436
26625
  } else {
26437
- const n = placed.length;
26438
- const spacing = n > 1 ? Math.max(MIN_SPACING, innerW / (n - 1)) : 0;
26439
- placed.forEach((p, i) => {
26440
- p.x = H_MARGIN + i * spacing;
26441
- });
26442
- }
26443
- for (const side of ["above", "below"]) {
26444
- const arr = placed.filter((p) => p.side === side).sort((a, b) => a.x - b.x);
26445
- const laneRight = [];
26446
- for (let i = 0; i < arr.length; i++) {
26447
- const p = arr[i];
26448
- const next = arr[i + 1];
26449
- const crowdedRight = !!next && next.x - p.x < CARD_W + FAN_GAP;
26450
- const preferred = crowdedRight ? p.x - CARD_W + CARD_INSET : p.x - CARD_W / 2;
26451
- const maxLeft = p.x - CARD_INSET;
26452
- const minLeft = p.x - CARD_W + CARD_INSET;
26453
- let lane = laneRight.length;
26454
- let left = Math.max(preferred, minLeft);
26455
- for (let l = 0; l < laneRight.length; l++) {
26456
- const want = Math.max(preferred, laneRight[l] + FAN_GAP, minLeft);
26457
- if (want <= maxLeft) {
26458
- lane = l;
26459
- left = want;
26460
- break;
26626
+ place(1);
26627
+ layout = derive();
26628
+ const panelAspect = width / heightHint;
26629
+ const aspectOf = (l) => l.contentW / l.contentH;
26630
+ if (scaled && layout.contentH > heightHint && aspectOf(layout) < panelAspect) {
26631
+ place(STRETCH_CAP);
26632
+ const capped = derive();
26633
+ if (aspectOf(capped) <= panelAspect) {
26634
+ layout = capped;
26635
+ } else {
26636
+ let loF = 1;
26637
+ let hiF = STRETCH_CAP;
26638
+ for (let it = 0; it < 8; it++) {
26639
+ const mid = (loF + hiF) / 2;
26640
+ place(mid);
26641
+ if (aspectOf(derive()) >= panelAspect) hiF = mid;
26642
+ else loF = mid;
26461
26643
  }
26644
+ place(hiF);
26645
+ layout = derive();
26462
26646
  }
26463
- p.lane = lane;
26464
- p.left = Math.max(6, left);
26465
- laneRight[lane] = p.left + CARD_W;
26466
26647
  }
26467
26648
  }
26468
- const contentW = Math.max(
26469
- width,
26470
- Math.max(...placed.map((p) => p.x)) + H_MARGIN,
26471
- Math.max(...placed.map((p) => p.left + CARD_W)) + 6
26472
- );
26473
- const rowGap = (side) => Math.max(0, ...placed.filter((p) => p.side === side).map((p) => p.cardH)) + LANE_GAP;
26474
- const rowGapA = rowGap("above");
26475
- const rowGapB = rowGap("below");
26476
- const ext = (side) => Math.max(
26477
- 0,
26478
- ...placed.filter((p) => p.side === side).map(
26479
- (p) => (side === "above" ? LEADER_ABOVE : LEADER_BELOW) + p.lane * (side === "above" ? rowGapA : rowGapB) + p.cardH
26480
- )
26481
- );
26482
- const dateAbove = placed.some((p) => p.side === "below" && p.date);
26483
- const dateBelow = placed.some((p) => p.side === "above" && p.date);
26484
- const contentAbove = Math.max(ext("above"), dateAbove ? DATE_OFFSET + 10 : 0);
26485
- const contentBelow = Math.max(ext("below"), dateBelow ? DATE_OFFSET + 10 : 0);
26486
- const aboveExt = contentAbove + (hasExpandedEra && eraSide === "above" ? ERA_BLOCK : 0);
26487
- const belowExt = contentBelow + (hasExpandedEra && eraSide === "below" ? ERA_BLOCK : 0);
26488
- const TOP_PAD = 14;
26489
- const BOT_PAD = 14;
26490
- const spineY = topUsed + TOP_PAD + aboveExt;
26491
- const eraBaseY = eraSide === "above" ? spineY - (contentAbove + 14) : spineY + (contentBelow + 14);
26492
- const totalH = Math.max(heightHint, spineY + belowExt + BOT_PAD);
26649
+ const { contentW, totalH, spineY, eraBaseY, laneNear } = layout;
26493
26650
  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);
26494
26651
  svg.append("rect").attr("width", contentW).attr("height", totalH).attr("fill", palette.bg);
26495
26652
  if (showTitle) {
@@ -26517,12 +26674,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26517
26674
  activeGroup
26518
26675
  });
26519
26676
  }
26520
- const xs = placed.map((p) => p.x);
26521
- const x0 = Math.min(...xs);
26522
- const x1 = Math.max(...xs);
26677
+ const x0 = Math.min(...placed.map((p) => p.x - p.spanHalf));
26678
+ const x1 = Math.max(...placed.map((p) => p.x + p.spanHalf));
26523
26679
  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");
26524
26680
  const geo = placed.map((p) => {
26525
- const near = p.side === "above" ? spineY - LEADER_ABOVE - p.lane * rowGapA : spineY + LEADER_BELOW + p.lane * rowGapB;
26681
+ const near = p.side === "above" ? spineY - laneNear(p) : spineY + laneNear(p);
26526
26682
  const top = p.side === "above" ? near - p.cardH : near;
26527
26683
  return { p, near, top };
26528
26684
  });
@@ -26634,7 +26790,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26634
26790
  for (const p of placed) {
26635
26791
  if (p.kind === "era") {
26636
26792
  const col = p.color;
26637
- const half = Math.min(46, 16 + (p.members.length - 1) * 9);
26793
+ const half = Math.max(16, p.spanHalf);
26638
26794
  const spineLeft = x0 - 20;
26639
26795
  const spineRight = x1 + 20;
26640
26796
  const bx0 = Math.max(spineLeft, p.x - half);
@@ -26645,6 +26801,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26645
26801
  "d",
26646
26802
  `M${bx0},${spineY} L${bx0},${barY} L${bx1},${barY} L${bx1},${spineY}`
26647
26803
  ).attr("fill", "none").attr("stroke", col).attr("stroke-width", 1.5).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
26804
+ const breakCx = (bx0 + bx1) / 2;
26805
+ 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");
26806
+ for (const d of axisBreakPaths(breakCx, spineY)) {
26807
+ 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);
26808
+ }
26648
26809
  if (onClickItem) {
26649
26810
  const ln = p.lineNumber;
26650
26811
  eg.style("cursor", "pointer").on("click", () => onClickItem(ln));
@@ -26678,13 +26839,23 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26678
26839
  }
26679
26840
  const clampX = (v) => Math.max(4, Math.min(contentW - 4, v));
26680
26841
  const cap = eraSide === "above" ? 1 : -1;
26681
- for (const r of runs) {
26682
- if (r.collapsed) continue;
26842
+ const drawn = runs.filter((r) => !r.collapsed).map((r) => ({
26843
+ run: r,
26844
+ x0: clampX(r.firstX - ERA_BRACKET_PAD),
26845
+ x1: clampX(r.lastX + ERA_BRACKET_PAD)
26846
+ })).sort((a, b) => a.run.firstX - b.run.firstX);
26847
+ for (let i = 1; i < drawn.length; i++) {
26848
+ const prev = drawn[i - 1];
26849
+ const cur = drawn[i];
26850
+ if (prev.x1 <= cur.x0) continue;
26851
+ const mid = (prev.run.lastX + cur.run.firstX) / 2;
26852
+ prev.x1 = Math.max(prev.run.lastX, Math.min(prev.x1, mid - ERA_SEAM_GAP));
26853
+ cur.x0 = Math.min(cur.run.firstX, Math.max(cur.x0, mid + ERA_SEAM_GAP));
26854
+ }
26855
+ for (const { run: r, x0: x02, x1: x12 } of drawn) {
26683
26856
  const neutral = !r.era.color;
26684
26857
  const col = neutral ? palette.text : resolveColor(r.era.color, palette) ?? palette.text;
26685
26858
  const op = neutral ? 0.5 : 0.85;
26686
- const x02 = clampX(r.firstX - 14);
26687
- const x12 = clampX(r.lastX + 14);
26688
26859
  const y = eraBaseY;
26689
26860
  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);
26690
26861
  eg.append("path").attr(
@@ -26710,6 +26881,22 @@ function renderEventLineForExport(container, parsed, palette, isDark, exportDims
26710
26881
  tagOverride
26711
26882
  );
26712
26883
  }
26884
+ function axisBreakPaths(cx, cy) {
26885
+ const HUMPS = 2;
26886
+ const STEPS = 14;
26887
+ const wave = (x0) => {
26888
+ let d = "";
26889
+ for (let i = 0; i <= STEPS; i++) {
26890
+ const t = i / STEPS;
26891
+ const y = cy - BREAK_HALF_H + t * 2 * BREAK_HALF_H;
26892
+ const x = x0 + BREAK_AMP * Math.sin(t * Math.PI * HUMPS);
26893
+ d += `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`;
26894
+ if (i < STEPS) d += " ";
26895
+ }
26896
+ return d;
26897
+ };
26898
+ return [wave(cx - BREAK_GAP / 2), wave(cx + BREAK_GAP / 2)];
26899
+ }
26713
26900
  function wrapDescription(lines, charsPerLine) {
26714
26901
  const out = [];
26715
26902
  for (const line11 of lines) {
@@ -26739,7 +26926,7 @@ function renderBody(cardG, lines, bodyColor, palette, startBaseline = CARD_BODY_
26739
26926
  y += DESC_LINE_H;
26740
26927
  }
26741
26928
  }
26742
- var 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;
26929
+ var 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;
26743
26930
  var init_renderer = __esm({
26744
26931
  "src/event-line/renderer.ts"() {
26745
26932
  "use strict";
@@ -26773,9 +26960,13 @@ var init_renderer = __esm({
26773
26960
  DATE_OFFSET = 28;
26774
26961
  ERA_BLOCK = 30;
26775
26962
  ERA_BRACKET_CAP = 8;
26963
+ ERA_BRACKET_PAD = 14;
26964
+ ERA_SEAM_GAP = 3;
26965
+ BREAK_HALF_H = 6;
26966
+ BREAK_AMP = 2.2;
26967
+ BREAK_GAP = 5;
26776
26968
  ERA_LEG = 13;
26777
26969
  ERA_LABEL_FONT = 11.5;
26778
- ERA_MEMBER_MAX = 6;
26779
26970
  }
26780
26971
  });
26781
26972
 
@@ -52935,6 +53126,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
52935
53126
  y,
52936
53127
  score,
52937
53128
  palette,
53129
+ isDark,
52938
53130
  SCORE_LABEL_R
52939
53131
  );
52940
53132
  if (!exportDims) {
@@ -52966,7 +53158,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
52966
53158
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
52967
53159
  for (const pt of layout.curvePoints) {
52968
53160
  const step = allSteps[pt.stepIndex];
52969
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53161
+ const faceG = renderScoreFace(
53162
+ curveG,
53163
+ pt.x,
53164
+ pt.y,
53165
+ pt.score,
53166
+ palette,
53167
+ isDark
53168
+ );
52970
53169
  addEmotionLabel(faceG, pt, palette);
52971
53170
  if (step) {
52972
53171
  faceG.attr("data-line-number", step.lineNumber);
@@ -52992,7 +53191,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
52992
53191
  const pt = layout.curvePoints[0];
52993
53192
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
52994
53193
  const step = allSteps[pt.stepIndex];
52995
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53194
+ const faceG = renderScoreFace(
53195
+ curveG,
53196
+ pt.x,
53197
+ pt.y,
53198
+ pt.score,
53199
+ palette,
53200
+ isDark
53201
+ );
52996
53202
  addEmotionLabel(faceG, pt, palette);
52997
53203
  if (step) {
52998
53204
  faceG.attr("data-line-number", step.lineNumber);
@@ -53089,6 +53295,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53089
53295
  faceCy,
53090
53296
  step.score,
53091
53297
  palette,
53298
+ isDark,
53092
53299
  COLLAPSED_FACE_R
53093
53300
  );
53094
53301
  faceG.attr("data-line-number", step.lineNumber);
@@ -53192,7 +53399,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53192
53399
  const fcy = parseFloat(el.attr("data-cy") ?? "0");
53193
53400
  el.attr(
53194
53401
  "transform",
53195
- `translate(${fcx},${fcy}) scale(1.5) translate(${-fcx},${-fcy})`
53402
+ `translate(${fcx},${fcy}) scale(${FACE_HOVER_SCALE}) translate(${-fcx},${-fcy})`
53196
53403
  );
53197
53404
  } else {
53198
53405
  el.attr("transform", null);
@@ -53238,12 +53445,12 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53238
53445
  PADDING2,
53239
53446
  Math.min(fcx - bw / 2, layout.totalWidth - PADDING2 - bw)
53240
53447
  );
53241
- const by = Math.max(PADDING2, fcy - FACE_RADIUS - THOUGHT_GAP - bh);
53448
+ const by = Math.max(PADDING2, fcy - FACE_HOVER_R - THOUGHT_GAP - bh);
53242
53449
  const scoreColor = scoreToColor(score, palette);
53243
53450
  const tintedBg = mix(scoreColor, palette.surface, 20);
53244
53451
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
53245
53452
  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);
53246
- 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);
53453
+ 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);
53247
53454
  const centerX = bx + bw / 2;
53248
53455
  for (let i = 0; i < lines.length; i++) {
53249
53456
  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]);
@@ -53486,24 +53693,23 @@ function renderPersonaSilhouette(parent, cx, cy, color, palette, scale = 1) {
53486
53693
  `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`
53487
53694
  ).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", 1.2);
53488
53695
  }
53489
- function renderScoreFace(parent, cx, cy, score, palette, radius) {
53696
+ function renderScoreFace(parent, cx, cy, score, palette, isDark, radius) {
53490
53697
  const r = radius ?? FACE_RADIUS;
53491
53698
  const color = scoreToColor(score, palette);
53492
53699
  const g = parent.append("g").attr("class", "journey-face").attr("data-cx", cx).attr("data-cy", cy);
53493
- g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", color).attr("stroke", palette.bg).attr("stroke-width", 1.5);
53494
- const eyeColor = contrastText(
53495
- color,
53496
- palette.textOnFillLight,
53497
- palette.textOnFillDark
53498
- );
53700
+ const faceFill = shapeFill(palette, color, isDark);
53701
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r + 1).attr("fill", palette.bg);
53702
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", faceFill).attr("stroke", color).attr("stroke-width", 2);
53703
+ const eyeColor = color;
53499
53704
  const eyeY = cy - r * 0.15;
53500
53705
  const eyeSpacing = r * 0.32;
53501
53706
  const eyeR = r * 0.12;
53502
53707
  g.append("circle").attr("cx", cx - eyeSpacing).attr("cy", eyeY).attr("r", eyeR).attr("fill", eyeColor);
53503
53708
  g.append("circle").attr("cx", cx + eyeSpacing).attr("cy", eyeY).attr("r", eyeR).attr("fill", eyeColor);
53504
- const mouthY = cy + r * 0.25;
53505
- const mouthW = r * 0.45;
53506
- const curve = (score - 3) / 2 * r * 0.35;
53709
+ const mouthW = r * 0.46;
53710
+ const curve = (score - 3) / 2 * r * 0.42;
53711
+ const apexY = cy + r * 0.32;
53712
+ const mouthY = apexY - curve / 2;
53507
53713
  g.append("path").attr(
53508
53714
  "d",
53509
53715
  `M ${cx - mouthW} ${mouthY} Q ${cx} ${mouthY + curve} ${cx + mouthW} ${mouthY}`
@@ -53574,7 +53780,7 @@ function renderJourneyMapForExport(content, theme, palette) {
53574
53780
  }
53575
53781
  return svgEl.outerHTML;
53576
53782
  }
53577
- var 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;
53783
+ var 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;
53578
53784
  var init_renderer17 = __esm({
53579
53785
  "src/journey-map/renderer.ts"() {
53580
53786
  "use strict";
@@ -53608,6 +53814,8 @@ var init_renderer17 = __esm({
53608
53814
  GRID_LINE_OPACITY = 0.15;
53609
53815
  CURVE_STROKE_WIDTH = 2.5;
53610
53816
  FACE_RADIUS = 14;
53817
+ FACE_HOVER_SCALE = 1.5;
53818
+ FACE_HOVER_R = (FACE_RADIUS + 1) * FACE_HOVER_SCALE;
53611
53819
  DIM_HOVER = 0.25;
53612
53820
  TITLE_LINE_HEIGHT = 16;
53613
53821
  EMOTION_LABEL_MAX_WIDTH = 180;
@@ -69036,7 +69244,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
69036
69244
 
69037
69245
  // src/auto/index.ts
69038
69246
  init_safe_href();
69039
- var VERSION = "0.39.0";
69247
+ var VERSION = "0.40.0";
69040
69248
  var DEFAULTS = {
69041
69249
  theme: "auto",
69042
69250
  palette: "slate",