# Theme `theme/` loads, edits, and saves the theme `.dft` files that color the TUI hosts — the same files the hosts scan at startup and stdext ships under `src/themes/`. Functions work on plain maps, so reading is a deep `get` and editing returns a new map that you rebind. The namespace is available in every host and the scripting binary; pair it with `tui/theme-reload` and `tui/set-theme` for a live edit → save → apply loop in the TUI hosts. ## Functions | Function | Purpose | |---|---| | `theme/load name-or-path` | Stem, display name, or `.dft` path → full theme map; records provenance for `theme/save` | | `theme/current` | The active workspace theme map; `nil` when no host theme is applied | | `theme/get $t path...` | Deep read by keyword path; `nil` when any level is missing | | `theme/set $t path... value` | Deep update returning a new map; missing intermediate levels become empty maps | | `theme/unset $t path...` | Remove the key at a path, returning a new map | | `theme/save $t ?path` | Canonical write; returns the written path, or `nil` on failure | | `theme/list` | Sorted vector of visible theme stems (keywords) | Notes: - Search chain. `theme/load` and `theme/list` look in order, first hit wins per stem: project `.deft/local/themes/`, user home `local/themes/`, the prelude `:themes :dir` (a string or a vector of paths), the stdext mount's `themes/`, and the legacy `./themes/` fallback. - Name forms. `theme/load` accepts a file stem (keyword or string, `:dracula` → `dracula.dft`), a display name matched against the file's `:name` field (`"Dracula"`), or an explicit `.dft` path. `theme/get` and the edit functions coerce string path segments to keywords, so `[theme/get $t "ui" "accent"]` reads the same slot as `[theme/get $t :ui :accent]`. - Edit semantics. `theme/set` and `theme/unset` never mutate the input map — bind the returned map. `theme/set` has assoc-in semantics, so `[theme/set $t :markdown :code-fg "#00ff00"]` creates the `:markdown` section if it does not exist yet. - Save targets. With no explicit path, `theme/save` follows the provenance `theme/load` recorded. A writable origin (project `.deft/local/themes`, user home, the prelude `:themes :dir`, legacy `./themes`) is overwritten in place. A shipped stdext default is never modified: it gets an override shadow written to `<project` .deft>/local/themes/<stem>.dft`. A map built in Deft instead of loaded` saves to that shadow directory under a slug of its `:name` (`"Catppuccin Mocha"` → `catppuccin-mocha.dft`), and errors when `:name` is missing. An explicit `path` argument wins over all of this. - Canonical format. Saved files carry a `# <name>` header, 4-space indented nested maps, sections ordered `name → ui → syntax → help →` markdown` then extras, alphabetically sorted keys, and an atomic` write-then-rename — so a theme round-trips diff-stable. - Current theme. `theme/current` is the full parsed map that the host has applied, identical to `[tui/theme]` and `[draw/theme $ctx]` in TUI hosts; per-package `theme.dft` defaults are merged under their app key. It returns `nil` in the standalone `deft` binary — fall back to `theme/load` plus your own defaults there. It parses per call, so cache the result and re-read after a theme change (stdext's `theme/on-change` wraps the dirty-check pattern). ## Examples
# Load by stem, display name, or explicit path
set t [theme/load :dracula]
set t [theme/load "Dracula"]
set t [theme/load "themes/my-theme.dft"]

# Deep read
[theme/get $t :ui :accent]          # => "#7a64a1"
[theme/get $t :help]                # => the whole :help sub-map
[theme/get $t :ui :nope]            # => nil

# Deep edits return a new map; rebind it
set t [theme/set $t :ui :accent "#ffffff"]
set t [theme/set $t :markdown :code-fg "#00ff00"]   # section auto-created
set t [theme/unset $t :markdown]

# Save, then apply in a running TUI host
[theme/save $t]                     # => ".deft/local/themes/dracula.dft"
[tui/theme-reload]                  # rescan + re-apply; panels repaint
[tui/set-theme "Nord"]              # switch to another loaded theme

# Discover what is installed
[theme/list]                        # => @{ :catppuccin-mocha :dracula ... }
[in? [theme/list] :dracula]         # => true
Outside a TUI host there is no active theme, so scripts fall back to a named one:
if [nil? [theme/current]] {
    set t [theme/load :dracula]
}
## See Also - TUI Overview — how hosts scan and apply themes - Graphics — where theme colors reach `draw/*` - Filesystem — locating and reading theme `.dft` files