pict-section-flow 1.0.0 → 1.0.1

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.
@@ -6,33 +6,39 @@ const _ProviderConfiguration =
6
6
  };
7
7
 
8
8
  /**
9
- * PictProvider-Flow-Theme
9
+ * PictProvider-Flow-Theme — back-compat shim.
10
10
  *
11
- * Central orchestrator for the flow diagram theming system.
11
+ * Flow's theming system was decomposed into three independent axes:
12
12
  *
13
- * Holds a registry of theme definitions, manages the active theme,
14
- * and provides hooks for node body rendering and path noise processing.
13
+ * 1. **ColorTheme** managed by pict-section-theme (the 7 `flow-*`
14
+ * bundled themes plus any other catalog entries).
15
+ * 2. **Renderer** — managed by PictProviderFlowRenderer (node body
16
+ * mode, bracket config, noise config, connection stroke,
17
+ * shape overrides, geometry CSS).
18
+ * 3. **EdgeTheme** — managed by PictService-Flow-Layout's edge themes
19
+ * (Bezier, Straight, Orthogonal, Perimeter…).
15
20
  *
16
- * ## Usage
21
+ * The user-facing concept of "Theme" maps to a **StylePreset** —
22
+ * a curated triple `(ColorTheme, Renderer, EdgeTheme)` — registered
23
+ * with PictProviderFlowStylePresets.
17
24
  *
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
- * ```
25
+ * This shim preserves the legacy API surface (`setTheme`, `getNoiseLevel`,
26
+ * `setNoiseLevel`, `processPathString`, `getNodeNoiseAmplitude`,
27
+ * `getActiveTheme`, `getActiveThemeKey`, `getThemeKeys`, `registerTheme`)
28
+ * so consumer code calling the old methods keeps working unmodified.
23
29
  *
24
- * ## Custom Themes
30
+ * Every method here delegates to the appropriate new provider — most
31
+ * commonly the Renderer provider (for noise + geometry) or the
32
+ * StylePresets provider (for `setTheme`/`getActiveThemeKey`).
25
33
  *
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
- * ```
34
+ * New code should use the per-axis APIs directly:
35
+ *
36
+ * ```javascript
37
+ * flowView.setStylePreset('sketch'); // primary
38
+ * flowView.setColorTheme('flow-blueprint');
39
+ * flowView.setRenderer('bracket');
40
+ * flowView.setEdgeTheme('orthogonal');
41
+ * ```
36
42
  */
37
43
  class PictProviderFlowTheme extends libFableServiceProviderBase
38
44
  {
@@ -44,709 +50,131 @@ class PictProviderFlowTheme extends libFableServiceProviderBase
44
50
  this.serviceType = 'PictProviderFlowTheme';
45
51
 
46
52
  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
53
  }
54
54
 
55
- // ── Theme Registry ────────────────────────────────────────────────────
55
+ // ── Helpers ───────────────────────────────────────────────────────────
56
56
 
57
- _registerBuiltInThemes()
57
+ _renderer()
58
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: var(--theme-color-background-panel, #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 var(--theme-color-background-panel, #ffffff);
462
- }
463
- .pict-flow-toolbar-btn {
464
- background-color: #c0c0c0;
465
- border: 2px outset #c0c0c0;
466
- border-radius: 0;
467
- color: var(--theme-color-text-primary, #000000);
468
- }
469
- .pict-flow-toolbar-btn:hover {
470
- background-color: var(--theme-color-border-default, #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
- };
59
+ return (this._FlowView && this._FlowView._RendererProvider) || null;
60
+ }
502
61
 
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: var(--theme-color-status-success, #27ae60); }
548
- .pict-flow-node-end .pict-flow-node-bracket { stroke: #1abc9c; }
549
- .pict-flow-node-halt .pict-flow-node-bracket { stroke: var(--theme-color-status-error, #e74c3c); }
550
- .pict-flow-node-decision .pict-flow-node-bracket { stroke: var(--theme-color-status-warning, #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
- };
62
+ _presets()
63
+ {
64
+ return (this._FlowView && this._FlowView._StylePresetsProvider) || null;
596
65
  }
597
66
 
598
- // ── Public API ────────────────────────────────────────────────────────
67
+ // ── Compat API ────────────────────────────────────────────────────────
599
68
 
600
69
  /**
601
- * Get the active theme definition.
70
+ * Compat: get the active style preset's "theme-shaped" view. Returns
71
+ * a synthetic object that exposes the legacy properties (NoiseConfig,
72
+ * NodeBodyMode, etc.) by pulling from the active renderer.
602
73
  * @returns {Object}
603
74
  */
604
75
  getActiveTheme()
605
76
  {
606
- return this._Themes[this._ActiveThemeKey] || this._Themes['default'];
77
+ let tmpRenderer = this._renderer();
78
+ let tmpPresets = this._presets();
79
+ let tmpPresetHash = tmpPresets ? tmpPresets.getActivePresetHash() : null;
80
+ let tmpR = tmpRenderer ? tmpRenderer.getActiveRenderer() : {};
81
+ return {
82
+ Key: tmpPresetHash || tmpR.Key || 'modern',
83
+ Label: tmpR.Label || 'Modern',
84
+ NodeBodyMode: tmpR.NodeBodyMode,
85
+ BracketConfig: tmpR.BracketConfig,
86
+ NoiseConfig: tmpR.NoiseConfig,
87
+ ConnectionConfig: tmpR.ConnectionConfig,
88
+ ShapeOverrides: tmpR.ShapeOverrides
89
+ };
607
90
  }
608
91
 
609
92
  /**
610
- * Get the active theme key.
93
+ * Compat: active style preset hash (or, if customized, the active renderer key).
611
94
  * @returns {string}
612
95
  */
613
96
  getActiveThemeKey()
614
97
  {
615
- return this._ActiveThemeKey;
98
+ let tmpPresets = this._presets();
99
+ let tmpHash = tmpPresets ? tmpPresets.getActivePresetHash() : null;
100
+ if (tmpHash) return tmpHash;
101
+ let tmpRenderer = this._renderer();
102
+ return tmpRenderer ? tmpRenderer.getActiveRendererKey() : 'modern';
616
103
  }
617
104
 
618
105
  /**
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
- *
106
+ * Compat: pass through to StylePresets.applyPreset.
624
107
  * @param {string} pThemeKey
625
- * @returns {boolean} Whether the theme was found and applied
108
+ * @returns {boolean}
626
109
  */
627
110
  setTheme(pThemeKey)
628
111
  {
629
- if (!this._Themes[pThemeKey])
112
+ let tmpPresets = this._presets();
113
+ if (!tmpPresets)
630
114
  {
631
- this.log.warn(`PictProviderFlowTheme: theme '${pThemeKey}' not found`);
115
+ this.log.warn('PictProviderFlowTheme: setTheme() called but StylePresets provider not available');
632
116
  return false;
633
117
  }
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;
118
+ return tmpPresets.applyPreset(pThemeKey);
660
119
  }
661
120
 
662
121
  /**
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.
122
+ * Compat: register a "theme" translates to a style preset registration.
123
+ * The legacy shape (CSSVariables / NodeBodyMode / NoiseConfig) is no
124
+ * longer supported wholesale; callers wanting full custom looks should
125
+ * register a Renderer and (optionally) a ColorTheme and StylePreset
126
+ * separately. This shim accepts a minimal preset-style payload:
127
+ * { Hash, Label?, ColorTheme, Renderer, EdgeTheme, NoiseLevel? }
682
128
  * @param {string} pKey
683
- * @param {Object} pThemeDefinition
129
+ * @param {Object} pDef
684
130
  */
685
- registerTheme(pKey, pThemeDefinition)
131
+ registerTheme(pKey, pDef)
686
132
  {
687
- if (!pKey || !pThemeDefinition)
133
+ let tmpPresets = this._presets();
134
+ if (!tmpPresets || !pKey || !pDef)
688
135
  {
689
- this.log.warn('PictProviderFlowTheme: registerTheme requires key and definition');
136
+ this.log.warn('PictProviderFlowTheme: registerTheme requires StylePresets provider, a key, and a definition');
690
137
  return;
691
138
  }
692
- pThemeDefinition.Key = pKey;
693
- this._Themes[pKey] = pThemeDefinition;
139
+ let tmpPreset = Object.assign({}, pDef, { Hash: pKey });
140
+ tmpPresets.register(tmpPreset);
694
141
  }
695
142
 
696
143
  /**
697
- * Get all registered theme keys.
144
+ * Compat: list registered preset hashes.
698
145
  * @returns {Array<string>}
699
146
  */
700
147
  getThemeKeys()
701
148
  {
702
- return Object.keys(this._Themes);
149
+ let tmpPresets = this._presets();
150
+ return tmpPresets ? tmpPresets.getPresetHashes() : [];
703
151
  }
704
152
 
705
- // ── Rendering Hooks ───────────────────────────────────────────────────
153
+ // ── Noise + path delegations (preserved verbatim API) ─────────────────
706
154
 
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)
155
+ getNoiseLevel()
716
156
  {
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
- }
157
+ let tmpRenderer = this._renderer();
158
+ return tmpRenderer ? tmpRenderer.getNoiseLevel() : 0;
159
+ }
733
160
 
734
- return pPathString;
161
+ setNoiseLevel(pLevel)
162
+ {
163
+ let tmpRenderer = this._renderer();
164
+ if (tmpRenderer) tmpRenderer.setNoiseLevel(pLevel);
735
165
  }
736
166
 
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
167
  getNodeNoiseAmplitude()
743
168
  {
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);
169
+ let tmpRenderer = this._renderer();
170
+ return tmpRenderer ? tmpRenderer.getNodeNoiseAmplitude() : 0;
171
+ }
172
+
173
+ processPathString(pPathString, pSeedString)
174
+ {
175
+ let tmpRenderer = this._renderer();
176
+ if (!tmpRenderer) return pPathString;
177
+ return tmpRenderer.processPathString(pPathString, pSeedString);
750
178
  }
751
179
  }
752
180