@diagrammo/dgmo 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto.js +1 -1
- package/dist/auto.mjs +1 -1
- package/dist/element.js +1 -1
- package/dist/element.mjs +1 -1
- package/package.json +19 -70
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
package/src/mindmap/renderer.ts
DELETED
|
@@ -1,637 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Mindmap SVG Renderer
|
|
3
|
-
// ============================================================
|
|
4
|
-
|
|
5
|
-
import { tagAttrKey } from '../utils/tag-groups';
|
|
6
|
-
import * as d3Selection from 'd3-selection';
|
|
7
|
-
import { FONT_FAMILY } from '../fonts';
|
|
8
|
-
import {
|
|
9
|
-
runInExportContainer,
|
|
10
|
-
extractExportSvg,
|
|
11
|
-
} from '../utils/export-container';
|
|
12
|
-
import type { PaletteColors } from '../palettes';
|
|
13
|
-
import { contrastText, shapeFill } from '../palettes/color-utils';
|
|
14
|
-
import type { ParsedMindmap } from './types';
|
|
15
|
-
import type { MindmapLayoutResult } from './types';
|
|
16
|
-
import { parseMindmap } from './parser';
|
|
17
|
-
import { layoutMindmap } from './layout';
|
|
18
|
-
import { computeNodeText } from './text-wrap';
|
|
19
|
-
import { renderInlineText } from '../utils/inline-markdown';
|
|
20
|
-
import { preprocessDescriptionLine } from '../utils/description-helpers';
|
|
21
|
-
import { renderIntegratedLegend } from '../utils/legend-integration';
|
|
22
|
-
import type { LegendConfig } from '../utils/legend-types';
|
|
23
|
-
import { LEGEND_GROUP_GAP } from '../utils/legend-constants';
|
|
24
|
-
import { getMaxLegendReservedHeight } from '../utils/legend-layout';
|
|
25
|
-
import { TITLE_FONT_SIZE, TITLE_FONT_WEIGHT } from '../utils/title-constants';
|
|
26
|
-
import { ScaleContext } from '../utils/scaling';
|
|
27
|
-
|
|
28
|
-
// ============================================================
|
|
29
|
-
// Constants
|
|
30
|
-
// ============================================================
|
|
31
|
-
|
|
32
|
-
const DIAGRAM_PADDING = 20;
|
|
33
|
-
const TITLE_HEIGHT = 30;
|
|
34
|
-
const SINGLE_LABEL_HEIGHT = 28;
|
|
35
|
-
const LABEL_LINE_HEIGHT = 18;
|
|
36
|
-
const DESC_LINE_HEIGHT = 14;
|
|
37
|
-
const NODE_RADIUS = 6;
|
|
38
|
-
const ROOT_STROKE_WIDTH = 2.5;
|
|
39
|
-
import {
|
|
40
|
-
NODE_STROKE_WIDTH,
|
|
41
|
-
EDGE_STROKE_WIDTH,
|
|
42
|
-
COLLAPSE_BAR_HEIGHT,
|
|
43
|
-
} from '../utils/visual-conventions'; // shared (Story 111.1)
|
|
44
|
-
|
|
45
|
-
function nodeFill(
|
|
46
|
-
palette: PaletteColors,
|
|
47
|
-
isDark: boolean,
|
|
48
|
-
nodeColor?: string,
|
|
49
|
-
solid?: boolean
|
|
50
|
-
): string {
|
|
51
|
-
const color = nodeColor ?? palette.primary;
|
|
52
|
-
return shapeFill(palette, color, isDark, {
|
|
53
|
-
...(solid !== undefined && { solid }),
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function nodeStroke(palette: PaletteColors, nodeColor?: string): string {
|
|
58
|
-
return nodeColor ?? palette.primary;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Depth color sequence — ROYGBIV-ish from the palette's named colors */
|
|
62
|
-
const DEPTH_COLOR_KEYS = [
|
|
63
|
-
'red',
|
|
64
|
-
'orange',
|
|
65
|
-
'yellow',
|
|
66
|
-
'green',
|
|
67
|
-
'blue',
|
|
68
|
-
'purple',
|
|
69
|
-
'teal',
|
|
70
|
-
'cyan',
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
function depthColor(depth: number, palette: PaletteColors): string {
|
|
74
|
-
const key = DEPTH_COLOR_KEYS[depth] as
|
|
75
|
-
| keyof typeof palette.colors
|
|
76
|
-
| undefined;
|
|
77
|
-
if (key && key in palette.colors) {
|
|
78
|
-
return palette.colors[key];
|
|
79
|
-
}
|
|
80
|
-
return palette.colors.gray;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ============================================================
|
|
84
|
-
// Main renderer
|
|
85
|
-
// ============================================================
|
|
86
|
-
|
|
87
|
-
export function renderMindmap(
|
|
88
|
-
container: HTMLDivElement,
|
|
89
|
-
parsed: ParsedMindmap,
|
|
90
|
-
layout: MindmapLayoutResult,
|
|
91
|
-
palette: PaletteColors,
|
|
92
|
-
isDark: boolean,
|
|
93
|
-
onClickItem?: (lineNumber: number) => void,
|
|
94
|
-
exportDims?: { width?: number; height?: number },
|
|
95
|
-
onToggleNode?: (nodeId: string) => void,
|
|
96
|
-
hideDescriptions?: boolean,
|
|
97
|
-
activeTagGroup?: string | null,
|
|
98
|
-
options?: {
|
|
99
|
-
colorByDepth?: boolean;
|
|
100
|
-
onToggleColorByDepth?: (active: boolean) => void;
|
|
101
|
-
onToggleDescriptions?: (active: boolean) => void;
|
|
102
|
-
controlsExpanded?: boolean;
|
|
103
|
-
onToggleControlsExpand?: () => void;
|
|
104
|
-
exportMode?: boolean;
|
|
105
|
-
/** When 'app', controls (Descriptions / Depth Colors) are hosted by the app
|
|
106
|
-
* overlay strip — inline gear suppressed, controls row + anchor reserved. */
|
|
107
|
-
controlsHost?: 'app' | 'inline';
|
|
108
|
-
}
|
|
109
|
-
): void {
|
|
110
|
-
const isExport = !!exportDims;
|
|
111
|
-
const containerWidth =
|
|
112
|
-
exportDims?.width ?? (container.getBoundingClientRect().width || 800);
|
|
113
|
-
const containerHeight =
|
|
114
|
-
exportDims?.height ?? (container.getBoundingClientRect().height || 600);
|
|
115
|
-
|
|
116
|
-
// Clear existing content
|
|
117
|
-
d3Selection.select(container).selectAll('*').remove();
|
|
118
|
-
|
|
119
|
-
const svg = d3Selection
|
|
120
|
-
.select(container)
|
|
121
|
-
.append('svg')
|
|
122
|
-
.attr('width', containerWidth)
|
|
123
|
-
.attr('height', containerHeight)
|
|
124
|
-
.attr('viewBox', `0 0 ${containerWidth} ${containerHeight}`)
|
|
125
|
-
.attr('preserveAspectRatio', 'xMidYMin meet')
|
|
126
|
-
.style('font-family', FONT_FAMILY);
|
|
127
|
-
|
|
128
|
-
const appHosted = options?.controlsHost === 'app';
|
|
129
|
-
// App-hosted: Descriptions / Depth Colors move to the app overlay, so a
|
|
130
|
-
// controls-only legend (no tag groups) has nothing left to render.
|
|
131
|
-
const hasControls =
|
|
132
|
-
!!options?.onToggleColorByDepth || !!options?.onToggleDescriptions;
|
|
133
|
-
const hasLegend = parsed.tagGroups.length > 0 || (hasControls && !appHosted);
|
|
134
|
-
const fixedLegend = !isExport && hasLegend;
|
|
135
|
-
const legendReserve = fixedLegend
|
|
136
|
-
? getMaxLegendReservedHeight(
|
|
137
|
-
{
|
|
138
|
-
groups: parsed.tagGroups,
|
|
139
|
-
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
140
|
-
mode: 'preview',
|
|
141
|
-
},
|
|
142
|
-
containerWidth
|
|
143
|
-
) + LEGEND_GROUP_GAP
|
|
144
|
-
: 0;
|
|
145
|
-
const showTitle = !!parsed.title && parsed.options['no-title'] !== 'on';
|
|
146
|
-
const fixedTitle = !isExport && showTitle;
|
|
147
|
-
const titleReserve = fixedTitle ? TITLE_HEIGHT : 0;
|
|
148
|
-
|
|
149
|
-
const availWidth = containerWidth;
|
|
150
|
-
const availHeight =
|
|
151
|
-
containerHeight - DIAGRAM_PADDING * 2 - legendReserve - titleReserve;
|
|
152
|
-
|
|
153
|
-
// Fit to BOTH axes so a tall tree shrinks to fit a short canvas instead of
|
|
154
|
-
// overflowing vertically (export sizes its own canvas, so it stays identity).
|
|
155
|
-
let ctx = isExport
|
|
156
|
-
? ScaleContext.identity()
|
|
157
|
-
: ScaleContext.fromBox(
|
|
158
|
-
availWidth,
|
|
159
|
-
layout.width,
|
|
160
|
-
availHeight,
|
|
161
|
-
layout.height
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
let renderLayout = layout;
|
|
165
|
-
if (ctx.factor < 1) {
|
|
166
|
-
const hiddenCounts = new Map<string, number>();
|
|
167
|
-
for (const n of layout.nodes) {
|
|
168
|
-
if (n.hiddenCount != null && n.hiddenCount > 0) {
|
|
169
|
-
hiddenCounts.set(n.id, n.hiddenCount);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const relayout = (c: ScaleContext): MindmapLayoutResult =>
|
|
173
|
-
layoutMindmap(parsed, palette, {
|
|
174
|
-
interactive: !isExport,
|
|
175
|
-
...(hiddenCounts.size > 0 && { hiddenCounts }),
|
|
176
|
-
activeTagGroup: activeTagGroup ?? null,
|
|
177
|
-
...(hideDescriptions !== undefined && { hideDescriptions }),
|
|
178
|
-
ctx: c,
|
|
179
|
-
});
|
|
180
|
-
renderLayout = relayout(ctx);
|
|
181
|
-
// Scaling is non-linear, so one pass can still overflow. Re-measure the
|
|
182
|
-
// laid-out result and tighten until it fits both axes or hits the floor.
|
|
183
|
-
for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
|
|
184
|
-
const refit = Math.min(
|
|
185
|
-
availWidth / renderLayout.width,
|
|
186
|
-
availHeight / renderLayout.height
|
|
187
|
-
);
|
|
188
|
-
if (refit >= 0.999) break; // already fits
|
|
189
|
-
ctx = ScaleContext.fromFactor(ctx.factor * refit);
|
|
190
|
-
renderLayout = relayout(ctx);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Re-layout keeps text readable but is floor-limited, so a dense tree can
|
|
195
|
-
// still exceed the canvas. Apply a final uniform scale as a hard guarantee
|
|
196
|
-
// that the diagram always fits within the canvas (no overflow), regardless
|
|
197
|
-
// of how small the canvas is. Export sizes its own canvas, so this is a no-op
|
|
198
|
-
// there (fitScale === 1).
|
|
199
|
-
const fitScale = isExport
|
|
200
|
-
? 1
|
|
201
|
-
: Math.min(
|
|
202
|
-
1,
|
|
203
|
-
renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
|
|
204
|
-
renderLayout.height > 0 ? availHeight / renderLayout.height : 1
|
|
205
|
-
);
|
|
206
|
-
const scaledWidth = renderLayout.width * fitScale;
|
|
207
|
-
const scaledHeight = renderLayout.height * fitScale;
|
|
208
|
-
|
|
209
|
-
const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
|
|
210
|
-
const offsetY =
|
|
211
|
-
DIAGRAM_PADDING +
|
|
212
|
-
legendReserve +
|
|
213
|
-
titleReserve +
|
|
214
|
-
Math.max(0, (availHeight - scaledHeight) / 2);
|
|
215
|
-
|
|
216
|
-
const mainG = svg
|
|
217
|
-
.append('g')
|
|
218
|
-
.attr(
|
|
219
|
-
'transform',
|
|
220
|
-
`translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ''}`
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
if (ctx.isBelowFloor) {
|
|
224
|
-
svg.attr('width', '100%');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Title — fixed at top in app mode (above legend), inside scaled group in export
|
|
228
|
-
if (showTitle) {
|
|
229
|
-
const titleParent = fixedTitle ? svg : mainG;
|
|
230
|
-
const titleX = fixedTitle ? containerWidth / 2 : renderLayout.width / 2;
|
|
231
|
-
const titleY = fixedTitle
|
|
232
|
-
? DIAGRAM_PADDING + TITLE_FONT_SIZE
|
|
233
|
-
: TITLE_FONT_SIZE;
|
|
234
|
-
const titleText = titleParent
|
|
235
|
-
.append('text')
|
|
236
|
-
.attr('x', titleX)
|
|
237
|
-
.attr('y', titleY)
|
|
238
|
-
.attr('text-anchor', 'middle')
|
|
239
|
-
.attr('font-size', TITLE_FONT_SIZE)
|
|
240
|
-
.attr('font-weight', TITLE_FONT_WEIGHT)
|
|
241
|
-
.attr('fill', palette.text)
|
|
242
|
-
.attr('class', 'chart-title')
|
|
243
|
-
.text(parsed.title);
|
|
244
|
-
|
|
245
|
-
if (parsed.titleLineNumber) {
|
|
246
|
-
titleText.attr('data-line-number', parsed.titleLineNumber);
|
|
247
|
-
}
|
|
248
|
-
if (onClickItem && parsed.titleLineNumber) {
|
|
249
|
-
titleText
|
|
250
|
-
.style('cursor', 'pointer')
|
|
251
|
-
.on('click', () => onClickItem(parsed.titleLineNumber!));
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Legend — fixed below title, outside scaled group, in interactive mode
|
|
256
|
-
if (fixedLegend) {
|
|
257
|
-
const legendG = svg
|
|
258
|
-
.append('g')
|
|
259
|
-
.attr('class', 'mindmap-legend-fixed')
|
|
260
|
-
.attr('transform', `translate(0, ${DIAGRAM_PADDING + titleReserve})`);
|
|
261
|
-
|
|
262
|
-
// Collect used tag values from all nodes to filter legend entries
|
|
263
|
-
const usedValues = new Map<string, Set<string>>(); // groupName → set of used values
|
|
264
|
-
for (const node of renderLayout.nodes) {
|
|
265
|
-
for (const tg of parsed.tagGroups) {
|
|
266
|
-
const key = tg.name.toLowerCase();
|
|
267
|
-
const val = node.metadata[key];
|
|
268
|
-
if (val) {
|
|
269
|
-
if (!usedValues.has(key)) usedValues.set(key, new Set());
|
|
270
|
-
usedValues.get(key)!.add(val.toLowerCase());
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Build controls toggles
|
|
276
|
-
const toggles: import('../utils/legend-types').ControlsGroupToggle[] = [];
|
|
277
|
-
if (options?.onToggleDescriptions) {
|
|
278
|
-
toggles.push({
|
|
279
|
-
id: 'descriptions',
|
|
280
|
-
type: 'toggle' as const,
|
|
281
|
-
label: 'Descriptions',
|
|
282
|
-
active: !hideDescriptions,
|
|
283
|
-
onToggle: (active) => options.onToggleDescriptions!(active),
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
if (options?.onToggleColorByDepth) {
|
|
287
|
-
toggles.push({
|
|
288
|
-
id: 'depth-colors',
|
|
289
|
-
type: 'toggle' as const,
|
|
290
|
-
label: 'Depth Colors',
|
|
291
|
-
active: options.colorByDepth ?? false,
|
|
292
|
-
onToggle: options.onToggleColorByDepth,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
const controlsToggles: LegendConfig['controlsGroup'] =
|
|
296
|
-
toggles.length > 0 ? { toggles } : undefined;
|
|
297
|
-
|
|
298
|
-
const legendConfig: LegendConfig = {
|
|
299
|
-
groups: parsed.tagGroups.map((tg) => {
|
|
300
|
-
const used = usedValues.get(tg.name.toLowerCase());
|
|
301
|
-
return {
|
|
302
|
-
name: tg.name,
|
|
303
|
-
...(tg.alias !== undefined && { alias: tg.alias }),
|
|
304
|
-
entries: tg.entries
|
|
305
|
-
.filter((e) => used?.has(e.value.toLowerCase()))
|
|
306
|
-
.map((e) => ({ value: e.value, color: e.color })),
|
|
307
|
-
};
|
|
308
|
-
}),
|
|
309
|
-
position: { placement: 'top-center', titleRelation: 'below-title' },
|
|
310
|
-
mode: options?.exportMode ? 'export' : 'preview',
|
|
311
|
-
...(controlsToggles !== undefined && { controlsGroup: controlsToggles }),
|
|
312
|
-
...(options?.controlsHost !== undefined && {
|
|
313
|
-
controlsHost: options.controlsHost,
|
|
314
|
-
}),
|
|
315
|
-
};
|
|
316
|
-
const legendPalette = {
|
|
317
|
-
text: palette.text,
|
|
318
|
-
textMuted: palette.textMuted,
|
|
319
|
-
bg: palette.bg,
|
|
320
|
-
surface: palette.surface,
|
|
321
|
-
primary: palette.primary,
|
|
322
|
-
};
|
|
323
|
-
const legendCallbacks: import('../utils/legend-types').LegendCallbacks = {
|
|
324
|
-
...(options?.onToggleControlsExpand !== undefined && {
|
|
325
|
-
onControlsExpand: options.onToggleControlsExpand,
|
|
326
|
-
}),
|
|
327
|
-
onControlsToggle: (id, active) => {
|
|
328
|
-
if (id === 'depth-colors' && options?.onToggleColorByDepth) {
|
|
329
|
-
options.onToggleColorByDepth(active);
|
|
330
|
-
}
|
|
331
|
-
if (id === 'descriptions' && options?.onToggleDescriptions) {
|
|
332
|
-
options.onToggleDescriptions(active);
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
renderIntegratedLegend(legendG, {
|
|
337
|
-
...legendConfig,
|
|
338
|
-
palette: legendPalette,
|
|
339
|
-
isDark,
|
|
340
|
-
width: containerWidth,
|
|
341
|
-
activeGroup: options?.colorByDepth
|
|
342
|
-
? null
|
|
343
|
-
: activeTagGroup !== undefined
|
|
344
|
-
? activeTagGroup
|
|
345
|
-
: (parsed.options['active-tag'] ?? null),
|
|
346
|
-
hiddenAttributes: new Set(),
|
|
347
|
-
...(options?.controlsExpanded !== undefined && {
|
|
348
|
-
controlsExpanded: options.controlsExpanded,
|
|
349
|
-
}),
|
|
350
|
-
callbacks: legendCallbacks,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Render edges (background layer)
|
|
355
|
-
for (const edge of renderLayout.edges) {
|
|
356
|
-
mainG
|
|
357
|
-
.append('path')
|
|
358
|
-
.attr('class', 'mindmap-edge')
|
|
359
|
-
.attr('d', edge.path)
|
|
360
|
-
.attr('fill', 'none')
|
|
361
|
-
.attr('stroke', palette.textMuted)
|
|
362
|
-
.attr('stroke-width', EDGE_STROKE_WIDTH)
|
|
363
|
-
.attr('stroke-opacity', 0.5);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Render nodes (foreground layer)
|
|
367
|
-
for (const node of renderLayout.nodes) {
|
|
368
|
-
const isRoot = node.radius === 0 && renderLayout.nodes.indexOf(node) === 0;
|
|
369
|
-
const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH;
|
|
370
|
-
const effectiveColor = options?.colorByDepth
|
|
371
|
-
? depthColor(node.depth, palette)
|
|
372
|
-
: node.color;
|
|
373
|
-
const fill = nodeFill(
|
|
374
|
-
palette,
|
|
375
|
-
isDark,
|
|
376
|
-
effectiveColor,
|
|
377
|
-
parsed.options['solid-fill'] === 'on'
|
|
378
|
-
);
|
|
379
|
-
const stroke = nodeStroke(palette, effectiveColor);
|
|
380
|
-
const onFillText = contrastText(
|
|
381
|
-
fill,
|
|
382
|
-
palette.textOnFillLight,
|
|
383
|
-
palette.textOnFillDark
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
const nodeG = mainG
|
|
387
|
-
.append('g')
|
|
388
|
-
.attr('class', 'mindmap-node')
|
|
389
|
-
.attr('data-line-number', node.lineNumber);
|
|
390
|
-
|
|
391
|
-
// Expose active tag group value for legend-entry hover dimming
|
|
392
|
-
if (activeTagGroup) {
|
|
393
|
-
const tagKey = tagAttrKey(activeTagGroup);
|
|
394
|
-
const metaValue = node.metadata[tagKey];
|
|
395
|
-
if (metaValue) {
|
|
396
|
-
nodeG.attr(`data-tag-${tagKey}`, metaValue.toLowerCase());
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Add collapse toggle attributes for nodes with children
|
|
401
|
-
if (node.hasChildren) {
|
|
402
|
-
nodeG
|
|
403
|
-
.attr('data-node-toggle', node.id)
|
|
404
|
-
.attr('tabindex', '0')
|
|
405
|
-
.attr('role', 'button')
|
|
406
|
-
.attr(
|
|
407
|
-
'aria-expanded',
|
|
408
|
-
node.hiddenCount != null && node.hiddenCount > 0 ? 'false' : 'true'
|
|
409
|
-
)
|
|
410
|
-
.attr('aria-label', node.label);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Node rectangle
|
|
414
|
-
nodeG
|
|
415
|
-
.append('rect')
|
|
416
|
-
.attr('x', node.x)
|
|
417
|
-
.attr('y', node.y)
|
|
418
|
-
.attr('width', node.width)
|
|
419
|
-
.attr('height', node.height)
|
|
420
|
-
.attr('rx', NODE_RADIUS)
|
|
421
|
-
.attr('ry', NODE_RADIUS)
|
|
422
|
-
.attr('fill', fill)
|
|
423
|
-
.attr('stroke', stroke)
|
|
424
|
-
.attr('stroke-width', strokeW);
|
|
425
|
-
|
|
426
|
-
// Determine if description is visible (needed for label centering)
|
|
427
|
-
const collapsed = node.hiddenCount != null && node.hiddenCount > 0;
|
|
428
|
-
const showDesc = !hideDescriptions && !!node.description && !collapsed;
|
|
429
|
-
|
|
430
|
-
// Compute wrapped text layout (same logic as layout.ts for sizing agreement)
|
|
431
|
-
const textLayout = computeNodeText(
|
|
432
|
-
node.label,
|
|
433
|
-
node.description,
|
|
434
|
-
node.depth,
|
|
435
|
-
node.width,
|
|
436
|
-
hideDescriptions || collapsed
|
|
437
|
-
);
|
|
438
|
-
const {
|
|
439
|
-
labelLines,
|
|
440
|
-
labelFontSize: fontSize,
|
|
441
|
-
descLines,
|
|
442
|
-
descFontSize,
|
|
443
|
-
} = textLayout;
|
|
444
|
-
|
|
445
|
-
// Label zone height
|
|
446
|
-
const labelLineCount = labelLines.length;
|
|
447
|
-
const labelZoneH =
|
|
448
|
-
labelLineCount <= 1
|
|
449
|
-
? SINGLE_LABEL_HEIGHT
|
|
450
|
-
: LABEL_LINE_HEIGHT * labelLineCount;
|
|
451
|
-
const labelZoneHeight = showDesc ? labelZoneH : node.height;
|
|
452
|
-
|
|
453
|
-
// Label text — vertically centered in the label zone
|
|
454
|
-
const centerX = node.x + node.width / 2;
|
|
455
|
-
if (labelLineCount <= 1) {
|
|
456
|
-
// Single line — simple centering
|
|
457
|
-
const labelY = node.y + labelZoneHeight / 2 + fontSize * 0.35;
|
|
458
|
-
nodeG
|
|
459
|
-
.append('text')
|
|
460
|
-
.attr('x', centerX)
|
|
461
|
-
.attr('y', labelY)
|
|
462
|
-
.attr('text-anchor', 'middle')
|
|
463
|
-
.attr('font-size', fontSize)
|
|
464
|
-
.attr('font-weight', isRoot ? 'bold' : 'normal')
|
|
465
|
-
.attr('fill', onFillText)
|
|
466
|
-
// labelLineCount <= 1 implies labelLines has exactly one element here.
|
|
467
|
-
.text(labelLines[0]!);
|
|
468
|
-
} else {
|
|
469
|
-
// Multi-line — use tspan elements
|
|
470
|
-
// Visual text block spans from first baseline to last baseline:
|
|
471
|
-
// blockH = (lineCount - 1) * lineHeight
|
|
472
|
-
// Center that block in the zone, then offset each baseline by fontSize * 0.35
|
|
473
|
-
const blockH = LABEL_LINE_HEIGHT * (labelLineCount - 1);
|
|
474
|
-
const firstBaselineY =
|
|
475
|
-
node.y + labelZoneHeight / 2 - blockH / 2 + fontSize * 0.35;
|
|
476
|
-
const textEl = nodeG
|
|
477
|
-
.append('text')
|
|
478
|
-
.attr('x', centerX)
|
|
479
|
-
.attr('text-anchor', 'middle')
|
|
480
|
-
.attr('font-size', fontSize)
|
|
481
|
-
.attr('font-weight', isRoot ? 'bold' : 'normal')
|
|
482
|
-
.attr('fill', onFillText);
|
|
483
|
-
|
|
484
|
-
for (let i = 0; i < labelLines.length; i++) {
|
|
485
|
-
textEl
|
|
486
|
-
.append('tspan')
|
|
487
|
-
.attr('x', centerX)
|
|
488
|
-
.attr('y', firstBaselineY + i * LABEL_LINE_HEIGHT)
|
|
489
|
-
// In-bounds by loop guard (i < labelLines.length).
|
|
490
|
-
.text(labelLines[i]!);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Hover tooltip for truncated/wrapped labels — on the <g>, not on <text>
|
|
495
|
-
if (labelLines.length > 1 || labelLines[0] !== node.label) {
|
|
496
|
-
nodeG.append('title').text(node.label);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Description — separator line + muted text below label
|
|
500
|
-
if (showDesc && descLines.length > 0) {
|
|
501
|
-
const separatorY = node.y + labelZoneH;
|
|
502
|
-
|
|
503
|
-
// Separator line
|
|
504
|
-
nodeG
|
|
505
|
-
.append('line')
|
|
506
|
-
.attr('x1', node.x)
|
|
507
|
-
.attr('y1', separatorY)
|
|
508
|
-
.attr('x2', node.x + node.width)
|
|
509
|
-
.attr('y2', separatorY)
|
|
510
|
-
.attr('stroke', stroke)
|
|
511
|
-
.attr('stroke-opacity', 0.3)
|
|
512
|
-
.attr('stroke-width', 1);
|
|
513
|
-
|
|
514
|
-
// Description text (with inline markdown + preprocessing)
|
|
515
|
-
if (descLines.length <= 1) {
|
|
516
|
-
const descY = separatorY + 4 + descFontSize;
|
|
517
|
-
// descLines.length > 0 from outer guard and <= 1 here means exactly one.
|
|
518
|
-
const processed = preprocessDescriptionLine(descLines[0]!);
|
|
519
|
-
const textEl = nodeG
|
|
520
|
-
.append('text')
|
|
521
|
-
.attr('x', centerX)
|
|
522
|
-
.attr('y', descY)
|
|
523
|
-
.attr('text-anchor', 'middle')
|
|
524
|
-
.attr('font-size', descFontSize)
|
|
525
|
-
.attr('fill', onFillText);
|
|
526
|
-
renderInlineText(textEl, processed, palette, descFontSize);
|
|
527
|
-
} else {
|
|
528
|
-
const descStartY = separatorY + 4 + descFontSize;
|
|
529
|
-
for (let i = 0; i < descLines.length; i++) {
|
|
530
|
-
// In-bounds by loop guard (i < descLines.length).
|
|
531
|
-
const processed = preprocessDescriptionLine(descLines[i]!);
|
|
532
|
-
const textEl = nodeG
|
|
533
|
-
.append('text')
|
|
534
|
-
.attr('x', centerX)
|
|
535
|
-
.attr('y', descStartY + i * DESC_LINE_HEIGHT)
|
|
536
|
-
.attr('text-anchor', 'middle')
|
|
537
|
-
.attr('font-size', descFontSize)
|
|
538
|
-
.attr('fill', onFillText);
|
|
539
|
-
renderInlineText(textEl, processed, palette, descFontSize);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Collapse drill-bar (interactive mode only)
|
|
545
|
-
if (!isExport && node.hiddenCount != null && node.hiddenCount > 0) {
|
|
546
|
-
// Clip path for rounded bottom
|
|
547
|
-
const clipId = `collapse-clip-${node.id}`;
|
|
548
|
-
const defs = mainG.append('defs');
|
|
549
|
-
defs
|
|
550
|
-
.append('clipPath')
|
|
551
|
-
.attr('id', clipId)
|
|
552
|
-
.append('rect')
|
|
553
|
-
.attr('x', node.x)
|
|
554
|
-
.attr('y', node.y)
|
|
555
|
-
.attr('width', node.width)
|
|
556
|
-
.attr('height', node.height)
|
|
557
|
-
.attr('rx', NODE_RADIUS)
|
|
558
|
-
.attr('ry', NODE_RADIUS);
|
|
559
|
-
|
|
560
|
-
nodeG
|
|
561
|
-
.append('rect')
|
|
562
|
-
.attr('class', 'collapse-bar')
|
|
563
|
-
.attr('x', node.x)
|
|
564
|
-
.attr('y', node.y + node.height - COLLAPSE_BAR_HEIGHT)
|
|
565
|
-
.attr('width', node.width)
|
|
566
|
-
.attr('height', COLLAPSE_BAR_HEIGHT)
|
|
567
|
-
.attr('fill', stroke)
|
|
568
|
-
.attr('clip-path', `url(#${clipId})`);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// Click handler
|
|
572
|
-
if (onClickItem) {
|
|
573
|
-
nodeG.style('cursor', 'pointer').on('click', (event: Event) => {
|
|
574
|
-
// If this node has a toggle and the toggle callback exists,
|
|
575
|
-
// use toggle behavior instead of navigation
|
|
576
|
-
if (node.hasChildren && onToggleNode) {
|
|
577
|
-
event.stopPropagation();
|
|
578
|
-
onToggleNode(node.id);
|
|
579
|
-
} else {
|
|
580
|
-
onClickItem(node.lineNumber);
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Hover opacity
|
|
586
|
-
if (!isExport) {
|
|
587
|
-
nodeG
|
|
588
|
-
.on('mouseenter', function () {
|
|
589
|
-
d3Selection.select(this).attr('opacity', 0.7);
|
|
590
|
-
})
|
|
591
|
-
.on('mouseleave', function () {
|
|
592
|
-
d3Selection.select(this).attr('opacity', 1);
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// ============================================================
|
|
599
|
-
// Export convenience function
|
|
600
|
-
// ============================================================
|
|
601
|
-
|
|
602
|
-
export function renderMindmapForExport(
|
|
603
|
-
content: string,
|
|
604
|
-
theme: 'light' | 'dark' | 'transparent',
|
|
605
|
-
palette: PaletteColors
|
|
606
|
-
): string {
|
|
607
|
-
const parsed = parseMindmap(content, palette);
|
|
608
|
-
if (parsed.error) return '';
|
|
609
|
-
|
|
610
|
-
const isDark = theme === 'dark';
|
|
611
|
-
const hideDescriptions = parsed.options['no-descriptions'] === 'true';
|
|
612
|
-
|
|
613
|
-
const layout = layoutMindmap(parsed, palette, {
|
|
614
|
-
interactive: false,
|
|
615
|
-
hideDescriptions,
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
const titleOffset =
|
|
619
|
-
parsed.title && parsed.options['no-title'] !== 'on' ? TITLE_HEIGHT : 0;
|
|
620
|
-
const exportWidth = layout.width + DIAGRAM_PADDING * 2;
|
|
621
|
-
const exportHeight = layout.height + DIAGRAM_PADDING * 2 + titleOffset;
|
|
622
|
-
|
|
623
|
-
return runInExportContainer(exportWidth, exportHeight, (container) => {
|
|
624
|
-
renderMindmap(
|
|
625
|
-
container,
|
|
626
|
-
parsed,
|
|
627
|
-
layout,
|
|
628
|
-
palette,
|
|
629
|
-
isDark,
|
|
630
|
-
undefined,
|
|
631
|
-
{ width: exportWidth, height: exportHeight },
|
|
632
|
-
undefined,
|
|
633
|
-
hideDescriptions
|
|
634
|
-
);
|
|
635
|
-
return extractExportSvg(container, theme);
|
|
636
|
-
});
|
|
637
|
-
}
|