feat(options/appearance): add basic illustration for old layout

This commit is contained in:
Elian Doran 2025-12-17 12:37:12 +02:00
parent af02685f2f
commit 948a6f84d6
No known key found for this signature in database
7 changed files with 237 additions and 33 deletions

View file

@ -0,0 +1,91 @@
.old-layout-illustration {
width: 170px;
height: 130px;
border: 1px solid var(--main-border-color);
border-radius: 6px;
display: flex;
background: var(--root-background);
overflow: hidden;
.launcher-pane {
width: 10%;
background: var(--launcher-pane-vert-background-color);
display: flex;
flex-direction: column;
align-items: center;
padding: 1px 0;
svg {
margin-top: 1px;
margin-bottom: 5px;
}
.bx {
margin: 4px 0;
font-size: 12px;
opacity: 0.5;
}
}
.tree {
width: 20%;
font-size: 4px;
padding: 12px 5px;
overflow: hidden;
flex-shrink: 0;
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
}
.main {
display: flex;
flex-direction: column;
flex-grow: 1;
.tab-bar {
height: 20px;
}
.content {
background-color: var(--main-background-color);
flex-grow: 1;
border-top-left-radius: 6px;
padding: 5px;
.title-bar {
display: flex;
align-items: center;
font-size: 14px;
.title {
flex-grow: 1;
}
}
.ribbon {
.bx {
font-size: 10px;
}
.ribbon-header {
display: flex;
}
.ribbon-body {
height: 20px;
background-color: rgba(0, 0, 0, 0.05);
border-radius: 6px;
margin: 1px 0;
}
}
.content-inner {
font-size: 6px;
}
}
}
}

View file

@ -1,18 +1,23 @@
import "./appearance.css";
import { FontFamily, OptionNames } from "@triliumnext/commons";
import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import { isElectron, isMobile, reloadFrontendApp, restartDesktopApp } from "../../../services/utils";
import Column from "../../react/Column";
import FormRadioGroup from "../../react/FormRadioGroup";
import FormSelect, { FormSelectWithGroups } from "../../react/FormSelect";
import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import OptionsSection from "./components/OptionsSection";
import server from "../../../services/server";
import { isElectron, isMobile, reloadFrontendApp, restartDesktopApp } from "../../../services/utils";
import Button from "../../react/Button";
import Column from "../../react/Column";
import FormCheckbox from "../../react/FormCheckbox";
import FormGroup from "../../react/FormGroup";
import { FontFamily, OptionNames } from "@triliumnext/commons";
import FormTextBox, { FormTextBoxWithUnit } from "../../react/FormTextBox";
import FormRadioGroup from "../../react/FormRadioGroup";
import FormSelect, { FormSelectWithGroups } from "../../react/FormSelect";
import FormText from "../../react/FormText";
import Button from "../../react/Button";
import FormTextBox, { FormTextBoxWithUnit } from "../../react/FormTextBox";
import { useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import Icon from "../../react/Icon";
import OptionsSection from "./components/OptionsSection";
import RadioWithIllustration from "./components/RadioWithIllustration";
import RelatedSettings from "./components/RelatedSettings";
const MIN_CONTENT_WIDTH = 640;
@ -30,7 +35,7 @@ const BUILTIN_THEMES: Theme[] = [
{ val: "auto", title: t("theme.auto_theme") },
{ val: "light", title: t("theme.light_theme") },
{ val: "dark", title: t("theme.dark_theme") }
]
];
interface FontFamilyEntry {
value: FontFamily;
@ -84,6 +89,7 @@ export default function AppearanceSettings() {
return (
<div>
{!isMobile() && <LayoutSwitcher />}
{!isMobile() && <LayoutOrientation />}
<ApplicationTheme />
{overrideThemeFonts === "true" && <Fonts />}
@ -102,7 +108,73 @@ export default function AppearanceSettings() {
}
]} />
</div>
)
);
}
function LayoutSwitcher() {
return (
<OptionsSection title="Layout">
<RadioWithIllustration
values={[
{ key: "old-layout", text: "Old layout", illustration: <LayoutIllustration /> },
{ key: "new-layout", text: "New layout", illustration: <LayoutIllustration isNewLayout /> }
]}
/>
</OptionsSection>
);
}
function LayoutIllustration({ isNewLayout }: { isNewLayout: boolean }) {
return (
<div className="old-layout-illustration">
<div className="launcher-pane">
<svg viewBox="0 0 256 256" aria-label="Menu" data-bs-original-title="Menu"><g><path d="m202.9 112.7c-22.5 16.1-54.5 12.8-74.9 6.3l14.8-11.8 14.1-11.3 49.1-39.3-51.2 35.9-14.3 10-14.9 10.5c0.7-21.2 7-49.9 28.6-65.4 1.8-1.3 3.9-2.6 6.1-3.8 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.4 2.8-4.9 5.4-7.4 7.8-3.4 3.5-6.8 6.4-10.1 8.8z" class="st0" /><path d="m213.1 104c-22.2 12.6-51.4 9.3-70.3 3.2l14.1-11.3 49.1-39.3-51.2 35.9-14.3 10c0.5-18.1 4.9-42.1 19.7-58.6 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.3 2.8-4.8 5.4-7.2 7.8z" class="st1" /><path d="m220.5 96.2c-21.1 8.6-46.6 5.3-63.7-0.2l49.2-39.4-51.2 35.9c0.3-15.8 3.5-36.6 14.3-52.8 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.8 66z" class="st2" /><path d="m106.7 179c-5.8-21 5.2-43.8 15.5-57.2l4.8 14.2 4.5 13.4 15.9 47-12.8-47.6-3.6-13.2-3.7-13.9c15.5 6.2 35.1 18.6 40.7 38.8 0.5 1.7 0.9 3.6 1.2 5.5 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.1-3.8-7.6-1.6-3.5-2.9-6.8-3.8-10z" class="st3" /><path d="m110.4 188.9c-3.4-19.8 6.9-40.5 16.6-52.9l4.5 13.4 15.9 47-12.8-47.6-3.6-13.2c13.3 5.2 29.9 15 38.1 30.4 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.2-3.8-7.7z" class="st4" /><path d="m114.2 196.5c-0.7-18 8.6-35.9 17.3-47.1l15.9 47-12.8-47.6c11.6 4.4 26.1 12.4 35.2 24.8 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8z" class="st5" /><path d="m86.3 59.1c21.7 10.9 32.4 36.6 35.8 54.9l-15.2-6.6-14.5-6.3-50.6-22 48.8 24.9 13.6 6.9 14.3 7.3c-16.6 7.9-41.3 14.5-62.1 4.1-1.8-0.9-3.6-1.9-5.4-3.2-2.3-1.5-4.5-3.2-6.8-5.1-19.9-16.4-40.3-46.4-42.7-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.2 0.8 6.2 1.6 9.1 2.5 4 1.3 7.6 2.8 10.9 4.4z" class="st6" /><path d="m75.4 54.8c18.9 12 28.4 35.6 31.6 52.6l-14.5-6.3-50.6-22 48.7 24.9 13.6 6.9c-14.1 6.8-34.5 13-53.3 8.2-2.3-1.5-4.5-3.2-6.8-5.1-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.1 0.8 6.2 1.6 9.1 2.6z" class="st7" /><path d="m66.3 52.2c15.3 12.8 23.3 33.6 26.1 48.9l-50.6-22 48.8 24.9c-12.2 6-29.6 11.8-46.5 10-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3z" class="st8" /></g></svg>
<Icon icon="bx bx-send" />
<Icon icon="bx bx-file-blank" />
<Icon icon="bx bx-search" />
</div>
<div className="tree">
<ul>
<li>Options</li>
<ul>
<li>Appearance</li>
<li>Shortcuts</li>
<li>Text Notes</li>
<li>Code Notes</li>
<li>Images</li>
</ul>
</ul>
</div>
<div className="main">
<div className="tab-bar" />
<div className="content">
<div className="title-bar">
<Icon icon="bx bx-note" />
<span className="title">Title</span>
<Icon icon="bx bx-dock-right" />
</div>
{!isNewLayout && <div className="ribbon">
<div className="ribbon-header">
<Icon icon="bx bx-slider" />
<Icon icon="bx bx-list-check" />
<Icon icon="bx bx-list-plus" />
<Icon icon="bx bx-collection" />
</div>
<div className="ribbon-body" />
</div>}
<div className="content-inner">
This is a "demo" document packaged with Trilium to showcase some of its features and also give you some ideas on how you might structure your notes. You can play with it, and modify the note content and tree structure as you wish.
</div>
</div>
</div>
</div>
);
}
function LayoutOrientation() {
@ -141,7 +213,7 @@ function ApplicationTheme() {
setThemes([
...BUILTIN_THEMES,
...userThemes
])
]);
});
}, []);
@ -162,7 +234,7 @@ function ApplicationTheme() {
</FormGroup>
</div>
</OptionsSection>
)
);
}
function Fonts() {
@ -245,7 +317,7 @@ function ElectronIntegration() {
<Button text={t("electron_integration.restart-app-button")} onClick={restartDesktopApp} />
</OptionsSection>
)
);
}
function Performance() {
@ -271,7 +343,7 @@ function Performance() {
{isElectron() && <SmoothScrollEnabledOption />}
</OptionsSection>
</OptionsSection>;
}
function SmoothScrollEnabledOption() {
@ -280,7 +352,7 @@ function SmoothScrollEnabledOption() {
return <FormCheckbox
label={`${t("ui-performance.enable-smooth-scroll")} ${t("ui-performance.app-restart-required")}`}
currentValue={smoothScrollEnabled} onChange={setSmoothScrollEnabled}
/>
/>;
}
function MaxContentWidth() {
@ -302,10 +374,10 @@ function MaxContentWidth() {
</Column>
<FormCheckbox label={t("max_content_width.centerContent")}
currentValue={centerContent}
onChange={setCenterContent} />
currentValue={centerContent}
onChange={setCenterContent} />
</OptionsSection>
)
);
}
function RibbonOptions() {
@ -318,5 +390,5 @@ function RibbonOptions() {
currentValue={editedNotesOpenInRibbon} onChange={setEditedNotesOpenInRibbon}
/>
</OptionsSection>
)
);
}

View file

@ -0,0 +1,8 @@
.radio-with-illustration {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
gap: 1em;
justify-content: center;
}

View file

@ -0,0 +1,28 @@
import "./RadioWithIllustration.css";
import { ComponentChild } from "preact";
interface RadioWithIllustrationProps {
values: {
key: string;
text: string;
illustration: ComponentChild;
}[];
currentValue: string;
onChange(newValue: string);
}
export default function RadioWithIllustration({ values }: RadioWithIllustrationProps) {
return (
<ul className="radio-with-illustration">
{values.map(value => (
<li key={value.key}>
<figure>
{value.illustration}
<figcaption>{value.text}</figcaption>
</figure>
</li>
))}
</ul>
);
}

View file

@ -1,13 +1,14 @@
"use strict";
import optionService from "../../services/options.js";
import log from "../../services/log.js";
import searchService from "../../services/search/services/search.js";
import ValidationError from "../../errors/validation_error.js";
import type { Request } from "express";
import { changeLanguage, getLocales } from "../../services/i18n.js";
import type { OptionNames } from "@triliumnext/commons";
import type { Request } from "express";
import ValidationError from "../../errors/validation_error.js";
import config from "../../services/config.js";
import { changeLanguage, getLocales } from "../../services/i18n.js";
import log from "../../services/log.js";
import optionService from "../../services/options.js";
import searchService from "../../services/search/services/search.js";
interface UserTheme {
val: string; // value of the theme, used in the URL
@ -100,6 +101,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"splitEditorOrientation",
"seenCallToActions",
"experimentalFeatures",
"newLayout",
// AI/LLM integration options
"aiEnabled",

View file

@ -1,10 +1,11 @@
import optionService from "./options.js";
import { type KeyboardShortcutWithRequiredActionName, type OptionMap, type OptionNames, SANITIZER_DEFAULT_ALLOWED_TAGS } from "@triliumnext/commons";
import appInfo from "./app_info.js";
import { randomSecureToken, isWindows } from "./utils.js";
import log from "./log.js";
import dateUtils from "./date_utils.js";
import keyboardActions from "./keyboard_actions.js";
import { SANITIZER_DEFAULT_ALLOWED_TAGS, type KeyboardShortcutWithRequiredActionName, type OptionMap, type OptionNames } from "@triliumnext/commons";
import log from "./log.js";
import optionService from "./options.js";
import { isWindows,randomSecureToken } from "./utils.js";
function initDocumentOptions() {
optionService.createOption("documentId", randomSecureToken(16), false);
@ -156,6 +157,7 @@ const defaultOptions: DefaultOption[] = [
{ name: "shadowsEnabled", value: "true", isSynced: false },
{ name: "backdropEffectsEnabled", value: "true", isSynced: false },
{ name: "smoothScrollEnabled", value: "true", isSynced: false },
{ name: "newLayout", value: "true", isSynced: true },
// Internationalization
{ name: "locale", value: "en", isSynced: true },
@ -171,9 +173,9 @@ const defaultOptions: DefaultOption[] = [
value: (optionsMap) => {
if (optionsMap.theme === "light") {
return "default:stackoverflow-light";
} else {
return "default:stackoverflow-dark";
}
return "default:stackoverflow-dark";
},
isSynced: false
},

View file

@ -131,6 +131,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
/** Whether keyboard auto-completion for editing commands is triggered when typing `/`. */
textNoteSlashCommandsEnabled: boolean;
backgroundEffects: boolean;
newLayout: boolean;
// Share settings
redirectBareDomain: boolean;