diff --git a/apps/client/src/desktop.ts b/apps/client/src/desktop.ts
index 42fe11655..42e1c44e9 100644
--- a/apps/client/src/desktop.ts
+++ b/apps/client/src/desktop.ts
@@ -1,7 +1,116 @@
-import $ from "jquery";
-(window as any).$ = $;
-(window as any).jQuery = $;
+import appContext from "./components/app_context.js";
+import utils from "./services/utils.js";
+import noteTooltipService from "./services/note_tooltip.js";
+import bundleService from "./services/bundle.js";
+import toastService from "./services/toast.js";
+import noteAutocompleteService from "./services/note_autocomplete.js";
+import electronContextMenu from "./menus/electron_context_menu.js";
+import glob from "./services/glob.js";
+import { t } from "./services/i18n.js";
+import options from "./services/options.js";
+import type ElectronRemote from "@electron/remote";
+import type Electron from "electron";
+import "./stylesheets/bootstrap.scss";
+import "boxicons";
+import "jquery-hotkeys";
+import "autocomplete.js/index_jquery.js";
-$("body").show();
+await appContext.earlyInit();
-await import("./desktop_main.js");
+bundleService.getWidgetBundlesByParent().then(async (widgetBundles) => {
+ // A dynamic import is required for layouts since they initialize components which require translations.
+ const DesktopLayout = (await import("./layouts/desktop_layout.js")).default;
+
+ appContext.setLayout(new DesktopLayout(widgetBundles));
+ appContext.start().catch((e) => {
+ toastService.showPersistent({
+ title: t("toast.critical-error.title"),
+ icon: "alert",
+ message: t("toast.critical-error.message", { message: e.message })
+ });
+ console.error("Critical error occured", e);
+ });
+});
+
+glob.setupGlobs();
+
+if (utils.isElectron()) {
+ initOnElectron();
+}
+
+noteTooltipService.setupGlobalTooltip();
+
+noteAutocompleteService.init();
+
+if (utils.isElectron()) {
+ electronContextMenu.setupContextMenu();
+}
+
+function initOnElectron() {
+ const electron: typeof Electron = utils.dynamicRequire("electron");
+ electron.ipcRenderer.on("globalShortcut", async (event, actionName) => appContext.triggerCommand(actionName));
+ electron.ipcRenderer.on("openInSameTab", async (event, noteId) => appContext.tabManager.openInSameTab(noteId));
+ const electronRemote: typeof ElectronRemote = utils.dynamicRequire("@electron/remote");
+ const currentWindow = electronRemote.getCurrentWindow();
+ const style = window.getComputedStyle(document.body);
+
+ initDarkOrLightMode(style);
+ initTransparencyEffects(style, currentWindow);
+
+ if (options.get("nativeTitleBarVisible") !== "true") {
+ initTitleBarButtons(style, currentWindow);
+ }
+}
+
+function initTitleBarButtons(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
+ if (window.glob.platform === "win32") {
+ const applyWindowsOverlay = () => {
+ const color = style.getPropertyValue("--native-titlebar-background");
+ const symbolColor = style.getPropertyValue("--native-titlebar-foreground");
+ if (color && symbolColor) {
+ currentWindow.setTitleBarOverlay({ color, symbolColor });
+ }
+ };
+
+ applyWindowsOverlay();
+
+ // Register for changes to the native title bar colors.
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyWindowsOverlay);
+ }
+
+ if (window.glob.platform === "darwin") {
+ const xOffset = parseInt(style.getPropertyValue("--native-titlebar-darwin-x-offset"), 10);
+ const yOffset = parseInt(style.getPropertyValue("--native-titlebar-darwin-y-offset"), 10);
+ currentWindow.setWindowButtonPosition({ x: xOffset, y: yOffset });
+ }
+}
+
+function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
+ if (window.glob.platform === "win32") {
+ const material = style.getPropertyValue("--background-material");
+ // TriliumNextTODO: find a nicer way to make TypeScript happy – unfortunately TS did not like Array.includes here
+ const bgMaterialOptions = ["auto", "none", "mica", "acrylic", "tabbed"] as const;
+ const foundBgMaterialOption = bgMaterialOptions.find((bgMaterialOption) => material === bgMaterialOption);
+ if (foundBgMaterialOption) {
+ currentWindow.setBackgroundMaterial(foundBgMaterialOption);
+ }
+ }
+}
+
+/**
+ * Informs Electron that we prefer a dark or light theme. Apart from changing prefers-color-scheme at CSS level which is a side effect,
+ * this fixes color issues with background effects or native title bars.
+ *
+ * @param style the root CSS element to read variables from.
+ */
+function initDarkOrLightMode(style: CSSStyleDeclaration) {
+ let themeSource: typeof nativeTheme.themeSource = "system";
+
+ const themeStyle = style.getPropertyValue("--theme-style");
+ if (style.getPropertyValue("--theme-style-auto") !== "true" && (themeStyle === "light" || themeStyle === "dark")) {
+ themeSource = themeStyle;
+ }
+
+ const { nativeTheme } = utils.dynamicRequire("@electron/remote") as typeof ElectronRemote;
+ nativeTheme.themeSource = themeSource;
+}
diff --git a/apps/client/src/desktop_main.ts b/apps/client/src/desktop_main.ts
deleted file mode 100644
index 42e1c44e9..000000000
--- a/apps/client/src/desktop_main.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import appContext from "./components/app_context.js";
-import utils from "./services/utils.js";
-import noteTooltipService from "./services/note_tooltip.js";
-import bundleService from "./services/bundle.js";
-import toastService from "./services/toast.js";
-import noteAutocompleteService from "./services/note_autocomplete.js";
-import electronContextMenu from "./menus/electron_context_menu.js";
-import glob from "./services/glob.js";
-import { t } from "./services/i18n.js";
-import options from "./services/options.js";
-import type ElectronRemote from "@electron/remote";
-import type Electron from "electron";
-import "./stylesheets/bootstrap.scss";
-import "boxicons";
-import "jquery-hotkeys";
-import "autocomplete.js/index_jquery.js";
-
-await appContext.earlyInit();
-
-bundleService.getWidgetBundlesByParent().then(async (widgetBundles) => {
- // A dynamic import is required for layouts since they initialize components which require translations.
- const DesktopLayout = (await import("./layouts/desktop_layout.js")).default;
-
- appContext.setLayout(new DesktopLayout(widgetBundles));
- appContext.start().catch((e) => {
- toastService.showPersistent({
- title: t("toast.critical-error.title"),
- icon: "alert",
- message: t("toast.critical-error.message", { message: e.message })
- });
- console.error("Critical error occured", e);
- });
-});
-
-glob.setupGlobs();
-
-if (utils.isElectron()) {
- initOnElectron();
-}
-
-noteTooltipService.setupGlobalTooltip();
-
-noteAutocompleteService.init();
-
-if (utils.isElectron()) {
- electronContextMenu.setupContextMenu();
-}
-
-function initOnElectron() {
- const electron: typeof Electron = utils.dynamicRequire("electron");
- electron.ipcRenderer.on("globalShortcut", async (event, actionName) => appContext.triggerCommand(actionName));
- electron.ipcRenderer.on("openInSameTab", async (event, noteId) => appContext.tabManager.openInSameTab(noteId));
- const electronRemote: typeof ElectronRemote = utils.dynamicRequire("@electron/remote");
- const currentWindow = electronRemote.getCurrentWindow();
- const style = window.getComputedStyle(document.body);
-
- initDarkOrLightMode(style);
- initTransparencyEffects(style, currentWindow);
-
- if (options.get("nativeTitleBarVisible") !== "true") {
- initTitleBarButtons(style, currentWindow);
- }
-}
-
-function initTitleBarButtons(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
- if (window.glob.platform === "win32") {
- const applyWindowsOverlay = () => {
- const color = style.getPropertyValue("--native-titlebar-background");
- const symbolColor = style.getPropertyValue("--native-titlebar-foreground");
- if (color && symbolColor) {
- currentWindow.setTitleBarOverlay({ color, symbolColor });
- }
- };
-
- applyWindowsOverlay();
-
- // Register for changes to the native title bar colors.
- window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyWindowsOverlay);
- }
-
- if (window.glob.platform === "darwin") {
- const xOffset = parseInt(style.getPropertyValue("--native-titlebar-darwin-x-offset"), 10);
- const yOffset = parseInt(style.getPropertyValue("--native-titlebar-darwin-y-offset"), 10);
- currentWindow.setWindowButtonPosition({ x: xOffset, y: yOffset });
- }
-}
-
-function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
- if (window.glob.platform === "win32") {
- const material = style.getPropertyValue("--background-material");
- // TriliumNextTODO: find a nicer way to make TypeScript happy – unfortunately TS did not like Array.includes here
- const bgMaterialOptions = ["auto", "none", "mica", "acrylic", "tabbed"] as const;
- const foundBgMaterialOption = bgMaterialOptions.find((bgMaterialOption) => material === bgMaterialOption);
- if (foundBgMaterialOption) {
- currentWindow.setBackgroundMaterial(foundBgMaterialOption);
- }
- }
-}
-
-/**
- * Informs Electron that we prefer a dark or light theme. Apart from changing prefers-color-scheme at CSS level which is a side effect,
- * this fixes color issues with background effects or native title bars.
- *
- * @param style the root CSS element to read variables from.
- */
-function initDarkOrLightMode(style: CSSStyleDeclaration) {
- let themeSource: typeof nativeTheme.themeSource = "system";
-
- const themeStyle = style.getPropertyValue("--theme-style");
- if (style.getPropertyValue("--theme-style-auto") !== "true" && (themeStyle === "light" || themeStyle === "dark")) {
- themeSource = themeStyle;
- }
-
- const { nativeTheme } = utils.dynamicRequire("@electron/remote") as typeof ElectronRemote;
- nativeTheme.themeSource = themeSource;
-}
diff --git a/apps/client/src/runtime.ts b/apps/client/src/runtime.ts
new file mode 100644
index 000000000..50c385778
--- /dev/null
+++ b/apps/client/src/runtime.ts
@@ -0,0 +1,5 @@
+import $ from "jquery";
+(window as any).$ = $;
+(window as any).jQuery = $;
+
+$("body").show();
diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts
index 7776dde30..2caf61127 100644
--- a/apps/client/vite.config.ts
+++ b/apps/client/vite.config.ts
@@ -50,6 +50,7 @@ export default defineConfig(() => ({
setup: join(__dirname, "src", "setup.ts"),
share: join(__dirname, "src", "share.ts"),
set_password: join(__dirname, "src", "set_password.ts"),
+ runtime: join(__dirname, "src", "runtime.ts")
},
output: {
entryFileNames: "[name].js",
diff --git a/apps/server/src/assets/views/desktop.ejs b/apps/server/src/assets/views/desktop.ejs
index 0822f9a3b..89e91b6af 100644
--- a/apps/server/src/assets/views/desktop.ejs
+++ b/apps/server/src/assets/views/desktop.ejs
@@ -53,6 +53,7 @@
+