diff --git a/src/components/TitleField.test.tsx b/src/components/TitleField.test.tsx index 8299d285..8b51116e 100644 --- a/src/components/TitleField.test.tsx +++ b/src/components/TitleField.test.tsx @@ -36,8 +36,12 @@ describe('TitleField', () => { expect(input).toHaveValue('Keep This') }) - it('shows filename indicator when slug differs from current filename', () => { + it('shows filename indicator when slug differs and title is focused', () => { render( {}} />) + // Not shown when unfocused + expect(screen.queryByTestId('title-field-filename')).not.toBeInTheDocument() + // Shown when focused + fireEvent.focus(screen.getByTestId('title-field-input')) expect(screen.getByTestId('title-field-filename')).toHaveTextContent('my-note.md') }) @@ -148,4 +152,17 @@ describe('TitleField', () => { fireEvent.focus(screen.getByTestId('title-field-input')) expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument() }) + + it('resolves vault-relative path when paths differ by symlink prefix', () => { + // vaultPath uses symlink /Users/luca/... but notePath is canonical /Volumes/Jupiter/... + render( {}} />) + fireEvent.focus(screen.getByTestId('title-field-input')) + expect(screen.getByTestId('title-field-path')).toHaveTextContent('docs/adr/0001-tauri-stack') + }) + + it('handles vaultPath with trailing slash', () => { + render( {}} />) + fireEvent.focus(screen.getByTestId('title-field-input')) + expect(screen.getByTestId('title-field-path')).toHaveTextContent('docs/adr/0001-tauri-stack') + }) }) diff --git a/src/components/TitleField.tsx b/src/components/TitleField.tsx index 146d679f..df7feb46 100644 --- a/src/components/TitleField.tsx +++ b/src/components/TitleField.tsx @@ -119,15 +119,25 @@ export function TitleField({ title, filename, editable = true, notePath, vaultPa const currentStem = filename.replace(/\.md$/, '') // Compute vault-relative path (only for notes in subdirectories) - const relativePath = notePath && vaultPath - ? notePath.replace(vaultPath + '/', '').replace(/\.md$/, '') - : null + const relativePath = (() => { + if (!notePath || !vaultPath) return null + const vp = vaultPath.replace(/\/+$/, '') + const np = notePath.replace(/\.md$/, '') + if (np.startsWith(vp + '/')) return np.slice(vp.length + 1) + // Fallback: match by vault directory name for symlink-resolved paths + const vaultName = vp.split('/').pop() + if (!vaultName) return null + const segments = np.split('/') + const idx = segments.lastIndexOf(vaultName) + if (idx >= 0) return segments.slice(idx + 1).join('/') + return null + })() const isSubdirectory = relativePath != null && relativePath.includes('/') // Show path only when title is focused and note is in a subdirectory const showRelativePath = isFocused && isSubdirectory - // Show filename hint when slug differs, but suppress when path is already visible - const showFilename = !showRelativePath && (isEditing || currentStem !== expectedSlug) + // Show filename hint when slug differs, but only when focused (not always) + const showFilename = isFocused && !showRelativePath && (isEditing || currentStem !== expectedSlug) return (