@dxos/plugin-automation 0.8.4-main.f9ba587 → 0.8.4-main.fffef41

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 (201) hide show
  1. package/dist/lib/browser/AutomationPanel-7OECLR5N.mjs +11 -0
  2. package/dist/lib/browser/AutomationSettings-KCZISXI6.mjs +68 -0
  3. package/dist/lib/browser/AutomationSettings-KCZISXI6.mjs.map +7 -0
  4. package/dist/lib/browser/FunctionsContainer-MIWEISRR.mjs +151 -0
  5. package/dist/lib/browser/FunctionsContainer-MIWEISRR.mjs.map +7 -0
  6. package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs → FunctionsPanel-CRW6SJUN.mjs} +3 -3
  7. package/dist/lib/browser/{app-graph-builder-ZTAUTFI4.mjs → app-graph-builder-W7LLC6XW.mjs} +13 -12
  8. package/dist/lib/browser/app-graph-builder-W7LLC6XW.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-EPEXQP45.mjs +14 -0
  10. package/dist/lib/browser/chunk-EPEXQP45.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-FSJZXTS2.mjs → chunk-JW7XSPYW.mjs} +69 -32
  12. package/dist/lib/browser/chunk-JW7XSPYW.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-LZQFZO3B.mjs +17 -0
  14. package/dist/lib/browser/chunk-LZQFZO3B.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-NAPXRXTY.mjs +100 -0
  16. package/dist/lib/browser/chunk-NAPXRXTY.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-ERTIGJYE.mjs → chunk-QKFBHAGN.mjs} +72 -35
  18. package/dist/lib/browser/chunk-QKFBHAGN.mjs.map +7 -0
  19. package/dist/lib/browser/{chunk-LYJVTIVD.mjs → chunk-TWWFNOIR.mjs} +30 -15
  20. package/dist/lib/browser/chunk-TWWFNOIR.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-XAKZ4ANY.mjs +15 -0
  22. package/dist/lib/browser/chunk-XAKZ4ANY.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-S2LE7SYB.mjs → chunk-YBPJCY3F.mjs} +6 -7
  24. package/dist/lib/browser/chunk-YBPJCY3F.mjs.map +7 -0
  25. package/dist/lib/browser/compute-runtime-EIQTHJFZ.mjs +113 -0
  26. package/dist/lib/browser/compute-runtime-EIQTHJFZ.mjs.map +7 -0
  27. package/dist/lib/browser/hooks/index.mjs +13 -0
  28. package/dist/lib/browser/index.mjs +48 -19
  29. package/dist/lib/browser/index.mjs.map +4 -4
  30. package/dist/lib/browser/{intent-resolver-YLRSMLH4.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-YQW7WCFW.mjs} +20 -20
  34. package/dist/lib/browser/react-surface-YQW7WCFW.mjs.map +7 -0
  35. package/dist/lib/browser/types/index.mjs +8 -0
  36. package/dist/lib/node-esm/{AutomationPanel-YYUMSK2W.mjs → AutomationPanel-HCVFNHGQ.mjs} +4 -4
  37. package/dist/lib/node-esm/AutomationSettings-UUUPVNUJ.mjs +69 -0
  38. package/dist/lib/node-esm/AutomationSettings-UUUPVNUJ.mjs.map +7 -0
  39. package/dist/lib/node-esm/FunctionsContainer-6OB3JN5O.mjs +152 -0
  40. package/dist/lib/node-esm/FunctionsContainer-6OB3JN5O.mjs.map +7 -0
  41. package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs → FunctionsPanel-RVVCS6VH.mjs} +3 -3
  42. package/dist/lib/node-esm/{app-graph-builder-3FP63ZSG.mjs → app-graph-builder-SLQOO7GH.mjs} +13 -12
  43. package/dist/lib/node-esm/app-graph-builder-SLQOO7GH.mjs.map +7 -0
  44. package/dist/lib/node-esm/{chunk-HIMYPGHF.mjs → chunk-6YRKST6M.mjs} +72 -35
  45. package/dist/lib/node-esm/chunk-6YRKST6M.mjs.map +7 -0
  46. package/dist/lib/node-esm/chunk-CEVIVRTY.mjs +19 -0
  47. package/dist/lib/node-esm/chunk-CEVIVRTY.mjs.map +7 -0
  48. package/dist/lib/node-esm/chunk-CPP35BE6.mjs +16 -0
  49. package/dist/lib/node-esm/chunk-CPP35BE6.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-UKATFY3R.mjs → chunk-ECJKIUBO.mjs} +6 -7
  51. package/dist/lib/node-esm/chunk-ECJKIUBO.mjs.map +7 -0
  52. package/dist/lib/node-esm/chunk-KB7NFEYY.mjs +16 -0
  53. package/dist/lib/node-esm/chunk-KB7NFEYY.mjs.map +7 -0
  54. package/dist/lib/node-esm/chunk-PICJ2REN.mjs +101 -0
  55. package/dist/lib/node-esm/chunk-PICJ2REN.mjs.map +7 -0
  56. package/dist/lib/node-esm/{chunk-ZGPUV5VS.mjs → chunk-RVK52XGK.mjs} +30 -15
  57. package/dist/lib/node-esm/chunk-RVK52XGK.mjs.map +7 -0
  58. package/dist/lib/node-esm/{chunk-AZH66CED.mjs → chunk-W76WUTZY.mjs} +69 -32
  59. package/dist/lib/node-esm/chunk-W76WUTZY.mjs.map +7 -0
  60. package/dist/lib/node-esm/compute-runtime-URROOC34.mjs +114 -0
  61. package/dist/lib/node-esm/compute-runtime-URROOC34.mjs.map +7 -0
  62. package/dist/lib/node-esm/hooks/index.mjs +14 -0
  63. package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
  64. package/dist/lib/node-esm/index.mjs +48 -19
  65. package/dist/lib/node-esm/index.mjs.map +4 -4
  66. package/dist/lib/node-esm/{intent-resolver-LU7EYWEO.mjs → intent-resolver-KDRYB5BC.mjs} +15 -14
  67. package/dist/lib/node-esm/intent-resolver-KDRYB5BC.mjs.map +7 -0
  68. package/dist/lib/node-esm/meta.json +1 -1
  69. package/dist/lib/node-esm/{react-surface-3PNW7NDW.mjs → react-surface-NNHYNBO6.mjs} +20 -20
  70. package/dist/lib/node-esm/react-surface-NNHYNBO6.mjs.map +7 -0
  71. package/dist/lib/node-esm/{types.mjs → types/index.mjs} +3 -3
  72. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  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 +20 -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 +48 -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/FunctionsRegistry/FunctionsRegistry.d.ts +8 -0
  96. package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts.map +1 -0
  97. package/dist/types/src/components/FunctionsRegistry/index.d.ts +2 -0
  98. package/dist/types/src/components/FunctionsRegistry/index.d.ts.map +1 -0
  99. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts +2 -2
  100. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
  101. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
  102. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +7 -5
  103. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  104. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +49 -4
  105. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  106. package/dist/types/src/components/TriggerSettings.d.ts +6 -0
  107. package/dist/types/src/components/TriggerSettings.d.ts.map +1 -0
  108. package/dist/types/src/components/index.d.ts +2 -2
  109. package/dist/types/src/components/index.d.ts.map +1 -1
  110. package/dist/types/src/events.d.ts +4 -0
  111. package/dist/types/src/events.d.ts.map +1 -0
  112. package/dist/types/src/hooks/index.d.ts +3 -0
  113. package/dist/types/src/hooks/index.d.ts.map +1 -0
  114. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +12 -0
  115. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -0
  116. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +11 -0
  117. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -0
  118. package/dist/types/src/index.d.ts +3 -0
  119. package/dist/types/src/index.d.ts.map +1 -1
  120. package/dist/types/src/meta.d.ts +0 -1
  121. package/dist/types/src/meta.d.ts.map +1 -1
  122. package/dist/types/src/translations.d.ts +5 -0
  123. package/dist/types/src/translations.d.ts.map +1 -1
  124. package/dist/types/src/types/index.d.ts +2 -0
  125. package/dist/types/src/types/index.d.ts.map +1 -0
  126. package/dist/types/src/{types.d.ts → types/schema.d.ts} +4 -4
  127. package/dist/types/src/types/schema.d.ts.map +1 -0
  128. package/dist/types/tsconfig.tsbuildinfo +1 -1
  129. package/package.json +66 -41
  130. package/src/AutomationPlugin.tsx +37 -31
  131. package/src/capabilities/app-graph-builder.ts +16 -15
  132. package/src/capabilities/capabilities.ts +41 -0
  133. package/src/capabilities/compute-runtime.ts +127 -0
  134. package/src/capabilities/index.ts +3 -0
  135. package/src/capabilities/intent-resolver.ts +11 -11
  136. package/src/capabilities/react-surface.tsx +15 -15
  137. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +16 -17
  138. package/src/components/AutomationPanel/AutomationPanel.tsx +105 -53
  139. package/src/components/AutomationSettings.tsx +30 -0
  140. package/src/components/FunctionsContainer.tsx +18 -13
  141. package/src/components/FunctionsPanel/FunctionsPanel.tsx +35 -16
  142. package/src/components/FunctionsRegistry/FunctionsRegistry.tsx +135 -0
  143. package/src/components/FunctionsRegistry/index.ts +5 -0
  144. package/src/components/TriggerEditor/FunctionInputEditor.tsx +10 -4
  145. package/src/components/TriggerEditor/SpecSelector.tsx +23 -17
  146. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +70 -27
  147. package/src/components/TriggerEditor/TriggerEditor.tsx +45 -25
  148. package/src/components/TriggerSettings.tsx +25 -0
  149. package/src/components/index.ts +1 -1
  150. package/src/events.ts +11 -0
  151. package/src/hooks/index.ts +6 -0
  152. package/src/hooks/useComputeRuntimeCallback.ts +68 -0
  153. package/src/hooks/useTriggerRuntimeControls.ts +53 -0
  154. package/src/index.ts +3 -0
  155. package/src/meta.ts +6 -5
  156. package/src/testing/test-functions.ts +1 -1
  157. package/src/translations.ts +7 -0
  158. package/src/types/index.ts +5 -0
  159. package/src/{types.ts → types/schema.ts} +3 -5
  160. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs +0 -38
  161. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs.map +0 -7
  162. package/dist/lib/browser/AutomationPanel-ZWA6GOFY.mjs +0 -11
  163. package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs +0 -39
  164. package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs.map +0 -7
  165. package/dist/lib/browser/app-graph-builder-ZTAUTFI4.mjs.map +0 -7
  166. package/dist/lib/browser/chunk-ECSTS2UI.mjs +0 -14
  167. package/dist/lib/browser/chunk-ECSTS2UI.mjs.map +0 -7
  168. package/dist/lib/browser/chunk-ERTIGJYE.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-FSJZXTS2.mjs.map +0 -7
  170. package/dist/lib/browser/chunk-GW5U2DGT.mjs +0 -15
  171. package/dist/lib/browser/chunk-GW5U2DGT.mjs.map +0 -7
  172. package/dist/lib/browser/chunk-LYJVTIVD.mjs.map +0 -7
  173. package/dist/lib/browser/chunk-S2LE7SYB.mjs.map +0 -7
  174. package/dist/lib/browser/intent-resolver-YLRSMLH4.mjs.map +0 -7
  175. package/dist/lib/browser/react-surface-4DFSM7OX.mjs.map +0 -7
  176. package/dist/lib/browser/types.mjs +0 -8
  177. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs +0 -39
  178. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs.map +0 -7
  179. package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs +0 -40
  180. package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs.map +0 -7
  181. package/dist/lib/node-esm/app-graph-builder-3FP63ZSG.mjs.map +0 -7
  182. package/dist/lib/node-esm/chunk-AZH66CED.mjs.map +0 -7
  183. package/dist/lib/node-esm/chunk-HIMYPGHF.mjs.map +0 -7
  184. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs +0 -17
  185. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs.map +0 -7
  186. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs +0 -16
  187. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs.map +0 -7
  188. package/dist/lib/node-esm/chunk-UKATFY3R.mjs.map +0 -7
  189. package/dist/lib/node-esm/chunk-ZGPUV5VS.mjs.map +0 -7
  190. package/dist/lib/node-esm/intent-resolver-LU7EYWEO.mjs.map +0 -7
  191. package/dist/lib/node-esm/react-surface-3PNW7NDW.mjs.map +0 -7
  192. package/dist/types/src/components/AutomationContainer.d.ts +0 -5
  193. package/dist/types/src/components/AutomationContainer.d.ts.map +0 -1
  194. package/dist/types/src/types.d.ts.map +0 -1
  195. package/src/components/AutomationContainer.tsx +0 -30
  196. /package/dist/lib/browser/{AutomationPanel-ZWA6GOFY.mjs.map → AutomationPanel-7OECLR5N.mjs.map} +0 -0
  197. /package/dist/lib/browser/{FunctionsPanel-56ZKRVM5.mjs.map → FunctionsPanel-CRW6SJUN.mjs.map} +0 -0
  198. /package/dist/lib/browser/{types.mjs.map → hooks/index.mjs.map} +0 -0
  199. /package/dist/lib/{node-esm/AutomationPanel-YYUMSK2W.mjs.map → browser/types/index.mjs.map} +0 -0
  200. /package/dist/lib/node-esm/{FunctionsPanel-KGIOZSPZ.mjs.map → AutomationPanel-HCVFNHGQ.mjs.map} +0 -0
  201. /package/dist/lib/node-esm/{types.mjs.map → FunctionsPanel-RVVCS6VH.mjs.map} +0 -0
@@ -2,56 +2,55 @@
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];
23
21
 
24
22
  return (
25
- <div className='w-96'>
23
+ <div className='is-96'>
26
24
  <AutomationPanel space={space} />
27
25
  </div>
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,62 @@
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 { View } from '@dxos/schema';
21
+ import { Project } from '@dxos/types';
23
22
 
24
- import { AUTOMATION_PLUGIN } from '../../meta';
23
+ import { meta } from '../../meta';
25
24
  import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
26
25
 
27
26
  const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
28
27
 
29
- export type AutomationPanelProps = {
28
+ export type AutomationPanelProps = ThemedClassName<{
30
29
  space: Space;
31
30
  object?: Obj.Any;
32
- initialTrigger?: FunctionTriggerType;
31
+ initialTrigger?: Trigger.Trigger;
33
32
  onDone?: () => void;
34
- };
33
+ }>;
35
34
 
36
35
  // TODO(burdon): Factor out common layout with ViewEditor.
37
- export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
38
- const { t } = useTranslation(AUTOMATION_PLUGIN);
36
+ export const AutomationPanel = ({ classNames, space, object, initialTrigger, onDone }: AutomationPanelProps) => {
37
+ const { t } = useTranslation(meta.id);
39
38
  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);
39
+ const functions = useQuery(space, Filter.type(Function.Function));
40
+ const triggers = useQuery(space, Filter.type(Trigger.Trigger));
41
+ const filteredTriggers = useMemo(() => {
42
+ return object ? triggers.filter(triggerMatch(object)) : triggers;
43
+ }, [object, triggers]);
44
+ const tags = useQuery(space, Filter.type(Tag.Tag));
45
+ const types = useTypeOptions({ space, annotation: ['dynamic', 'limited-static', 'object-form'] });
46
+
47
+ const [trigger, setTrigger] = useState<Trigger.Trigger | undefined>(initialTrigger);
48
+ const [selected, setSelected] = useState<Trigger.Trigger>();
49
+
50
+ const handleSelect = (trigger: Trigger.Trigger) => {
51
+ setTrigger(trigger);
50
52
  setSelected(trigger);
51
53
  };
52
54
 
53
55
  const handleAdd = () => {
54
- setTrigger(Obj.make(FunctionTriggerSchema, {}));
56
+ setTrigger(Trigger.make({}));
55
57
  setSelected(undefined);
56
58
  };
57
59
 
58
- const handleDelete = (trigger: FunctionTrigger) => {
60
+ const handleDelete = (trigger: Trigger.Trigger) => {
59
61
  space.db.remove(trigger);
60
62
  setTrigger(undefined);
61
63
  setSelected(undefined);
@@ -65,7 +67,7 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
65
67
  if (selected) {
66
68
  Object.assign(selected, trigger);
67
69
  } else {
68
- space.db.add(Obj.make(FunctionTrigger, trigger));
70
+ space.db.add(Trigger.make(trigger));
69
71
  }
70
72
 
71
73
  setTrigger(undefined);
@@ -81,24 +83,36 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
81
83
  if (trigger) {
82
84
  return (
83
85
  <ControlItem title={t('trigger editor title')}>
84
- <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />
86
+ <TriggerEditor
87
+ space={space}
88
+ trigger={trigger}
89
+ readonlySpec={Boolean(object)}
90
+ tags={tags}
91
+ types={types}
92
+ onSave={handleSave}
93
+ onCancel={handleCancel}
94
+ />
85
95
  </ControlItem>
86
96
  );
87
97
  }
88
98
 
89
99
  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 }) => (
94
- <div role='list' className='flex flex-col w-full'>
95
- {triggers?.map((trigger) => {
100
+ <div className={mx(controlItemClasses, classNames)}>
101
+ {filteredTriggers.length > 0 && (
102
+ <List.Root<Trigger.Trigger>
103
+ items={filteredTriggers}
104
+ isItem={Schema.is(Trigger.Trigger)}
105
+ getId={(field) => field.id}
106
+ >
107
+ {({ items: filteredTriggers }) => (
108
+ <div role='list' className='flex flex-col is-full'>
109
+ {filteredTriggers?.map((trigger) => {
96
110
  const copyAction = getCopyAction(client, trigger);
97
111
  return (
98
- <List.Item<FunctionTrigger>
112
+ <List.Item<Trigger.Trigger>
99
113
  key={trigger.id}
100
114
  item={trigger}
101
- classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
115
+ classNames={mx(grid, ghostHover, 'items-center', 'pli-2')}
102
116
  >
103
117
  <Input.Root>
104
118
  <Input.Switch
@@ -109,10 +123,10 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
109
123
 
110
124
  <div className={'flex'}>
111
125
  <List.ItemTitle
112
- classNames='px-1 cursor-pointer w-0 shrink truncate'
126
+ classNames='pli-1 cursor-pointer is-0 shrink truncate'
113
127
  onClick={() => handleSelect(trigger)}
114
128
  >
115
- {getFunctionName(scripts, functions, trigger) ?? '∅'}
129
+ {getFunctionName(functions, trigger) ?? '∅'}
116
130
  </List.ItemTitle>
117
131
 
118
132
  {/* TODO: a better way to expose copy action */}
@@ -132,25 +146,25 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
132
146
  )}
133
147
  </List.Root>
134
148
  )}
135
- {triggers.length > 0 && <Separator classNames='mlb-4' />}
149
+ {filteredTriggers.length > 0 && <Separator classNames='mlb-4' />}
136
150
  <IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
137
151
  </div>
138
152
  );
139
153
  };
140
154
 
141
- const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
142
- if (trigger?.spec?.kind === TriggerKind.Email) {
155
+ const getCopyAction = (client: Client, trigger: Trigger.Trigger | undefined) => {
156
+ if (trigger?.spec?.kind === 'email') {
143
157
  return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
144
158
  }
145
159
 
146
- if (trigger?.spec?.kind === TriggerKind.Webhook) {
160
+ if (trigger?.spec?.kind === 'webhook') {
147
161
  return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
148
162
  }
149
163
 
150
164
  return undefined;
151
165
  };
152
166
 
153
- const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
167
+ const getWebhookUrl = (client: Client, trigger: Trigger.Trigger) => {
154
168
  const spaceId = getSpace(trigger)!.id;
155
169
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
156
170
  const isSecure = edgeUrl.protocol.startsWith('https') || edgeUrl.protocol.startsWith('wss');
@@ -158,14 +172,52 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
158
172
  return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
159
173
  };
160
174
 
161
- const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
175
+ const getFunctionName = (functions: Function.Function[], trigger: Trigger.Trigger) => {
162
176
  // TODO(wittjosiah): Truncation should be done in the UI.
163
177
  // Warning that the List component is currently a can of worms.
164
178
  const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
165
179
  const functionObject = functions.find((fn) => fn === trigger.function?.target);
166
- if (!functionObject) {
167
- return shortId;
180
+ return functionObject?.name ?? shortId;
181
+ };
182
+
183
+ const scriptMatch = (script: Script.Script) => (trigger: Trigger.Trigger) => {
184
+ const fn = trigger.function?.target;
185
+ if (!Obj.instanceOf(Function.Function, fn)) {
186
+ return false;
168
187
  }
169
188
 
170
- return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
189
+ return fn.source?.target === script;
171
190
  };
191
+
192
+ const projectMatch = (project: Project.Project) => {
193
+ const viewQueries = EFn.pipe(
194
+ project.collections,
195
+ Array.map((collection) => collection.target),
196
+ Array.filter(Schema.is(View.View)),
197
+ Array.map((view) => Obj.getSnapshot(view).query.ast),
198
+ Array.map((ast) => JSON.stringify(ast)),
199
+ );
200
+
201
+ return (trigger: Trigger.Trigger) => {
202
+ const spec = Obj.getSnapshot(trigger).spec;
203
+ if (spec?.kind !== 'subscription') {
204
+ return false;
205
+ }
206
+
207
+ // TODO(wittjosiah): Implement better way of comparing query ASTs.
208
+ return viewQueries.some((query) => JSON.stringify(spec.query) === query);
209
+ };
210
+ };
211
+
212
+ const triggerMatch = Match.type<Obj.Any>().pipe(
213
+ Match.withReturnType<(trigger: Trigger.Trigger) => boolean>(),
214
+ Match.when(
215
+ (obj) => Obj.instanceOf(Script.Script, obj),
216
+ (obj) => scriptMatch(obj),
217
+ ),
218
+ Match.when(
219
+ (obj) => Obj.instanceOf(Project.Project, obj),
220
+ (obj) => projectMatch(obj),
221
+ ),
222
+ Match.orElse((_obj) => () => true),
223
+ );
@@ -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,29 @@ 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
+ import { FunctionsRegistry } from './FunctionsRegistry';
14
15
 
15
16
  export const FunctionsContainer = ({ space }: { space: Space }) => {
16
- const { t } = useTranslation(AUTOMATION_PLUGIN);
17
+ const { t } = useTranslation(meta.id);
17
18
  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>
19
+ <ControlPage>
20
+ <ControlSection
21
+ title={t('functions verbose label', { ns: meta.id })}
22
+ description={t('functions description', { ns: meta.id })}
23
+ >
24
+ <FunctionsPanel space={space} />
25
+ </ControlSection>
26
+ <ControlSection
27
+ title={t('functions registry verbose label', { ns: meta.id })}
28
+ description={t('functions registry description', { ns: meta.id })}
29
+ >
30
+ <FunctionsRegistry space={space} />
31
+ </ControlSection>
32
+ </ControlPage>
28
33
  );
29
34
  };
30
35
 
@@ -2,18 +2,21 @@
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 } from '@dxos/app-framework';
9
+ import { useIntentDispatcher } from '@dxos/app-framework/react';
10
+ import { Obj } from '@dxos/echo';
11
+ import { Function, Script } from '@dxos/functions';
12
+ import { SpaceAction } from '@dxos/plugin-space/types';
13
+ import { Filter, type Space, useQuery } from '@dxos/react-client/echo';
14
+ import { Button, IconButton, useTranslation } from '@dxos/react-ui';
12
15
  import { controlItemClasses } from '@dxos/react-ui-form';
13
16
  import { List } from '@dxos/react-ui-list';
14
17
  import { ghostHover, mx } from '@dxos/react-ui-theme';
15
18
 
16
- import { AUTOMATION_PLUGIN } from '../../meta';
19
+ import { meta } from '../../meta';
17
20
 
18
21
  const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
19
22
 
@@ -22,9 +25,9 @@ export type FunctionsPanelProps = {
22
25
  };
23
26
 
24
27
  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));
28
+ const { t } = useTranslation(meta.id);
29
+ const functions = useQuery(space, Filter.type(Function.Function));
30
+ const scripts = useQuery(space, Filter.type(Script.Script));
28
31
  const { dispatchPromise: dispatch } = useIntentDispatcher();
29
32
 
30
33
  const functionToScriptMap = useMemo(
@@ -40,13 +43,13 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
40
43
  }
41
44
  return map;
42
45
  },
43
- {} as Record<string, ScriptType>,
46
+ {} as Record<string, Script.Script>,
44
47
  ),
45
48
  [functions, scripts],
46
49
  );
47
50
 
48
51
  const getScriptName = useCallback(
49
- (func: FunctionType) => {
52
+ (func: Function.Function) => {
50
53
  const script = functionToScriptMap[func.id];
51
54
  return script?.name;
52
55
  },
@@ -54,23 +57,33 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
54
57
  );
55
58
 
56
59
  const handleGoToScript = useCallback(
57
- (func: FunctionType) => {
60
+ (func: Function.Function) => {
58
61
  const script = functionToScriptMap[func.id];
59
62
  if (script) {
60
- void dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [fullyQualifiedId(script)] }));
63
+ void dispatch(
64
+ createIntent(LayoutAction.Open, {
65
+ part: 'main',
66
+ subject: [Obj.getDXN(script).toString()],
67
+ }),
68
+ );
61
69
  }
62
70
  },
63
71
  [functionToScriptMap, dispatch],
64
72
  );
65
73
 
74
+ const handleDelete = useCallback(
75
+ (func: Function.Function) => dispatch(createIntent(SpaceAction.RemoveObjects, { objects: [func] })),
76
+ [dispatch],
77
+ );
78
+
66
79
  return (
67
80
  <div role='none' className={mx(controlItemClasses)}>
68
81
  {functions.length > 0 && (
69
- <List.Root<FunctionType> items={functions} isItem={Schema.is(FunctionType)} getId={(func) => func.id}>
82
+ <List.Root<Function.Function> items={functions} isItem={Schema.is(Function.Function)} getId={(func) => func.id}>
70
83
  {({ items }) => (
71
- <div role='list' className='flex flex-col w-full'>
84
+ <div role='list' className='flex flex-col is-full'>
72
85
  {items?.map((func) => (
73
- <List.Item<FunctionType>
86
+ <List.Item<Function.Function>
74
87
  key={func.id}
75
88
  item={func}
76
89
  classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
@@ -84,6 +97,12 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
84
97
  {functionToScriptMap[func.id] && (
85
98
  <Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
86
99
  )}
100
+ <IconButton
101
+ iconOnly
102
+ icon='ph--trash--regular'
103
+ label={t('delete function button label')}
104
+ onClick={() => handleDelete(func)}
105
+ />
87
106
  </List.Item>
88
107
  ))}
89
108
  </div>
@@ -0,0 +1,135 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Array from 'effect/Array';
6
+ import * as EffectFunction from 'effect/Function';
7
+ import * as Order from 'effect/Order';
8
+ import * as Schema from 'effect/Schema';
9
+ import { useState } from 'react';
10
+ import React, { useCallback } from 'react';
11
+
12
+ import { Obj } from '@dxos/echo';
13
+ import { Function } from '@dxos/functions';
14
+ import { getDeployedFunctions } from '@dxos/functions-runtime/edge';
15
+ import { useClient } from '@dxos/react-client';
16
+ import { Filter, Query, type Space, useQuery } from '@dxos/react-client/echo';
17
+ import { useAsyncEffect } from '@dxos/react-ui';
18
+ import { IconButton, useTranslation } from '@dxos/react-ui';
19
+ import { controlItemClasses } from '@dxos/react-ui-form';
20
+ import { List } from '@dxos/react-ui-list';
21
+ import { ghostHover, mx } from '@dxos/react-ui-theme';
22
+
23
+ import { meta } from '../../meta';
24
+
25
+ const grid = 'grid grid-cols-[1fr_1fr_auto] min-bs-[2.5rem]';
26
+
27
+ type FunctionsRegistryProps = {
28
+ space: Space;
29
+ };
30
+
31
+ export const FunctionsRegistry = ({ space }: FunctionsRegistryProps) => {
32
+ const client = useClient();
33
+ const [loading, setLoading] = useState(true);
34
+ const [functions, setFunctions] = useState<Function.Function[]>([]);
35
+ const { t } = useTranslation(meta.id);
36
+
37
+ const dbFunctions = useQuery(space, Filter.type(Function.Function));
38
+
39
+ const state = (func: Function.Function) => {
40
+ const dbFunction = dbFunctions.find((f) => f.key === func.key);
41
+ if (!dbFunction) {
42
+ return 'import';
43
+ }
44
+ if (dbFunction.version === func.version && dbFunction.updated === func.updated) {
45
+ return 'none';
46
+ }
47
+ return 'update';
48
+ };
49
+
50
+ useAsyncEffect(async () => {
51
+ setLoading(true);
52
+ const functions = await getDeployedFunctions(client);
53
+ setFunctions(functions);
54
+ setLoading(false);
55
+ }, []);
56
+
57
+ const dedupedFunctions = EffectFunction.pipe(
58
+ functions,
59
+ Array.filter((_) => _.key !== undefined),
60
+ Array.sort(Order.reverse(Order.mapInput(Order.string, (_: Function.Function) => _.updated ?? ''))),
61
+ Array.dedupeWith((self, that) => self.key === that.key),
62
+ Array.sort(Order.mapInput(Order.string, (_: Function.Function) => _.key ?? '')),
63
+ );
64
+
65
+ const hanleImportOrUpdate = useCallback(
66
+ async (func: Function.Function) => {
67
+ const {
68
+ objects: [existingFunc],
69
+ } = await space.db.query(Query.type(Function.Function, { key: func.key })).run();
70
+ if (!existingFunc) {
71
+ space.db.add(func);
72
+ return;
73
+ }
74
+ existingFunc.version = func.version;
75
+ existingFunc.updated = func.updated;
76
+ existingFunc.name = func.name;
77
+ existingFunc.description = func.description;
78
+ // TODO(dmaretskyi): A workaround for an ECHO bug.
79
+ existingFunc.inputSchema = JSON.parse(JSON.stringify(func.inputSchema));
80
+ existingFunc.outputSchema = JSON.parse(JSON.stringify(func.outputSchema));
81
+ Obj.getMeta(existingFunc).keys = JSON.parse(JSON.stringify(Obj.getMeta(func).keys));
82
+ },
83
+ [space],
84
+ );
85
+
86
+ return (
87
+ <div role='none' className={mx(controlItemClasses)}>
88
+ {dedupedFunctions.length > 0 && (
89
+ <List.Root<Function.Function>
90
+ items={dedupedFunctions}
91
+ isItem={Schema.is(Function.Function)}
92
+ getId={(func) => func.id}
93
+ >
94
+ {({ items }) => (
95
+ <div role='list' className='flex flex-col is-full'>
96
+ {items?.map((func) => (
97
+ <List.Item<Function.Function>
98
+ key={func.id}
99
+ item={func}
100
+ classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
101
+ >
102
+ <div className='flex flex-col truncate'>
103
+ <List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
104
+ <div className='text-xs text-description truncate'>{func.key}</div>
105
+ </div>
106
+ <div className='flex flex-col truncate'>
107
+ <div className='text-xs text-description truncate'>{func.version}</div>
108
+ <div className='text-xs text-description truncate'>
109
+ {func.updated ? `Uploaded ${new Date(func.updated).toLocaleString()}` : ''}
110
+ </div>
111
+ </div>
112
+
113
+ <IconButton
114
+ iconOnly
115
+ icon={state(func) === 'update' ? 'ph--arrows-clockwise--regular' : 'ph--download--regular'}
116
+ label={
117
+ state(func) === 'update' ? t('update function button label') : t('import function button label')
118
+ }
119
+ disabled={state(func) === 'none'}
120
+ onClick={() => hanleImportOrUpdate(func)}
121
+ />
122
+ </List.Item>
123
+ ))}
124
+ </div>
125
+ )}
126
+ </List.Root>
127
+ )}
128
+
129
+ {dedupedFunctions.length === 0 && !loading && (
130
+ <div className='text-center plb-4 text-gray-500'>{t('no functions found')}</div>
131
+ )}
132
+ {loading && <div className='text-center plb-4 text-gray-500'>{t('loading functions')}</div>}
133
+ </div>
134
+ );
135
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export { FunctionsRegistry } from './FunctionsRegistry';