# 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