@stocksharp/diagram 0.1.0
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.
- package/LICENSE +30 -0
- package/NOTICE +21 -0
- package/README.md +346 -0
- package/dist/esm/canvas-renderer.js +3226 -0
- package/dist/esm/canvas-renderer.js.map +1 -0
- package/dist/esm/core/action-registry.js +35 -0
- package/dist/esm/core/action-registry.js.map +1 -0
- package/dist/esm/core/document.js +350 -0
- package/dist/esm/core/document.js.map +1 -0
- package/dist/esm/core/history.js +136 -0
- package/dist/esm/core/history.js.map +1 -0
- package/dist/esm/core/model.js +2 -0
- package/dist/esm/core/model.js.map +1 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/state.js.map +1 -0
- package/dist/esm/core/view-state.js +70 -0
- package/dist/esm/core/view-state.js.map +1 -0
- package/dist/esm/diagram/api.js +2 -0
- package/dist/esm/diagram/api.js.map +1 -0
- package/dist/esm/diagram/catalog.js +45 -0
- package/dist/esm/diagram/catalog.js.map +1 -0
- package/dist/esm/diagram/event-emitter.js +42 -0
- package/dist/esm/diagram/event-emitter.js.map +1 -0
- package/dist/esm/diagram/palette.js +269 -0
- package/dist/esm/diagram/palette.js.map +1 -0
- package/dist/esm/diagram/stocksharp-diagram.js +784 -0
- package/dist/esm/diagram/stocksharp-diagram.js.map +1 -0
- package/dist/esm/diagram/types.js +105 -0
- package/dist/esm/diagram/types.js.map +1 -0
- package/dist/esm/embed.js +398 -0
- package/dist/esm/embed.js.map +1 -0
- package/dist/esm/i18n.js +12 -0
- package/dist/esm/i18n.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/ssdiagram.js +5141 -0
- package/dist/ssdiagram.js.map +7 -0
- package/dist/types/canvas-renderer.d.ts +512 -0
- package/dist/types/canvas-renderer.d.ts.map +1 -0
- package/dist/types/core/action-registry.d.ts +18 -0
- package/dist/types/core/action-registry.d.ts.map +1 -0
- package/dist/types/core/document.d.ts +12 -0
- package/dist/types/core/document.d.ts.map +1 -0
- package/dist/types/core/history.d.ts +33 -0
- package/dist/types/core/history.d.ts.map +1 -0
- package/dist/types/core/model.d.ts +85 -0
- package/dist/types/core/model.d.ts.map +1 -0
- package/dist/types/core/state.d.ts +68 -0
- package/dist/types/core/state.d.ts.map +1 -0
- package/dist/types/core/view-state.d.ts +14 -0
- package/dist/types/core/view-state.d.ts.map +1 -0
- package/dist/types/diagram/api.d.ts +213 -0
- package/dist/types/diagram/api.d.ts.map +1 -0
- package/dist/types/diagram/catalog.d.ts +19 -0
- package/dist/types/diagram/catalog.d.ts.map +1 -0
- package/dist/types/diagram/event-emitter.d.ts +9 -0
- package/dist/types/diagram/event-emitter.d.ts.map +1 -0
- package/dist/types/diagram/palette.d.ts +63 -0
- package/dist/types/diagram/palette.d.ts.map +1 -0
- package/dist/types/diagram/stocksharp-diagram.d.ts +157 -0
- package/dist/types/diagram/stocksharp-diagram.d.ts.map +1 -0
- package/dist/types/diagram/types.d.ts +171 -0
- package/dist/types/diagram/types.d.ts.map +1 -0
- package/dist/types/embed.d.ts +35 -0
- package/dist/types/embed.d.ts.map +1 -0
- package/dist/types/i18n.d.ts +167 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +126 -0
- package/sample.png +0 -0
- package/src/canvas-renderer.ts +3249 -0
- package/src/core/action-registry.ts +46 -0
- package/src/core/document.ts +387 -0
- package/src/core/history.ts +154 -0
- package/src/core/model.ts +99 -0
- package/src/core/state.ts +148 -0
- package/src/core/view-state.ts +82 -0
- package/src/diagram/api.ts +257 -0
- package/src/diagram/catalog.ts +55 -0
- package/src/diagram/event-emitter.ts +44 -0
- package/src/diagram/palette.ts +312 -0
- package/src/diagram/stocksharp-diagram.ts +945 -0
- package/src/diagram/types.ts +332 -0
- package/src/embed.ts +508 -0
- package/src/i18n.ts +210 -0
- package/src/index.ts +171 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import type { JsonObject } from '../core/model.js';
|
|
2
|
+
|
|
3
|
+
// Core data model — node/port catalog (palette) and the live diagram (DiagramNode + Link).
|
|
4
|
+
|
|
5
|
+
export interface PortTypeInit {
|
|
6
|
+
name: string;
|
|
7
|
+
color: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class PortType {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly color: string;
|
|
13
|
+
|
|
14
|
+
constructor(init: PortTypeInit) {
|
|
15
|
+
this.name = init.name;
|
|
16
|
+
this.color = init.color;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PortInit {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
maxLinks?: number;
|
|
26
|
+
/// Whitelist of socket-type names this port also accepts beyond its
|
|
27
|
+
/// declared <c>type</c> (input ports only). Empty = strict on <c>type</c>.
|
|
28
|
+
availableTypes?: string[];
|
|
29
|
+
/// True when this port can be added/removed at runtime on a node.
|
|
30
|
+
isDynamic?: boolean;
|
|
31
|
+
/// "" / "manual" / "onConnect" — see PalettePortDto.dynamicMode docs.
|
|
32
|
+
dynamicMode?: string;
|
|
33
|
+
/// True when this port was spawned by the grow-on-connect pipeline.
|
|
34
|
+
/// Distinguishes anchor + runtime siblings on save (only siblings are
|
|
35
|
+
/// persisted in the diagram blob; anchors come from the palette schema).
|
|
36
|
+
isSibling?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Mutable port properties. Port identity and direction remain stable. */
|
|
40
|
+
export type PortUpdate = Partial<Omit<PortInit, 'id'>>;
|
|
41
|
+
|
|
42
|
+
export class Port {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
type: string;
|
|
47
|
+
maxLinks: number;
|
|
48
|
+
availableTypes: string[];
|
|
49
|
+
isDynamic: boolean;
|
|
50
|
+
dynamicMode: string;
|
|
51
|
+
isSibling: boolean;
|
|
52
|
+
|
|
53
|
+
constructor(init: PortInit) {
|
|
54
|
+
this.id = init.id;
|
|
55
|
+
this.name = init.name;
|
|
56
|
+
this.description = init.description ?? '';
|
|
57
|
+
this.type = init.type ?? '';
|
|
58
|
+
this.maxLinks = typeof init.maxLinks === 'number' ? init.maxLinks : 0;
|
|
59
|
+
this.availableTypes = init.availableTypes ?? [];
|
|
60
|
+
this.isDynamic = init.isDynamic ?? false;
|
|
61
|
+
this.dynamicMode = init.dynamicMode ?? '';
|
|
62
|
+
this.isSibling = init.isSibling ?? false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clone(): Port {
|
|
66
|
+
return new Port({
|
|
67
|
+
id: this.id,
|
|
68
|
+
name: this.name,
|
|
69
|
+
description: this.description,
|
|
70
|
+
type: this.type,
|
|
71
|
+
maxLinks: this.maxLinks,
|
|
72
|
+
availableTypes: [...this.availableTypes],
|
|
73
|
+
isDynamic: this.isDynamic,
|
|
74
|
+
dynamicMode: this.dynamicMode,
|
|
75
|
+
isSibling: this.isSibling,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Element parameter schema — mirrors PaletteParameterDto on the wire.
|
|
81
|
+
/// Lives on the palette catalog so the Properties panel can render typed
|
|
82
|
+
/// editors per element; node-side values live in DiagramNode.parameters.
|
|
83
|
+
export interface ParamSchema {
|
|
84
|
+
name: string;
|
|
85
|
+
displayName: string;
|
|
86
|
+
description: string;
|
|
87
|
+
/// 'number' | 'bool' | 'string' | 'enum' | 'timespan' | 'datetime' | 'datatype' | …
|
|
88
|
+
/// (anything else falls back to a text input on the Properties panel)
|
|
89
|
+
type: string;
|
|
90
|
+
defaultValue: string;
|
|
91
|
+
options: string[];
|
|
92
|
+
min: number | null;
|
|
93
|
+
max: number | null;
|
|
94
|
+
displayOrder: number;
|
|
95
|
+
/// Section header in the Properties panel. Empty means "General".
|
|
96
|
+
category: string;
|
|
97
|
+
/// SetBasic(true) on the desktop param. Basic ones surface above the fold;
|
|
98
|
+
/// advanced ones live below a collapsible divider.
|
|
99
|
+
isBasic: boolean;
|
|
100
|
+
/// Optional editor hint from EditorAttribute (e.g. "ICandleDataTypeEditor").
|
|
101
|
+
/// Empty string when none — the type-driven default editor takes over.
|
|
102
|
+
editorType: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface NodeInit {
|
|
106
|
+
id: string;
|
|
107
|
+
name: string;
|
|
108
|
+
description?: string;
|
|
109
|
+
groupName?: string;
|
|
110
|
+
inPorts?: Array<Port | PortInit>;
|
|
111
|
+
outPorts?: Array<Port | PortInit>;
|
|
112
|
+
icon?: string;
|
|
113
|
+
parameters?: ParamSchema[];
|
|
114
|
+
/// Non-empty host action enables double-click tracking for this node type.
|
|
115
|
+
/// The component emits nodeOpen; the host decides what the action means.
|
|
116
|
+
openAction?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class Node {
|
|
120
|
+
id: string;
|
|
121
|
+
name: string;
|
|
122
|
+
description: string;
|
|
123
|
+
groupName: string;
|
|
124
|
+
inPorts: Port[];
|
|
125
|
+
outPorts: Port[];
|
|
126
|
+
icon: string;
|
|
127
|
+
parameters: ParamSchema[];
|
|
128
|
+
openAction: string;
|
|
129
|
+
|
|
130
|
+
constructor(init: NodeInit) {
|
|
131
|
+
this.id = init.id;
|
|
132
|
+
this.name = init.name;
|
|
133
|
+
this.description = init.description ?? '';
|
|
134
|
+
this.groupName = init.groupName ?? 'Common';
|
|
135
|
+
this.inPorts = (init.inPorts ?? []).map((p) => (p instanceof Port ? p : new Port(p)));
|
|
136
|
+
this.outPorts = (init.outPorts ?? []).map((p) => (p instanceof Port ? p : new Port(p)));
|
|
137
|
+
this.icon = init.icon ?? '';
|
|
138
|
+
this.parameters = init.parameters ?? [];
|
|
139
|
+
this.openAction = init.openAction ?? '';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
clone(): Node {
|
|
143
|
+
return new Node({
|
|
144
|
+
id: this.id,
|
|
145
|
+
name: this.name,
|
|
146
|
+
description: this.description,
|
|
147
|
+
groupName: this.groupName,
|
|
148
|
+
inPorts: this.inPorts.map((p) => p.clone()),
|
|
149
|
+
outPorts: this.outPorts.map((p) => p.clone()),
|
|
150
|
+
icon: this.icon,
|
|
151
|
+
parameters: this.parameters.map((p) => ({ ...p, options: [...p.options] })),
|
|
152
|
+
openAction: this.openAction,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface DiagramNodeInit extends NodeInit {
|
|
158
|
+
typeId?: string; // palette element this instance was spawned from (kept for save round-trip)
|
|
159
|
+
x?: number;
|
|
160
|
+
y?: number;
|
|
161
|
+
color?: string;
|
|
162
|
+
border?: string;
|
|
163
|
+
/// Error restored with the scheme. It is rendered as a red node background
|
|
164
|
+
/// and exposed as the node tooltip by the canvas runtime.
|
|
165
|
+
message?: string;
|
|
166
|
+
/// Runtime-only flag — `true` for the red "missing from palette"
|
|
167
|
+
/// placeholder. Never persisted: toDiagramPayload skips color/border/
|
|
168
|
+
/// message for placeholders so a missing-palette state can't get
|
|
169
|
+
/// frozen into the saved blob or local draft.
|
|
170
|
+
isPlaceholder?: boolean;
|
|
171
|
+
/// Per-instance values for the element's parameters, keyed by
|
|
172
|
+
/// ParamSchema.name. Missing entries fall back to the schema's
|
|
173
|
+
/// defaultValue when the Properties panel renders.
|
|
174
|
+
paramValues?: Record<string, string>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export class DiagramNode extends Node {
|
|
178
|
+
typeId: string; // palette TypeId — used to look up rendering data on save/load
|
|
179
|
+
x: number;
|
|
180
|
+
y: number;
|
|
181
|
+
color: string;
|
|
182
|
+
border: string;
|
|
183
|
+
message: string;
|
|
184
|
+
isPlaceholder: boolean;
|
|
185
|
+
paramValues: Record<string, string>;
|
|
186
|
+
|
|
187
|
+
constructor(init: DiagramNodeInit) {
|
|
188
|
+
super(init);
|
|
189
|
+
// Default to the palette element id so existing call sites that
|
|
190
|
+
// forget to pass typeId still round-trip cleanly.
|
|
191
|
+
this.typeId = init.typeId ?? init.id;
|
|
192
|
+
this.x = typeof init.x === 'number' ? init.x : 0;
|
|
193
|
+
this.y = typeof init.y === 'number' ? init.y : 0;
|
|
194
|
+
this.color = init.color ?? '#d7d7d7';
|
|
195
|
+
this.border = init.border ?? '#8c8c8c';
|
|
196
|
+
this.message = init.message ?? '';
|
|
197
|
+
this.isPlaceholder = init.isPlaceholder ?? false;
|
|
198
|
+
this.paramValues = init.paramValues ?? {};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
override clone(): DiagramNode {
|
|
202
|
+
return new DiagramNode({
|
|
203
|
+
id: this.id,
|
|
204
|
+
typeId: this.typeId,
|
|
205
|
+
name: this.name,
|
|
206
|
+
description: this.description,
|
|
207
|
+
groupName: this.groupName,
|
|
208
|
+
inPorts: this.inPorts.map((p) => p.clone()),
|
|
209
|
+
outPorts: this.outPorts.map((p) => p.clone()),
|
|
210
|
+
icon: this.icon,
|
|
211
|
+
x: this.x,
|
|
212
|
+
y: this.y,
|
|
213
|
+
color: this.color,
|
|
214
|
+
border: this.border,
|
|
215
|
+
message: this.message,
|
|
216
|
+
openAction: this.openAction,
|
|
217
|
+
isPlaceholder: this.isPlaceholder,
|
|
218
|
+
paramValues: { ...this.paramValues },
|
|
219
|
+
parameters: this.parameters.map((p) => ({ ...p, options: [...p.options] })),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export type LinkEndpoint = string | { id: string };
|
|
225
|
+
|
|
226
|
+
export interface LinkInit {
|
|
227
|
+
id?: string;
|
|
228
|
+
outNode: LinkEndpoint;
|
|
229
|
+
outPort: LinkEndpoint;
|
|
230
|
+
inNode: LinkEndpoint;
|
|
231
|
+
inPort: LinkEndpoint;
|
|
232
|
+
metadata?: JsonObject;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export class Link {
|
|
236
|
+
id: string;
|
|
237
|
+
outNode: LinkEndpoint;
|
|
238
|
+
outPort: LinkEndpoint;
|
|
239
|
+
inNode: LinkEndpoint;
|
|
240
|
+
inPort: LinkEndpoint;
|
|
241
|
+
metadata: JsonObject;
|
|
242
|
+
|
|
243
|
+
constructor(init: LinkInit) {
|
|
244
|
+
this.id = init.id ?? '';
|
|
245
|
+
this.outNode = init.outNode;
|
|
246
|
+
this.outPort = init.outPort;
|
|
247
|
+
this.inNode = init.inNode;
|
|
248
|
+
this.inPort = init.inPort;
|
|
249
|
+
this.metadata = init.metadata ?? {};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export type PortDirection = 'in' | 'out';
|
|
254
|
+
|
|
255
|
+
// Internal data shape stored in the diagram-engine GraphLinksModel.
|
|
256
|
+
// Kept separate from public Port/DiagramNode to avoid coupling rendering details to the API.
|
|
257
|
+
export interface PortData {
|
|
258
|
+
id: string;
|
|
259
|
+
name: string;
|
|
260
|
+
description: string;
|
|
261
|
+
type: string;
|
|
262
|
+
maxLinks: number;
|
|
263
|
+
direction: PortDirection;
|
|
264
|
+
/// Whitelist of socket-type names accepted in addition to <c>type</c>.
|
|
265
|
+
/// Mirrors Port.availableTypes — needed at link-time on the diagram side.
|
|
266
|
+
availableTypes?: string[];
|
|
267
|
+
/// Dynamic-port flag. When combined with <c>dynamicMode === 'onConnect'</c>,
|
|
268
|
+
/// the anchor port spawns a sibling
|
|
269
|
+
/// on each link drop and the link is rerouted to the sibling.
|
|
270
|
+
isDynamic?: boolean;
|
|
271
|
+
dynamicMode?: string;
|
|
272
|
+
/// True when this port was spawned by the dynamic-growth pipeline rather
|
|
273
|
+
/// than declared on the palette element. Persists in the diagram blob so
|
|
274
|
+
/// reload restores the same set of sibling sockets.
|
|
275
|
+
isSibling?: boolean;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface NodeData {
|
|
279
|
+
id: string;
|
|
280
|
+
typeId: string;
|
|
281
|
+
name: string;
|
|
282
|
+
description: string;
|
|
283
|
+
groupName: string;
|
|
284
|
+
inPorts: PortData[];
|
|
285
|
+
outPorts: PortData[];
|
|
286
|
+
icon: string;
|
|
287
|
+
color: string;
|
|
288
|
+
border: string;
|
|
289
|
+
message: string;
|
|
290
|
+
/// Runtime-only — see DiagramNode.isPlaceholder. Carried on the
|
|
291
|
+
/// diagram model so save() can omit color/border/message for
|
|
292
|
+
/// missing-palette nodes instead of freezing the red-state into
|
|
293
|
+
/// the persisted blob.
|
|
294
|
+
isPlaceholder?: boolean;
|
|
295
|
+
loc: string;
|
|
296
|
+
/// Non-empty double-click action copied from the catalog. The action is
|
|
297
|
+
/// interpreted by the host application through the nodeOpen event.
|
|
298
|
+
openAction?: string;
|
|
299
|
+
/// Per-element parameter schema snapshot (sourced from the palette at
|
|
300
|
+
/// drop/load time). Lives on the diagram model so the Properties panel
|
|
301
|
+
/// can render typed editors without a catalog round-trip every time
|
|
302
|
+
/// the user clicks a node.
|
|
303
|
+
parameters?: ParamSchema[];
|
|
304
|
+
/// Per-instance overrides for those parameters, keyed by ParamSchema.name.
|
|
305
|
+
paramValues?: Record<string, string>;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface LinkData {
|
|
309
|
+
from: string;
|
|
310
|
+
fromPort: string;
|
|
311
|
+
to: string;
|
|
312
|
+
toPort: string;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface PaletteNodeData {
|
|
316
|
+
id: string;
|
|
317
|
+
name: string;
|
|
318
|
+
description: string;
|
|
319
|
+
group: string;
|
|
320
|
+
icon: string;
|
|
321
|
+
inPorts: PortData[];
|
|
322
|
+
outPorts: PortData[];
|
|
323
|
+
visible: boolean;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface PaletteGroupData {
|
|
327
|
+
id: string;
|
|
328
|
+
name: string;
|
|
329
|
+
isGroup: true;
|
|
330
|
+
expanded: boolean;
|
|
331
|
+
visible: boolean;
|
|
332
|
+
}
|