@diagrammo/dgmo 0.47.0 → 0.48.1
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.js +11 -11
- package/dist/auto.js +16 -16
- package/dist/auto.mjs +41 -12
- package/dist/block.js +4 -4
- package/dist/{chunk-FNB3PNGF.js → chunk-7DTWEFUE.js} +3 -3
- package/dist/{chunk-2DXMZRWJ.js → chunk-B65G5DNL.js} +8 -1
- package/dist/{chunk-RZCF2VVM.js → chunk-B7JUCQUA.js} +3 -1
- package/dist/{chunk-DU7W62N5.js → chunk-FANX6UJR.js} +3 -3
- package/dist/{chunk-6FZYCKXB.js → chunk-HSH73DP6.js} +2 -2
- package/dist/{chunk-P4RH4OSP.js → chunk-ILKORZ3H.js} +2 -2
- package/dist/{chunk-Y5QGVTVC.js → chunk-JTSMEYIZ.js} +7 -3
- package/dist/{chunk-RCERWOJR.js → chunk-ODZVHU3R.js} +1 -2
- package/dist/{chunk-DZAQBW35.js → chunk-SGZYXELT.js} +3 -7
- package/dist/{chunk-CUVHQHZ7.js → chunk-THXCFRTU.js} +3 -3
- package/dist/{chunk-IH6ZMVQ5.js → chunk-UC7TFHUM.js} +29 -11
- package/dist/{chunk-EMS2SK26.js → chunk-ZBWMSD5N.js} +3 -1
- package/dist/cli.cjs +15 -15
- package/dist/{dgmo-router-DZCIDUO5.js → dgmo-router-X35VLZLM.js} +3 -3
- package/dist/element.js +16 -16
- package/dist/element.mjs +41 -12
- package/dist/{flowchart-renderer-ZAEOL4DX.js → flowchart-renderer-SNDSII4D.js} +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -4
- package/dist/{parser-FUSKFGZN.js → parser-2EWECDSA.js} +2 -2
- package/dist/{renderer-UWA5XS74.js → renderer-4JHRUBE6.js} +3 -3
- package/dist/{renderer-WZRREQ2P.js → renderer-5MYBMBYO.js} +2 -2
- package/dist/{renderer-TOCOVLHS.js → renderer-B2OMQIQG.js} +3 -3
- package/dist/{renderer-2XQQQ6QR.js → renderer-N43WR5GV.js} +3 -3
- package/dist/{renderer-T7YKC5VP.js → renderer-ZRNG54CW.js} +3 -3
- package/dist/{state-renderer-RFYHSP4K.js → state-renderer-B4DQBM6M.js} +2 -2
- package/package.json +23 -71
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Shared text measurement
|
|
3
|
-
// ============================================================
|
|
4
|
-
// One source of truth for "how wide is this string?" Before this
|
|
5
|
-
// module, ~30 renderers/layouts each carried their own char-width
|
|
6
|
-
// estimate (7.5px, 7px, 0.6·fontSize, 0.58, 0.55 …). The drift meant
|
|
7
|
-
// the same label measured differently per chart type — a label could
|
|
8
|
-
// fit inside an org node but overflow an infra node. All sizing,
|
|
9
|
-
// wrapping, and truncation now route through the per-glyph table here.
|
|
10
|
-
|
|
11
|
-
// Helvetica character width ratios (fraction of fontSize). Replaces the
|
|
12
|
-
// naive `chars * 0.6 * fontSize` estimate with per-character widths.
|
|
13
|
-
// prettier-ignore
|
|
14
|
-
const CHAR_W: Record<string, number> = {
|
|
15
|
-
' ':.28,'!': .28,'"': .36,'#': .56,'$': .56,'%': .89,'&': .67,"'":.19,
|
|
16
|
-
'(':.33,')':.33,'*': .39,'+':.58,',':.28,'-':.33,'.':.28,'/':.28,
|
|
17
|
-
'0':.56,'1':.56,'2':.56,'3':.56,'4':.56,'5':.56,'6':.56,'7':.56,'8':.56,'9':.56,
|
|
18
|
-
':':.28,';':.28,'<':.58,'=':.58,'>':.58,'?':.56,'@':1.02,
|
|
19
|
-
A:.67,B:.67,C:.72,D:.72,E:.67,F:.61,G:.78,H:.72,I:.28,J:.50,K:.67,L:.56,M:.83,
|
|
20
|
-
N:.72,O:.78,P:.67,Q:.78,R:.72,S:.67,T:.61,U:.72,V:.67,W:.94,X:.67,Y:.67,Z:.61,
|
|
21
|
-
a:.56,b:.56,c:.50,d:.56,e:.56,f:.28,g:.56,h:.56,i:.22,j:.22,k:.50,l:.22,m:.83,
|
|
22
|
-
n:.56,o:.56,p:.56,q:.56,r:.33,s:.50,t:.28,u:.56,v:.50,w:.72,x:.50,y:.50,z:.50,
|
|
23
|
-
};
|
|
24
|
-
const DEFAULT_W = 0.56;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Average glyph-width ratio (fraction of fontSize). Only for the rare
|
|
28
|
-
* call site that needs a single scalar (e.g. estimating a max char count
|
|
29
|
-
* up front). Prefer {@link measureText} / {@link wrapTextToWidth} —
|
|
30
|
-
* they account for actual glyph widths.
|
|
31
|
-
*/
|
|
32
|
-
export const CHAR_WIDTH_RATIO = DEFAULT_W;
|
|
33
|
-
|
|
34
|
-
/** Estimate rendered text width using Helvetica proportional character widths. */
|
|
35
|
-
export function measureText(text: string, fontSize: number): number {
|
|
36
|
-
let w = 0;
|
|
37
|
-
for (let i = 0; i < text.length; i++) {
|
|
38
|
-
// charAt returns '' for out-of-bounds, never undefined.
|
|
39
|
-
w += (CHAR_W[text.charAt(i)] ?? DEFAULT_W) * fontSize;
|
|
40
|
-
}
|
|
41
|
-
return w;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Truncate text with a trailing ellipsis to fit within maxWidth.
|
|
46
|
-
* Returns the original text if it already fits, or '' if even the
|
|
47
|
-
* ellipsis alone won't fit.
|
|
48
|
-
*/
|
|
49
|
-
export function truncateText(
|
|
50
|
-
text: string,
|
|
51
|
-
fontSize: number,
|
|
52
|
-
maxWidth: number
|
|
53
|
-
): string {
|
|
54
|
-
if (measureText(text, fontSize) <= maxWidth) return text;
|
|
55
|
-
const ellipsis = '…';
|
|
56
|
-
const ellipsisW = measureText(ellipsis, fontSize);
|
|
57
|
-
if (ellipsisW > maxWidth) return '';
|
|
58
|
-
let lo = 0;
|
|
59
|
-
let hi = text.length;
|
|
60
|
-
while (lo < hi) {
|
|
61
|
-
const mid = Math.ceil((lo + hi) / 2);
|
|
62
|
-
if (measureText(text.slice(0, mid), fontSize) + ellipsisW <= maxWidth) {
|
|
63
|
-
lo = mid;
|
|
64
|
-
} else {
|
|
65
|
-
hi = mid - 1;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return lo === 0 ? ellipsis : text.slice(0, lo) + ellipsis;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Greedy word-wrap to a pixel width using accurate glyph measurement.
|
|
73
|
-
*
|
|
74
|
-
* - Splits on whitespace; a word that alone exceeds `maxWidth` is kept
|
|
75
|
-
* whole unless `hardBreak` is set, in which case it is broken at the
|
|
76
|
-
* character boundary that fits.
|
|
77
|
-
* - Returns at least one line (`['']` for empty input).
|
|
78
|
-
*/
|
|
79
|
-
export function wrapTextToWidth(
|
|
80
|
-
text: string,
|
|
81
|
-
fontSize: number,
|
|
82
|
-
maxWidth: number,
|
|
83
|
-
opts?: { hardBreak?: boolean }
|
|
84
|
-
): string[] {
|
|
85
|
-
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
86
|
-
if (words.length === 0) return [''];
|
|
87
|
-
const hardBreak = opts?.hardBreak ?? false;
|
|
88
|
-
const lines: string[] = [];
|
|
89
|
-
let current = '';
|
|
90
|
-
const pushWord = (word: string) => {
|
|
91
|
-
const test = current ? `${current} ${word}` : word;
|
|
92
|
-
if (measureText(test, fontSize) <= maxWidth || !current) {
|
|
93
|
-
current = test;
|
|
94
|
-
} else {
|
|
95
|
-
lines.push(current);
|
|
96
|
-
current = word;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
for (const word of words) {
|
|
100
|
-
if (hardBreak && measureText(word, fontSize) > maxWidth) {
|
|
101
|
-
// Break the over-long word into width-fitting chunks.
|
|
102
|
-
if (current) {
|
|
103
|
-
lines.push(current);
|
|
104
|
-
current = '';
|
|
105
|
-
}
|
|
106
|
-
let chunk = '';
|
|
107
|
-
for (const ch of word) {
|
|
108
|
-
if (chunk && measureText(chunk + ch, fontSize) > maxWidth) {
|
|
109
|
-
lines.push(chunk);
|
|
110
|
-
chunk = ch;
|
|
111
|
-
} else {
|
|
112
|
-
chunk += ch;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
current = chunk;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
pushWord(word);
|
|
119
|
-
}
|
|
120
|
-
if (current) lines.push(current);
|
|
121
|
-
return lines.length > 0 ? lines : [''];
|
|
122
|
-
}
|
package/src/utils/time-ticks.ts
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Time axis tick computation — shared by d3.ts and gantt/renderer.ts
|
|
3
|
-
// ============================================================
|
|
4
|
-
|
|
5
|
-
import type * as d3Scale from 'd3-scale';
|
|
6
|
-
|
|
7
|
-
export const MONTH_ABBR = [
|
|
8
|
-
'Jan',
|
|
9
|
-
'Feb',
|
|
10
|
-
'Mar',
|
|
11
|
-
'Apr',
|
|
12
|
-
'May',
|
|
13
|
-
'Jun',
|
|
14
|
-
'Jul',
|
|
15
|
-
'Aug',
|
|
16
|
-
'Sep',
|
|
17
|
-
'Oct',
|
|
18
|
-
'Nov',
|
|
19
|
-
'Dec',
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
function fractionalYearToDate(frac: number): Date {
|
|
23
|
-
const year = Math.floor(frac);
|
|
24
|
-
const remainder = frac - year;
|
|
25
|
-
// Inverse of: (month-1)/12 + (day-1)/365 + hour/8760 + minute/525600 + second/31536000
|
|
26
|
-
const monthFrac = remainder * 12;
|
|
27
|
-
const month = Math.floor(monthFrac); // 0-based
|
|
28
|
-
const monthRemainder = remainder - month / 12;
|
|
29
|
-
const dayFrac = monthRemainder * 365; // fractional day-of-year offset
|
|
30
|
-
const day = Math.floor(dayFrac) + 1;
|
|
31
|
-
const dayRemainder = dayFrac - Math.floor(dayFrac);
|
|
32
|
-
const hourFrac = dayRemainder * 24;
|
|
33
|
-
const hour = Math.floor(hourFrac);
|
|
34
|
-
const minuteFrac = (hourFrac - hour) * 60;
|
|
35
|
-
const minute = Math.floor(minuteFrac);
|
|
36
|
-
const second = Math.round((minuteFrac - minute) * 60);
|
|
37
|
-
return new Date(year, month, day, hour, minute, second);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Convert a Date to a fractional year number. */
|
|
41
|
-
function dateToFractionalYear(d: Date): number {
|
|
42
|
-
return (
|
|
43
|
-
d.getFullYear() +
|
|
44
|
-
d.getMonth() / 12 +
|
|
45
|
-
(d.getDate() - 1) / 365 +
|
|
46
|
-
d.getHours() / 8760 +
|
|
47
|
-
d.getMinutes() / 525600 +
|
|
48
|
-
d.getSeconds() / 31536000
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Year axis label: negative years render as `N BCE`. */
|
|
53
|
-
function formatYearTick(y: number): string {
|
|
54
|
-
return y < 0 ? `${-y} BCE` : String(y);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Generates adaptive tick marks along a time axis.
|
|
59
|
-
* Picks the right granularity (years, months, weeks, days, hours, minutes)
|
|
60
|
-
* based on the domain span.
|
|
61
|
-
*
|
|
62
|
-
* Optional boundary parameters add ticks at exact data start/end:
|
|
63
|
-
* - boundaryStart/boundaryEnd: numeric date values
|
|
64
|
-
* - boundaryStartLabel/boundaryEndLabel: formatted labels for those dates
|
|
65
|
-
*/
|
|
66
|
-
export function computeTimeTicks(
|
|
67
|
-
domainMin: number,
|
|
68
|
-
domainMax: number,
|
|
69
|
-
scale: d3Scale.ScaleLinear<number, number>,
|
|
70
|
-
boundaryStart?: number,
|
|
71
|
-
boundaryEnd?: number,
|
|
72
|
-
boundaryStartLabel?: string,
|
|
73
|
-
boundaryEndLabel?: string
|
|
74
|
-
): { pos: number; label: string }[] {
|
|
75
|
-
const minYear = Math.floor(domainMin);
|
|
76
|
-
const maxYear = Math.floor(domainMax);
|
|
77
|
-
const span = domainMax - domainMin;
|
|
78
|
-
|
|
79
|
-
let ticks: { pos: number; label: string }[] = [];
|
|
80
|
-
|
|
81
|
-
// Year ticks for multi-year spans (need at least 2 boundaries)
|
|
82
|
-
const firstYear = Math.ceil(domainMin);
|
|
83
|
-
const lastYear = Math.floor(domainMax);
|
|
84
|
-
if (lastYear >= firstYear + 1) {
|
|
85
|
-
// Decimate ticks for long spans so labels don't overlap. The ladder runs
|
|
86
|
-
// up to century/bicentury steps so millennium-scale spans (and BCE labels,
|
|
87
|
-
// which are ~2× wider) stay legible.
|
|
88
|
-
const yearSpan = lastYear - firstYear;
|
|
89
|
-
let step = 1;
|
|
90
|
-
if (yearSpan > 800) step = 200;
|
|
91
|
-
else if (yearSpan > 400) step = 100;
|
|
92
|
-
else if (yearSpan > 160) step = 50;
|
|
93
|
-
else if (yearSpan > 80) step = 20;
|
|
94
|
-
else if (yearSpan > 40) step = 10;
|
|
95
|
-
else if (yearSpan > 20) step = 5;
|
|
96
|
-
else if (yearSpan > 10) step = 2;
|
|
97
|
-
|
|
98
|
-
// Align to step boundary so ticks land on round years (1700, 1710, …)
|
|
99
|
-
const alignedFirst = Math.ceil(firstYear / step) * step;
|
|
100
|
-
for (let y = alignedFirst; y <= lastYear; y += step) {
|
|
101
|
-
ticks.push({ pos: scale(y), label: formatYearTick(y) });
|
|
102
|
-
}
|
|
103
|
-
} else if (span > 0.25) {
|
|
104
|
-
// Month ticks for spans > ~3 months
|
|
105
|
-
const crossesYear = maxYear > minYear;
|
|
106
|
-
for (let y = minYear; y <= maxYear + 1; y++) {
|
|
107
|
-
for (let m = 1; m <= 12; m++) {
|
|
108
|
-
const val = y + (m - 1) / 12;
|
|
109
|
-
if (val > domainMax) break;
|
|
110
|
-
if (val >= domainMin) {
|
|
111
|
-
// m is 1..12, so MONTH_ABBR[m-1] is always defined.
|
|
112
|
-
const abbr = MONTH_ABBR[m - 1]!;
|
|
113
|
-
ticks.push({
|
|
114
|
-
pos: scale(val),
|
|
115
|
-
label: crossesYear ? `${abbr} '${String(y).slice(-2)}` : abbr,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} else if (span <= 0.0000114) {
|
|
121
|
-
// Second ticks for spans ≤ ~6 minutes
|
|
122
|
-
// Adaptive step: >3min → 30s, >1min → 15s, >30s → 10s, else 5s
|
|
123
|
-
let stepSec = 5;
|
|
124
|
-
const spanSec = span * 31536000;
|
|
125
|
-
if (spanSec > 180) stepSec = 30;
|
|
126
|
-
else if (spanSec > 60) stepSec = 15;
|
|
127
|
-
else if (spanSec > 30) stepSec = 10;
|
|
128
|
-
|
|
129
|
-
const startDate = fractionalYearToDate(domainMin);
|
|
130
|
-
// Round down to nearest step boundary
|
|
131
|
-
startDate.setSeconds(
|
|
132
|
-
Math.floor(startDate.getSeconds() / stepSec) * stepSec,
|
|
133
|
-
0
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
while (true) {
|
|
137
|
-
const val = dateToFractionalYear(startDate);
|
|
138
|
-
if (val > domainMax) break;
|
|
139
|
-
if (val >= domainMin) {
|
|
140
|
-
const hh = String(startDate.getHours()).padStart(2, '0');
|
|
141
|
-
const mm = String(startDate.getMinutes()).padStart(2, '0');
|
|
142
|
-
const ss = String(startDate.getSeconds()).padStart(2, '0');
|
|
143
|
-
ticks.push({ pos: scale(val), label: `${hh}:${mm}:${ss}` });
|
|
144
|
-
}
|
|
145
|
-
startDate.setSeconds(startDate.getSeconds() + stepSec);
|
|
146
|
-
}
|
|
147
|
-
} else if (span <= 0.000685) {
|
|
148
|
-
// Minute ticks for spans ≤ ~6 hours
|
|
149
|
-
// Adaptive step: >3h → 30min, >1h → 15min, >30min → 10min, else 5min
|
|
150
|
-
let stepMin = 5;
|
|
151
|
-
const spanHours = span * 8760;
|
|
152
|
-
if (spanHours > 3) stepMin = 30;
|
|
153
|
-
else if (spanHours > 1) stepMin = 15;
|
|
154
|
-
else if (spanHours > 0.5) stepMin = 10;
|
|
155
|
-
|
|
156
|
-
// Iterate from the start hour boundary
|
|
157
|
-
const startDate = fractionalYearToDate(domainMin);
|
|
158
|
-
// Round down to nearest step boundary
|
|
159
|
-
startDate.setMinutes(
|
|
160
|
-
Math.floor(startDate.getMinutes() / stepMin) * stepMin,
|
|
161
|
-
0,
|
|
162
|
-
0
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
while (true) {
|
|
166
|
-
const val = dateToFractionalYear(startDate);
|
|
167
|
-
if (val > domainMax) break;
|
|
168
|
-
if (val >= domainMin) {
|
|
169
|
-
const hh = String(startDate.getHours()).padStart(2, '0');
|
|
170
|
-
const mm = String(startDate.getMinutes()).padStart(2, '0');
|
|
171
|
-
ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
|
|
172
|
-
}
|
|
173
|
-
startDate.setMinutes(startDate.getMinutes() + stepMin);
|
|
174
|
-
}
|
|
175
|
-
} else if (span <= 0.00822) {
|
|
176
|
-
// Hour ticks for spans ≤ ~3 days
|
|
177
|
-
// Adaptive step: >2d → 6h, >1d → 3h, >12h → 2h, else 1h
|
|
178
|
-
let stepHour = 1;
|
|
179
|
-
const spanHours = span * 8760;
|
|
180
|
-
if (spanHours > 48) stepHour = 6;
|
|
181
|
-
else if (spanHours > 24) stepHour = 3;
|
|
182
|
-
else if (spanHours > 12) stepHour = 2;
|
|
183
|
-
|
|
184
|
-
// For single-day spans, just show HH:MM without the date prefix
|
|
185
|
-
const singleDay = spanHours <= 24;
|
|
186
|
-
|
|
187
|
-
const startDate = fractionalYearToDate(domainMin);
|
|
188
|
-
// Round down to nearest step boundary
|
|
189
|
-
startDate.setHours(
|
|
190
|
-
Math.floor(startDate.getHours() / stepHour) * stepHour,
|
|
191
|
-
0,
|
|
192
|
-
0,
|
|
193
|
-
0
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
while (true) {
|
|
197
|
-
const val = dateToFractionalYear(startDate);
|
|
198
|
-
if (val > domainMax) break;
|
|
199
|
-
if (val >= domainMin) {
|
|
200
|
-
const hh = String(startDate.getHours()).padStart(2, '0');
|
|
201
|
-
const mm = String(startDate.getMinutes()).padStart(2, '0');
|
|
202
|
-
if (singleDay) {
|
|
203
|
-
ticks.push({ pos: scale(val), label: `${hh}:${mm}` });
|
|
204
|
-
} else {
|
|
205
|
-
const mon = MONTH_ABBR[startDate.getMonth()];
|
|
206
|
-
const d = startDate.getDate();
|
|
207
|
-
ticks.push({ pos: scale(val), label: `${mon} ${d} ${hh}:${mm}` });
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
startDate.setHours(startDate.getHours() + stepHour);
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
// Week ticks for spans ≤ ~3 months (1st, 8th, 15th, 22nd of each month)
|
|
214
|
-
for (let y = minYear; y <= maxYear + 1; y++) {
|
|
215
|
-
for (let m = 1; m <= 12; m++) {
|
|
216
|
-
for (const d of [1, 8, 15, 22]) {
|
|
217
|
-
const val = y + (m - 1) / 12 + (d - 1) / 365;
|
|
218
|
-
if (val > domainMax) break;
|
|
219
|
-
if (val >= domainMin) {
|
|
220
|
-
ticks.push({
|
|
221
|
-
pos: scale(val),
|
|
222
|
-
label: `${MONTH_ABBR[m - 1]} ${d}`,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Add boundary ticks at exact data start/end if provided
|
|
231
|
-
// When a boundary tick collides with a standard tick, replace the standard tick
|
|
232
|
-
const collisionThreshold = 40; // pixels
|
|
233
|
-
|
|
234
|
-
if (boundaryStart !== undefined && boundaryStartLabel) {
|
|
235
|
-
const boundaryPos = scale(boundaryStart);
|
|
236
|
-
// Remove any standard ticks that would collide with the start boundary
|
|
237
|
-
ticks = ticks.filter(
|
|
238
|
-
(t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
|
|
239
|
-
);
|
|
240
|
-
ticks.unshift({ pos: boundaryPos, label: boundaryStartLabel });
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (boundaryEnd !== undefined && boundaryEndLabel) {
|
|
244
|
-
const boundaryPos = scale(boundaryEnd);
|
|
245
|
-
// Remove any standard ticks that would collide with the end boundary
|
|
246
|
-
ticks = ticks.filter(
|
|
247
|
-
(t) => Math.abs(t.pos - boundaryPos) >= collisionThreshold
|
|
248
|
-
);
|
|
249
|
-
ticks.push({ pos: boundaryPos, label: boundaryEndLabel });
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return ticks;
|
|
253
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Shared chart title constants
|
|
3
|
-
// All renderers import from here to stay in sync.
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
export const TITLE_FONT_SIZE = 20;
|
|
7
|
-
export const TITLE_FONT_WEIGHT = '700';
|
|
8
|
-
export const TITLE_Y = 30;
|
|
9
|
-
export const TITLE_OFFSET = 40;
|
|
10
|
-
|
|
11
|
-
// Caption — supplementary banner text. The pert renderer wraps caption
|
|
12
|
-
// content in a node-styled rectangle below the diagram body; backward-
|
|
13
|
-
// anchor annotations still render as a free-floating italic line below
|
|
14
|
-
// the title. Pixel `dy` (not `'1.2em'`) so layout math and rendered text
|
|
15
|
-
// never drift between JSDOM and resvg.
|
|
16
|
-
export const CAPTION_FONT_SIZE = 13;
|
|
17
|
-
export const CAPTION_FONT_WEIGHT = '400';
|
|
18
|
-
export const CAPTION_LINE_HEIGHT = 18;
|
|
19
|
-
export const CAPTION_TOP_GAP = 12;
|
|
20
|
-
// Inner padding for the caption rectangle. X = horizontal inset from
|
|
21
|
-
// rect edge to bullet glyph; Y = vertical inset from rect top to first
|
|
22
|
-
// baseline.
|
|
23
|
-
export const CAPTION_BOX_PADDING_X = 16;
|
|
24
|
-
export const CAPTION_BOX_PADDING_Y = 12;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Visual conventions — shared card/group/collapse constants (Story 111.1)
|
|
3
|
-
// ============================================================
|
|
4
|
-
//
|
|
5
|
-
// Single source of truth for the structured-diagram visual constants described
|
|
6
|
-
// in docs/architecture/diagram-visual-conventions.md. Before this module these
|
|
7
|
-
// values were re-declared in every renderer (NODE_STROKE_WIDTH in 13 files,
|
|
8
|
-
// HEADER_HEIGHT in 12, ...), so a convention change meant editing N files and
|
|
9
|
-
// hoping the snapshots agreed.
|
|
10
|
-
//
|
|
11
|
-
// Two tiers, because the renderers do NOT all agree:
|
|
12
|
-
//
|
|
13
|
-
// 1. UNIVERSAL — identical at every call site. Import these everywhere; never
|
|
14
|
-
// re-declare. Changing one of these is a deliberate cross-chart change.
|
|
15
|
-
//
|
|
16
|
-
// 2. CONVENTION DEFAULTS — the org/sitemap baseline from the convention. Most
|
|
17
|
-
// card-shaped renderers match these and should import them. A few have a
|
|
18
|
-
// DOCUMENTED intentional deviation and keep a local override (with a
|
|
19
|
-
// comment saying why):
|
|
20
|
-
// - infra: META_FONT_SIZE 10 / META_LINE_HEIGHT 14 (denser meta rows)
|
|
21
|
-
// - boxes-and-lines: COLLAPSE_BAR_HEIGHT 4 / SEPARATOR_GAP 4
|
|
22
|
-
// - cycle, raci: HEADER_HEIGHT 36 (not label+meta cards)
|
|
23
|
-
// Those overrides are the exception that proves the rule — they stay local
|
|
24
|
-
// and visible, not hidden inside a re-declared full constant set.
|
|
25
|
-
|
|
26
|
-
// ── Universal (same value at every site) ──
|
|
27
|
-
|
|
28
|
-
/** Node card + group border stroke width. */
|
|
29
|
-
export const NODE_STROKE_WIDTH = 1.5;
|
|
30
|
-
/** Edge / connector stroke width. */
|
|
31
|
-
export const EDGE_STROKE_WIDTH = 1.5;
|
|
32
|
-
/** Node card corner radius. */
|
|
33
|
-
export const CARD_RADIUS = 6;
|
|
34
|
-
/** Group / container corner radius. */
|
|
35
|
-
export const CONTAINER_RADIUS = 8;
|
|
36
|
-
/** Collapse accent bar horizontal inset from the card edges. */
|
|
37
|
-
export const COLLAPSE_BAR_INSET = 0;
|
|
38
|
-
|
|
39
|
-
// ── Convention defaults (org/sitemap baseline; see deviations above) ──
|
|
40
|
-
|
|
41
|
-
/** Node card header band height. */
|
|
42
|
-
export const HEADER_HEIGHT = 28;
|
|
43
|
-
/** Node card label font size. */
|
|
44
|
-
export const LABEL_FONT_SIZE = 13;
|
|
45
|
-
/** Node card metadata-row font size. */
|
|
46
|
-
export const META_FONT_SIZE = 11;
|
|
47
|
-
/** Node card metadata-row line height. */
|
|
48
|
-
export const META_LINE_HEIGHT = 16;
|
|
49
|
-
/** Gap between the header separator and the first metadata row. */
|
|
50
|
-
export const SEPARATOR_GAP = 6;
|
|
51
|
-
/** Collapse accent bar height. */
|
|
52
|
-
export const COLLAPSE_BAR_HEIGHT = 6;
|
|
53
|
-
|
|
54
|
-
/** Group / container reserved header band height. */
|
|
55
|
-
export const CONTAINER_HEADER_HEIGHT = 28;
|
|
56
|
-
/** Group / container label font size. */
|
|
57
|
-
export const CONTAINER_LABEL_FONT_SIZE = 13;
|
|
58
|
-
/** Group / container metadata-row font size. */
|
|
59
|
-
export const CONTAINER_META_FONT_SIZE = 11;
|
|
60
|
-
/** Group / container metadata-row line height. */
|
|
61
|
-
export const CONTAINER_META_LINE_HEIGHT = 16;
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Wrapped Description — shared bullet-aware text wrapping
|
|
3
|
-
// ============================================================
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* One rendered description line. `kind` controls horizontal placement and
|
|
7
|
-
* whether the renderer draws a bullet glyph:
|
|
8
|
-
* - `plain` — flush left at the description's left edge
|
|
9
|
-
* - `bullet-first` — "•" drawn at the left edge, body text at the bullet column
|
|
10
|
-
* - `bullet-cont` — body continuation at the bullet column (no glyph)
|
|
11
|
-
*
|
|
12
|
-
* Splitting first-line bullet rendering into separate text elements lets
|
|
13
|
-
* continuation lines align exactly under the first word past the bullet,
|
|
14
|
-
* regardless of font-width estimation drift.
|
|
15
|
-
*/
|
|
16
|
-
export interface WrappedDescLine {
|
|
17
|
-
text: string;
|
|
18
|
-
kind: 'plain' | 'bullet-first' | 'bullet-cont';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Bullet prefix that triggers split-rendering with a hanging body column. */
|
|
22
|
-
export const BULLET_PREFIX = '• ';
|
|
23
|
-
/** Equivalent body-column width in characters (subtracted from wrap width). */
|
|
24
|
-
export const BULLET_INDENT_CHARS = 2;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Optional measurer — returns the *display* length of a string in characters.
|
|
28
|
-
* Defaults to `s.length`. Pass a markdown-aware function (stripping link
|
|
29
|
-
* targets, emphasis markers, etc.) when wrapping rich text so the wrap
|
|
30
|
-
* boundary matches what the user will actually see.
|
|
31
|
-
*/
|
|
32
|
-
export type LengthFn = (s: string) => number;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Word-wrap description lines. Bullet lines (starting with "• ") are split
|
|
36
|
-
* so the renderer can place the "•" glyph at the description's left edge and
|
|
37
|
-
* the body text at a fixed bullet-body column — continuation lines align
|
|
38
|
-
* exactly under the first word past the bullet without depending on
|
|
39
|
-
* font-width estimation.
|
|
40
|
-
*/
|
|
41
|
-
export function wrapDescriptionLines(
|
|
42
|
-
lines: string[],
|
|
43
|
-
charsPerLine: number,
|
|
44
|
-
lengthFn: LengthFn = (s) => s.length
|
|
45
|
-
): WrappedDescLine[] {
|
|
46
|
-
const result: WrappedDescLine[] = [];
|
|
47
|
-
for (const line of lines) {
|
|
48
|
-
if (!line.startsWith(BULLET_PREFIX)) {
|
|
49
|
-
const wrapped = wrapPlainLine(line, charsPerLine, lengthFn);
|
|
50
|
-
for (const w of wrapped) result.push({ text: w, kind: 'plain' });
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
// Strip "• " — the renderer draws the bullet glyph; we only wrap the body.
|
|
54
|
-
const body = line.slice(BULLET_PREFIX.length);
|
|
55
|
-
const bodyLimit = Math.max(8, charsPerLine - BULLET_INDENT_CHARS);
|
|
56
|
-
const wrapped = wrapPlainLine(body, bodyLimit, lengthFn);
|
|
57
|
-
wrapped.forEach((w, i) => {
|
|
58
|
-
result.push({ text: w, kind: i === 0 ? 'bullet-first' : 'bullet-cont' });
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Greedy word-wrap of a single string at the given character limit.
|
|
66
|
-
* Long words are kept whole even if they exceed the limit (no mid-word break).
|
|
67
|
-
*/
|
|
68
|
-
function wrapPlainLine(
|
|
69
|
-
line: string,
|
|
70
|
-
charsPerLine: number,
|
|
71
|
-
lengthFn: LengthFn
|
|
72
|
-
): string[] {
|
|
73
|
-
const words = line.split(/\s+/);
|
|
74
|
-
const result: string[] = [];
|
|
75
|
-
let current = '';
|
|
76
|
-
for (const word of words) {
|
|
77
|
-
const test = current ? `${current} ${word}` : word;
|
|
78
|
-
if (lengthFn(test) > charsPerLine && current) {
|
|
79
|
-
result.push(current);
|
|
80
|
-
current = word;
|
|
81
|
-
} else {
|
|
82
|
-
current = test;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (current) result.push(current);
|
|
86
|
-
return result;
|
|
87
|
-
}
|
package/src/venn/parser.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// venn parser door — Story 109.2 (arch-review).
|
|
3
|
-
//
|
|
4
|
-
// The six visualizations share one parse state machine (visualizations/parse.ts);
|
|
5
|
-
// this is venn's typed entry point into it, returning the narrowed
|
|
6
|
-
// ParsedVenn shape. The registry binds the 'venn' chart type to this door.
|
|
7
|
-
// ============================================================
|
|
8
|
-
|
|
9
|
-
import type { PaletteColors } from '../palettes';
|
|
10
|
-
import { parseVisualization } from '../visualizations/parse';
|
|
11
|
-
import type { ParsedVenn } from '../visualizations/types';
|
|
12
|
-
|
|
13
|
-
export function parseVenn(
|
|
14
|
-
content: string,
|
|
15
|
-
palette?: PaletteColors
|
|
16
|
-
): ParsedVenn {
|
|
17
|
-
return parseVisualization(content, palette) as ParsedVenn;
|
|
18
|
-
}
|