feat(preview): folder grid — browse every previewable file of a folder

Folder paths in the terminal and the response viewer are now links
(rooted or ~/ without an extension, relative with a trailing slash); a
click opens a grid of the folder's supported files, newest first, with
subfolder chips and a parent button. A tile opens the file preview,
which pages through the folder with ‹ › / ←/→; the preview of any file
gets a "Show folder" button.

Server: POST /api/sessions/:id/folders grants + lists a folder (local
fs or one ssh round trip for a remote case) under the attachment guard;
GET …/folders/:folderId/files/:name/(raw|preview|thumbnail) serves one
file by bare name through resolveServableAttachmentPath, so browser
requests still never carry a path. Grants live in their own small
registry so a big folder cannot evict the session's attachments. The
attachment raw route's body is now the shared serveServableRaw helper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
RandalixandClaude Opus 5.5 committed 2026-10-10 21:54:37 +02:00
1 parent 7d46a9d336
commit 1010d80b4f
16 files changed
+1706 -44

No files matched your search

+1 -1
View File
@@ -308,7 +308,7 @@ describe('callers consult the hook first', () => {
it('response viewer links route through the hook and keep the file-path handler first', () => {
const bind = APP_JS.indexOf('_bindResponseViewerInteractions(body) {');
const section = APP_JS.slice(bind, bind + 2500);
const section = APP_JS.slice(bind, bind + 3500);
const pathHandler = section.indexOf("closest('a.rv-path')");
const urlHandler = section.indexOf("closest('a[href]')");
expect(pathHandler).toBeGreaterThan(-1);