@bian-womp/spark-workbench 0.1.28 → 0.1.30
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/lib/cjs/index.cjs +324 -246
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/index.d.ts +3 -1
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts +3 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts +8 -5
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +2 -2
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts +2 -2
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/hooks.d.ts +2 -2
- package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +29 -0
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -0
- package/lib/cjs/src/runtime/{GraphRunner.d.ts → IGraphRunner.d.ts} +21 -31
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -0
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +16 -0
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -0
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +27 -0
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -0
- package/lib/esm/index.js +325 -248
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/index.d.ts +3 -1
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts +3 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts +8 -5
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +2 -2
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts +2 -2
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/hooks.d.ts +2 -2
- package/lib/esm/src/misc/hooks.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +29 -0
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -0
- package/lib/esm/src/runtime/{GraphRunner.d.ts → IGraphRunner.d.ts} +21 -31
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -0
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +16 -0
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -0
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +27 -0
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -0
- package/package.json +4 -4
- package/lib/cjs/src/runtime/GraphRunner.d.ts.map +0 -1
- package/lib/esm/src/runtime/GraphRunner.d.ts.map +0 -1
package/lib/esm/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { GraphBuilder, StepEngine, HybridEngine, PullEngine, BatchedEngine, PushEngine, isTypedOutput, getTypedOutputValue, getTypedOutputTypeId, isInputPrivate, getInputTypeId, createSimpleGraphRegistry, createSimpleGraphDef, createAsyncGraphDef, createAsyncGraphRegistry, createProgressGraphDef, createProgressGraphRegistry, createValidationGraphDef, createValidationGraphRegistry, Registry } from '@bian-womp/spark-graph';
|
|
2
|
-
import {
|
|
2
|
+
import { HttpPollingTransport, WebSocketTransport, RemoteRunner } from '@bian-womp/spark-remote';
|
|
3
3
|
import React, { useCallback, useState, useEffect, useMemo, createContext, useContext, useRef, useImperativeHandle } from 'react';
|
|
4
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
5
|
import { XCircleIcon, WarningCircleIcon, PlugsConnectedIcon, ClockClockwiseIcon, WifiHighIcon, WifiSlashIcon } from '@phosphor-icons/react';
|
|
6
|
-
import ReactFlow, { Handle, Position, useReactFlow,
|
|
6
|
+
import ReactFlow, { Handle, Position, useReactFlow, Background, MiniMap, Controls } from 'reactflow';
|
|
7
7
|
import cx from 'classnames';
|
|
8
8
|
|
|
9
9
|
class DefaultUIExtensionRegistry {
|
|
@@ -317,104 +317,295 @@ class CLIWorkbench {
|
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
class
|
|
320
|
+
class AbstractGraphRunner {
|
|
321
321
|
constructor(registry, backend) {
|
|
322
322
|
this.registry = registry;
|
|
323
|
+
this.backend = backend;
|
|
323
324
|
this.listeners = new Map();
|
|
324
325
|
this.stagedInputs = {};
|
|
325
|
-
this.backend = { kind: "local" };
|
|
326
|
-
if (backend)
|
|
327
|
-
this.backend = backend;
|
|
328
|
-
// Emit initial transport status
|
|
329
|
-
if (this.backend.kind === "local")
|
|
330
|
-
this.emit("transport", { state: "local" });
|
|
331
326
|
}
|
|
332
|
-
|
|
333
|
-
if (this.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
327
|
+
launch(def, opts) {
|
|
328
|
+
if (this.engine) {
|
|
329
|
+
throw new Error("Engine already running. Stop the current engine first.");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
setInput(nodeId, handle, value) {
|
|
333
|
+
if (!this.stagedInputs[nodeId])
|
|
334
|
+
this.stagedInputs[nodeId] = {};
|
|
335
|
+
this.stagedInputs[nodeId][handle] = value;
|
|
336
|
+
if (this.engine) {
|
|
337
|
+
this.engine.setInput(nodeId, handle, value);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Emit a value event so UI updates even when engine isn't running
|
|
341
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Batch update multiple inputs on a node and trigger a single run
|
|
345
|
+
setInputs(nodeId, inputs) {
|
|
346
|
+
if (!inputs)
|
|
338
347
|
return;
|
|
348
|
+
if (!this.stagedInputs[nodeId])
|
|
349
|
+
this.stagedInputs[nodeId] = {};
|
|
350
|
+
Object.assign(this.stagedInputs[nodeId], inputs);
|
|
351
|
+
if (this.engine) {
|
|
352
|
+
// Running: set all inputs
|
|
353
|
+
this.engine.setInputs(nodeId, inputs);
|
|
339
354
|
}
|
|
340
|
-
|
|
355
|
+
else {
|
|
356
|
+
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
357
|
+
console.warn("Engine does not exists");
|
|
358
|
+
for (const [handle, value] of Object.entries(inputs)) {
|
|
359
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async whenIdle() {
|
|
364
|
+
await this.engine?.whenIdle();
|
|
365
|
+
}
|
|
366
|
+
on(event, handler) {
|
|
367
|
+
if (!this.listeners.has(event))
|
|
368
|
+
this.listeners.set(event, new Set());
|
|
369
|
+
const set = this.listeners.get(event);
|
|
370
|
+
set.add(handler);
|
|
371
|
+
return () => set.delete(handler);
|
|
372
|
+
}
|
|
373
|
+
emit(event, payload) {
|
|
374
|
+
const set = this.listeners.get(event);
|
|
375
|
+
if (set)
|
|
376
|
+
for (const h of Array.from(set))
|
|
377
|
+
h(payload);
|
|
378
|
+
}
|
|
379
|
+
dispose() {
|
|
380
|
+
this.engine?.dispose();
|
|
381
|
+
this.engine = undefined;
|
|
382
|
+
this.runtime?.dispose();
|
|
383
|
+
this.runtime = undefined;
|
|
384
|
+
if (this.runningKind) {
|
|
385
|
+
this.runningKind = undefined;
|
|
386
|
+
this.emit("status", { running: false, engine: undefined });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
isRunning() {
|
|
390
|
+
return !!this.engine;
|
|
391
|
+
}
|
|
392
|
+
getRunningEngine() {
|
|
393
|
+
return this.runningKind;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
class LocalGraphRunner extends AbstractGraphRunner {
|
|
398
|
+
constructor(registry) {
|
|
399
|
+
super(registry, { kind: "local" });
|
|
400
|
+
this.emit("transport", { state: "local" });
|
|
401
|
+
}
|
|
402
|
+
build(def) {
|
|
403
|
+
const builder = new GraphBuilder(this.registry);
|
|
404
|
+
this.runtime = builder.build(def);
|
|
405
|
+
// Signal UI that freshly built graph should be considered invalidated
|
|
406
|
+
this.emit("invalidate", { reason: "graph-built" });
|
|
341
407
|
}
|
|
342
408
|
update(def) {
|
|
343
|
-
if (this.
|
|
344
|
-
if (!this.runtime)
|
|
345
|
-
return;
|
|
346
|
-
// Prevent mid-run churn while wiring changes are applied
|
|
347
|
-
this.runtime.pause();
|
|
348
|
-
this.runtime.update(def, this.registry);
|
|
349
|
-
this.runtime.resume();
|
|
350
|
-
this.emit("invalidate", { reason: "graph-updated" });
|
|
409
|
+
if (!this.runtime)
|
|
351
410
|
return;
|
|
411
|
+
// Prevent mid-run churn while wiring changes are applied
|
|
412
|
+
this.runtime.pause();
|
|
413
|
+
this.runtime.update(def, this.registry);
|
|
414
|
+
this.runtime.resume();
|
|
415
|
+
this.emit("invalidate", { reason: "graph-updated" });
|
|
416
|
+
}
|
|
417
|
+
launch(def, opts) {
|
|
418
|
+
super.launch(def, opts);
|
|
419
|
+
this.build(def);
|
|
420
|
+
if (!this.runtime)
|
|
421
|
+
throw new Error("Runtime not built");
|
|
422
|
+
const rt = this.runtime;
|
|
423
|
+
switch (opts.engine) {
|
|
424
|
+
case "push":
|
|
425
|
+
this.engine = new PushEngine(rt);
|
|
426
|
+
break;
|
|
427
|
+
case "batched":
|
|
428
|
+
this.engine = new BatchedEngine(rt, {
|
|
429
|
+
flushIntervalMs: opts.batched?.flushIntervalMs ?? 0,
|
|
430
|
+
});
|
|
431
|
+
break;
|
|
432
|
+
case "pull":
|
|
433
|
+
this.engine = new PullEngine(rt);
|
|
434
|
+
break;
|
|
435
|
+
case "hybrid":
|
|
436
|
+
this.engine = new HybridEngine(rt, {
|
|
437
|
+
windowMs: opts.hybrid?.windowMs ?? 250,
|
|
438
|
+
batchThreshold: opts.hybrid?.batchThreshold ?? 3,
|
|
439
|
+
});
|
|
440
|
+
break;
|
|
441
|
+
case "step":
|
|
442
|
+
this.engine = new StepEngine(rt);
|
|
443
|
+
break;
|
|
444
|
+
default:
|
|
445
|
+
throw new Error("Unknown engine kind");
|
|
352
446
|
}
|
|
447
|
+
this.engine.on("value", (e) => this.emit("value", e));
|
|
448
|
+
this.engine.on("error", (e) => this.emit("error", e));
|
|
449
|
+
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
450
|
+
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
451
|
+
this.engine.launch();
|
|
452
|
+
this.runningKind = opts.engine;
|
|
453
|
+
this.emit("status", { running: true, engine: this.runningKind });
|
|
454
|
+
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
455
|
+
this.engine.setInputs(nodeId, map);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
async step() {
|
|
459
|
+
const eng = this.engine;
|
|
460
|
+
if (eng instanceof StepEngine)
|
|
461
|
+
await eng.step();
|
|
462
|
+
}
|
|
463
|
+
async computeNode(nodeId) {
|
|
464
|
+
const eng = this.engine;
|
|
465
|
+
if (eng instanceof PullEngine)
|
|
466
|
+
await eng.computeNode(nodeId);
|
|
467
|
+
}
|
|
468
|
+
flush() {
|
|
469
|
+
const eng = this.engine;
|
|
470
|
+
if (eng instanceof BatchedEngine)
|
|
471
|
+
eng.flush();
|
|
472
|
+
}
|
|
473
|
+
getOutputs(def) {
|
|
474
|
+
const out = {};
|
|
475
|
+
if (!this.runtime)
|
|
476
|
+
return out;
|
|
477
|
+
for (const n of def.nodes) {
|
|
478
|
+
const desc = this.registry.nodes.get(n.typeId);
|
|
479
|
+
const handles = Object.keys(desc?.outputs ?? {});
|
|
480
|
+
for (const h of handles) {
|
|
481
|
+
const v = this.runtime.getOutput(n.nodeId, h);
|
|
482
|
+
if (v !== undefined) {
|
|
483
|
+
if (!out[n.nodeId])
|
|
484
|
+
out[n.nodeId] = {};
|
|
485
|
+
out[n.nodeId][h] = v;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return out;
|
|
490
|
+
}
|
|
491
|
+
getInputs(def) {
|
|
492
|
+
const out = {};
|
|
493
|
+
for (const n of def.nodes) {
|
|
494
|
+
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
495
|
+
const runtimeInputs = this.runtime
|
|
496
|
+
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
497
|
+
: {};
|
|
498
|
+
if (this.isRunning()) {
|
|
499
|
+
out[n.nodeId] = runtimeInputs;
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
const merged = { ...runtimeInputs, ...staged };
|
|
503
|
+
if (Object.keys(merged).length > 0)
|
|
504
|
+
out[n.nodeId] = merged;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return out;
|
|
508
|
+
}
|
|
509
|
+
dispose() {
|
|
510
|
+
super.dispose();
|
|
511
|
+
this.runtime = undefined;
|
|
512
|
+
this.emit("transport", { state: "local" });
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
class RemoteGraphRunner extends AbstractGraphRunner {
|
|
517
|
+
constructor(registry, backend) {
|
|
518
|
+
super(registry, backend);
|
|
519
|
+
this.valueCache = new Map();
|
|
520
|
+
this.listenersBound = false;
|
|
521
|
+
// Auto-handle registry-changed invalidations from remote
|
|
522
|
+
// We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
|
|
523
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
524
|
+
const eng = runner.getEngine();
|
|
525
|
+
if (!this.listenersBound) {
|
|
526
|
+
eng.on("invalidate", async (e) => {
|
|
527
|
+
if (e.reason === "registry-changed") {
|
|
528
|
+
try {
|
|
529
|
+
const deltas = Array.isArray(e.deltas) ? e.deltas : [];
|
|
530
|
+
for (const d of deltas) {
|
|
531
|
+
if (!d || typeof d !== "object")
|
|
532
|
+
continue;
|
|
533
|
+
if (d.kind === "register-enum") {
|
|
534
|
+
this.registry.registerEnum({
|
|
535
|
+
id: d.id,
|
|
536
|
+
displayName: d.displayName,
|
|
537
|
+
options: d.options,
|
|
538
|
+
opts: d.opts,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
else if (d.kind === "register-type") {
|
|
542
|
+
if (!this.registry.types.has(d.id)) {
|
|
543
|
+
this.registry.registerType({
|
|
544
|
+
id: d.id,
|
|
545
|
+
displayName: d.displayName,
|
|
546
|
+
validate: (_v) => true,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
else if (d.kind === "register-node") {
|
|
551
|
+
if (!this.registry.nodes.has(d.desc?.id)) {
|
|
552
|
+
this.registry.registerNode({
|
|
553
|
+
id: String(d.desc?.id || ""),
|
|
554
|
+
categoryId: String(d.desc?.categoryId || "compute"),
|
|
555
|
+
displayName: d.desc?.displayName,
|
|
556
|
+
inputs: d.desc?.inputs || {},
|
|
557
|
+
outputs: d.desc?.outputs || {},
|
|
558
|
+
impl: () => { },
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
this.emit("registry", this.registry);
|
|
564
|
+
// Trigger update so validation/UI refreshes using last known graph
|
|
565
|
+
try {
|
|
566
|
+
if (this.lastDef)
|
|
567
|
+
this.update(this.lastDef);
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
console.error("Failed to update graph definition after registry changed");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
console.error("Failed to handle registry changed event");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
build(def) {
|
|
582
|
+
console.warn("Unsupported operation for remote runner");
|
|
583
|
+
}
|
|
584
|
+
update(def) {
|
|
353
585
|
// Remote: forward update; ignore errors (fire-and-forget)
|
|
354
|
-
|
|
586
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
355
587
|
try {
|
|
356
|
-
await
|
|
588
|
+
await runner.update(def);
|
|
357
589
|
this.emit("invalidate", { reason: "graph-updated" });
|
|
590
|
+
this.lastDef = def;
|
|
358
591
|
}
|
|
359
592
|
catch { }
|
|
360
593
|
});
|
|
361
594
|
}
|
|
362
595
|
launch(def, opts) {
|
|
363
|
-
|
|
364
|
-
throw new Error("Engine already running. Stop the current engine first.");
|
|
365
|
-
}
|
|
366
|
-
if (this.backend.kind === "local") {
|
|
367
|
-
this.build(def);
|
|
368
|
-
if (!this.runtime)
|
|
369
|
-
throw new Error("Runtime not built");
|
|
370
|
-
const rt = this.runtime;
|
|
371
|
-
switch (opts.engine) {
|
|
372
|
-
case "push":
|
|
373
|
-
this.engine = new PushEngine(rt);
|
|
374
|
-
break;
|
|
375
|
-
case "batched":
|
|
376
|
-
this.engine = new BatchedEngine(rt, {
|
|
377
|
-
flushIntervalMs: opts.batched?.flushIntervalMs ?? 0,
|
|
378
|
-
});
|
|
379
|
-
break;
|
|
380
|
-
case "pull":
|
|
381
|
-
this.engine = new PullEngine(rt);
|
|
382
|
-
break;
|
|
383
|
-
case "hybrid":
|
|
384
|
-
this.engine = new HybridEngine(rt, {
|
|
385
|
-
windowMs: opts.hybrid?.windowMs ?? 250,
|
|
386
|
-
batchThreshold: opts.hybrid?.batchThreshold ?? 3,
|
|
387
|
-
});
|
|
388
|
-
break;
|
|
389
|
-
case "step":
|
|
390
|
-
this.engine = new StepEngine(rt);
|
|
391
|
-
break;
|
|
392
|
-
default:
|
|
393
|
-
throw new Error("Unknown engine kind");
|
|
394
|
-
}
|
|
395
|
-
this.engine.on("value", (e) => this.emit("value", e));
|
|
396
|
-
this.engine.on("error", (e) => this.emit("error", e));
|
|
397
|
-
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
398
|
-
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
399
|
-
this.engine.launch();
|
|
400
|
-
this.runningKind = opts.engine;
|
|
401
|
-
this.emit("status", { running: true, engine: this.runningKind });
|
|
402
|
-
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
403
|
-
this.engine.setInputs(nodeId, map);
|
|
404
|
-
}
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
596
|
+
super.launch(def, opts);
|
|
407
597
|
// Remote: build remotely then launch
|
|
408
|
-
|
|
409
|
-
await
|
|
598
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
599
|
+
await runner.build(def);
|
|
410
600
|
// Signal UI after remote build as well
|
|
411
601
|
this.emit("invalidate", { reason: "graph-built" });
|
|
602
|
+
this.lastDef = def;
|
|
412
603
|
// Hydrate current remote inputs/outputs (including defaults) into cache
|
|
413
604
|
try {
|
|
414
|
-
const snap = await
|
|
605
|
+
const snap = await runner.snapshot();
|
|
415
606
|
for (const [nodeId, map] of Object.entries(snap.inputs || {})) {
|
|
416
607
|
for (const [handle, value] of Object.entries(map || {})) {
|
|
417
|
-
|
|
608
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
418
609
|
io: "input",
|
|
419
610
|
value,
|
|
420
611
|
});
|
|
@@ -423,7 +614,7 @@ class GraphRunner {
|
|
|
423
614
|
}
|
|
424
615
|
for (const [nodeId, map] of Object.entries(snap.outputs || {})) {
|
|
425
616
|
for (const [handle, value] of Object.entries(map || {})) {
|
|
426
|
-
|
|
617
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
427
618
|
io: "output",
|
|
428
619
|
value,
|
|
429
620
|
});
|
|
@@ -434,10 +625,10 @@ class GraphRunner {
|
|
|
434
625
|
catch {
|
|
435
626
|
console.error("Failed to hydrate remote inputs/outputs");
|
|
436
627
|
}
|
|
437
|
-
const eng =
|
|
438
|
-
if (!
|
|
628
|
+
const eng = runner.getEngine();
|
|
629
|
+
if (!this.listenersBound) {
|
|
439
630
|
eng.on("value", (e) => {
|
|
440
|
-
|
|
631
|
+
this.valueCache.set(`${e.nodeId}.${e.handle}`, {
|
|
441
632
|
io: e.io,
|
|
442
633
|
value: e.value,
|
|
443
634
|
runtimeTypeId: e.runtimeTypeId,
|
|
@@ -447,7 +638,7 @@ class GraphRunner {
|
|
|
447
638
|
eng.on("error", (e) => this.emit("error", e));
|
|
448
639
|
eng.on("invalidate", (e) => this.emit("invalidate", e));
|
|
449
640
|
eng.on("stats", (e) => this.emit("stats", e));
|
|
450
|
-
|
|
641
|
+
this.listenersBound = true;
|
|
451
642
|
}
|
|
452
643
|
this.engine = eng;
|
|
453
644
|
this.engine.launch();
|
|
@@ -458,85 +649,18 @@ class GraphRunner {
|
|
|
458
649
|
}
|
|
459
650
|
});
|
|
460
651
|
}
|
|
461
|
-
setInput(nodeId, handle, value) {
|
|
462
|
-
if (!this.stagedInputs[nodeId])
|
|
463
|
-
this.stagedInputs[nodeId] = {};
|
|
464
|
-
this.stagedInputs[nodeId][handle] = value;
|
|
465
|
-
if (this.engine) {
|
|
466
|
-
this.engine.setInput(nodeId, handle, value);
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
// Emit a value event so UI updates even when engine isn't running
|
|
470
|
-
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
// Batch update multiple inputs on a node and trigger a single run
|
|
474
|
-
setInputs(nodeId, inputs) {
|
|
475
|
-
if (!inputs)
|
|
476
|
-
return;
|
|
477
|
-
if (!this.stagedInputs[nodeId])
|
|
478
|
-
this.stagedInputs[nodeId] = {};
|
|
479
|
-
Object.assign(this.stagedInputs[nodeId], inputs);
|
|
480
|
-
// Local running: pause, set all inputs, resume, schedule a single recompute
|
|
481
|
-
if (this.backend.kind === "local" && this.engine && this.runtime) {
|
|
482
|
-
this.engine.setInputs(nodeId, inputs);
|
|
483
|
-
}
|
|
484
|
-
// Remote running: forward inputs individually (no batch API available)
|
|
485
|
-
else if (this.engine &&
|
|
486
|
-
this.backend.kind !== "local" &&
|
|
487
|
-
this.engine instanceof RemoteEngine) {
|
|
488
|
-
this.engine.setInputs(nodeId, inputs);
|
|
489
|
-
}
|
|
490
|
-
// Not running: emit value events so UI reflects staged values
|
|
491
|
-
else if (!this.engine) {
|
|
492
|
-
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
493
|
-
console.warn("Remote engine does not exists");
|
|
494
|
-
for (const [handle, value] of Object.entries(inputs)) {
|
|
495
|
-
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
652
|
async step() {
|
|
500
|
-
|
|
501
|
-
return; // unsupported remotely
|
|
502
|
-
const eng = this.engine;
|
|
503
|
-
if (eng instanceof StepEngine)
|
|
504
|
-
await eng.step();
|
|
653
|
+
console.warn("Unsupported operation for remote runner");
|
|
505
654
|
}
|
|
506
655
|
async computeNode(nodeId) {
|
|
507
|
-
|
|
508
|
-
return; // unsupported remotely
|
|
509
|
-
const eng = this.engine;
|
|
510
|
-
if (eng instanceof PullEngine)
|
|
511
|
-
await eng.computeNode(nodeId);
|
|
656
|
+
console.warn("Unsupported operation for remote runner");
|
|
512
657
|
}
|
|
513
658
|
flush() {
|
|
514
|
-
|
|
515
|
-
return; // unsupported remotely
|
|
516
|
-
const eng = this.engine;
|
|
517
|
-
if (eng instanceof BatchedEngine)
|
|
518
|
-
eng.flush();
|
|
659
|
+
console.warn("Unsupported operation for remote runner");
|
|
519
660
|
}
|
|
520
661
|
getOutputs(def) {
|
|
521
662
|
const out = {};
|
|
522
|
-
|
|
523
|
-
if (!this.runtime)
|
|
524
|
-
return out;
|
|
525
|
-
for (const n of def.nodes) {
|
|
526
|
-
const desc = this.registry.nodes.get(n.typeId);
|
|
527
|
-
const handles = Object.keys(desc?.outputs ?? {});
|
|
528
|
-
for (const h of handles) {
|
|
529
|
-
const v = this.runtime.getOutput(n.nodeId, h);
|
|
530
|
-
if (v !== undefined) {
|
|
531
|
-
if (!out[n.nodeId])
|
|
532
|
-
out[n.nodeId] = {};
|
|
533
|
-
out[n.nodeId][h] = v;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
return out;
|
|
538
|
-
}
|
|
539
|
-
const cache = this.remote?.valueCache;
|
|
663
|
+
const cache = this.valueCache;
|
|
540
664
|
if (!cache)
|
|
541
665
|
return out;
|
|
542
666
|
for (const n of def.nodes) {
|
|
@@ -556,31 +680,14 @@ class GraphRunner {
|
|
|
556
680
|
}
|
|
557
681
|
getInputs(def) {
|
|
558
682
|
const out = {};
|
|
559
|
-
|
|
560
|
-
for (const n of def.nodes) {
|
|
561
|
-
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
562
|
-
const runtimeInputs = this.runtime
|
|
563
|
-
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
564
|
-
: {};
|
|
565
|
-
if (this.isRunning()) {
|
|
566
|
-
out[n.nodeId] = runtimeInputs;
|
|
567
|
-
}
|
|
568
|
-
else {
|
|
569
|
-
const merged = { ...runtimeInputs, ...staged };
|
|
570
|
-
if (Object.keys(merged).length > 0)
|
|
571
|
-
out[n.nodeId] = merged;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return out;
|
|
575
|
-
}
|
|
576
|
-
const cache = this.remote?.valueCache;
|
|
683
|
+
const cache = this.valueCache;
|
|
577
684
|
for (const n of def.nodes) {
|
|
578
685
|
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
579
686
|
const desc = this.registry.nodes.get(n.typeId);
|
|
580
687
|
const handles = Object.keys(desc?.inputs ?? {});
|
|
581
688
|
const cur = {};
|
|
582
689
|
for (const h of handles) {
|
|
583
|
-
const rec = cache
|
|
690
|
+
const rec = cache.get(`${n.nodeId}.${h}`);
|
|
584
691
|
if (rec && rec.io === "input")
|
|
585
692
|
cur[h] = rec.value;
|
|
586
693
|
}
|
|
@@ -590,52 +697,21 @@ class GraphRunner {
|
|
|
590
697
|
}
|
|
591
698
|
return out;
|
|
592
699
|
}
|
|
593
|
-
async whenIdle() {
|
|
594
|
-
await this.engine?.whenIdle();
|
|
595
|
-
}
|
|
596
|
-
on(event, handler) {
|
|
597
|
-
if (!this.listeners.has(event))
|
|
598
|
-
this.listeners.set(event, new Set());
|
|
599
|
-
const set = this.listeners.get(event);
|
|
600
|
-
set.add(handler);
|
|
601
|
-
return () => set.delete(handler);
|
|
602
|
-
}
|
|
603
|
-
emit(event, payload) {
|
|
604
|
-
const set = this.listeners.get(event);
|
|
605
|
-
if (set)
|
|
606
|
-
for (const h of Array.from(set))
|
|
607
|
-
h(payload);
|
|
608
|
-
}
|
|
609
700
|
dispose() {
|
|
610
|
-
|
|
611
|
-
this.
|
|
612
|
-
this.
|
|
613
|
-
this.runtime = undefined;
|
|
614
|
-
this.remote = undefined;
|
|
615
|
-
if (this.runningKind) {
|
|
616
|
-
this.runningKind = undefined;
|
|
617
|
-
this.emit("status", { running: false, engine: undefined });
|
|
618
|
-
}
|
|
619
|
-
const kind = this.backend.kind === "local"
|
|
620
|
-
? undefined
|
|
621
|
-
: this.backend.kind;
|
|
701
|
+
super.dispose();
|
|
702
|
+
this.runner = undefined;
|
|
703
|
+
this.transport = undefined;
|
|
622
704
|
this.emit("transport", {
|
|
623
|
-
state:
|
|
624
|
-
kind,
|
|
705
|
+
state: "disconnected",
|
|
706
|
+
kind: this.backend.kind,
|
|
625
707
|
});
|
|
626
708
|
}
|
|
627
|
-
isRunning() {
|
|
628
|
-
return !!this.engine;
|
|
629
|
-
}
|
|
630
|
-
getRunningEngine() {
|
|
631
|
-
return this.runningKind;
|
|
632
|
-
}
|
|
633
709
|
// Ensure remote transport/runner
|
|
634
|
-
async
|
|
635
|
-
if (this.
|
|
636
|
-
return this.
|
|
710
|
+
async ensureRemoteRunner() {
|
|
711
|
+
if (this.runner)
|
|
712
|
+
return this.runner;
|
|
637
713
|
let transport;
|
|
638
|
-
const kind = this.backend.kind
|
|
714
|
+
const kind = this.backend.kind;
|
|
639
715
|
this.emit("transport", { state: "connecting", kind });
|
|
640
716
|
if (this.backend.kind === "remote-http") {
|
|
641
717
|
if (!HttpPollingTransport)
|
|
@@ -653,14 +729,12 @@ class GraphRunner {
|
|
|
653
729
|
throw new Error("Remote backend not configured");
|
|
654
730
|
}
|
|
655
731
|
const runner = new RemoteRunner(transport);
|
|
656
|
-
this.
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
listenersBound: false,
|
|
661
|
-
};
|
|
732
|
+
this.runner = runner;
|
|
733
|
+
this.transport = transport;
|
|
734
|
+
this.valueCache.clear();
|
|
735
|
+
this.listenersBound = false;
|
|
662
736
|
this.emit("transport", { state: "connected", kind });
|
|
663
|
-
return
|
|
737
|
+
return runner;
|
|
664
738
|
}
|
|
665
739
|
}
|
|
666
740
|
|
|
@@ -1333,6 +1407,23 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1333
1407
|
});
|
|
1334
1408
|
const off7 = wb.on("error", add("workbench", "error"));
|
|
1335
1409
|
wb.refreshValidation();
|
|
1410
|
+
// Registry updates: swap registry and refresh graph validation/UI
|
|
1411
|
+
const offReg = runner.on("registry", (newReg) => {
|
|
1412
|
+
try {
|
|
1413
|
+
setRegistry(newReg);
|
|
1414
|
+
wb.setRegistry(newReg);
|
|
1415
|
+
// Trigger a graph update so the UI revalidates with new types/enums/nodes
|
|
1416
|
+
try {
|
|
1417
|
+
runner.update(wb.export());
|
|
1418
|
+
}
|
|
1419
|
+
catch {
|
|
1420
|
+
console.error("Failed to update graph definition after registry changed");
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
catch {
|
|
1424
|
+
console.error("Failed to handle registry changed event");
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1336
1427
|
return () => {
|
|
1337
1428
|
off1();
|
|
1338
1429
|
off2();
|
|
@@ -1345,6 +1436,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1345
1436
|
off5b();
|
|
1346
1437
|
off6();
|
|
1347
1438
|
off7();
|
|
1439
|
+
offReg();
|
|
1348
1440
|
};
|
|
1349
1441
|
}, [runner, wb]);
|
|
1350
1442
|
// Push incremental updates into running engine without full reload
|
|
@@ -1526,7 +1618,7 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
1526
1618
|
return (jsxs("div", { className: "flex flex-col h-full min-h-0", children: [jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsx("div", { className: "font-semibold", children: "Events" }), jsxs("div", { className: "flex items-center gap-2", children: [jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsx("input", { type: "checkbox", checked: hideWorkbench, onChange: (e) => onHideWorkbenchChange?.(e.target.checked) }), jsx("span", { children: "Hide workbench" })] }), jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsx("input", { type: "checkbox", checked: autoScroll, onChange: (e) => onAutoScrollChange?.(e.target.checked) }), jsx("span", { children: "Auto scroll" })] }), jsx("button", { onClick: clearEvents, className: "text-xs px-2 py-0.5 border border-gray-300 rounded", children: "Clear" })] })] }), jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto text-[11px] leading-4 divide-y divide-gray-200", children: rows.map((ev, idx) => (jsxs("div", { className: "opacity-85 odd:bg-gray-50 px-2 py-1", children: [jsxs("div", { className: "flex items-baseline gap-2", children: [jsx("span", { className: "w-8 shrink-0 text-right text-gray-500 select-none", children: idx + 1 }), jsxs("span", { className: "text-gray-500", children: [new Date(ev.at).toLocaleTimeString(), " \u00B7 ", ev.source, ":", ev.type] })] }), jsx("pre", { className: "m-0 whitespace-pre-wrap ml-10", children: renderPayload(ev.payload) })] }, `${ev.at}:${idx}`))) })] }));
|
|
1527
1619
|
}
|
|
1528
1620
|
|
|
1529
|
-
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, setInput, }) {
|
|
1621
|
+
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, contextPanel, setInput, }) {
|
|
1530
1622
|
const safeToString = (typeId, value) => {
|
|
1531
1623
|
try {
|
|
1532
1624
|
if (typeof toString === "function") {
|
|
@@ -1615,7 +1707,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1615
1707
|
setOriginals(nextOriginals);
|
|
1616
1708
|
}, [selectedNodeId, selectedDesc, valuesTick]);
|
|
1617
1709
|
const widthClass = debug ? "w-[480px]" : "w-[320px]";
|
|
1618
|
-
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxs("div", { children: [selectedNode && (jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
1710
|
+
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [contextPanel && (jsx("div", { className: "mb-2", children: contextPanel })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxs("div", { children: [selectedNode && (jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
1619
1711
|
selectedNodeStatus.lastError) }))] })), jsxs("div", { className: "mb-2", children: [jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
1620
1712
|
const typeId = getInputTypeId(selectedDesc?.inputs, h);
|
|
1621
1713
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
@@ -1921,7 +2013,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
1921
2013
|
catch { }
|
|
1922
2014
|
},
|
|
1923
2015
|
}));
|
|
1924
|
-
const { onConnect, onNodesChange
|
|
2016
|
+
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, onSelectionChange, } = useWorkbenchBridge(wb);
|
|
1925
2017
|
const { nodeTypes, resolveNodeType } = useMemo(() => {
|
|
1926
2018
|
// Build nodeTypes map using UI extension registry
|
|
1927
2019
|
const ui = wb.getUI();
|
|
@@ -1942,7 +2034,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
1942
2034
|
return { nodeTypes: types, resolveNodeType: resolver };
|
|
1943
2035
|
// registry is stable; ui renderers expected to be set up before mount
|
|
1944
2036
|
}, [wb, registry]);
|
|
1945
|
-
const { nodes
|
|
2037
|
+
const { nodes, edges } = useMemo(() => {
|
|
1946
2038
|
const def = wb.export();
|
|
1947
2039
|
const sel = wb.getSelection();
|
|
1948
2040
|
const out = toReactFlow(def, wb.getPositions(), registry, {
|
|
@@ -1975,25 +2067,6 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
1975
2067
|
nodeTypes,
|
|
1976
2068
|
resolveNodeType,
|
|
1977
2069
|
]);
|
|
1978
|
-
// Local controlled state per requested pattern
|
|
1979
|
-
const [nodes, setNodes] = useState(computedNodes);
|
|
1980
|
-
const [edges, setEdges] = useState(computedEdges);
|
|
1981
|
-
// Sync from computed graph to local state when source changes
|
|
1982
|
-
useEffect(() => {
|
|
1983
|
-
setNodes(computedNodes);
|
|
1984
|
-
}, [computedNodes]);
|
|
1985
|
-
useEffect(() => {
|
|
1986
|
-
setEdges(computedEdges);
|
|
1987
|
-
}, [computedEdges]);
|
|
1988
|
-
// Bridge RF changes to both local state and workbench
|
|
1989
|
-
const onNodesChange = useCallback((changes) => {
|
|
1990
|
-
setNodes((nds) => applyNodeChanges(changes, nds));
|
|
1991
|
-
wbOnNodesChange(changes);
|
|
1992
|
-
}, [wbOnNodesChange]);
|
|
1993
|
-
const onEdgesChange = useCallback((changes) => {
|
|
1994
|
-
setEdges((eds) => applyEdgeChanges(changes, eds));
|
|
1995
|
-
wbOnEdgesChange(changes);
|
|
1996
|
-
}, [wbOnEdgesChange]);
|
|
1997
2070
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
1998
2071
|
const [menuPos, setMenuPos] = useState(null);
|
|
1999
2072
|
const [nodeMenuOpen, setNodeMenuOpen] = useState(false);
|
|
@@ -2458,18 +2531,22 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2458
2531
|
catch (err) {
|
|
2459
2532
|
alert(String(err?.message ?? err));
|
|
2460
2533
|
}
|
|
2461
|
-
}, disabled: !engine, children: "Start" })), jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: "Fit View" }), jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsx("span", { children: "Debug events" })] }), jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsx("span", { children: "Show values in nodes" })] })] }), jsxs("div", { className: "flex flex-1 min-h-0", children: [jsx("div", { className: "flex-1 min-w-0", children: jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement })] })] }));
|
|
2534
|
+
}, disabled: !engine, children: "Start" })), jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: "Fit View" }), jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsx("span", { children: "Debug events" })] }), jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsx("span", { children: "Show values in nodes" })] })] }), jsxs("div", { className: "flex flex-1 min-h-0", children: [jsx("div", { className: "flex-1 min-w-0", children: jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement, contextPanel: overrides?.contextPanel })] })] }));
|
|
2462
2535
|
}
|
|
2463
|
-
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, }) {
|
|
2536
|
+
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, contextPanel, }) {
|
|
2464
2537
|
const [registry, setRegistry] = useState(createSimpleGraphRegistry());
|
|
2465
2538
|
const [wb] = useState(() => new InMemoryWorkbench({ ui: new DefaultUIExtensionRegistry() }));
|
|
2466
2539
|
const runner = useMemo(() => {
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2540
|
+
if (backendKind === "remote-http") {
|
|
2541
|
+
return new RemoteGraphRunner(registry, {
|
|
2542
|
+
kind: "remote-http",
|
|
2543
|
+
baseUrl: httpBaseUrl,
|
|
2544
|
+
});
|
|
2545
|
+
}
|
|
2546
|
+
if (backendKind === "remote-ws") {
|
|
2547
|
+
return new RemoteGraphRunner(registry, { kind: "remote-ws", url: wsUrl });
|
|
2548
|
+
}
|
|
2549
|
+
return new LocalGraphRunner(registry);
|
|
2473
2550
|
}, [registry, backendKind, httpBaseUrl, wsUrl]);
|
|
2474
2551
|
// Allow external UI registration (e.g., node renderers) with access to wb
|
|
2475
2552
|
useEffect(() => {
|
|
@@ -2484,5 +2561,5 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
|
|
|
2484
2561
|
}, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, overrides: overrides, onInit: onInit, onChange: onChange }) }));
|
|
2485
2562
|
}
|
|
2486
2563
|
|
|
2487
|
-
export { AbstractWorkbench, CLIWorkbench, DefaultUIExtensionRegistry,
|
|
2564
|
+
export { AbstractWorkbench, CLIWorkbench, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, formatDataUrlAsLabel, formatDeclaredTypeSignature, getNodeBorderClassNames, preformatValueForDisplay, resolveOutputDisplay, summarizeDeep, toReactFlow, useQueryParamBoolean, useQueryParamString, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
|
|
2488
2565
|
//# sourceMappingURL=index.js.map
|