diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index ee38ab8cd..b2ebef90f 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -1,6 +1,7 @@
import FlexContainer from "../widgets/containers/flex_container.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import TabRowWidget from "../widgets/tab_row.js";
+import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
import LeftPaneContainer from "../widgets/containers/left_pane_container.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import NoteTitleWidget from "../widgets/note_title.js";
@@ -94,7 +95,11 @@ export default class DesktopLayout {
const launcherPaneIsHorizontal = (options.get("layoutOrientation") === "horizontal");
const launcherPane = this.#buildLauncherPane(launcherPaneIsHorizontal);
+ const isMac = (window.glob.platform === "darwin");
+ const isWindows = (window.glob.platform === "windows");
+ const hasNativeTitleBar = (window.glob.hasNativeTitleBar);
const fullWidthTabBar = true;
+ const customTitleBarButtons = (hasNativeTitleBar && !isMac && !isWindows);
return new RootContainer(true)
.setParent(appContext)
@@ -104,6 +109,7 @@ export default class DesktopLayout {
.child(new FlexContainer( "row").id("tab-row-left-spacer"))
.optChild(launcherPaneIsHorizontal, new LeftPaneToggleWidget(true))
.child(new TabRowWidget().class("full-width"))
+ .optChild(customTitleBarButtons, new TitleBarButtonsWidget())
.css('height', '40px')
.css('background-color', 'var(--launcher-pane-background-color)')
.setParent(appContext)
@@ -122,6 +128,7 @@ export default class DesktopLayout {
.css("flex-grow", "1")
.optChild(!fullWidthTabBar, new FlexContainer('row')
.child(new TabRowWidget())
+ .optChild(customTitleBarButtons, new TitleBarButtonsWidget())
.css('height', '40px')
)
.child(new FlexContainer('row')
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
new file mode 100644
index 000000000..edb270f77
--- /dev/null
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -0,0 +1,112 @@
+import BasicWidget from "./basic_widget.js";
+import options from "../services/options.js";
+import utils from "../services/utils.js";
+import { t } from "../services/i18n.js";
+
+const TPL = `
+
`;
+
+export default class TitleBarButtonsWidget extends BasicWidget {
+ doRender() {
+ if (!utils.isElectron() || options.is('nativeTitleBarVisible')) {
+ return this.$widget = $('');
+ }
+
+ this.$widget = $(TPL);
+ this.contentSized();
+
+ const $topBtn = this.$widget.find(".top-btn");
+ const $minimizeBtn = this.$widget.find(".minimize-btn");
+ const $maximizeBtn = this.$widget.find(".maximize-btn");
+ const $closeBtn = this.$widget.find(".close-btn");
+
+ // When the window is restarted, the window will not be reset when it is set to the top,
+ // so get the window status and set the icon background
+ setTimeout(() => {
+ const remote = utils.dynamicRequire('@electron/remote');
+ if (remote.BrowserWindow.getFocusedWindow()?.isAlwaysOnTop()) {
+ $topBtn.addClass('active');
+ }
+ }, 1000);
+
+ $topBtn.on('click', () => {
+ $topBtn.trigger('blur');
+ const remote = utils.dynamicRequire('@electron/remote');
+ const focusedWindow = remote.BrowserWindow.getFocusedWindow();
+ const isAlwaysOnTop = focusedWindow.isAlwaysOnTop()
+ if (isAlwaysOnTop) {
+ focusedWindow.setAlwaysOnTop(false)
+ $topBtn.removeClass('active');
+ } else {
+ focusedWindow.setAlwaysOnTop(true);
+ $topBtn.addClass('active');
+ }
+ });
+
+ $minimizeBtn.on('click', () => {
+ $minimizeBtn.trigger('blur');
+ const remote = utils.dynamicRequire('@electron/remote');
+ remote.BrowserWindow.getFocusedWindow().minimize();
+ });
+
+ $maximizeBtn.on('click', () => {
+ $maximizeBtn.trigger('blur');
+ const remote = utils.dynamicRequire('@electron/remote');
+ const focusedWindow = remote.BrowserWindow.getFocusedWindow();
+
+ if (focusedWindow.isMaximized()) {
+ focusedWindow.unmaximize();
+ } else {
+ focusedWindow.maximize();
+ }
+ });
+
+ $closeBtn.on('click', () => {
+ $closeBtn.trigger('blur');
+ const remote = utils.dynamicRequire('@electron/remote');
+ remote.BrowserWindow.getFocusedWindow().close();
+ });
+ }
+}
diff --git a/src/services/window.ts b/src/services/window.ts
index 44e71e9b7..72a337d9a 100644
--- a/src/services/window.ts
+++ b/src/services/window.ts
@@ -114,9 +114,21 @@ async function createMainWindow(app: App) {
function getWindowExtraOpts() {
const extraOpts: Partial = {};
+
+ const isMac = (process.platform === "darwin");
+ const isWindows = (process.platform === "win32");
+
if (!optionService.getOptionBool('nativeTitleBarVisible')) {
- extraOpts.titleBarStyle = (process.platform !== "darwin" ? "hidden" : "hiddenInset");
- extraOpts.titleBarOverlay = true;
+ if (isMac) {
+ extraOpts.titleBarStyle = "hiddenInset";
+ extraOpts.titleBarOverlay = true;
+ } else if (isWindows) {
+ extraOpts.titleBarStyle = "hidden";
+ extraOpts.titleBarOverlay = true;
+ } else {
+ // Linux or other platforms.
+ extraOpts.frame = false;
+ }
}
return extraOpts;
diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs
index f922e9454..3e1ce7229 100644
--- a/src/views/desktop.ejs
+++ b/src/views/desktop.ejs
@@ -36,6 +36,8 @@
triliumVersion: "<%= triliumVersion %>",
assetPath: "<%= assetPath %>",
appPath: "<%= appPath %>",
+ platform: "<%= platform %>",
+ hasNativeTitleBar: "<%= hasNativeTitleBar %>",
TRILIUM_SAFE_MODE: <%= !!process.env.TRILIUM_SAFE_MODE %>
};