@diagrammo/dgmo 0.3.0 → 0.3.2
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/.claude/skills/dgmo-chart/SKILL.md +6 -0
- package/README.md +5 -0
- package/dist/cli.cjs +139 -140
- package/dist/index.cjs +267 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +267 -113
- package/dist/index.js.map +1 -1
- package/docs/language-reference.md +35 -0
- package/package.json +1 -1
- package/src/chart.ts +12 -6
- package/src/d3.ts +129 -52
- package/src/echarts.ts +79 -28
- package/src/sequence/parser.ts +16 -3
- package/src/sequence/renderer.ts +22 -0
- package/src/utils/parsing.ts +31 -0
package/dist/index.d.cts
CHANGED
|
@@ -334,7 +334,7 @@ declare function buildEChartsOption(parsed: ParsedEChart, palette: PaletteColors
|
|
|
334
334
|
* Converts a ParsedChart into an EChartsOption.
|
|
335
335
|
* Renders standard chart types (bar, line, pie, etc.) with ECharts.
|
|
336
336
|
*/
|
|
337
|
-
declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean): EChartsOption;
|
|
337
|
+
declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean, chartWidth?: number): EChartsOption;
|
|
338
338
|
/**
|
|
339
339
|
* Renders an ECharts diagram to SVG using server-side rendering.
|
|
340
340
|
* Mirrors the `renderD3ForExport` API — returns an SVG string or empty string on failure.
|
|
@@ -579,6 +579,8 @@ interface SequenceMessage {
|
|
|
579
579
|
lineNumber: number;
|
|
580
580
|
async?: boolean;
|
|
581
581
|
bidirectional?: boolean;
|
|
582
|
+
/** Standalone return — the message itself IS a return (dashed arrow, no call). */
|
|
583
|
+
standaloneReturn?: boolean;
|
|
582
584
|
}
|
|
583
585
|
/**
|
|
584
586
|
* A conditional or loop block in the sequence diagram.
|
package/dist/index.d.ts
CHANGED
|
@@ -334,7 +334,7 @@ declare function buildEChartsOption(parsed: ParsedEChart, palette: PaletteColors
|
|
|
334
334
|
* Converts a ParsedChart into an EChartsOption.
|
|
335
335
|
* Renders standard chart types (bar, line, pie, etc.) with ECharts.
|
|
336
336
|
*/
|
|
337
|
-
declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean): EChartsOption;
|
|
337
|
+
declare function buildEChartsOptionFromChart(parsed: ParsedChart, palette: PaletteColors, isDark: boolean, chartWidth?: number): EChartsOption;
|
|
338
338
|
/**
|
|
339
339
|
* Renders an ECharts diagram to SVG using server-side rendering.
|
|
340
340
|
* Mirrors the `renderD3ForExport` API — returns an SVG string or empty string on failure.
|
|
@@ -579,6 +579,8 @@ interface SequenceMessage {
|
|
|
579
579
|
lineNumber: number;
|
|
580
580
|
async?: boolean;
|
|
581
581
|
bidirectional?: boolean;
|
|
582
|
+
/** Standalone return — the message itself IS a return (dashed arrow, no call). */
|
|
583
|
+
standaloneReturn?: boolean;
|
|
582
584
|
}
|
|
583
585
|
/**
|
|
584
586
|
* A conditional or loop block in the sequence diagram.
|
package/dist/index.js
CHANGED
|
@@ -1253,6 +1253,67 @@ var init_palettes = __esm({
|
|
|
1253
1253
|
}
|
|
1254
1254
|
});
|
|
1255
1255
|
|
|
1256
|
+
// src/utils/parsing.ts
|
|
1257
|
+
function measureIndent(line7) {
|
|
1258
|
+
let indent = 0;
|
|
1259
|
+
for (const ch of line7) {
|
|
1260
|
+
if (ch === " ") indent++;
|
|
1261
|
+
else if (ch === " ") indent += 4;
|
|
1262
|
+
else break;
|
|
1263
|
+
}
|
|
1264
|
+
return indent;
|
|
1265
|
+
}
|
|
1266
|
+
function extractColor(label, palette) {
|
|
1267
|
+
const m = label.match(COLOR_SUFFIX_RE);
|
|
1268
|
+
if (!m) return { label };
|
|
1269
|
+
const colorName = m[1].trim();
|
|
1270
|
+
return {
|
|
1271
|
+
label: label.substring(0, m.index).trim(),
|
|
1272
|
+
color: resolveColor(colorName, palette)
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
function collectIndentedValues(lines, startIndex) {
|
|
1276
|
+
const values = [];
|
|
1277
|
+
let j = startIndex + 1;
|
|
1278
|
+
for (; j < lines.length; j++) {
|
|
1279
|
+
const raw = lines[j];
|
|
1280
|
+
const trimmed = raw.trim();
|
|
1281
|
+
if (!trimmed) continue;
|
|
1282
|
+
if (trimmed.startsWith("//")) continue;
|
|
1283
|
+
if (raw[0] !== " " && raw[0] !== " ") break;
|
|
1284
|
+
values.push(trimmed.replace(/,\s*$/, ""));
|
|
1285
|
+
}
|
|
1286
|
+
return { values, newIndex: j - 1 };
|
|
1287
|
+
}
|
|
1288
|
+
function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map()) {
|
|
1289
|
+
const metadata = {};
|
|
1290
|
+
for (let j = 1; j < segments.length; j++) {
|
|
1291
|
+
for (const part of segments[j].split(",")) {
|
|
1292
|
+
const trimmedPart = part.trim();
|
|
1293
|
+
if (!trimmedPart) continue;
|
|
1294
|
+
const colonIdx = trimmedPart.indexOf(":");
|
|
1295
|
+
if (colonIdx > 0) {
|
|
1296
|
+
const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
|
|
1297
|
+
const key = aliasMap.get(rawKey) ?? rawKey;
|
|
1298
|
+
const value = trimmedPart.substring(colonIdx + 1).trim();
|
|
1299
|
+
metadata[key] = value;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return metadata;
|
|
1304
|
+
}
|
|
1305
|
+
var COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
|
|
1306
|
+
var init_parsing = __esm({
|
|
1307
|
+
"src/utils/parsing.ts"() {
|
|
1308
|
+
"use strict";
|
|
1309
|
+
init_colors();
|
|
1310
|
+
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1311
|
+
CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
|
|
1312
|
+
TITLE_RE = /^title\s*:\s*(.+)/i;
|
|
1313
|
+
OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1256
1317
|
// src/sequence/participant-inference.ts
|
|
1257
1318
|
function inferParticipantType(name) {
|
|
1258
1319
|
for (const rule of PARTICIPANT_RULES) {
|
|
@@ -1569,54 +1630,6 @@ var init_arrows = __esm({
|
|
|
1569
1630
|
}
|
|
1570
1631
|
});
|
|
1571
1632
|
|
|
1572
|
-
// src/utils/parsing.ts
|
|
1573
|
-
function measureIndent(line7) {
|
|
1574
|
-
let indent = 0;
|
|
1575
|
-
for (const ch of line7) {
|
|
1576
|
-
if (ch === " ") indent++;
|
|
1577
|
-
else if (ch === " ") indent += 4;
|
|
1578
|
-
else break;
|
|
1579
|
-
}
|
|
1580
|
-
return indent;
|
|
1581
|
-
}
|
|
1582
|
-
function extractColor(label, palette) {
|
|
1583
|
-
const m = label.match(COLOR_SUFFIX_RE);
|
|
1584
|
-
if (!m) return { label };
|
|
1585
|
-
const colorName = m[1].trim();
|
|
1586
|
-
return {
|
|
1587
|
-
label: label.substring(0, m.index).trim(),
|
|
1588
|
-
color: resolveColor(colorName, palette)
|
|
1589
|
-
};
|
|
1590
|
-
}
|
|
1591
|
-
function parsePipeMetadata(segments, aliasMap = /* @__PURE__ */ new Map()) {
|
|
1592
|
-
const metadata = {};
|
|
1593
|
-
for (let j = 1; j < segments.length; j++) {
|
|
1594
|
-
for (const part of segments[j].split(",")) {
|
|
1595
|
-
const trimmedPart = part.trim();
|
|
1596
|
-
if (!trimmedPart) continue;
|
|
1597
|
-
const colonIdx = trimmedPart.indexOf(":");
|
|
1598
|
-
if (colonIdx > 0) {
|
|
1599
|
-
const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
|
|
1600
|
-
const key = aliasMap.get(rawKey) ?? rawKey;
|
|
1601
|
-
const value = trimmedPart.substring(colonIdx + 1).trim();
|
|
1602
|
-
metadata[key] = value;
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
return metadata;
|
|
1607
|
-
}
|
|
1608
|
-
var COLOR_SUFFIX_RE, CHART_TYPE_RE, TITLE_RE, OPTION_RE;
|
|
1609
|
-
var init_parsing = __esm({
|
|
1610
|
-
"src/utils/parsing.ts"() {
|
|
1611
|
-
"use strict";
|
|
1612
|
-
init_colors();
|
|
1613
|
-
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1614
|
-
CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
|
|
1615
|
-
TITLE_RE = /^title\s*:\s*(.+)/i;
|
|
1616
|
-
OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
1617
|
-
}
|
|
1618
|
-
});
|
|
1619
|
-
|
|
1620
1633
|
// src/sequence/parser.ts
|
|
1621
1634
|
var parser_exports = {};
|
|
1622
1635
|
__export(parser_exports, {
|
|
@@ -1637,6 +1650,13 @@ function isSequenceNote(el) {
|
|
|
1637
1650
|
}
|
|
1638
1651
|
function parseReturnLabel(rawLabel) {
|
|
1639
1652
|
if (!rawLabel) return { label: "" };
|
|
1653
|
+
const standaloneMatch = rawLabel.match(/^<-\s*(.*)$/);
|
|
1654
|
+
if (standaloneMatch) {
|
|
1655
|
+
return {
|
|
1656
|
+
label: standaloneMatch[1].trim(),
|
|
1657
|
+
standaloneReturn: true
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1640
1660
|
const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
|
|
1641
1661
|
if (arrowReturn) {
|
|
1642
1662
|
return { label: arrowReturn[1].trim(), returnLabel: arrowReturn[2].trim() };
|
|
@@ -1974,14 +1994,15 @@ function parseSequenceDgmo(content) {
|
|
|
1974
1994
|
const to = arrowMatch[2];
|
|
1975
1995
|
lastMsgFrom = from;
|
|
1976
1996
|
const rawLabel = arrowMatch[3]?.trim() || "";
|
|
1977
|
-
const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
|
|
1997
|
+
const { label, returnLabel, standaloneReturn } = isAsync ? { label: rawLabel, returnLabel: void 0, standaloneReturn: void 0 } : parseReturnLabel(rawLabel);
|
|
1978
1998
|
const msg = {
|
|
1979
1999
|
from,
|
|
1980
2000
|
to,
|
|
1981
2001
|
label,
|
|
1982
2002
|
returnLabel,
|
|
1983
2003
|
lineNumber,
|
|
1984
|
-
...isAsync ? { async: true } : {}
|
|
2004
|
+
...isAsync ? { async: true } : {},
|
|
2005
|
+
...standaloneReturn ? { standaloneReturn: true } : {}
|
|
1985
2006
|
};
|
|
1986
2007
|
result.messages.push(msg);
|
|
1987
2008
|
currentContainer().push(msg);
|
|
@@ -3121,8 +3142,16 @@ function parseChart(content, palette) {
|
|
|
3121
3142
|
continue;
|
|
3122
3143
|
}
|
|
3123
3144
|
if (key === "series") {
|
|
3124
|
-
|
|
3125
|
-
|
|
3145
|
+
let rawNames;
|
|
3146
|
+
if (value) {
|
|
3147
|
+
result.series = value;
|
|
3148
|
+
rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3149
|
+
} else {
|
|
3150
|
+
const collected = collectIndentedValues(lines, i);
|
|
3151
|
+
i = collected.newIndex;
|
|
3152
|
+
rawNames = collected.values;
|
|
3153
|
+
result.series = rawNames.join(", ");
|
|
3154
|
+
}
|
|
3126
3155
|
const names = [];
|
|
3127
3156
|
const nameColors = [];
|
|
3128
3157
|
for (const raw of rawNames) {
|
|
@@ -3201,6 +3230,7 @@ var init_chart = __esm({
|
|
|
3201
3230
|
"use strict";
|
|
3202
3231
|
init_colors();
|
|
3203
3232
|
init_diagnostics();
|
|
3233
|
+
init_parsing();
|
|
3204
3234
|
VALID_TYPES = /* @__PURE__ */ new Set([
|
|
3205
3235
|
"bar",
|
|
3206
3236
|
"line",
|
|
@@ -3277,8 +3307,16 @@ function parseEChart(content, palette) {
|
|
|
3277
3307
|
continue;
|
|
3278
3308
|
}
|
|
3279
3309
|
if (key === "series") {
|
|
3280
|
-
|
|
3281
|
-
|
|
3310
|
+
let rawNames;
|
|
3311
|
+
if (value) {
|
|
3312
|
+
result.series = value;
|
|
3313
|
+
rawNames = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3314
|
+
} else {
|
|
3315
|
+
const collected = collectIndentedValues(lines, i);
|
|
3316
|
+
i = collected.newIndex;
|
|
3317
|
+
rawNames = collected.values;
|
|
3318
|
+
result.series = rawNames.join(", ");
|
|
3319
|
+
}
|
|
3282
3320
|
const names = [];
|
|
3283
3321
|
const nameColors = [];
|
|
3284
3322
|
for (const raw of rawNames) {
|
|
@@ -3314,11 +3352,23 @@ function parseEChart(content, palette) {
|
|
|
3314
3352
|
continue;
|
|
3315
3353
|
}
|
|
3316
3354
|
if (key === "columns") {
|
|
3317
|
-
|
|
3355
|
+
if (value) {
|
|
3356
|
+
result.columns = value.split(",").map((s) => s.trim());
|
|
3357
|
+
} else {
|
|
3358
|
+
const collected = collectIndentedValues(lines, i);
|
|
3359
|
+
i = collected.newIndex;
|
|
3360
|
+
result.columns = collected.values;
|
|
3361
|
+
}
|
|
3318
3362
|
continue;
|
|
3319
3363
|
}
|
|
3320
3364
|
if (key === "rows") {
|
|
3321
|
-
|
|
3365
|
+
if (value) {
|
|
3366
|
+
result.rows = value.split(",").map((s) => s.trim());
|
|
3367
|
+
} else {
|
|
3368
|
+
const collected = collectIndentedValues(lines, i);
|
|
3369
|
+
i = collected.newIndex;
|
|
3370
|
+
result.rows = collected.values;
|
|
3371
|
+
}
|
|
3322
3372
|
continue;
|
|
3323
3373
|
}
|
|
3324
3374
|
if (key === "x") {
|
|
@@ -4146,19 +4196,36 @@ function resolveAxisLabels(parsed) {
|
|
|
4146
4196
|
yLabel: parsed.ylabel ?? (isHorizontal ? void 0 : parsed.label)
|
|
4147
4197
|
};
|
|
4148
4198
|
}
|
|
4149
|
-
function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data, nameGapOverride) {
|
|
4199
|
+
function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacity, label, data, nameGapOverride, chartWidthHint) {
|
|
4150
4200
|
const defaultGap = type === "value" ? 75 : 40;
|
|
4201
|
+
let catFontSize = 16;
|
|
4202
|
+
let catLabelExtras = {};
|
|
4203
|
+
if (type === "category" && data && data.length > 0) {
|
|
4204
|
+
const maxLabelLen = Math.max(...data.map((l) => l.length));
|
|
4205
|
+
const count = data.length;
|
|
4206
|
+
if (count > 10 || maxLabelLen > 20) catFontSize = 10;
|
|
4207
|
+
else if (count > 5 || maxLabelLen > 14) catFontSize = 11;
|
|
4208
|
+
else if (maxLabelLen > 8) catFontSize = 12;
|
|
4209
|
+
if (chartWidthHint && count > 0) {
|
|
4210
|
+
const availPerLabel = Math.floor(chartWidthHint * 0.85 / count);
|
|
4211
|
+
catLabelExtras = {
|
|
4212
|
+
width: availPerLabel,
|
|
4213
|
+
overflow: "break"
|
|
4214
|
+
};
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4151
4217
|
return {
|
|
4152
4218
|
type,
|
|
4153
4219
|
...data && { data },
|
|
4154
4220
|
axisLine: { lineStyle: { color: axisLineColor } },
|
|
4155
4221
|
axisLabel: {
|
|
4156
4222
|
color: textColor,
|
|
4157
|
-
fontSize: type === "category" && data ?
|
|
4223
|
+
fontSize: type === "category" && data ? catFontSize : 16,
|
|
4158
4224
|
fontFamily: FONT_FAMILY,
|
|
4159
4225
|
...type === "category" && {
|
|
4160
4226
|
interval: 0,
|
|
4161
|
-
formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2")
|
|
4227
|
+
formatter: (value) => value.replace(/([a-z])([A-Z])/g, "$1\n$2"),
|
|
4228
|
+
...catLabelExtras
|
|
4162
4229
|
}
|
|
4163
4230
|
},
|
|
4164
4231
|
splitLine: { lineStyle: { color: splitLineColor, opacity: gridOpacity } },
|
|
@@ -4170,7 +4237,7 @@ function makeGridAxis(type, textColor, axisLineColor, splitLineColor, gridOpacit
|
|
|
4170
4237
|
}
|
|
4171
4238
|
};
|
|
4172
4239
|
}
|
|
4173
|
-
function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
4240
|
+
function buildEChartsOptionFromChart(parsed, palette, isDark, chartWidth) {
|
|
4174
4241
|
if (parsed.error) return {};
|
|
4175
4242
|
const textColor = palette.text;
|
|
4176
4243
|
const axisLineColor = palette.border;
|
|
@@ -4195,13 +4262,13 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
4195
4262
|
};
|
|
4196
4263
|
switch (parsed.type) {
|
|
4197
4264
|
case "bar":
|
|
4198
|
-
return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
|
|
4265
|
+
return buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth);
|
|
4199
4266
|
case "bar-stacked":
|
|
4200
|
-
return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme);
|
|
4267
|
+
return buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth);
|
|
4201
4268
|
case "line":
|
|
4202
|
-
return parsed.seriesNames ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
|
|
4269
|
+
return parsed.seriesNames ? buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) : buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
|
|
4203
4270
|
case "area":
|
|
4204
|
-
return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme);
|
|
4271
|
+
return buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth);
|
|
4205
4272
|
case "pie":
|
|
4206
4273
|
return buildPieOption(parsed, textColor, getSegmentColors(palette, parsed.data.length), titleConfig, tooltipTheme, false);
|
|
4207
4274
|
case "doughnut":
|
|
@@ -4212,7 +4279,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
4212
4279
|
return buildPolarAreaOption(parsed, textColor, getSegmentColors(palette, parsed.data.length), titleConfig, tooltipTheme);
|
|
4213
4280
|
}
|
|
4214
4281
|
}
|
|
4215
|
-
function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
|
|
4282
|
+
function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
|
|
4216
4283
|
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
4217
4284
|
const isHorizontal = parsed.orientation === "horizontal";
|
|
4218
4285
|
const labels = parsed.data.map((d) => d.label);
|
|
@@ -4221,7 +4288,7 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
|
|
|
4221
4288
|
itemStyle: { color: d.color ?? colors[i % colors.length] }
|
|
4222
4289
|
}));
|
|
4223
4290
|
const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
|
|
4224
|
-
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
|
|
4291
|
+
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap, !isHorizontal ? chartWidth : void 0);
|
|
4225
4292
|
const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel);
|
|
4226
4293
|
return {
|
|
4227
4294
|
backgroundColor: "transparent",
|
|
@@ -4253,7 +4320,7 @@ function buildBarOption(parsed, textColor, axisLineColor, splitLineColor, gridOp
|
|
|
4253
4320
|
]
|
|
4254
4321
|
};
|
|
4255
4322
|
}
|
|
4256
|
-
function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme) {
|
|
4323
|
+
function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth) {
|
|
4257
4324
|
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
4258
4325
|
const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
|
|
4259
4326
|
const labels = parsed.data.map((d) => d.label);
|
|
@@ -4274,7 +4341,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4274
4341
|
top: parsed.title ? "15%" : "5%",
|
|
4275
4342
|
containLabel: true
|
|
4276
4343
|
},
|
|
4277
|
-
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
|
|
4344
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
|
|
4278
4345
|
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
|
|
4279
4346
|
series: [
|
|
4280
4347
|
{
|
|
@@ -4292,7 +4359,7 @@ function buildLineOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4292
4359
|
]
|
|
4293
4360
|
};
|
|
4294
4361
|
}
|
|
4295
|
-
function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
|
|
4362
|
+
function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
|
|
4296
4363
|
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
4297
4364
|
const seriesNames = parsed.seriesNames ?? [];
|
|
4298
4365
|
const labels = parsed.data.map((d) => d.label);
|
|
@@ -4336,12 +4403,12 @@ function buildMultiLineOption(parsed, textColor, axisLineColor, splitLineColor,
|
|
|
4336
4403
|
top: parsed.title ? "15%" : "5%",
|
|
4337
4404
|
containLabel: true
|
|
4338
4405
|
},
|
|
4339
|
-
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
|
|
4406
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
|
|
4340
4407
|
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
|
|
4341
4408
|
series
|
|
4342
4409
|
};
|
|
4343
4410
|
}
|
|
4344
|
-
function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme) {
|
|
4411
|
+
function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineColor, gridOpacity, titleConfig, tooltipTheme, chartWidth) {
|
|
4345
4412
|
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
4346
4413
|
const lineColor = parsed.color ?? parsed.seriesNameColors?.[0] ?? palette.primary;
|
|
4347
4414
|
const labels = parsed.data.map((d) => d.label);
|
|
@@ -4362,7 +4429,7 @@ function buildAreaOption(parsed, palette, textColor, axisLineColor, splitLineCol
|
|
|
4362
4429
|
top: parsed.title ? "15%" : "5%",
|
|
4363
4430
|
containLabel: true
|
|
4364
4431
|
},
|
|
4365
|
-
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels),
|
|
4432
|
+
xAxis: makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, xLabel, labels, void 0, chartWidth),
|
|
4366
4433
|
yAxis: makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, yLabel),
|
|
4367
4434
|
series: [
|
|
4368
4435
|
{
|
|
@@ -4522,7 +4589,7 @@ function buildPolarAreaOption(parsed, textColor, colors, titleConfig, tooltipThe
|
|
|
4522
4589
|
]
|
|
4523
4590
|
};
|
|
4524
4591
|
}
|
|
4525
|
-
function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme) {
|
|
4592
|
+
function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor, gridOpacity, colors, titleConfig, tooltipTheme, chartWidth) {
|
|
4526
4593
|
const { xLabel, yLabel } = resolveAxisLabels(parsed);
|
|
4527
4594
|
const isHorizontal = parsed.orientation === "horizontal";
|
|
4528
4595
|
const seriesNames = parsed.seriesNames ?? [];
|
|
@@ -4554,8 +4621,9 @@ function buildBarStackedOption(parsed, textColor, axisLineColor, splitLineColor,
|
|
|
4554
4621
|
};
|
|
4555
4622
|
});
|
|
4556
4623
|
const hCatGap = isHorizontal && yLabel ? Math.max(40, Math.max(...labels.map((l) => l.length)) * 8 + 16) : void 0;
|
|
4557
|
-
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap);
|
|
4558
|
-
const
|
|
4624
|
+
const categoryAxis = makeGridAxis("category", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? yLabel : xLabel, labels, hCatGap, !isHorizontal ? chartWidth : void 0);
|
|
4625
|
+
const hValueGap = isHorizontal && xLabel ? 40 : void 0;
|
|
4626
|
+
const valueAxis = makeGridAxis("value", textColor, axisLineColor, splitLineColor, gridOpacity, isHorizontal ? xLabel : yLabel, void 0, hValueGap);
|
|
4559
4627
|
return {
|
|
4560
4628
|
backgroundColor: "transparent",
|
|
4561
4629
|
animation: false,
|
|
@@ -4592,7 +4660,7 @@ async function renderEChartsForExport(content, theme, palette, options) {
|
|
|
4592
4660
|
if (chartType && STANDARD_CHART_TYPES.has(chartType)) {
|
|
4593
4661
|
const parsed = parseChart(content, effectivePalette);
|
|
4594
4662
|
if (parsed.error) return "";
|
|
4595
|
-
option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark);
|
|
4663
|
+
option = buildEChartsOptionFromChart(parsed, effectivePalette, isDark, ECHART_EXPORT_WIDTH);
|
|
4596
4664
|
} else {
|
|
4597
4665
|
const parsed = parseEChart(content, effectivePalette);
|
|
4598
4666
|
if (parsed.error) return "";
|
|
@@ -4633,6 +4701,7 @@ var init_echarts = __esm({
|
|
|
4633
4701
|
init_palettes();
|
|
4634
4702
|
init_chart();
|
|
4635
4703
|
init_diagnostics();
|
|
4704
|
+
init_parsing();
|
|
4636
4705
|
ECHART_EXPORT_WIDTH = 1200;
|
|
4637
4706
|
ECHART_EXPORT_HEIGHT = 800;
|
|
4638
4707
|
STANDARD_CHART_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -12119,6 +12188,22 @@ function buildRenderSequence(messages) {
|
|
|
12119
12188
|
messageIndex: top.messageIndex
|
|
12120
12189
|
});
|
|
12121
12190
|
}
|
|
12191
|
+
if (msg.standaloneReturn) {
|
|
12192
|
+
for (let si = stack.length - 1; si >= 0; si--) {
|
|
12193
|
+
if (stack[si].from === msg.to && stack[si].to === msg.from) {
|
|
12194
|
+
stack.splice(si, 1);
|
|
12195
|
+
break;
|
|
12196
|
+
}
|
|
12197
|
+
}
|
|
12198
|
+
steps.push({
|
|
12199
|
+
type: "return",
|
|
12200
|
+
from: msg.from,
|
|
12201
|
+
to: msg.to,
|
|
12202
|
+
label: msg.label,
|
|
12203
|
+
messageIndex: mi
|
|
12204
|
+
});
|
|
12205
|
+
continue;
|
|
12206
|
+
}
|
|
12122
12207
|
steps.push({
|
|
12123
12208
|
type: "call",
|
|
12124
12209
|
from: msg.from,
|
|
@@ -13404,18 +13489,34 @@ function parseD3(content, palette) {
|
|
|
13404
13489
|
}
|
|
13405
13490
|
}
|
|
13406
13491
|
if (result.type === "quadrant") {
|
|
13407
|
-
const xAxisMatch = line7.match(/^x-axis\s*:\s*(
|
|
13492
|
+
const xAxisMatch = line7.match(/^x-axis\s*:\s*(.*)/i);
|
|
13408
13493
|
if (xAxisMatch) {
|
|
13409
|
-
const
|
|
13494
|
+
const val = xAxisMatch[1].trim();
|
|
13495
|
+
let parts;
|
|
13496
|
+
if (val) {
|
|
13497
|
+
parts = val.split(",").map((s) => s.trim());
|
|
13498
|
+
} else {
|
|
13499
|
+
const collected = collectIndentedValues(lines, i);
|
|
13500
|
+
i = collected.newIndex;
|
|
13501
|
+
parts = collected.values;
|
|
13502
|
+
}
|
|
13410
13503
|
if (parts.length >= 2) {
|
|
13411
13504
|
result.quadrantXAxis = [parts[0], parts[1]];
|
|
13412
13505
|
result.quadrantXAxisLineNumber = lineNumber;
|
|
13413
13506
|
}
|
|
13414
13507
|
continue;
|
|
13415
13508
|
}
|
|
13416
|
-
const yAxisMatch = line7.match(/^y-axis\s*:\s*(
|
|
13509
|
+
const yAxisMatch = line7.match(/^y-axis\s*:\s*(.*)/i);
|
|
13417
13510
|
if (yAxisMatch) {
|
|
13418
|
-
const
|
|
13511
|
+
const val = yAxisMatch[1].trim();
|
|
13512
|
+
let parts;
|
|
13513
|
+
if (val) {
|
|
13514
|
+
parts = val.split(",").map((s) => s.trim());
|
|
13515
|
+
} else {
|
|
13516
|
+
const collected = collectIndentedValues(lines, i);
|
|
13517
|
+
i = collected.newIndex;
|
|
13518
|
+
parts = collected.values;
|
|
13519
|
+
}
|
|
13419
13520
|
if (parts.length >= 2) {
|
|
13420
13521
|
result.quadrantYAxis = [parts[0], parts[1]];
|
|
13421
13522
|
result.quadrantYAxisLineNumber = lineNumber;
|
|
@@ -13697,6 +13798,19 @@ function tokenizeFreeformText(text) {
|
|
|
13697
13798
|
}
|
|
13698
13799
|
return Array.from(counts.entries()).map(([text2, count]) => ({ text: text2, weight: count, lineNumber: 0 })).sort((a, b) => b.weight - a.weight);
|
|
13699
13800
|
}
|
|
13801
|
+
function resolveVerticalCollisions(items, minGap) {
|
|
13802
|
+
if (items.length === 0) return [];
|
|
13803
|
+
const sorted = items.map((it, i) => ({ ...it, idx: i })).sort((a, b) => a.naturalY - b.naturalY);
|
|
13804
|
+
const adjustedY = new Array(items.length);
|
|
13805
|
+
let prevBottom = -Infinity;
|
|
13806
|
+
for (const item of sorted) {
|
|
13807
|
+
const halfH = item.height / 2;
|
|
13808
|
+
const top = Math.max(item.naturalY - halfH, prevBottom + minGap);
|
|
13809
|
+
adjustedY[item.idx] = top + halfH;
|
|
13810
|
+
prevBottom = top + item.height;
|
|
13811
|
+
}
|
|
13812
|
+
return adjustedY;
|
|
13813
|
+
}
|
|
13700
13814
|
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
13701
13815
|
d3Selection9.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
13702
13816
|
const { periods, data, title } = parsed;
|
|
@@ -13747,25 +13861,80 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
13747
13861
|
g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
|
|
13748
13862
|
}
|
|
13749
13863
|
const lineGen = d3Shape6.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
13750
|
-
data.
|
|
13864
|
+
const seriesInfo = data.map((item, idx) => {
|
|
13751
13865
|
const color = item.color ?? colors[idx % colors.length];
|
|
13752
|
-
const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
|
|
13753
13866
|
const firstVal = item.values[0];
|
|
13754
13867
|
const lastVal = item.values[item.values.length - 1];
|
|
13755
13868
|
const absChange = lastVal - firstVal;
|
|
13756
13869
|
const pctChange = firstVal !== 0 ? absChange / firstVal * 100 : null;
|
|
13757
13870
|
const sign = absChange > 0 ? "+" : "";
|
|
13758
|
-
const
|
|
13759
|
-
const tipLines = [`${sign}${absChange}`];
|
|
13871
|
+
const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
|
|
13760
13872
|
if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
|
|
13761
13873
|
const tipHtml = tipLines.join("<br>");
|
|
13874
|
+
const lastX = xScale(periods[periods.length - 1]);
|
|
13875
|
+
const labelText = `${lastVal} \u2014 ${item.label}`;
|
|
13876
|
+
const availableWidth = rightMargin - 15;
|
|
13877
|
+
const maxChars = Math.floor(availableWidth / SLOPE_CHAR_WIDTH);
|
|
13878
|
+
let labelLineCount = 1;
|
|
13879
|
+
let wrappedLines = null;
|
|
13880
|
+
if (labelText.length > maxChars) {
|
|
13881
|
+
const words = labelText.split(/\s+/);
|
|
13882
|
+
const lines = [];
|
|
13883
|
+
let current = "";
|
|
13884
|
+
for (const word of words) {
|
|
13885
|
+
const test = current ? `${current} ${word}` : word;
|
|
13886
|
+
if (test.length > maxChars && current) {
|
|
13887
|
+
lines.push(current);
|
|
13888
|
+
current = word;
|
|
13889
|
+
} else {
|
|
13890
|
+
current = test;
|
|
13891
|
+
}
|
|
13892
|
+
}
|
|
13893
|
+
if (current) lines.push(current);
|
|
13894
|
+
labelLineCount = lines.length;
|
|
13895
|
+
wrappedLines = lines;
|
|
13896
|
+
}
|
|
13897
|
+
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
13898
|
+
const labelHeight = labelLineCount === 1 ? SLOPE_LABEL_FONT_SIZE : labelLineCount * lineHeight;
|
|
13899
|
+
return {
|
|
13900
|
+
item,
|
|
13901
|
+
idx,
|
|
13902
|
+
color,
|
|
13903
|
+
firstVal,
|
|
13904
|
+
lastVal,
|
|
13905
|
+
tipHtml,
|
|
13906
|
+
lastX,
|
|
13907
|
+
labelText,
|
|
13908
|
+
maxChars,
|
|
13909
|
+
wrappedLines,
|
|
13910
|
+
labelHeight
|
|
13911
|
+
};
|
|
13912
|
+
});
|
|
13913
|
+
const leftLabelHeight = 20;
|
|
13914
|
+
const leftLabelCollisions = /* @__PURE__ */ new Map();
|
|
13915
|
+
for (let pi = 0; pi < periods.length - 1; pi++) {
|
|
13916
|
+
const entries = data.map((item) => ({
|
|
13917
|
+
naturalY: yScale(item.values[pi]),
|
|
13918
|
+
height: leftLabelHeight
|
|
13919
|
+
}));
|
|
13920
|
+
leftLabelCollisions.set(pi, resolveVerticalCollisions(entries, 4));
|
|
13921
|
+
}
|
|
13922
|
+
const rightEntries = seriesInfo.map((si) => ({
|
|
13923
|
+
naturalY: yScale(si.lastVal),
|
|
13924
|
+
height: Math.max(si.labelHeight, SLOPE_LABEL_FONT_SIZE * 1.4)
|
|
13925
|
+
}));
|
|
13926
|
+
const rightAdjustedY = resolveVerticalCollisions(rightEntries, 4);
|
|
13927
|
+
data.forEach((item, idx) => {
|
|
13928
|
+
const si = seriesInfo[idx];
|
|
13929
|
+
const color = si.color;
|
|
13930
|
+
const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
|
|
13762
13931
|
seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", color).attr("stroke-width", 2.5).attr("d", lineGen);
|
|
13763
13932
|
seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", 14).attr("d", lineGen).style("cursor", onClickItem ? "pointer" : "default").on(
|
|
13764
13933
|
"mouseenter",
|
|
13765
|
-
(event) => showTooltip(tooltip, tipHtml, event)
|
|
13934
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
13766
13935
|
).on(
|
|
13767
13936
|
"mousemove",
|
|
13768
|
-
(event) => showTooltip(tooltip, tipHtml, event)
|
|
13937
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
13769
13938
|
).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
|
|
13770
13939
|
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
13771
13940
|
});
|
|
@@ -13774,46 +13943,30 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
13774
13943
|
const y = yScale(val);
|
|
13775
13944
|
seriesG.append("circle").attr("cx", x).attr("cy", y).attr("r", 4).attr("fill", color).attr("stroke", bgColor).attr("stroke-width", 1.5).style("cursor", onClickItem ? "pointer" : "default").on(
|
|
13776
13945
|
"mouseenter",
|
|
13777
|
-
(event) => showTooltip(tooltip, tipHtml, event)
|
|
13946
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
13778
13947
|
).on(
|
|
13779
13948
|
"mousemove",
|
|
13780
|
-
(event) => showTooltip(tooltip, tipHtml, event)
|
|
13949
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
13781
13950
|
).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
|
|
13782
13951
|
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
13783
13952
|
});
|
|
13784
13953
|
const isFirst = i === 0;
|
|
13785
13954
|
const isLast = i === periods.length - 1;
|
|
13786
13955
|
if (!isLast) {
|
|
13787
|
-
|
|
13956
|
+
const adjustedY = leftLabelCollisions.get(i)[idx];
|
|
13957
|
+
seriesG.append("text").attr("x", isFirst ? x - 10 : x).attr("y", adjustedY).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", color).attr("font-size", "16px").text(val.toString());
|
|
13788
13958
|
}
|
|
13789
13959
|
});
|
|
13790
|
-
const
|
|
13791
|
-
const
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
const maxChars = Math.floor(availableWidth / SLOPE_CHAR_WIDTH);
|
|
13795
|
-
const labelEl = seriesG.append("text").attr("x", lastX + 10).attr("y", lastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", `${SLOPE_LABEL_FONT_SIZE}px`).attr("font-weight", "500");
|
|
13796
|
-
if (labelText.length <= maxChars) {
|
|
13797
|
-
labelEl.attr("dy", "0.35em").text(labelText);
|
|
13960
|
+
const adjustedLastY = rightAdjustedY[idx];
|
|
13961
|
+
const labelEl = seriesG.append("text").attr("x", si.lastX + 10).attr("y", adjustedLastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", `${SLOPE_LABEL_FONT_SIZE}px`).attr("font-weight", "500");
|
|
13962
|
+
if (!si.wrappedLines) {
|
|
13963
|
+
labelEl.attr("dy", "0.35em").text(si.labelText);
|
|
13798
13964
|
} else {
|
|
13799
|
-
const words = labelText.split(/\s+/);
|
|
13800
|
-
const lines = [];
|
|
13801
|
-
let current = "";
|
|
13802
|
-
for (const word of words) {
|
|
13803
|
-
const test = current ? `${current} ${word}` : word;
|
|
13804
|
-
if (test.length > maxChars && current) {
|
|
13805
|
-
lines.push(current);
|
|
13806
|
-
current = word;
|
|
13807
|
-
} else {
|
|
13808
|
-
current = test;
|
|
13809
|
-
}
|
|
13810
|
-
}
|
|
13811
|
-
if (current) lines.push(current);
|
|
13812
13965
|
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
13813
|
-
const totalHeight = (
|
|
13966
|
+
const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
|
|
13814
13967
|
const startDy = -totalHeight / 2;
|
|
13815
|
-
|
|
13816
|
-
labelEl.append("tspan").attr("x", lastX + 10).attr(
|
|
13968
|
+
si.wrappedLines.forEach((line7, li) => {
|
|
13969
|
+
labelEl.append("tspan").attr("x", si.lastX + 10).attr(
|
|
13817
13970
|
"dy",
|
|
13818
13971
|
li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
|
|
13819
13972
|
).text(line7);
|
|
@@ -16226,6 +16379,7 @@ var init_d3 = __esm({
|
|
|
16226
16379
|
init_colors();
|
|
16227
16380
|
init_palettes();
|
|
16228
16381
|
init_diagnostics();
|
|
16382
|
+
init_parsing();
|
|
16229
16383
|
DEFAULT_CLOUD_OPTIONS = {
|
|
16230
16384
|
rotate: "none",
|
|
16231
16385
|
max: 0,
|