feat: show vault-relative path below title in editor

Notes in subdirectories now display their vault-relative path
(e.g. docs/adr/0001-tauri-stack.md) as small muted text below
the title field. Root-level notes show no path. Note list entries
do not display any path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-04-02 19:02:42 +02:00
parent cd39070569
commit 0fb115bbe1
2 changed files with 31 additions and 1 deletions

View File

@@ -101,4 +101,19 @@ describe('TitleField', () => {
window.dispatchEvent(new CustomEvent('laputa:focus-editor', { detail: { selectTitle: true } }))
expect(document.activeElement).toBe(input)
})
it('shows vault-relative path for notes in subdirectories', () => {
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')
})
it('hides path for notes at vault root', () => {
render(<TitleField title="Root Note" filename="root-note.md" notePath="/Users/luca/Laputa/root-note.md" vaultPath="/Users/luca/Laputa" onTitleChange={() => {}} />)
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
})
it('hides path when vaultPath is not provided', () => {
render(<TitleField title="Note" filename="note.md" onTitleChange={() => {}} />)
expect(screen.queryByTestId('title-field-path')).not.toBeInTheDocument()
})
})

View File

@@ -5,6 +5,10 @@ interface TitleFieldProps {
title: string
filename: string
editable?: boolean
/** Absolute path of the note file. */
notePath?: string
/** Absolute path of the vault root. */
vaultPath?: string
/** Called when the user finishes editing the title (blur or Enter). */
onTitleChange: (newTitle: string) => void
}
@@ -48,7 +52,7 @@ function useOptimisticTitle(title: string, onTitleChange: (t: string) => void) {
* Dedicated title input field above the editor.
* Displays the title as an editable field and shows the resulting filename below.
*/
export function TitleField({ title, filename, editable = true, onTitleChange }: TitleFieldProps) {
export function TitleField({ title, filename, editable = true, notePath, vaultPath, onTitleChange }: TitleFieldProps) {
const inputRef = useRef<HTMLInputElement>(null)
const { value, isEditing, handleFocus, commitTitle, revert, setEdit } =
useOptimisticTitle(title, onTitleChange)
@@ -80,6 +84,12 @@ export function TitleField({ title, filename, editable = true, onTitleChange }:
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 + '/', '')
: null
const showRelativePath = relativePath && relativePath.includes('/')
return (
<div className="title-field" data-testid="title-field">
<input
@@ -100,6 +110,11 @@ export function TitleField({ title, filename, editable = true, onTitleChange }:
{expectedSlug}.md
</span>
)}
{showRelativePath && (
<span className="title-field__path" data-testid="title-field-path" style={{ display: 'block', fontSize: 11, color: 'var(--muted-foreground)', marginTop: 2 }}>
{relativePath}
</span>
)}
</div>
)
}