fix: "Show in folder" reveals in the explorer instead of opening a directory in the editor
The open-path interception reroutes EVERY chat-side file open into the
sidebar editor, including the stock "Show in folder" gesture: the
ui-deliverables row passes "." (resolved to "<cwd>/." by the chat view),
so clicking it opened a bogus editor tab for the workspace directory
(host error: '"..." is a directory').
- openpath-intercept: detect the folder-reveal gesture ("." / "./" /
"<cwd>/.") and route it to a new revealInExplorer dep instead of the editor.
- intercept: revealInExplorer switches to the explorer tab (expanding the
panel), expands the produced files' parent directories, and highlights /
scrolls to them; the intercepted produced-files row now also renders its
own "Show in folder" button when files are hidden.
- produced-files: selectProducedFiles reads the engine Turn data
(owner.turn.data.get("deliverables")) — the same source ui-deliverables
uses — with the node replica as fallback. The old code read owner.nodes,
which the real TurnTailOwnerProps ({turn, seq, openFile}) does not carry,
so the takeover never claimed the chain.
- state: transient `revealed` set (never persisted) plus a revealPaths
reducer that expands ancestor directories.
- ExplorerView: highlight + scroll revealed rows; Sidebar/tabs/service
thread `revealed` through TabComponentProps.
- tests: folder-gesture routing, engine-data selector, wiring seat, zh/en copy.
This commit is contained in:
@@ -30,13 +30,29 @@ export interface OpenPathInterceptDeps {
|
||||
currentSessionId(): string | undefined
|
||||
/** Route the open into the sidebar editor (the established openSidebarFile). */
|
||||
openInSidebar(path: string, sessionId: string): void
|
||||
/** Route a folder-reveal gesture ("Show in folder" passes '.') into the sidebar explorer. */
|
||||
revealInExplorer(path: string, sessionId: string): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a path is the "Show in folder" folder-reveal gesture. The stock
|
||||
* ui-deliverables row passes `'.'` (the session workspace root, resolved by
|
||||
* the chat view to `"<cwd>/."`); any path whose final segment is `.` is the
|
||||
* same gesture. A directory has no editor content, so these opens must reach
|
||||
* the explorer instead of an editor tab.
|
||||
*/
|
||||
export function isFolderRevealPath(path: string): boolean {
|
||||
if (path === '.' || path === './') return true
|
||||
const trimmed = path.replace(/[\\/]+$/, '')
|
||||
return trimmed === '.' || /[\\/]\.$/.test(trimmed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap `workspaces.openPath`: intercepted calls open the file in the sidebar
|
||||
* editor instead of the Host OS and resolve as success (the original's
|
||||
* callers ignore the result); anything that declines falls through to the
|
||||
* original method untouched.
|
||||
* original method untouched. The one exception is the folder-reveal gesture,
|
||||
* which is routed to {@link OpenPathInterceptDeps.revealInExplorer} instead.
|
||||
* @param workspaces - the client workspaces service to wrap.
|
||||
* @param deps - per-call takeover decisions.
|
||||
* @returns the disposer restoring the original method (HMR-safe).
|
||||
@@ -50,7 +66,8 @@ export function wrapOpenPath(workspaces: OpenPathService, deps: OpenPathIntercep
|
||||
if (deps.takeoverEnabled()) {
|
||||
const sessionId = deps.currentSessionId()
|
||||
if (sessionId !== undefined) {
|
||||
deps.openInSidebar(path, sessionId)
|
||||
if (isFolderRevealPath(path)) deps.revealInExplorer(path, sessionId)
|
||||
else deps.openInSidebar(path, sessionId)
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user