@diagrammo/dgmo 0.7.1 → 0.7.3

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/index.cjs CHANGED
@@ -7986,7 +7986,9 @@ function parseGantt(content, palette) {
7986
7986
  criticalPath: false,
7987
7987
  dependencies: false,
7988
7988
  sort: "default",
7989
- defaultSwimlaneGroup: null
7989
+ defaultSwimlaneGroup: null,
7990
+ optionLineNumbers: {},
7991
+ holidaysLineNumber: null
7990
7992
  },
7991
7993
  diagnostics,
7992
7994
  error: null
@@ -8171,6 +8173,7 @@ function parseGantt(content, palette) {
8171
8173
  inHolidaysBlock = true;
8172
8174
  holidaysBlockIndent = indent;
8173
8175
  inHeaderBlock = false;
8176
+ result.options.holidaysLineNumber = lineNumber;
8174
8177
  continue;
8175
8178
  }
8176
8179
  const tagMatch = matchTagBlockHeading(line10);
@@ -8197,7 +8200,8 @@ function parseGantt(content, palette) {
8197
8200
  startDate: eraMatch[1],
8198
8201
  endDate: eraMatch[2],
8199
8202
  label: eraExtracted.label,
8200
- color: eraExtracted.color || null
8203
+ color: eraExtracted.color || null,
8204
+ lineNumber
8201
8205
  });
8202
8206
  inHeaderBlock = false;
8203
8207
  continue;
@@ -8219,6 +8223,7 @@ function parseGantt(content, palette) {
8219
8223
  if (optMatch && isKnownOption(optMatch[1].toLowerCase())) {
8220
8224
  const key = optMatch[1].toLowerCase();
8221
8225
  const value = optMatch[2].trim();
8226
+ result.options.optionLineNumbers[key] = lineNumber;
8222
8227
  switch (key) {
8223
8228
  case "start":
8224
8229
  result.options.start = value;
@@ -13184,6 +13189,9 @@ function renderInitiativeStatus(container, parsed, layout, palette, isDark, opti
13184
13189
  const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
13185
13190
  const groupW = isActive ? lg.width : pillW;
13186
13191
  const gEl = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "is-legend-group").attr("data-legend-group", lg.key).style("cursor", "pointer");
13192
+ if (!isActive) {
13193
+ gEl.attr("data-export-ignore", "true");
13194
+ }
13187
13195
  if (isActive) {
13188
13196
  gEl.append("rect").attr("width", groupW).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
13189
13197
  }
@@ -13196,17 +13204,32 @@ function renderInitiativeStatus(container, parsed, layout, palette, isDark, opti
13196
13204
  gEl.append("text").attr("x", pillXOff + pillW / 2).attr("y", LEGEND_HEIGHT / 2 + LEGEND_PILL_FONT_SIZE / 2 - 2).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", "500").attr("fill", isActive ? palette.text : palette.textMuted).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).text(lg.name);
13197
13205
  if (isActive) {
13198
13206
  const hiddenSet = !lg.isStatus ? hiddenTagValues?.get(lg.key) : void 0;
13199
- let entryX = pillXOff + pillW + 4;
13207
+ const entryStartX = pillXOff + pillW + 4;
13208
+ const entryData = [];
13209
+ let estimatedX = entryStartX;
13200
13210
  for (const entry of lg.entries) {
13201
13211
  const isHidden = hiddenSet?.has(entry.value) ?? false;
13202
- const entryG = gEl.append("g").attr("data-legend-entry", entry.value).style("cursor", !lg.isStatus ? "pointer" : "default");
13212
+ const estimatedTextW = entry.label.length * LEGEND_ENTRY_FONT_W;
13213
+ const entryG = gEl.append("g").attr("data-legend-entry", entry.value).attr("transform", `translate(${estimatedX}, 0)`).style("cursor", "pointer");
13214
+ const entryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
13215
+ entryG.append("rect").attr("x", -2).attr("y", 0).attr("width", entryW + 4).attr("height", LEGEND_HEIGHT).attr("fill", "transparent");
13203
13216
  if (isHidden) {
13204
- entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", "none").attr("stroke", entry.color).attr("stroke-width", 1.2).attr("opacity", 0.5);
13217
+ entryG.append("circle").attr("cx", LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", "none").attr("stroke", entry.color).attr("stroke-width", 1.2).attr("opacity", 0.5);
13205
13218
  } else {
13206
- entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
13219
+ entryG.append("circle").attr("cx", LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
13207
13220
  }
13208
- entryG.append("text").attr("x", entryX + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("font-family", FONT_FAMILY).attr("opacity", isHidden ? 0.4 : 1).attr("text-decoration", isHidden ? "line-through" : "none").text(entry.label);
13209
- entryX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + entry.label.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
13221
+ const textEl = entryG.append("text").attr("x", LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 1).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("font-family", FONT_FAMILY).attr("opacity", isHidden ? 0.4 : 1).attr("text-decoration", isHidden ? "line-through" : "none").text(entry.label);
13222
+ entryData.push({ g: entryG, textEl: textEl.node(), estimatedW: estimatedTextW });
13223
+ estimatedX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
13224
+ }
13225
+ let reflowX = entryStartX;
13226
+ for (const ed of entryData) {
13227
+ const measuredW = ed.textEl.getComputedTextLength?.() ?? 0;
13228
+ const textW = measuredW > 0 ? measuredW : ed.estimatedW;
13229
+ ed.g.attr("transform", `translate(${reflowX}, 0)`);
13230
+ const actualEntryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + textW + LEGEND_ENTRY_TRAIL;
13231
+ ed.g.select("rect").attr("width", actualEntryW + 4);
13232
+ reflowX += actualEntryW;
13210
13233
  }
13211
13234
  }
13212
13235
  cursorX += groupW + LEGEND_GROUP_GAP;
@@ -16129,7 +16152,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
16129
16152
  }
16130
16153
  const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
16131
16154
  const LABEL_CHAR_W = 7;
16132
- const LABEL_PAD = 8;
16155
+ const LABEL_PAD2 = 8;
16133
16156
  const LABEL_H = 16;
16134
16157
  const PERP_OFFSET = 10;
16135
16158
  const labelPositions = [];
@@ -16138,7 +16161,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
16138
16161
  if (!edge.label || edge.points.length < 2) continue;
16139
16162
  const midIdx = Math.floor(edge.points.length / 2);
16140
16163
  const midPt = edge.points[midIdx];
16141
- const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD;
16164
+ const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD2;
16142
16165
  const prev = edge.points[Math.max(0, midIdx - 1)];
16143
16166
  const next = edge.points[Math.min(edge.points.length - 1, midIdx + 1)];
16144
16167
  const dx = next.x - prev.x;
@@ -19381,8 +19404,46 @@ __export(renderer_exports9, {
19381
19404
  buildTagLaneRowList: () => buildTagLaneRowList,
19382
19405
  renderGantt: () => renderGantt
19383
19406
  });
19407
+ function computeBarLabel(label, x1, barWidth, innerWidth, textColor) {
19408
+ const textWidth = label.length * CHAR_W;
19409
+ const x2 = x1 + barWidth;
19410
+ if (textWidth < barWidth - LABEL_PAD) {
19411
+ return { x: x1 + 6, anchor: "start", fill: textColor, text: label };
19412
+ }
19413
+ if (x2 + LABEL_GAP + textWidth <= innerWidth) {
19414
+ return { x: x2 + LABEL_GAP, anchor: "start", fill: textColor, text: label };
19415
+ }
19416
+ if (x1 - LABEL_GAP - textWidth >= 0) {
19417
+ return { x: x1 - LABEL_GAP, anchor: "end", fill: textColor, text: label };
19418
+ }
19419
+ const availWidth = x1 - LABEL_GAP;
19420
+ if (availWidth > CHAR_W * 3) {
19421
+ const maxChars = Math.floor(availWidth / CHAR_W) - 1;
19422
+ return { x: x1 - LABEL_GAP, anchor: "end", fill: textColor, text: label.slice(0, maxChars) + "\u2026" };
19423
+ }
19424
+ return null;
19425
+ }
19426
+ function renderLabelBand(svg, y, leftMargin, color, palette, cssPrefix, dataAttr) {
19427
+ const bandX = 5;
19428
+ const bandW = leftMargin - 7;
19429
+ const bandY = y - BAR_H / 2;
19430
+ const clipId = `gantt-band-clip-${bandClipCounter++}`;
19431
+ svg.append("clipPath").attr("id", clipId).append("rect").attr("x", bandX).attr("y", bandY).attr("width", bandW).attr("height", BAR_H).attr("rx", BAND_RADIUS);
19432
+ const tint2 = svg.append("rect").attr("class", `gantt-${cssPrefix}-band-bg`).attr("x", bandX).attr("y", bandY).attr("width", bandW).attr("height", BAR_H).attr("rx", BAND_RADIUS).attr("fill", mix(color, palette.bg, 20)).style("pointer-events", "none");
19433
+ const accent = svg.append("rect").attr("class", `gantt-${cssPrefix}-band-accent`).attr("x", bandX).attr("y", bandY).attr("width", BAND_ACCENT_W).attr("height", BAR_H).attr("fill", color).attr("clip-path", `url(#${clipId})`).style("pointer-events", "none");
19434
+ if (dataAttr) {
19435
+ tint2.attr(dataAttr.key, dataAttr.value);
19436
+ accent.attr(dataAttr.key, dataAttr.value);
19437
+ }
19438
+ }
19439
+ function appendTaskIcon(textEl, label, isMilestone, iconColor, textColor) {
19440
+ const icon = isMilestone ? "\u25C6" : "\u25CF";
19441
+ textEl.append("tspan").attr("fill", iconColor).text(icon);
19442
+ textEl.append("tspan").attr("fill", textColor).text(" " + label);
19443
+ }
19384
19444
  function renderGantt(container, resolved, palette, isDark, options, exportDims) {
19385
19445
  container.innerHTML = "";
19446
+ bandClipCounter = 0;
19386
19447
  if (resolved.tasks.length === 0) return;
19387
19448
  const onClickItem = options?.onClickItem;
19388
19449
  const collapsedGroups = options?.collapsedGroups;
@@ -19401,10 +19462,13 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19401
19462
  const isTagMode = tagRows !== null;
19402
19463
  const allLabels = isTagMode ? [
19403
19464
  ...rows.filter((r) => r.type === "lane-header").map((r) => r.laneName),
19404
- ...rows.filter((r) => r.type === "task").map((r) => r.task.task.label)
19465
+ ...rows.filter((r) => r.type === "task").map((r) => "\u25CF " + r.task.task.label)
19405
19466
  ] : [
19406
- ...resolved.tasks.map((t) => t.task.label),
19407
- ...resolved.groups.map((g2) => " ".repeat(g2.depth) + g2.name)
19467
+ ...resolved.tasks.map((t) => "\u25CF " + t.task.label),
19468
+ ...resolved.groups.map((g2) => {
19469
+ const px = g2.depth <= 2 ? g2.depth * 14 : 2 * 14 + (g2.depth - 2) * 8;
19470
+ return " ".repeat(Math.ceil(px / 7)) + g2.name;
19471
+ })
19408
19472
  ];
19409
19473
  const maxLabelLen = Math.max(...allLabels.map((l) => l.length), 10);
19410
19474
  const leftMargin = Math.max(MIN_LEFT_MARGIN, maxLabelLen * 7 + 30);
@@ -19413,8 +19477,10 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19413
19477
  const titleHeight = title ? 50 : 20;
19414
19478
  const tagLegendReserve = resolved.tagGroups.length > 0 ? LEGEND_HEIGHT + 8 : 0;
19415
19479
  const topDateLabelReserve = 22;
19480
+ const hasOverheadLabels = resolved.markers.length > 0 || resolved.eras.length > 0;
19481
+ const markerLabelReserve = hasOverheadLabels ? 18 : 0;
19416
19482
  const CONTENT_TOP_PAD = 16;
19417
- const marginTop = titleHeight + tagLegendReserve + topDateLabelReserve;
19483
+ const marginTop = titleHeight + tagLegendReserve + topDateLabelReserve + markerLabelReserve;
19418
19484
  const contentH = isTagMode ? totalRows * (BAR_H + ROW_GAP) : totalRows * (BAR_H + ROW_GAP) + GROUP_GAP2 * resolved.groups.length;
19419
19485
  const innerHeight = CONTENT_TOP_PAD + contentH;
19420
19486
  const outerHeight = marginTop + innerHeight + BOTTOM_MARGIN;
@@ -19442,6 +19508,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19442
19508
  isDark,
19443
19509
  hasCriticalPath,
19444
19510
  criticalPathActive,
19511
+ resolved.options.optionLineNumbers,
19445
19512
  (groupName) => {
19446
19513
  currentActiveGroup = currentActiveGroup?.toLowerCase() === groupName.toLowerCase() ? null : groupName;
19447
19514
  if (onActiveGroupChange) onActiveGroupChange(currentActiveGroup);
@@ -19480,7 +19547,25 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19480
19547
  renderTimeScaleHorizontal(g, xScale, innerWidth, innerHeight, palette.text);
19481
19548
  renderWeekendBands(g, resolved, xScale, innerHeight, palette, isDark);
19482
19549
  renderHolidayBands(g, svg, resolved, xScale, innerHeight, palette, isDark, marginTop - 4, leftMargin, onClickItem);
19483
- renderErasAndMarkers(g, resolved, xScale, innerHeight, palette);
19550
+ renderErasAndMarkers(g, svg, resolved, xScale, innerHeight, palette);
19551
+ let todayDate = null;
19552
+ let todayX = -1;
19553
+ const todayColor = palette.accent || "#e74c3c";
19554
+ const todayMarkerLineNum = resolved.options.optionLineNumbers["today-marker"];
19555
+ if (resolved.options.todayMarker !== "off") {
19556
+ if (resolved.options.todayMarker === "on") {
19557
+ todayDate = /* @__PURE__ */ new Date();
19558
+ } else {
19559
+ todayDate = /* @__PURE__ */ new Date(resolved.options.todayMarker + "T00:00:00");
19560
+ }
19561
+ todayX = xScale(dateToFractionalYear(todayDate));
19562
+ if (todayX >= 0 && todayX <= innerWidth) {
19563
+ const todayLine = g.append("line").attr("class", "gantt-today").attr("x1", todayX).attr("y1", 0).attr("x2", todayX).attr("y2", innerHeight + 10).attr("stroke", todayColor).attr("stroke-width", 2).attr("stroke-dasharray", "6 4").attr("opacity", 0.7).attr("pointer-events", "none");
19564
+ if (todayMarkerLineNum) todayLine.attr("data-line-number", String(todayMarkerLineNum));
19565
+ const todayLabel = g.append("text").attr("class", "gantt-today").attr("x", todayX).attr("y", innerHeight + 24).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", todayColor).attr("opacity", 0.7).attr("pointer-events", "none").text("Today");
19566
+ if (todayMarkerLineNum) todayLabel.attr("data-line-number", String(todayMarkerLineNum));
19567
+ }
19568
+ }
19484
19569
  const taskPositions = /* @__PURE__ */ new Map();
19485
19570
  const groupPositions = /* @__PURE__ */ new Map();
19486
19571
  const lanePositions = /* @__PURE__ */ new Map();
@@ -19516,6 +19601,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19516
19601
  laneBarWidth = Math.max(lx2 - lx1, 2);
19517
19602
  }
19518
19603
  lanePositions.set(row.laneName, { x1: lx1, x2: lx1 + laneBarWidth, y: yOffset + BAR_H / 2 });
19604
+ renderLabelBand(svg, marginTop + yOffset + BAR_H / 2, leftMargin, laneColor, palette, "lane", { key: "data-lane", value: row.laneName });
19519
19605
  const labelG = svg.append("g").attr("class", "gantt-lane-header").attr(`data-tag-${row.tagKey}`, row.laneName.toLowerCase()).attr("data-lane", row.laneName).style("cursor", onToggleLane ? "pointer" : "default").on("click", () => {
19520
19606
  if (onToggleLane) onToggleLane(row.laneName);
19521
19607
  }).on("mouseenter", () => {
@@ -19541,7 +19627,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19541
19627
  });
19542
19628
  laneBandG.append("rect").attr("class", "gantt-lane-band").attr("x", lx1).attr("y", yOffset).attr("width", laneBarWidth).attr("height", BAR_H).attr("rx", 4).attr("fill", barFill).attr("stroke", laneColor).attr("stroke-width", 2);
19543
19629
  if (row.aggregateProgress !== null && row.aggregateProgress > 0) {
19544
- laneBandG.append("rect").attr("class", "gantt-lane-progress").attr("x", lx1).attr("y", yOffset).attr("width", laneBarWidth * Math.min(row.aggregateProgress / 100, 1)).attr("height", BAR_H).attr("rx", 4).attr("fill", laneColor).attr("opacity", 0.5).attr("pointer-events", "none");
19630
+ laneBandG.append("rect").attr("class", "gantt-lane-progress").attr("x", lx1).attr("y", yOffset).attr("width", laneBarWidth * Math.min(row.aggregateProgress / 100, 1)).attr("height", BAR_H).attr("fill", laneColor).attr("opacity", 0.5).attr("pointer-events", "none");
19545
19631
  }
19546
19632
  }
19547
19633
  yOffset += BAR_H + ROW_GAP;
@@ -19552,6 +19638,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19552
19638
  const toggleIcon = isCollapsed ? "\u25BA" : "\u25BC";
19553
19639
  const tagColor = resolveTagColor(group.metadata, resolved.tagGroups, currentActiveGroup, true);
19554
19640
  const groupColor = tagColor && tagColor !== "#999999" ? tagColor : group.color || palette.textMuted;
19641
+ renderLabelBand(svg, marginTop + yOffset + BAR_H / 2, leftMargin, groupColor, palette, "group", { key: "data-group", value: group.name });
19555
19642
  const labelG = svg.append("g").attr("class", "gantt-group-label").attr("data-group", group.name).attr("data-line-number", String(group.lineNumber)).style("cursor", onToggleGroup ? "pointer" : "default").on("click", () => {
19556
19643
  if (onToggleGroup) onToggleGroup(group.name);
19557
19644
  }).on("mouseenter", () => {
@@ -19561,7 +19648,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19561
19648
  resetHighlight(g, svg);
19562
19649
  hideGanttDateIndicators(g);
19563
19650
  });
19564
- const labelX = 10 + group.depth * 14;
19651
+ const groupIndent = group.depth <= 2 ? group.depth * 14 : 2 * 14 + (group.depth - 2) * 8;
19652
+ const labelX = 10 + groupIndent;
19565
19653
  labelG.append("text").attr("x", labelX).attr("y", marginTop + yOffset + BAR_H / 2).attr("dy", "0.35em").attr("text-anchor", "start").attr("font-size", "11px").attr("font-weight", "bold").attr("fill", palette.text).text(toggleIcon + " " + group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : ""));
19566
19654
  const gStart = dateToFractionalYear(group.startDate);
19567
19655
  const gEnd = dateToFractionalYear(group.endDate);
@@ -19579,7 +19667,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19579
19667
  });
19580
19668
  summaryG.append("rect").attr("x", gx1).attr("y", yOffset).attr("width", barWidth).attr("height", BAR_H).attr("rx", 4).attr("fill", mix(groupColor, palette.bg, 30)).attr("stroke", groupColor).attr("stroke-width", 2);
19581
19669
  if (group.progress !== null && group.progress > 0) {
19582
- summaryG.append("rect").attr("x", gx1).attr("y", yOffset).attr("width", barWidth * Math.min(group.progress / 100, 1)).attr("height", BAR_H).attr("rx", 4).attr("fill", groupColor).attr("opacity", 0.5);
19670
+ summaryG.append("rect").attr("x", gx1).attr("y", yOffset).attr("width", barWidth * Math.min(group.progress / 100, 1)).attr("height", BAR_H).attr("fill", groupColor).attr("opacity", 0.5);
19671
+ }
19672
+ const summaryLabel = group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : "");
19673
+ const summaryPlacement = computeBarLabel(summaryLabel, gx1, barWidth, innerWidth, palette.text);
19674
+ if (summaryPlacement) {
19675
+ summaryG.append("text").attr("x", summaryPlacement.x).attr("y", yOffset + BAR_H / 2).attr("dy", "0.35em").attr("font-size", "10px").attr("font-weight", "bold").attr("text-anchor", summaryPlacement.anchor).attr("fill", summaryPlacement.fill).attr("pointer-events", "none").text(summaryPlacement.text);
19583
19676
  }
19584
19677
  groupPositions.set(group.name, { x1: gx1, x2: gx1 + barWidth, y: yOffset + BAR_H / 2 });
19585
19678
  } else {
@@ -19594,7 +19687,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19594
19687
  });
19595
19688
  groupBarG.append("rect").attr("x", gx1).attr("y", yOffset).attr("width", groupBarWidth).attr("height", BAR_H).attr("rx", 4).attr("fill", bandFill).attr("stroke", groupColor).attr("stroke-width", 2);
19596
19689
  if (group.progress !== null && group.progress > 0) {
19597
- groupBarG.append("rect").attr("class", "gantt-group-progress").attr("x", gx1).attr("y", yOffset).attr("width", groupBarWidth * Math.min(group.progress / 100, 1)).attr("height", BAR_H).attr("rx", 4).attr("fill", groupColor).attr("opacity", 0.5);
19690
+ groupBarG.append("rect").attr("class", "gantt-group-progress").attr("x", gx1).attr("y", yOffset).attr("width", groupBarWidth * Math.min(group.progress / 100, 1)).attr("height", BAR_H).attr("fill", groupColor).attr("opacity", 0.5);
19691
+ }
19692
+ const expandedLabel = group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : "");
19693
+ const expandedPlacement = computeBarLabel(expandedLabel, gx1, groupBarWidth, innerWidth, palette.text);
19694
+ if (expandedPlacement) {
19695
+ groupBarG.append("text").attr("x", expandedPlacement.x).attr("y", yOffset + BAR_H / 2).attr("dy", "0.35em").attr("font-size", "10px").attr("font-weight", "bold").attr("text-anchor", expandedPlacement.anchor).attr("fill", expandedPlacement.fill).attr("pointer-events", "none").text(expandedPlacement.text);
19598
19696
  }
19599
19697
  }
19600
19698
  }
@@ -19602,9 +19700,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19602
19700
  } else if (row.type === "task") {
19603
19701
  const rt = row.task;
19604
19702
  const task = rt.task;
19605
- const taskLabelX = isTagMode ? 20 : 6 + rt.groupPath.length * 14;
19703
+ const barColor = resolveTaskColor(rt, currentActiveGroup, resolved, seriesColors2, palette);
19704
+ const depth = rt.groupPath.length;
19705
+ const indent = depth <= 2 ? depth * 14 : 2 * 14 + (depth - 2) * 8;
19706
+ const taskLabelX = isTagMode ? 20 : 6 + indent;
19606
19707
  const topGroup = rt.groupPath.length > 0 ? rt.groupPath[0] : null;
19607
- const taskLabel = svg.append("text").attr("class", "gantt-task-label").attr("x", taskLabelX).attr("y", marginTop + yOffset + BAR_H / 2).attr("dy", "0.35em").attr("text-anchor", "start").attr("font-size", "11px").attr("fill", palette.text).attr("data-line-number", String(task.lineNumber)).attr("data-task-id", task.id).attr("data-group", topGroup).style("cursor", onClickItem ? "pointer" : "default").text(task.label).on("click", () => {
19708
+ const taskLabel = svg.append("text").attr("class", "gantt-task-label").attr("x", taskLabelX).attr("y", marginTop + yOffset + BAR_H / 2).attr("dy", "0.35em").attr("text-anchor", "start").attr("font-size", "11px").attr("fill", palette.text).attr("data-line-number", String(task.lineNumber)).attr("data-task-id", task.id).attr("data-group", topGroup).style("cursor", onClickItem ? "pointer" : "default").on("click", () => {
19608
19709
  if (onClickItem) onClickItem(task.lineNumber);
19609
19710
  }).on("mouseenter", () => {
19610
19711
  if (rt.isMilestone) {
@@ -19615,13 +19716,13 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19615
19716
  }).on("mouseleave", () => {
19616
19717
  resetHighlight(g, svg);
19617
19718
  });
19719
+ appendTaskIcon(taskLabel, task.label, rt.isMilestone, barColor, palette.text);
19618
19720
  for (const [key, value] of Object.entries(rt.effectiveMetadata)) {
19619
19721
  taskLabel.attr(`data-tag-${key}`, value.toLowerCase());
19620
19722
  }
19621
19723
  if (rt.isCriticalPath) {
19622
19724
  taskLabel.attr("data-critical-path", "true");
19623
19725
  }
19624
- let barColor = resolveTaskColor(rt, currentActiveGroup, resolved, seriesColors2, palette);
19625
19726
  if (rt.isMilestone) {
19626
19727
  const mx = xScale(dateToFractionalYear(rt.startDate));
19627
19728
  const my = yOffset + BAR_H / 2;
@@ -19699,32 +19800,40 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19699
19800
  progGrad.append("stop").attr("offset", "100%").attr("stop-color", barColor).attr("stop-opacity", 0);
19700
19801
  progressFill = `url(#${progGradId})`;
19701
19802
  }
19702
- taskG.append("rect").attr("class", "gantt-progress").attr("x", x1).attr("y", yOffset).attr("width", progressWidth).attr("height", BAR_H).attr("rx", 4).attr("fill", progressFill).attr("opacity", 0.5);
19803
+ taskG.append("rect").attr("class", "gantt-progress").attr("x", x1).attr("y", yOffset).attr("width", progressWidth).attr("height", BAR_H).attr("fill", progressFill).attr("opacity", 0.5);
19703
19804
  }
19704
19805
  if (rt.isCriticalPath) {
19705
19806
  taskG.attr("data-critical-path", "true");
19706
19807
  }
19707
- const textWidth = task.label.length * 6.5;
19708
- if (textWidth < barWidth - 8) {
19709
- taskG.append("text").attr("x", x1 + 6).attr("y", yOffset + BAR_H / 2).attr("dy", "0.35em").attr("font-size", "10px").attr("fill", palette.text).attr("pointer-events", "none").text(task.label);
19808
+ const labelPlacement = computeBarLabel(task.label, x1, barWidth, innerWidth, palette.text);
19809
+ if (labelPlacement) {
19810
+ taskG.append("text").attr("x", labelPlacement.x).attr("y", yOffset + BAR_H / 2).attr("dy", "0.35em").attr("font-size", "10px").attr("text-anchor", labelPlacement.anchor).attr("fill", labelPlacement.fill).attr("pointer-events", "none").text(labelPlacement.text);
19710
19811
  }
19711
19812
  taskPositions.set(task.id, { x1, x2: x1 + barWidth, y: yOffset + BAR_H / 2 });
19712
19813
  }
19713
19814
  yOffset += BAR_H + ROW_GAP;
19714
19815
  }
19715
19816
  }
19716
- if (resolved.options.todayMarker !== "off") {
19717
- let todayDate;
19718
- if (resolved.options.todayMarker === "on") {
19719
- todayDate = /* @__PURE__ */ new Date();
19720
- } else {
19721
- todayDate = /* @__PURE__ */ new Date(resolved.options.todayMarker + "T00:00:00");
19722
- }
19723
- const todayX = xScale(dateToFractionalYear(todayDate));
19724
- if (todayX >= 0 && todayX <= innerWidth) {
19725
- g.append("line").attr("class", "gantt-today").attr("x1", todayX).attr("y1", 0).attr("x2", todayX).attr("y2", innerHeight + 10).attr("stroke", palette.accent || "#e74c3c").attr("stroke-width", 2).attr("stroke-dasharray", "6 4").attr("opacity", 0.7);
19726
- g.append("text").attr("class", "gantt-today").attr("x", todayX + 4).attr("y", innerHeight + 24).attr("text-anchor", "start").attr("font-size", "10px").attr("fill", palette.accent || "#e74c3c").attr("opacity", 0.7).text("Today");
19727
- }
19817
+ if (todayDate && todayX >= 0 && todayX <= innerWidth) {
19818
+ const todayHoverG = g.append("g").attr("class", "gantt-today-hover").style("cursor", "pointer");
19819
+ todayHoverG.append("rect").attr("x", todayX - 10).attr("y", -6).attr("width", 20).attr("height", innerHeight + 16).attr("fill", "transparent").attr("pointer-events", "all");
19820
+ const todayDateObj = todayDate;
19821
+ todayHoverG.on("mouseenter", () => {
19822
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
19823
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
19824
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
19825
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
19826
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
19827
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
19828
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
19829
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
19830
+ g.selectAll(".gantt-era-group").attr("opacity", FADE_OPACITY);
19831
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
19832
+ showGanttDateIndicators(g, xScale, todayDateObj, null, innerHeight, todayColor);
19833
+ }).on("mouseleave", () => {
19834
+ resetHighlight(g, svg);
19835
+ hideGanttDateIndicators(g);
19836
+ });
19728
19837
  }
19729
19838
  if (resolved.options.dependencies) {
19730
19839
  renderDependencyArrows(g, resolved, taskPositions, groupPositions, collapsedGroups, palette, isDark, isTagMode, lanePositions, collapsedLanes, taskLaneMap);
@@ -19841,10 +19950,10 @@ function renderDependencyArrows(g, resolved, taskPositions, groupPositions, coll
19841
19950
  const path = `M ${sx} ${sy} C ${sx + cpOffset} ${sy}, ${tx - cpOffset} ${ty}, ${tx} ${ty}`;
19842
19951
  const arrowColor = mix(palette.text, palette.bg, 50);
19843
19952
  const isCpArrow = rt.isCriticalPath && targetTask.isCriticalPath;
19844
- g.append("path").attr("class", "gantt-dep-arrow").attr("data-dep-from", rt.task.id).attr("data-dep-to", targetTask.task.id).attr("data-critical-path", isCpArrow ? "true" : null).attr("d", path).attr("fill", "none").attr("stroke", arrowColor).attr("stroke-width", 1.5).attr("opacity", 0.5);
19953
+ g.append("path").attr("class", "gantt-dep-arrow").attr("data-dep-from", rt.task.id).attr("data-dep-to", targetTask.task.id).attr("data-line-number", String(dep.lineNumber)).attr("data-critical-path", isCpArrow ? "true" : null).attr("d", path).attr("fill", "none").attr("stroke", arrowColor).attr("stroke-width", 1.5).attr("opacity", 0.5);
19845
19954
  const headSize = 5;
19846
19955
  const angle = 0;
19847
- g.append("polygon").attr("class", "gantt-dep-arrowhead").attr("data-dep-from", rt.task.id).attr("data-dep-to", targetTask.task.id).attr("data-critical-path", isCpArrow ? "true" : null).attr("points", arrowheadPoints(tx, ty, headSize, angle)).attr("fill", arrowColor).attr("opacity", 0.5);
19956
+ g.append("polygon").attr("class", "gantt-dep-arrowhead").attr("data-dep-from", rt.task.id).attr("data-dep-to", targetTask.task.id).attr("data-line-number", String(dep.lineNumber)).attr("data-critical-path", isCpArrow ? "true" : null).attr("points", arrowheadPoints(tx, ty, headSize, angle)).attr("fill", arrowColor).attr("opacity", 0.5);
19848
19957
  }
19849
19958
  }
19850
19959
  }
@@ -19865,7 +19974,9 @@ function applyCriticalPathHighlight(svg, chartG) {
19865
19974
  el.attr("opacity", el.attr("data-critical-path") === "true" ? 1 : FADE_OPACITY);
19866
19975
  });
19867
19976
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
19977
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
19868
19978
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
19979
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
19869
19980
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", FADE_OPACITY);
19870
19981
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").each(function() {
19871
19982
  const el = d3Selection10.select(this);
@@ -19877,7 +19988,9 @@ function resetHighlightAll(svg, chartG) {
19877
19988
  chartG.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", 1);
19878
19989
  svg.selectAll(".gantt-task-label").attr("opacity", 1);
19879
19990
  svg.selectAll(".gantt-group-label").attr("opacity", 1);
19991
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", 1);
19880
19992
  svg.selectAll(".gantt-lane-header").attr("opacity", 1);
19993
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", 1);
19881
19994
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", 1);
19882
19995
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
19883
19996
  }
@@ -19893,7 +20006,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
19893
20006
  }
19894
20007
  return iconG;
19895
20008
  }
19896
- function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, onToggle, onToggleCriticalPath, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks) {
20009
+ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, optionLineNumbers, onToggle, onToggleCriticalPath, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks) {
19897
20010
  const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
19898
20011
  let visibleGroups;
19899
20012
  if (activeGroupName) {
@@ -19954,7 +20067,8 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
19954
20067
  if (visibleGroups.length > 0) totalW += LEGEND_GROUP_GAP;
19955
20068
  totalW += cpPillW;
19956
20069
  }
19957
- const legendX = chartLeftMargin + (chartInnerWidth - totalW) / 2;
20070
+ const containerWidth = chartLeftMargin + chartInnerWidth + RIGHT_MARGIN;
20071
+ const legendX = (containerWidth - totalW) / 2;
19958
20072
  const legendRow = svg.append("g").attr("class", "gantt-tag-legend-container").attr("transform", `translate(${legendX}, ${legendY})`);
19959
20073
  let cursorX = 0;
19960
20074
  for (let i = 0; i < visibleGroups.length; i++) {
@@ -19966,7 +20080,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
19966
20080
  const pillW = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD + iconReserve;
19967
20081
  const pillH = isActive ? LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2 : LEGEND_HEIGHT;
19968
20082
  const groupW = groupWidths[i];
19969
- const gEl = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "gantt-tag-legend-group").attr("data-tag-group", group.name).style("cursor", "pointer").on("click", () => {
20083
+ const gEl = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "gantt-tag-legend-group").attr("data-tag-group", group.name).attr("data-line-number", String(group.lineNumber)).style("cursor", "pointer").on("click", () => {
19970
20084
  if (onToggle) onToggle(group.name);
19971
20085
  });
19972
20086
  if (isActive) {
@@ -20000,7 +20114,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
20000
20114
  let ex = pillXOff + pillW + LEGEND_CAPSULE_PAD + 4;
20001
20115
  for (const entry of entries) {
20002
20116
  const entryValue = entry.value.toLowerCase();
20003
- const entryG = gEl.append("g").attr("class", "gantt-legend-entry").style("cursor", "pointer");
20117
+ const entryG = gEl.append("g").attr("class", "gantt-legend-entry").attr("data-line-number", String(entry.lineNumber)).style("cursor", "pointer");
20004
20118
  entryG.append("circle").attr("cx", ex + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
20005
20119
  entryG.append("text").attr("x", ex + LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP).attr("y", LEGEND_HEIGHT / 2 + LEGEND_ENTRY_FONT_SIZE / 2 - 2).attr("text-anchor", "start").attr("font-size", `${LEGEND_ENTRY_FONT_SIZE}px`).attr("fill", palette.textMuted).text(entry.value);
20006
20120
  entryG.on("mouseenter", () => {
@@ -20036,9 +20150,11 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
20036
20150
  cursorX += groupW + LEGEND_GROUP_GAP;
20037
20151
  }
20038
20152
  if (hasCriticalPath) {
20153
+ const cpLineNum = optionLineNumbers["critical-path"];
20039
20154
  const cpG = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "gantt-legend-critical-path").style("cursor", "pointer").on("click", () => {
20040
20155
  if (onToggleCriticalPath) onToggleCriticalPath();
20041
20156
  });
20157
+ if (cpLineNum) cpG.attr("data-line-number", String(cpLineNum));
20042
20158
  cpG.append("rect").attr("width", cpPillW).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", criticalPathActive ? palette.bg : groupBg);
20043
20159
  if (criticalPathActive) {
20044
20160
  cpG.append("rect").attr("width", cpPillW).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", "none").attr("stroke", mix(palette.textMuted, palette.bg, 50)).attr("stroke-width", 0.75);
@@ -20056,7 +20172,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
20056
20172
  });
20057
20173
  }
20058
20174
  }
20059
- function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20175
+ function renderErasAndMarkers(g, svg, resolved, xScale, innerHeight, palette) {
20060
20176
  for (let i = 0; i < resolved.eras.length; i++) {
20061
20177
  const era = resolved.eras[i];
20062
20178
  const color = era.color || ERA_COLORS[i % ERA_COLORS.length];
@@ -20067,13 +20183,23 @@ function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20067
20183
  const hoverEraOpacity = 0.16;
20068
20184
  const eraStartDate = parseDateStringToDate(era.startDate);
20069
20185
  const eraEndDate = parseDateStringToDate(era.endDate);
20070
- const eraG = g.append("g").attr("class", "gantt-era-group");
20186
+ const eraG = g.append("g").attr("class", "gantt-era-group").attr("data-line-number", String(era.lineNumber));
20071
20187
  const eraRect = eraG.append("rect").attr("class", "gantt-era").attr("x", sx).attr("y", 0).attr("width", ex - sx).attr("height", innerHeight).attr("fill", color).attr("opacity", baseEraOpacity);
20072
- eraG.append("text").attr("class", "gantt-era-label").attr("x", (sx + ex) / 2).attr("y", 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", color).attr("opacity", 0.7).attr("pointer-events", "none").text(era.label);
20188
+ eraG.append("text").attr("class", "gantt-era-label").attr("x", (sx + ex) / 2).attr("y", -24).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", color).attr("opacity", 0.7).style("cursor", "pointer").text(era.label);
20073
20189
  eraG.on("mouseenter", () => {
20190
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
20191
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
20192
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20193
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20194
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
20195
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20196
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20197
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20198
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20074
20199
  eraRect.attr("opacity", hoverEraOpacity);
20075
20200
  showGanttDateIndicators(g, xScale, eraStartDate, eraEndDate, innerHeight, color);
20076
20201
  }).on("mouseleave", () => {
20202
+ resetHighlight(g, svg);
20077
20203
  eraRect.attr("opacity", baseEraOpacity);
20078
20204
  hideGanttDateIndicators(g);
20079
20205
  });
@@ -20085,22 +20211,31 @@ function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20085
20211
  const diamondSize = 5;
20086
20212
  const labelY = -24;
20087
20213
  const diamondY = labelY + 14;
20088
- const markerG = g.append("g").attr("class", "gantt-marker-group").style("cursor", "pointer");
20214
+ const markerG = g.append("g").attr("class", "gantt-marker-group").attr("data-line-number", String(marker.lineNumber)).style("cursor", "pointer");
20089
20215
  markerG.append("rect").attr("x", mx - 40).attr("y", labelY - 12).attr("width", 80).attr("height", innerHeight - labelY + 12).attr("fill", "transparent").attr("pointer-events", "all");
20090
20216
  markerG.append("text").attr("class", "gantt-marker-label").attr("x", mx).attr("y", labelY).attr("text-anchor", "middle").attr("font-size", "11px").attr("font-weight", "600").attr("fill", color).text(marker.label);
20091
20217
  markerG.append("path").attr("d", `M${mx},${diamondY - diamondSize} l${diamondSize},${diamondSize} l-${diamondSize},${diamondSize} l-${diamondSize},-${diamondSize} Z`).attr("fill", color).attr("opacity", 0.9);
20092
20218
  markerG.append("line").attr("class", "gantt-marker").attr("x1", mx).attr("y1", diamondY + diamondSize).attr("x2", mx).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "6 4").attr("opacity", 0.5);
20093
20219
  const markerLine = markerG.select(".gantt-marker");
20094
- const markerLabel = markerG.select(".gantt-marker-label");
20095
20220
  const markerDiamond = markerG.select("path");
20096
20221
  markerG.on("mouseenter", () => {
20097
- markerLine.attr("opacity", 0);
20098
- markerLabel.attr("opacity", 0);
20222
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
20223
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
20224
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20225
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20226
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
20227
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20228
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20229
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20230
+ g.selectAll(".gantt-era-group").attr("opacity", FADE_OPACITY);
20231
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20232
+ markerG.attr("opacity", 1);
20233
+ markerLine.attr("opacity", 0.8);
20099
20234
  markerDiamond.attr("opacity", 0);
20100
- showGanttDateIndicators(g, xScale, markerDate, null, innerHeight, color);
20235
+ showGanttDateIndicators(g, xScale, markerDate, null, innerHeight, color, { skipStartLine: true });
20101
20236
  }).on("mouseleave", () => {
20237
+ resetHighlight(g, svg);
20102
20238
  markerLine.attr("opacity", 0.5);
20103
- markerLabel.attr("opacity", 1);
20104
20239
  markerDiamond.attr("opacity", 0.9);
20105
20240
  hideGanttDateIndicators(g);
20106
20241
  });
@@ -20114,11 +20249,7 @@ function parseDateStringToDate(s) {
20114
20249
  return new Date(year, month, day);
20115
20250
  }
20116
20251
  function parseDateToFractionalYear(s) {
20117
- const parts = s.split("-").map((p) => parseInt(p, 10));
20118
- const year = parts[0];
20119
- const month = parts.length >= 2 ? parts[1] : 1;
20120
- const day = parts.length >= 3 ? parts[2] : 1;
20121
- return year + (month - 1) / 12 + (day - 1) / 365;
20252
+ return dateToFractionalYear(parseDateStringToDate(s));
20122
20253
  }
20123
20254
  function highlightDeps(g, svg, taskId, resolved) {
20124
20255
  const related = /* @__PURE__ */ new Set([taskId]);
@@ -20152,6 +20283,7 @@ function highlightDeps(g, svg, taskId, resolved) {
20152
20283
  const isRelated = from && related.has(from) || to && related.has(to);
20153
20284
  el.attr("opacity", isRelated ? 0.5 : FADE_OPACITY);
20154
20285
  });
20286
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20155
20287
  }
20156
20288
  function highlightGroup(g, svg, groupName) {
20157
20289
  g.selectAll(".gantt-task").each(function() {
@@ -20174,7 +20306,12 @@ function highlightGroup(g, svg, groupName) {
20174
20306
  const el = d3Selection10.select(this);
20175
20307
  el.attr("opacity", el.attr("data-group") === groupName ? 1 : FADE_OPACITY);
20176
20308
  });
20309
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").each(function() {
20310
+ const el = d3Selection10.select(this);
20311
+ el.attr("opacity", el.attr("data-group") === groupName ? 1 : FADE_OPACITY);
20312
+ });
20177
20313
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20314
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20178
20315
  g.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", FADE_OPACITY);
20179
20316
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20180
20317
  }
@@ -20201,8 +20338,13 @@ function highlightLane(g, svg, tagKey, laneName) {
20201
20338
  const el = d3Selection10.select(this);
20202
20339
  el.attr("opacity", el.attr("data-lane") === laneName ? 1 : FADE_OPACITY);
20203
20340
  });
20341
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").each(function() {
20342
+ const el = d3Selection10.select(this);
20343
+ el.attr("opacity", el.attr("data-lane") === laneName ? 1 : FADE_OPACITY);
20344
+ });
20204
20345
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20205
20346
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20347
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20206
20348
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20207
20349
  }
20208
20350
  function highlightTask(g, svg, taskId) {
@@ -20217,7 +20359,9 @@ function highlightTask(g, svg, taskId) {
20217
20359
  });
20218
20360
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20219
20361
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20362
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20220
20363
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20364
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20221
20365
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20222
20366
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20223
20367
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
@@ -20234,7 +20378,9 @@ function highlightMilestone(g, svg, taskId) {
20234
20378
  });
20235
20379
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20236
20380
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20381
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20237
20382
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20383
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20238
20384
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20239
20385
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20240
20386
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
@@ -20253,11 +20399,14 @@ function resetHighlight(g, svg) {
20253
20399
  g.selectAll(".gantt-task, .gantt-milestone").attr("opacity", 1);
20254
20400
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", 1);
20255
20401
  svg.selectAll(".gantt-group-label").attr("opacity", 1);
20402
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", 1);
20256
20403
  svg.selectAll(".gantt-task-label").attr("opacity", 1);
20257
20404
  svg.selectAll(".gantt-lane-header").attr("opacity", 1);
20405
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", 1);
20258
20406
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", 1);
20259
20407
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
20260
20408
  g.selectAll(".gantt-marker-group").attr("opacity", 1);
20409
+ g.selectAll(".gantt-era-group").attr("opacity", 1);
20261
20410
  }
20262
20411
  function buildRowList(resolved, collapsedGroups) {
20263
20412
  const rows = [];
@@ -20411,15 +20560,18 @@ function diamondPoints(cx, cy, size) {
20411
20560
  function formatGanttDate(d) {
20412
20561
  return `${MONTH_ABBR[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
20413
20562
  }
20414
- function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, color) {
20563
+ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, color, options) {
20415
20564
  g.selectAll(".gantt-scale-tick").attr("opacity", 0.05);
20416
20565
  g.selectAll(".gantt-today").attr("opacity", 0.05);
20566
+ const hg = g.append("g").attr("class", "gantt-hover-date").attr("pointer-events", "none");
20417
20567
  const tickLen = 6;
20418
20568
  const startPos = xScale(dateToFractionalYear(startDate));
20419
20569
  const startLabel = formatGanttDate(startDate);
20420
- g.append("line").attr("class", "gantt-hover-date").attr("x1", startPos).attr("y1", -tickLen).attr("x2", startPos).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "4 4").attr("opacity", 0.6);
20421
- g.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", -tickLen - 4).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
20422
- g.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
20570
+ if (!options?.skipStartLine) {
20571
+ hg.append("line").attr("class", "gantt-hover-date").attr("x1", startPos).attr("y1", -tickLen).attr("x2", startPos).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "4 4").attr("opacity", 0.6);
20572
+ }
20573
+ hg.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", -tickLen - 4).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
20574
+ hg.append("text").attr("class", "gantt-hover-date").attr("x", startPos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(startLabel);
20423
20575
  if (endDate && endDate.getTime() !== startDate.getTime()) {
20424
20576
  const endPos = xScale(dateToFractionalYear(endDate));
20425
20577
  const endLabel = formatGanttDate(endDate);
@@ -20436,15 +20588,15 @@ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, col
20436
20588
  startAnchor = "middle";
20437
20589
  endAnchor = "middle";
20438
20590
  }
20439
- g.append("line").attr("class", "gantt-hover-date").attr("x1", endPos).attr("y1", -tickLen).attr("x2", endPos).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "4 4").attr("opacity", 0.6);
20440
- g.selectAll("text.gantt-hover-date").each(function() {
20591
+ hg.append("line").attr("class", "gantt-hover-date").attr("x1", endPos).attr("y1", -tickLen).attr("x2", endPos).attr("y2", innerHeight).attr("stroke", color).attr("stroke-width", 1.5).attr("stroke-dasharray", "4 4").attr("opacity", 0.6);
20592
+ hg.selectAll("text.gantt-hover-date").each(function() {
20441
20593
  const el = d3Selection10.select(this);
20442
20594
  if (el.text() === startLabel) {
20443
20595
  el.attr("x", startLabelX).attr("text-anchor", startAnchor);
20444
20596
  }
20445
20597
  });
20446
- g.append("text").attr("class", "gantt-hover-date").attr("x", endLabelX).attr("y", -tickLen - 4).attr("text-anchor", endAnchor).attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(endLabel);
20447
- g.append("text").attr("class", "gantt-hover-date").attr("x", endLabelX).attr("y", innerHeight + tickLen + 12).attr("text-anchor", endAnchor).attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(endLabel);
20598
+ hg.append("text").attr("class", "gantt-hover-date").attr("x", endLabelX).attr("y", -tickLen - 4).attr("text-anchor", endAnchor).attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(endLabel);
20599
+ hg.append("text").attr("class", "gantt-hover-date").attr("x", endLabelX).attr("y", innerHeight + tickLen + 12).attr("text-anchor", endAnchor).attr("fill", color).attr("font-size", "10px").attr("font-weight", "600").text(endLabel);
20448
20600
  }
20449
20601
  }
20450
20602
  function hideGanttDateIndicators(g) {
@@ -20489,7 +20641,7 @@ function renderTimeScaleHorizontal(g, scale, innerWidth, innerHeight, textColor)
20489
20641
  g.append("text").attr("class", "gantt-scale-tick").attr("x", tick.pos).attr("y", innerHeight + tickLen + 12).attr("text-anchor", "middle").attr("font-size", "10px").attr("fill", textColor).attr("opacity", opacity).text(tick.label);
20490
20642
  }
20491
20643
  }
20492
- var d3Scale, d3Selection10, BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, JS_DAY_TO_WEEKDAY2, ERA_COLORS, FADE_OPACITY, MONTH_ABBR;
20644
+ var d3Scale, d3Selection10, BAR_H, ROW_GAP, GROUP_GAP2, MILESTONE_SIZE, MIN_LEFT_MARGIN, BOTTOM_MARGIN, RIGHT_MARGIN, CHAR_W, LABEL_PAD, LABEL_GAP, BAND_ACCENT_W, BAND_RADIUS, bandClipCounter, JS_DAY_TO_WEEKDAY2, ERA_COLORS, FADE_OPACITY, MONTH_ABBR;
20493
20645
  var init_renderer9 = __esm({
20494
20646
  "src/gantt/renderer.ts"() {
20495
20647
  "use strict";
@@ -20508,6 +20660,12 @@ var init_renderer9 = __esm({
20508
20660
  MIN_LEFT_MARGIN = 120;
20509
20661
  BOTTOM_MARGIN = 40;
20510
20662
  RIGHT_MARGIN = 20;
20663
+ CHAR_W = 6.5;
20664
+ LABEL_PAD = 8;
20665
+ LABEL_GAP = 5;
20666
+ BAND_ACCENT_W = 4;
20667
+ BAND_RADIUS = 4;
20668
+ bandClipCounter = 0;
20511
20669
  JS_DAY_TO_WEEKDAY2 = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
20512
20670
  ERA_COLORS = ["#5e81ac", "#a3be8c", "#ebcb8b", "#d08770", "#b48ead"];
20513
20671
  FADE_OPACITY = 0.1;
@@ -20608,14 +20766,14 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
20608
20766
  nodePositionMap.set(node.id, node);
20609
20767
  }
20610
20768
  const LABEL_CHAR_W = 7;
20611
- const LABEL_PAD = 8;
20769
+ const LABEL_PAD2 = 8;
20612
20770
  const LABEL_H = 16;
20613
20771
  const PERP_OFFSET = 10;
20614
20772
  const labelPositions = [];
20615
20773
  for (let ei = 0; ei < layout.edges.length; ei++) {
20616
20774
  const edge = layout.edges[ei];
20617
20775
  if (!edge.label) continue;
20618
- const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD;
20776
+ const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD2;
20619
20777
  let lx, ly;
20620
20778
  if (edge.source === edge.target) {
20621
20779
  const node = nodePositionMap.get(edge.source);
@@ -25115,12 +25273,12 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
25115
25273
  };
25116
25274
  const LABEL_MAX_FONT = 48;
25117
25275
  const LABEL_MIN_FONT = 14;
25118
- const LABEL_PAD = 40;
25276
+ const LABEL_PAD2 = 40;
25119
25277
  const CHAR_WIDTH_RATIO3 = 0.6;
25120
25278
  const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO3;
25121
25279
  const quadrantLabelLayout = (text, qw2, qh2) => {
25122
- const availW = qw2 - LABEL_PAD;
25123
- const availH = qh2 - LABEL_PAD;
25280
+ const availW = qw2 - LABEL_PAD2;
25281
+ const availH = qh2 - LABEL_PAD2;
25124
25282
  const words = text.split(/\s+/);
25125
25283
  if (estTextWidth(text, LABEL_MAX_FONT) <= availW) {
25126
25284
  const fs = Math.min(LABEL_MAX_FONT, availH);
@@ -25314,6 +25472,7 @@ function finalizeSvgExport(container, theme, palette, options) {
25314
25472
  }
25315
25473
  svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
25316
25474
  svgEl.style.fontFamily = FONT_FAMILY;
25475
+ svgEl.querySelectorAll("[data-export-ignore]").forEach((el) => el.remove());
25317
25476
  const svgHtml = svgEl.outerHTML;
25318
25477
  document.body.removeChild(container);
25319
25478
  if (options?.branding !== false) {