Sidebar & Layout
Sidebar navigation, conversation list, TitleBar, AwayScreen, and SpeakerPicker — the chrome components that wrap the main content area.
Overview
The app shell is composed of four layout-level components that sit outside the main content panel:
| Component | File | Responsibility |
|---|---|---|
Sidebar | Layout/Sidebar.tsx | Nav, conversation list, mood indicator, clock. |
TitleBar | Layout/TitleBar.tsx | Electron drag region + window controls (min/max/close). |
AwayScreen | Layout/AwayScreen.tsx | Overlay shown when the user is marked away. |
SpeakerPicker | Layout/SpeakerPicker.tsx | Audio output device selector. |
Sidebar
Fixed 240 px left panel. Contains the Luna logo, navigation links, conversation history list, current mood emoji, live clock, and the online/offline status indicator for the Ollama backend.
import { Sidebar } from './components/Layout/Sidebar'
// Rendered once at the app root, always visible
<Sidebar />Navigation items
Each nav item maps a View identifier to an icon and a label. Clicking calls setView(id) on the Zustand store.
| View ID | Label | Icon |
|---|---|---|
chat | Chat | MessageCircle |
memory | Memory | Brain |
calendar | Calendar | Calendar |
activities | Activities | Activity |
agent | Agent | ShieldCheck |
sleep | Sleep | Moon |
train | Train | Dna |
extract | Extract | FlaskConical |
settings | Settings | Settings |
Active view is highlighted with a left border accent and a dimmed background.
Conversation list
Below the nav links, the sidebar shows the conversation history loaded fromGET /api/conversations. Each item shows the conversation title and a relative timestamp.
Actions:
- Click — loads the conversation into the chat view.
- Trash icon (hover to reveal) — deletes the conversation after confirmation.
- + button (at the top of the list) — starts a new conversation.
Mood display
The bottom of the sidebar shows Luna's current mood as an emoji badge, loaded from the personality field of the Zustand store. The store is populated by the chat hook from SSE personality update events.
{
happy: "😊",
playful: "😄",
thoughtful: "🤔",
excited: "✨",
concerned: "💙",
warm: "🌙",
neutral: "😌",
curious: "🔍",
melancholic:"🌧",
}Defaults to the neutral emoji if personality data hasn't loaded yet.
TitleBar
A 32 px draggable region at the top of the window. Contains the app name and, on the right, traffic-light-style window controls (minimize, maximize, close) implemented via window.electronAPI.
import { TitleBar } from './components/Layout/TitleBar'
// Rendered at the very top of the app root, above Sidebar + content
<TitleBar />AwayScreen
An animated overlay that appears over the entire app when the user is marked away (isAway === true in the store). Shows the current time and a dismissal hint. Clicking anywhere clears the away state.
Away state is set by:
- The command parser emitting an
[AWAY]command (farewell detection). - The scheduler's
state_aware_proactive()function. - Programmatic calls to
POST /api/away.
import { AwayScreen } from './components/Layout/AwayScreen'
// Conditionally mounted at the app root
{isAway && <AwayScreen />}SpeakerPicker
A floating panel that lists available audio output devices and lets the user switch the default speaker. Each device is shown with its name; clicking one calls the switch_audio tool via POST /api/tools/execute.
Appears as a popover triggered from the audio icon in the chat header.
import { SpeakerPicker } from './components/Layout/SpeakerPicker'
// Rendered as an absolute-positioned popover
{showSpeakerPicker && <SpeakerPicker onClose={() => setShowSpeakerPicker(false)} />}