@dxos/plugin-automation 0.8.4-main.b97322e → 0.8.4-main.bcb3aa67d6

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 (263) hide show
  1. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  2. package/dist/lib/browser/chunk-NQY2QL47.mjs +44 -0
  3. package/dist/lib/browser/chunk-NQY2QL47.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-WZKRIDQ6.mjs +54 -0
  5. package/dist/lib/browser/chunk-WZKRIDQ6.mjs.map +7 -0
  6. package/dist/lib/browser/cli/index.mjs +1122 -0
  7. package/dist/lib/browser/cli/index.mjs.map +7 -0
  8. package/dist/lib/browser/create-trigger-from-template-J2Z7AAHV.mjs +77 -0
  9. package/dist/lib/browser/create-trigger-from-template-J2Z7AAHV.mjs.map +7 -0
  10. package/dist/lib/browser/hooks/index.mjs +117 -0
  11. package/dist/lib/browser/hooks/index.mjs.map +7 -0
  12. package/dist/lib/browser/index.mjs +72 -83
  13. package/dist/lib/browser/index.mjs.map +4 -4
  14. package/dist/lib/browser/meta.json +1 -1
  15. package/dist/lib/browser/operations/index.mjs +14 -0
  16. package/dist/lib/browser/operations/index.mjs.map +7 -0
  17. package/dist/lib/browser/types/index.mjs +10 -4
  18. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  19. package/dist/lib/node-esm/chunk-POUAOKR5.mjs +56 -0
  20. package/dist/lib/node-esm/chunk-POUAOKR5.mjs.map +7 -0
  21. package/dist/lib/node-esm/chunk-Z6J7TDVZ.mjs +45 -0
  22. package/dist/lib/node-esm/chunk-Z6J7TDVZ.mjs.map +7 -0
  23. package/dist/lib/node-esm/cli/index.mjs +1123 -0
  24. package/dist/lib/node-esm/cli/index.mjs.map +7 -0
  25. package/dist/lib/node-esm/create-trigger-from-template-TRSHHYEB.mjs +78 -0
  26. package/dist/lib/node-esm/create-trigger-from-template-TRSHHYEB.mjs.map +7 -0
  27. package/dist/lib/node-esm/hooks/index.mjs +118 -0
  28. package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
  29. package/dist/lib/node-esm/index.mjs +72 -83
  30. package/dist/lib/node-esm/index.mjs.map +4 -4
  31. package/dist/lib/node-esm/meta.json +1 -1
  32. package/dist/lib/node-esm/operations/index.mjs +15 -0
  33. package/dist/lib/node-esm/operations/index.mjs.map +7 -0
  34. package/dist/lib/node-esm/types/index.mjs +10 -4
  35. package/dist/types/src/AutomationPlugin.d.ts +2 -1
  36. package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
  37. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  38. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  39. package/dist/types/src/capabilities/compute-runtime.d.ts +6 -0
  40. package/dist/types/src/capabilities/compute-runtime.d.ts.map +1 -0
  41. package/dist/types/src/capabilities/index.d.ts +6 -3
  42. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  43. package/dist/types/src/capabilities/node.d.ts +3 -0
  44. package/dist/types/src/capabilities/node.d.ts.map +1 -0
  45. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  46. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  48. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  49. package/dist/types/src/cli/commands/index.d.ts +2 -0
  50. package/dist/types/src/cli/commands/index.d.ts.map +1 -0
  51. package/dist/types/src/cli/commands/trigger/create/index.d.ts +25 -0
  52. package/dist/types/src/cli/commands/trigger/create/index.d.ts.map +1 -0
  53. package/dist/types/src/cli/commands/trigger/create/queue.d.ts +13 -0
  54. package/dist/types/src/cli/commands/trigger/create/queue.d.ts.map +1 -0
  55. package/dist/types/src/cli/commands/trigger/create/subscription.d.ts +15 -0
  56. package/dist/types/src/cli/commands/trigger/create/subscription.d.ts.map +1 -0
  57. package/dist/types/src/cli/commands/trigger/create/timer.d.ts +13 -0
  58. package/dist/types/src/cli/commands/trigger/create/timer.d.ts.map +1 -0
  59. package/dist/types/src/cli/commands/trigger/index.d.ts +57 -0
  60. package/dist/types/src/cli/commands/trigger/index.d.ts.map +1 -0
  61. package/dist/types/src/cli/commands/trigger/list.d.ts +7 -0
  62. package/dist/types/src/cli/commands/trigger/list.d.ts.map +1 -0
  63. package/dist/types/src/cli/commands/trigger/options.d.ts +11 -0
  64. package/dist/types/src/cli/commands/trigger/options.d.ts.map +1 -0
  65. package/dist/types/src/cli/commands/trigger/remove.d.ts +8 -0
  66. package/dist/types/src/cli/commands/trigger/remove.d.ts.map +1 -0
  67. package/dist/types/src/cli/commands/trigger/update/index.d.ts +28 -0
  68. package/dist/types/src/cli/commands/trigger/update/index.d.ts.map +1 -0
  69. package/dist/types/src/cli/commands/trigger/update/queue.d.ts +12 -0
  70. package/dist/types/src/cli/commands/trigger/update/queue.d.ts.map +1 -0
  71. package/dist/types/src/cli/commands/trigger/update/subscription.d.ts +13 -0
  72. package/dist/types/src/cli/commands/trigger/update/subscription.d.ts.map +1 -0
  73. package/dist/types/src/cli/commands/trigger/update/timer.d.ts +11 -0
  74. package/dist/types/src/cli/commands/trigger/update/timer.d.ts.map +1 -0
  75. package/dist/types/src/cli/commands/trigger/util.d.ts +46 -0
  76. package/dist/types/src/cli/commands/trigger/util.d.ts.map +1 -0
  77. package/dist/types/src/cli/index.d.ts +2 -0
  78. package/dist/types/src/cli/index.d.ts.map +1 -0
  79. package/dist/types/src/cli/plugin.d.ts +3 -0
  80. package/dist/types/src/cli/plugin.d.ts.map +1 -0
  81. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +3 -3
  82. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
  83. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts +47 -4
  84. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
  85. package/dist/types/src/components/FunctionsPanel/FunctionsPanel.d.ts.map +1 -1
  86. package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts +8 -0
  87. package/dist/types/src/components/FunctionsRegistry/FunctionsRegistry.d.ts.map +1 -0
  88. package/dist/types/src/components/FunctionsRegistry/index.d.ts +4 -0
  89. package/dist/types/src/components/FunctionsRegistry/index.d.ts.map +1 -0
  90. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts +12 -9
  91. package/dist/types/src/components/TriggerEditor/FunctionInputEditor.d.ts.map +1 -1
  92. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts +6 -3
  93. package/dist/types/src/components/TriggerEditor/SpecSelector.d.ts.map +1 -1
  94. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +11 -8
  95. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  96. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts +116 -4
  97. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  98. package/dist/types/src/components/index.d.ts +2 -3
  99. package/dist/types/src/components/index.d.ts.map +1 -1
  100. package/dist/types/src/containers/AutomationSettings/AutomationSettings.d.ts +4 -0
  101. package/dist/types/src/containers/AutomationSettings/AutomationSettings.d.ts.map +1 -0
  102. package/dist/types/src/containers/AutomationSettings/index.d.ts +3 -0
  103. package/dist/types/src/containers/AutomationSettings/index.d.ts.map +1 -0
  104. package/dist/types/src/{components → containers/FunctionsContainer}/FunctionsContainer.d.ts +0 -1
  105. package/dist/types/src/containers/FunctionsContainer/FunctionsContainer.d.ts.map +1 -0
  106. package/dist/types/src/containers/FunctionsContainer/index.d.ts +3 -0
  107. package/dist/types/src/containers/FunctionsContainer/index.d.ts.map +1 -0
  108. package/dist/types/src/containers/TriggerSettings/TriggerSettings.d.ts +6 -0
  109. package/dist/types/src/containers/TriggerSettings/TriggerSettings.d.ts.map +1 -0
  110. package/dist/types/src/containers/TriggerSettings/index.d.ts +3 -0
  111. package/dist/types/src/containers/TriggerSettings/index.d.ts.map +1 -0
  112. package/dist/types/src/containers/index.d.ts +5 -0
  113. package/dist/types/src/containers/index.d.ts.map +1 -0
  114. package/dist/types/src/hooks/index.d.ts +5 -0
  115. package/dist/types/src/hooks/index.d.ts.map +1 -0
  116. package/dist/types/src/hooks/useComputeRuntime.d.ts +7 -0
  117. package/dist/types/src/hooks/useComputeRuntime.d.ts.map +1 -0
  118. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts +13 -0
  119. package/dist/types/src/hooks/useComputeRuntimeCallback.d.ts.map +1 -0
  120. package/dist/types/src/hooks/useComputeRuntimeService.d.ts +5 -0
  121. package/dist/types/src/hooks/useComputeRuntimeService.d.ts.map +1 -0
  122. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +12 -0
  123. package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -0
  124. package/dist/types/src/index.d.ts +2 -3
  125. package/dist/types/src/index.d.ts.map +1 -1
  126. package/dist/types/src/meta.d.ts +2 -3
  127. package/dist/types/src/meta.d.ts.map +1 -1
  128. package/dist/types/src/operations/create-trigger-from-template.d.ts +5 -0
  129. package/dist/types/src/operations/create-trigger-from-template.d.ts.map +1 -0
  130. package/dist/types/src/operations/definitions.d.ts +19 -0
  131. package/dist/types/src/operations/definitions.d.ts.map +1 -0
  132. package/dist/types/src/operations/index.d.ts +4 -0
  133. package/dist/types/src/operations/index.d.ts.map +1 -0
  134. package/dist/types/src/testing/test-functions.d.ts +204 -3
  135. package/dist/types/src/testing/test-functions.d.ts.map +1 -1
  136. package/dist/types/src/translations.d.ts +29 -25
  137. package/dist/types/src/translations.d.ts.map +1 -1
  138. package/dist/types/src/types/capabilities.d.ts +24 -0
  139. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  140. package/dist/types/src/types/events.d.ts +5 -0
  141. package/dist/types/src/types/events.d.ts.map +1 -0
  142. package/dist/types/src/types/index.d.ts +2 -0
  143. package/dist/types/src/types/index.d.ts.map +1 -1
  144. package/dist/types/src/types/schema.d.ts +12 -4
  145. package/dist/types/src/types/schema.d.ts.map +1 -1
  146. package/dist/types/tsconfig.tsbuildinfo +1 -1
  147. package/package.json +105 -41
  148. package/src/AutomationPlugin.tsx +22 -33
  149. package/src/capabilities/app-graph-builder.ts +55 -79
  150. package/src/capabilities/compute-runtime.ts +180 -0
  151. package/src/capabilities/index.ts +9 -4
  152. package/src/capabilities/node.ts +7 -0
  153. package/src/capabilities/operation-handler.ts +16 -0
  154. package/src/capabilities/react-surface.tsx +46 -45
  155. package/src/cli/commands/index.ts +5 -0
  156. package/src/cli/commands/trigger/create/index.ts +14 -0
  157. package/src/cli/commands/trigger/create/queue.ts +90 -0
  158. package/src/cli/commands/trigger/create/subscription.ts +129 -0
  159. package/src/cli/commands/trigger/create/timer.ts +94 -0
  160. package/src/cli/commands/trigger/index.ts +16 -0
  161. package/src/cli/commands/trigger/list.ts +68 -0
  162. package/src/cli/commands/trigger/options.ts +59 -0
  163. package/src/cli/commands/trigger/remove.ts +45 -0
  164. package/src/cli/commands/trigger/update/index.ts +14 -0
  165. package/src/cli/commands/trigger/update/queue.ts +195 -0
  166. package/src/cli/commands/trigger/update/subscription.ts +279 -0
  167. package/src/cli/commands/trigger/update/timer.ts +194 -0
  168. package/src/cli/commands/trigger/util.ts +409 -0
  169. package/src/cli/index.ts +5 -0
  170. package/src/cli/plugin.ts +24 -0
  171. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +18 -17
  172. package/src/components/AutomationPanel/AutomationPanel.tsx +240 -90
  173. package/src/components/FunctionsPanel/FunctionsPanel.tsx +52 -32
  174. package/src/components/FunctionsRegistry/FunctionsRegistry.tsx +114 -0
  175. package/src/components/FunctionsRegistry/index.ts +8 -0
  176. package/src/components/TriggerEditor/FunctionInputEditor.tsx +42 -35
  177. package/src/components/TriggerEditor/SpecSelector.tsx +39 -22
  178. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +123 -34
  179. package/src/components/TriggerEditor/TriggerEditor.tsx +110 -48
  180. package/src/components/index.ts +1 -2
  181. package/src/containers/AutomationSettings/AutomationSettings.tsx +27 -0
  182. package/src/containers/AutomationSettings/index.ts +7 -0
  183. package/src/containers/FunctionsContainer/FunctionsContainer.tsx +33 -0
  184. package/src/containers/FunctionsContainer/index.ts +7 -0
  185. package/src/containers/TriggerSettings/TriggerSettings.tsx +26 -0
  186. package/src/containers/TriggerSettings/index.ts +6 -0
  187. package/src/containers/index.ts +9 -0
  188. package/src/hooks/index.ts +9 -0
  189. package/src/hooks/useComputeRuntime.ts +15 -0
  190. package/src/hooks/useComputeRuntimeCallback.ts +67 -0
  191. package/src/hooks/useComputeRuntimeService.ts +22 -0
  192. package/src/hooks/useTriggerRuntimeControls.ts +67 -0
  193. package/src/index.ts +2 -4
  194. package/src/meta.ts +8 -7
  195. package/src/operations/create-trigger-from-template.ts +74 -0
  196. package/src/operations/definitions.ts +27 -0
  197. package/src/operations/index.ts +9 -0
  198. package/src/testing/test-functions.ts +10 -7
  199. package/src/translations.ts +37 -29
  200. package/src/types/capabilities.ts +54 -0
  201. package/src/types/events.ts +11 -0
  202. package/src/types/index.ts +2 -0
  203. package/src/types/schema.ts +6 -6
  204. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs +0 -38
  205. package/dist/lib/browser/AutomationContainer-VZNV2ZQF.mjs.map +0 -7
  206. package/dist/lib/browser/AutomationPanel-ZWA6GOFY.mjs +0 -11
  207. package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs +0 -39
  208. package/dist/lib/browser/FunctionsContainer-IOB333TX.mjs.map +0 -7
  209. package/dist/lib/browser/FunctionsPanel-56ZKRVM5.mjs +0 -10
  210. package/dist/lib/browser/app-graph-builder-ZTAUTFI4.mjs +0 -80
  211. package/dist/lib/browser/app-graph-builder-ZTAUTFI4.mjs.map +0 -7
  212. package/dist/lib/browser/chunk-ECSTS2UI.mjs +0 -14
  213. package/dist/lib/browser/chunk-ECSTS2UI.mjs.map +0 -7
  214. package/dist/lib/browser/chunk-ERTIGJYE.mjs +0 -147
  215. package/dist/lib/browser/chunk-ERTIGJYE.mjs.map +0 -7
  216. package/dist/lib/browser/chunk-FSJZXTS2.mjs +0 -230
  217. package/dist/lib/browser/chunk-FSJZXTS2.mjs.map +0 -7
  218. package/dist/lib/browser/chunk-GW5U2DGT.mjs +0 -15
  219. package/dist/lib/browser/chunk-GW5U2DGT.mjs.map +0 -7
  220. package/dist/lib/browser/chunk-HN7OHFCB.mjs +0 -38
  221. package/dist/lib/browser/chunk-HN7OHFCB.mjs.map +0 -7
  222. package/dist/lib/browser/chunk-LYJVTIVD.mjs +0 -94
  223. package/dist/lib/browser/chunk-LYJVTIVD.mjs.map +0 -7
  224. package/dist/lib/browser/intent-resolver-U3ZAQEFW.mjs +0 -76
  225. package/dist/lib/browser/intent-resolver-U3ZAQEFW.mjs.map +0 -7
  226. package/dist/lib/browser/react-surface-4DFSM7OX.mjs +0 -64
  227. package/dist/lib/browser/react-surface-4DFSM7OX.mjs.map +0 -7
  228. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs +0 -39
  229. package/dist/lib/node-esm/AutomationContainer-WMIH3F4V.mjs.map +0 -7
  230. package/dist/lib/node-esm/AutomationPanel-YYUMSK2W.mjs +0 -12
  231. package/dist/lib/node-esm/AutomationPanel-YYUMSK2W.mjs.map +0 -7
  232. package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs +0 -40
  233. package/dist/lib/node-esm/FunctionsContainer-DJWB6WFH.mjs.map +0 -7
  234. package/dist/lib/node-esm/FunctionsPanel-KGIOZSPZ.mjs +0 -11
  235. package/dist/lib/node-esm/FunctionsPanel-KGIOZSPZ.mjs.map +0 -7
  236. package/dist/lib/node-esm/app-graph-builder-3FP63ZSG.mjs +0 -81
  237. package/dist/lib/node-esm/app-graph-builder-3FP63ZSG.mjs.map +0 -7
  238. package/dist/lib/node-esm/chunk-AZH66CED.mjs +0 -231
  239. package/dist/lib/node-esm/chunk-AZH66CED.mjs.map +0 -7
  240. package/dist/lib/node-esm/chunk-HIMYPGHF.mjs +0 -148
  241. package/dist/lib/node-esm/chunk-HIMYPGHF.mjs.map +0 -7
  242. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs +0 -17
  243. package/dist/lib/node-esm/chunk-NK5N3QKD.mjs.map +0 -7
  244. package/dist/lib/node-esm/chunk-OEZNHUL2.mjs +0 -39
  245. package/dist/lib/node-esm/chunk-OEZNHUL2.mjs.map +0 -7
  246. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs +0 -16
  247. package/dist/lib/node-esm/chunk-SGZPTJ47.mjs.map +0 -7
  248. package/dist/lib/node-esm/chunk-ZGPUV5VS.mjs +0 -95
  249. package/dist/lib/node-esm/chunk-ZGPUV5VS.mjs.map +0 -7
  250. package/dist/lib/node-esm/intent-resolver-3QWXEBPX.mjs +0 -77
  251. package/dist/lib/node-esm/intent-resolver-3QWXEBPX.mjs.map +0 -7
  252. package/dist/lib/node-esm/react-surface-3PNW7NDW.mjs +0 -65
  253. package/dist/lib/node-esm/react-surface-3PNW7NDW.mjs.map +0 -7
  254. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  255. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  256. package/dist/types/src/components/AutomationContainer.d.ts +0 -5
  257. package/dist/types/src/components/AutomationContainer.d.ts.map +0 -1
  258. package/dist/types/src/components/FunctionsContainer.d.ts.map +0 -1
  259. package/src/capabilities/intent-resolver.ts +0 -72
  260. package/src/components/AutomationContainer.tsx +0 -30
  261. package/src/components/FunctionsContainer.tsx +0 -31
  262. /package/dist/lib/browser/{AutomationPanel-ZWA6GOFY.mjs.map → chunk-J5LGTIGS.mjs.map} +0 -0
  263. /package/dist/lib/{browser/FunctionsPanel-56ZKRVM5.mjs.map → node-esm/chunk-HSLMI22Q.mjs.map} +0 -0
@@ -2,60 +2,73 @@
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, { useCallback, useMemo, useState } from 'react';
10
+
11
+ import { Context } from '@dxos/context';
12
+ import { Filter, Obj, Tag } from '@dxos/echo';
13
+ import { Script, Trigger } from '@dxos/functions';
14
+ import { Operation } from '@dxos/operation';
15
+ import { KEY_QUEUE_CURSOR } from '@dxos/functions-runtime';
16
+ import { FunctionsServiceClient } from '@dxos/functions-runtime/edge';
17
+ import { useTypeOptions } from '@dxos/app-toolkit/ui';
17
18
  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';
20
- import { ControlItem, controlItemClasses } from '@dxos/react-ui-form';
19
+ import { type Space, useObject, useQuery } from '@dxos/react-client/echo';
20
+ import { Clipboard, IconButton, type IconButtonProps, Input, Separator, useTranslation } from '@dxos/react-ui';
21
+ import { Settings } from '@dxos/react-ui-form';
21
22
  import { List } from '@dxos/react-ui-list';
22
- import { ghostHover, mx } from '@dxos/react-ui-theme';
23
+ import { Pipeline } from '@dxos/types';
24
+ import { ghostHover, mx } from '@dxos/ui-theme';
25
+ import { isNonNullable } from '@dxos/util';
23
26
 
24
- import { AUTOMATION_PLUGIN } from '../../meta';
27
+ import { meta } from '#meta';
25
28
  import { TriggerEditor, type TriggerEditorProps } from '../TriggerEditor';
26
29
 
27
- const grid = 'grid grid-cols-[40px_1fr_32px] min-bs-[2.5rem]';
30
+ const grid = 'grid grid-cols-[40px_1fr_32px_32px] min-h-[2.5rem]';
28
31
 
29
32
  export type AutomationPanelProps = {
30
33
  space: Space;
31
- object?: Obj.Any;
32
- initialTrigger?: FunctionTriggerType;
34
+ object?: Obj.Unknown;
35
+ initialTrigger?: Trigger.Trigger;
33
36
  onDone?: () => void;
34
37
  };
35
38
 
36
39
  // TODO(burdon): Factor out common layout with ViewEditor.
37
40
  export const AutomationPanel = ({ space, object, initialTrigger, onDone }: AutomationPanelProps) => {
38
- const { t } = useTranslation(AUTOMATION_PLUGIN);
41
+ const { t } = useTranslation(meta.id);
39
42
  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
+ const functionsServiceClient = useMemo(() => FunctionsServiceClient.fromClient(client), [client]);
44
+ const functions = useQuery(space.db, Filter.type(Operation.PersistentOperation));
45
+ const triggers = useQuery(space.db, Filter.type(Trigger.Trigger));
46
+ const filteredTriggers = useMemo(() => {
47
+ return object ? triggers.filter(triggerMatch(object)) : triggers;
48
+ }, [object, triggers]);
49
+ const tags = useQuery(space.db, Filter.type(Tag.Tag));
50
+ const types = useTypeOptions({
51
+ space,
52
+ annotation: {
53
+ location: ['database', 'runtime'],
54
+ kind: ['user'],
55
+ },
56
+ });
43
57
 
44
- const [trigger, setTrigger] = useState<FunctionTriggerType | undefined>(initialTrigger);
45
- const [selected, setSelected] = useState<FunctionTrigger>();
58
+ const [trigger, setTrigger] = useState<Trigger.Trigger | undefined>(initialTrigger);
59
+ const [selected, setSelected] = useState<Trigger.Trigger>();
46
60
 
47
- const handleSelect = (trigger: FunctionTrigger) => {
48
- const { id: _, ...values } = trigger;
49
- setTrigger(values);
61
+ const handleSelect = (trigger: Trigger.Trigger) => {
62
+ setTrigger(trigger);
50
63
  setSelected(trigger);
51
64
  };
52
65
 
53
66
  const handleAdd = () => {
54
- setTrigger(Obj.make(FunctionTriggerSchema, {}));
67
+ setTrigger(Trigger.make({}));
55
68
  setSelected(undefined);
56
69
  };
57
70
 
58
- const handleDelete = (trigger: FunctionTrigger) => {
71
+ const handleDelete = (trigger: Trigger.Trigger) => {
59
72
  space.db.remove(trigger);
60
73
  setTrigger(undefined);
61
74
  setSelected(undefined);
@@ -63,9 +76,11 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
63
76
 
64
77
  const handleSave: TriggerEditorProps['onSave'] = (trigger) => {
65
78
  if (selected) {
66
- Object.assign(selected, trigger);
79
+ Obj.change(selected, (mutable) => {
80
+ Object.assign(mutable, trigger);
81
+ });
67
82
  } else {
68
- space.db.add(Obj.make(FunctionTrigger, trigger));
83
+ space.db.add(Trigger.make(trigger));
69
84
  }
70
85
 
71
86
  setTrigger(undefined);
@@ -78,94 +93,229 @@ export const AutomationPanel = ({ space, object, initialTrigger, onDone }: Autom
78
93
  onDone?.();
79
94
  };
80
95
 
96
+ const handleForceRunTrigger = async (trigger: Trigger.Trigger) => {
97
+ await functionsServiceClient.forceRunCronTrigger(Context.default(), space.id, trigger.id);
98
+ };
99
+
100
+ const handleResetCursor = async (trigger: Trigger.Trigger) => {
101
+ Obj.change(trigger, (obj) => {
102
+ Obj.deleteKeys(obj, KEY_QUEUE_CURSOR);
103
+ });
104
+ await space.db.flush({ indexes: true });
105
+ };
106
+
81
107
  if (trigger) {
82
108
  return (
83
- <ControlItem title={t('trigger editor title')}>
84
- <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />
85
- </ControlItem>
109
+ <Settings.Item title={t('trigger-editor.title')} description={t('trigger-editor.description')}>
110
+ <TriggerEditor
111
+ db={space.db}
112
+ trigger={trigger}
113
+ readonlySpec={Boolean(object)}
114
+ tags={tags}
115
+ types={types}
116
+ onSave={handleSave}
117
+ onCancel={handleCancel}
118
+ />
119
+ </Settings.Item>
86
120
  );
87
121
  }
88
122
 
89
123
  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 }) => (
124
+ <Settings.Panel>
125
+ {filteredTriggers.length > 0 && (
126
+ <List.Root<Trigger.Trigger>
127
+ items={filteredTriggers}
128
+ isItem={Schema.is(Trigger.Trigger)}
129
+ getId={(field) => field.id}
130
+ >
131
+ {({ items: filteredTriggers }) => (
94
132
  <div role='list' className='flex flex-col w-full'>
95
- {triggers?.map((trigger) => {
96
- const copyAction = getCopyAction(client, trigger);
97
- return (
98
- <List.Item<FunctionTrigger>
99
- key={trigger.id}
100
- item={trigger}
101
- classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
102
- >
103
- <Input.Root>
104
- <Input.Switch
105
- checked={trigger.enabled}
106
- onCheckedChange={(checked) => (trigger.enabled = checked)}
107
- />
108
- </Input.Root>
109
-
110
- <div className={'flex'}>
111
- <List.ItemTitle
112
- classNames='px-1 cursor-pointer w-0 shrink truncate'
113
- onClick={() => handleSelect(trigger)}
114
- >
115
- {getFunctionName(scripts, functions, trigger) ?? '∅'}
116
- </List.ItemTitle>
117
-
118
- {/* TODO: a better way to expose copy action */}
119
- {copyAction && (
120
- <Clipboard.IconButton
121
- label={t(copyAction.translationKey)}
122
- value={copyAction.contentProvider()}
123
- />
124
- )}
125
- </div>
126
-
127
- <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
128
- </List.Item>
129
- );
130
- })}
133
+ {filteredTriggers?.map((trigger) => (
134
+ <TriggerListItem
135
+ key={trigger.id}
136
+ trigger={trigger}
137
+ functions={functions}
138
+ onSelect={handleSelect}
139
+ onDelete={handleDelete}
140
+ onResetCursor={handleResetCursor}
141
+ onForceRun={handleForceRunTrigger}
142
+ />
143
+ ))}
131
144
  </div>
132
145
  )}
133
146
  </List.Root>
134
147
  )}
135
- {triggers.length > 0 && <Separator classNames='mlb-4' />}
136
- <IconButton icon='ph--plus--regular' label={t('new trigger label')} onClick={handleAdd} />
137
- </div>
148
+
149
+ {filteredTriggers.length > 0 && <Separator classNames='my-4' />}
150
+ <IconButton icon='ph--plus--regular' label={t('new-trigger.label')} onClick={handleAdd} />
151
+ </Settings.Panel>
152
+ );
153
+ };
154
+
155
+ const TriggerListItem = ({
156
+ trigger,
157
+ functions,
158
+ onSelect,
159
+ onDelete,
160
+ onResetCursor,
161
+ onForceRun,
162
+ }: {
163
+ trigger: Trigger.Trigger;
164
+ functions: Operation.PersistentOperation[];
165
+ onSelect?: (trigger: Trigger.Trigger) => void;
166
+ onDelete?: (trigger: Trigger.Trigger) => void;
167
+ onResetCursor?: (trigger: Trigger.Trigger) => void;
168
+ onForceRun?: (trigger: Trigger.Trigger) => void;
169
+ }) => {
170
+ const client = useClient();
171
+ const copyAction = getCopyAction(client, trigger);
172
+ const { t } = useTranslation(meta.id);
173
+ const cursor = Obj.getKeys(trigger, KEY_QUEUE_CURSOR).at(0)?.id;
174
+ const [snapshot, updateTrigger] = useObject(trigger);
175
+
176
+ const enabled = snapshot.enabled ?? false;
177
+ const onEnabledChange = (checked: boolean) => {
178
+ updateTrigger((trigger) => {
179
+ trigger.enabled = checked;
180
+ });
181
+ };
182
+
183
+ const handleSelect = useCallback(() => {
184
+ onSelect?.(trigger);
185
+ }, [onSelect, trigger]);
186
+
187
+ const handleDelete = useCallback(() => {
188
+ onDelete?.(trigger);
189
+ }, [onDelete, trigger]);
190
+
191
+ const handleResetCursor = useCallback(() => {
192
+ onResetCursor?.(trigger);
193
+ }, [onResetCursor, trigger]);
194
+
195
+ const handleForceRun = useCallback(() => {
196
+ onForceRun?.(trigger);
197
+ }, [onForceRun, trigger]);
198
+
199
+ const actionProps = useMemo<IconButtonProps | undefined>(() => {
200
+ if (trigger.spec?.kind === 'timer' && onForceRun) {
201
+ return {
202
+ disabled: !enabled || trigger.spec?.kind !== 'timer',
203
+ icon: 'ph--play--regular',
204
+ label: 'Force run',
205
+ onClick: handleForceRun,
206
+ };
207
+ }
208
+
209
+ if (trigger.spec?.kind === 'queue' && onResetCursor) {
210
+ return {
211
+ disabled: !cursor,
212
+ icon: 'ph--arrow-clockwise--regular',
213
+ label: 'Reset cursor',
214
+ onClick: handleResetCursor,
215
+ };
216
+ }
217
+ }, [enabled, trigger.spec?.kind, handleForceRun]);
218
+
219
+ return (
220
+ <List.Item<Obj.Snapshot<Trigger.Trigger>>
221
+ key={trigger.id}
222
+ item={snapshot}
223
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
224
+ >
225
+ <Input.Root>
226
+ <Input.Switch checked={enabled} onCheckedChange={onEnabledChange} />
227
+ </Input.Root>
228
+
229
+ <div className={'flex'}>
230
+ <List.ItemTitle classNames='px-1 cursor-pointer w-0 shrink truncate' onClick={handleSelect}>
231
+ {getFunctionName(functions, trigger) ?? '∅'}
232
+ {cursor && <div className='text-xs text-description truncate ml-4'>Position: {cursor}</div>}
233
+ </List.ItemTitle>
234
+
235
+ {copyAction && (
236
+ <Clipboard.IconButton label={t(copyAction.translationKey)} value={copyAction.contentProvider()} />
237
+ )}
238
+ </div>
239
+
240
+ {actionProps ? <List.ItemIconButton {...actionProps} autoHide={false} /> : <div />}
241
+
242
+ {onDelete && <List.ItemDeleteButton onClick={handleDelete} />}
243
+ </List.Item>
138
244
  );
139
245
  };
140
246
 
141
- const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
142
- if (trigger?.spec?.kind === TriggerKind.Email) {
143
- return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
247
+ const getCopyAction = (client: Client, trigger: Trigger.Trigger | undefined) => {
248
+ if (trigger?.spec?.kind === 'email') {
249
+ return {
250
+ translationKey: 'trigger copy email' as const,
251
+ contentProvider: () => `${Obj.getDatabase(trigger)!.spaceId}@dxos.network`,
252
+ };
144
253
  }
145
254
 
146
- if (trigger?.spec?.kind === TriggerKind.Webhook) {
147
- return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
255
+ if (trigger?.spec?.kind === 'webhook') {
256
+ return {
257
+ translationKey: 'trigger copy url' as const,
258
+ contentProvider: () => getWebhookUrl(client, trigger!),
259
+ };
148
260
  }
149
261
 
150
262
  return undefined;
151
263
  };
152
264
 
153
- const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
154
- const spaceId = getSpace(trigger)!.id;
265
+ const getWebhookUrl = (client: Client, trigger: Trigger.Trigger) => {
266
+ const spaceId = Obj.getDatabase(trigger)!.spaceId;
155
267
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
156
268
  const isSecure = edgeUrl.protocol.startsWith('https') || edgeUrl.protocol.startsWith('wss');
157
269
  edgeUrl.protocol = isSecure ? 'https' : 'http';
158
270
  return new URL(`/webhook/${spaceId}:${trigger.id}`, edgeUrl).toString();
159
271
  };
160
272
 
161
- const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
273
+ const getFunctionName = (functions: Operation.PersistentOperation[], trigger: Trigger.Trigger) => {
162
274
  // TODO(wittjosiah): Truncation should be done in the UI.
163
275
  // Warning that the List component is currently a can of worms.
164
276
  const shortId = trigger.function && `${trigger.function.dxn.toString().slice(0, 16)}…`;
165
277
  const functionObject = functions.find((fn) => fn === trigger.function?.target);
166
- if (!functionObject) {
167
- return shortId;
278
+ return functionObject?.name ?? shortId;
279
+ };
280
+
281
+ const scriptMatch = (script: Script.Script) => (trigger: Trigger.Trigger) => {
282
+ const fn = trigger.function?.target;
283
+ if (!Obj.instanceOf(Operation.PersistentOperation, fn)) {
284
+ return false;
168
285
  }
169
286
 
170
- return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
287
+ return fn.source?.target === script;
171
288
  };
289
+
290
+ const projectMatch = (project: Pipeline.Pipeline) => {
291
+ const viewQueries = EFn.pipe(
292
+ project.columns,
293
+ Array.map((column) => column.view.target),
294
+ Array.filter(isNonNullable),
295
+ Array.map((view) => Obj.getSnapshot(view).query.ast),
296
+ Array.map((ast) => JSON.stringify(ast)),
297
+ );
298
+
299
+ return (trigger: Trigger.Trigger) => {
300
+ const spec = Obj.getSnapshot(trigger).spec;
301
+ if (spec?.kind !== 'subscription') {
302
+ return false;
303
+ }
304
+
305
+ // TODO(wittjosiah): Implement better way of comparing query ASTs.
306
+ return viewQueries.some((query) => JSON.stringify(spec.query) === query);
307
+ };
308
+ };
309
+
310
+ const triggerMatch = Match.type<Obj.Unknown>().pipe(
311
+ Match.withReturnType<(trigger: Trigger.Trigger) => boolean>(),
312
+ Match.when(
313
+ (obj) => Obj.instanceOf(Script.Script, obj),
314
+ (obj) => scriptMatch(obj),
315
+ ),
316
+ Match.when(
317
+ (obj) => Obj.instanceOf(Pipeline.Pipeline, obj),
318
+ (obj) => projectMatch(obj),
319
+ ),
320
+ Match.orElse((_obj) => () => true),
321
+ );
@@ -2,30 +2,31 @@
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';
12
- import { controlItemClasses } from '@dxos/react-ui-form';
8
+ import { useOperationInvoker } from '@dxos/app-framework/ui';
9
+ import { LayoutOperation, getObjectPathFromObject } from '@dxos/app-toolkit';
10
+ import { Script } from '@dxos/functions';
11
+ import { Operation } from '@dxos/operation';
12
+ import { SpaceOperation } from '@dxos/plugin-space/operations';
13
+ import { Filter, type Space, useQuery } from '@dxos/react-client/echo';
14
+ import { IconButton, useTranslation } from '@dxos/react-ui';
15
+ import { Settings } from '@dxos/react-ui-form';
13
16
  import { List } from '@dxos/react-ui-list';
14
- import { ghostHover, mx } from '@dxos/react-ui-theme';
17
+ import { ghostHover, mx } from '@dxos/ui-theme';
15
18
 
16
- import { AUTOMATION_PLUGIN } from '../../meta';
17
-
18
- const grid = 'grid grid-cols-[1fr_auto] min-bs-[2.5rem]';
19
+ import { meta } from '#meta';
19
20
 
20
21
  export type FunctionsPanelProps = {
21
22
  space: Space;
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));
28
- const { dispatchPromise: dispatch } = useIntentDispatcher();
26
+ const { t } = useTranslation(meta.id);
27
+ const functions = useQuery(space.db, Filter.type(Operation.PersistentOperation));
28
+ const scripts = useQuery(space.db, Filter.type(Script.Script));
29
+ const { invokePromise } = useOperationInvoker();
29
30
 
30
31
  const functionToScriptMap = useMemo(
31
32
  () =>
@@ -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: Operation.PersistentOperation) => {
50
51
  const script = functionToScriptMap[func.id];
51
52
  return script?.name;
52
53
  },
@@ -54,44 +55,63 @@ export const FunctionsPanel = ({ space }: FunctionsPanelProps) => {
54
55
  );
55
56
 
56
57
  const handleGoToScript = useCallback(
57
- (func: FunctionType) => {
58
+ (func: Operation.PersistentOperation) => {
58
59
  const script = functionToScriptMap[func.id];
59
60
  if (script) {
60
- void dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [fullyQualifiedId(script)] }));
61
+ void invokePromise(LayoutOperation.Open, { subject: [getObjectPathFromObject(script)] });
61
62
  }
62
63
  },
63
- [functionToScriptMap, dispatch],
64
+ [functionToScriptMap, invokePromise],
65
+ );
66
+
67
+ const handleDelete = useCallback(
68
+ (func: Operation.PersistentOperation) => invokePromise(SpaceOperation.RemoveObjects, { objects: [func] }),
69
+ [invokePromise],
64
70
  );
65
71
 
66
72
  return (
67
- <div role='none' className={mx(controlItemClasses)}>
73
+ <Settings.Panel>
68
74
  {functions.length > 0 && (
69
- <List.Root<FunctionType> items={functions} isItem={Schema.is(FunctionType)} getId={(func) => func.id}>
75
+ <List.Root<Operation.PersistentOperation>
76
+ items={functions}
77
+ isItem={Schema.is(Operation.PersistentOperation)}
78
+ getId={(func) => func.id}
79
+ >
70
80
  {({ items }) => (
71
81
  <div role='list' className='flex flex-col w-full'>
72
82
  {items?.map((func) => (
73
- <List.Item<FunctionType>
83
+ <List.Item<Operation.PersistentOperation>
74
84
  key={func.id}
75
85
  item={func}
76
- classNames={mx(grid, ghostHover, 'items-center', 'pli-2', 'min-bs-[3rem]')}
86
+ classNames={mx(
87
+ 'grid grid-cols-[1fr_min-content_auto] min-h-[2.5rem] min-h-[3rem] px-2 items-center',
88
+ ghostHover,
89
+ )}
77
90
  >
78
91
  <div className='flex flex-col truncate'>
79
92
  <List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
80
- {getScriptName(func) && (
81
- <div className='text-xs text-description truncate'>{getScriptName(func)}</div>
82
- )}
93
+ {getScriptName(func) && <p className='text-xs text-description truncate'>{getScriptName(func)}</p>}
83
94
  </div>
84
- {functionToScriptMap[func.id] && (
85
- <Button onClick={() => handleGoToScript(func)}>{t('go to function source button label')}</Button>
86
- )}
95
+ {(functionToScriptMap[func.id] && (
96
+ <IconButton
97
+ icon='ph--arrow-square-out--regular'
98
+ iconOnly
99
+ label={t('show-source-button.label')}
100
+ onClick={() => handleGoToScript(func)}
101
+ />
102
+ )) || <div />}
103
+ <IconButton
104
+ icon='ph--trash--regular'
105
+ iconOnly
106
+ label={t('delete-function-button.label')}
107
+ onClick={() => handleDelete(func)}
108
+ />
87
109
  </List.Item>
88
110
  ))}
89
111
  </div>
90
112
  )}
91
113
  </List.Root>
92
114
  )}
93
-
94
- {functions.length === 0 && <div className='text-center plb-4 text-gray-500'>{t('no functions found')}</div>}
95
- </div>
115
+ </Settings.Panel>
96
116
  );
97
117
  };
@@ -0,0 +1,114 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import { useState } from 'react';
7
+ import React, { useCallback } from 'react';
8
+
9
+ import { Context } from '@dxos/context';
10
+ import * as OperationModule from '@dxos/operation';
11
+ import { getDeployedFunctions } from '@dxos/functions-runtime/edge';
12
+ import { useClient } from '@dxos/react-client';
13
+ import { Filter, Query, type Space, useQuery } from '@dxos/react-client/echo';
14
+ import { IconButton, useAsyncEffect, useTranslation } from '@dxos/react-ui';
15
+ import { Settings } from '@dxos/react-ui-form';
16
+ import { List } from '@dxos/react-ui-list';
17
+ import { ghostHover, mx } from '@dxos/ui-theme';
18
+
19
+ import { meta } from '#meta';
20
+
21
+ const grid = 'grid grid-cols-[1fr_1fr_auto] min-h-[2.5rem]';
22
+
23
+ type FunctionsRegistryProps = {
24
+ space: Space;
25
+ };
26
+
27
+ export const FunctionsRegistry = ({ space }: FunctionsRegistryProps) => {
28
+ const client = useClient();
29
+ const [loading, setLoading] = useState(true);
30
+ const [functions, setFunctions] = useState<OperationModule.Operation.PersistentOperation[]>([]);
31
+ const { t } = useTranslation(meta.id);
32
+
33
+ const dbFunctions = useQuery(space.db, Filter.type(OperationModule.Operation.PersistentOperation));
34
+
35
+ const state = (func: OperationModule.Operation.PersistentOperation) => {
36
+ const dbFunction = dbFunctions.find((f) => f.key === func.key);
37
+ if (!dbFunction) {
38
+ return 'import';
39
+ }
40
+ if (dbFunction.version === func.version && dbFunction.updated === func.updated) {
41
+ return 'none';
42
+ }
43
+ return 'update';
44
+ };
45
+
46
+ useAsyncEffect(async () => {
47
+ setLoading(true);
48
+ const functions = await getDeployedFunctions(Context.default(), client, true);
49
+ setFunctions(functions);
50
+ setLoading(false);
51
+ }, []);
52
+
53
+ const hanleImportOrUpdate = useCallback(
54
+ async (func: OperationModule.Operation.PersistentOperation) => {
55
+ const functions = await space.db
56
+ .query(Query.type(OperationModule.Operation.PersistentOperation, { key: func.key }))
57
+ .run();
58
+ const [existingFunc] = functions;
59
+ if (!existingFunc) {
60
+ space.db.add(func);
61
+ return;
62
+ }
63
+ OperationModule.Operation.setFrom(existingFunc, func);
64
+ },
65
+ [space],
66
+ );
67
+
68
+ return (
69
+ <Settings.Panel>
70
+ {functions.length > 0 && (
71
+ <List.Root<OperationModule.Operation.PersistentOperation>
72
+ items={functions}
73
+ isItem={Schema.is(OperationModule.Operation.PersistentOperation)}
74
+ getId={(func) => func.id}
75
+ >
76
+ {({ items }) => (
77
+ <div role='list' className='flex flex-col w-full'>
78
+ {items?.map((func) => (
79
+ <List.Item<OperationModule.Operation.PersistentOperation>
80
+ key={func.id}
81
+ item={func}
82
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2', 'min-h-[3rem]')}
83
+ >
84
+ <div className='flex flex-col truncate'>
85
+ <List.ItemTitle classNames='truncate'>{func.name}</List.ItemTitle>
86
+ <div className='text-xs text-description truncate'>{func.key}</div>
87
+ </div>
88
+ <div className='flex flex-col truncate'>
89
+ <div className='text-xs text-description truncate'>{func.version}</div>
90
+ <div className='text-xs text-description truncate'>
91
+ {func.updated ? `Uploaded ${new Date(func.updated).toLocaleString()}` : ''}
92
+ </div>
93
+ </div>
94
+
95
+ <IconButton
96
+ iconOnly
97
+ icon={state(func) === 'update' ? 'ph--arrows-clockwise--regular' : 'ph--download--regular'}
98
+ label={
99
+ state(func) === 'update' ? t('update-function-button.label') : t('import-function-button.label')
100
+ }
101
+ disabled={state(func) === 'none'}
102
+ onClick={() => hanleImportOrUpdate(func)}
103
+ />
104
+ </List.Item>
105
+ ))}
106
+ </div>
107
+ )}
108
+ </List.Root>
109
+ )}
110
+
111
+ {loading && <div className='text-center py-4 text-gray-500'>{t('loading-functions.message')}</div>}
112
+ </Settings.Panel>
113
+ );
114
+ };
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { FunctionsRegistry } from './FunctionsRegistry';
6
+
7
+ export { FunctionsRegistry };
8
+ export default FunctionsRegistry;