undercity 1.0.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/AGENTS.md +26 -0
- package/README.md +58 -0
- package/actions/AGENTS.md +41 -0
- package/actions/_shared/container.js +16 -0
- package/actions/auth/ask-login/action.json +15 -0
- package/actions/auth/ask-signup/action.json +14 -0
- package/actions/display/clear/action.json +18 -0
- package/actions/display/clear/action.test.js +32 -0
- package/actions/display/markdown/action.json +12 -0
- package/actions/display/markdown/action.test.js +32 -0
- package/actions/display/rawHtml/action.json +24 -0
- package/actions/display/rawHtml/action.test.js +32 -0
- package/actions/display/safeHtml/action.json +24 -0
- package/actions/display/safeHtml/action.test.js +32 -0
- package/actions/display/text/action.js +9 -0
- package/actions/display/text/action.json +12 -0
- package/actions/display/text/action.test.js +40 -0
- package/actions/display/value/action.json +24 -0
- package/actions/display/value/action.test.js +32 -0
- package/actions/dom/addClass/action.json +23 -0
- package/actions/dom/addClass/action.test.js +32 -0
- package/actions/dom/focus/action.json +17 -0
- package/actions/dom/focus/action.test.js +32 -0
- package/actions/dom/hide/action.json +18 -0
- package/actions/dom/hide/action.test.js +32 -0
- package/actions/dom/removeClass/action.json +22 -0
- package/actions/dom/removeClass/action.test.js +32 -0
- package/actions/dom/scroll/action.json +29 -0
- package/actions/dom/scroll/action.test.js +32 -0
- package/actions/dom/setAttr/action.json +29 -0
- package/actions/dom/setAttr/action.test.js +32 -0
- package/actions/dom/setHtml/action.json +22 -0
- package/actions/dom/setHtml/action.test.js +32 -0
- package/actions/dom/setStyle/action.json +29 -0
- package/actions/dom/setStyle/action.test.js +32 -0
- package/actions/dom/setText/action.json +24 -0
- package/actions/dom/setText/action.test.js +32 -0
- package/actions/dom/show/action.json +18 -0
- package/actions/dom/show/action.test.js +32 -0
- package/actions/dom/toggle/action.json +17 -0
- package/actions/dom/toggle/action.test.js +32 -0
- package/actions/dom/toggleClass/action.json +22 -0
- package/actions/dom/toggleClass/action.test.js +32 -0
- package/actions/event/emit/action.json +24 -0
- package/actions/event/emit/action.test.js +32 -0
- package/actions/event/on/action.json +23 -0
- package/actions/event/on/action.test.js +32 -0
- package/actions/event/waitFor/action.json +28 -0
- package/actions/event/waitFor/action.test.js +32 -0
- package/actions/forms/bindField/action.js +27 -0
- package/actions/forms/bindField/action.json +24 -0
- package/actions/forms/bindField/action.test.js +20 -0
- package/actions/forms/check/action.json +22 -0
- package/actions/forms/check/action.test.js +32 -0
- package/actions/forms/clearErrors/action.json +11 -0
- package/actions/forms/clearErrors/action.test.js +35 -0
- package/actions/forms/clearField/action.json +17 -0
- package/actions/forms/clearField/action.test.js +32 -0
- package/actions/forms/getField/action.json +24 -0
- package/actions/forms/getField/action.test.js +32 -0
- package/actions/forms/getRange/action.json +22 -0
- package/actions/forms/getRange/action.test.js +32 -0
- package/actions/forms/getSelect/action.json +22 -0
- package/actions/forms/getSelect/action.test.js +32 -0
- package/actions/forms/index.js +140 -0
- package/actions/forms/serialize/action.json +24 -0
- package/actions/forms/serialize/action.test.js +32 -0
- package/actions/forms/setCheck/action.json +23 -0
- package/actions/forms/setCheck/action.test.js +32 -0
- package/actions/forms/setError/action.json +22 -0
- package/actions/forms/setError/action.test.js +32 -0
- package/actions/forms/setField/action.js +14 -0
- package/actions/forms/setField/action.json +24 -0
- package/actions/forms/setField/action.test.js +32 -0
- package/actions/forms/submit/action.json +18 -0
- package/actions/forms/submit/action.test.js +32 -0
- package/actions/forms/validate/action.json +24 -0
- package/actions/forms/validate/action.test.js +32 -0
- package/actions/http/delete/action.json +22 -0
- package/actions/http/delete/action.test.js +32 -0
- package/actions/http/get/action.json +24 -0
- package/actions/http/get/action.test.js +32 -0
- package/actions/http/post/action.json +30 -0
- package/actions/http/post/action.test.js +32 -0
- package/actions/http/put/action.json +27 -0
- package/actions/http/put/action.test.js +32 -0
- package/actions/http/upload/action.json +35 -0
- package/actions/http/upload/action.test.js +32 -0
- package/actions/index.js +306 -0
- package/actions/index.json +5 -0
- package/actions/input/askChoice/action.json +29 -0
- package/actions/input/askChoice/action.test.js +32 -0
- package/actions/input/askConfirm/action.json +24 -0
- package/actions/input/askConfirm/action.test.js +32 -0
- package/actions/input/askDate/action.json +24 -0
- package/actions/input/askDate/action.test.js +32 -0
- package/actions/input/askEmail/action.json +24 -0
- package/actions/input/askEmail/action.test.js +32 -0
- package/actions/input/askNumber/action.json +35 -0
- package/actions/input/askNumber/action.test.js +32 -0
- package/actions/input/askPassword/action.json +24 -0
- package/actions/input/askPassword/action.test.js +32 -0
- package/actions/input/askText/action.json +36 -0
- package/actions/input/askText/action.test.js +32 -0
- package/actions/logic/delay/action.json +18 -0
- package/actions/logic/delay/action.test.js +32 -0
- package/actions/logic/if/action.json +28 -0
- package/actions/logic/if/action.test.js +32 -0
- package/actions/logic/log/action.json +18 -0
- package/actions/logic/log/action.test.js +32 -0
- package/actions/logic/random/action.json +36 -0
- package/actions/logic/random/action.test.js +32 -0
- package/actions/logic/transform/action.json +24 -0
- package/actions/logic/transform/action.test.js +32 -0
- package/actions/media/askAudioUpload/action.json +30 -0
- package/actions/media/askAudioUpload/action.test.js +32 -0
- package/actions/media/askFileUpload/action.json +36 -0
- package/actions/media/askFileUpload/action.test.js +32 -0
- package/actions/media/askImageUpload/action.json +37 -0
- package/actions/media/askImageUpload/action.test.js +32 -0
- package/actions/media/askVideoUpload/action.js +74 -0
- package/actions/media/askVideoUpload/action.json +44 -0
- package/actions/media/askVideoUpload/action.test.js +51 -0
- package/actions/media/captureWebcam/action.json +24 -0
- package/actions/media/captureWebcam/action.test.js +32 -0
- package/actions/nav/back/action.json +11 -0
- package/actions/nav/back/action.test.js +35 -0
- package/actions/nav/goto/action.json +18 -0
- package/actions/nav/goto/action.test.js +32 -0
- package/actions/nav/redirect/action.json +28 -0
- package/actions/nav/redirect/action.test.js +32 -0
- package/actions/nav/reload/action.json +11 -0
- package/actions/nav/reload/action.test.js +35 -0
- package/actions/nav/reset/action.json +11 -0
- package/actions/nav/reset/action.test.js +35 -0
- package/actions/render/alert/action.js +12 -0
- package/actions/render/alert/action.json +36 -0
- package/actions/render/alert/action.test.js +32 -0
- package/actions/render/button/action.js +15 -0
- package/actions/render/button/action.json +44 -0
- package/actions/render/button/action.test.js +37 -0
- package/actions/render/clear/action.js +7 -0
- package/actions/render/clear/action.json +11 -0
- package/actions/render/clear/action.test.js +35 -0
- package/actions/render/divider/action.js +8 -0
- package/actions/render/divider/action.json +11 -0
- package/actions/render/divider/action.test.js +35 -0
- package/actions/render/field/action.js +17 -0
- package/actions/render/field/action.json +59 -0
- package/actions/render/field/action.test.js +57 -0
- package/actions/render/link/action.js +20 -0
- package/actions/render/link/action.json +30 -0
- package/actions/render/link/action.test.js +32 -0
- package/actions/render/markdown/action.json +18 -0
- package/actions/render/markdown/action.test.js +32 -0
- package/actions/render/paragraph/action.js +9 -0
- package/actions/render/paragraph/action.json +32 -0
- package/actions/render/paragraph/action.test.js +32 -0
- package/actions/render/section/action.js +10 -0
- package/actions/render/section/action.json +18 -0
- package/actions/render/section/action.test.js +32 -0
- package/actions/render/subtitle/action.js +9 -0
- package/actions/render/subtitle/action.json +18 -0
- package/actions/render/subtitle/action.test.js +32 -0
- package/actions/render/title/action.js +9 -0
- package/actions/render/title/action.json +30 -0
- package/actions/render/title/action.test.js +44 -0
- package/actions/session/clear/action.json +11 -0
- package/actions/session/clear/action.test.js +35 -0
- package/actions/session/load/action.json +22 -0
- package/actions/session/load/action.test.js +32 -0
- package/actions/session/local/action.json +22 -0
- package/actions/session/local/action.test.js +32 -0
- package/actions/session/save/action.json +22 -0
- package/actions/session/save/action.test.js +32 -0
- package/actions/ui/accordion/action.json +23 -0
- package/actions/ui/accordion/action.test.js +32 -0
- package/actions/ui/badge/action.json +22 -0
- package/actions/ui/badge/action.test.js +32 -0
- package/actions/ui/collapse/action.json +23 -0
- package/actions/ui/collapse/action.test.js +32 -0
- package/actions/ui/hideModal/action.json +17 -0
- package/actions/ui/hideModal/action.test.js +32 -0
- package/actions/ui/loading/action.json +18 -0
- package/actions/ui/loading/action.test.js +32 -0
- package/actions/ui/modal/action.json +18 -0
- package/actions/ui/modal/action.test.js +32 -0
- package/actions/ui/progress/action.json +24 -0
- package/actions/ui/progress/action.test.js +32 -0
- package/actions/ui/toast/action.json +29 -0
- package/actions/ui/toast/action.test.js +32 -0
- package/actions/ui/tooltip/action.json +17 -0
- package/actions/ui/tooltip/action.test.js +32 -0
- package/actions/user/carry/action.json +25 -0
- package/actions/user/carry/action.test.js +32 -0
- package/actions/user/check/action.json +24 -0
- package/actions/user/check/action.test.js +32 -0
- package/actions/user/clear/action.json +11 -0
- package/actions/user/clear/action.test.js +35 -0
- package/actions/user/delete/action.json +17 -0
- package/actions/user/delete/action.test.js +32 -0
- package/actions/user/dump/action.json +11 -0
- package/actions/user/dump/action.test.js +35 -0
- package/actions/user/get/action.json +24 -0
- package/actions/user/get/action.test.js +32 -0
- package/actions/user/merge/action.json +18 -0
- package/actions/user/merge/action.test.js +32 -0
- package/actions/user/set/action.json +24 -0
- package/actions/user/set/action.test.js +32 -0
- package/generator/base/css/bootstrap.min.css +6 -0
- package/generator/base/icons/app-indicator.svg +4 -0
- package/generator/base/icons/backpack.svg +4 -0
- package/generator/base/icons/broadcast.svg +3 -0
- package/generator/base/icons/bullseye.svg +6 -0
- package/generator/base/icons/chat-dots.svg +4 -0
- package/generator/base/icons/check-circle.svg +4 -0
- package/generator/base/icons/clipboard-check.svg +5 -0
- package/generator/base/icons/clipboard.svg +4 -0
- package/generator/base/icons/copy.svg +3 -0
- package/generator/base/icons/cursor.svg +3 -0
- package/generator/base/icons/diamond.svg +3 -0
- package/generator/base/icons/exclamation-triangle.svg +4 -0
- package/generator/base/icons/film.svg +3 -0
- package/generator/base/icons/floppy.svg +4 -0
- package/generator/base/icons/gear-wide-connected.svg +3 -0
- package/generator/base/icons/gear.svg +4 -0
- package/generator/base/icons/globe.svg +3 -0
- package/generator/base/icons/image.svg +4 -0
- package/generator/base/icons/layout-text-window.svg +4 -0
- package/generator/base/icons/lightning-charge.svg +3 -0
- package/generator/base/icons/magic.svg +3 -0
- package/generator/base/icons/pencil-square.svg +4 -0
- package/generator/base/icons/record-circle.svg +4 -0
- package/generator/base/icons/robot.svg +4 -0
- package/generator/base/icons/shield-check.svg +4 -0
- package/generator/base/icons/shield-lock.svg +4 -0
- package/generator/base/icons/signpost.svg +3 -0
- package/generator/base/icons/stars.svg +3 -0
- package/generator/base/icons/type.svg +3 -0
- package/generator/base/js/bootstrap.bundle.min.js +7 -0
- package/package.json +14 -0
- package/packages/undercity-http-server/index.js +249 -0
- package/packages/undercity-http-server/package.json +10 -0
- package/packages/undercity-parser/index.js +323 -0
- package/packages/undercity-parser/lexer.js +128 -0
- package/packages/undercity-parser/package.json +11 -0
- package/plugins/forms.js +397 -0
- package/plugins/index.js +83 -0
- package/plugins/multipage.js +165 -0
- package/plugins/wizard.js +239 -0
- package/projects/asd/project.json +1031 -0
- package/projects/test-1/project.json +335 -0
- package/projects/test-a/project.json +456 -0
- package/public/icons/arrows-angle-expand.svg +3 -0
- package/public/icons/arrows-fullscreen.svg +3 -0
- package/public/icons/bezier2.svg +3 -0
- package/public/icons/bootstrap/app-indicator.svg +4 -0
- package/public/icons/bootstrap/arrow-clockwise.svg +4 -0
- package/public/icons/bootstrap/arrow-counterclockwise.svg +4 -0
- package/public/icons/bootstrap/arrow-left.svg +3 -0
- package/public/icons/bootstrap/arrows-angle-expand.svg +3 -0
- package/public/icons/bootstrap/arrows-fullscreen.svg +3 -0
- package/public/icons/bootstrap/backpack.svg +4 -0
- package/public/icons/bootstrap/bezier2.svg +3 -0
- package/public/icons/bootstrap/bookmark-check.svg +4 -0
- package/public/icons/bootstrap/bookmark-plus.svg +4 -0
- package/public/icons/bootstrap/box-arrow-right.svg +4 -0
- package/public/icons/bootstrap/box-arrow-up.svg +4 -0
- package/public/icons/bootstrap/broadcast.svg +3 -0
- package/public/icons/bootstrap/bullseye.svg +6 -0
- package/public/icons/bootstrap/chat-dots.svg +4 -0
- package/public/icons/bootstrap/check-circle.svg +4 -0
- package/public/icons/bootstrap/check2.svg +3 -0
- package/public/icons/bootstrap/clipboard-check.svg +5 -0
- package/public/icons/bootstrap/clipboard.svg +4 -0
- package/public/icons/bootstrap/clock-history.svg +5 -0
- package/public/icons/bootstrap/command.svg +3 -0
- package/public/icons/bootstrap/copy.svg +3 -0
- package/public/icons/bootstrap/cursor.svg +3 -0
- package/public/icons/bootstrap/diagram-3.svg +3 -0
- package/public/icons/bootstrap/diamond.svg +3 -0
- package/public/icons/bootstrap/exclamation-triangle.svg +4 -0
- package/public/icons/bootstrap/eye.svg +4 -0
- package/public/icons/bootstrap/file-earmark-plus.svg +4 -0
- package/public/icons/bootstrap/film.svg +3 -0
- package/public/icons/bootstrap/floppy.svg +4 -0
- package/public/icons/bootstrap/folder2-open.svg +3 -0
- package/public/icons/bootstrap/gear-wide-connected.svg +3 -0
- package/public/icons/bootstrap/gear.svg +4 -0
- package/public/icons/bootstrap/globe.svg +3 -0
- package/public/icons/bootstrap/grid-3x3-gap.svg +3 -0
- package/public/icons/bootstrap/house-door.svg +3 -0
- package/public/icons/bootstrap/image.svg +4 -0
- package/public/icons/bootstrap/layout-text-window.svg +4 -0
- package/public/icons/bootstrap/lightning-charge.svg +3 -0
- package/public/icons/bootstrap/magic.svg +3 -0
- package/public/icons/bootstrap/pencil-square.svg +4 -0
- package/public/icons/bootstrap/pencil.svg +3 -0
- package/public/icons/bootstrap/play.svg +3 -0
- package/public/icons/bootstrap/plus-circle.svg +4 -0
- package/public/icons/bootstrap/plus-lg.svg +3 -0
- package/public/icons/bootstrap/record-circle.svg +4 -0
- package/public/icons/bootstrap/robot.svg +4 -0
- package/public/icons/bootstrap/save.svg +3 -0
- package/public/icons/bootstrap/scissors.svg +3 -0
- package/public/icons/bootstrap/shield-check.svg +4 -0
- package/public/icons/bootstrap/shield-lock.svg +4 -0
- package/public/icons/bootstrap/signpost.svg +3 -0
- package/public/icons/bootstrap/stars.svg +3 -0
- package/public/icons/bootstrap/stop-circle.svg +4 -0
- package/public/icons/bootstrap/terminal.svg +4 -0
- package/public/icons/bootstrap/trash3.svg +3 -0
- package/public/icons/bootstrap/type.svg +3 -0
- package/public/icons/bootstrap/x-circle.svg +4 -0
- package/public/icons/bootstrap/x-lg.svg +3 -0
- package/public/icons/bootstrap/zoom-in.svg +5 -0
- package/public/icons/bootstrap/zoom-out.svg +5 -0
- package/public/icons/bullseye.svg +6 -0
- package/public/icons/check2.svg +3 -0
- package/public/icons/cursor.svg +3 -0
- package/public/icons/diamond.svg +3 -0
- package/public/icons/eye.svg +4 -0
- package/public/icons/file-earmark-plus.svg +4 -0
- package/public/icons/floppy.svg +4 -0
- package/public/icons/gear.svg +4 -0
- package/public/icons/lightning-charge.svg +3 -0
- package/public/icons/pencil.svg +3 -0
- package/public/icons/play.svg +3 -0
- package/public/icons/plus-circle.svg +4 -0
- package/public/icons/record-circle.svg +4 -0
- package/public/icons/robot.svg +4 -0
- package/public/icons/save.svg +3 -0
- package/public/icons/stop-circle.svg +4 -0
- package/public/icons/terminal.svg +4 -0
- package/public/icons/trash3.svg +3 -0
- package/public/icons/x-circle.svg +4 -0
- package/public/icons/zoom-in.svg +5 -0
- package/public/icons/zoom-out.svg +5 -0
- package/public/index.html +424 -0
- package/public/testbench.html +899 -0
- package/scripts/extract-actions.js +128 -0
- package/server.js +11 -0
- package/src/emitter.js +48 -0
- package/src/generator/css.js +135 -0
- package/src/generator/index.js +122 -0
- package/src/generator/md-renderer-src.js +77 -0
- package/src/generator/page.js +300 -0
- package/src/generator/runtime.js +1632 -0
- package/src/generator/templates.js +508 -0
- package/src/ide/action-library.js +856 -0
- package/src/ide/af-icons.js +127 -0
- package/src/ide/app.js +1375 -0
- package/src/ide/command-line/commands.js +242 -0
- package/src/ide/command-line/index.js +329 -0
- package/src/ide/command-line/parser.js +21 -0
- package/src/ide/css/ide.css +1501 -0
- package/src/ide/graph.js +282 -0
- package/src/ide/history.js +46 -0
- package/src/ide/map-builder.js +583 -0
- package/src/ide/project-api.js +39 -0
- package/src/ide/savant-chat.js +513 -0
- package/src/ide/savant.js +1287 -0
- package/src/ide/thing-library.js +89 -0
- package/src/ide/undercity-map.js +978 -0
- package/src/lib/icons.js +72 -0
- package/src/lib/scope.js +88 -0
- package/src/lib/signal.js +155 -0
- package/src/lib/state-machine.js +113 -0
- package/src/server/index.js +96 -0
- package/src/server/routes/actions.js +144 -0
- package/src/server/routes/ai.js +176 -0
- package/src/server/routes/generate.js +54 -0
- package/src/server/routes/projects.js +106 -0
- package/src/server/routes/reset.js +30 -0
- package/src/server/routes/submit.js +30 -0
- package/src/server/routes/templates.js +139 -0
- package/src/server/routes/things.js +33 -0
- package/templates/auth-flow.json +335 -0
- package/templates/blank.json +39 -0
- package/things/auth-server/thing.json +17 -0
- package/things/persona-live/thing.json +20 -0
- package/things/workflow/thing.json +15 -0
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action-library.js
|
|
3
|
+
*
|
|
4
|
+
* Declarative catalogue of every built-in Savant action.
|
|
5
|
+
* Each entry describes how to render the action card and its param editor.
|
|
6
|
+
*
|
|
7
|
+
* Shape:
|
|
8
|
+
* category → { label, icon, color, actions: { id → ActionDef } }
|
|
9
|
+
* icon is a Bootstrap icon file name without the .svg extension
|
|
10
|
+
*
|
|
11
|
+
* ActionDef:
|
|
12
|
+
* label — human-readable name
|
|
13
|
+
* desc — one-liner description shown on the card
|
|
14
|
+
* params — ordered array of ParamDef
|
|
15
|
+
* runtime — string of JS executed at page-runtime (for code-gen doc)
|
|
16
|
+
*
|
|
17
|
+
* ParamDef:
|
|
18
|
+
* name, label, type, default?, placeholder?, options?
|
|
19
|
+
* type: text | code | select | room | boolean | number | json | textarea
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export const ACTION_LIBRARY = {
|
|
23
|
+
|
|
24
|
+
// ── Navigation ─────────────────────────────────────────────────────────────
|
|
25
|
+
navigation: {
|
|
26
|
+
label: 'Navigation',
|
|
27
|
+
icon: 'signpost',
|
|
28
|
+
color: 'var(--sol-blue)',
|
|
29
|
+
actions: {
|
|
30
|
+
|
|
31
|
+
'nav.goto': {
|
|
32
|
+
label: 'Go To Room',
|
|
33
|
+
desc: 'Navigate the user to another room in the flow.',
|
|
34
|
+
params: [
|
|
35
|
+
{ name: 'target', label: 'Room', type: 'room', placeholder: 'room-id' },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
'nav.back': {
|
|
39
|
+
label: 'Go Back',
|
|
40
|
+
desc: 'Return to the previous room (browser history).',
|
|
41
|
+
params: [],
|
|
42
|
+
},
|
|
43
|
+
'nav.reset': {
|
|
44
|
+
label: 'Reset Flow',
|
|
45
|
+
desc: 'Clear inventory and return the user to the Lobby.',
|
|
46
|
+
params: [],
|
|
47
|
+
},
|
|
48
|
+
'nav.reload': {
|
|
49
|
+
label: 'Reload Page',
|
|
50
|
+
desc: 'Hard-reload the current room.',
|
|
51
|
+
params: [],
|
|
52
|
+
},
|
|
53
|
+
'nav.redirect': {
|
|
54
|
+
label: 'Redirect URL',
|
|
55
|
+
desc: 'Navigate to an absolute or relative URL.',
|
|
56
|
+
params: [
|
|
57
|
+
{ name: 'url', label: 'URL', type: 'text', placeholder: 'https://…' },
|
|
58
|
+
{ name: 'target', label: 'Open in', type: 'select', options: ['_self','_blank'], default: '_self' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// ── User / Inventory ─────────────────────────────────────────────────────
|
|
66
|
+
user: {
|
|
67
|
+
label: 'User',
|
|
68
|
+
icon: 'backpack',
|
|
69
|
+
color: 'var(--sol-cyan)',
|
|
70
|
+
actions: {
|
|
71
|
+
|
|
72
|
+
'user.set': {
|
|
73
|
+
label: 'Set Inventory Item',
|
|
74
|
+
desc: 'Store a key/value in the user\'s inventory.',
|
|
75
|
+
params: [
|
|
76
|
+
{ name: 'key', label: 'Key', type: 'text', placeholder: 'myKey' },
|
|
77
|
+
{ name: 'value', label: 'Value', type: 'code', placeholder: '"hello" or inventory.email' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
'user.get': {
|
|
81
|
+
label: 'Read Inventory Item',
|
|
82
|
+
desc: 'Read a value from inventory when this step runs. Save it under a different key (alias) using "Save as".',
|
|
83
|
+
params: [
|
|
84
|
+
{ name: 'key', label: 'Key', type: 'text', placeholder: 'myKey' },
|
|
85
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'localAlias' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
'user.merge': {
|
|
89
|
+
label: 'Merge into Inventory',
|
|
90
|
+
desc: 'Merge an object (e.g., API response) into the user inventory.',
|
|
91
|
+
params: [
|
|
92
|
+
{ name: 'from', label: 'From variable', type: 'text', placeholder: 'responseVar' },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
'user.delete': {
|
|
96
|
+
label: 'Delete Inventory Item',
|
|
97
|
+
desc: 'Remove a key from the user\'s inventory.',
|
|
98
|
+
params: [
|
|
99
|
+
{ name: 'key', label: 'Key', type: 'text' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
'user.clear': {
|
|
103
|
+
label: 'Clear Inventory',
|
|
104
|
+
desc: 'Wipe everything the user carries.',
|
|
105
|
+
params: [],
|
|
106
|
+
},
|
|
107
|
+
'user.carry': {
|
|
108
|
+
label: 'Carry Form Result',
|
|
109
|
+
desc: 'Serialize a form into inventory so the user carries the answers.',
|
|
110
|
+
params: [
|
|
111
|
+
{ name: 'formSelector', label: 'Form', type: 'text', placeholder: '#my-form' },
|
|
112
|
+
{ name: 'namespace', label: 'Namespace', type: 'text', placeholder: 'loginForm', default: '' },
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
'user.check': {
|
|
116
|
+
label: 'Check Condition',
|
|
117
|
+
desc: 'Evaluate a JS expression against inventory. Branch via diamond.',
|
|
118
|
+
params: [
|
|
119
|
+
{ name: 'expr', label: 'Expression', type: 'code', placeholder: 'inventory.age >= 18' },
|
|
120
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'isAdult' },
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
'user.dump': {
|
|
124
|
+
label: 'Dump Inventory to Console',
|
|
125
|
+
desc: 'console.log the full user inventory (debug).',
|
|
126
|
+
params: [],
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// ── DOM ────────────────────────────────────────────────────────────────────
|
|
133
|
+
dom: {
|
|
134
|
+
label: 'DOM',
|
|
135
|
+
icon: 'image',
|
|
136
|
+
color: 'var(--sol-green)',
|
|
137
|
+
actions: {
|
|
138
|
+
|
|
139
|
+
'dom.show': {
|
|
140
|
+
label: 'Show Element',
|
|
141
|
+
desc: 'Remove d-none / display:none from matching elements.',
|
|
142
|
+
params: [{ name: 'selector', label: 'Selector', type: 'text', placeholder: '#my-div' }],
|
|
143
|
+
},
|
|
144
|
+
'dom.hide': {
|
|
145
|
+
label: 'Hide Element',
|
|
146
|
+
desc: 'Add d-none class to matching elements.',
|
|
147
|
+
params: [{ name: 'selector', label: 'Selector', type: 'text', placeholder: '#my-div' }],
|
|
148
|
+
},
|
|
149
|
+
'dom.toggle': {
|
|
150
|
+
label: 'Toggle Visibility',
|
|
151
|
+
desc: 'Toggle d-none on matching elements.',
|
|
152
|
+
params: [{ name: 'selector', label: 'Selector', type: 'text' }],
|
|
153
|
+
},
|
|
154
|
+
'dom.setText': {
|
|
155
|
+
label: 'Set Text',
|
|
156
|
+
desc: 'Set the textContent of matching elements.',
|
|
157
|
+
params: [
|
|
158
|
+
{ name: 'selector', label: 'Selector', type: 'text', placeholder: '#my-label' },
|
|
159
|
+
{ name: 'text', label: 'Text', type: 'code', placeholder: 'inventory.firstName' },
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
'dom.setHtml': {
|
|
163
|
+
label: 'Set Inner HTML',
|
|
164
|
+
desc: 'Set innerHTML of matching elements.',
|
|
165
|
+
params: [
|
|
166
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
167
|
+
{ name: 'html', label: 'HTML', type: 'textarea' },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
'dom.setAttr': {
|
|
171
|
+
label: 'Set Attribute',
|
|
172
|
+
desc: 'Set an attribute on matching elements.',
|
|
173
|
+
params: [
|
|
174
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
175
|
+
{ name: 'attr', label: 'Attribute',type: 'text', placeholder: 'src' },
|
|
176
|
+
{ name: 'value', label: 'Value', type: 'code', placeholder: '"https://…"' },
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
'dom.addClass': {
|
|
180
|
+
label: 'Add Class',
|
|
181
|
+
desc: 'Add one or more CSS classes.',
|
|
182
|
+
params: [
|
|
183
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
184
|
+
{ name: 'classes', label: 'Classes', type: 'text', placeholder: 'is-active text-info' },
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
'dom.removeClass': {
|
|
188
|
+
label: 'Remove Class',
|
|
189
|
+
desc: 'Remove one or more CSS classes.',
|
|
190
|
+
params: [
|
|
191
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
192
|
+
{ name: 'classes', label: 'Classes', type: 'text' },
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
'dom.toggleClass': {
|
|
196
|
+
label: 'Toggle Class',
|
|
197
|
+
desc: 'Toggle a CSS class on matching elements.',
|
|
198
|
+
params: [
|
|
199
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
200
|
+
{ name: 'class', label: 'Class', type: 'text' },
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
'dom.setStyle': {
|
|
204
|
+
label: 'Set Inline Style',
|
|
205
|
+
desc: 'Set a CSS property directly.',
|
|
206
|
+
params: [
|
|
207
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
208
|
+
{ name: 'prop', label: 'Property', type: 'text', placeholder: 'color' },
|
|
209
|
+
{ name: 'value', label: 'Value', type: 'text', placeholder: '#268bd2' },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
'dom.scroll': {
|
|
213
|
+
label: 'Scroll To Element',
|
|
214
|
+
desc: 'Smoothly scroll the selector into view.',
|
|
215
|
+
params: [
|
|
216
|
+
{ name: 'selector', label: 'Selector', type: 'text' },
|
|
217
|
+
{ name: 'block', label: 'Position', type: 'select', options: ['start','center','end','nearest'], default: 'start' },
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
'dom.focus': {
|
|
221
|
+
label: 'Focus Element',
|
|
222
|
+
desc: 'Move keyboard focus to a selector.',
|
|
223
|
+
params: [{ name: 'selector', label: 'Selector', type: 'text' }],
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// ── Input Prompts ──────────────────────────────────────────────────────────
|
|
230
|
+
input: {
|
|
231
|
+
label: 'Input Prompts',
|
|
232
|
+
icon: 'pencil-square',
|
|
233
|
+
color: 'var(--sol-orange)',
|
|
234
|
+
actions: {
|
|
235
|
+
|
|
236
|
+
'input.askText': {
|
|
237
|
+
label: 'Ask for Text Input',
|
|
238
|
+
desc: 'Show a modal prompting the user to type text. Waits for confirmation, then automatically saves the typed value to inventory under the key you specify in "Save as".',
|
|
239
|
+
params: [
|
|
240
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Your name' },
|
|
241
|
+
{ name: 'placeholder', label: 'Placeholder', type: 'text', placeholder: 'Type here…' },
|
|
242
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'userName' },
|
|
243
|
+
{ name: 'multiline', label: 'Multiline', type: 'boolean', default: false },
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
'input.askNumber': {
|
|
247
|
+
label: 'Ask for Number',
|
|
248
|
+
desc: 'Show a modal with a numeric input. Waits for confirmation, then automatically saves the number to inventory under the key you specify in "Save as".',
|
|
249
|
+
params: [
|
|
250
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Age' },
|
|
251
|
+
{ name: 'min', label: 'Min', type: 'number', default: 0 },
|
|
252
|
+
{ name: 'max', label: 'Max', type: 'number', default: 999 },
|
|
253
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
'input.askEmail': {
|
|
257
|
+
label: 'Ask for Email',
|
|
258
|
+
desc: 'Show a modal with an email-validated input. Waits for confirmation, then automatically saves the email to inventory under the key you specify in "Save as".',
|
|
259
|
+
params: [
|
|
260
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Your email' },
|
|
261
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'email' },
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
'input.askPassword': {
|
|
265
|
+
label: 'Ask for Password',
|
|
266
|
+
desc: 'Show a masked password input modal. Waits for confirmation, then automatically saves the value to inventory under the key you specify in "Save as".',
|
|
267
|
+
params: [
|
|
268
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Enter password' },
|
|
269
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'password' },
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
'input.askDate': {
|
|
273
|
+
label: 'Ask for Date',
|
|
274
|
+
desc: 'Show a date-picker modal. Waits for confirmation, then automatically saves the selected date to inventory under the key you specify in "Save as".',
|
|
275
|
+
params: [
|
|
276
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Select a date' },
|
|
277
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'selectedDate' },
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
'input.askChoice': {
|
|
281
|
+
label: 'Ask for Choice',
|
|
282
|
+
desc: 'Present a list of options as radio buttons. Waits for a selection, then automatically saves the chosen value to inventory under the key you specify in "Save as".',
|
|
283
|
+
params: [
|
|
284
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Choose one' },
|
|
285
|
+
{ name: 'options', label: 'Options (CSV)', type: 'text', placeholder: 'Red,Green,Blue' },
|
|
286
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
'input.askConfirm': {
|
|
290
|
+
label: 'Ask for Confirmation',
|
|
291
|
+
desc: 'Show a yes/no confirmation dialog. Automatically saves true or false to inventory under the key you specify in "Save as".',
|
|
292
|
+
params: [
|
|
293
|
+
{ name: 'message', label: 'Message', type: 'text', placeholder: 'Are you sure?' },
|
|
294
|
+
{ name: 'into', label: 'Save as',type: 'text', placeholder: 'confirmed' },
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// ── Media / File Upload ────────────────────────────────────────────────────
|
|
302
|
+
media: {
|
|
303
|
+
label: 'Media & Upload',
|
|
304
|
+
icon: 'film',
|
|
305
|
+
color: 'var(--sol-magenta)',
|
|
306
|
+
actions: {
|
|
307
|
+
|
|
308
|
+
'media.askVideoUpload': {
|
|
309
|
+
label: 'Ask for Video Upload',
|
|
310
|
+
desc: 'Open file picker for a video. Optionally extract a preview thumbnail frame.',
|
|
311
|
+
params: [
|
|
312
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Select a video' },
|
|
313
|
+
{ name: 'accept', label: 'Accept types', type: 'text', placeholder: 'video/*', default: 'video/*' },
|
|
314
|
+
{ name: 'thumbnailPrompt', label: 'Thumbnail prompt', type: 'text', placeholder: 'Select a frame as thumbnail', default: 'Select a frame that serves as a good preview thumbnail' },
|
|
315
|
+
{ name: 'extractThumb', label: 'Extract thumbnail', type: 'boolean', default: true },
|
|
316
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'uploadedVideo' },
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
'media.askImageUpload': {
|
|
320
|
+
label: 'Ask for Image Upload',
|
|
321
|
+
desc: 'Open file picker for an image. Preview before accepting.',
|
|
322
|
+
params: [
|
|
323
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Upload an image' },
|
|
324
|
+
{ name: 'accept', label: 'Accept', type: 'text', placeholder: 'image/*', default: 'image/*' },
|
|
325
|
+
{ name: 'preview', label: 'Show preview', type: 'boolean', default: true },
|
|
326
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'uploadedImage' },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
'media.askFileUpload': {
|
|
330
|
+
label: 'Ask for File Upload',
|
|
331
|
+
desc: 'Open generic file picker. Stores file metadata in inventory.',
|
|
332
|
+
params: [
|
|
333
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Upload a file' },
|
|
334
|
+
{ name: 'accept', label: 'Accept', type: 'text', placeholder: '*/*' },
|
|
335
|
+
{ name: 'multiple', label: 'Multiple', type: 'boolean', default: false },
|
|
336
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'uploadedFile' },
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
'media.askAudioUpload': {
|
|
340
|
+
label: 'Ask for Audio Upload',
|
|
341
|
+
desc: 'Open file picker for audio. Shows waveform preview.',
|
|
342
|
+
params: [
|
|
343
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Upload audio' },
|
|
344
|
+
{ name: 'accept', label: 'Accept', type: 'text', default: 'audio/*' },
|
|
345
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'audioFile' },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
'media.captureWebcam': {
|
|
349
|
+
label: 'Capture from Webcam',
|
|
350
|
+
desc: 'Ask camera permission and capture a still photo.',
|
|
351
|
+
params: [
|
|
352
|
+
{ name: 'label', label: 'Prompt label', type: 'text', placeholder: 'Take a photo' },
|
|
353
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'photo' },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
// ── HTTP ───────────────────────────────────────────────────────────────────
|
|
361
|
+
http: {
|
|
362
|
+
label: 'HTTP',
|
|
363
|
+
icon: 'globe',
|
|
364
|
+
color: 'var(--sol-violet)',
|
|
365
|
+
actions: {
|
|
366
|
+
|
|
367
|
+
'http.get': {
|
|
368
|
+
label: 'HTTP GET',
|
|
369
|
+
desc: 'Fetch JSON from a URL. Stores response in inventory.',
|
|
370
|
+
params: [
|
|
371
|
+
{ name: 'url', label: 'URL', type: 'code', placeholder: '"/api/user"' },
|
|
372
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'result' },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
'http.post': {
|
|
376
|
+
label: 'HTTP POST',
|
|
377
|
+
desc: 'POST JSON body to a URL.',
|
|
378
|
+
params: [
|
|
379
|
+
{ name: 'url', label: 'URL', type: 'code', placeholder: '"/api/login"' },
|
|
380
|
+
{ name: 'body', label: 'Body', type: 'code', placeholder: '{ email, password }' },
|
|
381
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'result' },
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
'http.put': {
|
|
385
|
+
label: 'HTTP PUT',
|
|
386
|
+
desc: 'PUT JSON body to a URL.',
|
|
387
|
+
params: [
|
|
388
|
+
{ name: 'url', label: 'URL', type: 'code' },
|
|
389
|
+
{ name: 'body', label: 'Body', type: 'code' },
|
|
390
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
'http.delete': {
|
|
394
|
+
label: 'HTTP DELETE',
|
|
395
|
+
desc: 'Send a DELETE request.',
|
|
396
|
+
params: [
|
|
397
|
+
{ name: 'url', label: 'URL', type: 'code' },
|
|
398
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
'http.upload': {
|
|
402
|
+
label: 'Upload File (multipart)',
|
|
403
|
+
desc: 'POST a file from inventory as multipart/form-data.',
|
|
404
|
+
params: [
|
|
405
|
+
{ name: 'url', label: 'URL', type: 'code' },
|
|
406
|
+
{ name: 'fileVar', label: 'File var', type: 'text', placeholder: 'uploadedVideo' },
|
|
407
|
+
{ name: 'fieldName',label: 'Field name', type: 'text', placeholder: 'file' },
|
|
408
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'uploadResult' },
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
// ── UI Feedback ────────────────────────────────────────────────────────────
|
|
416
|
+
ui: {
|
|
417
|
+
label: 'UI Feedback',
|
|
418
|
+
icon: 'chat-dots',
|
|
419
|
+
color: 'var(--sol-orange)',
|
|
420
|
+
actions: {
|
|
421
|
+
|
|
422
|
+
'ui.toast': {
|
|
423
|
+
label: 'Show Toast',
|
|
424
|
+
desc: 'Show a transient notification overlay.',
|
|
425
|
+
params: [
|
|
426
|
+
{ name: 'msg', label: 'Message', type: 'text' },
|
|
427
|
+
{ name: 'type', label: 'Type', type: 'select', options: ['info','success','warning','danger'], default: 'info' },
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
'ui.loading': {
|
|
431
|
+
label: 'Loading Spinner',
|
|
432
|
+
desc: 'Show or hide a full-screen loading overlay.',
|
|
433
|
+
params: [
|
|
434
|
+
{ name: 'show', label: 'Visible', type: 'boolean', default: true },
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
'ui.modal': {
|
|
438
|
+
label: 'Show Modal',
|
|
439
|
+
desc: 'Open a named Bootstrap modal by selector.',
|
|
440
|
+
params: [
|
|
441
|
+
{ name: 'selector', label: 'Modal selector', type: 'text', placeholder: '#my-modal' },
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
'ui.hideModal': {
|
|
445
|
+
label: 'Hide Modal',
|
|
446
|
+
desc: 'Close a named Bootstrap modal.',
|
|
447
|
+
params: [
|
|
448
|
+
{ name: 'selector', label: 'Modal selector', type: 'text' },
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
'ui.progress': {
|
|
452
|
+
label: 'Set Progress Bar',
|
|
453
|
+
desc: 'Update a Bootstrap progress bar by selector.',
|
|
454
|
+
params: [
|
|
455
|
+
{ name: 'selector', label: 'Progress bar', type: 'text', placeholder: '#my-progress' },
|
|
456
|
+
{ name: 'value', label: 'Value 0–100', type: 'number', default: 0 },
|
|
457
|
+
],
|
|
458
|
+
},
|
|
459
|
+
'ui.badge': {
|
|
460
|
+
label: 'Set Badge',
|
|
461
|
+
desc: 'Update text content of a badge element.',
|
|
462
|
+
params: [
|
|
463
|
+
{ name: 'selector', label: 'Badge selector', type: 'text' },
|
|
464
|
+
{ name: 'text', label: 'Text', type: 'code' },
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
'ui.accordion': {
|
|
468
|
+
label: 'Toggle Accordion',
|
|
469
|
+
desc: 'Open or close a Bootstrap accordion item.',
|
|
470
|
+
params: [
|
|
471
|
+
{ name: 'selector', label: 'Item selector', type: 'text' },
|
|
472
|
+
{ name: 'show', label: 'Show', type: 'boolean', default: true },
|
|
473
|
+
],
|
|
474
|
+
},
|
|
475
|
+
'ui.collapse': {
|
|
476
|
+
label: 'Toggle Collapse',
|
|
477
|
+
desc: 'Show or hide a Bootstrap collapse component.',
|
|
478
|
+
params: [
|
|
479
|
+
{ name: 'selector', label: 'Target', type: 'text' },
|
|
480
|
+
{ name: 'show', label: 'Show', type: 'boolean', default: true },
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
'ui.tooltip': {
|
|
484
|
+
label: 'Show Tooltip',
|
|
485
|
+
desc: 'Programmatically show a Bootstrap tooltip.',
|
|
486
|
+
params: [
|
|
487
|
+
{ name: 'selector', label: 'Target', type: 'text' },
|
|
488
|
+
],
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
// ── Events ─────────────────────────────────────────────────────────────────
|
|
495
|
+
events: {
|
|
496
|
+
label: 'Events',
|
|
497
|
+
icon: 'broadcast',
|
|
498
|
+
color: 'var(--sol-cyan)',
|
|
499
|
+
actions: {
|
|
500
|
+
|
|
501
|
+
'event.emit': {
|
|
502
|
+
label: 'Emit Event',
|
|
503
|
+
desc: 'Broadcast a named event on the flow event bus.',
|
|
504
|
+
params: [
|
|
505
|
+
{ name: 'event', label: 'Event name', type: 'text', placeholder: 'user-ready' },
|
|
506
|
+
{ name: 'data', label: 'Payload', type: 'code', placeholder: '{ id: inventory.userId }' },
|
|
507
|
+
],
|
|
508
|
+
},
|
|
509
|
+
'event.on': {
|
|
510
|
+
label: 'Listen for Event',
|
|
511
|
+
desc: 'Register a handler for an event. Auto-unsubscribed on room exit.',
|
|
512
|
+
params: [
|
|
513
|
+
{ name: 'event', label: 'Event name', type: 'text' },
|
|
514
|
+
{ name: 'handler', label: 'Handler fn', type: 'code', placeholder: '(data) => { /* ... */ }' },
|
|
515
|
+
],
|
|
516
|
+
},
|
|
517
|
+
'event.waitFor': {
|
|
518
|
+
label: 'Wait For Event',
|
|
519
|
+
desc: 'Pause flow until a named event fires (returns a Promise).',
|
|
520
|
+
params: [
|
|
521
|
+
{ name: 'event', label: 'Event name', type: 'text' },
|
|
522
|
+
{ name: 'timeout', label: 'Timeout (ms)', type: 'number', default: 0 },
|
|
523
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
524
|
+
],
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
|
|
530
|
+
// ── Logic ──────────────────────────────────────────────────────────────────
|
|
531
|
+
logic: {
|
|
532
|
+
label: 'Logic',
|
|
533
|
+
icon: 'gear',
|
|
534
|
+
color: 'var(--sol-yellow)',
|
|
535
|
+
actions: {
|
|
536
|
+
|
|
537
|
+
'logic.if': {
|
|
538
|
+
label: 'If / Else',
|
|
539
|
+
desc: 'Conditionally run a block of steps.',
|
|
540
|
+
params: [
|
|
541
|
+
{ name: 'condition', label: 'Condition', type: 'code', placeholder: 'inventory.age >= 18' },
|
|
542
|
+
{ name: 'then', label: 'Then room', type: 'room' },
|
|
543
|
+
{ name: 'else', label: 'Else room', type: 'room' },
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
'logic.delay': {
|
|
547
|
+
label: 'Delay',
|
|
548
|
+
desc: 'Pause execution for N milliseconds.',
|
|
549
|
+
params: [
|
|
550
|
+
{ name: 'ms', label: 'Milliseconds', type: 'number', default: 500 },
|
|
551
|
+
],
|
|
552
|
+
},
|
|
553
|
+
'logic.log': {
|
|
554
|
+
label: 'Log to Console',
|
|
555
|
+
desc: 'console.log a message or expression.',
|
|
556
|
+
params: [
|
|
557
|
+
{ name: 'msg', label: 'Message / expr', type: 'code', placeholder: '"Value: " + inventory.myKey' },
|
|
558
|
+
],
|
|
559
|
+
},
|
|
560
|
+
'logic.transform': {
|
|
561
|
+
label: 'Transform Value',
|
|
562
|
+
desc: 'Evaluate a JS expression and store the result.',
|
|
563
|
+
params: [
|
|
564
|
+
{ name: 'expr', label: 'Expression', type: 'code', placeholder: 'inventory.items.length' },
|
|
565
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'itemCount' },
|
|
566
|
+
],
|
|
567
|
+
},
|
|
568
|
+
'logic.random': {
|
|
569
|
+
label: 'Random Number',
|
|
570
|
+
desc: 'Store a random number (integer or float) in inventory.',
|
|
571
|
+
params: [
|
|
572
|
+
{ name: 'min', label: 'Min', type: 'number', default: 0 },
|
|
573
|
+
{ name: 'max', label: 'Max', type: 'number', default: 100 },
|
|
574
|
+
{ name: 'int', label: 'Integer', type: 'boolean', default: true },
|
|
575
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'rng' },
|
|
576
|
+
],
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
// ── Page Builder (render.*) ───────────────────────────────────────────────
|
|
583
|
+
// Creates and appends UI elements to #pw-content at runtime.
|
|
584
|
+
// Use on rooms with template: "blank" to build the page from the Savant.
|
|
585
|
+
// Elements are appended in the order the steps run; call render.clear first
|
|
586
|
+
// on onEnter to rebuild on each visit.
|
|
587
|
+
render: {
|
|
588
|
+
label: 'Page Builder',
|
|
589
|
+
icon: 'layout-text-window',
|
|
590
|
+
color: 'var(--sol-violet)',
|
|
591
|
+
actions: {
|
|
592
|
+
|
|
593
|
+
'render.clear': {
|
|
594
|
+
label: 'Clear Page',
|
|
595
|
+
desc: 'Empty #pw-content. Put this first in onEnter to rebuild on each visit.',
|
|
596
|
+
params: [],
|
|
597
|
+
},
|
|
598
|
+
'render.title': {
|
|
599
|
+
label: 'Page Title',
|
|
600
|
+
desc: 'Append a heading to the page content area.',
|
|
601
|
+
params: [
|
|
602
|
+
{ name: 'text', label: 'Title text', type: 'text', placeholder: 'Sign In' },
|
|
603
|
+
{ name: 'size', label: 'Size', type: 'select', options: ['h1','h2','h3','h4'], default: 'h2' },
|
|
604
|
+
],
|
|
605
|
+
},
|
|
606
|
+
'render.subtitle': {
|
|
607
|
+
label: 'Subtitle',
|
|
608
|
+
desc: 'Append a muted subtitle paragraph.',
|
|
609
|
+
params: [
|
|
610
|
+
{ name: 'text', label: 'Text', type: 'text', placeholder: 'Welcome back. Enter your credentials.' },
|
|
611
|
+
],
|
|
612
|
+
},
|
|
613
|
+
'render.paragraph': {
|
|
614
|
+
label: 'Paragraph',
|
|
615
|
+
desc: 'Append a styled paragraph of text.',
|
|
616
|
+
params: [
|
|
617
|
+
{ name: 'text', label: 'Text', type: 'textarea', placeholder: 'Body copy…' },
|
|
618
|
+
{ name: 'style', label: 'Style', type: 'select', options: ['muted','info','success','warning','danger','body'], default: 'muted' },
|
|
619
|
+
],
|
|
620
|
+
},
|
|
621
|
+
'render.field': {
|
|
622
|
+
label: 'Text Input',
|
|
623
|
+
desc: 'Append a labelled input field. The value is read by name when the form is serialized.',
|
|
624
|
+
params: [
|
|
625
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'email' },
|
|
626
|
+
{ name: 'label', label: 'Label', type: 'text', placeholder: 'Email Address' },
|
|
627
|
+
{ name: 'type', label: 'Input type', type: 'select', options: ['text','email','password','tel','url','number','date','search','textarea'], default: 'text' },
|
|
628
|
+
{ name: 'placeholder', label: 'Placeholder', type: 'text', placeholder: 'you@example.com' },
|
|
629
|
+
{ name: 'autocomplete', label: 'Autocomplete', type: 'text', placeholder: 'email', default: '' },
|
|
630
|
+
{ name: 'required', label: 'Required', type: 'boolean', default: true },
|
|
631
|
+
],
|
|
632
|
+
},
|
|
633
|
+
'render.textarea': {
|
|
634
|
+
label: 'Multiline Text',
|
|
635
|
+
desc: 'Append a multi-line textarea field. Value is collected by name on form serialization.',
|
|
636
|
+
params: [
|
|
637
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'bio' },
|
|
638
|
+
{ name: 'label', label: 'Label', type: 'text', placeholder: 'About you' },
|
|
639
|
+
{ name: 'placeholder', label: 'Placeholder', type: 'text', placeholder: 'Tell us about yourself…' },
|
|
640
|
+
{ name: 'rows', label: 'Rows', type: 'number', default: 4 },
|
|
641
|
+
{ name: 'required', label: 'Required', type: 'boolean', default: false },
|
|
642
|
+
],
|
|
643
|
+
},
|
|
644
|
+
'render.select': {
|
|
645
|
+
label: 'Dropdown',
|
|
646
|
+
desc: 'Append a <select> dropdown. Options are a comma-separated list.',
|
|
647
|
+
params: [
|
|
648
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'country' },
|
|
649
|
+
{ name: 'label', label: 'Label', type: 'text', placeholder: 'Country' },
|
|
650
|
+
{ name: 'options', label: 'Options (CSV)', type: 'text', placeholder: 'USA,Canada,UK' },
|
|
651
|
+
{ name: 'default', label: 'Default', type: 'text', placeholder: 'USA', default: '' },
|
|
652
|
+
{ name: 'required', label: 'Required', type: 'boolean', default: false },
|
|
653
|
+
],
|
|
654
|
+
},
|
|
655
|
+
'render.checkbox': {
|
|
656
|
+
label: 'Checkbox',
|
|
657
|
+
desc: 'Append a Bootstrap checkbox. Value is true/false when serialized.',
|
|
658
|
+
params: [
|
|
659
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'agreeTerms' },
|
|
660
|
+
{ name: 'label', label: 'Label', type: 'text', placeholder: 'I agree to the terms' },
|
|
661
|
+
{ name: 'checked', label: 'Checked by default', type: 'boolean', default: false },
|
|
662
|
+
{ name: 'required', label: 'Required', type: 'boolean', default: false },
|
|
663
|
+
],
|
|
664
|
+
},
|
|
665
|
+
'render.alert': {
|
|
666
|
+
label: 'Alert Box',
|
|
667
|
+
desc: 'Append a hidden Bootstrap alert (show via display.text or form errors).',
|
|
668
|
+
params: [
|
|
669
|
+
{ name: 'id', label: 'Element ID', type: 'text', placeholder: 'login-alert' },
|
|
670
|
+
{ name: 'type', label: 'Type', type: 'select', options: ['danger','warning','info','success'], default: 'danger' },
|
|
671
|
+
{ name: 'text', label: 'Initial text (leave blank to hide)', type: 'text', default: '' },
|
|
672
|
+
],
|
|
673
|
+
},
|
|
674
|
+
'render.button': {
|
|
675
|
+
label: 'Button',
|
|
676
|
+
desc: 'Append a button that navigates to a room (runs onExit first).',
|
|
677
|
+
params: [
|
|
678
|
+
{ name: 'label', label: 'Label', type: 'text', placeholder: 'Sign In' },
|
|
679
|
+
{ name: 'target', label: 'Room', type: 'room', placeholder: 'auth-check' },
|
|
680
|
+
{ name: 'variant', label: 'Style', type: 'select', options: ['primary','secondary','outline-secondary','outline-info','danger','success'], default: 'primary' },
|
|
681
|
+
{ name: 'full', label: 'Full width', type: 'boolean', default: true },
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
'render.link': {
|
|
685
|
+
label: 'Link / Text Link',
|
|
686
|
+
desc: 'Append a small centred text link that navigates to a room.',
|
|
687
|
+
params: [
|
|
688
|
+
{ name: 'text', label: 'Link text', type: 'text', placeholder: 'Forgot password?' },
|
|
689
|
+
{ name: 'target', label: 'Room', type: 'room', placeholder: 'forgot' },
|
|
690
|
+
{ name: 'prefix', label: 'Prefix text (optional)', type: 'text', default: '' },
|
|
691
|
+
],
|
|
692
|
+
},
|
|
693
|
+
'render.section': {
|
|
694
|
+
label: 'Section Header',
|
|
695
|
+
desc: 'Append a small uppercase section divider label.',
|
|
696
|
+
params: [
|
|
697
|
+
{ name: 'title', label: 'Title', type: 'text', placeholder: 'Personal Details' },
|
|
698
|
+
],
|
|
699
|
+
},
|
|
700
|
+
'render.divider': {
|
|
701
|
+
label: 'Card Divider',
|
|
702
|
+
desc: 'Close the current card and start a new one. Breaks the form into distinct visual sections.',
|
|
703
|
+
params: [],
|
|
704
|
+
},
|
|
705
|
+
'render.markdown': {
|
|
706
|
+
label: 'Markdown Block',
|
|
707
|
+
desc: 'Append a rendered Markdown block to the page.',
|
|
708
|
+
params: [
|
|
709
|
+
{ name: 'content', label: 'Markdown', type: 'textarea', placeholder: '**Bold** and _italic_…' },
|
|
710
|
+
],
|
|
711
|
+
},
|
|
712
|
+
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
// ── Display / Print ───────────────────────────────────────────────────────
|
|
717
|
+
// Writes content into existing elements already on the page.
|
|
718
|
+
// Use these to show feedback, labels, results — not to build structure.
|
|
719
|
+
display: {
|
|
720
|
+
label: 'Display',
|
|
721
|
+
icon: 'type',
|
|
722
|
+
color: 'var(--sol-cyan)',
|
|
723
|
+
actions: {
|
|
724
|
+
|
|
725
|
+
'display.text': {
|
|
726
|
+
label: 'Print Text',
|
|
727
|
+
desc: 'Set plain text content inside a selector. Safe — no HTML interpretation.',
|
|
728
|
+
params: [
|
|
729
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#my-label' },
|
|
730
|
+
{ name: 'text', label: 'Text', type: 'code', placeholder: '"Hello " + inventory.firstName' },
|
|
731
|
+
],
|
|
732
|
+
},
|
|
733
|
+
'display.markdown': {
|
|
734
|
+
label: 'Print Markdown',
|
|
735
|
+
desc: 'Render a Markdown string as HTML inside a selector.',
|
|
736
|
+
params: [
|
|
737
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#my-area' },
|
|
738
|
+
{ name: 'content', label: 'Markdown', type: 'textarea', placeholder: '**Bold** and _italic_' },
|
|
739
|
+
],
|
|
740
|
+
},
|
|
741
|
+
'display.safeHtml': {
|
|
742
|
+
label: 'Print Safe HTML',
|
|
743
|
+
desc: 'Sanitize and inject HTML — script/event attributes stripped.',
|
|
744
|
+
params: [
|
|
745
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#my-area' },
|
|
746
|
+
{ name: 'html', label: 'HTML', type: 'textarea', placeholder: '<b>Bold</b>' },
|
|
747
|
+
],
|
|
748
|
+
},
|
|
749
|
+
'display.rawHtml': {
|
|
750
|
+
label: 'Print Raw HTML',
|
|
751
|
+
desc: 'Inject trusted HTML directly (no sanitization). Use only with controlled content.',
|
|
752
|
+
params: [
|
|
753
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#my-area' },
|
|
754
|
+
{ name: 'html', label: 'HTML', type: 'textarea', placeholder: '<b>Trusted HTML</b>' },
|
|
755
|
+
],
|
|
756
|
+
},
|
|
757
|
+
'display.clear': {
|
|
758
|
+
label: 'Clear Content',
|
|
759
|
+
desc: 'Empty the innerHTML of a selector.',
|
|
760
|
+
params: [
|
|
761
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#my-area' },
|
|
762
|
+
],
|
|
763
|
+
},
|
|
764
|
+
'display.value': {
|
|
765
|
+
label: 'Show Inventory Value',
|
|
766
|
+
desc: 'Print an inventory key\'s value as text into a selector.',
|
|
767
|
+
params: [
|
|
768
|
+
{ name: 'selector', label: 'Target selector', type: 'text', placeholder: '#name-label' },
|
|
769
|
+
{ name: 'key', label: 'Inventory key', type: 'text', placeholder: 'firstName' },
|
|
770
|
+
],
|
|
771
|
+
},
|
|
772
|
+
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
|
|
776
|
+
// ── Session / Storage ──────────────────────────────────────────────────────
|
|
777
|
+
session: {
|
|
778
|
+
label: 'Session',
|
|
779
|
+
icon: 'floppy',
|
|
780
|
+
color: 'var(--sol-base1)',
|
|
781
|
+
actions: {
|
|
782
|
+
|
|
783
|
+
'session.save': {
|
|
784
|
+
label: 'Save to Session',
|
|
785
|
+
desc: 'Persist a value to sessionStorage.',
|
|
786
|
+
params: [
|
|
787
|
+
{ name: 'key', label: 'Key', type: 'text' },
|
|
788
|
+
{ name: 'value', label: 'Value', type: 'code' },
|
|
789
|
+
],
|
|
790
|
+
},
|
|
791
|
+
'session.load': {
|
|
792
|
+
label: 'Load from Session',
|
|
793
|
+
desc: 'Read a value from sessionStorage into inventory.',
|
|
794
|
+
params: [
|
|
795
|
+
{ name: 'key', label: 'Key', type: 'text' },
|
|
796
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
797
|
+
],
|
|
798
|
+
},
|
|
799
|
+
'session.local': {
|
|
800
|
+
label: 'Save to localStorage',
|
|
801
|
+
desc: 'Persist a value across browser sessions.',
|
|
802
|
+
params: [
|
|
803
|
+
{ name: 'key', label: 'Key', type: 'text' },
|
|
804
|
+
{ name: 'value', label: 'Value', type: 'code' },
|
|
805
|
+
],
|
|
806
|
+
},
|
|
807
|
+
'session.clear': {
|
|
808
|
+
label: 'Clear Session',
|
|
809
|
+
desc: 'Remove all Undercity keys from sessionStorage.',
|
|
810
|
+
params: [],
|
|
811
|
+
},
|
|
812
|
+
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
|
|
816
|
+
// ── Room ───────────────────────────────────────────────────────────────────
|
|
817
|
+
room: {
|
|
818
|
+
label: 'Room',
|
|
819
|
+
icon: 'broadcast',
|
|
820
|
+
color: 'var(--sol-orange)',
|
|
821
|
+
actions: {
|
|
822
|
+
|
|
823
|
+
'room.emit': {
|
|
824
|
+
label: 'Emit Room Event',
|
|
825
|
+
desc: 'Broadcast a named event in the current room. All listening workflows and Things will receive it.',
|
|
826
|
+
params: [
|
|
827
|
+
{ name: 'event', label: 'Event name', type: 'text', placeholder: 'completedEnterAction' },
|
|
828
|
+
{ name: 'data', label: 'Data (JSON)', type: 'json', default: '{}' },
|
|
829
|
+
],
|
|
830
|
+
},
|
|
831
|
+
|
|
832
|
+
},
|
|
833
|
+
},
|
|
834
|
+
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
838
|
+
|
|
839
|
+
/** Flat list of all actions with their category metadata attached. */
|
|
840
|
+
export function flatActions() {
|
|
841
|
+
const out = [];
|
|
842
|
+
for (const [catId, cat] of Object.entries(ACTION_LIBRARY)) {
|
|
843
|
+
for (const [actionId, def] of Object.entries(cat.actions)) {
|
|
844
|
+
out.push({ catId, actionId, catLabel: cat.label, catColor: cat.color, ...def });
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return out;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/** Find an action definition by actionId. */
|
|
851
|
+
export function findAction(actionId) {
|
|
852
|
+
for (const cat of Object.values(ACTION_LIBRARY)) {
|
|
853
|
+
if (cat.actions[actionId]) return cat.actions[actionId];
|
|
854
|
+
}
|
|
855
|
+
return null;
|
|
856
|
+
}
|