diff --git a/src/renderer/src/components/native-chat/native-chat-file-link.test.ts b/src/renderer/src/components/native-chat/native-chat-file-link.test.ts index 1abade7d057..740a7fb93eb 100644 --- a/src/renderer/src/components/native-chat/native-chat-file-link.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-file-link.test.ts @@ -299,4 +299,25 @@ describe('resolveNativeChatFileLink', () => { it('ignores http links so normal markdown navigation can handle them', () => { expect(resolveNativeChatFileLink('https://example.com/docs/guide.md', context)).toBeNull() }) + + it('reads URL-style /D:/ targets as the drive only in a Windows workspace', () => { + const windowsContext = { ...context, worktreePath: 'D:/repo' } + for (const href of ['/D:/Temp/report.txt', createNativeChatFileHref('/D:/Temp/report.txt')]) { + expect(resolveNativeChatFileLink(href, windowsContext)?.absolutePath).toBe( + 'D:/Temp/report.txt' + ) + } + expect( + resolveNativeChatFileLink( + createNativeChatFileHref('/D:/Temp/report.txt', 'literal'), + windowsContext + )?.absolutePath + ).toBe('D:/Temp/report.txt') + expect( + resolveNativeChatFileLink('//server/share/report.txt', windowsContext)?.absolutePath + ).toBe('//server/share/report.txt') + expect(resolveNativeChatFileLink('/D:/Temp/report.txt', context)?.absolutePath).toBe( + '/D:/Temp/report.txt' + ) + }) }) diff --git a/src/renderer/src/components/native-chat/native-chat-file-link.ts b/src/renderer/src/components/native-chat/native-chat-file-link.ts index 99bab0bb467..73eb4b9adf3 100644 --- a/src/renderer/src/components/native-chat/native-chat-file-link.ts +++ b/src/renderer/src/components/native-chat/native-chat-file-link.ts @@ -1,5 +1,10 @@ import { chatWorkspaceByTabId } from '@/lib/structured-chat-tab-index' -import { routeNativeChatHref } from '../../../../shared/native-chat-href-routing' +import { + routeNativeChatHref, + type NativeChatHrefRoute +} from '../../../../shared/native-chat-href-routing' +import { WINDOWS_DRIVE_PATH_PREFIX } from '../../../../shared/file-uri-path' +import { normalizeAbsolutePath } from '../../../../shared/terminal-path-normalization' import { parseExplicitFileLinkTarget, resolveExplicitFileLinkTarget, @@ -130,6 +135,19 @@ function resolvePathText( } } +// Chats open a drive-letter workspace as native Windows (no WSL mapping), so URL-style /D:/… there is D:. +function routeNativeChatFileHref( + href: string | undefined, + worktreePath: string +): NativeChatHrefRoute { + const route = routeNativeChatHref(href) + return route.kind === 'file' && + WINDOWS_DRIVE_PATH_PREFIX.test(route.pathText) && + normalizeAbsolutePath(worktreePath)?.rootKind === 'windows' + ? { ...route, pathText: route.pathText.slice(1) } + : route +} + export function resolveNativeChatFileLink( href: string | undefined, context: NativeChatFileLinkContext | null @@ -137,7 +155,7 @@ export function resolveNativeChatFileLink( if (!context) { return null } - const route = routeNativeChatHref(href) + const route = routeNativeChatFileHref(href, context.worktreePath) if (route.kind !== 'file') { return null } diff --git a/src/shared/file-uri-path.ts b/src/shared/file-uri-path.ts index 6f2b6333e58..6a473b75563 100644 --- a/src/shared/file-uri-path.ts +++ b/src/shared/file-uri-path.ts @@ -1,4 +1,4 @@ -const WINDOWS_DRIVE_PATH_PREFIX = /^\/[A-Za-z]:\// +export const WINDOWS_DRIVE_PATH_PREFIX = /^\/[A-Za-z]:\// const UNC_PATH_PREFIX = /^(?:\\\\|\/\/)([^\\/]+)[\\/]+([^\\/]+)(?:[\\/](.*))?$/ function encodePathSegments(path: string): string {