@dxos/react-ui-canvas-compute 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/lib/browser/index.mjs +778 -962
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +778 -962
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/compute.stories.d.ts +22 -1
  8. package/dist/types/src/compute.stories.d.ts.map +1 -1
  9. package/dist/types/src/graph/controller.d.ts +18 -31
  10. package/dist/types/src/graph/controller.d.ts.map +1 -1
  11. package/dist/types/src/hooks/useComputeNodeState.d.ts +2 -2
  12. package/dist/types/src/hooks/useComputeNodeState.d.ts.map +1 -1
  13. package/dist/types/src/hooks/useGraphMonitor.d.ts +2 -2
  14. package/dist/types/src/hooks/useGraphMonitor.d.ts.map +1 -1
  15. package/dist/types/src/shapes/Function.d.ts.map +1 -1
  16. package/dist/types/src/shapes/Gpt.d.ts.map +1 -1
  17. package/dist/types/src/shapes/Queue.d.ts.map +1 -1
  18. package/dist/types/src/shapes/RNG.d.ts.map +1 -1
  19. package/dist/types/src/shapes/Surface.d.ts.map +1 -1
  20. package/dist/types/src/shapes/Thread.d.ts.map +1 -1
  21. package/dist/types/src/shapes/Trigger.d.ts +6 -4
  22. package/dist/types/src/shapes/Trigger.d.ts.map +1 -1
  23. package/dist/types/src/shapes/common/Box.d.ts +4 -4
  24. package/dist/types/src/shapes/common/Box.d.ts.map +1 -1
  25. package/dist/types/src/shapes/common/FunctionBody.d.ts +2 -2
  26. package/dist/types/src/shapes/common/FunctionBody.d.ts.map +1 -1
  27. package/dist/types/src/testing/circuits.d.ts +18 -24
  28. package/dist/types/src/testing/circuits.d.ts.map +1 -1
  29. package/dist/types/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +59 -54
  31. package/src/README.md +0 -3
  32. package/src/compute.stories.tsx +72 -113
  33. package/src/graph/controller.ts +109 -71
  34. package/src/graph/node-defs.ts +31 -31
  35. package/src/hooks/useComputeNodeState.ts +4 -3
  36. package/src/hooks/useGraphMonitor.ts +11 -10
  37. package/src/json.test.ts +3 -3
  38. package/src/schema.test.ts +11 -11
  39. package/src/shapes/Function.tsx +10 -8
  40. package/src/shapes/Gpt.tsx +6 -1
  41. package/src/shapes/Queue.tsx +15 -9
  42. package/src/shapes/RNG.tsx +5 -1
  43. package/src/shapes/Surface.tsx +9 -3
  44. package/src/shapes/Table.tsx +3 -3
  45. package/src/shapes/Thread.tsx +17 -11
  46. package/src/shapes/Trigger.tsx +33 -44
  47. package/src/shapes/common/Box.tsx +5 -6
  48. package/src/shapes/common/FunctionBody.tsx +2 -2
  49. package/src/shapes/common/TypeSelect.tsx +1 -1
  50. package/src/shapes/defs.ts +3 -3
  51. package/src/testing/circuits.ts +5 -14
@@ -6,14 +6,17 @@ import type * as Context from 'effect/Context';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import * as Either from 'effect/Either';
8
8
  import * as Exit from 'effect/Exit';
9
+ import * as Layer from 'effect/Layer';
10
+ import type * as ManagedRuntime from 'effect/ManagedRuntime';
9
11
  import * as Scope from 'effect/Scope';
10
12
 
13
+ import type { AiService } from '@dxos/ai';
11
14
  import { Event, synchronized } from '@dxos/async';
12
15
  import {
13
16
  type ComputeEdge,
14
17
  type ComputeGraphModel,
15
- type ComputeMeta,
16
18
  type ComputeNode,
19
+ type ComputeNodeMeta,
17
20
  type GptInput,
18
21
  type GptOutput,
19
22
  type GraphDiagnostic,
@@ -22,11 +25,19 @@ import {
22
25
  isNotExecuted,
23
26
  } from '@dxos/conductor';
24
27
  import { Resource } from '@dxos/context';
25
- import { type ComputeEventLogger, type ComputeEventPayload } from '@dxos/functions';
26
- import { type ServiceContainer } from '@dxos/functions';
28
+ import type { Database, Feed } from '@dxos/echo';
29
+ import { unwrapExit } from '@dxos/effect';
30
+ import {
31
+ ComputeEventLogger,
32
+ type ComputeEventPayload,
33
+ type CredentialsService,
34
+ type FunctionInvocationService,
35
+ type QueueService,
36
+ TracingService,
37
+ } from '@dxos/functions';
27
38
  import { log } from '@dxos/log';
28
39
  import { type CanvasGraphModel } from '@dxos/react-ui-canvas-editor';
29
- import { type ContentBlock } from '@dxos/schema';
40
+ import { type ContentBlock } from '@dxos/types';
30
41
 
31
42
  import { createComputeGraph } from '../hooks';
32
43
  import { type ComputeShape } from '../shapes';
@@ -72,6 +83,15 @@ type ComputeOutputEvent = {
72
83
  value: RuntimeValue;
73
84
  };
74
85
 
86
+ // TODO(dmaretskyi): Re-use function servies definition.
87
+ export type ComputeServices =
88
+ | AiService.AiService
89
+ | Database.Service
90
+ | Feed.Service
91
+ | QueueService
92
+ | CredentialsService
93
+ | FunctionInvocationService;
94
+
75
95
  /**
76
96
  * Nodes that will automatically trigger the execution of the graph on startup.
77
97
  */
@@ -79,10 +99,10 @@ const AUTO_TRIGGER_NODES = ['chat', 'switch', 'constant'];
79
99
 
80
100
  export const createComputeGraphController = (
81
101
  graph: CanvasGraphModel<ComputeShape>,
82
- serviceContainer: ServiceContainer,
102
+ computeRuntime: ManagedRuntime.ManagedRuntime<ComputeServices, never>,
83
103
  ) => {
84
104
  const computeGraph = createComputeGraph(graph);
85
- const controller = new ComputeGraphController(serviceContainer, computeGraph);
105
+ const controller = new ComputeGraphController(computeRuntime, computeGraph);
86
106
  return { controller, graph };
87
107
  };
88
108
 
@@ -117,7 +137,7 @@ export class ComputeGraphController extends Resource {
117
137
  public readonly events = new Event<ComputeEventPayload>();
118
138
 
119
139
  constructor(
120
- private readonly _serviceContainer: ServiceContainer,
140
+ private readonly _computeRuntime: ManagedRuntime.ManagedRuntime<ComputeServices, never>,
121
141
  /** Persistent compute graph. */
122
142
  private readonly _graph: ComputeGraphModel,
123
143
  ) {
@@ -158,7 +178,14 @@ export class ComputeGraphController extends Resource {
158
178
  /**
159
179
  * Inputs and outputs for all nodes.
160
180
  */
161
- get state() {
181
+ get state(): Record<
182
+ string,
183
+ {
184
+ node: ComputeNode;
185
+ input: Record<string, RuntimeValue>;
186
+ output: Record<string, RuntimeValue>;
187
+ }
188
+ > {
162
189
  const ids = [...new Set([...Object.keys(this._runtimeStateInputs), ...Object.keys(this._runtimeStateOutputs)])];
163
190
  return Object.fromEntries(
164
191
  ids.map((id) => [
@@ -205,7 +232,7 @@ export class ComputeGraphController extends Resource {
205
232
  });
206
233
  }
207
234
 
208
- async getMeta(node: ComputeNode): Promise<ComputeMeta> {
235
+ async getMeta(node: ComputeNode): Promise<ComputeNodeMeta> {
209
236
  const { meta } = await resolveComputeNode(node);
210
237
  return meta;
211
238
  }
@@ -225,37 +252,41 @@ export class ComputeGraphController extends Resource {
225
252
  executor.setOutputs(nodeId, Effect.succeed(ValueBag.make(outputs)));
226
253
  }
227
254
 
228
- const serviceLayer = this._serviceContainer.createLayer();
229
- await Effect.runPromise(
230
- Effect.gen(this, function* () {
231
- const scope = yield* Scope.make();
232
-
233
- // TODO(dmaretskyi): Code duplication.
234
- const executable = yield* Effect.promise(() => resolveComputeNode(this._graph.getNode(nodeId)));
235
- const computingOutputs = executable.exec != null;
236
- // TODO(dmaretskyi): Check if the node has a compute function and run computeOutputs if it does.
237
- const effect = (computingOutputs ? executor.computeOutputs(nodeId) : executor.computeInputs(nodeId)).pipe(
238
- Effect.withSpan('runGraph'),
239
- Scope.extend(scope),
240
-
241
- Effect.flatMap(computeValueBag),
242
- Effect.provide(serviceLayer),
243
- Effect.withSpan('test'),
244
- Effect.tap((values) => {
245
- for (const [key, value] of Object.entries(values)) {
246
- if (computingOutputs) {
247
- this._onOutputComputed(nodeId, key, value);
248
- } else {
249
- this._onInputComputed(nodeId, key, value);
255
+ unwrapExit(
256
+ await this._computeRuntime.runPromiseExit(
257
+ Effect.gen(this, function* () {
258
+ const scope = yield* Scope.make();
259
+
260
+ // TODO(dmaretskyi): Code duplication.
261
+ const executable = yield* Effect.promise(() => resolveComputeNode(this._graph.getNode(nodeId)));
262
+ const computingOutputs = executable.exec != null;
263
+ // TODO(dmaretskyi): Check if the node has a compute function and run computeOutputs if it does.
264
+ const effect = (computingOutputs ? executor.computeOutputs(nodeId) : executor.computeInputs(nodeId)).pipe(
265
+ Effect.withSpan('runGraph'),
266
+ Scope.extend(scope),
267
+ Effect.provide(
268
+ ComputeEventLogger.layerFromTracing.pipe(
269
+ Layer.provideMerge(TracingService.layerNoop), // TODO(dmaretskyi): Plug-in tracing events to visual feedback in the compute graph editor.
270
+ ),
271
+ ),
272
+ Effect.flatMap(computeValueBag),
273
+ Effect.withSpan('test'),
274
+ Effect.tap((values) => {
275
+ for (const [key, value] of Object.entries(values)) {
276
+ if (computingOutputs) {
277
+ this._onOutputComputed(nodeId, key, value);
278
+ } else {
279
+ this._onInputComputed(nodeId, key, value);
280
+ }
250
281
  }
251
- }
252
- }),
253
- );
282
+ }),
283
+ );
254
284
 
255
- yield* effect;
285
+ yield* effect;
256
286
 
257
- yield* Scope.close(scope, Exit.void);
258
- }),
287
+ yield* Scope.close(scope, Exit.void);
288
+ }),
289
+ ),
259
290
  );
260
291
 
261
292
  this.update.emit();
@@ -283,44 +314,51 @@ export class ComputeGraphController extends Resource {
283
314
  : this._graph.nodes.filter((node) => node.type != null && AUTO_TRIGGER_NODES.includes(node.type));
284
315
  const allAffectedNodes = [...new Set(triggerNodes.flatMap((node) => executor.getAllDependantNodes(node.id)))];
285
316
 
286
- await Effect.runPromise(
287
- Effect.gen(this, function* () {
288
- const scope = yield* Scope.make();
289
-
290
- // TODO(burdon): Return map?
291
- const tasks: Effect.Effect<unknown, any, never>[] = [];
292
- for (const node of allAffectedNodes) {
293
- // TODO(dmaretskyi): Code duplication.
294
- const executable = yield* Effect.promise(() => resolveComputeNode(this._graph.getNode(node)));
295
- const computingOutputs = executable.exec != null;
296
-
297
- // TODO(dmaretskyi): Check if the node has a compute function and run computeOutputs if it does.
298
- const effect = (computingOutputs ? executor.computeOutputs(node) : executor.computeInputs(node)).pipe(
299
- Effect.withSpan('runGraph'),
300
- Scope.extend(scope),
301
- Effect.flatMap(computeValueBag),
302
- Effect.provide(this._serviceContainer.createLayer()),
303
- Effect.withSpan('test'),
304
- Effect.tap((values) => {
305
- for (const [key, value] of Object.entries(values)) {
306
- if (computingOutputs) {
307
- this._onOutputComputed(node, key, value);
308
- } else {
309
- this._onInputComputed(node, key, value);
317
+ unwrapExit(
318
+ await this._computeRuntime.runPromiseExit(
319
+ Effect.gen(this, function* () {
320
+ const scope = yield* Scope.make();
321
+
322
+ // TODO(burdon): Return map?
323
+ const tasks: Effect.Effect<unknown, any, ComputeServices>[] = [];
324
+ for (const node of allAffectedNodes) {
325
+ // TODO(dmaretskyi): Code duplication.
326
+ const executable = yield* Effect.promise(() => resolveComputeNode(this._graph.getNode(node)));
327
+ const computingOutputs = executable.exec != null;
328
+
329
+ // TODO(dmaretskyi): Check if the node has a compute function and run computeOutputs if it does.
330
+ const effect = (computingOutputs ? executor.computeOutputs(node) : executor.computeInputs(node)).pipe(
331
+ Effect.withSpan('runGraph'),
332
+ Scope.extend(scope),
333
+ Effect.flatMap(computeValueBag),
334
+ Effect.provide(
335
+ ComputeEventLogger.layerFromTracing.pipe(
336
+ Layer.provideMerge(TracingService.layerNoop), // TODO(dmaretskyi): Plug-in tracing events to visual feedback in the compute graph editor.
337
+ ),
338
+ ),
339
+
340
+ Effect.withSpan('test'),
341
+ Effect.tap((values) => {
342
+ for (const [key, value] of Object.entries(values)) {
343
+ if (computingOutputs) {
344
+ this._onOutputComputed(node, key, value);
345
+ } else {
346
+ this._onInputComputed(node, key, value);
347
+ }
310
348
  }
311
- }
312
- }),
313
- );
349
+ }),
350
+ );
314
351
 
315
- tasks.push(effect);
316
- }
352
+ tasks.push(effect);
353
+ }
317
354
 
318
- //
319
- yield* Effect.all(tasks);
355
+ //
356
+ yield* Effect.all(tasks);
320
357
 
321
- //
322
- yield* Scope.close(scope, Exit.void);
323
- }),
358
+ //
359
+ yield* Scope.close(scope, Exit.void);
360
+ }),
361
+ ),
324
362
  );
325
363
 
326
364
  this.update.emit();
@@ -12,7 +12,7 @@ import {
12
12
  registry,
13
13
  } from '@dxos/conductor';
14
14
  import { raise } from '@dxos/debug';
15
- import { ObjectId, toJsonSchema } from '@dxos/echo/internal';
15
+ import { JsonSchema, Obj } from '@dxos/echo';
16
16
  import { invariant } from '@dxos/invariant';
17
17
 
18
18
  import { type ComputeShape, type ConstantShape, type TemplateShape } from '../shapes';
@@ -39,45 +39,45 @@ const nodeFactory: Record<NodeType | 'trigger', (shape: ComputeShape) => Compute
39
39
  [NODE_OUTPUT]: () => createNode(NODE_OUTPUT),
40
40
 
41
41
  // Extensions.
42
- ['text-to-image' as const]: () => createNode('text-to-image'), // TODO(burdon): Rename ai-impage-tool
43
- ['and' as const]: () => createNode('and'),
44
- ['append' as const]: () => createNode('append'),
45
- ['audio' as const]: () => createNode('audio'),
46
- ['beacon' as const]: () => createNode('beacon'),
47
- ['chat' as const]: () => createNode('chat'),
48
- ['constant' as const]: (shape) =>
42
+ 'text-to-image': () => createNode('text-to-image'), // TODO(burdon): Rename ai-impage-tool
43
+ and: () => createNode('and'),
44
+ append: () => createNode('append'),
45
+ audio: () => createNode('audio'),
46
+ beacon: () => createNode('beacon'),
47
+ chat: () => createNode('chat'),
48
+ constant: (shape) =>
49
49
  createNode('constant', {
50
50
  value: (shape as ConstantShape).value,
51
51
  }),
52
- ['make-queue' as const]: () => createNode('make-queue'),
53
- ['database' as const]: () => createNode('database'),
54
- ['gpt' as const]: () => createNode('gpt'),
55
- ['gpt-realtime' as const]: () => createNode('gpt-realtime'),
56
- ['if' as const]: () => createNode('if'),
57
- ['if-else' as const]: () => createNode('if-else'),
58
- ['function' as const]: () => createNode('function'),
59
- ['json' as const]: () => createNode('json'),
60
- ['json-transform' as const]: () => createNode('json-transform'),
61
- ['not' as const]: () => createNode('not'),
62
- ['or' as const]: () => createNode('or'),
63
- ['queue' as const]: () => createNode('queue'),
64
- ['rng' as const]: () => createNode('rng'),
65
- ['reducer' as const]: () => createNode('reducer'),
66
- ['scope' as const]: () => createNode('scope'),
67
- ['surface' as const]: () => createNode('surface'),
68
- ['switch' as const]: () => createNode('switch'),
69
- ['template' as const]: (shape) => {
52
+ 'make-queue': () => createNode('make-queue'),
53
+ database: () => createNode('database'),
54
+ gpt: () => createNode('gpt'),
55
+ 'gpt-realtime': () => createNode('gpt-realtime'),
56
+ if: () => createNode('if'),
57
+ 'if-else': () => createNode('if-else'),
58
+ function: () => createNode('function'),
59
+ json: () => createNode('json'),
60
+ 'json-transform': () => createNode('json-transform'),
61
+ not: () => createNode('not'),
62
+ or: () => createNode('or'),
63
+ queue: () => createNode('queue'),
64
+ rng: () => createNode('rng'),
65
+ reducer: () => createNode('reducer'),
66
+ scope: () => createNode('scope'),
67
+ surface: () => createNode('surface'),
68
+ switch: () => createNode('switch'),
69
+ template: (shape) => {
70
70
  const node = createNode('template', { valueType: (shape as TemplateShape).valueType, value: shape.text });
71
- node.inputSchema = toJsonSchema(getTemplateInputSchema(node));
71
+ node.inputSchema = JsonSchema.toJsonSchema(getTemplateInputSchema(node));
72
72
  return node;
73
73
  },
74
- ['text' as const]: () => createNode('text'),
75
- ['thread' as const]: () => createNode('thread'),
76
- ['trigger' as const]: () => createNode(NODE_INPUT),
74
+ text: () => createNode('text'),
75
+ thread: () => createNode('thread'),
76
+ trigger: () => createNode(NODE_INPUT),
77
77
  };
78
78
 
79
79
  const createNode = (type: string, props?: Partial<ComputeNode>): ComputeNode => ({
80
- id: ObjectId.random(),
80
+ id: Obj.ID.random(),
81
81
  type,
82
82
  ...props,
83
83
  });
@@ -5,7 +5,7 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { useCallback, useEffect, useState } from 'react';
7
7
 
8
- import type { ComputeMeta, ComputeNode } from '@dxos/conductor';
8
+ import type { ComputeNode, ComputeNodeMeta } from '@dxos/conductor';
9
9
  import type { ComputeEventPayload } from '@dxos/functions';
10
10
  import { invariant } from '@dxos/invariant';
11
11
 
@@ -16,7 +16,7 @@ import { useComputeContext } from './compute-context';
16
16
 
17
17
  export type ComputeNodeState = {
18
18
  node: ComputeNode;
19
- meta: ComputeMeta;
19
+ meta: ComputeNodeMeta;
20
20
  runtime: {
21
21
  inputs: Record<string, RuntimeValue>;
22
22
  outputs: Record<string, RuntimeValue>;
@@ -33,7 +33,7 @@ export const useComputeNodeState = (shape: ComputeShape): ComputeNodeState => {
33
33
  const { controller } = useComputeContext();
34
34
  invariant(controller);
35
35
 
36
- const [meta, setMeta] = useState<ComputeMeta>();
36
+ const [meta, setMeta] = useState<ComputeNodeMeta>();
37
37
  useEffect(() => {
38
38
  let disposed = false;
39
39
  queueMicrotask(async () => {
@@ -43,6 +43,7 @@ export const useComputeNodeState = (shape: ComputeShape): ComputeNodeState => {
43
43
  if (disposed) {
44
44
  return;
45
45
  }
46
+
46
47
  setMeta(meta);
47
48
  });
48
49
 
@@ -5,10 +5,9 @@
5
5
  import { useMemo } from 'react';
6
6
 
7
7
  import { type ComputeEdge, ComputeGraphModel, type ComputeNode, DEFAULT_INPUT, DEFAULT_OUTPUT } from '@dxos/conductor';
8
- import { ObjectId, Ref } from '@dxos/echo/internal';
8
+ import { Obj, Ref } from '@dxos/echo';
9
9
  import { invariant } from '@dxos/invariant';
10
- import { getSpace } from '@dxos/react-client/echo';
11
- import { type CanvasGraphModel, type Connection, type GraphMonitor } from '@dxos/react-ui-canvas-editor';
10
+ import { type CanvasBoard, type CanvasGraphModel, type GraphMonitor } from '@dxos/react-ui-canvas-editor';
12
11
  import { isNonNullable } from '@dxos/util';
13
12
 
14
13
  import { createComputeNode, isValidComputeNode } from '../graph';
@@ -19,7 +18,7 @@ import { type ComputeShape, type TriggerShape } from '../shapes';
19
18
  */
20
19
  export const mapEdge = (
21
20
  graph: CanvasGraphModel,
22
- { source, target, output = DEFAULT_OUTPUT, input = DEFAULT_INPUT }: Connection,
21
+ { source, target, output = DEFAULT_OUTPUT, input = DEFAULT_INPUT }: CanvasBoard.Connection,
23
22
  ): ComputeEdge => {
24
23
  const sourceNode = graph.findNode(source) as ComputeShape;
25
24
  const targetNode = graph.findNode(target) as ComputeShape;
@@ -27,7 +26,7 @@ export const mapEdge = (
27
26
  invariant(targetNode?.node);
28
27
 
29
28
  return {
30
- id: ObjectId.random(),
29
+ id: Obj.ID.random(),
31
30
  source: sourceNode.node,
32
31
  target: targetNode.node,
33
32
  output,
@@ -113,19 +112,21 @@ export const createComputeGraph = (graph?: CanvasGraphModel<ComputeShape>) => {
113
112
  const linkTriggerToCompute = (graph: ComputeGraphModel, computeNode: ComputeNode, triggerData: TriggerShape) => {
114
113
  const functionTrigger = triggerData.functionTrigger?.target;
115
114
  invariant(functionTrigger);
116
- functionTrigger.function = Ref.make(graph.root);
117
- functionTrigger.inputNodeId = computeNode.id;
115
+ Obj.change(functionTrigger, (t) => {
116
+ t.function = Ref.make(graph.root);
117
+ t.inputNodeId = computeNode.id;
118
+ });
118
119
  };
119
120
 
120
121
  const deleteTriggerObjects = (computeGraph: ComputeGraphModel, deleted: CanvasGraphModel) => {
121
- const space = getSpace(computeGraph.root);
122
- if (!space) {
122
+ const db = Obj.getDatabase(computeGraph.root);
123
+ if (!db) {
123
124
  return;
124
125
  }
125
126
  for (const node of deleted.nodes) {
126
127
  if (node.type === 'trigger') {
127
128
  const trigger = node as TriggerShape;
128
- space.db.remove(trigger.functionTrigger!.target!);
129
+ db.remove(trigger.functionTrigger!.target!);
129
130
  }
130
131
  }
131
132
  };
package/src/json.test.ts CHANGED
@@ -5,12 +5,12 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { describe, test } from 'vitest';
7
7
 
8
- import { BaseGraphEdge, BaseGraphNode } from '@dxos/graph';
8
+ import { Graph } from '@dxos/graph';
9
9
 
10
10
  import { createGptCircuit } from './testing';
11
11
 
12
12
  export const Shape = Schema.extend(
13
- BaseGraphNode,
13
+ Graph.Node,
14
14
  Schema.Struct({
15
15
  text: Schema.optional(Schema.String),
16
16
  guide: Schema.optional(Schema.Boolean),
@@ -19,7 +19,7 @@ export const Shape = Schema.extend(
19
19
  );
20
20
 
21
21
  export const Connection = Schema.extend(
22
- BaseGraphEdge,
22
+ Graph.Edge,
23
23
  Schema.Struct({
24
24
  input: Schema.optional(Schema.String),
25
25
  output: Schema.optional(Schema.String),
@@ -5,11 +5,11 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import { describe, test } from 'vitest';
7
7
 
8
- import { live } from '@dxos/echo/internal';
9
- import { BaseGraphNode, Graph } from '@dxos/graph';
8
+ import { Graph } from '@dxos/graph';
10
9
  import {
10
+ CanvasBoard,
11
+ CanvasGraphModel,
11
12
  Polygon,
12
- Shape,
13
13
  createEllipse,
14
14
  createPath,
15
15
  createRectangle,
@@ -21,25 +21,25 @@ import { ComputeShape, createFunction, createSwitch } from './shapes';
21
21
 
22
22
  describe('compute', () => {
23
23
  test('model', ({ expect }) => {
24
- // const model = CanvasGraphModel.create<ComputeShape>();
24
+ const model = CanvasGraphModel.create<ComputeShape>();
25
25
  const node = createSwitch({ id: 'x', center: { x: 0, y: 0 }, size: { width: 80, height: 80 } });
26
26
  console.log(JSON.stringify(node, null, 2));
27
27
  expect(Schema.is(ComputeShape)(node)).toBe(true);
28
28
  expect(Schema.is(Polygon)(node)).toBe(true);
29
- expect(Schema.is(Shape)(node)).toBe(true);
30
- expect(Schema.is(BaseGraphNode)(node)).toBe(true);
29
+ expect(Schema.is(CanvasBoard.Shape)(node)).toBe(true);
30
+ expect(Schema.is(Graph.Node)(node)).toBe(true);
31
31
 
32
- const graph = live(Graph, { nodes: [], edges: [] });
33
- graph.nodes.push(node); // Throws.
32
+ const graph: Graph.Any = { nodes: [], edges: [] };
33
+ graph.nodes.push(node);
34
34
 
35
- // model.createNode(node);
36
- // console.log(JSON.stringify(model, null, 2));
35
+ model.createNode(node);
36
+ console.log(JSON.stringify(model, null, 2));
37
37
  });
38
38
  });
39
39
 
40
40
  describe('schema', () => {
41
41
  test('basic types', ({ expect }) => {
42
- const shapes: Shape[] = [];
42
+ const shapes: CanvasBoard.Shape[] = [];
43
43
  shapes.push(createRectangle({ id: 'shape-1', center: { x: 0, y: 0 }, size: { width: 80, height: 80 } }));
44
44
  shapes.push(createEllipse({ id: 'shape-2', center: { x: 0, y: 0 }, size: { width: 80, height: 80 } }));
45
45
  shapes.push(createFunction({ id: 'shape-3', center: { x: 0, y: 0 } }));
@@ -7,7 +7,7 @@ import React, { useCallback, useRef } from 'react';
7
7
 
8
8
  import { AnyOutput, FunctionInput } from '@dxos/conductor';
9
9
  import { Ref, getSnapshot, isInstanceOf } from '@dxos/echo/internal';
10
- import { FunctionType, ScriptType } from '@dxos/functions';
10
+ import { Function, Script } from '@dxos/functions';
11
11
  import { useClient } from '@dxos/react-client';
12
12
  import { Filter, parseId } from '@dxos/react-client/echo';
13
13
  import {
@@ -35,7 +35,11 @@ export type FunctionShape = Schema.Schema.Type<typeof FunctionShape>;
35
35
  export type CreateFunctionProps = CreateShapeProps<FunctionShape>;
36
36
 
37
37
  export const createFunction = (props: CreateFunctionProps) =>
38
- createShape<FunctionShape>({ type: 'function', size: { width: 256, height: 192 }, ...props });
38
+ createShape<FunctionShape>({
39
+ type: 'function',
40
+ size: { width: 256, height: 192 },
41
+ ...props,
42
+ });
39
43
 
40
44
  //
41
45
  // Component
@@ -58,21 +62,19 @@ const TextInputComponent = ({ shape, title, ...props }: TextInputComponentProps)
58
62
 
59
63
  const space = client.spaces.get(spaceId);
60
64
  const object = space?.db.getObjectById(objectId);
61
- if (!space || !isInstanceOf(ScriptType, object)) {
65
+ if (!space || !isInstanceOf(Script.Script, object)) {
62
66
  return;
63
67
  }
64
68
 
65
- const {
66
- objects: [fn],
67
- } = await space.db.query(Filter.type(FunctionType, { source: Ref.make(object) })).run();
69
+ const [fn] = await space.db.query(Filter.type(Function.Function, { source: Ref.make(object) })).run();
68
70
  if (!fn) {
69
71
  return;
70
72
  }
71
73
 
72
74
  node.value = value;
73
75
  node.function = Ref.make(fn);
74
- node.inputSchema = getSnapshot(fn.inputSchema);
75
- node.outputSchema = getSnapshot(fn.outputSchema);
76
+ node.inputSchema = fn.inputSchema ? getSnapshot(fn.inputSchema) : undefined;
77
+ node.outputSchema = fn.outputSchema ? getSnapshot(fn.outputSchema) : undefined;
76
78
  },
77
79
  [client, node],
78
80
  );
@@ -6,6 +6,7 @@ import * as Schema from 'effect/Schema';
6
6
  import React, { useEffect, useState } from 'react';
7
7
 
8
8
  import { GptInput, GptOutput } from '@dxos/conductor';
9
+ import { ScrollArea } from '@dxos/react-ui';
9
10
  import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
10
11
 
11
12
  import { useComputeNodeState } from '../hooks';
@@ -74,7 +75,11 @@ export const GptComponent = ({ shape }: ShapeComponentProps<GptShape>) => {
74
75
  return (
75
76
  <FunctionBody
76
77
  shape={shape}
77
- content={<div className='px-2 py-1 overflow-y-scroll'>{text}</div>}
78
+ content={
79
+ <ScrollArea.Root orientation='vertical' thin>
80
+ <ScrollArea.Viewport>{text}</ScrollArea.Viewport>
81
+ </ScrollArea.Root>
82
+ }
78
83
  status={`${tokens} tokens`}
79
84
  inputSchema={meta.input}
80
85
  outputSchema={meta.output}
@@ -6,9 +6,9 @@ import * as Schema from 'effect/Schema';
6
6
  import React, { Fragment } from 'react';
7
7
 
8
8
  import { DEFAULT_OUTPUT, QueueInput, QueueOutput } from '@dxos/conductor';
9
- import { type ThemedClassName } from '@dxos/react-ui';
9
+ import { ScrollArea, type ThemedClassName } from '@dxos/react-ui';
10
10
  import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
11
- import { mx } from '@dxos/react-ui-theme';
11
+ import { mx } from '@dxos/ui-theme';
12
12
 
13
13
  import { useComputeNodeState } from '../hooks';
14
14
 
@@ -28,7 +28,11 @@ export type QueueShape = Schema.Schema.Type<typeof QueueShape>;
28
28
  export type CreateQueueProps = CreateShapeProps<QueueShape>;
29
29
 
30
30
  export const createQueue = (props: CreateQueueProps) =>
31
- createShape<QueueShape>({ type: 'queue', size: { width: 256, height: 512 }, ...props });
31
+ createShape<QueueShape>({
32
+ type: 'queue',
33
+ size: { width: 256, height: 512 },
34
+ ...props,
35
+ });
32
36
 
33
37
  export const QueueComponent = ({ shape }: ShapeComponentProps<QueueShape>) => {
34
38
  const { runtime } = useComputeNodeState(shape);
@@ -42,11 +46,13 @@ export const QueueComponent = ({ shape }: ShapeComponentProps<QueueShape>) => {
42
46
 
43
47
  return (
44
48
  <Box shape={shape} status={`${items.length} items`} onAction={handleAction}>
45
- <div className='flex flex-col w-full overflow-y-scroll divide-y divide-separator'>
46
- {[...items].map((item, i) => (
47
- <QueueItem key={i} classNames='p-1 px-2' item={item} />
48
- ))}
49
- </div>
49
+ <ScrollArea.Root orientation='vertical'>
50
+ <ScrollArea.Viewport classNames='divide-y divide-separator'>
51
+ {[...items].map((item, i) => (
52
+ <QueueItem key={i} classNames='p-1 px-2' item={item} />
53
+ ))}
54
+ </ScrollArea.Viewport>
55
+ </ScrollArea.Root>
50
56
  </Box>
51
57
  );
52
58
  };
@@ -57,7 +63,7 @@ export const QueueItem = ({ classNames, item }: ThemedClassName<{ item: any }>)
57
63
  }
58
64
 
59
65
  return (
60
- <div className={mx('grid grid-cols-[80px,1fr]', classNames)}>
66
+ <div className={mx('grid grid-cols-[80px_1fr]', classNames)}>
61
67
  {Object.entries(item).map(([key, value]) => (
62
68
  <Fragment key={key}>
63
69
  <div className='p-1 text-xs text-subdued'>{key}</div>
@@ -27,7 +27,11 @@ export type RandomShape = Schema.Schema.Type<typeof RandomShape>;
27
27
  export type CreateRandomProps = CreateShapeProps<RandomShape>;
28
28
 
29
29
  export const createRandom = (props: CreateRandomProps) =>
30
- createShape<RandomShape>({ type: 'rng', size: { width: 64, height: 64 }, ...props });
30
+ createShape<RandomShape>({
31
+ type: 'rng',
32
+ size: { width: 64, height: 64 },
33
+ ...props,
34
+ });
31
35
 
32
36
  const icons = [
33
37
  'ph--dice-one--regular',