@diagrammo/dgmo 0.2.9 → 0.2.10
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/README.md +29 -1
- package/dist/cli.cjs +105 -105
- package/dist/index.cjs +7484 -7270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +7439 -7229
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chart.ts +9 -0
- package/src/cli.ts +14 -34
- package/src/d3.ts +42 -6
- package/src/echarts.ts +54 -2
- package/src/index.ts +7 -0
- package/src/render.ts +68 -0
- package/src/sequence/renderer.ts +305 -126
package/package.json
CHANGED
package/src/chart.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface ParsedChart {
|
|
|
32
32
|
orientation?: 'horizontal' | 'vertical';
|
|
33
33
|
color?: string;
|
|
34
34
|
label?: string;
|
|
35
|
+
labels?: 'name' | 'value' | 'percent' | 'full';
|
|
35
36
|
data: ChartDataPoint[];
|
|
36
37
|
error?: string;
|
|
37
38
|
}
|
|
@@ -140,6 +141,14 @@ export function parseChart(
|
|
|
140
141
|
continue;
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
if (key === 'labels') {
|
|
145
|
+
const v = value.toLowerCase();
|
|
146
|
+
if (v === 'name' || v === 'value' || v === 'percent' || v === 'full') {
|
|
147
|
+
result.labels = v;
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
143
152
|
if (key === 'orientation') {
|
|
144
153
|
const v = value.toLowerCase();
|
|
145
154
|
if (v === 'horizontal' || v === 'vertical') {
|
package/src/cli.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { resolve, basename, extname } from 'node:path';
|
|
3
|
-
import { JSDOM } from 'jsdom';
|
|
4
3
|
import { Resvg } from '@resvg/resvg-js';
|
|
5
|
-
import {
|
|
6
|
-
import { renderEChartsForExport } from './echarts';
|
|
7
|
-
import { parseDgmoChartType, getDgmoFramework } from './dgmo-router';
|
|
4
|
+
import { render } from './render';
|
|
8
5
|
import { getPalette } from './palettes/registry';
|
|
9
6
|
import { DEFAULT_FONT_NAME } from './fonts';
|
|
10
7
|
|
|
@@ -108,18 +105,6 @@ function parseArgs(argv: string[]): {
|
|
|
108
105
|
return result;
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
function setupDom(): void {
|
|
112
|
-
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
113
|
-
const win = dom.window;
|
|
114
|
-
|
|
115
|
-
// Expose DOM globals needed by d3-selection and renderers
|
|
116
|
-
Object.defineProperty(globalThis, 'document', { value: win.document, configurable: true });
|
|
117
|
-
Object.defineProperty(globalThis, 'window', { value: win, configurable: true });
|
|
118
|
-
Object.defineProperty(globalThis, 'navigator', { value: win.navigator, configurable: true });
|
|
119
|
-
Object.defineProperty(globalThis, 'HTMLElement', { value: win.HTMLElement, configurable: true });
|
|
120
|
-
Object.defineProperty(globalThis, 'SVGElement', { value: win.SVGElement, configurable: true });
|
|
121
|
-
}
|
|
122
|
-
|
|
123
108
|
function inferFormat(outputPath: string | undefined): 'svg' | 'png' {
|
|
124
109
|
if (outputPath && extname(outputPath).toLowerCase() === '.svg') {
|
|
125
110
|
return 'svg';
|
|
@@ -217,28 +202,23 @@ async function main(): Promise<void> {
|
|
|
217
202
|
noInput();
|
|
218
203
|
}
|
|
219
204
|
|
|
220
|
-
const
|
|
221
|
-
const paletteColors = isDark
|
|
222
|
-
? getPalette(opts.palette).dark
|
|
223
|
-
: getPalette(opts.palette).light;
|
|
224
|
-
|
|
225
|
-
// Determine which rendering framework to use
|
|
226
|
-
const chartType = parseDgmoChartType(content);
|
|
227
|
-
const framework = chartType ? getDgmoFramework(chartType) : null;
|
|
228
|
-
|
|
229
|
-
let svg: string;
|
|
205
|
+
const paletteColors = getPalette(opts.palette)[opts.theme === 'dark' ? 'dark' : 'light'];
|
|
230
206
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
console.error(`Error: Unknown chart framework "${framework}".`);
|
|
207
|
+
// Word clouds require Canvas APIs (HTMLCanvasElement.getContext('2d'))
|
|
208
|
+
// which are unavailable in Node.js — check before attempting render.
|
|
209
|
+
const wordcloudRe = /^\s*chart\s*:\s*wordcloud\b/im;
|
|
210
|
+
if (wordcloudRe.test(content)) {
|
|
211
|
+
console.error(
|
|
212
|
+
'Error: Word clouds are not supported in the CLI (requires Canvas). Use the desktop app or browser instead.'
|
|
213
|
+
);
|
|
239
214
|
process.exit(1);
|
|
240
215
|
}
|
|
241
216
|
|
|
217
|
+
const svg = await render(content, {
|
|
218
|
+
theme: opts.theme,
|
|
219
|
+
palette: opts.palette,
|
|
220
|
+
});
|
|
221
|
+
|
|
242
222
|
if (!svg) {
|
|
243
223
|
console.error(
|
|
244
224
|
'Error: Failed to render diagram. The input may be empty, invalid, or use an unsupported chart type.'
|
package/src/d3.ts
CHANGED
|
@@ -1179,6 +1179,12 @@ export function renderSlopeChart(
|
|
|
1179
1179
|
data.forEach((item, idx) => {
|
|
1180
1180
|
const color = item.color ?? colors[idx % colors.length];
|
|
1181
1181
|
|
|
1182
|
+
// Wrap each series in a group with data-line-number for sync adapter
|
|
1183
|
+
const seriesG = g
|
|
1184
|
+
.append('g')
|
|
1185
|
+
.attr('class', 'slope-series')
|
|
1186
|
+
.attr('data-line-number', String(item.lineNumber));
|
|
1187
|
+
|
|
1182
1188
|
// Tooltip content – overall change for this series
|
|
1183
1189
|
const firstVal = item.values[0];
|
|
1184
1190
|
const lastVal = item.values[item.values.length - 1];
|
|
@@ -1193,7 +1199,7 @@ export function renderSlopeChart(
|
|
|
1193
1199
|
`Change: ${sign}${absChange}${pctPart}`;
|
|
1194
1200
|
|
|
1195
1201
|
// Line
|
|
1196
|
-
|
|
1202
|
+
seriesG.append('path')
|
|
1197
1203
|
.datum(item.values)
|
|
1198
1204
|
.attr('fill', 'none')
|
|
1199
1205
|
.attr('stroke', color)
|
|
@@ -1201,7 +1207,7 @@ export function renderSlopeChart(
|
|
|
1201
1207
|
.attr('d', lineGen);
|
|
1202
1208
|
|
|
1203
1209
|
// Invisible wider path for easier hover targeting
|
|
1204
|
-
|
|
1210
|
+
seriesG.append('path')
|
|
1205
1211
|
.datum(item.values)
|
|
1206
1212
|
.attr('fill', 'none')
|
|
1207
1213
|
.attr('stroke', 'transparent')
|
|
@@ -1225,7 +1231,7 @@ export function renderSlopeChart(
|
|
|
1225
1231
|
const y = yScale(val);
|
|
1226
1232
|
|
|
1227
1233
|
// Point circle
|
|
1228
|
-
|
|
1234
|
+
seriesG.append('circle')
|
|
1229
1235
|
.attr('cx', x)
|
|
1230
1236
|
.attr('cy', y)
|
|
1231
1237
|
.attr('r', 4)
|
|
@@ -1248,7 +1254,7 @@ export function renderSlopeChart(
|
|
|
1248
1254
|
const isFirst = i === 0;
|
|
1249
1255
|
const isLast = i === periods.length - 1;
|
|
1250
1256
|
if (!isLast) {
|
|
1251
|
-
|
|
1257
|
+
seriesG.append('text')
|
|
1252
1258
|
.attr('x', isFirst ? x - 10 : x)
|
|
1253
1259
|
.attr('y', y)
|
|
1254
1260
|
.attr('dy', '0.35em')
|
|
@@ -1266,7 +1272,7 @@ export function renderSlopeChart(
|
|
|
1266
1272
|
const availableWidth = rightMargin - 15;
|
|
1267
1273
|
const maxChars = Math.floor(availableWidth / SLOPE_CHAR_WIDTH);
|
|
1268
1274
|
|
|
1269
|
-
const labelEl =
|
|
1275
|
+
const labelEl = seriesG
|
|
1270
1276
|
.append('text')
|
|
1271
1277
|
.attr('x', lastX + 10)
|
|
1272
1278
|
.attr('y', lastY)
|
|
@@ -1640,6 +1646,7 @@ export function renderArcDiagram(
|
|
|
1640
1646
|
g.append('rect')
|
|
1641
1647
|
.attr('class', 'arc-group-band')
|
|
1642
1648
|
.attr('data-group', group.name)
|
|
1649
|
+
.attr('data-line-number', String(group.lineNumber))
|
|
1643
1650
|
.attr('x', baseX - bandHalfW)
|
|
1644
1651
|
.attr('y', minY)
|
|
1645
1652
|
.attr('width', bandHalfW * 2)
|
|
@@ -1657,6 +1664,7 @@ export function renderArcDiagram(
|
|
|
1657
1664
|
g.append('text')
|
|
1658
1665
|
.attr('class', 'arc-group-label')
|
|
1659
1666
|
.attr('data-group', group.name)
|
|
1667
|
+
.attr('data-line-number', String(group.lineNumber))
|
|
1660
1668
|
.attr('x', baseX - bandHalfW + 6)
|
|
1661
1669
|
.attr('y', minY + 14)
|
|
1662
1670
|
.attr('fill', textColor)
|
|
@@ -1696,6 +1704,7 @@ export function renderArcDiagram(
|
|
|
1696
1704
|
.attr('class', 'arc-link')
|
|
1697
1705
|
.attr('data-source', link.source)
|
|
1698
1706
|
.attr('data-target', link.target)
|
|
1707
|
+
.attr('data-line-number', String(link.lineNumber))
|
|
1699
1708
|
.attr('d', `M ${baseX},${y1} Q ${controlX},${midY} ${baseX},${y2}`)
|
|
1700
1709
|
.attr('fill', 'none')
|
|
1701
1710
|
.attr('stroke', color)
|
|
@@ -1720,6 +1729,7 @@ export function renderArcDiagram(
|
|
|
1720
1729
|
.append('g')
|
|
1721
1730
|
.attr('class', 'arc-node')
|
|
1722
1731
|
.attr('data-node', node)
|
|
1732
|
+
.attr('data-line-number', nodeLink?.lineNumber ? String(nodeLink.lineNumber) : null)
|
|
1723
1733
|
.style('cursor', 'pointer')
|
|
1724
1734
|
.on('mouseenter', () => handleMouseEnter(node))
|
|
1725
1735
|
.on('mouseleave', handleMouseLeave)
|
|
@@ -1773,6 +1783,7 @@ export function renderArcDiagram(
|
|
|
1773
1783
|
g.append('rect')
|
|
1774
1784
|
.attr('class', 'arc-group-band')
|
|
1775
1785
|
.attr('data-group', group.name)
|
|
1786
|
+
.attr('data-line-number', String(group.lineNumber))
|
|
1776
1787
|
.attr('x', minX)
|
|
1777
1788
|
.attr('y', baseY - bandHalfH)
|
|
1778
1789
|
.attr('width', maxX - minX)
|
|
@@ -1790,6 +1801,7 @@ export function renderArcDiagram(
|
|
|
1790
1801
|
g.append('text')
|
|
1791
1802
|
.attr('class', 'arc-group-label')
|
|
1792
1803
|
.attr('data-group', group.name)
|
|
1804
|
+
.attr('data-line-number', String(group.lineNumber))
|
|
1793
1805
|
.attr('x', (minX + maxX) / 2)
|
|
1794
1806
|
.attr('y', baseY + bandHalfH - 4)
|
|
1795
1807
|
.attr('text-anchor', 'middle')
|
|
@@ -1830,6 +1842,7 @@ export function renderArcDiagram(
|
|
|
1830
1842
|
.attr('class', 'arc-link')
|
|
1831
1843
|
.attr('data-source', link.source)
|
|
1832
1844
|
.attr('data-target', link.target)
|
|
1845
|
+
.attr('data-line-number', String(link.lineNumber))
|
|
1833
1846
|
.attr('d', `M ${x1},${baseY} Q ${midX},${controlY} ${x2},${baseY}`)
|
|
1834
1847
|
.attr('fill', 'none')
|
|
1835
1848
|
.attr('stroke', color)
|
|
@@ -1854,6 +1867,7 @@ export function renderArcDiagram(
|
|
|
1854
1867
|
.append('g')
|
|
1855
1868
|
.attr('class', 'arc-node')
|
|
1856
1869
|
.attr('data-node', node)
|
|
1870
|
+
.attr('data-line-number', nodeLink?.lineNumber ? String(nodeLink.lineNumber) : null)
|
|
1857
1871
|
.style('cursor', 'pointer')
|
|
1858
1872
|
.on('mouseenter', () => handleMouseEnter(node))
|
|
1859
1873
|
.on('mouseleave', handleMouseLeave)
|
|
@@ -2019,6 +2033,7 @@ function renderMarkers(
|
|
|
2019
2033
|
.append('g')
|
|
2020
2034
|
.attr('class', 'tl-marker')
|
|
2021
2035
|
.attr('data-marker-date', String(dateVal))
|
|
2036
|
+
.attr('data-line-number', String(marker.lineNumber))
|
|
2022
2037
|
.style('cursor', 'pointer')
|
|
2023
2038
|
.on('mouseenter', function (event: MouseEvent) {
|
|
2024
2039
|
if (tooltip) {
|
|
@@ -2880,6 +2895,7 @@ export function renderTimeline(
|
|
|
2880
2895
|
.append('g')
|
|
2881
2896
|
.attr('class', 'tl-event')
|
|
2882
2897
|
.attr('data-group', laneName)
|
|
2898
|
+
.attr('data-line-number', String(ev.lineNumber))
|
|
2883
2899
|
.attr('data-date', String(parseTimelineDate(ev.date)))
|
|
2884
2900
|
.attr(
|
|
2885
2901
|
'data-end-date',
|
|
@@ -3090,6 +3106,7 @@ export function renderTimeline(
|
|
|
3090
3106
|
.append('g')
|
|
3091
3107
|
.attr('class', 'tl-event')
|
|
3092
3108
|
.attr('data-group', ev.group || '')
|
|
3109
|
+
.attr('data-line-number', String(ev.lineNumber))
|
|
3093
3110
|
.attr('data-date', String(parseTimelineDate(ev.date)))
|
|
3094
3111
|
.attr(
|
|
3095
3112
|
'data-end-date',
|
|
@@ -3363,6 +3380,7 @@ export function renderTimeline(
|
|
|
3363
3380
|
.append('g')
|
|
3364
3381
|
.attr('class', 'tl-event')
|
|
3365
3382
|
.attr('data-group', lane.name)
|
|
3383
|
+
.attr('data-line-number', String(ev.lineNumber))
|
|
3366
3384
|
.attr('data-date', String(parseTimelineDate(ev.date)))
|
|
3367
3385
|
.attr(
|
|
3368
3386
|
'data-end-date',
|
|
@@ -3643,6 +3661,7 @@ export function renderTimeline(
|
|
|
3643
3661
|
.append('g')
|
|
3644
3662
|
.attr('class', 'tl-event')
|
|
3645
3663
|
.attr('data-group', ev.group || '')
|
|
3664
|
+
.attr('data-line-number', String(ev.lineNumber))
|
|
3646
3665
|
.attr('data-date', String(parseTimelineDate(ev.date)))
|
|
3647
3666
|
.attr(
|
|
3648
3667
|
'data-end-date',
|
|
@@ -3895,6 +3914,10 @@ export function renderWordCloud(
|
|
|
3895
3914
|
'transform',
|
|
3896
3915
|
(d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`
|
|
3897
3916
|
)
|
|
3917
|
+
.attr('data-line-number', (d) => {
|
|
3918
|
+
const ln = (d as WordCloudWord).lineNumber;
|
|
3919
|
+
return ln ? String(ln) : null;
|
|
3920
|
+
})
|
|
3898
3921
|
.text((d) => d.text!)
|
|
3899
3922
|
.on('click', (_event, d) => {
|
|
3900
3923
|
const ln = (d as WordCloudWord).lineNumber;
|
|
@@ -4594,6 +4617,7 @@ export function renderVenn(
|
|
|
4594
4617
|
.attr('cy', c.y)
|
|
4595
4618
|
.attr('r', c.r)
|
|
4596
4619
|
.attr('fill', 'transparent')
|
|
4620
|
+
.attr('data-line-number', String(vennSets[i].lineNumber))
|
|
4597
4621
|
.style('cursor', onClickItem ? 'pointer' : 'default')
|
|
4598
4622
|
.on('mouseenter', (event: MouseEvent) => {
|
|
4599
4623
|
for (const rg of regionGroups) {
|
|
@@ -4848,6 +4872,9 @@ export function renderQuadrant(
|
|
|
4848
4872
|
.attr('fill', (d) => getQuadrantLabelColor(d))
|
|
4849
4873
|
.attr('font-size', '48px')
|
|
4850
4874
|
.attr('font-weight', '700')
|
|
4875
|
+
.attr('data-line-number', (d) =>
|
|
4876
|
+
d.label?.lineNumber ? String(d.label.lineNumber) : null
|
|
4877
|
+
)
|
|
4851
4878
|
.style('cursor', (d) =>
|
|
4852
4879
|
onClickItem && d.label?.lineNumber ? 'pointer' : 'default'
|
|
4853
4880
|
)
|
|
@@ -4871,11 +4898,13 @@ export function renderQuadrant(
|
|
|
4871
4898
|
// Low label (centered on left half)
|
|
4872
4899
|
const xLowLabel = svg
|
|
4873
4900
|
.append('text')
|
|
4901
|
+
.attr('class', 'quadrant-axis-label')
|
|
4874
4902
|
.attr('x', margin.left + chartWidth / 4)
|
|
4875
4903
|
.attr('y', height - 20)
|
|
4876
4904
|
.attr('text-anchor', 'middle')
|
|
4877
4905
|
.attr('fill', textColor)
|
|
4878
4906
|
.attr('font-size', '18px')
|
|
4907
|
+
.attr('data-line-number', quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null)
|
|
4879
4908
|
.style(
|
|
4880
4909
|
'cursor',
|
|
4881
4910
|
onClickItem && quadrantXAxisLineNumber ? 'pointer' : 'default'
|
|
@@ -4885,11 +4914,13 @@ export function renderQuadrant(
|
|
|
4885
4914
|
// High label (centered on right half)
|
|
4886
4915
|
const xHighLabel = svg
|
|
4887
4916
|
.append('text')
|
|
4917
|
+
.attr('class', 'quadrant-axis-label')
|
|
4888
4918
|
.attr('x', margin.left + (chartWidth * 3) / 4)
|
|
4889
4919
|
.attr('y', height - 20)
|
|
4890
4920
|
.attr('text-anchor', 'middle')
|
|
4891
4921
|
.attr('fill', textColor)
|
|
4892
4922
|
.attr('font-size', '18px')
|
|
4923
|
+
.attr('data-line-number', quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null)
|
|
4893
4924
|
.style(
|
|
4894
4925
|
'cursor',
|
|
4895
4926
|
onClickItem && quadrantXAxisLineNumber ? 'pointer' : 'default'
|
|
@@ -4918,12 +4949,14 @@ export function renderQuadrant(
|
|
|
4918
4949
|
// Low label (centered on bottom half)
|
|
4919
4950
|
const yLowLabel = svg
|
|
4920
4951
|
.append('text')
|
|
4952
|
+
.attr('class', 'quadrant-axis-label')
|
|
4921
4953
|
.attr('x', 22)
|
|
4922
4954
|
.attr('y', yMidBottom)
|
|
4923
4955
|
.attr('text-anchor', 'middle')
|
|
4924
4956
|
.attr('fill', textColor)
|
|
4925
4957
|
.attr('font-size', '18px')
|
|
4926
4958
|
.attr('transform', `rotate(-90, 22, ${yMidBottom})`)
|
|
4959
|
+
.attr('data-line-number', quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null)
|
|
4927
4960
|
.style(
|
|
4928
4961
|
'cursor',
|
|
4929
4962
|
onClickItem && quadrantYAxisLineNumber ? 'pointer' : 'default'
|
|
@@ -4933,12 +4966,14 @@ export function renderQuadrant(
|
|
|
4933
4966
|
// High label (centered on top half)
|
|
4934
4967
|
const yHighLabel = svg
|
|
4935
4968
|
.append('text')
|
|
4969
|
+
.attr('class', 'quadrant-axis-label')
|
|
4936
4970
|
.attr('x', 22)
|
|
4937
4971
|
.attr('y', yMidTop)
|
|
4938
4972
|
.attr('text-anchor', 'middle')
|
|
4939
4973
|
.attr('fill', textColor)
|
|
4940
4974
|
.attr('font-size', '18px')
|
|
4941
4975
|
.attr('transform', `rotate(-90, 22, ${yMidTop})`)
|
|
4976
|
+
.attr('data-line-number', quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null)
|
|
4942
4977
|
.style(
|
|
4943
4978
|
'cursor',
|
|
4944
4979
|
onClickItem && quadrantYAxisLineNumber ? 'pointer' : 'default'
|
|
@@ -4997,7 +5032,8 @@ export function renderQuadrant(
|
|
|
4997
5032
|
const pointColor =
|
|
4998
5033
|
quadDef?.label?.color ?? defaultColors[quadDef?.colorIdx ?? 0];
|
|
4999
5034
|
|
|
5000
|
-
const pointG = pointsG.append('g').attr('class', 'point-group')
|
|
5035
|
+
const pointG = pointsG.append('g').attr('class', 'point-group')
|
|
5036
|
+
.attr('data-line-number', String(point.lineNumber));
|
|
5001
5037
|
|
|
5002
5038
|
// Circle with white fill and colored border for visibility on opaque quadrants
|
|
5003
5039
|
pointG
|
package/src/echarts.ts
CHANGED
|
@@ -538,6 +538,7 @@ function buildSankeyOption(
|
|
|
538
538
|
type: 'sankey',
|
|
539
539
|
emphasis: {
|
|
540
540
|
focus: 'adjacency',
|
|
541
|
+
blurScope: 'global' as const,
|
|
541
542
|
},
|
|
542
543
|
nodeAlign: 'left',
|
|
543
544
|
nodeGap: 12,
|
|
@@ -746,6 +747,10 @@ function buildFunctionOption(
|
|
|
746
747
|
itemStyle: {
|
|
747
748
|
color: fnColor,
|
|
748
749
|
},
|
|
750
|
+
emphasis: {
|
|
751
|
+
focus: 'self' as const,
|
|
752
|
+
blurScope: 'global' as const,
|
|
753
|
+
},
|
|
749
754
|
};
|
|
750
755
|
});
|
|
751
756
|
|
|
@@ -1116,6 +1121,8 @@ function buildHeatmapOption(
|
|
|
1116
1121
|
fontWeight: 'bold' as const,
|
|
1117
1122
|
},
|
|
1118
1123
|
emphasis: {
|
|
1124
|
+
focus: 'self' as const,
|
|
1125
|
+
blurScope: 'global' as const,
|
|
1119
1126
|
itemStyle: {
|
|
1120
1127
|
shadowBlur: 10,
|
|
1121
1128
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
@@ -1210,6 +1217,8 @@ function buildFunnelOption(
|
|
|
1210
1217
|
lineStyle: { color: textColor, opacity: 0.3 },
|
|
1211
1218
|
},
|
|
1212
1219
|
emphasis: {
|
|
1220
|
+
focus: 'self' as const,
|
|
1221
|
+
blurScope: 'global' as const,
|
|
1213
1222
|
label: {
|
|
1214
1223
|
fontSize: 15,
|
|
1215
1224
|
},
|
|
@@ -1394,6 +1403,10 @@ function buildBarOption(
|
|
|
1394
1403
|
{
|
|
1395
1404
|
type: 'bar',
|
|
1396
1405
|
data,
|
|
1406
|
+
emphasis: {
|
|
1407
|
+
focus: 'self' as const,
|
|
1408
|
+
blurScope: 'global' as const,
|
|
1409
|
+
},
|
|
1397
1410
|
},
|
|
1398
1411
|
],
|
|
1399
1412
|
};
|
|
@@ -1442,6 +1455,10 @@ function buildLineOption(
|
|
|
1442
1455
|
symbolSize: 8,
|
|
1443
1456
|
lineStyle: { color: lineColor, width: 3 },
|
|
1444
1457
|
itemStyle: { color: lineColor },
|
|
1458
|
+
emphasis: {
|
|
1459
|
+
focus: 'self' as const,
|
|
1460
|
+
blurScope: 'global' as const,
|
|
1461
|
+
},
|
|
1445
1462
|
},
|
|
1446
1463
|
],
|
|
1447
1464
|
};
|
|
@@ -1476,6 +1493,10 @@ function buildMultiLineOption(
|
|
|
1476
1493
|
symbolSize: 8,
|
|
1477
1494
|
lineStyle: { color, width: 3 },
|
|
1478
1495
|
itemStyle: { color },
|
|
1496
|
+
emphasis: {
|
|
1497
|
+
focus: 'self' as const,
|
|
1498
|
+
blurScope: 'global' as const,
|
|
1499
|
+
},
|
|
1479
1500
|
};
|
|
1480
1501
|
});
|
|
1481
1502
|
|
|
@@ -1550,11 +1571,26 @@ function buildAreaOption(
|
|
|
1550
1571
|
lineStyle: { color: lineColor, width: 3 },
|
|
1551
1572
|
itemStyle: { color: lineColor },
|
|
1552
1573
|
areaStyle: { opacity: 0.25 },
|
|
1574
|
+
emphasis: {
|
|
1575
|
+
focus: 'self' as const,
|
|
1576
|
+
blurScope: 'global' as const,
|
|
1577
|
+
},
|
|
1553
1578
|
},
|
|
1554
1579
|
],
|
|
1555
1580
|
};
|
|
1556
1581
|
}
|
|
1557
1582
|
|
|
1583
|
+
// ── Segment label formatter ──────────────────────────────────
|
|
1584
|
+
|
|
1585
|
+
function segmentLabelFormatter(mode: ParsedChart['labels']): string {
|
|
1586
|
+
switch (mode) {
|
|
1587
|
+
case 'name': return '{b}';
|
|
1588
|
+
case 'value': return '{b} — {c}';
|
|
1589
|
+
case 'percent': return '{b} — {d}%';
|
|
1590
|
+
default: return '{b} — {c} ({d}%)';
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1558
1594
|
// ── Pie / Doughnut ───────────────────────────────────────────
|
|
1559
1595
|
|
|
1560
1596
|
function buildPieOption(
|
|
@@ -1586,11 +1622,15 @@ function buildPieOption(
|
|
|
1586
1622
|
data,
|
|
1587
1623
|
label: {
|
|
1588
1624
|
position: 'outside',
|
|
1589
|
-
formatter:
|
|
1625
|
+
formatter: segmentLabelFormatter(parsed.labels),
|
|
1590
1626
|
color: textColor,
|
|
1591
1627
|
fontFamily: FONT_FAMILY,
|
|
1592
1628
|
},
|
|
1593
1629
|
labelLine: { show: true },
|
|
1630
|
+
emphasis: {
|
|
1631
|
+
focus: 'self' as const,
|
|
1632
|
+
blurScope: 'global' as const,
|
|
1633
|
+
},
|
|
1594
1634
|
},
|
|
1595
1635
|
],
|
|
1596
1636
|
};
|
|
@@ -1660,6 +1700,10 @@ function buildRadarOption(
|
|
|
1660
1700
|
},
|
|
1661
1701
|
},
|
|
1662
1702
|
],
|
|
1703
|
+
emphasis: {
|
|
1704
|
+
focus: 'self' as const,
|
|
1705
|
+
blurScope: 'global' as const,
|
|
1706
|
+
},
|
|
1663
1707
|
},
|
|
1664
1708
|
],
|
|
1665
1709
|
};
|
|
@@ -1696,11 +1740,15 @@ function buildPolarAreaOption(
|
|
|
1696
1740
|
data,
|
|
1697
1741
|
label: {
|
|
1698
1742
|
position: 'outside',
|
|
1699
|
-
formatter:
|
|
1743
|
+
formatter: segmentLabelFormatter(parsed.labels),
|
|
1700
1744
|
color: textColor,
|
|
1701
1745
|
fontFamily: FONT_FAMILY,
|
|
1702
1746
|
},
|
|
1703
1747
|
labelLine: { show: true },
|
|
1748
|
+
emphasis: {
|
|
1749
|
+
focus: 'self' as const,
|
|
1750
|
+
blurScope: 'global' as const,
|
|
1751
|
+
},
|
|
1704
1752
|
},
|
|
1705
1753
|
],
|
|
1706
1754
|
};
|
|
@@ -1743,6 +1791,10 @@ function buildBarStackedOption(
|
|
|
1743
1791
|
fontWeight: 'bold' as const,
|
|
1744
1792
|
fontFamily: FONT_FAMILY,
|
|
1745
1793
|
},
|
|
1794
|
+
emphasis: {
|
|
1795
|
+
focus: 'self' as const,
|
|
1796
|
+
blurScope: 'global' as const,
|
|
1797
|
+
},
|
|
1746
1798
|
};
|
|
1747
1799
|
});
|
|
1748
1800
|
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Unified API
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
export { render } from './render';
|
|
6
|
+
|
|
1
7
|
// ============================================================
|
|
2
8
|
// Router
|
|
3
9
|
// ============================================================
|
|
@@ -109,6 +115,7 @@ export {
|
|
|
109
115
|
applyPositionOverrides,
|
|
110
116
|
applyGroupOrdering,
|
|
111
117
|
groupMessagesBySection,
|
|
118
|
+
buildNoteMessageMap,
|
|
112
119
|
} from './sequence/renderer';
|
|
113
120
|
export type {
|
|
114
121
|
RenderStep,
|
package/src/render.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { renderD3ForExport } from './d3';
|
|
2
|
+
import { renderEChartsForExport } from './echarts';
|
|
3
|
+
import { parseDgmoChartType, getDgmoFramework } from './dgmo-router';
|
|
4
|
+
import { getPalette } from './palettes/registry';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ensures DOM globals are available for D3 renderers.
|
|
8
|
+
* No-ops in browser environments where `document` already exists.
|
|
9
|
+
* Dynamically imports jsdom only in Node.js to avoid bundling it for browsers.
|
|
10
|
+
*/
|
|
11
|
+
async function ensureDom(): Promise<void> {
|
|
12
|
+
if (typeof document !== 'undefined') return;
|
|
13
|
+
|
|
14
|
+
const { JSDOM } = await import('jsdom');
|
|
15
|
+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
|
16
|
+
const win = dom.window;
|
|
17
|
+
|
|
18
|
+
Object.defineProperty(globalThis, 'document', { value: win.document, configurable: true });
|
|
19
|
+
Object.defineProperty(globalThis, 'window', { value: win, configurable: true });
|
|
20
|
+
Object.defineProperty(globalThis, 'navigator', { value: win.navigator, configurable: true });
|
|
21
|
+
Object.defineProperty(globalThis, 'HTMLElement', { value: win.HTMLElement, configurable: true });
|
|
22
|
+
Object.defineProperty(globalThis, 'SVGElement', { value: win.SVGElement, configurable: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Render DGMO source to an SVG string.
|
|
27
|
+
*
|
|
28
|
+
* Automatically detects the chart type, selects the appropriate renderer,
|
|
29
|
+
* and returns a complete SVG document string.
|
|
30
|
+
*
|
|
31
|
+
* @param content - DGMO source text
|
|
32
|
+
* @param options - Optional theme and palette settings
|
|
33
|
+
* @returns SVG string, or empty string on error
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { render } from '@diagrammo/dgmo';
|
|
38
|
+
*
|
|
39
|
+
* const svg = await render(`chart: pie
|
|
40
|
+
* title: Languages
|
|
41
|
+
* TypeScript: 45
|
|
42
|
+
* Python: 30
|
|
43
|
+
* Rust: 25`);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function render(
|
|
47
|
+
content: string,
|
|
48
|
+
options?: {
|
|
49
|
+
theme?: 'light' | 'dark' | 'transparent';
|
|
50
|
+
palette?: string;
|
|
51
|
+
},
|
|
52
|
+
): Promise<string> {
|
|
53
|
+
const theme = options?.theme ?? 'light';
|
|
54
|
+
const paletteName = options?.palette ?? 'nord';
|
|
55
|
+
|
|
56
|
+
const paletteColors = getPalette(paletteName)[theme === 'dark' ? 'dark' : 'light'];
|
|
57
|
+
|
|
58
|
+
const chartType = parseDgmoChartType(content);
|
|
59
|
+
const framework = chartType ? getDgmoFramework(chartType) : null;
|
|
60
|
+
|
|
61
|
+
if (framework === 'echart') {
|
|
62
|
+
return renderEChartsForExport(content, theme, paletteColors);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// D3 and unknown/null frameworks both go through D3 renderer
|
|
66
|
+
await ensureDom();
|
|
67
|
+
return renderD3ForExport(content, theme, paletteColors);
|
|
68
|
+
}
|