100 lines · 3.4 KB · TypeScript
RawDownload
import type { ScopedThreadRef } from "@t3tools/contracts";import { isWindowsAbsolutePath, normalizeProjectPathForComparison } from "@t3tools/shared/path"; import { useRightPanelStore } from "./rightPanelStore";import { resolvePathLinkTarget } from "./terminal-links"; interface OpenDiffFilePrimaryActionInput {  readonly threadRef: ScopedThreadRef | null;  readonly filePath: string;  readonly activeCwd: string | undefined;  readonly repositoryRoot?: string | undefined;  readonly openInEditor: (targetPath: string) => void;} function normalizedRelativePathSegments(filePath: string): ReadonlyArray<string> | null {  if (filePath.startsWith("/") || isWindowsAbsolutePath(filePath) || /^[a-zA-Z]:/.test(filePath)) {    return null;  }   const segments = filePath    .replaceAll("\\", "/")    .split("/")    .filter((segment) => segment.length > 0 && segment !== ".");  if (segments.length === 0 || segments.includes("..")) return null;  return segments;} function repositoryRelativeWorkspaceSegments(  workspaceRoot: string | undefined,  repositoryRoot: string | undefined,): ReadonlyArray<string> | null {  if (!workspaceRoot || !repositoryRoot) return null;   const normalizedWorkspaceRoot = normalizeProjectPathForComparison(workspaceRoot);  const normalizedRepositoryRoot = normalizeProjectPathForComparison(repositoryRoot);  if (normalizedWorkspaceRoot === normalizedRepositoryRoot) return [];   const separator = normalizedRepositoryRoot.includes("\\") ? "\\" : "/";  const repositoryPrefix = normalizedRepositoryRoot.endsWith(separator)    ? normalizedRepositoryRoot    : `${normalizedRepositoryRoot}${separator}`;  if (!normalizedWorkspaceRoot.startsWith(repositoryPrefix)) return null;   return normalizedWorkspaceRoot    .slice(repositoryPrefix.length)    .split(/[\\/]+/)    .filter(Boolean);} export function resolveDiffPathForWorkspace(input: {  readonly filePath: string;  readonly workspaceRoot: string | undefined;  readonly repositoryRoot: string | undefined;}): string | null {  const fileSegments = normalizedRelativePathSegments(input.filePath);  if (!fileSegments) return null;   const workspaceSegments = repositoryRelativeWorkspaceSegments(    input.workspaceRoot,    input.repositoryRoot,  );  if (!workspaceSegments || workspaceSegments.length === 0) {    return fileSegments.join("/");  }   const caseInsensitive = input.repositoryRoot    ? isWindowsAbsolutePath(input.repositoryRoot)    : false;  const belongsToWorkspace = workspaceSegments.every((segment, index) => {    const candidate = fileSegments[index];    if (candidate === undefined) return false;    return caseInsensitive ? candidate.toLowerCase() === segment : candidate === segment;  });  if (!belongsToWorkspace) return null;   const relativeSegments = fileSegments.slice(workspaceSegments.length);  return relativeSegments.length > 0 ? relativeSegments.join("/") : null;} export function openDiffFilePrimaryAction({  threadRef,  filePath,  activeCwd,  repositoryRoot,  openInEditor,}: OpenDiffFilePrimaryActionInput): void {  const workspaceFilePath = resolveDiffPathForWorkspace({    filePath,    workspaceRoot: activeCwd,    repositoryRoot,  });  if (!workspaceFilePath) return;   if (threadRef) {    useRightPanelStore.getState().openFile(threadRef, workspaceFilePath);    return;  }   openInEditor(activeCwd ? resolvePathLinkTarget(workspaceFilePath, activeCwd) : workspaceFilePath);}