@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.js CHANGED
@@ -7964,7 +7964,9 @@ function parseGantt(content, palette) {
7964
7964
  criticalPath: false,
7965
7965
  dependencies: false,
7966
7966
  sort: "default",
7967
- defaultSwimlaneGroup: null
7967
+ defaultSwimlaneGroup: null,
7968
+ optionLineNumbers: {},
7969
+ holidaysLineNumber: null
7968
7970
  },
7969
7971
  diagnostics,
7970
7972
  error: null
@@ -8149,6 +8151,7 @@ function parseGantt(content, palette) {
8149
8151
  inHolidaysBlock = true;
8150
8152
  holidaysBlockIndent = indent;
8151
8153
  inHeaderBlock = false;
8154
+ result.options.holidaysLineNumber = lineNumber;
8152
8155
  continue;
8153
8156
  }
8154
8157
  const tagMatch = matchTagBlockHeading(line10);
@@ -8175,7 +8178,8 @@ function parseGantt(content, palette) {
8175
8178
  startDate: eraMatch[1],
8176
8179
  endDate: eraMatch[2],
8177
8180
  label: eraExtracted.label,
8178
- color: eraExtracted.color || null
8181
+ color: eraExtracted.color || null,
8182
+ lineNumber
8179
8183
  });
8180
8184
  inHeaderBlock = false;
8181
8185
  continue;
@@ -8197,6 +8201,7 @@ function parseGantt(content, palette) {
8197
8201
  if (optMatch && isKnownOption(optMatch[1].toLowerCase())) {
8198
8202
  const key = optMatch[1].toLowerCase();
8199
8203
  const value = optMatch[2].trim();
8204
+ result.options.optionLineNumbers[key] = lineNumber;
8200
8205
  switch (key) {
8201
8206
  case "start":
8202
8207
  result.options.start = value;
@@ -13164,6 +13169,9 @@ function renderInitiativeStatus(container, parsed, layout, palette, isDark, opti
13164
13169
  const pillH = LEGEND_HEIGHT - (isActive ? LEGEND_CAPSULE_PAD * 2 : 0);
13165
13170
  const groupW = isActive ? lg.width : pillW;
13166
13171
  const gEl = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "is-legend-group").attr("data-legend-group", lg.key).style("cursor", "pointer");
13172
+ if (!isActive) {
13173
+ gEl.attr("data-export-ignore", "true");
13174
+ }
13167
13175
  if (isActive) {
13168
13176
  gEl.append("rect").attr("width", groupW).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", groupBg);
13169
13177
  }
@@ -13176,17 +13184,32 @@ function renderInitiativeStatus(container, parsed, layout, palette, isDark, opti
13176
13184
  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);
13177
13185
  if (isActive) {
13178
13186
  const hiddenSet = !lg.isStatus ? hiddenTagValues?.get(lg.key) : void 0;
13179
- let entryX = pillXOff + pillW + 4;
13187
+ const entryStartX = pillXOff + pillW + 4;
13188
+ const entryData = [];
13189
+ let estimatedX = entryStartX;
13180
13190
  for (const entry of lg.entries) {
13181
13191
  const isHidden = hiddenSet?.has(entry.value) ?? false;
13182
- const entryG = gEl.append("g").attr("data-legend-entry", entry.value).style("cursor", !lg.isStatus ? "pointer" : "default");
13192
+ const estimatedTextW = entry.label.length * LEGEND_ENTRY_FONT_W;
13193
+ const entryG = gEl.append("g").attr("data-legend-entry", entry.value).attr("transform", `translate(${estimatedX}, 0)`).style("cursor", "pointer");
13194
+ const entryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
13195
+ entryG.append("rect").attr("x", -2).attr("y", 0).attr("width", entryW + 4).attr("height", LEGEND_HEIGHT).attr("fill", "transparent");
13183
13196
  if (isHidden) {
13184
- 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);
13197
+ 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);
13185
13198
  } else {
13186
- entryG.append("circle").attr("cx", entryX + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
13199
+ entryG.append("circle").attr("cx", LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
13187
13200
  }
13188
- 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);
13189
- entryX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + entry.label.length * LEGEND_ENTRY_FONT_W + LEGEND_ENTRY_TRAIL;
13201
+ 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);
13202
+ entryData.push({ g: entryG, textEl: textEl.node(), estimatedW: estimatedTextW });
13203
+ estimatedX += LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + estimatedTextW + LEGEND_ENTRY_TRAIL;
13204
+ }
13205
+ let reflowX = entryStartX;
13206
+ for (const ed of entryData) {
13207
+ const measuredW = ed.textEl.getComputedTextLength?.() ?? 0;
13208
+ const textW = measuredW > 0 ? measuredW : ed.estimatedW;
13209
+ ed.g.attr("transform", `translate(${reflowX}, 0)`);
13210
+ const actualEntryW = LEGEND_DOT_R * 2 + LEGEND_ENTRY_DOT_GAP + textW + LEGEND_ENTRY_TRAIL;
13211
+ ed.g.select("rect").attr("width", actualEntryW + 4);
13212
+ reflowX += actualEntryW;
13190
13213
  }
13191
13214
  }
13192
13215
  cursorX += groupW + LEGEND_GROUP_GAP;
@@ -16109,7 +16132,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
16109
16132
  }
16110
16133
  const contentG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
16111
16134
  const LABEL_CHAR_W = 7;
16112
- const LABEL_PAD = 8;
16135
+ const LABEL_PAD2 = 8;
16113
16136
  const LABEL_H = 16;
16114
16137
  const PERP_OFFSET = 10;
16115
16138
  const labelPositions = [];
@@ -16118,7 +16141,7 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
16118
16141
  if (!edge.label || edge.points.length < 2) continue;
16119
16142
  const midIdx = Math.floor(edge.points.length / 2);
16120
16143
  const midPt = edge.points[midIdx];
16121
- const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD;
16144
+ const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD2;
16122
16145
  const prev = edge.points[Math.max(0, midIdx - 1)];
16123
16146
  const next = edge.points[Math.min(edge.points.length - 1, midIdx + 1)];
16124
16147
  const dx = next.x - prev.x;
@@ -19361,8 +19384,46 @@ __export(renderer_exports9, {
19361
19384
  });
19362
19385
  import * as d3Scale from "d3-scale";
19363
19386
  import * as d3Selection10 from "d3-selection";
19387
+ function computeBarLabel(label, x1, barWidth, innerWidth, textColor) {
19388
+ const textWidth = label.length * CHAR_W;
19389
+ const x2 = x1 + barWidth;
19390
+ if (textWidth < barWidth - LABEL_PAD) {
19391
+ return { x: x1 + 6, anchor: "start", fill: textColor, text: label };
19392
+ }
19393
+ if (x2 + LABEL_GAP + textWidth <= innerWidth) {
19394
+ return { x: x2 + LABEL_GAP, anchor: "start", fill: textColor, text: label };
19395
+ }
19396
+ if (x1 - LABEL_GAP - textWidth >= 0) {
19397
+ return { x: x1 - LABEL_GAP, anchor: "end", fill: textColor, text: label };
19398
+ }
19399
+ const availWidth = x1 - LABEL_GAP;
19400
+ if (availWidth > CHAR_W * 3) {
19401
+ const maxChars = Math.floor(availWidth / CHAR_W) - 1;
19402
+ return { x: x1 - LABEL_GAP, anchor: "end", fill: textColor, text: label.slice(0, maxChars) + "\u2026" };
19403
+ }
19404
+ return null;
19405
+ }
19406
+ function renderLabelBand(svg, y, leftMargin, color, palette, cssPrefix, dataAttr) {
19407
+ const bandX = 5;
19408
+ const bandW = leftMargin - 7;
19409
+ const bandY = y - BAR_H / 2;
19410
+ const clipId = `gantt-band-clip-${bandClipCounter++}`;
19411
+ 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);
19412
+ 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");
19413
+ 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");
19414
+ if (dataAttr) {
19415
+ tint2.attr(dataAttr.key, dataAttr.value);
19416
+ accent.attr(dataAttr.key, dataAttr.value);
19417
+ }
19418
+ }
19419
+ function appendTaskIcon(textEl, label, isMilestone, iconColor, textColor) {
19420
+ const icon = isMilestone ? "\u25C6" : "\u25CF";
19421
+ textEl.append("tspan").attr("fill", iconColor).text(icon);
19422
+ textEl.append("tspan").attr("fill", textColor).text(" " + label);
19423
+ }
19364
19424
  function renderGantt(container, resolved, palette, isDark, options, exportDims) {
19365
19425
  container.innerHTML = "";
19426
+ bandClipCounter = 0;
19366
19427
  if (resolved.tasks.length === 0) return;
19367
19428
  const onClickItem = options?.onClickItem;
19368
19429
  const collapsedGroups = options?.collapsedGroups;
@@ -19381,10 +19442,13 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19381
19442
  const isTagMode = tagRows !== null;
19382
19443
  const allLabels = isTagMode ? [
19383
19444
  ...rows.filter((r) => r.type === "lane-header").map((r) => r.laneName),
19384
- ...rows.filter((r) => r.type === "task").map((r) => r.task.task.label)
19445
+ ...rows.filter((r) => r.type === "task").map((r) => "\u25CF " + r.task.task.label)
19385
19446
  ] : [
19386
- ...resolved.tasks.map((t) => t.task.label),
19387
- ...resolved.groups.map((g2) => " ".repeat(g2.depth) + g2.name)
19447
+ ...resolved.tasks.map((t) => "\u25CF " + t.task.label),
19448
+ ...resolved.groups.map((g2) => {
19449
+ const px = g2.depth <= 2 ? g2.depth * 14 : 2 * 14 + (g2.depth - 2) * 8;
19450
+ return " ".repeat(Math.ceil(px / 7)) + g2.name;
19451
+ })
19388
19452
  ];
19389
19453
  const maxLabelLen = Math.max(...allLabels.map((l) => l.length), 10);
19390
19454
  const leftMargin = Math.max(MIN_LEFT_MARGIN, maxLabelLen * 7 + 30);
@@ -19393,8 +19457,10 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19393
19457
  const titleHeight = title ? 50 : 20;
19394
19458
  const tagLegendReserve = resolved.tagGroups.length > 0 ? LEGEND_HEIGHT + 8 : 0;
19395
19459
  const topDateLabelReserve = 22;
19460
+ const hasOverheadLabels = resolved.markers.length > 0 || resolved.eras.length > 0;
19461
+ const markerLabelReserve = hasOverheadLabels ? 18 : 0;
19396
19462
  const CONTENT_TOP_PAD = 16;
19397
- const marginTop = titleHeight + tagLegendReserve + topDateLabelReserve;
19463
+ const marginTop = titleHeight + tagLegendReserve + topDateLabelReserve + markerLabelReserve;
19398
19464
  const contentH = isTagMode ? totalRows * (BAR_H + ROW_GAP) : totalRows * (BAR_H + ROW_GAP) + GROUP_GAP2 * resolved.groups.length;
19399
19465
  const innerHeight = CONTENT_TOP_PAD + contentH;
19400
19466
  const outerHeight = marginTop + innerHeight + BOTTOM_MARGIN;
@@ -19422,6 +19488,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19422
19488
  isDark,
19423
19489
  hasCriticalPath,
19424
19490
  criticalPathActive,
19491
+ resolved.options.optionLineNumbers,
19425
19492
  (groupName) => {
19426
19493
  currentActiveGroup = currentActiveGroup?.toLowerCase() === groupName.toLowerCase() ? null : groupName;
19427
19494
  if (onActiveGroupChange) onActiveGroupChange(currentActiveGroup);
@@ -19460,7 +19527,25 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19460
19527
  renderTimeScaleHorizontal(g, xScale, innerWidth, innerHeight, palette.text);
19461
19528
  renderWeekendBands(g, resolved, xScale, innerHeight, palette, isDark);
19462
19529
  renderHolidayBands(g, svg, resolved, xScale, innerHeight, palette, isDark, marginTop - 4, leftMargin, onClickItem);
19463
- renderErasAndMarkers(g, resolved, xScale, innerHeight, palette);
19530
+ renderErasAndMarkers(g, svg, resolved, xScale, innerHeight, palette);
19531
+ let todayDate = null;
19532
+ let todayX = -1;
19533
+ const todayColor = palette.accent || "#e74c3c";
19534
+ const todayMarkerLineNum = resolved.options.optionLineNumbers["today-marker"];
19535
+ if (resolved.options.todayMarker !== "off") {
19536
+ if (resolved.options.todayMarker === "on") {
19537
+ todayDate = /* @__PURE__ */ new Date();
19538
+ } else {
19539
+ todayDate = /* @__PURE__ */ new Date(resolved.options.todayMarker + "T00:00:00");
19540
+ }
19541
+ todayX = xScale(dateToFractionalYear(todayDate));
19542
+ if (todayX >= 0 && todayX <= innerWidth) {
19543
+ 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");
19544
+ if (todayMarkerLineNum) todayLine.attr("data-line-number", String(todayMarkerLineNum));
19545
+ 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");
19546
+ if (todayMarkerLineNum) todayLabel.attr("data-line-number", String(todayMarkerLineNum));
19547
+ }
19548
+ }
19464
19549
  const taskPositions = /* @__PURE__ */ new Map();
19465
19550
  const groupPositions = /* @__PURE__ */ new Map();
19466
19551
  const lanePositions = /* @__PURE__ */ new Map();
@@ -19496,6 +19581,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19496
19581
  laneBarWidth = Math.max(lx2 - lx1, 2);
19497
19582
  }
19498
19583
  lanePositions.set(row.laneName, { x1: lx1, x2: lx1 + laneBarWidth, y: yOffset + BAR_H / 2 });
19584
+ renderLabelBand(svg, marginTop + yOffset + BAR_H / 2, leftMargin, laneColor, palette, "lane", { key: "data-lane", value: row.laneName });
19499
19585
  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", () => {
19500
19586
  if (onToggleLane) onToggleLane(row.laneName);
19501
19587
  }).on("mouseenter", () => {
@@ -19521,7 +19607,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19521
19607
  });
19522
19608
  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);
19523
19609
  if (row.aggregateProgress !== null && row.aggregateProgress > 0) {
19524
- 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");
19610
+ 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");
19525
19611
  }
19526
19612
  }
19527
19613
  yOffset += BAR_H + ROW_GAP;
@@ -19532,6 +19618,7 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19532
19618
  const toggleIcon = isCollapsed ? "\u25BA" : "\u25BC";
19533
19619
  const tagColor = resolveTagColor(group.metadata, resolved.tagGroups, currentActiveGroup, true);
19534
19620
  const groupColor = tagColor && tagColor !== "#999999" ? tagColor : group.color || palette.textMuted;
19621
+ renderLabelBand(svg, marginTop + yOffset + BAR_H / 2, leftMargin, groupColor, palette, "group", { key: "data-group", value: group.name });
19535
19622
  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", () => {
19536
19623
  if (onToggleGroup) onToggleGroup(group.name);
19537
19624
  }).on("mouseenter", () => {
@@ -19541,7 +19628,8 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19541
19628
  resetHighlight(g, svg);
19542
19629
  hideGanttDateIndicators(g);
19543
19630
  });
19544
- const labelX = 10 + group.depth * 14;
19631
+ const groupIndent = group.depth <= 2 ? group.depth * 14 : 2 * 14 + (group.depth - 2) * 8;
19632
+ const labelX = 10 + groupIndent;
19545
19633
  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)}%` : ""));
19546
19634
  const gStart = dateToFractionalYear(group.startDate);
19547
19635
  const gEnd = dateToFractionalYear(group.endDate);
@@ -19559,7 +19647,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19559
19647
  });
19560
19648
  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);
19561
19649
  if (group.progress !== null && group.progress > 0) {
19562
- 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);
19650
+ 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);
19651
+ }
19652
+ const summaryLabel = group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : "");
19653
+ const summaryPlacement = computeBarLabel(summaryLabel, gx1, barWidth, innerWidth, palette.text);
19654
+ if (summaryPlacement) {
19655
+ 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);
19563
19656
  }
19564
19657
  groupPositions.set(group.name, { x1: gx1, x2: gx1 + barWidth, y: yOffset + BAR_H / 2 });
19565
19658
  } else {
@@ -19574,7 +19667,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19574
19667
  });
19575
19668
  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);
19576
19669
  if (group.progress !== null && group.progress > 0) {
19577
- 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);
19670
+ 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);
19671
+ }
19672
+ const expandedLabel = group.name + (group.progress !== null ? ` ${Math.round(group.progress)}%` : "");
19673
+ const expandedPlacement = computeBarLabel(expandedLabel, gx1, groupBarWidth, innerWidth, palette.text);
19674
+ if (expandedPlacement) {
19675
+ 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);
19578
19676
  }
19579
19677
  }
19580
19678
  }
@@ -19582,9 +19680,12 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19582
19680
  } else if (row.type === "task") {
19583
19681
  const rt = row.task;
19584
19682
  const task = rt.task;
19585
- const taskLabelX = isTagMode ? 20 : 6 + rt.groupPath.length * 14;
19683
+ const barColor = resolveTaskColor(rt, currentActiveGroup, resolved, seriesColors2, palette);
19684
+ const depth = rt.groupPath.length;
19685
+ const indent = depth <= 2 ? depth * 14 : 2 * 14 + (depth - 2) * 8;
19686
+ const taskLabelX = isTagMode ? 20 : 6 + indent;
19586
19687
  const topGroup = rt.groupPath.length > 0 ? rt.groupPath[0] : null;
19587
- 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", () => {
19688
+ 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", () => {
19588
19689
  if (onClickItem) onClickItem(task.lineNumber);
19589
19690
  }).on("mouseenter", () => {
19590
19691
  if (rt.isMilestone) {
@@ -19595,13 +19696,13 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19595
19696
  }).on("mouseleave", () => {
19596
19697
  resetHighlight(g, svg);
19597
19698
  });
19699
+ appendTaskIcon(taskLabel, task.label, rt.isMilestone, barColor, palette.text);
19598
19700
  for (const [key, value] of Object.entries(rt.effectiveMetadata)) {
19599
19701
  taskLabel.attr(`data-tag-${key}`, value.toLowerCase());
19600
19702
  }
19601
19703
  if (rt.isCriticalPath) {
19602
19704
  taskLabel.attr("data-critical-path", "true");
19603
19705
  }
19604
- let barColor = resolveTaskColor(rt, currentActiveGroup, resolved, seriesColors2, palette);
19605
19706
  if (rt.isMilestone) {
19606
19707
  const mx = xScale(dateToFractionalYear(rt.startDate));
19607
19708
  const my = yOffset + BAR_H / 2;
@@ -19679,32 +19780,40 @@ function renderGantt(container, resolved, palette, isDark, options, exportDims)
19679
19780
  progGrad.append("stop").attr("offset", "100%").attr("stop-color", barColor).attr("stop-opacity", 0);
19680
19781
  progressFill = `url(#${progGradId})`;
19681
19782
  }
19682
- 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);
19783
+ 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);
19683
19784
  }
19684
19785
  if (rt.isCriticalPath) {
19685
19786
  taskG.attr("data-critical-path", "true");
19686
19787
  }
19687
- const textWidth = task.label.length * 6.5;
19688
- if (textWidth < barWidth - 8) {
19689
- 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);
19788
+ const labelPlacement = computeBarLabel(task.label, x1, barWidth, innerWidth, palette.text);
19789
+ if (labelPlacement) {
19790
+ 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);
19690
19791
  }
19691
19792
  taskPositions.set(task.id, { x1, x2: x1 + barWidth, y: yOffset + BAR_H / 2 });
19692
19793
  }
19693
19794
  yOffset += BAR_H + ROW_GAP;
19694
19795
  }
19695
19796
  }
19696
- if (resolved.options.todayMarker !== "off") {
19697
- let todayDate;
19698
- if (resolved.options.todayMarker === "on") {
19699
- todayDate = /* @__PURE__ */ new Date();
19700
- } else {
19701
- todayDate = /* @__PURE__ */ new Date(resolved.options.todayMarker + "T00:00:00");
19702
- }
19703
- const todayX = xScale(dateToFractionalYear(todayDate));
19704
- if (todayX >= 0 && todayX <= innerWidth) {
19705
- 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);
19706
- 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");
19707
- }
19797
+ if (todayDate && todayX >= 0 && todayX <= innerWidth) {
19798
+ const todayHoverG = g.append("g").attr("class", "gantt-today-hover").style("cursor", "pointer");
19799
+ todayHoverG.append("rect").attr("x", todayX - 10).attr("y", -6).attr("width", 20).attr("height", innerHeight + 16).attr("fill", "transparent").attr("pointer-events", "all");
19800
+ const todayDateObj = todayDate;
19801
+ todayHoverG.on("mouseenter", () => {
19802
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
19803
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
19804
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
19805
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
19806
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
19807
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
19808
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
19809
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
19810
+ g.selectAll(".gantt-era-group").attr("opacity", FADE_OPACITY);
19811
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
19812
+ showGanttDateIndicators(g, xScale, todayDateObj, null, innerHeight, todayColor);
19813
+ }).on("mouseleave", () => {
19814
+ resetHighlight(g, svg);
19815
+ hideGanttDateIndicators(g);
19816
+ });
19708
19817
  }
19709
19818
  if (resolved.options.dependencies) {
19710
19819
  renderDependencyArrows(g, resolved, taskPositions, groupPositions, collapsedGroups, palette, isDark, isTagMode, lanePositions, collapsedLanes, taskLaneMap);
@@ -19821,10 +19930,10 @@ function renderDependencyArrows(g, resolved, taskPositions, groupPositions, coll
19821
19930
  const path = `M ${sx} ${sy} C ${sx + cpOffset} ${sy}, ${tx - cpOffset} ${ty}, ${tx} ${ty}`;
19822
19931
  const arrowColor = mix(palette.text, palette.bg, 50);
19823
19932
  const isCpArrow = rt.isCriticalPath && targetTask.isCriticalPath;
19824
- 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);
19933
+ 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);
19825
19934
  const headSize = 5;
19826
19935
  const angle = 0;
19827
- 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);
19936
+ 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);
19828
19937
  }
19829
19938
  }
19830
19939
  }
@@ -19845,7 +19954,9 @@ function applyCriticalPathHighlight(svg, chartG) {
19845
19954
  el.attr("opacity", el.attr("data-critical-path") === "true" ? 1 : FADE_OPACITY);
19846
19955
  });
19847
19956
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
19957
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
19848
19958
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
19959
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
19849
19960
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", FADE_OPACITY);
19850
19961
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").each(function() {
19851
19962
  const el = d3Selection10.select(this);
@@ -19857,7 +19968,9 @@ function resetHighlightAll(svg, chartG) {
19857
19968
  chartG.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", 1);
19858
19969
  svg.selectAll(".gantt-task-label").attr("opacity", 1);
19859
19970
  svg.selectAll(".gantt-group-label").attr("opacity", 1);
19971
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", 1);
19860
19972
  svg.selectAll(".gantt-lane-header").attr("opacity", 1);
19973
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", 1);
19861
19974
  chartG.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", 1);
19862
19975
  chartG.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
19863
19976
  }
@@ -19873,7 +19986,7 @@ function drawSwimlaneIcon(parent, x, y, isActive, palette) {
19873
19986
  }
19874
19987
  return iconG;
19875
19988
  }
19876
- function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, onToggle, onToggleCriticalPath, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks) {
19989
+ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargin, chartInnerWidth, legendY, palette, isDark, hasCriticalPath, criticalPathActive, optionLineNumbers, onToggle, onToggleCriticalPath, currentSwimlaneGroup, onSwimlaneChange, legendViewMode, resolvedTasks) {
19877
19990
  const groupBg = isDark ? mix(palette.surface, palette.bg, 50) : mix(palette.surface, palette.bg, 30);
19878
19991
  let visibleGroups;
19879
19992
  if (activeGroupName) {
@@ -19934,7 +20047,8 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
19934
20047
  if (visibleGroups.length > 0) totalW += LEGEND_GROUP_GAP;
19935
20048
  totalW += cpPillW;
19936
20049
  }
19937
- const legendX = chartLeftMargin + (chartInnerWidth - totalW) / 2;
20050
+ const containerWidth = chartLeftMargin + chartInnerWidth + RIGHT_MARGIN;
20051
+ const legendX = (containerWidth - totalW) / 2;
19938
20052
  const legendRow = svg.append("g").attr("class", "gantt-tag-legend-container").attr("transform", `translate(${legendX}, ${legendY})`);
19939
20053
  let cursorX = 0;
19940
20054
  for (let i = 0; i < visibleGroups.length; i++) {
@@ -19946,7 +20060,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
19946
20060
  const pillW = group.name.length * LEGEND_PILL_FONT_W + LEGEND_PILL_PAD + iconReserve;
19947
20061
  const pillH = isActive ? LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2 : LEGEND_HEIGHT;
19948
20062
  const groupW = groupWidths[i];
19949
- 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", () => {
20063
+ 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", () => {
19950
20064
  if (onToggle) onToggle(group.name);
19951
20065
  });
19952
20066
  if (isActive) {
@@ -19980,7 +20094,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
19980
20094
  let ex = pillXOff + pillW + LEGEND_CAPSULE_PAD + 4;
19981
20095
  for (const entry of entries) {
19982
20096
  const entryValue = entry.value.toLowerCase();
19983
- const entryG = gEl.append("g").attr("class", "gantt-legend-entry").style("cursor", "pointer");
20097
+ const entryG = gEl.append("g").attr("class", "gantt-legend-entry").attr("data-line-number", String(entry.lineNumber)).style("cursor", "pointer");
19984
20098
  entryG.append("circle").attr("cx", ex + LEGEND_DOT_R).attr("cy", LEGEND_HEIGHT / 2).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
19985
20099
  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);
19986
20100
  entryG.on("mouseenter", () => {
@@ -20016,9 +20130,11 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
20016
20130
  cursorX += groupW + LEGEND_GROUP_GAP;
20017
20131
  }
20018
20132
  if (hasCriticalPath) {
20133
+ const cpLineNum = optionLineNumbers["critical-path"];
20019
20134
  const cpG = legendRow.append("g").attr("transform", `translate(${cursorX}, 0)`).attr("class", "gantt-legend-critical-path").style("cursor", "pointer").on("click", () => {
20020
20135
  if (onToggleCriticalPath) onToggleCriticalPath();
20021
20136
  });
20137
+ if (cpLineNum) cpG.attr("data-line-number", String(cpLineNum));
20022
20138
  cpG.append("rect").attr("width", cpPillW).attr("height", LEGEND_HEIGHT).attr("rx", LEGEND_HEIGHT / 2).attr("fill", criticalPathActive ? palette.bg : groupBg);
20023
20139
  if (criticalPathActive) {
20024
20140
  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);
@@ -20036,7 +20152,7 @@ function renderTagLegend(svg, chartG, tagGroups, activeGroupName, chartLeftMargi
20036
20152
  });
20037
20153
  }
20038
20154
  }
20039
- function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20155
+ function renderErasAndMarkers(g, svg, resolved, xScale, innerHeight, palette) {
20040
20156
  for (let i = 0; i < resolved.eras.length; i++) {
20041
20157
  const era = resolved.eras[i];
20042
20158
  const color = era.color || ERA_COLORS[i % ERA_COLORS.length];
@@ -20047,13 +20163,23 @@ function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20047
20163
  const hoverEraOpacity = 0.16;
20048
20164
  const eraStartDate = parseDateStringToDate(era.startDate);
20049
20165
  const eraEndDate = parseDateStringToDate(era.endDate);
20050
- const eraG = g.append("g").attr("class", "gantt-era-group");
20166
+ const eraG = g.append("g").attr("class", "gantt-era-group").attr("data-line-number", String(era.lineNumber));
20051
20167
  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);
20052
- 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);
20168
+ 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);
20053
20169
  eraG.on("mouseenter", () => {
20170
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
20171
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
20172
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20173
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20174
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
20175
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20176
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20177
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20178
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20054
20179
  eraRect.attr("opacity", hoverEraOpacity);
20055
20180
  showGanttDateIndicators(g, xScale, eraStartDate, eraEndDate, innerHeight, color);
20056
20181
  }).on("mouseleave", () => {
20182
+ resetHighlight(g, svg);
20057
20183
  eraRect.attr("opacity", baseEraOpacity);
20058
20184
  hideGanttDateIndicators(g);
20059
20185
  });
@@ -20065,22 +20191,31 @@ function renderErasAndMarkers(g, resolved, xScale, innerHeight, palette) {
20065
20191
  const diamondSize = 5;
20066
20192
  const labelY = -24;
20067
20193
  const diamondY = labelY + 14;
20068
- const markerG = g.append("g").attr("class", "gantt-marker-group").style("cursor", "pointer");
20194
+ const markerG = g.append("g").attr("class", "gantt-marker-group").attr("data-line-number", String(marker.lineNumber)).style("cursor", "pointer");
20069
20195
  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");
20070
20196
  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);
20071
20197
  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);
20072
20198
  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);
20073
20199
  const markerLine = markerG.select(".gantt-marker");
20074
- const markerLabel = markerG.select(".gantt-marker-label");
20075
20200
  const markerDiamond = markerG.select("path");
20076
20201
  markerG.on("mouseenter", () => {
20077
- markerLine.attr("opacity", 0);
20078
- markerLabel.attr("opacity", 0);
20202
+ g.selectAll(".gantt-task").attr("opacity", FADE_OPACITY);
20203
+ g.selectAll(".gantt-milestone").attr("opacity", FADE_OPACITY);
20204
+ g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20205
+ svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20206
+ svg.selectAll(".gantt-task-label").attr("opacity", FADE_OPACITY);
20207
+ svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20208
+ g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20209
+ g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20210
+ g.selectAll(".gantt-era-group").attr("opacity", FADE_OPACITY);
20211
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20212
+ markerG.attr("opacity", 1);
20213
+ markerLine.attr("opacity", 0.8);
20079
20214
  markerDiamond.attr("opacity", 0);
20080
- showGanttDateIndicators(g, xScale, markerDate, null, innerHeight, color);
20215
+ showGanttDateIndicators(g, xScale, markerDate, null, innerHeight, color, { skipStartLine: true });
20081
20216
  }).on("mouseleave", () => {
20217
+ resetHighlight(g, svg);
20082
20218
  markerLine.attr("opacity", 0.5);
20083
- markerLabel.attr("opacity", 1);
20084
20219
  markerDiamond.attr("opacity", 0.9);
20085
20220
  hideGanttDateIndicators(g);
20086
20221
  });
@@ -20094,11 +20229,7 @@ function parseDateStringToDate(s) {
20094
20229
  return new Date(year, month, day);
20095
20230
  }
20096
20231
  function parseDateToFractionalYear(s) {
20097
- const parts = s.split("-").map((p) => parseInt(p, 10));
20098
- const year = parts[0];
20099
- const month = parts.length >= 2 ? parts[1] : 1;
20100
- const day = parts.length >= 3 ? parts[2] : 1;
20101
- return year + (month - 1) / 12 + (day - 1) / 365;
20232
+ return dateToFractionalYear(parseDateStringToDate(s));
20102
20233
  }
20103
20234
  function highlightDeps(g, svg, taskId, resolved) {
20104
20235
  const related = /* @__PURE__ */ new Set([taskId]);
@@ -20132,6 +20263,7 @@ function highlightDeps(g, svg, taskId, resolved) {
20132
20263
  const isRelated = from && related.has(from) || to && related.has(to);
20133
20264
  el.attr("opacity", isRelated ? 0.5 : FADE_OPACITY);
20134
20265
  });
20266
+ g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20135
20267
  }
20136
20268
  function highlightGroup(g, svg, groupName) {
20137
20269
  g.selectAll(".gantt-task").each(function() {
@@ -20154,7 +20286,12 @@ function highlightGroup(g, svg, groupName) {
20154
20286
  const el = d3Selection10.select(this);
20155
20287
  el.attr("opacity", el.attr("data-group") === groupName ? 1 : FADE_OPACITY);
20156
20288
  });
20289
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").each(function() {
20290
+ const el = d3Selection10.select(this);
20291
+ el.attr("opacity", el.attr("data-group") === groupName ? 1 : FADE_OPACITY);
20292
+ });
20157
20293
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20294
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20158
20295
  g.selectAll(".gantt-lane-band, .gantt-lane-accent").attr("opacity", FADE_OPACITY);
20159
20296
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20160
20297
  }
@@ -20181,8 +20318,13 @@ function highlightLane(g, svg, tagKey, laneName) {
20181
20318
  const el = d3Selection10.select(this);
20182
20319
  el.attr("opacity", el.attr("data-lane") === laneName ? 1 : FADE_OPACITY);
20183
20320
  });
20321
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").each(function() {
20322
+ const el = d3Selection10.select(this);
20323
+ el.attr("opacity", el.attr("data-lane") === laneName ? 1 : FADE_OPACITY);
20324
+ });
20184
20325
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20185
20326
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20327
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20186
20328
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
20187
20329
  }
20188
20330
  function highlightTask(g, svg, taskId) {
@@ -20197,7 +20339,9 @@ function highlightTask(g, svg, taskId) {
20197
20339
  });
20198
20340
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20199
20341
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20342
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20200
20343
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20344
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20201
20345
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20202
20346
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20203
20347
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
@@ -20214,7 +20358,9 @@ function highlightMilestone(g, svg, taskId) {
20214
20358
  });
20215
20359
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", FADE_OPACITY);
20216
20360
  svg.selectAll(".gantt-group-label").attr("opacity", FADE_OPACITY);
20361
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", FADE_OPACITY);
20217
20362
  svg.selectAll(".gantt-lane-header").attr("opacity", FADE_OPACITY);
20363
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", FADE_OPACITY);
20218
20364
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", FADE_OPACITY);
20219
20365
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", FADE_OPACITY);
20220
20366
  g.selectAll(".gantt-marker-group").attr("opacity", FADE_OPACITY);
@@ -20233,11 +20379,14 @@ function resetHighlight(g, svg) {
20233
20379
  g.selectAll(".gantt-task, .gantt-milestone").attr("opacity", 1);
20234
20380
  g.selectAll(".gantt-group-bar, .gantt-group-summary").attr("opacity", 1);
20235
20381
  svg.selectAll(".gantt-group-label").attr("opacity", 1);
20382
+ svg.selectAll(".gantt-group-band-bg, .gantt-group-band-accent").attr("opacity", 1);
20236
20383
  svg.selectAll(".gantt-task-label").attr("opacity", 1);
20237
20384
  svg.selectAll(".gantt-lane-header").attr("opacity", 1);
20385
+ svg.selectAll(".gantt-lane-band-bg, .gantt-lane-band-accent").attr("opacity", 1);
20238
20386
  g.selectAll(".gantt-lane-band, .gantt-lane-accent, .gantt-lane-band-group").attr("opacity", 1);
20239
20387
  g.selectAll(".gantt-dep-arrow, .gantt-dep-arrowhead").attr("opacity", 0.5);
20240
20388
  g.selectAll(".gantt-marker-group").attr("opacity", 1);
20389
+ g.selectAll(".gantt-era-group").attr("opacity", 1);
20241
20390
  }
20242
20391
  function buildRowList(resolved, collapsedGroups) {
20243
20392
  const rows = [];
@@ -20391,15 +20540,18 @@ function diamondPoints(cx, cy, size) {
20391
20540
  function formatGanttDate(d) {
20392
20541
  return `${MONTH_ABBR[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
20393
20542
  }
20394
- function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, color) {
20543
+ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, color, options) {
20395
20544
  g.selectAll(".gantt-scale-tick").attr("opacity", 0.05);
20396
20545
  g.selectAll(".gantt-today").attr("opacity", 0.05);
20546
+ const hg = g.append("g").attr("class", "gantt-hover-date").attr("pointer-events", "none");
20397
20547
  const tickLen = 6;
20398
20548
  const startPos = xScale(dateToFractionalYear(startDate));
20399
20549
  const startLabel = formatGanttDate(startDate);
20400
- 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);
20401
- 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);
20402
- 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);
20550
+ if (!options?.skipStartLine) {
20551
+ 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);
20552
+ }
20553
+ 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);
20554
+ 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);
20403
20555
  if (endDate && endDate.getTime() !== startDate.getTime()) {
20404
20556
  const endPos = xScale(dateToFractionalYear(endDate));
20405
20557
  const endLabel = formatGanttDate(endDate);
@@ -20416,15 +20568,15 @@ function showGanttDateIndicators(g, xScale, startDate, endDate, innerHeight, col
20416
20568
  startAnchor = "middle";
20417
20569
  endAnchor = "middle";
20418
20570
  }
20419
- 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);
20420
- g.selectAll("text.gantt-hover-date").each(function() {
20571
+ 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);
20572
+ hg.selectAll("text.gantt-hover-date").each(function() {
20421
20573
  const el = d3Selection10.select(this);
20422
20574
  if (el.text() === startLabel) {
20423
20575
  el.attr("x", startLabelX).attr("text-anchor", startAnchor);
20424
20576
  }
20425
20577
  });
20426
- 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);
20427
- 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);
20578
+ 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);
20579
+ 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);
20428
20580
  }
20429
20581
  }
20430
20582
  function hideGanttDateIndicators(g) {
@@ -20469,7 +20621,7 @@ function renderTimeScaleHorizontal(g, scale, innerWidth, innerHeight, textColor)
20469
20621
  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);
20470
20622
  }
20471
20623
  }
20472
- var 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;
20624
+ var 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;
20473
20625
  var init_renderer9 = __esm({
20474
20626
  "src/gantt/renderer.ts"() {
20475
20627
  "use strict";
@@ -20486,6 +20638,12 @@ var init_renderer9 = __esm({
20486
20638
  MIN_LEFT_MARGIN = 120;
20487
20639
  BOTTOM_MARGIN = 40;
20488
20640
  RIGHT_MARGIN = 20;
20641
+ CHAR_W = 6.5;
20642
+ LABEL_PAD = 8;
20643
+ LABEL_GAP = 5;
20644
+ BAND_ACCENT_W = 4;
20645
+ BAND_RADIUS = 4;
20646
+ bandClipCounter = 0;
20489
20647
  JS_DAY_TO_WEEKDAY2 = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
20490
20648
  ERA_COLORS = ["#5e81ac", "#a3be8c", "#ebcb8b", "#d08770", "#b48ead"];
20491
20649
  FADE_OPACITY = 0.1;
@@ -20588,14 +20746,14 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
20588
20746
  nodePositionMap.set(node.id, node);
20589
20747
  }
20590
20748
  const LABEL_CHAR_W = 7;
20591
- const LABEL_PAD = 8;
20749
+ const LABEL_PAD2 = 8;
20592
20750
  const LABEL_H = 16;
20593
20751
  const PERP_OFFSET = 10;
20594
20752
  const labelPositions = [];
20595
20753
  for (let ei = 0; ei < layout.edges.length; ei++) {
20596
20754
  const edge = layout.edges[ei];
20597
20755
  if (!edge.label) continue;
20598
- const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD;
20756
+ const bgW = edge.label.length * LABEL_CHAR_W + LABEL_PAD2;
20599
20757
  let lx, ly;
20600
20758
  if (edge.source === edge.target) {
20601
20759
  const node = nodePositionMap.get(edge.source);
@@ -25098,12 +25256,12 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
25098
25256
  };
25099
25257
  const LABEL_MAX_FONT = 48;
25100
25258
  const LABEL_MIN_FONT = 14;
25101
- const LABEL_PAD = 40;
25259
+ const LABEL_PAD2 = 40;
25102
25260
  const CHAR_WIDTH_RATIO3 = 0.6;
25103
25261
  const estTextWidth = (text, fontSize) => text.length * fontSize * CHAR_WIDTH_RATIO3;
25104
25262
  const quadrantLabelLayout = (text, qw2, qh2) => {
25105
- const availW = qw2 - LABEL_PAD;
25106
- const availH = qh2 - LABEL_PAD;
25263
+ const availW = qw2 - LABEL_PAD2;
25264
+ const availH = qh2 - LABEL_PAD2;
25107
25265
  const words = text.split(/\s+/);
25108
25266
  if (estTextWidth(text, LABEL_MAX_FONT) <= availW) {
25109
25267
  const fs = Math.min(LABEL_MAX_FONT, availH);
@@ -25297,6 +25455,7 @@ function finalizeSvgExport(container, theme, palette, options) {
25297
25455
  }
25298
25456
  svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
25299
25457
  svgEl.style.fontFamily = FONT_FAMILY;
25458
+ svgEl.querySelectorAll("[data-export-ignore]").forEach((el) => el.remove());
25300
25459
  const svgHtml = svgEl.outerHTML;
25301
25460
  document.body.removeChild(container);
25302
25461
  if (options?.branding !== false) {