diff --git a/src/components/TitleField.test.tsx b/src/components/TitleField.test.tsx index 0121ff76..8299d285 100644 --- a/src/components/TitleField.test.tsx +++ b/src/components/TitleField.test.tsx @@ -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( {}} />) - 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( {}} />) + 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( {}} />) + fireEvent.focus(screen.getByTestId('title-field-input')) expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument() }) it('hides path when vaultPath is not provided', () => { render( {}} />) + fireEvent.focus(screen.getByTestId('title-field-input')) expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument() }) }) diff --git a/src/components/TitleField.tsx b/src/components/TitleField.tsx index 8e41d046..6b500446 100644 --- a/src/components/TitleField.tsx +++ b/src/components/TitleField.tsx @@ -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(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 (
@@ -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"