pict-section-flow 0.0.10 → 0.0.13

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.
Files changed (88) hide show
  1. package/.claude/launch.json +1 -1
  2. package/README.md +176 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/Architecture.md +303 -0
  5. package/docs/Custom-Styling.md +275 -0
  6. package/docs/Data_Model.md +158 -0
  7. package/docs/Event_System.md +156 -0
  8. package/docs/Getting_Started.md +237 -0
  9. package/docs/Implementation_Reference.md +528 -0
  10. package/docs/Layout_Persistence.md +117 -0
  11. package/docs/README.md +115 -52
  12. package/docs/_cover.md +11 -0
  13. package/docs/_sidebar.md +52 -0
  14. package/docs/_topbar.md +8 -0
  15. package/docs/api/PictFlowCard.md +216 -0
  16. package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
  17. package/docs/api/addConnection.md +101 -0
  18. package/docs/api/addNode.md +137 -0
  19. package/docs/api/autoLayout.md +77 -0
  20. package/docs/api/getFlowData.md +112 -0
  21. package/docs/api/marshalToView.md +95 -0
  22. package/docs/api/openPanel.md +128 -0
  23. package/docs/api/registerHandler.md +174 -0
  24. package/docs/api/registerNodeType.md +142 -0
  25. package/docs/api/removeConnection.md +57 -0
  26. package/docs/api/removeNode.md +80 -0
  27. package/docs/api/saveLayout.md +152 -0
  28. package/docs/api/screenToSVGCoords.md +68 -0
  29. package/docs/api/selectNode.md +116 -0
  30. package/docs/api/setTheme.md +168 -0
  31. package/docs/api/setZoom.md +97 -0
  32. package/docs/api/toggleFullscreen.md +68 -0
  33. package/docs/card-help/EACH.md +19 -0
  34. package/docs/card-help/FREAD.md +24 -0
  35. package/docs/card-help/FWRITE.md +24 -0
  36. package/docs/card-help/GET.md +22 -0
  37. package/docs/card-help/ITE.md +23 -0
  38. package/docs/card-help/LOG.md +23 -0
  39. package/docs/card-help/NOTE.md +17 -0
  40. package/docs/card-help/PREV.md +18 -0
  41. package/docs/card-help/SET.md +27 -0
  42. package/docs/card-help/SPKL.md +22 -0
  43. package/docs/card-help/STAT.md +23 -0
  44. package/docs/card-help/SW.md +25 -0
  45. package/docs/css/docuserve.css +73 -0
  46. package/docs/index.html +39 -0
  47. package/docs/retold-catalog.json +169 -0
  48. package/docs/retold-keyword-index.json +13942 -0
  49. package/example_applications/simple_cards/package.json +1 -0
  50. package/example_applications/simple_cards/source/card-help-content.js +16 -0
  51. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
  52. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
  53. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
  54. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
  55. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
  56. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
  57. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
  58. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
  59. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
  60. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
  61. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
  62. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
  63. package/package.json +11 -7
  64. package/scripts/generate-card-help.js +214 -0
  65. package/source/Pict-Section-Flow.js +4 -0
  66. package/source/PictFlowCard.js +3 -1
  67. package/source/providers/PictProvider-Flow-CSS.js +245 -152
  68. package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
  69. package/source/providers/PictProvider-Flow-Geometry.js +195 -38
  70. package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
  71. package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
  72. package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
  73. package/source/services/PictService-Flow-DataManager.js +338 -0
  74. package/source/services/PictService-Flow-InteractionManager.js +165 -7
  75. package/source/services/PictService-Flow-PathGenerator.js +282 -0
  76. package/source/services/PictService-Flow-PortRenderer.js +269 -0
  77. package/source/services/PictService-Flow-RenderManager.js +281 -0
  78. package/source/services/PictService-Flow-Tether.js +6 -42
  79. package/source/views/PictView-Flow-Node.js +2 -220
  80. package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
  81. package/source/views/PictView-Flow.js +130 -882
  82. package/test/ConnectionHandleManager_tests.js +717 -0
  83. package/test/ConnectionRenderer_tests.js +591 -0
  84. package/test/DataManager_tests.js +859 -0
  85. package/test/Geometry_tests.js +767 -0
  86. package/test/PathGenerator_tests.js +978 -0
  87. package/test/PortRenderer_tests.js +367 -0
  88. package/test/RenderManager_tests.js +756 -0
@@ -0,0 +1,275 @@
1
+ # Custom Styling
2
+
3
+ The flow diagram exposes a comprehensive set of CSS custom properties (design tokens) on the `.pict-flow-container` element. Override any of these variables to theme the flow diagram without modifying source code.
4
+
5
+ ## Quick Start
6
+
7
+ Add a `<style>` block after the flow diagram initializes, or use a theme provider:
8
+
9
+ ```css
10
+ /* Override via CSS */
11
+ .pict-flow-container {
12
+ --pf-canvas-bg: #1a1a2e;
13
+ --pf-node-body-fill: #16213e;
14
+ --pf-node-body-stroke: #0f3460;
15
+ --pf-text-primary: #e8e8e8;
16
+ --pf-node-selected-stroke: #e94560;
17
+ }
18
+ ```
19
+
20
+ ```javascript
21
+ // Override via the Theme Provider API
22
+ flowView._ThemeProvider.registerTheme('dark',
23
+ {
24
+ Key: 'dark',
25
+ Label: 'Dark Mode',
26
+ CSSVariables:
27
+ {
28
+ '--pf-canvas-bg': '#1a1a2e',
29
+ '--pf-node-body-fill': '#16213e',
30
+ '--pf-node-body-stroke': '#0f3460',
31
+ '--pf-text-primary': '#e8e8e8',
32
+ '--pf-node-selected-stroke': '#e94560'
33
+ },
34
+ NodeBodyMode: 'rect'
35
+ });
36
+ flowView.setTheme('dark');
37
+ ```
38
+
39
+ ## Design Token Reference
40
+
41
+ All variables are prefixed `--pf-` (short for *pict-flow*) and defined on `.pict-flow-container`. Default values shown below are for the built-in Modern theme.
42
+
43
+ ### Text
44
+
45
+ | Variable | Default | Description |
46
+ |----------|---------|-------------|
47
+ | `--pf-text-primary` | `#2c3e50` | Primary text color (labels, body text, form values) |
48
+ | `--pf-text-heading` | `#1a252f` | Heading text (info panel headers) |
49
+ | `--pf-text-secondary` | `#7f8c8d` | Secondary text (descriptions, field labels) |
50
+ | `--pf-text-tertiary` | `#8e99a4` | Tertiary text (section titles, constraints) |
51
+ | `--pf-text-placeholder` | `#95a5a6` | Placeholder/muted text (category labels, code badges) |
52
+
53
+ ### Node
54
+
55
+ | Variable | Default | Description |
56
+ |----------|---------|-------------|
57
+ | `--pf-node-body-fill` | `#ffffff` | Node body background |
58
+ | `--pf-node-body-stroke` | `#d0d4d8` | Node body border |
59
+ | `--pf-node-body-stroke-hover` | `#b0b8c0` | Node body border on hover |
60
+ | `--pf-node-body-stroke-width` | `1` | Node body border width |
61
+ | `--pf-node-body-radius` | `8px` | Node body corner radius |
62
+ | `--pf-node-shadow` | `drop-shadow(0 1px 3px rgba(0,0,0,0.10))` | Default node shadow |
63
+ | `--pf-node-shadow-hover` | `drop-shadow(0 2px 6px rgba(0,0,0,0.15))` | Shadow on hover |
64
+ | `--pf-node-shadow-selected` | `drop-shadow(0 2px 8px rgba(52,152,219,0.25))` | Shadow when selected |
65
+ | `--pf-node-shadow-dragging` | `drop-shadow(0 4px 12px rgba(0,0,0,0.20))` | Shadow while dragging |
66
+ | `--pf-node-title-fill` | `#ffffff` | Title text color (on the title bar) |
67
+ | `--pf-node-title-size` | `11.5px` | Title font size |
68
+ | `--pf-node-title-weight` | `600` | Title font weight |
69
+ | `--pf-node-title-bar-color` | `#2c3e50` | Default title bar background color |
70
+ | `--pf-node-type-label-fill` | `#a0a8b0` | Node type label color (appears on hover) |
71
+ | `--pf-node-selected-stroke` | `#3498db` | Primary accent / selection stroke color |
72
+
73
+ ### Node Variants
74
+
75
+ Built-in node types override the node body fill and stroke. Use these tokens to customize variant colors globally.
76
+
77
+ | Variable | Default | Description |
78
+ |----------|---------|-------------|
79
+ | `--pf-node-start-fill` | `#eafaf1` | Start node body fill |
80
+ | `--pf-node-start-stroke` | `#27ae60` | Start node body stroke |
81
+ | `--pf-node-end-fill` | `#e8f8f5` | End node body fill |
82
+ | `--pf-node-end-stroke` | `#1abc9c` | End node body stroke |
83
+ | `--pf-node-halt-fill` | `#fdedec` | Halt/error node body fill |
84
+ | `--pf-node-halt-stroke` | `#e74c3c` | Halt/error node body stroke |
85
+ | `--pf-node-decision-fill` | `#fff9e6` | Decision node body fill |
86
+ | `--pf-node-decision-stroke` | `#f39c12` | Decision node body stroke |
87
+
88
+ ### Ports
89
+
90
+ | Variable | Default | Description |
91
+ |----------|---------|-------------|
92
+ | `--pf-port-input-fill` | `#3498db` | Input port circle fill |
93
+ | `--pf-port-output-fill` | `#2ecc71` | Output port circle fill |
94
+ | `--pf-port-stroke` | `#ffffff` | Port circle border color |
95
+ | `--pf-port-stroke-width` | `2` | Port circle border width |
96
+ | `--pf-port-label-bg` | `rgba(255,253,240,0.5)` | Port label badge background |
97
+ | `--pf-port-label-text` | `#2c3e50` | Port label text color |
98
+
99
+ ### Port Type Colors
100
+
101
+ When ports have a `PortType` set, these colors override the default input/output fills.
102
+
103
+ | Variable | Default | Description |
104
+ |----------|---------|-------------|
105
+ | `--pf-port-event-in-fill` | `#3498db` | Event input port |
106
+ | `--pf-port-event-out-fill` | `#2ecc71` | Event output port |
107
+ | `--pf-port-setting-fill` | `#e67e22` | Setting port |
108
+ | `--pf-port-value-fill` | `#f1c40f` | Value port |
109
+ | `--pf-port-error-fill` | `#e74c3c` | Error port |
110
+
111
+ ### Connection Type Colors
112
+
113
+ Connections inherit color from their source port type.
114
+
115
+ | Variable | Default | Description |
116
+ |----------|---------|-------------|
117
+ | `--pf-connection-event-in-stroke` | `#3498db` | Event-in connection stroke |
118
+ | `--pf-connection-event-out-stroke` | `#2ecc71` | Event-out connection stroke |
119
+ | `--pf-connection-setting-stroke` | `#e67e22` | Setting connection stroke |
120
+ | `--pf-connection-value-stroke` | `#f1c40f` | Value connection stroke |
121
+ | `--pf-connection-error-stroke` | `#e74c3c` | Error connection stroke |
122
+
123
+ ### Connections
124
+
125
+ | Variable | Default | Description |
126
+ |----------|---------|-------------|
127
+ | `--pf-connection-stroke` | `#95a5a6` | Default connection line color |
128
+ | `--pf-connection-stroke-hover` | `#7f8c8d` | Connection line color on hover |
129
+ | `--pf-connection-selected-stroke` | `#3498db` | Connection line color when selected |
130
+
131
+ ### Panels
132
+
133
+ | Variable | Default | Description |
134
+ |----------|---------|-------------|
135
+ | `--pf-panel-bg` | `#ffffff` | Panel background |
136
+ | `--pf-panel-border` | `#d0d4d8` | Panel border color |
137
+ | `--pf-panel-radius` | `8px` | Panel corner radius |
138
+ | `--pf-panel-shadow` | `0 4px 12px rgba(0,0,0,0.10), ...` | Panel box shadow |
139
+ | `--pf-panel-titlebar-bg` | `#f7f8fa` | Panel title bar background |
140
+ | `--pf-panel-titlebar-border` | `#e8eaed` | Title bar bottom border |
141
+ | `--pf-panel-title-color` | `#2c3e50` | Panel title text color |
142
+
143
+ ### Tabs
144
+
145
+ | Variable | Default | Description |
146
+ |----------|---------|-------------|
147
+ | `--pf-tab-text` | `#8e99a4` | Inactive tab text color |
148
+ | `--pf-tab-text-hover` | `#5a6a7a` | Tab text color on hover |
149
+ | `--pf-tab-active-border` | `var(--pf-node-selected-stroke)` | Active tab top border color |
150
+ | `--pf-resize-handle-hover` | `#e0e3e6` | Panel resize handle hover color |
151
+
152
+ ### Forms & Inputs
153
+
154
+ | Variable | Default | Description |
155
+ |----------|---------|-------------|
156
+ | `--pf-input-border` | `#d5d8dc` | Input/select border color |
157
+ | `--pf-input-border-focus` | `#3498db` | Input border color on focus |
158
+ | `--pf-divider-light` | `#ecf0f1` | Light divider/separator color |
159
+ | `--pf-divider-medium` | `#e8eaed` | Medium divider/separator color |
160
+
161
+ ### Buttons
162
+
163
+ | Variable | Default | Description |
164
+ |----------|---------|-------------|
165
+ | `--pf-button-border` | `#bdc3c7` | Button border color |
166
+ | `--pf-button-hover-border` | `#95a5a6` | Button border color on hover |
167
+ | `--pf-button-hover-bg` | `#ecf0f1` | Button background on hover |
168
+ | `--pf-button-active-bg` | `#d5dbdb` | Button background on active/press |
169
+ | `--pf-button-danger-text` | `#e74c3c` | Danger button text color |
170
+ | `--pf-button-danger-hover-bg` | `#fdedec` | Danger button hover background |
171
+ | `--pf-button-close-color` | `#b0b8c0` | Panel close button color |
172
+
173
+ ### Badges
174
+
175
+ | Variable | Default | Description |
176
+ |----------|---------|-------------|
177
+ | `--pf-badge-category-bg` | `#f0f2f4` | Category badge background |
178
+ | `--pf-badge-category-text` | `#6b7b8d` | Category badge text color |
179
+ | `--pf-badge-code-bg` | `#eaf2f8` | Code badge background |
180
+ | `--pf-badge-code-text` | `#2980b9` | Code badge text color |
181
+
182
+ ### Info Panel
183
+
184
+ | Variable | Default | Description |
185
+ |----------|---------|-------------|
186
+ | `--pf-port-item-bg` | `#f8f9fa` | Port list item background |
187
+
188
+ ### Toolbar
189
+
190
+ | Variable | Default | Description |
191
+ |----------|---------|-------------|
192
+ | `--pf-toolbar-bg` | `#ffffff` | Toolbar background |
193
+ | `--pf-toolbar-border` | `#e0e0e0` | Toolbar border and group separators |
194
+
195
+ ### Palette Cards
196
+
197
+ | Variable | Default | Description |
198
+ |----------|---------|-------------|
199
+ | `--pf-card-border` | `#d5d8dc` | Palette card border |
200
+ | `--pf-card-hover-bg` | `#eaf2f8` | Palette card hover background |
201
+ | `--pf-card-hover-shadow` | `0 1px 3px rgba(52,152,219,0.15)` | Palette card hover shadow |
202
+
203
+ ### Canvas
204
+
205
+ | Variable | Default | Description |
206
+ |----------|---------|-------------|
207
+ | `--pf-canvas-bg` | `#fafafa` | Background color of the SVG canvas |
208
+ | `--pf-grid-stroke` | `#e8e8e8` | Grid line color |
209
+
210
+ ## Per-Node-Type Overrides
211
+
212
+ Individual node types can scope-override any variable. The flow renderer adds a CSS class `.pict-flow-node-{type}` to each node group. Target these classes to customize specific node types without affecting others:
213
+
214
+ ```css
215
+ /* Make "FileRead" nodes stand out */
216
+ .pict-flow-node-FileRead .pict-flow-node-body {
217
+ fill: #fef9e7;
218
+ stroke: #f4d03f;
219
+ }
220
+ ```
221
+
222
+ ## Theme Provider API
223
+
224
+ The `PictProviderFlowTheme` service manages named themes and applies them via CSS variable overrides.
225
+
226
+ ### Registering a Theme
227
+
228
+ ```javascript
229
+ flowView._ThemeProvider.registerTheme('corporate',
230
+ {
231
+ Key: 'corporate',
232
+ Label: 'Corporate',
233
+ CSSVariables:
234
+ {
235
+ '--pf-canvas-bg': '#f5f6fa',
236
+ '--pf-node-selected-stroke': '#0066cc',
237
+ '--pf-node-body-stroke': '#c8d6e5',
238
+ '--pf-toolbar-bg': '#ffffff',
239
+ '--pf-panel-titlebar-bg': '#f0f2f5'
240
+ },
241
+ NodeBodyMode: 'rect',
242
+ NoiseConfig: { Enabled: false }
243
+ });
244
+ ```
245
+
246
+ ### Switching Themes
247
+
248
+ ```javascript
249
+ flowView.setTheme('corporate'); // Apply the corporate theme
250
+ flowView.setTheme('default'); // Restore the default Modern theme
251
+ flowView.setTheme('sketch'); // Hand-drawn style (built-in)
252
+ flowView.setTheme('blueprint'); // Blueprint style (built-in)
253
+ ```
254
+
255
+ ### Theme Properties
256
+
257
+ | Property | Type | Description |
258
+ |----------|------|-------------|
259
+ | `Key` | `string` | Unique identifier for the theme |
260
+ | `Label` | `string` | Display name |
261
+ | `CSSVariables` | `object` | Map of `--pf-*` variable names to values |
262
+ | `AdditionalCSS` | `string` | Extra CSS appended after variable overrides |
263
+ | `NodeBodyMode` | `string` | Node rendering mode: `'rect'` or `'bracket'` |
264
+ | `NoiseConfig` | `object` | Hand-drawn effect configuration |
265
+
266
+ ### How It Works
267
+
268
+ When a theme is activated:
269
+
270
+ 1. The CSS provider calls `generateCSS()` which aggregates all domain CSS
271
+ 2. If an active theme defines `CSSVariables`, they are emitted as a `.pict-flow-container` override block appended after the base CSS
272
+ 3. If `AdditionalCSS` is defined, it is appended after the variable overrides
273
+ 4. The combined CSS is re-injected via the Pict `CSSMap` service
274
+
275
+ This means theme variable overrides have higher specificity than the base defaults (same selector, later in source order), and `AdditionalCSS` can add entirely new rules or increase specificity where needed.
@@ -0,0 +1,158 @@
1
+ # Data Model
2
+
3
+ The entire flow graph state is represented by a single JSON object called **FlowData**. Every mutation goes through the `DataManager` service and triggers a re-render. This structure is what gets persisted when you call `marshalFromView()` and restored with `marshalToView()`.
4
+
5
+ ## FlowData Structure
6
+
7
+ ```javascript
8
+ {
9
+ Nodes: [], // Array of node objects
10
+ Connections: [], // Array of connection objects
11
+ OpenPanels: [], // Array of panel objects
12
+ SavedLayouts: [], // Array of layout snapshots
13
+ ViewState:
14
+ {
15
+ PanX: 0, // Viewport horizontal offset
16
+ PanY: 0, // Viewport vertical offset
17
+ Zoom: 1, // Viewport zoom level
18
+ SelectedNodeHash: null,
19
+ SelectedConnectionHash: null,
20
+ SelectedTetherHash: null
21
+ }
22
+ }
23
+ ```
24
+
25
+ ## Node
26
+
27
+ Each node represents a discrete operation in the flow graph.
28
+
29
+ ```javascript
30
+ {
31
+ Hash: 'abc123', // Unique identifier (auto-generated)
32
+ Type: 'ITE', // Card type code (references NodeTypes)
33
+ X: 200, // Horizontal position in SVG space
34
+ Y: 150, // Vertical position in SVG space
35
+ Width: 200, // Node width in pixels
36
+ Height: 100, // Node height in pixels
37
+ Title: 'If-Then-Else', // Display title on the node
38
+ Ports: // Array of port definitions
39
+ [
40
+ {
41
+ Hash: 'port-1',
42
+ Direction: 'input', // 'input' or 'output'
43
+ Side: 'left', // Port position on node edge
44
+ Label: 'In', // Port label text
45
+ PortType: 'event-in', // Optional type for coloring
46
+ DataType: 'boolean', // Optional data type hint
47
+ MinimumInputCount: 1, // Minimum connections (0 = optional)
48
+ MaximumInputCount: 1 // Maximum connections (-1 = unlimited)
49
+ }
50
+ ],
51
+ Style: // Optional per-node style overrides
52
+ {
53
+ BodyFill: '#fef5e7',
54
+ BodyStroke: '#e67e22',
55
+ BodyStrokeWidth: 1,
56
+ TitleBarColor: '#e67e22'
57
+ },
58
+ Data: {} // User-defined payload (card-specific)
59
+ }
60
+ ```
61
+
62
+ ### Port Side Positions
63
+
64
+ Ports can be placed at 12 positions around the node perimeter using a zone system:
65
+
66
+ ```
67
+ top-left top top-right
68
+ left-top (body) right-top
69
+ left right
70
+ left-bottom (body) right-bottom
71
+ bottom-left bottom bottom-right
72
+ ```
73
+
74
+ The legacy four-value sides (`left`, `right`, `top`, `bottom`) map to the middle zone of each edge.
75
+
76
+ ## Connection
77
+
78
+ A connection links an output port on one node to an input port on another.
79
+
80
+ ```javascript
81
+ {
82
+ Hash: 'conn-1',
83
+ SourceNodeHash: 'node-a',
84
+ SourcePortHash: 'port-out',
85
+ TargetNodeHash: 'node-b',
86
+ TargetPortHash: 'port-in',
87
+ Data:
88
+ {
89
+ LineMode: 'bezier', // 'bezier' or 'orthogonal'
90
+ HandleCustomized: false, // True if handles have been manually moved
91
+
92
+ // Bezier handles (multi-point curve control)
93
+ BezierHandles: [{ x: 300, y: 175 }],
94
+
95
+ // Orthogonal handles (right-angle path corners)
96
+ OrthoCorner1X: 250,
97
+ OrthoCorner1Y: 150,
98
+ OrthoCorner2X: 350,
99
+ OrthoCorner2Y: 200,
100
+ OrthoMidOffset: 0
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### Connection Routing
106
+
107
+ Connections support two path modes:
108
+
109
+ - **Bezier** (default) — Smooth curves with one or more control handles. Right-click a connection to add a handle; drag handles to reshape the curve.
110
+ - **Orthogonal** — Right-angle paths with two corner points. Double-click a connection handle to toggle between bezier and orthogonal modes.
111
+
112
+ ## Panel
113
+
114
+ An open properties panel associated with a node.
115
+
116
+ ```javascript
117
+ {
118
+ Hash: 'panel-1',
119
+ NodeHash: 'node-a', // The node this panel belongs to
120
+ PanelType: 'Form', // 'Template', 'Markdown', 'Form', 'View', or 'Info'
121
+ Title: 'Set Value Properties',
122
+ X: 450, // Panel position in SVG space
123
+ Y: 100,
124
+ Width: 320,
125
+ Height: 200
126
+ }
127
+ ```
128
+
129
+ Each panel is rendered as an HTML `foreignObject` inside the SVG, with a tether line connecting it to its parent node. Panels can be dragged, resized, and have tabbed views for Properties, Help, and Appearance.
130
+
131
+ ## ViewState
132
+
133
+ Viewport and selection state, persisted with the flow data.
134
+
135
+ ```javascript
136
+ {
137
+ PanX: -120, // Horizontal pan offset
138
+ PanY: -50, // Vertical pan offset
139
+ Zoom: 0.85, // Zoom level (0.1 to 5.0)
140
+ SelectedNodeHash: 'node-a', // Currently selected node (null if none)
141
+ SelectedConnectionHash: null, // Currently selected connection
142
+ SelectedTetherHash: null // Currently selected tether line
143
+ }
144
+ ```
145
+
146
+ ## Relationships
147
+
148
+ ```mermaid
149
+ erDiagram
150
+ FlowData ||--o{ Node : contains
151
+ FlowData ||--o{ Connection : contains
152
+ FlowData ||--o{ Panel : contains
153
+ FlowData ||--|| ViewState : has
154
+ Node ||--o{ Port : has
155
+ Connection }o--|| Port : "source port"
156
+ Connection }o--|| Port : "target port"
157
+ Panel }o--|| Node : "attached to"
158
+ ```
@@ -0,0 +1,156 @@
1
+ # Event System
2
+
3
+ Pict-Section-Flow exposes a rich event system through the `EventHandlerProvider`. Register handlers to react to user interactions, data changes, and lifecycle events without modifying core code.
4
+
5
+ ## Registering Handlers
6
+
7
+ ```javascript
8
+ let tmpFlowView = _Pict.views.MyFlow;
9
+
10
+ // Register a named handler
11
+ tmpFlowView._EventHandlerProvider.registerHandler(
12
+ 'onNodeAdded',
13
+ (pNode, pFlowView) =>
14
+ {
15
+ console.log('Node added:', pNode.Title);
16
+ },
17
+ 'my-node-handler'
18
+ );
19
+ ```
20
+
21
+ The third parameter is an optional handler hash for later removal. If omitted, a unique hash is generated and returned.
22
+
23
+ ## Removing Handlers
24
+
25
+ ```javascript
26
+ // Remove a specific handler
27
+ tmpFlowView._EventHandlerProvider.removeHandler('onNodeAdded', 'my-node-handler');
28
+
29
+ // Remove all handlers for an event
30
+ tmpFlowView._EventHandlerProvider.removeAllHandlers('onNodeAdded');
31
+
32
+ // Remove all handlers for all events
33
+ tmpFlowView._EventHandlerProvider.removeAllHandlers();
34
+ ```
35
+
36
+ ## Querying Handlers
37
+
38
+ ```javascript
39
+ // Check if any handlers are registered
40
+ tmpFlowView._EventHandlerProvider.hasHandlers('onNodeAdded'); // true or false
41
+
42
+ // Count registered handlers
43
+ tmpFlowView._EventHandlerProvider.getHandlerCount('onNodeAdded'); // number
44
+ ```
45
+
46
+ ## Available Events
47
+
48
+ ### Node Events
49
+
50
+ | Event | Payload | Description |
51
+ |-------|---------|-------------|
52
+ | `onNodeSelected` | `node` or `null` | A node was selected or deselected |
53
+ | `onNodeAdded` | `node` | A new node was created |
54
+ | `onNodeRemoved` | `node` | A node was deleted |
55
+ | `onNodeMoved` | `node` | A node was dragged to a new position |
56
+
57
+ ### Connection Events
58
+
59
+ | Event | Payload | Description |
60
+ |-------|---------|-------------|
61
+ | `onConnectionSelected` | `connection` | A connection was selected |
62
+ | `onConnectionCreated` | `connection` | A new connection was created between ports |
63
+ | `onConnectionRemoved` | `connection` | A connection was deleted |
64
+ | `onConnectionHandleMoved` | `connection` | A bezier or orthogonal handle was dragged |
65
+ | `onConnectionModeChanged` | `connection` | A connection toggled between bezier and orthogonal |
66
+
67
+ ### Panel Events
68
+
69
+ | Event | Payload | Description |
70
+ |-------|---------|-------------|
71
+ | `onPanelOpened` | `panelData` | A properties panel was opened |
72
+ | `onPanelClosed` | `panelData` | A properties panel was closed |
73
+ | `onPanelMoved` | `panelData` | A properties panel was dragged |
74
+
75
+ ### Tether Events
76
+
77
+ | Event | Payload | Description |
78
+ |-------|---------|-------------|
79
+ | `onTetherSelected` | `panelData` | A tether line was selected |
80
+ | `onTetherHandleMoved` | `panelData` | A tether handle was dragged |
81
+ | `onTetherModeChanged` | `panelData` | A tether toggled between bezier and orthogonal |
82
+
83
+ ### Layout and Meta Events
84
+
85
+ | Event | Payload | Description |
86
+ |-------|---------|-------------|
87
+ | `onLayoutSaved` | `layoutData` | A layout snapshot was saved |
88
+ | `onLayoutRestored` | `layoutData` | A saved layout was restored |
89
+ | `onLayoutDeleted` | `layoutData` | A saved layout was deleted |
90
+ | `onFlowChanged` | `flowData` | Catch-all fired after any data mutation |
91
+ | `onThemeChanged` | `themeKey` | The active theme was switched |
92
+
93
+ ## Common Patterns
94
+
95
+ ### Undo/Redo Stack
96
+
97
+ ```javascript
98
+ let tmpUndoStack = [];
99
+ let tmpRedoStack = [];
100
+
101
+ tmpFlowView._EventHandlerProvider.registerHandler(
102
+ 'onFlowChanged',
103
+ (pFlowData) =>
104
+ {
105
+ tmpUndoStack.push(JSON.parse(JSON.stringify(pFlowData)));
106
+ tmpRedoStack = [];
107
+ },
108
+ 'undo-tracker'
109
+ );
110
+
111
+ function undo()
112
+ {
113
+ if (tmpUndoStack.length < 2) return;
114
+ tmpRedoStack.push(tmpUndoStack.pop());
115
+ tmpFlowView.setFlowData(tmpUndoStack[tmpUndoStack.length - 1]);
116
+ }
117
+ ```
118
+
119
+ ### Server Sync
120
+
121
+ ```javascript
122
+ tmpFlowView._EventHandlerProvider.registerHandler(
123
+ 'onFlowChanged',
124
+ (pFlowData) =>
125
+ {
126
+ fetch('/api/flows/save',
127
+ {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/json' },
130
+ body: JSON.stringify(pFlowData)
131
+ });
132
+ },
133
+ 'server-sync'
134
+ );
135
+ ```
136
+
137
+ ### Selection Sidebar
138
+
139
+ ```javascript
140
+ tmpFlowView._EventHandlerProvider.registerHandler(
141
+ 'onNodeSelected',
142
+ (pNode) =>
143
+ {
144
+ if (pNode)
145
+ {
146
+ document.getElementById('sidebar-title').textContent = pNode.Title;
147
+ document.getElementById('sidebar-type').textContent = pNode.Type;
148
+ }
149
+ else
150
+ {
151
+ document.getElementById('sidebar-title').textContent = 'No selection';
152
+ }
153
+ },
154
+ 'sidebar-updater'
155
+ );
156
+ ```