fix: show type label for all notes in wiki-link autocomplete (#76)
* fix: show type label for all notes in wiki-link autocomplete Notes with the default 'Note' type (isA: null) had noteType set to undefined, hiding their type label. Now every note always shows its type in the autocomplete dropdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add wiki-autocomplete-type-fix wireframe Shows the fixed autocomplete dropdown where every note displays its type label, including default "Note" types in muted color. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
238
design/wiki-autocomplete-type-fix.pen
Normal file
238
design/wiki-autocomplete-type-fix.pen
Normal file
@@ -0,0 +1,238 @@
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wikiTypeFix",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"name": "Wiki-link Autocomplete — All Notes Show Type Labels",
|
||||
"clip": true,
|
||||
"width": 400,
|
||||
"height": 340,
|
||||
"fill": "#FFFFFF",
|
||||
"layout": "vertical",
|
||||
"gap": 0,
|
||||
"children": [
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfHeader",
|
||||
"name": "Context: Editor line with [[query",
|
||||
"width": "fill_container",
|
||||
"height": 40,
|
||||
"fill": "#FAFAFA",
|
||||
"padding": [10, 16],
|
||||
"alignItems": "center",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfQuery",
|
||||
"content": "Linking to [[no",
|
||||
"fontSize": 15,
|
||||
"fill": "#1a1a1a"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfMenu",
|
||||
"name": "Suggestion Menu — every item shows type",
|
||||
"width": 320,
|
||||
"fill": "#FFFFFF",
|
||||
"layout": "vertical",
|
||||
"gap": 0,
|
||||
"padding": 4,
|
||||
"cornerRadius": 6,
|
||||
"stroke": {
|
||||
"align": "outside",
|
||||
"thickness": 1,
|
||||
"fill": "rgba(0,0,0,0.1)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem1",
|
||||
"name": "Item: Daily Notes (Note — default type shown)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"fill": "rgba(21,93,255,0.08)",
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle1",
|
||||
"content": "Daily Notes",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType1",
|
||||
"content": "Note",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--muted-foreground, #888888)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem2",
|
||||
"name": "Item: Node.js Setup (Note — no frontmatter type)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle2",
|
||||
"content": "Node.js Setup",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType2",
|
||||
"content": "Note",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--muted-foreground, #888888)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem3",
|
||||
"name": "Item: Notification System (Project — explicit type)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle3",
|
||||
"content": "Notification System",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType3",
|
||||
"content": "Project",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--accent-red, #E53935)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem4",
|
||||
"name": "Item: NoSQL Patterns (Topic — explicit type)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle4",
|
||||
"content": "NoSQL Patterns",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType4",
|
||||
"content": "Topic",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--accent-green, #43A047)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem5",
|
||||
"name": "Item: Norman (Person — explicit type)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle5",
|
||||
"content": "Norman",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType5",
|
||||
"content": "Person",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--accent-yellow, #FDD835)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "frame",
|
||||
"id": "wtfItem6",
|
||||
"name": "Item: Notes on Architecture (Note — default type shown)",
|
||||
"width": "fill_container",
|
||||
"height": 32,
|
||||
"padding": [6, 10],
|
||||
"cornerRadius": 4,
|
||||
"alignItems": "center",
|
||||
"justifyContent": "space-between",
|
||||
"children": [
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfTitle6",
|
||||
"content": "Notes on Architecture",
|
||||
"fontSize": 13,
|
||||
"fill": "#1a1a1a",
|
||||
"fontWeight": "400"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "wtfType6",
|
||||
"content": "Note",
|
||||
"fontSize": 11,
|
||||
"fontWeight": "500",
|
||||
"fill": "var(--muted-foreground, #888888)",
|
||||
"opacity": 0.8
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"variables": {}
|
||||
}
|
||||
@@ -390,6 +390,35 @@ describe('wikilink autocomplete', () => {
|
||||
mockFilterSuggestionItems.mockImplementation((items: unknown[]) => items)
|
||||
})
|
||||
|
||||
it('always includes noteType for every item, including default Note type (regression)', async () => {
|
||||
const mixedEntries: VaultEntry[] = [
|
||||
{ ...mockEntry, title: 'My Project', filename: 'proj.md', path: '/vault/proj.md', isA: 'Project', aliases: [] },
|
||||
{ ...mockEntry, title: 'Plain Note', filename: 'plain.md', path: '/vault/plain.md', isA: null, aliases: [] },
|
||||
{ ...mockEntry, title: 'Explicit Note', filename: 'explicit.md', path: '/vault/explicit.md', isA: 'Note', aliases: [] },
|
||||
]
|
||||
capturedGetItems = null
|
||||
mockFilterSuggestionItems.mockImplementation((items: unknown[]) => items)
|
||||
render(
|
||||
<Editor
|
||||
{...defaultProps}
|
||||
tabs={[mockTab]}
|
||||
activeTabPath={mockEntry.path}
|
||||
entries={mixedEntries}
|
||||
/>
|
||||
)
|
||||
const items = await capturedGetItems!('Note')
|
||||
// Every item must have a defined noteType — none should be blank
|
||||
for (const item of items) {
|
||||
expect(item.noteType).toBeTruthy()
|
||||
expect(item.typeColor).toBeTruthy()
|
||||
}
|
||||
// Default notes (isA: null) should show 'Note' as their type
|
||||
const plainNote = items.find((i: { title: string }) => i.title === 'Plain Note')
|
||||
expect(plainNote).toBeDefined()
|
||||
expect(plainNote!.noteType).toBe('Note')
|
||||
mockFilterSuggestionItems.mockImplementation((items: unknown[]) => items)
|
||||
})
|
||||
|
||||
it('disambiguates entries with the same title by appending folder name', async () => {
|
||||
const sameTitle: VaultEntry[] = [
|
||||
{ ...mockEntry, title: 'Standup', filename: 'standup.md', path: '/vault/work/standup.md', aliases: [] },
|
||||
|
||||
@@ -165,8 +165,8 @@ function SingleEditorView({ editor, entries, onNavigateWikilink, onChange }: { e
|
||||
const final = disambiguateTitles(deduplicateByPath(filtered))
|
||||
return final.map(({ group, ...rest }) => ({
|
||||
...rest,
|
||||
noteType: group !== 'Note' ? group : undefined,
|
||||
typeColor: group !== 'Note' ? getTypeColor(group) : undefined,
|
||||
noteType: group,
|
||||
typeColor: getTypeColor(group),
|
||||
}))
|
||||
}, [baseItems, editor])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user