Skip to content

Spin the Files refresh icon only on a manual refresh - #397

Open
coolestnick wants to merge 1 commit into
useagenthq:mainfrom
coolestnick:fix/artifacts-rail-refresh-spinner
Open

coolestnick wants to merge 1 commit into
useagenthq:mainfrom
coolestnick:fix/artifacts-rail-refresh-spinner

Conversation

@coolestnick

@coolestnick coolestnick commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Closes #348

Problem

The Files panel next to a thread has no tests, and its 5-second background refresh reused the manual refresh path. So while a task runs, the refresh icon spins and the button flickers disabled every 5 seconds on its own, even when the viewer never touched it.

Fix

  • load() now takes { manual } and sets the refreshing state only when the viewer clicks. The initial load, the 5-second poll and org-change reloads all refresh quietly, so the icon stays still unless clicked.
  • The markup moved into ArtifactsRailView, a presentational component that takes the rail's state as props (artifacts, available, refreshing, onRefresh). The spinner is now driven purely by the refreshing prop.
  • ArtifactsRail keeps its public { threadId, live } signature, so its only consumer (session-view.tsx) is unchanged.

Tests (new artifacts-rail.test.tsx)

  • Render tests for the loading, empty, unavailable and list states.
  • The refresh icon spins and the button disables only when the refreshing prop is set.
  • An interaction test mounts the real container and proves the rule end to end: the initial load and the 5-second poll share one non-manual code path that never spins the icon, while a viewer's click does (and locks the button until it settles).

The interaction test drives the rail through local fetch/EventSource stubs restored in a finally, not bun's mock.module: mock.module is process-wide and would leak a never-resolving fetch into every component test file loaded afterward, hanging the suite.

Screenshots

Each frame below reports the refresh icon's real spin state (read from the DOM) as the rail polls every 5 seconds. No clicks happen except where noted.

Before (on main): the icon auto-spins every 5s

before-filmstrip

After (this PR): the icon stays still, and spins only on a manual click

after-filmstrip

Checks

  • bun test components/chat: 855 pass / 0 fail
  • bun run test (frontend): 1774 pass / 0 fail
  • bun run typecheck (root, all packages): pass
  • bun run lint: 0 errors
  • bun run build: compiled successfully
  • bun test scripts/agent-docs.test.ts: 5 pass
  • Full components suite runs clean in ~3s (no mock-leak hang)

Merge order: this PR overlaps PR #395

PR #395 (reword the thread side panels) edits the same file's copy. This PR intentionally leaves that copy untouched so it stays a single-purpose change. Please merge this PR first. It touches only the refresh behavior and structure; #395 then rebases on top and changes the strings ("Session files", the "Refresh session files" aria-label, the subtitle) over the new ArtifactsRailView. Merging in the other order would force this behavioral fix to rebase around the copy change for no benefit.

The Files rail reloads every 5 seconds while a task runs, and that poll
reused the same path as the refresh button, so the icon spun and the
button flickered disabled every 5 seconds on its own.

Track the manual refresh separately: load() now spins the icon only when
the viewer clicks, and the background poll and org-change reloads refresh
quietly. Move the markup into ArtifactsRailView, which takes the rail's
state as props, and add render tests for the loading, empty, unavailable
and list states plus the rule that the icon spins only on a manual click.
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.

Split the Files panel view from its fetching and test each state

1 participant