schematex 0.9.5 → 0.9.7

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.
@@ -9258,8 +9258,8 @@ function parseHeaderLine(ln, ast) {
9258
9258
  }
9259
9259
  case "layout": {
9260
9260
  const l = value.toLowerCase();
9261
- if (l !== "network" && l !== "timescaled" && l !== "aoa") {
9262
- throw new PertParseError(`layout must be network, timescaled, or aoa (got '${value}')`, ln.line);
9261
+ if (l !== "network" && l !== "timescaled" && l !== "aoa" && l !== "gantt") {
9262
+ throw new PertParseError(`layout must be network, timescaled, aoa, or gantt (got '${value}')`, ln.line);
9263
9263
  }
9264
9264
  ast.layout = l;
9265
9265
  return true;
@@ -9270,6 +9270,26 @@ function parseHeaderLine(ln, ast) {
9270
9270
  case "show-sentinels":
9271
9271
  ast.showSentinels = /^(true|yes|on)$/i.test(value);
9272
9272
  return true;
9273
+ case "start":
9274
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
9275
+ throw new PertParseError(`start must be a date 'YYYY-MM-DD' (got '${value}')`, ln.line);
9276
+ }
9277
+ ast.start = value;
9278
+ return true;
9279
+ case "calendar": {
9280
+ const c = value.toLowerCase();
9281
+ if (c !== "continuous" && c !== "5day" && c !== "7day") {
9282
+ throw new PertParseError(`calendar must be continuous, 7day, or 5day (got '${value}')`, ln.line);
9283
+ }
9284
+ ast.calendar = c === "5day" ? "5day" : "continuous";
9285
+ return true;
9286
+ }
9287
+ case "today":
9288
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
9289
+ throw new PertParseError(`today must be a date 'YYYY-MM-DD' (got '${value}')`, ln.line);
9290
+ }
9291
+ ast.today = value;
9292
+ return true;
9273
9293
  default:
9274
9294
  return false;
9275
9295
  }
@@ -9334,7 +9354,7 @@ function parseDepRef(raw, unit, lineNo) {
9334
9354
  }
9335
9355
  throw new PertParseError(`cannot parse predecessor reference '${ref}'`, lineNo);
9336
9356
  }
9337
- var KEY_RE = /\b(duration|after|tags|class|lane)\s*:/gi;
9357
+ var KEY_RE = /\b(duration|after|tags|class|lane|progress|done)\s*:/gi;
9338
9358
  function parseTaskLine(ln, ast) {
9339
9359
  const head = ln.text.match(/^task\s+(\S+)\s*(.*)$/i);
9340
9360
  if (!head) {
@@ -9409,6 +9429,13 @@ function parseTaskLine(ln, ast) {
9409
9429
  const tags = values.tags ? values.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
9410
9430
  const className = values.class ? values.class.trim() : void 0;
9411
9431
  const lane = values.lane ? stripQuotes3(values.lane) : void 0;
9432
+ const progressRaw = values.progress ?? values.done;
9433
+ let progress;
9434
+ if (progressRaw !== void 0 && progressRaw !== "") {
9435
+ const pct = progressRaw.trim().replace(/%$/, "");
9436
+ const n = parseNumber(pct, ln.line, "progress");
9437
+ progress = Math.max(0, Math.min(100, n));
9438
+ }
9412
9439
  const task = {
9413
9440
  id,
9414
9441
  label,
@@ -9422,6 +9449,7 @@ function parseTaskLine(ln, ast) {
9422
9449
  if (variance !== void 0) task.variance = variance;
9423
9450
  if (className) task.className = className;
9424
9451
  if (lane) task.lane = lane;
9452
+ if (progress !== void 0) task.progress = progress;
9425
9453
  ast.tasks.push(task);
9426
9454
  }
9427
9455
  function parsePert(src) {
@@ -9432,18 +9460,21 @@ function parsePert(src) {
9432
9460
  layout: "network",
9433
9461
  criticalTolerance: 0,
9434
9462
  showSentinels: false,
9463
+ calendar: "continuous",
9435
9464
  tasks: [],
9436
9465
  warnings: []
9437
9466
  };
9438
9467
  const lines = preprocess8(src);
9439
9468
  if (lines.length === 0) {
9440
- throw new PertParseError("empty document \u2014 expected 'pert' header", 1);
9469
+ throw new PertParseError("empty document \u2014 expected 'pert' or 'gantt' header", 1);
9441
9470
  }
9442
9471
  const first = lines[0];
9443
- if (!/^pert\b/i.test(first.text)) {
9444
- throw new PertParseError(`first non-comment line must start with 'pert' (got: ${first.text})`, first.line);
9472
+ if (!/^(pert|gantt)\b/i.test(first.text)) {
9473
+ throw new PertParseError(`first non-comment line must start with 'pert' or 'gantt' (got: ${first.text})`, first.line);
9445
9474
  }
9446
- const inlineTitle = first.text.replace(/^pert\b/i, "").trim();
9475
+ const isGanttHeader = /^gantt\b/i.test(first.text);
9476
+ if (isGanttHeader) ast.layout = "gantt";
9477
+ const inlineTitle = first.text.replace(/^(pert|gantt)\b/i, "").trim();
9447
9478
  if (inlineTitle) {
9448
9479
  const m = inlineTitle.match(/^"([^"]+)"$/) || inlineTitle.match(/^'([^']+)'$/);
9449
9480
  if (m) ast.title = m[1];
@@ -10523,10 +10554,381 @@ function layoutPert(ast, schedule) {
10523
10554
  return layoutNetwork(ast, sched);
10524
10555
  }
10525
10556
 
10526
- // src/diagrams/pert/renderer.ts
10557
+ // src/diagrams/pert/gantt.ts
10558
+ var PALETTES = {
10559
+ default: {
10560
+ bar: "#cfe0f5",
10561
+ barStroke: "#5b85c0",
10562
+ barDone: "#5b85c0",
10563
+ critBar: "#fbe6e0",
10564
+ critStroke: "#d2604f",
10565
+ critDone: "#d2604f",
10566
+ milestone: "#3c5a80",
10567
+ text: "#234567",
10568
+ subtext: "#728198",
10569
+ axis: "#8497ad",
10570
+ grid: "#e3eaf3",
10571
+ sectionBand: "#eef3fa",
10572
+ sectionText: "#41597a",
10573
+ dep: "#9fb0c6",
10574
+ today: "#d2604f"
10575
+ },
10576
+ monochrome: {
10577
+ bar: "#ffffff",
10578
+ barStroke: "#000000",
10579
+ barDone: "#9a9a9a",
10580
+ critBar: "#ffffff",
10581
+ critStroke: "#000000",
10582
+ critDone: "#000000",
10583
+ milestone: "#000000",
10584
+ text: "#000000",
10585
+ subtext: "#555555",
10586
+ axis: "#000000",
10587
+ grid: "#dddddd",
10588
+ sectionBand: "#f2f2f2",
10589
+ sectionText: "#000000",
10590
+ dep: "#888888",
10591
+ today: "#000000"
10592
+ },
10593
+ dark: {
10594
+ bar: "#37506e",
10595
+ barStroke: "#89b4fa",
10596
+ barDone: "#89b4fa",
10597
+ critBar: "#5a3540",
10598
+ critStroke: "#f38ba8",
10599
+ critDone: "#f38ba8",
10600
+ milestone: "#bac2de",
10601
+ text: "#cdd6f4",
10602
+ subtext: "#9399b2",
10603
+ axis: "#7f849c",
10604
+ grid: "#313244",
10605
+ sectionBand: "#272838",
10606
+ sectionText: "#bac2de",
10607
+ dep: "#6c7086",
10608
+ today: "#f38ba8"
10609
+ }
10610
+ };
10611
+ function ymdToDays(y, m, d) {
10612
+ const yy = m <= 2 ? y - 1 : y;
10613
+ const era = Math.floor((yy >= 0 ? yy : yy - 399) / 400);
10614
+ const yoe = yy - era * 400;
10615
+ const doy = Math.floor((153 * (m > 2 ? m - 3 : m + 9) + 2) / 5) + d - 1;
10616
+ const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
10617
+ return era * 146097 + doe - 719468;
10618
+ }
10619
+ function daysToYmd(z) {
10620
+ const zz = z + 719468;
10621
+ const era = Math.floor((zz >= 0 ? zz : zz - 146096) / 146097);
10622
+ const doe = zz - era * 146097;
10623
+ const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
10624
+ const y = yoe + era * 400;
10625
+ const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
10626
+ const mp = Math.floor((5 * doy + 2) / 153);
10627
+ const d = doy - Math.floor((153 * mp + 2) / 5) + 1;
10628
+ const m = mp < 10 ? mp + 3 : mp - 9;
10629
+ return { y: m <= 2 ? y + 1 : y, m, d };
10630
+ }
10631
+ function parseISODays(s) {
10632
+ const [y, m, d] = s.split("-").map(Number);
10633
+ return ymdToDays(y, m, d);
10634
+ }
10635
+ function dow(days) {
10636
+ return (days % 7 + 4 + 7e3) % 7;
10637
+ }
10638
+ function isWeekend(days) {
10639
+ const w = dow(days);
10640
+ return w === 0 || w === 6;
10641
+ }
10642
+ function advanceWorking(startDays, n) {
10643
+ let cur = startDays;
10644
+ let left = Math.round(n);
10645
+ const step = left >= 0 ? 1 : -1;
10646
+ left = Math.abs(left);
10647
+ while (left > 0) {
10648
+ cur += step;
10649
+ if (!isWeekend(cur)) left--;
10650
+ }
10651
+ return cur;
10652
+ }
10653
+ var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
10654
+ function fmtDate(days) {
10655
+ const { m, d } = daysToYmd(days);
10656
+ return `${MONTHS[m - 1]} ${d}`;
10657
+ }
10658
+ var G = {
10659
+ PAD: 20,
10660
+ TITLE_H: 30,
10661
+ AXIS_H: 26,
10662
+ ROW_H: 28,
10663
+ BAR_H: 15,
10664
+ SECTION_H: 24,
10665
+ LABEL_MIN: 120,
10666
+ LABEL_MAX: 250,
10667
+ CHAR_W: 6.6,
10668
+ LABEL_PAD: 12,
10669
+ TARGET_CHART_W: 740,
10670
+ MIN_PX_UNIT: 5,
10671
+ MAX_PX_UNIT: 56,
10672
+ FOOTER_H: 28
10673
+ };
10674
+ function renderGantt2(ast, schedule, config) {
10675
+ const base = resolveBaseTheme(config?.theme ?? "default");
10676
+ const themeName = (config?.theme ?? "default") in PALETTES ? config.theme : "default";
10677
+ const P = PALETTES[themeName];
10678
+ const tasks = ast.tasks;
10679
+ const span = Math.max(schedule.projectDuration, 1);
10680
+ const unitToDay = ast.unit === "weeks" ? 7 : 1;
10681
+ const unitToWork = ast.unit === "weeks" ? 5 : 1;
10682
+ const pxUnit = Math.max(G.MIN_PX_UNIT, Math.min(G.MAX_PX_UNIT, G.TARGET_CHART_W / span));
10683
+ const longest = tasks.reduce((mx, t) => Math.max(mx, (t.label ?? t.id).length), 4);
10684
+ const labelW = Math.max(G.LABEL_MIN, Math.min(G.LABEL_MAX, Math.ceil(longest * G.CHAR_W) + 2 * G.LABEL_PAD));
10685
+ const chartX = G.PAD + labelW + 8;
10686
+ const chartW = Math.ceil(span * pxUnit);
10687
+ const hasTitle = !!ast.title;
10688
+ const topY = G.PAD + (hasTitle ? G.TITLE_H : 0);
10689
+ const axisBottom = topY + G.AXIS_H;
10690
+ const rows = [];
10691
+ let cursorY = axisBottom + 6;
10692
+ let lastLane = "\0";
10693
+ for (const t of tasks) {
10694
+ const lane = t.lane;
10695
+ if (lane !== lastLane && lane !== void 0) {
10696
+ rows.push({ kind: "section", y: cursorY, h: G.SECTION_H, label: lane });
10697
+ cursorY += G.SECTION_H;
10698
+ lastLane = lane;
10699
+ } else if (lane === void 0) {
10700
+ lastLane = void 0;
10701
+ }
10702
+ rows.push({ kind: "task", y: cursorY, h: G.ROW_H, task: t });
10703
+ cursorY += G.ROW_H;
10704
+ }
10705
+ const chartBottom = cursorY;
10706
+ const totalH = chartBottom + G.FOOTER_H + G.PAD;
10707
+ const totalW = chartX + chartW + G.PAD;
10708
+ const startDays = ast.start ? parseISODays(ast.start) : void 0;
10709
+ const xAt = (unitOffset) => chartX + unitOffset * pxUnit;
10710
+ const offsetToDate = (u) => {
10711
+ if (startDays === void 0) return void 0;
10712
+ return ast.calendar === "5day" ? advanceWorking(startDays, u * unitToWork) : startDays + Math.round(u * unitToDay);
10713
+ };
10714
+ const niceStep2 = (s) => {
10715
+ const raw = s / 9;
10716
+ const candidates = [1, 2, 5, 7, 10, 14, 20, 25, 50, 100];
10717
+ for (const c of candidates) if (c >= raw) return c;
10718
+ return Math.ceil(raw / 50) * 50;
10719
+ };
10720
+ const step = niceStep2(span);
10721
+ const styleBlock = el("style", {}, `
10722
+ .sx-gantt-bg { fill: ${base.bg}; }
10723
+ .sx-gantt-title { fill: ${P.text}; font-size: 15px; font-weight: 700; }
10724
+ .sx-gantt-grid { stroke: ${P.grid}; stroke-width: 1; }
10725
+ .sx-gantt-axis-tick { stroke: ${P.axis}; stroke-width: 1; }
10726
+ .sx-gantt-axis-text { fill: ${P.subtext}; font-size: 9.5px; }
10727
+ .sx-gantt-axis-base { stroke: ${P.axis}; stroke-width: 1.2; }
10728
+ .sx-gantt-section { fill: ${P.sectionBand}; }
10729
+ .sx-gantt-section-text { fill: ${P.sectionText}; font-size: 11px; font-weight: 700; }
10730
+ .sx-gantt-rowlabel { fill: ${P.text}; font-size: 11.5px; }
10731
+ .sx-gantt-rowlabel.crit { fill: ${P.critStroke}; font-weight: 600; }
10732
+ .sx-gantt-bar { fill: ${P.bar}; stroke: ${P.barStroke}; stroke-width: 1; }
10733
+ .sx-gantt-bar[data-critical="true"] { fill: ${P.critBar}; stroke: ${P.critStroke}; stroke-width: 1.4; }
10734
+ .sx-gantt-done { fill: ${P.barDone}; }
10735
+ .sx-gantt-done[data-critical="true"] { fill: ${P.critDone}; }
10736
+ .sx-gantt-ms { fill: ${P.milestone}; stroke: ${P.milestone}; }
10737
+ .sx-gantt-ms[data-critical="true"] { fill: ${P.critStroke}; stroke: ${P.critStroke}; }
10738
+ .sx-gantt-dep { fill: none; stroke: ${P.dep}; stroke-width: 1; }
10739
+ .sx-gantt-barlabel { fill: ${P.subtext}; font-size: 9.5px; }
10740
+ .sx-gantt-today { stroke: ${P.today}; stroke-width: 1.3; stroke-dasharray: 4 3; }
10741
+ .sx-gantt-today-text { fill: ${P.today}; font-size: 9.5px; font-weight: 700; }
10742
+ .sx-gantt-footer { fill: ${P.subtext}; font-size: 10px; }
10743
+ `.trim());
10744
+ const children = [
10745
+ title(`Gantt chart${ast.title ? " \u2014 " + ast.title : ""}`),
10746
+ desc(summarise(ast, schedule)),
10747
+ styleBlock,
10748
+ rect({ x: 0, y: 0, width: totalW, height: totalH, class: "sx-gantt-bg" })
10749
+ ];
10750
+ if (hasTitle) {
10751
+ children.push(text({ x: G.PAD, y: G.PAD + 18, class: "sx-gantt-title" }, ast.title));
10752
+ }
10753
+ const bandEls = [];
10754
+ for (const r7 of rows) {
10755
+ if (r7.kind === "section") {
10756
+ bandEls.push(rect({ x: G.PAD, y: r7.y, width: totalW - 2 * G.PAD, height: r7.h, class: "sx-gantt-section" }));
10757
+ bandEls.push(text({ x: G.PAD + 8, y: r7.y + r7.h / 2 + 4, class: "sx-gantt-section-text" }, r7.label));
10758
+ }
10759
+ }
10760
+ children.push(group({ class: "sx-gantt-sections" }, bandEls));
10761
+ const axisEls = [];
10762
+ for (let u = 0; u <= span + 1e-4; u += step) {
10763
+ const x = xAt(u);
10764
+ axisEls.push(line({ class: "sx-gantt-grid", x1: x, y1: axisBottom, x2: x, y2: chartBottom }));
10765
+ axisEls.push(line({ class: "sx-gantt-axis-tick", x1: x, y1: axisBottom - 5, x2: x, y2: axisBottom }));
10766
+ const cal = offsetToDate(u);
10767
+ const label = cal !== void 0 ? fmtDate(cal) : fmtVal(u);
10768
+ axisEls.push(text({ x, y: axisBottom - 9, class: "sx-gantt-axis-text", "text-anchor": "middle" }, label));
10769
+ }
10770
+ axisEls.push(line({ class: "sx-gantt-axis-base", x1: chartX, y1: axisBottom, x2: chartX + chartW, y2: axisBottom }));
10771
+ children.push(group({ class: "sx-gantt-axis" }, axisEls));
10772
+ const labelEls = [];
10773
+ for (const r7 of rows) {
10774
+ if (r7.kind !== "task" || !r7.task) continue;
10775
+ const c = schedule.computed.get(r7.task.id);
10776
+ const crit = c?.critical ?? false;
10777
+ labelEls.push(
10778
+ text(
10779
+ { x: G.PAD + 4, y: r7.y + r7.h / 2 + 4, class: `sx-gantt-rowlabel${crit ? " crit" : ""}` },
10780
+ clip(r7.task.label ?? r7.task.id, Math.floor(labelW / G.CHAR_W))
10781
+ )
10782
+ );
10783
+ }
10784
+ children.push(group({ class: "sx-gantt-rowlabels" }, labelEls));
10785
+ const rowOf = /* @__PURE__ */ new Map();
10786
+ for (const r7 of rows) if (r7.kind === "task" && r7.task) rowOf.set(r7.task.id, r7);
10787
+ const depEls = [];
10788
+ for (const t of tasks) {
10789
+ const tr = rowOf.get(t.id);
10790
+ const tc = schedule.computed.get(t.id);
10791
+ if (!tr || !tc) continue;
10792
+ const succMidY = tr.y + tr.h / 2;
10793
+ const succX = xAt(tc.es);
10794
+ for (const dep of t.deps) {
10795
+ const pr = rowOf.get(dep.pred);
10796
+ const pc2 = schedule.computed.get(dep.pred);
10797
+ if (!pr || !pc2) continue;
10798
+ const predMidY = pr.y + pr.h / 2;
10799
+ const predX = xAt(pc2.ef);
10800
+ const midX = Math.max(predX + 8, succX - 10);
10801
+ depEls.push(path({
10802
+ class: "sx-gantt-dep",
10803
+ d: `M ${r1(predX)} ${r1(predMidY)} H ${r1(midX)} V ${r1(succMidY)} H ${r1(succX - 3)}`
10804
+ }));
10805
+ }
10806
+ }
10807
+ children.push(group({ class: "sx-gantt-deps" }, depEls));
10808
+ const barEls = [];
10809
+ for (const r7 of rows) {
10810
+ if (r7.kind !== "task" || !r7.task) continue;
10811
+ const t = r7.task;
10812
+ const c = schedule.computed.get(t.id);
10813
+ if (!c) continue;
10814
+ const crit = c.critical;
10815
+ const cy = r7.y + r7.h / 2;
10816
+ if (t.milestone || t.duration === 0) {
10817
+ const x2 = xAt(c.es);
10818
+ const s = 7;
10819
+ barEls.push(group(
10820
+ { class: "sx-gantt-task", "data-id": t.id, "data-critical": String(crit), "data-es": String(c.es) },
10821
+ [polygon({
10822
+ class: "sx-gantt-ms",
10823
+ "data-critical": String(crit),
10824
+ points: `${r1(x2)},${r1(cy - s)} ${r1(x2 + s)},${r1(cy)} ${r1(x2)},${r1(cy + s)} ${r1(x2 - s)},${r1(cy)}`
10825
+ })]
10826
+ ));
10827
+ continue;
10828
+ }
10829
+ const x = xAt(c.es);
10830
+ const w = Math.max(2, (c.ef - c.es) * pxUnit);
10831
+ const by = cy - G.BAR_H / 2;
10832
+ const parts = [
10833
+ rect({ x: r1(x), y: r1(by), width: r1(w), height: G.BAR_H, rx: 3, class: "sx-gantt-bar", "data-critical": String(crit) })
10834
+ ];
10835
+ if (t.progress !== void 0 && t.progress > 0) {
10836
+ parts.push(rect({
10837
+ x: r1(x),
10838
+ y: r1(by),
10839
+ width: r1(Math.max(2, w * t.progress / 100)),
10840
+ height: G.BAR_H,
10841
+ rx: 3,
10842
+ class: "sx-gantt-done",
10843
+ "data-critical": String(crit)
10844
+ }));
10845
+ }
10846
+ const cap2 = c.slack > 0 ? `${fmtVal(c.ef - c.es)}${unitSuffix2(ast.unit)} \xB7 slack ${fmtVal(c.slack)}` : `${fmtVal(c.ef - c.es)}${unitSuffix2(ast.unit)}`;
10847
+ parts.push(text({ x: r1(x + w + 5), y: r1(cy + 3.5), class: "sx-gantt-barlabel" }, cap2));
10848
+ barEls.push(group(
10849
+ {
10850
+ class: "sx-gantt-task",
10851
+ "data-id": t.id,
10852
+ "data-critical": String(crit),
10853
+ "data-es": String(c.es),
10854
+ "data-ef": String(c.ef),
10855
+ "data-slack": String(c.slack),
10856
+ ...t.progress !== void 0 ? { "data-progress": String(t.progress) } : {}
10857
+ },
10858
+ parts
10859
+ ));
10860
+ }
10861
+ children.push(group({ class: "sx-gantt-bars" }, barEls));
10862
+ if (ast.today && startDays !== void 0) {
10863
+ const todayDays = parseISODays(ast.today);
10864
+ let u;
10865
+ if (ast.calendar === "5day") {
10866
+ u = workingDaysBetween(startDays, todayDays) / unitToWork;
10867
+ } else {
10868
+ u = (todayDays - startDays) / unitToDay;
10869
+ }
10870
+ if (u >= 0 && u <= span) {
10871
+ const x = xAt(u);
10872
+ children.push(group({ class: "sx-gantt-today-g" }, [
10873
+ line({ class: "sx-gantt-today", x1: x, y1: axisBottom, x2: x, y2: chartBottom }),
10874
+ text({ x, y: chartBottom + 12, class: "sx-gantt-today-text", "text-anchor": "middle" }, "today")
10875
+ ]));
10876
+ }
10877
+ }
10878
+ const footer = `${schedule.order.length} tasks \xB7 duration ${fmtVal(schedule.projectDuration)}${unitSuffix2(ast.unit)} \xB7 critical path: ${schedule.criticalPath.join(" \u2192 ") || "\u2014"}`;
10879
+ children.push(text({ x: G.PAD, y: totalH - 10, class: "sx-gantt-footer" }, clip(footer, Math.floor((totalW - 2 * G.PAD) / 5.4))));
10880
+ return svgRoot(
10881
+ {
10882
+ class: "sx-gantt",
10883
+ role: "img",
10884
+ "aria-label": escapeXml(ast.title ?? "Gantt chart"),
10885
+ width: totalW,
10886
+ height: totalH,
10887
+ viewBox: `0 0 ${totalW} ${totalH}`,
10888
+ "data-diagram-type": "pert",
10889
+ "data-layout": "gantt"
10890
+ },
10891
+ children
10892
+ );
10893
+ }
10894
+ function workingDaysBetween(a, b) {
10895
+ if (b < a) return -workingDaysBetween(b, a);
10896
+ let n = 0;
10897
+ for (let d = a; d < b; d++) if (!isWeekend(d)) n++;
10898
+ return n;
10899
+ }
10900
+ function unitSuffix2(unit) {
10901
+ switch (unit) {
10902
+ case "days":
10903
+ return "d";
10904
+ case "weeks":
10905
+ return "w";
10906
+ case "hours":
10907
+ return "h";
10908
+ default:
10909
+ return "";
10910
+ }
10911
+ }
10527
10912
  function fmtVal(n) {
10528
10913
  return String(parseFloat(n.toFixed(2)));
10529
10914
  }
10915
+ function r1(n) {
10916
+ return Math.round(n * 10) / 10;
10917
+ }
10918
+ function clip(s, n) {
10919
+ return s.length <= n ? s : s.slice(0, Math.max(1, n - 1)) + "\u2026";
10920
+ }
10921
+ function summarise(ast, s) {
10922
+ const parts = [`Gantt chart: ${s.order.length} tasks, project duration ${fmtVal(s.projectDuration)}${unitSuffix2(ast.unit)}.`];
10923
+ if (ast.start) parts.push(`Starts ${ast.start} (${ast.calendar === "5day" ? "weekdays only" : "continuous"}).`);
10924
+ if (s.criticalPath.length) parts.push(`Critical path: ${s.criticalPath.join(" \u2192 ")}.`);
10925
+ return parts.join(" ");
10926
+ }
10927
+
10928
+ // src/diagrams/pert/renderer.ts
10929
+ function fmtVal2(n) {
10930
+ return String(parseFloat(n.toFixed(2)));
10931
+ }
10530
10932
  function unitWord(unit) {
10531
10933
  return unit === "abstract" ? "" : unit;
10532
10934
  }
@@ -10630,19 +11032,19 @@ function dataAttrs(b) {
10630
11032
  const attrs = {
10631
11033
  class: `sx-pert-task${c.critical ? " critical" : ""}${b.task.className ? " " + b.task.className : ""}`,
10632
11034
  "data-id": b.id,
10633
- "data-es": fmtVal(c.es),
10634
- "data-ef": fmtVal(c.ef),
10635
- "data-ls": fmtVal(c.ls),
10636
- "data-lf": fmtVal(c.lf),
10637
- "data-slack": fmtVal(c.slack),
10638
- "data-duration": fmtVal(b.task.duration),
11035
+ "data-es": fmtVal2(c.es),
11036
+ "data-ef": fmtVal2(c.ef),
11037
+ "data-ls": fmtVal2(c.ls),
11038
+ "data-lf": fmtVal2(c.lf),
11039
+ "data-slack": fmtVal2(c.slack),
11040
+ "data-duration": fmtVal2(b.task.duration),
10639
11041
  "data-critical": String(c.critical)
10640
11042
  };
10641
11043
  if (b.task.tags.length) attrs["data-tag"] = b.task.tags.join(" ");
10642
11044
  if (b.task.threePoint) {
10643
11045
  const tp = b.task.threePoint;
10644
- attrs["data-pert-triple"] = `${fmtVal(tp.o)}/${fmtVal(tp.m)}/${fmtVal(tp.p)}`;
10645
- if (b.task.variance !== void 0) attrs["data-pert-variance"] = fmtVal(b.task.variance);
11046
+ attrs["data-pert-triple"] = `${fmtVal2(tp.o)}/${fmtVal2(tp.m)}/${fmtVal2(tp.p)}`;
11047
+ if (b.task.variance !== void 0) attrs["data-pert-variance"] = fmtVal2(b.task.variance);
10646
11048
  }
10647
11049
  return attrs;
10648
11050
  }
@@ -10669,16 +11071,16 @@ function renderSixField(b) {
10669
11071
  text({ class: "sx-pert-field-label", x: fx, y: y + 9, "text-anchor": "middle" }, label),
10670
11072
  text({ class: cls, x: fx, y: y + 19, "text-anchor": "middle" }, value)
10671
11073
  ]);
10672
- parts.push(field(x + col / 2, "ES", fmtVal(c.es)));
10673
- parts.push(field(x + col * 1.5, "DUR", fmtVal(b.task.duration)));
10674
- parts.push(field(x + col * 2.5, "EF", fmtVal(c.ef)));
11074
+ parts.push(field(x + col / 2, "ES", fmtVal2(c.es)));
11075
+ parts.push(field(x + col * 1.5, "DUR", fmtVal2(b.task.duration)));
11076
+ parts.push(field(x + col * 2.5, "EF", fmtVal2(c.ef)));
10675
11077
  const fieldBot = (fx, label, value, cls = "sx-pert-field") => group({}, [
10676
11078
  text({ class: cls, x: fx, y: yBot + 13, "text-anchor": "middle" }, value),
10677
11079
  text({ class: "sx-pert-field-label", x: fx, y: yBot + 21, "text-anchor": "middle" }, label)
10678
11080
  ]);
10679
- parts.push(fieldBot(x + col / 2, "LS", fmtVal(c.ls)));
10680
- parts.push(fieldBot(x + col * 1.5, "SLACK", fmtVal(c.slack), "sx-pert-field slack"));
10681
- parts.push(fieldBot(x + col * 2.5, "LF", fmtVal(c.lf)));
11081
+ parts.push(fieldBot(x + col / 2, "LS", fmtVal2(c.ls)));
11082
+ parts.push(fieldBot(x + col * 1.5, "SLACK", fmtVal2(c.slack), "sx-pert-field slack"));
11083
+ parts.push(fieldBot(x + col * 2.5, "LF", fmtVal2(c.lf)));
10682
11084
  const hasSigma = b.task.variance !== void 0;
10683
11085
  const nameY = hasSigma ? y + topH + 18 : y + topH + 19;
10684
11086
  parts.push(text({ class: "sx-pert-name", x: cx, y: nameY, "text-anchor": "middle" }, b.task.label));
@@ -10687,7 +11089,7 @@ function renderSixField(b) {
10687
11089
  parts.push(
10688
11090
  text(
10689
11091
  { class: "sx-pert-sigma", x: cx, y: nameY + 27, "text-anchor": "middle" },
10690
- `\u03C3=${fmtVal(Math.sqrt(b.task.variance))}`
11092
+ `\u03C3=${fmtVal2(Math.sqrt(b.task.variance))}`
10691
11093
  )
10692
11094
  );
10693
11095
  }
@@ -10701,7 +11103,7 @@ function renderMilestone(b) {
10701
11103
  const parts = [
10702
11104
  polygon({ class: "sx-pert-ms", points: pts }),
10703
11105
  text({ class: "sx-pert-name", x: cx, y: cy - 2, "text-anchor": "middle" }, b.task.label),
10704
- text({ class: "sx-pert-id", x: cx, y: cy + 12, "text-anchor": "middle" }, `@ ${fmtVal(b.computed.es)}`)
11106
+ text({ class: "sx-pert-id", x: cx, y: cy + 12, "text-anchor": "middle" }, `@ ${fmtVal2(b.computed.es)}`)
10705
11107
  ];
10706
11108
  return group(dataAttrs(b), parts);
10707
11109
  }
@@ -10714,12 +11116,12 @@ function renderTimescaledBar(b) {
10714
11116
  parts.push(text({ class: "sx-pert-id", x: cx, y: y + h / 2 + 4, "text-anchor": "middle" }, b.id));
10715
11117
  const wide = w >= 104;
10716
11118
  if (wide) {
10717
- parts.push(text({ class: "sx-pert-field-label", x: x + 7, y: y + 15, "text-anchor": "start" }, `ES ${fmtVal(c.es)}`));
10718
- parts.push(text({ class: "sx-pert-field-label", x: x + w - 7, y: y + 15, "text-anchor": "end" }, `EF ${fmtVal(c.ef)}`));
11119
+ parts.push(text({ class: "sx-pert-field-label", x: x + 7, y: y + 15, "text-anchor": "start" }, `ES ${fmtVal2(c.es)}`));
11120
+ parts.push(text({ class: "sx-pert-field-label", x: x + w - 7, y: y + 15, "text-anchor": "end" }, `EF ${fmtVal2(c.ef)}`));
10719
11121
  parts.push(
10720
11122
  text(
10721
11123
  { class: `sx-pert-field-label${c.critical ? " slack" : ""}`, x: cx, y: y + h - 8, "text-anchor": "middle" },
10722
- `slack ${fmtVal(c.slack)}`
11124
+ `slack ${fmtVal2(c.slack)}`
10723
11125
  )
10724
11126
  );
10725
11127
  }
@@ -10804,7 +11206,7 @@ function renderAxis2(axis, gridTop) {
10804
11206
  parts.push(line({ x1: tk.pos, y1: axis.baseline, x2: tk.pos, y2: axis.baseline + len }));
10805
11207
  if (tk.major) {
10806
11208
  parts.push(
10807
- text({ x: tk.pos, y: axis.baseline + 18, "text-anchor": "middle" }, fmtVal(tk.value))
11209
+ text({ x: tk.pos, y: axis.baseline + 18, "text-anchor": "middle" }, fmtVal2(tk.value))
10808
11210
  );
10809
11211
  }
10810
11212
  }
@@ -10830,7 +11232,7 @@ function renderAoa(aoa) {
10830
11232
  );
10831
11233
  if (!a.dummy && a.label) {
10832
11234
  const critCls = a.critical ? " critical" : "";
10833
- const durStr = fmtVal(a.duration ?? 0);
11235
+ const durStr = fmtVal2(a.duration ?? 0);
10834
11236
  const w = Math.max(a.label.length * 6.2, durStr.length * 6) + 8;
10835
11237
  labelEls.push(
10836
11238
  rect({ class: "sx-pert-edge-halo", x: a.labelX - w / 2, y: a.labelY - 16, width: w, height: 31, rx: 3, ry: 3 })
@@ -10860,8 +11262,8 @@ function renderAoa(aoa) {
10860
11262
  }
10861
11263
  function summaryText(summary) {
10862
11264
  const u = unitWord(summary.unit);
10863
- const dur = `${fmtVal(summary.projectDuration)}${u ? " " + u : ""}`;
10864
- const sigma = summary.projectStdDev !== void 0 ? ` \xB7 \u03C3 \u2248 ${fmtVal(summary.projectStdDev)}` : "";
11265
+ const dur = `${fmtVal2(summary.projectDuration)}${u ? " " + u : ""}`;
11266
+ const sigma = summary.projectStdDev !== void 0 ? ` \xB7 \u03C3 \u2248 ${fmtVal2(summary.projectStdDev)}` : "";
10865
11267
  const plain = `Project duration ${dur} \xB7 ${summary.taskCount} tasks \xB7 ${summary.depCount} dependencies \xB7 ${summary.criticalCount} critical${sigma}`;
10866
11268
  const critPath = summary.criticalPath.map((id) => id).join(" \u2192 ");
10867
11269
  return { plain, critPath };
@@ -10873,7 +11275,7 @@ function renderPertLayout(layout, config) {
10873
11275
  children.push(title(`PERT network${layout.title ? " \u2014 " + layout.title : ""}`));
10874
11276
  children.push(
10875
11277
  desc(
10876
- `${layout.summary.taskCount} activities, project duration ${fmtVal(layout.summary.projectDuration)} ${unitWord(layout.unit)}` + (cp.length ? `, critical path ${cp.join(" \u2192 ")}` : "") + "."
11278
+ `${layout.summary.taskCount} activities, project duration ${fmtVal2(layout.summary.projectDuration)} ${unitWord(layout.unit)}` + (cp.length ? `, critical path ${cp.join(" \u2192 ")}` : "") + "."
10877
11279
  )
10878
11280
  );
10879
11281
  children.push(el("style", {}, buildCss5(t)));
@@ -10941,6 +11343,7 @@ var PERT_PAD = 24;
10941
11343
  function renderPert(textOrAst, config) {
10942
11344
  const ast = typeof textOrAst === "string" ? parsePert(textOrAst) : textOrAst;
10943
11345
  const schedule = schedulePert(ast);
11346
+ if (ast.layout === "gantt") return renderGantt2(ast, schedule, config);
10944
11347
  const layout = layoutPert(ast, schedule);
10945
11348
  return renderPertLayout(layout, config);
10946
11349
  }
@@ -10953,7 +11356,7 @@ var pert = {
10953
11356
  const t = raw.trim();
10954
11357
  if (!t) continue;
10955
11358
  if (t.startsWith("#") || t.startsWith("//")) continue;
10956
- return /^pert\b/i.test(t);
11359
+ return /^(pert|gantt)\b/i.test(t);
10957
11360
  }
10958
11361
  return false;
10959
11362
  },
@@ -17061,6 +17464,19 @@ function truncate3(s, n) {
17061
17464
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
17062
17465
  }
17063
17466
 
17467
+ // src/core/format.ts
17468
+ function formatProbability(n) {
17469
+ if (!Number.isFinite(n)) return String(n);
17470
+ if (n <= 0) return "0";
17471
+ if (n >= 1) return "1";
17472
+ if (n < 1e-3) return n.toExponential(2);
17473
+ for (let p = 3; p <= 9; p++) {
17474
+ const s = parseFloat(n.toPrecision(p));
17475
+ if (s > 0 && s < 1) return String(s);
17476
+ }
17477
+ return String(parseFloat(n.toPrecision(9)));
17478
+ }
17479
+
17064
17480
  // src/diagrams/faulttree/analysis.ts
17065
17481
  var EXPANSION_CAP = 5e4;
17066
17482
  var EXACT_CUTSET_CAP = 20;
@@ -17627,7 +18043,7 @@ function renderFaultTreeLayout(layout, config) {
17627
18043
  const instById = new Map(layout.events.map((e) => [e.instanceId, e]));
17628
18044
  const children = [
17629
18045
  title(a11y),
17630
- desc(summarise(layout)),
18046
+ desc(summarise2(layout)),
17631
18047
  styleBlock,
17632
18048
  rect({ x: 0, y: 0, width, height, class: "sx-ft-bg" })
17633
18049
  ];
@@ -17730,7 +18146,7 @@ function leafCaption(e, shapeBottom, label, showProb) {
17730
18146
  const out = [];
17731
18147
  const hasOwnLabel = !!e.event.label && e.event.label !== e.event.id;
17732
18148
  if (hasOwnLabel) {
17733
- out.push(text({ x: e.cx, y: shapeBottom + FAULTTREE_CONST.CAP_GAP, class: "sx-ft-cap", "text-anchor": "middle" }, clip(label, 20)));
18149
+ out.push(text({ x: e.cx, y: shapeBottom + FAULTTREE_CONST.CAP_GAP, class: "sx-ft-cap", "text-anchor": "middle" }, clip2(label, 20)));
17734
18150
  }
17735
18151
  if (showProb && e.event.prob !== void 0) {
17736
18152
  const y = shapeBottom + (hasOwnLabel ? FAULTTREE_CONST.CAP_GAP + FAULTTREE_CONST.CAP_LINE_H : FAULTTREE_CONST.CAP_GAP);
@@ -17788,7 +18204,7 @@ function topProbLabel(analysis) {
17788
18204
  if (analysis.topProb === void 0) return "P(top) = n/a (missing p)";
17789
18205
  return `P(top) = ${fmtProb(analysis.topProb)} (${analysis.method})`;
17790
18206
  }
17791
- function summarise(layout) {
18207
+ function summarise2(layout) {
17792
18208
  const { ast, analysis } = layout;
17793
18209
  const counts = {};
17794
18210
  for (const e of ast.events) counts[e.kind] = (counts[e.kind] ?? 0) + 1;
@@ -17814,11 +18230,9 @@ function summarise(layout) {
17814
18230
  return parts.join(" ");
17815
18231
  }
17816
18232
  function fmtProb(n) {
17817
- if (n === 0) return "0";
17818
- if (n >= 1e-3) return String(parseFloat(n.toPrecision(3)));
17819
- return n.toExponential(2);
18233
+ return formatProbability(n);
17820
18234
  }
17821
- function clip(s, n) {
18235
+ function clip2(s, n) {
17822
18236
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
17823
18237
  }
17824
18238
  function wrap2(s, perLine) {
@@ -17836,7 +18250,7 @@ function wrap2(s, perLine) {
17836
18250
  if (cur) lines.push(cur);
17837
18251
  if (lines.length > 2) {
17838
18252
  lines.length = 2;
17839
- lines[1] = clip(lines[1], perLine);
18253
+ lines[1] = clip2(lines[1], perLine);
17840
18254
  }
17841
18255
  return lines.join("<br/>");
17842
18256
  }
@@ -18325,7 +18739,7 @@ function renderBowtieLayout(layout, config) {
18325
18739
  );
18326
18740
  const children = [
18327
18741
  title(a11y),
18328
- desc(summarise2(layout)),
18742
+ desc(summarise3(layout)),
18329
18743
  styleBlock,
18330
18744
  rect({ x: 0, y: 0, width, height, class: "sx-bowtie-bg" })
18331
18745
  ];
@@ -18415,7 +18829,7 @@ function renderLegend2(layout, fontFamily) {
18415
18829
  }
18416
18830
  return group({ class: "sx-bowtie-legend", "data-role": "legend" }, parts);
18417
18831
  }
18418
- function summarise2(layout) {
18832
+ function summarise3(layout) {
18419
18833
  const { ast } = layout;
18420
18834
  const barrierCount = layout.boxes.filter((b) => b.role === "barrier").length;
18421
18835
  const escCount = layout.boxes.filter((b) => b.role === "escalation").length;
@@ -18443,11 +18857,11 @@ function wrap3(s, perLine, maxLines) {
18443
18857
  }
18444
18858
  }
18445
18859
  if (cur && lines.length < maxLines) lines.push(cur);
18446
- else if (cur && lines.length === maxLines) lines[maxLines - 1] = clip2(`${lines[maxLines - 1]} ${cur}`, perLine);
18860
+ else if (cur && lines.length === maxLines) lines[maxLines - 1] = clip3(`${lines[maxLines - 1]} ${cur}`, perLine);
18447
18861
  if (lines.length === 0) lines.push(s);
18448
18862
  return lines;
18449
18863
  }
18450
- function clip2(s, n) {
18864
+ function clip3(s, n) {
18451
18865
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
18452
18866
  }
18453
18867
  function r4(n) {
@@ -18953,7 +19367,7 @@ function renderEventTreeLayout(layout, config) {
18953
19367
  );
18954
19368
  const children = [
18955
19369
  title(a11y),
18956
- desc(summarise3(layout)),
19370
+ desc(summarise4(layout)),
18957
19371
  styleBlock,
18958
19372
  rect({ x: 0, y: 0, width, height, class: "sx-et-bg" })
18959
19373
  ];
@@ -19024,7 +19438,7 @@ function renderEventTreeLayout(layout, config) {
19024
19438
  el("circle", { cx: leaf.x, cy: leaf.y, r: 3, class: "sx-et-dot", ...dom }),
19025
19439
  text(
19026
19440
  { x: leaf.x + 10, y: leaf.y - 4, class: "sx-et-outcome", ...dom },
19027
- clip3(seq.outcome, 30)
19441
+ clip4(seq.outcome, 30)
19028
19442
  ),
19029
19443
  text(
19030
19444
  { x: leaf.x + 10, y: leaf.y + EVENTTREE_CONST.LEAF_LINE_H - 2, class: "sx-et-freq", ...dom },
@@ -19049,7 +19463,7 @@ function renderEventTreeLayout(layout, config) {
19049
19463
  children
19050
19464
  );
19051
19465
  }
19052
- function summarise3(layout) {
19466
+ function summarise4(layout) {
19053
19467
  const { ast, analysis } = layout;
19054
19468
  const parts = [
19055
19469
  `Event tree for "${ast.initiating.label ?? ast.initiating.id}" (f\u2080 = ${fmtFreq(ast.initiating.freq)}): ${ast.functions.length} function${ast.functions.length === 1 ? "" : "s"}, ${analysis.sequences.length} sequence${analysis.sequences.length === 1 ? "" : "s"}.`
@@ -19082,7 +19496,7 @@ function fmtProb2(n) {
19082
19496
  if (n >= 1e-3) return String(parseFloat(n.toPrecision(3)));
19083
19497
  return n.toExponential(2);
19084
19498
  }
19085
- function clip3(s, n) {
19499
+ function clip4(s, n) {
19086
19500
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
19087
19501
  }
19088
19502
 
@@ -20031,7 +20445,7 @@ function renderFmeaLayout(layout, config) {
20031
20445
  const style = el("style", {}, buildCss9(p));
20032
20446
  const children = [
20033
20447
  title(a11y),
20034
- desc(summarise4(layout)),
20448
+ desc(summarise5(layout)),
20035
20449
  style,
20036
20450
  rect({ x: 0, y: 0, width, height, class: "sx-fmea-bg" })
20037
20451
  ];
@@ -20183,7 +20597,7 @@ function renderHeaderBlock(layout) {
20183
20597
  function capitalise(s) {
20184
20598
  return s.charAt(0).toUpperCase() + s.slice(1);
20185
20599
  }
20186
- function summarise4(layout) {
20600
+ function summarise5(layout) {
20187
20601
  const { ast, analysis } = layout;
20188
20602
  const top = analysis.rows[0];
20189
20603
  const parts = [
@@ -20279,6 +20693,7 @@ function parseRbd(text2) {
20279
20693
  seenIds.add(id);
20280
20694
  let label;
20281
20695
  let R;
20696
+ let dist;
20282
20697
  if (peek()?.t === "string") {
20283
20698
  label = tokens[pos++].v;
20284
20699
  }
@@ -20287,10 +20702,16 @@ function parseRbd(text2) {
20287
20702
  const attr = parseAttr(w);
20288
20703
  if (!attr) break;
20289
20704
  pos++;
20290
- if (attr.key === "p") R = clamp01(1 - attr.value, w, warnings);
20291
- else R = clamp01(attr.value, w, warnings);
20705
+ if (attr.kind === "dist") dist = attr.dist;
20706
+ else R = clamp01(attr.failure ? 1 - attr.value : attr.value, w, warnings);
20292
20707
  }
20293
- return { kind: "block", id, ...label !== void 0 ? { label } : {}, ...R !== void 0 ? { R } : {} };
20708
+ return {
20709
+ kind: "block",
20710
+ id,
20711
+ ...label !== void 0 ? { label } : {},
20712
+ ...R !== void 0 ? { R } : {},
20713
+ ...dist !== void 0 ? { dist } : {}
20714
+ };
20294
20715
  };
20295
20716
  const parseGroup = (kwRaw) => {
20296
20717
  const kw = kwRaw.toLowerCase();
@@ -20364,10 +20785,19 @@ function parseRbd(text2) {
20364
20785
  } else {
20365
20786
  root = { kind: "series", children: top };
20366
20787
  }
20788
+ let mission;
20789
+ if (metadata.mission !== void 0) {
20790
+ const v = parseFloat(metadata.mission);
20791
+ if (!Number.isFinite(v) || v < 0) {
20792
+ throw new RbdParseError(`mission time must be a non-negative number (got '${metadata.mission}')`);
20793
+ }
20794
+ mission = v;
20795
+ }
20367
20796
  return {
20368
20797
  type: "rbd",
20369
20798
  ...title2 ? { title: title2 } : {},
20370
20799
  root,
20800
+ ...mission !== void 0 ? { mission } : {},
20371
20801
  warnings,
20372
20802
  ...Object.keys(metadata).length > 0 ? { metadata } : {}
20373
20803
  };
@@ -20420,11 +20850,10 @@ function tokenize6(src) {
20420
20850
  }
20421
20851
  function stripBodyDirectives(body, metadata) {
20422
20852
  return body.split("\n").filter((line2) => {
20423
- const m = line2.match(/^\s*(title|standard|note)\s*:\s*(.+)$/i);
20853
+ const m = line2.match(/^\s*(title|standard|note|mission)\s*:\s*(.+)$/i);
20424
20854
  if (m) {
20425
20855
  const key = m[1].toLowerCase();
20426
- if (key !== "title") metadata[key] = m[2].trim();
20427
- else metadata.title = m[2].trim();
20856
+ metadata[key] = m[2].trim();
20428
20857
  return false;
20429
20858
  }
20430
20859
  return true;
@@ -20436,15 +20865,33 @@ function extractQuoted(s) {
20436
20865
  return s.length > 0 ? s.trim() : void 0;
20437
20866
  }
20438
20867
  function parseAttr(w) {
20868
+ const wb = w.match(/^weibull\s*[=:]\s*(.+)$/i);
20869
+ if (wb) {
20870
+ const parts = wb[1].split(",").map((s) => parseFloat(s.trim()));
20871
+ if (parts.length === 2 && parts.every((x) => Number.isFinite(x) && x > 0)) {
20872
+ return { kind: "dist", dist: { kind: "weibull", beta: parts[0], eta: parts[1] } };
20873
+ }
20874
+ return null;
20875
+ }
20876
+ const rt = w.match(/^rate\s*[=:]\s*(.+)$/i);
20877
+ if (rt) {
20878
+ const v = parseFloat(rt[1].trim());
20879
+ return Number.isFinite(v) && v >= 0 ? { kind: "dist", dist: { kind: "exp", rate: v } } : null;
20880
+ }
20881
+ const mt = w.match(/^(mtbf|mttf)\s*[=:]\s*(.+)$/i);
20882
+ if (mt) {
20883
+ const v = parseFloat(mt[2].trim());
20884
+ return Number.isFinite(v) && v > 0 ? { kind: "dist", dist: { kind: "exp", rate: 1 / v } } : null;
20885
+ }
20439
20886
  const m = w.match(/^(R|r|p|prob|q)\s*[=:]\s*(.+)$/);
20440
20887
  if (m) {
20441
20888
  const key = m[1].toLowerCase();
20442
20889
  const value = parseNum(m[2]);
20443
20890
  if (value === void 0) return null;
20444
- return { key: key === "p" || key === "q" ? "p" : "R", value };
20891
+ return { kind: "R", value, failure: key === "p" || key === "q" };
20445
20892
  }
20446
20893
  const bare = parseNum(w);
20447
- if (bare !== void 0) return { key: "R", value: bare };
20894
+ if (bare !== void 0) return { kind: "R", value: bare, failure: false };
20448
20895
  return null;
20449
20896
  }
20450
20897
  function parseNum(s) {
@@ -20473,8 +20920,11 @@ var KOFN_ENUM_CAP = 18;
20473
20920
  function analyseRbd(ast) {
20474
20921
  const notes = [];
20475
20922
  const warnings = [...ast.warnings];
20923
+ if (ast.mission !== void 0) {
20924
+ notes.push(`Mission time t = ${ast.mission}; block reliabilities with a rate/MTBF/Weibull are evaluated as R(t).`);
20925
+ }
20476
20926
  const blocks = [];
20477
- collectBlocks(ast.root, blocks);
20927
+ collectBlocks(ast.root, blocks, ast.mission, warnings);
20478
20928
  const missing = blocks.filter((b) => b.R === void 0).map((b) => b.id);
20479
20929
  const baseEnv = new Map(blocks.map((b) => [b.id, b.R]));
20480
20930
  const systemReliability = evalStructure(ast.root, baseEnv, notes);
@@ -20490,10 +20940,15 @@ function analyseRbd(ast) {
20490
20940
  const rDown = evalStructure(ast.root, down, notes);
20491
20941
  const importance = rUp !== void 0 && rDown !== void 0 ? rUp - rDown : void 0;
20492
20942
  const isSpof = rDown === 0;
20943
+ let criticality;
20944
+ if (importance !== void 0 && b.R !== void 0 && systemReliability < 1) {
20945
+ criticality = importance * (1 - b.R) / (1 - systemReliability);
20946
+ }
20493
20947
  return {
20494
20948
  id: b.id,
20495
20949
  ...b.R !== void 0 ? { R: b.R } : {},
20496
20950
  ...importance !== void 0 ? { importance } : {},
20951
+ ...criticality !== void 0 ? { criticality } : {},
20497
20952
  isSpof
20498
20953
  };
20499
20954
  });
@@ -20518,12 +20973,25 @@ function analyseRbd(ast) {
20518
20973
  notes
20519
20974
  };
20520
20975
  }
20521
- function collectBlocks(s, out) {
20976
+ function collectBlocks(s, out, mission, warnings) {
20522
20977
  if (s.kind === "block") {
20523
- out.push({ id: s.id, ...s.R !== void 0 ? { R: s.R } : {} });
20978
+ const R = resolveBlockR(s, mission, warnings);
20979
+ out.push({ id: s.id, ...R !== void 0 ? { R } : {} });
20524
20980
  return;
20525
20981
  }
20526
- for (const c of s.children) collectBlocks(c, out);
20982
+ for (const c of s.children) collectBlocks(c, out, mission, warnings);
20983
+ }
20984
+ function resolveBlockR(b, mission, warnings) {
20985
+ if (b.dist) {
20986
+ if (mission === void 0) {
20987
+ warnings.push(`Block "${b.id}" has a failure distribution but no mission time \u2014 add 'mission: <t>' to evaluate R(t)${b.R !== void 0 ? ", falling back to its constant R" : ""}.`);
20988
+ return b.R;
20989
+ }
20990
+ const t = mission;
20991
+ if (b.dist.kind === "exp") return Math.exp(-b.dist.rate * t);
20992
+ return Math.exp(-Math.pow(t / b.dist.eta, b.dist.beta));
20993
+ }
20994
+ return b.R;
20527
20995
  }
20528
20996
  function evalStructure(s, env, notes) {
20529
20997
  if (s.kind === "block") return env.get(s.id);
@@ -20733,7 +21201,7 @@ function renderRbdLayout(layout, config) {
20733
21201
  );
20734
21202
  const children = [
20735
21203
  title(a11y),
20736
- desc(summarise5(layout)),
21204
+ desc(summarise6(layout)),
20737
21205
  styleBlock,
20738
21206
  rect({ x: 0, y: 0, width, height, class: "sx-rbd-bg" })
20739
21207
  ];
@@ -20754,7 +21222,7 @@ function renderRbdLayout(layout, config) {
20754
21222
  class: "sx-rbd-rsys",
20755
21223
  "text-anchor": "middle"
20756
21224
  },
20757
- systemLabel(analysis)
21225
+ systemLabel(analysis, ast.mission)
20758
21226
  )
20759
21227
  );
20760
21228
  for (const w of layout.wires) inner.push(path({ d: w.path, class: "sx-rbd-wire" }));
@@ -20824,12 +21292,16 @@ function renderBlock(b) {
20824
21292
  parts
20825
21293
  );
20826
21294
  }
20827
- function systemLabel(analysis) {
21295
+ function systemLabel(analysis, mission) {
20828
21296
  if (analysis.systemReliability === void 0) {
20829
21297
  const miss = analysis.missing.length > 0 ? ` \u2014 missing R on ${analysis.missing.join(", ")}` : "";
20830
21298
  return `System reliability: n/a${miss}`;
20831
21299
  }
20832
- return `System reliability R = ${fmtR(analysis.systemReliability)}`;
21300
+ const arg = mission !== void 0 ? `(t=${fmtVal3(mission)})` : "";
21301
+ return `System reliability R${arg} = ${fmtR(analysis.systemReliability)}`;
21302
+ }
21303
+ function fmtVal3(n) {
21304
+ return String(parseFloat(n.toFixed(2)));
20833
21305
  }
20834
21306
  function fmtR(n) {
20835
21307
  if (n >= 1) return "1";
@@ -20840,7 +21312,7 @@ function fmtR(n) {
20840
21312
  }
20841
21313
  return String(parseFloat(n.toPrecision(9)));
20842
21314
  }
20843
- function summarise5(layout) {
21315
+ function summarise6(layout) {
20844
21316
  const { analysis } = layout;
20845
21317
  const n = layout.blocks.length;
20846
21318
  const parts = [`Reliability block diagram: ${n} block${n === 1 ? "" : "s"}.`];
@@ -21580,7 +22052,7 @@ function renderCausalLoopLayout(layout, config) {
21580
22052
  );
21581
22053
  const children = [
21582
22054
  title(a11y),
21583
- desc(summarise6(layout)),
22055
+ desc(summarise7(layout)),
21584
22056
  styleBlock,
21585
22057
  el("rect", { x: 0, y: 0, width, height, class: "sx-cld-bg" })
21586
22058
  ];
@@ -21749,7 +22221,7 @@ function glyphArrow(x, y, tx, ty) {
21749
22221
  const p2 = `${fmt4(bx - px * wide)},${fmt4(by - py * wide)}`;
21750
22222
  return polygon({ points: `${fmt4(x)},${fmt4(y)} ${p1} ${p2}`, class: "sx-cld-glyph-head" });
21751
22223
  }
21752
- function summarise6(layout) {
22224
+ function summarise7(layout) {
21753
22225
  const { ast, analysis } = layout;
21754
22226
  const parts = [
21755
22227
  `Causal loop diagram${ast.title ? ` "${ast.title}"` : ""}: ${ast.variables.length} variable${ast.variables.length === 1 ? "" : "s"}, ${ast.links.length} causal link${ast.links.length === 1 ? "" : "s"}.`
@@ -23418,7 +23890,7 @@ function renderGitGraphLayout(layout, config) {
23418
23890
  const styleBlock = buildStyle2(pal, ast.showBranches);
23419
23891
  const children = [
23420
23892
  title(a11y),
23421
- desc(summarise7(layout)),
23893
+ desc(summarise8(layout)),
23422
23894
  styleBlock,
23423
23895
  rect({ x: 0, y: 0, width, height, class: "sx-gg-bg" })
23424
23896
  ];
@@ -23668,7 +24140,7 @@ ${laneRules}
23668
24140
  function num2(n) {
23669
24141
  return Number.isInteger(n) ? String(n) : n.toFixed(2);
23670
24142
  }
23671
- function summarise7(layout) {
24143
+ function summarise8(layout) {
23672
24144
  const c = layout.replay.commits.length;
23673
24145
  const b = layout.replay.branches.length;
23674
24146
  const merges = layout.replay.commits.filter((n) => n.isMerge).length;
@@ -24509,7 +24981,7 @@ function renderEpcLayout(layout, config) {
24509
24981
  );
24510
24982
  const children = [
24511
24983
  title(a11y),
24512
- desc(summarise8(layout)),
24984
+ desc(summarise9(layout)),
24513
24985
  styleBlock,
24514
24986
  defs([
24515
24987
  el("marker", {
@@ -24645,7 +25117,7 @@ function renderEdge6(e) {
24645
25117
  if (e.edge.label) {
24646
25118
  parts.push(text(
24647
25119
  { x: e.mid.x, y: e.mid.y - 4, class: "sx-epc-edge-label", "text-anchor": "middle" },
24648
- clip4(e.edge.label, 24)
25120
+ clip5(e.edge.label, 24)
24649
25121
  ));
24650
25122
  }
24651
25123
  return group(
@@ -24653,7 +25125,7 @@ function renderEdge6(e) {
24653
25125
  parts
24654
25126
  );
24655
25127
  }
24656
- function summarise8(layout) {
25128
+ function summarise9(layout) {
24657
25129
  const { ast, analysis } = layout;
24658
25130
  const counts = { event: 0, function: 0, connector: 0 };
24659
25131
  for (const n of ast.nodes) counts[n.kind]++;
@@ -24673,7 +25145,7 @@ function describeWellFormed(analysis) {
24673
25145
  if (warns) bits.push(`${warns} warning${warns > 1 ? "s" : ""}`);
24674
25146
  return `${bits.join(", ")}.`;
24675
25147
  }
24676
- function clip4(s, n) {
25148
+ function clip5(s, n) {
24677
25149
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
24678
25150
  }
24679
25151
  function wrap5(s, perLine) {
@@ -24690,7 +25162,7 @@ function wrap5(s, perLine) {
24690
25162
  if (cur) lines.push(cur);
24691
25163
  if (lines.length > 3) {
24692
25164
  lines.length = 3;
24693
- lines[2] = clip4(lines[2], perLine);
25165
+ lines[2] = clip5(lines[2], perLine);
24694
25166
  }
24695
25167
  return lines.join("<br/>");
24696
25168
  }
@@ -25266,7 +25738,7 @@ function renderIdef0Layout(layout, config) {
25266
25738
  );
25267
25739
  const children = [
25268
25740
  title(a11y),
25269
- desc(summarise9(layout)),
25741
+ desc(summarise10(layout)),
25270
25742
  styleBlock,
25271
25743
  rect({ x: 0, y: 0, width, height, class: "sx-idef0-bg" })
25272
25744
  ];
@@ -25420,11 +25892,11 @@ function renderTitleBlock(width, height, node, title2) {
25420
25892
  text({ x: x0 + 6, y: y + 13, class: "sx-idef0-tb-key" }, "NODE"),
25421
25893
  text({ x: x0 + 6, y: y + 27, class: "sx-idef0-tb-text" }, node),
25422
25894
  text({ x: c1 + 6, y: y + 13, class: "sx-idef0-tb-key" }, "TITLE"),
25423
- text({ x: c1 + 6, y: y + 27, class: "sx-idef0-tb-text" }, clip5(title2 || "\u2014", 60)),
25895
+ text({ x: c1 + 6, y: y + 27, class: "sx-idef0-tb-text" }, clip6(title2 || "\u2014", 60)),
25424
25896
  text({ x: c2 + 6, y: y + 13, class: "sx-idef0-tb-key" }, "NUMBER")
25425
25897
  ]);
25426
25898
  }
25427
- function summarise9(layout) {
25899
+ function summarise10(layout) {
25428
25900
  const { ast } = layout;
25429
25901
  const counts = {};
25430
25902
  for (const a of ast.arrows) counts[a.role] = (counts[a.role] ?? 0) + 1;
@@ -25439,7 +25911,7 @@ function summarise9(layout) {
25439
25911
  for (const w of ast.warnings) parts.push(w);
25440
25912
  return parts.join(" ");
25441
25913
  }
25442
- function clip5(s, n) {
25914
+ function clip6(s, n) {
25443
25915
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
25444
25916
  }
25445
25917
  function wrap6(s, perLine) {
@@ -25458,7 +25930,7 @@ function wrap6(s, perLine) {
25458
25930
  if (cur) lines.push(cur);
25459
25931
  if (lines.length > 3) {
25460
25932
  lines.length = 3;
25461
- lines[2] = clip5(lines[2], perLine);
25933
+ lines[2] = clip6(lines[2], perLine);
25462
25934
  }
25463
25935
  return lines.join("<br/>");
25464
25936
  }
@@ -26085,7 +26557,7 @@ function renderThreatModelLayout(layout, config) {
26085
26557
  ]);
26086
26558
  const children = [
26087
26559
  title(a11y),
26088
- desc(summarise10(layout)),
26560
+ desc(summarise11(layout)),
26089
26561
  styleBlock,
26090
26562
  markerDefs,
26091
26563
  rect({ x: 0, y: 0, width, height, class: "sx-tm-bg" })
@@ -26302,7 +26774,7 @@ function arrowMarker2(id, cls, crossing) {
26302
26774
  function round8(n) {
26303
26775
  return Math.round(n * 100) / 100;
26304
26776
  }
26305
- function summarise10(layout) {
26777
+ function summarise11(layout) {
26306
26778
  const a = layout.analysis;
26307
26779
  const counts = {
26308
26780
  external: layout.nodes.filter((n) => n.kind === "external").length,
@@ -40555,7 +41027,7 @@ function renderPlaybookLayout(lay, config) {
40555
41027
  const surfaceBase = rect({ class: surfaceCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
40556
41028
  const boundary = rect({ class: boundaryCls, x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx });
40557
41029
  const clipId = "sx-pb-clip";
40558
- const clip6 = el("clipPath", { id: clipId }, [rect({ x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx })]);
41030
+ const clip7 = el("clipPath", { id: clipId }, [rect({ x: EDGE, y: r28(fieldTop), width: r28(fieldW), height: r28(fieldH), rx: fieldRx })]);
40559
41031
  const field = group({ "clip-path": `url(#${clipId})` }, [mod.drawField(lay, ctx, t)]);
40560
41032
  const zones = [];
40561
41033
  for (const z of lay.zones) {
@@ -40583,7 +41055,7 @@ function renderPlaybookLayout(lay, config) {
40583
41055
  desc(descText),
40584
41056
  el("style", {}, buildCss14(t)),
40585
41057
  rect({ fill: t.bg, x: 0, y: 0, width: W2, height: H2 }),
40586
- el("defs", {}, [clip6]),
41058
+ el("defs", {}, [clip7]),
40587
41059
  text({ class: "sx-pb-title", x: r28(W2 / 2), y: TITLE.y, "text-anchor": "middle" }, lay.title),
40588
41060
  ...annoParts,
40589
41061
  surround,
@@ -40843,5 +41315,5 @@ function renderWithPlugin(prepared, plugin, config) {
40843
41315
  }
40844
41316
 
40845
41317
  export { FLOORPLAN_SYMBOLS, GEOMETRY, bowtie2 as bowtie, causalloop, decisiontree, drawDeviceIcon, epc, eventtree, faulttree, fmea, gitgraph, iconSize, idef0, markov, network, parse, parseResult, pert, petri, pid, prisma, rbd, render, renderEquip, renderPreview, renderResult, sequence, state, threatmodel, timeline, umlclass, usecase, welding };
40846
- //# sourceMappingURL=chunk-LMNWUZMD.js.map
40847
- //# sourceMappingURL=chunk-LMNWUZMD.js.map
41318
+ //# sourceMappingURL=chunk-3K4WCRVI.js.map
41319
+ //# sourceMappingURL=chunk-3K4WCRVI.js.map