# Dialogs (`dialog/*`) The dialog system is a generic, themed alternative to ad-hoc popups. Available in both the TUI hosts (`retro`, `retra`) and the scripting binary (`deft`), with the same stdlib function signatures. ## Kinds | Function | Result value | |-----------|--------------| | `dialog/prompt "Title" ?default` | the entered text | | `dialog/password "Title" ?default` | the entered text (masked in UI) | | `dialog/confirm "Title" ?prompt` | `"yes"` or `"no"` | | `dialog/msgbox "Title" "Message"` | `nil` (dismissed) | | `dialog/menu "Title" @{a b c} ?sel-idx` | the selected item text | | `dialog/checklist "Title" @{a b c} ?checked`| newline-joined selected item texts | | `dialog/search "Title" @{a b c} ?query` | the selected item text (incremental filter) | | `dialog/file-open "Title" ?start-dir` | absolute path | | `dialog/file-save "Title" ?start-dir ?fn` | absolute path | Each stdlib function may also accept a single spec map:
 [dialog/prompt %{ :title "Name" :default "Alice" }]
[dialog/menu   %{ :title "Pick" :items @{a b c} }] 
## Explicit Dialog Ids Every function accepts an explicit id — a **leading keyword** or the spec map's `:id` key — so the result event is identifiable without stashing the returned request id:
 [dialog/confirm :save "Save changes?"]
[dialog/menu    :manual-index %{ :title "Pick" :items @{a b c} }] 
Ids are **per-panel scoped**: the result routes to the requesting panel's own event queue, so two apps may safely reuse the same id (both can use `:save`). Within one app, the same id may not be queued twice concurrently — the second call returns `false`. ## Async Contract — Two Modes ### TUI builds (`retro`, `retra`) The stdlib function returns immediately: `true` when an explicit id was given, otherwise the auto-assigned numeric **request-id** (`false` when the pending queue is full or a duplicate id is queued). Pending requests form a bounded FIFO (8) — one dialog is visible at a time; requests that arrive while one is showing wait their turn. When the user submits, a `:message` event is pushed to the panel's app-thread event queue with topic `"dialog-result"` and a structured `:data` map:
 %{ :type :message :topic "dialog-result"
   :data %{ :id :save            # your keyword (or the numeric request-id)
           :value "yes"          # the encoded result ("" when cancelled)
           :cancelled false } } 
The app's `on-event` handler dispatches on `:data~id`:
 [dialog/confirm :save "Save changes?"]

def on-event {|ev|
    if ($ev~topic == "dialog-result" and $ev~data~id == :save) {
        if $ev~data~cancelled { echo "cancelled" }
        else { echo "got: " $ev~data~value }
    }
} 
Without an explicit id, `:data~id` carries the numeric request-id the call returned — see `packages/editor/src/editor.dft` for the canonical structured-id pattern. ### Scripting binary (`deft`) The stdlib function **blocks synchronously** on stdin, renders the ANSI dialog, and returns the value directly. No event handler needed (ids are ignored there).
 deft> [dialog/prompt "What is your name?"]
Alice
"Alice" 
The script blocks until the user submits; the return value is the entered text. ## Why no `tui/popup` / `tui/file-open`? Those stdlib functions were referenced in old docstrings but never registered. The unified `dialog/*` namespace supersedes them. The legacy `tui/file-save` (which only ever opened the workspace-level Ctrl+Shift+S dialog) is unaffected. ## Spec Map Form All stdlib functions accept a single spec map in place of positional args. Supported keys: | Key | Used by | |---|---| | `:id` | All dialogs (explicit result-routing id) | | `:title` | All dialogs (defaults to "Input", "Password", "Confirm", ...) | | `:prompt` | `confirm` (sub-text under the title) | | `:default` | `prompt`, `password` (default text) | | `:items` | `menu`, `checklist` (list of item strings) | | `:selected` | `menu` (initial selection index) | | `:checked` | `checklist` (initial set of selected indices) | | `:start-dir` | `file-open`, `file-save` (initial directory; default `.`) | | `:filename` | `file-save` (suggested filename) |
 [dialog/file-open %{ :id :open :title "Open File" :start-dir "." }]
[dialog/file-save %{ :title "Save As"   :start-dir "." :filename $default-name }] 
## Examples ### Confirm before destructive action (TUI)
 def cmd-delete {|path|
    @doc "Delete a file after confirming."
    set pending-delete $path
    [dialog/confirm :delete [str "Delete " $path "?"] "This cannot be undone."]
    # returns true immediately; result handled in on-event
}

def on-event {|ev|
    if ($ev~topic == "dialog-result" and $ev~data~id == :delete) {
        if !$ev~data~cancelled {
            fs/remove $pending-delete
            echo "deleted"
        }
    }
} 
### Menu selection (script)
 def pick [dialog/menu "Pick a colour" @{red green blue}]
echo "You picked: $pick" 
### File open (script)
 def path [dialog/file-open "Open file" "."]
if not(empty? $path) {
    echo "Selected: $path"
} 
## Backend Notes - **TUI build** — the dialog opens as a modal overlay centered on the calling panel, themed with the workspace's current theme. - **Script build** — the dialog renders inline using ANSI control sequences. A C-bridged render function is wired at startup; the result is returned synchronously when the user submits. - **Shared spec type** — `src/host/dialog_types.zig` (open deft repo) defines the dialog kinds and option structs used by both backends and by the MCP debug plugin. ## Defaults When no title is provided, the backend picks one: "Input", "Password", "Confirm", "Message", "Select", "Open File", "Save As". File dialogs default `:start-dir` to `"."`. ## Cancellation A cancelled dialog returns: - **`dialog/prompt` / `dialog/password`** — empty string `""`. - **`dialog/confirm`** — `"no"`. - **`dialog/menu` / `dialog/checklist`** — empty string `""`. - **`dialog/file-open` / `dialog/file-save`** — empty string `""`. - **`dialog/msgbox`** — `nil` (always; no cancel notion). In TUI builds, the result arrives as `:data` of the `:message` event with `:cancelled true` and `:value ""`. ## Quick Reference | Form | Result | |---|---| | `[dialog/prompt "Title" ?default]` | text | | `[dialog/password "Title" ?default]` | text (masked) | | `[dialog/confirm "Title" ?prompt]` | `"yes"` or `"no"` | | `[dialog/msgbox "Title" "Message"]` | `nil` | | `[dialog/menu "Title" $items ?sel]` | selected text | | `[dialog/checklist "Title" $items ?checked]` | newline-joined texts | | `[dialog/file-open "Title" ?dir]` | path | | `[dialog/file-save "Title" ?dir ?fn]` | path | | `[dialog/ :my-id …]` | `true` (TUI); result via `:data~id :my-id` | | Spec map form (`:id` key included) | same |