@dxos/plugin-automation 0.8.4-main.67995b8 → 0.8.4-main.ae835ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/dist/lib/browser/AutomationPanel-LBXJDM6Z.mjs +11 -0
  2. package/dist/lib/browser/AutomationSettings-IIEI5D2K.mjs +68 -0
  3. package/dist/lib/browser/AutomationSettings-IIEI5D2K.mjs.map +7 -0
  4. package/dist/lib/browser/{FunctionsContainer-IOB333TX.mjs → FunctionsContainer-IZTCGNJZ.mjs} +9 -12
  5. package/dist/lib/browser/FunctionsContainer-IZTCGNJZ.mjs.map +7 -0
  6. package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs → FunctionsPanel-HFCK3JRB.mjs} +3 -3
  7. package/dist/lib/browser/{app-graph-builder-ZTAUTFI4.mjs → app-graph-builder-7VKA2GS3.mjs} +12 -11
  8. package/dist/lib/browser/app-graph-builder-7VKA2GS3.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-CVYJM6OO.mjs +66 -0
  10. package/dist/lib/browser/chunk-CVYJM6OO.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-LYJVTIVD.mjs → chunk-FHSN7G4H.mjs} +25 -12
  12. package/dist/lib/browser/chunk-FHSN7G4H.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-GCGHLQM2.mjs +15 -0
  14. package/dist/lib/browser/chunk-GCGHLQM2.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-FSJZXTS2.mjs → chunk-JW7XSPYW.mjs} +69 -32
  16. package/dist/lib/browser/chunk-JW7XSPYW.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-ERTIGJYE.mjs → chunk-K5YNWESC.mjs} +68 -32
  18. package/dist/lib/browser/chunk-K5YNWESC.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-LZQFZO3B.mjs +17 -0
  20. package/dist/lib/browser/chunk-LZQFZO3B.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-XXZNGORC.mjs +14 -0
  22. package/dist/lib/browser/chunk-XXZNGORC.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-HN7OHFCB.mjs → chunk-YBPJCY3F.mjs} +3 -3
  24. package/dist/lib/browser/chunk-YBPJCY3F.mjs.map +7 -0
  25. package/dist/lib/browser/compute-runtime-PFYRMGN2.mjs +114 -0
  26. package/dist/lib/browser/compute-runtime-PFYRMGN2.mjs.map +7 -0
  27. package/dist/lib/browser/hooks/index.mjs +11 -0
  28. package/dist/lib/browser/index.mjs +44 -19
  29. package/dist/lib/browser/index.mjs.map +4 -4
  30. package/dist/lib/browser/{intent-resolver-U3ZAQEFW.mjs → intent-resolver-5HR7M7T6.mjs} +15 -14
  31. package/dist/lib/browser/intent-resolver-5HR7M7T6.mjs.map +7 -0
  32. package/dist/lib/browser/meta.json +1 -1
  33. package/dist/lib/browser/{react-surface-4DFSM7OX.mjs → react-surface-WPMY3EX2.mjs} +18 -19
  34. package/dist/lib/browser/react-surface-WPMY3EX2.mjs.map +7 -0
  35. package/dist/lib/browser/types/index.mjs +2 -2
  36. package/dist/lib/node-esm/{AutomationPanel-YYUMSK2W.mjs → AutomationPanel-NVEIUIZP.mjs} +4 -4
  37. package/dist/lib/node-esm/AutomationSettings-VIIEPI2P.mjs +69 -0
  38. package/dist/lib/node-esm/AutomationSettings-VIIEPI2P.mjs.map +7 -0
  39. package/dist/lib/node-esm/{FunctionsContainer-DJWB6WFH.mjs → FunctionsContainer-KKRCIXGB.mjs} +9 -12
  40. package/dist/lib/node-esm/FunctionsContainer-KKRCIXGB.mjs.map +7 -0
  41. package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs → FunctionsPanel-74ELGR7P.mjs} +3 -3
  42. package/dist/lib/node-esm/FunctionsPanel-74ELGR7P.mjs.map +7 -0
  43. package/dist/lib/node-esm/{app-graph-builder-3FP63ZSG.mjs → app-graph-builder-RTOFJNFV.mjs} +12 -11
  44. package/dist/lib/node-esm/app-graph-builder-RTOFJNFV.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-3BJ6BR3E.mjs +67 -0
  46. package/dist/lib/node-esm/chunk-3BJ6BR3E.mjs.map +7 -0
  47. package/dist/lib/node-esm/chunk-CEVIVRTY.mjs +19 -0
  48. package/dist/lib/node-esm/chunk-CEVIVRTY.mjs.map +7 -0
  49. package/dist/lib/node-esm/{chunk-OEZNHUL2.mjs → chunk-ECJKIUBO.mjs} +3 -3
  50. package/dist/lib/node-esm/chunk-ECJKIUBO.mjs.map +7 -0
  51. package/dist/lib/node-esm/{chunk-ZGPUV5VS.mjs → chunk-JOCKHLFT.mjs} +25 -12
  52. package/dist/lib/node-esm/chunk-JOCKHLFT.mjs.map +7 -0
  53. package/dist/lib/node-esm/chunk-MRYKW5TM.mjs +16 -0
  54. package/dist/lib/node-esm/chunk-MRYKW5TM.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-U5Q2NI7H.mjs +16 -0
  56. package/dist/lib/node-esm/chunk-U5Q2NI7H.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-AZH66CED.mjs → chunk-W76WUTZY.mjs} +69 -32
  58. package/dist/lib/node-esm/chunk-W76WUTZY.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-HIMYPGHF.mjs → chunk-Y7PJXFCJ.mjs} +68 -32
  60. package/dist/lib/node-esm/chunk-Y7PJXFCJ.mjs.map +7 -0
  61. package/dist/lib/node-esm/compute-runtime-IXCSD3W7.mjs +115 -0
  62. package/dist/lib/node-esm/compute-runtime-IXCSD3W7.mjs.map +7 -0
  63. package/dist/lib/node-esm/hooks/index.mjs +12 -0
  64. package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
  65. package/dist/lib/node-esm/index.mjs +44 -19
  66. package/dist/lib/node-esm/index.mjs.map +4 -4
  67. package/dist/lib/node-esm/{intent-resolver-3QWXEBPX.mjs → intent-resolver-KDRYB5BC.mjs} +15 -14
  68. package/dist/lib/node-esm/intent-resolver-KDRYB5BC.mjs.map +7 -0
  69. package/dist/lib/node-esm/meta.json +1 -1
  70. package/dist/lib/node-esm/{react-surface-3PNW7NDW.mjs → react-surface-Y2BBJV2I.mjs} +18 -19
  71. package/dist/lib/node-esm/react-surface-Y2BBJV2I.mjs.map +7 -0
  72. package/dist/lib/node-esm/types/index.mjs +2 -2
  73. package/dist/types/src/AutomationPlugin.d.ts +1 -1
  74. package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
  75. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  76. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  77. package/dist/types/src/capabilities/capabilities.d.ts +19 -0
  78. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  79. package/dist/types/src/capabilities/compute-runtime.d.ts +5 -0
  80. package/dist/types/src/capabilities/compute-runtime.d.ts.map +1 -0
  81. package/dist/types/src/capabilities/index.d.ts +5 -3
  82. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  83. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  84. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  86. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  87. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +6 -5
  88. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
  89. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts +46 -4
  90. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
  91. package/dist/types/src/components/AutomationSettings.d.ts +5 -0
  92. package/dist/types/src/components/AutomationSettings.d.ts.map +1 -0
  93. package/dist/types/src/components/FunctionsContainer.d.ts.map +1 -1
  94. package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -1
  95. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts +2 -2
  96. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
  97. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
  98. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +7 -5
  99. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  100. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +47 -4
  101. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  102. package/dist/types/src/components/TriggerSettings.d.ts +6 -0
  103. package/dist/types/src/components/TriggerSettings.d.ts.map +1 -0
  104. package/dist/types/src/components/index.d.ts +2 -2
  105. package/dist/types/src/components/index.d.ts.map +1 -1
  106. package/dist/types/src/events.d.ts +4 -0
  107. package/dist/types/src/events.d.ts.map +1 -0
  108. package/dist/types/src/hooks/index.d.ts +3 -0
  109. package/dist/types/src/hooks/index.d.ts.map +1 -0
  110. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +8 -0
  111. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -0
  112. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +11 -0
  113. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -0
  114. package/dist/types/src/index.d.ts +3 -0
  115. package/dist/types/src/index.d.ts.map +1 -1
  116. package/dist/types/src/meta.d.ts +0 -1
  117. package/dist/types/src/meta.d.ts.map +1 -1
  118. package/dist/types/src/translations.d.ts +3 -0
  119. package/dist/types/src/translations.d.ts.map +1 -1
  120. package/dist/types/src/types/schema.d.ts +1 -1
  121. package/dist/types/src/types/schema.d.ts.map +1 -1
  122. package/dist/types/tsconfig.tsbuildinfo +1 -1
  123. package/package.json +62 -41
  124. package/src/AutomationPlugin.tsx +37 -31
  125. package/src/capabilities/app-graph-builder.ts +12 -11
  126. package/src/capabilities/capabilities.ts +48 -0
  127. package/src/capabilities/compute-runtime.ts +138 -0
  128. package/src/capabilities/index.ts +3 -0
  129. package/src/capabilities/intent-resolver.ts +11 -11
  130. package/src/capabilities/react-surface.tsx +13 -14
  131. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +15 -16
  132. package/src/components/AutomationPanel/AutomationPanel.tsx +101 -50
  133. package/src/components/AutomationSettings.tsx +30 -0
  134. package/src/components/FunctionsContainer.tsx +11 -13
  135. package/src/components/FunctionsPanel/FunctionsPanel.tsx +26 -14
  136. package/src/components/TriggerEditor/FunctionInputEditor.tsx +10 -4
  137. package/src/components/TriggerEditor/SpecSelector.tsx +23 -17
  138. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +68 -26
  139. package/src/components/TriggerEditor/TriggerEditor.tsx +45 -25
  140. package/src/components/TriggerSettings.tsx +25 -0
  141. package/src/components/index.ts +1 -1
  142. package/src/events.ts +11 -0
  143. package/src/hooks/index.ts +6 -0
  144. package/src/hooks/useComputeRuntimeCallback.ts +32 -0
  145. package/src/hooks/useTriggerRuntimeControls.ts +52 -0
  146. package/src/index.ts +3 -0
  147. package/src/meta.ts +6 -5
  148. package/src/testing/test-functions.ts +1 -1
  149. package/src/translations.ts +5 -0
  150. package/src/types/schema.ts +1 -1
  151. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs +0 -38
  152. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs.map +0 -7
  153. package/dist/lib/browser/AutomationPanel-ZWA6GOFY.mjs +0 -11
  154. package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs.map +0 -7
  155. package/dist/lib/browser/app-graph-builder-ZTAUTFI4.mjs.map +0 -7
  156. package/dist/lib/browser/chunk-ECSTS2UI.mjs +0 -14
  157. package/dist/lib/browser/chunk-ECSTS2UI.mjs.map +0 -7
  158. package/dist/lib/browser/chunk-ERTIGJYE.mjs.map +0 -7
  159. package/dist/lib/browser/chunk-FSJZXTS2.mjs.map +0 -7
  160. package/dist/lib/browser/chunk-GW5U2DGT.mjs +0 -15
  161. package/dist/lib/browser/chunk-GW5U2DGT.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-HN7OHFCB.mjs.map +0 -7
  163. package/dist/lib/browser/chunk-LYJVTIVD.mjs.map +0 -7
  164. package/dist/lib/browser/intent-resolver-U3ZAQEFW.mjs.map +0 -7
  165. package/dist/lib/browser/react-surface-4DFSM7OX.mjs.map +0 -7
  166. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs +0 -39
  167. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs.map +0 -7
  168. package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs.map +0 -7
  169. package/dist/lib/node-esm/app-graph-builder-3FP63ZSG.mjs.map +0 -7
  170. package/dist/lib/node-esm/chunk-AZH66CED.mjs.map +0 -7
  171. package/dist/lib/node-esm/chunk-HIMYPGHF.mjs.map +0 -7
  172. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs +0 -17
  173. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs.map +0 -7
  174. package/dist/lib/node-esm/chunk-OEZNHUL2.mjs.map +0 -7
  175. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs +0 -16
  176. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs.map +0 -7
  177. package/dist/lib/node-esm/chunk-ZGPUV5VS.mjs.map +0 -7
  178. package/dist/lib/node-esm/intent-resolver-3QWXEBPX.mjs.map +0 -7
  179. package/dist/lib/node-esm/react-surface-3PNW7NDW.mjs.map +0 -7
  180. package/dist/types/src/components/AutomationContainer.d.ts +0 -5
  181. package/dist/types/src/components/AutomationContainer.d.ts.map +0 -1
  182. package/src/components/AutomationContainer.tsx +0 -30
  183. /package/dist/lib/browser/{AutomationPanel-ZWA6GOFY.mjs.map → AutomationPanel-LBXJDM6Z.mjs.map} +0 -0
  184. /package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs.map → FunctionsPanel-HFCK3JRB.mjs.map} +0 -0
  185. /package/dist/lib/{node-esm/AutomationPanel-YYUMSK2W.mjs.map → browser/hooks/index.mjs.map} +0 -0
  186. /package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs.map → AutomationPanel-NVEIUIZP.mjs.map} +0 -0
@@ -2,21 +2,19 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { type Meta } from '@storybook/react-vite';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
- import { Obj } from '@dxos/echo';
11
- import { FunctionType, FunctionTrigger } from '@dxos/functions';
8
+ import { Function, Trigger } from '@dxos/functions';
12
9
  import { useSpaces } from '@dxos/react-client/echo';
13
10
  import { withClientProvider } from '@dxos/react-client/testing';
14
- import { withLayout, withTheme } from '@dxos/storybook-utils';
11
+ import { withTheme } from '@dxos/react-ui/testing';
15
12
 
16
- import { AutomationPanel } from './AutomationPanel';
17
13
  import { functions } from '../../testing';
18
14
  import { translations } from '../../translations';
19
15
 
16
+ import { AutomationPanel } from './AutomationPanel';
17
+
20
18
  const DefaultStory = () => {
21
19
  const spaces = useSpaces();
22
20
  const space = spaces[1];
@@ -28,30 +26,31 @@ const DefaultStory = () => {
28
26
  );
29
27
  };
30
28
 
31
- const meta: Meta = {
29
+ const meta = {
32
30
  title: 'plugins/plugin-automation/AutomationPanel',
33
- component: AutomationPanel,
31
+ component: AutomationPanel as any,
34
32
  render: DefaultStory,
35
33
  decorators: [
34
+ withTheme,
36
35
  withClientProvider({
37
36
  createIdentity: true,
38
37
  createSpace: true,
39
- types: [FunctionType, FunctionTrigger],
40
- onSpaceCreated: ({ space }) => {
38
+ types: [Function.Function, Trigger.Trigger],
39
+ onCreateSpace: ({ space }) => {
41
40
  for (const fn of functions) {
42
- space.db.add(Obj.make(FunctionType, fn));
41
+ space.db.add(Function.make(fn));
43
42
  }
44
43
  },
45
44
  }),
46
- withLayout(),
47
- withTheme,
48
45
  ],
49
46
  parameters: {
50
47
  layout: 'centered',
51
48
  translations,
52
49
  },
53
- };
50
+ } satisfies Meta<typeof DefaultStory>;
54
51
 
55
52
  export default meta;
56
53
 
57
- export const Default = {};
54
+ type Story = StoryObj<typeof meta>;
55
+
56
+ export const Default: Story = {};
@@ -2,60 +2,61 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
6
- import React, { useState } from 'react';
7
-
8
- import { Filter, Obj } from '@dxos/echo';
9
- import {
10
- FunctionType,
11
- FunctionTrigger,
12
- FunctionTriggerSchema,
13
- type FunctionTriggerType,
14
- ScriptType,
15
- TriggerKind,
16
- } from '@dxos/functions';
5
+ import * as Array from 'effect/Array';
6
+ import * as EFn from 'effect/Function';
7
+ import * as Match from 'effect/Match';
8
+ import * as Schema from 'effect/Schema';
9
+ import React, { useMemo, useState } from 'react';
10
+
11
+ import { Filter, Obj, Tag } from '@dxos/echo';
12
+ import { Function, Script, Trigger } from '@dxos/functions';
13
+ import { useTypeOptions } from '@dxos/plugin-space';
17
14
  import { type Client, useClient } from '@dxos/react-client';
18
- import { useQuery, type Space, getSpace } from '@dxos/react-client/echo';
19
- import { Clipboard, IconButton, Input, Separator, useTranslation } from '@dxos/react-ui';
15
+ import { type Space, getSpace, useQuery } from '@dxos/react-client/echo';
16
+ import { Clipboard, IconButton, Input, Separator, type ThemedClassName, useTranslation } from '@dxos/react-ui';
20
17
  import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
21
18
  import { List } from '@dxos/react-ui-list';
22
19
  import { ghostHover, mx } from '@dxos/react-ui-theme';
20
+ import { DataType } from '@dxos/schema';
23
21
 
24
- import { AUTOMATION_PLUGIN } from '../../meta';
22
+ import { meta } from '../../meta';
25
23
  import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
26
24
 
27
25
  const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
28
26
 
29
- export type AutomationPanelProps = {
27
+ export type AutomationPanelProps = ThemedClassName<{
30
28
  space: Space;
31
29
  object?: Obj.Any;
32
- initialTrigger?: FunctionTriggerType;
30
+ initialTrigger?: Trigger.Trigger;
33
31
  onDone?: () => void;
34
- };
32
+ }>;
35
33
 
36
34
  // TODO(burdon): Factor out common layout with ViewEditor.
37
- export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
38
- const { t } = useTranslation(AUTOMATION_PLUGIN);
35
+ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onDone }: AutomationPanelProps) => {
36
+ const { t } = useTranslation(meta.id);
39
37
  const client = useClient();
40
- const triggers = useQuery(space, Filter.type(FunctionTrigger));
41
- const functions = useQuery(space, Filter.type(FunctionType));
42
- const scripts = useQuery(space, Filter.type(ScriptType));
43
-
44
- const [trigger, setTrigger] = useState<FunctionTriggerType | undefined>(initialTrigger);
45
- const [selected, setSelected] = useState<FunctionTrigger>();
46
-
47
- const handleSelect = (trigger: FunctionTrigger) => {
48
- const { id: _, ...values } = trigger;
49
- setTrigger(values);
38
+ const functions = useQuery(space, Filter.type(Function.Function));
39
+ const triggers = useQuery(space, Filter.type(Trigger.Trigger));
40
+ const filteredTriggers = useMemo(() => {
41
+ return object ? triggers.filter(triggerMatch(object)) : triggers;
42
+ }, [object, triggers]);
43
+ const tags = useQuery(space, Filter.type(Tag.Tag));
44
+ const types = useTypeOptions({ space, annotation: ['dynamic', 'limited-static', 'object-form'] });
45
+
46
+ const [trigger, setTrigger] = useState<Trigger.Trigger | undefined>(initialTrigger);
47
+ const [selected, setSelected] = useState<Trigger.Trigger>();
48
+
49
+ const handleSelect = (trigger: Trigger.Trigger) => {
50
+ setTrigger(trigger);
50
51
  setSelected(trigger);
51
52
  };
52
53
 
53
54
  const handleAdd = () => {
54
- setTrigger(Obj.make(FunctionTriggerSchema, {}));
55
+ setTrigger(Trigger.make({}));
55
56
  setSelected(undefined);
56
57
  };
57
58
 
58
- const handleDelete = (trigger: FunctionTrigger) => {
59
+ const handleDelete = (trigger: Trigger.Trigger) => {
59
60
  space.db.remove(trigger);
60
61
  setTrigger(undefined);
61
62
  setSelected(undefined);
@@ -65,7 +66,7 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
65
66
  if (selected) {
66
67
  Object.assign(selected, trigger);
67
68
  } else {
68
- space.db.add(Obj.make(FunctionTrigger, trigger));
69
+ space.db.add(Trigger.make(trigger));
69
70
  }
70
71
 
71
72
  setTrigger(undefined);
@@ -81,21 +82,33 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
81
82
  if (trigger) {
82
83
  return (
83
84
  <ControlItem title={t('trigger editor title')}>
84
- <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />
85
+ <TriggerEditor
86
+ space={space}
87
+ trigger={trigger}
88
+ readonlySpec={Boolean(object)}
89
+ tags={tags}
90
+ types={types}
91
+ onSave={handleSave}
92
+ onCancel={handleCancel}
93
+ />
85
94
  </ControlItem>
86
95
  );
87
96
  }
88
97
 
89
98
  return (
90
- <div className={controlItemClasses}>
91
- {triggers.length > 0 && (
92
- <List.Root<FunctionTrigger> items={triggers} isItem={Schema.is(FunctionTrigger)} getId={(field) => field.id}>
93
- {({ items: triggers }) => (
99
+ <div className={mx(controlItemClasses, classNames)}>
100
+ {filteredTriggers.length > 0 && (
101
+ <List.Root<Trigger.Trigger>
102
+ items={filteredTriggers}
103
+ isItem={Schema.is(Trigger.Trigger)}
104
+ getId={(field) => field.id}
105
+ >
106
+ {({ items: filteredTriggers }) => (
94
107
  <div role='list' className='flex flex-col w-full'>
95
- {triggers?.map((trigger) => {
108
+ {filteredTriggers?.map((trigger) => {
96
109
  const copyAction = getCopyAction(client, trigger);
97
110
  return (
98
- <List.Item<FunctionTrigger>
111
+ <List.Item<Trigger.Trigger>
99
112
  key={trigger.id}
100
113
  item={trigger}
101
114
  classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
@@ -112,7 +125,7 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
112
125
  classNames='px-1 cursor-pointer w-0 shrink truncate'
113
126
  onClick={() => handleSelect(trigger)}
114
127
  >
115
- {getFunctionName(scripts, functions, trigger) ?? '∅'}
128
+ {getFunctionName(functions, trigger) ?? '∅'}
116
129
  </List.ItemTitle>
117
130
 
118
131
  {/* TODO: a better way to expose copy action */}
@@ -132,25 +145,25 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
132
145
  )}
133
146
  </List.Root>
134
147
  )}
135
- {triggers.length > 0 && <Separator classNames='mlb-4' />}
148
+ {filteredTriggers.length > 0 && <Separator classNames='mlb-4' />}
136
149
  <IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
137
150
  </div>
138
151
  );
139
152
  };
140
153
 
141
- const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
142
- if (trigger?.spec?.kind === TriggerKind.Email) {
154
+ const getCopyAction = (client: Client, trigger: Trigger.Trigger | undefined) => {
155
+ if (trigger?.spec?.kind === 'email') {
143
156
  return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
144
157
  }
145
158
 
146
- if (trigger?.spec?.kind === TriggerKind.Webhook) {
159
+ if (trigger?.spec?.kind === 'webhook') {
147
160
  return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
148
161
  }
149
162
 
150
163
  return undefined;
151
164
  };
152
165
 
153
- const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
166
+ const getWebhookUrl = (client: Client, trigger: Trigger.Trigger) => {
154
167
  const spaceId = getSpace(trigger)!.id;
155
168
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
156
169
  const isSecure = edgeUrl.protocol.startsWith('https') || edgeUrl.protocol.startsWith('wss');
@@ -158,14 +171,52 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
158
171
  return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
159
172
  };
160
173
 
161
- const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
174
+ const getFunctionName = (functions: Function.Function[], trigger: Trigger.Trigger) => {
162
175
  // TODO(wittjosiah): Truncation should be done in the UI.
163
176
  // Warning that the List component is currently a can of worms.
164
177
  const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
165
178
  const functionObject = functions.find((fn) => fn === trigger.function?.target);
166
- if (!functionObject) {
167
- return shortId;
179
+ return functionObject?.name ?? shortId;
180
+ };
181
+
182
+ const scriptMatch = (script: Script.Script) => (trigger: Trigger.Trigger) => {
183
+ const fn = trigger.function?.target;
184
+ if (!Obj.instanceOf(Function.Function, fn)) {
185
+ return false;
168
186
  }
169
187
 
170
- return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
188
+ return fn.source?.target === script;
171
189
  };
190
+
191
+ const projectMatch = (project: DataType.Project) => {
192
+ const viewQueries = EFn.pipe(
193
+ project.collections,
194
+ Array.map((collection) => collection.target),
195
+ Array.filter(Schema.is(DataType.View)),
196
+ Array.map((view) => Obj.getSnapshot(view).query.ast),
197
+ Array.map((ast) => JSON.stringify(ast)),
198
+ );
199
+
200
+ return (trigger: Trigger.Trigger) => {
201
+ const spec = Obj.getSnapshot(trigger).spec;
202
+ if (spec?.kind !== 'subscription') {
203
+ return false;
204
+ }
205
+
206
+ // TODO(wittjosiah): Implement better way of comparing query ASTs.
207
+ return viewQueries.some((query) => JSON.stringify(spec.query) === query);
208
+ };
209
+ };
210
+
211
+ const triggerMatch = Match.type<Obj.Any>().pipe(
212
+ Match.withReturnType<(trigger: Trigger.Trigger) => boolean>(),
213
+ Match.when(
214
+ (obj) => Obj.instanceOf(Script.Script, obj),
215
+ (obj) => scriptMatch(obj),
216
+ ),
217
+ Match.when(
218
+ (obj) => Obj.instanceOf(DataType.Project, obj),
219
+ (obj) => projectMatch(obj),
220
+ ),
221
+ Match.orElse((_obj) => () => true),
222
+ );
@@ -0,0 +1,30 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { useTranslation } from '@dxos/react-ui';
8
+ import { ControlPage, ControlSection } from '@dxos/react-ui-form';
9
+
10
+ import { meta } from '../meta';
11
+
12
+ import { AutomationPanel, type AutomationPanelProps } from './AutomationPanel';
13
+ import { TriggersSettings } from './TriggerSettings';
14
+
15
+ export const AutomationSettings = (props: AutomationPanelProps) => {
16
+ const { t } = useTranslation(meta.id);
17
+ return (
18
+ <ControlPage>
19
+ <ControlSection
20
+ title={t('automation verbose label', { ns: meta.id })}
21
+ description={t('automation description', { ns: meta.id })}
22
+ >
23
+ <AutomationPanel {...props} />
24
+ <TriggersSettings space={props.space} />
25
+ </ControlSection>
26
+ </ControlPage>
27
+ );
28
+ };
29
+
30
+ export default AutomationSettings;
@@ -7,24 +7,22 @@ import React from 'react';
7
7
  import { type Space } from '@dxos/react-client/echo';
8
8
  import { useTranslation } from '@dxos/react-ui';
9
9
  import { ControlPage, ControlSection } from '@dxos/react-ui-form';
10
- import { StackItem } from '@dxos/react-ui-stack';
10
+
11
+ import { meta } from '../meta';
11
12
 
12
13
  import { FunctionsPanel } from './FunctionsPanel';
13
- import { AUTOMATION_PLUGIN } from '../meta';
14
14
 
15
15
  export const FunctionsContainer = ({ space }: { space: Space }) => {
16
- const { t } = useTranslation(AUTOMATION_PLUGIN);
16
+ const { t } = useTranslation(meta.id);
17
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>
18
+ <ControlPage>
19
+ <ControlSection
20
+ title={t('functions verbose label', { ns: meta.id })}
21
+ description={t('functions description', { ns: meta.id })}
22
+ >
23
+ <FunctionsPanel space={space} />
24
+ </ControlSection>
25
+ </ControlPage>
28
26
  );
29
27
  };
30
28
 
@@ -2,18 +2,19 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import React, { useCallback, useMemo } from 'react';
7
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';
8
+ import { LayoutAction, createIntent, useIntentDispatcher } from '@dxos/app-framework';
9
+ import { Function, Script } from '@dxos/functions';
10
+ import { SpaceAction } from '@dxos/plugin-space/types';
11
+ import { Filter, type Space, fullyQualifiedId, useQuery } from '@dxos/react-client/echo';
12
+ import { Button, IconButton, useTranslation } from '@dxos/react-ui';
12
13
  import { controlItemClasses } from '@dxos/react-ui-form';
13
14
  import { List } from '@dxos/react-ui-list';
14
15
  import { ghostHover, mx } from '@dxos/react-ui-theme';
15
16
 
16
- import { AUTOMATION_PLUGIN } from '../../meta';
17
+ import { meta } from '../../meta';
17
18
 
18
19
  const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
19
20
 
@@ -22,9 +23,9 @@ export type FunctionsPanelProps = {
22
23
  };
23
24
 
24
25
  export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
25
- const { t } = useTranslation(AUTOMATION_PLUGIN);
26
- const functions = useQuery(space, Filter.type(FunctionType));
27
- const scripts = useQuery(space, Filter.type(ScriptType));
26
+ const { t } = useTranslation(meta.id);
27
+ const functions = useQuery(space, Filter.type(Function.Function));
28
+ const scripts = useQuery(space, Filter.type(Script.Script));
28
29
  const { dispatchPromise: dispatch } = useIntentDispatcher();
29
30
 
30
31
  const functionToScriptMap = useMemo(
@@ -40,13 +41,13 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
40
41
  }
41
42
  return map;
42
43
  },
43
- {} as Record<string, ScriptType>,
44
+ {} as Record<string, Script.Script>,
44
45
  ),
45
46
  [functions, scripts],
46
47
  );
47
48
 
48
49
  const getScriptName = useCallback(
49
- (func: FunctionType) => {
50
+ (func: Function.Function) => {
50
51
  const script = functionToScriptMap[func.id];
51
52
  return script?.name;
52
53
  },
@@ -54,7 +55,7 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
54
55
  );
55
56
 
56
57
  const handleGoToScript = useCallback(
57
- (func: FunctionType) => {
58
+ (func: Function.Function) => {
58
59
  const script = functionToScriptMap[func.id];
59
60
  if (script) {
60
61
  void dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [fullyQualifiedId(script)] }));
@@ -63,14 +64,19 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
63
64
  [functionToScriptMap, dispatch],
64
65
  );
65
66
 
67
+ const handleDelete = useCallback(
68
+ (func: Function.Function) => dispatch(createIntent(SpaceAction.RemoveObjects, { objects: [func] })),
69
+ [dispatch],
70
+ );
71
+
66
72
  return (
67
73
  <div role='none' className={mx(controlItemClasses)}>
68
74
  {functions.length > 0 && (
69
- <List.Root<FunctionType> items={functions} isItem={Schema.is(FunctionType)} getId={(func) => func.id}>
75
+ <List.Root<Function.Function> items={functions} isItem={Schema.is(Function.Function)} getId={(func) => func.id}>
70
76
  {({ items }) => (
71
77
  <div role='list' className='flex flex-col w-full'>
72
78
  {items?.map((func) => (
73
- <List.Item<FunctionType>
79
+ <List.Item<Function.Function>
74
80
  key={func.id}
75
81
  item={func}
76
82
  classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
@@ -84,6 +90,12 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
84
90
  {functionToScriptMap[func.id] && (
85
91
  <Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
86
92
  )}
93
+ <IconButton
94
+ iconOnly
95
+ icon='ph--trash--regular'
96
+ label={t('delete function button label')}
97
+ onClick={() => handleDelete(func)}
98
+ />
87
99
  </List.Item>
88
100
  ))}
89
101
  </div>
@@ -5,13 +5,13 @@
5
5
  import React, { useCallback, useMemo } from 'react';
6
6
 
7
7
  import { Ref, Type } from '@dxos/echo';
8
- import { type JsonPath } from '@dxos/echo-schema';
9
- import { type FunctionType } from '@dxos/functions';
8
+ import { type JsonPath } from '@dxos/echo/internal';
9
+ import { type Function } from '@dxos/functions';
10
10
  import { useOnTransition } from '@dxos/react-ui';
11
11
  import { Form, type FormInputStateProps, type QueryRefOptions, useFormValues } from '@dxos/react-ui-form';
12
12
 
13
13
  export type FunctionInputEditorProps = {
14
- functions: FunctionType[];
14
+ functions: Function.Function[];
15
15
  onQueryRefOptions: QueryRefOptions;
16
16
  } & FormInputStateProps;
17
17
 
@@ -39,7 +39,13 @@ export const FunctionInputEditor = ({
39
39
  useOnTransition(
40
40
  // Clear function parameter input when the function changes.
41
41
  selectedFunctionValue,
42
- (prevValue) => prevValue !== undefined && prevValue !== selectedFunctionValue,
42
+ (prevValue) => {
43
+ if (!Ref.isRef(prevValue) || !Ref.isRef(selectedFunctionValue)) {
44
+ return false;
45
+ }
46
+
47
+ return prevValue.dxn.toString() !== selectedFunctionValue.dxn.toString();
48
+ },
43
49
  (currValue) => currValue !== undefined,
44
50
  () => onValueChange('object', {}),
45
51
  );
@@ -4,32 +4,38 @@
4
4
 
5
5
  import React, { useCallback, useMemo } from 'react';
6
6
 
7
- import { type FunctionTriggerType, TriggerKind, type TriggerType } from '@dxos/functions';
7
+ import { Filter, Query } from '@dxos/echo';
8
+ import { Trigger } from '@dxos/functions';
8
9
  import { useTranslation } from '@dxos/react-ui';
9
- import { SelectInput, type InputProps, useInputProps } from '@dxos/react-ui-form';
10
+ import { type InputProps, SelectInput, useInputProps } from '@dxos/react-ui-form';
10
11
 
11
- import { AUTOMATION_PLUGIN } from '../../meta';
12
+ import { meta } from '../../meta';
12
13
 
13
14
  export type SpecSelectorProps = InputProps;
14
15
 
15
16
  export const SpecSelector = (props: SpecSelectorProps) => {
16
- const { t } = useTranslation(AUTOMATION_PLUGIN);
17
- const specProps = useInputProps(['spec' satisfies keyof FunctionTriggerType]);
17
+ const { t } = useTranslation(meta.id);
18
+ const specProps = useInputProps(['spec' satisfies keyof Trigger.Trigger]);
18
19
 
19
20
  const handleTypeChange = useCallback(
20
- (_type: any, value: string): TriggerType | undefined => {
21
+ (_type: any, value: string): Trigger.Spec | undefined => {
21
22
  const getDefaultTriggerSpec = (kind: string) => {
22
23
  switch (kind) {
23
- case TriggerKind.Timer:
24
- return { kind: TriggerKind.Timer, cron: '' };
25
- case TriggerKind.Subscription:
26
- return { kind: TriggerKind.Subscription, filter: {} };
27
- case TriggerKind.Queue:
28
- return { kind: TriggerKind.Queue, queue: '' };
29
- case TriggerKind.Email:
30
- return { kind: TriggerKind.Email };
31
- case TriggerKind.Webhook:
32
- return { kind: TriggerKind.Webhook };
24
+ case 'timer':
25
+ return { kind: 'timer', cron: '' };
26
+ case 'subscription':
27
+ return {
28
+ kind: 'subscription',
29
+ query: {
30
+ ast: Query.select(Filter.nothing()).ast,
31
+ },
32
+ };
33
+ case 'queue':
34
+ return { kind: 'queue', queue: 'dxn:' };
35
+ case 'email':
36
+ return { kind: 'email' };
37
+ case 'webhook':
38
+ return { kind: 'webhook' };
33
39
  default:
34
40
  return undefined;
35
41
  }
@@ -48,7 +54,7 @@ export const SpecSelector = (props: SpecSelectorProps) => {
48
54
 
49
55
  const options = useMemo(
50
56
  () =>
51
- Object.values(TriggerKind).map((kind) => ({
57
+ Trigger.Kinds.map((kind) => ({
52
58
  value: kind,
53
59
  label: t(`trigger type ${kind}`),
54
60
  })),