diff --git a/apps/client/src/widgets/sidebar/TableOfContents.tsx b/apps/client/src/widgets/sidebar/TableOfContents.tsx index 01135d440..2fc3db61e 100644 --- a/apps/client/src/widgets/sidebar/TableOfContents.tsx +++ b/apps/client/src/widgets/sidebar/TableOfContents.tsx @@ -1,15 +1,62 @@ +import { CKTextEditor, ModelElement } from "@triliumnext/ckeditor5"; +import { useEffect, useState } from "preact/hooks"; + import FNote from "../../entities/fnote"; import { t } from "../../services/i18n"; -import { useNoteSavedData } from "../react/hooks"; +import { useActiveNoteContext, useNoteSavedData, useTriliumEvent } from "../react/hooks"; import RightPanelWidget from "./RightPanelWidget"; -export default function TableOfContents({ note }: { note: FNote }) { - const content = useNoteSavedData(note.noteId); +interface CKHeading { + level: number; + text: string; + element: ModelElement; +} + +export default function TableOfContents() { + const { ntxId } = useActiveNoteContext(); + const [ textEditor, setTextEditor ] = useState(null); + const [ headings, setHeadings ] = useState([]); + + // Populate the cache with the toolbar of every note context. + useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => { + if (eventNtxId !== ntxId) return; + setTextEditor(editor); + }); + + useEffect(() => { + if (!textEditor) return; + const headings = extractTocFromTextEditor(textEditor); + setHeadings(headings); + }, [ textEditor ]); + + console.log("Render with ", headings); return ( - {content?.length} + {headings.map(heading => ( +
  • {heading.text}
  • + ))}
    ); } + +function extractTocFromTextEditor(editor: CKTextEditor) { + const headings: CKHeading[] = []; + + const root = editor.model.document.getRoot(); + if (!root) return []; + + for (const { type, item } of editor.model.createRangeIn(root).getWalker()) { + if (type !== "elementStart" || !item.is('element') || !item.name.startsWith('heading')) continue; + + const level = Number(item.name.replace( 'heading', '' )); + const text = Array.from( item.getChildren() ) + .map( c => c.is( '$text' ) ? c.data : '' ) + .join( '' ); + + headings.push({ level, text, element: item }); + } + + return headings; +}