diff --git a/design/hide-backlinks-empty.pen b/design/hide-backlinks-empty.pen
new file mode 100644
index 00000000..ae27a8df
--- /dev/null
+++ b/design/hide-backlinks-empty.pen
@@ -0,0 +1,65 @@
+{
+ "children": [
+ {
+ "type": "frame",
+ "id": "hideBacklinksEmpty_before",
+ "x": 0,
+ "y": 0,
+ "name": "Hide Empty Backlinks Label — Before (shows No backlinks label)",
+ "width": 280,
+ "height": 80,
+ "fill": "#ffffff",
+ "gap": 4,
+ "padding": 12,
+ "alignItems": "flex-start",
+ "children": [
+ {
+ "type": "text",
+ "id": "bl_header_before",
+ "name": "section-header",
+ "fill": "#6b7280",
+ "content": "Backlinks",
+ "fontFamily": "Inter",
+ "fontSize": 10,
+ "fontWeight": "600",
+ "letterSpacing": 1.2
+ },
+ {
+ "type": "text",
+ "id": "bl_empty_before",
+ "name": "empty-label",
+ "fill": "#9ca3af",
+ "content": "No backlinks",
+ "fontFamily": "Inter",
+ "fontSize": 12,
+ "fontWeight": "normal"
+ }
+ ]
+ },
+ {
+ "type": "frame",
+ "id": "hideBacklinksEmpty_after",
+ "x": 320,
+ "y": 0,
+ "name": "Hide Empty Backlinks Label — After (section hidden when empty)",
+ "width": 280,
+ "height": 80,
+ "fill": "#ffffff",
+ "gap": 4,
+ "padding": 12,
+ "alignItems": "flex-start",
+ "children": [
+ {
+ "type": "text",
+ "id": "bl_note_after",
+ "name": "note",
+ "fill": "#9ca3af",
+ "content": "(Backlinks, Referenced By, and Relations sections are hidden when empty — no placeholder label shown)",
+ "fontFamily": "Inter",
+ "fontSize": 11,
+ "fontWeight": "normal"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx
index b95a18c7..edf7e1da 100644
--- a/src/components/Inspector.test.tsx
+++ b/src/components/Inspector.test.tsx
@@ -159,7 +159,7 @@ describe('Inspector', () => {
expect(onNavigate).toHaveBeenCalledWith('responsibility/grow-newsletter')
})
- it('shows "No relationships" when entry has no belongsTo/relatedTo', () => {
+ it('hides relationships label when entry has no belongsTo/relatedTo', () => {
const noRels = { ...mockEntry, belongsTo: [], relatedTo: [] }
const contentNoRels = `---
title: Test Project
@@ -172,7 +172,7 @@ Status: Active
This is a test note with some words to count.
`
render()
- expect(screen.getByText('No relationships')).toBeInTheDocument()
+ expect(screen.queryByText('No relationships')).not.toBeInTheDocument()
})
it('shows backlinks from notes that reference the current note via outgoingLinks', () => {
@@ -197,8 +197,8 @@ This is a test note with some words to count.
entries={[mockEntry, { ...referrerEntry, outgoingLinks: [] }]}
/>
)
- // Initially no backlinks because referrer has empty outgoingLinks
- expect(screen.getByText('No backlinks')).toBeInTheDocument()
+ // Initially no backlinks — section is hidden entirely
+ expect(screen.queryByText('Backlinks')).not.toBeInTheDocument()
// Rerender with updated outgoingLinks (simulates adding [[Test Project]] to referrer)
rerender(
@@ -212,7 +212,7 @@ This is a test note with some words to count.
expect(screen.getByText('Referrer Note')).toBeInTheDocument()
})
- it('shows "No backlinks" when no notes reference the current note', () => {
+ it('hides backlinks section when no notes reference the current note', () => {
render(
)
- expect(screen.getByText('No backlinks')).toBeInTheDocument()
+ expect(screen.queryByText('No backlinks')).not.toBeInTheDocument()
+ expect(screen.queryByText('Backlinks')).not.toBeInTheDocument()
})
it('navigates when a backlink is clicked', () => {
@@ -499,7 +500,7 @@ Status: Active
expect(allTwos.some(el => el.classList.contains('ml-1'))).toBe(true)
})
- it('shows "No references" when no entries reference the current note', () => {
+ it('hides referenced-by section when no entries reference the current note', () => {
render(
)
- expect(screen.getByText('No references')).toBeInTheDocument()
+ expect(screen.queryByText('No references')).not.toBeInTheDocument()
+ expect(screen.queryByText('Referenced by')).not.toBeInTheDocument()
})
it('navigates when clicking a referenced-by entry', () => {
@@ -549,8 +551,8 @@ Status: Active
)
// On Writing Well references responsibility via "Belongs to" (path match), not via "Type"
// But the Type entry is at type/responsibility.md, so wikilinks to
- // responsibility/grow-newsletter won't match. Should show "No references"
- expect(screen.getByText('No references')).toBeInTheDocument()
+ // responsibility/grow-newsletter won't match. Section should be hidden
+ expect(screen.queryByText('Referenced by')).not.toBeInTheDocument()
})
it('resolves references via aliased wikilinks', () => {
@@ -612,8 +614,8 @@ Status: Active
// noteA shows in Referenced By (via Belongs to)
expect(screen.getByText(/via Belongs to/)).toBeInTheDocument()
expect(screen.getByText('On Writing Well')).toBeInTheDocument()
- // But NOT in Backlinks (even though outgoingLinks matches)
- expect(screen.getByText('No backlinks')).toBeInTheDocument()
+ // But NOT in Backlinks (even though outgoingLinks matches) — section hidden
+ expect(screen.queryByText('Backlinks')).not.toBeInTheDocument()
})
it('does not show self-references', () => {
@@ -633,7 +635,7 @@ Status: Active
/>
)
- expect(screen.getByText('No references')).toBeInTheDocument()
+ expect(screen.queryByText('Referenced by')).not.toBeInTheDocument()
})
})
})
diff --git a/src/components/Inspector.tsx b/src/components/Inspector.tsx
index 59815747..3a695faf 100644
--- a/src/components/Inspector.tsx
+++ b/src/components/Inspector.tsx
@@ -99,22 +99,7 @@ function InspectorHeader({ collapsed, onToggle }: { collapsed: boolean; onToggle
function EmptyInspector() {
return (
- <>
-
-
-
-
Referenced by
-
No references
-
-
-
Backlinks
-
No backlinks
-
-
-
History
-
No revision history
-
- >
+
)
}
diff --git a/src/components/InspectorPanels.test.tsx b/src/components/InspectorPanels.test.tsx
index 2f03abf3..9744c4c4 100644
--- a/src/components/InspectorPanels.test.tsx
+++ b/src/components/InspectorPanels.test.tsx
@@ -81,7 +81,7 @@ describe('DynamicRelationshipsPanel', () => {
expect(chip!.style.color).toBe(expectedColor)
})
- it('shows "No relationships" when frontmatter has no relations', () => {
+ it('hides empty state label when frontmatter has no relations', () => {
render(
{
onNavigate={onNavigate}
/>
)
- expect(screen.getByText('No relationships')).toBeInTheDocument()
+ expect(screen.queryByText('No relationships')).not.toBeInTheDocument()
})
it('renders relationship groups with wikilinks', () => {
@@ -414,9 +414,9 @@ describe('BacklinksPanel', () => {
vi.clearAllMocks()
})
- it('shows "No backlinks" when empty', () => {
- render()
- expect(screen.getByText('No backlinks')).toBeInTheDocument()
+ it('renders nothing when empty', () => {
+ const { container } = render()
+ expect(container.innerHTML).toBe('')
})
it('renders backlink entries', () => {
@@ -450,9 +450,9 @@ describe('ReferencedByPanel', () => {
vi.clearAllMocks()
})
- it('shows "No references" when items is empty', () => {
- render()
- expect(screen.getByText('No references')).toBeInTheDocument()
+ it('renders nothing when empty', () => {
+ const { container } = render()
+ expect(container.innerHTML).toBe('')
})
it('renders referenced-by entries grouped by relationship key', () => {
diff --git a/src/components/InspectorPanels.tsx b/src/components/InspectorPanels.tsx
index 01b449d9..8dfe4035 100644
--- a/src/components/InspectorPanels.tsx
+++ b/src/components/InspectorPanels.tsx
@@ -364,16 +364,13 @@ export function DynamicRelationshipsPanel({ frontmatter, entries, typeEntryMap,
return (
- {relationshipEntries.length === 0
- ?
No relationships
- : relationshipEntries.map(({ key, refs }) => (
-
handleRemoveRef(key, ref) : undefined}
- onAddRef={canEdit ? (noteTitle) => handleAddRef(key, noteTitle) : undefined}
- />
- ))
- }
+ {relationshipEntries.map(({ key, refs }) => (
+ handleRemoveRef(key, ref) : undefined}
+ onAddRef={canEdit ? (noteTitle) => handleAddRef(key, noteTitle) : undefined}
+ />
+ ))}
{onAddProperty
?
:
@@ -383,24 +380,20 @@ export function DynamicRelationshipsPanel({ frontmatter, entries, typeEntryMap,
}
export function BacklinksPanel({ backlinks, typeEntryMap, onNavigate }: { backlinks: VaultEntry[]; typeEntryMap: Record; onNavigate: (target: string) => void }) {
+ if (backlinks.length === 0) return null
return (
- Backlinks {backlinks.length > 0 && {backlinks.length}}
+ Backlinks {backlinks.length}
- {backlinks.length === 0
- ?
No backlinks
- : (
-
- {backlinks.map((e) => {
- const te = typeEntryMap[e.isA ?? '']
- return (
- onNavigate(e.title)} title={e.trashed ? 'Trashed' : e.archived ? 'Archived' : undefined} TypeIcon={getTypeIcon(e.isA, te?.icon)} />
- )
- })}
-
- )
- }
+
+ {backlinks.map((e) => {
+ const te = typeEntryMap[e.isA ?? '']
+ return (
+ onNavigate(e.title)} title={e.trashed ? 'Trashed' : e.archived ? 'Archived' : undefined} TypeIcon={getTypeIcon(e.isA, te?.icon)} />
+ )
+ })}
+
)
}
@@ -425,42 +418,39 @@ export function ReferencedByPanel({ items, typeEntryMap, onNavigate }: {
return Array.from(map.entries())
}, [items])
+ if (items.length === 0) return null
+
return (
- Referenced by {items.length > 0 && {items.length}}
+ Referenced by {items.length}
- {items.length === 0
- ?
No references
- : (
-
- {grouped.map(([viaKey, groupEntries]) => (
-
-
- via {viaKey}
-
-
- {groupEntries.map((e) => {
- const te = typeEntryMap[e.isA ?? '']
- return (
- onNavigate(e.title)}
- title={e.trashed ? 'Trashed' : e.archived ? 'Archived' : undefined}
- TypeIcon={getTypeIcon(e.isA, te?.icon)}
- />
- )
- })}
-
-
- ))}
+
+ {grouped.map(([viaKey, groupEntries]) => (
+
+
+ via {viaKey}
+
+
+ {groupEntries.map((e) => {
+ const te = typeEntryMap[e.isA ?? '']
+ return (
+ onNavigate(e.title)}
+ title={e.trashed ? 'Trashed' : e.archived ? 'Archived' : undefined}
+ TypeIcon={getTypeIcon(e.isA, te?.icon)}
+ />
+ )
+ })}
+
- )
- }
+ ))}
+
)
}