@dxos/react-ui-canvas-compute 0.7.5-labs.5f04cf6
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/LICENSE +8 -0
- package/README.md +1 -0
- package/dist/lib/browser/index.mjs +2499 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/node/index.cjs +2591 -0
- package/dist/lib/node/index.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -0
- package/dist/lib/node-esm/index.mjs +2499 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/types/src/compute-layout.d.ts +9 -0
- package/dist/types/src/compute-layout.d.ts.map +1 -0
- package/dist/types/src/compute.stories.d.ts +28 -0
- package/dist/types/src/compute.stories.d.ts.map +1 -0
- package/dist/types/src/graph/controller.d.ts +139 -0
- package/dist/types/src/graph/controller.d.ts.map +1 -0
- package/dist/types/src/graph/index.d.ts +3 -0
- package/dist/types/src/graph/index.d.ts.map +1 -0
- package/dist/types/src/graph/node-defs.d.ts +6 -0
- package/dist/types/src/graph/node-defs.d.ts.map +1 -0
- package/dist/types/src/hooks/compute-context.d.ts +7 -0
- package/dist/types/src/hooks/compute-context.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeNodeState.d.ts +19 -0
- package/dist/types/src/hooks/useComputeNodeState.d.ts.map +1 -0
- package/dist/types/src/hooks/useGraphMonitor.d.ts +14 -0
- package/dist/types/src/hooks/useGraphMonitor.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +6 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/json.test.d.ts +21 -0
- package/dist/types/src/json.test.d.ts.map +1 -0
- package/dist/types/src/registry.d.ts +9 -0
- package/dist/types/src/registry.d.ts.map +1 -0
- package/dist/types/src/schema.test.d.ts +2 -0
- package/dist/types/src/schema.test.d.ts.map +1 -0
- package/dist/types/src/shapes/Append.d.ts +54 -0
- package/dist/types/src/shapes/Append.d.ts.map +1 -0
- package/dist/types/src/shapes/Array.d.ts +38 -0
- package/dist/types/src/shapes/Array.d.ts.map +1 -0
- package/dist/types/src/shapes/Audio.d.ts +54 -0
- package/dist/types/src/shapes/Audio.d.ts.map +1 -0
- package/dist/types/src/shapes/Beacon.d.ts +54 -0
- package/dist/types/src/shapes/Beacon.d.ts.map +1 -0
- package/dist/types/src/shapes/Boolean.d.ts +233 -0
- package/dist/types/src/shapes/Boolean.d.ts.map +1 -0
- package/dist/types/src/shapes/Chat.d.ts +57 -0
- package/dist/types/src/shapes/Chat.d.ts.map +1 -0
- package/dist/types/src/shapes/Constant.d.ts +60 -0
- package/dist/types/src/shapes/Constant.d.ts.map +1 -0
- package/dist/types/src/shapes/Database.d.ts +54 -0
- package/dist/types/src/shapes/Database.d.ts.map +1 -0
- package/dist/types/src/shapes/Function.d.ts +54 -0
- package/dist/types/src/shapes/Function.d.ts.map +1 -0
- package/dist/types/src/shapes/Gpt.d.ts +54 -0
- package/dist/types/src/shapes/Gpt.d.ts.map +1 -0
- package/dist/types/src/shapes/GptRealtime.d.ts +54 -0
- package/dist/types/src/shapes/GptRealtime.d.ts.map +1 -0
- package/dist/types/src/shapes/Json.d.ts +107 -0
- package/dist/types/src/shapes/Json.d.ts.map +1 -0
- package/dist/types/src/shapes/Logic.d.ts +109 -0
- package/dist/types/src/shapes/Logic.d.ts.map +1 -0
- package/dist/types/src/shapes/Queue.d.ts +58 -0
- package/dist/types/src/shapes/Queue.d.ts.map +1 -0
- package/dist/types/src/shapes/RNG.d.ts +58 -0
- package/dist/types/src/shapes/RNG.d.ts.map +1 -0
- package/dist/types/src/shapes/Scope.d.ts +54 -0
- package/dist/types/src/shapes/Scope.d.ts.map +1 -0
- package/dist/types/src/shapes/Surface.d.ts +54 -0
- package/dist/types/src/shapes/Surface.d.ts.map +1 -0
- package/dist/types/src/shapes/Switch.d.ts +54 -0
- package/dist/types/src/shapes/Switch.d.ts.map +1 -0
- package/dist/types/src/shapes/Table.d.ts +54 -0
- package/dist/types/src/shapes/Table.d.ts.map +1 -0
- package/dist/types/src/shapes/Template.d.ts +56 -0
- package/dist/types/src/shapes/Template.d.ts.map +1 -0
- package/dist/types/src/shapes/Text.d.ts +54 -0
- package/dist/types/src/shapes/Text.d.ts.map +1 -0
- package/dist/types/src/shapes/TextToImage.d.ts +54 -0
- package/dist/types/src/shapes/TextToImage.d.ts.map +1 -0
- package/dist/types/src/shapes/Thread.d.ts +58 -0
- package/dist/types/src/shapes/Thread.d.ts.map +1 -0
- package/dist/types/src/shapes/Trigger.d.ts +64 -0
- package/dist/types/src/shapes/Trigger.d.ts.map +1 -0
- package/dist/types/src/shapes/common/Box.d.ts +25 -0
- package/dist/types/src/shapes/common/Box.d.ts.map +1 -0
- package/dist/types/src/shapes/common/FunctionBody.d.ts +15 -0
- package/dist/types/src/shapes/common/FunctionBody.d.ts.map +1 -0
- package/dist/types/src/shapes/common/TypeSelect.d.ts +4 -0
- package/dist/types/src/shapes/common/TypeSelect.d.ts.map +1 -0
- package/dist/types/src/shapes/common/index.d.ts +4 -0
- package/dist/types/src/shapes/common/index.d.ts.map +1 -0
- package/dist/types/src/shapes/defs.d.ts +39 -0
- package/dist/types/src/shapes/defs.d.ts.map +1 -0
- package/dist/types/src/shapes/index.d.ts +27 -0
- package/dist/types/src/shapes/index.d.ts.map +1 -0
- package/dist/types/src/testing/circuits.d.ts +193 -0
- package/dist/types/src/testing/circuits.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +85 -0
- package/src/README.md +47 -0
- package/src/compute-layout.ts +37 -0
- package/src/compute.stories.tsx +362 -0
- package/src/graph/controller.ts +405 -0
- package/src/graph/index.ts +6 -0
- package/src/graph/node-defs.ts +82 -0
- package/src/hooks/compute-context.ts +19 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/useComputeNodeState.ts +83 -0
- package/src/hooks/useGraphMonitor.ts +133 -0
- package/src/index.ts +9 -0
- package/src/json.test.ts +35 -0
- package/src/registry.ts +100 -0
- package/src/schema.test.ts +62 -0
- package/src/shapes/Append.tsx +43 -0
- package/src/shapes/Array.tsx +61 -0
- package/src/shapes/Audio.tsx +55 -0
- package/src/shapes/Beacon.tsx +56 -0
- package/src/shapes/Boolean.tsx +215 -0
- package/src/shapes/Chat.tsx +77 -0
- package/src/shapes/Constant.tsx +125 -0
- package/src/shapes/Database.tsx +39 -0
- package/src/shapes/Function.tsx +40 -0
- package/src/shapes/Gpt.tsx +91 -0
- package/src/shapes/GptRealtime.tsx +168 -0
- package/src/shapes/Json.tsx +103 -0
- package/src/shapes/Logic.tsx +82 -0
- package/src/shapes/Queue.tsx +78 -0
- package/src/shapes/RNG.tsx +84 -0
- package/src/shapes/Scope.tsx +54 -0
- package/src/shapes/Surface.tsx +57 -0
- package/src/shapes/Switch.tsx +53 -0
- package/src/shapes/Table.tsx +45 -0
- package/src/shapes/Template.tsx +98 -0
- package/src/shapes/Text.tsx +56 -0
- package/src/shapes/TextToImage.tsx +39 -0
- package/src/shapes/Thread.tsx +87 -0
- package/src/shapes/Trigger.tsx +152 -0
- package/src/shapes/common/Box.tsx +74 -0
- package/src/shapes/common/FunctionBody.tsx +122 -0
- package/src/shapes/common/TypeSelect.tsx +27 -0
- package/src/shapes/common/index.ts +7 -0
- package/src/shapes/defs.ts +50 -0
- package/src/shapes/index.ts +31 -0
- package/src/testing/circuits.ts +320 -0
- package/src/testing/index.ts +5 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { createInputSchema, createOutputSchema, GptMessage } from '@dxos/conductor';
|
|
8
|
+
import { S } from '@dxos/echo-schema';
|
|
9
|
+
import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
|
+
|
|
11
|
+
import { createFunctionAnchors, Box } from './common';
|
|
12
|
+
import { ComputeShape, createShape, type CreateShapeProps } from './defs';
|
|
13
|
+
|
|
14
|
+
const InputSchema = createInputSchema(GptMessage);
|
|
15
|
+
const OutputSchema = createOutputSchema(S.mutable(S.Array(GptMessage)));
|
|
16
|
+
|
|
17
|
+
export const TableShape = S.extend(
|
|
18
|
+
ComputeShape,
|
|
19
|
+
S.Struct({
|
|
20
|
+
type: S.Literal('table'),
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export type TableShape = S.Schema.Type<typeof TableShape>;
|
|
25
|
+
|
|
26
|
+
export type CreateTableProps = CreateShapeProps<TableShape>;
|
|
27
|
+
|
|
28
|
+
export const createTable = (props: CreateTableProps) =>
|
|
29
|
+
createShape<TableShape>({ type: 'table', size: { width: 320, height: 512 }, ...props });
|
|
30
|
+
|
|
31
|
+
export const TableComponent = ({ shape }: ShapeComponentProps<TableShape>) => {
|
|
32
|
+
// const items = shape.node.items.value;
|
|
33
|
+
|
|
34
|
+
return <Box shape={shape}></Box>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const tableShape: ShapeDef<TableShape> = {
|
|
38
|
+
type: 'table',
|
|
39
|
+
name: 'Table',
|
|
40
|
+
icon: 'ph--table--regular',
|
|
41
|
+
component: TableComponent,
|
|
42
|
+
createShape: createTable,
|
|
43
|
+
getAnchors: (shape) => createFunctionAnchors(shape, InputSchema, OutputSchema),
|
|
44
|
+
resizable: true,
|
|
45
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { ComputeValueType, getTemplateInputSchema, TemplateOutput, VoidInput } from '@dxos/conductor';
|
|
8
|
+
import { S, toJsonSchema } from '@dxos/echo-schema';
|
|
9
|
+
import { invariant } from '@dxos/invariant';
|
|
10
|
+
import {
|
|
11
|
+
type ShapeComponentProps,
|
|
12
|
+
type ShapeDef,
|
|
13
|
+
TextBox,
|
|
14
|
+
type TextBoxControl,
|
|
15
|
+
type TextBoxProps,
|
|
16
|
+
} from '@dxos/react-ui-canvas-editor';
|
|
17
|
+
|
|
18
|
+
import { Box, createFunctionAnchors, TypeSelect } from './common';
|
|
19
|
+
import { ComputeShape, createShape, type CreateShapeProps } from './defs';
|
|
20
|
+
import { useComputeNodeState } from '../hooks';
|
|
21
|
+
|
|
22
|
+
//
|
|
23
|
+
// Data
|
|
24
|
+
//
|
|
25
|
+
|
|
26
|
+
export const TemplateShape = S.extend(
|
|
27
|
+
ComputeShape,
|
|
28
|
+
S.Struct({
|
|
29
|
+
type: S.Literal('template'),
|
|
30
|
+
valueType: S.optional(ComputeValueType),
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export type TemplateShape = S.Schema.Type<typeof TemplateShape>;
|
|
35
|
+
|
|
36
|
+
//
|
|
37
|
+
// Component
|
|
38
|
+
//
|
|
39
|
+
|
|
40
|
+
type TextInputComponentProps = ShapeComponentProps<TemplateShape> & TextBoxProps & { title?: string };
|
|
41
|
+
|
|
42
|
+
const TextInputComponent = ({ shape, title, ...props }: TextInputComponentProps) => {
|
|
43
|
+
const { node } = useComputeNodeState(shape);
|
|
44
|
+
const inputRef = useRef<TextBoxControl>(null);
|
|
45
|
+
|
|
46
|
+
const handleEnter: TextBoxProps['onEnter'] = (text) => {
|
|
47
|
+
const value = text.trim();
|
|
48
|
+
if (value.length) {
|
|
49
|
+
const schema = getTemplateInputSchema(node);
|
|
50
|
+
|
|
51
|
+
node.value = value;
|
|
52
|
+
node.inputSchema = toJsonSchema(schema);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleTypeChange = (newType: string) => {
|
|
57
|
+
invariant(S.is(ComputeValueType)(newType), 'Invalid type');
|
|
58
|
+
|
|
59
|
+
node.valueType = newType;
|
|
60
|
+
node.inputSchema = toJsonSchema(getTemplateInputSchema(node));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Box
|
|
65
|
+
shape={shape}
|
|
66
|
+
title={'Template'}
|
|
67
|
+
status={<TypeSelect value={node.valueType ?? 'string'} onValueChange={handleTypeChange} />}
|
|
68
|
+
>
|
|
69
|
+
<TextBox
|
|
70
|
+
{...props}
|
|
71
|
+
ref={inputRef}
|
|
72
|
+
value={node.value}
|
|
73
|
+
language={node.valueType === 'object' ? 'json' : undefined}
|
|
74
|
+
onBlur={handleEnter}
|
|
75
|
+
onEnter={handleEnter}
|
|
76
|
+
/>
|
|
77
|
+
</Box>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
//
|
|
82
|
+
// Defs
|
|
83
|
+
//
|
|
84
|
+
|
|
85
|
+
export type CreateTemplateProps = CreateShapeProps<TemplateShape> & { text?: string };
|
|
86
|
+
|
|
87
|
+
export const createTemplate = (props: CreateTemplateProps) =>
|
|
88
|
+
createShape<TemplateShape>({ type: 'template', size: { width: 256, height: 384 }, ...props });
|
|
89
|
+
|
|
90
|
+
export const templateShape: ShapeDef<TemplateShape> = {
|
|
91
|
+
type: 'template',
|
|
92
|
+
name: 'Template',
|
|
93
|
+
icon: 'ph--article--regular',
|
|
94
|
+
component: (props) => <TextInputComponent {...props} placeholder={'Prompt'} />,
|
|
95
|
+
createShape: createTemplate,
|
|
96
|
+
getAnchors: (shape) => createFunctionAnchors(shape, VoidInput, TemplateOutput),
|
|
97
|
+
resizable: true,
|
|
98
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { DEFAULT_INPUT } from '@dxos/conductor';
|
|
8
|
+
import { S } from '@dxos/echo-schema';
|
|
9
|
+
import { TextBox, type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
|
+
import { createAnchorMap } from '@dxos/react-ui-canvas-editor';
|
|
11
|
+
|
|
12
|
+
import { Box, type BoxActionHandler } from './common';
|
|
13
|
+
import { ComputeShape, createAnchorId, createShape, type CreateShapeProps } from './defs';
|
|
14
|
+
import { useComputeNodeState } from '../hooks';
|
|
15
|
+
|
|
16
|
+
export const TextShape = S.extend(
|
|
17
|
+
ComputeShape,
|
|
18
|
+
S.Struct({
|
|
19
|
+
type: S.Literal('text'),
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type TextShape = S.Schema.Type<typeof TextShape>;
|
|
24
|
+
|
|
25
|
+
export type CreateTextProps = CreateShapeProps<TextShape>;
|
|
26
|
+
|
|
27
|
+
export const createText = (props: CreateTextProps) =>
|
|
28
|
+
createShape<TextShape>({ type: 'text', size: { width: 384, height: 384 }, ...props });
|
|
29
|
+
|
|
30
|
+
export const TextComponent = ({ shape }: ShapeComponentProps<TextShape>) => {
|
|
31
|
+
const { runtime } = useComputeNodeState(shape);
|
|
32
|
+
const input = runtime.inputs[DEFAULT_INPUT];
|
|
33
|
+
const value = input?.type === 'executed' ? input.value : 0;
|
|
34
|
+
|
|
35
|
+
const handleAction: BoxActionHandler = (action) => {
|
|
36
|
+
if (action === 'run') {
|
|
37
|
+
runtime.evalNode();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Box shape={shape} onAction={handleAction}>
|
|
43
|
+
<TextBox value={value} />
|
|
44
|
+
</Box>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const textShape: ShapeDef<TextShape> = {
|
|
49
|
+
type: 'text',
|
|
50
|
+
name: 'Text',
|
|
51
|
+
icon: 'ph--article--regular',
|
|
52
|
+
component: TextComponent,
|
|
53
|
+
createShape: createText,
|
|
54
|
+
getAnchors: (shape) => createAnchorMap(shape, { [createAnchorId('input')]: { x: -1, y: 0 } }),
|
|
55
|
+
resizable: true,
|
|
56
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { S } from '@dxos/echo-schema';
|
|
8
|
+
import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
9
|
+
import { createAnchorMap } from '@dxos/react-ui-canvas-editor';
|
|
10
|
+
|
|
11
|
+
import { Box } from './common';
|
|
12
|
+
import { ComputeShape, createAnchorId, createShape, type CreateShapeProps } from './defs';
|
|
13
|
+
|
|
14
|
+
export const TextToImageShape = S.extend(
|
|
15
|
+
ComputeShape,
|
|
16
|
+
S.Struct({
|
|
17
|
+
type: S.Literal('text-to-image'),
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export type TextToImageShape = S.Schema.Type<typeof TextToImageShape>;
|
|
22
|
+
|
|
23
|
+
export type CreateTextToImageProps = CreateShapeProps<TextToImageShape>;
|
|
24
|
+
|
|
25
|
+
export const createTextToImage = (props: CreateTextToImageProps) =>
|
|
26
|
+
createShape<TextToImageShape>({ type: 'text-to-image', size: { width: 128, height: 64 }, ...props });
|
|
27
|
+
|
|
28
|
+
export const TextToImageComponent = ({ shape }: ShapeComponentProps<TextToImageShape>) => {
|
|
29
|
+
return <Box shape={shape} />;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const textToImageShape: ShapeDef<TextToImageShape> = {
|
|
33
|
+
type: 'text-to-image',
|
|
34
|
+
name: 'Image',
|
|
35
|
+
icon: 'ph--image--regular',
|
|
36
|
+
component: TextToImageComponent,
|
|
37
|
+
createShape: createTextToImage,
|
|
38
|
+
getAnchors: (shape) => createAnchorMap(shape, { [createAnchorId('output')]: { x: 1, y: 0 } }),
|
|
39
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect, useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
import { createInputSchema, createOutputSchema, GptMessage } from '@dxos/conductor';
|
|
8
|
+
import { S } from '@dxos/echo-schema';
|
|
9
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
10
|
+
import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
11
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
12
|
+
|
|
13
|
+
import { createFunctionAnchors, Box } from './common';
|
|
14
|
+
import { ComputeShape, createShape, type CreateShapeProps } from './defs';
|
|
15
|
+
|
|
16
|
+
const InputSchema = createInputSchema(GptMessage);
|
|
17
|
+
const OutputSchema = createOutputSchema(S.mutable(S.Array(GptMessage)));
|
|
18
|
+
|
|
19
|
+
export const ThreadShape = S.extend(
|
|
20
|
+
ComputeShape,
|
|
21
|
+
S.Struct({
|
|
22
|
+
type: S.Literal('thread'),
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export type ThreadShape = S.Schema.Type<typeof ThreadShape>;
|
|
27
|
+
|
|
28
|
+
export type CreateThreadProps = CreateShapeProps<ThreadShape>;
|
|
29
|
+
|
|
30
|
+
export const createThread = (props: CreateThreadProps) =>
|
|
31
|
+
createShape<ThreadShape>({ type: 'thread', size: { width: 384, height: 384 }, ...props });
|
|
32
|
+
|
|
33
|
+
export const ThreadComponent = ({ shape }: ShapeComponentProps<ThreadShape>) => {
|
|
34
|
+
const items: any[] = [];
|
|
35
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (scrollRef.current) {
|
|
38
|
+
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
39
|
+
}
|
|
40
|
+
}, [items]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Box shape={shape}>
|
|
44
|
+
<div ref={scrollRef} className='flex flex-col w-full overflow-y-scroll gap-2 p-2'>
|
|
45
|
+
{[...items].map((item, i) => (
|
|
46
|
+
<ThreadItem key={i} item={item} />
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
</Box>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const ThreadItem = ({ classNames, item }: ThemedClassName<{ item: any }>) => {
|
|
54
|
+
if (typeof item !== 'object') {
|
|
55
|
+
return <div className={mx(classNames)}>{item}</div>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// TODO(burdon): Hack; introspect type.
|
|
59
|
+
// TODO(burdon): Markdown parser.
|
|
60
|
+
const { role, message } = item;
|
|
61
|
+
return (
|
|
62
|
+
<div className={mx('flex', classNames, role === 'user' && 'justify-end')}>
|
|
63
|
+
<div
|
|
64
|
+
className={mx(
|
|
65
|
+
'block rounded-md p-1 px-2 text-sm',
|
|
66
|
+
role === 'user'
|
|
67
|
+
? 'bg-blue-100 dark:bg-blue-800'
|
|
68
|
+
: role === 'system'
|
|
69
|
+
? 'bg-red-100, dark:bg-red-800'
|
|
70
|
+
: 'whitespace-pre-wrap bg-neutral-50 dark:bg-neutral-800',
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
{message}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const threadShape: ShapeDef<ThreadShape> = {
|
|
80
|
+
type: 'thread',
|
|
81
|
+
name: 'Thread',
|
|
82
|
+
icon: 'ph--chats-circle--regular',
|
|
83
|
+
component: ThreadComponent,
|
|
84
|
+
createShape: createThread,
|
|
85
|
+
getAnchors: (shape) => createFunctionAnchors(shape, InputSchema, OutputSchema),
|
|
86
|
+
resizable: true,
|
|
87
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
EmailTriggerOutput,
|
|
9
|
+
SubscriptionTriggerOutput,
|
|
10
|
+
TimerTriggerOutput,
|
|
11
|
+
VoidInput,
|
|
12
|
+
WebhookTriggerOutput,
|
|
13
|
+
} from '@dxos/conductor';
|
|
14
|
+
import { Ref, S } from '@dxos/echo-schema';
|
|
15
|
+
import {
|
|
16
|
+
type EmailTrigger,
|
|
17
|
+
FunctionTrigger,
|
|
18
|
+
type SubscriptionTrigger,
|
|
19
|
+
type TimerTrigger,
|
|
20
|
+
TriggerKind,
|
|
21
|
+
type TriggerType,
|
|
22
|
+
type WebhookTrigger,
|
|
23
|
+
} from '@dxos/functions';
|
|
24
|
+
import { create, makeRef } from '@dxos/react-client/echo';
|
|
25
|
+
import { Select, type SelectRootProps } from '@dxos/react-ui';
|
|
26
|
+
import { type ShapeComponentProps, type ShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
27
|
+
|
|
28
|
+
import { createFunctionAnchors, FunctionBody, getHeight } from './common';
|
|
29
|
+
import { ComputeShape, createShape, type CreateShapeProps } from './defs';
|
|
30
|
+
|
|
31
|
+
export const TriggerShape = S.extend(
|
|
32
|
+
ComputeShape,
|
|
33
|
+
S.Struct({
|
|
34
|
+
type: S.Literal('trigger'),
|
|
35
|
+
functionTrigger: S.optional(Ref(FunctionTrigger)),
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
export type TriggerShape = S.Schema.Type<typeof TriggerShape>;
|
|
39
|
+
|
|
40
|
+
export type CreateTriggerProps = CreateShapeProps<Omit<TriggerShape, 'functionTrigger'>> & {
|
|
41
|
+
triggerKind?: TriggerKind;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const createTrigger = (props: CreateTriggerProps): TriggerShape => {
|
|
45
|
+
const functionTrigger = create(FunctionTrigger, {
|
|
46
|
+
enabled: true,
|
|
47
|
+
spec: createTriggerSpec(props.triggerKind ?? TriggerKind.Email),
|
|
48
|
+
});
|
|
49
|
+
return createShape<TriggerShape>({
|
|
50
|
+
type: 'trigger',
|
|
51
|
+
functionTrigger: makeRef(functionTrigger),
|
|
52
|
+
size: { width: 192, height: getHeight(EmailTriggerOutput) },
|
|
53
|
+
...props,
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type TriggerComponentProps = ShapeComponentProps<TriggerShape>;
|
|
58
|
+
|
|
59
|
+
export const TriggerComponent = ({ shape }: TriggerComponentProps) => {
|
|
60
|
+
const functionTrigger = shape.functionTrigger?.target;
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (functionTrigger && !functionTrigger.spec) {
|
|
64
|
+
functionTrigger.spec = createTriggerSpec(TriggerKind.Email);
|
|
65
|
+
}
|
|
66
|
+
}, [functionTrigger, functionTrigger?.spec]);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
shape.size.height = getHeight(getOutputSchema(functionTrigger?.spec?.type ?? TriggerKind.Email));
|
|
70
|
+
}, [functionTrigger?.spec?.type]);
|
|
71
|
+
|
|
72
|
+
const setKind = (kind: TriggerKind) => {
|
|
73
|
+
if (functionTrigger?.spec?.type !== kind) {
|
|
74
|
+
functionTrigger!.spec = createTriggerSpec(kind);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (!functionTrigger?.spec) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<FunctionBody
|
|
84
|
+
shape={shape}
|
|
85
|
+
status={
|
|
86
|
+
<TriggerKindSelect value={functionTrigger.spec?.type} onValueChange={(kind) => setKind(kind as TriggerKind)} />
|
|
87
|
+
}
|
|
88
|
+
inputSchema={VoidInput}
|
|
89
|
+
outputSchema={getOutputSchema(functionTrigger.spec!.type!)}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// TODO(burdon): Factor out.
|
|
95
|
+
const TriggerKindSelect = ({ value, onValueChange }: Pick<SelectRootProps, 'value' | 'onValueChange'>) => {
|
|
96
|
+
return (
|
|
97
|
+
<Select.Root value={value} onValueChange={onValueChange}>
|
|
98
|
+
<Select.TriggerButton variant='ghost' classNames='w-full !px-0' />
|
|
99
|
+
<Select.Portal>
|
|
100
|
+
<Select.Content>
|
|
101
|
+
<Select.ScrollUpButton />
|
|
102
|
+
<Select.Viewport>
|
|
103
|
+
{Object.values(TriggerKind).map((type) => (
|
|
104
|
+
<Select.Option key={type} value={type}>
|
|
105
|
+
{type}
|
|
106
|
+
</Select.Option>
|
|
107
|
+
))}
|
|
108
|
+
</Select.Viewport>
|
|
109
|
+
<Select.ScrollDownButton />
|
|
110
|
+
<Select.Arrow />
|
|
111
|
+
</Select.Content>
|
|
112
|
+
</Select.Portal>
|
|
113
|
+
</Select.Root>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const createTriggerSpec = (kind: TriggerKind): TriggerType => {
|
|
118
|
+
switch (kind) {
|
|
119
|
+
case TriggerKind.Timer:
|
|
120
|
+
return { type: TriggerKind.Timer, cron: '0 0 * * *' } satisfies TimerTrigger;
|
|
121
|
+
case TriggerKind.Webhook:
|
|
122
|
+
return { type: TriggerKind.Webhook, method: 'POST' } satisfies WebhookTrigger;
|
|
123
|
+
case TriggerKind.Subscription:
|
|
124
|
+
return { type: TriggerKind.Subscription, filter: {} } satisfies SubscriptionTrigger;
|
|
125
|
+
case TriggerKind.Email:
|
|
126
|
+
return { type: TriggerKind.Email } satisfies EmailTrigger;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const getOutputSchema = (kind: TriggerKind) => {
|
|
131
|
+
const kindToSchema: Record<TriggerKind, S.Schema<any>> = {
|
|
132
|
+
[TriggerKind.Email]: EmailTriggerOutput,
|
|
133
|
+
[TriggerKind.Subscription]: SubscriptionTriggerOutput,
|
|
134
|
+
[TriggerKind.Timer]: TimerTriggerOutput,
|
|
135
|
+
[TriggerKind.Webhook]: WebhookTriggerOutput,
|
|
136
|
+
};
|
|
137
|
+
return kindToSchema[kind];
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const triggerShape: ShapeDef<TriggerShape> = {
|
|
141
|
+
type: 'trigger',
|
|
142
|
+
name: 'Trigger',
|
|
143
|
+
icon: 'ph--lightning--regular',
|
|
144
|
+
component: TriggerComponent,
|
|
145
|
+
createShape: createTrigger,
|
|
146
|
+
getAnchors: (shape) =>
|
|
147
|
+
createFunctionAnchors(
|
|
148
|
+
shape,
|
|
149
|
+
VoidInput,
|
|
150
|
+
getOutputSchema(shape.functionTrigger?.target?.spec?.type ?? TriggerKind.Email),
|
|
151
|
+
),
|
|
152
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { forwardRef, type PropsWithChildren, type ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
import { invariant } from '@dxos/invariant';
|
|
8
|
+
import { Icon, IconButton, type ThemedClassName } from '@dxos/react-ui';
|
|
9
|
+
import { useEditorContext, useShapeDef } from '@dxos/react-ui-canvas-editor';
|
|
10
|
+
import { type Shape } from '@dxos/react-ui-canvas-editor';
|
|
11
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
12
|
+
|
|
13
|
+
export const headerHeight = 32;
|
|
14
|
+
export const footerHeight = 32;
|
|
15
|
+
|
|
16
|
+
export type BoxActionHandler = (action: 'run' | 'open' | 'close') => void;
|
|
17
|
+
|
|
18
|
+
export type BoxProps = PropsWithChildren<
|
|
19
|
+
ThemedClassName<{
|
|
20
|
+
shape: Shape;
|
|
21
|
+
title?: string;
|
|
22
|
+
status?: string | ReactNode;
|
|
23
|
+
open?: boolean;
|
|
24
|
+
onAction?: BoxActionHandler;
|
|
25
|
+
}>
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
export const Box = forwardRef<HTMLDivElement, BoxProps>(
|
|
29
|
+
({ children, classNames, shape, title, status, open, onAction }, forwardedRef) => {
|
|
30
|
+
invariant(shape.type);
|
|
31
|
+
const { icon, name, openable } = useShapeDef(shape.type) ?? { icon: 'ph--placeholder--regular' };
|
|
32
|
+
const { debug } = useEditorContext();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div ref={forwardedRef} className='flex flex-col h-full w-full justify-between'>
|
|
36
|
+
<div className='flex shrink-0 w-full justify-between items-center h-[32px] bg-hoverSurface'>
|
|
37
|
+
<Icon icon={icon} classNames='mx-2' />
|
|
38
|
+
<div className='grow text-sm truncate'>{debug ? shape.type : name ?? shape.text ?? title}</div>
|
|
39
|
+
<IconButton
|
|
40
|
+
classNames='p-1 text-green-500'
|
|
41
|
+
variant='ghost'
|
|
42
|
+
icon='ph--play--regular'
|
|
43
|
+
size={4}
|
|
44
|
+
label='run'
|
|
45
|
+
iconOnly
|
|
46
|
+
onDoubleClick={(ev) => ev.stopPropagation()}
|
|
47
|
+
onClick={(ev) => {
|
|
48
|
+
ev.stopPropagation();
|
|
49
|
+
onAction?.('run');
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
<div className={mx('flex flex-col h-full grow overflow-hidden', classNames)}>{children}</div>
|
|
54
|
+
<div className='flex shrink-0 w-full justify-between items-center h-[32px] bg-hoverSurface'>
|
|
55
|
+
<div className='grow px-2 text-sm truncate'>{debug ? shape.id : status}</div>
|
|
56
|
+
{openable && (
|
|
57
|
+
<IconButton
|
|
58
|
+
classNames='p-1'
|
|
59
|
+
variant='ghost'
|
|
60
|
+
icon={open ? 'ph--caret-up--regular' : 'ph--caret-down--regular'}
|
|
61
|
+
size={4}
|
|
62
|
+
label={open ? 'close' : 'open'}
|
|
63
|
+
iconOnly
|
|
64
|
+
onClick={(ev) => {
|
|
65
|
+
ev.stopPropagation();
|
|
66
|
+
onAction?.(open ? 'close' : 'open');
|
|
67
|
+
}}
|
|
68
|
+
/>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type JSX, useRef, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { VoidInput, VoidOutput } from '@dxos/conductor';
|
|
8
|
+
import { AST, type S } from '@dxos/echo-schema';
|
|
9
|
+
import { useCanvasContext } from '@dxos/react-ui-canvas';
|
|
10
|
+
import { type Polygon, type Shape } from '@dxos/react-ui-canvas-editor';
|
|
11
|
+
import { getParentShapeElement, createAnchors, rowHeight } from '@dxos/react-ui-canvas-editor';
|
|
12
|
+
|
|
13
|
+
import { Box, type BoxProps, footerHeight, headerHeight } from '../common';
|
|
14
|
+
import { createAnchorId, getProperties } from '../defs';
|
|
15
|
+
|
|
16
|
+
const bodyPadding = 8;
|
|
17
|
+
const expandedHeight = 200;
|
|
18
|
+
|
|
19
|
+
export type FunctionBodyProps = {
|
|
20
|
+
shape: Shape;
|
|
21
|
+
name?: string;
|
|
22
|
+
content?: JSX.Element;
|
|
23
|
+
inputSchema?: S.Schema.Any;
|
|
24
|
+
outputSchema?: S.Schema.Any;
|
|
25
|
+
} & Pick<BoxProps, 'status'>;
|
|
26
|
+
|
|
27
|
+
export const FunctionBody = ({
|
|
28
|
+
shape,
|
|
29
|
+
name,
|
|
30
|
+
content,
|
|
31
|
+
inputSchema = VoidInput,
|
|
32
|
+
outputSchema = VoidOutput,
|
|
33
|
+
...props
|
|
34
|
+
}: FunctionBodyProps) => {
|
|
35
|
+
const { scale } = useCanvasContext();
|
|
36
|
+
const rootRef = useRef<HTMLDivElement>(null);
|
|
37
|
+
const [open, setOpen] = useState(false);
|
|
38
|
+
|
|
39
|
+
const handleAction: BoxProps['onAction'] = (action) => {
|
|
40
|
+
if (!rootRef.current) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
switch (action) {
|
|
45
|
+
case 'open': {
|
|
46
|
+
const el = getParentShapeElement(rootRef.current, shape.id)!;
|
|
47
|
+
const { height } = el.getBoundingClientRect();
|
|
48
|
+
el.style.height = `${height / scale + expandedHeight}px`;
|
|
49
|
+
setOpen(true);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'close': {
|
|
53
|
+
const el = getParentShapeElement(rootRef.current, shape.id)!;
|
|
54
|
+
el.style.height = '';
|
|
55
|
+
setOpen(false);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// TODO(burdon): Move labels to anchor?
|
|
62
|
+
const inputs = getProperties(inputSchema.ast);
|
|
63
|
+
const outputs = getProperties(outputSchema.ast);
|
|
64
|
+
const columnCount = inputs.length && outputs.length ? 2 : 1;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Box
|
|
68
|
+
ref={rootRef}
|
|
69
|
+
shape={shape}
|
|
70
|
+
title={name}
|
|
71
|
+
classNames='divide-y divide-separator'
|
|
72
|
+
open={open}
|
|
73
|
+
onAction={handleAction}
|
|
74
|
+
{...props}
|
|
75
|
+
>
|
|
76
|
+
<div
|
|
77
|
+
className={`grid grid-cols-${columnCount} items-center`}
|
|
78
|
+
style={{ paddingTop: bodyPadding, paddingBottom: bodyPadding }}
|
|
79
|
+
>
|
|
80
|
+
{(inputs?.length ?? 0) > 0 && (
|
|
81
|
+
<div className='flex flex-col'>
|
|
82
|
+
{inputs?.map(({ name }) => (
|
|
83
|
+
<div key={name} className='px-2 truncate text-sm font-mono items-center' style={{ height: rowHeight }}>
|
|
84
|
+
{name}
|
|
85
|
+
</div>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
{(outputs?.length ?? 0) > 0 && (
|
|
90
|
+
<div className='flex flex-col'>
|
|
91
|
+
{outputs?.map(({ name }) => (
|
|
92
|
+
<div
|
|
93
|
+
key={name}
|
|
94
|
+
className='px-2 truncate text-sm font-mono items-center text-right'
|
|
95
|
+
style={{ height: rowHeight }}
|
|
96
|
+
>
|
|
97
|
+
{name}
|
|
98
|
+
</div>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
{open && <div className='flex flex-col grow overflow-hidden'>{content}</div>}
|
|
104
|
+
</Box>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const getHeight = (input: S.Schema<any>) => {
|
|
109
|
+
const properties = AST.getPropertySignatures(input.ast);
|
|
110
|
+
return headerHeight + footerHeight + bodyPadding * 2 + properties.length * rowHeight + 2; // Incl. borders.
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const createFunctionAnchors = (
|
|
114
|
+
shape: Polygon,
|
|
115
|
+
input: S.Schema<any> = VoidInput,
|
|
116
|
+
output: S.Schema<any> = VoidOutput,
|
|
117
|
+
) => {
|
|
118
|
+
// TODO(burdon): Set type.
|
|
119
|
+
const inputs = AST.getPropertySignatures(input.ast).map(({ name }) => createAnchorId('input', name.toString()));
|
|
120
|
+
const outputs = AST.getPropertySignatures(output.ast).map(({ name }) => createAnchorId('output', name.toString()));
|
|
121
|
+
return createAnchors({ shape, inputs, outputs, center: { x: 0, y: (headerHeight - footerHeight) / 2 + 1 } });
|
|
122
|
+
};
|