fix(desktop): Windows path separator handling across 10 components
On Windows, Tauri returns paths with backslash separators. Multiple
components used .split('/') to extract filenames/segments, which
returned the entire path as a single element on Windows. Changed all
instances to .split(/[/\]/) to handle both Unix and Windows paths.
Affected: ProjectContextChip, ToolCallBlock, PermissionDialog,
ToolCallGroup, FileSearchMenu, InlineImageGallery,
LocalSlashCommandPanel, ProjectFilter, StatusBar, DirectoryPicker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -137,8 +137,8 @@ export const FileSearchMenu = forwardRef<FileSearchMenuHandle, Props>(({ cwd, fi
|
||||
// Build breadcrumb segments from current path relative to cwd
|
||||
const breadcrumbs: string[] = []
|
||||
if (currentPath !== cwd && currentPath.startsWith(cwd)) {
|
||||
const rel = currentPath.slice(cwd.length).replace(/^\//, '')
|
||||
if (rel) breadcrumbs.push(...rel.split('/'))
|
||||
const rel = currentPath.slice(cwd.length).replace(/^[/\\]/, '')
|
||||
if (rel) breadcrumbs.push(...rel.split(/[/\\]/))
|
||||
}
|
||||
|
||||
const dirs = entries.filter((e) => e.isDirectory)
|
||||
@@ -153,7 +153,7 @@ export const FileSearchMenu = forwardRef<FileSearchMenuHandle, Props>(({ cwd, fi
|
||||
{/* Header with path */}
|
||||
<div className="flex items-center gap-1.5 border-b border-[var(--color-border)] px-3 py-2 text-[11px]">
|
||||
<span className="material-symbols-outlined text-[14px] text-[var(--color-text-tertiary)]">folder_open</span>
|
||||
<span className="text-[var(--color-text-tertiary)] font-mono">{cwd.split('/').pop() || cwd}</span>
|
||||
<span className="text-[var(--color-text-tertiary)] font-mono">{cwd.split(/[/\\]/).pop() || cwd}</span>
|
||||
{breadcrumbs.map((seg, i) => (
|
||||
<span key={i} className="flex items-center gap-1">
|
||||
<span className="text-[var(--color-text-tertiary)]">/</span>
|
||||
|
||||
@@ -32,7 +32,7 @@ function fileUrl(filePath: string): string {
|
||||
}
|
||||
|
||||
function fileName(filePath: string): string {
|
||||
return filePath.split('/').pop() || filePath
|
||||
return filePath.split(/[/\\]/).pop() || filePath
|
||||
}
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -59,7 +59,7 @@ function scopeLabel(scope: string, t: ReturnType<typeof useTranslation>) {
|
||||
|
||||
function projectBadge(path?: string, t?: ReturnType<typeof useTranslation>) {
|
||||
if (!path || !t) return null
|
||||
const label = path.replace(/\/$/, '').split('/').pop() || path
|
||||
const label = path.replace(/\/$/, '').split(/[/\\]/).pop() || path
|
||||
return t('slash.mcp.projectBadge', { name: label })
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ function extractToolDetails(toolName: string, input: unknown, t: (key: Translati
|
||||
function getPermissionTitle(toolName: string, input: unknown, t: (key: TranslationKey, params?: Record<string, string | number>) => string) {
|
||||
const obj = (input && typeof input === 'object') ? input as Record<string, unknown> : {}
|
||||
const filePath = typeof obj.file_path === 'string' ? obj.file_path : ''
|
||||
const fileName = filePath ? filePath.split('/').pop() || filePath : ''
|
||||
const fileName = filePath ? filePath.split(/[/\\]/).pop() || filePath : ''
|
||||
|
||||
switch (toolName) {
|
||||
case 'Edit':
|
||||
|
||||
@@ -68,7 +68,7 @@ export function ToolCallBlock({ toolName, input, result, compact = false }: Prop
|
||||
</span>
|
||||
{filePath ? (
|
||||
<span className="min-w-0 flex-1 truncate font-[var(--font-mono)] text-[11px] text-[var(--color-text-tertiary)]">
|
||||
{filePath.split('/').pop()}
|
||||
{filePath.split(/[/\\]/).pop()}
|
||||
</span>
|
||||
) : summary ? (
|
||||
<span className="min-w-0 flex-1 truncate font-[var(--font-mono)] text-[11px] text-[var(--color-text-tertiary)]">
|
||||
|
||||
@@ -518,7 +518,7 @@ function formatRecentToolUseSummary(
|
||||
case 'Bash':
|
||||
return `Bash · ${typeof input.command === 'string' ? input.command : ''}${suffix}`
|
||||
case 'Read':
|
||||
return `Read · ${typeof input.file_path === 'string' ? input.file_path.split('/').pop() : 'file'}${suffix}`
|
||||
return `Read · ${typeof input.file_path === 'string' ? input.file_path.split(/[/\\]/).pop() : 'file'}${suffix}`
|
||||
case 'Glob':
|
||||
return `Glob · ${typeof input.pattern === 'string' ? input.pattern : ''}${suffix}`
|
||||
case 'Grep':
|
||||
|
||||
@@ -284,8 +284,8 @@ function compareProjectOptions(a: ProjectOption, b: ProjectOption) {
|
||||
|
||||
function fallbackProjectTitle(projectPath: string, fallback: string) {
|
||||
if (!projectPath || projectPath === '_unknown') return fallback
|
||||
if (projectPath.includes('/')) {
|
||||
return projectPath.split('/').filter(Boolean).pop() || fallback
|
||||
if (projectPath.includes('/') || projectPath.includes('\\')) {
|
||||
return projectPath.split(/[/\\]/).filter(Boolean).pop() || fallback
|
||||
}
|
||||
|
||||
const segments = projectPath.split('-').filter(Boolean)
|
||||
|
||||
@@ -12,7 +12,7 @@ export function StatusBar() {
|
||||
const projectPath = useSessionStore((s) => s.sessions.find((session) => session.id === activeTabId)?.projectPath)
|
||||
|
||||
const projectName = projectPath
|
||||
? projectPath.split('-').filter(Boolean).pop() || ''
|
||||
? projectPath.split(/[/\\]/).filter(Boolean).pop() || ''
|
||||
: ''
|
||||
const modelLabel = runtimeSelection?.modelId ?? currentModel?.name ?? null
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ export function DirectoryPicker({ value, onChange }: Props) {
|
||||
<span className="material-symbols-outlined text-[14px] text-[var(--color-text-secondary)]">folder</span>
|
||||
)}
|
||||
<span className="font-medium text-[var(--color-text-primary)]">
|
||||
{selectedProject?.repoName || selectedProject?.projectName || value.split('/').pop()}
|
||||
{selectedProject?.repoName || selectedProject?.projectName || value.split(/[/\\]/).pop()}
|
||||
</span>
|
||||
{selectedProject?.branch && (
|
||||
<>
|
||||
@@ -259,7 +259,7 @@ export function DirectoryPicker({ value, onChange }: Props) {
|
||||
{'← ' + t('dirPicker.recent')}
|
||||
</button>
|
||||
<button onClick={() => loadBrowseDir('/')} className="text-[10px] text-[var(--color-text-tertiary)] hover:text-[var(--color-text-primary)]">/</button>
|
||||
{browsePath.split('/').filter(Boolean).map((seg, i, arr) => (
|
||||
{browsePath.split(/[/\\]/).filter(Boolean).map((seg, i, arr) => (
|
||||
<span key={i} className="flex items-center gap-1">
|
||||
<span className="text-[10px] text-[var(--color-text-tertiary)]">/</span>
|
||||
<button
|
||||
|
||||
@@ -5,7 +5,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export function ProjectContextChip({ workDir, repoName, branch }: Props) {
|
||||
const label = branch ? (repoName || workDir?.split('/').pop() || '') : (workDir?.split('/').pop() || repoName || '')
|
||||
const label = branch ? (repoName || workDir?.split(/[/\\]/).pop() || '') : (workDir?.split(/[/\\]/).pop() || repoName || '')
|
||||
|
||||
if (!label) return null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user