Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lat.md/chat-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The handler reads the ref, applies a delta, writes the ref back, then calls `set

Streamed reasoning and tool calls are folded into compact, collapsible transcript rows rather than stacked bubbles, so a turn with heavy thinking or many tool calls stays scannable.

[[src/renderer/src/screens/Chat/HistoryRow.tsx#ReasoningRow]] renders the `Thought` / `Thinking…` row and [[src/renderer/src/screens/Chat/HistoryRow.tsx#ToolActivityGroup]] folds a contiguous run of tool calls/results into one row titled by [[src/renderer/src/screens/Chat/HistoryRow.tsx#toolActivityGroupTitle]]. Each row is collapsed by default and borderless (Codex-style): dim at rest, it brightens and reveals an expand chevron beside the title on hover/focus, and clicking toggles the body open. While the turn is still streaming the leading icon is a `Grid` loader (purple for reasoning, blue for tools); once finished it shows the brain/tool glyph.
[[src/renderer/src/screens/Chat/HistoryRow.tsx#ReasoningRow]] renders the `Thought` / `Thinking…` row and [[src/renderer/src/screens/Chat/HistoryRow.tsx#ToolActivityGroup]] folds a contiguous run of tool calls/results into one row titled by [[src/renderer/src/screens/Chat/HistoryRow.tsx#toolActivityGroupTitle]]. Each row is collapsed by default and borderless (Codex-style): dim at rest, it brightens and reveals an expand chevron beside the title on hover/focus, and clicking toggles the body open. While the turn is still streaming the leading icon is a thinking-orbs [[loading-indicators|OrbLoader]] (`solving` for reasoning, `working` for tools); once finished it shows the brain/tool glyph.

### Reasoning reconciliation

Expand Down
1 change: 1 addition & 0 deletions lat.md/lat.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This directory defines the high-level concepts, business logic, and architecture
- [[reasoning-effort]] — the composer's Faster⟷Smarter effort control: a draggable `role="slider"` over six ordered levels that stays open until dismissed and commits one `onChange` per real change.
- [[web-preview]] — the in-app split-screen webview and the `partition`-based gate that lets only it load remote HTTPS while staying sandboxed.
- [[code-blocks]] — collapsible long code blocks, and why expansion state is keyed on source position to survive react-markdown's streaming remounts.
- [[loading-indicators]] — the thinking-orbs dotted-orb loaders behind every loading state, and the OrbLoader wrapper that pins dark/light from the Hermes theme registry instead of the library's auto-detection.
- [[window-chrome]] — the browser-style title bar where open-conversation tabs sit on top of the window drag region, clickable while empty space still drags.
- [[desktop-updates]] — GitHub release checks, startup upgrade button behavior, and the Settings auto-upgrade preference.
- [[sidebar-navigation]] — the recent-sessions list under the Chat nav item, capped at five with a "Show more" button that opens the full session list in a modal.
Expand Down
29 changes: 29 additions & 0 deletions lat.md/loading-indicators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Loading Indicators

All renderer loading states use the [thinking-orbs](https://www.npmjs.com/package/thinking-orbs) dotted-orb canvas animations through one theme-aware wrapper, replacing the old `react-loader-spinner` Grid and the CSS `.loading-spinner` circle.

## OrbLoader wrapper

[[src/renderer/src/components/OrbLoader.tsx#OrbLoader]] pins the orb theme from Hermes' own theme registry instead of the library's auto-detection.

The library's `auto` mode only recognises `data-theme="dark|light"` (or `.dark`/`.light` classes) and otherwise falls back to `prefers-color-scheme`. Hermes writes theme **ids** ("dracula", "nord", …) to `data-theme`, so auto-detection would follow the OS instead of the picked theme (e.g. light OS + Dracula theme would render dark ink on a dark background). The wrapper reads the resolved theme id from [[src/renderer/src/components/ThemeProvider.tsx#ThemeProvider]] and maps it to `dark`/`light` via each `ThemeDef.appearance` in `THEMES` ([[src/renderer/src/constants.ts]]).

The wrapper also accepts **any numeric `size`**, not just the two shipped presets. ThinkingOrb's `resolvePreset` throws on any size other than 20 or 64, but callers still need arbitrary pixel footprints (e.g. a 30px chat avatar). So `OrbLoader` snaps the *design* (dot count / tuning) to the nearest preset around `PRESET_MIDPOINT` (42) while setting the *visual* footprint from the requested number via `style` — an explicit `style` from the caller still wins. Any number is therefore safe and never a runtime throw.

An `invert` prop flips the ink relative to the app theme (light-theme ink while the app is dark, and vice versa). Use it when the orb sits on an inverted surface — a light circle on a dark page — so the ink contrasts the circle rather than the page.

## Usage conventions

The library ships two tuned size *designs* (separate tunings, not a scale factor): `20` inline with text, `64` for pane/screen-level loading. `OrbLoader` picks the design from the nearest of those two; the number given is the rendered footprint.

- Chat reasoning summary ("Thinking…"): `state="solving"`, size 20 — [[src/renderer/src/screens/Chat/HistoryRow.tsx]].
- Chat tool-call summary and slash-command pending bubble: `state="working"`, size 20 — [[src/renderer/src/screens/Chat/HistoryRow.tsx]], [[src/renderer/src/screens/Chat/MessageRow.tsx]].
- Screen/pane data fetches (Discover, Memory, Tools, Schedules, Agents, Sessions, Skills, Soul, Kanban, registry browser, profile modal panes): `state="searching"`, size 64 (size 20 for the inline Kanban detail loader).
- Streaming conversation tab chip: `state="composing"`, size 20, in the avatar slot — [[src/renderer/src/screens/Layout/ActiveSessionsBar.tsx]] (see [[window-chrome]]).
- Generating agent turn avatar: `state="solving"` (nearest preset → the 64 design), `invert`ed on a `--text-primary` disc, scaled to the ~30px avatar footprint via `style={{ width, height }}` — [[src/renderer/src/screens/Chat/MessageRow.tsx#HermesAvatar]].

This replaced the former `loadingo.gif` avatar animation — the orb needs no loop-boundary stop dance (a canvas frame never freezes mid-loop the way the gif did), so `HermesAvatar` swaps straight to the agent's [[src/renderer/src/components/common/ProfileAvatar.tsx]] the instant `active` goes false, and falls back to the orb when no agent identity is known (the live typing indicator).

Because the orb ink is monochrome and can wash out against the near-black page, the loading avatar carries a `.chat-avatar-orb` class that backs it with an **inverted circle** — `--text-primary`, which is light on dark themes and dark on light themes — and passes `invert` to [[src/renderer/src/components/OrbLoader.tsx#OrbLoader]] so the ink flips to match: a dark orb on a white disc under a dark theme, a light orb on a dark disc under a light theme. The canvas is inset slightly so the dots sit inside the circle instead of clipping at its edge.

Tests mock `thinking-orbs` in [[src/renderer/src/test/setup.ts]] because its canvas + IntersectionObserver rendering has no jsdom equivalent.
2 changes: 1 addition & 1 deletion lat.md/sidebar-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Opening a sidebar session after switching profiles consumes that blank selected-

The profile chip preserves the old app-brand label for an unrenamed default profile: when `listProfiles` returns the fallback `name === id === "default"`, the chip shows `common.appName`; once a custom name is stored, it shows that user-facing name.

The same per-profile appearance also drives the agent avatar inside the transcript. [[src/renderer/src/screens/Layout/Layout.tsx#Layout]] passes `getAppearance(run.profile)` to each [[src/renderer/src/screens/Chat/Chat.tsx]] as `agentAppearance`, which forwards `{ name, color, avatar }` through [[src/renderer/src/screens/Chat/MessageList.tsx]] to every [[src/renderer/src/screens/Chat/MessageRow.tsx#HermesAvatar]] (and the reasoning/tool-activity rows in [[src/renderer/src/screens/Chat/HistoryRow.tsx]]). `HermesAvatar` plays the looping `loadingo.gif` only while a turn is generating (`active`); once generation stops it runs out the current gif loop, then swaps to the agent's [[src/renderer/src/components/common/ProfileAvatar.tsx]] so idle turns are identified by who produced them. The live typing indicator has no resolved agent yet, so it falls back to the gif.
The same per-profile appearance also drives the agent avatar inside the transcript. [[src/renderer/src/screens/Layout/Layout.tsx#Layout]] passes `getAppearance(run.profile)` to each [[src/renderer/src/screens/Chat/Chat.tsx]] as `agentAppearance`, which forwards `{ name, color, avatar }` through [[src/renderer/src/screens/Chat/MessageList.tsx]] to every [[src/renderer/src/screens/Chat/MessageRow.tsx#HermesAvatar]] (and the reasoning/tool-activity rows in [[src/renderer/src/screens/Chat/HistoryRow.tsx]]). `HermesAvatar` shows the animated thinking-orb ([[loading-indicators]]) only while a turn is generating (`active`); once generation stops it swaps straight to the agent's [[src/renderer/src/components/common/ProfileAvatar.tsx]] so idle turns are identified by who produced them. The live typing indicator has no resolved agent yet, so it falls back to the orb.

### SSH tunnel profile routing

Expand Down
2 changes: 1 addition & 1 deletion lat.md/window-chrome.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Visually the strip is a Safari-style tab bar: the strip uses the darker `--bg-se

The bar always renders so it is always a drag area, but chips stay hidden only while the sole conversation is still a blank scratch chat.

Chips show when more than one run is open, any run is loading, or any run has a session id/title (`showChips` in [[src/renderer/src/screens/Layout/ActiveSessionsBar.tsx#ActiveSessionsBar]]). When chips show, a browser-style new-tab **"+"** button (`.active-session-new`, `no-drag`) trails them and calls `onNew` → `handleNewChat` in [[src/renderer/src/screens/Layout/Layout.tsx]] to open a fresh conversation.
Chips show when more than one run is open, any run is loading, or any run has a session id/title (`showChips` in [[src/renderer/src/screens/Layout/ActiveSessionsBar.tsx#ActiveSessionsBar]]). A loading chip renders a thinking-orbs [[loading-indicators|OrbLoader]] (`composing`, size 20) in place of its profile avatar — the orb's canvas is transparent and theme-aware, so `.active-session-chip-orb` drops the colour-filled avatar circle rather than painting the profile colour behind it. When chips show, a browser-style new-tab **"+"** button (`.active-session-new`, `no-drag`) trails them and calls `onNew` → `handleNewChat` in [[src/renderer/src/screens/Layout/Layout.tsx]] to open a fresh conversation.

Because the bar doubles as the drag strip, [[src/renderer/src/screens/Layout/Layout.tsx]] renders it as the first child of `.content`; the verify-warning banner (when shown) sits just below it, clear of the drag layer.

Expand Down
129 changes: 15 additions & 114 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@
"react-file-icon": "^1.6.0",
"react-hot-toast": "^2.6.0",
"react-i18next": "^15.7.3",
"react-loader-spinner": "^8.0.2",
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^16.1.1",
"remark-gfm": "^4.0.1",
"thinking-orbs": "^0.1.1",
"three": "^0.183.2",
"troika-three-text": "^0.52.4",
"vscode-material-icons": "^0.1.1",
Expand Down Expand Up @@ -92,4 +92,4 @@
"vite": "^7.2.6",
"vitest": "^4.1.4"
}
}
}
Loading
Loading