Portable UI schema: confirmPhrase, open-URL action, file input, breadcrumbs — types, validation, web renderer #34

Closed
opened 2026-08-11 23:40:48 +02:00 by julian · 0 comments
Owner

Core half of OpsDeck/mobile#1. The schema contract lives here, so this lands first — the Android renderer (mobile#1) and module adoption (OpsDeck/module_filebrowser#1) build on it.

Each addition touches three core surfaces:

  1. Typespackages/sdk/ui.ts, the contract modules compile against.
  2. Validationpackages/server/src/modules/ui-schema.ts. The zod discriminated union over literal block types means a module declaring an unknown block or input kind fails validation, and an invalid schema disables the module at load. Additive fields fare no better: zod strips unknown keys, so a confirmPhrase shipped before validation knows it would be silently removed from the served schema and no client would ever see it.
  3. Web schema rendererpackages/shell/src/schema/SchemaPage.svelte + blocks/*.svelte. The schema is also the web UI for schema-only modules, so the web renderer needs the same four or parity breaks in the other direction.

The four additions (priority order, rationale in mobile#1)

  1. confirmPhrase?: string on UiAction beside confirm (sdk/ui.ts:293-294). Client shows a text field and enables the confirming button only on exact match — the schema equivalent of ConfirmDialog.confirmPhrase from #31. Web renderer: blocks/ActionsBlock.svelte can pass it straight to ConfirmDialog. Degradation note: a client that ignores the field falls back to one-tap confirm, so dangerous actions should keep their confirm text scary regardless.
  2. An action kind that opens a URL — module-relative, resolved against the module's authenticated routes. Web renders an anchor (download-friendly); Android hands it to the download manager. Unlocks file downloads for filebrowser (README: "Mobile cannot download or upload bytes").
  3. file as a UiInputKind (sdk/ui.ts:324-330, currently text/number/password/toggle/select/chips). The upload half. Bigger lift: form submit today posts JSON, so this needs a defined wire shape for binary bodies (multipart or streamed PUT) plus progress reporting.
  4. Breadcrumbs / parent navigation — either a block (segments + page links, the schema sibling of #31's Breadcrumbs) or a page-level parent affordance. Kills filebrowser's up/:p redirect-page workaround (module_filebrowser backend/ui.ts:175-200).

Sequencing

  • All four are additive within schema version: 1 — optional fields and new members of existing unions/enums. No version bump needed if old clients tolerate unknowns (mobile's renderer has a graceful unknown-block fallback; verify the web renderer does too before adding a new block type).
  • Order: this issue → mobile renderer (OpsDeck/mobile#1) → module wiring (OpsDeck/module_filebrowser#1 covers the module-side items that need no schema change).
  • Update docs/modules.md block-type and input-kind lists alongside — the doc was just rewritten in #32 and currently matches sdk/ui.ts exactly.
Core half of OpsDeck/mobile#1. The schema contract lives here, so this lands first — the Android renderer (mobile#1) and module adoption (OpsDeck/module_filebrowser#1) build on it. Each addition touches three core surfaces: 1. **Types** — `packages/sdk/ui.ts`, the contract modules compile against. 2. **Validation** — `packages/server/src/modules/ui-schema.ts`. The zod discriminated union over literal block types means a module declaring an unknown block or input kind fails validation, and an invalid schema disables the module at load. Additive fields fare no better: zod strips unknown keys, so a `confirmPhrase` shipped before validation knows it would be silently removed from the served schema and no client would ever see it. 3. **Web schema renderer** — `packages/shell/src/schema/SchemaPage.svelte` + `blocks/*.svelte`. The schema is also the web UI for schema-only modules, so the web renderer needs the same four or parity breaks in the other direction. ## The four additions (priority order, rationale in mobile#1) 1. **`confirmPhrase?: string` on `UiAction`** beside `confirm` (`sdk/ui.ts:293-294`). Client shows a text field and enables the confirming button only on exact match — the schema equivalent of `ConfirmDialog.confirmPhrase` from #31. Web renderer: `blocks/ActionsBlock.svelte` can pass it straight to `ConfirmDialog`. Degradation note: a client that ignores the field falls back to one-tap confirm, so dangerous actions should keep their `confirm` text scary regardless. 2. **An action kind that opens a URL** — module-relative, resolved against the module's authenticated routes. Web renders an anchor (download-friendly); Android hands it to the download manager. Unlocks file downloads for filebrowser (README: "Mobile cannot download or upload bytes"). 3. **`file` as a `UiInputKind`** (`sdk/ui.ts:324-330`, currently text/number/password/toggle/select/chips). The upload half. Bigger lift: form submit today posts JSON, so this needs a defined wire shape for binary bodies (multipart or streamed PUT) plus progress reporting. 4. **Breadcrumbs / parent navigation** — either a block (segments + page links, the schema sibling of #31's `Breadcrumbs`) or a page-level parent affordance. Kills filebrowser's `up/:p` redirect-page workaround (`module_filebrowser backend/ui.ts:175-200`). ## Sequencing - All four are additive within schema `version: 1` — optional fields and new members of existing unions/enums. No version bump needed if old clients tolerate unknowns (mobile's renderer has a graceful unknown-block fallback; verify the web renderer does too before adding a new block type). - Order: this issue → mobile renderer (OpsDeck/mobile#1) → module wiring (OpsDeck/module_filebrowser#1 covers the module-side items that need no schema change). - Update `docs/modules.md` block-type and input-kind lists alongside — the doc was just rewritten in #32 and currently matches `sdk/ui.ts` exactly.
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
OpsDeck/core#34
No description provided.