Repository navigation
Description
Problem
The file browser has three related usability issues:
- Mobile access: enabling File Viewer in header settings does not expose its button on phones. Opening it requires returning to Settings.
- Incomplete listings: larger repositories lose files and folders when the recursive listing reaches its shared entry or depth limit. There is no way to retrieve omitted entries.
- Hidden-file behavior: enabling Show hidden files can make ordinary entries disappear, giving the impression that it switches to a hidden-files-only view.
Investigation
Phone CSS explicitly hides the File Viewer header button regardless of the enabled setting. An existing policy test enforces this behavior.
The browser currently requests a recursive tree with a depth of five levels and a shared budget of approximately 5,000 files and directories. The server stops scanning when it reaches that budget. Expanding a folder only reveals prefetched children; it does not request missing entries.
Show hidden files already includes both ordinary and hidden entries. However, hidden directories can exhaust the shared budget before ordinary entries are visited.
For example:
.aaa/ # Contains 5,000 files
src/visible.ts
README.md
With hidden files disabled, src/ and README.md appear. Enabling hidden files returns only .aaa/, with the listing marked truncated. Searching for visible.ts also returns no matches and reports truncation.
Proposed behavior
Mobile access
Honor the existing File Viewer header setting on phones. Keep it disabled by default, but allow users who explicitly enable it to open and close the browser directly from the header.
Ensure the browser and its essential controls remain usable within a phone viewport.
Load directories on demand
Replace the recursive preload with lazy directory loading:
- Opening the browser loads the workspace root’s immediate files and folders.
- Expanding a folder requests that folder’s immediate children.
- Each directory request has an independent budget. The size of a subtree cannot consume the budget for root entries or sibling folders.
- If an individual directory exceeds its request limit, pagination or “Load more” allows users to retrieve its remaining entries.
- Folders with children that have not been loaded must remain distinguishable from empty folders.
This is a functional change involving both the frontend tree and listing API, proposed for maintainer agreement before implementation. Existing directory exclusions remain unchanged.
Hidden files
Show hidden files should add eligible hidden entries to the current view while preserving access to ordinary entries.
Toggling it should retain applicable expanded folders and the active search query. Directory results must be refreshed or invalidated consistently with the new visibility setting.
Search
Repository-wide search must continue to search folders that have not been expanded.
Search currently shares the recursive scan’s entry and depth limits. Its incomplete results must be clearly identified, with an agreed way to continue or narrow the search. The directory-loading change alone does not resolve search truncation.
Acceptance criteria
Mobile
- Explicitly enabling File Viewer exposes a usable header button on phones after saving and reloading.
- The button opens and closes the active session’s file browser without visiting Settings.
- Disabling the setting hides the button; the phone default remains disabled.
- Essential browser controls remain visible and usable within the phone viewport.
Directory browsing
- Opening the browser lists the workspace root’s immediate eligible files and folders.
- Root entries remain reachable regardless of the size or depth of their descendants.
- Expanding a folder loads its immediate children on demand.
- Loading a large folder does not remove or prevent access to root entries or sibling folders.
- Files deeper than the current five-level preload remain reachable through navigation.
- An individual directory exceeding its request limit provides a way to retrieve all remaining eligible entries.
- An unloaded or partially loaded folder is not presented as empty.
- Requests and responses remain bounded.
Hidden files and request state
- Enabling hidden files adds eligible hidden entries without making ordinary entries unreachable.
- The 5,000-file hidden-directory fixture above preserves access to
src/visible.tsandREADME.md. - Toggling preserves applicable expanded folders and the active search query.
- Session changes, refreshes, and rapid toggles cannot display stale results from an earlier request.
Search and compatibility
- Search includes eligible files in folders that have not been expanded.
- Incomplete search results are clearly distinguished from a completed search with no matches.
- The search continuation or narrowing behavior is agreed before implementation.
- Existing ownership checks, filesystem restrictions, and directory exclusions remain intact.
- Existing preview and download actions continue to work.
Integration points
| Area | Existing integration points |
|---|---|
| Mobile access | mobile.css, header markup, settings-ui.js, mobile header policy tests |
| Directory listing | /api/sessions/:id/files in file-routes.ts |
| Expansion, caching, and request state | panels-ui.js |
| Search | Existing q handling and compileFileQuery() |
| Validation | Route tests, hidden-file/search tests, real mobile browser tests |
The separate filesystem picker already loads individual directories, but it has different access rules and its own entry cap. Reusing it would require deliberate review.
Scope decisions requested
Before implementation, please confirm:
- Approval to honor the explicitly enabled File Viewer header setting on phones.
- Agreement with lazy directory loading and per-directory continuation.
- The intended repository-wide search behavior when a bounded scan is incomplete.
- The expected behavior of the existing Expand all action under lazy loading.
Changing generated-directory exclusions, file editing, preview-content limits, or adding remote filesystem capabilities is outside this proposal.
Investigation evidence
Investigated on v1.47.0 (f1f7eec4). The hidden-directory interaction was reproduced through the actual listing route with a temporary filesystem fixture.
The four focused existing test suites passed: 175 tests. They do not currently cover this large-tree interaction. Mobile visibility was confirmed through CSS and policy tests; a real-browser reproduction remains part of implementation validation.
Activity
Ark0N commentedon Nov 5, 2026
Thanks for the write-up, this is a good one. Two open PRs already cover a lot of it, so here's where each part stands.
Hidden entries using up the budget. #614 fixes this. I ran your fixture against it (
.aaa/with 5,000 files, plussrc/visible.tsandREADME.md). With hidden files on, master lists only.aaa/and a search forvisible.tsfinds nothing. On the #614 branch the tree shows both files and the search finds it. That PR is waiting on one change, which is also your "not presented as empty" point: folders the scan ran out of budget for get marked instead of looking empty.Lazy loading. #633 is a draft that does this. It sits on top of #631, and both need a rebase. It lists one folder per request and fetches a folder when you expand it. It also has a proposal for two of your open questions: typing filters what's already loaded while Enter runs the whole-workspace search, and Expand All only opens folders that are already loaded. I haven't reviewed it yet, so treat those as its proposal rather than a decision. It would be worth checking your acceptance list against it, especially retrieving the rest of a very large folder.
File Viewer button on phones.
mobile.csshides it on phones on purpose, and a test pins that, because the panel was built for desktop. Honoring the setting means changing that policy, so I'll come back to you on this one separately.Please hold off on implementing for now, so we don't end up with a third take on the same code.
Ark0N commentedon Nov 9, 2026
@DodgyBadger, thanks for waiting. Here's where each part stands now, including the two answers I owed you.
Hidden entries using up the budget: fixed. #614 shipped in 1.49.0. The walk now takes folders in fair rounds, so with your fixture
src/visible.tsandREADME.mdstay in the tree with hidden files on and the search findsvisible.ts. Folders the scan ran out of budget for are marked incomplete instead of looking empty.File Viewer button on phones: yes, as an opt-in. If someone turns File Viewer on in the header settings, the button should show on a phone too. The phone default stays off. The condition is the one in your acceptance list: the panel and its essential controls have to be usable at phone width (around 390px). That means changing the rule in
mobile.cssand the phone header policy test that pins it, and checking it in a real mobile browser context. Please send it as its own small PR.Lazy loading: it's yours if you want it. #633 has been closed (no activity from its author in a month), so you won't be stepping on anyone. Please use it as a reference rather than starting from zero: its
lazy=1listing of one folder per request, the folder path confined throughvalidateSessionFilePath(), folders sent withoutchildrenmeaning "not loaded", and the epoch/owner/showHidden guards that drop a listing landing after a refresh or a session switch.On your scope questions 3 and 4, start from #633's answers: typing filters what's already loaded with no request, Enter runs the existing whole-workspace search, and Expand All only opens folders that are already loaded. An incomplete search has to say so plainly, so it never reads as "no matches". If your testing shows a problem with either, say so in the PR and we'll settle it there.
A few things to carry over from the current walk:
readdir(the fix(cases): bound path probes for linked workspaces and session creation, so an unreachable mount cannot freeze the server #516 rule in CLAUDE.md), and an unreachable one is skipped, never shown as empty.root=live/root=live-any) landed after feat(files): load the Files panel one folder at a time, sort it, filter locally #633 was written. The lazy listing has to keep it, including the absolute paths of a worktree outside the session folder.Two PRs (phone button, then lazy loading) will be quicker to review than one. Thanks again for the write-up, it made all of this easy to decide.
Merge #809: the File Viewer header button follows its setting on phones
Ark0N commentedon Nov 12, 2026
The phone part shipped today in Codeman 1.51.0 with your #809: the File Viewer header button follows its setting on a phone and stays off until you turn it on. Thanks, @DodgyBadger.
What is left here is loading directories on demand. That part is still yours if you want it, with the notes above.