feat: support mounted vault workspaces

This commit is contained in:
lucaronin
2026-05-11 16:37:06 +02:00
parent 5c05347690
commit 07edfac400
176 changed files with 8868 additions and 1535 deletions

View File

@@ -28,6 +28,19 @@ const makeEntry = (overrides: Partial<VaultEntry> = {}): VaultEntry => ({
...overrides,
})
const makeWorkspace = (path: string, alias = 'workspace'): NonNullable<VaultEntry['workspace']> => ({
id: alias,
label: alias,
alias,
path,
shortLabel: alias.slice(0, 2).toUpperCase(),
color: null,
icon: null,
mounted: true,
available: true,
defaultForNewNotes: false,
})
describe('needsRenameOnSave', () => {
it('returns true when filename does not match title slug', () => {
expect(needsRenameOnSave('My New Note', 'untitled-note.md')).toBe(true)
@@ -168,6 +181,61 @@ describe('useNoteRename hook', () => {
expect(onEntryRenamed).toHaveBeenCalled()
})
it.each([
{
name: 'title rename',
command: 'rename_note',
oldPath: '/team/old.md',
filename: 'old.md',
title: 'Old',
newPath: '/team/new.md',
run: async (hook: ReturnType<typeof renderUseNoteRename>['result']['current']) =>
hook.handleRenameNote('/team/old.md', 'New', '/personal', vi.fn()),
expected: { old_path: '/team/old.md', old_title: 'Old' },
},
{
name: 'filename rename',
command: 'rename_note_filename',
oldPath: '/team/old-name.md',
filename: 'old-name.md',
title: 'Project Kickoff',
newPath: '/team/manual-name.md',
run: async (hook: ReturnType<typeof renderUseNoteRename>['result']['current']) =>
hook.handleRenameFilename('/team/old-name.md', 'manual-name', '/personal', vi.fn()),
expected: { old_path: '/team/old-name.md', new_filename_stem: 'manual-name' },
},
])('uses the note workspace root for $name even when the app-level vault path differs', async ({
command,
oldPath,
filename,
title,
newPath,
run,
expected,
}) => {
const entry = makeEntry({
path: oldPath,
filename,
title,
workspace: makeWorkspace('/team', 'team'),
})
vi.mocked(mockInvoke).mockImplementation(async (cmd: string) => {
if (cmd === command) return { new_path: newPath, updated_files: 0, failed_updates: 0 }
if (cmd === 'get_note_content') return '# New\n'
return ''
})
const { result } = renderUseNoteRename([entry])
await act(async () => {
await run(result.current)
})
expect(mockInvoke).toHaveBeenCalledWith(command, expect.objectContaining({
vault_path: '/team',
...expected,
}))
})
it('handleRenameNote passes null old_title when entry not found', async () => {
await runHandleRenameNote()
@@ -373,4 +441,59 @@ describe('useNoteRename hook', () => {
}))
expect(setToastMessage).toHaveBeenCalledWith('Moved to "active"')
})
it('handleMoveNoteToWorkspace moves the note to a different workspace', async () => {
const sourceWorkspace = makeWorkspace('/personal', 'personal')
const destinationWorkspace = makeWorkspace('/team', 'team')
destinationWorkspace.label = 'Team'
const entry = makeEntry({
path: '/personal/notes/project-kickoff.md',
filename: 'project-kickoff.md',
title: 'Project Kickoff',
workspace: sourceWorkspace,
})
vi.mocked(mockInvoke).mockImplementation(async (cmd: string) => {
if (cmd === 'move_note_to_workspace') {
return {
new_path: '/team/notes/project-kickoff.md',
updated_files: 1,
failed_updates: 0,
}
}
if (cmd === 'get_note_content') return '# Project Kickoff\n'
return ''
})
const { result } = renderHook(() => useNoteRename(
{ entries: [entry], setToastMessage },
{ tabs: [], setTabs, activeTabPathRef, handleSwitchTab, updateTabContent },
))
const onEntryRenamed = vi.fn()
await act(async () => {
await result.current.handleMoveNoteToWorkspace(
'/personal/notes/project-kickoff.md',
destinationWorkspace,
'/personal',
onEntryRenamed,
)
})
expect(mockInvoke).toHaveBeenCalledWith('move_note_to_workspace', expect.objectContaining({
source_vault_path: '/personal',
destination_vault_path: '/team',
old_path: '/personal/notes/project-kickoff.md',
replacement_target: 'team/notes/project-kickoff',
}))
expect(onEntryRenamed).toHaveBeenCalledWith(
'/personal/notes/project-kickoff.md',
expect.objectContaining({
path: '/team/notes/project-kickoff.md',
filename: 'project-kickoff.md',
workspace: destinationWorkspace,
}),
'# Project Kickoff\n',
)
expect(setToastMessage).toHaveBeenCalledWith('Moved to "Team" and updated 1 note')
})
})