@dxos/react-ui-canvas-compute 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/chunk-types.mjs +142 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/index.mjs +526 -430
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/types.mjs +2 -0
- package/dist/types/src/graph/controller.d.ts +1 -1
- package/dist/types/src/graph/controller.d.ts.map +1 -1
- package/dist/types/src/hooks/compute-context.d.ts +5 -4
- package/dist/types/src/hooks/compute-context.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +0 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useGraphMonitor.d.ts +18 -1
- package/dist/types/src/hooks/useGraphMonitor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/registry.d.ts +5 -8
- package/dist/types/src/registry.d.ts.map +1 -1
- package/dist/types/src/scene/compute.stories.d.ts.map +1 -1
- package/dist/types/src/scene/echo-store.d.ts +1 -1
- package/dist/types/src/scene/echo-store.d.ts.map +1 -1
- package/dist/types/src/scene/index.d.ts +0 -3
- package/dist/types/src/scene/index.d.ts.map +1 -1
- package/dist/types/src/scene/projection.d.ts.map +1 -1
- package/dist/types/src/scene/scene.d.ts +2 -2
- package/dist/types/src/scene/scene.d.ts.map +1 -1
- package/dist/types/src/shapes/Append.d.ts +2 -2
- package/dist/types/src/shapes/Append.d.ts.map +1 -1
- package/dist/types/src/shapes/Array.d.ts +3 -3
- package/dist/types/src/shapes/Array.d.ts.map +1 -1
- package/dist/types/src/shapes/Audio.d.ts +2 -2
- package/dist/types/src/shapes/Audio.d.ts.map +1 -1
- package/dist/types/src/shapes/Beacon.d.ts +2 -2
- package/dist/types/src/shapes/Beacon.d.ts.map +1 -1
- package/dist/types/src/shapes/Boolean.d.ts +4 -52
- package/dist/types/src/shapes/Boolean.d.ts.map +1 -1
- package/dist/types/src/shapes/Chat.d.ts +3 -3
- package/dist/types/src/shapes/Chat.d.ts.map +1 -1
- package/dist/types/src/shapes/Constant.d.ts +3 -4
- package/dist/types/src/shapes/Constant.d.ts.map +1 -1
- package/dist/types/src/shapes/Database.d.ts +2 -2
- package/dist/types/src/shapes/Database.d.ts.map +1 -1
- package/dist/types/src/shapes/Feed.d.ts +2 -2
- package/dist/types/src/shapes/Feed.d.ts.map +1 -1
- package/dist/types/src/shapes/Function.d.ts +3 -5
- package/dist/types/src/shapes/Function.d.ts.map +1 -1
- package/dist/types/src/shapes/Gpt.d.ts +2 -2
- package/dist/types/src/shapes/Gpt.d.ts.map +1 -1
- package/dist/types/src/shapes/GptRealtime.d.ts +2 -2
- package/dist/types/src/shapes/GptRealtime.d.ts.map +1 -1
- package/dist/types/src/shapes/Json.d.ts +5 -5
- package/dist/types/src/shapes/Json.d.ts.map +1 -1
- package/dist/types/src/shapes/Logic.d.ts +5 -5
- package/dist/types/src/shapes/Logic.d.ts.map +1 -1
- package/dist/types/src/shapes/RNG.d.ts +2 -2
- package/dist/types/src/shapes/RNG.d.ts.map +1 -1
- package/dist/types/src/shapes/Scope.d.ts +2 -2
- package/dist/types/src/shapes/Scope.d.ts.map +1 -1
- package/dist/types/src/shapes/Surface.d.ts +2 -2
- package/dist/types/src/shapes/Surface.d.ts.map +1 -1
- package/dist/types/src/shapes/Switch.d.ts +2 -2
- package/dist/types/src/shapes/Switch.d.ts.map +1 -1
- package/dist/types/src/shapes/Table.d.ts +2 -2
- package/dist/types/src/shapes/Table.d.ts.map +1 -1
- package/dist/types/src/shapes/Template.d.ts +2 -2
- package/dist/types/src/shapes/Template.d.ts.map +1 -1
- package/dist/types/src/shapes/Text.d.ts +2 -2
- package/dist/types/src/shapes/Text.d.ts.map +1 -1
- package/dist/types/src/shapes/TextToImage.d.ts +2 -2
- package/dist/types/src/shapes/TextToImage.d.ts.map +1 -1
- package/dist/types/src/shapes/Thread.d.ts +2 -2
- package/dist/types/src/shapes/Thread.d.ts.map +1 -1
- package/dist/types/src/shapes/Trigger.d.ts +3 -3
- package/dist/types/src/shapes/Trigger.d.ts.map +1 -1
- package/dist/types/src/shapes/append-def.d.ts +1 -2
- package/dist/types/src/shapes/append-def.d.ts.map +1 -1
- package/dist/types/src/shapes/array-def.d.ts +2 -3
- package/dist/types/src/shapes/array-def.d.ts.map +1 -1
- package/dist/types/src/shapes/audio-def.d.ts +1 -2
- package/dist/types/src/shapes/audio-def.d.ts.map +1 -1
- package/dist/types/src/shapes/beacon-def.d.ts +1 -2
- package/dist/types/src/shapes/beacon-def.d.ts.map +1 -1
- package/dist/types/src/shapes/chat-def.d.ts +1 -2
- package/dist/types/src/shapes/chat-def.d.ts.map +1 -1
- package/dist/types/src/shapes/common/Box.d.ts.map +1 -1
- package/dist/types/src/shapes/common/FunctionBody.d.ts.map +1 -1
- package/dist/types/src/shapes/common/TextBox.d.ts +26 -0
- package/dist/types/src/shapes/common/TextBox.d.ts.map +1 -0
- package/dist/types/src/shapes/common/index.d.ts +3 -1
- package/dist/types/src/shapes/common/index.d.ts.map +1 -1
- package/dist/types/src/shapes/common/node-def.d.ts +24 -0
- package/dist/types/src/shapes/common/node-def.d.ts.map +1 -0
- package/dist/types/src/shapes/common/ports.d.ts +33 -0
- package/dist/types/src/shapes/common/ports.d.ts.map +1 -0
- package/dist/types/src/shapes/constant-def.d.ts +1 -2
- package/dist/types/src/shapes/constant-def.d.ts.map +1 -1
- package/dist/types/src/shapes/database-def.d.ts +1 -2
- package/dist/types/src/shapes/database-def.d.ts.map +1 -1
- package/dist/types/src/shapes/defs.d.ts +7 -2
- package/dist/types/src/shapes/defs.d.ts.map +1 -1
- package/dist/types/src/shapes/feed-def.d.ts +1 -2
- package/dist/types/src/shapes/feed-def.d.ts.map +1 -1
- package/dist/types/src/shapes/function-def.d.ts +1 -2
- package/dist/types/src/shapes/function-def.d.ts.map +1 -1
- package/dist/types/src/shapes/gpt-def.d.ts +1 -2
- package/dist/types/src/shapes/gpt-def.d.ts.map +1 -1
- package/dist/types/src/shapes/gpt-realtime-def.d.ts +1 -2
- package/dist/types/src/shapes/gpt-realtime-def.d.ts.map +1 -1
- package/dist/types/src/shapes/json-def.d.ts +2 -3
- package/dist/types/src/shapes/json-def.d.ts.map +1 -1
- package/dist/types/src/shapes/logic-def.d.ts +2 -3
- package/dist/types/src/shapes/logic-def.d.ts.map +1 -1
- package/dist/types/src/shapes/ports.test.d.ts +2 -0
- package/dist/types/src/shapes/ports.test.d.ts.map +1 -0
- package/dist/types/src/shapes/rng-def.d.ts +1 -2
- package/dist/types/src/shapes/rng-def.d.ts.map +1 -1
- package/dist/types/src/shapes/scope-def.d.ts +1 -2
- package/dist/types/src/shapes/scope-def.d.ts.map +1 -1
- package/dist/types/src/shapes/surface-def.d.ts +1 -2
- package/dist/types/src/shapes/surface-def.d.ts.map +1 -1
- package/dist/types/src/shapes/switch-def.d.ts +1 -2
- package/dist/types/src/shapes/switch-def.d.ts.map +1 -1
- package/dist/types/src/shapes/table-def.d.ts +1 -2
- package/dist/types/src/shapes/table-def.d.ts.map +1 -1
- package/dist/types/src/shapes/template-def.d.ts +1 -2
- package/dist/types/src/shapes/template-def.d.ts.map +1 -1
- package/dist/types/src/shapes/text-def.d.ts +1 -2
- package/dist/types/src/shapes/text-def.d.ts.map +1 -1
- package/dist/types/src/shapes/text-to-image-def.d.ts +1 -2
- package/dist/types/src/shapes/text-to-image-def.d.ts.map +1 -1
- package/dist/types/src/shapes/thread-def.d.ts +1 -2
- package/dist/types/src/shapes/thread-def.d.ts.map +1 -1
- package/dist/types/src/shapes/trigger-def.d.ts +1 -2
- package/dist/types/src/shapes/trigger-def.d.ts.map +1 -1
- package/dist/types/src/testing/circuits.d.ts +1 -1
- package/dist/types/src/testing/circuits.d.ts.map +1 -1
- package/dist/types/src/types/canvas-board.test.d.ts +2 -0
- package/dist/types/src/types/canvas-board.test.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +4 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/types/model.d.ts +10 -0
- package/dist/types/src/types/model.d.ts.map +1 -0
- package/dist/types/src/types/schema.d.ts +66 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/types/shapes.d.ts +114 -0
- package/dist/types/src/types/shapes.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -35
- package/src/graph/controller.ts +1 -1
- package/src/hooks/compute-context.ts +7 -4
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useGraphMonitor.ts +10 -1
- package/src/index.ts +1 -1
- package/src/playwright/scene.spec.ts +3 -3
- package/src/registry.ts +63 -91
- package/src/scene/compute.stories.tsx +3 -6
- package/src/scene/echo-store.test.ts +18 -7
- package/src/scene/echo-store.ts +7 -10
- package/src/scene/index.ts +0 -3
- package/src/scene/projection.ts +2 -3
- package/src/scene/scene.test.ts +4 -4
- package/src/scene/scene.ts +6 -4
- package/src/schema.test.ts +7 -36
- package/src/shapes/Append.tsx +2 -3
- package/src/shapes/Array.tsx +3 -4
- package/src/shapes/Audio.tsx +2 -2
- package/src/shapes/Beacon.tsx +2 -2
- package/src/shapes/Boolean.tsx +32 -29
- package/src/shapes/Chat.tsx +5 -10
- package/src/shapes/Constant.tsx +19 -12
- package/src/shapes/Database.tsx +2 -4
- package/src/shapes/Feed.tsx +2 -3
- package/src/shapes/Function.tsx +4 -10
- package/src/shapes/Gpt.tsx +2 -3
- package/src/shapes/GptRealtime.tsx +2 -2
- package/src/shapes/Json.tsx +5 -6
- package/src/shapes/Logic.tsx +5 -6
- package/src/shapes/RNG.tsx +2 -2
- package/src/shapes/Scope.tsx +2 -2
- package/src/shapes/Surface.tsx +9 -6
- package/src/shapes/Switch.tsx +10 -12
- package/src/shapes/Table.tsx +2 -4
- package/src/shapes/Template.tsx +10 -9
- package/src/shapes/Text.tsx +2 -3
- package/src/shapes/TextToImage.tsx +2 -4
- package/src/shapes/Thread.tsx +2 -3
- package/src/shapes/Trigger.tsx +3 -4
- package/src/shapes/append-def.ts +8 -7
- package/src/shapes/array-def.ts +8 -8
- package/src/shapes/audio-def.ts +8 -7
- package/src/shapes/beacon-def.ts +9 -8
- package/src/shapes/chat-def.tsx +8 -8
- package/src/shapes/common/Box.tsx +7 -4
- package/src/shapes/common/FunctionBody.tsx +13 -26
- package/src/shapes/common/TextBox.tsx +137 -0
- package/src/shapes/common/index.ts +3 -1
- package/src/shapes/common/node-def.tsx +117 -0
- package/src/shapes/common/ports.ts +107 -0
- package/src/shapes/constant-def.tsx +8 -8
- package/src/shapes/database-def.ts +8 -8
- package/src/shapes/defs.ts +21 -6
- package/src/shapes/feed-def.ts +8 -7
- package/src/shapes/function-def.ts +8 -7
- package/src/shapes/gpt-def.ts +8 -7
- package/src/shapes/gpt-realtime-def.ts +10 -10
- package/src/shapes/json-def.ts +15 -13
- package/src/shapes/logic-def.ts +14 -11
- package/src/shapes/ports.test.ts +23 -0
- package/src/shapes/rng-def.ts +8 -7
- package/src/shapes/scope-def.ts +8 -7
- package/src/shapes/surface-def.ts +8 -8
- package/src/shapes/switch-def.ts +8 -7
- package/src/shapes/table-def.ts +7 -7
- package/src/shapes/template-def.ts +8 -7
- package/src/shapes/text-def.ts +8 -8
- package/src/shapes/text-to-image-def.ts +8 -8
- package/src/shapes/thread-def.ts +8 -7
- package/src/shapes/trigger-def.ts +11 -8
- package/src/testing/circuits.ts +21 -7
- package/src/types/canvas-board.test.ts +84 -0
- package/src/types/index.ts +7 -0
- package/src/types/model.ts +63 -0
- package/src/types/schema.ts +73 -0
- package/src/types/shapes.ts +73 -0
- package/dist/types/src/compute-layout.d.ts +0 -9
- package/dist/types/src/compute-layout.d.ts.map +0 -1
- package/dist/types/src/compute.stories.d.ts +0 -58
- package/dist/types/src/compute.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/useComputeGraphController.d.ts +0 -10
- package/dist/types/src/hooks/useComputeGraphController.d.ts.map +0 -1
- package/dist/types/src/scene/NodeView.d.ts +0 -11
- package/dist/types/src/scene/NodeView.d.ts.map +0 -1
- package/dist/types/src/scene/defs.d.ts +0 -5
- package/dist/types/src/scene/defs.d.ts.map +0 -1
- package/dist/types/src/scene/ports.d.ts +0 -5
- package/dist/types/src/scene/ports.d.ts.map +0 -1
- package/dist/types/src/shapes/common/function-anchors.d.ts +0 -12
- package/dist/types/src/shapes/common/function-anchors.d.ts.map +0 -1
- package/src/compute-layout.ts +0 -37
- package/src/compute.stories.tsx +0 -302
- package/src/hooks/useComputeGraphController.ts +0 -65
- package/src/scene/NodeView.tsx +0 -42
- package/src/scene/defs.ts +0 -158
- package/src/scene/ports.ts +0 -30
- package/src/shapes/common/function-anchors.ts +0 -44
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
//
|
|
6
|
+
// The compute shapes as scene-engine node types: the schema is the shape's with the engine's `z`, the view
|
|
7
|
+
// renders the shape's component under the engine's view props, and the ports come from the shape.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import * as Schema from 'effect/Schema';
|
|
11
|
+
import React, { type ComponentType, type FC } from 'react';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
type CreateProps,
|
|
15
|
+
DEFAULT_CELL,
|
|
16
|
+
type Node,
|
|
17
|
+
type NodeDef,
|
|
18
|
+
type NodeViewProps,
|
|
19
|
+
type Port,
|
|
20
|
+
type Size,
|
|
21
|
+
UnknownNodeView,
|
|
22
|
+
nodeDef,
|
|
23
|
+
} from '@dxos/react-ui-canvas/scene';
|
|
24
|
+
import * as Icon from '@dxos/react-ui/Icon';
|
|
25
|
+
|
|
26
|
+
import { ComputeNodeDefContext, useComputeContext } from '../../hooks/compute-context.ts';
|
|
27
|
+
import { useControllerUpdates } from '../../hooks/useControllerUpdates.ts';
|
|
28
|
+
import { type ComputeShape } from '../defs.ts';
|
|
29
|
+
|
|
30
|
+
/** The engine's view props with the node narrowed to the shape. */
|
|
31
|
+
export type ComputeNodeViewProps<S extends ComputeShape> = Omit<NodeViewProps, 'node'> & { node: S };
|
|
32
|
+
|
|
33
|
+
export type ComputeNodeSpec<S extends ComputeShape> = Pick<
|
|
34
|
+
NodeDef,
|
|
35
|
+
'name' | 'icon' | 'group' | 'resizable' | 'openable'
|
|
36
|
+
> & {
|
|
37
|
+
type: S['type'];
|
|
38
|
+
/** The shape's schema with `z` (`withZ`), so the node is checked as the shape before its view or ports read it. */
|
|
39
|
+
schema: Schema.Codec<S & { z: string }, unknown, never, never>;
|
|
40
|
+
component: FC<ComputeNodeViewProps<S>>;
|
|
41
|
+
/** The shape's factory; the engine's id, centre, size and z are applied over what it makes. */
|
|
42
|
+
create: (props: Pick<ComputeShape, 'id' | 'center'>) => S;
|
|
43
|
+
/** In scene px, as the shapes were designed; defaults to the size the factory gives a new shape. */
|
|
44
|
+
defaultSize?: Size;
|
|
45
|
+
ports: (shape: S) => readonly Port[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A size in scene px as nominal units (major cells) to the nearest half cell, so a new node lands close to that px
|
|
50
|
+
* size and its edges on the grid.
|
|
51
|
+
*/
|
|
52
|
+
const toNominal = ({ width, height }: Size): Size => ({
|
|
53
|
+
width: Math.round((width / DEFAULT_CELL) * 2) / 2,
|
|
54
|
+
height: Math.round((height / DEFAULT_CELL) * 2) / 2,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** A compute shape's node definition. */
|
|
58
|
+
export const defineComputeNode = <S extends ComputeShape>({
|
|
59
|
+
schema,
|
|
60
|
+
component,
|
|
61
|
+
create,
|
|
62
|
+
defaultSize,
|
|
63
|
+
ports,
|
|
64
|
+
...rest
|
|
65
|
+
}: ComputeNodeSpec<S>): NodeDef => {
|
|
66
|
+
const isShape = Schema.is(schema);
|
|
67
|
+
return {
|
|
68
|
+
...rest,
|
|
69
|
+
schema,
|
|
70
|
+
component: computeNodeView(isShape, component),
|
|
71
|
+
create: ({ id, z, center, size }: CreateProps): Node => ({ ...create({ id, center }), z, size }),
|
|
72
|
+
// The engine sizes a new node in nominal units (major grid cells), so the px size is stated in cells.
|
|
73
|
+
defaultSize: toNominal(defaultSize ?? create({ id: 'default', center: { x: 0, y: 0 } }).size),
|
|
74
|
+
ports: (node) => (isShape(node) ? ports(node) : []),
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The shape's component under the engine's view props, re-rendered on every controller update since the
|
|
80
|
+
* runtime state it reads is not reactive; a node that is not the shape renders as an unknown node.
|
|
81
|
+
*/
|
|
82
|
+
const computeNodeView = <S extends ComputeShape>(
|
|
83
|
+
isShape: (node: Node) => node is Node & S & { z: string },
|
|
84
|
+
Component: FC<ComputeNodeViewProps<S>>,
|
|
85
|
+
): ComponentType<NodeViewProps> => {
|
|
86
|
+
const View = (props: NodeViewProps) => {
|
|
87
|
+
const { node, registry } = props;
|
|
88
|
+
const { controller } = useComputeContext();
|
|
89
|
+
useControllerUpdates(controller);
|
|
90
|
+
if (!isShape(node)) {
|
|
91
|
+
return <UnknownNodeView {...props} />;
|
|
92
|
+
}
|
|
93
|
+
// A shape with no compute node behind it yet (the ghost of a create or palette drag) has no runtime state to
|
|
94
|
+
// read, so it shows what it will be rather than mounting the live component.
|
|
95
|
+
if (!node.node) {
|
|
96
|
+
const def = nodeDef(registry, node);
|
|
97
|
+
return (
|
|
98
|
+
<div className='dx-cover flex flex-col items-center justify-center gap-1 text-fg-muted'>
|
|
99
|
+
{def?.icon && <Icon.Icon icon={def.icon} size='lg' />}
|
|
100
|
+
<span className='text-sm truncate'>{def?.name ?? node.type}</span>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The components lay out as a full-size flex container (centring with `grow` / `w-full`); the engine's
|
|
106
|
+
// node frame is not one, so the wrapper provides it.
|
|
107
|
+
return (
|
|
108
|
+
<ComputeNodeDefContext.Provider value={nodeDef(registry, node)}>
|
|
109
|
+
<div className='dx-cover flex'>
|
|
110
|
+
<Component {...props} node={node} />
|
|
111
|
+
</div>
|
|
112
|
+
</ComputeNodeDefContext.Provider>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
View.displayName = `ComputeNodeView(${Component.displayName ?? Component.name})`;
|
|
116
|
+
return View;
|
|
117
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
import { VoidInput, VoidOutput } from '@dxos/conductor';
|
|
8
|
+
import * as SchemaAST from '@dxos/effect/SchemaAST';
|
|
9
|
+
import { DEFAULT_GRID, type Point, type Port, type Size } from '@dxos/react-ui-canvas/scene';
|
|
10
|
+
|
|
11
|
+
import { createAnchorId, parseAnchorId } from '../defs.ts';
|
|
12
|
+
import { footerHeight, headerHeight } from './box-defs.ts';
|
|
13
|
+
|
|
14
|
+
// Kept out of `FunctionBody.tsx`: react-refresh only fast-refreshes a module whose
|
|
15
|
+
// exports are all components, so values exported beside them force a full page reload on every edit.
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Height of one property row, two minor grid cells: with the rows centred in a body that is a whole number of rows,
|
|
19
|
+
* every row's centre, and so every port, lands on a minor grid line.
|
|
20
|
+
*/
|
|
21
|
+
export const rowHeight = 2 * DEFAULT_GRID;
|
|
22
|
+
|
|
23
|
+
/** A function shape's height: the chrome plus one row per input (at least one), each a whole number of rows. */
|
|
24
|
+
export const getHeight = (input: Schema.Schema<any>) => {
|
|
25
|
+
const properties = SchemaAST.getPropertySignatures(input.ast);
|
|
26
|
+
return headerHeight + footerHeight + Math.max(properties.length, 1) * rowHeight;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** `n` points one row apart, centred on `center`. */
|
|
30
|
+
export const getRowPoints = (center: Point, n: number): Point[] => {
|
|
31
|
+
const span = (n - 1) * rowHeight;
|
|
32
|
+
return [...Array(n)].map((_, index) => ({ x: center.x, y: center.y - span / 2 + index * rowHeight }));
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** A port at a point relative to the node's centre, in px. */
|
|
36
|
+
export type PortPoint = { id: string; pos: Point };
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The ports at the given points: each sits on whichever edge it is nearer to, at its offset along it; the
|
|
40
|
+
* id's `input.` / `output.` prefix says which way a link may run.
|
|
41
|
+
*/
|
|
42
|
+
export const pointsToPorts = (points: readonly PortPoint[], size: Size): Port[] =>
|
|
43
|
+
points.map(({ id, pos }) => {
|
|
44
|
+
const [kind] = parseAnchorId(id);
|
|
45
|
+
const half = { x: size.width / 2, y: size.height / 2 };
|
|
46
|
+
// Ties go to the vertical edges (the input / output sides).
|
|
47
|
+
const vertical = Math.abs(Math.abs(pos.x) - half.x) <= Math.abs(Math.abs(pos.y) - half.y);
|
|
48
|
+
const side = vertical ? (pos.x < 0 ? 'w' : 'e') : pos.y < 0 ? 'n' : 's';
|
|
49
|
+
const along = vertical ? (pos.y + half.y) / size.height : (pos.x + half.x) / size.width;
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
side,
|
|
53
|
+
offset: Math.min(Math.max(along, 0), 1),
|
|
54
|
+
...(kind === 'input' ? { accepts: 'in' as const } : kind === 'output' ? { accepts: 'out' as const } : {}),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/** Ports at points given in half-sizes from the centre (`{ x: 1, y: 0 }` is the middle of the east side). */
|
|
59
|
+
export const createPorts = (size: Size, points: Record<string, Point>): Port[] =>
|
|
60
|
+
pointsToPorts(
|
|
61
|
+
Object.entries(points).map(([id, { x, y }]) => ({
|
|
62
|
+
id,
|
|
63
|
+
pos: { x: (x * size.width) / 2, y: (y * size.height) / 2 },
|
|
64
|
+
})),
|
|
65
|
+
size,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
/** Inputs stacked one row apart down the west side and outputs down the east, centred on `center`. */
|
|
69
|
+
export const createRowPorts = ({
|
|
70
|
+
size,
|
|
71
|
+
inputs,
|
|
72
|
+
outputs,
|
|
73
|
+
center = { x: 0, y: 0 },
|
|
74
|
+
}: {
|
|
75
|
+
size: Size;
|
|
76
|
+
inputs: readonly string[];
|
|
77
|
+
outputs: readonly string[];
|
|
78
|
+
center?: Point;
|
|
79
|
+
}): Port[] =>
|
|
80
|
+
pointsToPorts(
|
|
81
|
+
[
|
|
82
|
+
...getRowPoints({ x: center.x - size.width / 2, y: center.y }, inputs.length).map((pos, index) => ({
|
|
83
|
+
id: inputs[index],
|
|
84
|
+
pos,
|
|
85
|
+
})),
|
|
86
|
+
...getRowPoints({ x: center.x + size.width / 2, y: center.y }, outputs.length).map((pos, index) => ({
|
|
87
|
+
id: outputs[index],
|
|
88
|
+
pos,
|
|
89
|
+
})),
|
|
90
|
+
],
|
|
91
|
+
size,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/** A function shape's ports: one per property of its input and output schemas, level with the body's rows. */
|
|
95
|
+
export const createFunctionPorts = (
|
|
96
|
+
size: Size,
|
|
97
|
+
input: Schema.Schema<any> = VoidInput,
|
|
98
|
+
output: Schema.Schema<any> = VoidOutput,
|
|
99
|
+
): Port[] => {
|
|
100
|
+
// TODO(burdon): Set type.
|
|
101
|
+
const inputs = SchemaAST.getPropertySignatures(input.ast).map(({ name }) => createAnchorId('input', name.toString()));
|
|
102
|
+
const outputs = SchemaAST.getPropertySignatures(output.ast).map(({ name }) =>
|
|
103
|
+
createAnchorId('output', name.toString()),
|
|
104
|
+
);
|
|
105
|
+
// The rows are centred in the body, between the header and the footer.
|
|
106
|
+
return createRowPorts({ size, inputs, outputs, center: { x: 0, y: (headerHeight - footerHeight) / 2 } });
|
|
107
|
+
};
|
|
@@ -6,11 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
import React from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import { createAnchorMap } from '@dxos/react-ui-canvas-editor';
|
|
11
|
-
|
|
9
|
+
import { createPorts, defineComputeNode } from './common/index.ts';
|
|
12
10
|
import { ConstantComponent } from './Constant.tsx';
|
|
13
|
-
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape } from './defs.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape, withZ } from './defs.ts';
|
|
14
12
|
|
|
15
13
|
// Kept out of `Constant.tsx`: react-refresh only fast-refreshes a module whose
|
|
16
14
|
// exports are all components, so values exported beside them force a full page reload on every edit.
|
|
@@ -37,12 +35,14 @@ export type CreateConstantProps = CreateShapeProps<ConstantShape>;
|
|
|
37
35
|
export const createConstant = (props: CreateConstantProps) =>
|
|
38
36
|
createShape<ConstantShape>({ type: 'constant', size: { width: 192, height: 128 }, ...props });
|
|
39
37
|
|
|
40
|
-
export const
|
|
38
|
+
export const constantNodeDef = defineComputeNode<ConstantShape>({
|
|
41
39
|
type: 'constant',
|
|
42
40
|
name: 'Value',
|
|
43
41
|
icon: 'ph--dots-three-circle--regular',
|
|
42
|
+
group: 'Inputs',
|
|
43
|
+
schema: withZ(ConstantShape),
|
|
44
44
|
component: (props) => <ConstantComponent {...props} placeholder={'Constant'} />,
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
create: createConstant,
|
|
46
|
+
ports: (shape) => createPorts(shape.size, { [createAnchorId('output')]: { x: 1, y: 0 } }),
|
|
47
47
|
resizable: true,
|
|
48
|
-
};
|
|
48
|
+
});
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { createAnchorMap } from '@dxos/react-ui-canvas-editor';
|
|
10
|
-
|
|
8
|
+
import { createPorts, defineComputeNode } from './common/index.ts';
|
|
11
9
|
import { DatabaseComponent } from './Database.tsx';
|
|
12
|
-
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape } from './defs.ts';
|
|
10
|
+
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape, withZ } from './defs.ts';
|
|
13
11
|
|
|
14
12
|
// Kept out of `Database.tsx`: react-refresh only fast-refreshes a module whose
|
|
15
13
|
// exports are all components, so values exported beside them force a full page reload on every edit.
|
|
@@ -27,11 +25,13 @@ export type CreateDatabaseProps = CreateShapeProps<DatabaseShape>;
|
|
|
27
25
|
export const createDatabase = (props: CreateDatabaseProps) =>
|
|
28
26
|
createShape<DatabaseShape>({ type: 'database', size: { width: 128, height: 64 }, ...props });
|
|
29
27
|
|
|
30
|
-
export const
|
|
28
|
+
export const databaseNodeDef = defineComputeNode<DatabaseShape>({
|
|
31
29
|
type: 'database',
|
|
32
30
|
name: 'ECHO',
|
|
33
31
|
icon: 'ph--database--regular',
|
|
32
|
+
group: 'Transform',
|
|
33
|
+
schema: withZ(DatabaseShape),
|
|
34
34
|
component: DatabaseComponent,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
35
|
+
create: createDatabase,
|
|
36
|
+
ports: (shape) => createPorts(shape.size, { [createAnchorId('output')]: { x: 1, y: 0 } }),
|
|
37
|
+
});
|
package/src/shapes/defs.ts
CHANGED
|
@@ -8,9 +8,11 @@ import * as Struct from 'effect/Struct';
|
|
|
8
8
|
import { DEFAULT_INPUT, DEFAULT_OUTPUT } from '@dxos/conductor';
|
|
9
9
|
import { Obj } from '@dxos/echo';
|
|
10
10
|
import * as SchemaAST from '@dxos/effect/SchemaAST';
|
|
11
|
-
import {
|
|
11
|
+
import { Point, Size } from '@dxos/react-ui-canvas/scene';
|
|
12
12
|
import { type MakeOptional } from '@dxos/util';
|
|
13
13
|
|
|
14
|
+
import { CanvasBoard } from '../types/index.ts';
|
|
15
|
+
|
|
14
16
|
//
|
|
15
17
|
// Properties
|
|
16
18
|
//
|
|
@@ -32,17 +34,30 @@ export const parseAnchorId = (id: string): [PropertyKind | undefined, string] =>
|
|
|
32
34
|
// Shapes
|
|
33
35
|
//
|
|
34
36
|
|
|
35
|
-
export type CreateShapeProps<S extends
|
|
37
|
+
export type CreateShapeProps<S extends ComputeShape> = Omit<MakeOptional<S, 'id' | 'size'>, 'type' | 'node'>;
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
// The board's stored shape (a closed shape with a centre and size, every key mutable), so boards the editor wrote
|
|
40
|
+
// still decode.
|
|
41
|
+
export const ComputeShape = CanvasBoard.Shape.mapFields(
|
|
38
42
|
Struct.assign({
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
center: Point,
|
|
44
|
+
size: Size.mapFields(Struct.map(Schema.mutableKey)),
|
|
41
45
|
}),
|
|
42
|
-
)
|
|
46
|
+
)
|
|
47
|
+
.mapFields(Struct.map(Schema.mutableKey))
|
|
48
|
+
.mapFields(
|
|
49
|
+
Struct.assign({
|
|
50
|
+
// TODO(burdon): Rename computeNode?
|
|
51
|
+
node: Schema.mutableKey(Schema.optional(Obj.ID.annotate({ description: 'Compute node id' }))),
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
43
54
|
|
|
44
55
|
export type ComputeShape = Schema.Schema.Type<typeof ComputeShape>;
|
|
45
56
|
|
|
57
|
+
/** The shape's schema with the engine's z-order key, so it is a node schema. */
|
|
58
|
+
export const withZ = <Fields extends Schema.Struct.Fields>(shape: Schema.Struct<Fields>) =>
|
|
59
|
+
shape.mapFields(Struct.assign({ z: Schema.String }));
|
|
60
|
+
|
|
46
61
|
export const createShape = <S extends ComputeShape>({ id, ...rest }: CreateShapeProps<S> & { type: string }): S => {
|
|
47
62
|
return {
|
|
48
63
|
id: id ?? Obj.ID.random(),
|
package/src/shapes/feed-def.ts
CHANGED
|
@@ -6,10 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
8
|
import { QueueInput, QueueOutput } from '@dxos/conductor';
|
|
9
|
-
import { type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { ComputeShape, type CreateShapeProps, createShape } from './defs.ts';
|
|
10
|
+
import { createFunctionPorts, defineComputeNode } from './common/index.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createShape, withZ } from './defs.ts';
|
|
13
12
|
import { FeedComponent } from './Feed.tsx';
|
|
14
13
|
|
|
15
14
|
// Kept out of `Feed.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -32,13 +31,15 @@ export const createFeed = (props: CreateFeedProps) =>
|
|
|
32
31
|
...props,
|
|
33
32
|
});
|
|
34
33
|
|
|
35
|
-
export const
|
|
34
|
+
export const feedNodeDef = defineComputeNode<FeedShape>({
|
|
36
35
|
// Must match the shape's `type` literal ('queue') so the registry resolves it; the registry is keyed by this.
|
|
37
36
|
type: 'queue',
|
|
38
37
|
name: 'Feed',
|
|
39
38
|
icon: 'ph--queue--regular',
|
|
39
|
+
group: 'Outputs',
|
|
40
|
+
schema: withZ(FeedShape),
|
|
40
41
|
component: FeedComponent,
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
create: createFeed,
|
|
43
|
+
ports: (shape) => createFunctionPorts(shape.size, QueueInput, QueueOutput),
|
|
43
44
|
resizable: true,
|
|
44
|
-
};
|
|
45
|
+
});
|
|
@@ -6,10 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
8
|
import { AnyOutput, FunctionInput } from '@dxos/conductor';
|
|
9
|
-
import { type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { ComputeShape, type CreateShapeProps, createShape } from './defs.ts';
|
|
10
|
+
import { createFunctionPorts, defineComputeNode } from './common/index.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createShape, withZ } from './defs.ts';
|
|
13
12
|
import { FunctionShapeComponent } from './Function.tsx';
|
|
14
13
|
|
|
15
14
|
// Kept out of `Function.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -36,11 +35,13 @@ export const createFunction = (props: CreateFunctionProps) =>
|
|
|
36
35
|
// Defs
|
|
37
36
|
//
|
|
38
37
|
|
|
39
|
-
export const
|
|
38
|
+
export const functionNodeDef = defineComputeNode<FunctionShape>({
|
|
40
39
|
type: 'function',
|
|
41
40
|
name: 'Function',
|
|
42
41
|
icon: 'ph--function--regular',
|
|
42
|
+
group: 'Transform',
|
|
43
|
+
schema: withZ(FunctionShape),
|
|
43
44
|
component: FunctionShapeComponent,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
};
|
|
45
|
+
create: createFunction,
|
|
46
|
+
ports: (shape) => createFunctionPorts(shape.size, FunctionInput, AnyOutput),
|
|
47
|
+
});
|
package/src/shapes/gpt-def.ts
CHANGED
|
@@ -6,10 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
8
|
import { GptInput, GptOutput } from '@dxos/conductor';
|
|
9
|
-
import { type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { ComputeShape, type CreateShapeProps, createShape } from './defs.ts';
|
|
10
|
+
import { createFunctionPorts, defineComputeNode, getHeight } from './common/index.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createShape, withZ } from './defs.ts';
|
|
13
12
|
import { GptComponent } from './Gpt.tsx';
|
|
14
13
|
|
|
15
14
|
// Kept out of `Gpt.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -32,12 +31,14 @@ export const createGpt = (props: CreateGptProps) =>
|
|
|
32
31
|
...props,
|
|
33
32
|
});
|
|
34
33
|
|
|
35
|
-
export const
|
|
34
|
+
export const gptNodeDef = defineComputeNode<GptShape>({
|
|
36
35
|
type: 'gpt',
|
|
37
36
|
name: 'GPT',
|
|
38
37
|
icon: 'ph--brain--regular',
|
|
38
|
+
group: 'Transform',
|
|
39
|
+
schema: withZ(GptShape),
|
|
39
40
|
component: GptComponent,
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
create: createGpt,
|
|
42
|
+
ports: (shape) => createFunctionPorts(shape.size, GptInput, GptOutput),
|
|
42
43
|
openable: true,
|
|
43
|
-
};
|
|
44
|
+
});
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { createFunctionAnchors } from './common/index.ts';
|
|
11
|
-
import { ComputeShape, type CreateShapeProps, createShape } from './defs.ts';
|
|
8
|
+
import { createFunctionPorts, defineComputeNode } from './common/index.ts';
|
|
9
|
+
import { ComputeShape, type CreateShapeProps, createShape, withZ } from './defs.ts';
|
|
12
10
|
import { GptRealtimeComponent } from './GptRealtime.tsx';
|
|
13
11
|
|
|
14
12
|
// Kept out of `GptRealtime.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -27,20 +25,22 @@ export type CreateGptRealtimeProps = CreateShapeProps<GptRealtimeShape>;
|
|
|
27
25
|
export const createGptRealtime = (props: CreateGptRealtimeProps) =>
|
|
28
26
|
createShape<GptRealtimeShape>({ type: 'gpt-realtime', size: { width: 256, height: 256 }, ...props });
|
|
29
27
|
|
|
30
|
-
export const
|
|
28
|
+
export const gptRealtimeNodeDef = defineComputeNode<GptRealtimeShape>({
|
|
31
29
|
type: 'gpt-realtime',
|
|
32
30
|
name: 'GPT Realtime',
|
|
33
31
|
icon: 'ph--pulse--regular',
|
|
32
|
+
group: 'Transform',
|
|
33
|
+
schema: withZ(GptRealtimeShape),
|
|
34
34
|
component: GptRealtimeComponent,
|
|
35
|
-
|
|
35
|
+
create: createGptRealtime,
|
|
36
36
|
// TODO(dmaretskyi): Can we fetch the schema dynamically?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
shape,
|
|
37
|
+
ports: (shape) =>
|
|
38
|
+
createFunctionPorts(
|
|
39
|
+
shape.size,
|
|
40
40
|
Schema.Struct({
|
|
41
41
|
audio: Schema.Any,
|
|
42
42
|
}),
|
|
43
43
|
Schema.Struct({}),
|
|
44
44
|
),
|
|
45
45
|
resizable: true,
|
|
46
|
-
};
|
|
46
|
+
});
|
package/src/shapes/json-def.ts
CHANGED
|
@@ -6,11 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
8
|
import { DefaultOutput, JsonTransformInput } from '@dxos/conductor';
|
|
9
|
-
import { type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
|
-
import { createAnchorMap } from '@dxos/react-ui-canvas-editor';
|
|
11
9
|
|
|
12
|
-
import {
|
|
13
|
-
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape } from './defs.ts';
|
|
10
|
+
import { createFunctionPorts, createPorts, defineComputeNode, getHeight } from './common/index.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape, withZ } from './defs.ts';
|
|
14
12
|
import { JsonComponent, JsonTransformComponent } from './Json.tsx';
|
|
15
13
|
|
|
16
14
|
// Kept out of `Json.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -45,19 +43,21 @@ export type CreateJsonProps = CreateShapeProps<JsonShape>;
|
|
|
45
43
|
export const createJson = (props: CreateJsonProps) =>
|
|
46
44
|
createShape<JsonShape>({ type: 'json', size: { width: 256, height: 256 }, ...props });
|
|
47
45
|
|
|
48
|
-
export const
|
|
46
|
+
export const jsonNodeDef = defineComputeNode<JsonShape>({
|
|
49
47
|
type: 'json',
|
|
50
48
|
name: 'JSON',
|
|
51
49
|
icon: 'ph--code--regular',
|
|
50
|
+
group: 'Outputs',
|
|
51
|
+
schema: withZ(JsonShape),
|
|
52
52
|
component: JsonComponent,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
create: createJson,
|
|
54
|
+
ports: (shape) =>
|
|
55
|
+
createPorts(shape.size, {
|
|
56
56
|
[createAnchorId('input')]: { x: -1, y: 0 },
|
|
57
57
|
[createAnchorId('output')]: { x: 1, y: 0 },
|
|
58
58
|
}),
|
|
59
59
|
resizable: true,
|
|
60
|
-
};
|
|
60
|
+
});
|
|
61
61
|
|
|
62
62
|
export type CreateJsonTransformProps = CreateShapeProps<JsonTransformShape> & { expression?: string };
|
|
63
63
|
|
|
@@ -68,12 +68,14 @@ export const createJsonTransform = (props: CreateJsonTransformProps) =>
|
|
|
68
68
|
...props,
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
export const
|
|
71
|
+
export const jsonTransformNodeDef = defineComputeNode<JsonTransformShape>({
|
|
72
72
|
type: 'json-transform',
|
|
73
73
|
name: 'Transform',
|
|
74
74
|
icon: 'ph--shuffle-simple--regular',
|
|
75
|
+
group: 'Operations',
|
|
76
|
+
schema: withZ(JsonTransformShape),
|
|
75
77
|
component: JsonTransformComponent,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
create: createJsonTransform,
|
|
79
|
+
ports: (shape) => createFunctionPorts(shape.size, JsonTransformInput, DefaultOutput),
|
|
78
80
|
resizable: true,
|
|
79
|
-
};
|
|
81
|
+
});
|
package/src/shapes/logic-def.ts
CHANGED
|
@@ -6,10 +6,9 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
8
|
import { IfElseInput, IfElseOutput, IfInput, IfOutput } from '@dxos/conductor';
|
|
9
|
-
import { type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { ComputeShape, type CreateShapeProps, createShape } from './defs.ts';
|
|
10
|
+
import { createFunctionPorts, defineComputeNode, getHeight } from './common/index.ts';
|
|
11
|
+
import { ComputeShape, type CreateShapeProps, createShape, withZ } from './defs.ts';
|
|
13
12
|
import { IfComponent, IfElseComponent } from './Logic.tsx';
|
|
14
13
|
|
|
15
14
|
// Kept out of `Logic.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -44,25 +43,29 @@ export type CreateIfProps = CreateShapeProps<IfShape> & { if?: string };
|
|
|
44
43
|
export const createIf = (props: CreateIfProps) =>
|
|
45
44
|
createShape<IfShape>({ type: 'if', size: { width: 192, height: getHeight(IfInput) }, ...props });
|
|
46
45
|
|
|
47
|
-
export const
|
|
46
|
+
export const ifNodeDef = defineComputeNode<IfShape>({
|
|
48
47
|
type: 'if',
|
|
49
48
|
name: 'IF',
|
|
50
49
|
icon: 'ph--arrows-split--regular',
|
|
50
|
+
group: 'Operations',
|
|
51
|
+
schema: withZ(IfShape),
|
|
51
52
|
component: IfComponent,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
53
|
+
create: createIf,
|
|
54
|
+
ports: (shape) => createFunctionPorts(shape.size, IfInput, IfOutput),
|
|
55
|
+
});
|
|
55
56
|
|
|
56
57
|
export type CreateIfElseProps = CreateShapeProps<IfShape> & { if?: string };
|
|
57
58
|
|
|
58
59
|
export const createIfElse = (props: CreateIfElseProps) =>
|
|
59
60
|
createShape<IfElseShape>({ type: 'if-else', size: { width: 192, height: getHeight(IfElseInput) }, ...props });
|
|
60
61
|
|
|
61
|
-
export const
|
|
62
|
+
export const ifElseNodeDef = defineComputeNode<IfElseShape>({
|
|
62
63
|
type: 'if-else',
|
|
63
64
|
name: 'IF/ELSE',
|
|
64
65
|
icon: 'ph--arrows-merge--regular',
|
|
66
|
+
group: 'Operations',
|
|
67
|
+
schema: withZ(IfElseShape),
|
|
65
68
|
component: IfElseComponent,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
+
create: createIfElse,
|
|
70
|
+
ports: (shape) => createFunctionPorts(shape.size, IfElseInput, IfElseOutput),
|
|
71
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { DEFAULT_GRID, nominalSize } from '@dxos/react-ui-canvas/scene';
|
|
8
|
+
|
|
9
|
+
import { computeNodeDefs } from '../registry.ts';
|
|
10
|
+
|
|
11
|
+
describe('compute ports', () => {
|
|
12
|
+
test('every port of a new shape sits on a minor grid line', ({ expect }) => {
|
|
13
|
+
for (const def of computeNodeDefs) {
|
|
14
|
+
const size = nominalSize(def.defaultSize);
|
|
15
|
+
const node = def.create({ id: 'n', z: 'a0', center: { x: size.width / 2, y: size.height / 2 }, size });
|
|
16
|
+
for (const port of def.ports?.(node) ?? []) {
|
|
17
|
+
const along = port.side === 'w' || port.side === 'e' ? size.height : size.width;
|
|
18
|
+
// Distance from the node's top (or left) edge, which snaps to the grid.
|
|
19
|
+
expect(`${def.type} ${port.id} ${(port.offset * along) % DEFAULT_GRID}`).toBe(`${def.type} ${port.id} 0`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
package/src/shapes/rng-def.ts
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import * as Struct from 'effect/Struct';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape } from './defs.ts';
|
|
8
|
+
import { createPorts, defineComputeNode } from './common/index.ts';
|
|
9
|
+
import { ComputeShape, type CreateShapeProps, createAnchorId, createShape, withZ } from './defs.ts';
|
|
11
10
|
import { RandomComponent } from './RNG.tsx';
|
|
12
11
|
|
|
13
12
|
// Kept out of `RNG.tsx`: react-refresh only fast-refreshes a module whose
|
|
@@ -32,11 +31,13 @@ export const createRandom = (props: CreateRandomProps) =>
|
|
|
32
31
|
...props,
|
|
33
32
|
});
|
|
34
33
|
|
|
35
|
-
export const
|
|
34
|
+
export const randomNodeDef = defineComputeNode<RandomShape>({
|
|
36
35
|
type: 'rng',
|
|
37
36
|
name: 'Random',
|
|
38
37
|
icon: 'ph--dice-six--regular',
|
|
38
|
+
group: 'Inputs',
|
|
39
|
+
schema: withZ(RandomShape),
|
|
39
40
|
component: RandomComponent,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
};
|
|
41
|
+
create: createRandom,
|
|
42
|
+
ports: (shape) => createPorts(shape.size, { [createAnchorId('output')]: { x: 1, y: 0 } }),
|
|
43
|
+
});
|