@diagrammo/dgmo 0.39.0 → 0.40.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/advanced.cjs +324 -116
- package/dist/advanced.js +324 -116
- package/dist/auto.cjs +325 -117
- package/dist/auto.js +92 -92
- package/dist/auto.mjs +325 -117
- package/dist/cli.cjs +115 -115
- package/dist/index.cjs +502 -171
- package/dist/index.js +502 -171
- package/gallery/fixtures/block.dgmo +16 -0
- package/gallery/fixtures/event-line.dgmo +26 -0
- package/package.json +1 -1
- package/src/diagnostics.ts +0 -0
- package/src/echarts.ts +5 -0
- package/src/error-card.ts +200 -0
- package/src/event-line/parser.ts +91 -1
- package/src/event-line/renderer.ts +350 -139
- package/src/index.ts +7 -30
- package/src/journey-map/renderer.ts +50 -18
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { palettes, getPalette } from './palettes';
|
|
|
20
20
|
import type { PaletteConfig } from './palettes/types';
|
|
21
21
|
import type { Theme } from './themes';
|
|
22
22
|
import { formatDgmoError, type DgmoError } from './diagnostics';
|
|
23
|
+
import { renderErrorCard } from './error-card';
|
|
23
24
|
|
|
24
25
|
export type { CompactViewState } from './sharing';
|
|
25
26
|
|
|
@@ -93,41 +94,17 @@ export async function render(
|
|
|
93
94
|
return result;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
// 'svg' (default): render an inline error
|
|
97
|
+
// 'svg' (default): render an inline error card so the author sees what
|
|
98
|
+
// broke, on which line, and the offending source — see error-card.ts.
|
|
99
|
+
// We show the card whenever there are error-severity diagnostics, even if a
|
|
100
|
+
// parser produced a partial SVG: a misleading half-diagram is worse than a
|
|
101
|
+
// clear "here's what's wrong" for an embedded host (remark/astro/obsidian).
|
|
97
102
|
return {
|
|
98
|
-
svg:
|
|
103
|
+
svg: renderErrorCard(errors, text, palette, options?.theme),
|
|
99
104
|
diagnostics: result.diagnostics,
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
function renderErrorSvg(
|
|
104
|
-
errors: DgmoError[],
|
|
105
|
-
palette: PaletteConfig,
|
|
106
|
-
theme?: Theme
|
|
107
|
-
): string {
|
|
108
|
-
const colors = palette[theme === 'dark' ? 'dark' : 'light'];
|
|
109
|
-
const bg = theme === 'transparent' ? 'transparent' : colors.bg;
|
|
110
|
-
const fg = colors.destructive ?? colors.text;
|
|
111
|
-
const muted = colors.textMuted ?? colors.text;
|
|
112
|
-
const lines = errors.slice(0, 5).map((e) => formatDgmoError(e));
|
|
113
|
-
const height = 60 + lines.length * 20;
|
|
114
|
-
const body = lines
|
|
115
|
-
.map(
|
|
116
|
-
(line, i) =>
|
|
117
|
-
`<text x="20" y="${60 + i * 20}" fill="${fg}" font-family="ui-monospace, monospace" font-size="12">${escapeXml(line)}</text>`
|
|
118
|
-
)
|
|
119
|
-
.join('');
|
|
120
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="640" height="${height}" viewBox="0 0 640 ${height}"><rect width="100%" height="100%" fill="${bg}"/><text x="20" y="34" fill="${muted}" font-family="system-ui, sans-serif" font-size="13" font-weight="600">DGMO parse error</text>${body}</svg>`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function escapeXml(s: string): string {
|
|
124
|
-
return s
|
|
125
|
-
.replace(/&/g, '&')
|
|
126
|
-
.replace(/</g, '<')
|
|
127
|
-
.replace(/>/g, '>')
|
|
128
|
-
.replace(/"/g, '"');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
108
|
// ============================================================
|
|
132
109
|
// validate(text)
|
|
133
110
|
// ============================================================
|
|
@@ -71,6 +71,10 @@ const FONT_SIZE_META = 10;
|
|
|
71
71
|
const GRID_LINE_OPACITY = 0.15;
|
|
72
72
|
const CURVE_STROKE_WIDTH = 2.5;
|
|
73
73
|
const FACE_RADIUS = 14;
|
|
74
|
+
// Faces grow on hover/active; the thought bubble + connector must clear the
|
|
75
|
+
// enlarged outer edge (radius + ring halo), not the base radius.
|
|
76
|
+
const FACE_HOVER_SCALE = 1.5;
|
|
77
|
+
const FACE_HOVER_R = (FACE_RADIUS + 1) * FACE_HOVER_SCALE;
|
|
74
78
|
const DIM_HOVER = 0.25;
|
|
75
79
|
const TITLE_LINE_HEIGHT = 16;
|
|
76
80
|
|
|
@@ -450,6 +454,7 @@ export function renderJourneyMap(
|
|
|
450
454
|
y,
|
|
451
455
|
score,
|
|
452
456
|
palette,
|
|
457
|
+
isDark,
|
|
453
458
|
SCORE_LABEL_R
|
|
454
459
|
);
|
|
455
460
|
|
|
@@ -517,7 +522,14 @@ export function renderJourneyMap(
|
|
|
517
522
|
|
|
518
523
|
for (const pt of layout.curvePoints) {
|
|
519
524
|
const step = allSteps[pt.stepIndex];
|
|
520
|
-
const faceG = renderScoreFace(
|
|
525
|
+
const faceG = renderScoreFace(
|
|
526
|
+
curveG,
|
|
527
|
+
pt.x,
|
|
528
|
+
pt.y,
|
|
529
|
+
pt.score,
|
|
530
|
+
palette,
|
|
531
|
+
isDark
|
|
532
|
+
);
|
|
521
533
|
addEmotionLabel(faceG, pt, palette);
|
|
522
534
|
if (step) {
|
|
523
535
|
faceG.attr('data-line-number', step.lineNumber);
|
|
@@ -549,7 +561,14 @@ export function renderJourneyMap(
|
|
|
549
561
|
? parsed.phases.flatMap((p) => p.steps)
|
|
550
562
|
: parsed.steps;
|
|
551
563
|
const step = allSteps[pt.stepIndex];
|
|
552
|
-
const faceG = renderScoreFace(
|
|
564
|
+
const faceG = renderScoreFace(
|
|
565
|
+
curveG,
|
|
566
|
+
pt.x,
|
|
567
|
+
pt.y,
|
|
568
|
+
pt.score,
|
|
569
|
+
palette,
|
|
570
|
+
isDark
|
|
571
|
+
);
|
|
553
572
|
addEmotionLabel(faceG, pt, palette);
|
|
554
573
|
if (step) {
|
|
555
574
|
faceG.attr('data-line-number', step.lineNumber);
|
|
@@ -723,6 +742,7 @@ export function renderJourneyMap(
|
|
|
723
742
|
faceCy,
|
|
724
743
|
step.score,
|
|
725
744
|
palette,
|
|
745
|
+
isDark,
|
|
726
746
|
COLLAPSED_FACE_R
|
|
727
747
|
);
|
|
728
748
|
faceG.attr('data-line-number', step.lineNumber);
|
|
@@ -856,7 +876,7 @@ export function renderJourneyMap(
|
|
|
856
876
|
const fcy = parseFloat(el.attr('data-cy') ?? '0');
|
|
857
877
|
el.attr(
|
|
858
878
|
'transform',
|
|
859
|
-
`translate(${fcx},${fcy}) scale(
|
|
879
|
+
`translate(${fcx},${fcy}) scale(${FACE_HOVER_SCALE}) translate(${-fcx},${-fcy})`
|
|
860
880
|
);
|
|
861
881
|
} else {
|
|
862
882
|
el.attr('transform', null);
|
|
@@ -917,7 +937,7 @@ export function renderJourneyMap(
|
|
|
917
937
|
PADDING,
|
|
918
938
|
Math.min(fcx - bw / 2, layout.totalWidth - PADDING - bw)
|
|
919
939
|
);
|
|
920
|
-
const by = Math.max(PADDING, fcy -
|
|
940
|
+
const by = Math.max(PADDING, fcy - FACE_HOVER_R - THOUGHT_GAP - bh);
|
|
921
941
|
|
|
922
942
|
const scoreColor = scoreToColor(score, palette);
|
|
923
943
|
const tintedBg = mix(scoreColor, palette.surface, 20);
|
|
@@ -939,7 +959,7 @@ export function renderJourneyMap(
|
|
|
939
959
|
.attr('x1', fcx)
|
|
940
960
|
.attr('y1', by + bh)
|
|
941
961
|
.attr('x2', fcx)
|
|
942
|
-
.attr('y2', fcy -
|
|
962
|
+
.attr('y2', fcy - FACE_HOVER_R - 1)
|
|
943
963
|
.attr('stroke', scoreColor)
|
|
944
964
|
.attr('stroke-width', CARD_STROKE_WIDTH);
|
|
945
965
|
|
|
@@ -1400,6 +1420,7 @@ function renderScoreFace(
|
|
|
1400
1420
|
cy: number,
|
|
1401
1421
|
score: number,
|
|
1402
1422
|
palette: PaletteColors,
|
|
1423
|
+
isDark: boolean,
|
|
1403
1424
|
radius?: number
|
|
1404
1425
|
): d3.Selection<SVGGElement, unknown, null, undefined> {
|
|
1405
1426
|
const r = radius ?? FACE_RADIUS;
|
|
@@ -1410,21 +1431,28 @@ function renderScoreFace(
|
|
|
1410
1431
|
.attr('data-cx', cx)
|
|
1411
1432
|
.attr('data-cy', cy);
|
|
1412
1433
|
|
|
1413
|
-
// Face
|
|
1434
|
+
// Face: a solid colored ring over the canonical tinted fill (the same
|
|
1435
|
+
// shapeFill() tint used for unsolid shapes elsewhere), with the eyes and
|
|
1436
|
+
// mouth drawn in the full score color.
|
|
1437
|
+
const faceFill = shapeFill(palette, color, isDark);
|
|
1438
|
+
|
|
1439
|
+
// Thin bg halo so the colored ring reads crisply where it crosses the line.
|
|
1440
|
+
g.append('circle')
|
|
1441
|
+
.attr('cx', cx)
|
|
1442
|
+
.attr('cy', cy)
|
|
1443
|
+
.attr('r', r + 1)
|
|
1444
|
+
.attr('fill', palette.bg);
|
|
1445
|
+
|
|
1414
1446
|
g.append('circle')
|
|
1415
1447
|
.attr('cx', cx)
|
|
1416
1448
|
.attr('cy', cy)
|
|
1417
1449
|
.attr('r', r)
|
|
1418
|
-
.attr('fill',
|
|
1419
|
-
.attr('stroke',
|
|
1420
|
-
.attr('stroke-width',
|
|
1450
|
+
.attr('fill', faceFill)
|
|
1451
|
+
.attr('stroke', color)
|
|
1452
|
+
.attr('stroke-width', 2);
|
|
1421
1453
|
|
|
1422
|
-
// Eyes
|
|
1423
|
-
const eyeColor =
|
|
1424
|
-
color,
|
|
1425
|
-
palette.textOnFillLight,
|
|
1426
|
-
palette.textOnFillDark
|
|
1427
|
-
);
|
|
1454
|
+
// Eyes & mouth share the solid score color.
|
|
1455
|
+
const eyeColor = color;
|
|
1428
1456
|
const eyeY = cy - r * 0.15;
|
|
1429
1457
|
const eyeSpacing = r * 0.32;
|
|
1430
1458
|
const eyeR = r * 0.12;
|
|
@@ -1441,10 +1469,14 @@ function renderScoreFace(
|
|
|
1441
1469
|
|
|
1442
1470
|
// Mouth — arc curvature based on score
|
|
1443
1471
|
// score 5: big smile, score 3: straight, score 1: deep frown
|
|
1444
|
-
const
|
|
1445
|
-
const mouthW = r * 0.45;
|
|
1472
|
+
const mouthW = r * 0.46;
|
|
1446
1473
|
// Curve amount: positive = smile, negative = frown
|
|
1447
|
-
const curve = ((score - 3) / 2) * r * 0.
|
|
1474
|
+
const curve = ((score - 3) / 2) * r * 0.42;
|
|
1475
|
+
// Pin the mouth's visual apex (its mid-point) to a fixed Y and let the
|
|
1476
|
+
// curvature swing symmetrically about it. Anchoring the corners instead made
|
|
1477
|
+
// the frown's arch ride up into the eyes and read as a cramped grimace.
|
|
1478
|
+
const apexY = cy + r * 0.32;
|
|
1479
|
+
const mouthY = apexY - curve / 2;
|
|
1448
1480
|
|
|
1449
1481
|
g.append('path')
|
|
1450
1482
|
.attr(
|