prompts
Adds prompts: steps that ask the person a question and return the answer, implemented in an extension.
Details #
A prompt is a step that stops the macro and asks the person something. Your handler does not draw anything. It returns a description of the question, and Switchboard shows its own panel, waits, and gives the answer to the macro as the step's output.
You declare it under adds.prompts and implement it with prompt(id, handler). The step is <extension id>.<prompt id> and shows in the add-step picker under the extension's name.
{
"adds": {
"prompts": [{
"id": "pick-one", "title": "Pick from a list",
"params": { "type": "object", "properties": { "choices": { "type": "string" } }, "required": ["choices"] },
"output": { "type": "string" }
}]
}
}
import { defineExtension, prompt } from '@switchboard/sdk';
export default defineExtension({
prompts: [
prompt('pick-one', (p: { choices: string }) => ({
kind: 'list',
title: 'Which one?',
items: p.choices.split(',').map((s) => s.trim()),
})),
],
});
The declaration and the code must match. An extension that declares a prompt it does not implement, or implements one it does not declare, is refused when it loads.
Gotchas #
If the person cancels, the macro stops at the step. It is not an error.
In a dry run nothing is shown. The step answers with the request's
default(an empty string when there is none), and the run log says so.Return the request quickly. The handler's time limit applies to building the question, not to waiting for the person.
Return a valid request. A handler that returns something with an unknown
kindmakes the step fail with "returned something that is not a prompt".Unlike an action, a prompt has no
intoparam. The answer is the step's output.
What the handler returns #
The handler gets the step's params and returns the request:
| Field | Used by | Meaning |
|---|---|---|
kind | all | text, list, form or panel. Default text. Anything else is an error. |
title | all | The question or heading. |
message | all | Smaller text under the title. |
default | text, list | The text to start with, or the item chosen to begin with. |
items | list | The strings to choose from. |
fields | form | A list of {name, label, default}. name is the key in the answer, label is what people read. |
buttons | form | Button names. The first is the default. Cancel is always there. |
The step's output depends on the kind:
text: the typed string.
list: the chosen item.
form: an object with one key per field name.
Custom panels #
A prompt that needs more than a line, a list or a form returns kind: "panel" and a body: the views of Show a panel, drawn natively. The SDK has builders (vstack, hstack, text, image, button, field, toggle, slider, picker, progress, table, chart, divider, spacer) that only produce that JSON; nothing in an extension runs inside the panel.
import { defineExtension, prompt, vstack, text, picker, button } from '@switchboard/sdk';
export default defineExtension({
prompts: [
prompt('choose-env', () => ({
kind: 'panel',
id: 'env',
title: 'Deploy',
body: vstack([
text('Where to?', { style: 'title' }),
picker('target', ['staging', 'production'], { style: 'segmented', value: 'staging' }),
button('Deploy', { role: 'default' }),
], { gap: 10 }),
})),
],
});
The request takes title, body, buttons, id, wait, width, position and keepOpen as the step does, and is checked by the same validator. The answer is {id, button, values}. A panel with wait: false returns at once, and the extension changes it later with ctx.panel(id): update(set, {force}), read() and close(result). An extension reaches only its own panels, and needs no permission for them.
Full example #
The sample below has three prompts, one for each kind: a line of text, a pick from a list and a small form. You can download it from the Full example section and edit it into your own.