Compare commits

...

4 Commits

Author SHA1 Message Date
Elian Doran
3a15878629 fix(mindmap): not reacting to switch between light/dark mode 2026-02-24 18:46:46 +02:00
Elian Doran
021a908c9c fix(canvas): not reacting to switch between light/dark mode 2026-02-24 18:35:36 +02:00
Elian Doran
c09ef3af80 fix(note_title): help pages have editable title 2026-02-24 18:14:13 +02:00
Elian Doran
3151e6dafc fix(client/layout): scroll padding enabled when note is protected 2026-02-24 18:10:51 +02:00
5 changed files with 46 additions and 10 deletions

View File

@@ -26,6 +26,7 @@ export default function NoteTitleWidget(props: {className?: string}) {
|| note === undefined
|| (note.isProtected && !protected_session_holder.isProtectedSessionAvailable())
|| isLaunchBarConfig(note.noteId)
|| note.noteId.startsWith("_help_")
|| viewScope?.viewMode !== "default";
setReadOnly(isReadOnly);
}, [ note, note?.noteId, note?.isProtected, viewScope?.viewMode ]);

View File

@@ -1383,3 +1383,28 @@ export function useGetContextDataFrom<K extends keyof NoteContextDataMap>(
return data;
}
export function useColorScheme() {
const themeStyle = getThemeStyle();
const defaultValue = themeStyle === "auto" ? (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) : themeStyle === "dark";
const [ prefersDark, setPrefersDark ] = useState(defaultValue);
useEffect(() => {
if (themeStyle !== "auto") return;
const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
const listener = () => setPrefersDark((window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches));
mediaQueryList.addEventListener("change", listener);
return () => mediaQueryList.removeEventListener("change", listener);
}, []);
return prefersDark ? "dark" : "light";
}
function getThemeStyle() {
const style = window.getComputedStyle(document.body);
const themeStyle = style.getPropertyValue("--theme-style");
if (style.getPropertyValue("--theme-style-auto") !== "true" && (themeStyle === "light" || themeStyle === "dark")) {
return themeStyle as "light" | "dark";
}
return "auto";
}

View File

@@ -8,6 +8,7 @@ export default function ScrollPadding() {
const [height, setHeight] = useState<number>(10);
const isEnabled = ["text", "code"].includes(note?.type ?? "")
&& viewScope?.viewMode === "default"
&& note?.isContentAvailable()
&& !note?.isTriliumSqlite();
const refreshHeight = () => {

View File

@@ -6,12 +6,12 @@ import "./MindMap.css";
import nodeMenu from "@mind-elixir/node-menu";
import { DISPLAYABLE_LOCALE_IDS } from "@triliumnext/commons";
import { snapdom } from "@zumer/snapdom";
import { default as VanillaMindElixir,MindElixirData, MindElixirInstance, Operation, Options } from "mind-elixir";
import { default as VanillaMindElixir,MindElixirData, MindElixirInstance, Operation, Options, THEME as LIGHT_THEME, DARK_THEME } from "mind-elixir";
import { HTMLAttributes, RefObject } from "preact";
import { useCallback, useEffect, useRef } from "preact/hooks";
import utils from "../../services/utils";
import { useEditorSpacedUpdate, useNoteLabelBoolean, useSyncedRef, useTriliumEvent, useTriliumEvents, useTriliumOption } from "../react/hooks";
import { useColorScheme, useEditorSpacedUpdate, useNoteLabelBoolean, useSyncedRef, useTriliumEvent, useTriliumEvents, useTriliumOption } from "../react/hooks";
import { refToJQuerySelector } from "../react/react_utils";
import { TypeWidgetProps } from "./type_widget";
@@ -85,9 +85,11 @@ export default function MindMap({ note, ntxId, noteContext }: TypeWidgetProps) {
},
onContentChange: (content) => {
let newContent: MindElixirData;
if (content) {
try {
newContent = JSON.parse(content) as MindElixirData;
delete newContent.theme; // The theme is managed internally by the widget, so we remove it from the loaded content to avoid inconsistencies.
} catch (e) {
console.warn(e);
console.debug("Wrong JSON content: ", content);
@@ -151,6 +153,7 @@ function MindElixir({ containerRef: externalContainerRef, containerProps, apiRef
const containerRef = useSyncedRef<HTMLDivElement>(externalContainerRef, null);
const apiRef = useRef<MindElixirInstance>(null);
const [ locale ] = useTriliumOption("locale");
const colorScheme = useColorScheme();
function reinitialize() {
if (!containerRef.current) return;
@@ -158,7 +161,8 @@ function MindElixir({ containerRef: externalContainerRef, containerProps, apiRef
const mind = new VanillaMindElixir({
el: containerRef.current,
locale: LOCALE_MAPPINGS[locale as DISPLAYABLE_LOCALE_IDS] ?? undefined,
editable
editable,
theme: LIGHT_THEME
});
if (editable) {
@@ -179,6 +183,14 @@ function MindElixir({ containerRef: externalContainerRef, containerProps, apiRef
};
}, []);
// React to theme changes.
useEffect(() => {
if (!apiRef.current) return;
const newTheme = colorScheme === "dark" ? DARK_THEME : LIGHT_THEME;
if (apiRef.current.theme === newTheme) return; // Avoid unnecessary theme changes, which can be expensive to render.
apiRef.current.changeTheme(newTheme);
}, [ colorScheme ]);
useEffect(() => {
const data = apiRef.current?.getData();
reinitialize();

View File

@@ -1,7 +1,7 @@
import { Excalidraw } from "@excalidraw/excalidraw";
import { TypeWidgetProps } from "../type_widget";
import "@excalidraw/excalidraw/index.css";
import { useNoteLabelBoolean, useTriliumOption } from "../../react/hooks";
import { useColorScheme, useNoteLabelBoolean, useTriliumOption } from "../../react/hooks";
import { useCallback, useMemo, useRef } from "preact/hooks";
import { type ExcalidrawImperativeAPI, type AppState } from "@excalidraw/excalidraw/types";
import options from "../../../services/options";
@@ -19,12 +19,9 @@ window.EXCALIDRAW_ASSET_PATH = `${window.location.pathname}/node_modules/@excali
export default function Canvas({ note, noteContext }: TypeWidgetProps) {
const apiRef = useRef<ExcalidrawImperativeAPI>(null);
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const themeStyle = useMemo(() => {
const documentStyle = window.getComputedStyle(document.documentElement);
return documentStyle.getPropertyValue("--theme-style")?.trim() as AppState["theme"];
}, []);
const colorScheme = useColorScheme();
const [ locale ] = useTriliumOption("locale");
const persistence = useCanvasPersistence(note, noteContext, apiRef, themeStyle, isReadOnly);
const persistence = useCanvasPersistence(note, noteContext, apiRef, colorScheme, isReadOnly);
/** Use excalidraw's native zoom instead of the global zoom. */
const onWheel = useCallback((e: MouseEvent) => {
@@ -54,7 +51,7 @@ export default function Canvas({ note, noteContext }: TypeWidgetProps) {
<div className="excalidraw-wrapper">
<Excalidraw
excalidrawAPI={api => apiRef.current = api}
theme={themeStyle}
theme={colorScheme}
viewModeEnabled={isReadOnly || options.is("databaseReadonly")}
zenModeEnabled={false}
isCollaborating={false}