@kabel-project/kabel 1.0.7 → 1.0.9

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 (273) hide show
  1. package/dist/comment-renderer/renderer.d.ts +32 -0
  2. package/dist/comment-renderer/renderer.d.ts.map +1 -0
  3. package/dist/controllers/base.d.ts +39 -0
  4. package/dist/controllers/base.d.ts.map +1 -0
  5. package/dist/controllers/wasd.d.ts +33 -0
  6. package/dist/controllers/wasd.d.ts.map +1 -0
  7. package/dist/events/comment-drag-handle.d.ts +2 -0
  8. package/dist/events/comment-drag-handle.d.ts.map +1 -0
  9. package/dist/events/comment-input.d.ts +4 -0
  10. package/dist/events/comment-input.d.ts.map +1 -0
  11. package/dist/events/connection-line.d.ts +2 -0
  12. package/dist/events/connection-line.d.ts.map +1 -0
  13. package/dist/events/connector.d.ts +2 -0
  14. package/dist/events/connector.d.ts.map +1 -0
  15. package/dist/events/draggable.d.ts +2 -0
  16. package/dist/events/draggable.d.ts.map +1 -0
  17. package/{events/events.ts → dist/events/events.d.ts} +8 -7
  18. package/dist/events/events.d.ts.map +1 -0
  19. package/dist/events/input-box.d.ts +2 -0
  20. package/dist/events/input-box.d.ts.map +1 -0
  21. package/dist/events/node-x-btn.d.ts +2 -0
  22. package/dist/events/node-x-btn.d.ts.map +1 -0
  23. package/dist/kabel.js +2 -0
  24. package/dist/kabel.js.map +1 -0
  25. package/dist/renderers/apollo/apollo.d.ts +12 -0
  26. package/dist/renderers/apollo/apollo.d.ts.map +1 -0
  27. package/dist/renderers/apollo/constants.d.ts +21 -0
  28. package/dist/renderers/apollo/constants.d.ts.map +1 -0
  29. package/dist/renderers/apollo/renderer.d.ts +97 -0
  30. package/dist/renderers/apollo/renderer.d.ts.map +1 -0
  31. package/{renderers/atlas/atlas.ts → dist/renderers/atlas/atlas.d.ts} +6 -15
  32. package/dist/renderers/atlas/atlas.d.ts.map +1 -0
  33. package/dist/renderers/constants.d.ts +51 -0
  34. package/dist/renderers/constants.d.ts.map +1 -0
  35. package/dist/renderers/renderer.d.ts +470 -0
  36. package/dist/renderers/renderer.d.ts.map +1 -0
  37. package/dist/renderers/representer-node.d.ts +27 -0
  38. package/dist/renderers/representer-node.d.ts.map +1 -0
  39. package/dist/renderers/representer.d.ts +13 -0
  40. package/dist/renderers/representer.d.ts.map +1 -0
  41. package/dist/src/category.d.ts +48 -0
  42. package/dist/src/category.d.ts.map +1 -0
  43. package/{src/colors.ts → dist/src/colors.d.ts} +21 -20
  44. package/dist/src/colors.d.ts.map +1 -0
  45. package/dist/src/comment.d.ts +88 -0
  46. package/dist/src/comment.d.ts.map +1 -0
  47. package/dist/src/connection.d.ts +52 -0
  48. package/dist/src/connection.d.ts.map +1 -0
  49. package/dist/src/context-menu.d.ts +76 -0
  50. package/dist/src/context-menu.d.ts.map +1 -0
  51. package/dist/src/coordinates.d.ts +52 -0
  52. package/dist/src/coordinates.d.ts.map +1 -0
  53. package/dist/src/core.d.ts +153 -0
  54. package/dist/src/core.d.ts.map +1 -0
  55. package/dist/src/ctx-menu-registry.d.ts +22 -0
  56. package/dist/src/ctx-menu-registry.d.ts.map +1 -0
  57. package/dist/src/dropdown-menu.d.ts +87 -0
  58. package/dist/src/dropdown-menu.d.ts.map +1 -0
  59. package/dist/src/field.d.ts +305 -0
  60. package/dist/src/field.d.ts.map +1 -0
  61. package/dist/src/flyout.d.ts +41 -0
  62. package/dist/src/flyout.d.ts.map +1 -0
  63. package/dist/src/fonts-manager.d.ts +6 -0
  64. package/dist/src/fonts-manager.d.ts.map +1 -0
  65. package/dist/src/grid.d.ts +60 -0
  66. package/dist/src/grid.d.ts.map +1 -0
  67. package/dist/src/headless-node.d.ts +11 -0
  68. package/dist/src/headless-node.d.ts.map +1 -0
  69. package/dist/src/index.d.ts +38 -0
  70. package/dist/src/index.d.ts.map +1 -0
  71. package/dist/src/inject-headless.d.ts +4 -0
  72. package/dist/src/inject-headless.d.ts.map +1 -0
  73. package/{src/inject.ts → dist/src/inject.d.ts} +142 -213
  74. package/dist/src/inject.d.ts.map +1 -0
  75. package/{src/main-workspace.ts → dist/src/main-workspace.d.ts} +31 -51
  76. package/dist/src/main-workspace.d.ts.map +1 -0
  77. package/dist/src/mutator.d.ts +2 -0
  78. package/dist/src/mutator.d.ts.map +1 -0
  79. package/{src/node-types.ts → dist/src/node-types.d.ts} +26 -27
  80. package/dist/src/node-types.d.ts.map +1 -0
  81. package/dist/src/nodesvg.d.ts +266 -0
  82. package/dist/src/nodesvg.d.ts.map +1 -0
  83. package/{src/prototypes.ts → dist/src/prototypes.d.ts} +10 -9
  84. package/dist/src/prototypes.d.ts.map +1 -0
  85. package/{src/renderer-map.ts → dist/src/renderer-map.d.ts} +51 -86
  86. package/dist/src/renderer-map.d.ts.map +1 -0
  87. package/dist/src/toolbox.d.ts +51 -0
  88. package/dist/src/toolbox.d.ts.map +1 -0
  89. package/{src/types.ts → dist/src/types.d.ts} +159 -205
  90. package/dist/src/types.d.ts.map +1 -0
  91. package/dist/src/undo-redo.d.ts +15 -0
  92. package/dist/src/undo-redo.d.ts.map +1 -0
  93. package/{src/visual-types.ts → dist/src/visual-types.d.ts} +34 -29
  94. package/dist/src/visual-types.d.ts.map +1 -0
  95. package/dist/src/widget-prototypes.d.ts +10 -0
  96. package/dist/src/widget-prototypes.d.ts.map +1 -0
  97. package/dist/src/widget.d.ts +62 -0
  98. package/dist/src/widget.d.ts.map +1 -0
  99. package/{src/workspace-coords.ts → dist/src/workspace-coords.d.ts} +10 -14
  100. package/dist/src/workspace-coords.d.ts.map +1 -0
  101. package/dist/src/workspace-svg.d.ts +371 -0
  102. package/dist/src/workspace-svg.d.ts.map +1 -0
  103. package/dist/src/workspace.d.ts +50 -0
  104. package/dist/src/workspace.d.ts.map +1 -0
  105. package/dist/themes/dark.d.ts +4 -0
  106. package/dist/themes/dark.d.ts.map +1 -0
  107. package/dist/themes/default.d.ts +4 -0
  108. package/dist/themes/default.d.ts.map +1 -0
  109. package/dist/themes/themes.d.ts +6 -0
  110. package/dist/themes/themes.d.ts.map +1 -0
  111. package/dist/util/emitter.d.ts +10 -0
  112. package/dist/util/emitter.d.ts.map +1 -0
  113. package/dist/util/env.d.ts +7 -0
  114. package/dist/util/env.d.ts.map +1 -0
  115. package/{util/escape-html.ts → dist/util/escape-html.d.ts} +16 -22
  116. package/dist/util/escape-html.d.ts.map +1 -0
  117. package/dist/util/eventer.d.ts +29 -0
  118. package/dist/util/eventer.d.ts.map +1 -0
  119. package/dist/util/has-prop.d.ts +2 -0
  120. package/dist/util/has-prop.d.ts.map +1 -0
  121. package/dist/util/parse-color.d.ts +6 -0
  122. package/dist/util/parse-color.d.ts.map +1 -0
  123. package/dist/util/path.d.ts +25 -0
  124. package/dist/util/path.d.ts.map +1 -0
  125. package/dist/util/styler.d.ts +12 -0
  126. package/dist/util/styler.d.ts.map +1 -0
  127. package/dist/util/uid.d.ts +42 -0
  128. package/dist/util/uid.d.ts.map +1 -0
  129. package/{util/unescape-html.ts → dist/util/unescape-html.d.ts} +16 -22
  130. package/dist/util/unescape-html.d.ts.map +1 -0
  131. package/dist/util/user-state.d.ts +37 -0
  132. package/dist/util/user-state.d.ts.map +1 -0
  133. package/{util/wait-anim-frames.ts → dist/util/wait-anim-frames.d.ts} +11 -24
  134. package/dist/util/wait-anim-frames.d.ts.map +1 -0
  135. package/dist/util/window-listeners.d.ts +8 -0
  136. package/dist/util/window-listeners.d.ts.map +1 -0
  137. package/package.json +5 -2
  138. package/(1.0.7)kabel.md +0 -18
  139. package/_READ_ME_MEDIA_/documentation/docs.md +0 -293
  140. package/_READ_ME_MEDIA_/workspace.png +0 -0
  141. package/comment-renderer/renderer.ts +0 -228
  142. package/controllers/base.ts +0 -186
  143. package/controllers/wasd.ts +0 -132
  144. package/docs/README.md +0 -98
  145. package/docs/_media/docs.md +0 -289
  146. package/docs/_media/workspace.png +0 -0
  147. package/docs/classes/CommentModel.md +0 -271
  148. package/docs/classes/CommentRenderer.md +0 -457
  149. package/docs/classes/ConnectableField.md +0 -597
  150. package/docs/classes/Connection.md +0 -191
  151. package/docs/classes/ContextMenuHTML.md +0 -163
  152. package/docs/classes/Coordinates.md +0 -187
  153. package/docs/classes/DropdownContainer.md +0 -300
  154. package/docs/classes/DummyField.md +0 -393
  155. package/docs/classes/Eventer.md +0 -185
  156. package/docs/classes/Field.md +0 -461
  157. package/docs/classes/InjectMsg.md +0 -85
  158. package/docs/classes/NodeSvg.md +0 -1011
  159. package/docs/classes/NumberField.md +0 -559
  160. package/docs/classes/OptConnectField.md +0 -624
  161. package/docs/classes/Renderer.md +0 -1636
  162. package/docs/classes/RendererConstants.md +0 -343
  163. package/docs/classes/Representer.md +0 -95
  164. package/docs/classes/RepresenterNode.md +0 -175
  165. package/docs/classes/TextField.md +0 -559
  166. package/docs/classes/Toolbox.md +0 -172
  167. package/docs/classes/WASDController.md +0 -616
  168. package/docs/classes/Widget.md +0 -195
  169. package/docs/classes/WorkspaceController.md +0 -385
  170. package/docs/classes/WorkspaceCoords.md +0 -218
  171. package/docs/classes/WorkspaceSvg.md +0 -1380
  172. package/docs/functions/clearMainWorkspace.md +0 -20
  173. package/docs/functions/getMainWorkspace.md +0 -19
  174. package/docs/functions/inject.md +0 -35
  175. package/docs/functions/setMainWorkspace.md +0 -28
  176. package/docs/globals.md +0 -95
  177. package/docs/interfaces/ColorStyle.md +0 -43
  178. package/docs/interfaces/ConnectorToFrom.md +0 -57
  179. package/docs/interfaces/DrawState.md +0 -81
  180. package/docs/interfaces/FieldConnectionData.md +0 -25
  181. package/docs/interfaces/FieldOptions.md +0 -63
  182. package/docs/interfaces/FieldRawBoxData.md +0 -25
  183. package/docs/interfaces/FieldVisualInfo.md +0 -65
  184. package/docs/interfaces/GridOptions.md +0 -61
  185. package/docs/interfaces/InjectOptions.md +0 -133
  186. package/docs/interfaces/InputFieldJson.md +0 -50
  187. package/docs/interfaces/KabelCommentRendering.md +0 -31
  188. package/docs/interfaces/KabelInterface.md +0 -469
  189. package/docs/interfaces/KabelNodeRendering.md +0 -77
  190. package/docs/interfaces/KabelUIX.md +0 -105
  191. package/docs/interfaces/KabelUtils.md +0 -215
  192. package/docs/interfaces/NodeEvents.md +0 -42
  193. package/docs/interfaces/NodeJson.md +0 -104
  194. package/docs/interfaces/NodePrototype.md +0 -82
  195. package/docs/interfaces/RegisteredEl.md +0 -53
  196. package/docs/interfaces/SerializedNode.md +0 -128
  197. package/docs/interfaces/TblxCategoryStruct.md +0 -41
  198. package/docs/interfaces/TblxFieldStruct.md +0 -28
  199. package/docs/interfaces/TblxNodeStruct.md +0 -35
  200. package/docs/interfaces/WidgetOptions.md +0 -115
  201. package/docs/interfaces/WidgetPrototypeList.md +0 -15
  202. package/docs/type-aliases/AnyField.md +0 -13
  203. package/docs/type-aliases/AnyFieldCls.md +0 -13
  204. package/docs/type-aliases/Color.md +0 -13
  205. package/docs/type-aliases/Connectable.md +0 -13
  206. package/docs/type-aliases/EventArgs.md +0 -11
  207. package/docs/type-aliases/EventSetupFn.md +0 -25
  208. package/docs/type-aliases/Hex.md +0 -13
  209. package/docs/type-aliases/RGBObject.md +0 -37
  210. package/docs/type-aliases/RGBString.md +0 -13
  211. package/docs/type-aliases/RGBTuple.md +0 -13
  212. package/docs/type-aliases/TblxObjStruct.md +0 -52
  213. package/docs/variables/CategoryColors.md +0 -29
  214. package/docs/variables/FieldMap.md +0 -41
  215. package/docs/variables/NodePrototypes.md +0 -18
  216. package/docs/variables/default.md +0 -11
  217. package/events/comment-drag-handle.ts +0 -61
  218. package/events/comment-input.ts +0 -291
  219. package/events/connection-line.ts +0 -68
  220. package/events/connector.ts +0 -116
  221. package/events/draggable.ts +0 -119
  222. package/events/input-box.ts +0 -213
  223. package/events/node-x-btn.ts +0 -25
  224. package/index.d.ts +0 -4
  225. package/renderers/apollo/apollo.ts +0 -21
  226. package/renderers/apollo/constants.ts +0 -40
  227. package/renderers/apollo/renderer.ts +0 -331
  228. package/renderers/constants.ts +0 -87
  229. package/renderers/renderer.ts +0 -1288
  230. package/renderers/representer-node.ts +0 -52
  231. package/renderers/representer.ts +0 -25
  232. package/src/category.ts +0 -107
  233. package/src/comment.ts +0 -142
  234. package/src/connection.ts +0 -114
  235. package/src/context-menu.ts +0 -194
  236. package/src/coordinates.ts +0 -74
  237. package/src/core.ts +0 -202
  238. package/src/ctx-menu-registry.ts +0 -143
  239. package/src/dropdown-menu.ts +0 -215
  240. package/src/field.ts +0 -595
  241. package/src/flyout.ts +0 -165
  242. package/src/fonts-manager.ts +0 -38
  243. package/src/grid.ts +0 -162
  244. package/src/headless-node.ts +0 -27
  245. package/src/index.ts +0 -115
  246. package/src/inject-headless.ts +0 -18
  247. package/src/mutator.ts +0 -40
  248. package/src/nodesvg.ts +0 -756
  249. package/src/styles.css +0 -224
  250. package/src/toolbox.ts +0 -125
  251. package/src/undo-redo.ts +0 -87
  252. package/src/widget-prototypes.ts +0 -11
  253. package/src/widget.ts +0 -139
  254. package/src/workspace-svg.ts +0 -736
  255. package/src/workspace.ts +0 -155
  256. package/test-server.js +0 -61
  257. package/themes/dark.ts +0 -32
  258. package/themes/default.ts +0 -28
  259. package/themes/themes.ts +0 -9
  260. package/tsconfig.json +0 -25
  261. package/typedoc.json +0 -10
  262. package/util/emitter.ts +0 -33
  263. package/util/env.ts +0 -11
  264. package/util/eventer.ts +0 -108
  265. package/util/has-prop.ts +0 -4
  266. package/util/parse-color.ts +0 -42
  267. package/util/path.ts +0 -99
  268. package/util/styler.ts +0 -41
  269. package/util/uid.ts +0 -184
  270. package/util/user-state.ts +0 -68
  271. package/util/window-listeners.ts +0 -62
  272. package/webpack.config.js +0 -80
  273. /package/{docs/_media → dist}/index.html +0 -0
package/src/nodesvg.ts DELETED
@@ -1,756 +0,0 @@
1
- import Connection, { Connectable } from "./connection";
2
- import { NodePrototype } from "./node-types";
3
- import { ColorStyle, Color } from './visual-types';
4
- import hasProp from '../util/has-prop';
5
- import Field, { AnyField, AnyFieldCls, DummyField, FieldMap, FieldOptions } from "./field";
6
- import CategoryColors from "./colors";
7
- import Coordinates from "./coordinates";
8
- import { generateUID } from "../util/uid";
9
- import EventEmitter from '../util/emitter';
10
- import { G } from "@svgdotjs/svg.js";
11
- import WorkspaceSvg from "./workspace-svg";
12
- import RendererConstants from "../renderers/constants";
13
- import CommentModel, { CommentSerialized } from "./comment";
14
- import { RepresenterNode } from '../renderers/representer-node';
15
- import NodePrototypes from "./prototypes";
16
- /**
17
- * Represents the JSON structure used to initialize a field on a node.
18
- * Each field has a type, label, and name. Additional properties can be included for field-specific configuration.
19
- */
20
- export interface InputFieldJson {
21
- /** Human-readable label for the field, shown on the node UI */
22
- label: string;
23
-
24
- /** Field type identifier, corresponding to a field constructor in FieldMap */
25
- type: string;
26
-
27
- /** Unique field name within the node */
28
- name: string;
29
-
30
- /**
31
- * Optional additional properties for field initialization.
32
- * Can include type-specific options like min/max for number fields,
33
- * default values, dropdown options, etc.
34
- */
35
- [key: string]: any;
36
- }
37
-
38
- /**
39
- * Represents a JSON structure for initializing a NodeSvg instance.
40
- * Includes colors, connections, label, fields, category, and type information.
41
- */
42
- export interface NodeJson {
43
- /** Primary color of the node (e.g., top bar, main connections) */
44
- primaryColor?: Color | undefined;
45
-
46
- /** Secondary color of the node (e.g., field backgrounds) */
47
- secondaryColor?: Color | undefined;
48
-
49
- /** Tertiary color of the node (e.g., outlines) */
50
- tertiaryColor?: Color | undefined;
51
-
52
- /**
53
- * Optional previous connection data.
54
- * Presence triggers creation of a previous connection when initializing NodeSvg.
55
- */
56
- previousConnection?: any | undefined;
57
-
58
- /**
59
- * Optional next connection data.
60
- * Presence triggers creation of a next connection when initializing NodeSvg.
61
- */
62
- nextConnection?: any | undefined;
63
-
64
- /** Optional node label text to display */
65
- labelText?: string | undefined;
66
-
67
- /** Array of field definitions (InputFieldJson) to attach to this node */
68
- arguments?: InputFieldJson[] | undefined;
69
-
70
- /** Optional category name for color theming */
71
- category?: string | undefined;
72
-
73
- /** Node type identifier, used to look up the NodePrototype */
74
- type: string;
75
- }
76
-
77
- /**
78
- * Represents a fully serialized node including its fields, colors, coordinates, connections, and optional comment.
79
- * Used for saving or transferring node data.
80
- */
81
- export interface SerializedNode {
82
- /** Node type string */
83
- type: string;
84
-
85
- /** Unique node ID */
86
- id: string;
87
-
88
- /** Display label of the node */
89
- label: string;
90
-
91
- /** Node colors including primary, secondary, tertiary, and category */
92
- colors: ColorStyle;
93
-
94
- /** Coordinates of the node relative to its workspace */
95
- relativeCoords: { x: number; y: number };
96
-
97
- /** Optional comment text attached to the node */
98
- comment?: CommentSerialized | undefined;
99
-
100
- /** Array of serialized fields, may contain any field-specific structure */
101
- fields?: any[] | undefined;
102
-
103
- /**
104
- * Serialized representation of the previous connection.
105
- * If `field` is true, the connection originates from a field rather than a node.
106
- */
107
- previousConnection?: { field?: boolean | undefined; node?: SerializedNode | undefined } | undefined;
108
-
109
- /**
110
- * Serialized representation of the next connection.
111
- * If `field` is true, the connection originates from a field rather than a node.
112
- */
113
- nextConnection?: { field?: boolean | undefined; node?: SerializedNode | undefined } | undefined;
114
- }
115
-
116
- /**
117
- * NodeStyle represents the styling configuration of a node.
118
- * Includes ColorStyle properties plus optional renderer-specific constants and arbitrary additional fields.
119
- */
120
- export type NodeStyle = ColorStyle &
121
- {
122
- /** Optional renderer-specific constants from RendererConstants */
123
- [key in keyof RendererConstants]?: RendererConstants[key];
124
- } &
125
- {
126
- /** Any additional style properties supported by extensions or custom renderers */
127
- [key: string]: any;
128
- };
129
-
130
- /**
131
- * Events emitted by NodeSvg instances.
132
- * Consumers can listen to these events to react to node lifecycle changes.
133
- */
134
- export interface NodeEvents {
135
- /** Triggered before the node is removed from the workspace */
136
- "REMOVING": null;
137
-
138
- /** Triggered immediately after node initialization */
139
- "INITING": null;
140
-
141
- /** Triggered while the node is being dragged */
142
- "NODE_DRAG": null;
143
- }
144
-
145
- /**
146
- * Represents a node in the workspace.
147
- * Handles connections, fields, colors, serialization, and events.
148
- */
149
- class NodeSvg extends EventEmitter<NodeEvents> {
150
- /** The previous connection for this node (null if none) */
151
- previousConnection: Connection | null;
152
- /** The next connection for this node (null if none) */
153
- nextConnection: Connection | null;
154
- /** Node type string, usually derived from prototype */
155
- type: string | null;
156
- /** Prototype object providing behavior for this node */
157
- prototype: NodePrototype | null;
158
- /** Node color style object */
159
- colors: NodeStyle; // Node's color scheme
160
- /** Displayed label text for this node */
161
- labelText: string; // Displayed node label
162
- /** Set of fields attached to this node */
163
- _fieldColumn: Set<AnyField>; // Stores attached fields
164
- /** Node coordinates relative to workspace */
165
- relativeCoords: Coordinates;
166
- /** Unique node ID */
167
- id: string;
168
- /** SVG representation of this node */
169
- svg?: RepresenterNode | object | null = null;
170
- /** Workspace this node belongs to */
171
- workspace: WorkspaceSvg | null = null;
172
- /** Optional comment attached to this node */
173
- comment: CommentModel | null
174
- /** Event key: "REMOVING" */
175
- static REMOVING: keyof NodeEvents = "REMOVING";
176
-
177
- /** Event key: "INITING" */
178
- static INITING: keyof NodeEvents = "INITING";
179
- /**
180
- * Creates a NodeSvg instance.
181
- * @param prototype Optional NodePrototype to associate with this node.
182
- * @param workspace Optional WorkspaceSvg this node belongs to.
183
- * @param svgGroup Optional SVG group to attach node visuals.
184
- */
185
- constructor(prototype: NodePrototype | null, workspace?: WorkspaceSvg, svgGroup?: G) {
186
- super();
187
- this.type = null;
188
- this.comment = null;
189
- this.prototype = prototype;
190
- this.colors = {
191
- primary: '#000000', // Topbar & connection color
192
- secondary: '#000000', // Field & dropdown backgrounds
193
- tertiary: '#000000', // Outline color
194
- category: '' // Node category name (optional)
195
- };
196
- this.previousConnection = new Connection(null, this, true); //1st arg is from, 2nd is to, third is if this conn is prev
197
- this.nextConnection = new Connection(this, null, false); //1st arg is from, 2nd is to, third is if this conn is prev
198
- this.labelText = '';
199
- this._fieldColumn = new Set();
200
- this.relativeCoords = new Coordinates(0, 0);
201
- this.id = generateUID('nanoid', { alphabet: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0129384756!)@(#*$&%^' });
202
- if (workspace) {
203
- this.workspace = workspace;
204
- }
205
- if (svgGroup) {
206
- this.svg = new RepresenterNode(this, {
207
- id: this.id,
208
- pendingConnections: [],
209
- group: svgGroup
210
- }, workspace!.renderer)
211
- }
212
- }
213
- /** Returns true if this node has no previous connection (i.e., top-level node) */
214
- get topLevel() {
215
- return !(this.previousConnection?.getFrom());
216
- }
217
- /** Returns the raw SVG group element if present */
218
- get svgGroup() {
219
- return (this.svg as RepresenterNode)?.getRaw?.();
220
- }
221
- /** Returns the text of the node's comment, if any */
222
- getCommentText() {
223
- return this.comment?.getText?.();
224
- }
225
- /** Returns the CommentModel instance for this node, if any */
226
- getComment() {
227
- return this.comment;
228
- }
229
- /** Adds a new comment to this node if none exists */
230
- addComment() {
231
- if (!this.comment) {
232
- this.comment = new CommentModel(this);
233
- return;
234
- }
235
- console.warn('Comment already exists.')
236
- }
237
- /** Sets the text for the node's comment, creating one if needed */
238
- setCommentText(text: string) {
239
- if (!this.comment) {
240
- this.comment = new CommentModel(this);
241
- }
242
- this.comment.setText(text);
243
- }
244
- /** Removes the comment from the node and triggers workspace redraw */
245
- removeComment() {
246
- this.comment = null;
247
- this.workspace?.redrawComments?.();
248
- }
249
- /** Returns an array of all fields attached to this node */
250
- allFields() {
251
- return Array.from(this._fieldColumn);
252
- }
253
- /** Retrieves a field by name from this node */
254
- getFieldByName(name: string): AnyField | null | undefined {
255
- let field: AnyField | null | undefined = this.allFields().find(fld => fld.getName() === name);
256
-
257
- return field;
258
- }
259
- /** Alias for getFieldByName */
260
- getField(name: string): AnyField | null | undefined {
261
- return this.getFieldByName(name);
262
- }
263
- /** Retrieves the current value of a field by name */
264
- getFieldValue(name: string): any | undefined {
265
- const fld: AnyField | null | undefined = this.getFieldByName(name);
266
- if (fld) {
267
- return fld.getValue();
268
- }
269
- return undefined;
270
- }
271
- /** Retrieves the display value of a field by name */
272
- getFieldDisplayValue(name: string): any | undefined {
273
- const fld: AnyField | null | undefined = this.getFieldByName(name);
274
- if (fld) {
275
- return fld.getDisplayValue();
276
- }
277
- return undefined;
278
- }
279
- /** Sets the value of a field by name */
280
- setFieldValue(name: string, value: any) {
281
- const fld: AnyField | null | undefined = this.getFieldByName(name);
282
- if (fld) {
283
- fld.setValue(value as never); // I don't like using the "as" statement here, but it's necessary to satisfy TypeScript.
284
- }
285
- return fld;
286
- }
287
-
288
- /**
289
- * Initiates the node, calling prototype methods.
290
- */
291
- init() {
292
- this.emit(NodeSvg.INITING, null);
293
- if (this.prototype) {
294
- if (this.prototype.init) this.prototype.init.call(this, this.prototype, this);
295
- if (this.workspace) {
296
- this.workspace.addNode(this)
297
- }
298
- if (this.prototype.removed) {
299
- this.on(NodeSvg.REMOVING, () => {
300
- this.prototype?.removed.call(this, this.prototype, this);
301
- })
302
- }
303
- } else {
304
- console.warn(`Node with id ${this.id} is missing a prototype.`)
305
- }
306
- }
307
- /** Returns whether this node has a category style applied */
308
- hasCategoryStyle() {
309
- return !!this.colors?.category && this.colors?.category?.length > 0;
310
- }
311
-
312
- /** Returns the category name or null if none */
313
- getCategoryName() {
314
- return this.colors?.category || null;
315
- }
316
-
317
- /** Returns the node's current ColorStyle */
318
- getStyle() {
319
- return this.colors;
320
- }
321
-
322
- /** Internal helper: attach a field to this node */
323
- _appendFieldItem(field: AnyField) {
324
- if (!field) console.warn("Falsey field passed to _appendFieldItem.");
325
- this._fieldColumn.add(field);
326
- }
327
-
328
- /** Initialize node from a NodeJson object */
329
- jsonInit(json: NodeJson) {
330
- if (json.primaryColor) this.colors.primary = json.primaryColor;
331
- if (json.secondaryColor) this.colors.secondary = json.secondaryColor;
332
- if (json.tertiaryColor) this.colors.tertiary = json.tertiaryColor;
333
-
334
- // Apply category colors if defined
335
- if (json.category && CategoryColors[json.category]) {
336
- const style: ColorStyle = CategoryColors[json.category] as ColorStyle;
337
- Object.assign(this.colors, { category: json.category }, style);
338
- }
339
-
340
- this.previousConnection = hasProp(json, 'previousConnection') ? new Connection(null, this, true) : null;
341
- this.nextConnection = hasProp(json, 'nextConnection') ? new Connection(this, null, false) : null;
342
-
343
- if (json.labelText) this.labelText = json.labelText;
344
- if (json.arguments) this.applyJsonArguments(json.arguments);
345
- if (json.type) {
346
- this.type = json.type;
347
- }
348
- }
349
-
350
- /* JAVASCRIPT API */
351
-
352
- /** Apply field definitions from a JSON-like array without full NodeJson */
353
- applyJsonArguments(args: InputFieldJson[]) {
354
- for (let field of args) {
355
- if (!field.type || !field.name) {
356
- console.warn(`Invalid argument definition at: ${args.indexOf(field)}.`);
357
- continue;
358
- }
359
-
360
- const FieldConstructor: AnyFieldCls | undefined = FieldMap[field.type] as AnyFieldCls | undefined;
361
- if (!FieldConstructor) {
362
- console.warn(`Missing field constructor for ${field.type}!`);
363
- continue;
364
- }
365
-
366
- const fld: AnyField = new FieldConstructor();
367
- fld.fromJson(field); // initialize field
368
- fld.node = this;
369
- this._appendFieldItem(fld);
370
- }
371
- return this;
372
- }
373
- /** Appends a connection field to this node */
374
- appendConnection(name: string): Field {
375
- const fld = new (FieldMap['connection'])();
376
- this._appendFieldItem(fld);
377
- fld.setName(name);
378
- fld.node = this;
379
- return fld;
380
- }
381
- /** Appends a numeric input field to this node */
382
- appendNumber(name: string): Field {
383
- const fld = new (FieldMap['field_num'])();
384
- this._appendFieldItem(fld);
385
- fld.setName(name);
386
- fld.node = this;
387
- return fld;
388
- }
389
- /** Appends a text input field to this node */
390
- appendText(name: string): Field {
391
- const fld = new (FieldMap['field_str'])();
392
- this._appendFieldItem(fld);
393
- fld.setName(name);
394
- fld.node = this;
395
- return fld;
396
- }
397
-
398
- /** Appends a field that can hold a connection or raw value */
399
- appendOptLink(name: string): Field {
400
- const fld = new (FieldMap['field_both'])();
401
- this._appendFieldItem(fld);
402
- fld.setName(name);
403
- fld.node = this;
404
- return fld;
405
- }
406
- /** Sets the category name for the node */
407
- setCategoryName(name: string) {
408
- this.colors.category = name;
409
- return this;
410
- }
411
- /** Applies a ColorStyle to the node */
412
- setStyle(style: ColorStyle) {
413
- // apply properties from style into this.colors
414
- Object.assign(this.colors, style);
415
- return this;
416
- }
417
-
418
- /** Sets primary, secondary, and tertiary colors for the node */
419
- setColor(primary: Color, secondary: Color, tertiary: Color) {
420
- this.setStyle({ primary, secondary, tertiary });
421
- return this;
422
- }
423
- /** Sets the label text for the node */
424
- setLabelText(text: string) {
425
- this.labelText = text;
426
- return this;
427
- }
428
-
429
- /** Add or replace a previous/next connection based on argument */
430
- setConnection(prevOrNext: string | number | boolean) {
431
- const stringed = String(prevOrNext).toLowerCase();
432
- const cast = stringed == '0' ? 0 : (stringed == '1' ? 1 : (stringed == 'true' ? 1 : (stringed == 'false' ? 0 : 3)));
433
-
434
- if (cast === 0) {
435
- this.previousConnection = new Connection(null, this, true);
436
- } else if (cast === 1) {
437
- this.nextConnection = new Connection(this, null, false);
438
- } else {
439
- console.warn('Invalid prevOrNext argument for NodeSvg.setConnection');
440
- }
441
- return this;
442
- }
443
- /** Copies another NodeSvg into this node */
444
- fromNode(other: NodeSvg) {
445
- if (!other) return;
446
-
447
- // Copy primitive props
448
- this.type = other.type;
449
- this.labelText = other.labelText;
450
- this.relativeCoords = new Coordinates(other.relativeCoords.x, other.relativeCoords.y);
451
-
452
- // Copy colors
453
- this.colors = { ...other.colors };
454
-
455
- // Copy connections
456
- this.previousConnection = other.previousConnection
457
- ? new Connection(null, this, true)
458
- : null;
459
- this.nextConnection = other.nextConnection
460
- ? new Connection(this, null, false)
461
- : null;
462
-
463
- // Copy fields
464
- this._fieldColumn.clear();
465
- for (let field of other._fieldColumn) {
466
- const FieldCls = field.constructor as AnyFieldCls;
467
- const newField = (new FieldCls()) as any;
468
-
469
- // Copy basic properties
470
- newField.setName(field.getName());
471
- if ('getValue' in field && 'setValue' in newField) {
472
- newField.setValue(field.getValue());
473
- }
474
- if ('getLabel' in field && 'setLabel' in newField) {
475
- newField.setLabel(field.getLabel())
476
- }
477
- this._appendFieldItem(newField);
478
- }
479
-
480
- // Copy workspace reference
481
- this.workspace = other.workspace;
482
-
483
- // Copy prototype reference
484
- this.prototype = other.prototype;
485
-
486
-
487
- return this;
488
- }
489
- /** Serializes a Connection object, handling fields and nested nodes */
490
- _serializeConnection(
491
- c: Connection,
492
- alreadyProcessed: { [key: string]: SerializedNode }
493
- ): { field?: boolean | undefined; node?: SerializedNode | undefined } {
494
- const returned: { field?: boolean; node?: SerializedNode } = {};
495
- let connected: NodeSvg | AnyField | null = c.isPrevious ? c.getFrom() : c.getTo();
496
-
497
- if (!connected) return returned;
498
-
499
- if (connected instanceof NodeSvg) {
500
- // Avoid serializing the same node twice
501
- if (alreadyProcessed[connected.id]) {
502
- return { node: alreadyProcessed[connected.id] };
503
- }
504
- returned.node = connected.serialize(alreadyProcessed);
505
- } else {
506
- // Field serialization
507
- const fld = connected as AnyField;
508
- // If the field has a node, we serialize the node first
509
- let fieldNode: SerializedNode | undefined;
510
- if (fld.node) {
511
- if (alreadyProcessed[fld.node.id]) {
512
- fieldNode = alreadyProcessed[fld.node.id];
513
- } else {
514
- fieldNode = fld.node.serialize(alreadyProcessed);
515
- }
516
- }
517
- returned.field = true;
518
- if (fieldNode) returned.node = fieldNode;
519
- }
520
-
521
- return returned;
522
- }
523
-
524
- /**
525
- * Serialize a node, this includes circular references. use toJson to avoid those.
526
- * @param alreadyProcessed - Internal.
527
- * @returns
528
- */
529
- serialize(alreadyProcessed: { [key: string]: SerializedNode } = {}): SerializedNode {
530
- if (alreadyProcessed[this.id]) {
531
- return alreadyProcessed[this.id] as SerializedNode;
532
- }
533
-
534
- // Put a placeholder in map *before* serializing connections
535
- const serialized: SerializedNode = {
536
- id: this.id,
537
- type: this.type || '',
538
- colors: { primary: this.colors.primary, secondary: this.colors.secondary, tertiary: this.colors.tertiary, category: this.colors.category } as ColorStyle,
539
- label: this.labelText,
540
- previousConnection: undefined,
541
- nextConnection: undefined,
542
- relativeCoords: { x: this.relativeCoords.x, y: this.relativeCoords.y },
543
- comment: this.comment?.toJson?.(),
544
- fields: [], // fill after placeholder
545
- };
546
- alreadyProcessed[this.id] = serialized;
547
-
548
- // Now safely fill in the heavy parts
549
- serialized.fields = this.allFields().map(fld =>
550
- fld.toJson
551
- ? fld.toJson(true, alreadyProcessed)
552
- : {
553
- name: fld.getName(),
554
- type: fld.constructor.name,
555
- value: fld.getValue ? fld.getValue() : undefined,
556
- }
557
- );
558
-
559
- serialized.previousConnection = this.previousConnection
560
- ? this._serializeConnection(this.previousConnection, alreadyProcessed)
561
- : undefined;
562
-
563
- serialized.nextConnection = this.nextConnection
564
- ? this._serializeConnection(this.nextConnection, alreadyProcessed)
565
- : undefined;
566
-
567
- return serialized;
568
- }
569
-
570
- /**
571
- * Return a flattened version of the serialized node structure, which is non-circular.
572
- * Any node reference inside connections or fields is replaced by its ID.
573
- */
574
- toJson(): {
575
- [id: string]: Omit<SerializedNode, 'previousConnection' | 'nextConnection'> & {
576
- previousConnection?: { field?: FieldOptions; node?: string };
577
- nextConnection?: { field?: FieldOptions; node?: string };
578
- }
579
- } {
580
- const serialized = this.serialize();
581
- const flat: { [id: string]: any } = {};
582
-
583
- const processNode = (node: SerializedNode) => {
584
- if (flat[node.id]) return;
585
-
586
- const copy: any = {
587
- ...node,
588
- previousConnection: node.previousConnection ? { ...node.previousConnection } : undefined,
589
- nextConnection: node.nextConnection ? { ...node.nextConnection } : undefined,
590
- };
591
-
592
- flat[node.id] = copy;
593
-
594
- // Handle connections
595
- if (copy.previousConnection?.node) {
596
- const prevNode = copy.previousConnection.node;
597
- copy.previousConnection.node = prevNode.id;
598
- processNode(prevNode);
599
- }
600
- if (copy.nextConnection?.node) {
601
- const nextNode = copy.nextConnection.node;
602
- copy.nextConnection.node = nextNode.id;
603
- processNode(nextNode);
604
- }
605
-
606
- // Handle fields recursively
607
- if (Array.isArray(copy.fields)) {
608
- for (let fld of copy.fields) {
609
- if (fld.node) {
610
- processNode(fld.node);
611
- fld.node = fld.node.id;
612
- }
613
- for (let key in fld) {
614
- if (fld[key]?.node) {
615
- processNode(fld[key]?.node);
616
- fld[key].node = fld[key].node.id;
617
- }
618
- }
619
- }
620
- }
621
- };
622
-
623
- processNode(serialized);
624
- return flat;
625
- }
626
-
627
- /**
628
- * Reconstruct a NodeSvg from a SerializedNode structure (handles circular references)
629
- */
630
- static _deserialize(
631
- data: SerializedNode,
632
- allNodes: { [id: string]: NodeSvg } = {},
633
- workspace?: WorkspaceSvg
634
- ): NodeSvg {
635
- // If already created, return the existing instance
636
- if (allNodes[data.id]) return allNodes[data.id] as NodeSvg;
637
-
638
- if (workspace && workspace.getNode(data.id)) {
639
- workspace.removeNodeById(data.id); // remove old node which had the same id.
640
- }
641
- // Create a new node with minimal prototype info (can be patched later)
642
- const node = new NodeSvg(NodePrototypes[data.type] as NodePrototype, workspace);
643
- node.id = data.id;
644
- node.init();
645
- node.type = data.type;
646
- node.relativeCoords.set(data.relativeCoords.x, data.relativeCoords.y);
647
- node.labelText = data.label || '';
648
- if (data.comment && workspace) {
649
- node.comment = CommentModel.fromJson(data.comment as CommentSerialized);
650
- node.comment._parent = node;
651
- node.comment._isWorkspaceComment = false;
652
- }
653
- // IMPORTANT: restore colors from serialized data (if present)
654
- if (data.colors) {
655
- // Start with category colors if a category is present and known
656
- if (data.colors.category && CategoryColors[data.colors.category]) {
657
- const style: ColorStyle = CategoryColors[data.colors.category] as ColorStyle;
658
- Object.assign(node.colors, style, { category: data.colors.category });
659
- }
660
- // Then override with explicit serialized colors (primary/secondary/tertiary)
661
- // This preserves explicit color values even when a category was saved
662
- const explicit: Partial<ColorStyle> = {};
663
- if (data.colors.primary) explicit.primary = data.colors.primary;
664
- if (data.colors.secondary) explicit.secondary = data.colors.secondary;
665
- if (data.colors.tertiary) explicit.tertiary = data.colors.tertiary;
666
- if (data.colors.category) explicit.category = data.colors.category;
667
- node.setStyle(explicit as ColorStyle);
668
- }
669
- // Register placeholder before deserializing connections to handle circular refs
670
- allNodes[node.id] = node;
671
-
672
- // Deserialize fields
673
- if (Array.isArray(data.fields)) {
674
- node._fieldColumn = new Set();
675
- for (let fldData of data.fields) {
676
- const FieldConstructor = FieldMap[fldData.type];
677
- if (!FieldConstructor) continue;
678
-
679
- const fld: AnyField = new FieldConstructor();
680
- fld.fromJson(fldData, workspace); // ONLY fldData and workspace
681
- fld.node = node;
682
- node._appendFieldItem(fld);
683
- }
684
- }
685
-
686
- // Deserialize previous/next connections
687
- if (data.previousConnection?.node) {
688
- node.previousConnection = new Connection(null, node, true);
689
- node.previousConnection.setFrom(NodeSvg._deserialize(data.previousConnection.node, allNodes, workspace));
690
- }
691
-
692
- if (data.nextConnection?.node) {
693
- node.nextConnection = new Connection(node, null, false);
694
- node.nextConnection.setTo(NodeSvg._deserialize(data.nextConnection.node, allNodes, workspace));
695
- } else {
696
- workspace?.redraw(); // redraw if we reached the end.
697
- }
698
-
699
- return node;
700
- }
701
- /** Public: Deserialize a SerializedNode or plain object into a NodeSvg attached to a workspace */
702
- static deserialize(json: SerializedNode | any, workspace: WorkspaceSvg) {
703
- return this._deserialize(json as SerializedNode, {}, workspace);
704
- }
705
- /** Reconstructs nodes from a flattened JSON structure into a NodeSvg tree */
706
- static fromJson(flat: Record<string, any>, workspace: WorkspaceSvg): any {
707
- const nodes: Record<string, any> = {};
708
- // shallow clone so we can safely mutate
709
- for (const id in flat) {
710
- nodes[id] = { ...flat[id] };
711
- }
712
-
713
- // rebuild references
714
- for (const id in nodes) {
715
- const node = nodes[id];
716
-
717
- // fix connection refs
718
- if (node.previousConnection?.node) {
719
- const refId = node.previousConnection.node as string;
720
- node.previousConnection = {
721
- ...node.previousConnection,
722
- node: nodes[refId],
723
- };
724
- }
725
- if (node.nextConnection?.node) {
726
- const refId = node.nextConnection.node as string;
727
- node.nextConnection = {
728
- ...node.nextConnection,
729
- node: nodes[refId],
730
- };
731
- }
732
-
733
- // fix fields
734
- if (Array.isArray(node.fields)) {
735
- for (const fld of node.fields) {
736
- if (typeof fld.node === 'string')
737
- fld.node = nodes[fld.node];
738
-
739
- for (const key in fld) {
740
- const maybe = fld[key];
741
- if (maybe?.node && typeof maybe.node === 'string')
742
- maybe.node = nodes[maybe.node];
743
- }
744
- }
745
- }
746
- }
747
-
748
- // the root is just the one w/ no previousConnection
749
- const root = Object.values(nodes).find(n => !n.previousConnection?.node) ?? null;
750
- return this._deserialize(root, {}, workspace);
751
- }
752
-
753
-
754
- }
755
-
756
- export default NodeSvg;