@diagrammo/dgmo 0.8.9 → 0.8.11
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/AGENTS.md +3 -0
- package/dist/cli.cjs +245 -672
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +2 -3
- package/dist/editor.d.ts +2 -3
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +1623 -800
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +153 -1
- package/dist/index.d.ts +153 -1
- package/dist/index.js +1619 -802
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +28 -2
- package/gallery/fixtures/sitemap-full.dgmo +1 -0
- package/package.json +14 -17
- package/src/boxes-and-lines/layout.ts +48 -8
- package/src/boxes-and-lines/parser.ts +59 -13
- package/src/boxes-and-lines/renderer.ts +34 -138
- package/src/c4/layout.ts +31 -10
- package/src/c4/renderer.ts +25 -138
- package/src/class/renderer.ts +185 -186
- package/src/d3.ts +194 -222
- package/src/echarts.ts +56 -57
- package/src/editor/index.ts +1 -2
- package/src/er/renderer.ts +52 -245
- package/src/gantt/renderer.ts +140 -182
- package/src/gantt/resolver.ts +19 -14
- package/src/index.ts +23 -1
- package/src/infra/renderer.ts +91 -244
- package/src/kanban/renderer.ts +29 -133
- package/src/label-layout.ts +286 -0
- package/src/org/renderer.ts +103 -170
- package/src/render.ts +39 -9
- package/src/sequence/parser.ts +4 -0
- package/src/sequence/renderer.ts +47 -154
- package/src/sitemap/layout.ts +180 -38
- package/src/sitemap/parser.ts +64 -23
- package/src/sitemap/renderer.ts +73 -161
- package/src/utils/arrows.ts +1 -1
- package/src/utils/legend-constants.ts +6 -0
- package/src/utils/legend-d3.ts +400 -0
- package/src/utils/legend-layout.ts +491 -0
- package/src/utils/legend-svg.ts +28 -2
- package/src/utils/legend-types.ts +166 -0
- package/src/utils/parsing.ts +1 -1
- package/src/utils/tag-groups.ts +1 -1
package/src/gantt/renderer.ts
CHANGED
|
@@ -22,6 +22,12 @@ import {
|
|
|
22
22
|
LEGEND_ICON_W,
|
|
23
23
|
measureLegendText,
|
|
24
24
|
} from '../utils/legend-constants';
|
|
25
|
+
import { renderLegendD3 } from '../utils/legend-d3';
|
|
26
|
+
import type {
|
|
27
|
+
LegendConfig,
|
|
28
|
+
LegendState,
|
|
29
|
+
LegendCallbacks,
|
|
30
|
+
} from '../utils/legend-types';
|
|
25
31
|
import {
|
|
26
32
|
TITLE_FONT_SIZE,
|
|
27
33
|
TITLE_FONT_WEIGHT,
|
|
@@ -1893,7 +1899,7 @@ function renderTagLegend(
|
|
|
1893
1899
|
const isSwimlane =
|
|
1894
1900
|
currentSwimlaneGroup?.toLowerCase() === group.name.toLowerCase();
|
|
1895
1901
|
const showIcon = !legendViewMode && tagGroups.length > 0;
|
|
1896
|
-
const iconReserve = showIcon ? LEGEND_ICON_W : 0;
|
|
1902
|
+
const iconReserve = showIcon && isActive ? LEGEND_ICON_W : 0;
|
|
1897
1903
|
const pillW =
|
|
1898
1904
|
measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) +
|
|
1899
1905
|
LEGEND_PILL_PAD +
|
|
@@ -1940,196 +1946,148 @@ function renderTagLegend(
|
|
|
1940
1946
|
|
|
1941
1947
|
let cursorX = 0;
|
|
1942
1948
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
const isActive =
|
|
1946
|
-
activeGroupName?.toLowerCase() === group.name.toLowerCase();
|
|
1947
|
-
const isSwimlane =
|
|
1948
|
-
currentSwimlaneGroup?.toLowerCase() === group.name.toLowerCase();
|
|
1949
|
+
// Render tag groups via centralized legend system
|
|
1950
|
+
if (visibleGroups.length > 0) {
|
|
1949
1951
|
const showIcon = !legendViewMode && tagGroups.length > 0;
|
|
1950
1952
|
const iconReserve = showIcon ? LEGEND_ICON_W : 0;
|
|
1951
|
-
const pillW =
|
|
1952
|
-
measureLegendText(group.name, LEGEND_PILL_FONT_SIZE) +
|
|
1953
|
-
LEGEND_PILL_PAD +
|
|
1954
|
-
iconReserve;
|
|
1955
|
-
const pillH = LEGEND_HEIGHT - LEGEND_CAPSULE_PAD * 2;
|
|
1956
|
-
const groupW = groupWidths[i];
|
|
1957
|
-
|
|
1958
|
-
const gEl = legendRow
|
|
1959
|
-
.append('g')
|
|
1960
|
-
.attr('transform', `translate(${cursorX}, 0)`)
|
|
1961
|
-
.attr('class', 'gantt-tag-legend-group')
|
|
1962
|
-
.attr('data-tag-group', group.name)
|
|
1963
|
-
.attr('data-line-number', String(group.lineNumber))
|
|
1964
|
-
.style('cursor', 'pointer')
|
|
1965
|
-
.on('click', () => {
|
|
1966
|
-
if (onToggle) onToggle(group.name);
|
|
1967
|
-
});
|
|
1968
|
-
|
|
1969
|
-
if (isActive) {
|
|
1970
|
-
// Outer capsule background
|
|
1971
|
-
gEl
|
|
1972
|
-
.append('rect')
|
|
1973
|
-
.attr('width', groupW)
|
|
1974
|
-
.attr('height', LEGEND_HEIGHT)
|
|
1975
|
-
.attr('rx', LEGEND_HEIGHT / 2)
|
|
1976
|
-
.attr('fill', groupBg);
|
|
1977
|
-
}
|
|
1978
1953
|
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
.attr('height', pillH)
|
|
1989
|
-
.attr('rx', pillH / 2)
|
|
1990
|
-
.attr('fill', isActive ? palette.bg : groupBg);
|
|
1991
|
-
|
|
1992
|
-
// Active pill border
|
|
1993
|
-
if (isActive) {
|
|
1994
|
-
gEl
|
|
1995
|
-
.append('rect')
|
|
1996
|
-
.attr('x', pillXOff)
|
|
1997
|
-
.attr('y', pillYOff)
|
|
1998
|
-
.attr('width', pillW)
|
|
1999
|
-
.attr('height', pillH)
|
|
2000
|
-
.attr('rx', pillH / 2)
|
|
2001
|
-
.attr('fill', 'none')
|
|
2002
|
-
.attr('stroke', mix(palette.textMuted, palette.bg, 50))
|
|
2003
|
-
.attr('stroke-width', 0.75);
|
|
2004
|
-
}
|
|
1954
|
+
// Build groups with filtered entries
|
|
1955
|
+
const legendGroups = visibleGroups.map((g) => {
|
|
1956
|
+
const key = g.name.toLowerCase();
|
|
1957
|
+
const entries = filteredEntries.get(key) ?? g.entries;
|
|
1958
|
+
return {
|
|
1959
|
+
name: g.name,
|
|
1960
|
+
entries: entries.map((e) => ({ value: e.value, color: e.color })),
|
|
1961
|
+
};
|
|
1962
|
+
});
|
|
2005
1963
|
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
1964
|
+
const legendConfig: LegendConfig = {
|
|
1965
|
+
groups: legendGroups,
|
|
1966
|
+
position: {
|
|
1967
|
+
placement: 'top-center' as const,
|
|
1968
|
+
titleRelation: 'below-title' as const,
|
|
1969
|
+
},
|
|
1970
|
+
mode: 'fixed' as const,
|
|
1971
|
+
capsulePillAddonWidth: iconReserve,
|
|
1972
|
+
};
|
|
1973
|
+
const legendState: LegendState = { activeGroup: activeGroupName };
|
|
1974
|
+
|
|
1975
|
+
const tagGroupsW =
|
|
1976
|
+
visibleGroups.reduce((s, _, i) => s + groupWidths[i], 0) +
|
|
1977
|
+
Math.max(0, (visibleGroups.length - 1) * LEGEND_GROUP_GAP);
|
|
1978
|
+
const tagGroupG = legendRow.append('g');
|
|
1979
|
+
|
|
1980
|
+
const legendCallbacks: LegendCallbacks = {
|
|
1981
|
+
onGroupToggle: onToggle,
|
|
1982
|
+
onEntryHover: (groupName, entryValue) => {
|
|
1983
|
+
const tagKey = groupName.toLowerCase();
|
|
1984
|
+
if (entryValue) {
|
|
1985
|
+
const ev = entryValue.toLowerCase();
|
|
1986
|
+
chartG
|
|
1987
|
+
.selectAll<SVGGElement, unknown>('.gantt-task')
|
|
1988
|
+
.each(function () {
|
|
1989
|
+
const el = d3Selection.select(this);
|
|
1990
|
+
el.attr(
|
|
1991
|
+
'opacity',
|
|
1992
|
+
el.attr(`data-tag-${tagKey}`) === ev ? 1 : FADE_OPACITY
|
|
1993
|
+
);
|
|
1994
|
+
});
|
|
1995
|
+
chartG
|
|
1996
|
+
.selectAll<SVGElement, unknown>('.gantt-milestone')
|
|
1997
|
+
.attr('opacity', FADE_OPACITY);
|
|
1998
|
+
chartG
|
|
1999
|
+
.selectAll<
|
|
2000
|
+
SVGElement,
|
|
2001
|
+
unknown
|
|
2002
|
+
>('.gantt-group-bar, .gantt-group-summary')
|
|
2003
|
+
.attr('opacity', FADE_OPACITY);
|
|
2004
|
+
svg
|
|
2005
|
+
.selectAll<SVGTextElement, unknown>('.gantt-task-label')
|
|
2006
|
+
.each(function () {
|
|
2007
|
+
const el = d3Selection.select(this);
|
|
2008
|
+
el.attr(
|
|
2009
|
+
'opacity',
|
|
2010
|
+
el.attr(`data-tag-${tagKey}`) === ev ? 1 : FADE_OPACITY
|
|
2011
|
+
);
|
|
2012
|
+
});
|
|
2013
|
+
svg
|
|
2014
|
+
.selectAll<SVGGElement, unknown>('.gantt-group-label')
|
|
2015
|
+
.attr('opacity', FADE_OPACITY);
|
|
2016
|
+
svg
|
|
2017
|
+
.selectAll<SVGGElement, unknown>('.gantt-lane-header')
|
|
2018
|
+
.each(function () {
|
|
2019
|
+
const el = d3Selection.select(this);
|
|
2020
|
+
el.attr(
|
|
2021
|
+
'opacity',
|
|
2022
|
+
el.attr(`data-tag-${tagKey}`) === ev ? 1 : FADE_OPACITY
|
|
2023
|
+
);
|
|
2024
|
+
});
|
|
2025
|
+
chartG
|
|
2026
|
+
.selectAll<
|
|
2027
|
+
SVGElement,
|
|
2028
|
+
unknown
|
|
2029
|
+
>('.gantt-lane-band, .gantt-lane-accent')
|
|
2030
|
+
.attr('opacity', FADE_OPACITY);
|
|
2031
|
+
} else {
|
|
2032
|
+
if (criticalPathActive) {
|
|
2033
|
+
applyCriticalPathHighlight(svg, chartG);
|
|
2034
|
+
} else {
|
|
2035
|
+
resetHighlightAll(svg, chartG);
|
|
2036
|
+
}
|
|
2033
2037
|
}
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
.text(entry.value);
|
|
2069
|
-
|
|
2070
|
-
// Hover: highlight matching tasks + labels + lane headers, fade others
|
|
2071
|
-
entryG
|
|
2072
|
-
.on('mouseenter', () => {
|
|
2073
|
-
chartG
|
|
2074
|
-
.selectAll<SVGGElement, unknown>('.gantt-task')
|
|
2075
|
-
.each(function () {
|
|
2076
|
-
const el = d3Selection.select(this);
|
|
2077
|
-
const matches = el.attr(`data-tag-${tagKey}`) === entryValue;
|
|
2078
|
-
el.attr('opacity', matches ? 1 : FADE_OPACITY);
|
|
2079
|
-
});
|
|
2080
|
-
chartG
|
|
2081
|
-
.selectAll<SVGElement, unknown>('.gantt-milestone')
|
|
2082
|
-
.attr('opacity', FADE_OPACITY);
|
|
2083
|
-
chartG
|
|
2084
|
-
.selectAll<
|
|
2085
|
-
SVGElement,
|
|
2086
|
-
unknown
|
|
2087
|
-
>('.gantt-group-bar, .gantt-group-summary')
|
|
2088
|
-
.attr('opacity', FADE_OPACITY);
|
|
2089
|
-
// Fade left-side task labels
|
|
2090
|
-
svg
|
|
2091
|
-
.selectAll<SVGTextElement, unknown>('.gantt-task-label')
|
|
2092
|
-
.each(function () {
|
|
2093
|
-
const el = d3Selection.select(this);
|
|
2094
|
-
const matches = el.attr(`data-tag-${tagKey}`) === entryValue;
|
|
2095
|
-
el.attr('opacity', matches ? 1 : FADE_OPACITY);
|
|
2096
|
-
});
|
|
2097
|
-
// Fade group labels
|
|
2098
|
-
svg
|
|
2099
|
-
.selectAll<SVGGElement, unknown>('.gantt-group-label')
|
|
2100
|
-
.attr('opacity', FADE_OPACITY);
|
|
2101
|
-
// Fade non-matching lane headers + bands + accents
|
|
2102
|
-
svg
|
|
2103
|
-
.selectAll<SVGGElement, unknown>('.gantt-lane-header')
|
|
2104
|
-
.each(function () {
|
|
2105
|
-
const el = d3Selection.select(this);
|
|
2106
|
-
const matches = el.attr(`data-tag-${tagKey}`) === entryValue;
|
|
2107
|
-
el.attr('opacity', matches ? 1 : FADE_OPACITY);
|
|
2108
|
-
});
|
|
2109
|
-
chartG
|
|
2110
|
-
.selectAll<
|
|
2111
|
-
SVGElement,
|
|
2112
|
-
unknown
|
|
2113
|
-
>('.gantt-lane-band, .gantt-lane-accent')
|
|
2114
|
-
.attr('opacity', FADE_OPACITY);
|
|
2115
|
-
})
|
|
2116
|
-
.on('mouseleave', () => {
|
|
2117
|
-
if (criticalPathActive) {
|
|
2118
|
-
applyCriticalPathHighlight(svg, chartG);
|
|
2119
|
-
} else {
|
|
2120
|
-
resetHighlightAll(svg, chartG);
|
|
2038
|
+
},
|
|
2039
|
+
onGroupRendered: (groupName, groupEl, _isActive) => {
|
|
2040
|
+
// Add swimlane icon and data attributes
|
|
2041
|
+
const group = visibleGroups.find((g) => g.name === groupName);
|
|
2042
|
+
if (group) {
|
|
2043
|
+
groupEl
|
|
2044
|
+
.attr('data-tag-group', group.name)
|
|
2045
|
+
.attr('data-line-number', String(group.lineNumber));
|
|
2046
|
+
}
|
|
2047
|
+
if (showIcon && _isActive) {
|
|
2048
|
+
const isSwimlane =
|
|
2049
|
+
currentSwimlaneGroup?.toLowerCase() === groupName.toLowerCase();
|
|
2050
|
+
const textW =
|
|
2051
|
+
measureLegendText(groupName, LEGEND_PILL_FONT_SIZE) +
|
|
2052
|
+
LEGEND_PILL_PAD;
|
|
2053
|
+
const pillXOff = LEGEND_CAPSULE_PAD;
|
|
2054
|
+
const iconX = pillXOff + textW + 3;
|
|
2055
|
+
const iconY = (LEGEND_HEIGHT - 10) / 2;
|
|
2056
|
+
const iconEl = drawSwimlaneIcon(
|
|
2057
|
+
groupEl,
|
|
2058
|
+
iconX,
|
|
2059
|
+
iconY,
|
|
2060
|
+
isSwimlane,
|
|
2061
|
+
palette
|
|
2062
|
+
);
|
|
2063
|
+
iconEl.append('title').text(`Group by ${groupName}`);
|
|
2064
|
+
iconEl.style('cursor', 'pointer').on('click', (event: Event) => {
|
|
2065
|
+
event.stopPropagation();
|
|
2066
|
+
if (onSwimlaneChange) {
|
|
2067
|
+
onSwimlaneChange(
|
|
2068
|
+
currentSwimlaneGroup?.toLowerCase() === groupName.toLowerCase()
|
|
2069
|
+
? null
|
|
2070
|
+
: groupName
|
|
2071
|
+
);
|
|
2121
2072
|
}
|
|
2122
2073
|
});
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
2076
|
+
};
|
|
2123
2077
|
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2078
|
+
renderLegendD3(
|
|
2079
|
+
tagGroupG,
|
|
2080
|
+
legendConfig,
|
|
2081
|
+
legendState,
|
|
2082
|
+
palette,
|
|
2083
|
+
isDark,
|
|
2084
|
+
legendCallbacks,
|
|
2085
|
+
tagGroupsW
|
|
2086
|
+
);
|
|
2131
2087
|
|
|
2132
|
-
|
|
2088
|
+
for (let i = 0; i < visibleGroups.length; i++) {
|
|
2089
|
+
cursorX += groupWidths[i] + LEGEND_GROUP_GAP;
|
|
2090
|
+
}
|
|
2133
2091
|
}
|
|
2134
2092
|
|
|
2135
2093
|
// Critical Path pill
|
package/src/gantt/resolver.ts
CHANGED
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
import type { GanttTask, GanttNode } from './types';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ResolverMatch {
|
|
11
11
|
task: GanttTask;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface ResolverError {
|
|
15
15
|
kind: 'not_found' | 'ambiguous';
|
|
16
16
|
message: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
type ResolverResult = ResolverMatch | ResolverError;
|
|
20
20
|
|
|
21
21
|
export function isResolverError(r: ResolverResult): r is ResolverError {
|
|
22
22
|
return 'kind' in r;
|
|
@@ -53,23 +53,23 @@ export function collectTasks(nodes: GanttNode[]): GanttTask[] {
|
|
|
53
53
|
*/
|
|
54
54
|
export function resolveTaskName(
|
|
55
55
|
name: string,
|
|
56
|
-
allTasks: GanttTask[]
|
|
56
|
+
allTasks: GanttTask[]
|
|
57
57
|
): ResolverResult {
|
|
58
58
|
const trimmed = name.trim();
|
|
59
59
|
|
|
60
60
|
// 1. Try exact label match (no dots involved)
|
|
61
|
-
const exactMatches = allTasks.filter(t => t.label === trimmed);
|
|
61
|
+
const exactMatches = allTasks.filter((t) => t.label === trimmed);
|
|
62
62
|
if (exactMatches.length === 1) {
|
|
63
63
|
return { task: exactMatches[0] };
|
|
64
64
|
}
|
|
65
65
|
if (exactMatches.length > 1) {
|
|
66
66
|
// Multiple tasks with same name — need disambiguation
|
|
67
|
-
const suggestions = exactMatches.map(t =>
|
|
67
|
+
const suggestions = exactMatches.map((t) =>
|
|
68
68
|
t.groupPath.length > 0 ? `${t.groupPath.join('.')}.${t.label}` : t.label
|
|
69
69
|
);
|
|
70
70
|
return {
|
|
71
71
|
kind: 'ambiguous',
|
|
72
|
-
message: `Multiple tasks match "${trimmed}". Did you mean ${suggestions.map(s => `\`${s}\``).join(' or ')}?`,
|
|
72
|
+
message: `Multiple tasks match "${trimmed}". Did you mean ${suggestions.map((s) => `\`${s}\``).join(' or ')}?`,
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -80,7 +80,7 @@ export function resolveTaskName(
|
|
|
80
80
|
const taskLabel = trimmed.substring(lastDotIdx + 1);
|
|
81
81
|
|
|
82
82
|
// Find tasks whose label matches and whose group path ends with the prefix
|
|
83
|
-
const matches = allTasks.filter(t => {
|
|
83
|
+
const matches = allTasks.filter((t) => {
|
|
84
84
|
if (t.label !== taskLabel) return false;
|
|
85
85
|
return matchesGroupPath(t.groupPath, groupPrefix);
|
|
86
86
|
});
|
|
@@ -89,12 +89,12 @@ export function resolveTaskName(
|
|
|
89
89
|
return { task: matches[0] };
|
|
90
90
|
}
|
|
91
91
|
if (matches.length > 1) {
|
|
92
|
-
const suggestions = matches.map(t =>
|
|
92
|
+
const suggestions = matches.map((t) =>
|
|
93
93
|
t.groupPath.length > 0 ? `${t.groupPath.join('.')}.${t.label}` : t.label
|
|
94
94
|
);
|
|
95
95
|
return {
|
|
96
96
|
kind: 'ambiguous',
|
|
97
|
-
message: `Multiple tasks match "${trimmed}". Did you mean ${suggestions.map(s => `\`${s}\``).join(' or ')}?`,
|
|
97
|
+
message: `Multiple tasks match "${trimmed}". Did you mean ${suggestions.map((s) => `\`${s}\``).join(' or ')}?`,
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -106,8 +106,8 @@ export function resolveTaskName(
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// 3. No match found — try case-insensitive as a fallback for suggestions
|
|
109
|
-
const caseInsensitive = allTasks.filter(
|
|
110
|
-
t.label.toLowerCase() === trimmed.toLowerCase()
|
|
109
|
+
const caseInsensitive = allTasks.filter(
|
|
110
|
+
(t) => t.label.toLowerCase() === trimmed.toLowerCase()
|
|
111
111
|
);
|
|
112
112
|
if (caseInsensitive.length > 0) {
|
|
113
113
|
return {
|
|
@@ -134,11 +134,16 @@ export function resolveTaskName(
|
|
|
134
134
|
function matchesGroupPath(groupPath: string[], prefix: string): boolean {
|
|
135
135
|
// Simple case: prefix is a single segment
|
|
136
136
|
if (!prefix.includes('.')) {
|
|
137
|
-
return groupPath.some(g => g === prefix);
|
|
137
|
+
return groupPath.some((g) => g === prefix);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
// Multi-segment prefix: try matching from the start of the group path
|
|
141
141
|
const pathStr = groupPath.join('.');
|
|
142
142
|
// Check if the full prefix matches any contiguous section of the path
|
|
143
|
-
return
|
|
143
|
+
return (
|
|
144
|
+
pathStr === prefix ||
|
|
145
|
+
pathStr.endsWith('.' + prefix) ||
|
|
146
|
+
pathStr.startsWith(prefix + '.') ||
|
|
147
|
+
pathStr.includes('.' + prefix + '.')
|
|
148
|
+
);
|
|
144
149
|
}
|
package/src/index.ts
CHANGED
|
@@ -356,8 +356,28 @@ export {
|
|
|
356
356
|
computeScatterLabelGraphics,
|
|
357
357
|
} from './echarts';
|
|
358
358
|
export type { ScatterLabelPoint } from './echarts';
|
|
359
|
-
export {
|
|
359
|
+
export {
|
|
360
|
+
renderLegendSvg,
|
|
361
|
+
renderLegendSvgFromConfig,
|
|
362
|
+
type LegendGroupData,
|
|
363
|
+
} from './utils/legend-svg';
|
|
360
364
|
export { LEGEND_HEIGHT } from './utils/legend-constants';
|
|
365
|
+
export { renderLegendD3 } from './utils/legend-d3';
|
|
366
|
+
export {
|
|
367
|
+
computeLegendLayout,
|
|
368
|
+
getLegendReservedHeight,
|
|
369
|
+
} from './utils/legend-layout';
|
|
370
|
+
export type {
|
|
371
|
+
LegendConfig,
|
|
372
|
+
LegendState,
|
|
373
|
+
LegendCallbacks,
|
|
374
|
+
LegendPosition,
|
|
375
|
+
LegendMode,
|
|
376
|
+
LegendControl,
|
|
377
|
+
LegendLayout,
|
|
378
|
+
LegendHandle,
|
|
379
|
+
LegendPalette,
|
|
380
|
+
} from './utils/legend-types';
|
|
361
381
|
export { buildMermaidQuadrant } from './dgmo-mermaid';
|
|
362
382
|
|
|
363
383
|
// ============================================================
|
|
@@ -420,6 +440,8 @@ export {
|
|
|
420
440
|
tokyoNightPalette,
|
|
421
441
|
oneDarkPalette,
|
|
422
442
|
boldPalette,
|
|
443
|
+
draculaPalette,
|
|
444
|
+
monokaiPalette,
|
|
423
445
|
// Mermaid bridge
|
|
424
446
|
buildMermaidThemeVars,
|
|
425
447
|
buildThemeCSS,
|