@bendyline/squisq 1.5.2 → 2.0.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/README.md +63 -10
- package/dist/{Doc-BBVGq1_9.d.ts → Doc-ylqWKDc_.d.ts} +248 -37
- package/dist/{ImageEditDoc-FuyGtt6o.d.ts → ImageEditDoc-D828IXJ1.d.ts} +1 -1
- package/dist/annotationCoercion-Dr_DY9Hx.d.ts +86 -0
- package/dist/{chunk-VWUFZ6ZG.js → chunk-3E5F2XMR.js} +2 -29
- package/dist/chunk-3E5F2XMR.js.map +1 -0
- package/dist/chunk-4X3JQXNM.js +35 -0
- package/dist/chunk-4X3JQXNM.js.map +1 -0
- package/dist/{chunk-P3K7NLHF.js → chunk-5DKBFXPY.js} +31 -121
- package/dist/chunk-5DKBFXPY.js.map +1 -0
- package/dist/{chunk-DLXZMS5K.js → chunk-5J4HKRJF.js} +51 -3
- package/dist/chunk-5J4HKRJF.js.map +1 -0
- package/dist/chunk-5UYI3RJB.js +2503 -0
- package/dist/chunk-5UYI3RJB.js.map +1 -0
- package/dist/{chunk-BEQODJWV.js → chunk-66QO7XFB.js} +11 -291
- package/dist/chunk-66QO7XFB.js.map +1 -0
- package/dist/{chunk-KKNUBQ6Y.js → chunk-6PJRB2CF.js} +156 -91
- package/dist/chunk-6PJRB2CF.js.map +1 -0
- package/dist/{chunk-HWVFJAAH.js → chunk-AMIR72JP.js} +28 -10
- package/dist/chunk-AMIR72JP.js.map +1 -0
- package/dist/chunk-FVJUUTEM.js +74 -0
- package/dist/chunk-FVJUUTEM.js.map +1 -0
- package/dist/{chunk-5TYS5V3G.js → chunk-GF3BVNLU.js} +4127 -3538
- package/dist/chunk-GF3BVNLU.js.map +1 -0
- package/dist/{chunk-Q3ROPT5H.js → chunk-GZY66SDN.js} +1 -1
- package/dist/{chunk-Q3ROPT5H.js.map → chunk-GZY66SDN.js.map} +1 -1
- package/dist/chunk-JOQYPRDZ.js +127 -0
- package/dist/chunk-JOQYPRDZ.js.map +1 -0
- package/dist/{chunk-A4O7GIWE.js → chunk-LYNJEOAC.js} +10 -73
- package/dist/chunk-LYNJEOAC.js.map +1 -0
- package/dist/{chunk-LDQ2HJIX.js → chunk-PXLVXTLW.js} +252 -54
- package/dist/chunk-PXLVXTLW.js.map +1 -0
- package/dist/{chunk-TQWLI6S2.js → chunk-Q7LROV2H.js} +4 -2
- package/dist/{chunk-TQWLI6S2.js.map → chunk-Q7LROV2H.js.map} +1 -1
- package/dist/{chunk-T5UK6YOB.js → chunk-QTSJOY54.js} +60 -17
- package/dist/chunk-QTSJOY54.js.map +1 -0
- package/dist/chunk-QVANWDPH.js +2358 -0
- package/dist/chunk-QVANWDPH.js.map +1 -0
- package/dist/{chunk-GPLTCSXO.js → chunk-R2GC6RLD.js} +7 -7
- package/dist/chunk-R2GC6RLD.js.map +1 -0
- package/dist/chunk-R2YZTCLO.js +292 -0
- package/dist/chunk-R2YZTCLO.js.map +1 -0
- package/dist/chunk-S7A5D7ZJ.js +174 -0
- package/dist/chunk-S7A5D7ZJ.js.map +1 -0
- package/dist/chunk-T3TI7GSZ.js +884 -0
- package/dist/chunk-T3TI7GSZ.js.map +1 -0
- package/dist/{chunk-LTN6I7QW.js → chunk-TUWMK62B.js} +55 -4
- package/dist/chunk-TUWMK62B.js.map +1 -0
- package/dist/{chunk-IMSCRLLV.js → chunk-VR7UPJ7C.js} +35 -25
- package/dist/chunk-VR7UPJ7C.js.map +1 -0
- package/dist/doc/index.d.ts +2334 -5
- package/dist/doc/index.js +127 -33
- package/dist/generate/index.d.ts +7 -2
- package/dist/generate/index.js +1 -1
- package/dist/imageEdit/index.d.ts +9 -6
- package/dist/imageEdit/index.js +3 -3
- package/dist/index.d.ts +12 -10
- package/dist/index.js +246 -80
- package/dist/jsonForm/index.d.ts +7 -3
- package/dist/jsonForm/index.js +9 -4
- package/dist/markdown/index.d.ts +6 -70
- package/dist/markdown/index.js +13 -9
- package/dist/narration/index.d.ts +618 -0
- package/dist/narration/index.js +82 -0
- package/dist/recommend/index.d.ts +15 -1
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +28 -6
- package/dist/schemas/index.js +15 -13
- package/dist/spatial/index.d.ts +0 -1
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/{themeLibrary-CehcJhzz.d.ts → themeLibrary-RjVgLpUi.d.ts} +10 -11
- package/dist/timing/index.js +5 -3
- package/dist/transform/index.d.ts +35 -24
- package/dist/transform/index.js +9 -10
- package/dist/{types-DlAZ7MW4.d.ts → types-BwMPl2CB.d.ts} +1 -1
- package/dist/{types-D98li1gV.d.ts → types-C4kiRn_k.d.ts} +3 -1
- package/dist/versions/index.d.ts +2 -1
- package/dist/versions/index.js +2 -2
- package/package.json +6 -6
- package/src/__tests__/applyNarrationTiming.test.ts +190 -0
- package/src/__tests__/applyRenderStyle.test.ts +3 -3
- package/src/__tests__/asciiDiagramBattle.test.ts +246 -0
- package/src/__tests__/asciiDiagramDetect.test.ts +162 -0
- package/src/__tests__/asciiDiagramMapping.test.ts +38 -0
- package/src/__tests__/asciiDiagramParse.test.ts +233 -0
- package/src/__tests__/asciiDiagramPipeline.test.ts +273 -0
- package/src/__tests__/asciiDiagramRender.test.ts +162 -0
- package/src/__tests__/asciiDiagramRepair.test.ts +84 -0
- package/src/__tests__/asciiTimelineDetect.test.ts +157 -0
- package/src/__tests__/asciiTimelineParse.test.ts +205 -0
- package/src/__tests__/asciiTimelinePipeline.test.ts +349 -0
- package/src/__tests__/asciiTimelineRender.test.ts +376 -0
- package/src/__tests__/attrTokens.test.ts +5 -0
- package/src/__tests__/contentContainer.test.ts +43 -0
- package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -15
- package/src/__tests__/dataTable.test.ts +25 -15
- package/src/__tests__/diagramBlock.test.ts +3 -3
- package/src/__tests__/diagramText.test.ts +26 -0
- package/src/__tests__/docToMarkdownMedia.test.ts +83 -0
- package/src/__tests__/drawingBlock.test.ts +3 -3
- package/src/__tests__/fixtures/asciiDiagramBattle.ts +512 -0
- package/src/__tests__/fixtures/asciiDiagrams.ts +287 -0
- package/src/__tests__/fixtures/asciiTimelines.ts +35 -0
- package/src/__tests__/fixtures/treeviewFixtures.ts +243 -0
- package/src/__tests__/getLayers.test.ts +32 -18
- package/src/__tests__/getLayersFallback.test.ts +8 -8
- package/src/__tests__/imageEditVersions.test.ts +2 -0
- package/src/__tests__/jsonForm.pointer.test.ts +47 -1
- package/src/__tests__/layoutBlock.test.ts +3 -3
- package/src/__tests__/majorApiTypes.test.ts +18 -0
- package/src/__tests__/markdown.test.ts +41 -0
- package/src/__tests__/matchFontFamily.test.ts +114 -0
- package/src/__tests__/materializeBlockLayers.test.ts +135 -0
- package/src/__tests__/materializeTestUtils.ts +17 -0
- package/src/__tests__/narrationAlign.test.ts +150 -0
- package/src/__tests__/narrationFeatures.test.ts +71 -0
- package/src/__tests__/narrationNuclei.test.ts +45 -0
- package/src/__tests__/narrationPacing.test.ts +189 -0
- package/src/__tests__/narrationScript.test.ts +149 -0
- package/src/__tests__/narrationSessionRepro.test.ts +62 -0
- package/src/__tests__/narrationSidecar.test.ts +88 -0
- package/src/__tests__/narrationSyllables.test.ts +65 -0
- package/src/__tests__/narrationTestSignals.ts +167 -0
- package/src/__tests__/narrationVad.test.ts +66 -0
- package/src/__tests__/resolveDocTheme.test.ts +16 -2
- package/src/__tests__/rootExports.test.ts +18 -0
- package/src/__tests__/scopedContentContainer.test.ts +20 -0
- package/src/__tests__/shapeGeometry.test.ts +12 -0
- package/src/__tests__/templateAnnotationParse.test.ts +10 -0
- package/src/__tests__/templateInputs.test.ts +69 -0
- package/src/__tests__/templateMetadata.test.ts +2 -1
- package/src/__tests__/templates.test.ts +143 -17
- package/src/__tests__/themeCompile.test.ts +49 -22
- package/src/__tests__/transformProvenance.test.ts +160 -0
- package/src/__tests__/transformV2.test.ts +40 -14
- package/src/__tests__/treeviewBattle.test.ts +76 -0
- package/src/__tests__/treeviewDetect.test.ts +168 -0
- package/src/__tests__/treeviewParse.test.ts +118 -0
- package/src/__tests__/treeviewPipeline.test.ts +183 -0
- package/src/__tests__/treeviewRender.test.ts +91 -0
- package/src/__tests__/versions.test.ts +44 -0
- package/src/doc/applyNarrationTiming.ts +244 -0
- package/src/doc/asciiDiagram/chars.ts +210 -0
- package/src/doc/asciiDiagram/detect.ts +175 -0
- package/src/doc/asciiDiagram/index.ts +38 -0
- package/src/doc/asciiDiagram/mapping.ts +225 -0
- package/src/doc/asciiDiagram/parse.ts +1095 -0
- package/src/doc/asciiDiagram/render.ts +963 -0
- package/src/doc/asciiDiagram/repair.ts +400 -0
- package/src/doc/asciiDiagram/types.ts +65 -0
- package/src/doc/asciiTimeline/detect.ts +138 -0
- package/src/doc/asciiTimeline/index.ts +26 -0
- package/src/doc/asciiTimeline/mapping.ts +43 -0
- package/src/doc/asciiTimeline/parse.ts +870 -0
- package/src/doc/asciiTimeline/render.ts +427 -0
- package/src/doc/asciiTimeline/types.ts +73 -0
- package/src/doc/asciiTimeline/wrappedFlow.ts +427 -0
- package/src/doc/audioMapping.ts +60 -46
- package/src/doc/customTemplatesFrontmatter.ts +0 -41
- package/src/doc/docToMarkdown.ts +70 -2
- package/src/doc/index.ts +76 -4
- package/src/doc/markdownToDoc.ts +135 -1
- package/src/doc/materializeBlockLayers.ts +304 -0
- package/src/doc/mediaAnnotations.ts +20 -0
- package/src/doc/resolveDocTheme.ts +8 -7
- package/src/doc/templateInputs.ts +185 -2
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +22 -8
- package/src/doc/templates/diagramBlock.ts +237 -38
- package/src/doc/templates/fallbackBlock.ts +1 -1
- package/src/doc/templates/index.ts +134 -279
- package/src/doc/templates/inputDescriptors.ts +20 -1
- package/src/doc/templates/metadata.ts +10 -0
- package/src/doc/templates/persistentLayers.ts +12 -126
- package/src/doc/templates/registry.ts +88 -0
- package/src/doc/templates/templateNames.ts +22 -0
- package/src/doc/templates/timelineBlock.ts +613 -0
- package/src/doc/templates/treeBlock.ts +131 -0
- package/src/doc/treeview/chars.ts +72 -0
- package/src/doc/treeview/detect.ts +145 -0
- package/src/doc/treeview/index.ts +30 -0
- package/src/doc/treeview/mapping.ts +95 -0
- package/src/doc/treeview/parse.ts +201 -0
- package/src/doc/treeview/render.ts +41 -0
- package/src/doc/treeview/types.ts +43 -0
- package/src/doc/utils/applyRenderStyle.ts +2 -9
- package/src/doc/utils/diagramText.ts +78 -0
- package/src/doc/utils/shapeGeometry.ts +67 -5
- package/src/doc/validate.ts +1 -1
- package/src/generate/templateMapper.ts +7 -0
- package/src/imageEdit/export.ts +6 -5
- package/src/imageEdit/versionPaths.ts +13 -26
- package/src/imageEdit/versions.ts +21 -100
- package/src/index.ts +1 -0
- package/src/internal/immutable.ts +58 -0
- package/src/jsonForm/index.ts +9 -1
- package/src/jsonForm/pointer.ts +68 -23
- package/src/markdown/attrTokens.ts +7 -5
- package/src/markdown/index.ts +1 -0
- package/src/markdown/stringify.ts +4 -9
- package/src/markdown/utils.ts +36 -14
- package/src/narration/align.ts +514 -0
- package/src/narration/features.ts +231 -0
- package/src/narration/index.ts +86 -0
- package/src/narration/nuclei.ts +118 -0
- package/src/narration/pacing.ts +211 -0
- package/src/narration/script.ts +239 -0
- package/src/narration/session.ts +100 -0
- package/src/narration/sidecar.ts +187 -0
- package/src/narration/syllables.ts +85 -0
- package/src/narration/trace.ts +50 -0
- package/src/narration/types.ts +299 -0
- package/src/narration/vad.ts +131 -0
- package/src/recommend/numberHighlight.ts +25 -0
- package/src/recommend/templates.ts +108 -12
- package/src/schemas/BlockTemplates.ts +156 -7
- package/src/schemas/Doc.ts +72 -9
- package/src/schemas/Media.ts +12 -2
- package/src/schemas/Theme.ts +45 -25
- package/src/schemas/Viewport.ts +2 -2
- package/src/schemas/fontStacks.ts +79 -0
- package/src/schemas/themeCompile.ts +15 -0
- package/src/schemas/themeConstants.ts +2 -0
- package/src/schemas/themeLibrary.ts +25 -19
- package/src/schemas/themeValidator.ts +1 -1
- package/src/spatial/Geohash.ts +0 -3
- package/src/storage/ContentContainer.ts +17 -9
- package/src/storage/ScopedContentContainer.ts +45 -15
- package/src/transform/applyTransform.ts +5 -5
- package/src/transform/index.ts +3 -1
- package/src/transform/registry.ts +169 -49
- package/src/transform/templateSelector.ts +21 -2
- package/src/transform/timingAllocator.ts +152 -21
- package/src/transform/types.ts +14 -1
- package/src/versions/gitignore.ts +34 -0
- package/src/versions/index.ts +2 -0
- package/src/versions/operations.ts +18 -104
- package/src/versions/paths.ts +13 -28
- package/src/versions/snapshotStore.ts +202 -0
- package/src/versions/types.ts +3 -1
- package/dist/chunk-5TYS5V3G.js.map +0 -1
- package/dist/chunk-A4O7GIWE.js.map +0 -1
- package/dist/chunk-BEQODJWV.js.map +0 -1
- package/dist/chunk-DLXZMS5K.js.map +0 -1
- package/dist/chunk-GPLTCSXO.js.map +0 -1
- package/dist/chunk-HWVFJAAH.js.map +0 -1
- package/dist/chunk-IMSCRLLV.js.map +0 -1
- package/dist/chunk-JELIRVDP.js +0 -24
- package/dist/chunk-JELIRVDP.js.map +0 -1
- package/dist/chunk-KKNUBQ6Y.js.map +0 -1
- package/dist/chunk-LDQ2HJIX.js.map +0 -1
- package/dist/chunk-LTN6I7QW.js.map +0 -1
- package/dist/chunk-P3K7NLHF.js.map +0 -1
- package/dist/chunk-T5UK6YOB.js.map +0 -1
- package/dist/chunk-UY7KGQ5R.js +0 -192
- package/dist/chunk-UY7KGQ5R.js.map +0 -1
- package/dist/chunk-VLJJHDAC.js +0 -218
- package/dist/chunk-VLJJHDAC.js.map +0 -1
- package/dist/chunk-VWUFZ6ZG.js.map +0 -1
- package/dist/story/index.d.ts +0 -1801
- package/dist/story/index.js +0 -279
- package/src/doc/getLayers.ts +0 -195
- /package/dist/{story → narration}/index.js.map +0 -0
|
@@ -1,35 +1,175 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Diagram block template.
|
|
3
3
|
*
|
|
4
|
-
* Renders
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Renders a node-and-edge diagram from one of two data sources:
|
|
5
|
+
* 1. Child headings (`context.children`) — the legacy authored form; each
|
|
6
|
+
* child becomes a node positioned by `x`/`y` and connected by
|
|
7
|
+
* `connectsTo` (see `diagramLayout.ts` for auto-placement rules).
|
|
8
|
+
* 2. `input.nodes`/`input.edges` (usually via `templateData`) — the
|
|
9
|
+
* data-driven form derived from an ASCII-art diagram fence. Nodes may
|
|
10
|
+
* carry per-node sizes and a `container` reference; containers draw
|
|
11
|
+
* behind their children as translucent grouping cards.
|
|
10
12
|
*
|
|
11
|
-
* Layout coordinates
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* absolute pixel ranges.
|
|
13
|
+
* Layout coordinates are author-defined units. The template computes a
|
|
14
|
+
* bounding box of all nodes and scales it to fit the block's viewport
|
|
15
|
+
* with padding, so users don't have to think about absolute pixel ranges.
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import type { Layer, ShapeLayer, TextLayer, PathLayer, MarkerStyle } from '../../schemas/Doc.js';
|
|
18
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
DiagramBlockInput,
|
|
21
|
+
DiagramEdgeAnchor,
|
|
22
|
+
DiagramTemplateEdge,
|
|
23
|
+
DiagramTemplateNode,
|
|
24
|
+
TemplateContext,
|
|
25
|
+
} from '../../schemas/BlockTemplates.js';
|
|
19
26
|
import { resolveColorScheme, getThemeFont, themedFontSize } from '../utils/themeUtils.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
27
|
+
import { DIAGRAM_LABEL_LINE_HEIGHT, fitDiagramLabel } from '../utils/diagramText.js';
|
|
28
|
+
import {
|
|
29
|
+
anchorPoint,
|
|
30
|
+
connectorPath,
|
|
31
|
+
lineStyleDasharray,
|
|
32
|
+
snapEndpoints,
|
|
33
|
+
type ConnectorRouting,
|
|
34
|
+
} from '../utils/shapeGeometry.js';
|
|
35
|
+
import { computeDiagramLayout } from './diagramLayout.js';
|
|
22
36
|
|
|
23
37
|
const NODE_WIDTH = 180;
|
|
24
38
|
const NODE_HEIGHT = 64;
|
|
25
39
|
const PADDING = 80;
|
|
26
40
|
|
|
41
|
+
interface ResolvedDiagramNode {
|
|
42
|
+
id: string;
|
|
43
|
+
label: string;
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
w: number;
|
|
47
|
+
h: number;
|
|
48
|
+
/** Containment depth (0 = top level); containers draw depth-ascending. */
|
|
49
|
+
depth: number;
|
|
50
|
+
isContainer: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ResolvedDiagramEdge {
|
|
54
|
+
id: string;
|
|
55
|
+
source: string;
|
|
56
|
+
target: string;
|
|
57
|
+
label?: string;
|
|
58
|
+
directed: boolean;
|
|
59
|
+
sourceAnchor?: DiagramEdgeAnchor;
|
|
60
|
+
targetAnchor?: DiagramEdgeAnchor;
|
|
61
|
+
routing?: ConnectorRouting;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ResolvedDiagram {
|
|
65
|
+
nodes: ResolvedDiagramNode[];
|
|
66
|
+
edges: ResolvedDiagramEdge[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function nodesFromChildren(context: TemplateContext): ResolvedDiagram {
|
|
70
|
+
const layout = computeDiagramLayout(context.children ?? []);
|
|
71
|
+
return {
|
|
72
|
+
nodes: layout.nodes.map((n) => ({
|
|
73
|
+
id: n.id,
|
|
74
|
+
label: n.label,
|
|
75
|
+
x: n.x,
|
|
76
|
+
y: n.y,
|
|
77
|
+
w: NODE_WIDTH,
|
|
78
|
+
h: NODE_HEIGHT,
|
|
79
|
+
depth: 0,
|
|
80
|
+
isContainer: false,
|
|
81
|
+
})),
|
|
82
|
+
edges: layout.edges.map((e) => ({
|
|
83
|
+
id: e.id,
|
|
84
|
+
source: e.source,
|
|
85
|
+
target: e.target,
|
|
86
|
+
...(e.type ? { label: e.type } : {}),
|
|
87
|
+
directed: true,
|
|
88
|
+
})),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Defensive resolution of `templateData`-supplied nodes/edges: the data is
|
|
94
|
+
* untyped at runtime (it can come from a hand-written `json data` fence),
|
|
95
|
+
* so malformed entries are dropped rather than crashing the template.
|
|
96
|
+
*/
|
|
97
|
+
function nodesFromTemplateData(input: DiagramBlockInput): ResolvedDiagram {
|
|
98
|
+
const rawNodes = Array.isArray(input.nodes) ? input.nodes : [];
|
|
99
|
+
const valid = rawNodes.filter(
|
|
100
|
+
(n): n is DiagramTemplateNode =>
|
|
101
|
+
typeof n === 'object' &&
|
|
102
|
+
n !== null &&
|
|
103
|
+
typeof n.id === 'string' &&
|
|
104
|
+
n.id.length > 0 &&
|
|
105
|
+
typeof n.label === 'string' &&
|
|
106
|
+
typeof n.x === 'number' &&
|
|
107
|
+
Number.isFinite(n.x) &&
|
|
108
|
+
typeof n.y === 'number' &&
|
|
109
|
+
Number.isFinite(n.y),
|
|
110
|
+
);
|
|
111
|
+
const ids = new Set(valid.map((n) => n.id));
|
|
112
|
+
const containerIds = new Set(
|
|
113
|
+
valid.map((n) => n.container).filter((c): c is string => typeof c === 'string' && ids.has(c)),
|
|
114
|
+
);
|
|
115
|
+
const depthOf = (node: DiagramTemplateNode): number => {
|
|
116
|
+
let depth = 0;
|
|
117
|
+
let current: DiagramTemplateNode | undefined = node;
|
|
118
|
+
const seen = new Set<string>();
|
|
119
|
+
while (current?.container && ids.has(current.container) && !seen.has(current.id)) {
|
|
120
|
+
seen.add(current.id);
|
|
121
|
+
depth++;
|
|
122
|
+
current = valid.find((n) => n.id === current?.container);
|
|
123
|
+
}
|
|
124
|
+
return depth;
|
|
125
|
+
};
|
|
126
|
+
const nodes: ResolvedDiagramNode[] = valid.map((n) => ({
|
|
127
|
+
id: n.id,
|
|
128
|
+
label: n.label,
|
|
129
|
+
x: n.x,
|
|
130
|
+
y: n.y,
|
|
131
|
+
w: typeof n.w === 'number' && Number.isFinite(n.w) && n.w > 0 ? n.w : NODE_WIDTH,
|
|
132
|
+
h: typeof n.h === 'number' && Number.isFinite(n.h) && n.h > 0 ? n.h : NODE_HEIGHT,
|
|
133
|
+
depth: depthOf(n),
|
|
134
|
+
isContainer: containerIds.has(n.id),
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
const rawEdges = Array.isArray(input.edges) ? input.edges : [];
|
|
138
|
+
const edges: ResolvedDiagramEdge[] = rawEdges
|
|
139
|
+
.filter(
|
|
140
|
+
(e): e is DiagramTemplateEdge =>
|
|
141
|
+
typeof e === 'object' &&
|
|
142
|
+
e !== null &&
|
|
143
|
+
typeof e.source === 'string' &&
|
|
144
|
+
typeof e.target === 'string' &&
|
|
145
|
+
ids.has(e.source) &&
|
|
146
|
+
ids.has(e.target),
|
|
147
|
+
)
|
|
148
|
+
.map((e, i) => ({
|
|
149
|
+
id: e.label ? `${e.source}->${e.target}:${e.label}` : `${e.source}->${e.target}-${i}`,
|
|
150
|
+
source: e.source,
|
|
151
|
+
target: e.target,
|
|
152
|
+
...(e.label ? { label: e.label } : {}),
|
|
153
|
+
directed: e.directed !== false,
|
|
154
|
+
...(e.sourceAnchor ? { sourceAnchor: e.sourceAnchor } : {}),
|
|
155
|
+
...(e.targetAnchor ? { targetAnchor: e.targetAnchor } : {}),
|
|
156
|
+
...(e.routing ? { routing: e.routing } : {}),
|
|
157
|
+
}));
|
|
158
|
+
return { nodes, edges };
|
|
159
|
+
}
|
|
160
|
+
|
|
27
161
|
export function diagramBlock(input: DiagramBlockInput, context: TemplateContext): Layer[] {
|
|
28
162
|
const { theme, viewport, children = [] } = context;
|
|
29
163
|
const colors = resolveColorScheme(context, input.colorScheme ?? 'blue');
|
|
30
164
|
|
|
31
|
-
const
|
|
32
|
-
|
|
165
|
+
const resolved =
|
|
166
|
+
children.length > 0
|
|
167
|
+
? nodesFromChildren(context)
|
|
168
|
+
: input.nodes && input.nodes.length > 0
|
|
169
|
+
? nodesFromTemplateData(input)
|
|
170
|
+
: { nodes: [], edges: [] };
|
|
171
|
+
|
|
172
|
+
if (resolved.nodes.length === 0) {
|
|
33
173
|
// Empty diagram — render a single hint label so the block has visible content.
|
|
34
174
|
return [
|
|
35
175
|
{
|
|
@@ -50,10 +190,10 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
50
190
|
}
|
|
51
191
|
|
|
52
192
|
// Compute the bounding box of all nodes in author coordinates.
|
|
53
|
-
const minX = Math.min(...
|
|
54
|
-
const maxX = Math.max(...
|
|
55
|
-
const minY = Math.min(...
|
|
56
|
-
const maxY = Math.max(...
|
|
193
|
+
const minX = Math.min(...resolved.nodes.map((n) => n.x));
|
|
194
|
+
const maxX = Math.max(...resolved.nodes.map((n) => n.x + n.w));
|
|
195
|
+
const minY = Math.min(...resolved.nodes.map((n) => n.y));
|
|
196
|
+
const maxY = Math.max(...resolved.nodes.map((n) => n.y + n.h));
|
|
57
197
|
|
|
58
198
|
// Available area in viewport pixels (after reserving space for the title, if any).
|
|
59
199
|
const titleHeight = input.title ? 120 : 0;
|
|
@@ -79,11 +219,11 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
79
219
|
const groupTop = Math.max(PADDING / 2, (viewport.height - titleHeight - scaledH) / 2);
|
|
80
220
|
const offsetX = PADDING + (availW - scaledW) / 2;
|
|
81
221
|
const offsetY = groupTop + titleHeight;
|
|
82
|
-
const transform = (n:
|
|
222
|
+
const transform = (n: ResolvedDiagramNode): { x: number; y: number; w: number; h: number } => ({
|
|
83
223
|
x: offsetX + (n.x - minX) * scale,
|
|
84
224
|
y: offsetY + (n.y - minY) * scale,
|
|
85
|
-
w:
|
|
86
|
-
h:
|
|
225
|
+
w: n.w * scale,
|
|
226
|
+
h: n.h * scale,
|
|
87
227
|
});
|
|
88
228
|
|
|
89
229
|
const layers: Layer[] = [];
|
|
@@ -107,29 +247,57 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
107
247
|
});
|
|
108
248
|
}
|
|
109
249
|
|
|
110
|
-
|
|
250
|
+
const containers = resolved.nodes
|
|
251
|
+
.filter((n) => n.isContainer)
|
|
252
|
+
.sort((a, b) => a.depth - b.depth || a.y - b.y || a.x - b.x);
|
|
253
|
+
const leaves = resolved.nodes.filter((n) => !n.isContainer);
|
|
254
|
+
|
|
255
|
+
// Container cards first — translucent grouping surfaces behind everything.
|
|
256
|
+
for (const node of containers) {
|
|
257
|
+
const t = transform(node);
|
|
258
|
+
const card: ShapeLayer = {
|
|
259
|
+
type: 'shape',
|
|
260
|
+
id: `node-card-${node.id}`,
|
|
261
|
+
content: {
|
|
262
|
+
shape: 'rect',
|
|
263
|
+
fill: colors.bg ?? theme.colors.backgroundLight,
|
|
264
|
+
fillOpacity: 0.25,
|
|
265
|
+
stroke: colors.text ?? theme.colors.primary,
|
|
266
|
+
strokeWidth: Math.max(1, strokeW - 1),
|
|
267
|
+
borderRadius: 10,
|
|
268
|
+
},
|
|
269
|
+
position: { x: t.x, y: t.y, width: t.w, height: t.h },
|
|
270
|
+
};
|
|
271
|
+
layers.push(card);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Position lookup by id for edge routing (containers included — edges may
|
|
275
|
+
// attach to them, e.g. a through-border split from a parsed fence).
|
|
111
276
|
const positions = new Map<string, { cx: number; cy: number; rx: number; ry: number }>();
|
|
112
|
-
for (const node of
|
|
277
|
+
for (const node of resolved.nodes) {
|
|
113
278
|
const t = transform(node);
|
|
114
279
|
positions.set(node.id, { cx: t.x + t.w / 2, cy: t.y + t.h / 2, rx: t.w / 2, ry: t.h / 2 });
|
|
115
280
|
}
|
|
116
281
|
|
|
117
|
-
// Edge styling (applies to all edges; per-edge
|
|
282
|
+
// Edge styling (applies to all edges; direction comes per-edge).
|
|
118
283
|
const startMarker: MarkerStyle = input.startStyle ?? 'none';
|
|
119
|
-
const
|
|
284
|
+
const configuredEnd: MarkerStyle = input.endStyle ?? 'arrow';
|
|
120
285
|
const edgeDash = lineStyleDasharray(input.lineStyle);
|
|
121
286
|
|
|
122
|
-
// Edges
|
|
123
|
-
for (const edge of
|
|
287
|
+
// Edges above containers, behind leaf nodes.
|
|
288
|
+
for (const edge of resolved.edges) {
|
|
124
289
|
const a = positions.get(edge.source);
|
|
125
290
|
const b = positions.get(edge.target);
|
|
126
291
|
if (!a || !b) continue;
|
|
127
|
-
const
|
|
292
|
+
const endMarker: MarkerStyle = edge.directed ? configuredEnd : 'none';
|
|
293
|
+
const snapped = snapEndpoints(a, b);
|
|
294
|
+
const start = edge.sourceAnchor ? anchorPoint(a, edge.sourceAnchor) : snapped.start;
|
|
295
|
+
const end = edge.targetAnchor ? anchorPoint(b, edge.targetAnchor) : snapped.end;
|
|
128
296
|
const pathLayer: PathLayer = {
|
|
129
297
|
type: 'path',
|
|
130
298
|
id: `edge-${edge.id}`,
|
|
131
299
|
content: {
|
|
132
|
-
d: connectorPath(input.edgeStyle ?? 'curved', start, end),
|
|
300
|
+
d: connectorPath(edge.routing ?? input.edgeStyle ?? 'curved', start, end),
|
|
133
301
|
stroke: colors.text ?? theme.colors.primary,
|
|
134
302
|
strokeWidth: strokeW,
|
|
135
303
|
fill: 'none',
|
|
@@ -141,9 +309,9 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
141
309
|
};
|
|
142
310
|
layers.push(pathLayer);
|
|
143
311
|
|
|
144
|
-
if (edge.
|
|
145
|
-
//
|
|
146
|
-
//
|
|
312
|
+
if (edge.label) {
|
|
313
|
+
// Edge label, floated just above the edge midpoint so the line never
|
|
314
|
+
// strikes through the text.
|
|
147
315
|
const mx = (a.cx + b.cx) / 2;
|
|
148
316
|
const my = (a.cy + b.cy) / 2;
|
|
149
317
|
const labelFontSize = themedFontSize(Math.round(18 * fontAdj), context, false);
|
|
@@ -151,7 +319,7 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
151
319
|
type: 'text',
|
|
152
320
|
id: `edge-label-${edge.id}`,
|
|
153
321
|
content: {
|
|
154
|
-
text: edge.
|
|
322
|
+
text: edge.label,
|
|
155
323
|
style: {
|
|
156
324
|
fontSize: labelFontSize,
|
|
157
325
|
fontFamily: getThemeFont(context, 'body'),
|
|
@@ -165,8 +333,8 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
165
333
|
}
|
|
166
334
|
}
|
|
167
335
|
|
|
168
|
-
//
|
|
169
|
-
for (const node of
|
|
336
|
+
// Leaf nodes (card + label) on top of edges.
|
|
337
|
+
for (const node of leaves) {
|
|
170
338
|
const t = transform(node);
|
|
171
339
|
const card: ShapeLayer = {
|
|
172
340
|
type: 'shape',
|
|
@@ -182,20 +350,51 @@ export function diagramBlock(input: DiagramBlockInput, context: TemplateContext)
|
|
|
182
350
|
};
|
|
183
351
|
layers.push(card);
|
|
184
352
|
|
|
353
|
+
const preferredFontSize = themedFontSize(Math.round(22 * fontAdj), context, false);
|
|
354
|
+
const fit = fitDiagramLabel(node.label, t.w, t.h, preferredFontSize);
|
|
185
355
|
const label: TextLayer = {
|
|
186
356
|
type: 'text',
|
|
187
357
|
id: `node-label-${node.id}`,
|
|
188
358
|
content: {
|
|
189
359
|
text: node.label,
|
|
190
360
|
style: {
|
|
191
|
-
fontSize:
|
|
361
|
+
fontSize: fit.fontSize,
|
|
192
362
|
fontFamily: getThemeFont(context, 'body'),
|
|
193
363
|
fontWeight: 'bold',
|
|
194
364
|
color: colors.text ?? theme.colors.text,
|
|
195
365
|
textAlign: 'center',
|
|
366
|
+
lineHeight: DIAGRAM_LABEL_LINE_HEIGHT,
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
position: {
|
|
370
|
+
x: t.x + t.w / 2,
|
|
371
|
+
y: t.y + t.h / 2 + fit.firstLineOffset,
|
|
372
|
+
anchor: 'center',
|
|
373
|
+
width: fit.textWidth,
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
layers.push(label);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Container titles last — anchored near the container's top edge, above
|
|
380
|
+
// any child content that could otherwise overprint them.
|
|
381
|
+
for (const node of containers) {
|
|
382
|
+
const t = transform(node);
|
|
383
|
+
const fontSize = themedFontSize(Math.round(20 * fontAdj), context, false);
|
|
384
|
+
const label: TextLayer = {
|
|
385
|
+
type: 'text',
|
|
386
|
+
id: `node-label-${node.id}`,
|
|
387
|
+
content: {
|
|
388
|
+
text: node.label.split('\n')[0] ?? '',
|
|
389
|
+
style: {
|
|
390
|
+
fontSize,
|
|
391
|
+
fontFamily: getThemeFont(context, 'body'),
|
|
392
|
+
fontWeight: 'bold',
|
|
393
|
+
color: theme.colors.textMuted,
|
|
394
|
+
textAlign: 'center',
|
|
196
395
|
},
|
|
197
396
|
},
|
|
198
|
-
position: { x: t.x + t.w / 2, y: t.y +
|
|
397
|
+
position: { x: t.x + t.w / 2, y: t.y + fontSize, anchor: 'center', width: t.w },
|
|
199
398
|
};
|
|
200
399
|
layers.push(label);
|
|
201
400
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* agents reviewing screenshots) can see *why* the block degraded.
|
|
9
9
|
*
|
|
10
10
|
* This is intentionally not a registered template: it can't be requested by
|
|
11
|
-
* name, only reached when a requested template fails. See `
|
|
11
|
+
* name, only reached when a requested template fails. See `materializeBlockLayers()`.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { Layer } from '../../schemas/Doc.js';
|