@bian-womp/spark-workbench 0.1.29 → 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 +322 -225
- 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/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 +322 -226
- 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/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/cjs/index.cjs
CHANGED
|
@@ -319,104 +319,295 @@ class CLIWorkbench {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
class
|
|
322
|
+
class AbstractGraphRunner {
|
|
323
323
|
constructor(registry, backend) {
|
|
324
324
|
this.registry = registry;
|
|
325
|
+
this.backend = backend;
|
|
325
326
|
this.listeners = new Map();
|
|
326
327
|
this.stagedInputs = {};
|
|
327
|
-
this.backend = { kind: "local" };
|
|
328
|
-
if (backend)
|
|
329
|
-
this.backend = backend;
|
|
330
|
-
// Emit initial transport status
|
|
331
|
-
if (this.backend.kind === "local")
|
|
332
|
-
this.emit("transport", { state: "local" });
|
|
333
328
|
}
|
|
334
|
-
|
|
335
|
-
if (this.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
329
|
+
launch(def, opts) {
|
|
330
|
+
if (this.engine) {
|
|
331
|
+
throw new Error("Engine already running. Stop the current engine first.");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
setInput(nodeId, handle, value) {
|
|
335
|
+
if (!this.stagedInputs[nodeId])
|
|
336
|
+
this.stagedInputs[nodeId] = {};
|
|
337
|
+
this.stagedInputs[nodeId][handle] = value;
|
|
338
|
+
if (this.engine) {
|
|
339
|
+
this.engine.setInput(nodeId, handle, value);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
// Emit a value event so UI updates even when engine isn't running
|
|
343
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Batch update multiple inputs on a node and trigger a single run
|
|
347
|
+
setInputs(nodeId, inputs) {
|
|
348
|
+
if (!inputs)
|
|
340
349
|
return;
|
|
350
|
+
if (!this.stagedInputs[nodeId])
|
|
351
|
+
this.stagedInputs[nodeId] = {};
|
|
352
|
+
Object.assign(this.stagedInputs[nodeId], inputs);
|
|
353
|
+
if (this.engine) {
|
|
354
|
+
// Running: set all inputs
|
|
355
|
+
this.engine.setInputs(nodeId, inputs);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
359
|
+
console.warn("Engine does not exists");
|
|
360
|
+
for (const [handle, value] of Object.entries(inputs)) {
|
|
361
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
362
|
+
}
|
|
341
363
|
}
|
|
342
|
-
|
|
364
|
+
}
|
|
365
|
+
async whenIdle() {
|
|
366
|
+
await this.engine?.whenIdle();
|
|
367
|
+
}
|
|
368
|
+
on(event, handler) {
|
|
369
|
+
if (!this.listeners.has(event))
|
|
370
|
+
this.listeners.set(event, new Set());
|
|
371
|
+
const set = this.listeners.get(event);
|
|
372
|
+
set.add(handler);
|
|
373
|
+
return () => set.delete(handler);
|
|
374
|
+
}
|
|
375
|
+
emit(event, payload) {
|
|
376
|
+
const set = this.listeners.get(event);
|
|
377
|
+
if (set)
|
|
378
|
+
for (const h of Array.from(set))
|
|
379
|
+
h(payload);
|
|
380
|
+
}
|
|
381
|
+
dispose() {
|
|
382
|
+
this.engine?.dispose();
|
|
383
|
+
this.engine = undefined;
|
|
384
|
+
this.runtime?.dispose();
|
|
385
|
+
this.runtime = undefined;
|
|
386
|
+
if (this.runningKind) {
|
|
387
|
+
this.runningKind = undefined;
|
|
388
|
+
this.emit("status", { running: false, engine: undefined });
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
isRunning() {
|
|
392
|
+
return !!this.engine;
|
|
393
|
+
}
|
|
394
|
+
getRunningEngine() {
|
|
395
|
+
return this.runningKind;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
class LocalGraphRunner extends AbstractGraphRunner {
|
|
400
|
+
constructor(registry) {
|
|
401
|
+
super(registry, { kind: "local" });
|
|
402
|
+
this.emit("transport", { state: "local" });
|
|
403
|
+
}
|
|
404
|
+
build(def) {
|
|
405
|
+
const builder = new sparkGraph.GraphBuilder(this.registry);
|
|
406
|
+
this.runtime = builder.build(def);
|
|
407
|
+
// Signal UI that freshly built graph should be considered invalidated
|
|
408
|
+
this.emit("invalidate", { reason: "graph-built" });
|
|
343
409
|
}
|
|
344
410
|
update(def) {
|
|
345
|
-
if (this.
|
|
346
|
-
if (!this.runtime)
|
|
347
|
-
return;
|
|
348
|
-
// Prevent mid-run churn while wiring changes are applied
|
|
349
|
-
this.runtime.pause();
|
|
350
|
-
this.runtime.update(def, this.registry);
|
|
351
|
-
this.runtime.resume();
|
|
352
|
-
this.emit("invalidate", { reason: "graph-updated" });
|
|
411
|
+
if (!this.runtime)
|
|
353
412
|
return;
|
|
413
|
+
// Prevent mid-run churn while wiring changes are applied
|
|
414
|
+
this.runtime.pause();
|
|
415
|
+
this.runtime.update(def, this.registry);
|
|
416
|
+
this.runtime.resume();
|
|
417
|
+
this.emit("invalidate", { reason: "graph-updated" });
|
|
418
|
+
}
|
|
419
|
+
launch(def, opts) {
|
|
420
|
+
super.launch(def, opts);
|
|
421
|
+
this.build(def);
|
|
422
|
+
if (!this.runtime)
|
|
423
|
+
throw new Error("Runtime not built");
|
|
424
|
+
const rt = this.runtime;
|
|
425
|
+
switch (opts.engine) {
|
|
426
|
+
case "push":
|
|
427
|
+
this.engine = new sparkGraph.PushEngine(rt);
|
|
428
|
+
break;
|
|
429
|
+
case "batched":
|
|
430
|
+
this.engine = new sparkGraph.BatchedEngine(rt, {
|
|
431
|
+
flushIntervalMs: opts.batched?.flushIntervalMs ?? 0,
|
|
432
|
+
});
|
|
433
|
+
break;
|
|
434
|
+
case "pull":
|
|
435
|
+
this.engine = new sparkGraph.PullEngine(rt);
|
|
436
|
+
break;
|
|
437
|
+
case "hybrid":
|
|
438
|
+
this.engine = new sparkGraph.HybridEngine(rt, {
|
|
439
|
+
windowMs: opts.hybrid?.windowMs ?? 250,
|
|
440
|
+
batchThreshold: opts.hybrid?.batchThreshold ?? 3,
|
|
441
|
+
});
|
|
442
|
+
break;
|
|
443
|
+
case "step":
|
|
444
|
+
this.engine = new sparkGraph.StepEngine(rt);
|
|
445
|
+
break;
|
|
446
|
+
default:
|
|
447
|
+
throw new Error("Unknown engine kind");
|
|
448
|
+
}
|
|
449
|
+
this.engine.on("value", (e) => this.emit("value", e));
|
|
450
|
+
this.engine.on("error", (e) => this.emit("error", e));
|
|
451
|
+
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
452
|
+
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
453
|
+
this.engine.launch();
|
|
454
|
+
this.runningKind = opts.engine;
|
|
455
|
+
this.emit("status", { running: true, engine: this.runningKind });
|
|
456
|
+
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
457
|
+
this.engine.setInputs(nodeId, map);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async step() {
|
|
461
|
+
const eng = this.engine;
|
|
462
|
+
if (eng instanceof sparkGraph.StepEngine)
|
|
463
|
+
await eng.step();
|
|
464
|
+
}
|
|
465
|
+
async computeNode(nodeId) {
|
|
466
|
+
const eng = this.engine;
|
|
467
|
+
if (eng instanceof sparkGraph.PullEngine)
|
|
468
|
+
await eng.computeNode(nodeId);
|
|
469
|
+
}
|
|
470
|
+
flush() {
|
|
471
|
+
const eng = this.engine;
|
|
472
|
+
if (eng instanceof sparkGraph.BatchedEngine)
|
|
473
|
+
eng.flush();
|
|
474
|
+
}
|
|
475
|
+
getOutputs(def) {
|
|
476
|
+
const out = {};
|
|
477
|
+
if (!this.runtime)
|
|
478
|
+
return out;
|
|
479
|
+
for (const n of def.nodes) {
|
|
480
|
+
const desc = this.registry.nodes.get(n.typeId);
|
|
481
|
+
const handles = Object.keys(desc?.outputs ?? {});
|
|
482
|
+
for (const h of handles) {
|
|
483
|
+
const v = this.runtime.getOutput(n.nodeId, h);
|
|
484
|
+
if (v !== undefined) {
|
|
485
|
+
if (!out[n.nodeId])
|
|
486
|
+
out[n.nodeId] = {};
|
|
487
|
+
out[n.nodeId][h] = v;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
354
490
|
}
|
|
491
|
+
return out;
|
|
492
|
+
}
|
|
493
|
+
getInputs(def) {
|
|
494
|
+
const out = {};
|
|
495
|
+
for (const n of def.nodes) {
|
|
496
|
+
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
497
|
+
const runtimeInputs = this.runtime
|
|
498
|
+
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
499
|
+
: {};
|
|
500
|
+
if (this.isRunning()) {
|
|
501
|
+
out[n.nodeId] = runtimeInputs;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
const merged = { ...runtimeInputs, ...staged };
|
|
505
|
+
if (Object.keys(merged).length > 0)
|
|
506
|
+
out[n.nodeId] = merged;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return out;
|
|
510
|
+
}
|
|
511
|
+
dispose() {
|
|
512
|
+
super.dispose();
|
|
513
|
+
this.runtime = undefined;
|
|
514
|
+
this.emit("transport", { state: "local" });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
class RemoteGraphRunner extends AbstractGraphRunner {
|
|
519
|
+
constructor(registry, backend) {
|
|
520
|
+
super(registry, backend);
|
|
521
|
+
this.valueCache = new Map();
|
|
522
|
+
this.listenersBound = false;
|
|
523
|
+
// Auto-handle registry-changed invalidations from remote
|
|
524
|
+
// We listen on invalidate and if reason matches, we rehydrate registry and emit a registry event
|
|
525
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
526
|
+
const eng = runner.getEngine();
|
|
527
|
+
if (!this.listenersBound) {
|
|
528
|
+
eng.on("invalidate", async (e) => {
|
|
529
|
+
if (e.reason === "registry-changed") {
|
|
530
|
+
try {
|
|
531
|
+
const deltas = Array.isArray(e.deltas) ? e.deltas : [];
|
|
532
|
+
for (const d of deltas) {
|
|
533
|
+
if (!d || typeof d !== "object")
|
|
534
|
+
continue;
|
|
535
|
+
if (d.kind === "register-enum") {
|
|
536
|
+
this.registry.registerEnum({
|
|
537
|
+
id: d.id,
|
|
538
|
+
displayName: d.displayName,
|
|
539
|
+
options: d.options,
|
|
540
|
+
opts: d.opts,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
else if (d.kind === "register-type") {
|
|
544
|
+
if (!this.registry.types.has(d.id)) {
|
|
545
|
+
this.registry.registerType({
|
|
546
|
+
id: d.id,
|
|
547
|
+
displayName: d.displayName,
|
|
548
|
+
validate: (_v) => true,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (d.kind === "register-node") {
|
|
553
|
+
if (!this.registry.nodes.has(d.desc?.id)) {
|
|
554
|
+
this.registry.registerNode({
|
|
555
|
+
id: String(d.desc?.id || ""),
|
|
556
|
+
categoryId: String(d.desc?.categoryId || "compute"),
|
|
557
|
+
displayName: d.desc?.displayName,
|
|
558
|
+
inputs: d.desc?.inputs || {},
|
|
559
|
+
outputs: d.desc?.outputs || {},
|
|
560
|
+
impl: () => { },
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
this.emit("registry", this.registry);
|
|
566
|
+
// Trigger update so validation/UI refreshes using last known graph
|
|
567
|
+
try {
|
|
568
|
+
if (this.lastDef)
|
|
569
|
+
this.update(this.lastDef);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
console.error("Failed to update graph definition after registry changed");
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
console.error("Failed to handle registry changed event");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
build(def) {
|
|
584
|
+
console.warn("Unsupported operation for remote runner");
|
|
585
|
+
}
|
|
586
|
+
update(def) {
|
|
355
587
|
// Remote: forward update; ignore errors (fire-and-forget)
|
|
356
|
-
|
|
588
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
357
589
|
try {
|
|
358
|
-
await
|
|
590
|
+
await runner.update(def);
|
|
359
591
|
this.emit("invalidate", { reason: "graph-updated" });
|
|
592
|
+
this.lastDef = def;
|
|
360
593
|
}
|
|
361
594
|
catch { }
|
|
362
595
|
});
|
|
363
596
|
}
|
|
364
597
|
launch(def, opts) {
|
|
365
|
-
|
|
366
|
-
throw new Error("Engine already running. Stop the current engine first.");
|
|
367
|
-
}
|
|
368
|
-
if (this.backend.kind === "local") {
|
|
369
|
-
this.build(def);
|
|
370
|
-
if (!this.runtime)
|
|
371
|
-
throw new Error("Runtime not built");
|
|
372
|
-
const rt = this.runtime;
|
|
373
|
-
switch (opts.engine) {
|
|
374
|
-
case "push":
|
|
375
|
-
this.engine = new sparkGraph.PushEngine(rt);
|
|
376
|
-
break;
|
|
377
|
-
case "batched":
|
|
378
|
-
this.engine = new sparkGraph.BatchedEngine(rt, {
|
|
379
|
-
flushIntervalMs: opts.batched?.flushIntervalMs ?? 0,
|
|
380
|
-
});
|
|
381
|
-
break;
|
|
382
|
-
case "pull":
|
|
383
|
-
this.engine = new sparkGraph.PullEngine(rt);
|
|
384
|
-
break;
|
|
385
|
-
case "hybrid":
|
|
386
|
-
this.engine = new sparkGraph.HybridEngine(rt, {
|
|
387
|
-
windowMs: opts.hybrid?.windowMs ?? 250,
|
|
388
|
-
batchThreshold: opts.hybrid?.batchThreshold ?? 3,
|
|
389
|
-
});
|
|
390
|
-
break;
|
|
391
|
-
case "step":
|
|
392
|
-
this.engine = new sparkGraph.StepEngine(rt);
|
|
393
|
-
break;
|
|
394
|
-
default:
|
|
395
|
-
throw new Error("Unknown engine kind");
|
|
396
|
-
}
|
|
397
|
-
this.engine.on("value", (e) => this.emit("value", e));
|
|
398
|
-
this.engine.on("error", (e) => this.emit("error", e));
|
|
399
|
-
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
400
|
-
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
401
|
-
this.engine.launch();
|
|
402
|
-
this.runningKind = opts.engine;
|
|
403
|
-
this.emit("status", { running: true, engine: this.runningKind });
|
|
404
|
-
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
405
|
-
this.engine.setInputs(nodeId, map);
|
|
406
|
-
}
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
598
|
+
super.launch(def, opts);
|
|
409
599
|
// Remote: build remotely then launch
|
|
410
|
-
|
|
411
|
-
await
|
|
600
|
+
this.ensureRemoteRunner().then(async (runner) => {
|
|
601
|
+
await runner.build(def);
|
|
412
602
|
// Signal UI after remote build as well
|
|
413
603
|
this.emit("invalidate", { reason: "graph-built" });
|
|
604
|
+
this.lastDef = def;
|
|
414
605
|
// Hydrate current remote inputs/outputs (including defaults) into cache
|
|
415
606
|
try {
|
|
416
|
-
const snap = await
|
|
607
|
+
const snap = await runner.snapshot();
|
|
417
608
|
for (const [nodeId, map] of Object.entries(snap.inputs || {})) {
|
|
418
609
|
for (const [handle, value] of Object.entries(map || {})) {
|
|
419
|
-
|
|
610
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
420
611
|
io: "input",
|
|
421
612
|
value,
|
|
422
613
|
});
|
|
@@ -425,7 +616,7 @@ class GraphRunner {
|
|
|
425
616
|
}
|
|
426
617
|
for (const [nodeId, map] of Object.entries(snap.outputs || {})) {
|
|
427
618
|
for (const [handle, value] of Object.entries(map || {})) {
|
|
428
|
-
|
|
619
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
429
620
|
io: "output",
|
|
430
621
|
value,
|
|
431
622
|
});
|
|
@@ -436,10 +627,10 @@ class GraphRunner {
|
|
|
436
627
|
catch {
|
|
437
628
|
console.error("Failed to hydrate remote inputs/outputs");
|
|
438
629
|
}
|
|
439
|
-
const eng =
|
|
440
|
-
if (!
|
|
630
|
+
const eng = runner.getEngine();
|
|
631
|
+
if (!this.listenersBound) {
|
|
441
632
|
eng.on("value", (e) => {
|
|
442
|
-
|
|
633
|
+
this.valueCache.set(`${e.nodeId}.${e.handle}`, {
|
|
443
634
|
io: e.io,
|
|
444
635
|
value: e.value,
|
|
445
636
|
runtimeTypeId: e.runtimeTypeId,
|
|
@@ -449,7 +640,7 @@ class GraphRunner {
|
|
|
449
640
|
eng.on("error", (e) => this.emit("error", e));
|
|
450
641
|
eng.on("invalidate", (e) => this.emit("invalidate", e));
|
|
451
642
|
eng.on("stats", (e) => this.emit("stats", e));
|
|
452
|
-
|
|
643
|
+
this.listenersBound = true;
|
|
453
644
|
}
|
|
454
645
|
this.engine = eng;
|
|
455
646
|
this.engine.launch();
|
|
@@ -460,85 +651,18 @@ class GraphRunner {
|
|
|
460
651
|
}
|
|
461
652
|
});
|
|
462
653
|
}
|
|
463
|
-
setInput(nodeId, handle, value) {
|
|
464
|
-
if (!this.stagedInputs[nodeId])
|
|
465
|
-
this.stagedInputs[nodeId] = {};
|
|
466
|
-
this.stagedInputs[nodeId][handle] = value;
|
|
467
|
-
if (this.engine) {
|
|
468
|
-
this.engine.setInput(nodeId, handle, value);
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
// Emit a value event so UI updates even when engine isn't running
|
|
472
|
-
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
// Batch update multiple inputs on a node and trigger a single run
|
|
476
|
-
setInputs(nodeId, inputs) {
|
|
477
|
-
if (!inputs)
|
|
478
|
-
return;
|
|
479
|
-
if (!this.stagedInputs[nodeId])
|
|
480
|
-
this.stagedInputs[nodeId] = {};
|
|
481
|
-
Object.assign(this.stagedInputs[nodeId], inputs);
|
|
482
|
-
// Local running: pause, set all inputs, resume, schedule a single recompute
|
|
483
|
-
if (this.backend.kind === "local" && this.engine && this.runtime) {
|
|
484
|
-
this.engine.setInputs(nodeId, inputs);
|
|
485
|
-
}
|
|
486
|
-
// Remote running: forward inputs individually (no batch API available)
|
|
487
|
-
else if (this.engine &&
|
|
488
|
-
this.backend.kind !== "local" &&
|
|
489
|
-
this.engine instanceof sparkRemote.RemoteEngine) {
|
|
490
|
-
this.engine.setInputs(nodeId, inputs);
|
|
491
|
-
}
|
|
492
|
-
// Not running: emit value events so UI reflects staged values
|
|
493
|
-
else if (!this.engine) {
|
|
494
|
-
// Not running: emit a single synthetic value event per handle; UI will coalesce
|
|
495
|
-
console.warn("Remote engine does not exists");
|
|
496
|
-
for (const [handle, value] of Object.entries(inputs)) {
|
|
497
|
-
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
654
|
async step() {
|
|
502
|
-
|
|
503
|
-
return; // unsupported remotely
|
|
504
|
-
const eng = this.engine;
|
|
505
|
-
if (eng instanceof sparkGraph.StepEngine)
|
|
506
|
-
await eng.step();
|
|
655
|
+
console.warn("Unsupported operation for remote runner");
|
|
507
656
|
}
|
|
508
657
|
async computeNode(nodeId) {
|
|
509
|
-
|
|
510
|
-
return; // unsupported remotely
|
|
511
|
-
const eng = this.engine;
|
|
512
|
-
if (eng instanceof sparkGraph.PullEngine)
|
|
513
|
-
await eng.computeNode(nodeId);
|
|
658
|
+
console.warn("Unsupported operation for remote runner");
|
|
514
659
|
}
|
|
515
660
|
flush() {
|
|
516
|
-
|
|
517
|
-
return; // unsupported remotely
|
|
518
|
-
const eng = this.engine;
|
|
519
|
-
if (eng instanceof sparkGraph.BatchedEngine)
|
|
520
|
-
eng.flush();
|
|
661
|
+
console.warn("Unsupported operation for remote runner");
|
|
521
662
|
}
|
|
522
663
|
getOutputs(def) {
|
|
523
664
|
const out = {};
|
|
524
|
-
|
|
525
|
-
if (!this.runtime)
|
|
526
|
-
return out;
|
|
527
|
-
for (const n of def.nodes) {
|
|
528
|
-
const desc = this.registry.nodes.get(n.typeId);
|
|
529
|
-
const handles = Object.keys(desc?.outputs ?? {});
|
|
530
|
-
for (const h of handles) {
|
|
531
|
-
const v = this.runtime.getOutput(n.nodeId, h);
|
|
532
|
-
if (v !== undefined) {
|
|
533
|
-
if (!out[n.nodeId])
|
|
534
|
-
out[n.nodeId] = {};
|
|
535
|
-
out[n.nodeId][h] = v;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
return out;
|
|
540
|
-
}
|
|
541
|
-
const cache = this.remote?.valueCache;
|
|
665
|
+
const cache = this.valueCache;
|
|
542
666
|
if (!cache)
|
|
543
667
|
return out;
|
|
544
668
|
for (const n of def.nodes) {
|
|
@@ -558,31 +682,14 @@ class GraphRunner {
|
|
|
558
682
|
}
|
|
559
683
|
getInputs(def) {
|
|
560
684
|
const out = {};
|
|
561
|
-
|
|
562
|
-
for (const n of def.nodes) {
|
|
563
|
-
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
564
|
-
const runtimeInputs = this.runtime
|
|
565
|
-
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
566
|
-
: {};
|
|
567
|
-
if (this.isRunning()) {
|
|
568
|
-
out[n.nodeId] = runtimeInputs;
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
const merged = { ...runtimeInputs, ...staged };
|
|
572
|
-
if (Object.keys(merged).length > 0)
|
|
573
|
-
out[n.nodeId] = merged;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return out;
|
|
577
|
-
}
|
|
578
|
-
const cache = this.remote?.valueCache;
|
|
685
|
+
const cache = this.valueCache;
|
|
579
686
|
for (const n of def.nodes) {
|
|
580
687
|
const staged = this.stagedInputs[n.nodeId] ?? {};
|
|
581
688
|
const desc = this.registry.nodes.get(n.typeId);
|
|
582
689
|
const handles = Object.keys(desc?.inputs ?? {});
|
|
583
690
|
const cur = {};
|
|
584
691
|
for (const h of handles) {
|
|
585
|
-
const rec = cache
|
|
692
|
+
const rec = cache.get(`${n.nodeId}.${h}`);
|
|
586
693
|
if (rec && rec.io === "input")
|
|
587
694
|
cur[h] = rec.value;
|
|
588
695
|
}
|
|
@@ -592,52 +699,21 @@ class GraphRunner {
|
|
|
592
699
|
}
|
|
593
700
|
return out;
|
|
594
701
|
}
|
|
595
|
-
async whenIdle() {
|
|
596
|
-
await this.engine?.whenIdle();
|
|
597
|
-
}
|
|
598
|
-
on(event, handler) {
|
|
599
|
-
if (!this.listeners.has(event))
|
|
600
|
-
this.listeners.set(event, new Set());
|
|
601
|
-
const set = this.listeners.get(event);
|
|
602
|
-
set.add(handler);
|
|
603
|
-
return () => set.delete(handler);
|
|
604
|
-
}
|
|
605
|
-
emit(event, payload) {
|
|
606
|
-
const set = this.listeners.get(event);
|
|
607
|
-
if (set)
|
|
608
|
-
for (const h of Array.from(set))
|
|
609
|
-
h(payload);
|
|
610
|
-
}
|
|
611
702
|
dispose() {
|
|
612
|
-
|
|
613
|
-
this.
|
|
614
|
-
this.
|
|
615
|
-
this.runtime = undefined;
|
|
616
|
-
this.remote = undefined;
|
|
617
|
-
if (this.runningKind) {
|
|
618
|
-
this.runningKind = undefined;
|
|
619
|
-
this.emit("status", { running: false, engine: undefined });
|
|
620
|
-
}
|
|
621
|
-
const kind = this.backend.kind === "local"
|
|
622
|
-
? undefined
|
|
623
|
-
: this.backend.kind;
|
|
703
|
+
super.dispose();
|
|
704
|
+
this.runner = undefined;
|
|
705
|
+
this.transport = undefined;
|
|
624
706
|
this.emit("transport", {
|
|
625
|
-
state:
|
|
626
|
-
kind,
|
|
707
|
+
state: "disconnected",
|
|
708
|
+
kind: this.backend.kind,
|
|
627
709
|
});
|
|
628
710
|
}
|
|
629
|
-
isRunning() {
|
|
630
|
-
return !!this.engine;
|
|
631
|
-
}
|
|
632
|
-
getRunningEngine() {
|
|
633
|
-
return this.runningKind;
|
|
634
|
-
}
|
|
635
711
|
// Ensure remote transport/runner
|
|
636
|
-
async
|
|
637
|
-
if (this.
|
|
638
|
-
return this.
|
|
712
|
+
async ensureRemoteRunner() {
|
|
713
|
+
if (this.runner)
|
|
714
|
+
return this.runner;
|
|
639
715
|
let transport;
|
|
640
|
-
const kind = this.backend.kind
|
|
716
|
+
const kind = this.backend.kind;
|
|
641
717
|
this.emit("transport", { state: "connecting", kind });
|
|
642
718
|
if (this.backend.kind === "remote-http") {
|
|
643
719
|
if (!sparkRemote.HttpPollingTransport)
|
|
@@ -655,14 +731,12 @@ class GraphRunner {
|
|
|
655
731
|
throw new Error("Remote backend not configured");
|
|
656
732
|
}
|
|
657
733
|
const runner = new sparkRemote.RemoteRunner(transport);
|
|
658
|
-
this.
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
listenersBound: false,
|
|
663
|
-
};
|
|
734
|
+
this.runner = runner;
|
|
735
|
+
this.transport = transport;
|
|
736
|
+
this.valueCache.clear();
|
|
737
|
+
this.listenersBound = false;
|
|
664
738
|
this.emit("transport", { state: "connected", kind });
|
|
665
|
-
return
|
|
739
|
+
return runner;
|
|
666
740
|
}
|
|
667
741
|
}
|
|
668
742
|
|
|
@@ -1335,6 +1409,23 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1335
1409
|
});
|
|
1336
1410
|
const off7 = wb.on("error", add("workbench", "error"));
|
|
1337
1411
|
wb.refreshValidation();
|
|
1412
|
+
// Registry updates: swap registry and refresh graph validation/UI
|
|
1413
|
+
const offReg = runner.on("registry", (newReg) => {
|
|
1414
|
+
try {
|
|
1415
|
+
setRegistry(newReg);
|
|
1416
|
+
wb.setRegistry(newReg);
|
|
1417
|
+
// Trigger a graph update so the UI revalidates with new types/enums/nodes
|
|
1418
|
+
try {
|
|
1419
|
+
runner.update(wb.export());
|
|
1420
|
+
}
|
|
1421
|
+
catch {
|
|
1422
|
+
console.error("Failed to update graph definition after registry changed");
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
catch {
|
|
1426
|
+
console.error("Failed to handle registry changed event");
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1338
1429
|
return () => {
|
|
1339
1430
|
off1();
|
|
1340
1431
|
off2();
|
|
@@ -1347,6 +1438,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1347
1438
|
off5b();
|
|
1348
1439
|
off6();
|
|
1349
1440
|
off7();
|
|
1441
|
+
offReg();
|
|
1350
1442
|
};
|
|
1351
1443
|
}, [runner, wb]);
|
|
1352
1444
|
// Push incremental updates into running engine without full reload
|
|
@@ -1528,7 +1620,7 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
1528
1620
|
return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Events" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: hideWorkbench, onChange: (e) => onHideWorkbenchChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Hide workbench" })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoScroll, onChange: (e) => onAutoScrollChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Auto scroll" })] }), jsxRuntime.jsx("button", { onClick: clearEvents, className: "text-xs px-2 py-0.5 border border-gray-300 rounded", children: "Clear" })] })] }), jsxRuntime.jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto text-[11px] leading-4 divide-y divide-gray-200", children: rows.map((ev, idx) => (jsxRuntime.jsxs("div", { className: "opacity-85 odd:bg-gray-50 px-2 py-1", children: [jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [jsxRuntime.jsx("span", { className: "w-8 shrink-0 text-right text-gray-500 select-none", children: idx + 1 }), jsxRuntime.jsxs("span", { className: "text-gray-500", children: [new Date(ev.at).toLocaleTimeString(), " \u00B7 ", ev.source, ":", ev.type] })] }), jsxRuntime.jsx("pre", { className: "m-0 whitespace-pre-wrap ml-10", children: renderPayload(ev.payload) })] }, `${ev.at}:${idx}`))) })] }));
|
|
1529
1621
|
}
|
|
1530
1622
|
|
|
1531
|
-
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, setInput, }) {
|
|
1623
|
+
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, contextPanel, setInput, }) {
|
|
1532
1624
|
const safeToString = (typeId, value) => {
|
|
1533
1625
|
try {
|
|
1534
1626
|
if (typeof toString === "function") {
|
|
@@ -1617,7 +1709,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1617
1709
|
setOriginals(nextOriginals);
|
|
1618
1710
|
}, [selectedNodeId, selectedDesc, valuesTick]);
|
|
1619
1711
|
const widthClass = debug ? "w-[480px]" : "w-[320px]";
|
|
1620
|
-
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsxRuntime.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 ??
|
|
1712
|
+
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [contextPanel && (jsxRuntime.jsx("div", { className: "mb-2", children: contextPanel })), jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsxRuntime.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 ??
|
|
1621
1713
|
selectedNodeStatus.lastError) }))] })), jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
1622
1714
|
const typeId = sparkGraph.getInputTypeId(selectedDesc?.inputs, h);
|
|
1623
1715
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
@@ -2441,18 +2533,22 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2441
2533
|
catch (err) {
|
|
2442
2534
|
alert(String(err?.message ?? err));
|
|
2443
2535
|
}
|
|
2444
|
-
}, disabled: !engine, children: "Start" })), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.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" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement })] })] }));
|
|
2536
|
+
}, disabled: !engine, children: "Start" })), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.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" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement, contextPanel: overrides?.contextPanel })] })] }));
|
|
2445
2537
|
}
|
|
2446
|
-
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, }) {
|
|
2538
|
+
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, contextPanel, }) {
|
|
2447
2539
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|
|
2448
2540
|
const [wb] = React.useState(() => new InMemoryWorkbench({ ui: new DefaultUIExtensionRegistry() }));
|
|
2449
2541
|
const runner = React.useMemo(() => {
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2542
|
+
if (backendKind === "remote-http") {
|
|
2543
|
+
return new RemoteGraphRunner(registry, {
|
|
2544
|
+
kind: "remote-http",
|
|
2545
|
+
baseUrl: httpBaseUrl,
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
if (backendKind === "remote-ws") {
|
|
2549
|
+
return new RemoteGraphRunner(registry, { kind: "remote-ws", url: wsUrl });
|
|
2550
|
+
}
|
|
2551
|
+
return new LocalGraphRunner(registry);
|
|
2456
2552
|
}, [registry, backendKind, httpBaseUrl, wsUrl]);
|
|
2457
2553
|
// Allow external UI registration (e.g., node renderers) with access to wb
|
|
2458
2554
|
React.useEffect(() => {
|
|
@@ -2470,9 +2566,10 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
|
|
|
2470
2566
|
exports.AbstractWorkbench = AbstractWorkbench;
|
|
2471
2567
|
exports.CLIWorkbench = CLIWorkbench;
|
|
2472
2568
|
exports.DefaultUIExtensionRegistry = DefaultUIExtensionRegistry;
|
|
2473
|
-
exports.GraphRunner = GraphRunner;
|
|
2474
2569
|
exports.InMemoryWorkbench = InMemoryWorkbench;
|
|
2475
2570
|
exports.Inspector = Inspector;
|
|
2571
|
+
exports.LocalGraphRunner = LocalGraphRunner;
|
|
2572
|
+
exports.RemoteGraphRunner = RemoteGraphRunner;
|
|
2476
2573
|
exports.WorkbenchCanvas = WorkbenchCanvas;
|
|
2477
2574
|
exports.WorkbenchContext = WorkbenchContext;
|
|
2478
2575
|
exports.WorkbenchProvider = WorkbenchProvider;
|