Skip to content

feat: editable explorer root path input + outside-cwd preview allowance - #84

Open
NolanHo wants to merge 1 commit into
omdsh-dev:mainfrom
NolanHo:feat/explorer-editable-root
Open

feat: editable explorer root path input + outside-cwd preview allowance#84
NolanHo wants to merge 1 commit into
omdsh-dev:mainfrom
NolanHo:feat/explorer-editable-root

Conversation

@NolanHo

@NolanHo NolanHo commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

改什么

资源管理器顶部的根路径标签变为可编辑输入框

  • 点击标签 → 输入框(预填完整根路径,全选)
  • 输入目录 → 文件树以该目录为根;输入文件 → 以文件所在目录为根(新增 host 路由 /sidebar/api/fs.resolvestat 判定)
  • 默认根 = 会话 cwd;相对路径基于当前根解析;Enter 提交、Esc/失焦取消;路径不存在时输入框保持打开并在下方显示错误条
  • 根覆盖值按会话持久化(SidebarState.explorerRoot),切换根时重置展开集并清空层级缓存;同根重提交不折叠树

cwd 外预览/下载默认放行(可开关)

  • /sidebar/file(图片/PDF/下载)与 /sidebar/html 原先受 isWithin(cwd) 限制,根切到 cwd 外后预览 403
  • 现在 fs.resolve 确认过的根目录记入 per-session 已批准目录注册表(上限 16,按最近使用淘汰);media/html 路由放行 cwd ∪ 已批准根 内的路径——只对用户在资源管理器里显式确认过的目录放开,不放任意路径
  • 新增侧边卡片开关 explorerOutsideCwdPreview默认开,schema/默认值/客户端解析/声明式设置 UI 全链路);关闭后立即恢复严格 cwd 围栏(每次请求读当前设置);cwd 内文件不受影响;文本打开(fs.read)不受影响

为什么

原实现 root 固定为会话 cwd,无法在侧边栏里浏览其它目录;放开后图片/PDF/HTML 预览与下载却因 media/html 路由的 cwd 围栏而 403,体验断裂。本 PR 用「用户确认过的根目录」作为放行边界,兼顾完整性与安全面(不放任意客户端声称的路径,仍受浏览器信任栅栏保护)。

影响范围

  • host:fs.resolve 新路由、已批准根注册表、media/html 路由门禁改由 isPathServable 判定(含新偏好读取)
  • client:ExplorerView 头部输入交互、per-session explorerRoot 状态、侧边卡片新开关
  • 安全面变化:media/html 可服务目录从「仅会话 cwd」扩为「cwd ∪ 用户确认过的根(≤16 个/会话)」,默认开启;关闭开关恢复原围栏

验收与证据

  • 新增/更新测试 15+:fs.resolve 路由(目录/文件/相对/缺失/非法 base)、批准放行与 403 边界、开关实时 200↔403、无 settings 服务默认开、16 上限淘汰、sanitize/parsePrefs/内置开关声明断言、ExplorerView 9 个 jsdom 交互用例(含 resolve 中不 disable 的回归钉)
  • 本地全套 514 测试通过(唯一偶发失败为既有 bundle-route 的 /tmp mtime 粒度 flaky,与本 PR 无关——已用 stash 对照确认);typecheck / build / check-consumer-types / diff-check 全绿
  • 真机验证(独立 DSH 实例 3082 + agent-browser):点击标签→输入框预填;目录/文件/相对路径切换、错误条、Esc 取消均符合预期;cwd 外图片 默认 200 → 关开关 403 → 重新打开 200;无浏览器控制台错误
  • 两轮独立 subagent 审查:均为 APPROVED_WITH_NOTES,finding 已全部处置(blur/readonly 修复、sanitize 收紧、cap 淘汰与内置开关断言补齐、cast 注释说明)

备注

  • README/AGENTS.md 已同步(上游新结构下并入「文件工作台」条目;内置 pref 键列表补 explorerOutsideCwdPreview
  • 未改版本号(跟随仓库发布节奏)

What

The explorer header root label becomes an editable input: entering a directory roots the tree at it, entering a file roots at its parent directory (new fs.resolve host route); relative inputs join the current root; the override is per-session persisted. Previews/downloads outside the session cwd are now allowed by default under roots the explorer confirmed per session (capped at 16), gated by a new explorerOutsideCwdPreview side-card toggle (live, default on).

Why

The explorer root was fixed to the session cwd, and the media/HTML routes' strict cwd fence made outside-cwd previews 403 right after switching roots. Approving only user-confirmed roots keeps the byte-serving surface bounded while the feature stays coherent.

Verification

  • 514 tests pass locally (only the pre-existing /tmp mtime flake in bundle-route.spec is intermittent, confirmed unrelated via stash control); typecheck/build/check-consumer-types clean.
  • Real-browser verification on a throwaway DSH instance: directory/file/relative switching, error strip, Escape cancel, outside-cwd image 200 → toggle off 403 → toggle on 200, no console errors.
  • Two independent review rounds, all findings addressed.

The explorer header root label becomes an editable input: submitting a directory roots the tree at it, submitting a file roots at its parent directory (new host fs.resolve route); relative inputs join the current root. The override lives in the per-session state (persisted, expansion reset on switch). Media/HTML previews and downloads outside the session cwd are allowed by default under roots fs.resolve confirmed for the session (capped at 16), gated by the explorerOutsideCwdPreview side-card toggle.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@Menghuan1918

Copy link
Copy Markdown
Collaborator

@codex Do CR

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c591c9633a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +152 to +157
api.fsResolve({ sessionId, cwd }, text, root).then((result) => {
setResolvingRoot(false)
setEditingRoot(false)
setRootError(null)
// A same-root submit never collapses the tree (the reducer no-ops).
if (result.root !== root) applyRoot(result.root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply resolved roots to the originating session

If fs.resolve is still pending when the user switches conversations, the explorer component can be reused because tabs are keyed only by tab.id, while this completion calls store.reduce() against whichever session is currently active. A response initiated in session A can therefore persist its root into session B, and the shared edit/error state can also update B's view. Capture the initiating session and use a session-targeted update, or cancel/ignore the completion when sessionId changes.

Useful? React with 👍 / 👎.

Comment on lines 106 to 112
if (root === undefined) return
if (lastRootRef.current !== root) {
lastRootRef.current = root
dataRef.current = {}
setData({})
}
loadDir(root)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reapprove persisted roots after host restart

When explorerRoot is restored from localStorage after the host or plugin restarts, the host's approvedRoots map is newly empty, but this effect loads the persisted root only through fs.tree. The tree consequently works while image/PDF/HTML previews and downloads beneath that outside-cwd root return 403 until the user manually resubmits the same path through fs.resolve. Revalidate/reapprove a restored override before loading it.

Useful? React with 👍 / 👎.

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.

2 participants