@dxos/plugin-routine 0.9.1-staging.ee54ba693a → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLUGIN.mdl +18 -13
- package/dist/lib/RoutinePlugin.mjs +2 -0
- package/dist/lib/RoutinePlugin.node.mjs +894 -0
- package/dist/lib/RoutinePlugin.node.mjs.map +1 -0
- package/dist/lib/RoutinePlugin.workerd.mjs +20 -0
- package/dist/lib/RoutinePlugin.workerd.mjs.map +1 -0
- package/dist/lib/capabilities/node.mjs +12 -0
- package/dist/lib/capabilities/node.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +14 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-RoutineArticle.mjs +79 -0
- package/dist/lib/chunk-RoutineArticle.mjs.map +1 -0
- package/dist/lib/chunk-RoutineCompanion.mjs +192 -0
- package/dist/lib/chunk-RoutineCompanion.mjs.map +1 -0
- package/dist/lib/chunk-RoutinePlugin.mjs +38 -0
- package/dist/lib/chunk-RoutinePlugin.mjs.map +1 -0
- package/dist/lib/chunk-RoutineSettings.mjs +50 -0
- package/dist/lib/chunk-RoutineSettings.mjs.map +1 -0
- package/dist/lib/chunk-RoutineTraceCompanion.mjs +208 -0
- package/dist/lib/chunk-RoutineTraceCompanion.mjs.map +1 -0
- package/dist/lib/chunk-SkillArticle.mjs +28 -0
- package/dist/lib/chunk-SkillArticle.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +71 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-create-object.mjs +23 -0
- package/dist/lib/chunk-create-object.mjs.map +1 -0
- package/dist/lib/chunk-create-routine.mjs +34 -0
- package/dist/lib/chunk-create-routine.mjs.map +1 -0
- package/dist/lib/chunk-create-trigger-from-template.mjs +48 -0
- package/dist/lib/chunk-create-trigger-from-template.mjs.map +1 -0
- package/dist/lib/chunk-layer-specs.mjs +179 -0
- package/dist/lib/chunk-layer-specs.mjs.map +1 -0
- package/dist/lib/{neutral/chunk-HLO4LYXT.mjs → chunk-meta.mjs} +19 -27
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-paths.mjs +11 -0
- package/dist/lib/chunk-paths.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +64 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-registry-sync.mjs +104 -0
- package/dist/lib/chunk-registry-sync.mjs.map +1 -0
- package/dist/lib/chunk-run-routine.mjs +57 -0
- package/dist/lib/chunk-run-routine.mjs.map +1 -0
- package/dist/lib/chunk-templates.mjs +37 -0
- package/dist/lib/chunk-templates.mjs.map +1 -0
- package/dist/lib/chunk-trigger-runtime-controller.mjs +73 -0
- package/dist/lib/chunk-trigger-runtime-controller.mjs.map +1 -0
- package/dist/lib/chunk-types.mjs +149 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/chunk-util.mjs +142 -0
- package/dist/lib/chunk-util.mjs.map +1 -0
- package/dist/lib/components.mjs +1645 -0
- package/dist/lib/components.mjs.map +1 -0
- package/dist/lib/containers.mjs +11 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/hooks.mjs +37 -0
- package/dist/lib/hooks.mjs.map +1 -0
- package/dist/lib/index.mjs +5 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +7 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/testing.mjs +2 -0
- package/dist/lib/translations.mjs +110 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/types/src/RoutinePlugin.d.ts.map +1 -1
- package/dist/types/src/RoutinePlugin.node.d.ts.map +1 -1
- package/dist/types/src/RoutinePlugin.workerd.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/create-object.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts +0 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/layer-specs.d.ts +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/registry-sync.d.ts +1 -1
- package/dist/types/src/capabilities/templates.d.ts +1 -1
- package/dist/types/src/capabilities/trigger-runtime-controller.d.ts +1 -1
- package/dist/types/src/commands/trigger/create/{queue.d.ts → feed.d.ts} +2 -2
- package/dist/types/src/commands/trigger/create/feed.d.ts.map +1 -0
- package/dist/types/src/commands/trigger/update/{queue.d.ts → feed.d.ts} +2 -2
- package/dist/types/src/commands/trigger/update/feed.d.ts.map +1 -0
- package/dist/types/src/commands/trigger/util.d.ts +1 -1
- package/dist/types/src/commands/trigger/util.d.ts.map +1 -1
- package/dist/types/src/components/InstructionsEditor/InstructionsEditor.d.ts +7 -5
- package/dist/types/src/components/InstructionsEditor/InstructionsEditor.d.ts.map +1 -1
- package/dist/types/src/components/InstructionsEditor/InstructionsEditor.stories.d.ts +3 -3
- package/dist/types/src/components/InstructionsEditor/InstructionsEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/RoutineCard/RoutineCard.d.ts +9 -0
- package/dist/types/src/components/RoutineCard/RoutineCard.d.ts.map +1 -0
- package/dist/types/src/components/RoutineCard/RoutineCard.stories.d.ts +21 -0
- package/dist/types/src/components/RoutineCard/RoutineCard.stories.d.ts.map +1 -0
- package/dist/types/src/components/RoutineCard/index.d.ts +2 -0
- package/dist/types/src/components/RoutineCard/index.d.ts.map +1 -0
- package/dist/types/src/components/RoutineForm/RoutineForm.d.ts +7 -2
- package/dist/types/src/components/RoutineForm/RoutineForm.d.ts.map +1 -1
- package/dist/types/src/components/Schedule/Schedule.d.ts +2 -26
- package/dist/types/src/components/Schedule/Schedule.d.ts.map +1 -1
- package/dist/types/src/components/Schedule/days.d.ts +24 -0
- package/dist/types/src/components/Schedule/days.d.ts.map +1 -0
- package/dist/types/src/components/Schedule/describe-schedule.d.ts +3 -0
- package/dist/types/src/components/Schedule/describe-schedule.d.ts.map +1 -0
- package/dist/types/src/components/Schedule/index.d.ts +2 -0
- package/dist/types/src/components/Schedule/index.d.ts.map +1 -1
- package/dist/types/src/components/TemplateEditor/TemplateEditor.d.ts +1 -1
- package/dist/types/src/components/TemplateEditor/TemplateEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +1 -2
- package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/TriggerKindSelector.d.ts +5 -2
- package/dist/types/src/components/TriggerEditor/TriggerKindSelector.d.ts.map +1 -1
- package/dist/types/src/components/TriggerEditor/trigger-kind-icon.d.ts +5 -0
- package/dist/types/src/components/TriggerEditor/trigger-kind-icon.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineArticle/RoutineArticle.d.ts +5 -10
- package/dist/types/src/containers/RoutineArticle/RoutineArticle.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts +6 -2
- package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.d.ts +4 -12
- package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineCompanion/RoutineCompanion.stories.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineSettings/RoutineSettings.d.ts +4 -8
- package/dist/types/src/containers/RoutineSettings/RoutineSettings.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.d.ts +5 -3
- package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.stories.d.ts.map +1 -1
- package/dist/types/src/containers/RoutineTraceCompanion/useRoutineRuns.d.ts +1 -1
- package/dist/types/src/containers/RoutineTraceCompanion/useRoutineRuns.d.ts.map +1 -1
- package/dist/types/src/containers/SkillArticle/SkillArticle.d.ts +4 -1
- package/dist/types/src/containers/SkillArticle/SkillArticle.d.ts.map +1 -1
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts +1 -1
- package/dist/types/src/hooks/useTriggerRuntimeControls.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/operations/run-routine.d.ts.map +1 -1
- package/dist/types/src/operations/run-routine.test.d.ts +2 -0
- package/dist/types/src/operations/run-routine.test.d.ts.map +1 -0
- package/dist/types/src/paths.d.ts.map +1 -1
- package/dist/types/src/templates/index.d.ts +1 -1
- package/dist/types/src/templates/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-functions.d.ts +30 -6
- package/dist/types/src/testing/test-functions.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/RoutineCapabilities.d.ts +9 -3
- package/dist/types/src/types/RoutineCapabilities.d.ts.map +1 -1
- package/dist/types/src/types/RoutineOperation.d.ts +2 -3
- package/dist/types/src/types/RoutineOperation.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +0 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/routines-for-object.d.ts +6 -3
- package/dist/types/src/util/routines-for-object.d.ts.map +1 -1
- package/dist/types/src/util/wire.d.ts +21 -0
- package/dist/types/src/util/wire.d.ts.map +1 -0
- package/dist/types/src/util/wire.test.d.ts +2 -0
- package/dist/types/src/util/wire.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dx.config.ts +3 -2
- package/package.json +73 -71
- package/src/RoutinePlugin.node.ts +1 -2
- package/src/RoutinePlugin.tsx +2 -4
- package/src/RoutinePlugin.workerd.ts +1 -2
- package/src/capabilities/app-graph-builder.ts +9 -7
- package/src/capabilities/create-object.ts +2 -2
- package/src/capabilities/index.ts +0 -1
- package/src/capabilities/layer-specs.ts +78 -12
- package/src/capabilities/{react-surface.tsx → react-surface.ts} +18 -22
- package/src/capabilities/trigger-runtime-controller.test.ts +12 -18
- package/src/capabilities/trigger-runtime-controller.ts +22 -25
- package/src/commands/trigger/create/{queue.ts → feed.ts} +1 -1
- package/src/commands/trigger/create/index.ts +2 -2
- package/src/commands/trigger/update/{queue.ts → feed.ts} +1 -1
- package/src/commands/trigger/update/index.ts +2 -2
- package/src/commands/trigger/util.ts +1 -2
- package/src/components/InstructionsEditor/InstructionsEditor.stories.tsx +7 -6
- package/src/components/InstructionsEditor/InstructionsEditor.tsx +31 -19
- package/src/components/RoutineCard/RoutineCard.stories.tsx +95 -0
- package/src/components/RoutineCard/RoutineCard.tsx +60 -0
- package/src/components/{MasterDetail → RoutineCard}/index.ts +1 -1
- package/src/components/RoutineForm/RoutineForm.tsx +60 -52
- package/src/components/Schedule/Schedule.stories.tsx +1 -1
- package/src/components/Schedule/Schedule.tsx +4 -73
- package/src/components/Schedule/days.ts +18 -0
- package/src/components/Schedule/describe-schedule.ts +64 -0
- package/src/components/Schedule/index.ts +2 -0
- package/src/components/TemplateEditor/TemplateEditor.stories.tsx +1 -1
- package/src/components/TemplateEditor/TemplateForm.tsx +1 -1
- package/src/components/TriggerEditor/TriggerEditor.stories.tsx +3 -3
- package/src/components/TriggerEditor/TriggerEditor.tsx +79 -66
- package/src/components/TriggerEditor/TriggerKindSelector.tsx +4 -30
- package/src/components/TriggerEditor/trigger-kind-icon.ts +38 -0
- package/src/components/index.ts +1 -1
- package/src/containers/RoutineArticle/RoutineArticle.stories.tsx +78 -19
- package/src/containers/RoutineArticle/RoutineArticle.tsx +8 -4
- package/src/containers/RoutineCompanion/RoutineCompanion.stories.tsx +5 -4
- package/src/containers/RoutineCompanion/RoutineCompanion.tsx +11 -7
- package/src/containers/RoutineSettings/RoutineSettings.tsx +22 -33
- package/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.stories.tsx +6 -5
- package/src/containers/RoutineTraceCompanion/RoutineTraceCompanion.tsx +3 -1
- package/src/containers/RoutineTraceCompanion/useRoutineRuns.ts +3 -5
- package/src/containers/SkillArticle/SkillArticle.tsx +2 -0
- package/src/hooks/useTriggerRuntimeControls.ts +2 -2
- package/src/index.ts +1 -0
- package/src/operations/create-routine.ts +1 -1
- package/src/operations/create-trigger-from-template.ts +2 -2
- package/src/operations/run-routine.test.ts +150 -0
- package/src/operations/run-routine.ts +18 -2
- package/src/paths.ts +5 -5
- package/src/templates/index.ts +5 -3
- package/src/translations.ts +5 -6
- package/src/types/RoutineCapabilities.ts +10 -4
- package/src/types/RoutineOperation.ts +7 -6
- package/src/types/index.ts +0 -1
- package/src/util/index.ts +1 -0
- package/src/util/routines-for-object.test.ts +37 -5
- package/src/util/routines-for-object.ts +33 -8
- package/src/util/wire.test.ts +77 -0
- package/src/util/wire.ts +76 -0
- package/dist/lib/neutral/RoutineArticle-UNTMV4CC.mjs +0 -95
- package/dist/lib/neutral/RoutineArticle-UNTMV4CC.mjs.map +0 -7
- package/dist/lib/neutral/RoutineCompanion-ZZ6WMY7I.mjs +0 -249
- package/dist/lib/neutral/RoutineCompanion-ZZ6WMY7I.mjs.map +0 -7
- package/dist/lib/neutral/RoutinePlugin.mjs +0 -10
- package/dist/lib/neutral/RoutinePlugin.mjs.map +0 -7
- package/dist/lib/neutral/RoutinePlugin.node.mjs +0 -1133
- package/dist/lib/neutral/RoutinePlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/RoutinePlugin.workerd.mjs +0 -36
- package/dist/lib/neutral/RoutinePlugin.workerd.mjs.map +0 -7
- package/dist/lib/neutral/RoutineSettings-ZJOM4F3S.mjs +0 -48
- package/dist/lib/neutral/RoutineSettings-ZJOM4F3S.mjs.map +0 -7
- package/dist/lib/neutral/RoutineTraceCompanion-5U6R7LJE.mjs +0 -236
- package/dist/lib/neutral/RoutineTraceCompanion-5U6R7LJE.mjs.map +0 -7
- package/dist/lib/neutral/SkillArticle-WWDNKW2Z.mjs +0 -27
- package/dist/lib/neutral/SkillArticle-WWDNKW2Z.mjs.map +0 -7
- package/dist/lib/neutral/app-graph-builder-FALWLZTI.mjs +0 -90
- package/dist/lib/neutral/app-graph-builder-FALWLZTI.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/index.mjs +0 -25
- package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/node.mjs +0 -19
- package/dist/lib/neutral/capabilities/node.mjs.map +0 -7
- package/dist/lib/neutral/chunk-674LDIUJ.mjs +0 -9
- package/dist/lib/neutral/chunk-674LDIUJ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-HLO4LYXT.mjs.map +0 -7
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-P3TDQKOO.mjs +0 -8
- package/dist/lib/neutral/chunk-P3TDQKOO.mjs.map +0 -7
- package/dist/lib/neutral/chunk-R7J2MU2M.mjs +0 -263
- package/dist/lib/neutral/chunk-R7J2MU2M.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WLNL77C7.mjs +0 -14
- package/dist/lib/neutral/chunk-WLNL77C7.mjs.map +0 -7
- package/dist/lib/neutral/chunk-YGWSAKLD.mjs +0 -65
- package/dist/lib/neutral/chunk-YGWSAKLD.mjs.map +0 -7
- package/dist/lib/neutral/components/index.mjs +0 -1921
- package/dist/lib/neutral/components/index.mjs.map +0 -7
- package/dist/lib/neutral/containers/index.mjs +0 -17
- package/dist/lib/neutral/containers/index.mjs.map +0 -7
- package/dist/lib/neutral/create-object-V23KVDMJ.mjs +0 -25
- package/dist/lib/neutral/create-object-V23KVDMJ.mjs.map +0 -7
- package/dist/lib/neutral/create-routine-6QBY6TWF.mjs +0 -38
- package/dist/lib/neutral/create-routine-6QBY6TWF.mjs.map +0 -7
- package/dist/lib/neutral/create-trigger-from-template-U3KNS77V.mjs +0 -70
- package/dist/lib/neutral/create-trigger-from-template-U3KNS77V.mjs.map +0 -7
- package/dist/lib/neutral/hooks/index.mjs +0 -47
- package/dist/lib/neutral/hooks/index.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -27
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/layer-specs-2TWJU5HI.mjs +0 -135
- package/dist/lib/neutral/layer-specs-2TWJU5HI.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/meta.mjs +0 -8
- package/dist/lib/neutral/meta.mjs.map +0 -7
- package/dist/lib/neutral/navigation-resolver-KQIWUH6F.mjs +0 -18
- package/dist/lib/neutral/navigation-resolver-KQIWUH6F.mjs.map +0 -7
- package/dist/lib/neutral/operation-handler-QKLJSDCQ.mjs +0 -13
- package/dist/lib/neutral/operation-handler-QKLJSDCQ.mjs.map +0 -7
- package/dist/lib/neutral/operations/index.mjs +0 -8
- package/dist/lib/neutral/operations/index.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -16
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-GMO7I7YE.mjs +0 -67
- package/dist/lib/neutral/react-surface-GMO7I7YE.mjs.map +0 -7
- package/dist/lib/neutral/registry-sync-DYPOC7ZG.mjs +0 -86
- package/dist/lib/neutral/registry-sync-DYPOC7ZG.mjs.map +0 -7
- package/dist/lib/neutral/run-routine-AEWO6SYD.mjs +0 -37
- package/dist/lib/neutral/run-routine-AEWO6SYD.mjs.map +0 -7
- package/dist/lib/neutral/templates-IIUCEBAX.mjs +0 -49
- package/dist/lib/neutral/templates-IIUCEBAX.mjs.map +0 -7
- package/dist/lib/neutral/testing.mjs +0 -8
- package/dist/lib/neutral/testing.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -115
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/dist/lib/neutral/trigger-runtime-controller-WIDDFGQT.mjs +0 -77
- package/dist/lib/neutral/trigger-runtime-controller-WIDDFGQT.mjs.map +0 -7
- package/dist/lib/neutral/types/index.mjs +0 -17
- package/dist/lib/neutral/types/index.mjs.map +0 -7
- package/dist/types/src/capabilities/navigation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/navigation-resolver.d.ts.map +0 -1
- package/dist/types/src/commands/trigger/create/queue.d.ts.map +0 -1
- package/dist/types/src/commands/trigger/update/queue.d.ts.map +0 -1
- package/dist/types/src/components/MasterDetail/MasterDetail.d.ts +0 -51
- package/dist/types/src/components/MasterDetail/MasterDetail.d.ts.map +0 -1
- package/dist/types/src/components/MasterDetail/MasterDetail.stories.d.ts +0 -16
- package/dist/types/src/components/MasterDetail/MasterDetail.stories.d.ts.map +0 -1
- package/dist/types/src/components/MasterDetail/index.d.ts +0 -2
- package/dist/types/src/components/MasterDetail/index.d.ts.map +0 -1
- package/dist/types/src/types/Routine.d.ts +0 -55
- package/dist/types/src/types/Routine.d.ts.map +0 -1
- package/dist/types/src/types/Routine.test.d.ts +0 -2
- package/dist/types/src/types/Routine.test.d.ts.map +0 -1
- package/src/capabilities/navigation-resolver.ts +0 -21
- package/src/components/MasterDetail/MasterDetail.stories.tsx +0 -100
- package/src/components/MasterDetail/MasterDetail.tsx +0 -175
- package/src/types/Routine.test.ts +0 -51
- package/src/types/Routine.ts +0 -144
|
@@ -0,0 +1,1645 @@
|
|
|
1
|
+
import { n as wireTriggers } from "./chunk-util.mjs";
|
|
2
|
+
import { isNonNullable } from "@dxos/util";
|
|
3
|
+
import { Instructions, Operation, Routine, Skill, Trigger } from "@dxos/compute";
|
|
4
|
+
import { meta } from "#meta";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
import { DXN, Entity, Feed, Filter, Obj, Query, Ref, Scope, Type } from "@dxos/echo";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
import { ParentLabelAnnotation } from "@dxos/schema";
|
|
9
|
+
import { createContext, forwardRef, useCallback, useContext, useMemo, useState } from "react";
|
|
10
|
+
import { useObject, useQuery } from "@dxos/echo-react";
|
|
11
|
+
import { Card, Icon, IconButton, Input, ToggleGroup, ToggleGroupItem, composable, composableProps, useThemeContext, useTranslation } from "@dxos/react-ui";
|
|
12
|
+
import { Form, RefField, SelectField } from "@dxos/react-ui-form";
|
|
13
|
+
import { HiddenAnnotation } from "@dxos/echo/Annotation";
|
|
14
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import { mx } from "@dxos/ui-theme";
|
|
16
|
+
import cronstrue from "cronstrue";
|
|
17
|
+
import { invariant } from "@dxos/invariant";
|
|
18
|
+
import { Listbox } from "@dxos/react-ui-list";
|
|
19
|
+
import { useCapabilities } from "@dxos/app-framework/ui";
|
|
20
|
+
import { SearchList, useSearchListResults } from "@dxos/react-ui-search";
|
|
21
|
+
import { RoutineCapabilities } from "#types";
|
|
22
|
+
import { defaultHighlightStyle, syntaxHighlighting, syntaxTree } from "@codemirror/language";
|
|
23
|
+
import { composeRefs } from "@radix-ui/react-compose-refs";
|
|
24
|
+
import { Doc } from "@dxos/echo-doc";
|
|
25
|
+
import { useTextEditor } from "@dxos/react-ui-editor";
|
|
26
|
+
import { createBasicExtensions, createDataExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown } from "@dxos/ui-editor";
|
|
27
|
+
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
28
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
29
|
+
import { Decoration, ViewPlugin, WidgetType, keymap } from "@codemirror/view";
|
|
30
|
+
import { Domino, mx as mx$1 } from "@dxos/ui";
|
|
31
|
+
//#region src/components/InstructionsEditor/InstructionsEditor.tsx
|
|
32
|
+
var INSTRUCTIONS_SCHEMA = Type.getSchema(Instructions.Instructions);
|
|
33
|
+
var DEFAULT_FIELDS = ["text", "skills"];
|
|
34
|
+
/**
|
|
35
|
+
* Sub-form: edits the owned Instructions in place — by default its `text` (Markdown) and `skills`,
|
|
36
|
+
* both fields of the Instructions schema. A bare Form.Root + FieldSet (no Viewport) keeps these
|
|
37
|
+
* fields left-aligned with the sibling general/action forms; only the rendered fields are written
|
|
38
|
+
* back so a partial editor never clobbers the rest of the object.
|
|
39
|
+
*/
|
|
40
|
+
var InstructionsEditor = ({ db: dbProp, instructions, readonly, fields = DEFAULT_FIELDS }) => {
|
|
41
|
+
const db = dbProp ?? Obj.getDatabase(instructions);
|
|
42
|
+
const defaultValues = useMemo(() => ({ ...Obj.getSnapshot(instructions) }), [instructions]);
|
|
43
|
+
const fieldSet = useMemo(() => new Set(fields), [fields]);
|
|
44
|
+
const handleValuesChanged = useCallback((values, { isValid }) => {
|
|
45
|
+
if (!isValid) return;
|
|
46
|
+
Obj.update(instructions, (instructions) => {
|
|
47
|
+
if (fieldSet.has("text") && values.text) instructions.text = values.text;
|
|
48
|
+
if (fieldSet.has("skills")) instructions.skills = [...values.skills ?? []];
|
|
49
|
+
if (fieldSet.has("objects")) instructions.objects = [...values.objects ?? []];
|
|
50
|
+
});
|
|
51
|
+
}, [instructions, fieldSet]);
|
|
52
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
53
|
+
schema: INSTRUCTIONS_SCHEMA,
|
|
54
|
+
db,
|
|
55
|
+
readonly,
|
|
56
|
+
defaultValues,
|
|
57
|
+
getOptions: getRefOptions,
|
|
58
|
+
onValuesChanged: handleValuesChanged,
|
|
59
|
+
children: /* @__PURE__ */ jsx(Form.FieldSet, { filter: (props) => props.filter((prop) => fieldSet.has(prop.name.toString())) })
|
|
60
|
+
}, instructions.id);
|
|
61
|
+
};
|
|
62
|
+
var isSystemObject = (object) => {
|
|
63
|
+
if (!Obj.isObject(object)) return false;
|
|
64
|
+
const typeEntity = Obj.getType(object);
|
|
65
|
+
if (!typeEntity) return false;
|
|
66
|
+
return HiddenAnnotation.get(Type.getSchema(typeEntity)).pipe(Option.getOrElse(() => false));
|
|
67
|
+
};
|
|
68
|
+
var getRefOptions = (results, { getTypePlaceholder } = {}) => results.filter((entity) => !isSystemObject(entity)).map((entity) => {
|
|
69
|
+
const id = Entity.getURI(entity, { prefer: "named" });
|
|
70
|
+
const typename = Entity.getTypename(entity);
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
label: Entity.getLabel(entity) ?? (typename ? getTypePlaceholder?.(typename) : void 0) ?? id,
|
|
74
|
+
description: Obj.instanceOf(Skill.Skill, entity) ? Obj.getMeta(entity).key : typename
|
|
75
|
+
};
|
|
76
|
+
}).sort((left, right) => left.label.localeCompare(right.label));
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/components/Schedule/cron.ts
|
|
79
|
+
var DOW_MAP = {
|
|
80
|
+
sun: 0,
|
|
81
|
+
mon: 1,
|
|
82
|
+
tue: 2,
|
|
83
|
+
wed: 3,
|
|
84
|
+
thu: 4,
|
|
85
|
+
fri: 5,
|
|
86
|
+
sat: 6
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Derives a 5-field standard cron expression from a decomposed CronSpec value.
|
|
90
|
+
* Fields: minute hour day-of-month month day-of-week
|
|
91
|
+
*/
|
|
92
|
+
var toCron = (value) => {
|
|
93
|
+
switch (value.frequency) {
|
|
94
|
+
case "minutely": return `*/${value.interval} * * * *`;
|
|
95
|
+
case "hourly": return `${value.minute} */${value.interval} * * *`;
|
|
96
|
+
case "daily": return `${value.minute} ${value.hour} * * *`;
|
|
97
|
+
case "weekly": {
|
|
98
|
+
const dow = value.daysOfWeek.length > 0 ? value.daysOfWeek.map((d) => DOW_MAP[d]).join(",") : "*";
|
|
99
|
+
return `${value.minute} ${value.hour} * * ${dow}`;
|
|
100
|
+
}
|
|
101
|
+
case "monthly": {
|
|
102
|
+
const dom = value.daysOfMonth.length > 0 ? value.daysOfMonth.join(",") : "*";
|
|
103
|
+
return `${value.minute} ${value.hour} ${dom} * *`;
|
|
104
|
+
}
|
|
105
|
+
case "custom": return value.cronExpression || "* * * * *";
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var DOW_REVERSE = {
|
|
109
|
+
"0": "sun",
|
|
110
|
+
"1": "mon",
|
|
111
|
+
"2": "tue",
|
|
112
|
+
"3": "wed",
|
|
113
|
+
"4": "thu",
|
|
114
|
+
"5": "fri",
|
|
115
|
+
"6": "sat",
|
|
116
|
+
"7": "sun"
|
|
117
|
+
};
|
|
118
|
+
/** Returns a non-negative integer if the token is a valid decimal integer, otherwise undefined. */
|
|
119
|
+
var parseUInt = (token) => /^\d+$/.test(token) ? Number(token) : void 0;
|
|
120
|
+
/** Returns the parsed integer if it falls within [min, max], otherwise undefined. */
|
|
121
|
+
var parseBoundedUInt = (token, min, max) => {
|
|
122
|
+
const value = parseUInt(token);
|
|
123
|
+
return value !== void 0 && value >= min && value <= max ? value : void 0;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Parses a 5-field cron expression into a CronSpec value.
|
|
127
|
+
* Recognises common patterns (minutely, hourly, daily, weekly, monthly) and falls back to `custom`.
|
|
128
|
+
*/
|
|
129
|
+
var fromCron = (cron) => {
|
|
130
|
+
const parts = cron.trim().split(/\s+/);
|
|
131
|
+
if (parts.length !== 5) return {
|
|
132
|
+
frequency: "custom",
|
|
133
|
+
cronExpression: cron
|
|
134
|
+
};
|
|
135
|
+
const [minute, hour, dom, month, dow] = parts;
|
|
136
|
+
const minutelyMatch = minute.match(/^\*\/(\d+)$/);
|
|
137
|
+
if (minutelyMatch && hour === "*" && dom === "*" && month === "*" && dow === "*") return {
|
|
138
|
+
frequency: "minutely",
|
|
139
|
+
interval: parseInt(minutelyMatch[1])
|
|
140
|
+
};
|
|
141
|
+
const hourlyMatch = hour.match(/^\*\/(\d+)$/);
|
|
142
|
+
const minuteNum = parseBoundedUInt(minute, 0, 59);
|
|
143
|
+
if (hourlyMatch && dom === "*" && month === "*" && dow === "*" && minuteNum !== void 0) return {
|
|
144
|
+
frequency: "hourly",
|
|
145
|
+
interval: parseInt(hourlyMatch[1]),
|
|
146
|
+
minute: minuteNum
|
|
147
|
+
};
|
|
148
|
+
const hourNum = parseBoundedUInt(hour, 0, 23);
|
|
149
|
+
if (minuteNum === void 0 || hourNum === void 0) return {
|
|
150
|
+
frequency: "custom",
|
|
151
|
+
cronExpression: cron
|
|
152
|
+
};
|
|
153
|
+
if (dom === "*" && month === "*" && dow !== "*") {
|
|
154
|
+
const tokens = dow.split(",");
|
|
155
|
+
const days = tokens.map((d) => DOW_REVERSE[d]).filter((d) => d != null);
|
|
156
|
+
if (days.length === tokens.length && days.length > 0) return {
|
|
157
|
+
frequency: "weekly",
|
|
158
|
+
daysOfWeek: days,
|
|
159
|
+
hour: hourNum,
|
|
160
|
+
minute: minuteNum
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
frequency: "custom",
|
|
164
|
+
cronExpression: cron
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (dom !== "*" && month === "*" && dow === "*") {
|
|
168
|
+
const tokens = dom.split(",");
|
|
169
|
+
const days = tokens.map((d) => parseBoundedUInt(d, 1, 31)).filter((d) => d !== void 0);
|
|
170
|
+
if (days.length === tokens.length && days.length > 0) return {
|
|
171
|
+
frequency: "monthly",
|
|
172
|
+
daysOfMonth: days,
|
|
173
|
+
hour: hourNum,
|
|
174
|
+
minute: minuteNum
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
frequency: "custom",
|
|
178
|
+
cronExpression: cron
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (dom === "*" && month === "*" && dow === "*") return {
|
|
182
|
+
frequency: "daily",
|
|
183
|
+
hour: hourNum,
|
|
184
|
+
minute: minuteNum
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
frequency: "custom",
|
|
188
|
+
cronExpression: cron
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Returns a human-readable description of a cron expression, e.g. "At 09:00 on Monday".
|
|
193
|
+
* Falls back to the raw expression string on parse errors.
|
|
194
|
+
*/
|
|
195
|
+
var describeCron = (cron) => {
|
|
196
|
+
try {
|
|
197
|
+
return cronstrue.toString(cron, { throwExceptionOnParseError: true });
|
|
198
|
+
} catch {
|
|
199
|
+
return cron;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var pad$1 = (value) => String(value).padStart(2, "0");
|
|
203
|
+
var splitTime = (time) => {
|
|
204
|
+
const [hour, minute] = time.split(":").map(Number);
|
|
205
|
+
return {
|
|
206
|
+
hour: Number.isFinite(hour) ? hour : 0,
|
|
207
|
+
minute: Number.isFinite(minute) ? minute : 0
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
var joinTime = (hour, minute) => `${pad$1(hour)}:${pad$1(minute)}`;
|
|
211
|
+
/**
|
|
212
|
+
* Convert a recurring {@link ScheduleValue} to a 5-field cron expression. The one-time `once` kind has no
|
|
213
|
+
* cron representation and returns `undefined`.
|
|
214
|
+
*/
|
|
215
|
+
var scheduleToCron = (value) => {
|
|
216
|
+
switch (value.kind) {
|
|
217
|
+
case "hourly": return toCron({
|
|
218
|
+
frequency: "hourly",
|
|
219
|
+
interval: 1,
|
|
220
|
+
minute: value.minute
|
|
221
|
+
});
|
|
222
|
+
case "daily": {
|
|
223
|
+
const { hour, minute } = splitTime(value.time);
|
|
224
|
+
return toCron({
|
|
225
|
+
frequency: "daily",
|
|
226
|
+
hour,
|
|
227
|
+
minute
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
case "weekly": {
|
|
231
|
+
const { hour, minute } = splitTime(value.time);
|
|
232
|
+
return toCron({
|
|
233
|
+
frequency: "weekly",
|
|
234
|
+
daysOfWeek: value.days,
|
|
235
|
+
hour,
|
|
236
|
+
minute
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
case "monthly": {
|
|
240
|
+
const { hour, minute } = splitTime(value.time);
|
|
241
|
+
return toCron({
|
|
242
|
+
frequency: "monthly",
|
|
243
|
+
daysOfMonth: [value.day],
|
|
244
|
+
hour,
|
|
245
|
+
minute
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
case "custom": return toCron({
|
|
249
|
+
frequency: "custom",
|
|
250
|
+
cronExpression: value.cron
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var MINUTE_SECONDS = 60;
|
|
255
|
+
var HOUR_SECONDS = 3600;
|
|
256
|
+
var DAY_SECONDS = 24 * HOUR_SECONDS;
|
|
257
|
+
/** Upper bound for a Schedule minimum interval: a 5-field cron's coarsest single-field cadence is hourly. */
|
|
258
|
+
var MAX_MIN_INTERVAL_SECONDS = HOUR_SECONDS;
|
|
259
|
+
/** Parses a comma-separated list of bounded integers; returns undefined if any token is not a plain integer in range. */
|
|
260
|
+
var parseList = (field, min, max) => {
|
|
261
|
+
const tokens = field.split(",");
|
|
262
|
+
const values = tokens.map((token) => parseBoundedUInt(token, min, max)).filter((value) => value !== void 0);
|
|
263
|
+
return values.length === tokens.length && values.length > 0 ? values : void 0;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Smallest cyclic gap between values in a field whose domain wraps at `span` (60 for minutes, 24 for hours),
|
|
267
|
+
* or Infinity for a single value. The wrap is required so e.g. minutes `0,59` register their 1-unit gap
|
|
268
|
+
* (59 → 0 of the next cycle), not just the 59-unit one.
|
|
269
|
+
*/
|
|
270
|
+
var minGap = (values, span) => {
|
|
271
|
+
const sorted = [...new Set(values)].sort((lhs, rhs) => lhs - rhs);
|
|
272
|
+
if (sorted.length <= 1) return Number.POSITIVE_INFINITY;
|
|
273
|
+
let gap = Number.POSITIVE_INFINITY;
|
|
274
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
275
|
+
const next = index === sorted.length - 1 ? sorted[0] + span : sorted[index + 1];
|
|
276
|
+
gap = Math.min(gap, next - sorted[index]);
|
|
277
|
+
}
|
|
278
|
+
return gap;
|
|
279
|
+
};
|
|
280
|
+
/** Step value of a `*\/N`, `M/N`, or `A-B/N` field (the cadence between fires), or undefined if not a step form. */
|
|
281
|
+
var parseStep = (field, max) => {
|
|
282
|
+
const match = field.match(/^(?:\*|\d+(?:-\d+)?)\/(\d+)$/);
|
|
283
|
+
return match ? parseBoundedUInt(match[1], 1, max) : void 0;
|
|
284
|
+
};
|
|
285
|
+
/** Minimum seconds between fires implied by a cron's hour field (independent of minute granularity). */
|
|
286
|
+
var hourIntervalSeconds = (hour) => {
|
|
287
|
+
const step = parseStep(hour, 23);
|
|
288
|
+
if (step !== void 0) return step * HOUR_SECONDS;
|
|
289
|
+
if (hour === "*") return HOUR_SECONDS;
|
|
290
|
+
const hours = parseList(hour, 0, 23);
|
|
291
|
+
if (hours) return hours.length > 1 ? minGap(hours, 24) * HOUR_SECONDS : DAY_SECONDS;
|
|
292
|
+
return Number.POSITIVE_INFINITY;
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Best-effort minimum seconds between consecutive fires of a 5-field cron expression. Recognises the patterns
|
|
296
|
+
* a too-frequent schedule can take (`* …`, `*\/N …`, explicit minute/hour lists); returns Infinity for
|
|
297
|
+
* expressions it cannot analyse, so unrecognised crons are treated as satisfying any minimum.
|
|
298
|
+
*/
|
|
299
|
+
var cronIntervalSeconds = (cron) => {
|
|
300
|
+
const parts = cron.trim().split(/\s+/);
|
|
301
|
+
if (parts.length !== 5) return Number.POSITIVE_INFINITY;
|
|
302
|
+
const [minute, hour] = parts;
|
|
303
|
+
const minuteStep = parseStep(minute, 59);
|
|
304
|
+
if (minuteStep !== void 0) return minuteStep * MINUTE_SECONDS;
|
|
305
|
+
if (minute === "*") return MINUTE_SECONDS;
|
|
306
|
+
const minutes = parseList(minute, 0, 59);
|
|
307
|
+
if (minutes) {
|
|
308
|
+
const minuteGap = minutes.length > 1 ? minGap(minutes, 60) * MINUTE_SECONDS : Number.POSITIVE_INFINITY;
|
|
309
|
+
return Math.min(minuteGap, hourIntervalSeconds(hour));
|
|
310
|
+
}
|
|
311
|
+
return Number.POSITIVE_INFINITY;
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Rewrites a cron's minute field so it fires no more often than `minIntervalSeconds`. Used to clamp a custom
|
|
315
|
+
* expression; leaves non-5-field expressions untouched since their cadence cannot be reasoned about.
|
|
316
|
+
*/
|
|
317
|
+
var clampCronMinute = (cron, minIntervalSeconds) => {
|
|
318
|
+
const parts = cron.trim().split(/\s+/);
|
|
319
|
+
if (parts.length !== 5) return cron;
|
|
320
|
+
const stepMinutes = Math.ceil(minIntervalSeconds / MINUTE_SECONDS);
|
|
321
|
+
parts[0] = stepMinutes >= 60 ? "0" : `*/${stepMinutes}`;
|
|
322
|
+
return parts.join(" ");
|
|
323
|
+
};
|
|
324
|
+
/** Minimum seconds between fires for a recurring {@link ScheduleValue}. */
|
|
325
|
+
var scheduleIntervalSeconds = (value) => {
|
|
326
|
+
switch (value.kind) {
|
|
327
|
+
case "hourly": return HOUR_SECONDS;
|
|
328
|
+
case "daily": return DAY_SECONDS;
|
|
329
|
+
case "weekly": return DAY_SECONDS;
|
|
330
|
+
case "monthly": return 28 * DAY_SECONDS;
|
|
331
|
+
case "custom": return cronIntervalSeconds(value.cron);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Coerces a schedule so it fires no more often than `minIntervalSeconds`. Only `custom` expressions can exceed
|
|
336
|
+
* the limit (structured kinds are hourly or slower), so clamping rewrites the cron's minute field; all other
|
|
337
|
+
* values pass through unchanged.
|
|
338
|
+
*/
|
|
339
|
+
var clampSchedule = (value, minIntervalSeconds) => {
|
|
340
|
+
if (value.kind !== "custom" || cronIntervalSeconds(value.cron) >= minIntervalSeconds) return value;
|
|
341
|
+
return {
|
|
342
|
+
kind: "custom",
|
|
343
|
+
cron: clampCronMinute(value.cron, minIntervalSeconds)
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Convert a cron expression to a recurring {@link ScheduleValue}. Patterns that Schedule cannot model exactly
|
|
348
|
+
* (sub-hourly intervals, multi-day months, etc.) fall back to the `custom` kind preserving the raw expression.
|
|
349
|
+
*/
|
|
350
|
+
var cronToSchedule = (cron) => {
|
|
351
|
+
const spec = fromCron(cron);
|
|
352
|
+
switch (spec.frequency) {
|
|
353
|
+
case "hourly": return spec.interval === 1 ? {
|
|
354
|
+
kind: "hourly",
|
|
355
|
+
minute: spec.minute
|
|
356
|
+
} : {
|
|
357
|
+
kind: "custom",
|
|
358
|
+
cron
|
|
359
|
+
};
|
|
360
|
+
case "daily": return {
|
|
361
|
+
kind: "daily",
|
|
362
|
+
time: joinTime(spec.hour, spec.minute)
|
|
363
|
+
};
|
|
364
|
+
case "weekly": return {
|
|
365
|
+
kind: "weekly",
|
|
366
|
+
time: joinTime(spec.hour, spec.minute),
|
|
367
|
+
days: [...spec.daysOfWeek]
|
|
368
|
+
};
|
|
369
|
+
case "monthly": return spec.daysOfMonth.length === 1 ? {
|
|
370
|
+
kind: "monthly",
|
|
371
|
+
day: spec.daysOfMonth[0],
|
|
372
|
+
time: joinTime(spec.hour, spec.minute)
|
|
373
|
+
} : {
|
|
374
|
+
kind: "custom",
|
|
375
|
+
cron
|
|
376
|
+
};
|
|
377
|
+
default: return {
|
|
378
|
+
kind: "custom",
|
|
379
|
+
cron
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/components/Schedule/types.ts
|
|
385
|
+
var MinutelySpec = Schema.Struct({
|
|
386
|
+
frequency: Schema.Literal("minutely").annotations({ title: "Frequency" }),
|
|
387
|
+
interval: Schema.Number.pipe(Schema.int(), Schema.between(1, 59)).annotations({
|
|
388
|
+
title: "Every (minutes)",
|
|
389
|
+
description: "1–59"
|
|
390
|
+
})
|
|
391
|
+
});
|
|
392
|
+
var HourlySpec = Schema.Struct({
|
|
393
|
+
frequency: Schema.Literal("hourly").annotations({ title: "Frequency" }),
|
|
394
|
+
interval: Schema.Number.pipe(Schema.int(), Schema.between(1, 23)).annotations({
|
|
395
|
+
title: "Every (hours)",
|
|
396
|
+
description: "1–23"
|
|
397
|
+
}),
|
|
398
|
+
minute: Schema.Number.pipe(Schema.int(), Schema.between(0, 59)).annotations({
|
|
399
|
+
title: "At minute",
|
|
400
|
+
description: "0–59"
|
|
401
|
+
})
|
|
402
|
+
});
|
|
403
|
+
var DailySpec = Schema.Struct({
|
|
404
|
+
frequency: Schema.Literal("daily").annotations({ title: "Frequency" }),
|
|
405
|
+
hour: Schema.Number.pipe(Schema.int(), Schema.between(0, 23)).annotations({
|
|
406
|
+
title: "Hour",
|
|
407
|
+
description: "0–23"
|
|
408
|
+
}),
|
|
409
|
+
minute: Schema.Number.pipe(Schema.int(), Schema.between(0, 59)).annotations({
|
|
410
|
+
title: "Minute",
|
|
411
|
+
description: "0–59"
|
|
412
|
+
})
|
|
413
|
+
});
|
|
414
|
+
var DayOfWeek = Schema.Literal("sun", "mon", "tue", "wed", "thu", "fri", "sat");
|
|
415
|
+
var WeeklySpec = Schema.Struct({
|
|
416
|
+
frequency: Schema.Literal("weekly").annotations({ title: "Frequency" }),
|
|
417
|
+
daysOfWeek: Schema.Array(DayOfWeek.annotations({ title: "Day" })).annotations({ title: "Days of week" }),
|
|
418
|
+
hour: Schema.Number.pipe(Schema.int(), Schema.between(0, 23)).annotations({
|
|
419
|
+
title: "Hour",
|
|
420
|
+
description: "0–23"
|
|
421
|
+
}),
|
|
422
|
+
minute: Schema.Number.pipe(Schema.int(), Schema.between(0, 59)).annotations({
|
|
423
|
+
title: "Minute",
|
|
424
|
+
description: "0–59"
|
|
425
|
+
})
|
|
426
|
+
});
|
|
427
|
+
var MonthlySpec = Schema.Struct({
|
|
428
|
+
frequency: Schema.Literal("monthly").annotations({ title: "Frequency" }),
|
|
429
|
+
daysOfMonth: Schema.Array(Schema.Number.pipe(Schema.int(), Schema.between(1, 31)).annotations({ title: "Day" })).annotations({ title: "Days of month" }),
|
|
430
|
+
hour: Schema.Number.pipe(Schema.int(), Schema.between(0, 23)).annotations({
|
|
431
|
+
title: "Hour",
|
|
432
|
+
description: "0–23"
|
|
433
|
+
}),
|
|
434
|
+
minute: Schema.Number.pipe(Schema.int(), Schema.between(0, 59)).annotations({
|
|
435
|
+
title: "Minute",
|
|
436
|
+
description: "0–59"
|
|
437
|
+
})
|
|
438
|
+
});
|
|
439
|
+
var CustomSpec = Schema.Struct({
|
|
440
|
+
frequency: Schema.Literal("custom").annotations({ title: "Frequency" }),
|
|
441
|
+
cronExpression: Schema.String.annotations({
|
|
442
|
+
title: "Cron expression",
|
|
443
|
+
description: "e.g. 0 9 * * MON-FRI"
|
|
444
|
+
})
|
|
445
|
+
});
|
|
446
|
+
var CronSpec = Schema.Union(MinutelySpec, HourlySpec, DailySpec, WeeklySpec, MonthlySpec, CustomSpec).annotations({ title: "" });
|
|
447
|
+
var FrequencyDefaults = {
|
|
448
|
+
minutely: {
|
|
449
|
+
frequency: "minutely",
|
|
450
|
+
interval: 15
|
|
451
|
+
},
|
|
452
|
+
hourly: {
|
|
453
|
+
frequency: "hourly",
|
|
454
|
+
interval: 1,
|
|
455
|
+
minute: 0
|
|
456
|
+
},
|
|
457
|
+
daily: {
|
|
458
|
+
frequency: "daily",
|
|
459
|
+
hour: 9,
|
|
460
|
+
minute: 0
|
|
461
|
+
},
|
|
462
|
+
weekly: {
|
|
463
|
+
frequency: "weekly",
|
|
464
|
+
daysOfWeek: ["mon"],
|
|
465
|
+
hour: 9,
|
|
466
|
+
minute: 0
|
|
467
|
+
},
|
|
468
|
+
monthly: {
|
|
469
|
+
frequency: "monthly",
|
|
470
|
+
daysOfMonth: [1],
|
|
471
|
+
hour: 9,
|
|
472
|
+
minute: 0
|
|
473
|
+
},
|
|
474
|
+
custom: {
|
|
475
|
+
frequency: "custom",
|
|
476
|
+
cronExpression: "* * * * *"
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/components/Schedule/days.ts
|
|
481
|
+
var Days = [
|
|
482
|
+
{
|
|
483
|
+
value: "mon",
|
|
484
|
+
label: "Mon"
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
value: "tue",
|
|
488
|
+
label: "Tue"
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
value: "wed",
|
|
492
|
+
label: "Wed"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
value: "thu",
|
|
496
|
+
label: "Thu"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
value: "fri",
|
|
500
|
+
label: "Fri"
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
value: "sat",
|
|
504
|
+
label: "Sat"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
value: "sun",
|
|
508
|
+
label: "Sun"
|
|
509
|
+
}
|
|
510
|
+
];
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/components/Schedule/describe-schedule.ts
|
|
513
|
+
/** Human-readable summary of the schedule, suitable for the header. */
|
|
514
|
+
var DAY_NAMES = {
|
|
515
|
+
mon: "Monday",
|
|
516
|
+
tue: "Tuesday",
|
|
517
|
+
wed: "Wednesday",
|
|
518
|
+
thu: "Thursday",
|
|
519
|
+
fri: "Friday",
|
|
520
|
+
sat: "Saturday",
|
|
521
|
+
sun: "Sunday"
|
|
522
|
+
};
|
|
523
|
+
var withZone = (text, timezone) => timezone ? `${text} ${timezone}` : text;
|
|
524
|
+
var formatTime = (time) => {
|
|
525
|
+
const [h, m] = time.split(":").map(Number);
|
|
526
|
+
if (Number.isNaN(h) || Number.isNaN(m)) return time;
|
|
527
|
+
const period = h < 12 ? "AM" : "PM";
|
|
528
|
+
return `${h % 12 === 0 ? 12 : h % 12}:${String(m).padStart(2, "0")} ${period}`;
|
|
529
|
+
};
|
|
530
|
+
var describeSchedule = (value, timezone) => {
|
|
531
|
+
switch (value.kind) {
|
|
532
|
+
case "hourly": return `Runs every hour at minute ${value.minute}`;
|
|
533
|
+
case "daily": return withZone(`Runs every day at ${formatTime(value.time)}`, timezone);
|
|
534
|
+
case "weekly": return withZone(`Runs every ${value.days.length === 0 ? "no days" : value.days.length === 1 ? DAY_NAMES[value.days[0]] : Days.filter((d) => value.days.includes(d.value)).map((d) => d.label).join(", ")} at ${formatTime(value.time)}`, timezone);
|
|
535
|
+
case "monthly": return withZone(`Runs monthly on day ${value.day} at ${formatTime(value.time)}`, timezone);
|
|
536
|
+
case "custom": return withZone(describeCron(value.cron), timezone);
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/components/Schedule/Schedule.tsx
|
|
541
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-routine/src/components/Schedule/Schedule.tsx";
|
|
542
|
+
var ScheduleKinds = [
|
|
543
|
+
"hourly",
|
|
544
|
+
"daily",
|
|
545
|
+
"weekly",
|
|
546
|
+
"monthly",
|
|
547
|
+
"custom"
|
|
548
|
+
];
|
|
549
|
+
var isScheduleKind = (value) => ScheduleKinds.includes(value);
|
|
550
|
+
var DEFAULT_TIME = "08:00";
|
|
551
|
+
/** Default minimum interval: a schedule may fire at most once every 60s (the finest 5-field cron granularity). */
|
|
552
|
+
var DEFAULT_MIN_INTERVAL_SECONDS = 60;
|
|
553
|
+
/** Clamp a requested minimum interval to the achievable range; a 5-field cron cannot fire more often than every minute or require slower than hourly. */
|
|
554
|
+
var normalizeMinInterval = (minInterval) => {
|
|
555
|
+
return Math.min(MAX_MIN_INTERVAL_SECONDS, Math.max(DEFAULT_MIN_INTERVAL_SECONDS, Math.round(Number.isFinite(minInterval) ? minInterval : DEFAULT_MIN_INTERVAL_SECONDS)));
|
|
556
|
+
};
|
|
557
|
+
var KIND_LABEL_KEYS = {
|
|
558
|
+
hourly: "schedule.kind.hourly.label",
|
|
559
|
+
daily: "schedule.kind.daily.label",
|
|
560
|
+
weekly: "schedule.kind.weekly.label",
|
|
561
|
+
monthly: "schedule.kind.monthly.label",
|
|
562
|
+
custom: "schedule.kind.custom.label"
|
|
563
|
+
};
|
|
564
|
+
var DEFAULTS = {
|
|
565
|
+
hourly: {
|
|
566
|
+
kind: "hourly",
|
|
567
|
+
minute: 0
|
|
568
|
+
},
|
|
569
|
+
daily: {
|
|
570
|
+
kind: "daily",
|
|
571
|
+
time: DEFAULT_TIME
|
|
572
|
+
},
|
|
573
|
+
weekly: {
|
|
574
|
+
kind: "weekly",
|
|
575
|
+
time: DEFAULT_TIME,
|
|
576
|
+
days: ["mon"]
|
|
577
|
+
},
|
|
578
|
+
monthly: {
|
|
579
|
+
kind: "monthly",
|
|
580
|
+
day: 1,
|
|
581
|
+
time: DEFAULT_TIME
|
|
582
|
+
},
|
|
583
|
+
custom: {
|
|
584
|
+
kind: "custom",
|
|
585
|
+
cron: "0 9 * * 1"
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
var pad = (value) => String(value).padStart(2, "0");
|
|
589
|
+
/**
|
|
590
|
+
* Switch to a different kind, preserving the current schedule's timing by routing it through its cron
|
|
591
|
+
* representation. The cron is the canonical carrier: each target kind reuses whatever fields it supports
|
|
592
|
+
* (time-of-day, weekdays, day-of-month), falling back to the kind's default for fields the cron doesn't hold.
|
|
593
|
+
*/
|
|
594
|
+
var switchKind = (current, kind) => {
|
|
595
|
+
const cron = scheduleToCron(current);
|
|
596
|
+
const spec = cron ? fromCron(cron) : void 0;
|
|
597
|
+
const [defaultHour, defaultMinute] = DEFAULT_TIME.split(":").map(Number);
|
|
598
|
+
const minute = spec && "minute" in spec ? spec.minute : defaultMinute;
|
|
599
|
+
const time = `${pad(spec && "hour" in spec ? spec.hour : defaultHour)}:${pad(minute)}`;
|
|
600
|
+
switch (kind) {
|
|
601
|
+
case "hourly": return {
|
|
602
|
+
kind: "hourly",
|
|
603
|
+
minute
|
|
604
|
+
};
|
|
605
|
+
case "daily": return {
|
|
606
|
+
kind: "daily",
|
|
607
|
+
time
|
|
608
|
+
};
|
|
609
|
+
case "weekly": return {
|
|
610
|
+
kind: "weekly",
|
|
611
|
+
time,
|
|
612
|
+
days: spec?.frequency === "weekly" ? [...spec.daysOfWeek] : DEFAULTS.weekly.days
|
|
613
|
+
};
|
|
614
|
+
case "monthly": return {
|
|
615
|
+
kind: "monthly",
|
|
616
|
+
time,
|
|
617
|
+
day: spec?.frequency === "monthly" ? spec.daysOfMonth[0] : DEFAULTS.monthly.day
|
|
618
|
+
};
|
|
619
|
+
case "custom": return {
|
|
620
|
+
kind: "custom",
|
|
621
|
+
cron: cron ?? DEFAULTS.custom.cron
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var ScheduleContext = createContext(null);
|
|
626
|
+
var useScheduleContext = (consumer) => {
|
|
627
|
+
const context = useContext(ScheduleContext);
|
|
628
|
+
invariant(context, `${consumer} must be used within Schedule.Root`, {
|
|
629
|
+
"~LogMeta": "~LogMeta",
|
|
630
|
+
F: __dxlog_file,
|
|
631
|
+
L: 148,
|
|
632
|
+
S: void 0,
|
|
633
|
+
A: ["context", "`${consumer} must be used within Schedule.Root`"]
|
|
634
|
+
});
|
|
635
|
+
return context;
|
|
636
|
+
};
|
|
637
|
+
/**
|
|
638
|
+
* Schedule picker root: provides the shared {@link Column} grid (all rows align to the same gutters)
|
|
639
|
+
* and the schedule state context. Controllable via `value`/`onValueChange`, or self-managing via
|
|
640
|
+
* `defaultValue`. Compose with `Schedule.Header`, `Schedule.Kind`, `Schedule.Body`, `Schedule.Description`.
|
|
641
|
+
*/
|
|
642
|
+
var ScheduleRoot = composable(({ children, value: valueProp, defaultValue, onValueChange, kinds = ScheduleKinds, minInterval: minIntervalProp = DEFAULT_MIN_INTERVAL_SECONDS, timezone, ...props }, forwardedRef) => {
|
|
643
|
+
const minInterval = normalizeMinInterval(minIntervalProp);
|
|
644
|
+
const [value, setValue] = useState(() => clampSchedule(valueProp ?? defaultValue ?? DEFAULTS.weekly, minInterval));
|
|
645
|
+
const handleValueChange = useCallback((next) => {
|
|
646
|
+
const clamped = clampSchedule(next, minInterval);
|
|
647
|
+
setValue(clamped);
|
|
648
|
+
onValueChange?.(clamped);
|
|
649
|
+
}, [onValueChange, minInterval]);
|
|
650
|
+
const allowedKinds = kinds.filter((kind) => kind === "custom" || scheduleIntervalSeconds(DEFAULTS[kind]) >= minInterval);
|
|
651
|
+
return /* @__PURE__ */ jsx(ScheduleContext.Provider, {
|
|
652
|
+
value: {
|
|
653
|
+
value: clampSchedule(valueProp ?? value, minInterval),
|
|
654
|
+
onValueChange: handleValueChange,
|
|
655
|
+
kinds: allowedKinds,
|
|
656
|
+
minInterval,
|
|
657
|
+
timezone
|
|
658
|
+
},
|
|
659
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
660
|
+
...composableProps(props, { classNames: "flex flex-col gap-y-3" }),
|
|
661
|
+
ref: forwardedRef,
|
|
662
|
+
children
|
|
663
|
+
})
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
ScheduleRoot.displayName = "Schedule.Root";
|
|
667
|
+
/**
|
|
668
|
+
* Summary row laid out on the shared grid: the icon in the leading gutter (column 1), the schedule
|
|
669
|
+
* description (or `children`) in the center column, and an optional close button in the trailing gutter
|
|
670
|
+
* (column 3).
|
|
671
|
+
*/
|
|
672
|
+
var ScheduleHeader = forwardRef(({ classNames }, forwardedRef) => {
|
|
673
|
+
const { t } = useTranslation(meta.profile.key);
|
|
674
|
+
const { value, timezone } = useScheduleContext("Schedule.Header");
|
|
675
|
+
return /* @__PURE__ */ jsx("p", {
|
|
676
|
+
className: mx("grow truncate text-description", classNames),
|
|
677
|
+
children: describeSchedule(value, timezone)
|
|
678
|
+
});
|
|
679
|
+
});
|
|
680
|
+
ScheduleHeader.displayName = "Schedule.Header";
|
|
681
|
+
/** Segmented frequency tabs (Once / Hourly / Daily / Weekly / Custom). */
|
|
682
|
+
var ScheduleKindRow = forwardRef(({ classNames }, forwardedRef) => {
|
|
683
|
+
const { t } = useTranslation(meta.profile.key);
|
|
684
|
+
const { value, onValueChange, kinds } = useScheduleContext("Schedule.Kind");
|
|
685
|
+
const handleKindChange = useCallback((kind) => {
|
|
686
|
+
if (isScheduleKind(kind)) onValueChange(switchKind(value, kind));
|
|
687
|
+
}, [value, onValueChange]);
|
|
688
|
+
return /* @__PURE__ */ jsx(ToggleGroup, {
|
|
689
|
+
classNames: "overflow-x-auto scrollbar-none",
|
|
690
|
+
type: "single",
|
|
691
|
+
value: value.kind,
|
|
692
|
+
onValueChange: handleKindChange,
|
|
693
|
+
children: kinds.map((kind) => /* @__PURE__ */ jsx(ToggleGroupItem, {
|
|
694
|
+
value: kind,
|
|
695
|
+
children: t(KIND_LABEL_KEYS[kind])
|
|
696
|
+
}, kind))
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
ScheduleKindRow.displayName = "Schedule.Kind";
|
|
700
|
+
/** Per-kind editor for the active frequency. Rendered as a 3-column row so editors can use the gutters. */
|
|
701
|
+
var ScheduleBody = forwardRef(({ classNames }, forwardedRef) => {
|
|
702
|
+
const { value, onValueChange } = useScheduleContext("Schedule.Body");
|
|
703
|
+
return /* @__PURE__ */ jsx(ScheduleEditor, {
|
|
704
|
+
value,
|
|
705
|
+
onChange: onValueChange
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
ScheduleBody.displayName = "Schedule.Body";
|
|
709
|
+
var Schedule = {
|
|
710
|
+
Root: ScheduleRoot,
|
|
711
|
+
Header: ScheduleHeader,
|
|
712
|
+
Kind: ScheduleKindRow,
|
|
713
|
+
Body: ScheduleBody
|
|
714
|
+
};
|
|
715
|
+
var Field = ({ label, children, classNames }) => /* @__PURE__ */ jsxs("label", {
|
|
716
|
+
className: mx("flex items-center gap-2 shrink-0", classNames),
|
|
717
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
718
|
+
className: "text-sm",
|
|
719
|
+
children: label
|
|
720
|
+
}), children]
|
|
721
|
+
});
|
|
722
|
+
var ScheduleEditor = ({ value, onChange }) => {
|
|
723
|
+
const { t } = useTranslation(meta.profile.key);
|
|
724
|
+
switch (value.kind) {
|
|
725
|
+
case "hourly": return /* @__PURE__ */ jsx(Field, {
|
|
726
|
+
label: t("schedule.minute.label"),
|
|
727
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
728
|
+
type: "number",
|
|
729
|
+
min: 0,
|
|
730
|
+
max: 59,
|
|
731
|
+
step: 1,
|
|
732
|
+
classNames: "w-15",
|
|
733
|
+
value: String(value.minute),
|
|
734
|
+
onChange: (event) => {
|
|
735
|
+
onChange({
|
|
736
|
+
kind: "hourly",
|
|
737
|
+
minute: Math.min(59, Math.max(0, Math.round(Number(event.target.value) || 0)))
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}) })
|
|
741
|
+
});
|
|
742
|
+
case "daily": return /* @__PURE__ */ jsx(Field, {
|
|
743
|
+
label: t("schedule.at.label"),
|
|
744
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.Time, {
|
|
745
|
+
hourCycle: 12,
|
|
746
|
+
value: value.time,
|
|
747
|
+
onValueChange: (time) => onChange({
|
|
748
|
+
kind: "daily",
|
|
749
|
+
time
|
|
750
|
+
})
|
|
751
|
+
}) })
|
|
752
|
+
});
|
|
753
|
+
case "weekly": return /* @__PURE__ */ jsxs("div", {
|
|
754
|
+
className: "@container dx-inline-size-container min-w-0 flex justify-between items-center gap-2 overflow-x-auto scrollbar-none",
|
|
755
|
+
children: [/* @__PURE__ */ jsx(Field, {
|
|
756
|
+
label: t("schedule.at.label"),
|
|
757
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.Time, {
|
|
758
|
+
hourCycle: 12,
|
|
759
|
+
value: value.time,
|
|
760
|
+
onValueChange: (time) => onChange({
|
|
761
|
+
...value,
|
|
762
|
+
time
|
|
763
|
+
})
|
|
764
|
+
}) })
|
|
765
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
766
|
+
className: "flex shrink-0 items-center gap-2",
|
|
767
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
768
|
+
className: "shrink-0 text-sm",
|
|
769
|
+
children: t("schedule.on.label")
|
|
770
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
771
|
+
className: "grid w-max shrink-0 grid-cols-7 gap-x-2",
|
|
772
|
+
children: Days.map(({ value: day, label }) => {
|
|
773
|
+
const checked = value.days.includes(day);
|
|
774
|
+
return /* @__PURE__ */ jsx("div", {
|
|
775
|
+
className: "flex shrink-0 items-center gap-1",
|
|
776
|
+
children: /* @__PURE__ */ jsxs(Input.Root, { children: [
|
|
777
|
+
/* @__PURE__ */ jsx(Input.Checkbox, {
|
|
778
|
+
checked,
|
|
779
|
+
onCheckedChange: (next) => {
|
|
780
|
+
const nextDays = next ? Days.map((d) => d.value).filter((d) => d === day || value.days.includes(d)) : value.days.filter((d) => d !== day);
|
|
781
|
+
onChange({
|
|
782
|
+
...value,
|
|
783
|
+
days: nextDays.length > 0 ? nextDays : value.days
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
}),
|
|
787
|
+
/* @__PURE__ */ jsx(Input.Label, {
|
|
788
|
+
classNames: "hidden @min-[32rem]:inline-block text-xs uppercase",
|
|
789
|
+
children: label
|
|
790
|
+
}),
|
|
791
|
+
/* @__PURE__ */ jsx(Input.Label, {
|
|
792
|
+
classNames: "inline-block @min-[32rem]:hidden text-xs",
|
|
793
|
+
children: label.charAt(0)
|
|
794
|
+
})
|
|
795
|
+
] })
|
|
796
|
+
}, day);
|
|
797
|
+
})
|
|
798
|
+
})]
|
|
799
|
+
})]
|
|
800
|
+
});
|
|
801
|
+
case "monthly": return /* @__PURE__ */ jsxs("div", {
|
|
802
|
+
className: "flex items-center gap-3",
|
|
803
|
+
children: [/* @__PURE__ */ jsx(Field, {
|
|
804
|
+
label: t("schedule.day.label"),
|
|
805
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
806
|
+
type: "number",
|
|
807
|
+
min: 1,
|
|
808
|
+
max: 31,
|
|
809
|
+
step: 1,
|
|
810
|
+
classNames: "w-15",
|
|
811
|
+
value: String(value.day),
|
|
812
|
+
onChange: (event) => {
|
|
813
|
+
const day = Math.min(31, Math.max(1, Math.round(Number(event.target.value) || 1)));
|
|
814
|
+
onChange({
|
|
815
|
+
...value,
|
|
816
|
+
day
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}) })
|
|
820
|
+
}), /* @__PURE__ */ jsx(Field, {
|
|
821
|
+
label: t("schedule.at.label"),
|
|
822
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.Time, {
|
|
823
|
+
hourCycle: 12,
|
|
824
|
+
value: value.time,
|
|
825
|
+
onValueChange: (time) => onChange({
|
|
826
|
+
...value,
|
|
827
|
+
time
|
|
828
|
+
})
|
|
829
|
+
}) })
|
|
830
|
+
})]
|
|
831
|
+
});
|
|
832
|
+
case "custom": return /* @__PURE__ */ jsx(Field, {
|
|
833
|
+
label: t("schedule.cron.label"),
|
|
834
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
835
|
+
classNames: "w-50 tabular-nums",
|
|
836
|
+
placeholder: "0 9 * * MON-FRI",
|
|
837
|
+
value: value.cron,
|
|
838
|
+
onChange: (event) => onChange({
|
|
839
|
+
kind: "custom",
|
|
840
|
+
cron: event.target.value
|
|
841
|
+
})
|
|
842
|
+
}) })
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
//#endregion
|
|
847
|
+
//#region src/components/TriggerEditor/trigger-kind-icon.ts
|
|
848
|
+
/** Icon for a trigger kind, shared by the picker rows and the selected-variant editor header. */
|
|
849
|
+
var OPTIONS = [
|
|
850
|
+
{
|
|
851
|
+
kind: "timer",
|
|
852
|
+
icon: "ph--clock-countdown--regular"
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
kind: "feed",
|
|
856
|
+
icon: "ph--cards-three--regular"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
kind: "subscription",
|
|
860
|
+
icon: "ph--funnel--regular"
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
kind: "webhook",
|
|
864
|
+
icon: "ph--webhooks-logo--regular",
|
|
865
|
+
disabled: true
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
kind: "email",
|
|
869
|
+
icon: "ph--envelope--regular",
|
|
870
|
+
disabled: true
|
|
871
|
+
}
|
|
872
|
+
];
|
|
873
|
+
//#endregion
|
|
874
|
+
//#region src/components/TriggerEditor/TriggerKindSelector.tsx
|
|
875
|
+
/**
|
|
876
|
+
* Single-select list of pluggable trigger variants (Schedule / Feed / Query / Webhook / Email): each row
|
|
877
|
+
* shows an icon, title and description. Built on {@link Listbox} (role=listbox/option, arrow-key navigation);
|
|
878
|
+
* selecting a row emits its kind. Selection is transient — the parent swaps in the variant editor on change.
|
|
879
|
+
*/
|
|
880
|
+
var TriggerKindSelector = ({ onChange }) => {
|
|
881
|
+
const { t } = useTranslation(meta.profile.key);
|
|
882
|
+
const handleValueChange = useCallback((id) => {
|
|
883
|
+
const option = OPTIONS.find((option) => option.kind === id);
|
|
884
|
+
if (option) onChange(option.kind);
|
|
885
|
+
}, [onChange]);
|
|
886
|
+
return /* @__PURE__ */ jsx(Listbox.Root, {
|
|
887
|
+
onValueChange: handleValueChange,
|
|
888
|
+
children: /* @__PURE__ */ jsx(Listbox.Content, {
|
|
889
|
+
classNames: "gap-1",
|
|
890
|
+
"aria-label": t("trigger-kind.placeholder"),
|
|
891
|
+
children: OPTIONS.map(({ kind, icon, disabled }) => /* @__PURE__ */ jsx(Listbox.Item, {
|
|
892
|
+
id: kind,
|
|
893
|
+
disabled,
|
|
894
|
+
classNames: "dx-input-surface rounded-sm",
|
|
895
|
+
children: /* @__PURE__ */ jsx(Listbox.ItemContent, {
|
|
896
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
897
|
+
icon,
|
|
898
|
+
size: 5,
|
|
899
|
+
classNames: "text-description"
|
|
900
|
+
}),
|
|
901
|
+
title: /* @__PURE__ */ jsx("span", {
|
|
902
|
+
className: "font-medium",
|
|
903
|
+
children: t(`trigger-kind.${kind}.label`)
|
|
904
|
+
}),
|
|
905
|
+
description: t(`trigger-kind.${kind}.description`)
|
|
906
|
+
})
|
|
907
|
+
}, kind))
|
|
908
|
+
})
|
|
909
|
+
});
|
|
910
|
+
};
|
|
911
|
+
//#endregion
|
|
912
|
+
//#region src/components/TriggerEditor/TriggerEditor.tsx
|
|
913
|
+
var RECURRING_KINDS = [
|
|
914
|
+
"hourly",
|
|
915
|
+
"daily",
|
|
916
|
+
"weekly",
|
|
917
|
+
"monthly",
|
|
918
|
+
"custom"
|
|
919
|
+
];
|
|
920
|
+
var EnabledForm = Type.getSchema(Trigger.Trigger).pipe(Schema.pick("enabled", "remote"));
|
|
921
|
+
var TimerSpecForm = Schema.extend(Schema.Struct({
|
|
922
|
+
kind: Schema.Literal("timer"),
|
|
923
|
+
cron: Schema.String.pipe(Schema.annotations({ title: "Schedule (cron)" }), Schema.optional)
|
|
924
|
+
}), EnabledForm);
|
|
925
|
+
var SubscriptionSpecForm = Schema.extend(Schema.Struct({
|
|
926
|
+
kind: Schema.Literal("subscription"),
|
|
927
|
+
typename: Schema.String.pipe(Schema.annotations({ title: "Type" }), Schema.optional),
|
|
928
|
+
deep: Schema.Boolean.pipe(Schema.annotations({ title: "Nested" }), Schema.optional),
|
|
929
|
+
delay: Schema.Number.pipe(Schema.annotations({ title: "Delay (ms)" }), Schema.optional)
|
|
930
|
+
}), EnabledForm);
|
|
931
|
+
var WebhookSpecForm = Schema.extend(Schema.Struct({
|
|
932
|
+
kind: Schema.Literal("webhook"),
|
|
933
|
+
method: Schema.String.pipe(Schema.annotations({ title: "Method" }), Schema.optional),
|
|
934
|
+
port: Schema.Number.pipe(Schema.annotations({ title: "Port" }), Schema.optional)
|
|
935
|
+
}), EnabledForm);
|
|
936
|
+
var FeedSpecForm = Schema.extend(Schema.Struct({
|
|
937
|
+
kind: Schema.Literal("feed"),
|
|
938
|
+
feed: Ref.Ref(Feed.Feed).pipe(ParentLabelAnnotation.set(true), Schema.annotations({ title: "Feed" }), Schema.optional)
|
|
939
|
+
}), EnabledForm);
|
|
940
|
+
var EmailSpecForm = Schema.extend(Schema.Struct({ kind: Schema.Literal("email") }), EnabledForm);
|
|
941
|
+
var TriggerForm = Schema.Union(TimerSpecForm, SubscriptionSpecForm, WebhookSpecForm, FeedSpecForm, EmailSpecForm);
|
|
942
|
+
/** Project a trigger spec onto the form's discriminated-union members. */
|
|
943
|
+
var triggerFormValues = (spec) => {
|
|
944
|
+
switch (spec?.kind) {
|
|
945
|
+
case "subscription": return {
|
|
946
|
+
kind: "subscription",
|
|
947
|
+
typename: spec.query?.raw,
|
|
948
|
+
deep: spec.options?.deep,
|
|
949
|
+
delay: spec.options?.delay
|
|
950
|
+
};
|
|
951
|
+
case "feed": return {
|
|
952
|
+
kind: "feed",
|
|
953
|
+
feed: spec.feed
|
|
954
|
+
};
|
|
955
|
+
case "webhook": return {
|
|
956
|
+
kind: "webhook",
|
|
957
|
+
method: spec.method,
|
|
958
|
+
port: spec.port
|
|
959
|
+
};
|
|
960
|
+
case "email": return { kind: "email" };
|
|
961
|
+
case "timer": return {
|
|
962
|
+
kind: "timer",
|
|
963
|
+
cron: spec.cron
|
|
964
|
+
};
|
|
965
|
+
default: return {};
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
var DEFAULT_TIMER_CRON = toCron(FrequencyDefaults.daily);
|
|
969
|
+
/** Build the subscription query: filter by the chosen type, or match everything until one is picked. */
|
|
970
|
+
var subscriptionQuery = (typename) => typename ? Query.select(Filter.type(DXN.make(typename))) : Query.select(Filter.everything());
|
|
971
|
+
/** Build a trigger spec from the form's values. */
|
|
972
|
+
var triggerFormSpec = (values) => {
|
|
973
|
+
switch (values.kind) {
|
|
974
|
+
case "subscription": {
|
|
975
|
+
const hasOptions = values.deep != null || values.delay != null;
|
|
976
|
+
return {
|
|
977
|
+
kind: "subscription",
|
|
978
|
+
query: {
|
|
979
|
+
raw: values.typename,
|
|
980
|
+
ast: subscriptionQuery(values.typename).ast
|
|
981
|
+
},
|
|
982
|
+
options: hasOptions ? {
|
|
983
|
+
deep: values.deep,
|
|
984
|
+
delay: values.delay
|
|
985
|
+
} : void 0
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
case "feed": return {
|
|
989
|
+
kind: "feed",
|
|
990
|
+
feed: values.feed
|
|
991
|
+
};
|
|
992
|
+
case "webhook": return Trigger.specWebhook({
|
|
993
|
+
method: values.method,
|
|
994
|
+
port: values.port
|
|
995
|
+
});
|
|
996
|
+
case "email": return Trigger.specEmail();
|
|
997
|
+
default: return Trigger.specTimer(values.cron || DEFAULT_TIMER_CRON);
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
var TriggerEditor = ({ classNames, db, routine, trigger, readonly }) => {
|
|
1001
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1002
|
+
const { defaultValues, fieldMap, kind, reset, handleValuesChanged } = useTriggerForm(routine, trigger);
|
|
1003
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Form.Root, {
|
|
1004
|
+
schema: TriggerForm,
|
|
1005
|
+
db,
|
|
1006
|
+
readonly,
|
|
1007
|
+
fieldMap,
|
|
1008
|
+
defaultValues,
|
|
1009
|
+
onValuesChanged: handleValuesChanged,
|
|
1010
|
+
children: /* @__PURE__ */ jsxs(Form.Content, {
|
|
1011
|
+
classNames: mx(kind && "pb-2 dx-card-surface border border-separator rounded-xs", classNames),
|
|
1012
|
+
children: [kind ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
1013
|
+
className: "flex items-center",
|
|
1014
|
+
children: [/* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.Label, {
|
|
1015
|
+
classNames: "pl-2 grow truncate",
|
|
1016
|
+
children: t(`trigger-kind.${kind}.label`)
|
|
1017
|
+
}) }), !readonly && /* @__PURE__ */ jsx(IconButton, {
|
|
1018
|
+
variant: "ghost",
|
|
1019
|
+
icon: "ph--x--regular",
|
|
1020
|
+
iconOnly: true,
|
|
1021
|
+
square: true,
|
|
1022
|
+
label: t("trigger-kind.clear.label"),
|
|
1023
|
+
onClick: reset
|
|
1024
|
+
})]
|
|
1025
|
+
}), /* @__PURE__ */ jsx(Form.FieldSet, { classNames: "px-2" })] }) : /* @__PURE__ */ jsx(Form.FieldSet, {}), kind === "email" && /* @__PURE__ */ jsx("p", {
|
|
1026
|
+
className: "px-2 text-sm text-description",
|
|
1027
|
+
children: t("trigger-kind.email-note.message")
|
|
1028
|
+
})]
|
|
1029
|
+
})
|
|
1030
|
+
}, `${trigger?.id ?? "new"}:${trigger?.spec?.kind ?? "none"}`) });
|
|
1031
|
+
};
|
|
1032
|
+
/** Selects the ECHO object type to watch (subscription `typename`) from the space + registry schemas. */
|
|
1033
|
+
var TypeSelectField = (props) => {
|
|
1034
|
+
const types = useQuery(props.db, Query.select(Filter.type(Type.Type)).from(Scope.space(), Scope.registry()));
|
|
1035
|
+
const options = useMemo(() => types.filter((type) => !Type.isTypeKind(type)).map((type) => Type.getTypename(type)).filter((typename) => !!typename).map((typename) => ({
|
|
1036
|
+
value: typename,
|
|
1037
|
+
label: typename
|
|
1038
|
+
})), [types]);
|
|
1039
|
+
return /* @__PURE__ */ jsx(SelectField, {
|
|
1040
|
+
...props,
|
|
1041
|
+
options
|
|
1042
|
+
});
|
|
1043
|
+
};
|
|
1044
|
+
TypeSelectField.displayName = "TriggerEditor.TypeSelectField";
|
|
1045
|
+
/** Edits the cron via the Schedule picker (recurring kinds only) with a live cronstrue description below it. */
|
|
1046
|
+
var CronField = (props) => {
|
|
1047
|
+
const cron = props.getValue() || DEFAULT_TIMER_CRON;
|
|
1048
|
+
const initial = useMemo(() => cronToSchedule(cron), []);
|
|
1049
|
+
const handleChange = useCallback((value) => {
|
|
1050
|
+
const next = scheduleToCron(value);
|
|
1051
|
+
if (next) props.onValueChange(props.type, next);
|
|
1052
|
+
}, [props.type, props.onValueChange]);
|
|
1053
|
+
return /* @__PURE__ */ jsxs(Schedule.Root, {
|
|
1054
|
+
kinds: RECURRING_KINDS,
|
|
1055
|
+
defaultValue: initial,
|
|
1056
|
+
onValueChange: handleChange,
|
|
1057
|
+
children: [
|
|
1058
|
+
/* @__PURE__ */ jsx(Schedule.Header, {}),
|
|
1059
|
+
/* @__PURE__ */ jsx(Schedule.Kind, {}),
|
|
1060
|
+
/* @__PURE__ */ jsx(Schedule.Body, {})
|
|
1061
|
+
]
|
|
1062
|
+
});
|
|
1063
|
+
};
|
|
1064
|
+
CronField.displayName = "TriggerEditor.CronField";
|
|
1065
|
+
/** Form state for the Trigger section: the chosen variant spec, plus create-on-first-edit handler. */
|
|
1066
|
+
var useTriggerForm = (routine, trigger) => {
|
|
1067
|
+
const methodOptions = useMemo(() => [{
|
|
1068
|
+
value: "GET",
|
|
1069
|
+
label: "GET"
|
|
1070
|
+
}, {
|
|
1071
|
+
value: "POST",
|
|
1072
|
+
label: "POST"
|
|
1073
|
+
}], []);
|
|
1074
|
+
const fieldMap = useMemo(() => ({
|
|
1075
|
+
kind: (props) => props.getValue() ? null : /* @__PURE__ */ jsx(TriggerKindSelector, { onChange: (next) => props.onValueChange(props.type, next) }),
|
|
1076
|
+
cron: (props) => /* @__PURE__ */ jsx(CronField, { ...props }),
|
|
1077
|
+
method: (props) => /* @__PURE__ */ jsx(SelectField, {
|
|
1078
|
+
...props,
|
|
1079
|
+
options: methodOptions
|
|
1080
|
+
}),
|
|
1081
|
+
typename: (props) => /* @__PURE__ */ jsx(TypeSelectField, { ...props })
|
|
1082
|
+
}), [methodOptions]);
|
|
1083
|
+
const defaultValues = useMemo(() => ({
|
|
1084
|
+
...triggerFormValues(trigger?.spec),
|
|
1085
|
+
enabled: trigger?.enabled,
|
|
1086
|
+
remote: trigger?.remote
|
|
1087
|
+
}), [
|
|
1088
|
+
trigger,
|
|
1089
|
+
trigger?.spec,
|
|
1090
|
+
trigger?.enabled,
|
|
1091
|
+
trigger?.remote
|
|
1092
|
+
]);
|
|
1093
|
+
const [kind, setKind] = useState(() => triggerFormValues(trigger?.spec).kind);
|
|
1094
|
+
return {
|
|
1095
|
+
defaultValues,
|
|
1096
|
+
fieldMap,
|
|
1097
|
+
kind,
|
|
1098
|
+
reset: useCallback(() => {
|
|
1099
|
+
setKind(void 0);
|
|
1100
|
+
if (trigger) Obj.update(trigger, (trigger) => {
|
|
1101
|
+
trigger.spec = void 0;
|
|
1102
|
+
});
|
|
1103
|
+
}, [trigger]),
|
|
1104
|
+
handleValuesChanged: useCallback((values) => {
|
|
1105
|
+
if (!values.kind) return;
|
|
1106
|
+
const spec = triggerFormSpec(values);
|
|
1107
|
+
setKind(values.kind);
|
|
1108
|
+
const enabled = values.enabled === true;
|
|
1109
|
+
const remote = values.remote;
|
|
1110
|
+
if (trigger) Obj.update(trigger, (trigger) => {
|
|
1111
|
+
trigger.spec = spec;
|
|
1112
|
+
trigger.enabled = enabled;
|
|
1113
|
+
trigger.remote = remote;
|
|
1114
|
+
});
|
|
1115
|
+
else {
|
|
1116
|
+
const created = Trigger.make({
|
|
1117
|
+
spec,
|
|
1118
|
+
enabled,
|
|
1119
|
+
remote
|
|
1120
|
+
});
|
|
1121
|
+
Obj.setParent(created, routine);
|
|
1122
|
+
Obj.update(routine, (routine) => {
|
|
1123
|
+
routine.triggers.push(Ref.make(created));
|
|
1124
|
+
});
|
|
1125
|
+
wireTriggers(routine);
|
|
1126
|
+
}
|
|
1127
|
+
}, [routine, trigger])
|
|
1128
|
+
};
|
|
1129
|
+
};
|
|
1130
|
+
//#endregion
|
|
1131
|
+
//#region src/components/RoutineForm/RoutineForm.tsx
|
|
1132
|
+
var GeneralForm = Type.getSchema(Routine.Routine).pipe(Schema.pick("name", "description"));
|
|
1133
|
+
/**
|
|
1134
|
+
* Composite routine form: the general fields (name + description) are the outer {@link Form},
|
|
1135
|
+
* with the Actions and Triggers sections rendered as sub-forms inside its content:
|
|
1136
|
+
* - Actions — Operation/Instructions variants chosen via a button group (single action; `spec` isn't an array).
|
|
1137
|
+
* - Triggers — the {@link TriggerEditor}.
|
|
1138
|
+
*
|
|
1139
|
+
* The sub-forms read and autosave the routine's owned instructions and primary trigger directly off the
|
|
1140
|
+
* `routine` graph (live editing); `readonly` displays them without edit affordances. The optional Save/Cancel
|
|
1141
|
+
* row is used only by the companion's create-from-template flow.
|
|
1142
|
+
*
|
|
1143
|
+
* Created with `composable()` so it carries the COMPOSABLE marker and can be the child of
|
|
1144
|
+
* `Panel.Content asChild` (forwards ref and merges layout props onto the scroll viewport).
|
|
1145
|
+
*/
|
|
1146
|
+
var RoutineForm = composable(({ db, routine, readonly = false, onSave, onCancel, ...props }, forwardedRef) => {
|
|
1147
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1148
|
+
const [auto, updateAuto] = useObject(routine);
|
|
1149
|
+
const trigger = usePrimaryTrigger(routine);
|
|
1150
|
+
const defaultValues = useMemo(() => ({
|
|
1151
|
+
name: auto.name,
|
|
1152
|
+
description: auto.description
|
|
1153
|
+
}), [routine]);
|
|
1154
|
+
const handleValuesChanged = useCallback((values) => {
|
|
1155
|
+
updateAuto((routine) => {
|
|
1156
|
+
routine.name = values.name;
|
|
1157
|
+
routine.description = values.description;
|
|
1158
|
+
});
|
|
1159
|
+
}, [updateAuto]);
|
|
1160
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
1161
|
+
schema: GeneralForm,
|
|
1162
|
+
readonly,
|
|
1163
|
+
defaultValues,
|
|
1164
|
+
onValuesChanged: handleValuesChanged,
|
|
1165
|
+
onSave,
|
|
1166
|
+
onCancel,
|
|
1167
|
+
children: /* @__PURE__ */ jsx(Form.Viewport, {
|
|
1168
|
+
scroll: true,
|
|
1169
|
+
...composableProps(props),
|
|
1170
|
+
ref: forwardedRef,
|
|
1171
|
+
children: /* @__PURE__ */ jsxs(Form.Content, { children: [
|
|
1172
|
+
/* @__PURE__ */ jsx(Form.FieldSet, {}),
|
|
1173
|
+
/* @__PURE__ */ jsx(Section, {
|
|
1174
|
+
title: t("actions.title"),
|
|
1175
|
+
children: /* @__PURE__ */ jsx(ActionEditor, {
|
|
1176
|
+
db,
|
|
1177
|
+
routine,
|
|
1178
|
+
readonly
|
|
1179
|
+
})
|
|
1180
|
+
}),
|
|
1181
|
+
/* @__PURE__ */ jsx(Section, {
|
|
1182
|
+
title: t("triggers.title"),
|
|
1183
|
+
children: /* @__PURE__ */ jsx(TriggerEditor, {
|
|
1184
|
+
db,
|
|
1185
|
+
routine,
|
|
1186
|
+
trigger,
|
|
1187
|
+
readonly
|
|
1188
|
+
})
|
|
1189
|
+
}),
|
|
1190
|
+
onSave && /* @__PURE__ */ jsx(Form.Actions, {})
|
|
1191
|
+
] })
|
|
1192
|
+
})
|
|
1193
|
+
});
|
|
1194
|
+
});
|
|
1195
|
+
/** Lightweight labelled grouping for a sub-form (no `Settings` chrome). */
|
|
1196
|
+
var Section = ({ title, children }) => /* @__PURE__ */ jsxs("div", {
|
|
1197
|
+
className: "flex flex-col mbs-4",
|
|
1198
|
+
children: [/* @__PURE__ */ jsx(Form.Label, {
|
|
1199
|
+
standalone: true,
|
|
1200
|
+
label: title
|
|
1201
|
+
}), children]
|
|
1202
|
+
});
|
|
1203
|
+
/**
|
|
1204
|
+
* Single action: an Operation (the routine's `spec.runnable`) or an owned Instructions edited inline. The
|
|
1205
|
+
* action kind is derived from the routine's `spec` — an absent spec means "operation, none chosen yet" (a
|
|
1206
|
+
* scaffolded routine always carries an instructions spec, so the only way to clear it is switching to an
|
|
1207
|
+
* operation). `makeRoutine` establishes the owned-instructions wiring when the routine is scaffolded.
|
|
1208
|
+
*/
|
|
1209
|
+
var ActionEditor = ({ db, routine: routineProp, readonly }) => {
|
|
1210
|
+
const operations = useOperations(db);
|
|
1211
|
+
const [routine, updateRoutine] = useObject(routineProp);
|
|
1212
|
+
const kind = routine.spec?.kind ?? "runnable";
|
|
1213
|
+
const operationRef = Routine.runnableRef(routine);
|
|
1214
|
+
const instructions = Routine.instructionsRef(routine)?.target;
|
|
1215
|
+
const handleOperationChange = useCallback((operation) => {
|
|
1216
|
+
updateRoutine((routine) => {
|
|
1217
|
+
routine.spec = operation ? {
|
|
1218
|
+
kind: "runnable",
|
|
1219
|
+
runnable: operation
|
|
1220
|
+
} : void 0;
|
|
1221
|
+
});
|
|
1222
|
+
wireTriggers(routineProp);
|
|
1223
|
+
}, [updateRoutine, routineProp]);
|
|
1224
|
+
const handleKindChange = useCallback((next) => {
|
|
1225
|
+
updateRoutine((routine) => {
|
|
1226
|
+
if (next === "runnable") routine.spec = void 0;
|
|
1227
|
+
else if (routine.spec?.kind !== "instructions") {
|
|
1228
|
+
const instructions = Instructions.make({});
|
|
1229
|
+
Obj.setParent(instructions, routine);
|
|
1230
|
+
routine.spec = {
|
|
1231
|
+
kind: "instructions",
|
|
1232
|
+
instructions: Ref.make(instructions)
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
wireTriggers(routineProp);
|
|
1237
|
+
}, [updateRoutine, routineProp]);
|
|
1238
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1239
|
+
className: "flex flex-col",
|
|
1240
|
+
children: [!readonly && /* @__PURE__ */ jsx(ActionKindToggle, {
|
|
1241
|
+
value: kind,
|
|
1242
|
+
onChange: handleKindChange
|
|
1243
|
+
}), kind === "runnable" ? /* @__PURE__ */ jsx(OperationEditor, {
|
|
1244
|
+
db,
|
|
1245
|
+
operations,
|
|
1246
|
+
operation: operationRef,
|
|
1247
|
+
onChange: handleOperationChange,
|
|
1248
|
+
readonly
|
|
1249
|
+
}) : instructions ? /* @__PURE__ */ jsx(InstructionsEditor, {
|
|
1250
|
+
db,
|
|
1251
|
+
instructions,
|
|
1252
|
+
readonly
|
|
1253
|
+
}) : null]
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
var ActionKindToggle = ({ value, onChange }) => {
|
|
1257
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1258
|
+
return /* @__PURE__ */ jsxs(ToggleGroup, {
|
|
1259
|
+
type: "single",
|
|
1260
|
+
value,
|
|
1261
|
+
onValueChange: (next) => {
|
|
1262
|
+
if (next === "instructions" || next === "runnable") onChange(next);
|
|
1263
|
+
},
|
|
1264
|
+
children: [/* @__PURE__ */ jsx(ToggleGroupItem, {
|
|
1265
|
+
value: "instructions",
|
|
1266
|
+
children: t("action-kind.instructions.label")
|
|
1267
|
+
}), /* @__PURE__ */ jsx(ToggleGroupItem, {
|
|
1268
|
+
value: "runnable",
|
|
1269
|
+
children: t("action-kind.operation.label")
|
|
1270
|
+
})]
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
1273
|
+
var OperationActionForm = Schema.Struct({ operation: Ref.Ref(Operation.PersistentOperation).pipe(Schema.annotations({ title: "Operation" }), Schema.optional) });
|
|
1274
|
+
var getOperationOptions = (results) => results.filter((operation) => !Obj.instanceOf(Operation.PersistentOperation, operation) || Operation.isVisible(operation)).map((operation) => {
|
|
1275
|
+
const id = Entity.getURI(operation, { prefer: "named" });
|
|
1276
|
+
return {
|
|
1277
|
+
id,
|
|
1278
|
+
label: Entity.getLabel(operation) ?? id,
|
|
1279
|
+
description: Obj.instanceOf(Operation.PersistentOperation, operation) ? Operation.getKey(operation) : void 0
|
|
1280
|
+
};
|
|
1281
|
+
}).sort((left, right) => left.label.localeCompare(right.label));
|
|
1282
|
+
/** Sub-form: ref picker for an Operation. Draws options from the queried operation set (space + registry). */
|
|
1283
|
+
var OperationEditor = ({ db, operations, operation, onChange, readonly }) => {
|
|
1284
|
+
const fieldMap = useMemo(() => ({ operation: (props) => /* @__PURE__ */ jsx(RefField, {
|
|
1285
|
+
...props,
|
|
1286
|
+
db,
|
|
1287
|
+
useResults: () => operations,
|
|
1288
|
+
getOptions: getOperationOptions
|
|
1289
|
+
}) }), [db, operations]);
|
|
1290
|
+
const defaultValues = useMemo(() => ({ operation }), [operation]);
|
|
1291
|
+
const handleValuesChanged = useCallback((values) => onChange?.(values.operation), [onChange]);
|
|
1292
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
1293
|
+
schema: OperationActionForm,
|
|
1294
|
+
db,
|
|
1295
|
+
readonly,
|
|
1296
|
+
defaultValues,
|
|
1297
|
+
fieldMap,
|
|
1298
|
+
onValuesChanged: handleValuesChanged,
|
|
1299
|
+
children: /* @__PURE__ */ jsx(Form.FieldSet, {})
|
|
1300
|
+
});
|
|
1301
|
+
};
|
|
1302
|
+
/** Operations available to bind as an action (space-resident plus registry / plugin-provided). */
|
|
1303
|
+
var useOperations = (db) => useQuery(db, Query.select(Filter.type(DXN.make(Type.getTypename(Operation.PersistentOperation)))).from(Scope.space(), Scope.registry()));
|
|
1304
|
+
/** Subscribe to the routine and derive its primary (first) trigger. */
|
|
1305
|
+
var usePrimaryTrigger = (routine) => {
|
|
1306
|
+
const [snapshot] = useObject(routine);
|
|
1307
|
+
return useMemo(() => {
|
|
1308
|
+
for (const ref of snapshot.triggers) {
|
|
1309
|
+
const target = ref.target;
|
|
1310
|
+
if (Obj.instanceOf(Trigger.Trigger, target)) return target;
|
|
1311
|
+
}
|
|
1312
|
+
}, [snapshot.triggers]);
|
|
1313
|
+
};
|
|
1314
|
+
//#endregion
|
|
1315
|
+
//#region src/components/CreateRoutinePanel/CreateRoutinePanel.tsx
|
|
1316
|
+
/**
|
|
1317
|
+
* Create panel for routines: a SearchList picker over contributed templates. Selecting one submits
|
|
1318
|
+
* `{ templateId }`; plugin-routine's CreateObjectEntry.createObject resolves the templateId and runs the
|
|
1319
|
+
* template's `scaffold`.
|
|
1320
|
+
*/
|
|
1321
|
+
var CreateRoutinePanel = ({ onCreateObject, templates: templatesProp }) => {
|
|
1322
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1323
|
+
const capabilityTemplates = useCapabilities(RoutineCapabilities.Template);
|
|
1324
|
+
const templates = templatesProp ?? capabilityTemplates;
|
|
1325
|
+
const { results, handleSearch } = useSearchListResults({
|
|
1326
|
+
items: useMemo(() => [...templates].filter((template) => template.appliesTo?.(void 0) ?? true).sort((a, b) => a.label.localeCompare(b.label)), [templates]),
|
|
1327
|
+
extract: (template) => template.label
|
|
1328
|
+
});
|
|
1329
|
+
const handleSelect = useCallback((templateId) => {
|
|
1330
|
+
onCreateObject({ templateId });
|
|
1331
|
+
}, [onCreateObject]);
|
|
1332
|
+
return /* @__PURE__ */ jsxs(SearchList.Root, {
|
|
1333
|
+
onSearch: handleSearch,
|
|
1334
|
+
children: [/* @__PURE__ */ jsx(SearchList.Input, {
|
|
1335
|
+
classNames: "mb-form-gap",
|
|
1336
|
+
autoFocus: true,
|
|
1337
|
+
"data-testid": "create-automation-panel.template-input",
|
|
1338
|
+
placeholder: t("create-panel.template.placeholder")
|
|
1339
|
+
}), /* @__PURE__ */ jsx(SearchList.Viewport, { children: results.map((template) => /* @__PURE__ */ jsx(SearchList.Item, {
|
|
1340
|
+
value: template.id,
|
|
1341
|
+
label: template.label,
|
|
1342
|
+
icon: template.icon ?? "ph--lightning--regular",
|
|
1343
|
+
onSelect: () => handleSelect(template.id)
|
|
1344
|
+
}, template.id)) })]
|
|
1345
|
+
});
|
|
1346
|
+
};
|
|
1347
|
+
//#endregion
|
|
1348
|
+
//#region src/components/RoutineCard/RoutineCard.tsx
|
|
1349
|
+
/**
|
|
1350
|
+
* Compact preview of a {@link Routine.Routine}: when it runs. Rendered into the
|
|
1351
|
+
* `AppSurface.CardContent` slot — Card.Root is supplied by the surface host (the project article's
|
|
1352
|
+
* gallery, popovers, related-objects), so this emits Card.Body only. Without it a routine falls back
|
|
1353
|
+
* to the generic form preview, which renders its `spec`/`triggers` internals rather than a summary.
|
|
1354
|
+
*/
|
|
1355
|
+
var RoutineCard = ({ subject }) => {
|
|
1356
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1357
|
+
const [routine] = useObject(subject);
|
|
1358
|
+
const [trigger] = useObject(routine.triggers.at(0));
|
|
1359
|
+
const active = trigger?.enabled === true;
|
|
1360
|
+
return /* @__PURE__ */ jsx(Card.Body, { children: /* @__PURE__ */ jsxs(Card.Row, { children: [/* @__PURE__ */ jsx(Card.Block, { children: active && /* @__PURE__ */ jsx(Icon, {
|
|
1361
|
+
icon: "ph--check-circle--regular",
|
|
1362
|
+
classNames: "text-green-text"
|
|
1363
|
+
}) }), /* @__PURE__ */ jsx(Card.Text, {
|
|
1364
|
+
variant: "description",
|
|
1365
|
+
classNames: "line-clamp-2",
|
|
1366
|
+
children: describeTrigger(trigger?.spec, t)
|
|
1367
|
+
})] }) });
|
|
1368
|
+
};
|
|
1369
|
+
RoutineCard.displayName = "RoutineCard";
|
|
1370
|
+
/**
|
|
1371
|
+
* The same sentence the trigger editor shows above the schedule picker. Non-timer kinds have no
|
|
1372
|
+
* parameters worth summarizing, so they reuse the kind description from the trigger-kind picker.
|
|
1373
|
+
*/
|
|
1374
|
+
var describeTrigger = (spec, t) => {
|
|
1375
|
+
if (!spec) return t("add-trigger-first.message");
|
|
1376
|
+
return spec.kind === "timer" ? describeSchedule(cronToSchedule(spec.cron)) : t(`trigger-kind.${spec.kind}.description`);
|
|
1377
|
+
};
|
|
1378
|
+
//#endregion
|
|
1379
|
+
//#region src/components/TemplateEditor/extensions/handlebars-extension.ts
|
|
1380
|
+
/**
|
|
1381
|
+
* Simple Handlebars plugin for CodeMirror.
|
|
1382
|
+
* Supports:
|
|
1383
|
+
* - Comments: {{! comment }}
|
|
1384
|
+
* - Commands: {{#command}} {{/command}}
|
|
1385
|
+
* - Variables: {{var}}
|
|
1386
|
+
* - Commands with variables: {{#each items}} {{/each}}
|
|
1387
|
+
*/
|
|
1388
|
+
var handlebars = (_ = {}) => {
|
|
1389
|
+
return [
|
|
1390
|
+
handlebarsHighlightPlugin,
|
|
1391
|
+
autocompletion({
|
|
1392
|
+
activateOnTyping: true,
|
|
1393
|
+
aboveCursor: true,
|
|
1394
|
+
closeOnBlur: true,
|
|
1395
|
+
override: [handlebarsCompletions]
|
|
1396
|
+
}),
|
|
1397
|
+
keymap.of(completionKeymap)
|
|
1398
|
+
];
|
|
1399
|
+
};
|
|
1400
|
+
var regex = {
|
|
1401
|
+
comment: /\{\{!\s*[^}]*\}\}/g,
|
|
1402
|
+
brackets: /\{\{[^}]*\}\}/g,
|
|
1403
|
+
command: /\{\{[#/]([^}]+)\}\}/g,
|
|
1404
|
+
var: /\{\{(?!\s*!)(\w[^}]*)\}\}/g,
|
|
1405
|
+
dxn: /@?(?:echo:\/{1,3}[\w/]+|dxn:[\w.-]+)/g,
|
|
1406
|
+
url: /[\w.-]+\.[\w.-]+\/[\w/]+/g
|
|
1407
|
+
};
|
|
1408
|
+
var tagPadding = "mx-0.5 px-1 rounded-xs";
|
|
1409
|
+
/**
|
|
1410
|
+
* ViewPlugin that decorates Handlebars syntax.
|
|
1411
|
+
*/
|
|
1412
|
+
var handlebarsHighlightPlugin = ViewPlugin.fromClass(class {
|
|
1413
|
+
decorations;
|
|
1414
|
+
constructor(view) {
|
|
1415
|
+
this.decorations = this.buildDecorations(view);
|
|
1416
|
+
}
|
|
1417
|
+
update(update) {
|
|
1418
|
+
if (update.docChanged || update.viewportChanged || update.selectionSet) this.decorations = this.buildDecorations(update.view);
|
|
1419
|
+
}
|
|
1420
|
+
buildDecorations(view) {
|
|
1421
|
+
const selection = view.state.selection.main;
|
|
1422
|
+
const decorations = [];
|
|
1423
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1424
|
+
const text = view.state.doc.sliceString(from, to);
|
|
1425
|
+
{
|
|
1426
|
+
let match;
|
|
1427
|
+
while ((match = regex.dxn.exec(text)) !== null) {
|
|
1428
|
+
const start = from + match.index;
|
|
1429
|
+
const end = start + match[0].length;
|
|
1430
|
+
if (!(selection.to > start && selection.from <= end)) decorations.push({
|
|
1431
|
+
from: start,
|
|
1432
|
+
to: end,
|
|
1433
|
+
decoration: Decoration.widget({ widget: new DXNWidget(match[0]) })
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
{
|
|
1438
|
+
let match;
|
|
1439
|
+
while ((match = regex.url.exec(text)) !== null) {
|
|
1440
|
+
const start = from + match.index;
|
|
1441
|
+
const end = start + match[0].length;
|
|
1442
|
+
decorations.push({
|
|
1443
|
+
from: start,
|
|
1444
|
+
to: end,
|
|
1445
|
+
decoration: Decoration.mark({ class: mx$1("dx-tag--blue", tagPadding) })
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
{
|
|
1450
|
+
let match;
|
|
1451
|
+
while ((match = regex.brackets.exec(text)) !== null) {
|
|
1452
|
+
const start = from + match.index;
|
|
1453
|
+
const end = start + match[0].length;
|
|
1454
|
+
decorations.push({
|
|
1455
|
+
from: start,
|
|
1456
|
+
to: end,
|
|
1457
|
+
decoration: Decoration.mark({ class: "text-subdued" })
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
{
|
|
1462
|
+
let match;
|
|
1463
|
+
while ((match = regex.command.exec(text)) !== null) {
|
|
1464
|
+
const start = from + match.index + 2;
|
|
1465
|
+
let end = start + match[0].length - 4;
|
|
1466
|
+
const parts = view.state.doc.sliceString(start, end).split(/\s+/);
|
|
1467
|
+
if (parts.length > 1) {
|
|
1468
|
+
const idx = start + parts[0].length;
|
|
1469
|
+
decorations.push({
|
|
1470
|
+
from: idx,
|
|
1471
|
+
to: end,
|
|
1472
|
+
decoration: Decoration.mark({ class: "text-green-text" })
|
|
1473
|
+
});
|
|
1474
|
+
end = idx;
|
|
1475
|
+
}
|
|
1476
|
+
decorations.push({
|
|
1477
|
+
from: start,
|
|
1478
|
+
to: end,
|
|
1479
|
+
decoration: Decoration.mark({ class: "text-blue-text" })
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
{
|
|
1484
|
+
let match;
|
|
1485
|
+
while ((match = regex.var.exec(text)) !== null) {
|
|
1486
|
+
const start = from + match.index + 2;
|
|
1487
|
+
const end = start + match[0].length - 4;
|
|
1488
|
+
decorations.push({
|
|
1489
|
+
from: start,
|
|
1490
|
+
to: end,
|
|
1491
|
+
decoration: Decoration.mark({ class: "text-green-text" })
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
decorations.sort((a, b) => a.from - b.from || a.to - b.to);
|
|
1497
|
+
const builder = new RangeSetBuilder();
|
|
1498
|
+
for (const { from, to, decoration } of decorations) builder.add(from, to, decoration);
|
|
1499
|
+
return builder.finish();
|
|
1500
|
+
}
|
|
1501
|
+
}, { decorations: (v) => v.decorations });
|
|
1502
|
+
var DXNWidget = class extends WidgetType {
|
|
1503
|
+
_identifier;
|
|
1504
|
+
constructor(_identifier) {
|
|
1505
|
+
super();
|
|
1506
|
+
this._identifier = _identifier;
|
|
1507
|
+
}
|
|
1508
|
+
ignoreEvent() {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
eq(other) {
|
|
1512
|
+
return this._identifier === other._identifier;
|
|
1513
|
+
}
|
|
1514
|
+
toDOM() {
|
|
1515
|
+
const text = this._identifier.split(":").map((part) => {
|
|
1516
|
+
const len = 16;
|
|
1517
|
+
const plen = 4;
|
|
1518
|
+
if (part.length > len) return `[${part.slice(0, plen)}…${part.slice(-4)}]`;
|
|
1519
|
+
return part;
|
|
1520
|
+
}).join(":");
|
|
1521
|
+
return Domino.of("span").classNames(mx$1("font-mono dx-tag--blue", tagPadding)).text(text).root;
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
var variables = ["this"];
|
|
1525
|
+
var commands = [
|
|
1526
|
+
"this",
|
|
1527
|
+
"each",
|
|
1528
|
+
"if",
|
|
1529
|
+
"unless",
|
|
1530
|
+
"with"
|
|
1531
|
+
];
|
|
1532
|
+
/**
|
|
1533
|
+
* Provides completions for Handlebars variables.
|
|
1534
|
+
*/
|
|
1535
|
+
function handlebarsCompletions(context) {
|
|
1536
|
+
const match = context.matchBefore(/\{\{[^}]*/);
|
|
1537
|
+
if (!match || match.from === match.to) return null;
|
|
1538
|
+
let type = "variable";
|
|
1539
|
+
let text = match.text.slice(2);
|
|
1540
|
+
let from = match.from + 2;
|
|
1541
|
+
let matches = [];
|
|
1542
|
+
if (text.startsWith("#") || text.startsWith("/")) {
|
|
1543
|
+
const idx = text.lastIndexOf(" ");
|
|
1544
|
+
if (idx !== -1) {
|
|
1545
|
+
type = "variable";
|
|
1546
|
+
matches = variables;
|
|
1547
|
+
text = text.slice(idx + 1);
|
|
1548
|
+
from += idx + 1;
|
|
1549
|
+
} else {
|
|
1550
|
+
type = "command";
|
|
1551
|
+
text = text.slice(1);
|
|
1552
|
+
matches = commands;
|
|
1553
|
+
from += 1;
|
|
1554
|
+
}
|
|
1555
|
+
} else {
|
|
1556
|
+
type = "variable";
|
|
1557
|
+
matches = variables;
|
|
1558
|
+
}
|
|
1559
|
+
const options = matches.filter((name) => name.startsWith(text)).map((name) => ({
|
|
1560
|
+
type,
|
|
1561
|
+
label: name
|
|
1562
|
+
}));
|
|
1563
|
+
return {
|
|
1564
|
+
from,
|
|
1565
|
+
options
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
//#endregion
|
|
1569
|
+
//#region src/components/TemplateEditor/extensions/xml-extension.ts
|
|
1570
|
+
/**
|
|
1571
|
+
* XML decorator plugin for CodeMirror.
|
|
1572
|
+
* Decorates XML tags with monospace font using the XML language grammar.
|
|
1573
|
+
*/
|
|
1574
|
+
var xmlDecorator = (_ = {}) => {
|
|
1575
|
+
return [xmlDecoratorPlugin];
|
|
1576
|
+
};
|
|
1577
|
+
/**
|
|
1578
|
+
* ViewPlugin that decorates XML tags using syntax tree.
|
|
1579
|
+
*/
|
|
1580
|
+
var xmlDecoratorPlugin = ViewPlugin.fromClass(class {
|
|
1581
|
+
decorations;
|
|
1582
|
+
constructor(view) {
|
|
1583
|
+
this.decorations = this.buildDecorations(view);
|
|
1584
|
+
}
|
|
1585
|
+
update(update) {
|
|
1586
|
+
if (update.docChanged || update.viewportChanged) this.decorations = this.buildDecorations(update.view);
|
|
1587
|
+
}
|
|
1588
|
+
buildDecorations(view) {
|
|
1589
|
+
const builder = new RangeSetBuilder();
|
|
1590
|
+
for (const { from, to } of view.visibleRanges) syntaxTree(view.state).iterate({
|
|
1591
|
+
from,
|
|
1592
|
+
to,
|
|
1593
|
+
enter: (node) => {
|
|
1594
|
+
if (node.name === "HTMLTag" || node.name === "OpenTag" || node.name === "CloseTag" || node.name === "SelfClosingTag" || node.name === "Element") builder.add(node.from, node.to, Decoration.mark({ class: "font-mono text-subdued" }));
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
return builder.finish();
|
|
1598
|
+
}
|
|
1599
|
+
}, { decorations: (v) => v.decorations });
|
|
1600
|
+
//#endregion
|
|
1601
|
+
//#region src/components/TemplateEditor/TemplateEditor.tsx
|
|
1602
|
+
var TemplateEditor = composable(({ classNames, id, source, lineNumbers = true, ...props }, forwardedRef) => {
|
|
1603
|
+
const { t } = useTranslation(meta.profile.key);
|
|
1604
|
+
const { themeMode } = useThemeContext();
|
|
1605
|
+
const { parentRef } = useTextEditor(() => {
|
|
1606
|
+
const target = source?.target;
|
|
1607
|
+
if (!target) return {};
|
|
1608
|
+
return {
|
|
1609
|
+
initialValue: target.content ?? "",
|
|
1610
|
+
extensions: [
|
|
1611
|
+
createDataExtensions({
|
|
1612
|
+
id,
|
|
1613
|
+
text: Doc.createAccessor(target, ["content"])
|
|
1614
|
+
}),
|
|
1615
|
+
createBasicExtensions({
|
|
1616
|
+
bracketMatching: false,
|
|
1617
|
+
lineNumbers,
|
|
1618
|
+
lineWrapping: true,
|
|
1619
|
+
placeholder: t("template.placeholder")
|
|
1620
|
+
}),
|
|
1621
|
+
createThemeExtensions({ themeMode }),
|
|
1622
|
+
createMarkdownExtensions(),
|
|
1623
|
+
decorateMarkdown(),
|
|
1624
|
+
handlebars(),
|
|
1625
|
+
xmlDecorator(),
|
|
1626
|
+
syntaxHighlighting(defaultHighlightStyle)
|
|
1627
|
+
].filter(isNonNullable)
|
|
1628
|
+
};
|
|
1629
|
+
}, [
|
|
1630
|
+
themeMode,
|
|
1631
|
+
source?.target,
|
|
1632
|
+
lineNumbers
|
|
1633
|
+
]);
|
|
1634
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1635
|
+
...composableProps(props, {
|
|
1636
|
+
role: "none",
|
|
1637
|
+
classNames: ["h-full overflow-hidden", classNames]
|
|
1638
|
+
}),
|
|
1639
|
+
ref: composeRefs(parentRef, forwardedRef)
|
|
1640
|
+
});
|
|
1641
|
+
});
|
|
1642
|
+
//#endregion
|
|
1643
|
+
export { CreateRoutinePanel, CronSpec, CustomSpec, DailySpec, DayOfWeek, Days, FrequencyDefaults, HourlySpec, InstructionsEditor, MAX_MIN_INTERVAL_SECONDS, MinutelySpec, MonthlySpec, RoutineCard, RoutineForm, Schedule, ScheduleKinds, TemplateEditor, TriggerEditor, WeeklySpec, clampSchedule, cronIntervalSeconds, cronToSchedule, describeCron, describeSchedule, fromCron, scheduleIntervalSeconds, scheduleToCron, toCron };
|
|
1644
|
+
|
|
1645
|
+
//# sourceMappingURL=components.mjs.map
|