@babsey/code-graph 0.0.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.
@@ -0,0 +1,1820 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("baklavajs"), require("mustache"), require("vue"), require("toposort"), require("uuid"), require("@vueuse/core")) : typeof define === "function" && define.amd ? define(["exports", "baklavajs", "mustache", "vue", "toposort", "uuid", "@vueuse/core"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["code-graph"] = {}, global.baklavajs, global.mustache, global.Vue, global.toposort, global.uuid, global["@vueuse/core"]));
3
+ })(this, (function(exports2, baklavajs, mustache, vue, toposort, uuid, core) {
4
+ "use strict";
5
+ class AbstractCodeNode extends baklavajs.AbstractNode {
6
+ state;
7
+ code;
8
+ isCodeNode = true;
9
+ inputs = {};
10
+ outputs = {};
11
+ constructor() {
12
+ super();
13
+ this.initializeIo();
14
+ this.width = 400;
15
+ this.twoColumn = true;
16
+ this.state = vue.reactive({
17
+ codeTemplate: "{{ &outputs.code }}",
18
+ hidden: false,
19
+ integrated: false,
20
+ modules: [],
21
+ script: "",
22
+ variableName: ""
23
+ });
24
+ }
25
+ get codeTemplate() {
26
+ return this.state.codeTemplate;
27
+ }
28
+ get idx() {
29
+ return this.code?.codeNodes.filter((node) => !node.state.integrated).indexOf(this) ?? -1;
30
+ }
31
+ get idxByVariableNames() {
32
+ return this.code?.getNodesBySameVariableNames(this.state.variableName).indexOf(this) ?? -1;
33
+ }
34
+ get script() {
35
+ return this.state.script;
36
+ }
37
+ get shortId() {
38
+ return this.id.slice(0, 6);
39
+ }
40
+ get variableName() {
41
+ return this.state.variableName ? this.state.variableName + (this.idxByVariableNames + 1) : "";
42
+ }
43
+ /**
44
+ * Get connected nodes to the node.
45
+ * @param type inputs or outputs
46
+ * @returns code node instances
47
+ */
48
+ getConnectedNodes(type) {
49
+ let nodeIds = [];
50
+ if (type !== "inputs") {
51
+ const targets = this.graph?.connections.filter((c) => c.from.name !== "_node").filter((c) => c.from.nodeId === this.id).map((c) => c.to.nodeId);
52
+ if (targets) nodeIds = nodeIds.concat(targets);
53
+ }
54
+ if (type !== "outputs") {
55
+ const sources = this.graph?.connections.filter((c) => c.from.name !== "_node").filter((c) => c.to.nodeId === this.id).map((c) => c.from.nodeId);
56
+ if (sources) nodeIds = nodeIds.concat(sources);
57
+ }
58
+ if (!nodeIds || nodeIds.length == 0) return [];
59
+ return nodeIds.map((nodeId) => this.graph?.findNodeById(nodeId));
60
+ }
61
+ registerCode(code) {
62
+ this.code = code;
63
+ }
64
+ /**
65
+ * Render code of this node.
66
+ */
67
+ renderCode() {
68
+ const inputs = {};
69
+ Object.keys(this.inputs).forEach((intfKey) => {
70
+ if (intfKey === "_node") return;
71
+ const intf = this.inputs[intfKey];
72
+ const value = intf.isString ? `'${intf.value}'` : intf.value;
73
+ if (intf && intf.state) inputs[intfKey] = intf.state.script.length > 0 ? intf.state.script : value;
74
+ });
75
+ const outputs = {};
76
+ Object.keys(this.outputs).forEach((intfKey) => {
77
+ if (intfKey === "_node") return;
78
+ const intf = this.outputs[intfKey];
79
+ const value = intf.isString ? `'${intf.value}'` : intf.value;
80
+ if (intf && intf.state) outputs[intfKey] = value;
81
+ });
82
+ this.state.script = mustache.render(this.state.codeTemplate, { inputs, outputs });
83
+ if (this.outputs.code) this.outputs.code.state.script = this.state.script;
84
+ }
85
+ updateOutputVariableName() {
86
+ if (this.outputs.code) this.outputs.code.name = this.variableName;
87
+ }
88
+ }
89
+ class CodeNode extends AbstractCodeNode {
90
+ /**
91
+ * The default implementation does nothing.
92
+ * Overwrite this method to do calculation.
93
+ * @param inputs Values of all input interfaces
94
+ * @param globalValues Set of values passed to every node by the engine plugin
95
+ * @return Values for output interfaces
96
+ */
97
+ calculate;
98
+ load(state) {
99
+ super.load(state);
100
+ loadNodeState(this.graph, state);
101
+ }
102
+ save() {
103
+ const state = super.save();
104
+ saveNodeState(this.graph, state);
105
+ return state;
106
+ }
107
+ updateModules(modules) {
108
+ if (modules) {
109
+ this.state.modules = modules;
110
+ } else if (this.type.includes(".")) {
111
+ const modules2 = this.type.split(".");
112
+ this.state.modules.push(modules2.slice(0, modules2.length - 1).join("."));
113
+ }
114
+ }
115
+ }
116
+ const loadNodeState = (graph, nodeState) => {
117
+ if (!graph) return;
118
+ const node = graph.findNodeById(nodeState.id);
119
+ if (!node || node.subgraph) return;
120
+ const codeNode = node;
121
+ if (codeNode.state) {
122
+ codeNode.state.integrated = nodeState.integrated;
123
+ codeNode.state.modules = nodeState.modules;
124
+ codeNode.state.props = nodeState.props;
125
+ }
126
+ Object.entries(nodeState.inputs).forEach(([inputKey, inputItem]) => {
127
+ if (inputKey === "_node") return;
128
+ if (codeNode.inputs[inputKey]) codeNode.inputs[inputKey].hidden = inputItem.hidden;
129
+ });
130
+ Object.entries(nodeState.outputs).forEach(([outputKey, outputItem]) => {
131
+ if (outputKey === "_node") return;
132
+ if (codeNode.outputs[outputKey]) codeNode.outputs[outputKey].hidden = outputItem.hidden;
133
+ });
134
+ };
135
+ const saveNodeState = (graph, nodeState) => {
136
+ if (!graph) return;
137
+ const node = graph.findNodeById(nodeState.id);
138
+ if (!node || node.subgraph) return;
139
+ const codeNode = node;
140
+ if (codeNode.state) {
141
+ nodeState.integrated = codeNode.state.integrated;
142
+ nodeState.modules = codeNode.state.modules;
143
+ }
144
+ Object.entries(nodeState.inputs).forEach(([inputKey, inputItem]) => {
145
+ if (inputKey === "_node") return;
146
+ if (codeNode.inputs[inputKey]) inputItem.hidden = codeNode.inputs[inputKey].hidden;
147
+ });
148
+ Object.entries(nodeState.outputs).forEach(([outputKey, outputItem]) => {
149
+ if (outputKey === "_node") return;
150
+ if (codeNode.outputs[outputKey]) outputItem.hidden = codeNode.outputs[outputKey].hidden;
151
+ });
152
+ };
153
+ new baklavajs.NodeInterfaceType("boolean");
154
+ new baklavajs.NodeInterfaceType("dict");
155
+ new baklavajs.NodeInterfaceType("list");
156
+ const nodeType = new baklavajs.NodeInterfaceType("node");
157
+ const numberType = new baklavajs.NodeInterfaceType("number");
158
+ const stringType = new baklavajs.NodeInterfaceType("string");
159
+ const _hoisted_1$f = ["title"];
160
+ const _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
161
+ __name: "CodeNodeInterface",
162
+ props: {
163
+ intf: {}
164
+ },
165
+ setup(__props) {
166
+ return (_ctx, _cache) => {
167
+ return vue.openBlock(), vue.createElementBlock("div", {
168
+ title: _ctx.intf.state?.script
169
+ }, vue.toDisplayString(_ctx.intf.name), 9, _hoisted_1$f);
170
+ };
171
+ }
172
+ });
173
+ class CodeNodeInterface extends baklavajs.NodeInterface {
174
+ optional = false;
175
+ code;
176
+ state;
177
+ constructor(name, value) {
178
+ super(name, value);
179
+ this.setComponent(vue.markRaw(_sfc_main$g));
180
+ this.state = vue.reactive({
181
+ script: ""
182
+ });
183
+ }
184
+ get shortId() {
185
+ return this.id.slice(0, 6);
186
+ }
187
+ // override get value(): T {
188
+ // return super.value
189
+ // }
190
+ // override set value(value: T) {
191
+ // super.value = value;
192
+ // if (this.name !== '_node') this.setHidden(false);
193
+ // }
194
+ }
195
+ class ButtonInterface extends CodeNodeInterface {
196
+ component = vue.markRaw(baklavajs.ButtonInterfaceComponent);
197
+ callback;
198
+ constructor(name, callback) {
199
+ super(name, void 0);
200
+ this.callback = callback;
201
+ this.setPort(false);
202
+ }
203
+ }
204
+ class CheckboxInterface extends CodeNodeInterface {
205
+ component = vue.markRaw(baklavajs.CheckboxInterfaceComponent);
206
+ }
207
+ class BaseNumericInterface extends CodeNodeInterface {
208
+ min;
209
+ max;
210
+ constructor(name, value, min, max) {
211
+ super(name, value);
212
+ this.min = min;
213
+ this.max = max;
214
+ }
215
+ validate(v) {
216
+ return (this.min === void 0 || v >= this.min) && (this.max === void 0 || v <= this.max);
217
+ }
218
+ }
219
+ class IntegerInterface extends BaseNumericInterface {
220
+ component = vue.markRaw(baklavajs.IntegerInterfaceComponent);
221
+ validate(v) {
222
+ return Number.isInteger(v) && super.validate(v);
223
+ }
224
+ }
225
+ class CodeInputInterface extends CodeNodeInterface {
226
+ isCodeInput = true;
227
+ constructor(name = "", value) {
228
+ super(name, value);
229
+ this.setComponent(vue.markRaw(_sfc_main$g));
230
+ }
231
+ }
232
+ class CodeOutputInterface extends CodeNodeInterface {
233
+ isCodeOutput = true;
234
+ constructor(name = "", value = "") {
235
+ super(name, value);
236
+ this.setComponent(vue.markRaw(_sfc_main$g));
237
+ }
238
+ get script() {
239
+ return this.state.script;
240
+ }
241
+ get value() {
242
+ return super.value;
243
+ }
244
+ set value(value) {
245
+ super.value = value;
246
+ this.state.script = this.name.length > 0 ? this.name : this.value;
247
+ }
248
+ }
249
+ class NumberInterface extends BaseNumericInterface {
250
+ component = vue.markRaw(baklavajs.NumberInterfaceComponent);
251
+ }
252
+ class SelectInterface extends CodeNodeInterface {
253
+ component = vue.markRaw(baklavajs.SelectInterfaceComponent);
254
+ items;
255
+ constructor(name, value, items) {
256
+ super(name, value);
257
+ this.items = items;
258
+ }
259
+ }
260
+ class SliderInterface extends BaseNumericInterface {
261
+ component = vue.markRaw(baklavajs.SliderInterfaceComponent);
262
+ min;
263
+ max;
264
+ constructor(name, value, min, max) {
265
+ super(name, value, min, max);
266
+ this.min = min;
267
+ this.max = max;
268
+ }
269
+ }
270
+ class TextInterface extends CodeNodeInterface {
271
+ component = vue.markRaw(baklavajs.TextInputInterfaceComponent);
272
+ constructor(name, value) {
273
+ super(name, value);
274
+ this.setPort(false);
275
+ }
276
+ }
277
+ class TextInputInterface extends CodeNodeInterface {
278
+ isString = true;
279
+ component = vue.markRaw(baklavajs.TextInputInterfaceComponent);
280
+ }
281
+ class TextareaInputInterface extends CodeNodeInterface {
282
+ component = vue.markRaw(baklavajs.TextareaInputInterfaceComponent);
283
+ }
284
+ function defineCodeNode(definition) {
285
+ return class extends CodeNode {
286
+ type = definition.type;
287
+ inputs = {};
288
+ outputs = {};
289
+ constructor() {
290
+ super();
291
+ this._title = definition.title ?? definition.type;
292
+ this.updateModules(definition.modules);
293
+ if (definition.codeTemplate) this.state.codeTemplate = definition.codeTemplate(this);
294
+ if (definition.variableName) this.state.variableName = definition.variableName;
295
+ this.addInput(
296
+ "_node",
297
+ new CodeNodeInterface("", []).use(baklavajs.setType, nodeType).use(baklavajs.allowMultipleConnections).setHidden(true)
298
+ );
299
+ this.addOutput(
300
+ "_node",
301
+ new CodeNodeInterface("", []).use(baklavajs.setType, nodeType).use(baklavajs.allowMultipleConnections).setHidden(true)
302
+ );
303
+ this.executeFactory("input", definition.inputs);
304
+ this.executeFactory("output", definition.outputs);
305
+ definition.onCreate?.call(this);
306
+ }
307
+ calculate = definition.calculate ? (inputs, globalValues) => ({
308
+ ...definition.calculate.call(this, inputs, globalValues),
309
+ _node: null
310
+ }) : void 0;
311
+ onPlaced() {
312
+ definition.onPlaced?.call(this);
313
+ }
314
+ onDestroy() {
315
+ definition.onDestroy?.call(this);
316
+ }
317
+ onCodeUpdate() {
318
+ definition.onCodeUpdate?.call(this);
319
+ }
320
+ executeFactory(type, factory) {
321
+ Object.keys(factory || {}).forEach((k) => {
322
+ const intf = factory[k]();
323
+ if (type === "input") {
324
+ this.addInput(k, intf);
325
+ } else {
326
+ this.addOutput(k, intf);
327
+ }
328
+ });
329
+ }
330
+ };
331
+ }
332
+ class DynamicCodeNode extends CodeNode {
333
+ /**
334
+ * The default implementation does nothing.
335
+ * Overwrite this method to do calculation.
336
+ * @param inputs Values of all input interfaces
337
+ * @param globalValues Set of values passed to every node by the engine plugin
338
+ * @return Values for output interfaces
339
+ */
340
+ calculate;
341
+ }
342
+ function defineDynamicCodeNode(definition) {
343
+ return class extends DynamicCodeNode {
344
+ type = definition.type;
345
+ inputs = {};
346
+ outputs = {};
347
+ calculate;
348
+ preventUpdate = false;
349
+ staticInputKeys = Object.keys(definition.inputs ?? {});
350
+ staticOutputKeys = Object.keys(definition.outputs ?? {});
351
+ constructor() {
352
+ super();
353
+ this._title = definition.title ?? definition.type;
354
+ this.updateModules(definition.modules);
355
+ if (definition.codeTemplate) this.state.codeTemplate = definition.codeTemplate(this);
356
+ if (definition.variableName) this.state.variableName = definition.variableName;
357
+ this.addInput(
358
+ "_node",
359
+ new CodeNodeInterface("", []).use(baklavajs.setType, nodeType).use(baklavajs.allowMultipleConnections).setHidden(true)
360
+ );
361
+ this.addOutput(
362
+ "_node",
363
+ new CodeNodeInterface("", []).use(baklavajs.setType, nodeType).use(baklavajs.allowMultipleConnections).setHidden(true)
364
+ );
365
+ this.staticInputKeys.push("_node");
366
+ this.staticOutputKeys.push("_node");
367
+ this.executeFactory("input", definition.inputs);
368
+ this.executeFactory("output", definition.outputs);
369
+ if (definition.calculate) {
370
+ this.calculate = (inputs, globalValues) => ({
371
+ ...definition.calculate?.call(this, inputs, globalValues),
372
+ _node: null
373
+ });
374
+ }
375
+ definition.onCreate?.call(this);
376
+ }
377
+ onPlaced() {
378
+ this.events.update.subscribe(this, (data) => {
379
+ if (!data) return;
380
+ if (data.type === "input" && this.staticInputKeys.includes(data.name) || data.type === "output" && this.staticOutputKeys.includes(data.name)) {
381
+ this.onUpdate();
382
+ }
383
+ });
384
+ this.onUpdate();
385
+ definition.onPlaced?.call(this);
386
+ }
387
+ onDestroy() {
388
+ definition.onDestroy?.call(this);
389
+ }
390
+ onCodeUpdate() {
391
+ definition.onCodeUpdate?.call(this);
392
+ }
393
+ load(state) {
394
+ this.preventUpdate = true;
395
+ this.hooks.beforeLoad.execute(state);
396
+ this.id = state.id;
397
+ this.title = state.title;
398
+ for (const k of this.staticInputKeys) {
399
+ this.inputs[k].load(state.inputs[k]);
400
+ this.inputs[k].nodeId = this.id;
401
+ if (k === "_node") continue;
402
+ this.inputs[k].hidden = state.inputs[k].hidden;
403
+ }
404
+ for (const k of this.staticOutputKeys) {
405
+ this.outputs[k].load(state.outputs[k]);
406
+ this.outputs[k].nodeId = this.id;
407
+ if (k === "_node") continue;
408
+ this.outputs[k].hidden = state.outputs[k].hidden;
409
+ }
410
+ this.preventUpdate = false;
411
+ this.onUpdate();
412
+ this.preventUpdate = true;
413
+ for (const k of Object.keys(state.inputs)) {
414
+ if (this.staticInputKeys.includes(k)) continue;
415
+ if (!this.inputs[k]) {
416
+ const value = state.inputs[k].value;
417
+ let inputInterface;
418
+ if (typeof value == "number") {
419
+ inputInterface = new baklavajs.IntegerInterface(k, value).use(baklavajs.setType, numberType);
420
+ } else {
421
+ inputInterface = new baklavajs.TextInputInterface(k, JSON.stringify(value)).use(baklavajs.setType, stringType);
422
+ }
423
+ inputInterface.use(baklavajs.displayInSidebar, true);
424
+ this.addInput(k, inputInterface);
425
+ }
426
+ if (this.inputs[k]) {
427
+ this.inputs[k].load(state.inputs[k]);
428
+ this.inputs[k].nodeId = this.id;
429
+ }
430
+ }
431
+ for (const k of Object.keys(state.outputs)) {
432
+ if (this.staticOutputKeys.includes(k)) continue;
433
+ if (!this.outputs[k]) {
434
+ const outputInterface = new CodeOutputInterface(k);
435
+ this.addOutput(k, outputInterface);
436
+ }
437
+ if (this.outputs[k]) {
438
+ this.outputs[k].load(state.outputs[k]);
439
+ this.outputs[k].nodeId = this.id;
440
+ }
441
+ }
442
+ loadNodeState(this.graph, state);
443
+ this.preventUpdate = false;
444
+ this.events.loaded.emit(this);
445
+ }
446
+ onUpdate() {
447
+ if (this.preventUpdate) return;
448
+ if (this.graph) this.graph.activeTransactions++;
449
+ const inputValues = this.getStaticValues(this.staticInputKeys, this.inputs);
450
+ const outputValues = this.getStaticValues(this.staticOutputKeys, this.outputs);
451
+ const result = definition.onUpdate.call(this, inputValues, outputValues);
452
+ this.updateInterfaces("input", result.inputs ?? {}, result.forceUpdateInputs ?? []);
453
+ this.updateInterfaces("output", result.outputs ?? {}, result.forceUpdateOutputs ?? []);
454
+ if (this.graph) this.graph.activeTransactions--;
455
+ }
456
+ getStaticValues(keys, interfaces) {
457
+ const values = {};
458
+ for (const k of keys) {
459
+ values[k] = interfaces[k].value;
460
+ }
461
+ return values;
462
+ }
463
+ updateInterfaces(type, newInterfaces, forceUpdates) {
464
+ const staticKeys = type === "input" ? this.staticInputKeys : this.staticOutputKeys;
465
+ const currentInterfaces = type === "input" ? this.inputs : this.outputs;
466
+ for (const k of Object.keys(currentInterfaces)) {
467
+ if (staticKeys.includes(k) || newInterfaces[k] && !forceUpdates.includes(k)) continue;
468
+ if (type === "input") {
469
+ this.removeInput(k);
470
+ } else {
471
+ this.removeOutput(k);
472
+ }
473
+ }
474
+ for (const k of Object.keys(newInterfaces)) {
475
+ if (currentInterfaces[k]) continue;
476
+ const intf = newInterfaces[k]();
477
+ if (type === "input") {
478
+ this.addInput(k, intf);
479
+ } else {
480
+ this.addOutput(k, intf);
481
+ }
482
+ }
483
+ }
484
+ executeFactory(type, factory) {
485
+ Object.keys(factory || {}).forEach((k) => {
486
+ const intf = factory[k]();
487
+ if (type === "input") {
488
+ this.addInput(k, intf);
489
+ } else {
490
+ this.addOutput(k, intf);
491
+ }
492
+ });
493
+ }
494
+ };
495
+ }
496
+ class Code {
497
+ _id;
498
+ _viewModel;
499
+ _state;
500
+ constructor(viewModel) {
501
+ this._id = uuid.v4();
502
+ this._viewModel = viewModel;
503
+ this._state = vue.reactive({
504
+ autosort: false,
505
+ modules: {},
506
+ script: "",
507
+ token: null,
508
+ template: ""
509
+ });
510
+ }
511
+ get codeNodes() {
512
+ return getCodeNodes(this.graph);
513
+ }
514
+ get connections() {
515
+ return this.graph.connections;
516
+ }
517
+ get graph() {
518
+ return this.viewModel.displayedGraph;
519
+ }
520
+ get id() {
521
+ return this._id;
522
+ }
523
+ get modules() {
524
+ let categories = [];
525
+ this.codeNodes.filter((node) => node.state.modules?.length > 0).forEach((node) => {
526
+ categories = categories.concat(node.state.modules);
527
+ });
528
+ if (!categories) return [];
529
+ categories.sort();
530
+ return Array.from(new Set(categories.map((category) => this.viewModel.state.modules[category])));
531
+ }
532
+ get nodeIds() {
533
+ return this.codeNodes.map((node) => node.id);
534
+ }
535
+ get nodes() {
536
+ return this.graph.nodes;
537
+ }
538
+ get scriptedCodeNodes() {
539
+ return getCodeNodes(this.graph).filter(
540
+ (codeNode) => codeNode.state?.script.length > 0
541
+ );
542
+ }
543
+ get shortId() {
544
+ return this.id.slice(0);
545
+ }
546
+ get state() {
547
+ return this._state;
548
+ }
549
+ get viewModel() {
550
+ return this._viewModel;
551
+ }
552
+ get visibleNodes() {
553
+ return this.codeNodes.filter((node) => !node.state?.hidden);
554
+ }
555
+ /**
556
+ * Add code node to graph.
557
+ * @param node code node
558
+ * @param props optional
559
+ */
560
+ addNode(node, props) {
561
+ if (props) node.state.props = props;
562
+ return this.graph.addNode(node);
563
+ }
564
+ /**
565
+ * Add code node at coordinates.
566
+ * @param node code node
567
+ * @param position position
568
+ * @param props optional
569
+ * @returns code node
570
+ */
571
+ addNodeAtCoordinates = (node, position = { x: 0, y: 0 }, props) => {
572
+ this.addNode(node, props);
573
+ if (node.position) node.position = position;
574
+ return node;
575
+ };
576
+ /**
577
+ * Add connection of code nodes
578
+ * @param from code node interface
579
+ * @param to code node interface
580
+ */
581
+ addConnection(from, to) {
582
+ if (from.name !== "_node") from.hidden = false;
583
+ if (to.name !== "_node") to.hidden = false;
584
+ this.graph.addConnection(from, to);
585
+ }
586
+ /**
587
+ * Clear code graph.
588
+ */
589
+ clear() {
590
+ this.graph._nodes = [];
591
+ this.graph._connections = [];
592
+ }
593
+ findNodeById(id) {
594
+ return this.graph.findNodeById(id);
595
+ }
596
+ findNodeByType(nodeType2) {
597
+ return this.codeNodes.find((codeNode) => codeNode.type === nodeType2);
598
+ }
599
+ getNodesBySameType(type) {
600
+ return this.codeNodes.filter((codeNode) => codeNode.type === type);
601
+ }
602
+ getNodesBySameVariableNames(variableName) {
603
+ return this.codeNodes.filter(
604
+ (codeNode) => codeNode.state.variableName === variableName
605
+ );
606
+ }
607
+ /**
608
+ * Check whether the graph has this connection.
609
+ * @param from node interface
610
+ * @param to node interface
611
+ * @returns boolean
612
+ */
613
+ hasConnection(from, to) {
614
+ return this.connections.some(
615
+ (connection) => connection.from.id === from.id && connection.to.id === to.id
616
+ );
617
+ }
618
+ /**
619
+ * Load template from the file.
620
+ */
621
+ loadTemplate(resolve) {
622
+ resolve.then((template) => {
623
+ this._state.template = template.default ?? "";
624
+ });
625
+ }
626
+ onCodeUpdate() {
627
+ this.codeNodes.forEach((codeNode) => codeNode.onCodeUpdate());
628
+ }
629
+ /**
630
+ * Remove connection from the graph
631
+ * @param connection connection between code nodes
632
+ */
633
+ removeConnection(connection) {
634
+ this.graph.removeConnection(connection);
635
+ }
636
+ /**
637
+ * Remove node from the graph.
638
+ * @param codeNode code node
639
+ */
640
+ removeNode(codeNode) {
641
+ this.graph.removeNode(codeNode);
642
+ }
643
+ /**
644
+ * Render node codes.
645
+ */
646
+ renderNodeCodes() {
647
+ if (this.codeNodes.length === 0) return;
648
+ this.codeNodes.forEach((node) => node.renderCode());
649
+ }
650
+ /**
651
+ * Render code.
652
+ */
653
+ renderCode() {
654
+ this.state.script = mustache.render(this.state.template || "", this);
655
+ }
656
+ /**
657
+ * Save code graph.
658
+ * @returns graph state
659
+ */
660
+ save() {
661
+ if (this.state.autosort) this.sortNodes();
662
+ const editorState = this.viewModel.editor.save();
663
+ editorState.graph.id = this.id;
664
+ this.saveNodeStates(editorState.graph.nodes);
665
+ return JSON.parse(JSON.stringify(editorState));
666
+ }
667
+ /**
668
+ * Save node states.
669
+ * @param nodeStates a list of node state.
670
+ */
671
+ saveNodeStates(nodeStates) {
672
+ nodeStates.forEach((nodeState, nodeIdx) => {
673
+ const node = this.nodes[nodeIdx];
674
+ Object.entries(nodeState.inputs).forEach(([inputKey]) => {
675
+ if (node.inputs[inputKey]) nodeState.inputs[inputKey].hidden = node.inputs[inputKey].hidden;
676
+ });
677
+ Object.entries(nodeState.outputs).forEach(([outputKey]) => {
678
+ if (node.inputs[outputKey]) nodeState.outputs[outputKey].hidden = node.outputs[outputKey].hidden;
679
+ });
680
+ });
681
+ }
682
+ /**
683
+ * Sort code nodes.
684
+ */
685
+ sortNodes() {
686
+ if (this.nodes.length === 0 || this.connections.length === 0) return;
687
+ try {
688
+ const edges = this.connections.map((connection) => [
689
+ connection.to.nodeId,
690
+ connection.from.nodeId
691
+ ]);
692
+ let nodeIds = [...this.nodeIds];
693
+ nodeIds.reverse();
694
+ nodeIds = toposort.array(nodeIds, edges);
695
+ nodeIds.reverse();
696
+ const unconnected = this.graph.nodes.map((node) => node.id).filter((nodeId) => !nodeIds.includes(nodeId));
697
+ nodeIds = nodeIds.concat(unconnected);
698
+ this.graph._nodes = nodeIds.map((nodeId) => this.findNodeById(nodeId));
699
+ } catch {
700
+ console.warn("Failed to sort nodes.");
701
+ }
702
+ }
703
+ updateOutputVariableNames() {
704
+ this.codeNodes.forEach((codeNode) => codeNode.updateOutputVariableName());
705
+ }
706
+ }
707
+ const getCodeNodes = (graph) => {
708
+ let nodes = [];
709
+ graph.nodes.forEach((node) => {
710
+ if (node.subgraph) {
711
+ nodes = nodes.concat(getCodeNodes(node.subgraph));
712
+ } else if (node.isCodeNode) {
713
+ nodes.push(node);
714
+ }
715
+ });
716
+ return nodes;
717
+ };
718
+ const getPositionAtColumn = (col = 0, offset = 100) => {
719
+ const width = 350;
720
+ const padding = 70;
721
+ return {
722
+ x: col * (width + padding),
723
+ y: offset
724
+ };
725
+ };
726
+ const getPositionBeforeNode = (node) => {
727
+ const position = { ...node.position };
728
+ position.x -= 400;
729
+ position.y += 50;
730
+ return position;
731
+ };
732
+ const transferCodeScript = (graph) => {
733
+ const { calculationOrder, connectionsFromNode } = baklavajs.sortTopologically(graph);
734
+ calculationOrder.forEach((node) => {
735
+ if (!node.isCodeNode) return;
736
+ const codeNode = node;
737
+ if (connectionsFromNode.has(codeNode)) {
738
+ connectionsFromNode.get(codeNode).forEach((c) => {
739
+ if (c.to.state && c.from.script) c.to.state.script = c.from.script;
740
+ });
741
+ }
742
+ });
743
+ };
744
+ const _hoisted_1$e = ["id"];
745
+ const _hoisted_2$5 = { class: "align-middle" };
746
+ const _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
747
+ __name: "CodeGraphNodeInterface",
748
+ props: {
749
+ node: {},
750
+ intf: {}
751
+ },
752
+ setup(__props) {
753
+ const props = __props;
754
+ const { viewModel } = baklavajs.useViewModel();
755
+ const { hoveredOver, temporaryConnection } = baklavajs.useTemporaryConnection();
756
+ const el = vue.ref(null);
757
+ const isConnected = vue.computed(() => props.intf.connectionCount > 0);
758
+ const classes = vue.computed(() => ({
759
+ "--connected": isConnected.value
760
+ }));
761
+ const startHover = () => {
762
+ hoveredOver(props.intf);
763
+ };
764
+ const endHover = () => {
765
+ hoveredOver(void 0);
766
+ };
767
+ const onRender = () => {
768
+ if (el.value) {
769
+ viewModel.value.hooks.renderInterface.execute({ intf: props.intf, el: el.value });
770
+ }
771
+ };
772
+ vue.onMounted(onRender);
773
+ vue.onUpdated(onRender);
774
+ return (_ctx, _cache) => {
775
+ return vue.openBlock(), vue.createElementBlock("div", {
776
+ id: _ctx.intf.id,
777
+ ref_key: "el",
778
+ ref: el,
779
+ class: vue.normalizeClass(["baklava-node-interface", classes.value])
780
+ }, [
781
+ _ctx.intf.port ? (vue.openBlock(), vue.createElementBlock("div", {
782
+ key: 0,
783
+ class: vue.normalizeClass(["__port", { "--selected": vue.unref(temporaryConnection)?.from === _ctx.intf }]),
784
+ onPointerover: startHover,
785
+ onPointerout: endHover
786
+ }, null, 34)) : vue.createCommentVNode("", true),
787
+ vue.createElementVNode("span", _hoisted_2$5, [
788
+ vue.renderSlot(_ctx.$slots, "default")
789
+ ])
790
+ ], 10, _hoisted_1$e);
791
+ };
792
+ }
793
+ });
794
+ const _export_sfc = (sfc, props) => {
795
+ const target = sfc.__vccOpts || sfc;
796
+ for (const [key, val] of props) {
797
+ target[key] = val;
798
+ }
799
+ return target;
800
+ };
801
+ const _sfc_main$e = {};
802
+ const _hoisted_1$d = {
803
+ xmlns: "http://www.w3.org/2000/svg",
804
+ width: "24",
805
+ height: "24",
806
+ viewBox: "0 0 24 24",
807
+ fill: "currentColor",
808
+ class: "baklava-icon icon icon-tabler icons-tabler-filled icon-tabler-layout-sidebar-left-collapse"
809
+ };
810
+ function _sfc_render$9(_ctx, _cache) {
811
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$d, [..._cache[0] || (_cache[0] = [
812
+ vue.createElementVNode("path", {
813
+ stroke: "none",
814
+ d: "M0 0h24v24H0z",
815
+ fill: "none"
816
+ }, null, -1),
817
+ vue.createElementVNode("path", { d: "M18 3a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h12zm0 2h-9v14h9a1 1 0 0 0 .993 -.883l.007 -.117v-12a1 1 0 0 0 -.883 -.993l-.117 -.007zm-2.293 4.293a1 1 0 0 1 .083 1.32l-.083 .094l-1.292 1.293l1.292 1.293a1 1 0 0 1 .083 1.32l-.083 .094a1 1 0 0 1 -1.32 .083l-.094 -.083l-2 -2a1 1 0 0 1 -.083 -1.32l.083 -.094l2 -2a1 1 0 0 1 1.414 0z" }, null, -1)
818
+ ])]);
819
+ }
820
+ const LayoutSidebarLeftCollapse = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$9]]);
821
+ const _sfc_main$d = {};
822
+ const _hoisted_1$c = {
823
+ xmlns: "http://www.w3.org/2000/svg",
824
+ width: "24",
825
+ height: "24",
826
+ viewBox: "0 0 24 24",
827
+ fill: "currentColor",
828
+ class: "baklava-icon icon icon-tabler icons-tabler-filled icon-tabler-layout-sidebar-left-expand"
829
+ };
830
+ function _sfc_render$8(_ctx, _cache) {
831
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$c, [..._cache[0] || (_cache[0] = [
832
+ vue.createElementVNode("path", {
833
+ stroke: "none",
834
+ d: "M0 0h24v24H0z",
835
+ fill: "none"
836
+ }, null, -1),
837
+ vue.createElementVNode("path", { d: "M18 3a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h12zm0 2h-9v14h9a1 1 0 0 0 .993 -.883l.007 -.117v-12a1 1 0 0 0 -.883 -.993l-.117 -.007zm-4.387 4.21l.094 .083l2 2a1 1 0 0 1 .083 1.32l-.083 .094l-2 2a1 1 0 0 1 -1.497 -1.32l.083 -.094l1.292 -1.293l-1.292 -1.293a1 1 0 0 1 -.083 -1.32l.083 -.094a1 1 0 0 1 1.32 -.083z" }, null, -1)
838
+ ])]);
839
+ }
840
+ const LayoutSidebarLeftExpand = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$8]]);
841
+ const _sfc_main$c = {};
842
+ const _hoisted_1$b = {
843
+ xmlns: "http://www.w3.org/2000/svg",
844
+ width: "24",
845
+ height: "24",
846
+ viewBox: "0 0 24 24",
847
+ fill: "currentColor",
848
+ class: "balkava-icon icon icon-tabler icons-tabler-filled icon-tabler-layout-sidebar-right"
849
+ };
850
+ function _sfc_render$7(_ctx, _cache) {
851
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$b, [..._cache[0] || (_cache[0] = [
852
+ vue.createElementVNode("path", {
853
+ stroke: "none",
854
+ d: "M0 0h24v24H0z",
855
+ fill: "none"
856
+ }, null, -1),
857
+ vue.createElementVNode("path", { d: "M6 21a3 3 0 0 1 -3 -3v-12a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3zm8 -16h-8a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h8z" }, null, -1)
858
+ ])]);
859
+ }
860
+ const LayoutSidebarRight = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["render", _sfc_render$7]]);
861
+ const _sfc_main$b = {};
862
+ const _hoisted_1$a = {
863
+ xmlns: "http://www.w3.org/2000/svg",
864
+ width: "24",
865
+ height: "24",
866
+ viewBox: "0 0 24 24",
867
+ fill: "currentColor",
868
+ class: "baklava-icon icon icon-tabler icons-tabler-filled icon-tabler-layout-sidebar-right-collapse"
869
+ };
870
+ function _sfc_render$6(_ctx, _cache) {
871
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$a, [..._cache[0] || (_cache[0] = [
872
+ vue.createElementVNode("path", {
873
+ stroke: "none",
874
+ d: "M0 0h24v24H0z",
875
+ fill: "none"
876
+ }, null, -1),
877
+ vue.createElementVNode("path", { d: "M18 3a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h12zm-3 2h-9a1 1 0 0 0 -.993 .883l-.007 .117v12a1 1 0 0 0 .883 .993l.117 .007h9v-14zm-5.387 4.21l.094 .083l2 2a1 1 0 0 1 .083 1.32l-.083 .094l-2 2a1 1 0 0 1 -1.497 -1.32l.083 -.094l1.292 -1.293l-1.292 -1.293a1 1 0 0 1 -.083 -1.32l.083 -.094a1 1 0 0 1 1.32 -.083z" }, null, -1)
878
+ ])]);
879
+ }
880
+ const LayoutSidebarRightCollapse = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["render", _sfc_render$6]]);
881
+ const _sfc_main$a = {};
882
+ const _hoisted_1$9 = {
883
+ xmlns: "http://www.w3.org/2000/svg",
884
+ width: "24",
885
+ height: "24",
886
+ viewBox: "0 0 24 24",
887
+ fill: "currentColor",
888
+ class: "baklava-icon icon icon-tabler icons-tabler-filled icon-tabler-layout-sidebar-right-expand"
889
+ };
890
+ function _sfc_render$5(_ctx, _cache) {
891
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$9, [..._cache[0] || (_cache[0] = [
892
+ vue.createElementVNode("path", {
893
+ stroke: "none",
894
+ d: "M0 0h24v24H0z",
895
+ fill: "none"
896
+ }, null, -1),
897
+ vue.createElementVNode("path", { d: "M18 3a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h12zm-3 2h-9a1 1 0 0 0 -.993 .883l-.007 .117v12a1 1 0 0 0 .883 .993l.117 .007h9v-14zm-3.293 4.293a1 1 0 0 1 .083 1.32l-.083 .094l-1.292 1.293l1.292 1.293a1 1 0 0 1 .083 1.32l-.083 .094a1 1 0 0 1 -1.32 .083l-.094 -.083l-2 -2a1 1 0 0 1 -.083 -1.32l.083 -.094l2 -2a1 1 0 0 1 1.414 0z" }, null, -1)
898
+ ])]);
899
+ }
900
+ const LayoutSidebarRightExpand = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$5]]);
901
+ const _sfc_main$9 = {};
902
+ const _hoisted_1$8 = {
903
+ xmlns: "http://www.w3.org/2000/svg",
904
+ class: "baklava-icon",
905
+ width: "24",
906
+ height: "24",
907
+ viewBox: "0 0 24 24",
908
+ fill: "none",
909
+ stroke: "currentColor",
910
+ "stroke-width": "2",
911
+ "stroke-linecap": "round",
912
+ "stroke-linejoin": "round"
913
+ };
914
+ function _sfc_render$4(_ctx, _cache) {
915
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$8, [..._cache[0] || (_cache[0] = [
916
+ vue.createStaticVNode('<path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 2h5v4h-5z"></path><path d="M15 10h5v4h-5z"></path><path d="M5 18h5v4h-5z"></path><path d="M5 10h5v4h-5z"></path><path d="M10 12h5"></path><path d="M7.5 6v4"></path><path d="M7.5 14v4"></path>', 8)
917
+ ])]);
918
+ }
919
+ const Schema = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$4]]);
920
+ const _sfc_main$8 = {};
921
+ const _hoisted_1$7 = {
922
+ xmlns: "http://www.w3.org/2000/svg",
923
+ class: "baklava-icon",
924
+ width: "24",
925
+ height: "24",
926
+ viewBox: "0 0 24 24",
927
+ fill: "none",
928
+ stroke: "currentColor",
929
+ "stroke-width": "2",
930
+ "stroke-linecap": "round",
931
+ "stroke-linejoin": "round"
932
+ };
933
+ function _sfc_render$3(_ctx, _cache) {
934
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$7, [..._cache[0] || (_cache[0] = [
935
+ vue.createStaticVNode('<path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M6 2h4v4m-4 0h-1v-1"></path><path d="M15 11v-1h5v4h-2"></path><path d="M5 18h5v4h-5z"></path><path d="M5 10h5v4h-5z"></path><path d="M10 12h2"></path><path d="M7.5 7.5v2.5"></path><path d="M7.5 14v4"></path><path d="M3 3l18 18"></path>', 9)
936
+ ])]);
937
+ }
938
+ const SchemaOff = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$3]]);
939
+ const _sfc_main$7 = {};
940
+ const _hoisted_1$6 = {
941
+ xmlns: "http://www.w3.org/2000/svg",
942
+ class: "baklava-icon",
943
+ width: "24",
944
+ height: "24",
945
+ viewBox: "0 0 24 24",
946
+ fill: "none",
947
+ stroke: "currentColor",
948
+ "stroke-width": "2",
949
+ "stroke-linecap": "round",
950
+ "stroke-linejoin": "round"
951
+ };
952
+ function _sfc_render$2(_ctx, _cache) {
953
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$6, [..._cache[0] || (_cache[0] = [
954
+ vue.createStaticVNode('<path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M3 3l18 18"></path><path d="M4 7h3m4 0h9"></path><path d="M10 11l0 6"></path><path d="M14 14l0 3"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l.077 -.923"></path><path d="M18.384 14.373l.616 -7.373"></path><path d="M9 5v-1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path>', 8)
955
+ ])]);
956
+ }
957
+ const TrashOff = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$2]]);
958
+ const _sfc_main$6 = {};
959
+ const _hoisted_1$5 = {
960
+ xmlns: "http://www.w3.org/2000/svg",
961
+ class: "baklava-icon",
962
+ width: "16",
963
+ height: "16",
964
+ viewBox: "0 0 24 24",
965
+ "stroke-width": "2",
966
+ stroke: "currentColor",
967
+ fill: "none",
968
+ "stroke-linecap": "round",
969
+ "stroke-linejoin": "round"
970
+ };
971
+ function _sfc_render$1(_ctx, _cache) {
972
+ return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$5, [..._cache[0] || (_cache[0] = [
973
+ vue.createElementVNode("path", {
974
+ stroke: "none",
975
+ d: "M0 0h24v24H0z",
976
+ fill: "none"
977
+ }, null, -1),
978
+ vue.createElementVNode("circle", {
979
+ cx: "12",
980
+ cy: "12",
981
+ r: "1"
982
+ }, null, -1),
983
+ vue.createElementVNode("circle", {
984
+ cx: "12",
985
+ cy: "19",
986
+ r: "1"
987
+ }, null, -1),
988
+ vue.createElementVNode("circle", {
989
+ cx: "12",
990
+ cy: "5",
991
+ r: "1"
992
+ }, null, -1)
993
+ ])]);
994
+ }
995
+ const VerticalDots = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$1]]);
996
+ const _hoisted_1$4 = ["id", "data-node-type"];
997
+ const _hoisted_2$4 = {
998
+ class: "__title-label",
999
+ style: { "flex-grow": "1" }
1000
+ };
1001
+ const _hoisted_3$2 = { key: 0 };
1002
+ const _hoisted_4$1 = {
1003
+ class: "__menu",
1004
+ style: { "display": "flex" }
1005
+ };
1006
+ const _hoisted_5 = { class: "__outputs" };
1007
+ const _hoisted_6 = { key: 0 };
1008
+ const _hoisted_7 = ["id", "title"];
1009
+ const _hoisted_8 = { class: "__inputs" };
1010
+ const _hoisted_9 = { key: 0 };
1011
+ const _hoisted_10 = ["id", "title"];
1012
+ const _sfc_main$5 = /* @__PURE__ */ vue.defineComponent({
1013
+ __name: "CodeGraphNode",
1014
+ props: {
1015
+ node: {},
1016
+ selected: { type: Boolean, default: false },
1017
+ dragging: { type: Boolean }
1018
+ },
1019
+ emits: ["select", "start-drag", "update"],
1020
+ setup(__props, { emit: __emit }) {
1021
+ const ContextMenu = baklavajs.Components.ContextMenu;
1022
+ const NodeInterface = baklavajs.Components.NodeInterface;
1023
+ const props = __props;
1024
+ const node = vue.computed(() => props.node);
1025
+ const emit = __emit;
1026
+ const { viewModel } = baklavajs.useViewModel();
1027
+ const { graph, switchGraph } = baklavajs.useGraph();
1028
+ const el = vue.ref(null);
1029
+ const renaming = vue.ref(false);
1030
+ const tempName = vue.ref("");
1031
+ const renameInputEl = vue.ref(null);
1032
+ const isResizing = vue.ref(false);
1033
+ let resizeStartWidth = 0;
1034
+ let resizeStartMouseX = 0;
1035
+ const showContextMenu = vue.ref(false);
1036
+ const contextMenuItems = vue.computed(() => {
1037
+ const items = [
1038
+ { value: "edit", label: "Edit" },
1039
+ { value: "rename", label: "Rename" },
1040
+ { value: "delete", label: "Delete" }
1041
+ ];
1042
+ if (props.node.type.startsWith(baklavajs.GRAPH_NODE_TYPE_PREFIX)) {
1043
+ items.push({ value: "editSubgraph", label: "Edit Subgraph" });
1044
+ }
1045
+ return items;
1046
+ });
1047
+ const classes = vue.computed(() => ({
1048
+ "--selected": props.selected,
1049
+ "--dragging": props.dragging,
1050
+ "--two-column": !!props.node.twoColumn,
1051
+ "--hidden": node.value.state?.hidden
1052
+ }));
1053
+ const classesContent = vue.computed(() => ({
1054
+ "--reverse-y": props.node.reverseY ?? viewModel.value.settings.nodes.reverseY
1055
+ }));
1056
+ const styles = vue.computed(() => ({
1057
+ "top": `${props.node.position?.y ?? 0}px`,
1058
+ "left": `${props.node.position?.x ?? 0}px`,
1059
+ "--width": `${props.node.width ?? viewModel.value.settings.nodes.defaultWidth}px`
1060
+ }));
1061
+ const displayedInputs = vue.computed(() => Object.values(props.node.inputs).filter((ni) => !ni.hidden));
1062
+ const displayedOutputs = vue.computed(() => Object.values(props.node.outputs).filter((ni) => !ni.hidden));
1063
+ const select = () => {
1064
+ emit("select");
1065
+ };
1066
+ const startDrag = (ev) => {
1067
+ if (!props.selected) {
1068
+ select();
1069
+ }
1070
+ emit("start-drag", ev);
1071
+ };
1072
+ const openContextMenu = () => {
1073
+ showContextMenu.value = true;
1074
+ };
1075
+ const closeSidebar = () => {
1076
+ const sidebar = viewModel.value.displayedGraph.sidebar;
1077
+ sidebar.nodeId = "";
1078
+ sidebar.visible = false;
1079
+ };
1080
+ const openSidebar = () => {
1081
+ const sidebar = viewModel.value.displayedGraph.sidebar;
1082
+ sidebar.nodeId = props.node.id;
1083
+ sidebar.visible = true;
1084
+ };
1085
+ const updateSidebar = () => {
1086
+ const sidebar = viewModel.value.displayedGraph.sidebar;
1087
+ sidebar.nodeId = props.node.id;
1088
+ };
1089
+ const onContextMenuClick = async (action) => {
1090
+ switch (action) {
1091
+ case "edit":
1092
+ openSidebar();
1093
+ break;
1094
+ case "delete":
1095
+ graph.value.removeNode(props.node);
1096
+ break;
1097
+ case "rename":
1098
+ tempName.value = props.node.title;
1099
+ renaming.value = true;
1100
+ await vue.nextTick();
1101
+ renameInputEl.value?.focus();
1102
+ break;
1103
+ case "editSubgraph":
1104
+ switchGraph(props.node.template);
1105
+ break;
1106
+ }
1107
+ };
1108
+ const doneRenaming = () => {
1109
+ props.node.title = tempName.value;
1110
+ renaming.value = false;
1111
+ };
1112
+ const onRender = () => {
1113
+ if (el.value) {
1114
+ viewModel.value.hooks.renderNode.execute({ node: props.node, el: el.value });
1115
+ }
1116
+ };
1117
+ const startResize = (ev) => {
1118
+ isResizing.value = true;
1119
+ resizeStartWidth = props.node.width;
1120
+ resizeStartMouseX = ev.clientX;
1121
+ ev.preventDefault();
1122
+ };
1123
+ const doResize = (ev) => {
1124
+ if (!isResizing.value) return;
1125
+ const deltaX = ev.clientX - resizeStartMouseX;
1126
+ const newWidth = resizeStartWidth + deltaX / graph.value.scaling;
1127
+ const minWidth = viewModel.value.settings.nodes.minWidth;
1128
+ const maxWidth = viewModel.value.settings.nodes.maxWidth;
1129
+ props.node.width = Math.max(minWidth, Math.min(maxWidth, newWidth));
1130
+ };
1131
+ const stopResize = () => {
1132
+ isResizing.value = false;
1133
+ };
1134
+ vue.onMounted(() => {
1135
+ onRender();
1136
+ window.addEventListener("mousemove", doResize);
1137
+ window.addEventListener("mouseup", stopResize);
1138
+ });
1139
+ vue.onUpdated(onRender);
1140
+ vue.onBeforeUnmount(() => {
1141
+ window.removeEventListener("mousemove", doResize);
1142
+ window.removeEventListener("mouseup", stopResize);
1143
+ });
1144
+ return (_ctx, _cache) => {
1145
+ return vue.openBlock(), vue.createElementBlock("div", {
1146
+ id: node.value.id,
1147
+ ref_key: "el",
1148
+ ref: el,
1149
+ class: vue.normalizeClass([classes.value, "baklava-node"]),
1150
+ "data-node-type": node.value.type,
1151
+ style: vue.normalizeStyle(styles.value),
1152
+ onPointerdown: select
1153
+ }, [
1154
+ vue.unref(viewModel).settings.nodes.resizable ? (vue.openBlock(), vue.createElementBlock("div", {
1155
+ key: 0,
1156
+ class: "__resize-handle",
1157
+ onMousedown: startResize
1158
+ }, null, 32)) : vue.createCommentVNode("", true),
1159
+ vue.createElementVNode("div", {
1160
+ class: "__title",
1161
+ onPointerdown: vue.withModifiers(startDrag, ["self", "stop"]),
1162
+ onContextmenu: vue.withModifiers(openContextMenu, ["prevent"])
1163
+ }, [
1164
+ node.value.inputs._node ? (vue.openBlock(), vue.createBlock(_sfc_main$f, {
1165
+ key: 0,
1166
+ node: node.value,
1167
+ intf: node.value.inputs._node,
1168
+ class: "--input",
1169
+ "data-interface-type": "node",
1170
+ style: { "flex-grow": "0" }
1171
+ }, null, 8, ["node", "intf"])) : vue.createCommentVNode("", true),
1172
+ !renaming.value ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 1 }, [
1173
+ vue.createElementVNode("div", _hoisted_2$4, [
1174
+ node.value.idx > -1 ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_3$2, vue.toDisplayString(node.value.idx + 1) + " - ", 1)) : vue.createCommentVNode("", true),
1175
+ vue.createTextVNode(vue.toDisplayString(node.value.title) + " (" + vue.toDisplayString(node.value.shortId) + ") ", 1)
1176
+ ]),
1177
+ vue.createElementVNode("div", _hoisted_4$1, [
1178
+ !node.value.subgraph ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
1179
+ !vue.unref(viewModel).displayedGraph.sidebar.visible && vue.unref(viewModel).displayedGraph.sidebar.nodeId !== node.value.id ? (vue.openBlock(), vue.createBlock(vue.unref(LayoutSidebarRightExpand), {
1180
+ key: 0,
1181
+ class: "--clickable mx-1",
1182
+ onClick: openSidebar
1183
+ })) : vue.unref(viewModel).displayedGraph.sidebar.visible && vue.unref(viewModel).displayedGraph.sidebar.nodeId !== node.value.id ? (vue.openBlock(), vue.createBlock(vue.unref(LayoutSidebarRight), {
1184
+ key: 1,
1185
+ class: "--clickable mx-1",
1186
+ onClick: updateSidebar
1187
+ })) : (vue.openBlock(), vue.createBlock(vue.unref(LayoutSidebarRightCollapse), {
1188
+ key: 2,
1189
+ class: "--clickable mx-1",
1190
+ onClick: closeSidebar
1191
+ }))
1192
+ ], 64)) : vue.createCommentVNode("", true),
1193
+ vue.createVNode(vue.unref(VerticalDots), {
1194
+ class: "--clickable mx-1",
1195
+ onClick: openContextMenu
1196
+ }),
1197
+ vue.createVNode(vue.unref(ContextMenu), {
1198
+ modelValue: showContextMenu.value,
1199
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => showContextMenu.value = $event),
1200
+ x: 0,
1201
+ y: 0,
1202
+ items: contextMenuItems.value,
1203
+ onClick: onContextMenuClick
1204
+ }, null, 8, ["modelValue", "items"])
1205
+ ])
1206
+ ], 64)) : vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", {
1207
+ key: 2,
1208
+ ref_key: "renameInputEl",
1209
+ ref: renameInputEl,
1210
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => tempName.value = $event),
1211
+ class: "baklava-input",
1212
+ placeholder: "Node Name",
1213
+ style: { "flex-grow": "1" },
1214
+ type: "text",
1215
+ onBlur: doneRenaming,
1216
+ onKeydown: vue.withKeys(doneRenaming, ["enter"])
1217
+ }, null, 544)), [
1218
+ [vue.vModelText, tempName.value]
1219
+ ]),
1220
+ node.value.outputs._node ? (vue.openBlock(), vue.createBlock(_sfc_main$f, {
1221
+ key: 3,
1222
+ node: node.value,
1223
+ intf: node.value.outputs._node,
1224
+ class: "--output",
1225
+ "data-interface-type": "node"
1226
+ }, null, 8, ["node", "intf"])) : vue.createCommentVNode("", true)
1227
+ ], 32),
1228
+ vue.createElementVNode("div", {
1229
+ class: vue.normalizeClass(["__content", classesContent.value]),
1230
+ onKeydown: _cache[2] || (_cache[2] = vue.withKeys(vue.withModifiers(() => {
1231
+ }, ["stop"]), ["delete"])),
1232
+ onContextmenu: _cache[3] || (_cache[3] = vue.withModifiers(() => {
1233
+ }, ["prevent"]))
1234
+ }, [
1235
+ vue.createElementVNode("div", _hoisted_5, [
1236
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(displayedOutputs.value, (output) => {
1237
+ return vue.openBlock(), vue.createElementBlock(vue.Fragment, {
1238
+ key: output.id
1239
+ }, [
1240
+ node.value.state?.hidden ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_6, [
1241
+ output.port ? (vue.openBlock(), vue.createElementBlock("div", {
1242
+ key: 0,
1243
+ id: output.id,
1244
+ title: output.name,
1245
+ class: "baklava-node-interface --output --connected"
1246
+ }, [..._cache[4] || (_cache[4] = [
1247
+ vue.createElementVNode("div", { class: "__port" }, null, -1)
1248
+ ])], 8, _hoisted_7)) : vue.createCommentVNode("", true)
1249
+ ])) : vue.renderSlot(_ctx.$slots, "nodeInterface", {
1250
+ key: 1,
1251
+ type: "output",
1252
+ node: node.value,
1253
+ intf: output
1254
+ }, () => [
1255
+ vue.createVNode(vue.unref(NodeInterface), {
1256
+ node: node.value,
1257
+ intf: output
1258
+ }, null, 8, ["node", "intf"])
1259
+ ])
1260
+ ], 64);
1261
+ }), 128))
1262
+ ]),
1263
+ vue.createElementVNode("div", _hoisted_8, [
1264
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(displayedInputs.value, (input) => {
1265
+ return vue.openBlock(), vue.createElementBlock(vue.Fragment, {
1266
+ key: input.id
1267
+ }, [
1268
+ node.value.state?.hidden ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9, [
1269
+ input.port ? (vue.openBlock(), vue.createElementBlock("div", {
1270
+ key: 0,
1271
+ id: input.id,
1272
+ title: input.name,
1273
+ class: "baklava-node-interface --input --connected"
1274
+ }, [..._cache[5] || (_cache[5] = [
1275
+ vue.createElementVNode("div", { class: "__port" }, null, -1)
1276
+ ])], 8, _hoisted_10)) : vue.createCommentVNode("", true)
1277
+ ])) : vue.renderSlot(_ctx.$slots, "nodeInterface", {
1278
+ key: 1,
1279
+ node: node.value,
1280
+ intf: input,
1281
+ type: "input"
1282
+ }, () => [
1283
+ vue.createVNode(vue.unref(NodeInterface), {
1284
+ node: node.value,
1285
+ intf: input,
1286
+ title: input.name
1287
+ }, null, 8, ["node", "intf", "title"])
1288
+ ])
1289
+ ], 64);
1290
+ }), 128))
1291
+ ])
1292
+ ], 34)
1293
+ ], 46, _hoisted_1$4);
1294
+ };
1295
+ }
1296
+ });
1297
+ const _hoisted_1$3 = ["title"];
1298
+ const _hoisted_2$3 = {
1299
+ key: 0,
1300
+ class: "__label"
1301
+ };
1302
+ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
1303
+ __name: "Checkbox",
1304
+ props: {
1305
+ modelValue: { type: Boolean },
1306
+ inversed: { type: Boolean },
1307
+ name: {}
1308
+ },
1309
+ emits: ["update:modelValue"],
1310
+ setup(__props, { emit: __emit }) {
1311
+ const emit = __emit;
1312
+ return (_ctx, _cache) => {
1313
+ return vue.openBlock(), vue.createElementBlock("div", {
1314
+ class: vue.normalizeClass(["baklava-checkbox", { "--checked": _ctx.inversed ? !_ctx.modelValue : _ctx.modelValue }]),
1315
+ title: _ctx.name,
1316
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("update:modelValue", !_ctx.modelValue))
1317
+ }, [
1318
+ _cache[1] || (_cache[1] = vue.createElementVNode("div", { class: "__checkmark-container" }, [
1319
+ vue.createElementVNode("svg", {
1320
+ xmlns: "http://www.w3.org/2000/svg",
1321
+ width: "18",
1322
+ height: "18",
1323
+ viewBox: "0 0 18 18"
1324
+ }, [
1325
+ vue.createElementVNode("path", {
1326
+ class: "__checkmark",
1327
+ d: "M 6 5 L 6 10 L 16 10",
1328
+ transform: "rotate(-45 10 10)"
1329
+ })
1330
+ ])
1331
+ ], -1)),
1332
+ _ctx.name ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$3, vue.toDisplayString(_ctx.name), 1)) : vue.createCommentVNode("", true)
1333
+ ], 10, _hoisted_1$3);
1334
+ };
1335
+ }
1336
+ });
1337
+ const _hoisted_1$2 = { class: "__header" };
1338
+ const _hoisted_2$2 = { class: "__node-name" };
1339
+ const _hoisted_3$1 = { style: { "display": "flex" } };
1340
+ const _hoisted_4 = {
1341
+ key: 1,
1342
+ class: "__interface"
1343
+ };
1344
+ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
1345
+ __name: "CodeGraphSidebar",
1346
+ setup(__props) {
1347
+ const { viewModel } = baklavajs.useViewModel();
1348
+ const { graph } = baklavajs.useGraph();
1349
+ const el = vue.ref(null);
1350
+ const width = vue.toRef(viewModel.value.settings.sidebar, "width");
1351
+ const resizable = vue.computed(() => viewModel.value.settings.sidebar.resizable);
1352
+ let resizeStartWidth = 0;
1353
+ let resizeStartMouseX = 0;
1354
+ const node = vue.computed(() => {
1355
+ const id = graph.value.sidebar.nodeId;
1356
+ return graph.value.nodes.find((x) => x.id === id);
1357
+ });
1358
+ const codeNode = vue.computed(() => node.value);
1359
+ const styles = vue.computed(() => ({
1360
+ width: `${width.value}px`
1361
+ }));
1362
+ const displayedInterfaces = vue.computed(() => {
1363
+ if (!node.value) return [];
1364
+ const allIntfs = [...Object.values(node.value.inputs), ...Object.values(node.value.outputs)];
1365
+ return allIntfs.filter((intf) => intf.displayInSidebar && intf.component);
1366
+ });
1367
+ const close = () => {
1368
+ graph.value.sidebar.visible = false;
1369
+ };
1370
+ const doneRenaming = () => {
1371
+ node.value?.events.update.emit(null);
1372
+ };
1373
+ const startResize = (event) => {
1374
+ resizeStartWidth = width.value;
1375
+ resizeStartMouseX = event.clientX;
1376
+ window.addEventListener("mousemove", onMouseMove);
1377
+ window.addEventListener(
1378
+ "mouseup",
1379
+ () => {
1380
+ window.removeEventListener("mousemove", onMouseMove);
1381
+ },
1382
+ { once: true }
1383
+ );
1384
+ };
1385
+ const onMouseMove = (event) => {
1386
+ const maxwidth = el.value?.parentElement?.getBoundingClientRect().width ?? 500;
1387
+ const deltaX = event.clientX - resizeStartMouseX;
1388
+ let newWidth = resizeStartWidth - deltaX;
1389
+ if (newWidth < 300) {
1390
+ newWidth = 300;
1391
+ } else if (newWidth > 0.9 * maxwidth) {
1392
+ newWidth = 0.9 * maxwidth;
1393
+ }
1394
+ width.value = newWidth;
1395
+ };
1396
+ return (_ctx, _cache) => {
1397
+ return vue.openBlock(), vue.createElementBlock("div", {
1398
+ ref_key: "el",
1399
+ ref: el,
1400
+ class: vue.normalizeClass(["baklava-sidebar", { "--open": vue.unref(graph).sidebar.visible }]),
1401
+ style: vue.normalizeStyle(styles.value)
1402
+ }, [
1403
+ resizable.value ? (vue.openBlock(), vue.createElementBlock("div", {
1404
+ key: 0,
1405
+ class: "__resizer",
1406
+ onMousedown: startResize
1407
+ }, null, 32)) : vue.createCommentVNode("", true),
1408
+ vue.createElementVNode("div", _hoisted_1$2, [
1409
+ vue.createElementVNode("button", {
1410
+ tabindex: "-1",
1411
+ class: "__close",
1412
+ onClick: close
1413
+ }, "×"),
1414
+ vue.createElementVNode("div", _hoisted_2$2, [
1415
+ vue.createElementVNode("b", null, vue.toDisplayString(node.value ? node.value.title : ""), 1)
1416
+ ])
1417
+ ]),
1418
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(displayedInterfaces.value, (intf) => {
1419
+ return vue.openBlock(), vue.createElementBlock("div", {
1420
+ key: intf.id,
1421
+ class: "__interface"
1422
+ }, [
1423
+ vue.createElementVNode("div", _hoisted_3$1, [
1424
+ vue.createVNode(_sfc_main$4, {
1425
+ modelValue: intf.hidden,
1426
+ "onUpdate:modelValue": [
1427
+ ($event) => intf.hidden = $event,
1428
+ _cache[0] || (_cache[0] = () => node.value?.events.update.emit(null))
1429
+ ],
1430
+ inversed: "",
1431
+ style: { "margin-right": "8px" }
1432
+ }, null, 8, ["modelValue", "onUpdate:modelValue"]),
1433
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(intf.component), {
1434
+ modelValue: intf.value,
1435
+ "onUpdate:modelValue": ($event) => intf.value = $event,
1436
+ node: node.value,
1437
+ intf,
1438
+ style: { "width": "100%" }
1439
+ }, null, 8, ["modelValue", "onUpdate:modelValue", "node", "intf"]))
1440
+ ])
1441
+ ]);
1442
+ }), 128)),
1443
+ codeNode.value && codeNode.value.state ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [
1444
+ _cache[2] || (_cache[2] = vue.createElementVNode("label", null, "Variable name", -1)),
1445
+ vue.withDirectives(vue.createElementVNode("input", {
1446
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => codeNode.value.state.variableName = $event),
1447
+ type: "text",
1448
+ class: "baklava-input",
1449
+ title: "Variable name",
1450
+ onBlur: doneRenaming,
1451
+ onKeydown: vue.withKeys(doneRenaming, ["enter"])
1452
+ }, null, 544), [
1453
+ [vue.vModelText, codeNode.value.state.variableName]
1454
+ ])
1455
+ ])) : vue.createCommentVNode("", true)
1456
+ ], 6);
1457
+ };
1458
+ }
1459
+ });
1460
+ const _sfc_main$2 = vue.defineComponent({
1461
+ props: {
1462
+ type: {
1463
+ type: String,
1464
+ required: true
1465
+ },
1466
+ title: {
1467
+ type: String,
1468
+ required: true
1469
+ }
1470
+ },
1471
+ setup(props) {
1472
+ const { viewModel } = baklavajs.useViewModel();
1473
+ const { switchGraph } = baklavajs.useGraph();
1474
+ const showContextMenu = vue.ref(false);
1475
+ const hasContextMenu = vue.computed(() => props.type.startsWith(baklavajs.GRAPH_NODE_TYPE_PREFIX));
1476
+ const contextMenuItems = [
1477
+ { label: "Edit Subgraph", value: "editSubgraph" },
1478
+ { label: "Delete Subgraph", value: "deleteSubgraph" }
1479
+ ];
1480
+ const openContextMenu = () => {
1481
+ showContextMenu.value = true;
1482
+ };
1483
+ const onContextMenuClick = (action) => {
1484
+ const graphTemplateId = props.type.substring(baklavajs.GRAPH_NODE_TYPE_PREFIX.length);
1485
+ const graphTemplate = viewModel.value.editor.graphTemplates.find((gt) => gt.id === graphTemplateId);
1486
+ if (!graphTemplate) {
1487
+ return;
1488
+ }
1489
+ switch (action) {
1490
+ case "editSubgraph":
1491
+ switchGraph(graphTemplate);
1492
+ break;
1493
+ case "deleteSubgraph":
1494
+ viewModel.value.editor.removeGraphTemplate(graphTemplate);
1495
+ break;
1496
+ }
1497
+ };
1498
+ return { showContextMenu, hasContextMenu, contextMenuItems, openContextMenu, onContextMenuClick };
1499
+ }
1500
+ });
1501
+ const _hoisted_1$1 = ["data-node-type"];
1502
+ const _hoisted_2$1 = { class: "__title" };
1503
+ const _hoisted_3 = { class: "__title-label" };
1504
+ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
1505
+ return vue.openBlock(), vue.createElementBlock("div", {
1506
+ class: "baklava-node --palette",
1507
+ "data-node-type": _ctx.type
1508
+ }, [
1509
+ vue.createElementVNode("div", _hoisted_2$1, [
1510
+ vue.createElementVNode("div", _hoisted_3, vue.toDisplayString(_ctx.title), 1)
1511
+ ])
1512
+ ], 8, _hoisted_1$1);
1513
+ }
1514
+ const PaletteEntry = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render]]);
1515
+ const _hoisted_1 = {
1516
+ class: "baklava-node --palette",
1517
+ style: { "margin-top": "-20px", "margin-bottom": "30px" }
1518
+ };
1519
+ const _hoisted_2 = { key: 0 };
1520
+ const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
1521
+ __name: "CodeNodePalette",
1522
+ setup(__props) {
1523
+ const { viewModel } = baklavajs.useViewModel();
1524
+ const { x: mouseX, y: mouseY } = core.usePointer();
1525
+ const { transform } = baklavajs.useTransform();
1526
+ const categories = baklavajs.useNodeCategories(viewModel);
1527
+ const editorEl = vue.inject("editorEl");
1528
+ const searchQuery = vue.ref("");
1529
+ const draggedNode = vue.ref(null);
1530
+ const filterCategoryBySearch = (categories2) => {
1531
+ if (searchQuery.value) {
1532
+ return categories2.filter(
1533
+ (c) => Object.values(c.nodeTypes).some(
1534
+ (nodeType2) => nodeType2.title.toLowerCase().includes(searchQuery.value?.toLowerCase())
1535
+ )
1536
+ );
1537
+ }
1538
+ return categories2;
1539
+ };
1540
+ const filterNodesBySearch = (nodeTypes) => {
1541
+ if (searchQuery.value) {
1542
+ return Object.values(nodeTypes).filter(
1543
+ (nt) => nt.title.toLowerCase().includes(searchQuery.value?.toLowerCase())
1544
+ );
1545
+ }
1546
+ return Object.values(nodeTypes);
1547
+ };
1548
+ const draggedNodeStyles = vue.computed(() => {
1549
+ if (!draggedNode.value || !editorEl?.value) return {};
1550
+ const { left, top } = editorEl.value.getBoundingClientRect();
1551
+ return {
1552
+ top: `${mouseY.value - top}px`,
1553
+ left: `${mouseX.value - left}px`
1554
+ };
1555
+ });
1556
+ const onDragStart = (type, nodeInformation) => {
1557
+ draggedNode.value = {
1558
+ type,
1559
+ nodeInformation
1560
+ };
1561
+ const onDragEnd = () => {
1562
+ const instance = vue.reactive(new nodeInformation.type());
1563
+ viewModel.value.displayedGraph.addNode(instance);
1564
+ const rect = editorEl.value.getBoundingClientRect();
1565
+ const [x, y] = transform(mouseX.value - rect.left, mouseY.value - rect.top);
1566
+ instance.position.x = x;
1567
+ instance.position.y = y;
1568
+ draggedNode.value = null;
1569
+ document.removeEventListener("pointerup", onDragEnd);
1570
+ };
1571
+ document.addEventListener("pointerup", onDragEnd);
1572
+ };
1573
+ return (_ctx, _cache) => {
1574
+ return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
1575
+ vue.createElementVNode("div", {
1576
+ class: vue.normalizeClass(["baklava-node-palette", { "--open": vue.unref(viewModel).settings.palette.enabled }]),
1577
+ onContextmenu: _cache[1] || (_cache[1] = vue.withModifiers(() => {
1578
+ }, ["stop", "prevent"]))
1579
+ }, [
1580
+ vue.createElementVNode("div", _hoisted_1, [
1581
+ vue.withDirectives(vue.createElementVNode("input", {
1582
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
1583
+ type: "text",
1584
+ class: "baklava-input",
1585
+ title: "Filter node types"
1586
+ }, null, 512), [
1587
+ [vue.vModelText, searchQuery.value]
1588
+ ])
1589
+ ]),
1590
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(filterCategoryBySearch(vue.unref(categories)), (c) => {
1591
+ return vue.openBlock(), vue.createElementBlock("section", {
1592
+ key: c.name
1593
+ }, [
1594
+ c.name !== "default" ? (vue.openBlock(), vue.createElementBlock("h3", _hoisted_2, vue.toDisplayString(c.name), 1)) : vue.createCommentVNode("", true),
1595
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(filterNodesBySearch(c.nodeTypes), (ni, nt) => {
1596
+ return vue.openBlock(), vue.createBlock(PaletteEntry, {
1597
+ key: nt,
1598
+ type: nt,
1599
+ title: ni.title,
1600
+ onPointerdown: ($event) => onDragStart(nt, ni)
1601
+ }, null, 8, ["type", "title", "onPointerdown"]);
1602
+ }), 128))
1603
+ ]);
1604
+ }), 128))
1605
+ ], 34),
1606
+ vue.createVNode(vue.Transition, { name: "fade" }, {
1607
+ default: vue.withCtx(() => [
1608
+ draggedNode.value ? (vue.openBlock(), vue.createElementBlock("div", {
1609
+ key: 0,
1610
+ class: "baklava-dragged-node",
1611
+ style: vue.normalizeStyle(draggedNodeStyles.value)
1612
+ }, [
1613
+ vue.createVNode(PaletteEntry, {
1614
+ type: draggedNode.value.type,
1615
+ title: draggedNode.value.nodeInformation.title
1616
+ }, null, 8, ["type", "title"])
1617
+ ], 4)) : vue.createCommentVNode("", true)
1618
+ ]),
1619
+ _: 1
1620
+ })
1621
+ ], 64);
1622
+ };
1623
+ }
1624
+ });
1625
+ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
1626
+ __name: "CodeGraphEditor",
1627
+ props: {
1628
+ viewModel: {}
1629
+ },
1630
+ setup(__props) {
1631
+ const props = __props;
1632
+ const viewModel = vue.toRef(props, "viewModel");
1633
+ const onUpdate = (node) => node.events.update.emit(null);
1634
+ vue.onMounted(() => {
1635
+ viewModel.value.subscribe();
1636
+ viewModel.value.engine.start();
1637
+ });
1638
+ vue.onUnmounted(() => {
1639
+ viewModel.value.unsubscribe();
1640
+ viewModel.value.engine.stop();
1641
+ });
1642
+ return (_ctx, _cache) => {
1643
+ return vue.openBlock(), vue.createBlock(vue.unref(baklavajs.BaklavaEditor), { "view-model": viewModel.value }, {
1644
+ palette: vue.withCtx(() => [
1645
+ vue.createVNode(_sfc_main$1)
1646
+ ]),
1647
+ node: vue.withCtx((nodeProps) => [
1648
+ vue.createVNode(_sfc_main$5, vue.mergeProps(nodeProps, {
1649
+ onUpdate: ($event) => onUpdate(nodeProps.node)
1650
+ }), null, 16, ["onUpdate"])
1651
+ ]),
1652
+ sidebar: vue.withCtx((nodeProps) => [
1653
+ vue.createVNode(_sfc_main$3, vue.normalizeProps(vue.guardReactiveProps(nodeProps)), null, 16)
1654
+ ]),
1655
+ _: 1
1656
+ }, 8, ["view-model"]);
1657
+ };
1658
+ }
1659
+ });
1660
+ const addToolbarCommands = (viewModel) => {
1661
+ const TOGGLE_PALETTE_COMMAND = "TOGGLE_PALETTE";
1662
+ viewModel.commandHandler.registerCommand(TOGGLE_PALETTE_COMMAND, {
1663
+ execute: () => viewModel.settings.palette.enabled = !viewModel.settings.palette.enabled,
1664
+ canExecute: () => true
1665
+ });
1666
+ const CLEAR_ALL_COMMAND = "CLEAR_ALL";
1667
+ viewModel.commandHandler.registerCommand(CLEAR_ALL_COMMAND, {
1668
+ execute: () => viewModel.displayedGraph.nodes.forEach((node) => viewModel.displayedGraph.removeNode(node)),
1669
+ canExecute: () => viewModel.displayedGraph.nodes.length > 0
1670
+ });
1671
+ const TOGGLE_MINIMAP_COMMAND = "TOGGLE_MINIMAP";
1672
+ viewModel.commandHandler.registerCommand(TOGGLE_MINIMAP_COMMAND, {
1673
+ execute: () => viewModel.settings.enableMinimap = !viewModel.settings.enableMinimap,
1674
+ canExecute: () => viewModel.displayedGraph.nodes.length > 1
1675
+ });
1676
+ viewModel.settings.toolbar.commands = [
1677
+ {
1678
+ command: TOGGLE_PALETTE_COMMAND,
1679
+ title: "Toggle palette",
1680
+ // Tooltip text
1681
+ icon: vue.computed(() => viewModel.settings.palette.enabled ? LayoutSidebarLeftCollapse : LayoutSidebarLeftExpand)
1682
+ },
1683
+ ...baklavajs.DEFAULT_TOOLBAR_COMMANDS,
1684
+ {
1685
+ command: CLEAR_ALL_COMMAND,
1686
+ title: "Clear all",
1687
+ // Tooltip text
1688
+ icon: TrashOff
1689
+ },
1690
+ {
1691
+ command: TOGGLE_MINIMAP_COMMAND,
1692
+ title: "Toggle minimap",
1693
+ // Tooltip text
1694
+ icon: vue.computed(() => viewModel.settings.enableMinimap ? SchemaOff : Schema)
1695
+ }
1696
+ ];
1697
+ };
1698
+ const DEFAULT_SETTINGS = {
1699
+ enableMinimap: false,
1700
+ toolbar: {
1701
+ enabled: true
1702
+ },
1703
+ palette: {
1704
+ enabled: true
1705
+ },
1706
+ sidebar: {
1707
+ enabled: true,
1708
+ resizable: true,
1709
+ width: 350
1710
+ },
1711
+ displayValueOnHover: false
1712
+ };
1713
+ function useCodeGraph(existingEditor) {
1714
+ const viewModel = baklavajs.useBaklava(existingEditor);
1715
+ addToolbarCommands(viewModel);
1716
+ viewModel.code = new Code(viewModel);
1717
+ const settings = {};
1718
+ Object.keys(DEFAULT_SETTINGS).forEach((K) => {
1719
+ settings[K] = typeof DEFAULT_SETTINGS[K] === "object" ? { ...viewModel.settings[K], ...DEFAULT_SETTINGS[K] } : DEFAULT_SETTINGS[K];
1720
+ });
1721
+ viewModel.settings = vue.reactive({ ...viewModel.settings, ...settings });
1722
+ viewModel.settings.nodes.defaultWidth = 350;
1723
+ viewModel.state = vue.reactive({
1724
+ modules: {},
1725
+ token: null
1726
+ });
1727
+ viewModel.engine = new baklavajs.DependencyEngine(viewModel.editor);
1728
+ viewModel.subscribe = () => {
1729
+ if (viewModel.state.token) viewModel.unsubscribe();
1730
+ const token = Symbol();
1731
+ viewModel.displayedGraph.events.addNode.subscribe(token, (node) => node.code = viewModel.code);
1732
+ viewModel.engine.events.beforeRun.subscribe(token, () => {
1733
+ viewModel.engine.pause();
1734
+ viewModel.code.onCodeUpdate();
1735
+ viewModel.code.sortNodes();
1736
+ viewModel.code.updateOutputVariableNames();
1737
+ viewModel.engine.resume();
1738
+ });
1739
+ viewModel.engine.events.afterRun.subscribe(token, (result) => {
1740
+ viewModel.engine.pause();
1741
+ baklavajs.applyResult(result, viewModel.editor);
1742
+ transferCodeScript(viewModel.displayedGraph);
1743
+ viewModel.code.renderNodeCodes();
1744
+ viewModel.code.renderCode();
1745
+ viewModel.engine.resume();
1746
+ });
1747
+ viewModel.state.token = token;
1748
+ };
1749
+ viewModel.unsubscribe = () => {
1750
+ if (!viewModel.state.token) return;
1751
+ const token = viewModel.state.token;
1752
+ viewModel.displayedGraph.events.addNode.unsubscribe(token);
1753
+ viewModel.engine.events.beforeRun.unsubscribe(token);
1754
+ viewModel.engine.events.afterRun.unsubscribe(token);
1755
+ viewModel.state.token = null;
1756
+ };
1757
+ return viewModel;
1758
+ }
1759
+ Object.defineProperty(exports2, "ButtonInterfaceComponent", {
1760
+ enumerable: true,
1761
+ get: () => baklavajs.ButtonInterfaceComponent
1762
+ });
1763
+ Object.defineProperty(exports2, "CheckboxInterfaceComponent", {
1764
+ enumerable: true,
1765
+ get: () => baklavajs.CheckboxInterfaceComponent
1766
+ });
1767
+ Object.defineProperty(exports2, "IntegerInterfaceComponent", {
1768
+ enumerable: true,
1769
+ get: () => baklavajs.IntegerInterfaceComponent
1770
+ });
1771
+ Object.defineProperty(exports2, "NumberInterfaceComponent", {
1772
+ enumerable: true,
1773
+ get: () => baklavajs.NumberInterfaceComponent
1774
+ });
1775
+ Object.defineProperty(exports2, "SelectInterfaceComponent", {
1776
+ enumerable: true,
1777
+ get: () => baklavajs.SelectInterfaceComponent
1778
+ });
1779
+ Object.defineProperty(exports2, "SliderInterfaceComponent", {
1780
+ enumerable: true,
1781
+ get: () => baklavajs.SliderInterfaceComponent
1782
+ });
1783
+ Object.defineProperty(exports2, "TextInputInterfaceComponent", {
1784
+ enumerable: true,
1785
+ get: () => baklavajs.TextInputInterfaceComponent
1786
+ });
1787
+ Object.defineProperty(exports2, "TextareaInputInterfaceComponent", {
1788
+ enumerable: true,
1789
+ get: () => baklavajs.TextareaInputInterfaceComponent
1790
+ });
1791
+ exports2.AbstractCodeNode = AbstractCodeNode;
1792
+ exports2.ButtonInterface = ButtonInterface;
1793
+ exports2.CheckboxInterface = CheckboxInterface;
1794
+ exports2.Code = Code;
1795
+ exports2.CodeGraphEditor = _sfc_main;
1796
+ exports2.CodeInputInterface = CodeInputInterface;
1797
+ exports2.CodeNode = CodeNode;
1798
+ exports2.CodeNodeInterface = CodeNodeInterface;
1799
+ exports2.CodeOutputInterface = CodeOutputInterface;
1800
+ exports2.DEFAULT_SETTINGS = DEFAULT_SETTINGS;
1801
+ exports2.DynamicCodeNode = DynamicCodeNode;
1802
+ exports2.IntegerInterface = IntegerInterface;
1803
+ exports2.NumberInterface = NumberInterface;
1804
+ exports2.SelectInterface = SelectInterface;
1805
+ exports2.SliderInterface = SliderInterface;
1806
+ exports2.TextInputInterface = TextInputInterface;
1807
+ exports2.TextInterface = TextInterface;
1808
+ exports2.TextareaInputInterface = TextareaInputInterface;
1809
+ exports2.addToolbarCommands = addToolbarCommands;
1810
+ exports2.defineCodeNode = defineCodeNode;
1811
+ exports2.defineDynamicCodeNode = defineDynamicCodeNode;
1812
+ exports2.getCodeNodes = getCodeNodes;
1813
+ exports2.getPositionAtColumn = getPositionAtColumn;
1814
+ exports2.getPositionBeforeNode = getPositionBeforeNode;
1815
+ exports2.loadNodeState = loadNodeState;
1816
+ exports2.saveNodeState = saveNodeState;
1817
+ exports2.transferCodeScript = transferCodeScript;
1818
+ exports2.useCodeGraph = useCodeGraph;
1819
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
1820
+ }));