diff --git a/apps/client/src/widgets/react/FormGroup.tsx b/apps/client/src/widgets/react/FormGroup.tsx
index 513594dfa..8b2acd72e 100644
--- a/apps/client/src/widgets/react/FormGroup.tsx
+++ b/apps/client/src/widgets/react/FormGroup.tsx
@@ -14,10 +14,12 @@ export default function FormGroup({ label, title, className, children, descripti
return (
-
diff --git a/apps/client/src/widgets/react/FormSelect.tsx b/apps/client/src/widgets/react/FormSelect.tsx
index ce42c4815..cd003c6ae 100644
--- a/apps/client/src/widgets/react/FormSelect.tsx
+++ b/apps/client/src/widgets/react/FormSelect.tsx
@@ -2,7 +2,7 @@ import type { ComponentChildren } from "preact";
type OnChangeListener = (newValue: string) => void;
-interface FormSelectGroup {
+export interface FormSelectGroup {
title: string;
items: T[];
}
diff --git a/apps/client/src/widgets/react/RawHtml.tsx b/apps/client/src/widgets/react/RawHtml.tsx
index fc4632881..1e5aef648 100644
--- a/apps/client/src/widgets/react/RawHtml.tsx
+++ b/apps/client/src/widgets/react/RawHtml.tsx
@@ -24,7 +24,7 @@ function getProps({ className, html, style }: RawHtmlProps) {
}
}
-function getHtml(html: string | HTMLElement | JQuery) {
+export function getHtml(html: string | HTMLElement | JQuery) {
if (typeof html === "object" && "length" in html) {
html = html[0];
}
diff --git a/apps/client/src/widgets/type_widgets/options/text_notes.tsx b/apps/client/src/widgets/type_widgets/options/text_notes.tsx
index d1d362128..fd9c8d933 100644
--- a/apps/client/src/widgets/type_widgets/options/text_notes.tsx
+++ b/apps/client/src/widgets/type_widgets/options/text_notes.tsx
@@ -1,10 +1,20 @@
-import { useEffect } from "preact/hooks";
+import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import FormCheckbox from "../../react/FormCheckbox";
import FormRadioGroup from "../../react/FormRadioGroup";
import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import OptionsSection from "./components/OptionsSection";
import { toggleBodyClass } from "../../../services/utils";
+import FormGroup from "../../react/FormGroup";
+import Column from "../../react/Column";
+import { FormSelectGroup, FormSelectWithGroups } from "../../react/FormSelect";
+import { Themes, type Theme } from "@triliumnext/highlightjs";
+import { ensureMimeTypesForHighlighting, loadHighlightingTheme } from "../../../services/syntax_highlight";
+import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
+import { ComponentChildren } from "preact";
+import RawHtml, { getHtml } from "../../react/RawHtml";
+import { ParentComponent } from "../../react/ReactBasicWidget";
+import { CSSProperties } from "preact/compat";
export default function TextNoteSettings() {
return (
@@ -12,6 +22,7 @@ export default function TextNoteSettings() {
+
>
)
}
@@ -90,4 +101,133 @@ function HeadingStyle() {
/>
);
+}
+
+function CodeBlockStyle() {
+ const themes = useMemo(() => groupThemesByLightOrDark(), []);
+ const [ codeBlockTheme, setCodeBlockTheme ] = useTriliumOption("codeBlockTheme");
+ const [ codeBlockWordWrap, setCodeBlockWordWrap ] = useTriliumOptionBool("codeBlockWordWrap");
+
+ return (
+
+
+
+ {t("highlighting.color-scheme")}
+ {
+ loadHighlightingTheme(newTheme);
+ setCodeBlockTheme(newTheme);
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const SAMPLE_LANGUAGE = normalizeMimeTypeForCKEditor("application/javascript;env=frontend");
+const SAMPLE_CODE = `\
+const n = 10;
+greet(n); // Print "Hello World" for n times
+
+/**
+ * Displays a "Hello World!" message for a given amount of times, on the standard console. The "Hello World!" text will be displayed once per line.
+ *
+ * @param {number} times The number of times to print the \`Hello World!\` message.
+ */
+function greet(times) {
+ for (let i = 0; i++; i < times) {
+ console.log("Hello World!");
+ }
+}
+`;
+
+function CodeBlockPreview({ theme, wordWrap }: { theme: string, wordWrap: boolean }) {
+ const [ code, setCode ] = useState(SAMPLE_CODE);
+
+ useEffect(() => {
+ if (theme !== "none") {
+ import("@triliumnext/highlightjs").then(async (hljs) => {
+ await ensureMimeTypesForHighlighting();
+ const highlightedText = hljs.highlight(SAMPLE_CODE, {
+ language: SAMPLE_LANGUAGE
+ });
+ if (highlightedText) {
+ setCode(highlightedText.value);
+ }
+ });
+ } else {
+ setCode(SAMPLE_CODE);
+ }
+ }, [theme]);
+
+ const codeStyle = useMemo(() => {
+ if (wordWrap) {
+ return { whiteSpace: "pre-wrap" };
+ } else {
+ return { whiteSpace: "pre"};
+ }
+ }, [ wordWrap ]);
+
+ return (
+
+ )
+}
+
+interface ThemeData {
+ val: string;
+ title: string;
+}
+
+function groupThemesByLightOrDark() {
+ const darkThemes: ThemeData[] = [];
+ const lightThemes: ThemeData[] = [];
+
+ for (const [ id, theme ] of Object.entries(Themes)) {
+ const data: ThemeData = {
+ val: "default:" + id,
+ title: theme.name
+ };
+
+ if (theme.name.includes("Dark")) {
+ darkThemes.push(data);
+ } else {
+ lightThemes.push(data);
+ }
+ }
+
+ const output: FormSelectGroup[] = [
+ {
+ title: "",
+ items: [{
+ val: "none",
+ title: t("code_block.theme_none")
+ }]
+ },
+ {
+ title: t("code_block.theme_group_light"),
+ items: lightThemes
+ },
+ {
+ title: t("code_block.theme_group_dark"),
+ items: darkThemes
+ }
+ ];
+ return output;
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts b/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts
deleted file mode 100644
index 0a97d37d3..000000000
--- a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts
+++ /dev/null
@@ -1,165 +0,0 @@
-import { normalizeMimeTypeForCKEditor, type OptionMap } from "@triliumnext/commons";
-import { t } from "../../../../services/i18n.js";
-import server from "../../../../services/server.js";
-import OptionsWidget from "../options_widget.js";
-import { ensureMimeTypesForHighlighting, loadHighlightingTheme } from "../../../../services/syntax_highlight.js";
-import { Themes, type Theme } from "@triliumnext/highlightjs";
-
-const SAMPLE_LANGUAGE = normalizeMimeTypeForCKEditor("application/javascript;env=frontend");
-const SAMPLE_CODE = `\
-const n = 10;
-greet(n); // Print "Hello World" for n times
-
-/**
- * Displays a "Hello World!" message for a given amount of times, on the standard console. The "Hello World!" text will be displayed once per line.
- *
- * @param {number} times The number of times to print the \`Hello World!\` message.
- */
-function greet(times) {
- for (let i = 0; i++; i < times) {
- console.log("Hello World!");
- }
-}
-`;
-
-const TPL = /*html*/`
-
-
${t("highlighting.title")}
-
-
-
-
-
-
-
-`;
-
-/**
- * Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter.
- */
-export default class CodeBlockOptions extends OptionsWidget {
-
- private $themeSelect!: JQuery;
- private $wordWrap!: JQuery;
- private $sampleEl!: JQuery;
-
- doRender() {
- this.$widget = $(TPL);
- this.$themeSelect = this.$widget.find(".theme-select");
-
- // Populate the list of themes.
- const themeGroups = groupThemesByLightOrDark();
- for (const [key, themes] of Object.entries(themeGroups)) {
- const $group = key ? $("