pict-section-flow 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/docs/README.md +19 -0
  2. package/{example_application → example_applications/simple_cards}/html/index.html +2 -2
  3. package/example_applications/simple_cards/package.json +43 -0
  4. package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +434 -0
  5. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +36 -0
  6. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +54 -0
  7. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +48 -0
  8. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +35 -0
  9. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +47 -0
  10. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +53 -0
  11. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +95 -0
  12. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +37 -0
  13. package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +59 -0
  14. package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Layout.js +5 -1
  15. package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +312 -0
  16. package/package.json +6 -6
  17. package/source/Pict-Section-Flow.js +19 -0
  18. package/source/PictFlowCard.js +207 -0
  19. package/source/PictFlowCardPropertiesPanel.js +105 -0
  20. package/source/panels/FlowCardPropertiesPanel-Form.js +174 -0
  21. package/source/panels/FlowCardPropertiesPanel-Markdown.js +148 -0
  22. package/source/panels/FlowCardPropertiesPanel-Template.js +88 -0
  23. package/source/panels/FlowCardPropertiesPanel-View.js +114 -0
  24. package/source/providers/PictProvider-Flow-EventHandler.js +19 -8
  25. package/source/providers/PictProvider-Flow-Geometry.js +64 -0
  26. package/source/providers/PictProvider-Flow-Layouts.js +284 -0
  27. package/source/providers/PictProvider-Flow-NodeTypes.js +70 -0
  28. package/source/providers/PictProvider-Flow-PanelChrome.js +72 -0
  29. package/source/providers/PictProvider-Flow-SVGHelpers.js +30 -0
  30. package/source/services/PictService-Flow-ConnectionRenderer.js +324 -66
  31. package/source/services/PictService-Flow-InteractionManager.js +399 -75
  32. package/source/services/PictService-Flow-Layout.js +159 -0
  33. package/source/services/PictService-Flow-PathGenerator.js +199 -0
  34. package/source/services/PictService-Flow-Tether.js +544 -0
  35. package/source/views/PictView-Flow-Node.js +95 -18
  36. package/source/views/PictView-Flow-PropertiesPanel.js +435 -0
  37. package/source/views/PictView-Flow-Toolbar.js +491 -5
  38. package/source/views/PictView-Flow.js +830 -8
  39. package/example_application/package.json +0 -41
  40. package/example_application/source/Pict-Application-FlowExample.js +0 -241
  41. package/example_application/source/views/PictView-FlowExample-MainWorkspace.js +0 -191
  42. /package/{example_application → example_applications/simple_cards}/css/flowexample.css +0 -0
  43. /package/{example_application → example_applications/simple_cards}/source/Pict-Application-FlowExample-Configuration.json +0 -0
  44. /package/{example_application → example_applications/simple_cards}/source/providers/PictRouter-FlowExample-Configuration.json +0 -0
  45. /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-About.js +0 -0
  46. /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-BottomBar.js +0 -0
  47. /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Documentation.js +0 -0
  48. /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-TopBar.js +0 -0
@@ -0,0 +1,207 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+
3
+ /**
4
+ * PictFlowCard - Base class for flow diagram cards.
5
+ *
6
+ * Developers create subclasses of PictFlowCard to define reusable node types
7
+ * that appear in the flow palette. Each card describes a discrete operation
8
+ * (e.g. "If-Then-Else", "File Read") with configurable inputs, outputs, and
9
+ * metadata. Pict-Section-Flow uses registered cards to build a palette for
10
+ * the user to drag onto the graph.
11
+ *
12
+ * Configurable properties:
13
+ * - Title (string, required) - Display name shown on the node
14
+ * - Name (string, optional) - Longer descriptive name
15
+ * - Code (string, required) - Short identifier (e.g. "ITE", "SW")
16
+ * - Description (string, optional) - Brief explanation of what the card does
17
+ * - Icon (string, optional) - Icon identifier or emoji
18
+ * - PreviewImage (string, optional) - URL to a preview/thumbnail image
19
+ * - Documentation (string, optional) - URL or inline documentation text
20
+ * - Tooltip (string, optional) - Hover tooltip text
21
+ * - Inputs (array) - Named input ports, each with:
22
+ * - Name (string) - Port label
23
+ * - Side (string) - Port side ('left', 'top', etc.)
24
+ * - MinimumInputCount (number) - Minimum connections accepted (default 0)
25
+ * - MaximumInputCount (number) - Maximum connections accepted (default -1, unlimited)
26
+ * - Outputs (array) - Named output ports
27
+ * - Enabled (boolean) - Whether this card is available in the palette
28
+ * - PropertiesPanel (object, optional) - Configuration for the on-graph properties panel
29
+ * - PanelType (string) - 'Template', 'Markdown', 'Form', or 'View'
30
+ * - DefaultWidth (number) - Panel width (default 300)
31
+ * - DefaultHeight (number) - Panel height (default 200)
32
+ * - Title (string) - Panel title bar text
33
+ * - Configuration (object) - Panel-type-specific configuration
34
+ *
35
+ * Usage:
36
+ * class MyCard extends PictFlowCard {
37
+ * constructor(pFable, pOptions, pServiceHash) {
38
+ * super(pFable, pOptions, pServiceHash);
39
+ * this.cardTitle = 'My Card';
40
+ * this.cardCode = 'MC';
41
+ * this.cardInputs = [{ Name: 'Data', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }];
42
+ * this.cardOutputs = [{ Name: 'Result', Side: 'right' }];
43
+ * }
44
+ * }
45
+ */
46
+ class PictFlowCard extends libFableServiceProviderBase
47
+ {
48
+ constructor(pFable, pOptions, pServiceHash)
49
+ {
50
+ let tmpOptions = Object.assign({}, PictFlowCard.default_configuration, pOptions);
51
+ super(pFable, tmpOptions, pServiceHash);
52
+
53
+ this.serviceType = 'PictFlowCard';
54
+
55
+ // --- Card metadata ---
56
+ this.cardTitle = (tmpOptions.Title) ? tmpOptions.Title : 'Card';
57
+ this.cardName = (tmpOptions.Name) ? tmpOptions.Name : false;
58
+ this.cardCode = (tmpOptions.Code) ? tmpOptions.Code : '';
59
+ this.cardDescription = (tmpOptions.Description) ? tmpOptions.Description : false;
60
+ this.cardIcon = (tmpOptions.Icon) ? tmpOptions.Icon : false;
61
+ this.cardPreviewImage = (tmpOptions.PreviewImage) ? tmpOptions.PreviewImage : false;
62
+ this.cardDocumentation = (tmpOptions.Documentation) ? tmpOptions.Documentation : false;
63
+ this.cardTooltip = (tmpOptions.Tooltip) ? tmpOptions.Tooltip : false;
64
+
65
+ // --- Card enabled state ---
66
+ this.cardEnabled = (typeof tmpOptions.Enabled === 'boolean') ? tmpOptions.Enabled : true;
67
+
68
+ // --- Card appearance ---
69
+ this.cardTitleBarColor = (tmpOptions.TitleBarColor) ? tmpOptions.TitleBarColor : '#2c3e50';
70
+ this.cardBodyStyle = (tmpOptions.BodyStyle) ? tmpOptions.BodyStyle : {};
71
+ this.cardWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 180;
72
+ this.cardHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 80;
73
+ this.cardCategory = (tmpOptions.Category) ? tmpOptions.Category : 'General';
74
+
75
+ // --- Input and Output port definitions ---
76
+ // Inputs: [{ Name: 'In', Side: 'left', MinimumInputCount: 0, MaximumInputCount: -1 }]
77
+ // Outputs: [{ Name: 'Out', Side: 'right' }]
78
+ this.cardInputs = Array.isArray(tmpOptions.Inputs) ? tmpOptions.Inputs : [];
79
+ this.cardOutputs = Array.isArray(tmpOptions.Outputs) ? tmpOptions.Outputs : [];
80
+
81
+ // --- Properties panel configuration ---
82
+ this.cardPropertiesPanel = (tmpOptions.PropertiesPanel && typeof tmpOptions.PropertiesPanel === 'object')
83
+ ? tmpOptions.PropertiesPanel
84
+ : null;
85
+ }
86
+
87
+ /**
88
+ * Generate the node type configuration object for the NodeTypes provider.
89
+ * This converts the card's properties into the format expected by
90
+ * PictProviderFlowNodeTypes.registerNodeType().
91
+ *
92
+ * @returns {Object} Node type configuration
93
+ */
94
+ getNodeTypeConfiguration()
95
+ {
96
+ let tmpPorts = [];
97
+
98
+ // Build input ports
99
+ for (let i = 0; i < this.cardInputs.length; i++)
100
+ {
101
+ let tmpInput = this.cardInputs[i];
102
+ let tmpPort =
103
+ {
104
+ Hash: null,
105
+ Direction: 'input',
106
+ Side: tmpInput.Side || 'left',
107
+ Label: tmpInput.Name || `In ${i + 1}`,
108
+ MinimumInputCount: (typeof tmpInput.MinimumInputCount === 'number') ? tmpInput.MinimumInputCount : 0,
109
+ MaximumInputCount: (typeof tmpInput.MaximumInputCount === 'number') ? tmpInput.MaximumInputCount : -1
110
+ };
111
+ tmpPorts.push(tmpPort);
112
+ }
113
+
114
+ // Build output ports
115
+ for (let i = 0; i < this.cardOutputs.length; i++)
116
+ {
117
+ let tmpOutput = this.cardOutputs[i];
118
+ tmpPorts.push(
119
+ {
120
+ Hash: null,
121
+ Direction: 'output',
122
+ Side: tmpOutput.Side || 'right',
123
+ Label: tmpOutput.Name || `Out ${i + 1}`
124
+ });
125
+ }
126
+
127
+ // If no ports were defined, provide sensible defaults
128
+ if (tmpPorts.length === 0)
129
+ {
130
+ tmpPorts.push({ Hash: null, Direction: 'input', Side: 'left', Label: 'In' });
131
+ tmpPorts.push({ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' });
132
+ }
133
+
134
+ let tmpResult =
135
+ {
136
+ Hash: this.cardCode,
137
+ Label: this.cardTitle,
138
+ DefaultWidth: this.cardWidth,
139
+ DefaultHeight: this.cardHeight,
140
+ DefaultPorts: tmpPorts,
141
+ TitleBarColor: this.cardTitleBarColor,
142
+ BodyStyle: JSON.parse(JSON.stringify(this.cardBodyStyle)),
143
+ // Extended FlowCard metadata stored alongside the type
144
+ CardMetadata:
145
+ {
146
+ Name: this.cardName,
147
+ Code: this.cardCode,
148
+ Description: this.cardDescription,
149
+ Icon: this.cardIcon,
150
+ PreviewImage: this.cardPreviewImage,
151
+ Documentation: this.cardDocumentation,
152
+ Tooltip: this.cardTooltip,
153
+ Enabled: this.cardEnabled,
154
+ Category: this.cardCategory
155
+ }
156
+ };
157
+
158
+ // Include properties panel config if defined
159
+ if (this.cardPropertiesPanel)
160
+ {
161
+ tmpResult.PropertiesPanel = JSON.parse(JSON.stringify(this.cardPropertiesPanel));
162
+ }
163
+
164
+ return tmpResult;
165
+ }
166
+
167
+ /**
168
+ * Register this card with a FlowView's node type provider.
169
+ *
170
+ * @param {Object} pFlowView - The PictViewFlow instance
171
+ * @returns {boolean} Whether registration succeeded
172
+ */
173
+ registerWithFlowView(pFlowView)
174
+ {
175
+ if (!pFlowView || !pFlowView._NodeTypeProvider)
176
+ {
177
+ this.log.warn('PictFlowCard registerWithFlowView: no valid FlowView or NodeTypeProvider');
178
+ return false;
179
+ }
180
+
181
+ let tmpConfig = this.getNodeTypeConfiguration();
182
+ return pFlowView._NodeTypeProvider.registerNodeType(tmpConfig);
183
+ }
184
+ }
185
+
186
+ module.exports = PictFlowCard;
187
+
188
+ module.exports.default_configuration =
189
+ {
190
+ Title: 'Card',
191
+ Name: false,
192
+ Code: '',
193
+ Description: false,
194
+ Icon: false,
195
+ PreviewImage: false,
196
+ Documentation: false,
197
+ Tooltip: false,
198
+ Inputs: [],
199
+ Outputs: [],
200
+ Enabled: true,
201
+ TitleBarColor: '#2c3e50',
202
+ BodyStyle: {},
203
+ Width: 180,
204
+ Height: 80,
205
+ Category: 'General',
206
+ PropertiesPanel: null
207
+ };
@@ -0,0 +1,105 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+
3
+ /**
4
+ * PictFlowCardPropertiesPanel - Base class for flow card property panels.
5
+ *
6
+ * Developers create subclasses to define what UI appears when a user opens
7
+ * the properties panel for a node on the flow graph. Four built-in panel
8
+ * types are provided:
9
+ *
10
+ * - Template - Renders pict templates
11
+ * - Markdown - Renders markdown via pict-section-content
12
+ * - Form - Creates an ephemeral pict-section-form section
13
+ * - View - Renders an existing registered pict-view
14
+ *
15
+ * Configurable properties:
16
+ * - PanelType (string) - Panel type identifier
17
+ * - Title (string) - Panel title bar text
18
+ * - Width (number) - Default panel width in pixels
19
+ * - Height (number) - Default panel height in pixels
20
+ * - Configuration (object) - Panel-type-specific configuration
21
+ */
22
+ class PictFlowCardPropertiesPanel extends libFableServiceProviderBase
23
+ {
24
+ constructor(pFable, pOptions, pServiceHash)
25
+ {
26
+ let tmpOptions = Object.assign({}, PictFlowCardPropertiesPanel.default_configuration, pOptions);
27
+ super(pFable, tmpOptions, pServiceHash);
28
+
29
+ this.serviceType = 'PictFlowCardPropertiesPanel';
30
+
31
+ // Panel metadata
32
+ this.panelType = tmpOptions.PanelType || 'Base';
33
+ this.panelTitle = tmpOptions.Title || 'Properties';
34
+ this.panelWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 300;
35
+ this.panelHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 200;
36
+
37
+ // Reference to the flow view (set when panel is activated)
38
+ this._FlowView = null;
39
+
40
+ // The node data this panel is operating on (set when panel is opened)
41
+ this._NodeData = null;
42
+
43
+ // The DOM container element for panel content (set during render)
44
+ this._ContentContainer = null;
45
+
46
+ // The panel configuration (panel-type-specific)
47
+ this._Configuration = tmpOptions.Configuration || {};
48
+ }
49
+
50
+ /**
51
+ * Render the panel's content into a DOM container element.
52
+ * Subclasses MUST override this.
53
+ *
54
+ * @param {HTMLElement} pContainer - The DOM element to render into
55
+ * @param {Object} pNodeData - The node data object (has .Data property)
56
+ */
57
+ render(pContainer, pNodeData)
58
+ {
59
+ this._ContentContainer = pContainer;
60
+ this._NodeData = pNodeData;
61
+ }
62
+
63
+ /**
64
+ * Marshal data FROM the node's Data object INTO the panel UI.
65
+ * Called when the panel opens or when data changes externally.
66
+ *
67
+ * @param {Object} pNodeData
68
+ */
69
+ marshalToPanel(pNodeData)
70
+ {
71
+ this._NodeData = pNodeData;
72
+ }
73
+
74
+ /**
75
+ * Marshal data FROM the panel UI INTO the node's Data object.
76
+ * Called before saving or when the panel is about to close.
77
+ *
78
+ * @param {Object} pNodeData
79
+ */
80
+ marshalFromPanel(pNodeData)
81
+ {
82
+ // Subclasses override
83
+ }
84
+
85
+ /**
86
+ * Called when the panel is being destroyed (closed).
87
+ * Subclasses should clean up resources.
88
+ */
89
+ destroy()
90
+ {
91
+ this._ContentContainer = null;
92
+ this._NodeData = null;
93
+ }
94
+ }
95
+
96
+ module.exports = PictFlowCardPropertiesPanel;
97
+
98
+ module.exports.default_configuration =
99
+ {
100
+ PanelType: 'Base',
101
+ Title: 'Properties',
102
+ Width: 300,
103
+ Height: 200,
104
+ Configuration: {}
105
+ };
@@ -0,0 +1,174 @@
1
+ const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
2
+
3
+ /**
4
+ * FlowCardPropertiesPanel-Form
5
+ *
6
+ * Creates an ephemeral pict-section-form section to edit the node's Data object.
7
+ * Uses PictViewFormMetacontroller.injectManifest() to dynamically create form
8
+ * sections at runtime.
9
+ *
10
+ * Note: pict-section-form must be available in the consuming application
11
+ * (it is an optional/peer dependency, not bundled with pict-section-flow).
12
+ *
13
+ * Configuration:
14
+ * {
15
+ * PanelType: 'Form',
16
+ * Configuration: {
17
+ * Manifest: {
18
+ * Sections: [...],
19
+ * Descriptors: {...}
20
+ * }
21
+ * }
22
+ * }
23
+ */
24
+ class FlowCardPropertiesPanelForm extends libPictFlowCardPropertiesPanel
25
+ {
26
+ constructor(pFable, pOptions, pServiceHash)
27
+ {
28
+ super(pFable, pOptions, pServiceHash);
29
+
30
+ this.serviceType = 'PictFlowCardPropertiesPanel-Form';
31
+
32
+ this._Metacontroller = null;
33
+ this._InjectedSectionHash = null;
34
+ }
35
+
36
+ /**
37
+ * Render the form into the panel body.
38
+ */
39
+ render(pContainer, pNodeData)
40
+ {
41
+ super.render(pContainer, pNodeData);
42
+
43
+ if (!this._Configuration || !this._Configuration.Manifest)
44
+ {
45
+ pContainer.innerHTML = '<em>No form manifest configured</em>';
46
+ return;
47
+ }
48
+
49
+ // Create a unique container ID for the form section
50
+ let tmpContainerID = `pict-flow-panel-form-${pNodeData.Hash}`;
51
+ pContainer.innerHTML = `<div id="${tmpContainerID}"></div>`;
52
+
53
+ try
54
+ {
55
+ // Look for an existing metacontroller or create one
56
+ if (!this._Metacontroller)
57
+ {
58
+ // Try to find PictFormMetacontroller service type (check both common names)
59
+ let tmpServiceType = null;
60
+ if (this.fable.servicesMap.hasOwnProperty('PictFormMetacontroller'))
61
+ {
62
+ tmpServiceType = 'PictFormMetacontroller';
63
+ }
64
+ else if (this.fable.servicesMap.hasOwnProperty('PictViewFormMetacontroller'))
65
+ {
66
+ tmpServiceType = 'PictViewFormMetacontroller';
67
+ }
68
+
69
+ if (tmpServiceType)
70
+ {
71
+ this._Metacontroller = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceType,
72
+ {
73
+ ViewIdentifier: `FlowPanelForm-${pNodeData.Hash}`,
74
+ DefaultDestinationAddress: `#${tmpContainerID}`,
75
+ AutoRender: false,
76
+ AutoPopulateAfterRender: true,
77
+ AutoSolveBeforeRender: false
78
+ });
79
+ }
80
+ }
81
+
82
+ if (this._Metacontroller && typeof this._Metacontroller.injectManifestAndRender === 'function')
83
+ {
84
+ // Create the FormContainer div that the metacontroller's
85
+ // updateMetatemplateInDOM() expects when adding section wrappers.
86
+ // Normally this is created when the metacontroller renders its own
87
+ // metatemplate, but we skip that step since we only need the
88
+ // injected section views, not the metacontroller's own renderable.
89
+ let tmpFormContainerID = `Pict-${this._Metacontroller.UUID}-FormContainer`;
90
+ let tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);
91
+ if (tmpContainerEl)
92
+ {
93
+ tmpContainerEl.innerHTML = `<div id="${tmpFormContainerID}" class="pict-form"></div>`;
94
+ }
95
+
96
+ // Deep clone the manifest so each panel gets its own copy
97
+ let tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));
98
+ this._InjectedSectionHash = tmpManifest.Hash || null;
99
+
100
+ // Use injectManifestAndRender which properly creates section views,
101
+ // updates the metatemplate in the DOM, and renders each section view
102
+ this._Metacontroller.injectManifestAndRender(tmpManifest);
103
+ }
104
+ else if (this._Metacontroller && typeof this._Metacontroller.injectManifest === 'function')
105
+ {
106
+ // Fallback for older pict-section-form versions: inject the
107
+ // manifest and render each section view individually
108
+ let tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));
109
+ let tmpViewsToRender = this._Metacontroller.injectManifest(tmpManifest);
110
+ this._InjectedSectionHash = tmpManifest.Hash || null;
111
+
112
+ // Create container divs for each section view and render them
113
+ let tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);
114
+ if (tmpContainerEl && tmpViewsToRender.length > 0)
115
+ {
116
+ let tmpInnerHTML = '';
117
+ for (let i = 0; i < tmpViewsToRender.length; i++)
118
+ {
119
+ let tmpDestID = tmpViewsToRender[i].options.DefaultDestinationAddress;
120
+ if (tmpDestID && tmpDestID.charAt(0) === '#')
121
+ {
122
+ tmpDestID = tmpDestID.substring(1);
123
+ }
124
+ tmpInnerHTML += `<div id="${tmpDestID}" class="pict-form-view"></div>`;
125
+ }
126
+ tmpContainerEl.innerHTML = tmpInnerHTML;
127
+
128
+ for (let i = 0; i < tmpViewsToRender.length; i++)
129
+ {
130
+ tmpViewsToRender[i].render();
131
+ }
132
+ }
133
+ }
134
+ else
135
+ {
136
+ pContainer.innerHTML = '<em>pict-section-form is not available. Install it in your application to use Form panels.</em>';
137
+ }
138
+ }
139
+ catch (pError)
140
+ {
141
+ this.log.warn(`FlowCardPropertiesPanel-Form render error: ${pError.message}`);
142
+ pContainer.innerHTML = `<em>Form render error: ${pError.message}</em>`;
143
+ }
144
+ }
145
+
146
+ marshalFromPanel(pNodeData)
147
+ {
148
+ if (this._Metacontroller && typeof this._Metacontroller.marshalFromView === 'function')
149
+ {
150
+ this._Metacontroller.marshalFromView();
151
+ }
152
+ }
153
+
154
+ destroy()
155
+ {
156
+ this._Metacontroller = null;
157
+ this._InjectedSectionHash = null;
158
+ super.destroy();
159
+ }
160
+ }
161
+
162
+ module.exports = FlowCardPropertiesPanelForm;
163
+
164
+ module.exports.default_configuration = Object.assign(
165
+ {},
166
+ libPictFlowCardPropertiesPanel.default_configuration,
167
+ {
168
+ PanelType: 'Form',
169
+ Configuration:
170
+ {
171
+ Manifest: null
172
+ }
173
+ }
174
+ );
@@ -0,0 +1,148 @@
1
+ const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
2
+
3
+ /**
4
+ * FlowCardPropertiesPanel-Markdown
5
+ *
6
+ * Renders markdown content into the panel body using pict-section-content's
7
+ * PictContentProvider service. When pict-section-content is installed and its
8
+ * PictContentProvider service type has been registered with the pict instance,
9
+ * full markdown rendering is available (headings, lists, tables, code blocks
10
+ * with syntax highlighting, KaTeX equations, Mermaid diagrams, etc.).
11
+ *
12
+ * If PictContentProvider is not available, the panel falls back to displaying
13
+ * the raw markdown as pre-formatted text.
14
+ *
15
+ * Configuration:
16
+ * {
17
+ * PanelType: 'Markdown',
18
+ * Configuration: {
19
+ * Markdown: '# Title\nSome **markdown** content'
20
+ * }
21
+ * }
22
+ *
23
+ * Or use an address to pull markdown from the node's data:
24
+ * {
25
+ * PanelType: 'Markdown',
26
+ * Configuration: {
27
+ * MarkdownAddress: 'Data.MarkdownContent'
28
+ * }
29
+ * }
30
+ */
31
+ class FlowCardPropertiesPanelMarkdown extends libPictFlowCardPropertiesPanel
32
+ {
33
+ constructor(pFable, pOptions, pServiceHash)
34
+ {
35
+ super(pFable, pOptions, pServiceHash);
36
+
37
+ this.serviceType = 'PictFlowCardPropertiesPanel-Markdown';
38
+
39
+ this._ContentProvider = null;
40
+ }
41
+
42
+ /**
43
+ * Render markdown into the panel.
44
+ */
45
+ render(pContainer, pNodeData)
46
+ {
47
+ super.render(pContainer, pNodeData);
48
+
49
+ this._renderMarkdown();
50
+ }
51
+
52
+ marshalToPanel(pNodeData)
53
+ {
54
+ super.marshalToPanel(pNodeData);
55
+ this._renderMarkdown();
56
+ }
57
+
58
+ _renderMarkdown()
59
+ {
60
+ if (!this._ContentContainer || !this._Configuration) return;
61
+
62
+ let tmpMarkdown = '';
63
+
64
+ if (this._Configuration.MarkdownAddress && this._NodeData)
65
+ {
66
+ // Resolve markdown from node data using manyfest
67
+ tmpMarkdown = this.fable.manifest.getValueByHash(this._NodeData, this._Configuration.MarkdownAddress) || '';
68
+ }
69
+ else if (this._Configuration.Markdown)
70
+ {
71
+ tmpMarkdown = this._Configuration.Markdown;
72
+ }
73
+
74
+ if (!tmpMarkdown)
75
+ {
76
+ this._ContentContainer.innerHTML = '<em>No content</em>';
77
+ return;
78
+ }
79
+
80
+ // Use pict-section-content's PictContentProvider for markdown rendering.
81
+ // The consuming application must register the PictContentProvider service
82
+ // type (from pict-section-content) for this to work.
83
+ try
84
+ {
85
+ if (!this._ContentProvider)
86
+ {
87
+ if (this.fable.servicesMap.hasOwnProperty('PictContentProvider'))
88
+ {
89
+ this._ContentProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictContentProvider', {});
90
+ }
91
+ }
92
+
93
+ if (this._ContentProvider && typeof this._ContentProvider.parseMarkdown === 'function')
94
+ {
95
+ let tmpHTML = this._ContentProvider.parseMarkdown(tmpMarkdown);
96
+ this._ContentContainer.innerHTML = tmpHTML;
97
+
98
+ // Post-render hooks for equations and diagrams
99
+ if (typeof this._ContentProvider.renderKaTeXEquations === 'function')
100
+ {
101
+ this._ContentProvider.renderKaTeXEquations(this._ContentContainer);
102
+ }
103
+ if (typeof this._ContentProvider.renderMermaidDiagrams === 'function')
104
+ {
105
+ this._ContentProvider.renderMermaidDiagrams(this._ContentContainer);
106
+ }
107
+ }
108
+ else
109
+ {
110
+ // PictContentProvider not registered — render as pre-formatted text
111
+ this._ContentContainer.innerHTML = `<pre style="white-space: pre-wrap; font-family: inherit;">${this._escapeHTML(tmpMarkdown)}</pre>`;
112
+ }
113
+ }
114
+ catch (pError)
115
+ {
116
+ this.log.warn(`FlowCardPropertiesPanel-Markdown render error: ${pError.message}`);
117
+ this._ContentContainer.innerHTML = `<pre style="white-space: pre-wrap; font-family: inherit;">${this._escapeHTML(tmpMarkdown)}</pre>`;
118
+ }
119
+ }
120
+
121
+ _escapeHTML(pText)
122
+ {
123
+ let tmpDiv = document.createElement('div');
124
+ tmpDiv.textContent = pText;
125
+ return tmpDiv.innerHTML;
126
+ }
127
+
128
+ destroy()
129
+ {
130
+ this._ContentProvider = null;
131
+ super.destroy();
132
+ }
133
+ }
134
+
135
+ module.exports = FlowCardPropertiesPanelMarkdown;
136
+
137
+ module.exports.default_configuration = Object.assign(
138
+ {},
139
+ libPictFlowCardPropertiesPanel.default_configuration,
140
+ {
141
+ PanelType: 'Markdown',
142
+ Configuration:
143
+ {
144
+ Markdown: '',
145
+ MarkdownAddress: ''
146
+ }
147
+ }
148
+ );