Skip to content

Appearance & Theming

The Hive desktop and Android apps support full theming of the UI chrome, embedded terminals, and both CodeMirror-based editors via CSS custom properties.

Open Customize navigation from the sidebar footer or press F1 and run Customize navigation. The dialog controls the feature destinations shown in both the desktop sidebar and mobile menu:

  • Full shows every feature destination.
  • Simple shows only Chats and Notes.
  • Custom lets you show or hide each feature destination and move visible items up or down. Changing an item while Full or Simple is selected switches the profile to Custom.
  • Comfortable and Compact change the vertical spacing between feature destinations without changing which ones are visible.

The selected profile, custom order, visibility, and density are stored on the current device. Reset restores Full navigation, the default item order, and Comfortable density.

Connection, Settings, Documentation, and Customize navigation remain available as permanent footer escape hatches even when feature destinations are hidden. The footer also keeps Cluster, Logs, and Sessions available. On mobile, the same configured feature list scrolls above a touch-sized footer, so Simple and Custom profiles never hide the controls needed to restore Full navigation.

The F1 command palette also provides Use Simple navigation, Use Full navigation, Customize navigation, and Open Hive documentation. The documentation action opens the public Hive docs and is also available directly from the desktop and mobile menus.

Where to configure

Open Settings → Appearance. The card contains:

  • Preset picker - pick a built-in theme. Each theme is a complete light or dark look; selecting one also sets the app's light/dark mode, so there is no separate light/dark toggle. Built-ins: Default Dark/Light, Solarized Dark/Light, Dracula Dark/Light, Catppuccin Mocha (dark), Catppuccin Latte (light), Nord (dark), Tokyo Night (dark), and Custom.
  • Mode selector - shown only for the Custom theme; chooses whether your custom CSS renders as a light or dark theme.
  • CSS Variables editor - a CodeMirror editor showing the active theme. Read-only while a built-in preset is selected; pick Edit as Custom to fork the active CSS into the Custom slot and start editing.
  • Reset - drops back to the Default theme (matching your OS light/dark preference) and clears any custom CSS.

Edits in Custom mode apply live to the running app and are persisted in the device's localStorage. Each theme is a single :root { ... } block; the Custom theme's Mode selector decides whether it renders light or dark.

OpenRouter API key

The Appearance tab also contains OpenRouter API for the connected node. You can jump directly to it with F1 > Configure OpenRouter API key. The card shows whether a key is configured and whether its source is the node's private config or daemon environment, but never displays or returns the saved key.

Saving a key creates or replaces [openrouter].api_key. Clear stored key removes that config value. An OPENROUTER_API_KEY supplied by the daemon environment remains effective and cannot be cleared from the app. Key changes apply to the next native OpenRouter turn without restarting the daemon. See Native OpenRouter API for setup and runtime details.

Available variables

UI chrome:

VariablePurpose
--background / --foregroundApp body
--card / --card-foregroundCard surfaces
--popover / --popover-foregroundPopovers, dropdowns
--primary / --primary-foregroundPrimary action buttons
--secondary / --secondary-foregroundSecondary surfaces
--muted / --muted-foregroundMuted text and surfaces
--accent / --accent-foregroundHover / selected states
--destructiveDestructive actions
--border / --input / --ringBorders, inputs, focus rings
--sidebar*Sidebar surface, foreground, primary, accent, border, ring

Terminal:

VariablePurpose
--terminal-bg / --terminal-fgTerminal background / default text
--terminal-cursorCursor color
--terminal-selection-bgMouse selection background
--terminal-black / --terminal-bright-blackANSI 0 / 8
--terminal-white / --terminal-bright-whiteANSI 7 / 15
--terminal-font-familyTerminal font stack

ANSI colors 1-6 (red, green, yellow, blue, magenta, cyan) are not driven by CSS variables; they come from a fixed light/dark palette in useTerminalManager.ts.

Editor:

VariablePurpose
--editor-bg / --editor-foregroundEditor canvas and default text
--editor-gutter-bg / --editor-gutter-fgLine-number gutter colors
--editor-gutter-active-fgActive line number
--editor-borderGutter / panel borders
--editor-selection-bgText selection
--editor-active-line-bg / --editor-active-line-gutter-bgActive line highlight
--editor-cursorCaret color
--editor-search-match-*Search result highlight fill / border / selected match
--editor-bracket-match-*Matching bracket highlight fill / border

Example: minimal custom theme

A custom theme is a single :root block. Set the Mode selector to light or dark to tell the app which ANSI palette and dark: utilities to use.

css
:root {
  --background: #1c1b18;
  --foreground: #e6e1d6;
  --primary: #d98a52;
  --editor-bg: #24221f;
  --terminal-bg: #1c1b18;
  --terminal-fg: #e6e1d6;
}

Notes

  • Themes are device-local by default. Use Settings → Sync preferences with cluster → Save to cluster to push the current theme (and every other hive-* localStorage preference) to the connected daemon; any client that later connects to that cluster can pull the same blob with Pull from cluster. Sync is opt-in per action: editing locally never touches the cluster value.
  • The base styles in crates/clusto-app/src/style.css define the defaults; your overrides cascade on top of those, so you only need to declare the variables you want to change.
  • Already-open terminals and editors refresh their colors live when the theme changes; no reopen / reattach needed.

Resizable panels

Every side panel can be resized by dragging the thin handle on its border, the same gesture used between terminal panes. On desktop (md+) only - on phone widths these panels are full-width swappable panes or sheets, so the handle is hidden. Each panel remembers its width across restarts in localStorage:

PanelPageRangeKey
Main navigation (left)all pages180-420 pxhive-nav-width
Right sidebar (Files/Git/Run/Commands)Workspace220-560 pxhive-right-sidebar-width
Notes file tree (left)Notes180-480 pxhive-notes-tree-width
Notes editor split dividerNotes (split mode)20-80%hive-notes-editor-split

The shared drag logic lives in src/composables/useResizablePanel.ts; the handle visual is src/components/common/ResizeHandle.vue. The collapsed main nav keeps its fixed w-12 rail and is not resizable until re-expanded.

Cluster-shared preferences

The daemon persists the synced blob in cluster_preferences.json next to its config.toml and broadcasts updates to its cluster peers via PeerMessage::SyncClusterPreferences. Conflict resolution is last-writer-wins on the whole blob, ordered by a server-side updated_at timestamp - so a slow peer broadcast can never clobber a fresher local write. Wire protocol: ClientMessage::GetClusterPreferences / SetClusterPreferencesServerMessage::ClusterPreferences { preferences }.

Hive - remote AI coding agents over WebSocket.