@diagrammo/dgmo 0.8.21 → 0.8.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/README.md +1 -0
- package/dist/cli.cjs +145 -93
- package/dist/editor.cjs +20 -3
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +20 -3
- package/dist/editor.js.map +1 -1
- package/dist/highlight.cjs +15 -2
- package/dist/highlight.cjs.map +1 -1
- package/dist/highlight.js +15 -2
- package/dist/highlight.js.map +1 -1
- package/dist/index.cjs +20843 -14937
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +426 -17
- package/dist/index.d.ts +426 -17
- package/dist/index.js +20795 -14912
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +380 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.cts +179 -0
- package/dist/internal.d.ts +179 -0
- package/dist/internal.js +337 -0
- package/dist/internal.js.map +1 -0
- package/docs/guide/chart-cycle.md +156 -0
- package/docs/guide/chart-journey-map.md +179 -0
- package/docs/guide/chart-pyramid.md +111 -0
- package/docs/guide/chart-sitemap.md +18 -1
- package/docs/guide/chart-tech-radar.md +219 -0
- package/docs/guide/registry.json +6 -0
- package/docs/language-reference.md +177 -6
- package/gallery/fixtures/boxes-and-lines.dgmo +10 -3
- package/gallery/fixtures/c4-full.dgmo +2 -2
- package/gallery/fixtures/cycle/ooda-loop.dgmo +25 -0
- package/gallery/fixtures/cycle/pdca-circle-nodes.dgmo +12 -0
- package/gallery/fixtures/cycle/pdca-minimal.dgmo +6 -0
- package/gallery/fixtures/cycle/sprint-cycle-span.dgmo +17 -0
- package/gallery/fixtures/gantt-full.dgmo +2 -2
- package/gallery/fixtures/gantt.dgmo +2 -2
- package/gallery/fixtures/infra-full.dgmo +2 -2
- package/gallery/fixtures/infra.dgmo +1 -1
- package/gallery/fixtures/pyramid/dikw.dgmo +17 -0
- package/gallery/fixtures/pyramid/inverted-funnel.dgmo +16 -0
- package/gallery/fixtures/pyramid/minimal.dgmo +5 -0
- package/gallery/fixtures/sequence-tags-protocols.dgmo +2 -2
- package/gallery/fixtures/sequence-tags.dgmo +2 -2
- package/gallery/fixtures/tech-radar-dense.dgmo +77 -0
- package/gallery/fixtures/tech-radar.dgmo +36 -0
- package/gallery/fixtures/timeline.dgmo +1 -1
- package/package.json +11 -1
- package/src/boxes-and-lines/layout.ts +309 -33
- package/src/boxes-and-lines/parser.ts +86 -10
- package/src/boxes-and-lines/renderer.ts +250 -91
- package/src/boxes-and-lines/types.ts +1 -1
- package/src/c4/layout.ts +8 -8
- package/src/c4/parser.ts +35 -2
- package/src/c4/renderer.ts +19 -3
- package/src/c4/types.ts +1 -0
- package/src/chart.ts +14 -7
- package/src/cli.ts +5 -35
- package/src/completion.ts +233 -41
- package/src/cycle/layout.ts +723 -0
- package/src/cycle/parser.ts +352 -0
- package/src/cycle/renderer.ts +566 -0
- package/src/cycle/types.ts +98 -0
- package/src/d3.ts +107 -8
- package/src/dgmo-router.ts +82 -3
- package/src/echarts.ts +8 -5
- package/src/editor/dgmo.grammar +5 -1
- package/src/editor/dgmo.grammar.js +1 -1
- package/src/editor/keywords.ts +17 -0
- package/src/gantt/parser.ts +2 -8
- package/src/graph/flowchart-parser.ts +15 -21
- package/src/graph/state-parser.ts +5 -10
- package/src/index.ts +63 -2
- package/src/infra/layout.ts +218 -74
- package/src/infra/parser.ts +32 -8
- package/src/infra/renderer.ts +14 -8
- package/src/infra/types.ts +10 -3
- package/src/internal.ts +16 -0
- package/src/journey-map/layout.ts +386 -0
- package/src/journey-map/parser.ts +540 -0
- package/src/journey-map/renderer.ts +1521 -0
- package/src/journey-map/types.ts +47 -0
- package/src/kanban/parser.ts +3 -10
- package/src/kanban/renderer.ts +31 -15
- package/src/mindmap/parser.ts +12 -18
- package/src/mindmap/renderer.ts +14 -13
- package/src/mindmap/text-wrap.ts +22 -12
- package/src/mindmap/types.ts +2 -2
- package/src/org/collapse.ts +81 -0
- package/src/org/parser.ts +2 -6
- package/src/org/renderer.ts +212 -4
- package/src/pyramid/parser.ts +172 -0
- package/src/pyramid/renderer.ts +684 -0
- package/src/pyramid/types.ts +28 -0
- package/src/render.ts +2 -8
- package/src/sequence/parser.ts +62 -20
- package/src/sequence/renderer.ts +146 -40
- package/src/sharing.ts +1 -0
- package/src/sitemap/layout.ts +21 -6
- package/src/sitemap/parser.ts +26 -17
- package/src/sitemap/renderer.ts +34 -0
- package/src/sitemap/types.ts +1 -0
- package/src/tech-radar/index.ts +14 -0
- package/src/tech-radar/interactive.ts +1112 -0
- package/src/tech-radar/layout.ts +190 -0
- package/src/tech-radar/parser.ts +385 -0
- package/src/tech-radar/renderer.ts +1159 -0
- package/src/tech-radar/shared.ts +187 -0
- package/src/tech-radar/types.ts +81 -0
- package/src/utils/description-helpers.ts +33 -0
- package/src/utils/legend-layout.ts +3 -1
- package/src/utils/parsing.ts +47 -7
- package/src/utils/tag-groups.ts +46 -60
package/src/c4/types.ts
CHANGED
package/src/chart.ts
CHANGED
|
@@ -64,6 +64,7 @@ import type { PaletteColors } from './palettes';
|
|
|
64
64
|
import { makeDgmoError, formatDgmoError, suggest } from './diagnostics';
|
|
65
65
|
import {
|
|
66
66
|
extractColor,
|
|
67
|
+
normalizeNumericToken,
|
|
67
68
|
parseFirstLine,
|
|
68
69
|
parseSeriesNames,
|
|
69
70
|
} from './utils/parsing';
|
|
@@ -528,7 +529,8 @@ export function parseDataRowValues(
|
|
|
528
529
|
// Find how many trailing comma-separated parts are numeric
|
|
529
530
|
let numericCount = 0;
|
|
530
531
|
for (let j = commaParts.length - 1; j >= 0; j--) {
|
|
531
|
-
const part =
|
|
532
|
+
const part =
|
|
533
|
+
normalizeNumericToken(commaParts[j].trim()) ?? commaParts[j].trim();
|
|
532
534
|
if (part && !isNaN(parseFloat(part)) && isFinite(Number(part))) {
|
|
533
535
|
numericCount++;
|
|
534
536
|
} else {
|
|
@@ -545,7 +547,9 @@ export function parseDataRowValues(
|
|
|
545
547
|
// Split firstPart from the right: last space-separated token must be numeric
|
|
546
548
|
const lastSpaceIdx = firstPart.lastIndexOf(' ');
|
|
547
549
|
if (lastSpaceIdx >= 0) {
|
|
548
|
-
const
|
|
550
|
+
const rawFirstVal = firstPart.substring(lastSpaceIdx + 1).trim();
|
|
551
|
+
const possibleFirstVal =
|
|
552
|
+
normalizeNumericToken(rawFirstVal) ?? rawFirstVal;
|
|
549
553
|
if (
|
|
550
554
|
possibleFirstVal &&
|
|
551
555
|
!isNaN(parseFloat(possibleFirstVal)) &&
|
|
@@ -555,7 +559,8 @@ export function parseDataRowValues(
|
|
|
555
559
|
if (label) {
|
|
556
560
|
const values = [parseFloat(possibleFirstVal)];
|
|
557
561
|
for (const p of extraValueParts) {
|
|
558
|
-
|
|
562
|
+
const normP = normalizeNumericToken(p.trim()) ?? p.trim();
|
|
563
|
+
values.push(parseFloat(normP));
|
|
559
564
|
}
|
|
560
565
|
return { label, values };
|
|
561
566
|
}
|
|
@@ -577,8 +582,9 @@ export function parseDataRowValues(
|
|
|
577
582
|
let idx = tokens.length - 1;
|
|
578
583
|
while (idx >= 1 && values.length < limit) {
|
|
579
584
|
const tok = tokens[idx];
|
|
580
|
-
const
|
|
581
|
-
|
|
585
|
+
const normTok = normalizeNumericToken(tok) ?? tok;
|
|
586
|
+
const num = parseFloat(normTok);
|
|
587
|
+
if (isNaN(num) || !isFinite(Number(normTok))) break;
|
|
582
588
|
values.unshift(num);
|
|
583
589
|
idx--;
|
|
584
590
|
}
|
|
@@ -590,8 +596,9 @@ export function parseDataRowValues(
|
|
|
590
596
|
|
|
591
597
|
// Single-value mode: only the last space-separated token
|
|
592
598
|
const lastToken = tokens[tokens.length - 1];
|
|
593
|
-
const
|
|
594
|
-
|
|
599
|
+
const normalizedLast = normalizeNumericToken(lastToken) ?? lastToken;
|
|
600
|
+
const num = parseFloat(normalizedLast);
|
|
601
|
+
if (isNaN(num) || !isFinite(Number(normalizedLast))) return null;
|
|
595
602
|
|
|
596
603
|
const label = tokens.slice(0, -1).join(' ');
|
|
597
604
|
if (!label) return null;
|
package/src/cli.ts
CHANGED
|
@@ -6,7 +6,11 @@ import { resolve, join, basename, extname } from 'node:path';
|
|
|
6
6
|
import { createInterface } from 'node:readline';
|
|
7
7
|
import { Resvg } from '@resvg/resvg-js';
|
|
8
8
|
import { render } from './render';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
parseDgmo,
|
|
11
|
+
getAllChartTypes,
|
|
12
|
+
CHART_TYPE_DESCRIPTIONS,
|
|
13
|
+
} from './dgmo-router';
|
|
10
14
|
import { parseDgmoChartType } from './dgmo-router';
|
|
11
15
|
import { formatDgmoError } from './diagnostics';
|
|
12
16
|
import { getPalette, getAvailablePalettes } from './palettes';
|
|
@@ -19,40 +23,6 @@ const PALETTES = getAvailablePalettes().map((p) => p.id);
|
|
|
19
23
|
|
|
20
24
|
const THEMES = ['light', 'dark', 'transparent'] as const;
|
|
21
25
|
|
|
22
|
-
const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
23
|
-
bar: 'Bar chart — categorical comparisons',
|
|
24
|
-
line: 'Line chart — trends over time',
|
|
25
|
-
'multi-line': 'Multi-line chart — multiple series trends',
|
|
26
|
-
area: 'Area chart — filled line chart',
|
|
27
|
-
pie: 'Pie chart — part-to-whole proportions',
|
|
28
|
-
doughnut: 'Doughnut chart — ring-style pie chart',
|
|
29
|
-
radar: 'Radar chart — multi-dimensional metrics',
|
|
30
|
-
'polar-area': 'Polar area chart — radial bar chart',
|
|
31
|
-
'bar-stacked': 'Stacked bar chart — multi-series categorical',
|
|
32
|
-
scatter: 'Scatter plot — 2D data points or bubble chart',
|
|
33
|
-
sankey: 'Sankey diagram — flow/allocation visualization',
|
|
34
|
-
chord: 'Chord diagram — circular flow relationships',
|
|
35
|
-
function: 'Function plot — mathematical expressions',
|
|
36
|
-
heatmap: 'Heatmap — matrix intensity visualization',
|
|
37
|
-
funnel: 'Funnel chart — conversion pipeline',
|
|
38
|
-
slope: 'Slope chart — change between two periods',
|
|
39
|
-
wordcloud: 'Word cloud — term frequency visualization',
|
|
40
|
-
arc: 'Arc diagram — network relationships',
|
|
41
|
-
timeline: 'Timeline — events, eras, and date ranges',
|
|
42
|
-
venn: 'Venn diagram — set overlaps',
|
|
43
|
-
quadrant: 'Quadrant chart — 2x2 positioning matrix',
|
|
44
|
-
sequence: 'Sequence diagram — message/interaction flows',
|
|
45
|
-
flowchart: 'Flowchart — decision trees and process flows',
|
|
46
|
-
class: 'Class diagram — UML class hierarchies',
|
|
47
|
-
er: 'ER diagram — database schemas and relationships',
|
|
48
|
-
org: 'Org chart — hierarchical tree structures',
|
|
49
|
-
kanban: 'Kanban board — task/workflow columns',
|
|
50
|
-
c4: 'C4 diagram — system architecture (context, container, component, deployment)',
|
|
51
|
-
infra: 'Infra chart — infrastructure traffic flow with rps computation',
|
|
52
|
-
'boxes-and-lines':
|
|
53
|
-
'Boxes and lines — general-purpose node-edge diagrams with groups and tags',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
26
|
const CLAUDE_SKILL_CONTENT = `# dgmo — Diagrammo Diagram Assistant
|
|
57
27
|
|
|
58
28
|
You are helping the user author, render, and share diagrams using the \`dgmo\` CLI and \`.dgmo\` file format.
|
package/src/completion.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { extractSymbols as extractFlowchartSymbols } from './graph/flowchart-par
|
|
|
16
16
|
import { extractSymbols as extractInfraSymbols } from './infra/parser';
|
|
17
17
|
import { extractSymbols as extractClassSymbols } from './class/parser';
|
|
18
18
|
import { parseFirstLine, ALL_CHART_TYPES } from './utils/parsing';
|
|
19
|
+
import { CHART_TYPE_DESCRIPTIONS } from './dgmo-router';
|
|
19
20
|
|
|
20
21
|
// ============================================================
|
|
21
22
|
// Symbol extraction
|
|
@@ -349,53 +350,52 @@ export const COMPLETION_REGISTRY = new Map<string, DirectiveSpec>([
|
|
|
349
350
|
'active-tag': { description: 'Active tag group name' },
|
|
350
351
|
}),
|
|
351
352
|
],
|
|
353
|
+
[
|
|
354
|
+
'tech-radar',
|
|
355
|
+
withGlobals({
|
|
356
|
+
rings: { description: 'Ring names block (innermost to outermost)' },
|
|
357
|
+
quadrant: {
|
|
358
|
+
description:
|
|
359
|
+
'Quadrant position (top-left, top-right, bottom-left, bottom-right)',
|
|
360
|
+
},
|
|
361
|
+
ring: { description: 'Ring assignment for a blip' },
|
|
362
|
+
trend: { description: 'Blip trend (new, up, down, stable)' },
|
|
363
|
+
color: { description: 'Override quadrant color' },
|
|
364
|
+
}),
|
|
365
|
+
],
|
|
366
|
+
[
|
|
367
|
+
'cycle',
|
|
368
|
+
withGlobals({
|
|
369
|
+
'direction-counterclockwise': {
|
|
370
|
+
description: 'Reverse cycle direction to counterclockwise',
|
|
371
|
+
},
|
|
372
|
+
'hide-descriptions': { description: 'Hide node and edge descriptions' },
|
|
373
|
+
'circle-nodes': {
|
|
374
|
+
description: 'Render nodes as circles instead of rectangles',
|
|
375
|
+
},
|
|
376
|
+
}),
|
|
377
|
+
],
|
|
378
|
+
[
|
|
379
|
+
'journey-map',
|
|
380
|
+
withGlobals({
|
|
381
|
+
'no-legend': { description: 'Hide the score legend' },
|
|
382
|
+
persona: { description: 'Define the journey persona' },
|
|
383
|
+
}),
|
|
384
|
+
],
|
|
385
|
+
[
|
|
386
|
+
'pyramid',
|
|
387
|
+
withGlobals({
|
|
388
|
+
inverted: { description: 'Flip apex to the bottom (funnel orientation)' },
|
|
389
|
+
color: { description: 'Override layer color (pipe metadata)' },
|
|
390
|
+
description: { description: 'Layer description (pipe or indented body)' },
|
|
391
|
+
}),
|
|
392
|
+
],
|
|
352
393
|
]);
|
|
353
394
|
|
|
354
395
|
// ============================================================
|
|
355
396
|
// Chart types array (for chart type completion popup)
|
|
356
397
|
// ============================================================
|
|
357
398
|
|
|
358
|
-
const CHART_TYPE_DESCRIPTIONS: Record<string, string> = {
|
|
359
|
-
// Data charts
|
|
360
|
-
bar: 'Bar chart',
|
|
361
|
-
line: 'Line chart',
|
|
362
|
-
pie: 'Pie chart',
|
|
363
|
-
doughnut: 'Doughnut chart',
|
|
364
|
-
area: 'Area chart',
|
|
365
|
-
'polar-area': 'Polar area chart',
|
|
366
|
-
radar: 'Radar chart',
|
|
367
|
-
'bar-stacked': 'Stacked bar chart',
|
|
368
|
-
// Extended charts
|
|
369
|
-
scatter: 'Scatter plot',
|
|
370
|
-
heatmap: 'Heatmap',
|
|
371
|
-
sankey: 'Sankey flow diagram',
|
|
372
|
-
chord: 'Chord diagram',
|
|
373
|
-
funnel: 'Funnel chart',
|
|
374
|
-
function: 'Mathematical function plot',
|
|
375
|
-
// Visualizations
|
|
376
|
-
slope: 'Slope chart',
|
|
377
|
-
wordcloud: 'Word cloud',
|
|
378
|
-
arc: 'Arc diagram',
|
|
379
|
-
timeline: 'Timeline',
|
|
380
|
-
venn: 'Venn diagram',
|
|
381
|
-
quadrant: 'Quadrant chart',
|
|
382
|
-
// Diagrams
|
|
383
|
-
sequence: 'Sequence diagram',
|
|
384
|
-
flowchart: 'Flowchart',
|
|
385
|
-
class: 'Class diagram',
|
|
386
|
-
er: 'Entity-relationship diagram',
|
|
387
|
-
org: 'Organization chart',
|
|
388
|
-
kanban: 'Kanban board',
|
|
389
|
-
c4: 'C4 architecture diagram',
|
|
390
|
-
state: 'State diagram',
|
|
391
|
-
sitemap: 'Sitemap diagram',
|
|
392
|
-
infra: 'Infrastructure diagram',
|
|
393
|
-
gantt: 'Gantt chart',
|
|
394
|
-
'boxes-and-lines': 'Boxes and lines diagram',
|
|
395
|
-
mindmap: 'Mindmap diagram',
|
|
396
|
-
wireframe: 'UI wireframe diagram',
|
|
397
|
-
};
|
|
398
|
-
|
|
399
399
|
/** All chart types with descriptions, for chart type autocomplete. Excludes `multi-line` alias. */
|
|
400
400
|
export const CHART_TYPES: ReadonlyArray<{ name: string; description: string }> =
|
|
401
401
|
[...ALL_CHART_TYPES]
|
|
@@ -537,6 +537,38 @@ export const PIPE_METADATA = new Map<
|
|
|
537
537
|
edge: {},
|
|
538
538
|
},
|
|
539
539
|
],
|
|
540
|
+
[
|
|
541
|
+
'tech-radar',
|
|
542
|
+
{
|
|
543
|
+
node: {
|
|
544
|
+
quadrant: {
|
|
545
|
+
description: 'Quadrant position',
|
|
546
|
+
values: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
|
547
|
+
},
|
|
548
|
+
ring: { description: 'Ring assignment for blip' },
|
|
549
|
+
trend: {
|
|
550
|
+
description: 'Blip trend indicator',
|
|
551
|
+
values: ['new', 'up', 'down', 'stable'],
|
|
552
|
+
},
|
|
553
|
+
color: { description: 'Override quadrant color' },
|
|
554
|
+
},
|
|
555
|
+
edge: {},
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
[
|
|
559
|
+
'cycle',
|
|
560
|
+
{
|
|
561
|
+
node: {
|
|
562
|
+
color: { description: 'Node fill color (palette name)' },
|
|
563
|
+
span: { description: 'Relative arc distance to next node' },
|
|
564
|
+
description: { description: 'Node description text' },
|
|
565
|
+
},
|
|
566
|
+
edge: {
|
|
567
|
+
color: { description: 'Edge stroke color (palette name)' },
|
|
568
|
+
width: { description: 'Edge stroke width in pixels' },
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
],
|
|
540
572
|
]);
|
|
541
573
|
|
|
542
574
|
// ============================================================
|
|
@@ -1035,3 +1067,163 @@ registerExtractor('sitemap', extractSitemapSymbols);
|
|
|
1035
1067
|
registerExtractor('c4', extractC4Symbols);
|
|
1036
1068
|
registerExtractor('gantt', extractGanttSymbols);
|
|
1037
1069
|
registerExtractor('boxes-and-lines', extractBoxesAndLinesSymbols);
|
|
1070
|
+
registerExtractor('tech-radar', extractTechRadarSymbols);
|
|
1071
|
+
registerExtractor('cycle', extractCycleSymbols);
|
|
1072
|
+
registerExtractor('journey-map', extractJourneyMapSymbols);
|
|
1073
|
+
|
|
1074
|
+
function extractTechRadarSymbols(docText: string): DiagramSymbols {
|
|
1075
|
+
const entities: string[] = [];
|
|
1076
|
+
const keywords: string[] = [
|
|
1077
|
+
'rings',
|
|
1078
|
+
'quadrant',
|
|
1079
|
+
'ring',
|
|
1080
|
+
'trend',
|
|
1081
|
+
'new',
|
|
1082
|
+
'up',
|
|
1083
|
+
'down',
|
|
1084
|
+
'stable',
|
|
1085
|
+
'top-left',
|
|
1086
|
+
'top-right',
|
|
1087
|
+
'bottom-left',
|
|
1088
|
+
'bottom-right',
|
|
1089
|
+
'alias',
|
|
1090
|
+
'aka',
|
|
1091
|
+
'color',
|
|
1092
|
+
];
|
|
1093
|
+
|
|
1094
|
+
// Extract ring names and aliases from the rings block
|
|
1095
|
+
const lines = docText.split('\n');
|
|
1096
|
+
let inRings = false;
|
|
1097
|
+
for (const line of lines) {
|
|
1098
|
+
const trimmed = line.trim();
|
|
1099
|
+
if (trimmed.toLowerCase() === 'rings') {
|
|
1100
|
+
inRings = true;
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
if (inRings) {
|
|
1104
|
+
if (!trimmed || (line[0] !== ' ' && line[0] !== '\t')) {
|
|
1105
|
+
inRings = false;
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
// Parse ring name (and alias)
|
|
1109
|
+
const aliasMatch = trimmed.match(/^(.+?)\s+(?:alias|aka)\s+(\S+)\s*$/i);
|
|
1110
|
+
if (aliasMatch) {
|
|
1111
|
+
entities.push(aliasMatch[1].trim());
|
|
1112
|
+
entities.push(aliasMatch[2].trim());
|
|
1113
|
+
} else {
|
|
1114
|
+
entities.push(trimmed);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
return { kind: 'tech-radar', entities, keywords };
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// ============================================================
|
|
1123
|
+
// Cycle extractor
|
|
1124
|
+
// ============================================================
|
|
1125
|
+
|
|
1126
|
+
function extractCycleSymbols(docText: string): DiagramSymbols {
|
|
1127
|
+
const lines = docText.split('\n');
|
|
1128
|
+
const entities: string[] = [];
|
|
1129
|
+
let pastFirstLine = false;
|
|
1130
|
+
|
|
1131
|
+
for (const line of lines) {
|
|
1132
|
+
const trimmed = line.trim();
|
|
1133
|
+
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
1134
|
+
|
|
1135
|
+
if (!pastFirstLine) {
|
|
1136
|
+
pastFirstLine = true;
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Skip directives/metadata
|
|
1141
|
+
const firstToken = trimmed.split(/\s+/)[0].toLowerCase();
|
|
1142
|
+
if (METADATA_KEY_SET.has(firstToken)) continue;
|
|
1143
|
+
if (
|
|
1144
|
+
firstToken === 'direction-counterclockwise' ||
|
|
1145
|
+
firstToken === 'circle-nodes' ||
|
|
1146
|
+
firstToken === 'hide-descriptions'
|
|
1147
|
+
)
|
|
1148
|
+
continue;
|
|
1149
|
+
|
|
1150
|
+
// Skip indented lines (descriptions, edges)
|
|
1151
|
+
if (line[0] === ' ' || line[0] === '\t') continue;
|
|
1152
|
+
|
|
1153
|
+
// Node label (strip pipe metadata)
|
|
1154
|
+
const label = trimmed.split('|')[0].trim();
|
|
1155
|
+
if (label && !entities.includes(label)) entities.push(label);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return {
|
|
1159
|
+
kind: 'cycle',
|
|
1160
|
+
entities,
|
|
1161
|
+
keywords: [
|
|
1162
|
+
'direction-counterclockwise',
|
|
1163
|
+
'hide-descriptions',
|
|
1164
|
+
'circle-nodes',
|
|
1165
|
+
],
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function extractJourneyMapSymbols(docText: string): DiagramSymbols {
|
|
1170
|
+
const lines = docText.split('\n');
|
|
1171
|
+
const entities: string[] = [];
|
|
1172
|
+
let pastFirstLine = false;
|
|
1173
|
+
|
|
1174
|
+
for (const line of lines) {
|
|
1175
|
+
const trimmed = line.trim();
|
|
1176
|
+
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
1177
|
+
|
|
1178
|
+
if (!pastFirstLine) {
|
|
1179
|
+
pastFirstLine = true;
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// Skip directives/metadata at indent 0
|
|
1184
|
+
const firstToken = trimmed.split(/\s+/)[0].toLowerCase();
|
|
1185
|
+
if (METADATA_KEY_SET.has(firstToken)) continue;
|
|
1186
|
+
if (
|
|
1187
|
+
firstToken === 'persona' ||
|
|
1188
|
+
firstToken === 'tag' ||
|
|
1189
|
+
firstToken === 'no-legend'
|
|
1190
|
+
)
|
|
1191
|
+
continue;
|
|
1192
|
+
|
|
1193
|
+
const isIndented = line[0] === ' ' || line[0] === '\t';
|
|
1194
|
+
|
|
1195
|
+
// Skip deep-indented lines (annotations, descriptions under steps)
|
|
1196
|
+
// but keep singly-indented lines (steps within phases)
|
|
1197
|
+
if (isIndented) {
|
|
1198
|
+
// Annotation/description keywords — skip
|
|
1199
|
+
if (/^(pain|opportunity|thought|description)\s*:/i.test(trimmed))
|
|
1200
|
+
continue;
|
|
1201
|
+
// Tag group entries — skip
|
|
1202
|
+
if (/^\S+\([^)]+\)/.test(trimmed)) continue;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// Phase header
|
|
1206
|
+
const phaseMatch = trimmed.match(/^\[(.+?)\]$/);
|
|
1207
|
+
if (phaseMatch) {
|
|
1208
|
+
entities.push(phaseMatch[1].trim());
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// Step label (strip pipe metadata) — works for both indent 0 and indented steps
|
|
1213
|
+
const label = trimmed.split('|')[0].trim();
|
|
1214
|
+
if (label && !entities.includes(label)) entities.push(label);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return {
|
|
1218
|
+
kind: 'journey-map',
|
|
1219
|
+
entities,
|
|
1220
|
+
keywords: [
|
|
1221
|
+
'persona',
|
|
1222
|
+
'no-legend',
|
|
1223
|
+
'pain',
|
|
1224
|
+
'opportunity',
|
|
1225
|
+
'thought',
|
|
1226
|
+
'description',
|
|
1227
|
+
],
|
|
1228
|
+
};
|
|
1229
|
+
}
|