Skip to content

Appearance & Theming ​

The Clusto 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 keeps the core Terminals, Chats, and Notes destinations visible while hiding the other optional feature destinations.
  • 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 Clusto documentation. The documentation action opens the public Clusto docs and is also available directly from the desktop and mobile menus.

Command palette shortcuts ​

Press F1 to search and run actions from anywhere in Clusto. Every action has an editable shortcut badge on the right. The badge shows the current binding, or Not set when the action has no shortcut.

Click or tap a badge, then press the key or key combination you want to assign. Press Esc to cancel recording. The saved shortcut appears immediately and runs the same action even when that action had no default binding.

Customized actions show a separate Reset to default button. Reset restores the built-in shortcut, or returns the badge to Not set when the action has no built-in binding. Shortcut and reset controls remain visible and touch-sized, and can also be reached with the keyboard.

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.

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 / SetClusterPreferences → ServerMessage::ClusterPreferences { preferences }.

Clusto - remote AI coding agents over WebSocket.