@diagrammo/dgmo 0.39.0 → 0.41.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",
@@ -15145,6 +15148,8 @@ function parseEventLine(content, palette) {
15145
15148
  let currentTagGroup = null;
15146
15149
  let currentEvent = null;
15147
15150
  let currentEra = null;
15151
+ let eraIndent = -1;
15152
+ let currentEventIndent = 0;
15148
15153
  const aliasMap = /* @__PURE__ */ new Map();
15149
15154
  for (let i = 0; i < lines.length; i++) {
15150
15155
  const line11 = lines[i];
@@ -15219,52 +15224,15 @@ function parseEventLine(content, palette) {
15219
15224
  });
15220
15225
  continue;
15221
15226
  }
15222
- if (indent > 0) {
15223
- if (!currentEvent) {
15224
- pushWarning(
15225
- lineNumber,
15226
- `Indented description "${trimmed}" has no event above it \u2014 add an event line first. (\xA728)`
15227
- );
15228
- continue;
15229
- }
15227
+ if (currentEvent && indent > currentEventIndent) {
15230
15228
  const descLine = trimmed.startsWith("- ") ? `\u2022 ${trimmed.substring(2)}` : trimmed;
15231
15229
  currentEvent.description.push(descLine);
15232
15230
  continue;
15233
15231
  }
15234
- currentTagGroup = null;
15235
- if (trimmed.toLowerCase() === "no-scale") {
15236
- options.scale = false;
15237
- continue;
15238
- }
15239
- const sideMatch = trimmed.match(SIDE_RE);
15240
- if (sideMatch) {
15241
- options.side = sideMatch[1].toLowerCase();
15242
- continue;
15243
- }
15244
- if (trimmed.toLowerCase() === "no-box") {
15245
- options.noBox = true;
15246
- continue;
15247
- }
15248
- if (trimmed.toLowerCase() === "no-legend") {
15249
- options.noLegend = true;
15250
- continue;
15251
- }
15252
- const dirMatch = trimmed.match(DIRECTION_RE);
15253
- if (dirMatch) {
15254
- const dir = dirMatch[1].toUpperCase();
15255
- if (dir !== "LR") {
15256
- pushWarning(
15257
- lineNumber,
15258
- `event-line is horizontal-only in v1; \`direction ${dir}\` (vertical orientation) is a fast-follow.`,
15259
- EVENT_LINE_DIAGNOSTIC_CODES.UNSUPPORTED
15260
- );
15261
- }
15262
- continue;
15263
- }
15264
- if (tryParseSharedOption(trimmed, sharedOptions)) continue;
15265
15232
  const eraMatch = trimmed.match(ERA_RE);
15266
15233
  if (eraMatch) {
15267
15234
  contentStarted = true;
15235
+ currentTagGroup = null;
15268
15236
  currentEvent = null;
15269
15237
  const name = eraMatch[1].trim();
15270
15238
  let rest = (eraMatch[2] ?? "").trim();
@@ -15288,16 +15256,52 @@ function parseEventLine(content, palette) {
15288
15256
  const era = { name, color, collapsed, lineNumber };
15289
15257
  result.eras.push(era);
15290
15258
  currentEra = name;
15259
+ eraIndent = indent;
15291
15260
  continue;
15292
15261
  }
15293
- if (SECTION_SEAM_RE.test(trimmed)) {
15294
- pushWarning(
15295
- lineNumber,
15296
- "Group events with `[Name]` era brackets (\xA728.6a), not `section`.",
15297
- EVENT_LINE_DIAGNOSTIC_CODES.UNSUPPORTED
15298
- );
15299
- continue;
15262
+ if (indent === 0) {
15263
+ currentTagGroup = null;
15264
+ if (trimmed.toLowerCase() === "no-scale") {
15265
+ options.scale = false;
15266
+ continue;
15267
+ }
15268
+ const sideMatch = trimmed.match(SIDE_RE);
15269
+ if (sideMatch) {
15270
+ options.side = sideMatch[1].toLowerCase();
15271
+ continue;
15272
+ }
15273
+ if (trimmed.toLowerCase() === "no-box") {
15274
+ options.noBox = true;
15275
+ continue;
15276
+ }
15277
+ if (trimmed.toLowerCase() === "no-legend") {
15278
+ options.noLegend = true;
15279
+ continue;
15280
+ }
15281
+ const dirMatch = trimmed.match(DIRECTION_RE);
15282
+ if (dirMatch) {
15283
+ const dir = dirMatch[1].toUpperCase();
15284
+ if (dir !== "LR") {
15285
+ pushWarning(
15286
+ lineNumber,
15287
+ `event-line is horizontal-only in v1; \`direction ${dir}\` (vertical orientation) is a fast-follow.`,
15288
+ EVENT_LINE_DIAGNOSTIC_CODES.UNSUPPORTED
15289
+ );
15290
+ }
15291
+ continue;
15292
+ }
15293
+ if (tryParseSharedOption(trimmed, sharedOptions)) continue;
15294
+ if (SECTION_SEAM_RE.test(trimmed)) {
15295
+ pushWarning(
15296
+ lineNumber,
15297
+ "Group events with `[Name]` era brackets (\xA728.6a), not `section`.",
15298
+ EVENT_LINE_DIAGNOSTIC_CODES.UNSUPPORTED
15299
+ );
15300
+ continue;
15301
+ }
15300
15302
  }
15303
+ const inEra = currentEra !== null && indent > eraIndent;
15304
+ if (!inEra) currentEra = null;
15301
15305
  contentStarted = true;
15302
15306
  const event = parseEventHeader(
15303
15307
  trimmed,
@@ -15309,6 +15313,7 @@ function parseEventLine(content, palette) {
15309
15313
  );
15310
15314
  result.events.push(event);
15311
15315
  currentEvent = event;
15316
+ currentEventIndent = indent;
15312
15317
  }
15313
15318
  options.noTitle = sharedOptions["no-title"] === "on";
15314
15319
  finalizeAutoTagColors(result.tagGroups, palette);
@@ -15323,8 +15328,61 @@ function parseEventLine(content, palette) {
15323
15328
  EVENT_LINE_DIAGNOSTIC_CODES.NO_EVENTS
15324
15329
  );
15325
15330
  }
15331
+ if (options.scale) {
15332
+ validateEraDateOrder(result.events, result.eras, pushWarning);
15333
+ }
15326
15334
  return result;
15327
15335
  }
15336
+ function validateEraDateOrder(events, eras, pushWarning) {
15337
+ if (eras.length < 2) return;
15338
+ const order = new Map(eras.map((e, i) => [e.name, i]));
15339
+ const dated = events.filter(
15340
+ (e) => e.dateValue !== null && e.era !== null && order.has(e.era)
15341
+ );
15342
+ if (dated.length === 0) return;
15343
+ const perEra = /* @__PURE__ */ new Map();
15344
+ for (const ev of dated) {
15345
+ const idx = order.get(ev.era);
15346
+ const cur = perEra.get(idx);
15347
+ if (!cur) {
15348
+ perEra.set(idx, { min: ev, max: ev });
15349
+ } else {
15350
+ if (ev.dateValue < cur.min.dateValue) cur.min = ev;
15351
+ if (ev.dateValue > cur.max.dateValue) cur.max = ev;
15352
+ }
15353
+ }
15354
+ const n = eras.length;
15355
+ const prefixMax = new Array(n).fill(null);
15356
+ const suffixMin = new Array(n).fill(null);
15357
+ for (let k = 1; k < n; k++) {
15358
+ const prev = perEra.get(k - 1)?.max ?? null;
15359
+ const carried = prefixMax[k - 1] ?? null;
15360
+ prefixMax[k] = prev && (!carried || prev.dateValue > carried.dateValue) ? prev : carried;
15361
+ }
15362
+ for (let k = n - 2; k >= 0; k--) {
15363
+ const next = perEra.get(k + 1)?.min ?? null;
15364
+ const carried = suffixMin[k + 1] ?? null;
15365
+ suffixMin[k] = next && (!carried || next.dateValue < carried.dateValue) ? next : carried;
15366
+ }
15367
+ for (const ev of dated) {
15368
+ const k = order.get(ev.era);
15369
+ const ahead = suffixMin[k];
15370
+ const behind = prefixMax[k];
15371
+ if (ahead && ev.dateValue > ahead.dateValue) {
15372
+ pushWarning(
15373
+ ev.lineNumber,
15374
+ `"${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.`,
15375
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15376
+ );
15377
+ } else if (behind && ev.dateValue < behind.dateValue) {
15378
+ pushWarning(
15379
+ ev.lineNumber,
15380
+ `"${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.`,
15381
+ EVENT_LINE_DIAGNOSTIC_CODES.ERA_DATE_ORDER
15382
+ );
15383
+ }
15384
+ }
15385
+ }
15328
15386
  function parseEventHeader(trimmed, lineNumber, era, aliasMap, diagnostics, pushWarning) {
15329
15387
  let date = null;
15330
15388
  let dateValue = null;
@@ -15391,7 +15449,8 @@ var init_parser13 = __esm({
15391
15449
  EVENT_LINE_DIAGNOSTIC_CODES = {
15392
15450
  NO_EVENTS: "E_EVENT_LINE_NO_EVENTS",
15393
15451
  BAD_DATE: "E_EVENT_LINE_BAD_DATE",
15394
- UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED"
15452
+ UNSUPPORTED: "E_EVENT_LINE_UNSUPPORTED",
15453
+ ERA_DATE_ORDER: "E_EVENT_LINE_ERA_DATE_ORDER"
15395
15454
  };
15396
15455
  NON_ISO_DATE_RE = /^\d{1,4}[/.]\d/;
15397
15456
  SIDE_RE = /^side\s+(above|below|alternate)\b/i;
@@ -26327,9 +26386,6 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26327
26386
  const hasExpandedEra = parsed.events.some(
26328
26387
  (e) => e.era && eraByName.has(e.era) && !collapsedSet.has(e.era)
26329
26388
  );
26330
- const anyCollapsed = parsed.events.some(
26331
- (e) => e.era && collapsedSet.has(e.era)
26332
- );
26333
26389
  const sideOpt = parsed.options.side;
26334
26390
  const alternate = sideOpt === "alternate";
26335
26391
  const eraSide = alternate ? "below" : sideOpt === "above" ? "below" : "above";
@@ -26396,7 +26452,8 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26396
26452
  x: 0,
26397
26453
  side,
26398
26454
  lane: 0,
26399
- left: 0
26455
+ left: 0,
26456
+ spanHalf: 0
26400
26457
  };
26401
26458
  }
26402
26459
  const era = slot.era;
@@ -26409,19 +26466,19 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26409
26466
  palette.textOnFillLight,
26410
26467
  palette.textOnFillDark
26411
26468
  );
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(
26469
+ let spanLo = null;
26470
+ let spanHi = null;
26471
+ for (const m of slot.members) {
26472
+ if (m.dateValue === null) continue;
26473
+ if (spanLo === null || m.dateValue < spanLo) spanLo = m.dateValue;
26474
+ if (spanHi === null || m.dateValue > spanHi) spanHi = m.dateValue;
26475
+ }
26476
+ const repDateValue = spanLo !== null && spanHi !== null ? (spanLo + spanHi) / 2 : null;
26477
+ const memberStrs = slot.members.map(
26415
26478
  (m) => `\u2022 ${m.date ? `${m.date} ` : ""}${m.label}`
26416
26479
  );
26417
- const bulletColors = shown.map(eventColor);
26480
+ const bulletColors = slot.members.map(eventColor);
26418
26481
  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
26482
  return {
26426
26483
  kind: "era",
26427
26484
  event: null,
@@ -26429,7 +26486,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26429
26486
  members: slot.members,
26430
26487
  label: era.name,
26431
26488
  date: null,
26432
- dateValue: null,
26489
+ dateValue: repDateValue,
26433
26490
  lineNumber: era.lineNumber,
26434
26491
  eraName: era.name,
26435
26492
  color: solid,
@@ -26441,87 +26498,189 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26441
26498
  x: 0,
26442
26499
  side,
26443
26500
  lane: 0,
26444
- left: 0
26501
+ left: 0,
26502
+ spanHalf: 0
26445
26503
  };
26446
26504
  });
26447
- const scaled = parsed.options.scale && !anyCollapsed && placed.every((p) => p.dateValue !== null);
26505
+ const scaled = parsed.options.scale && placed.every((p) => p.dateValue !== null);
26448
26506
  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);
26507
+ const MIN_DOT_GAP = 16;
26508
+ const STRETCH_CAP = 8;
26509
+ const COLLAPSE_W = 64;
26510
+ const SEG_GAP = 16;
26511
+ const segments = [];
26512
+ let run = [];
26513
+ const flushRun = () => {
26514
+ if (!run.length) return;
26515
+ const ds = run.map((e) => e.dateValue);
26516
+ segments.push({
26517
+ kind: "run",
26518
+ events: run,
26519
+ lo: Math.min(...ds),
26520
+ hi: Math.max(...ds)
26455
26521
  });
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;
26522
+ run = [];
26523
+ };
26524
+ for (const p of placed) {
26525
+ if (p.kind === "era") {
26526
+ flushRun();
26527
+ segments.push({ kind: "capsule", p });
26528
+ } else {
26529
+ run.push(p);
26530
+ }
26531
+ }
26532
+ flushRun();
26533
+ const totalRunTime = segments.reduce(
26534
+ (s, seg) => s + (seg.kind === "run" ? seg.hi - seg.lo : 0),
26535
+ 0
26536
+ );
26537
+ const place = (stretch) => {
26538
+ if (scaled) {
26539
+ const pxPerUnit = totalRunTime > 0 ? innerW * stretch / totalRunTime : 0;
26540
+ let cursor = H_MARGIN;
26541
+ for (const seg of segments) {
26542
+ if (seg.kind === "capsule") {
26543
+ seg.p.x = cursor + COLLAPSE_W / 2;
26544
+ seg.p.spanHalf = COLLAPSE_W / 2;
26545
+ cursor += COLLAPSE_W + SEG_GAP;
26546
+ } else {
26547
+ for (const e of seg.events) {
26548
+ e.x = cursor + (e.dateValue - seg.lo) * pxPerUnit;
26549
+ e.spanHalf = 0;
26550
+ }
26551
+ cursor += (seg.hi - seg.lo) * pxPerUnit + SEG_GAP;
26552
+ }
26553
+ }
26554
+ let prevX = -Infinity;
26555
+ let prevVal = null;
26556
+ for (const p of [...placed].sort((a, b) => a.x - b.x)) {
26557
+ if (p.kind === "era") {
26558
+ prevX = p.x + p.spanHalf;
26559
+ prevVal = null;
26560
+ continue;
26561
+ }
26562
+ if (p.dateValue === prevVal) {
26563
+ p.x = prevX;
26564
+ continue;
26565
+ }
26566
+ if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26567
+ prevX = p.x;
26568
+ prevVal = p.dateValue;
26569
+ }
26570
+ } else {
26571
+ placed.forEach((p) => {
26572
+ p.spanHalf = 0;
26573
+ });
26574
+ const n = placed.length;
26575
+ const spacing = n > 1 ? Math.max(MIN_SPACING, innerW / (n - 1)) : 0;
26576
+ placed.forEach((p, i) => {
26577
+ p.x = H_MARGIN + i * spacing;
26578
+ });
26579
+ }
26580
+ for (const side of ["above", "below"]) {
26581
+ const arr = placed.filter((p) => p.side === side).sort((a, b) => a.x - b.x);
26582
+ const laneRight = [];
26583
+ for (let i = 0; i < arr.length; i++) {
26584
+ const p = arr[i];
26585
+ const next = arr[i + 1];
26586
+ const crowdedRight = !!next && next.x - p.x < CARD_W + FAN_GAP;
26587
+ const preferred = crowdedRight ? p.x - CARD_W + CARD_INSET : p.x - CARD_W / 2;
26588
+ const maxLeft = p.x - CARD_INSET;
26589
+ const minLeft = p.x - CARD_W + CARD_INSET;
26590
+ let lane = laneRight.length;
26591
+ let left = Math.max(preferred, minLeft);
26592
+ for (let l = 0; l < laneRight.length; l++) {
26593
+ const want = Math.max(preferred, laneRight[l] + FAN_GAP, minLeft);
26594
+ if (want <= maxLeft) {
26595
+ lane = l;
26596
+ left = want;
26597
+ break;
26598
+ }
26599
+ }
26600
+ p.lane = lane;
26601
+ p.left = Math.max(6, left);
26602
+ laneRight[lane] = p.left + CARD_W;
26463
26603
  }
26464
- if (p.x < prevX + MIN_DOT_GAP) p.x = prevX + MIN_DOT_GAP;
26465
- prevX = p.x;
26466
- prevVal = p.dateValue;
26467
26604
  }
26605
+ };
26606
+ const derive = () => {
26607
+ const contentW2 = Math.max(
26608
+ width,
26609
+ Math.max(...placed.map((p) => p.x + p.spanHalf)) + H_MARGIN,
26610
+ Math.max(...placed.map((p) => p.left + CARD_W)) + 6
26611
+ );
26612
+ const laneOffset = { above: [], below: [] };
26613
+ for (const side of ["above", "below"]) {
26614
+ const laneH = [];
26615
+ for (const p of placed) {
26616
+ if (p.side !== side) continue;
26617
+ laneH[p.lane] = Math.max(laneH[p.lane] ?? 0, p.cardH);
26618
+ }
26619
+ const off = [];
26620
+ let acc = 0;
26621
+ for (let l = 0; l < laneH.length; l++) {
26622
+ off[l] = acc;
26623
+ acc += (laneH[l] ?? 0) + LANE_GAP;
26624
+ }
26625
+ laneOffset[side] = off;
26626
+ }
26627
+ const laneNear2 = (p) => (p.side === "above" ? LEADER_ABOVE : LEADER_BELOW) + (laneOffset[p.side][p.lane] ?? 0);
26628
+ const ext = (side) => Math.max(
26629
+ 0,
26630
+ ...placed.filter((p) => p.side === side).map((p) => laneNear2(p) + p.cardH)
26631
+ );
26632
+ const dateAbove = placed.some((p) => p.side === "below" && p.date);
26633
+ const dateBelow = placed.some((p) => p.side === "above" && p.date);
26634
+ const contentAbove = Math.max(
26635
+ ext("above"),
26636
+ dateAbove ? DATE_OFFSET + 10 : 0
26637
+ );
26638
+ const contentBelow = Math.max(
26639
+ ext("below"),
26640
+ dateBelow ? DATE_OFFSET + 10 : 0
26641
+ );
26642
+ const aboveExt = contentAbove + (hasExpandedEra && eraSide === "above" ? ERA_BLOCK : 0);
26643
+ const belowExt = contentBelow + (hasExpandedEra && eraSide === "below" ? ERA_BLOCK : 0);
26644
+ const TOP_PAD = 14;
26645
+ const BOT_PAD = 14;
26646
+ const spineY2 = topUsed + TOP_PAD + aboveExt;
26647
+ const eraBaseY2 = eraSide === "above" ? spineY2 - (contentAbove + 14) : spineY2 + (contentBelow + 14);
26648
+ const contentH = spineY2 + belowExt + BOT_PAD;
26649
+ const totalH2 = Math.max(heightHint, contentH);
26650
+ return { contentW: contentW2, contentH, totalH: totalH2, spineY: spineY2, eraBaseY: eraBaseY2, laneNear: laneNear2 };
26651
+ };
26652
+ const envStretch = Number(
26653
+ globalThis.process?.env?.["DGMO_EVT_STRETCH"]
26654
+ );
26655
+ let layout;
26656
+ if (Number.isFinite(envStretch) && envStretch > 0) {
26657
+ place(envStretch);
26658
+ layout = derive();
26468
26659
  } 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;
26660
+ place(1);
26661
+ layout = derive();
26662
+ const panelAspect = width / heightHint;
26663
+ const aspectOf = (l) => l.contentW / l.contentH;
26664
+ if (scaled && layout.contentH > heightHint && aspectOf(layout) < panelAspect) {
26665
+ place(STRETCH_CAP);
26666
+ const capped = derive();
26667
+ if (aspectOf(capped) <= panelAspect) {
26668
+ layout = capped;
26669
+ } else {
26670
+ let loF = 1;
26671
+ let hiF = STRETCH_CAP;
26672
+ for (let it = 0; it < 8; it++) {
26673
+ const mid = (loF + hiF) / 2;
26674
+ place(mid);
26675
+ if (aspectOf(derive()) >= panelAspect) hiF = mid;
26676
+ else loF = mid;
26493
26677
  }
26678
+ place(hiF);
26679
+ layout = derive();
26494
26680
  }
26495
- p.lane = lane;
26496
- p.left = Math.max(6, left);
26497
- laneRight[lane] = p.left + CARD_W;
26498
26681
  }
26499
26682
  }
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);
26683
+ const { contentW, totalH, spineY, eraBaseY, laneNear } = layout;
26525
26684
  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
26685
  svg.append("rect").attr("width", contentW).attr("height", totalH).attr("fill", palette.bg);
26527
26686
  if (showTitle) {
@@ -26549,12 +26708,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26549
26708
  activeGroup
26550
26709
  });
26551
26710
  }
26552
- const xs = placed.map((p) => p.x);
26553
- const x0 = Math.min(...xs);
26554
- const x1 = Math.max(...xs);
26711
+ const x0 = Math.min(...placed.map((p) => p.x - p.spanHalf));
26712
+ const x1 = Math.max(...placed.map((p) => p.x + p.spanHalf));
26555
26713
  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
26714
  const geo = placed.map((p) => {
26557
- const near = p.side === "above" ? spineY - LEADER_ABOVE - p.lane * rowGapA : spineY + LEADER_BELOW + p.lane * rowGapB;
26715
+ const near = p.side === "above" ? spineY - laneNear(p) : spineY + laneNear(p);
26558
26716
  const top = p.side === "above" ? near - p.cardH : near;
26559
26717
  return { p, near, top };
26560
26718
  });
@@ -26666,7 +26824,7 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26666
26824
  for (const p of placed) {
26667
26825
  if (p.kind === "era") {
26668
26826
  const col = p.color;
26669
- const half = Math.min(46, 16 + (p.members.length - 1) * 9);
26827
+ const half = Math.max(16, p.spanHalf);
26670
26828
  const spineLeft = x0 - 20;
26671
26829
  const spineRight = x1 + 20;
26672
26830
  const bx0 = Math.max(spineLeft, p.x - half);
@@ -26677,6 +26835,11 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26677
26835
  "d",
26678
26836
  `M${bx0},${spineY} L${bx0},${barY} L${bx1},${barY} L${bx1},${spineY}`
26679
26837
  ).attr("fill", "none").attr("stroke", col).attr("stroke-width", 1.5).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
26838
+ const breakCx = (bx0 + bx1) / 2;
26839
+ 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");
26840
+ for (const d of axisBreakPaths(breakCx, spineY)) {
26841
+ 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);
26842
+ }
26680
26843
  if (onClickItem) {
26681
26844
  const ln = p.lineNumber;
26682
26845
  eg.style("cursor", "pointer").on("click", () => onClickItem(ln));
@@ -26710,13 +26873,23 @@ function renderEventLine(container, parsed, palette, isDark, onClickItem, export
26710
26873
  }
26711
26874
  const clampX = (v) => Math.max(4, Math.min(contentW - 4, v));
26712
26875
  const cap = eraSide === "above" ? 1 : -1;
26713
- for (const r of runs) {
26714
- if (r.collapsed) continue;
26876
+ const drawn = runs.filter((r) => !r.collapsed).map((r) => ({
26877
+ run: r,
26878
+ x0: clampX(r.firstX - ERA_BRACKET_PAD),
26879
+ x1: clampX(r.lastX + ERA_BRACKET_PAD)
26880
+ })).sort((a, b) => a.run.firstX - b.run.firstX);
26881
+ for (let i = 1; i < drawn.length; i++) {
26882
+ const prev = drawn[i - 1];
26883
+ const cur = drawn[i];
26884
+ if (prev.x1 <= cur.x0) continue;
26885
+ const mid = (prev.run.lastX + cur.run.firstX) / 2;
26886
+ prev.x1 = Math.max(prev.run.lastX, Math.min(prev.x1, mid - ERA_SEAM_GAP));
26887
+ cur.x0 = Math.min(cur.run.firstX, Math.max(cur.x0, mid + ERA_SEAM_GAP));
26888
+ }
26889
+ for (const { run: r, x0: x02, x1: x12 } of drawn) {
26715
26890
  const neutral = !r.era.color;
26716
26891
  const col = neutral ? palette.text : resolveColor(r.era.color, palette) ?? palette.text;
26717
26892
  const op = neutral ? 0.5 : 0.85;
26718
- const x02 = clampX(r.firstX - 14);
26719
- const x12 = clampX(r.lastX + 14);
26720
26893
  const y = eraBaseY;
26721
26894
  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
26895
  eg.append("path").attr(
@@ -26742,6 +26915,22 @@ function renderEventLineForExport(container, parsed, palette, isDark, exportDims
26742
26915
  tagOverride
26743
26916
  );
26744
26917
  }
26918
+ function axisBreakPaths(cx, cy) {
26919
+ const HUMPS = 2;
26920
+ const STEPS = 14;
26921
+ const wave = (x0) => {
26922
+ let d = "";
26923
+ for (let i = 0; i <= STEPS; i++) {
26924
+ const t = i / STEPS;
26925
+ const y = cy - BREAK_HALF_H + t * 2 * BREAK_HALF_H;
26926
+ const x = x0 + BREAK_AMP * Math.sin(t * Math.PI * HUMPS);
26927
+ d += `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`;
26928
+ if (i < STEPS) d += " ";
26929
+ }
26930
+ return d;
26931
+ };
26932
+ return [wave(cx - BREAK_GAP / 2), wave(cx + BREAK_GAP / 2)];
26933
+ }
26745
26934
  function wrapDescription(lines, charsPerLine) {
26746
26935
  const out = [];
26747
26936
  for (const line11 of lines) {
@@ -26771,7 +26960,7 @@ function renderBody(cardG, lines, bodyColor, palette, startBaseline = CARD_BODY_
26771
26960
  y += DESC_LINE_H;
26772
26961
  }
26773
26962
  }
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;
26963
+ 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
26964
  var init_renderer = __esm({
26776
26965
  "src/event-line/renderer.ts"() {
26777
26966
  "use strict";
@@ -26806,9 +26995,13 @@ var init_renderer = __esm({
26806
26995
  DATE_OFFSET = 28;
26807
26996
  ERA_BLOCK = 30;
26808
26997
  ERA_BRACKET_CAP = 8;
26998
+ ERA_BRACKET_PAD = 14;
26999
+ ERA_SEAM_GAP = 3;
27000
+ BREAK_HALF_H = 6;
27001
+ BREAK_AMP = 2.2;
27002
+ BREAK_GAP = 5;
26809
27003
  ERA_LEG = 13;
26810
27004
  ERA_LABEL_FONT = 11.5;
26811
- ERA_MEMBER_MAX = 6;
26812
27005
  }
26813
27006
  });
26814
27007
 
@@ -53279,6 +53472,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53279
53472
  y,
53280
53473
  score,
53281
53474
  palette,
53475
+ isDark,
53282
53476
  SCORE_LABEL_R
53283
53477
  );
53284
53478
  if (!exportDims) {
@@ -53310,7 +53504,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53310
53504
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
53311
53505
  for (const pt of layout.curvePoints) {
53312
53506
  const step = allSteps[pt.stepIndex];
53313
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53507
+ const faceG = renderScoreFace(
53508
+ curveG,
53509
+ pt.x,
53510
+ pt.y,
53511
+ pt.score,
53512
+ palette,
53513
+ isDark
53514
+ );
53314
53515
  addEmotionLabel(faceG, pt, palette);
53315
53516
  if (step) {
53316
53517
  faceG.attr("data-line-number", step.lineNumber);
@@ -53336,7 +53537,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53336
53537
  const pt = layout.curvePoints[0];
53337
53538
  const allSteps = parsed.phases.length > 0 ? parsed.phases.flatMap((p) => p.steps) : parsed.steps;
53338
53539
  const step = allSteps[pt.stepIndex];
53339
- const faceG = renderScoreFace(curveG, pt.x, pt.y, pt.score, palette);
53540
+ const faceG = renderScoreFace(
53541
+ curveG,
53542
+ pt.x,
53543
+ pt.y,
53544
+ pt.score,
53545
+ palette,
53546
+ isDark
53547
+ );
53340
53548
  addEmotionLabel(faceG, pt, palette);
53341
53549
  if (step) {
53342
53550
  faceG.attr("data-line-number", step.lineNumber);
@@ -53433,6 +53641,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53433
53641
  faceCy,
53434
53642
  step.score,
53435
53643
  palette,
53644
+ isDark,
53436
53645
  COLLAPSED_FACE_R
53437
53646
  );
53438
53647
  faceG.attr("data-line-number", step.lineNumber);
@@ -53536,7 +53745,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53536
53745
  const fcy = parseFloat(el.attr("data-cy") ?? "0");
53537
53746
  el.attr(
53538
53747
  "transform",
53539
- `translate(${fcx},${fcy}) scale(1.5) translate(${-fcx},${-fcy})`
53748
+ `translate(${fcx},${fcy}) scale(${FACE_HOVER_SCALE}) translate(${-fcx},${-fcy})`
53540
53749
  );
53541
53750
  } else {
53542
53751
  el.attr("transform", null);
@@ -53582,12 +53791,12 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
53582
53791
  PADDING2,
53583
53792
  Math.min(fcx - bw / 2, layout.totalWidth - PADDING2 - bw)
53584
53793
  );
53585
- const by = Math.max(PADDING2, fcy - FACE_RADIUS - THOUGHT_GAP - bh);
53794
+ const by = Math.max(PADDING2, fcy - FACE_HOVER_R - THOUGHT_GAP - bh);
53586
53795
  const scoreColor = scoreToColor(score, palette);
53587
53796
  const tintedBg = mix(scoreColor, palette.surface, 20);
53588
53797
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
53589
53798
  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);
53799
+ 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
53800
  const centerX = bx + bw / 2;
53592
53801
  for (let i = 0; i < lines.length; i++) {
53593
53802
  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 +54039,23 @@ function renderPersonaSilhouette(parent, cx, cy, color, palette, scale = 1) {
53830
54039
  `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
54040
  ).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", 1.2);
53832
54041
  }
53833
- function renderScoreFace(parent, cx, cy, score, palette, radius) {
54042
+ function renderScoreFace(parent, cx, cy, score, palette, isDark, radius) {
53834
54043
  const r = radius ?? FACE_RADIUS;
53835
54044
  const color = scoreToColor(score, palette);
53836
54045
  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
- );
54046
+ const faceFill = shapeFill(palette, color, isDark);
54047
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r + 1).attr("fill", palette.bg);
54048
+ g.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", faceFill).attr("stroke", color).attr("stroke-width", 2);
54049
+ const eyeColor = color;
53843
54050
  const eyeY = cy - r * 0.15;
53844
54051
  const eyeSpacing = r * 0.32;
53845
54052
  const eyeR = r * 0.12;
53846
54053
  g.append("circle").attr("cx", cx - eyeSpacing).attr("cy", eyeY).attr("r", eyeR).attr("fill", eyeColor);
53847
54054
  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;
54055
+ const mouthW = r * 0.46;
54056
+ const curve = (score - 3) / 2 * r * 0.42;
54057
+ const apexY = cy + r * 0.32;
54058
+ const mouthY = apexY - curve / 2;
53851
54059
  g.append("path").attr(
53852
54060
  "d",
53853
54061
  `M ${cx - mouthW} ${mouthY} Q ${cx} ${mouthY + curve} ${cx + mouthW} ${mouthY}`
@@ -53918,7 +54126,7 @@ function renderJourneyMapForExport(content, theme, palette) {
53918
54126
  }
53919
54127
  return svgEl.outerHTML;
53920
54128
  }
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;
54129
+ 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
54130
  var init_renderer17 = __esm({
53923
54131
  "src/journey-map/renderer.ts"() {
53924
54132
  "use strict";
@@ -53954,6 +54162,8 @@ var init_renderer17 = __esm({
53954
54162
  GRID_LINE_OPACITY = 0.15;
53955
54163
  CURVE_STROKE_WIDTH = 2.5;
53956
54164
  FACE_RADIUS = 14;
54165
+ FACE_HOVER_SCALE = 1.5;
54166
+ FACE_HOVER_R = (FACE_RADIUS + 1) * FACE_HOVER_SCALE;
53957
54167
  DIM_HOVER = 0.25;
53958
54168
  TITLE_LINE_HEIGHT = 16;
53959
54169
  EMOTION_LABEL_MAX_WIDTH = 180;