diff options
| author | Erik Demaine <[email protected]> | 2026-02-22 18:49:05 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-23 09:49:05 +1000 |
| commit | a74fedd23beecc70cb7cf7f07c6a14186787c960 (patch) | |
| tree | 42800bd7b9e4e3b8d100bf99cbab92774e6b881a /packages/app/src | |
| parent | eb64ce08b8a862c27b7dab58c3a8d2ea1981655f (diff) | |
| download | opencode-a74fedd23beecc70cb7cf7f07c6a14186787c960.tar.gz opencode-a74fedd23beecc70cb7cf7f07c6a14186787c960.zip | |
fix(desktop): change detection on Windows, especially Cygwin (#13659)
Co-authored-by: LukeParkerDev <[email protected]>
Diffstat (limited to 'packages/app/src')
| -rw-r--r-- | packages/app/src/context/file/path.test.ts | 8 | ||||
| -rw-r--r-- | packages/app/src/context/file/path.ts | 24 | ||||
| -rw-r--r-- | packages/app/src/i18n/en.ts | 1 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 7 |
4 files changed, 29 insertions, 11 deletions
diff --git a/packages/app/src/context/file/path.test.ts b/packages/app/src/context/file/path.test.ts index f2a3c44b6..7eb5e8b2a 100644 --- a/packages/app/src/context/file/path.test.ts +++ b/packages/app/src/context/file/path.test.ts @@ -13,6 +13,14 @@ describe("file path helpers", () => { expect(path.pathFromTab("other://src/app.ts")).toBeUndefined() }) + test("normalizes Windows absolute paths with mixed separators", () => { + const path = createPathHelpers(() => "C:\\repo") + expect(path.normalize("C:\\repo\\src\\app.ts")).toBe("src/app.ts") + expect(path.normalize("C:/repo/src/app.ts")).toBe("src/app.ts") + expect(path.normalize("file://C:/repo/src/app.ts")).toBe("src/app.ts") + expect(path.normalize("c:\\repo\\src\\app.ts")).toBe("src/app.ts") + }) + test("keeps query/hash stripping behavior stable", () => { expect(stripQueryAndHash("a/b.ts#L12?x=1")).toBe("a/b.ts") expect(stripQueryAndHash("a/b.ts?x=1#L12")).toBe("a/b.ts") diff --git a/packages/app/src/context/file/path.ts b/packages/app/src/context/file/path.ts index 859fdc040..6be7588f9 100644 --- a/packages/app/src/context/file/path.ts +++ b/packages/app/src/context/file/path.ts @@ -103,16 +103,20 @@ export function encodeFilePath(filepath: string): string { export function createPathHelpers(scope: () => string) { const normalize = (input: string) => { - const root = scope() - const prefix = root.endsWith("/") ? root : root + "/" - - let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))) - - if (path.startsWith(prefix)) { - path = path.slice(prefix.length) - } - - if (path.startsWith(root)) { + const root = scope().replace(/\\/g, "/") + + let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))).replace(/\\/g, "/") + + // Remove initial root prefix, if it's a complete match or followed by / + // (don't want /foo/bar to root of /f). + // For Windows paths, also check for case-insensitive match. + const windows = /^[A-Za-z]:/.test(root) + const canonRoot = windows ? root.toLowerCase() : root + const canonPath = windows ? path.toLowerCase() : path + if (canonPath.startsWith(canonRoot) && + (canonRoot.endsWith("/") || canonPath === canonRoot || + canonPath.startsWith(canonRoot + "/"))) { + // If we match canonRoot + "/", the slash will be removed below. path = path.slice(root.length) } diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 0fa3777dd..992509fcf 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -495,6 +495,7 @@ export const dict = { "session.review.change.other": "Changes", "session.review.loadingChanges": "Loading changes...", "session.review.empty": "No changes in this session yet", + "session.review.noVcs": "No git VCS detected, so session changes will not be detected", "session.review.noChanges": "No changes", "session.files.selectToOpen": "Select a file to open", diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index a3f4b7164..e0ef92682 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -274,6 +274,11 @@ export default function Page() { if (!hasReview()) return true return sync.data.session_diff[id] !== undefined }) + const reviewEmptyKey = createMemo(() => { + const project = sync.project + if (!project || project.vcs) return "session.review.empty" + return "session.review.noVcs" + }) let inputRef!: HTMLDivElement let promptDock: HTMLDivElement | undefined @@ -531,7 +536,7 @@ export default function Page() { ) : ( <div class={input.emptyClass}> <Mark class="w-14 opacity-10" /> - <div class="text-14-regular text-text-weak max-w-56">{language.t("session.review.empty")}</div> + <div class="text-14-regular text-text-weak max-w-56">{language.t(reviewEmptyKey())}</div> </div> ) } |
