pict-section-flow 0.0.16 → 0.0.18
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 +18 -18
- package/docs/Architecture.md +1 -1
- package/docs/Data_Model.md +2 -2
- package/docs/Getting_Started.md +5 -5
- package/docs/Implementation_Reference.md +6 -6
- package/docs/Layout_Persistence.md +3 -3
- package/docs/README.md +12 -12
- package/docs/_cover.md +1 -1
- package/docs/_sidebar.md +6 -6
- package/docs/_version.json +7 -0
- package/docs/api/PictFlowCard.md +6 -6
- package/docs/api/PictFlowCardPropertiesPanel.md +2 -2
- package/docs/api/addConnection.md +4 -4
- package/docs/api/addNode.md +6 -6
- package/docs/api/autoLayout.md +2 -2
- package/docs/api/getFlowData.md +5 -5
- package/docs/api/marshalToView.md +3 -3
- package/docs/api/openPanel.md +2 -2
- package/docs/api/registerHandler.md +3 -3
- package/docs/api/registerNodeType.md +3 -3
- package/docs/api/removeConnection.md +5 -5
- package/docs/api/removeNode.md +6 -6
- package/docs/api/saveLayout.md +2 -2
- package/docs/api/screenToSVGCoords.md +2 -2
- package/docs/api/selectNode.md +3 -3
- package/docs/api/setTheme.md +2 -2
- package/docs/api/setZoom.md +3 -3
- package/docs/api/toggleFullscreen.md +2 -2
- package/docs/card-help/EACH.md +3 -3
- package/docs/card-help/FREAD.md +5 -5
- package/docs/card-help/FWRITE.md +5 -5
- package/docs/card-help/GET.md +2 -2
- package/docs/card-help/ITE.md +3 -3
- package/docs/card-help/LOG.md +4 -4
- package/docs/card-help/NOTE.md +1 -1
- package/docs/card-help/PREV.md +2 -2
- package/docs/card-help/SET.md +5 -5
- package/docs/card-help/SPKL.md +2 -2
- package/docs/card-help/STAT.md +3 -3
- package/docs/card-help/SW.md +4 -4
- package/docs/css/docuserve.css +277 -23
- package/docs/index.html +2 -2
- package/docs/retold-catalog.json +1 -1
- package/docs/retold-keyword-index.json +1 -1
- package/example_applications/simple_cards/css/flowexample.css +2 -2
- package/example_applications/simple_cards/source/card-help-content.js +12 -12
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +1 -1
- package/example_applications/simple_cards/source/sample-flows.js +410 -0
- package/example_applications/simple_cards/source/views/PictView-FlowExample-About.js +5 -5
- package/example_applications/simple_cards/source/views/PictView-FlowExample-Documentation.js +5 -5
- package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +4 -4
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +141 -8
- package/example_applications/simple_cards/source/views/PictView-FlowExample-TopBar.js +2 -2
- package/package.json +3 -2
- package/source/Pict-Section-Flow.js +26 -0
- package/source/providers/PictProvider-Flow-CSS.js +244 -14
- package/source/providers/PictProvider-Flow-Theme.js +7 -7
- package/source/providers/edges/Edge-Bezier.js +41 -0
- package/source/providers/edges/Edge-Orthogonal.js +37 -0
- package/source/providers/edges/Edge-OrthogonalSnap.js +72 -0
- package/source/providers/edges/Edge-Perimeter-Linear.js +31 -0
- package/source/providers/edges/Edge-Perimeter-Orthogonal.js +39 -0
- package/source/providers/edges/Edge-Perimeter.js +48 -0
- package/source/providers/edges/Edge-PerimeterMath.js +92 -0
- package/source/providers/edges/Edge-Straight.js +24 -0
- package/source/providers/layouts/Layout-Circular.js +203 -0
- package/source/providers/layouts/Layout-Coerce.js +40 -0
- package/source/providers/layouts/Layout-Columnar.js +134 -0
- package/source/providers/layouts/Layout-Custom.js +27 -0
- package/source/providers/layouts/Layout-ForcedFromCenter.js +256 -0
- package/source/providers/layouts/Layout-Grid.js +134 -0
- package/source/providers/layouts/Layout-Layered.js +209 -0
- package/source/providers/layouts/Layout-Tabular.js +94 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +532 -28
- package/source/services/PictService-Flow-DataManager.js +12 -1
- package/source/services/PictService-Flow-Layout.js +305 -121
- package/source/services/PictService-Flow-PortRenderer.js +122 -26
- package/source/services/PictService-Flow-RenderManager.js +41 -11
- package/source/views/PictView-Flow-FloatingToolbar.js +3 -3
- package/source/views/PictView-Flow-Node.js +28 -0
- package/source/views/PictView-Flow-Toolbar.js +715 -10
- package/source/views/PictView-Flow.js +272 -5
- package/test/Layout_tests.js +1400 -0
- package/test/PortRenderer_tests.js +11 -2
|
@@ -33,7 +33,7 @@ class FlowCardDataPreview extends libPictFlowCard
|
|
|
33
33
|
BodyContent:
|
|
34
34
|
{
|
|
35
35
|
ContentType: 'html',
|
|
36
|
-
Template: '<table style="width:100%;border-collapse:collapse;font-size:9px;color:#2c3e50"><tr style="background:#d6eaf8"><td style="padding:3px 5px;font-weight:600">Field</td><td style="padding:3px 5px;font-weight:600">Type</td><td style="padding:3px 5px;font-weight:600">Value</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">name</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">str</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">"config"</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">count</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">num</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">42</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">active</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">bool</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color
|
|
36
|
+
Template: '<table style="width:100%;border-collapse:collapse;font-size:9px;color:#2c3e50"><tr style="background:#d6eaf8"><td style="padding:3px 5px;font-weight:600">Field</td><td style="padding:3px 5px;font-weight:600">Type</td><td style="padding:3px 5px;font-weight:600">Value</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">name</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">str</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">"config"</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">count</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">num</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">42</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">active</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">bool</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:var(--theme-color-status-success, #27ae60)">true</td></tr></table>'
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
pOptions),
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sample-flows.js
|
|
3
|
+
*
|
|
4
|
+
* Showcase graphs for trying out the seven layout algorithms in
|
|
5
|
+
* pict-section-flow. Each sample is a small flow definition with
|
|
6
|
+
* `Name`, `Description`, `Recommended` (layout that shines), and
|
|
7
|
+
* `Flow` (a `_FlowData`-shaped object).
|
|
8
|
+
*
|
|
9
|
+
* Use the dropdown above the flow diagram to load a sample, then
|
|
10
|
+
* open the Algorithm popup to compare layouts. The descriptions
|
|
11
|
+
* call out which layouts shine and which struggle on each shape.
|
|
12
|
+
*
|
|
13
|
+
* All nodes use the 'default' card type with simple In/Out ports —
|
|
14
|
+
* the focus is on graph topology, not on the cards themselves.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Ultravisor-flavored category palette — same families as
|
|
18
|
+
// Ultravisor-CardConfigGenerator's _CategoryColors so the per-node
|
|
19
|
+
// hint/border colors here read the same way they do in real flows.
|
|
20
|
+
const _CATEGORY_COLORS =
|
|
21
|
+
{
|
|
22
|
+
'core': { TitleBarColor: '#ab47bc', BodyStyle: { fill: '#f3e5f5', stroke: '#ab47bc' } },
|
|
23
|
+
'flow': { TitleBarColor: '#78909c', BodyStyle: { fill: '#eceff1', stroke: '#78909c' } },
|
|
24
|
+
'data': { TitleBarColor: '#ff9800', BodyStyle: { fill: '#fff3e0', stroke: '#ff9800' } },
|
|
25
|
+
'file-io': { TitleBarColor: '#42a5f5', BodyStyle: { fill: '#eaf2f8', stroke: '#42a5f5' } },
|
|
26
|
+
'rest': { TitleBarColor: '#29b6f6', BodyStyle: { fill: '#e1f5fe', stroke: '#29b6f6' } },
|
|
27
|
+
'meadow': { TitleBarColor: '#66bb6a', BodyStyle: { fill: '#e8f5e9', stroke: '#66bb6a' } },
|
|
28
|
+
'pipeline': { TitleBarColor: '#ec407a', BodyStyle: { fill: '#fce4ec', stroke: '#ec407a' } },
|
|
29
|
+
'llm': { TitleBarColor: '#26a69a', BodyStyle: { fill: '#e0f7fa', stroke: '#26a69a' } },
|
|
30
|
+
'ext': { TitleBarColor: '#9c6afe', BodyStyle: { fill: '#ede9fe', stroke: '#9c6afe' } }
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function _node(pHash, pTitle, pX, pY, pCategory)
|
|
34
|
+
{
|
|
35
|
+
let tmpNode =
|
|
36
|
+
{
|
|
37
|
+
Hash: pHash,
|
|
38
|
+
Type: 'default',
|
|
39
|
+
X: pX,
|
|
40
|
+
Y: pY,
|
|
41
|
+
Width: 140,
|
|
42
|
+
Height: 70,
|
|
43
|
+
Title: pTitle,
|
|
44
|
+
Ports:
|
|
45
|
+
[
|
|
46
|
+
{ Hash: `${pHash}-in`, Direction: 'input', Side: 'left', Label: 'In' },
|
|
47
|
+
{ Hash: `${pHash}-out`, Direction: 'output', Side: 'right', Label: 'Out' }
|
|
48
|
+
],
|
|
49
|
+
Data: {}
|
|
50
|
+
};
|
|
51
|
+
if (pCategory && _CATEGORY_COLORS[pCategory])
|
|
52
|
+
{
|
|
53
|
+
let tmpColors = _CATEGORY_COLORS[pCategory];
|
|
54
|
+
tmpNode.TitleBarColor = tmpColors.TitleBarColor;
|
|
55
|
+
tmpNode.BodyStyle = tmpColors.BodyStyle;
|
|
56
|
+
}
|
|
57
|
+
return tmpNode;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function _edge(pSourceHash, pTargetHash, pSuffix)
|
|
61
|
+
{
|
|
62
|
+
let tmpHash = `c-${pSourceHash}-${pTargetHash}${pSuffix ? `-${pSuffix}` : ''}`;
|
|
63
|
+
return {
|
|
64
|
+
Hash: tmpHash,
|
|
65
|
+
SourceNodeHash: pSourceHash,
|
|
66
|
+
SourcePortHash: `${pSourceHash}-out`,
|
|
67
|
+
TargetNodeHash: pTargetHash,
|
|
68
|
+
TargetPortHash: `${pTargetHash}-in`,
|
|
69
|
+
Data: {}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _emptyViewState()
|
|
74
|
+
{
|
|
75
|
+
return { PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null, SelectedTetherHash: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _flow(pNodes, pConnections, pAlgorithm, pParameters, pAutoApply)
|
|
79
|
+
{
|
|
80
|
+
return {
|
|
81
|
+
Nodes: pNodes,
|
|
82
|
+
Connections: pConnections || [],
|
|
83
|
+
OpenPanels: [],
|
|
84
|
+
SavedLayouts: [],
|
|
85
|
+
ViewState: _emptyViewState(),
|
|
86
|
+
LayoutAlgorithm: pAlgorithm || 'Custom',
|
|
87
|
+
LayoutParameters: pParameters || {},
|
|
88
|
+
LayoutAutoApply: !!pAutoApply
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── 1. Linear Chain ────────────────────────────────────────────────────
|
|
93
|
+
// A → B → C → D → E → F
|
|
94
|
+
function _linearChain()
|
|
95
|
+
{
|
|
96
|
+
let tmpCount = 6;
|
|
97
|
+
let tmpNodes = [];
|
|
98
|
+
let tmpEdges = [];
|
|
99
|
+
for (let i = 0; i < tmpCount; i++)
|
|
100
|
+
{
|
|
101
|
+
let tmpHash = `lc-${String.fromCharCode(65 + i)}`;
|
|
102
|
+
tmpNodes.push(_node(tmpHash, String.fromCharCode(65 + i), 100, 100 + i * 110));
|
|
103
|
+
if (i > 0)
|
|
104
|
+
{
|
|
105
|
+
let tmpPrev = `lc-${String.fromCharCode(65 + i - 1)}`;
|
|
106
|
+
tmpEdges.push(_edge(tmpPrev, tmpHash));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return _flow(tmpNodes, tmpEdges);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── 2. Binary Tree (depth 3) ───────────────────────────────────────────
|
|
113
|
+
// Root → 2 children → 4 grandchildren = 7 nodes
|
|
114
|
+
function _binaryTree()
|
|
115
|
+
{
|
|
116
|
+
let tmpNodes = [];
|
|
117
|
+
let tmpEdges = [];
|
|
118
|
+
let tmpHashes = ['root', 'l', 'r', 'll', 'lr', 'rl', 'rr'];
|
|
119
|
+
let tmpLabels = ['Root', 'L', 'R', 'LL', 'LR', 'RL', 'RR'];
|
|
120
|
+
let tmpPositions = [
|
|
121
|
+
[400, 100], // root
|
|
122
|
+
[250, 240], [550, 240], // l, r
|
|
123
|
+
[150, 380], [350, 380], [450, 380], [650, 380] // ll lr rl rr
|
|
124
|
+
];
|
|
125
|
+
for (let i = 0; i < tmpHashes.length; i++)
|
|
126
|
+
{
|
|
127
|
+
tmpNodes.push(_node(`bt-${tmpHashes[i]}`, tmpLabels[i], tmpPositions[i][0], tmpPositions[i][1]));
|
|
128
|
+
}
|
|
129
|
+
tmpEdges.push(_edge('bt-root', 'bt-l'));
|
|
130
|
+
tmpEdges.push(_edge('bt-root', 'bt-r'));
|
|
131
|
+
tmpEdges.push(_edge('bt-l', 'bt-ll'));
|
|
132
|
+
tmpEdges.push(_edge('bt-l', 'bt-lr'));
|
|
133
|
+
tmpEdges.push(_edge('bt-r', 'bt-rl'));
|
|
134
|
+
tmpEdges.push(_edge('bt-r', 'bt-rr'));
|
|
135
|
+
return _flow(tmpNodes, tmpEdges);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── 3. Star Hub (1 hub + 8 spokes) ─────────────────────────────────────
|
|
139
|
+
// Each spoke gets a distinct Ultravisor-style category color so the
|
|
140
|
+
// hint beziers (which are colored by the *other end's* identity) fan
|
|
141
|
+
// out in 8 different colors when you hover the hub's "Out" badge.
|
|
142
|
+
function _starHub()
|
|
143
|
+
{
|
|
144
|
+
let tmpNodes = [];
|
|
145
|
+
let tmpEdges = [];
|
|
146
|
+
let tmpSpokeKinds =
|
|
147
|
+
[
|
|
148
|
+
{ key: 'rest', label: 'REST API' },
|
|
149
|
+
{ key: 'meadow', label: 'Meadow' },
|
|
150
|
+
{ key: 'data', label: 'Transform' },
|
|
151
|
+
{ key: 'file-io', label: 'File I/O' },
|
|
152
|
+
{ key: 'pipeline', label: 'Pipeline' },
|
|
153
|
+
{ key: 'llm', label: 'LLM' },
|
|
154
|
+
{ key: 'ext', label: 'Extension' },
|
|
155
|
+
{ key: 'flow', label: 'Control' }
|
|
156
|
+
];
|
|
157
|
+
tmpNodes.push(_node('star-hub', 'Hub', 400, 350, 'core'));
|
|
158
|
+
for (let i = 0; i < tmpSpokeKinds.length; i++)
|
|
159
|
+
{
|
|
160
|
+
let tmpAngle = (i / tmpSpokeKinds.length) * 2 * Math.PI;
|
|
161
|
+
let tmpX = 400 + Math.round(Math.cos(tmpAngle) * 240);
|
|
162
|
+
let tmpY = 350 + Math.round(Math.sin(tmpAngle) * 240);
|
|
163
|
+
let tmpHash = `star-s${i}`;
|
|
164
|
+
tmpNodes.push(_node(tmpHash, tmpSpokeKinds[i].label, tmpX, tmpY, tmpSpokeKinds[i].key));
|
|
165
|
+
tmpEdges.push(_edge('star-hub', tmpHash));
|
|
166
|
+
}
|
|
167
|
+
return _flow(tmpNodes, tmpEdges);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── 4. Diamond Lattice ─────────────────────────────────────────────────
|
|
171
|
+
// Start → {A,B,C} → {D,E,F} (cross-edges) → Merge → End. Nine nodes.
|
|
172
|
+
function _diamondLattice()
|
|
173
|
+
{
|
|
174
|
+
let tmpNodes =
|
|
175
|
+
[
|
|
176
|
+
_node('dl-start', 'Start', 100, 200),
|
|
177
|
+
_node('dl-a', 'A', 300, 100),
|
|
178
|
+
_node('dl-b', 'B', 300, 250),
|
|
179
|
+
_node('dl-c', 'C', 300, 400),
|
|
180
|
+
_node('dl-d', 'D', 550, 100),
|
|
181
|
+
_node('dl-e', 'E', 550, 250),
|
|
182
|
+
_node('dl-f', 'F', 550, 400),
|
|
183
|
+
_node('dl-merge', 'Merge', 800, 250),
|
|
184
|
+
_node('dl-end', 'End', 1050, 250)
|
|
185
|
+
];
|
|
186
|
+
let tmpEdges =
|
|
187
|
+
[
|
|
188
|
+
_edge('dl-start', 'dl-a'),
|
|
189
|
+
_edge('dl-start', 'dl-b'),
|
|
190
|
+
_edge('dl-start', 'dl-c'),
|
|
191
|
+
_edge('dl-a', 'dl-d'),
|
|
192
|
+
_edge('dl-a', 'dl-e'), // cross
|
|
193
|
+
_edge('dl-b', 'dl-e'),
|
|
194
|
+
_edge('dl-c', 'dl-e'), // cross
|
|
195
|
+
_edge('dl-c', 'dl-f'),
|
|
196
|
+
_edge('dl-d', 'dl-merge'),
|
|
197
|
+
_edge('dl-e', 'dl-merge'),
|
|
198
|
+
_edge('dl-f', 'dl-merge'),
|
|
199
|
+
_edge('dl-merge', 'dl-end')
|
|
200
|
+
];
|
|
201
|
+
return _flow(tmpNodes, tmpEdges);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ── 5. Disconnected Components (3 chains × 4 nodes) ────────────────────
|
|
205
|
+
function _disconnectedComponents()
|
|
206
|
+
{
|
|
207
|
+
let tmpNodes = [];
|
|
208
|
+
let tmpEdges = [];
|
|
209
|
+
let tmpClusters = ['α', 'β', 'γ'];
|
|
210
|
+
for (let c = 0; c < tmpClusters.length; c++)
|
|
211
|
+
{
|
|
212
|
+
for (let i = 0; i < 4; i++)
|
|
213
|
+
{
|
|
214
|
+
let tmpHash = `dc-${tmpClusters[c]}-${i}`;
|
|
215
|
+
tmpNodes.push(_node(tmpHash, `${tmpClusters[c]}${i}`, 150 + c * 350, 100 + i * 110));
|
|
216
|
+
if (i > 0)
|
|
217
|
+
{
|
|
218
|
+
let tmpPrev = `dc-${tmpClusters[c]}-${i - 1}`;
|
|
219
|
+
tmpEdges.push(_edge(tmpPrev, tmpHash));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return _flow(tmpNodes, tmpEdges);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── 6. Dense Cluster (7 nodes, ~15 edges, near-complete) ───────────────
|
|
227
|
+
function _denseCluster()
|
|
228
|
+
{
|
|
229
|
+
let tmpNodes = [];
|
|
230
|
+
let tmpEdges = [];
|
|
231
|
+
let tmpCount = 7;
|
|
232
|
+
for (let i = 0; i < tmpCount; i++)
|
|
233
|
+
{
|
|
234
|
+
let tmpAngle = (i / tmpCount) * 2 * Math.PI;
|
|
235
|
+
let tmpX = 400 + Math.round(Math.cos(tmpAngle) * 200);
|
|
236
|
+
let tmpY = 300 + Math.round(Math.sin(tmpAngle) * 200);
|
|
237
|
+
tmpNodes.push(_node(`dn-${i}`, `N${i}`, tmpX, tmpY));
|
|
238
|
+
}
|
|
239
|
+
// Connect each node to ~3 others (skip-1 and skip-2 patterns) — yields ~14 edges
|
|
240
|
+
for (let i = 0; i < tmpCount; i++)
|
|
241
|
+
{
|
|
242
|
+
tmpEdges.push(_edge(`dn-${i}`, `dn-${(i + 1) % tmpCount}`));
|
|
243
|
+
tmpEdges.push(_edge(`dn-${i}`, `dn-${(i + 2) % tmpCount}`));
|
|
244
|
+
}
|
|
245
|
+
return _flow(tmpNodes, tmpEdges);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── 7. Cyclic Network (8 nodes, ring + chord) ──────────────────────────
|
|
249
|
+
function _cyclicNetwork()
|
|
250
|
+
{
|
|
251
|
+
let tmpNodes = [];
|
|
252
|
+
let tmpEdges = [];
|
|
253
|
+
let tmpCount = 8;
|
|
254
|
+
for (let i = 0; i < tmpCount; i++)
|
|
255
|
+
{
|
|
256
|
+
let tmpAngle = (i / tmpCount) * 2 * Math.PI;
|
|
257
|
+
let tmpX = 400 + Math.round(Math.cos(tmpAngle) * 220);
|
|
258
|
+
let tmpY = 300 + Math.round(Math.sin(tmpAngle) * 220);
|
|
259
|
+
tmpNodes.push(_node(`cy-${i}`, `Z${i}`, tmpX, tmpY));
|
|
260
|
+
}
|
|
261
|
+
// Ring of forward edges 0→1→2→…→7→0 (back-edge creates the cycle)
|
|
262
|
+
for (let i = 0; i < tmpCount; i++)
|
|
263
|
+
{
|
|
264
|
+
tmpEdges.push(_edge(`cy-${i}`, `cy-${(i + 1) % tmpCount}`));
|
|
265
|
+
}
|
|
266
|
+
// One chord across the ring
|
|
267
|
+
tmpEdges.push(_edge('cy-0', 'cy-4'));
|
|
268
|
+
return _flow(tmpNodes, tmpEdges);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ── 8. Mesh 4×4 (16 nodes, 4-neighbor edges) ───────────────────────────
|
|
272
|
+
function _mesh4x4()
|
|
273
|
+
{
|
|
274
|
+
let tmpNodes = [];
|
|
275
|
+
let tmpEdges = [];
|
|
276
|
+
let tmpRows = 4, tmpCols = 4;
|
|
277
|
+
for (let r = 0; r < tmpRows; r++)
|
|
278
|
+
{
|
|
279
|
+
for (let c = 0; c < tmpCols; c++)
|
|
280
|
+
{
|
|
281
|
+
let tmpHash = `mesh-${r}-${c}`;
|
|
282
|
+
tmpNodes.push(_node(tmpHash, `(${r},${c})`, 150 + c * 180, 100 + r * 130));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
for (let r = 0; r < tmpRows; r++)
|
|
286
|
+
{
|
|
287
|
+
for (let c = 0; c < tmpCols; c++)
|
|
288
|
+
{
|
|
289
|
+
if (c < tmpCols - 1) tmpEdges.push(_edge(`mesh-${r}-${c}`, `mesh-${r}-${c + 1}`));
|
|
290
|
+
if (r < tmpRows - 1) tmpEdges.push(_edge(`mesh-${r}-${c}`, `mesh-${r + 1}-${c}`));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return _flow(tmpNodes, tmpEdges);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── 9. Floating Widgets (12 isolated nodes, no edges) ──────────────────
|
|
297
|
+
function _floatingWidgets()
|
|
298
|
+
{
|
|
299
|
+
let tmpNodes = [];
|
|
300
|
+
let tmpLabels = ['Chart', 'Table', 'KPI', 'Gauge', 'Sparkline', 'Heatmap', 'Pie', 'Bar', 'Map', 'Timeline', 'Note', 'Image'];
|
|
301
|
+
for (let i = 0; i < tmpLabels.length; i++)
|
|
302
|
+
{
|
|
303
|
+
let tmpRow = Math.floor(i / 4);
|
|
304
|
+
let tmpCol = i % 4;
|
|
305
|
+
tmpNodes.push(_node(`fw-${i}`, tmpLabels[i], 150 + tmpCol * 180, 100 + tmpRow * 130));
|
|
306
|
+
}
|
|
307
|
+
return _flow(tmpNodes, []);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ── 10. Wide Fan-Out (1 root → 12 leaves) ──────────────────────────────
|
|
311
|
+
function _wideFanOut()
|
|
312
|
+
{
|
|
313
|
+
let tmpNodes = [];
|
|
314
|
+
let tmpEdges = [];
|
|
315
|
+
tmpNodes.push(_node('fan-root', 'Source', 100, 400));
|
|
316
|
+
for (let i = 0; i < 12; i++)
|
|
317
|
+
{
|
|
318
|
+
let tmpHash = `fan-leaf-${i}`;
|
|
319
|
+
tmpNodes.push(_node(tmpHash, `L${i}`, 400 + (i % 4) * 160, 100 + Math.floor(i / 4) * 130));
|
|
320
|
+
tmpEdges.push(_edge('fan-root', tmpHash));
|
|
321
|
+
}
|
|
322
|
+
return _flow(tmpNodes, tmpEdges);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const SAMPLE_FLOWS =
|
|
326
|
+
{
|
|
327
|
+
'linear-chain':
|
|
328
|
+
{
|
|
329
|
+
Name: 'Linear Chain',
|
|
330
|
+
Description: 'A → B → C → D → E → F. Six nodes, single path. Layered (left-to-right) and Tabular (top-to-bottom) read like a story; Circular wastes a ring on what should be a line; ForcedFromCenter is overkill.',
|
|
331
|
+
Recommended: 'Layered',
|
|
332
|
+
Flow: _linearChain()
|
|
333
|
+
},
|
|
334
|
+
'binary-tree':
|
|
335
|
+
{
|
|
336
|
+
Name: 'Binary Tree (depth 3)',
|
|
337
|
+
Description: 'Root branches into 2 children, each into 2 grandchildren. Layered shows the hierarchy clearly (depth-by-depth); Circular puts the root at center with leaves on outer ring; Grid loses the parent-child relationships entirely.',
|
|
338
|
+
Recommended: 'Layered',
|
|
339
|
+
Flow: _binaryTree()
|
|
340
|
+
},
|
|
341
|
+
'star-hub':
|
|
342
|
+
{
|
|
343
|
+
Name: 'Star Hub (8 spokes)',
|
|
344
|
+
Description: 'One hub connected to 8 leaves. Circular shines (root at center, spokes evenly around); Layered crowds all 8 spokes in a single second column; ForcedFromCenter does an OK job; Grid ignores the topology completely.',
|
|
345
|
+
Recommended: 'Circular',
|
|
346
|
+
Flow: _starHub()
|
|
347
|
+
},
|
|
348
|
+
'diamond-lattice':
|
|
349
|
+
{
|
|
350
|
+
Name: 'Diamond Lattice (DAG)',
|
|
351
|
+
Description: 'Start fans out to 3 paths through 6 middle nodes (with cross-edges) and merges at End. Layered is in its element — the parallel depth structure becomes obvious. Tabular flattens it; Circular loses direction.',
|
|
352
|
+
Recommended: 'Layered',
|
|
353
|
+
Flow: _diamondLattice()
|
|
354
|
+
},
|
|
355
|
+
'disconnected-components':
|
|
356
|
+
{
|
|
357
|
+
Name: 'Disconnected Components',
|
|
358
|
+
Description: 'Three independent chains of 4 nodes each, no cross-edges. Grid and Columnar give each chain equal real estate. Layered piles the 3 roots in one column. ForcedFromCenter clusters them naturally but with no separation guarantee.',
|
|
359
|
+
Recommended: 'Columnar',
|
|
360
|
+
Flow: _disconnectedComponents()
|
|
361
|
+
},
|
|
362
|
+
'dense-cluster':
|
|
363
|
+
{
|
|
364
|
+
Name: 'Dense Cluster (near-complete)',
|
|
365
|
+
Description: 'Seven nodes with ~14 edges (each node connects to its two nearest neighbors in both directions). ForcedFromCenter shines — spring forces find a clean radial layout. Layered tries to topo-sort it and produces a wide-and-shallow mess. Circular is also pretty good here.',
|
|
366
|
+
Recommended: 'ForcedFromCenter',
|
|
367
|
+
Flow: _denseCluster()
|
|
368
|
+
},
|
|
369
|
+
'cyclic-network':
|
|
370
|
+
{
|
|
371
|
+
Name: 'Cyclic Network',
|
|
372
|
+
Description: 'Eight nodes in a ring plus one chord. The cycle defeats Kahn topological sort — Layered falls back to a "remaining nodes" pass (correct but ugly). Circular renders the ring cleanly. ForcedFromCenter finds a relaxed equilibrium.',
|
|
373
|
+
Recommended: 'Circular',
|
|
374
|
+
Flow: _cyclicNetwork()
|
|
375
|
+
},
|
|
376
|
+
'mesh-4x4':
|
|
377
|
+
{
|
|
378
|
+
Name: 'Mesh 4×4',
|
|
379
|
+
Description: '16 nodes in a 4×4 grid topology with right/down neighbor edges. Grid is the obvious win (the layout matches the data). Columnar (4 cols, FillOrder=row) also nails it. Layered produces a long zig-zag through 7 layers.',
|
|
380
|
+
Recommended: 'Grid',
|
|
381
|
+
Flow: _mesh4x4()
|
|
382
|
+
},
|
|
383
|
+
'floating-widgets':
|
|
384
|
+
{
|
|
385
|
+
Name: 'Floating Widgets (no edges)',
|
|
386
|
+
Description: '12 unconnected nodes — exactly the dashboard-builder shape. Grid, Columnar, and Tabular all produce clean deterministic arrangements. Layered, Circular, and ForcedFromCenter degrade gracefully (single layer / single ring / random spread).',
|
|
387
|
+
Recommended: 'Grid',
|
|
388
|
+
Flow: _floatingWidgets()
|
|
389
|
+
},
|
|
390
|
+
'wide-fan-out':
|
|
391
|
+
{
|
|
392
|
+
Name: 'Wide Fan-Out (1 → 12)',
|
|
393
|
+
Description: 'One source node connects to 12 leaves. Circular places the source at center with leaves on a single outer ring — clean and balanced. Layered makes the second column 12 nodes tall. Grid spreads leaves and loses the source-to-leaf relationship.',
|
|
394
|
+
Recommended: 'Circular',
|
|
395
|
+
Flow: _wideFanOut()
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
module.exports =
|
|
400
|
+
{
|
|
401
|
+
SAMPLE_FLOWS: SAMPLE_FLOWS,
|
|
402
|
+
getSampleNames: function ()
|
|
403
|
+
{
|
|
404
|
+
return Object.keys(SAMPLE_FLOWS);
|
|
405
|
+
},
|
|
406
|
+
getSample: function (pKey)
|
|
407
|
+
{
|
|
408
|
+
return SAMPLE_FLOWS[pKey] || null;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
@@ -18,7 +18,7 @@ const _ViewConfiguration =
|
|
|
18
18
|
.flowexample-about-header {
|
|
19
19
|
text-align: center;
|
|
20
20
|
padding-bottom: 1.5em;
|
|
21
|
-
border-bottom: 1px solid #eee;
|
|
21
|
+
border-bottom: 1px solid var(--theme-color-border-light, #eee);
|
|
22
22
|
margin-bottom: 2em;
|
|
23
23
|
}
|
|
24
24
|
.flowexample-about-header h1 {
|
|
@@ -39,7 +39,7 @@ const _ViewConfiguration =
|
|
|
39
39
|
font-size: 1.3em;
|
|
40
40
|
}
|
|
41
41
|
.flowexample-about p {
|
|
42
|
-
color: #555;
|
|
42
|
+
color: var(--theme-color-text-secondary, #555);
|
|
43
43
|
line-height: 1.7;
|
|
44
44
|
}
|
|
45
45
|
.flowexample-about-tech {
|
|
@@ -62,7 +62,7 @@ const _ViewConfiguration =
|
|
|
62
62
|
}
|
|
63
63
|
.flowexample-about-tech-item span {
|
|
64
64
|
font-size: 0.85em;
|
|
65
|
-
color: #666;
|
|
65
|
+
color: var(--theme-color-text-secondary, #666);
|
|
66
66
|
}
|
|
67
67
|
.flowexample-about-features {
|
|
68
68
|
display: grid;
|
|
@@ -71,8 +71,8 @@ const _ViewConfiguration =
|
|
|
71
71
|
margin-top: 1em;
|
|
72
72
|
}
|
|
73
73
|
.flowexample-about-feature {
|
|
74
|
-
background: #fff;
|
|
75
|
-
border: 1px solid #e0e0e0;
|
|
74
|
+
background: var(--theme-color-background-panel, #fff);
|
|
75
|
+
border: 1px solid var(--theme-color-border-default, #e0e0e0);
|
|
76
76
|
border-radius: 6px;
|
|
77
77
|
padding: 1.25em;
|
|
78
78
|
}
|
package/example_applications/simple_cards/source/views/PictView-FlowExample-Documentation.js
CHANGED
|
@@ -18,7 +18,7 @@ const _ViewConfiguration =
|
|
|
18
18
|
.flowexample-docs-header {
|
|
19
19
|
text-align: center;
|
|
20
20
|
padding-bottom: 1.5em;
|
|
21
|
-
border-bottom: 1px solid #eee;
|
|
21
|
+
border-bottom: 1px solid var(--theme-color-border-light, #eee);
|
|
22
22
|
margin-bottom: 2em;
|
|
23
23
|
}
|
|
24
24
|
.flowexample-docs-header h1 {
|
|
@@ -37,7 +37,7 @@ const _ViewConfiguration =
|
|
|
37
37
|
font-weight: 400;
|
|
38
38
|
color: #2c3e50;
|
|
39
39
|
font-size: 1.3em;
|
|
40
|
-
border-bottom: 1px solid #eee;
|
|
40
|
+
border-bottom: 1px solid var(--theme-color-border-light, #eee);
|
|
41
41
|
padding-bottom: 0.35em;
|
|
42
42
|
}
|
|
43
43
|
.flowexample-docs h3 {
|
|
@@ -47,7 +47,7 @@ const _ViewConfiguration =
|
|
|
47
47
|
font-size: 1.05em;
|
|
48
48
|
}
|
|
49
49
|
.flowexample-docs p {
|
|
50
|
-
color: #555;
|
|
50
|
+
color: var(--theme-color-text-secondary, #555);
|
|
51
51
|
line-height: 1.7;
|
|
52
52
|
}
|
|
53
53
|
.flowexample-docs code {
|
|
@@ -55,7 +55,7 @@ const _ViewConfiguration =
|
|
|
55
55
|
padding: 0.15em 0.4em;
|
|
56
56
|
border-radius: 3px;
|
|
57
57
|
font-size: 0.9em;
|
|
58
|
-
color: #e74c3c;
|
|
58
|
+
color: var(--theme-color-status-error, #e74c3c);
|
|
59
59
|
}
|
|
60
60
|
.flowexample-docs pre {
|
|
61
61
|
background: #2c3e50;
|
|
@@ -72,7 +72,7 @@ const _ViewConfiguration =
|
|
|
72
72
|
color: #ecf0f1;
|
|
73
73
|
}
|
|
74
74
|
.flowexample-docs ul {
|
|
75
|
-
color: #555;
|
|
75
|
+
color: var(--theme-color-text-secondary, #555);
|
|
76
76
|
line-height: 1.8;
|
|
77
77
|
padding-left: 1.5em;
|
|
78
78
|
}
|
package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js
CHANGED
|
@@ -17,17 +17,17 @@ const _ViewConfiguration =
|
|
|
17
17
|
<div style="padding:6px;font-size:12px;line-height:1.6;color:#2c3e50">
|
|
18
18
|
<div style="margin-bottom:8px">
|
|
19
19
|
<label style="font-weight:600;font-size:11px;color:#7f8c8d;text-transform:uppercase;letter-spacing:0.5px">Output Path</label>
|
|
20
|
-
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid #e0e0e0;border-radius:3px;margin-top:2px;font-family:monospace;font-size:11px">{~D:Record.Data.OutputPath~}</div>
|
|
20
|
+
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid var(--theme-color-border-default, #e0e0e0);border-radius:3px;margin-top:2px;font-family:monospace;font-size:11px">{~D:Record.Data.OutputPath~}</div>
|
|
21
21
|
</div>
|
|
22
22
|
<div style="margin-bottom:8px">
|
|
23
23
|
<label style="font-weight:600;font-size:11px;color:#7f8c8d;text-transform:uppercase;letter-spacing:0.5px">Write Mode</label>
|
|
24
|
-
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid #e0e0e0;border-radius:3px;margin-top:2px">{~D:Record.Data.WriteMode~}</div>
|
|
24
|
+
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid var(--theme-color-border-default, #e0e0e0);border-radius:3px;margin-top:2px">{~D:Record.Data.WriteMode~}</div>
|
|
25
25
|
</div>
|
|
26
26
|
<div style="margin-bottom:8px">
|
|
27
27
|
<label style="font-weight:600;font-size:11px;color:#7f8c8d;text-transform:uppercase;letter-spacing:0.5px">Encoding</label>
|
|
28
|
-
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid #e0e0e0;border-radius:3px;margin-top:2px">{~D:Record.Data.Encoding~}</div>
|
|
28
|
+
<div style="padding:4px 6px;background:#f8f9fa;border:1px solid var(--theme-color-border-default, #e0e0e0);border-radius:3px;margin-top:2px">{~D:Record.Data.Encoding~}</div>
|
|
29
29
|
</div>
|
|
30
|
-
<div style="padding:6px;background:#eafaf1;border:1px solid #27ae60;border-radius:3px;font-size:11px;color
|
|
30
|
+
<div style="padding:6px;background:#eafaf1;border:1px solid var(--theme-color-status-success, #27ae60);border-radius:3px;font-size:11px;color:var(--theme-color-status-success, #27ae60)">
|
|
31
31
|
This panel is rendered by a registered pict-view (View panel type).
|
|
32
32
|
</div>
|
|
33
33
|
</div>
|