6b31479ec2
Co-authored-by: Kandev Agent <agent@kandev.dev>
51 lines
2.3 KiB
TypeScript
51 lines
2.3 KiB
TypeScript
export type FileTreeEditorTarget = {
|
|
/** Path relative to the resolved worktree root, as the editors API expects. */
|
|
filePath: string;
|
|
worktreeId?: string;
|
|
};
|
|
|
|
type WorktreeLike = { id: string; path?: string };
|
|
|
|
function pathBasename(path: string): string {
|
|
return path.split(/[\\/]/).filter(Boolean).pop() ?? "";
|
|
}
|
|
|
|
/**
|
|
* Maps a Files-panel tree path onto the `{worktreeId, filePath}` pair the
|
|
* editors API resolves against, or `null` when the node is not addressable by
|
|
* that API.
|
|
*
|
|
* The Files tree is served either from inside a worktree — in which case node
|
|
* paths are already worktree-relative — or from the task workspace root, where
|
|
* the first segment names a workspace entry. A task reaches the second shape
|
|
* both by holding several worktrees and by having any source attached through
|
|
* *Add Repositories to workspace*, which rebinds the session to the task root
|
|
* even when it adds no worktree. The worktree count therefore cannot tell the
|
|
* two apart; the tree's own root directory name can.
|
|
*
|
|
* Entries that are not worktrees (an attached plain folder) have no worktree to
|
|
* resolve against, and the editors API rejects paths that escape the worktree,
|
|
* so those nodes return `null` and callers hide the action.
|
|
*/
|
|
export function resolveFileTreeEditorTarget(
|
|
nodePath: string,
|
|
worktrees: WorktreeLike[],
|
|
treeRootName?: string,
|
|
): FileTreeEditorTarget | null {
|
|
const known = worktrees.filter((worktree) => worktree.path);
|
|
// Repository-backed sessions without a worktree fall back to the repository
|
|
// checkout server-side, and an unloaded tree has no root to compare against.
|
|
// Both keep the pass-through behaviour rather than hiding the action.
|
|
if (known.length === 0 || !treeRootName) return { filePath: nodePath };
|
|
|
|
const rooted = known.find((worktree) => pathBasename(worktree.path!) === treeRootName);
|
|
if (rooted) return { filePath: nodePath, worktreeId: rooted.id };
|
|
|
|
const separatorIndex = nodePath.indexOf("/");
|
|
const head = separatorIndex === -1 ? nodePath : nodePath.slice(0, separatorIndex);
|
|
const rest = separatorIndex === -1 ? "" : nodePath.slice(separatorIndex + 1);
|
|
const match = known.find((worktree) => pathBasename(worktree.path!) === head);
|
|
if (!match) return null;
|
|
return { filePath: rest, worktreeId: match.id };
|
|
}
|