@dxos/plugin-automation 0.8.1 → 0.8.2-main.2f9c567
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/browser/AutomationContainer-3MP23OGQ.mjs +34 -0
- package/dist/lib/browser/AutomationContainer-3MP23OGQ.mjs.map +7 -0
- package/dist/lib/browser/AutomationPanel-75HVBBNG.mjs +11 -0
- package/dist/lib/browser/AutomationPanel-75HVBBNG.mjs.map +7 -0
- package/dist/lib/browser/FunctionsContainer-HLISW5QI.mjs +33 -0
- package/dist/lib/browser/FunctionsContainer-HLISW5QI.mjs.map +7 -0
- package/dist/lib/browser/FunctionsPanel-OKFRBXLO.mjs +10 -0
- package/dist/lib/browser/FunctionsPanel-OKFRBXLO.mjs.map +7 -0
- package/dist/lib/browser/{app-graph-builder-K3BIQFWW.mjs → app-graph-builder-ND64CHOM.mjs} +42 -1
- package/dist/lib/browser/app-graph-builder-ND64CHOM.mjs.map +7 -0
- package/dist/lib/browser/chunk-ADYCSC6Y.mjs +39 -0
- package/dist/lib/browser/chunk-ADYCSC6Y.mjs.map +7 -0
- package/dist/lib/browser/{AutomationPanel-YAHFXQX6.mjs → chunk-HTVXRMNH.mjs} +19 -18
- package/dist/lib/browser/chunk-HTVXRMNH.mjs.map +7 -0
- package/dist/lib/browser/chunk-IDCVJ2JW.mjs +88 -0
- package/dist/lib/browser/chunk-IDCVJ2JW.mjs.map +7 -0
- package/dist/lib/browser/chunk-N4PTKEWO.mjs +14 -0
- package/dist/lib/browser/chunk-N4PTKEWO.mjs.map +7 -0
- package/dist/lib/browser/chunk-Q2ACSERA.mjs +211 -0
- package/dist/lib/browser/chunk-Q2ACSERA.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +25 -19
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/intent-resolver-YH4YPX52.mjs +77 -0
- package/dist/lib/browser/intent-resolver-YH4YPX52.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-surface-GA67CQBV.mjs +68 -0
- package/dist/lib/browser/react-surface-GA67CQBV.mjs.map +7 -0
- package/dist/lib/browser/types.mjs +8 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/lib/node/AutomationContainer-RIDHNHPT.cjs +62 -0
- package/dist/lib/node/AutomationContainer-RIDHNHPT.cjs.map +7 -0
- package/dist/lib/node/AutomationPanel-6RLBWAGL.cjs +32 -0
- package/dist/lib/node/AutomationPanel-6RLBWAGL.cjs.map +7 -0
- package/dist/lib/node/FunctionsContainer-RNZTNQEU.cjs +61 -0
- package/dist/lib/node/FunctionsContainer-RNZTNQEU.cjs.map +7 -0
- package/dist/lib/node/FunctionsPanel-7I7YCHPJ.cjs +31 -0
- package/dist/lib/node/FunctionsPanel-7I7YCHPJ.cjs.map +7 -0
- package/dist/lib/node/{app-graph-builder-HO4FPGZ5.cjs → app-graph-builder-TVFR6RXO.cjs} +45 -4
- package/dist/lib/node/app-graph-builder-TVFR6RXO.cjs.map +7 -0
- package/dist/lib/node/{AutomationPanel-ZKAMIU6O.cjs → chunk-52JFIUOD.cjs} +31 -26
- package/dist/lib/node/chunk-52JFIUOD.cjs.map +7 -0
- package/dist/lib/node/chunk-ESVJ2X6U.cjs +234 -0
- package/dist/lib/node/chunk-ESVJ2X6U.cjs.map +7 -0
- package/dist/lib/node/chunk-GDCG2BML.cjs +58 -0
- package/dist/lib/node/chunk-GDCG2BML.cjs.map +7 -0
- package/dist/lib/node/chunk-GLXGAIZC.cjs +116 -0
- package/dist/lib/node/chunk-GLXGAIZC.cjs.map +7 -0
- package/dist/lib/node/{chunk-AGJ6XTDN.cjs → chunk-HZTVNN4S.cjs} +16 -7
- package/dist/lib/node/chunk-HZTVNN4S.cjs.map +7 -0
- package/dist/lib/node/index.cjs +28 -22
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/intent-resolver-6FJ6PJJI.cjs +93 -0
- package/dist/lib/node/intent-resolver-6FJ6PJJI.cjs.map +7 -0
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{react-surface-52M54VWV.cjs → react-surface-UJDYSHZ5.cjs} +39 -15
- package/dist/lib/node/react-surface-UJDYSHZ5.cjs.map +7 -0
- package/dist/lib/node/types.cjs +30 -0
- package/dist/lib/node/types.cjs.map +7 -0
- package/dist/lib/node-esm/AutomationContainer-MCUWF267.mjs +35 -0
- package/dist/lib/node-esm/AutomationContainer-MCUWF267.mjs.map +7 -0
- package/dist/lib/node-esm/AutomationPanel-U34H2Q7Z.mjs +12 -0
- package/dist/lib/node-esm/AutomationPanel-U34H2Q7Z.mjs.map +7 -0
- package/dist/lib/node-esm/FunctionsContainer-435FW56J.mjs +34 -0
- package/dist/lib/node-esm/FunctionsContainer-435FW56J.mjs.map +7 -0
- package/dist/lib/node-esm/FunctionsPanel-NXXT5ERU.mjs +11 -0
- package/dist/lib/node-esm/FunctionsPanel-NXXT5ERU.mjs.map +7 -0
- package/dist/lib/node-esm/{app-graph-builder-XCJR33VS.mjs → app-graph-builder-NYLOXWVV.mjs} +42 -1
- package/dist/lib/node-esm/app-graph-builder-NYLOXWVV.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-5JC4JVYZ.mjs +212 -0
- package/dist/lib/node-esm/chunk-5JC4JVYZ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-EEA6CZ6B.mjs +40 -0
- package/dist/lib/node-esm/chunk-EEA6CZ6B.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-RG3M42SO.mjs +16 -0
- package/dist/lib/node-esm/chunk-RG3M42SO.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-VYSTWH6Q.mjs +89 -0
- package/dist/lib/node-esm/chunk-VYSTWH6Q.mjs.map +7 -0
- package/dist/lib/node-esm/{AutomationPanel-XF7YPSKM.mjs → chunk-WYXWQFPK.mjs} +19 -18
- package/dist/lib/node-esm/chunk-WYXWQFPK.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +25 -19
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/intent-resolver-PVY22PBL.mjs +78 -0
- package/dist/lib/node-esm/intent-resolver-PVY22PBL.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-surface-R72A4EHM.mjs +69 -0
- package/dist/lib/node-esm/react-surface-R72A4EHM.mjs.map +7 -0
- package/dist/lib/node-esm/types.mjs +9 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/components/AutomationContainer.d.ts +7 -0
- package/dist/types/src/components/AutomationContainer.d.ts.map +1 -0
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +6 -3
- package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
- package/dist/types/src/components/AutomationPanel/index.d.ts +1 -0
- package/dist/types/src/components/AutomationPanel/index.d.ts.map +1 -1
- package/dist/types/src/components/FunctionsContainer.d.ts +7 -0
- package/dist/types/src/components/FunctionsContainer.d.ts.map +1 -0
- package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts +7 -0
- package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -0
- package/dist/types/src/components/FunctionsPanel/index.d.ts +4 -0
- package/dist/types/src/components/FunctionsPanel/index.d.ts.map +1 -0
- package/dist/types/src/components/TriggerEditor/FunctionPayloadEditor.d.ts +12 -0
- package/dist/types/src/components/TriggerEditor/FunctionPayloadEditor.d.ts.map +1 -0
- package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts +5 -0
- package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -0
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +8 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-functions.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +8 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +25 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/package.json +33 -24
- package/src/AutomationPlugin.tsx +5 -10
- package/src/capabilities/app-graph-builder.ts +31 -0
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/intent-resolver.ts +73 -0
- package/src/capabilities/react-surface.tsx +32 -8
- package/src/components/AutomationContainer.tsx +31 -0
- package/src/components/AutomationPanel/AutomationPanel.stories.tsx +5 -5
- package/src/components/AutomationPanel/AutomationPanel.tsx +67 -67
- package/src/components/AutomationPanel/index.ts +2 -0
- package/src/components/FunctionsContainer.tsx +31 -0
- package/src/components/FunctionsPanel/FunctionsPanel.tsx +95 -0
- package/src/components/FunctionsPanel/index.ts +8 -0
- package/src/components/TriggerEditor/FunctionPayloadEditor.tsx +77 -0
- package/src/components/TriggerEditor/SpecSelector.tsx +59 -0
- package/src/components/TriggerEditor/TriggerEditor.stories.tsx +18 -9
- package/src/components/TriggerEditor/TriggerEditor.tsx +49 -89
- package/src/components/index.ts +3 -0
- package/src/testing/test-functions.ts +23 -9
- package/src/translations.ts +10 -1
- package/src/types.ts +33 -0
- package/dist/lib/browser/AutomationPanel-YAHFXQX6.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-K3BIQFWW.mjs.map +0 -7
- package/dist/lib/browser/chunk-FALBBJNO.mjs +0 -138
- package/dist/lib/browser/chunk-FALBBJNO.mjs.map +0 -7
- package/dist/lib/browser/chunk-MT3FZH4V.mjs +0 -8
- package/dist/lib/browser/chunk-MT3FZH4V.mjs.map +0 -7
- package/dist/lib/browser/react-surface-4QQSJR4A.mjs +0 -42
- package/dist/lib/browser/react-surface-4QQSJR4A.mjs.map +0 -7
- package/dist/lib/node/AutomationPanel-ZKAMIU6O.cjs.map +0 -7
- package/dist/lib/node/app-graph-builder-HO4FPGZ5.cjs.map +0 -7
- package/dist/lib/node/chunk-AGJ6XTDN.cjs.map +0 -7
- package/dist/lib/node/chunk-FTEDH5Q6.cjs +0 -167
- package/dist/lib/node/chunk-FTEDH5Q6.cjs.map +0 -7
- package/dist/lib/node/react-surface-52M54VWV.cjs.map +0 -7
- package/dist/lib/node-esm/AutomationPanel-XF7YPSKM.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-XCJR33VS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-M4QXMIIB.mjs +0 -139
- package/dist/lib/node-esm/chunk-M4QXMIIB.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-OA75PSGH.mjs +0 -10
- package/dist/lib/node-esm/chunk-OA75PSGH.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-MGKM3OO3.mjs +0 -43
- package/dist/lib/node-esm/react-surface-MGKM3OO3.mjs.map +0 -7
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Schema } from 'effect';
|
|
5
6
|
import React, { useState } from 'react';
|
|
6
7
|
|
|
7
|
-
import { S } from '@dxos/echo-schema';
|
|
8
8
|
import {
|
|
9
9
|
FunctionType,
|
|
10
10
|
FunctionTrigger,
|
|
@@ -12,13 +12,12 @@ import {
|
|
|
12
12
|
TriggerKind,
|
|
13
13
|
type FunctionTriggerType,
|
|
14
14
|
ScriptType,
|
|
15
|
-
} from '@dxos/functions
|
|
15
|
+
} from '@dxos/functions';
|
|
16
16
|
import { type Client, useClient } from '@dxos/react-client';
|
|
17
|
-
import {
|
|
17
|
+
import { live, Filter, useQuery, type Space, type Live, getSpace } from '@dxos/react-client/echo';
|
|
18
18
|
import { Clipboard, IconButton, Input, Separator, useTranslation } from '@dxos/react-ui';
|
|
19
19
|
import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
|
|
20
20
|
import { List } from '@dxos/react-ui-list';
|
|
21
|
-
import { StackItem } from '@dxos/react-ui-stack';
|
|
22
21
|
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
23
22
|
|
|
24
23
|
import { AUTOMATION_PLUGIN } from '../../meta';
|
|
@@ -28,18 +27,20 @@ const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
|
|
|
28
27
|
|
|
29
28
|
export type AutomationPanelProps = {
|
|
30
29
|
space: Space;
|
|
31
|
-
object?:
|
|
30
|
+
object?: Live<any>;
|
|
31
|
+
initialTrigger?: FunctionTriggerType;
|
|
32
|
+
onDone?: () => void;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
// TODO(burdon): Factor out common layout with ViewEditor.
|
|
35
|
-
export const AutomationPanel = ({ space, object }: AutomationPanelProps) => {
|
|
36
|
+
export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
|
|
36
37
|
const { t } = useTranslation(AUTOMATION_PLUGIN);
|
|
37
38
|
const client = useClient();
|
|
38
39
|
const triggers = useQuery(space, Filter.schema(FunctionTrigger));
|
|
39
40
|
const functions = useQuery(space, Filter.schema(FunctionType));
|
|
40
41
|
const scripts = useQuery(space, Filter.schema(ScriptType));
|
|
41
42
|
|
|
42
|
-
const [trigger, setTrigger] = useState<FunctionTriggerType>();
|
|
43
|
+
const [trigger, setTrigger] = useState<FunctionTriggerType | undefined>(initialTrigger);
|
|
43
44
|
const [selected, setSelected] = useState<FunctionTrigger>();
|
|
44
45
|
|
|
45
46
|
const handleSelect = (trigger: FunctionTrigger) => {
|
|
@@ -49,7 +50,7 @@ export const AutomationPanel = ({ space, object }: AutomationPanelProps) => {
|
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
const handleAdd = () => {
|
|
52
|
-
setTrigger(
|
|
53
|
+
setTrigger(live(FunctionTriggerSchema, {}));
|
|
53
54
|
setSelected(undefined);
|
|
54
55
|
};
|
|
55
56
|
|
|
@@ -63,76 +64,74 @@ export const AutomationPanel = ({ space, object }: AutomationPanelProps) => {
|
|
|
63
64
|
if (selected) {
|
|
64
65
|
Object.assign(selected, trigger);
|
|
65
66
|
} else {
|
|
66
|
-
space.db.add(
|
|
67
|
+
space.db.add(live(FunctionTrigger, trigger));
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
setTrigger(undefined);
|
|
70
71
|
setSelected(undefined);
|
|
72
|
+
onDone?.();
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
const handleCancel: TriggerEditorProps['onCancel'] = () => {
|
|
74
76
|
setTrigger(undefined);
|
|
77
|
+
onDone?.();
|
|
75
78
|
};
|
|
76
79
|
|
|
77
80
|
return (
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
<
|
|
81
|
-
{trigger
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
{(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
<div className='flex flex-col w-full'>
|
|
82
|
+
{trigger ? (
|
|
83
|
+
<ControlItem title={t('trigger editor title')}>
|
|
84
|
+
<TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />
|
|
85
|
+
</ControlItem>
|
|
86
|
+
) : (
|
|
87
|
+
<div role='none' className={controlItemClasses}>
|
|
88
|
+
<List.Root<FunctionTrigger> items={triggers} isItem={Schema.is(FunctionTrigger)} getId={(field) => field.id}>
|
|
89
|
+
{({ items: triggers }) => (
|
|
90
|
+
<div role='list' className='flex flex-col w-full'>
|
|
91
|
+
{triggers?.map((trigger) => {
|
|
92
|
+
const copyAction = getCopyAction(client, trigger);
|
|
93
|
+
return (
|
|
94
|
+
<List.Item<FunctionTrigger>
|
|
95
|
+
key={trigger.id}
|
|
96
|
+
item={trigger}
|
|
97
|
+
classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
|
|
98
|
+
>
|
|
99
|
+
<Input.Root>
|
|
100
|
+
<Input.Switch
|
|
101
|
+
checked={trigger.enabled}
|
|
102
|
+
onCheckedChange={(checked) => (trigger.enabled = checked)}
|
|
103
|
+
/>
|
|
104
|
+
</Input.Root>
|
|
105
|
+
|
|
106
|
+
<div className={'flex'}>
|
|
107
|
+
<List.ItemTitle
|
|
108
|
+
classNames='px-1 cursor-pointer w-0 shrink truncate'
|
|
109
|
+
onClick={() => handleSelect(trigger)}
|
|
97
110
|
>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
<List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
|
|
123
|
-
</List.Item>
|
|
124
|
-
);
|
|
125
|
-
})}
|
|
126
|
-
</div>
|
|
127
|
-
)}
|
|
128
|
-
</List.Root>
|
|
129
|
-
{triggers.length > 0 && <Separator classNames='mlb-4' />}
|
|
130
|
-
<IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
|
|
131
|
-
</div>
|
|
132
|
-
)}
|
|
111
|
+
{getFunctionName(scripts, functions, trigger) ?? '∅'}
|
|
112
|
+
</List.ItemTitle>
|
|
113
|
+
|
|
114
|
+
{/* TODO: a better way to expose copy action */}
|
|
115
|
+
{copyAction && (
|
|
116
|
+
<Clipboard.IconButton
|
|
117
|
+
label={t(copyAction.translationKey)}
|
|
118
|
+
value={copyAction.contentProvider()}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
|
|
124
|
+
</List.Item>
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</List.Root>
|
|
130
|
+
{triggers.length > 0 && <Separator classNames='mlb-4' />}
|
|
131
|
+
<IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
|
|
133
132
|
</div>
|
|
134
|
-
|
|
135
|
-
</
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
136
135
|
);
|
|
137
136
|
};
|
|
138
137
|
|
|
@@ -159,10 +158,11 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
|
|
|
159
158
|
const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
|
|
160
159
|
// TODO(wittjosiah): Truncation should be done in the UI.
|
|
161
160
|
// Warning that the List component is currently a can of worms.
|
|
162
|
-
const shortId = trigger.function && `${trigger.function
|
|
163
|
-
const functionObject = functions.find((fn) =>
|
|
161
|
+
const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
|
|
162
|
+
const functionObject = functions.find((fn) => fn === trigger.function?.target);
|
|
164
163
|
if (!functionObject) {
|
|
165
164
|
return shortId;
|
|
166
165
|
}
|
|
166
|
+
|
|
167
167
|
return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
|
|
168
168
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Space } from '@dxos/react-client/echo';
|
|
8
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { ControlPage, ControlSection } from '@dxos/react-ui-form';
|
|
10
|
+
import { StackItem } from '@dxos/react-ui-stack';
|
|
11
|
+
|
|
12
|
+
import { FunctionsPanel } from './FunctionsPanel';
|
|
13
|
+
import { AUTOMATION_PLUGIN } from '../meta';
|
|
14
|
+
|
|
15
|
+
export const FunctionsContainer = ({ space }: { space: Space }) => {
|
|
16
|
+
const { t } = useTranslation(AUTOMATION_PLUGIN);
|
|
17
|
+
return (
|
|
18
|
+
<StackItem.Content classNames='block overflow-y-auto'>
|
|
19
|
+
<ControlPage>
|
|
20
|
+
<ControlSection
|
|
21
|
+
title={t('functions verbose label', { ns: AUTOMATION_PLUGIN })}
|
|
22
|
+
description={t('functions description', { ns: AUTOMATION_PLUGIN })}
|
|
23
|
+
>
|
|
24
|
+
<FunctionsPanel space={space} />
|
|
25
|
+
</ControlSection>
|
|
26
|
+
</ControlPage>
|
|
27
|
+
</StackItem.Content>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default FunctionsContainer;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
|
+
import React, { useCallback, useMemo } from 'react';
|
|
7
|
+
|
|
8
|
+
import { createIntent, LayoutAction, useIntentDispatcher } from '@dxos/app-framework';
|
|
9
|
+
import { FunctionType, ScriptType } from '@dxos/functions';
|
|
10
|
+
import { Filter, fullyQualifiedId, useQuery, type Space } from '@dxos/react-client/echo';
|
|
11
|
+
import { Button, useTranslation } from '@dxos/react-ui';
|
|
12
|
+
import { controlItemClasses } from '@dxos/react-ui-form';
|
|
13
|
+
import { List } from '@dxos/react-ui-list';
|
|
14
|
+
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
15
|
+
|
|
16
|
+
import { AUTOMATION_PLUGIN } from '../../meta';
|
|
17
|
+
|
|
18
|
+
const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
|
|
19
|
+
|
|
20
|
+
export type FunctionsPanelProps = {
|
|
21
|
+
space: Space;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
|
|
25
|
+
const { t } = useTranslation(AUTOMATION_PLUGIN);
|
|
26
|
+
const functions = useQuery(space, Filter.schema(FunctionType));
|
|
27
|
+
const scripts = useQuery(space, Filter.schema(ScriptType));
|
|
28
|
+
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
29
|
+
|
|
30
|
+
const functionToScriptMap = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
functions.reduce(
|
|
33
|
+
(map, func) => {
|
|
34
|
+
const scriptId = func.source?.target?.id;
|
|
35
|
+
if (scriptId) {
|
|
36
|
+
const script = scripts.find((s) => s.id === scriptId);
|
|
37
|
+
if (script) {
|
|
38
|
+
map[func.id] = script;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return map;
|
|
42
|
+
},
|
|
43
|
+
{} as Record<string, ScriptType>,
|
|
44
|
+
),
|
|
45
|
+
[functions, scripts],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const getScriptName = useCallback(
|
|
49
|
+
(func: FunctionType) => {
|
|
50
|
+
const script = functionToScriptMap[func.id];
|
|
51
|
+
return script?.name;
|
|
52
|
+
},
|
|
53
|
+
[functionToScriptMap],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const handleGoToScript = useCallback(
|
|
57
|
+
(func: FunctionType) => {
|
|
58
|
+
const script = functionToScriptMap[func.id];
|
|
59
|
+
if (script) {
|
|
60
|
+
void dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [fullyQualifiedId(script)] }));
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[functionToScriptMap, dispatch],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div role='none' className={mx(controlItemClasses)}>
|
|
68
|
+
<List.Root<FunctionType> items={functions} isItem={Schema.is(FunctionType)} getId={(func) => func.id}>
|
|
69
|
+
{({ items }) => (
|
|
70
|
+
<div role='list' className='flex flex-col w-full'>
|
|
71
|
+
{items?.map((func) => (
|
|
72
|
+
<List.Item<FunctionType>
|
|
73
|
+
key={func.id}
|
|
74
|
+
item={func}
|
|
75
|
+
classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
|
|
76
|
+
>
|
|
77
|
+
<div className='flex flex-col truncate'>
|
|
78
|
+
<List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
|
|
79
|
+
{getScriptName(func) && (
|
|
80
|
+
<div className='text-xs text-description truncate'>{getScriptName(func)}</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
{functionToScriptMap[func.id] && (
|
|
84
|
+
<Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
|
|
85
|
+
)}
|
|
86
|
+
</List.Item>
|
|
87
|
+
))}
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</List.Root>
|
|
91
|
+
|
|
92
|
+
{functions.length === 0 && <div className='text-center plb-4 text-gray-500'>{t('no functions found')}</div>}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type JsonPath, RefImpl, toEffectSchema } from '@dxos/echo-schema';
|
|
8
|
+
import { type FunctionType } from '@dxos/functions';
|
|
9
|
+
import { useOnTransition } from '@dxos/react-ui';
|
|
10
|
+
import { Form, type FormInputStateProps, type QueryRefOptions, useFormValues } from '@dxos/react-ui-form';
|
|
11
|
+
|
|
12
|
+
export type FunctionMetaEditorProps = {
|
|
13
|
+
functions: FunctionType[];
|
|
14
|
+
onQueryRefOptions: QueryRefOptions;
|
|
15
|
+
} & FormInputStateProps;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Editor component for function meta parameters.
|
|
19
|
+
*/
|
|
20
|
+
export const FunctionPayloadEditor = ({
|
|
21
|
+
functions,
|
|
22
|
+
getValue,
|
|
23
|
+
onValueChange,
|
|
24
|
+
onQueryRefOptions,
|
|
25
|
+
}: FunctionMetaEditorProps) => {
|
|
26
|
+
const selectedFunctionValue = useFormValues(['function' as JsonPath]);
|
|
27
|
+
const selectedFunctionName = useMemo(() => {
|
|
28
|
+
if (selectedFunctionValue instanceof RefImpl) {
|
|
29
|
+
return selectedFunctionValue.dxn.toString().split('dxn:worker:').at(1);
|
|
30
|
+
}
|
|
31
|
+
}, [selectedFunctionValue]);
|
|
32
|
+
|
|
33
|
+
const selectedFunction = useMemo(
|
|
34
|
+
() => functions.find((f) => f.name === selectedFunctionName),
|
|
35
|
+
[functions, selectedFunctionName],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
useOnTransition(
|
|
39
|
+
// Clear function parameter meta when the function changes.
|
|
40
|
+
selectedFunctionValue,
|
|
41
|
+
(prevValue) => prevValue !== undefined && prevValue !== selectedFunctionValue,
|
|
42
|
+
(currValue) => currValue !== undefined,
|
|
43
|
+
() => onValueChange('object', {}),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const inputSchema = useMemo(() => selectedFunction?.inputSchema, [selectedFunction]);
|
|
47
|
+
const effectSchema = useMemo(() => (inputSchema ? toEffectSchema(inputSchema) : undefined), [inputSchema]);
|
|
48
|
+
const propertyCount = inputSchema?.properties ? Object.keys(inputSchema.properties).length : 0;
|
|
49
|
+
|
|
50
|
+
const values = useMemo(() => getValue() ?? {}, [getValue]);
|
|
51
|
+
|
|
52
|
+
const handleValuesChanged = useCallback(
|
|
53
|
+
(values: any) => {
|
|
54
|
+
onValueChange('object', values);
|
|
55
|
+
},
|
|
56
|
+
[onValueChange],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (selectedFunction === undefined || effectSchema === undefined || propertyCount === 0) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<h3 className='text-md'>Function parameters</h3>
|
|
66
|
+
{/* TODO(ZaymonFC): Try using <FormFields /> internal component for this nesting.
|
|
67
|
+
This would allow errors to flow up to the root context. */}
|
|
68
|
+
<Form
|
|
69
|
+
schema={effectSchema}
|
|
70
|
+
values={values}
|
|
71
|
+
classNames='p-0'
|
|
72
|
+
onValuesChanged={handleValuesChanged}
|
|
73
|
+
onQueryRefOptions={onQueryRefOptions}
|
|
74
|
+
/>
|
|
75
|
+
</>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type FunctionTriggerType, TriggerKind, type TriggerType } from '@dxos/functions';
|
|
8
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { SelectInput, type InputProps, useInputProps } from '@dxos/react-ui-form';
|
|
10
|
+
|
|
11
|
+
import { AUTOMATION_PLUGIN } from '../../meta';
|
|
12
|
+
|
|
13
|
+
export type SpecSelectorProps = InputProps;
|
|
14
|
+
|
|
15
|
+
export const SpecSelector = (props: SpecSelectorProps) => {
|
|
16
|
+
const { t } = useTranslation(AUTOMATION_PLUGIN);
|
|
17
|
+
const specProps = useInputProps(['spec' satisfies keyof FunctionTriggerType]);
|
|
18
|
+
|
|
19
|
+
const handleTypeChange = useCallback(
|
|
20
|
+
(_type: any, value: string): TriggerType | undefined => {
|
|
21
|
+
const getDefaultTriggerSpec = (kind: string) => {
|
|
22
|
+
switch (kind) {
|
|
23
|
+
case TriggerKind.Timer:
|
|
24
|
+
return { type: TriggerKind.Timer, cron: '' };
|
|
25
|
+
case TriggerKind.Subscription:
|
|
26
|
+
return { type: TriggerKind.Subscription, filter: {} };
|
|
27
|
+
case TriggerKind.Queue:
|
|
28
|
+
return { type: TriggerKind.Queue, queue: '' };
|
|
29
|
+
case TriggerKind.Email:
|
|
30
|
+
return { type: TriggerKind.Email };
|
|
31
|
+
case TriggerKind.Webhook:
|
|
32
|
+
return { type: TriggerKind.Webhook };
|
|
33
|
+
default:
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const defaultSpec = getDefaultTriggerSpec(value);
|
|
39
|
+
if (!defaultSpec) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Update the entire spec object, not just the `spec.type`.
|
|
44
|
+
specProps.onValueChange('object', defaultSpec);
|
|
45
|
+
},
|
|
46
|
+
[specProps],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const options = useMemo(
|
|
50
|
+
() =>
|
|
51
|
+
Object.values(TriggerKind).map((kind) => ({
|
|
52
|
+
value: kind,
|
|
53
|
+
label: t(`trigger type ${kind}`),
|
|
54
|
+
})),
|
|
55
|
+
[t],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return <SelectInput {...props} options={options} onValueChange={handleTypeChange} />;
|
|
59
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
@@ -7,10 +7,11 @@ import '@dxos-theme';
|
|
|
7
7
|
import { type Meta } from '@storybook/react';
|
|
8
8
|
import React, { useEffect, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import { FunctionType, FunctionTrigger, TriggerKind } from '@dxos/functions
|
|
11
|
-
import {
|
|
10
|
+
import { FunctionType, FunctionTrigger, TriggerKind } from '@dxos/functions';
|
|
11
|
+
import { live } from '@dxos/live-object';
|
|
12
|
+
import { faker } from '@dxos/random';
|
|
12
13
|
import { useSpaces } from '@dxos/react-client/echo';
|
|
13
|
-
import { withClientProvider } from '@dxos/react-client/testing';
|
|
14
|
+
import { ContactType, withClientProvider } from '@dxos/react-client/testing';
|
|
14
15
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
15
16
|
|
|
16
17
|
import { TriggerEditor } from './TriggerEditor';
|
|
@@ -26,7 +27,7 @@ const DefaultStory = () => {
|
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
const trigger = space.db.add(
|
|
30
|
+
const trigger = space.db.add(live(FunctionTrigger, { spec: { type: TriggerKind.Timer, cron: '' } }));
|
|
30
31
|
setTrigger(trigger);
|
|
31
32
|
}, [space]);
|
|
32
33
|
|
|
@@ -35,8 +36,8 @@ const DefaultStory = () => {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
|
-
<div role='none' className='
|
|
39
|
-
<TriggerEditor space={space} trigger={trigger} />
|
|
39
|
+
<div role='none' className='w-[32rem] bs-fit border border-separator rounded-sm'>
|
|
40
|
+
<TriggerEditor space={space} trigger={trigger} onSave={(values) => console.log('on save', values)} />
|
|
40
41
|
</div>
|
|
41
42
|
);
|
|
42
43
|
};
|
|
@@ -49,11 +50,19 @@ const meta: Meta = {
|
|
|
49
50
|
withClientProvider({
|
|
50
51
|
createIdentity: true,
|
|
51
52
|
createSpace: true,
|
|
52
|
-
types: [FunctionType, FunctionTrigger],
|
|
53
|
+
types: [FunctionType, FunctionTrigger, ContactType],
|
|
53
54
|
onSpaceCreated: ({ space }) => {
|
|
54
55
|
for (const fn of functions) {
|
|
55
|
-
space.db.add(
|
|
56
|
+
space.db.add(live(FunctionType, fn));
|
|
56
57
|
}
|
|
58
|
+
Array.from({ length: 10 }).map(() => {
|
|
59
|
+
return space.db.add(
|
|
60
|
+
live(ContactType, {
|
|
61
|
+
name: faker.person.fullName(),
|
|
62
|
+
identifiers: [],
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
});
|
|
57
66
|
},
|
|
58
67
|
}),
|
|
59
68
|
withLayout({ fullscreen: true, tooltips: true, classNames: 'flex justify-center m-2' }),
|