feat(webui): mount built-in browser panel + fix history and workspace-relative paths (slice 04b) - #53
Merged
Merged
Conversation
…ace-relative resolution (webui-parity slice 04b)
Slice 04 shipped the panel exported but deliberately unmounted because
components/panels.tsx was owned by the in-flight slice 03. Slice 03
merged; this slice wires the panel into the right-hand drawer AND
fixes the two functional defects acceptance surfaced.
F1 — history echo. The controlled-sync effect unconditionally rebuilt
the back/forward stack on every controlledPath change. Under the
natural round-trip wiring (onNavigate -> parent state -> currentPath
prop) the Go button pushed once and the effect wiped the back stack.
The fix detects echo vs external: if the new controlledPath equals
the internal currentPath the change is the panel's own navigation —
keep the stack and only sync the address bar. If they differ it is an
external change (file-tree click) and re-seed the stack. The push is
also deduped inside the back/forward handlers so a no-op step does not
fire an empty echo back to the parent.
F2 — workspace-relative resolution. The address bar accepted
workspace-relative paths but buildSandboxUrl emitted them verbatim.
The server route resolves relative paths against its own CWD, which
is almost never an allowed root, so a workspace-relative entry landed
on a 403 / ENOENT. The component now carries a real workspaceDir prop
and a new helper resolveWorkspacePath in lib/browser-nav.ts stitches
the two halves before the URL is built. Absolute in-root paths pass
through unchanged; .. segments are deliberately preserved so the
server-side containment gate stays the single source of truth.
Mount. 'browser' added to PanelKind in components/panels.tsx and to
the deserialiser's valid-kinds set in lib/persist.ts; the toolbar
launcher (slice-04 left it disabled) is now wired through
onOpenBrowser -> openPanel('browser'). The file tree routes HTML/HTM
rows through onOpenInBrowser -> browserPath + panel='browser', while
non-HTML rows still call the slice-12 single-source openFileInWeb.
Switching workspaces clears the relative path so the next gesture
re-anchors cleanly.
F3 — sub-resource subset URLs. Multi-file static sites that load
relative CSS/JS cannot resolve them under /api/fs/raw because the
route has no path-suffix resolution. The slice instructions pin 'do
not change the fs route's contract in this slice unless you can do it
additively and safely' — skipped. The HTML the self-check serves uses
absolute /api/fs/raw URLs that include the workspace dir explicitly
to prove F2 still works end-to-end; F3 will be a follow-up slice.
Tests. 13 new unit tests in webapp/test/browser-nav.test.ts (workspace
resolution rules: empty / POSIX-absolute / relative / ./ collapse /
trailing separators / .. preservation / Windows drives; and three
buildSandboxUrl overload cases). 1 new persist test confirming the
'browser' kind round-trips. All 514 webapp tests pass; webapp:typecheck
+ repo typecheck clean.
fengzhi09
force-pushed
the
feat/browser-panel-mount
branch
from
September 27, 2026 14:55
67dceb0 to
7a12557
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Slice 04b: mounts the built-in browser panel (slice 04 shipped it unmounted because
panels.tsxbelonged to the then-in-flight slice 03) and fixes the two functional defects that slice 04's acceptance assigned to the wiring step.currentPath(history)the change is the panel's own navigation — keep the stack, sync the draft, clear errors. When they differ it is external (a file-tree HTML click) and re-seeds viacreateHistory. Back/forward dedupe no-op steps so a click at the boundary does not echo back to the parent.resolveWorkspacePath(path, workspaceDir)stitches relative → absolute before the sandbox URL is built, andBrowserPanelgained a realworkspaceDirprop (the doc comment previously promised one that did not exist)...segments pass through untouched so the server containment gate remains the single source of truth."browser"added toPanelKindandvalidKinds(so it round-trips across reload), toolbar launcher wired,page.tsxownsbrowserPath. File-tree.html/.htmrows route toonOpenInBrowser(sets path and opens the panel); non-HTML rows still go through slice 12's single-sourceopenFileInWeb. Switching workspaces clears the relative path so the next gesture re-anchors.Acceptance — PASS (independent agent, live UI + fresh gates)
index.html,pages/about.html,./index.htmlall resolve to the workspace; absolute in-root renders; non-HTML refused client-side; out-of-root and..traversal → 403 with the explicit allowed-roots error.allow-scriptson every observed iframe — noallow-same-origin/allow-top-navigation/ popups / forms / modals. Preview JS executed, yet the address bar still refuseshttp(s)://,file://,javascript:,vbscript:,data:with an inline error and no iframe created..htmlclick opens in the browser panel; workspace switch clears the path and the same relative string re-anchors to the new workspace.Disclosed, non-blocking
<link href="style.css">) do not resolve under/api/fs/rawbecause that route has no path-suffix resolution. The slice proved F2 end-to-end using absolute/api/fs/raw?path=…URLs instead of changing the fs route's contract. F3 is a follow-up slice.router-auth-gateifdist/webui/webapp/outis absent (passes afterpnpm build) — pre-existing infra quirk, this diff is webapp-only; worth its own ticket.Gates
Full server suite 1790 pass / 0 fail / 2 skipped ·
test:webapp514/514 · typecheck ✓ · build ✓ ·check:source✓