chore(react/type_widget): port backend log

This commit is contained in:
Elian Doran 2025-09-20 12:16:51 +03:00
parent 4a4502dfea
commit 256d1863d2
No known key found for this signature in database
6 changed files with 73 additions and 72 deletions

View File

@ -18,10 +18,11 @@ import InternationalizationOptions from "./options/i18n";
import AdvancedSettings from "./options/advanced"; import AdvancedSettings from "./options/advanced";
import "./ContentWidget.css"; import "./ContentWidget.css";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import BackendLog from "./code/BackendLog";
export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsAi" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced"; export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsAi" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced";
const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", () => JSX.Element> = { const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", (props: TypeWidgetProps) => JSX.Element> = {
_optionsAppearance: AppearanceSettings, _optionsAppearance: AppearanceSettings,
_optionsShortcuts: ShortcutSettings, _optionsShortcuts: ShortcutSettings,
_optionsTextNotes: TextNoteSettings, _optionsTextNotes: TextNoteSettings,
@ -37,7 +38,7 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", () => JSX.Element> =
_optionsOther: OtherSettings, _optionsOther: OtherSettings,
_optionsLocalization: InternationalizationOptions, _optionsLocalization: InternationalizationOptions,
_optionsAdvanced: AdvancedSettings, _optionsAdvanced: AdvancedSettings,
_backendLog: () => <></> // FIXME _backendLog: BackendLog
} }
/** /**
@ -46,13 +47,13 @@ const CONTENT_WIDGETS: Record<OptionPages | "_backendLog", () => JSX.Element> =
* @param param0 * @param param0
* @returns * @returns
*/ */
export default function ContentWidget({ note }: TypeWidgetProps) { export default function ContentWidget({ note, ...restProps }: TypeWidgetProps) {
const Content = CONTENT_WIDGETS[note.noteId]; const Content = CONTENT_WIDGETS[note.noteId];
return ( return (
<div className="note-detail-content-widget note-detail-printable"> <div className="note-detail-content-widget note-detail-printable">
<div className={`note-detail-content-widget-content ${note.noteId.startsWith("_options") ? "options" : ""}`}> <div className={`note-detail-content-widget-content ${note.noteId.startsWith("_options") ? "options" : ""}`}>
{Content {Content
? <Content /> ? <Content note={note} {...restProps} />
: (t("content_widget.unknown_widget", { id: note.noteId }))} : (t("content_widget.unknown_widget", { id: note.noteId }))}
</div> </div>
</div> </div>

View File

@ -0,0 +1,44 @@
import { useEffect, useRef, useState } from "preact/hooks";
import "./code.css";
import { CodeEditor } from "./Code";
import CodeMirror from "@triliumnext/codemirror";
import server from "../../../services/server";
import { useTriliumEvent } from "../../react/hooks";
import { TypeWidgetProps } from "../type_widget";
export default function BackendLog({ ntxId, parentComponent }: TypeWidgetProps) {
const [ content, setContent ] = useState<string>();
const editorRef = useRef<CodeMirror>(null);
function refresh() {
server.get<string>("backend-log").then(content => {
setContent(content);
});
}
useEffect(refresh, []);
// Scroll to end
useEffect(() => {
requestAnimationFrame(() => editorRef.current?.scrollToEnd());
}, [ content ]);
// React to refresh button.
useTriliumEvent("refreshData", ({ ntxId: eventNtxId }) => {
if (eventNtxId !== ntxId) return;
refresh();
});
return (
<div className="backend-log-editor-container">
<CodeEditor
editorRef={editorRef}
ntxId={ntxId} parentComponent={parentComponent}
content={content ?? ""}
mime="text/plain"
readOnly
preferPerformance
/>
</div>
)
}

View File

@ -25,7 +25,7 @@ export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWi
return ( return (
<div className="note-detail-readonly-code note-detail-printable"> <div className="note-detail-readonly-code note-detail-printable">
<CodeEditor <CodeEditor
ntxId={ntxId} note={note} parentComponent={parentComponent} ntxId={ntxId} parentComponent={parentComponent}
className="note-detail-readonly-code-content" className="note-detail-readonly-code-content"
content={content} content={content}
mime={note.mime} mime={note.mime}
@ -63,8 +63,9 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent }: T
return ( return (
<div className="note-detail-code note-detail-printable"> <div className="note-detail-code note-detail-printable">
<CodeEditor <CodeEditor
ntxId={ntxId} note={note} parentComponent={parentComponent} ntxId={ntxId} parentComponent={parentComponent}
editorRef={editorRef} containerRef={containerRef} editorRef={editorRef} containerRef={containerRef}
mime={note.mime}
className="note-detail-code-editor" className="note-detail-code-editor"
placeholder={t("editable_code.placeholder")} placeholder={t("editable_code.placeholder")}
vimKeybindings={vimKeymapEnabled} vimKeybindings={vimKeymapEnabled}
@ -86,7 +87,7 @@ export function EditableCode({ note, ntxId, debounceUpdate, parentComponent }: T
) )
} }
function CodeEditor({ note, parentComponent, ntxId, containerRef: externalContainerRef, editorRef: externalEditorRef, ...editorProps }: Omit<CodeMirrorProps, "onThemeChange" | "lineWrapping"> & Pick<TypeWidgetProps, "note" | "parentComponent" | "ntxId">) { export function CodeEditor({ parentComponent, ntxId, containerRef: externalContainerRef, editorRef: externalEditorRef, mime, onInitialized, ...editorProps }: Omit<CodeMirrorProps, "onThemeChange" | "lineWrapping"> & Pick<TypeWidgetProps, "parentComponent" | "ntxId">) {
const codeEditorRef = useRef<VanillaCodeMirror>(null); const codeEditorRef = useRef<VanillaCodeMirror>(null);
const containerRef = useSyncedRef(externalContainerRef); const containerRef = useSyncedRef(externalContainerRef);
const initialized = useRef($.Deferred()); const initialized = useRef($.Deferred());
@ -109,7 +110,7 @@ function CodeEditor({ note, parentComponent, ntxId, containerRef: externalContai
const theme = getThemeById(codeNoteTheme.substring(DEFAULT_PREFIX.length)); const theme = getThemeById(codeNoteTheme.substring(DEFAULT_PREFIX.length));
if (theme) { if (theme) {
codeEditorRef.current.setTheme(theme).then(() => { codeEditorRef.current.setTheme(theme).then(() => {
if (note?.mime === "text/x-sqlite;schema=trilium") return; if (mime === "text/x-sqlite;schema=trilium") return;
const editor = containerRef.current?.querySelector(".cm-editor"); const editor = containerRef.current?.querySelector(".cm-editor");
if (!editor) return; if (!editor) return;
const style = window.getComputedStyle(editor); const style = window.getComputedStyle(editor);
@ -145,6 +146,7 @@ function CodeEditor({ note, parentComponent, ntxId, containerRef: externalContai
return <CodeMirror return <CodeMirror
{...editorProps} {...editorProps}
mime={mime}
editorRef={codeEditorRef} editorRef={codeEditorRef}
containerRef={containerRef} containerRef={containerRef}
lineWrapping={codeLineWrapEnabled} lineWrapping={codeLineWrapEnabled}
@ -156,6 +158,7 @@ function CodeEditor({ note, parentComponent, ntxId, containerRef: externalContai
externalEditorRef.current = codeEditorRef.current; externalEditorRef.current = codeEditorRef.current;
} }
initialized.current.resolve(); initialized.current.resolve();
onInitialized?.();
}} }}
/> />
} }

View File

@ -4,7 +4,7 @@ import { useSyncedRef } from "../../react/hooks";
import { RefObject } from "preact"; import { RefObject } from "preact";
export interface CodeMirrorProps extends Omit<EditorConfig, "parent"> { export interface CodeMirrorProps extends Omit<EditorConfig, "parent"> {
content: string; content?: string;
mime: string; mime: string;
className?: string; className?: string;
editorRef?: RefObject<VanillaCodeMirror>; editorRef?: RefObject<VanillaCodeMirror>;

View File

@ -16,3 +16,19 @@
height: 100%; height: 100%;
} }
/* #endregion */ /* #endregion */
/* #region Backend log */
.backend-log-editor-container {
height: 100%;
display: flex;
flex-direction: column;
}
.backend-log-editor {
flex-grow: 1;
width: 100%;
border: none;
resize: none;
margin-bottom: 0;
}
/* #endregion */

View File

@ -1,63 +0,0 @@
import server from "../../../services/server.js";
import AbstractCodeTypeWidget from "../abstract_code_type_widget.js";
import type { EventData } from "../../../components/app_context.js";
import type { EditorConfig } from "@triliumnext/codemirror";
const TPL = /*html*/`<div style="height: 100%; display: flex; flex-direction: column;">
<style>
.backend-log-editor {
flex-grow: 1;
width: 100%;
border: none;
resize: none;
margin-bottom: 0;
}
</style>
<pre class="backend-log-editor"></pre
</div>`;
export default class BackendLogWidget extends AbstractCodeTypeWidget {
private $refreshBackendLog!: JQuery<HTMLElement>;
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find(".backend-log-editor");
super.doRender();
}
async refresh() {
await this.load();
}
async refreshDataEvent({ ntxId }: EventData<"refreshData">) {
if (ntxId !== this.noteContext?.ntxId) {
return;
}
this.refresh();
}
getExtraOpts(): Partial<EditorConfig> {
return {
readOnly: true,
preferPerformance: true
};
}
async load() {
const content = await server.get<string>("backend-log");
await this.initialized;
this._update(
{
mime: "text/plain"
},
content
);
this.show();
this.scrollToEnd();
}
}