Skip to content

fix: add global spinner animation to prevent stuck loaders#40

Merged
gsxdsm merged 6 commits intoRunfusion:mainfrom
timothyjlaurent:timothyjlaurent/stuck-spinners
May 5, 2026
Merged

fix: add global spinner animation to prevent stuck loaders#40
gsxdsm merged 6 commits intoRunfusion:mainfrom
timothyjlaurent:timothyjlaurent/stuck-spinners

Conversation

@timothyjlaurent
Copy link
Copy Markdown
Contributor

@timothyjlaurent timothyjlaurent commented May 5, 2026

Summary

  • Moves @keyframes spin and .animate-spin from TaskCard.css to the global styles.css, making the spinner animation available on every page from the first paint

Problem

The .animate-spin CSS class (used by 25+ components, ~60 instances) was defined only in TaskCard.css. On pages that don't render a TaskCard (loading screen, settings, agent detail, etc.), the class didn't exist in the stylesheet, so all Loader2 spinners appeared frozen.

Similarly, BackgroundTasksIndicator.tsx used inline style={{ animation: "spin 1s linear infinite" }} without defining @keyframes spin in its own CSS — relying on other components' CSS files being loaded.

Test Plan

  • vite build passes
  • Verified .animate-spin class and @keyframes spin appear in the main CSS bundle (index-*.css)
  • Manual: navigate to dashboard loading screen, settings, agent detail view — confirm spinners animate
  • Manual: open DevTools → Elements → check that .animate-spin is defined before TaskCard renders

Closes #39

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Style
    • Added a reusable spinning animation utility for the dashboard to standardize rotating effects for loaders and animated UI elements.
  • Tests
    • Updated test utilities and mocks to ensure consistent mocked behavior for agent/session paths and to include mission-listing in runtime tests, reducing test setup friction.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a global @keyframes spin and .animate-spin utility to packages/dashboard/app/styles.css, and updated test-related hooks/mocks so tests that mock agent creation also cover the resolved-session path and the MissionStore's listMissions method.

Changes

Global Spinner Animation

Layer / File(s) Summary
Declaration
packages/dashboard/app/styles.css
Introduces top-level @keyframes spin (rotate 0deg → 360deg).
Utility
packages/dashboard/app/styles.css
Adds .animate-spin { animation: spin 1s linear infinite; } to make spinner animation available globally.

Test Hooks / Mocks

Layer / File(s) Summary
Test Hook Update
packages/dashboard/src/chat.ts
__setCreateFnAgent now sets createFnAgent = mock and also createResolvedAgentSession = (async (options: any) => mock(options)), so both session-creation code paths use the provided mock in tests.
Test Mock Surface
packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts
TaskStore test mock extended to include MissionStore.listMissions returning an empty array for tests that call it.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I twitch my nose at frozen spin,
A tiny keyframe lets it spin again,
Tests now mock both routes with care,
Dashboards whirl in morning air,
Hop, twirl, rejoice — code baked with flair.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Changes to chat.ts and in-process-runtime.test.ts appear to be supporting fixes for test flakiness rather than direct spinner animation fixes, but are acknowledged as necessary stabilization for CI. Clarify whether the test mock additions (createResolvedAgentSession in chat.ts and listMissions in in-process-runtime.test.ts) are essential to the spinner animation fix or should be separated into a distinct PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: add global spinner animation to prevent stuck loaders' accurately summarizes the main change: moving spinner animations to global styles to prevent frozen loaders.
Linked Issues check ✅ Passed The PR successfully implements the fix required by issue #39: adds @keyframes spin and .animate-spin to global styles.css to ensure spinner animations are available from first paint regardless of rendered components.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 5, 2026

Greptile Summary

This PR fixes frozen spinner animations by promoting @keyframes spin and .animate-spin from component-scoped CSS files to styles.css at the top level (outside any :root {} block), ensuring the animation is available from first paint. A missing listMissions mock in the in-process runtime test is also added to unblock tests that call getMissionStore().listMissions().

  • styles.css: @keyframes spin and .animate-spin are inserted between the end of the token :root {} block and the start of the theme :root {} block — correct top-level placement that the browser will parse globally.
  • in-process-runtime.test.ts: listMissions: vi.fn().mockReturnValue([]) is added to the getMissionStore mock, matching the interface the runtime now calls during operation.

Confidence Score: 5/5

Safe to merge — the CSS change is correctly placed at the top level and the test change adds a missing mock stub.

The @keyframes spin block is now positioned at the stylesheet top level, between two :root {} close/open boundaries, which is valid CSS parsed by all browsers. The .animate-spin utility and the inline animation: spin ... references in components like BackgroundTasksIndicator will now resolve on every page. The test change is a straightforward mock addition with no logic risk.

No files require special attention.

Important Files Changed

Filename Overview
packages/dashboard/app/styles.css Moves @keyframes spin and .animate-spin to the top level between two :root {} blocks — valid CSS placement that makes the spinner available globally before any component CSS loads.
packages/engine/src/runtimes/tests/in-process-runtime.test.ts Adds missing listMissions mock to the getMissionStore mock object so tests don't fail when the runtime calls listMissions() during startup.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["styles.css loads on every page"] --> B["@keyframes spin defined at top level"]
    B --> C[".animate-spin class available"]
    B --> D["inline animation: spin ... resolves"]
    C --> E["25+ components using className=animate-spin"]
    D --> F["BackgroundTasksIndicator inline style"]
    E --> G["Spinners animate correctly on all pages"]
    F --> G
Loading

Reviews (7): Last reviewed commit: "Merge branch 'main' into timothyjlaurent..." | Re-trigger Greptile

Comment thread packages/dashboard/app/styles.css Outdated
Comment thread packages/dashboard/app/styles.css Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/dashboard/app/styles.css`:
- Around line 167-176: Move the `@keyframes` spin and .animate-spin rules out of
any :root block to the stylesheet top level (so the keyframes and utility class
are not nested); update the .animate-spin animation duration to use a design
token like var(--animation-duration-spin) instead of the hardcoded 1s, and
add/ensure the token definition exists inside :root (e.g.,
--animation-duration-spin: 1s) so the duration is configurable and follows the
coding guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 93400971-62e9-4a18-b94f-6611e148245e

📥 Commits

Reviewing files that changed from the base of the PR and between ba6666f and 6b740c1.

📒 Files selected for processing (1)
  • packages/dashboard/app/styles.css

Comment thread packages/dashboard/app/styles.css Outdated
timothyjlaurent added a commit to timothyjlaurent/Fusion that referenced this pull request May 5, 2026
The @Keyframes spin and .animate-spin rules were incorrectly placed
inside the first :root {} block, which is invalid CSS — @Keyframes
cannot be nested inside selector blocks. Browsers silently ignore
them in that position, so the spinners would still get stuck on pages
without component CSS loaded.

Moves the rules to the stylesheet top level between the two :root
blocks. Also fixes the mobile-css regression test that parses the
first :root block with a non-greedy regex.

Addresses review feedback from Greptile (P1) and CodeRabbit on PR Runfusion#40.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@timothyjlaurent
Copy link
Copy Markdown
Contributor Author

Addressing review feedback

Greptile P1 — @keyframes inside :root {}: Fixed in fe82bb0. Moved both @keyframes spin and .animate-spin to the stylesheet top level, between the two :root {} blocks. This also resolved the mobile-css.test.tsx CI failure — the non-greedy regex in getFirstRootBlock() was matching the nested } as the block end, cutting the capture short before --mobile-breakpoint.

Greptile P2 — Duplicate keyframes in component CSS: Acknowledged. The ~10 copies in TaskCard.css, Header.css, InsightsView.css, etc. are now dead code. They're harmless (identical definitions) but worth cleaning up. I'd like to defer that to a follow-up PR to keep this one focused on the root-cause fix.

CodeRabbit — Design token for duration: The existing component copies hardcode various values (1s, 0.6s, 0.8s), so introducing a token would require aligning all call sites. Suggest as a follow-up.

CI — Test shard 3/3 failure: Caused by the same :root placement issue. The mobile-css.test.tsx regression test parses the first :root block with /:root\s*\{([\s\S]*?)\n\}/ — the nested @keyframes { } introduced an early \n} match. Now passing locally (8/8 tests).

timothyjlaurent and others added 2 commits May 5, 2026 12:01
The `.animate-spin` CSS utility class and `@keyframes spin` were only
defined in TaskCard.css but used by 25+ components (~60 instances).
On pages without a TaskCard rendered, the class didn't exist in the
stylesheet, causing all spinners to appear frozen.

Moves both the keyframes and utility class to the global styles.css
so they're available from the first paint regardless of which
components are loaded.

Closes Runfusion#39

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @Keyframes spin and .animate-spin rules were incorrectly placed
inside the first :root {} block, which is invalid CSS — @Keyframes
cannot be nested inside selector blocks. Browsers silently ignore
them in that position, so the spinners would still get stuck on pages
without component CSS loaded.

Moves the rules to the stylesheet top level between the two :root
blocks. Also fixes the mobile-css regression test that parses the
first :root block with a non-greedy regex.

Addresses review feedback from Greptile (P1) and CodeRabbit on PR Runfusion#40.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@timothyjlaurent timothyjlaurent force-pushed the timothyjlaurent/stuck-spinners branch from fe82bb0 to c68bfff Compare May 5, 2026 19:01
The options parameter in __setCreateFnAgent's inline arrow already
uses any to match the module-level let (which has its own suppress
comment). Add the missing eslint-disable-next-line so CI passes.

Unrelated to the spinner fix — pre-existing on main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gsxdsm
Copy link
Copy Markdown
Collaborator

gsxdsm commented May 5, 2026

@copilot resolve the merge conflicts in this pull request

…SS change)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@timothyjlaurent
Copy link
Copy Markdown
Contributor Author

Flaky test in Test shard 2/3

The in-process-runtime.test.ts failure is a pre-existing flaky test in packages/engine — a mock setup race condition (this.missionStore.listMissions is not a function). It passed on PR #38 and flaked on this run. Completely unrelated to the CSS-only change in this PR.

Pushed an empty commit to re-trigger CI. All other checks were green:

  • Lint: PASS
  • Typecheck: PASS
  • Test shard 1/3: PASS
  • Test shard 3/3: PASS
  • CodeRabbit: PASS

timothyjlaurent and others added 2 commits May 5, 2026 13:55
The MissionLoop calls this.missionStore.listMissions() during startup
recovery, but the mock TaskStore's getMissionStore() return value didn't
include this method. When the runtime startup sequence raced ahead, it
would hit "listMissions is not a function" — making the test flaky.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gsxdsm gsxdsm merged commit cfca41d into Runfusion:main May 5, 2026
6 checks passed
timothyjlaurent added a commit to timothyjlaurent/Fusion that referenced this pull request May 5, 2026
The @Keyframes spin and .animate-spin rules were incorrectly placed
inside the first :root {} block, which is invalid CSS — @Keyframes
cannot be nested inside selector blocks. Browsers silently ignore
them in that position, so the spinners would still get stuck on pages
without component CSS loaded.

Moves the rules to the stylesheet top level between the two :root
blocks. Also fixes the mobile-css regression test that parses the
first :root block with a non-greedy regex.

Addresses review feedback from Greptile (P1) and CodeRabbit on PR Runfusion#40.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Bug: Spinner animations get stuck — animate-spin class undefined on pages without TaskCard

2 participants