feat: split-pane main page with scope-aware file explorer #19
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/session-explorer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Revamps the main page (
/) into a split layout: workspaces + sessions on the left, a scope-aware file explorer + viewer/editor on the right. The explorer browses and edits files in either a workspace's main worktree (/workspace) or any session's worktree (/workspace/.nexus/worktrees/<name>). Markdown plan files render as preview by default with a source toggle.Both file-explorer surfaces (the new main page and the existing
/config-explorer) now consume a singleFileExplorerPanelcomponent behind aFileExplorerProviderabstraction, so markdown preview and breadcrumbs work on the Config Explorer too.What's new
File explorer (server)
/api/workers/:id/files/*— six HTTP endpoints (tree, file GET/PUT, mkdir, rename, path DELETE, upload) that talk to worker containers viadocker exec.WorkerFsServiceatnexus/src/lib/server/worker-fs/. Path data flows via argv ($0); base64 payload streams via stdin (avoidsMAX_ARG_STRLEN128 KiB cap).lib/server/file-explorer/path-safety.tsmodule so both backends use the same regex, depth caps, and binary sniff.mv -non rename closes the stat-then-mv TOCTOU race.File explorer (client)
FileExplorerProviderinterface withConfigVolumeProvider(existing endpoints) andWorkerFsProvider(new endpoints).FileExplorerPanel— the shared component used by both the main page and the Config Explorer. Toolbar with breadcrumb, scope chip, refresh / +file / +folder / upload / save, plus md preview↔source toggle on the right.MarkdownView—marked+isomorphic-dompurify, dynamic-imported on first.mdopen. Sanitized, GFM-aware, forcestarget=_blank rel=noopener noreferreron external links.FileTree/FileEditormoved tolib/components/file-explorer/; old locations are nowComponentProps-typed re-export shims so/config-explorerkeeps working unchanged.Main page
MainSplit— split shell with scope state mirrored to URL (?scope=workspace&worker=…or?scope=session&worker=…&session=…&base=…); mobile slide-in pattern (right pane slides in over workspaces).Workers.svelte+Sessions.sveltewireonSelectScopeupward. Running workspace cards are full-card click targets that select the workspace as the file-explorer scope.relativeTime(created_at)("2 days ago") instead of a raw datetime.Header + tokens
aria-current="page"on the active link.+ Newlink removed from the header — the bottom-of-list "+ New workspace" CTA covers it.--accentstays at#cddc39for text/borders/icons; new--accent-bgat#b5c249for solid button fills (.btn.primary, settings ON-toggle, install-skill CTA) so the bright text hue doesn't read as "glowing" when used as a large background.start agent/ "× workspace agent" →stop agent; repositioned to the left of Remove.Configurable max file size
max_file_bytes(envFILE_EXPLORER_MAX_FILE_BYTES, default262144= unchanged from the prior hardcoded value; range 1 KiB to 10 MiB). Surfaced in the Settings pane.What to look at first
lib/components/file-explorer/provider.ts— the load-bearing abstraction.lib/server/worker-fs/service.ts+lib/server/lib/docker-exec.ts:execAndCaptureWithStdin— the shell-injection model. Paths via argv ($0), payload via stdin.service.test.tslocks the no-payload-in-argv contract.lib/components/file-explorer/FileExplorerPanel.svelte— the single panel both surfaces consume.lib/components/config-explorer/{FileTree,FileEditor}.svelte— confirm/config-explorer/+page.sveltekeeps working through them.Spec + plan
docs/superpowers/specs/2026-05-23-main-page-file-explorer-design.mddocs/superpowers/plans/2026-05-23-main-page-file-explorer.mdTest plan
Vitest unit suite (326 passing, 65 SQLite/POSIX-shell host-skips on Windows; CI runs them on
node:22-alpine):pnpm testcleanpnpm typecheckcleanassertSafePath(rejections + acceptances incl. dotfiles), plus theWorkerFsServiceallowlist + traversal rejection.mv -nrace-window backstop.javascript:URL strip, target=_blank, GFM checkboxes preserved, bold + heading).scopeFromSearch/scopeToSearch.relativeTime: 12 boundary cases for "just now" → "N years ago".max_file_bytesregistry: default, env override, bounds validation.Manual smoke (verified locally via
docker compose up):/loads with workspaces on the left, "No workspace selected" prompt on the right..mdfile → preview by default,preview | sourcetoggle in the toolbar's right cluster..mdfile → editor opens./config-explorer): same panel, neutral "~/.claude" scope chip; markdown files now render as preview; SKILL.md from the Installed Assets Panel opens in the panel viabind:this.Known v1 trade-offs (documented in the spec)
GET .../diffendpoint can plug in without UI restructure).Sessions.svelterow has an svelte-check a11y advisory (tabindex+ conditionalrole) that's a static-analysis limitation; the row IS keyboard-focusable when scope selection is wired.The Task 1 migration of assertSafePath tests dropped two leading-dot acceptance cases that existed in the original volume-fs.test.ts ('.credentials.json', 'inner/.config'). Per AGENTS.md fact #13 these guard against future regex tightening silently re-hiding dotfile-name artifacts like .nexus-skill.json. Also added two tests for the non-string defensive guard that crosses HTTP boundaries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>3a3bd63eceec75a8b926