Skip to content

Modern loaders: replace spinners with thinking-orbs - #868

Merged
fathah merged 1 commit into
mainfrom
modern-loaders
Jul 22, 2026
Merged

Modern loaders: replace spinners with thinking-orbs#868
fathah merged 1 commit into
mainfrom
modern-loaders

Conversation

@fathah

@fathah fathah commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Swaps every loading indicator in the app — the old react-loader-spinner Grid, the CSS .loading-spinner circle, and the animated loadingo.gif avatar — for the dotted thinking-orbs canvas animations, wired through one theme-aware wrapper.

Why

The app had three unrelated loading treatments (a lucide spinner, a CSS border-spin circle, and a 610 KB looping gif) with no shared theming. thinking-orbs gives us a single, cohesive set of state-specific animations (working, searching, solving, composing, …) that render on a plain 2D canvas — no WebGL, cheap on low-end devices, and identical across Chrome/Safari/Firefox.

What changed

New: OrbLoader wrapper

src/renderer/src/components/OrbLoader.tsx — a thin wrapper around ThinkingOrb that does three things the raw component can't:

  1. Theme. The library's auto mode only recognises data-theme="dark|light", but Hermes writes theme ids ("dracula", "nord", …) to that attribute. The wrapper reads the resolved theme from ThemeProvider and pins the orb's ink from each theme's declared appearance (dark/light), so the ink is correct across all 12 themes — not just when the OS setting happens to agree.
  2. Any numeric size. ThinkingOrb ships exactly two design presets (20, 64) and throws on any other size. OrbLoader accepts any number: it snaps the design to the nearest preset (split at 42) and drives the visual footprint from the number via style. Arbitrary pixel sizes (e.g. a 30 px avatar) are safe and never crash.
  3. invert. Flips the ink relative to the app theme, for orbs that sit on an inverted surface (see the chat avatar below).

Loaders replaced

  • Chat reasoning row ("Thinking…") → solving orb.
  • Chat tool-call row and slash-command pending bubbleworking orb.
  • Screen / pane data fetches (Discover, Memory, Tools, Schedules, Agents, Sessions, Skills, Soul, Kanban, registry browser, profile modal panes) → searching orb, replacing the CSS .loading-spinner.
  • Streaming conversation tab chip (ActiveSessionsBar) → composing orb, replacing the lucide Loader2 spinner-on-a-colour-circle.
  • Generating-agent avatar (HermesAvatar) → composing orb, replacing loadingo.gif. The gif's loop-boundary stop logic is gone — a canvas orb never freezes mid-frame, so the avatar swaps straight to the agent's ProfileAvatar the instant the turn goes idle.

Chat avatar: inverted disc for legibility

The monochrome orb ink washed out against the near-black page, so the loading avatar (.chat-avatar-orb) is backed by an inverted circle--text-primary, which is light on dark themes and dark on light themes — and passes invert to OrbLoader so the ink flips to match. Result: 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.

Dependency + cleanup

  • package.json: react-loader-spinnerthinking-orbs@^0.1.1.
  • Removed the dead .loading-spinner CSS rule.
  • test/setup.ts: replaced the 34-entry react-loader-spinner mock with a one-line thinking-orbs mock (its canvas + IntersectionObserver rendering has no jsdom equivalent).

Notes / follow-ups

  • loadingo.gif (610 KB) is now unused but still committed — can be deleted in a follow-up to trim the bundle.
  • On a few muted-text themes (e.g. Solarized Dark, where --text-primary is a grayish tone) the avatar disc isn't pure white. If a crisp pure white/black disc is wanted regardless of theme, switch the background from --text-primary to appearance-keyed #fff/#111.

Verification

  • npm run typecheck:web — clean.
  • npm test — 171 files, 1819 tests pass.
  • electron-vite build — production bundle builds.
  • lat check — all wiki links and code refs pass; lat.md/ updated (loading-indicators.md added; chat-commands, sidebar-navigation, window-chrome, index updated).

Docs

See lat.md/loading-indicators.md for the full design notes: the OrbLoader wrapper, per-state usage conventions, numeric-size snapping, the invert inverted-disc avatar, and the test mock.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR unifies the renderer’s loading indicators around a theme-aware thinking-orbs wrapper. The main changes are:

  • Adds OrbLoader with theme mapping, inversion, and arbitrary visual sizing.
  • Replaces chat, tab, screen, pane, and modal loading indicators.
  • Simplifies the active agent avatar lifecycle by removing GIF timing logic.
  • Updates loader CSS, dependencies, test setup, and design documentation.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • All former .loading-spinner callers were migrated before its CSS was removed.
  • The avatar rewrite removes timer state without changing which turns are marked active.

Important Files Changed

Filename Overview
src/renderer/src/components/OrbLoader.tsx Adds the shared adapter for application themes, inversion, preset selection, and visual sizing.
src/renderer/src/screens/Chat/MessageRow.tsx Replaces GIF and Grid loaders with state-driven orbs and removes obsolete timer state.
src/renderer/src/screens/Chat/HistoryRow.tsx Uses state-specific orbs for active reasoning and tool rows.
src/renderer/src/screens/Layout/ActiveSessionsBar.tsx Uses a composing orb in the loading conversation-tab avatar slot.
src/renderer/src/assets/main.css Removes the obsolete border spinner and adds scoped orb styling.
src/renderer/src/test/setup.ts Replaces the old loader mock with a jsdom-safe thinking-orbs mock.
package.json Replaces react-loader-spinner with thinking-orbs.

Reviews (1): Last reviewed commit: "Loading" | Re-trigger Greptile

@fathah
fathah merged commit 4192e7d into main Jul 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant