MCPcopy Create free account
hub / github.com/ZenNotes/zennotes / OutlinePanel

Function OutlinePanel

packages/app-core/src/components/OutlinePanel.tsx:30–125  ·  view source on GitHub ↗
({ note, activeLine, onJump }: Props)

Source from the content-addressed store, hash-verified

28}
29
30export function OutlinePanel({ note, activeLine, onJump }: Props): JSX.Element {
31 const items = useMemo(() => parseOutline(note.body), [note.body])
32 const [query, setQuery] = useState('')
33 const activeItemRef = useRef<HTMLLIElement | null>(null)
34 const width = useStore((s) => s.panelWidths.outline)
35 const setPanelWidth = useStore((s) => s.setPanelWidth)
36 const { startResize } = usePanelResize(width, (px) => setPanelWidth('outline', px))
37
38 // Reset the filter when the note changes so the outline reflects the
39 // new document from the top.
40 useEffect(() => setQuery(''), [note.path])
41
42 const filtered = useMemo(() => {
43 const q = query.trim().toLowerCase()
44 if (!q) return items
45 return items.filter((item) => item.text.toLowerCase().includes(q))
46 }, [items, query])
47
48 // Keep the active heading in view as the user scrolls. `nearest`
49 // avoids fighting them when the row is already visible.
50 useEffect(() => {
51 if (activeLine == null) return
52 activeItemRef.current?.scrollIntoView({ block: 'nearest' })
53 }, [activeLine])
54
55 return (
56 <section
57 aria-label="Outline"
58 style={{ width }}
59 className="relative flex shrink-0 flex-col border-l border-paper-300/70 bg-paper-50/18"
60 >
61 <PanelResizeHandle onStart={startResize} />
62 <div className="border-b border-paper-300/60 px-4 py-4">
63 <div className="text-xs font-medium uppercase tracking-[0.16em] text-ink-400">
64 Outline
65 </div>
66 <div className="mt-2 text-xs text-ink-500">
67 {items.length === 0
68 ? 'No headings yet — add `#` to build an outline.'
69 : `${items.length} heading${items.length === 1 ? '' : 's'}`}
70 </div>
71 {items.length > 0 && (
72 <input
73 value={query}
74 onChange={(e) => setQuery(e.target.value)}
75 placeholder="Filter…"
76 className="mt-3 w-full rounded-md border border-paper-300/60 bg-paper-100 px-2 py-1 text-xs text-ink-900 outline-none placeholder:text-ink-400 focus:border-accent/60"
77 />
78 )}
79 </div>
80 <div className="min-h-0 flex-1 overflow-y-auto px-2 py-2">
81 {filtered.length === 0 ? (
82 <div className="px-2 py-4 text-center text-xs text-ink-400">
83 {items.length === 0 ? 'Nothing to show.' : 'No matches.'}
84 </div>
85 ) : (
86 <ul className="flex flex-col">
87 {filtered.map((item) => {

Callers

nothing calls this directly

Calls 2

parseOutlineFunction · 0.90
usePanelResizeFunction · 0.90

Tested by

no test coverage detected