Themes
Choose built-in Auto, Light or Dark terminal appearance; theme-file customization is disabled.
octet's compiled default theme retains model-family accents, terminal-background adaptation, and semantic status colours. Auto, Dark, and Light share the same default geometry: 16×4 model-blended startup mark, compact per-line historical prompt highlights preserving inline Markdown styles, model-coloured composer rules, one blinking tool-like subagent transcript row while workers run, two-row command previews, and full available prose width without narrowing code or tables. The footer may omit a redundant catalogue-owned provider prefix, never a configured model name. These are compiled presentation policies, not new theme-file fields: custom surfaces, composer frames, and loaded theme colours/geometry keep their own styling. Unknown-background and no-colour terminals retain an unpainted readable prompt.
The built-in theme picker offers:
Auto (recommended)detects the terminal background through reliable environment or terminal capability signals and uses a readable neutral fallback when detection is unavailable.Light terminalandDark terminalexplicitly select the corresponding contrast profile and override detection.
Moving through the picker previews each appearance without saving it. Confirming
persists theme = "auto", theme = "light", or theme = "dark" in the
user config without replacing unrelated settings.
Use /theme later to revisit it. Cancelling
/theme restores the previous appearance; dismissing first-run onboarding uses
Auto. Existing configured installations do not reopen onboarding, and
print/plain/RPC, redirected, and TERM=dumb sessions never open it.
The built-in choices also work with --theme and OCTET_THEME. Other theme
names, --theme-dir, and arbitrary theme files are accepted only through the
bounded file loader: a name that resolves through normal resource discovery
(global ~/.octet/themes, a trusted project .octet/themes, or --theme-dir)
is loaded at startup when named by --theme/OCTET_THEME, while the
interactive /theme command accepts only auto, light, and dark.
There is no theme marketplace, and an unrecognized or malformed name falls back
to the compiled default. An explicit
OCTET_COLOR_SCHEME is also treated as an existing terminal-appearance choice,
so automation and already-configured shells do not get interrupted by
onboarding.
Activity status contrast#
Thinking and Working use model-family foreground colours only; the shimmer
never paints a character background. On known Dark/Light TrueColor or ANSI256 profiles,
the default physical shimmer uses a raised-cosine light field: it has a short
leading edge, a longer trailing tail, and a small central glint. Dark labels move
from about 0.55 toward 0.99 luminance; light labels move from about 0.0085
toward 0.11. The motion completes in roughly 0.85–1.0 seconds and then parks
for two ticks before repeating. The margin dot shares that phase without changing
its glyph or size.
Set OCTET_SHIMMER=classic to use the legacy stepped shimmer for A/B testing.
Classic is also forced for ANSI16, unknown-background, no-colour, and reduced-motion
profiles. An unrecognized variable value uses the physical default when the
terminal profile supports it. The qualification fixture checks representative
Dark/Light composited surfaces; it does not measure arbitrary transparency.
Custom ANSI16 palettes can also change perceived contrast.
Startup and terminal replies#
The first branded frame waits for resolved model/setup state, workspace, and appearance; the welcome animation starts at that boundary. SSH alone does not reduce the advertised terminal color capability.
Auto can issue one OSC 11 background query and use a neutral fallback after its short detection deadline. The shared input owner continues recognizing a late reply after that deadline, including slowly fragmented bodies once the OSC 11 header is recognized. Genuine typing and bracketed paste are retained.
Escape and Alt+] remain genuine keys: an incomplete opening header has a 250 ms
ambiguity timeout. A header fragmented more slowly can still pass through as
input. Use explicit --theme dark or --theme light to skip the query on such
terminals. This is not a guarantee for arbitrary terminal-protocol corruption.
Variant reference#
octet ships a complete reference file for the compiled default theme at
examples/themes/octet-default.toml.
It is not read at runtime — the compiled default is always the fallback — but it
documents the accepted sections in one place and is a valid starting point:
cp examples/themes/octet-default.toml ~/.octet/themes/mine.tomlA theme file is a bounded TOML document (256 KiB) with these typed sections:
[metadata]—name,description,author,version,terminal(light-dark,dark,light, orany), and optionaladaptiveto rebalance RGB foregrounds and surfaces for the detected terminal background.[colors]/[tokens]— flat or nested colour/token values."default"means the terminal's own colour.[roles.<name>]— per-roleforeground,background,bold,dim,italic,underline,strikethrough,inverse, andadaptive.[glyphs]/[glyphs_ascii]— typed glyphs; every structural glyph is one column and ASCII fallbacks must be ASCII.[surfaces.<kind>]— bounded layout recipes for theuser,assistant,reasoning,tool,notice,outcome,shell, andcompactiontranscript surfaces.[layout]— density, shell visibility, and narrow-terminal overrides.[variants.*]— background overlays described below.
[variants.universal], [variants.dark], and [variants.light] merge
recursively over the base document using the same section shapes. universal
applies to every terminal; dark/light then overlay it for the detected
background profile, so one variant may override a single token without
restating the whole table. [variants.unknown] applies when detection fails.
Theme files reject terminal control bytes, unknown sections and fields, invalid role names, non-ASCII ASCII-fallbacks, wide or empty structural glyphs, and oversized values. Unknown or partial files fall back to the compiled default rather than starting with a broken shell.
Semantic role vocabulary#
[roles.<name>] is typed. These names are the published, terminal-independent
vocabulary that maps onto octet's semantic text roles; the
published_semantic_role_vocabulary_is_closed_and_accepted test in
crates/octet-coding-agent/src/tui/theme.rs keeps this list and
SEMANTIC_ROLE_VOCABULARY in sync:
text, foreground, muted, subtle, dim, accent, success, warning,
error, heading, md_heading, emphasis, md_emphasis, strong,
md_strong, inline_code, md_code, code, md_code_block, quote,
md_quote, border, link, md_link, list_marker, md_list_bullet,
diff_add, diff_added, diff_remove, diff_removed, diff_context,
diff_hunk, diff_header, syntax_comment, syntax_keyword,
syntax_function, syntax_variable, syntax_string, syntax_number,
syntax_type, syntax_operator, syntax_punctuation.
Alias spellings (foreground/text, dim/subtle, code/md_code_block,
diff_add/diff_added, and so on) resolve to the same underlying role.
Extension-contributed themes and roles#
Two channel types are open to extensions:
- Roles. Any name of the form
extension.<namespace>.<role>is accepted in[roles](up to 96 bytes, ASCII alphanumerics plus_,-, and.). The schema is open but typed: an extension may add[roles."extension.git.branch"]without a host change, but it cannot inject the same name under a private, unnamespaced key. This is tested bysemantic_extension_roles_are_open_but_typedintheme_schema.rs. - Theme files. An extension package may ship
themes/<name>.toml. Install or copy it into a discovery root so the shared resolver can select it:~/.octet/themes/(global),.octet/themes/(project, requires trust), or a directory passed with--theme-dir. Discovered files use the same bounded, no-follow reader as the compiled default and never execute extension code.
A manifest-level contributes.themes channel that would register an
extension's own directory as a theme root is not implemented; it requires a
change in the extension manifest schema and discovery (outside the theme
module). Until then, publishing a theme file into a discovery root is the
supported contribution path.