fix: note path below title — show only on focus, remove duplicate filename

Two bugs fixed in TitleField:

1. Vault-relative path was always visible for subdirectory notes. Now
   only appears when the title input is focused, hidden on blur.

2. Both bare filename and vault-relative path rendered simultaneously.
   Now the filename hint is suppressed when the path is visible, so
   only one line shows (e.g. `docs/adr/0001-tauri-stack`). Also
   strips the .md extension from the displayed path.

Root-level notes continue to show no path (unchanged).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-04-03 19:33:52 +02:00
parent e6e8e8aa00
commit da51174777
2 changed files with 30 additions and 8 deletions

View File

@@ -117,18 +117,35 @@ describe('TitleField', () => {
expect(input).toHaveValue('Untitled note')
})
it('shows vault-relative path for notes in subdirectories', () => {
it('shows vault-relative path (without .md) only when title is focused', () => {
render(<TitleField title="ADR" filename="0001-tauri-stack.md" notePath="/Users/luca/Laputa/docs/adr/0001-tauri-stack.md" vaultPath="/Users/luca/Laputa" onTitleChange={() => {}} />)
expect(screen.getByTestId('title-field-path')).toHaveTextContent('docs/adr/0001-tauri-stack.md')
// Path hidden by default
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
// Focus title → path appears
fireEvent.focus(screen.getByTestId('title-field-input'))
expect(screen.getByTestId('title-field-path')).toHaveTextContent('docs/adr/0001-tauri-stack')
// No bare filename shown when path is visible
expect(screen.queryByTestId('title-field-filename')).not.toBeInTheDocument()
})
it('hides path for notes at vault root', () => {
it('hides path on blur', () => {
render(<TitleField title="ADR" filename="0001-tauri-stack.md" notePath="/Users/luca/Laputa/docs/adr/0001-tauri-stack.md" vaultPath="/Users/luca/Laputa" onTitleChange={() => {}} />)
const input = screen.getByTestId('title-field-input')
fireEvent.focus(input)
expect(screen.getByTestId('title-field-path')).toBeInTheDocument()
fireEvent.blur(input)
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
})
it('hides path for notes at vault root even when focused', () => {
render(<TitleField title="Root Note" filename="root-note.md" notePath="/Users/luca/Laputa/root-note.md" vaultPath="/Users/luca/Laputa" onTitleChange={() => {}} />)
fireEvent.focus(screen.getByTestId('title-field-input'))
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
})
it('hides path when vaultPath is not provided', () => {
render(<TitleField title="Note" filename="note.md" onTitleChange={() => {}} />)
fireEvent.focus(screen.getByTestId('title-field-input'))
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
})
})

View File

@@ -63,6 +63,7 @@ function useOptimisticTitle(title: string, onTitleChange: (t: string) => void) {
*/
export function TitleField({ title, filename, editable = true, notePath, vaultPath, onTitleChange }: TitleFieldProps) {
const inputRef = useRef<HTMLInputElement>(null)
const [isFocused, setIsFocused] = useState(false)
const { value, isEditing, handleFocus, commitTitle, revert, setEdit } =
useOptimisticTitle(title, onTitleChange)
@@ -91,13 +92,17 @@ export function TitleField({ title, filename, editable = true, notePath, vaultPa
const expectedSlug = slugify(value.trim() || title)
const currentStem = filename.replace(/\.md$/, '')
const showFilename = isEditing || currentStem !== expectedSlug
// Compute vault-relative path (only for notes in subdirectories)
const relativePath = notePath && vaultPath
? notePath.replace(vaultPath + '/', '')
? notePath.replace(vaultPath + '/', '').replace(/\.md$/, '')
: null
const showRelativePath = relativePath && relativePath.includes('/')
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)
return (
<div className="title-field" data-testid="title-field">
@@ -106,8 +111,8 @@ export function TitleField({ title, filename, editable = true, notePath, vaultPa
className="title-field__input"
value={value}
onChange={e => setEdit(e.target.value)}
onFocus={handleFocus}
onBlur={commitTitle}
onFocus={() => { setIsFocused(true); handleFocus() }}
onBlur={() => { setIsFocused(false); commitTitle() }}
onKeyDown={handleKeyDown}
disabled={!editable}
placeholder="Untitled"