pict-section-flow 0.0.2 → 0.0.3

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 (38) hide show
  1. package/.claude/launch.json +11 -0
  2. package/docs/README.md +51 -0
  3. package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +105 -0
  4. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +36 -0
  5. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +42 -0
  6. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +1 -1
  7. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +1 -1
  8. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +1 -1
  9. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +1 -1
  10. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +1 -1
  11. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +1 -1
  12. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +1 -1
  13. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +98 -0
  14. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +44 -0
  15. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +1 -1
  16. package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +9 -1
  17. package/package.json +2 -2
  18. package/source/Pict-Section-Flow.js +8 -1
  19. package/source/PictFlowCard.js +49 -1
  20. package/source/providers/PictProvider-Flow-CSS.js +1440 -0
  21. package/source/providers/PictProvider-Flow-ConnectorShapes.js +413 -0
  22. package/source/providers/PictProvider-Flow-Geometry.js +43 -0
  23. package/source/providers/PictProvider-Flow-Icons.js +335 -0
  24. package/source/providers/PictProvider-Flow-Layouts.js +214 -2
  25. package/source/providers/PictProvider-Flow-NodeTypes.js +30 -7
  26. package/source/providers/PictProvider-Flow-Noise.js +241 -0
  27. package/source/providers/PictProvider-Flow-PanelChrome.js +19 -0
  28. package/source/providers/PictProvider-Flow-Theme.js +755 -0
  29. package/source/services/PictService-Flow-ConnectionRenderer.js +95 -32
  30. package/source/services/PictService-Flow-PanelManager.js +188 -0
  31. package/source/services/PictService-Flow-SelectionManager.js +109 -0
  32. package/source/services/PictService-Flow-Tether.js +52 -25
  33. package/source/services/PictService-Flow-ViewportManager.js +176 -0
  34. package/source/views/PictView-Flow-FloatingToolbar.js +352 -0
  35. package/source/views/PictView-Flow-Node.js +654 -169
  36. package/source/views/PictView-Flow-PropertiesPanel.js +176 -1
  37. package/source/views/PictView-Flow-Toolbar.js +846 -379
  38. package/source/views/PictView-Flow.js +279 -671
@@ -0,0 +1,755 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+
3
+ const _ProviderConfiguration =
4
+ {
5
+ ProviderIdentifier: 'PictProviderFlowTheme'
6
+ };
7
+
8
+ /**
9
+ * PictProvider-Flow-Theme
10
+ *
11
+ * Central orchestrator for the flow diagram theming system.
12
+ *
13
+ * Holds a registry of theme definitions, manages the active theme,
14
+ * and provides hooks for node body rendering and path noise processing.
15
+ *
16
+ * ## Usage
17
+ *
18
+ * ```javascript
19
+ * flowView.setTheme('sketch'); // Switch to hand-drawn style
20
+ * flowView.setNoiseLevel(0.6); // Increase bracket/connection wobble
21
+ * flowView.setTheme('default'); // Restore modern style
22
+ * ```
23
+ *
24
+ * ## Custom Themes
25
+ *
26
+ * ```javascript
27
+ * flowView._ThemeProvider.registerTheme('custom', {
28
+ * Key: 'custom',
29
+ * Label: 'My Custom Theme',
30
+ * CSSVariables: { '--pf-canvas-bg': '#222' },
31
+ * NodeBodyMode: 'rect',
32
+ * NoiseConfig: { Enabled: false }
33
+ * });
34
+ * flowView.setTheme('custom');
35
+ * ```
36
+ */
37
+ class PictProviderFlowTheme extends libFableServiceProviderBase
38
+ {
39
+ constructor(pFable, pOptions, pServiceHash)
40
+ {
41
+ let tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);
42
+ super(pFable, tmpOptions, pServiceHash);
43
+
44
+ this.serviceType = 'PictProviderFlowTheme';
45
+
46
+ this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
47
+
48
+ this._ActiveThemeKey = 'default';
49
+ this._NoiseLevel = 0;
50
+ this._Themes = {};
51
+
52
+ this._registerBuiltInThemes();
53
+ }
54
+
55
+ // ── Theme Registry ────────────────────────────────────────────────────
56
+
57
+ _registerBuiltInThemes()
58
+ {
59
+ // ── 1. Default (Modern) ──────────────────────────────────────
60
+ this._Themes['default'] =
61
+ {
62
+ Key: 'default',
63
+ Label: 'Modern',
64
+ CSSVariables: {},
65
+ AdditionalCSS: '',
66
+ NodeBodyMode: 'rect',
67
+ BracketConfig: null,
68
+ ConnectionConfig:
69
+ {
70
+ StrokeDashArray: null,
71
+ StrokeWidth: 2,
72
+ ArrowheadStyle: 'triangle'
73
+ },
74
+ NoiseConfig:
75
+ {
76
+ Enabled: false,
77
+ DefaultLevel: 0,
78
+ MaxJitterPx: 0,
79
+ AffectsNodes: false,
80
+ AffectsConnections: false
81
+ },
82
+ ShapeOverrides: {}
83
+ };
84
+
85
+ // ── 2. Sketch (Hand-drawn) ───────────────────────────────────
86
+ this._Themes['sketch'] =
87
+ {
88
+ Key: 'sketch',
89
+ Label: 'Sketch',
90
+ CSSVariables:
91
+ {
92
+ '--pf-node-body-fill': '#fffef5',
93
+ '--pf-node-body-stroke': '#444444',
94
+ '--pf-node-body-stroke-width': '1.5',
95
+ '--pf-node-body-radius': '0px',
96
+ '--pf-node-shadow': 'none',
97
+ '--pf-node-shadow-hover': 'none',
98
+ '--pf-node-shadow-selected': 'none',
99
+ '--pf-node-shadow-dragging': 'none',
100
+ '--pf-node-title-fill': '#333333',
101
+ '--pf-node-title-size': '12px',
102
+ '--pf-node-title-weight': '400',
103
+ '--pf-node-title-bar-color': '#f0ece0',
104
+ '--pf-node-type-label-fill': '#888888',
105
+ '--pf-node-selected-stroke': '#2255aa',
106
+ '--pf-port-input-fill': '#5577bb',
107
+ '--pf-port-output-fill': '#55aa77',
108
+ '--pf-port-stroke': '#fffef5',
109
+ '--pf-connection-stroke': '#555555',
110
+ '--pf-connection-selected-stroke': '#2255aa',
111
+ '--pf-canvas-bg': '#fffef5',
112
+ '--pf-grid-stroke': '#e8e4d8',
113
+ '--pf-panel-bg': '#fffef5',
114
+ '--pf-panel-border': '#ccccaa',
115
+ '--pf-panel-radius': '0px',
116
+ '--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.08)',
117
+ '--pf-panel-titlebar-bg': '#f0ece0',
118
+ '--pf-panel-titlebar-border': '#ccccaa',
119
+ '--pf-panel-title-color': '#333333'
120
+ },
121
+ AdditionalCSS: `
122
+ .pict-flow-node-title,
123
+ .pict-flow-node-type-label,
124
+ .pict-flow-port-label,
125
+ .pict-flow-node-card-code {
126
+ font-family: "Courier New", "Courier", monospace !important;
127
+ }
128
+ .pict-flow-panel-title-text,
129
+ .pict-flow-panel-node-props-title,
130
+ .pict-flow-info-panel {
131
+ font-family: "Courier New", "Courier", monospace !important;
132
+ }
133
+ .pict-flow-node-title-icon {
134
+ filter: brightness(0) !important;
135
+ }
136
+ `,
137
+ NodeBodyMode: 'bracket',
138
+ BracketConfig:
139
+ {
140
+ SerifLength: 20,
141
+ TitleSeparator: true
142
+ },
143
+ ConnectionConfig:
144
+ {
145
+ StrokeDashArray: null,
146
+ StrokeWidth: 1.5,
147
+ ArrowheadStyle: 'triangle'
148
+ },
149
+ NoiseConfig:
150
+ {
151
+ Enabled: true,
152
+ DefaultLevel: 0.4,
153
+ MaxJitterPx: 4,
154
+ AffectsNodes: true,
155
+ AffectsConnections: true
156
+ },
157
+ ShapeOverrides:
158
+ {
159
+ 'arrowhead-connection': { Fill: '#555555' },
160
+ 'arrowhead-connection-selected': { Fill: '#2255aa' }
161
+ }
162
+ };
163
+
164
+ // ── 3. Blueprint (Technical) ─────────────────────────────────
165
+ this._Themes['blueprint'] =
166
+ {
167
+ Key: 'blueprint',
168
+ Label: 'Blueprint',
169
+ CSSVariables:
170
+ {
171
+ '--pf-node-body-fill': 'rgba(255,255,255,0.05)',
172
+ '--pf-node-body-stroke': '#ffffff',
173
+ '--pf-node-body-stroke-width': '1',
174
+ '--pf-node-body-radius': '0px',
175
+ '--pf-node-shadow': 'none',
176
+ '--pf-node-shadow-hover': 'none',
177
+ '--pf-node-shadow-selected': 'none',
178
+ '--pf-node-shadow-dragging': 'none',
179
+ '--pf-node-title-fill': '#ffffff',
180
+ '--pf-node-title-size': '11px',
181
+ '--pf-node-title-weight': '400',
182
+ '--pf-node-title-bar-color': 'rgba(255,255,255,0.1)',
183
+ '--pf-node-type-label-fill': 'rgba(255,255,255,0.5)',
184
+ '--pf-node-selected-stroke': '#ffdd44',
185
+ '--pf-port-input-fill': '#88bbff',
186
+ '--pf-port-output-fill': '#88ffbb',
187
+ '--pf-port-stroke': '#1a3a6a',
188
+ '--pf-connection-stroke': 'rgba(255,255,255,0.6)',
189
+ '--pf-connection-selected-stroke': '#ffdd44',
190
+ '--pf-canvas-bg': '#1a3a6a',
191
+ '--pf-grid-stroke': 'rgba(255,255,255,0.08)',
192
+ '--pf-panel-bg': '#1a3a6a',
193
+ '--pf-panel-border': 'rgba(255,255,255,0.3)',
194
+ '--pf-panel-radius': '0px',
195
+ '--pf-panel-shadow': 'none',
196
+ '--pf-panel-titlebar-bg': 'rgba(255,255,255,0.05)',
197
+ '--pf-panel-titlebar-border': 'rgba(255,255,255,0.15)',
198
+ '--pf-panel-title-color': '#ffffff'
199
+ },
200
+ AdditionalCSS: `
201
+ .pict-flow-node-title,
202
+ .pict-flow-node-type-label,
203
+ .pict-flow-port-label,
204
+ .pict-flow-node-card-code {
205
+ font-family: "Courier New", monospace !important;
206
+ text-transform: uppercase;
207
+ letter-spacing: 1px;
208
+ }
209
+ .pict-flow-container {
210
+ border-color: #0d2244;
211
+ }
212
+ .pict-flow-node-title-icon {
213
+ filter: brightness(0) invert(1) !important;
214
+ }
215
+ .pict-flow-toolbar {
216
+ background-color: #142e54;
217
+ border-bottom-color: rgba(255,255,255,0.15);
218
+ }
219
+ .pict-flow-toolbar-btn {
220
+ background-color: rgba(255,255,255,0.05);
221
+ border-color: rgba(255,255,255,0.2);
222
+ color: #ffffff;
223
+ }
224
+ .pict-flow-toolbar-btn:hover {
225
+ background-color: rgba(255,255,255,0.1);
226
+ }
227
+ `,
228
+ NodeBodyMode: 'bracket',
229
+ BracketConfig:
230
+ {
231
+ SerifLength: 18,
232
+ TitleSeparator: true
233
+ },
234
+ ConnectionConfig:
235
+ {
236
+ StrokeDashArray: '8 4',
237
+ StrokeWidth: 1,
238
+ ArrowheadStyle: 'triangle'
239
+ },
240
+ NoiseConfig:
241
+ {
242
+ Enabled: false,
243
+ DefaultLevel: 0,
244
+ MaxJitterPx: 0,
245
+ AffectsNodes: false,
246
+ AffectsConnections: false
247
+ },
248
+ ShapeOverrides:
249
+ {
250
+ 'arrowhead-connection': { Fill: 'rgba(255,255,255,0.6)' },
251
+ 'arrowhead-connection-selected': { Fill: '#ffdd44' }
252
+ }
253
+ };
254
+
255
+ // ── 4. Mono (Black & White) ──────────────────────────────────
256
+ this._Themes['mono'] =
257
+ {
258
+ Key: 'mono',
259
+ Label: 'Monochrome',
260
+ CSSVariables:
261
+ {
262
+ '--pf-node-body-fill': '#ffffff',
263
+ '--pf-node-body-stroke': '#000000',
264
+ '--pf-node-body-stroke-width': '1',
265
+ '--pf-node-body-radius': '0px',
266
+ '--pf-node-shadow': 'none',
267
+ '--pf-node-shadow-hover': 'none',
268
+ '--pf-node-shadow-selected': 'none',
269
+ '--pf-node-shadow-dragging': 'none',
270
+ '--pf-node-title-fill': '#ffffff',
271
+ '--pf-node-title-size': '11px',
272
+ '--pf-node-title-weight': '600',
273
+ '--pf-node-title-bar-color': '#000000',
274
+ '--pf-node-type-label-fill': '#888888',
275
+ '--pf-node-selected-stroke': '#444444',
276
+ '--pf-port-input-fill': '#000000',
277
+ '--pf-port-output-fill': '#666666',
278
+ '--pf-port-stroke': '#ffffff',
279
+ '--pf-connection-stroke': '#000000',
280
+ '--pf-connection-selected-stroke': '#444444',
281
+ '--pf-canvas-bg': '#ffffff',
282
+ '--pf-grid-stroke': '#eeeeee',
283
+ '--pf-panel-bg': '#ffffff',
284
+ '--pf-panel-border': '#000000',
285
+ '--pf-panel-radius': '0px',
286
+ '--pf-panel-shadow': 'none',
287
+ '--pf-panel-titlebar-bg': '#f0f0f0',
288
+ '--pf-panel-titlebar-border': '#000000',
289
+ '--pf-panel-title-color': '#000000'
290
+ },
291
+ AdditionalCSS: `
292
+ .pict-flow-node-title {
293
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
294
+ }
295
+ .pict-flow-node-title-icon {
296
+ filter: brightness(0) invert(1) !important;
297
+ }
298
+ `,
299
+ NodeBodyMode: 'rect',
300
+ BracketConfig: null,
301
+ ConnectionConfig:
302
+ {
303
+ StrokeDashArray: null,
304
+ StrokeWidth: 1,
305
+ ArrowheadStyle: 'triangle'
306
+ },
307
+ NoiseConfig:
308
+ {
309
+ Enabled: false,
310
+ DefaultLevel: 0,
311
+ MaxJitterPx: 0,
312
+ AffectsNodes: false,
313
+ AffectsConnections: false
314
+ },
315
+ ShapeOverrides:
316
+ {
317
+ 'arrowhead-connection': { Fill: '#000000' },
318
+ 'arrowhead-connection-selected': { Fill: '#444444' }
319
+ }
320
+ };
321
+
322
+ // ── 5. Retro 80s (Neon) ──────────────────────────────────────
323
+ this._Themes['retro-80s'] =
324
+ {
325
+ Key: 'retro-80s',
326
+ Label: '80s Retro',
327
+ CSSVariables:
328
+ {
329
+ '--pf-node-body-fill': '#1a0a2e',
330
+ '--pf-node-body-stroke': '#ff00ff',
331
+ '--pf-node-body-stroke-width': '2',
332
+ '--pf-node-body-radius': '0px',
333
+ '--pf-node-shadow': 'drop-shadow(0 0 8px rgba(255,0,255,0.4))',
334
+ '--pf-node-shadow-hover': 'drop-shadow(0 0 12px rgba(255,0,255,0.6))',
335
+ '--pf-node-shadow-selected': 'drop-shadow(0 0 16px rgba(0,255,255,0.5))',
336
+ '--pf-node-shadow-dragging': 'drop-shadow(0 0 20px rgba(255,0,255,0.7))',
337
+ '--pf-node-title-fill': '#00ffff',
338
+ '--pf-node-title-size': '11px',
339
+ '--pf-node-title-weight': '700',
340
+ '--pf-node-title-bar-color': '#2a0a4e',
341
+ '--pf-node-type-label-fill': '#ff66ff',
342
+ '--pf-node-selected-stroke': '#00ffff',
343
+ '--pf-port-input-fill': '#ff00ff',
344
+ '--pf-port-output-fill': '#00ff66',
345
+ '--pf-port-stroke': '#1a0a2e',
346
+ '--pf-connection-stroke': '#ff00ff',
347
+ '--pf-connection-selected-stroke': '#00ffff',
348
+ '--pf-canvas-bg': '#0a0015',
349
+ '--pf-grid-stroke': '#1a0a2e',
350
+ '--pf-panel-bg': '#1a0a2e',
351
+ '--pf-panel-border': '#ff00ff',
352
+ '--pf-panel-radius': '0px',
353
+ '--pf-panel-shadow': '0 0 20px rgba(255,0,255,0.3)',
354
+ '--pf-panel-titlebar-bg': '#2a0a4e',
355
+ '--pf-panel-titlebar-border': '#ff00ff',
356
+ '--pf-panel-title-color': '#00ffff'
357
+ },
358
+ AdditionalCSS: `
359
+ .pict-flow-node-title,
360
+ .pict-flow-node-type-label,
361
+ .pict-flow-port-label,
362
+ .pict-flow-node-card-code {
363
+ font-family: "Courier New", monospace !important;
364
+ text-transform: uppercase;
365
+ letter-spacing: 0.5px;
366
+ }
367
+ .pict-flow-connection {
368
+ filter: drop-shadow(0 0 3px rgba(255,0,255,0.4));
369
+ }
370
+ .pict-flow-node-title-icon {
371
+ filter: brightness(0) invert(1) hue-rotate(180deg) !important;
372
+ }
373
+ .pict-flow-toolbar {
374
+ background-color: #1a0a2e;
375
+ border-bottom-color: #ff00ff;
376
+ }
377
+ .pict-flow-toolbar-btn {
378
+ background-color: #1a0a2e;
379
+ border-color: #ff00ff;
380
+ color: #00ffff;
381
+ }
382
+ .pict-flow-toolbar-btn:hover {
383
+ background-color: #2a0a4e;
384
+ }
385
+ .pict-flow-container {
386
+ border-color: #ff00ff;
387
+ }
388
+ `,
389
+ NodeBodyMode: 'rect',
390
+ BracketConfig: null,
391
+ ConnectionConfig:
392
+ {
393
+ StrokeDashArray: null,
394
+ StrokeWidth: 2,
395
+ ArrowheadStyle: 'triangle'
396
+ },
397
+ NoiseConfig:
398
+ {
399
+ Enabled: false,
400
+ DefaultLevel: 0,
401
+ MaxJitterPx: 0,
402
+ AffectsNodes: false,
403
+ AffectsConnections: false
404
+ },
405
+ ShapeOverrides:
406
+ {
407
+ 'arrowhead-connection': { Fill: '#ff00ff' },
408
+ 'arrowhead-connection-selected': { Fill: '#00ffff' }
409
+ }
410
+ };
411
+
412
+ // ── 6. Retro 90s (Windows) ───────────────────────────────────
413
+ this._Themes['retro-90s'] =
414
+ {
415
+ Key: 'retro-90s',
416
+ Label: '90s Retro',
417
+ CSSVariables:
418
+ {
419
+ '--pf-node-body-fill': '#c0c0c0',
420
+ '--pf-node-body-stroke': '#808080',
421
+ '--pf-node-body-stroke-width': '1',
422
+ '--pf-node-body-radius': '0px',
423
+ '--pf-node-shadow': 'drop-shadow(2px 2px 0px #404040)',
424
+ '--pf-node-shadow-hover': 'drop-shadow(3px 3px 0px #404040)',
425
+ '--pf-node-shadow-selected': 'drop-shadow(2px 2px 0px #008080)',
426
+ '--pf-node-shadow-dragging': 'drop-shadow(4px 4px 0px #404040)',
427
+ '--pf-node-title-fill': '#ffffff',
428
+ '--pf-node-title-size': '11px',
429
+ '--pf-node-title-weight': '700',
430
+ '--pf-node-title-bar-color': '#000080',
431
+ '--pf-node-type-label-fill': '#606060',
432
+ '--pf-node-selected-stroke': '#008080',
433
+ '--pf-port-input-fill': '#000080',
434
+ '--pf-port-output-fill': '#008000',
435
+ '--pf-port-stroke': '#c0c0c0',
436
+ '--pf-connection-stroke': '#808080',
437
+ '--pf-connection-selected-stroke': '#008080',
438
+ '--pf-canvas-bg': '#008080',
439
+ '--pf-grid-stroke': 'rgba(0,0,0,0.06)',
440
+ '--pf-panel-bg': '#c0c0c0',
441
+ '--pf-panel-border': '#808080',
442
+ '--pf-panel-radius': '0px',
443
+ '--pf-panel-shadow': '2px 2px 0px #404040',
444
+ '--pf-panel-titlebar-bg': '#000080',
445
+ '--pf-panel-titlebar-border': '#c0c0c0',
446
+ '--pf-panel-title-color': '#ffffff'
447
+ },
448
+ AdditionalCSS: `
449
+ .pict-flow-node-title,
450
+ .pict-flow-node-type-label,
451
+ .pict-flow-port-label,
452
+ .pict-flow-node-card-code {
453
+ font-family: "MS Sans Serif", "Arial", sans-serif !important;
454
+ }
455
+ .pict-flow-node-title-icon {
456
+ filter: brightness(0) invert(1) !important;
457
+ }
458
+ .pict-flow-toolbar {
459
+ background-color: #c0c0c0;
460
+ border-bottom: 2px solid #808080;
461
+ border-top: 1px solid #ffffff;
462
+ }
463
+ .pict-flow-toolbar-btn {
464
+ background-color: #c0c0c0;
465
+ border: 2px outset #c0c0c0;
466
+ border-radius: 0;
467
+ color: #000000;
468
+ }
469
+ .pict-flow-toolbar-btn:hover {
470
+ background-color: #d0d0d0;
471
+ }
472
+ .pict-flow-toolbar-btn:active {
473
+ border-style: inset;
474
+ }
475
+ .pict-flow-container {
476
+ border: 2px outset #c0c0c0;
477
+ border-radius: 0;
478
+ }
479
+ `,
480
+ NodeBodyMode: 'rect',
481
+ BracketConfig: null,
482
+ ConnectionConfig:
483
+ {
484
+ StrokeDashArray: null,
485
+ StrokeWidth: 2,
486
+ ArrowheadStyle: 'triangle'
487
+ },
488
+ NoiseConfig:
489
+ {
490
+ Enabled: false,
491
+ DefaultLevel: 0,
492
+ MaxJitterPx: 0,
493
+ AffectsNodes: false,
494
+ AffectsConnections: false
495
+ },
496
+ ShapeOverrides:
497
+ {
498
+ 'arrowhead-connection': { Fill: '#808080' },
499
+ 'arrowhead-connection-selected': { Fill: '#008080' }
500
+ }
501
+ };
502
+
503
+ // ── 7. Whiteboard (Minimal brackets, no fills) ──────────────
504
+ this._Themes['whiteboard'] =
505
+ {
506
+ Key: 'whiteboard',
507
+ Label: 'Whiteboard',
508
+ CSSVariables:
509
+ {
510
+ '--pf-node-body-fill': 'transparent',
511
+ '--pf-node-body-stroke': '#555555',
512
+ '--pf-node-body-stroke-width': '2',
513
+ '--pf-node-body-radius': '0px',
514
+ '--pf-node-shadow': 'none',
515
+ '--pf-node-shadow-hover': 'none',
516
+ '--pf-node-shadow-selected': 'none',
517
+ '--pf-node-shadow-dragging': 'none',
518
+ '--pf-node-title-fill': '#333333',
519
+ '--pf-node-title-size': '12px',
520
+ '--pf-node-title-weight': '600',
521
+ '--pf-node-title-bar-color': 'transparent',
522
+ '--pf-node-type-label-fill': '#999999',
523
+ '--pf-node-selected-stroke': '#2255aa',
524
+ '--pf-port-input-fill': '#5577bb',
525
+ '--pf-port-output-fill': '#55aa77',
526
+ '--pf-port-stroke': '#ffffff',
527
+ '--pf-connection-stroke': '#888888',
528
+ '--pf-connection-selected-stroke': '#2255aa',
529
+ '--pf-canvas-bg': '#ffffff',
530
+ '--pf-grid-stroke': '#f0f0f0',
531
+ '--pf-panel-bg': '#ffffff',
532
+ '--pf-panel-border': '#cccccc',
533
+ '--pf-panel-radius': '0px',
534
+ '--pf-panel-shadow': '2px 2px 0px rgba(0,0,0,0.06)',
535
+ '--pf-panel-titlebar-bg': '#f8f8f8',
536
+ '--pf-panel-titlebar-border': '#e0e0e0',
537
+ '--pf-panel-title-color': '#333333'
538
+ },
539
+ AdditionalCSS: `
540
+ .pict-flow-node-title,
541
+ .pict-flow-node-type-label,
542
+ .pict-flow-port-label,
543
+ .pict-flow-node-card-code {
544
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
545
+ }
546
+ /* Node-type bracket colors — each type gets its own bracket color */
547
+ .pict-flow-node-start .pict-flow-node-bracket { stroke: #27ae60; }
548
+ .pict-flow-node-end .pict-flow-node-bracket { stroke: #1abc9c; }
549
+ .pict-flow-node-halt .pict-flow-node-bracket { stroke: #e74c3c; }
550
+ .pict-flow-node-decision .pict-flow-node-bracket { stroke: #f39c12; }
551
+ .pict-flow-node-default .pict-flow-node-bracket { stroke: #3498db; }
552
+ .pict-flow-node-action .pict-flow-node-bracket { stroke: #2c3e50; }
553
+ /* Override variant rules: no fills/strokes on body rects in whiteboard */
554
+ .pict-flow-node-decision .pict-flow-node-body,
555
+ .pict-flow-node-start .pict-flow-node-body,
556
+ .pict-flow-node-end .pict-flow-node-body,
557
+ .pict-flow-node-halt .pict-flow-node-body {
558
+ fill: transparent;
559
+ stroke: transparent;
560
+ stroke-width: 0;
561
+ }
562
+ /* Title bar fills transparent too */
563
+ .pict-flow-node .pict-flow-node-bracket-title-fill {
564
+ fill: transparent !important;
565
+ }
566
+ .pict-flow-node-title-icon {
567
+ filter: none !important;
568
+ }
569
+ `,
570
+ NodeBodyMode: 'bracket',
571
+ BracketConfig:
572
+ {
573
+ SerifLength: 22,
574
+ TitleSeparator: false
575
+ },
576
+ ConnectionConfig:
577
+ {
578
+ StrokeDashArray: null,
579
+ StrokeWidth: 1.5,
580
+ ArrowheadStyle: 'triangle'
581
+ },
582
+ NoiseConfig:
583
+ {
584
+ Enabled: true,
585
+ DefaultLevel: 0.3,
586
+ MaxJitterPx: 3,
587
+ AffectsNodes: true,
588
+ AffectsConnections: true
589
+ },
590
+ ShapeOverrides:
591
+ {
592
+ 'arrowhead-connection': { Fill: '#888888' },
593
+ 'arrowhead-connection-selected': { Fill: '#2255aa' }
594
+ }
595
+ };
596
+ }
597
+
598
+ // ── Public API ────────────────────────────────────────────────────────
599
+
600
+ /**
601
+ * Get the active theme definition.
602
+ * @returns {Object}
603
+ */
604
+ getActiveTheme()
605
+ {
606
+ return this._Themes[this._ActiveThemeKey] || this._Themes['default'];
607
+ }
608
+
609
+ /**
610
+ * Get the active theme key.
611
+ * @returns {string}
612
+ */
613
+ getActiveThemeKey()
614
+ {
615
+ return this._ActiveThemeKey;
616
+ }
617
+
618
+ /**
619
+ * Switch the active theme.
620
+ * This updates the internal key and applies shape overrides.
621
+ * The caller (FlowView.setTheme) is responsible for re-registering
622
+ * CSS and triggering a full re-render.
623
+ *
624
+ * @param {string} pThemeKey
625
+ * @returns {boolean} Whether the theme was found and applied
626
+ */
627
+ setTheme(pThemeKey)
628
+ {
629
+ if (!this._Themes[pThemeKey])
630
+ {
631
+ this.log.warn(`PictProviderFlowTheme: theme '${pThemeKey}' not found`);
632
+ return false;
633
+ }
634
+
635
+ this._ActiveThemeKey = pThemeKey;
636
+ let tmpTheme = this._Themes[pThemeKey];
637
+
638
+ // Apply noise defaults from theme
639
+ if (tmpTheme.NoiseConfig && typeof tmpTheme.NoiseConfig.DefaultLevel === 'number')
640
+ {
641
+ this._NoiseLevel = tmpTheme.NoiseConfig.DefaultLevel;
642
+ }
643
+ else
644
+ {
645
+ this._NoiseLevel = 0;
646
+ }
647
+
648
+ // Apply shape overrides
649
+ if (this._FlowView && this._FlowView._ConnectorShapesProvider)
650
+ {
651
+ this._FlowView._ConnectorShapesProvider.resetToDefaults();
652
+ if (tmpTheme.ShapeOverrides && Object.keys(tmpTheme.ShapeOverrides).length > 0)
653
+ {
654
+ this._FlowView._ConnectorShapesProvider.applyThemeOverrides(tmpTheme.ShapeOverrides);
655
+ }
656
+ }
657
+
658
+ this.log.trace(`PictProviderFlowTheme: switched to '${pThemeKey}'`);
659
+ return true;
660
+ }
661
+
662
+ /**
663
+ * Get the current noise level (0 to 1).
664
+ * @returns {number}
665
+ */
666
+ getNoiseLevel()
667
+ {
668
+ return this._NoiseLevel;
669
+ }
670
+
671
+ /**
672
+ * Set the noise level (0 to 1).
673
+ * @param {number} pLevel
674
+ */
675
+ setNoiseLevel(pLevel)
676
+ {
677
+ this._NoiseLevel = Math.max(0, Math.min(1, pLevel || 0));
678
+ }
679
+
680
+ /**
681
+ * Register a custom theme.
682
+ * @param {string} pKey
683
+ * @param {Object} pThemeDefinition
684
+ */
685
+ registerTheme(pKey, pThemeDefinition)
686
+ {
687
+ if (!pKey || !pThemeDefinition)
688
+ {
689
+ this.log.warn('PictProviderFlowTheme: registerTheme requires key and definition');
690
+ return;
691
+ }
692
+ pThemeDefinition.Key = pKey;
693
+ this._Themes[pKey] = pThemeDefinition;
694
+ }
695
+
696
+ /**
697
+ * Get all registered theme keys.
698
+ * @returns {Array<string>}
699
+ */
700
+ getThemeKeys()
701
+ {
702
+ return Object.keys(this._Themes);
703
+ }
704
+
705
+ // ── Rendering Hooks ───────────────────────────────────────────────────
706
+
707
+ /**
708
+ * Post-process an SVG path string to apply noise/jitter if the
709
+ * active theme has noise enabled for connections.
710
+ *
711
+ * @param {string} pPathString - SVG path d attribute
712
+ * @param {string} pSeedString - Hash for deterministic noise
713
+ * @returns {string} Possibly-modified path string
714
+ */
715
+ processPathString(pPathString, pSeedString)
716
+ {
717
+ let tmpTheme = this.getActiveTheme();
718
+ if (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsConnections)
719
+ {
720
+ return pPathString;
721
+ }
722
+
723
+ let tmpAmplitude = this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);
724
+ if (tmpAmplitude <= 0)
725
+ {
726
+ return pPathString;
727
+ }
728
+
729
+ if (this._FlowView && this._FlowView._NoiseProvider)
730
+ {
731
+ return this._FlowView._NoiseProvider.jitterPath(pPathString, tmpAmplitude, pSeedString);
732
+ }
733
+
734
+ return pPathString;
735
+ }
736
+
737
+ /**
738
+ * Get the noise amplitude for node bracket rendering.
739
+ * Returns 0 if noise is not enabled for nodes in the active theme.
740
+ * @returns {number}
741
+ */
742
+ getNodeNoiseAmplitude()
743
+ {
744
+ let tmpTheme = this.getActiveTheme();
745
+ if (!tmpTheme || !tmpTheme.NoiseConfig || !tmpTheme.NoiseConfig.Enabled || !tmpTheme.NoiseConfig.AffectsNodes)
746
+ {
747
+ return 0;
748
+ }
749
+ return this._NoiseLevel * (tmpTheme.NoiseConfig.MaxJitterPx || 3);
750
+ }
751
+ }
752
+
753
+ module.exports = PictProviderFlowTheme;
754
+
755
+ module.exports.default_configuration = _ProviderConfiguration;