@synergenius/flow-weaver 0.33.1 → 0.33.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli/commands/diagram.js +1 -1
- package/dist/cli/flow-weaver.mjs +299 -413
- package/dist/diagram/geometry.d.ts +7 -2
- package/dist/diagram/geometry.js +15 -56
- package/dist/diagram/html-viewer.js +98 -42
- package/dist/diagram/orthogonal-router.d.ts +16 -51
- package/dist/diagram/orthogonal-router.js +138 -248
- package/dist/diagram/renderer.js +79 -65
- package/dist/diagram/theme.d.ts +2 -0
- package/dist/diagram/theme.js +17 -17
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/package.json +1 -1
package/dist/diagram/renderer.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { getTheme, getPortColor,
|
|
2
|
-
import { PORT_RADIUS, BORDER_RADIUS,
|
|
1
|
+
import { getTheme, getPortColor, TYPE_ABBREVIATIONS, NODE_ICON_PATHS, NODE_DEFAULT_COLOR, NODE_VARIANT_COLORS } from './theme.js';
|
|
2
|
+
import { PORT_RADIUS, BORDER_RADIUS, LABEL_GAP, SCOPE_PORT_COLUMN, measureText } from './geometry.js';
|
|
3
3
|
function escapeXml(str) {
|
|
4
4
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
5
5
|
}
|
|
6
|
+
/** Resolve icon color from a node's resolved border hex back to the variant icon color */
|
|
7
|
+
function resolveIconColor(nodeColor, themeName, theme) {
|
|
8
|
+
if (nodeColor === NODE_DEFAULT_COLOR)
|
|
9
|
+
return theme.nodeIconColor;
|
|
10
|
+
for (const v of Object.values(NODE_VARIANT_COLORS)) {
|
|
11
|
+
if (v.darkBorder === nodeColor || v.border === nodeColor) {
|
|
12
|
+
return themeName === 'dark' ? v.darkIcon : v.icon;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return nodeColor; // custom hex — use as-is
|
|
16
|
+
}
|
|
6
17
|
/** Collect all connections (main + scope) for gradient def generation */
|
|
7
18
|
function collectAllConnections(graph) {
|
|
8
19
|
const all = [...graph.connections];
|
|
@@ -32,22 +43,22 @@ export function renderSVG(graph, options = {}) {
|
|
|
32
43
|
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${vbX} ${vbY} ${vbWidth} ${vbHeight}" width="${svgWidth}" height="${svgHeight}">`);
|
|
33
44
|
// Styles
|
|
34
45
|
parts.push(`<style>`);
|
|
35
|
-
parts.push(` text { font-family: Montserrat, 'Segoe UI', Roboto, sans-serif; }`);
|
|
36
|
-
parts.push(` .node-label { font-size:
|
|
46
|
+
parts.push(` text { font-family: Montserrat, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; }`);
|
|
47
|
+
parts.push(` .node-label { font-size: 18px; font-weight: 600; fill: ${theme.labelColor}; }`);
|
|
37
48
|
parts.push(` .port-label { font-size: 10px; font-weight: 600; fill: ${theme.labelColor}; }`);
|
|
38
49
|
parts.push(` .port-type-label { font-size: 10px; font-weight: 600; }`);
|
|
39
50
|
parts.push(`</style>`);
|
|
40
51
|
// Defs (dot grid pattern + node shadow filter + connection gradients)
|
|
41
52
|
parts.push(`<defs>`);
|
|
42
53
|
parts.push(` <pattern id="dot-grid" width="20" height="20" patternUnits="userSpaceOnUse">`);
|
|
43
|
-
parts.push(` <circle cx="10" cy="10" r="
|
|
54
|
+
parts.push(` <circle cx="10" cy="10" r="0.75" fill="${theme.dotColor}" opacity="${theme.dotOpacity}"/>`);
|
|
44
55
|
parts.push(` </pattern>`);
|
|
45
|
-
|
|
46
|
-
parts.push(` <feDropShadow dx="0" dy="2" stdDeviation="4" flood-opacity="${theme.nodeShadowOpacity}" flood-color="#000"/>`);
|
|
47
|
-
parts.push(` </filter>`);
|
|
56
|
+
// No drop shadow — matches platform (nodes use outline glow, not shadow)
|
|
48
57
|
for (let i = 0; i < allConnections.length; i++) {
|
|
49
58
|
const conn = allConnections[i];
|
|
50
|
-
|
|
59
|
+
// Use userSpaceOnUse so gradients work on flat horizontal paths
|
|
60
|
+
// (objectBoundingBox fails when bounding box height is zero)
|
|
61
|
+
parts.push(` <linearGradient id="conn-grad-${i}" gradientUnits="userSpaceOnUse" x1="${vbX}" y1="0" x2="${vbX + vbWidth}" y2="0">`);
|
|
51
62
|
parts.push(` <stop offset="0%" stop-color="${conn.sourceColor}"/>`);
|
|
52
63
|
parts.push(` <stop offset="100%" stop-color="${conn.targetColor}"/>`);
|
|
53
64
|
parts.push(` </linearGradient>`);
|
|
@@ -56,12 +67,11 @@ export function renderSVG(graph, options = {}) {
|
|
|
56
67
|
// Background
|
|
57
68
|
parts.push(`<rect x="${vbX}" y="${vbY}" width="${vbWidth}" height="${vbHeight}" fill="${theme.background}"/>`);
|
|
58
69
|
parts.push(`<rect x="${vbX}" y="${vbY}" width="${vbWidth}" height="${vbHeight}" fill="url(#dot-grid)"/>`);
|
|
59
|
-
// Connections + stubs (
|
|
70
|
+
// Connections + stubs (below nodes and labels)
|
|
60
71
|
parts.push(`<g class="connections">`);
|
|
61
72
|
for (let i = 0; i < graph.connections.length; i++) {
|
|
62
73
|
renderConnection(parts, graph.connections[i], i, !graph.connections[i].path);
|
|
63
74
|
}
|
|
64
|
-
// Stubs sit alongside connection paths; short-distance ones start hidden for HTML viewer toggling
|
|
65
75
|
parts.push(` <g class="stubs">`);
|
|
66
76
|
for (const conn of graph.connections) {
|
|
67
77
|
const hideStubs = !!conn.path;
|
|
@@ -72,13 +82,13 @@ export function renderSVG(graph, options = {}) {
|
|
|
72
82
|
}
|
|
73
83
|
parts.push(` </g>`);
|
|
74
84
|
parts.push(`</g>`);
|
|
75
|
-
// Nodes (bodies, icons, port dots
|
|
85
|
+
// Nodes (bodies, icons, port dots)
|
|
76
86
|
parts.push(`<g class="nodes">`);
|
|
77
87
|
for (const node of graph.nodes) {
|
|
78
88
|
parts.push(renderNode(node, theme, themeName, allConnections));
|
|
79
89
|
}
|
|
80
90
|
parts.push(`</g>`);
|
|
81
|
-
// Labels rendered last so they appear on top of everything
|
|
91
|
+
// Labels rendered last so they appear on top of everything
|
|
82
92
|
parts.push(`<g class="labels">`);
|
|
83
93
|
for (const node of graph.nodes) {
|
|
84
94
|
renderNodeLabel(parts, node, theme);
|
|
@@ -87,7 +97,6 @@ export function renderSVG(graph, options = {}) {
|
|
|
87
97
|
for (const child of node.scopeChildren) {
|
|
88
98
|
renderNodeLabel(parts, child, theme);
|
|
89
99
|
}
|
|
90
|
-
// Scope port labels + child port labels
|
|
91
100
|
if (showPortLabels && node.scopePorts) {
|
|
92
101
|
renderPortLabels(parts, node.id, node.scopePorts.inputs, node.scopePorts.outputs, theme, themeName);
|
|
93
102
|
}
|
|
@@ -97,14 +106,6 @@ export function renderSVG(graph, options = {}) {
|
|
|
97
106
|
}
|
|
98
107
|
}
|
|
99
108
|
parts.push(`</g>`);
|
|
100
|
-
// Watermark (logo + text)
|
|
101
|
-
const wmX = vbX + vbWidth - 16;
|
|
102
|
-
const wmY = vbY + vbHeight - 14;
|
|
103
|
-
const wmBrand = themeName === 'dark' ? '#8e9eff' : '#5468ff';
|
|
104
|
-
parts.push(`<g opacity="0.5">`);
|
|
105
|
-
parts.push(` <svg x="${wmX - 118}" y="${wmY - 18}" width="22" height="22" viewBox="0 0 256 256" fill="none"><path d="M80 128C134 128 122 49 176 49" stroke="${wmBrand}" stroke-width="14" stroke-linecap="round"/><path d="M80 128C134 128 122 207 176 207" stroke="${wmBrand}" stroke-width="14" stroke-linecap="round"/><rect x="28" y="102" width="52" height="52" rx="10" stroke="${wmBrand}" stroke-width="14"/><rect x="176" y="23" width="52" height="52" rx="10" stroke="${wmBrand}" stroke-width="14"/><rect x="176" y="181" width="52" height="52" rx="10" stroke="${wmBrand}" stroke-width="14"/></svg>`);
|
|
106
|
-
parts.push(` <text x="${wmX}" y="${wmY}" text-anchor="end" font-size="14" font-weight="700" fill="${wmBrand}" font-family="Montserrat, 'Segoe UI', Roboto, sans-serif">Flow Weaver</text>`);
|
|
107
|
-
parts.push(`</g>`);
|
|
108
109
|
parts.push(`</svg>`);
|
|
109
110
|
return parts.join('\n');
|
|
110
111
|
}
|
|
@@ -113,7 +114,7 @@ function renderConnection(parts, conn, gradIndex, hidden = false) {
|
|
|
113
114
|
const dashAttr = conn.isStepConnection ? '' : ' stroke-dasharray="8 4"';
|
|
114
115
|
const displayAttr = hidden ? ' display="none"' : '';
|
|
115
116
|
const pathD = conn.path || 'M0,0';
|
|
116
|
-
parts.push(` <path d="${pathD}" fill="none" stroke="url(#conn-grad-${gradIndex})" stroke-width="
|
|
117
|
+
parts.push(` <path d="${pathD}" fill="none" stroke="url(#conn-grad-${gradIndex})" stroke-width="1"${dashAttr} stroke-linecap="round" data-source="${escapeXml(conn.fromNode)}.${escapeXml(conn.fromPort)}:output" data-target="${escapeXml(conn.toNode)}.${escapeXml(conn.toPort)}:input"${displayAttr}/>`);
|
|
117
118
|
}
|
|
118
119
|
function renderStub(parts, stub, conn, hidden = false) {
|
|
119
120
|
const dashAttr = stub.dashed ? ' stroke-dasharray="6 3"' : '';
|
|
@@ -132,16 +133,17 @@ function renderScopeConnection(parts, conn, allConnections, parentNodeId) {
|
|
|
132
133
|
if (gradIndex < 0)
|
|
133
134
|
return;
|
|
134
135
|
const dashAttr = conn.isStepConnection ? '' : ' stroke-dasharray="8 4"';
|
|
135
|
-
parts.push(` <path d="${conn.path}" fill="none" stroke="url(#conn-grad-${gradIndex})" stroke-width="
|
|
136
|
+
parts.push(` <path d="${conn.path}" fill="none" stroke="url(#conn-grad-${gradIndex})" stroke-width="1"${dashAttr} stroke-linecap="round" data-source="${escapeXml(conn.fromNode)}.${escapeXml(conn.fromPort)}:output" data-target="${escapeXml(conn.toNode)}.${escapeXml(conn.toPort)}:input" data-scope="${escapeXml(parentNodeId)}"/>`);
|
|
136
137
|
}
|
|
137
138
|
// ---- Node rendering ----
|
|
138
139
|
/** Render node body rect + icon */
|
|
139
|
-
function renderNodeBody(parts, node, theme, indent) {
|
|
140
|
+
function renderNodeBody(parts, node, theme, themeName, indent) {
|
|
140
141
|
const strokeColor = node.color !== NODE_DEFAULT_COLOR ? node.color : theme.nodeIconColor;
|
|
141
|
-
parts.push(`${indent}<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="${BORDER_RADIUS}" fill="${theme.
|
|
142
|
+
parts.push(`${indent}<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="${BORDER_RADIUS}" fill="${theme.background}" stroke="${strokeColor}" stroke-width="2"/>`);
|
|
142
143
|
const iconPath = NODE_ICON_PATHS[node.icon] ?? NODE_ICON_PATHS.code;
|
|
143
|
-
|
|
144
|
-
const
|
|
144
|
+
// node.color is a resolved hex (e.g. "#5e9eff"), find matching variant by border value
|
|
145
|
+
const iconColor = resolveIconColor(node.color, themeName, theme);
|
|
146
|
+
const iconSize = 50;
|
|
145
147
|
const iconX = node.x + (node.width - iconSize) / 2;
|
|
146
148
|
const iconY = node.y + (node.height - iconSize) / 2;
|
|
147
149
|
parts.push(`${indent}<svg x="${iconX}" y="${iconY}" width="${iconSize}" height="${iconSize}" viewBox="0 -960 960 960"><path d="${iconPath}" fill="${iconColor}"/></svg>`);
|
|
@@ -152,57 +154,61 @@ function renderNode(node, theme, themeName, allConnections) {
|
|
|
152
154
|
if (node.scopeChildren && node.scopeChildren.length > 0) {
|
|
153
155
|
// Scoped node: body rect only (icon omitted — children occupy the inner area)
|
|
154
156
|
const strokeColor = node.color !== NODE_DEFAULT_COLOR ? node.color : theme.nodeIconColor;
|
|
155
|
-
parts.push(` <rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="${BORDER_RADIUS}" fill="${theme.
|
|
157
|
+
parts.push(` <rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="${BORDER_RADIUS}" fill="${theme.background}" stroke="${strokeColor}" stroke-width="2"/>`);
|
|
156
158
|
renderScopedContent(parts, node, theme, themeName, allConnections);
|
|
157
159
|
}
|
|
158
160
|
else {
|
|
159
|
-
renderNodeBody(parts, node, theme, ' ');
|
|
161
|
+
renderNodeBody(parts, node, theme, themeName, ' ');
|
|
160
162
|
}
|
|
161
163
|
// External port dots (labels rendered in top-level labels pass)
|
|
162
|
-
renderPortDots(parts, node.id, node.inputs, node.outputs, themeName);
|
|
164
|
+
renderPortDots(parts, node.id, node.inputs, node.outputs, themeName, theme);
|
|
163
165
|
parts.push(` </g>`);
|
|
164
166
|
return parts.join('\n');
|
|
165
167
|
}
|
|
166
168
|
function renderScopedContent(parts, node, theme, themeName, allConnections) {
|
|
167
169
|
const children = node.scopeChildren;
|
|
168
170
|
const scopePorts = node.scopePorts;
|
|
169
|
-
// Scope area
|
|
171
|
+
// Scope area dividers — matches platform scopeContainerStyle (2px solid outline columns)
|
|
172
|
+
// Vertical lines at left/right scope port columns + horizontal top/bottom dividers
|
|
170
173
|
const scopeX = node.x + SCOPE_PORT_COLUMN;
|
|
171
|
-
const scopeY = node.y + 4;
|
|
172
174
|
const scopeW = node.width - SCOPE_PORT_COLUMN * 2;
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
+
const scopeRightX = node.x + node.width - SCOPE_PORT_COLUMN;
|
|
176
|
+
const lineY1 = node.y;
|
|
177
|
+
const lineY2 = node.y + node.height;
|
|
178
|
+
const strokeColor = node.color !== NODE_DEFAULT_COLOR ? node.color : theme.nodeIconColor;
|
|
179
|
+
// Vertical column dividers (left and right scope port columns)
|
|
180
|
+
parts.push(` <line x1="${scopeX}" y1="${lineY1}" x2="${scopeX}" y2="${lineY2}" stroke="${strokeColor}" stroke-width="2" opacity="0.5"/>`);
|
|
181
|
+
parts.push(` <line x1="${scopeRightX}" y1="${lineY1}" x2="${scopeRightX}" y2="${lineY2}" stroke="${strokeColor}" stroke-width="2" opacity="0.5"/>`);
|
|
182
|
+
// Horizontal top/bottom area dividers
|
|
183
|
+
parts.push(` <line x1="${scopeX}" y1="${lineY1 + 2}" x2="${scopeRightX}" y2="${lineY1 + 2}" stroke="${theme.scopeAreaStroke}" stroke-width="1" opacity="0.3"/>`);
|
|
184
|
+
parts.push(` <line x1="${scopeX}" y1="${lineY2 - 2}" x2="${scopeRightX}" y2="${lineY2 - 2}" stroke="${theme.scopeAreaStroke}" stroke-width="1" opacity="0.3"/>`);
|
|
175
185
|
// Scope connections (before ports so ports appear on top)
|
|
176
186
|
for (const conn of node.scopeConnections ?? []) {
|
|
177
187
|
renderScopeConnection(parts, conn, allConnections, node.id);
|
|
178
188
|
}
|
|
179
189
|
// Scope port dots (before children so dots sit on top of connections)
|
|
180
190
|
if (scopePorts) {
|
|
181
|
-
renderPortDots(parts, node.id, scopePorts.inputs, scopePorts.outputs, themeName);
|
|
191
|
+
renderPortDots(parts, node.id, scopePorts.inputs, scopePorts.outputs, themeName, theme);
|
|
182
192
|
}
|
|
183
193
|
// Child nodes (all labels handled in top-level labels pass)
|
|
184
194
|
for (const child of children) {
|
|
185
195
|
parts.push(` <g data-node-id="${escapeXml(child.id)}">`);
|
|
186
|
-
renderNodeBody(parts, child, theme, ' ');
|
|
187
|
-
renderPortDots(parts, child.id, child.inputs, child.outputs, themeName);
|
|
196
|
+
renderNodeBody(parts, child, theme, themeName, ' ');
|
|
197
|
+
renderPortDots(parts, child.id, child.inputs, child.outputs, themeName, theme);
|
|
188
198
|
parts.push(` </g>`);
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
// ---- Label rendering ----
|
|
192
|
-
/** Render a node name label badge */
|
|
202
|
+
/** Render a node name label (plain text, no badge background — matches platform) */
|
|
193
203
|
function renderNodeLabel(parts, node, theme) {
|
|
194
204
|
const isScoped = !!(node.scopeChildren && node.scopeChildren.length > 0);
|
|
195
205
|
const labelText = escapeXml(node.label);
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
const labelBgHeight = LABEL_HEIGHT;
|
|
199
|
-
const labelBgX = isScoped ? node.x : node.x + node.width / 2 - labelBgWidth / 2;
|
|
200
|
-
const labelBgY = node.y - LABEL_GAP - labelBgHeight;
|
|
201
|
-
const labelTextX = isScoped ? node.x + 8 : node.x + node.width / 2;
|
|
206
|
+
const labelTextX = isScoped ? node.x + 6 : node.x + node.width / 2;
|
|
207
|
+
const labelTextY = node.y - LABEL_GAP;
|
|
202
208
|
const labelAnchor = isScoped ? 'start' : 'middle';
|
|
209
|
+
const labelColor = node.color !== NODE_DEFAULT_COLOR ? node.color : theme.labelColor;
|
|
203
210
|
parts.push(` <g data-label-for="${escapeXml(node.id)}">`);
|
|
204
|
-
parts.push(` <
|
|
205
|
-
parts.push(` <text class="node-label" x="${labelTextX}" y="${labelBgY + labelBgHeight / 2 + 6}" text-anchor="${labelAnchor}" fill="${node.color !== NODE_DEFAULT_COLOR ? node.color : theme.labelColor}">${labelText}</text>`);
|
|
211
|
+
parts.push(` <text class="node-label" x="${labelTextX}" y="${labelTextY}" text-anchor="${labelAnchor}" fill="${labelColor}">${labelText}</text>`);
|
|
206
212
|
parts.push(` </g>`);
|
|
207
213
|
}
|
|
208
214
|
/** Render port labels for a node if showPortLabels is enabled */
|
|
@@ -212,51 +218,59 @@ function renderPortLabelsForNode(parts, node, theme, themeName, showPortLabels)
|
|
|
212
218
|
}
|
|
213
219
|
}
|
|
214
220
|
// ---- Port rendering ----
|
|
215
|
-
/** Render
|
|
216
|
-
function renderPortDots(parts, nodeId, inputs, outputs, themeName) {
|
|
221
|
+
/** Render port indicators: outer ring (port color) + inner bar (bg) matching platform portStyle */
|
|
222
|
+
function renderPortDots(parts, nodeId, inputs, outputs, themeName, theme) {
|
|
223
|
+
// 2px bar with 2px colored ring (boxShadow) — matches platform portStyle
|
|
224
|
+
// SVG equivalent: inner rect (subtle bg) + outer rect (port color, slightly larger)
|
|
225
|
+
const barWidth = 2;
|
|
226
|
+
const barHeight = 14;
|
|
227
|
+
const ringWidth = 2; // boxShadow spread
|
|
228
|
+
const outerW = barWidth + ringWidth * 2; // 6px total
|
|
229
|
+
const outerH = barHeight + ringWidth * 2; // 18px total
|
|
217
230
|
for (const port of [...inputs, ...outputs]) {
|
|
218
231
|
const color = getPortColor(port.dataType, port.isFailure, themeName);
|
|
219
|
-
const ringColor = getPortRingColor(port.dataType, port.isFailure, themeName);
|
|
220
232
|
const dir = port.direction === 'INPUT' ? 'input' : 'output';
|
|
221
|
-
|
|
233
|
+
const ox = port.cx - outerW / 2;
|
|
234
|
+
const oy = port.cy - outerH / 2;
|
|
235
|
+
const ix = port.cx - barWidth / 2;
|
|
236
|
+
const iy = port.cy - barHeight / 2;
|
|
237
|
+
// Outer ring (port-type color)
|
|
238
|
+
parts.push(` <rect x="${ox}" y="${oy}" width="${outerW}" height="${outerH}" rx="4" fill="${color}" data-port-id="${escapeXml(nodeId)}.${escapeXml(port.name)}:${dir}" data-direction="${dir}"/>`);
|
|
239
|
+
// Inner bar (subtle bg)
|
|
240
|
+
parts.push(` <rect x="${ix}" y="${iy}" width="${barWidth}" height="${barHeight}" rx="2" fill="${theme.background}" pointer-events="none"/>`);
|
|
222
241
|
}
|
|
223
242
|
}
|
|
224
|
-
/** Render only port label badges (no dots) */
|
|
243
|
+
/** Render only port label badges (no dots) — rectangular with port-type border */
|
|
225
244
|
function renderPortLabels(parts, nodeId, inputs, outputs, theme, themeName) {
|
|
226
245
|
for (const port of [...inputs, ...outputs]) {
|
|
227
246
|
const color = getPortColor(port.dataType, port.isFailure, themeName);
|
|
228
247
|
const isInput = port.direction === 'INPUT';
|
|
229
|
-
const abbrev = TYPE_ABBREVIATIONS[port.dataType] ?? port.dataType;
|
|
230
248
|
const dir = isInput ? 'input' : 'output';
|
|
231
249
|
const portId = `${escapeXml(nodeId)}.${escapeXml(port.name)}:${dir}`;
|
|
232
250
|
const portLabel = port.label;
|
|
233
|
-
const typeWidth = measureText(abbrev);
|
|
234
251
|
const labelWidth = measureText(portLabel);
|
|
235
|
-
const pad =
|
|
236
|
-
const
|
|
237
|
-
const
|
|
252
|
+
const pad = 6;
|
|
253
|
+
const gap = 4;
|
|
254
|
+
const abbrev = TYPE_ABBREVIATIONS[port.dataType] ?? port.dataType;
|
|
255
|
+
const typeWidth = measureText(abbrev);
|
|
256
|
+
const badgeWidth = pad + typeWidth + gap + labelWidth + pad;
|
|
238
257
|
const badgeHeight = 16;
|
|
239
258
|
const badgeGap = 5;
|
|
240
|
-
const rr = badgeHeight / 2;
|
|
241
259
|
const badgeX = isInput
|
|
242
260
|
? port.cx - PORT_RADIUS - badgeGap - badgeWidth
|
|
243
261
|
: port.cx + PORT_RADIUS + badgeGap;
|
|
244
262
|
const badgeY = port.cy - badgeHeight / 2;
|
|
245
263
|
parts.push(` <g data-port-label="${portId}">`);
|
|
246
|
-
parts.push(` <rect x="${badgeX}" y="${badgeY}" width="${badgeWidth}" height="${badgeHeight}" rx="
|
|
264
|
+
parts.push(` <rect x="${badgeX}" y="${badgeY}" width="${badgeWidth}" height="${badgeHeight}" rx="3" fill="${theme.background}" stroke="${color}" stroke-width="1"/>`);
|
|
247
265
|
if (isInput) {
|
|
248
266
|
const typeX = badgeX + badgeWidth - pad - typeWidth / 2;
|
|
249
|
-
const
|
|
250
|
-
const nameX = divX - divGap;
|
|
251
|
-
parts.push(` <line x1="${divX}" y1="${badgeY + 3}" x2="${divX}" y2="${badgeY + badgeHeight - 3}" stroke="${theme.labelBadgeBorder}" stroke-width="1"/>`);
|
|
267
|
+
const nameX = typeX - typeWidth / 2 - gap;
|
|
252
268
|
parts.push(` <text class="port-label" x="${nameX}" y="${port.cy + 3.5}" text-anchor="end">${escapeXml(portLabel)}</text>`);
|
|
253
269
|
parts.push(` <text class="port-type-label" x="${typeX}" y="${port.cy + 3.5}" text-anchor="middle" fill="${color}">${escapeXml(abbrev)}</text>`);
|
|
254
270
|
}
|
|
255
271
|
else {
|
|
256
272
|
const typeX = badgeX + pad + typeWidth / 2;
|
|
257
|
-
const
|
|
258
|
-
const nameX = divX + 1 + divGap;
|
|
259
|
-
parts.push(` <line x1="${divX}" y1="${badgeY + 3}" x2="${divX}" y2="${badgeY + badgeHeight - 3}" stroke="${theme.labelBadgeBorder}" stroke-width="1"/>`);
|
|
273
|
+
const nameX = typeX + typeWidth / 2 + gap;
|
|
260
274
|
parts.push(` <text class="port-type-label" x="${typeX}" y="${port.cy + 3.5}" text-anchor="middle" fill="${color}">${escapeXml(abbrev)}</text>`);
|
|
261
275
|
parts.push(` <text class="port-label" x="${nameX}" y="${port.cy + 3.5}" text-anchor="start">${escapeXml(portLabel)}</text>`);
|
|
262
276
|
}
|
package/dist/diagram/theme.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export declare const NODE_DEFAULT_COLOR = "#334155";
|
|
|
4
4
|
export declare const NODE_VARIANT_COLORS: Record<string, {
|
|
5
5
|
border: string;
|
|
6
6
|
darkBorder: string;
|
|
7
|
+
icon: string;
|
|
8
|
+
darkIcon: string;
|
|
7
9
|
}>;
|
|
8
10
|
export declare function getTheme(name: 'dark' | 'light'): ThemePalette;
|
|
9
11
|
export declare function getPortColor(dataType: TDataType, isFailure: boolean, theme?: 'dark' | 'light'): string;
|
package/dist/diagram/theme.js
CHANGED
|
@@ -28,15 +28,15 @@ const LIGHT_FAILURE_COLOR = '#e34646'; // red-shade-2
|
|
|
28
28
|
// Light: border = X-shade-2
|
|
29
29
|
export const NODE_DEFAULT_COLOR = '#334155';
|
|
30
30
|
export const NODE_VARIANT_COLORS = {
|
|
31
|
-
blue: { border: '#548ce3', darkBorder: '#5e9eff' }, // blue-shade-2 / blue-dark-shade-1
|
|
32
|
-
purple: { border: '#9f5fe3', darkBorder: '#b36bff' }, // purple-shade-2 / purple-dark-shade-1
|
|
33
|
-
cyan: { border: '#63ccc4', darkBorder: '#6fe5dc' }, // cyan-shade-2 / cyan-dark-shade-1
|
|
34
|
-
teal: { border: '#63ccc4', darkBorder: '#6fe5dc' }, // alias for cyan
|
|
35
|
-
orange: { border: '#e3732d', darkBorder: '#ff8133' }, // orange-shade-2 / orange-dark-shade-1
|
|
36
|
-
pink: { border: '#e349c2', darkBorder: '#ff52da' }, // pink-shade-2 / pink-dark-shade-1
|
|
37
|
-
green: { border: '#0ec850', darkBorder: '#10e15a' }, // green-shade-2 / green-dark-shade-1
|
|
38
|
-
red: { border: '#e34646', darkBorder: '#ff4f4f' }, // red-shade-2 / red-dark-shade-1
|
|
39
|
-
yellow: { border: '#e3a82b', darkBorder: '#ffbd30' }, // yellow-shade-2 / yellow-dark-shade-1
|
|
31
|
+
blue: { border: '#548ce3', darkBorder: '#5e9eff', icon: '#3a6bbf', darkIcon: '#4a7ad4' }, // blue-shade-2 / blue-dark-shade-1 / blue-shade-3 / blue-dark-shade-3
|
|
32
|
+
purple: { border: '#9f5fe3', darkBorder: '#b36bff', icon: '#7d44bf', darkIcon: '#9050d4' }, // purple-shade-2 / purple-dark-shade-1
|
|
33
|
+
cyan: { border: '#63ccc4', darkBorder: '#6fe5dc', icon: '#4aada6', darkIcon: '#56c4bb' }, // cyan-shade-2 / cyan-dark-shade-1
|
|
34
|
+
teal: { border: '#63ccc4', darkBorder: '#6fe5dc', icon: '#4aada6', darkIcon: '#56c4bb' }, // alias for cyan
|
|
35
|
+
orange: { border: '#e3732d', darkBorder: '#ff8133', icon: '#bf5a1e', darkIcon: '#d46a28' }, // orange-shade-2 / orange-dark-shade-1
|
|
36
|
+
pink: { border: '#e349c2', darkBorder: '#ff52da', icon: '#bf349f', darkIcon: '#d43fb5' }, // pink-shade-2 / pink-dark-shade-1
|
|
37
|
+
green: { border: '#0ec850', darkBorder: '#10e15a', icon: '#0aa53f', darkIcon: '#0dbd4a' }, // green-shade-2 / green-dark-shade-1
|
|
38
|
+
red: { border: '#e34646', darkBorder: '#ff4f4f', icon: '#bf3333', darkIcon: '#d43b3b' }, // red-shade-2 / red-dark-shade-1
|
|
39
|
+
yellow: { border: '#e3a82b', darkBorder: '#ffbd30', icon: '#bf8c21', darkIcon: '#d49e28' }, // yellow-shade-2 / yellow-dark-shade-1
|
|
40
40
|
};
|
|
41
41
|
// ---- Theme palettes (exact values from token system) ----
|
|
42
42
|
const DARK_PALETTE = {
|
|
@@ -46,13 +46,13 @@ const DARK_PALETTE = {
|
|
|
46
46
|
labelColor: '#a4beff', // secondary-dark-base (node label text)
|
|
47
47
|
sublabelColor: '#babac0', // color-text-subtle = dark-shade-30
|
|
48
48
|
connectionColor: '#5f5f6d', // color-border-subtle = dark-shade-70
|
|
49
|
-
dotColor: '#
|
|
49
|
+
dotColor: '#7b8cd9', // color-background-dots-secondary = primary-dark-tint-2
|
|
50
50
|
labelBadgeFill: '#252538', // color-surface-low = dark-shade-95
|
|
51
51
|
labelBadgeBorder: '#313143', // color-surface-lowest = dark-shade-90
|
|
52
|
-
nodeIconColor: '#
|
|
52
|
+
nodeIconColor: '#8e9eff', // color-brand-main = primary-dark-tint-1
|
|
53
53
|
scopeAreaStroke: '#5f5f6d', // color-border-subtle = dark-shade-70
|
|
54
|
-
nodeShadowOpacity: 0
|
|
55
|
-
dotOpacity: 0.
|
|
54
|
+
nodeShadowOpacity: 0,
|
|
55
|
+
dotOpacity: 0.4,
|
|
56
56
|
};
|
|
57
57
|
const LIGHT_PALETTE = {
|
|
58
58
|
background: '#f6f7ff', // color-brand-subtle-bg = primary-extended-tint-95
|
|
@@ -61,13 +61,13 @@ const LIGHT_PALETTE = {
|
|
|
61
61
|
labelColor: '#223354', // secondary-base (light)
|
|
62
62
|
sublabelColor: '#808080', // shade-50
|
|
63
63
|
connectionColor: '#b3b3b3', // shade-30
|
|
64
|
-
dotColor: '#
|
|
64
|
+
dotColor: '#4a5ce0', // color-background-dots-secondary = primary-shade-2
|
|
65
65
|
labelBadgeFill: '#ffffff', // surface-main — 80% opacity applied in renderer
|
|
66
66
|
labelBadgeBorder: '#e6e6e6', // shade-10
|
|
67
|
-
nodeIconColor: '#5468ff', // primary-base
|
|
67
|
+
nodeIconColor: '#5468ff', // color-brand-main = primary-base
|
|
68
68
|
scopeAreaStroke: '#cccccc', // color-border-default
|
|
69
|
-
nodeShadowOpacity: 0
|
|
70
|
-
dotOpacity: 0.
|
|
69
|
+
nodeShadowOpacity: 0,
|
|
70
|
+
dotOpacity: 0.4,
|
|
71
71
|
};
|
|
72
72
|
export function getTheme(name) {
|
|
73
73
|
return name === 'light' ? LIGHT_PALETTE : DARK_PALETTE;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.33.
|
|
1
|
+
export declare const VERSION = "0.33.2";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.2",
|
|
4
4
|
"description": "Flow Weaver: deterministic TypeScript workflow compiler. Define workflows with JSDoc annotations, compile to standalone functions with zero runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|