From e4eb96a1ae718770ecd4d022ae42d399807e9e8e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 21 Sep 2025 20:03:28 +0300 Subject: [PATCH] chore(react/type_widget): start porting read-only text --- apps/client/src/widgets/NoteDetail.tsx | 2 + .../type_widgets/text/ReadOnlyText.css | 58 +++++++++++++ .../type_widgets/text/ReadOnlyText.tsx | 41 ++++++++++ .../type_widgets_old/read_only_text.ts | 82 ------------------- 4 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 apps/client/src/widgets/type_widgets/text/ReadOnlyText.css create mode 100644 apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index 8a11c404d..8c6df291b 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -19,6 +19,7 @@ import { ReadOnlyCode, EditableCode } from "./type_widgets/code/Code"; import Mermaid from "./type_widgets/Mermaid"; import MindMap from "./type_widgets/MindMap"; import { AttachmentDetail, AttachmentList } from "./type_widgets/Attachment"; +import ReadOnlyText from "./type_widgets/text/ReadOnlyText"; /** * A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one, @@ -101,6 +102,7 @@ function getCorrespondingWidget(noteType: ExtendedNoteType | undefined, props: T case "mindMap": return case "attachmentList": return case "attachmentDetail": return + case "readOnlyText": return default: break; } } diff --git a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.css b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.css new file mode 100644 index 000000000..4ab25d973 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.css @@ -0,0 +1,58 @@ +/* h1 should not be used at all since semantically that's a note title */ +.note-detail-readonly-text h1 { font-size: 1.8em; } +.note-detail-readonly-text h2 { font-size: 1.6em; } +.note-detail-readonly-text h3 { font-size: 1.4em; } +.note-detail-readonly-text h4 { font-size: 1.2em; } +.note-detail-readonly-text h5 { font-size: 1.1em; } +.note-detail-readonly-text h6 { font-size: 1.0em; } + +body.heading-style-markdown .note-detail-readonly-text h1::before { content: "#\\2004"; color: var(--muted-text-color); } +body.heading-style-markdown .note-detail-readonly-text h2::before { content: "##\\2004"; color: var(--muted-text-color); } +body.heading-style-markdown .note-detail-readonly-text h3::before { content: "###\\2004"; color: var(--muted-text-color); } +body.heading-style-markdown .note-detail-readonly-text h4:not(.include-note-title)::before { content: "####\\2004"; color: var(--muted-text-color); } +body.heading-style-markdown .note-detail-readonly-text h5::before { content: "#####\\2004"; color: var(--muted-text-color); } +body.heading-style-markdown .note-detail-readonly-text h6::before { content: "######\\2004"; color: var(--muted-text-color); } + +body.heading-style-underline .note-detail-readonly-text h1 { border-bottom: 1px solid var(--main-border-color); } +body.heading-style-underline .note-detail-readonly-text h2 { border-bottom: 1px solid var(--main-border-color); } +body.heading-style-underline .note-detail-readonly-text h3 { border-bottom: 1px solid var(--main-border-color); } +body.heading-style-underline .note-detail-readonly-text h4:not(.include-note-title) { border-bottom: 1px solid var(--main-border-color); } +body.heading-style-underline .note-detail-readonly-text h5 { border-bottom: 1px solid var(--main-border-color); } +body.heading-style-underline .note-detail-readonly-text h6 { border-bottom: 1px solid var(--main-border-color); } + +.note-detail-readonly-text { + padding-inline-start: 24px; + padding-top: 10px; + font-family: var(--detail-font-family); + min-height: 50px; + position: relative; +} + +body.mobile .note-detail-readonly-text { + padding-left: 10px; +} + +.note-detail-readonly-text p:first-child, .note-detail-readonly-text::before { + margin-top: 0; +} + +.note-detail-readonly-text img { + max-width: 100%; + cursor: pointer; +} + +.edit-text-note-button { + position: absolute; + top: 5px; + right: 10px; + font-size: 150%; + padding: 5px; + cursor: pointer; + border: 1px solid transparent; + border-radius: var(--button-border-radius); + color: var(--button-text-color); +} + +.edit-text-note-button:hover { + border-color: var(--button-border-color); +} \ No newline at end of file diff --git a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx new file mode 100644 index 000000000..fab9680f3 --- /dev/null +++ b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx @@ -0,0 +1,41 @@ +import { useEffect, useMemo, useRef } from "preact/hooks"; +import { TypeWidgetProps } from "../type_widget"; +import "./ReadOnlyText.css"; +import { useNoteBlob, useNoteLabel } from "../../react/hooks"; +import RawHtml from "../../react/RawHtml"; + +// we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes +// we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time +// (see https://github.com/zadam/trilium/issues/1590 for example of such conflict) +import "@triliumnext/ckeditor5"; +import FNote from "../../../entities/fnote"; +import { getLocaleById } from "../../../services/i18n"; + +export default function ReadOnlyText({ note }: TypeWidgetProps) { + const blob = useNoteBlob(note); + const contentRef = useRef(null); + const { isRtl } = useNoteLanguage(note); + + return ( +
+ +
+ ) +} + +function useNoteLanguage(note: FNote) { + const [ language ] = useNoteLabel(note, "language"); + const isRtl = useMemo(() => { + const correspondingLocale = getLocaleById(language); + return correspondingLocale?.rtl; + }, [ language ]); + return { isRtl }; +} diff --git a/apps/client/src/widgets/type_widgets_old/read_only_text.ts b/apps/client/src/widgets/type_widgets_old/read_only_text.ts index 59f99af33..223fa97f3 100644 --- a/apps/client/src/widgets/type_widgets_old/read_only_text.ts +++ b/apps/client/src/widgets/type_widgets_old/read_only_text.ts @@ -2,78 +2,10 @@ import AbstractTextTypeWidget from "./abstract_text_type_widget.js"; import { formatCodeBlocks } from "../../services/syntax_highlight.js"; import type FNote from "../../entities/fnote.js"; import type { CommandListenerData, EventData } from "../../components/app_context.js"; -import { getLocaleById } from "../../services/i18n.js"; import appContext from "../../components/app_context.js"; import { getMermaidConfig } from "../../services/mermaid.js"; import { renderMathInElement } from "../../services/math.js"; -const TPL = /*html*/` -
- - - -
-`; - export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { private $content!: JQuery; @@ -83,8 +15,6 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { } doRender() { - this.$widget = $(TPL); - this.$content = this.$widget.find(".note-detail-readonly-text-content"); this.setupImageOpening(true); @@ -97,11 +27,6 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { } async doRefresh(note: FNote) { - // we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes - // we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time - // (see https://github.com/zadam/trilium/issues/1590 for example of such conflict) - await import("@triliumnext/ckeditor5"); - this.onLanguageChanged(); const blob = await note.getBlob(); @@ -161,13 +86,6 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { resolve(this.$content); } - async onLanguageChanged(): Promise { - const languageCode = this.note?.getLabelValue("language"); - const correspondingLocale = getLocaleById(languageCode); - const isRtl = correspondingLocale?.rtl; - this.$widget.attr("dir", isRtl ? "rtl" : "ltr"); - } - buildTouchBarCommand({ TouchBar, buildIcon }: CommandListenerData<"buildTouchBar">) { return [ new TouchBar.TouchBarSpacer({ size: "flexible" }),