feat: separate Backlinks and History with horizontal dividers in Inspector
Add visual separation between properties, Backlinks, and History sections using Separator components. Restyle Backlinks with arrow-up-right icon header and blue clickable links. Restyle History with counter-clockwise icon header, combined hash+message blue links, and muted date below. Both sections now hide entirely when empty instead of showing placeholder text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import type { VaultEntry } from '../../types'
|
||||
import { CaretRight, Trash } from '@phosphor-icons/react'
|
||||
import { getTypeColor } from '../../utils/typeColors'
|
||||
import { ArrowUpRight, Trash } from '@phosphor-icons/react'
|
||||
import { isEmoji } from '../../utils/emoji'
|
||||
import { entryStatusTitle } from './shared'
|
||||
import { StatusSuffix } from './LinkButton'
|
||||
@@ -11,23 +9,21 @@ export interface BacklinkItem {
|
||||
context: string | null
|
||||
}
|
||||
|
||||
function BacklinkEntry({ entry, context, typeEntryMap, onNavigate }: {
|
||||
function BacklinkEntry({ entry, context, onNavigate }: {
|
||||
entry: VaultEntry
|
||||
context: string | null
|
||||
typeEntryMap: Record<string, VaultEntry>
|
||||
onNavigate: (target: string) => void
|
||||
}) {
|
||||
const te = typeEntryMap[entry.isA ?? '']
|
||||
const isDimmed = entry.archived || entry.trashed
|
||||
return (
|
||||
<button
|
||||
className="flex w-full cursor-pointer flex-col items-start gap-0.5 border-none bg-transparent p-0 text-left hover:opacity-80"
|
||||
className="flex w-full cursor-pointer flex-col items-start gap-0.5 border-none bg-transparent p-0 text-left hover:underline"
|
||||
onClick={() => onNavigate(entry.title)}
|
||||
title={entryStatusTitle(entry)}
|
||||
>
|
||||
<span
|
||||
className="flex items-center gap-1 text-xs font-medium"
|
||||
style={{ color: isDimmed ? 'var(--muted-foreground)' : getTypeColor(entry.isA, te?.color) }}
|
||||
className="flex items-center gap-1 text-xs text-primary"
|
||||
style={isDimmed ? { color: 'var(--muted-foreground)' } : undefined}
|
||||
>
|
||||
{entry.trashed && <Trash size={12} className="shrink-0" />}
|
||||
{entry.icon && isEmoji(entry.icon) && <span className="shrink-0">{entry.icon}</span>}
|
||||
@@ -43,42 +39,28 @@ function BacklinkEntry({ entry, context, typeEntryMap, onNavigate }: {
|
||||
)
|
||||
}
|
||||
|
||||
export function BacklinksPanel({ backlinks, typeEntryMap, onNavigate }: {
|
||||
export function BacklinksPanel({ backlinks, onNavigate }: {
|
||||
backlinks: BacklinkItem[]
|
||||
typeEntryMap: Record<string, VaultEntry>
|
||||
onNavigate: (target: string) => void
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
|
||||
if (backlinks.length === 0) return null
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
className="font-mono-overline mb-2 flex w-full cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
data-testid="backlinks-toggle"
|
||||
>
|
||||
<CaretRight
|
||||
size={12}
|
||||
className="shrink-0 transition-transform"
|
||||
style={{ transform: expanded ? 'rotate(90deg)' : undefined }}
|
||||
/>
|
||||
Backlinks ({backlinks.length})
|
||||
</button>
|
||||
{expanded && (
|
||||
<div className="flex flex-col gap-1.5" data-testid="backlinks-list">
|
||||
{backlinks.map(({ entry, context }) => (
|
||||
<BacklinkEntry
|
||||
key={entry.path}
|
||||
entry={entry}
|
||||
context={context}
|
||||
typeEntryMap={typeEntryMap}
|
||||
onNavigate={onNavigate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<h4 className="font-mono-overline mb-2 flex items-center gap-1 text-muted-foreground">
|
||||
<ArrowUpRight size={12} className="shrink-0" />
|
||||
Backlinks
|
||||
</h4>
|
||||
<div className="flex flex-col gap-1.5" data-testid="backlinks-list">
|
||||
{backlinks.map(({ entry, context }) => (
|
||||
<BacklinkEntry
|
||||
key={entry.path}
|
||||
entry={entry}
|
||||
context={context}
|
||||
onNavigate={onNavigate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ArrowCounterClockwise } from '@phosphor-icons/react'
|
||||
import type { GitCommit } from '../../types'
|
||||
|
||||
function formatRelativeDate(timestamp: number): string {
|
||||
@@ -11,26 +12,32 @@ function formatRelativeDate(timestamp: number): string {
|
||||
}
|
||||
|
||||
export function GitHistoryPanel({ commits, onViewCommitDiff }: { commits: GitCommit[]; onViewCommitDiff?: (commitHash: string) => void }) {
|
||||
if (commits.length === 0) return null
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4 className="font-mono-overline mb-2 text-muted-foreground">History</h4>
|
||||
{commits.length === 0
|
||||
? <p className="m-0 text-[13px] text-muted-foreground">No revision history</p>
|
||||
: (
|
||||
<div className="flex flex-col gap-2.5">
|
||||
{commits.map((c) => (
|
||||
<div key={c.hash} style={{ borderLeft: '2px solid var(--border)', paddingLeft: 10 }}>
|
||||
<div className="mb-0.5 flex items-center justify-between">
|
||||
<button className="border-none bg-transparent p-0 font-mono text-primary cursor-pointer hover:underline" style={{ fontSize: 11 }} onClick={() => onViewCommitDiff?.(c.hash)} title={`View diff for ${c.shortHash}`}>{c.shortHash}</button>
|
||||
<span className="text-muted-foreground" style={{ fontSize: 10 }}>{formatRelativeDate(c.date)}</span>
|
||||
</div>
|
||||
<div className="truncate text-xs text-secondary-foreground">{c.message}</div>
|
||||
{c.author && <div className="truncate text-muted-foreground" style={{ fontSize: 10, marginTop: 1 }}>{c.author}</div>}
|
||||
</div>
|
||||
))}
|
||||
<h4 className="font-mono-overline mb-2 flex items-center gap-1 text-muted-foreground">
|
||||
<ArrowCounterClockwise size={12} className="shrink-0" />
|
||||
History
|
||||
</h4>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
{commits.map((c) => (
|
||||
<div key={c.hash} style={{ borderLeft: '2px solid var(--border)', paddingLeft: 10 }}>
|
||||
<button
|
||||
className="mb-0.5 w-full cursor-pointer truncate border-none bg-transparent p-0 text-left text-xs text-primary hover:underline"
|
||||
onClick={() => onViewCommitDiff?.(c.hash)}
|
||||
title={`View diff for ${c.shortHash}`}
|
||||
>
|
||||
<span className="font-mono" style={{ fontSize: 11 }}>{c.shortHash}</span>
|
||||
{' · '}
|
||||
{c.message}
|
||||
</button>
|
||||
<div className="text-muted-foreground" style={{ fontSize: 10 }}>
|
||||
{formatRelativeDate(c.date)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user