diff --git a/frontend/src/lib/components/JsonInputs.svelte b/frontend/src/lib/components/JsonInputs.svelte
index 2f4b04f8e4..546ab36069 100644
--- a/frontend/src/lib/components/JsonInputs.svelte
+++ b/frontend/src/lib/components/JsonInputs.svelte
@@ -13,13 +13,18 @@
* payload nobody has typed into tracks the schema instead of going stale. The first edit
* hands the buffer to the user and later changes stop overwriting it. */
initialCode?: string
+ /** Show the payload without taking edits. For a caller that must keep this editor
+ * mounted while refusing writes: swapping it for the fields instead would mount the
+ * widgets, and a `password` field mints a variable from whatever literal it opens on. */
+ readOnly?: boolean
}
let {
updateOnBlur = true,
placeholder = 'Write a JSON payload. The input schema will be inferred.
Example:
{
"foo": "12"
}',
selected = false,
- initialCode = ''
+ initialCode = '',
+ readOnly = false
}: Props = $props()
let pendingJson = $state(untrack(() => initialCode))
@@ -130,6 +135,7 @@
bind:code={pendingJson}
lang="json"
class="h-full json-inputs-editor"
+ {readOnly}
{placeholder}
/>
diff --git a/frontend/src/lib/components/RunForm.svelte b/frontend/src/lib/components/RunForm.svelte
index cede84f394..c01f7598ca 100644
--- a/frontend/src/lib/components/RunForm.svelte
+++ b/frontend/src/lib/components/RunForm.svelte
@@ -135,10 +135,13 @@
}
| undefined
/** Set to refuse every write this form can make, and to say so when a run is
- * attempted. No edits, no dynamic-select helper, no JSON, and no run — `run()` mints
- * the `password` arguments it is about to send, so refusing in `runAction` is already
- * too late. For a form standing in for a chat card under plan mode, which promised
- * none of it. */
+ * attempted: the fields are disabled, the dynamic-select helper is not wired, the JSON
+ * editor is read-only, and `run()` refuses before it mints the `password` arguments it
+ * would send — so a caller cannot gate this in `runAction`, which comes after that
+ * mint. For a form standing in for a chat card under plan mode, which promised none of
+ * it. The caller must also leave this form on the side it is showing: swapping fields
+ * for JSON or back mounts a `password` widget, which mints from the literal it opens
+ * on regardless of being disabled. */
runRefusal?: string
runAction: (
scheduledForStr: string | undefined,
@@ -349,6 +352,7 @@
>
{
// The other way into the arguments, and the one disabling the fields misses.
if (argsReadonly) return
diff --git a/frontend/src/lib/components/details/FlowDetail.svelte b/frontend/src/lib/components/details/FlowDetail.svelte
index 8612f871f9..b9ca597f6d 100644
--- a/frontend/src/lib/components/details/FlowDetail.svelte
+++ b/frontend/src/lib/components/details/FlowDetail.svelte
@@ -865,6 +865,10 @@
{inputSelected}
/>
+
{#if hasSchema && !pendingRun?.planModeActive}
diff --git a/frontend/src/lib/components/details/ScriptDetail.svelte b/frontend/src/lib/components/details/ScriptDetail.svelte
index acb61567f8..6d463434eb 100644
--- a/frontend/src/lib/components/details/ScriptDetail.svelte
+++ b/frontend/src/lib/components/details/ScriptDetail.svelte
@@ -1059,6 +1059,10 @@
}}
{inputSelected}
/>
+
{#if hasSchema && !pendingRun?.planModeActive}