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:
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user