Compare commits

...

9 Commits

Author SHA1 Message Date
Elian Doran
5d5947f676 feat(call_to_action): enable background effects CTA on macOS 2026-01-27 23:31:05 +02:00
Elian Doran
8fc889ae08 chore(options): change description 2026-01-27 23:22:31 +02:00
Elian Doran
ff46493775 feat(options): add platform indicator 2026-01-27 23:17:38 +02:00
Elian Doran
a1cb3b8371 style(desktop): change material for horizontal layout 2026-01-27 16:42:52 +02:00
Elian Doran
51131433d3 chore(client): address requested changes 2026-01-27 15:56:57 +02:00
Elian Doran
3c8a066f76 feat(desktop): handle both vertical and horizontal layouts 2026-01-26 23:28:29 +02:00
Elian Doran
6856a98d50 feat(desktop): integrate vibrancy 2026-01-26 23:21:40 +02:00
Elian Doran
120b767a68 fix(desktop): background effects not applied correctly 2026-01-26 23:10:19 +02:00
Elian Doran
8c0d4cde86 feat(desktop): basic vibrancy support 2026-01-26 23:02:14 +02:00
10 changed files with 107 additions and 40 deletions

View File

@@ -99,15 +99,22 @@ function initFullScreenDetection(currentWindow: Electron.BrowserWindow) {
}
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
const material = style.getPropertyValue("--background-material").trim();
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);
}
}
if (window.glob.platform === "darwin") {
const bgMaterialOptions = [ "popover", "tooltip", "titlebar", "selection", "menu", "sidebar", "header", "sheet", "window", "hud", "fullscreen-ui", "content", "under-window", "under-page" ] as const;
const foundBgMaterialOption = bgMaterialOptions.find((bgMaterialOption) => material === bgMaterialOption);
if (foundBgMaterialOption) {
currentWindow.setVibrancy(foundBgMaterialOption);
}
}
}
/**

View File

@@ -40,13 +40,30 @@ body.mobile {
/* #region Mica */
/* Quirk: --background-material is read before "theme-supports-background-effects" class
* is applied. Apply the matterial even if the theme doesn't support it. */
body.background-effects.platform-win32 {
/* Quirk: --background-material is read before "theme-supports-background-effects" class
* is applied. Apply the matterial even if the theme doesn't support it. */
--background-material: tabbed;
&.layout-vertical {
--background-material: mica;
}
&.layout-horizontal {
--background-material: tabbed;
}
}
body.background-effects.theme-supports-background-effects.platform-win32 {
body.background-effects.platform-darwin {
/** Reference: https://developer.apple.com/documentation/appkit/nsvisualeffectview?preferredLanguage=objc **/
&.layout-vertical {
--background-material: under-window;
}
&.layout-horizontal {
--background-material: hud;
}
}
body.background-effects.theme-supports-background-effects {
--launcher-pane-horiz-border-color: var(--launcher-pane-horiz-border-color-bgfx);
--launcher-pane-horiz-background-color: var(--launcher-pane-horiz-background-color-bgfx);
--launcher-pane-vert-background-color: var(--launcher-pane-vert-background-color-bgfx);
@@ -56,33 +73,29 @@ body.background-effects.theme-supports-background-effects.platform-win32 {
--root-background: transparent;
}
body.background-effects.platform-win32.layout-vertical {
--background-material: mica;
}
body.background-effects.theme-supports-background-effects.platform-win32.layout-vertical {
body.background-effects.theme-supports-background-effects.layout-vertical {
--left-pane-background-color: var(--window-background-color-bgfx);
--center-pane-background-color-bgfx: var(--center-pane-vert-layout-background-color-bgfx);
--right-pane-background-color: var(--right-pane-background-color-bgfx);
}
body.background-effects.theme-supports-background-effects.platform-win32.layout-horizontal {
body.background-effects.theme-supports-background-effects.layout-horizontal {
--center-pane-background-color-bgfx: var(--center-pane-horiz-layout-background-color-bgfx);
--gutter-color: var(--left-pane-background-color);
}
body.background-effects.theme-supports-background-effects.platform-win32,
body.background-effects.theme-supports-background-effects.platform-win32 #root-widget {
body.background-effects.theme-supports-background-effects,
body.background-effects.theme-supports-background-effects #root-widget {
background: var(--window-background-color-bgfx) !important;
}
body.background-effects.theme-supports-background-effects.platform-win32.layout-horizontal #horizontal-main-container,
body.background-effects.theme-supports-background-effects.platform-win32.layout-vertical #vertical-main-container {
body.background-effects.theme-supports-background-effects.layout-horizontal #horizontal-main-container,
body.background-effects.theme-supports-background-effects.layout-vertical #vertical-main-container {
background-color: var(--root-background);
}
/* Note split with background effects */
body.background-effects.theme-supports-background-effects.platform-win32 #center-pane .note-split.bgfx {
body.background-effects.theme-supports-background-effects #center-pane .note-split.bgfx {
--note-split-background-color: var(--center-pane-background-color-bgfx);
}
@@ -1054,7 +1067,7 @@ body.layout-horizontal .tab-row-widget-container {
overflow: hidden;
}
body.desktop:not(.background-effects.platform-win32) #root-widget.horizontal-layout {
body.desktop:not(.background-effects) #root-widget.horizontal-layout {
background-color: var(--root-background) !important;
}

View File

@@ -1958,8 +1958,8 @@
"desktop-application": "Desktop Application",
"native-title-bar": "Native title bar",
"native-title-bar-description": "For Windows and macOS, keeping the native title bar off makes the application look more compact. On Linux, keeping the native title bar on integrates better with the rest of the system.",
"background-effects": "Enable background effects (Windows 11 only)",
"background-effects-description": "The Mica effect adds a blurred, stylish background to app windows, creating depth and a modern look. \"Native title bar\" must be disabled.",
"background-effects": "Enable background effects",
"background-effects-description": "Adds a blurred, stylish background to app windows, creating depth and a modern look. \"Native title bar\" must be disabled.",
"restart-app-button": "Restart the application to view the changes",
"zoom-factor": "Zoom factor"
},
@@ -2152,7 +2152,7 @@
"next_theme_message": "You are currently using the legacy theme, would you like to try the new theme?",
"next_theme_button": "Try the new theme",
"background_effects_title": "Background effects are now stable",
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of color to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer.",
"background_effects_message": "On Windows and macOS devices, background effects are now stable. The background effects adds a touch of color to the user interface by blurring the background behind it.",
"background_effects_button": "Enable background effects",
"new_layout_title": "New layout",
"new_layout_message": "Weve introduced a modernized layout for Trilium. The ribbon has been removed and seamlessly integrated into the main interface, with a new status bar and expandable sections (such as promoted attributes) taking over key functions.\n\nThe new layout is enabled by default, and can be temporarily disabled via Options → Appearance.",
@@ -2267,5 +2267,8 @@
"pages_other": "{{count}} pages",
"pages_alt": "Page {{pageNumber}}",
"pages_loading": "Loading..."
},
"platform_indicator": {
"available_on": "Available on {{platform}}"
}
}

View File

@@ -1,7 +1,7 @@
import appContext from "../../components/app_context";
import { t } from "../../services/i18n";
import options from "../../services/options";
import utils from "../../services/utils";
import utils, { isMac } from "../../services/utils";
/**
* A "call-to-action" is an interactive message for the user, generally to present new features.
@@ -41,10 +41,6 @@ export interface CallToAction {
}[];
}
function isNextTheme() {
return [ "next", "next-light", "next-dark" ].includes(options.get("theme"));
}
const CALL_TO_ACTIONS: CallToAction[] = [
{
id: "new_layout",
@@ -63,7 +59,7 @@ const CALL_TO_ACTIONS: CallToAction[] = [
id: "background_effects",
title: t("call_to_action.background_effects_title"),
message: t("call_to_action.background_effects_message"),
enabled: () => false,
enabled: () => (isMac() && !options.is("backgroundEffects")),
buttons: [
{
text: t("call_to_action.background_effects_button"),
@@ -78,7 +74,7 @@ const CALL_TO_ACTIONS: CallToAction[] = [
id: "next_theme",
title: t("call_to_action.next_theme_title"),
message: t("call_to_action.next_theme_message"),
enabled: () => !isNextTheme(),
enabled: () => ![ "next", "next-light", "next-dark" ].includes(options.get("theme")),
buttons: [
{
text: t("call_to_action.next_theme_button"),

View File

@@ -1,7 +1,7 @@
import clsx from "clsx";
import { HTMLAttributes } from "preact";
interface IconProps extends Pick<HTMLAttributes<HTMLSpanElement>, "className" | "onClick"> {
interface IconProps extends Pick<HTMLAttributes<HTMLSpanElement>, "className" | "onClick" | "title"> {
icon?: string;
className?: string;
}

View File

@@ -18,6 +18,7 @@ import FormTextBox, { FormTextBoxWithUnit } from "../../react/FormTextBox";
import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import Icon from "../../react/Icon";
import OptionsSection from "./components/OptionsSection";
import PlatformIndicator from "./components/PlatformIndicator";
import RadioWithIllustration from "./components/RadioWithIllustration";
import RelatedSettings from "./components/RelatedSettings";
@@ -174,13 +175,13 @@ function LayoutIllustration({ isNewLayout }: { isNewLayout?: boolean }) {
</div>
</div>
) : (
<div>
<div className="title-bar">
<Icon icon="bx bx-leaf" />
<span className="title">Title</span>
<Icon icon="bx bx-dock-right" />
<div>
<div className="title-bar">
<Icon icon="bx bx-leaf" />
<span className="title">Title</span>
<Icon icon="bx bx-dock-right" />
</div>
</div>
</div>
)}
{!isNewLayout && <div className="ribbon">
@@ -192,7 +193,7 @@ function LayoutIllustration({ isNewLayout }: { isNewLayout?: boolean }) {
</div>
<div className="ribbon-body">
<div className="ribbon-body-content"></div>
<div className="ribbon-body-content" />
</div>
</div>}
@@ -356,7 +357,11 @@ function ElectronIntegration() {
<FormGroup name="background-effects" description={t("electron_integration.background-effects-description")}>
<FormCheckbox
label={t("electron_integration.background-effects")}
label={<>
{t("electron_integration.background-effects")}
{" "}
<PlatformIndicator windows="11" mac />
</>}
currentValue={backgroundEffects} onChange={setBackgroundEffects}
disabled={nativeTitleBarVisible}
/>

View File

@@ -0,0 +1,5 @@
.platform-indicator {
display: inline-flex;
gap: 0.25em;
color: var(--muted-text-color);
}

View File

@@ -0,0 +1,34 @@
import "./PlatformIndicator.css";
import { useRef } from "preact/hooks";
import { t } from "../../../../services/i18n";
import { useStaticTooltip } from "../../../react/hooks";
import Icon from "../../../react/Icon";
interface PlatformIndicatorProps {
windows?: boolean | "11";
mac: boolean;
}
export default function PlatformIndicator({ windows, mac }: PlatformIndicatorProps) {
const containerRef = useRef<HTMLDivElement>(null);
useStaticTooltip(containerRef, {
selector: "span",
animation: false,
title() { return this.title; },
});
return (
<div ref={containerRef} className="platform-indicator">
{windows && <Icon
icon="bx bxl-windows"
title={t("platform_indicator.available_on", { platform: windows === "11" ? "Windows 11" : "Windows" })}
/>}
{mac && <Icon
icon="bx bxl-apple"
title={t("platform_indicator.available_on", { platform: "macOS" })}
/>}
</div>
);
}

View File

@@ -12,7 +12,7 @@ import log from "../services/log.js";
import optionService from "../services/options.js";
import protectedSessionService from "../services/protected_session.js";
import sql from "../services/sql.js";
import { isDev, isElectron, isWindows11 } from "../services/utils.js";
import { isDev, isElectron, isMac, isWindows11 } from "../services/utils.js";
import { generateToken as generateCsrfToken } from "./csrf_protection.js";
@@ -43,7 +43,10 @@ export function bootstrap(req: Request, res: Response) {
platform: process.platform,
isElectron,
hasNativeTitleBar: isElectron && nativeTitleBarVisible,
hasBackgroundEffects: isElectron && isWindows11 && !nativeTitleBarVisible && options.backgroundEffects === "true",
hasBackgroundEffects: options.backgroundEffects === "true"
&& isElectron
&& (isWindows11 || isMac)
&& !nativeTitleBarVisible,
maxEntityChangeIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes"),
maxEntityChangeSyncIdAtLoad: sql.getValue("SELECT COALESCE(MAX(id), 0) FROM entity_changes WHERE isSynced = 1"),
instanceName: config.General ? config.General.instanceName : null,

View File

@@ -241,7 +241,8 @@ function getWindowExtraOpts() {
// Window effects (Mica)
if (optionService.getOptionBool("backgroundEffects")) {
if (isMac) {
// Vibrancy not yet supported.
extraOpts.transparent = true;
extraOpts.visualEffectState = "active";
} else if (isWindows) {
extraOpts.backgroundMaterial = "auto";
} else {