Skip to content

fix(web): persist file explorer expanded tree and scroll position across navigation#911

Open
swear01 wants to merge 2 commits into
tiann:mainfrom
swear01:fix/file-explorer-state-loss
Open

fix(web): persist file explorer expanded tree and scroll position across navigation#911
swear01 wants to merge 2 commits into
tiann:mainfrom
swear01:fix/file-explorer-state-loss

Conversation

@swear01

@swear01 swear01 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Expanded folder state in the Directories tab was stored in local React state; navigating to a file and back caused DirectoryTree to unmount and reset to only the root node
  • Scroll position of the file list was similarly lost on remount
  • Both are now persisted in sessionStorage keyed by sessionId and restored on remount

Changes

  • DirectoryTree.tsx: reads initial expanded from sessionStorage on mount; writes on every toggle via useEffect
  • files.tsx: adds a ref on the scroll container; restores scrollTop from sessionStorage on mount and saves it on unmount

Test plan

  • Open Files → Directories tab for a session
  • Expand several nested folders and scroll down
  • Click a file to view it, then press back
  • Confirm the tree is still expanded and scroll position is restored
  • Confirm that a fresh session (no prior sessionStorage entry) still opens with only the root expanded

Fixes #910

🤖 Generated with Claude Code

…oss navigation

Expanded folder state and scroll position in the Directories tab were
stored only in local React state, so navigating to a file and back
would reset the tree to the root and scroll to top.

Now both are saved to sessionStorage (keyed by sessionId) on every
change and restored on remount, so the explorer resumes exactly where
the user left off.

Closes tiann#910

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Findings

  • [Major] Reset persisted tree state when the session id changes — useState(() => readExpanded(props.sessionId)) only reads storage on the first mount, but this route can be reused while $sessionId changes (web/src/router.tsx:992). The next render keeps the previous session's expanded set and the new write effect stores it under the new session key, so opening another session's Files page can inherit/overwrite the wrong tree state. Evidence: web/src/components/SessionFiles/DirectoryTree.tsx:203.
    Suggested fix:
    <DirectoryTree
        key={sessionId}
        api={api}
        sessionId={sessionId}
        rootLabel={rootLabel}
        onOpenFile={(path) => handleOpenFile(path)}
    />

Summary

  • Review mode: initial
  • One state scoping issue found in the new sessionStorage-backed directory expansion persistence.

Testing

  • Not run (automation): bun typecheck unavailable in this runner (bun: command not found).

HAPI Bot

onOpenFile: (path: string) => void
}) {
const [expanded, setExpanded] = useState<Set<string>>(() => new Set(['']))
const [expanded, setExpanded] = useState<Set<string>>(() => readExpanded(props.sessionId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Major] useState(() => readExpanded(props.sessionId)) only reads storage on the first mount. Because the files route is under the parameterized $sessionId route, navigating from one session's Files page to another can reuse this component with a new sessionId; the old expanded set is then written under the new session key by the effect below. Key the tree by sessionId (or otherwise reset state before writing for a new id) so each session hydrates its own persisted state.

Suggested fix:

<DirectoryTree
    key={sessionId}
    api={api}
    sessionId={sessionId}
    rootLabel={rootLabel}
    onOpenFile={(path) => handleOpenFile(path)}
/>

…ate across sessions

When navigating between sessions, React can reuse the same DirectoryTree
instance. The useState lazy initializer only runs on first mount, so the
tree would hydrate with the wrong session's expanded set and then
overwrite the new session's storage key.

Adding key={sessionId} forces a fresh mount per session.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Findings
No issues found.

Questions
None.

Summary

  • Review mode: follow-up after new commits
  • Previous HAPI Bot finding is addressed by remounting DirectoryTree per sessionId at web/src/routes/sessions/files.tsx:467.
  • Residual risk: no automated coverage found for the new directory expansion and scroll restoration behavior; current validation appears manual.

Testing

  • Not run (automation)

HAPI Bot

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(web): file explorer loses expanded folder tree and scroll position after viewing a file

1 participant