From cd9654cd5fbd6e075fede6d5dbe590e2baf8c373 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 18 Dec 2025 13:29:36 +0200 Subject: [PATCH] chore(highlights_list): reintroduce support for read-only notes --- .../src/widgets/sidebar/HighlightsList.tsx | 60 ++++++++++++++++++- .../src/widgets/sidebar/TableOfContents.tsx | 3 + 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/sidebar/HighlightsList.tsx b/apps/client/src/widgets/sidebar/HighlightsList.tsx index 523359da3..a575f2f1a 100644 --- a/apps/client/src/widgets/sidebar/HighlightsList.tsx +++ b/apps/client/src/widgets/sidebar/HighlightsList.tsx @@ -1,8 +1,9 @@ +import { headingIsHorizontal } from "@excalidraw/excalidraw/element/heading"; import { CKTextEditor, ModelText } from "@triliumnext/ckeditor5"; import { useCallback, useEffect, useState } from "preact/hooks"; import { t } from "../../services/i18n"; -import { useActiveNoteContext, useIsNoteReadOnly, useNoteProperty, useTextEditor } from "../react/hooks"; +import { useActiveNoteContext, useContentElement, useIsNoteReadOnly, useNoteProperty, useTextEditor } from "../react/hooks"; import RightPanelWidget from "./RightPanelWidget"; interface RawHighlight { @@ -157,7 +158,62 @@ function extractHighlightsFromTextEditor(editor: CKTextEditor) { //#endregion //#region Read-only text +interface DomHighlight extends RawHighlight { + element: HTMLElement; +} + function ReadOnlyTextHighlightsList() { - return "Read-only"; + const { noteContext } = useActiveNoteContext(); + const contentEl = useContentElement(noteContext); + const highlights = extractHeadingsFromStaticHtml(contentEl); + + const scrollToHighlight = useCallback((highlight: DomHighlight) => { + highlight.element.scrollIntoView(); + }, []); + + return ; +} + +function extractHeadingsFromStaticHtml(el: HTMLElement | null) { + if (!el) return []; + + const walker = document.createTreeWalker( + el, + NodeFilter.SHOW_TEXT, + null + ); + + const highlights: DomHighlight[] = []; + + let node: Node | null; + while ((node = walker.nextNode())) { + const el = node.parentElement; + if (!el || !node.textContent?.trim()) continue; + + const style = getComputedStyle(el); + + if ( + el.closest('strong, em, u') || + style.color || style.backgroundColor + ) { + highlights.push({ + id: crypto.randomUUID(), + text: node.textContent, + element: el, + attrs: { + bold: !!el.closest("strong"), + italic: !!el.closest("em"), + underline: !!el.closest("u"), + background: el.style.backgroundColor, + color: el.style.color + } + }); + } + } + + return highlights; } //#endregion diff --git a/apps/client/src/widgets/sidebar/TableOfContents.tsx b/apps/client/src/widgets/sidebar/TableOfContents.tsx index ccc623ab8..46967655f 100644 --- a/apps/client/src/widgets/sidebar/TableOfContents.tsx +++ b/apps/client/src/widgets/sidebar/TableOfContents.tsx @@ -179,6 +179,8 @@ function extractTocFromTextEditor(editor: CKTextEditor) { return headings; } //#endregion + +//#region Read-only text interface DomHeading extends RawHeading { element: HTMLHeadingElement; } @@ -213,3 +215,4 @@ function extractTocFromStaticHtml(el: HTMLElement | null) { return headings; } +//#endregion