Merge branch 'main' into main

This commit is contained in:
Xen0r 2025-12-18 09:36:55 +01:00 committed by GitHub
commit 28da93fc65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 1563 additions and 356 deletions

View File

@ -1,14 +1,14 @@
import Component from "./component.js";
import appContext, { type CommandData, type CommandListenerData } from "./app_context.js";
import dateNoteService from "../services/date_notes.js"; import dateNoteService from "../services/date_notes.js";
import treeService from "../services/tree.js";
import openService from "../services/open.js";
import protectedSessionService from "../services/protected_session.js";
import options from "../services/options.js";
import froca from "../services/froca.js"; import froca from "../services/froca.js";
import utils, { openInReusableSplit } from "../services/utils.js";
import toastService from "../services/toast.js";
import noteCreateService from "../services/note_create.js"; import noteCreateService from "../services/note_create.js";
import openService from "../services/open.js";
import options from "../services/options.js";
import protectedSessionService from "../services/protected_session.js";
import toastService from "../services/toast.js";
import treeService from "../services/tree.js";
import utils, { openInReusableSplit } from "../services/utils.js";
import appContext, { type CommandListenerData } from "./app_context.js";
import Component from "./component.js";
export default class RootCommandExecutor extends Component { export default class RootCommandExecutor extends Component {
editReadOnlyNoteCommand() { editReadOnlyNoteCommand() {
@ -193,10 +193,13 @@ export default class RootCommandExecutor extends Component {
appContext.triggerEvent("zenModeChanged", { isEnabled }); appContext.triggerEvent("zenModeChanged", { isEnabled });
} }
async toggleRibbonTabNoteMapCommand() { async toggleRibbonTabNoteMapCommand(data: CommandListenerData<"toggleRibbonTabNoteMap">) {
const { isExperimentalFeatureEnabled } = await import("../services/experimental_features.js"); const { isExperimentalFeatureEnabled } = await import("../services/experimental_features.js");
const isNewLayout = isExperimentalFeatureEnabled("new-layout"); const isNewLayout = isExperimentalFeatureEnabled("new-layout");
if (!isNewLayout) return; if (!isNewLayout) {
this.triggerEvent("toggleRibbonTabNoteMap", data);
return;
}
const activeContext = appContext.tabManager.getActiveContext(); const activeContext = appContext.tabManager.getActiveContext();
if (!activeContext?.notePath) return; if (!activeContext?.notePath) return;
@ -272,7 +275,7 @@ export default class RootCommandExecutor extends Component {
} }
catch (e) { catch (e) {
console.error("Error creating AI Chat note:", e); console.error("Error creating AI Chat note:", e);
toastService.showError("Failed to create AI Chat note: " + (e as Error).message); toastService.showError(`Failed to create AI Chat note: ${(e as Error).message}`);
} }
} }
} }

View File

@ -1,17 +1,17 @@
import server from "../services/server.js";
import noteAttributeCache from "../services/note_attribute_cache.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import cssClassManager from "../services/css_class_manager.js"; import cssClassManager from "../services/css_class_manager.js";
import type { Froca } from "../services/froca-interface.js"; import type { Froca } from "../services/froca-interface.js";
import type FAttachment from "./fattachment.js"; import noteAttributeCache from "../services/note_attribute_cache.js";
import type { default as FAttribute, AttributeType } from "./fattribute.js"; import protectedSessionHolder from "../services/protected_session_holder.js";
import utils from "../services/utils.js";
import search from "../services/search.js"; import search from "../services/search.js";
import server from "../services/server.js";
import utils from "../services/utils.js";
import type FAttachment from "./fattachment.js";
import type { AttributeType,default as FAttribute } from "./fattribute.js";
const LABEL = "label"; const LABEL = "label";
const RELATION = "relation"; const RELATION = "relation";
const NOTE_TYPE_ICONS = { export const NOTE_TYPE_ICONS = {
file: "bx bx-file", file: "bx bx-file",
image: "bx bx-image", image: "bx bx-image",
code: "bx bx-code", code: "bx bx-code",
@ -268,13 +268,12 @@ export default class FNote {
} }
} }
return results; return results;
} else {
return this.children;
} }
return this.children;
} }
async getSubtreeNoteIds(includeArchived = false) { async getSubtreeNoteIds(includeArchived = false) {
let noteIds: (string | string[])[] = []; const noteIds: (string | string[])[] = [];
for (const child of await this.getChildNotes()) { for (const child of await this.getChildNotes()) {
if (child.isArchived && !includeArchived) continue; if (child.isArchived && !includeArchived) continue;
@ -471,9 +470,8 @@ export default class FNote {
return a.isHidden ? 1 : -1; return a.isHidden ? 1 : -1;
} else if (a.isSearch !== b.isSearch) { } else if (a.isSearch !== b.isSearch) {
return a.isSearch ? 1 : -1; return a.isSearch ? 1 : -1;
} else {
return a.notePath.length - b.notePath.length;
} }
return a.notePath.length - b.notePath.length;
}); });
return notePaths; return notePaths;
@ -597,14 +595,12 @@ export default class FNote {
} else if (this.type === "text") { } else if (this.type === "text") {
if (this.isFolder()) { if (this.isFolder()) {
return "bx bx-folder"; return "bx bx-folder";
} else {
return "bx bx-note";
} }
return "bx bx-note";
} else if (this.type === "code" && this.mime.startsWith("text/x-sql")) { } else if (this.type === "code" && this.mime.startsWith("text/x-sql")) {
return "bx bx-data"; return "bx bx-data";
} else {
return NOTE_TYPE_ICONS[this.type];
} }
return NOTE_TYPE_ICONS[this.type];
} }
getColorClass() { getColorClass() {
@ -617,7 +613,7 @@ export default class FNote {
} }
getFilteredChildBranches() { getFilteredChildBranches() {
let childBranches = this.getChildBranches(); const childBranches = this.getChildBranches();
if (!childBranches) { if (!childBranches) {
console.error(`No children for '${this.noteId}'. This shouldn't happen.`); console.error(`No children for '${this.noteId}'. This shouldn't happen.`);
@ -811,9 +807,9 @@ export default class FNote {
return this.getLabelValue(nameWithPrefix.substring(1)); return this.getLabelValue(nameWithPrefix.substring(1));
} else if (nameWithPrefix.startsWith("~")) { } else if (nameWithPrefix.startsWith("~")) {
return this.getRelationValue(nameWithPrefix.substring(1)); return this.getRelationValue(nameWithPrefix.substring(1));
} else {
return this.getLabelValue(nameWithPrefix);
} }
return this.getLabelValue(nameWithPrefix);
} }
/** /**
@ -878,10 +874,10 @@ export default class FNote {
promotedAttrs.sort((a, b) => { promotedAttrs.sort((a, b) => {
if (a.noteId === b.noteId) { if (a.noteId === b.noteId) {
return a.position < b.position ? -1 : 1; return a.position < b.position ? -1 : 1;
} else {
// inherited promoted attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
return a.noteId < b.noteId ? -1 : 1;
} }
// inherited promoted attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
return a.noteId < b.noteId ? -1 : 1;
}); });
return promotedAttrs; return promotedAttrs;

View File

@ -20,11 +20,19 @@ export type ExperimentalFeatureId = typeof experimentalFeatures[number]["id"];
let enabledFeatures: Set<ExperimentalFeatureId> | null = null; let enabledFeatures: Set<ExperimentalFeatureId> | null = null;
export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId): boolean { export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId): boolean {
if (featureId === "new-layout") {
return options.is("newLayout");
}
return getEnabledFeatures().has(featureId); return getEnabledFeatures().has(featureId);
} }
export function getEnabledExperimentalFeatureIds() { export function getEnabledExperimentalFeatureIds() {
return getEnabledFeatures().values(); const values = [ ...getEnabledFeatures().values() ];
if (options.is("newLayout")) {
values.push("new-layout");
}
return values;
} }
export async function toggleExperimentalFeature(featureId: ExperimentalFeatureId, enable: boolean) { export async function toggleExperimentalFeature(featureId: ExperimentalFeatureId, enable: boolean) {

View File

@ -717,12 +717,17 @@ table.promoted-attributes-in-tooltip th {
.tooltip { .tooltip {
font-size: var(--main-font-size) !important; font-size: var(--main-font-size) !important;
z-index: calc(var(--ck-z-panel) - 1) !important; z-index: calc(var(--ck-z-panel) - 1) !important;
white-space: pre-wrap;
} }
.tooltip.tooltip-top { .tooltip.tooltip-top {
z-index: 32767 !important; z-index: 32767 !important;
} }
.pre-wrap-text {
white-space: pre-wrap;
}
.bs-tooltip-bottom .tooltip-arrow::before { .bs-tooltip-bottom .tooltip-arrow::before {
border-bottom-color: var(--main-border-color) !important; border-bottom-color: var(--main-border-color) !important;
} }

View File

@ -22,7 +22,7 @@
--ck-color-button-on-background: transparent; --ck-color-button-on-background: transparent;
--ck-color-button-on-hover-background: var(--hover-item-background-color); --ck-color-button-on-hover-background: var(--hover-item-background-color);
--ck-color-button-default-active-background: var(--hover-item-background-color); --ck-color-button-default-active-background: var(--hover-item-background-color);
--ck-color-split-button-hover-background: var(--ck-editor-toolbar-dropdown-button-open-background); --ck-color-split-button-hover-background: var(--ck-editor-toolbar-dropdown-button-open-background);
--ck-focus-ring: 1px solid transparent; --ck-focus-ring: 1px solid transparent;
@ -77,7 +77,7 @@
visibility: collapse; visibility: collapse;
} }
/* /*
* Dropdowns * Dropdowns
*/ */
@ -85,7 +85,7 @@
:root .ck.ck-dropdown__panel, :root .ck.ck-dropdown__panel,
:root .ck-balloon-panel { :root .ck-balloon-panel {
--ck-editor-popup-padding: 4px; --ck-editor-popup-padding: 4px;
--ck-color-panel-background: var(--menu-background-color); --ck-color-panel-background: var(--menu-background-color);
--ck-color-panel-border: var(--ck-editor-popup-border-color); --ck-color-panel-border: var(--ck-editor-popup-border-color);
@ -487,7 +487,7 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
.ck.ck-labeled-field-view > .ck.ck-labeled-field-view__input-wrapper > label.ck.ck-label { .ck.ck-labeled-field-view > .ck.ck-labeled-field-view__input-wrapper > label.ck.ck-label {
/* Move the label above the text box regardless of the text box state */ /* Move the label above the text box regardless of the text box state */
transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important; transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important;
padding-inline-start: 0 !important; padding-inline-start: 0 !important;
background: transparent; background: transparent;
font-size: .85em; font-size: .85em;
@ -518,7 +518,7 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
*/ */
/* /*
* Code Blocks * Code Blocks
*/ */
.attachment-content-wrapper pre, .attachment-content-wrapper pre,
@ -526,10 +526,14 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
.ck-mermaid__editing-view { .ck-mermaid__editing-view {
border: 0; border: 0;
border-radius: 6px; border-radius: 6px;
box-shadow: var(--code-block-box-shadow); box-shadow: var(--code-block-box-shadow);
margin-top: 2px !important; margin-top: 2px !important;
} }
.attachment-content-wrapper pre {
border-radius: var(--dropdown-border-radius);
}
:root .ck-content pre:has(> code) { :root .ck-content pre:has(> code) {
padding: 0; padding: 0;
} }
@ -542,7 +546,7 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
* for single-line code blocks */ * for single-line code blocks */
--copy-button-margin-size: calc((1em * 1.5 + var(--padding-size) * 2 - var(--icon-button-size)) / 2); --copy-button-margin-size: calc((1em * 1.5 + var(--padding-size) * 2 - var(--icon-button-size)) / 2);
/* Where: Line height /* Where: Line height
* Font size * Font size
*/ */
@ -690,4 +694,4 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
.note-list-widget { .note-list-widget {
outline: 0 !important; outline: 0 !important;
} }

View File

@ -2109,6 +2109,9 @@
"background_effects_title": "Background effects are now stable", "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 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_button": "Enable background effects", "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.",
"new_layout_button": "More info",
"dismiss": "Dismiss" "dismiss": "Dismiss"
}, },
"settings": { "settings": {
@ -2116,7 +2119,10 @@
}, },
"settings_appearance": { "settings_appearance": {
"related_code_blocks": "Color scheme for code blocks in text notes", "related_code_blocks": "Color scheme for code blocks in text notes",
"related_code_notes": "Color scheme for code notes" "related_code_notes": "Color scheme for code notes",
"ui": "User interface",
"ui_old_layout": "Old layout",
"ui_new_layout": "New layout"
}, },
"units": { "units": {
"percentage": "%" "percentage": "%"

View File

@ -1,7 +1,7 @@
import "./global_menu.css"; import "./global_menu.css";
import { KeyboardActionNames } from "@triliumnext/commons"; import { KeyboardActionNames } from "@triliumnext/commons";
import { ComponentChildren } from "preact"; import { ComponentChildren, RefObject } from "preact";
import { useContext, useEffect, useRef, useState } from "preact/hooks"; import { useContext, useEffect, useRef, useState } from "preact/hooks";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
@ -30,13 +30,15 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
const parentComponent = useContext(ParentComponent); const parentComponent = useContext(ParentComponent);
const { isUpdateAvailable, latestVersion } = useTriliumUpdateStatus(); const { isUpdateAvailable, latestVersion } = useTriliumUpdateStatus();
const isMobileLocal = isMobile(); const isMobileLocal = isMobile();
const logoRef = useRef<SVGSVGElement>(null);
useStaticTooltip(logoRef);
return ( return (
<Dropdown <Dropdown
className="global-menu" className="global-menu"
buttonClassName={`global-menu-button ${isHorizontalLayout ? "bx bx-menu" : ""}`} noSelectButtonStyle iconAction hideToggleArrow buttonClassName={`global-menu-button ${isHorizontalLayout ? "bx bx-menu" : ""}`} noSelectButtonStyle iconAction hideToggleArrow
text={<> text={<>
{isVerticalLayout && <VerticalLayoutIcon />} {isVerticalLayout && <VerticalLayoutIcon logoRef={logoRef} />}
{isUpdateAvailable && <div class="global-menu-button-update-available"> {isUpdateAvailable && <div class="global-menu-button-update-available">
<span className="bx bxs-down-arrow-alt global-menu-button-update-available-button" title={t("update_available.update_available")} /> <span className="bx bxs-down-arrow-alt global-menu-button-update-available-button" title={t("update_available.update_available")} />
</div>} </div>}
@ -135,9 +137,9 @@ function SwitchToOptions() {
return; return;
} else if (!isMobile()) { } else if (!isMobile()) {
return <MenuItem command="switchToMobileVersion" icon="bx bx-mobile" text={t("global_menu.switch_to_mobile_version")} />; return <MenuItem command="switchToMobileVersion" icon="bx bx-mobile" text={t("global_menu.switch_to_mobile_version")} />;
} }
return <MenuItem command="switchToDesktopVersion" icon="bx bx-desktop" text={t("global_menu.switch_to_desktop_version")} />; return <MenuItem command="switchToDesktopVersion" icon="bx bx-desktop" text={t("global_menu.switch_to_desktop_version")} />;
} }
function MenuItem({ icon, text, title, command, disabled, active }: MenuItemProps<KeyboardActionNames | CommandNames | (() => void)>) { function MenuItem({ icon, text, title, command, disabled, active }: MenuItemProps<KeyboardActionNames | CommandNames | (() => void)>) {
@ -159,10 +161,7 @@ function KeyboardActionMenuItem({ text, command, ...props }: MenuItemProps<Keybo
/>; />;
} }
function VerticalLayoutIcon() { export function VerticalLayoutIcon({ logoRef }: { logoRef?: RefObject<SVGSVGElement> }) {
const logoRef = useRef<SVGSVGElement>(null);
useStaticTooltip(logoRef);
return ( return (
<svg ref={logoRef} viewBox="0 0 256 256" title={t("global_menu.menu")}> <svg ref={logoRef} viewBox="0 0 256 256" title={t("global_menu.menu")}>
<g> <g>

View File

@ -1,11 +1,12 @@
import { useMemo, useState } from "preact/hooks"; import { useMemo, useState } from "preact/hooks";
import { t } from "../../services/i18n";
import Button from "../react/Button"; import Button from "../react/Button";
import Modal from "../react/Modal"; import Modal from "../react/Modal";
import { dismissCallToAction, getCallToActions } from "./call_to_action_definitions"; import { dismissCallToAction, getCallToActions } from "./call_to_action_definitions";
import { t } from "../../services/i18n";
export default function CallToActionDialog() { export default function CallToActionDialog() {
const activeCallToActions = useMemo(() => getCallToActions(), []); const activeCallToActions = useMemo(() => getCallToActions(), []);
const [ activeIndex, setActiveIndex ] = useState(0); const [ activeIndex, setActiveIndex ] = useState(0);
const [ shown, setShown ] = useState(true); const [ shown, setShown ] = useState(true);
const activeItem = activeCallToActions[activeIndex]; const activeItem = activeCallToActions[activeIndex];
@ -36,11 +37,11 @@ export default function CallToActionDialog() {
await dismissCallToAction(activeItem.id); await dismissCallToAction(activeItem.id);
await button.onClick(); await button.onClick();
goToNext(); goToNext();
}}/> }}/>
)} )}
</>} </>}
> >
<p>{activeItem.message}</p> <p className="pre-wrap-text">{activeItem.message}</p>
</Modal> </Modal>
) );
} }

View File

@ -1,6 +1,7 @@
import utils from "../../services/utils"; import appContext from "../../components/app_context";
import options from "../../services/options";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import options from "../../services/options";
import utils from "../../services/utils";
/** /**
* A "call-to-action" is an interactive message for the user, generally to present new features. * A "call-to-action" is an interactive message for the user, generally to present new features.
@ -46,18 +47,15 @@ function isNextTheme() {
const CALL_TO_ACTIONS: CallToAction[] = [ const CALL_TO_ACTIONS: CallToAction[] = [
{ {
id: "next_theme", id: "new_layout",
title: t("call_to_action.next_theme_title"), title: t("call_to_action.new_layout_title"),
message: t("call_to_action.next_theme_message"), message: t("call_to_action.new_layout_message"),
enabled: () => !isNextTheme(), enabled: () => true,
buttons: [ buttons: [
{ {
text: t("call_to_action.next_theme_button"),
async onClick() { text: t("call_to_action.new_layout_button"),
await options.save("theme", "next"); onClick: () => appContext.tabManager.openInNewTab("_help_IjZS7iK5EXtb", "_help", true)
await options.save("backgroundEffects", "true");
utils.reloadFrontendApp("call-to-action");
}
} }
] ]
}, },
@ -75,6 +73,22 @@ 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(),
buttons: [
{
text: t("call_to_action.next_theme_button"),
async onClick() {
await options.save("theme", "next");
await options.save("backgroundEffects", "true");
utils.reloadFrontendApp("call-to-action");
}
}
]
} }
]; ];

View File

@ -7,7 +7,6 @@
} }
.inline-title { .inline-title {
margin-top: 2px; /* Allow space for the focus outline */
max-width: var(--max-content-width); max-width: var(--max-content-width);
container-type: inline-size; container-type: inline-size;
padding-inline-start: 24px; padding-inline-start: 24px;
@ -111,7 +110,7 @@ body.prefers-centered-content .inline-title {
.note-type-switcher { .note-type-switcher {
--badge-radius: 12px; --badge-radius: 12px;
position: relative; position: relative;
top: 5px; top: 5px;
padding: .25em 0; padding: .25em 0;
@ -121,7 +120,7 @@ body.prefers-centered-content .inline-title {
min-width: 0; min-width: 0;
gap: 5px; gap: 5px;
min-height: 35px; min-height: 35px;
>* { >* {
flex-shrink: 0; flex-shrink: 0;
animation: note-type-switcher-intro 200ms ease-in; animation: note-type-switcher-intro 200ms ease-in;

View File

@ -328,20 +328,19 @@ function EditedNotes() {
function EditedNotesContent({ note }: { note: FNote }) { function EditedNotesContent({ note }: { note: FNote }) {
const editedNotes = useEditedNotes(note); const editedNotes = useEditedNotes(note);
return ( return (editedNotes !== undefined &&
<> (editedNotes.length > 0 ? editedNotes?.map(editedNote => (
{editedNotes?.map(editedNote => ( <SimpleBadge
<SimpleBadge key={editedNote.noteId}
key={editedNote.noteId} title={(
title={( <NoteLink
<NoteLink notePath={editedNote.noteId}
notePath={editedNote.noteId} showNoteIcon
showNoteIcon />
/> )}
)} />
/> )) : (
))} <div className="no-edited-notes-found">{t("edited_notes.no_edited_notes_found")}</div>
</> )));
);
} }
//#endregion //#endregion

View File

@ -22,11 +22,10 @@ export default function NoteBadges() {
function ReadOnlyBadge() { function ReadOnlyBadge() {
const { note, noteContext } = useNoteContext(); const { note, noteContext } = useNoteContext();
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext); const { isReadOnly, enableEditing, temporarilyEditable } = useIsNoteReadOnly(note, noteContext);
const isExplicitReadOnly = note?.isLabelTruthy("readOnly"); const isExplicitReadOnly = note?.isLabelTruthy("readOnly");
const isTemporarilyEditable = noteContext?.ntxId !== "_popup-editor" && noteContext?.viewScope?.readOnlyTemporarilyDisabled;
if (isTemporarilyEditable) { if (temporarilyEditable) {
return <Badge return <Badge
icon="bx bx-lock-open-alt" icon="bx bx-lock-open-alt"
text={t("breadcrumb_badges.read_only_temporarily_disabled")} text={t("breadcrumb_badges.read_only_temporarily_disabled")}

View File

@ -1,7 +1,7 @@
import "./NoteTitleActions.css"; import "./NoteTitleActions.css";
import clsx from "clsx"; import clsx from "clsx";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useRef, useState } from "preact/hooks";
import NoteContext from "../../components/note_context"; import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
@ -10,7 +10,7 @@ import CollectionProperties from "../note_bars/CollectionProperties";
import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail"; import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail";
import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes"; import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes";
import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible"; import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible";
import { useNoteContext, useNoteProperty } from "../react/hooks"; import { useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks";
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab"; import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
export default function NoteTitleActions() { export default function NoteTitleActions() {
@ -57,6 +57,9 @@ function PromotedAttributes({ note, componentId, noteContext }: {
}); });
}, [ note, noteContext ]); }, [ note, noteContext ]);
// Keyboard shortcut.
useTriliumEvent("toggleRibbonTabPromotedAttributes", () => setExpanded(!expanded));
if (!cells?.length) return false; if (!cells?.length) return false;
return (note && ( return (note && (
<ExternallyControlledCollapsible <ExternallyControlledCollapsible

View File

@ -1,6 +1,6 @@
import "./StatusBar.css"; import "./StatusBar.css";
import { Locale } from "@triliumnext/commons"; import { KeyboardActionNames, Locale } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap"; import { Dropdown as BootstrapDropdown } from "bootstrap";
import clsx from "clsx"; import clsx from "clsx";
import { type ComponentChildren } from "preact"; import { type ComponentChildren } from "preact";
@ -9,7 +9,7 @@ import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import NoteContext from "../../components/note_context"; import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote"; import FNote, { NOTE_TYPE_ICONS } from "../../entities/fnote";
import attributes from "../../services/attributes"; import attributes from "../../services/attributes";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import { ViewScope } from "../../services/link"; import { ViewScope } from "../../services/link";
@ -216,14 +216,19 @@ interface NoteInfoContext extends StatusBarContext {
setSimilarNotesShown: (value: boolean) => void; setSimilarNotesShown: (value: boolean) => void;
} }
export function NoteInfoBadge({ note, setSimilarNotesShown }: NoteInfoContext) { export function NoteInfoBadge({ note, similarNotesShown, setSimilarNotesShown }: NoteInfoContext) {
const dropdownRef = useRef<BootstrapDropdown>(null); const dropdownRef = useRef<BootstrapDropdown>(null);
const { metadata, ...sizeProps } = useNoteMetadata(note); const { metadata, ...sizeProps } = useNoteMetadata(note);
const [ originalFileName ] = useNoteLabel(note, "originalFileName"); const [ originalFileName ] = useNoteLabel(note, "originalFileName");
const currentNoteType = useNoteProperty(note, "type"); const noteType = useNoteProperty(note, "type");
const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]); const noteTypeMapping = useMemo(() => NOTE_TYPES.find(t => t.type === noteType), [ noteType ]);
const enabled = note && noteType && noteTypeMapping;
return (note && currentNoteTypeData && // Keyboard shortcut.
useTriliumEvent("toggleRibbonTabNoteInfo", () => enabled && dropdownRef.current?.show());
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown(!similarNotesShown));
return (enabled &&
<StatusBarDropdown <StatusBarDropdown
icon="bx bx-info-circle" icon="bx bx-info-circle"
title={t("status_bar.note_info_title")} title={t("status_bar.note_info_title")}
@ -235,7 +240,7 @@ export function NoteInfoBadge({ note, setSimilarNotesShown }: NoteInfoContext) {
{originalFileName && <NoteInfoValue text={t("file_properties.original_file_name")} value={originalFileName} />} {originalFileName && <NoteInfoValue text={t("file_properties.original_file_name")} value={originalFileName} />}
<NoteInfoValue text={t("note_info_widget.created")} value={formatDateTime(metadata?.dateCreated)} /> <NoteInfoValue text={t("note_info_widget.created")} value={formatDateTime(metadata?.dateCreated)} />
<NoteInfoValue text={t("note_info_widget.modified")} value={formatDateTime(metadata?.dateModified)} /> <NoteInfoValue text={t("note_info_widget.modified")} value={formatDateTime(metadata?.dateModified)} />
<NoteInfoValue text={t("note_info_widget.type")} value={<><Icon icon={`bx ${currentNoteTypeData.icon}`}/>{" "}{currentNoteTypeData?.title}</>} /> <NoteInfoValue text={t("note_info_widget.type")} value={<><Icon icon={`bx ${noteTypeMapping.icon ?? NOTE_TYPE_ICONS[noteType]}`}/>{" "}{noteTypeMapping?.title}</>} />
{note.mime && <NoteInfoValue text={t("note_info_widget.mime")} value={note.mime} />} {note.mime && <NoteInfoValue text={t("note_info_widget.mime")} value={note.mime} />}
<NoteInfoValue text={t("note_info_widget.note_id")} value={<code>{note.noteId}</code>} /> <NoteInfoValue text={t("note_info_widget.note_id")} value={<code>{note.noteId}</code>} />
<NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} /> <NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} />
@ -349,6 +354,10 @@ function AttributesPane({ note, noteContext, attributesShown, setAttributesShown
// Show on keyboard shortcuts. // Show on keyboard shortcuts.
useTriliumEvents([ "addNewLabel", "addNewRelation" ], () => setAttributesShown(true)); useTriliumEvents([ "addNewLabel", "addNewRelation" ], () => setAttributesShown(true));
useTriliumEvents([ "toggleRibbonTabOwnedAttributes", "toggleRibbonTabInheritedAttributes" ], () => setAttributesShown(!attributesShown));
// Auto-focus the owned attributes.
useEffect(() => api.current?.focus(), [ attributesShown ]);
// Interaction with the attribute editor. // Interaction with the attribute editor.
useLegacyImperativeHandlers(useMemo(() => ({ useLegacyImperativeHandlers(useMemo(() => ({
@ -373,12 +382,18 @@ function AttributesPane({ note, noteContext, attributesShown, setAttributesShown
//#region Note paths //#region Note paths
function NotePaths({ note, hoistedNoteId, notePath }: StatusBarContext) { function NotePaths({ note, hoistedNoteId, notePath }: StatusBarContext) {
const dropdownRef = useRef<BootstrapDropdown>(null);
const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId); const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId);
const count = sortedNotePaths?.length ?? 0; const count = sortedNotePaths?.length ?? 0;
const enabled = count > 1;
return (count > 1 && // Keyboard shortcut.
useTriliumEvent("toggleRibbonTabNotePaths", () => enabled && dropdownRef.current?.show());
return (enabled &&
<StatusBarDropdown <StatusBarDropdown
title={t("status_bar.note_paths_title")} title={t("status_bar.note_paths_title")}
dropdownRef={dropdownRef}
dropdownContainerClassName="dropdown-note-paths" dropdownContainerClassName="dropdown-note-paths"
icon="bx bx-directions" icon="bx bx-directions"
text={t("status_bar.note_paths", { count })} text={t("status_bar.note_paths", { count })}

View File

@ -1,7 +1,7 @@
import "./CollectionProperties.css"; import "./CollectionProperties.css";
import { t } from "i18next"; import { t } from "i18next";
import { useContext } from "preact/hooks"; import { useContext, useRef } from "preact/hooks";
import { Fragment } from "preact/jsx-runtime"; import { Fragment } from "preact/jsx-runtime";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
@ -12,7 +12,7 @@ import ActionButton from "../react/ActionButton";
import Dropdown from "../react/Dropdown"; import Dropdown from "../react/Dropdown";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList"; import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
import FormTextBox from "../react/FormTextBox"; import FormTextBox from "../react/FormTextBox";
import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault } from "../react/hooks"; import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
import Icon from "../react/Icon"; import Icon from "../react/Icon";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config"; import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config";
@ -42,8 +42,15 @@ export default function CollectionProperties({ note }: { note: FNote }) {
} }
function ViewTypeSwitcher({ viewType, setViewType }: { viewType: ViewTypeOptions, setViewType: (newValue: ViewTypeOptions) => void }) { function ViewTypeSwitcher({ viewType, setViewType }: { viewType: ViewTypeOptions, setViewType: (newValue: ViewTypeOptions) => void }) {
// Keyboard shortcut
const dropdownContainerRef = useRef<HTMLDivElement>(null);
useTriliumEvent("toggleRibbonTabBookProperties", () => {
dropdownContainerRef.current?.querySelector("button")?.focus();
});
return ( return (
<Dropdown <Dropdown
dropdownContainerRef={dropdownContainerRef}
text={<> text={<>
<Icon icon={ICON_MAPPINGS[viewType]} />&nbsp; <Icon icon={ICON_MAPPINGS[viewType]} />&nbsp;
{VIEW_TYPE_MAPPINGS[viewType]} {VIEW_TYPE_MAPPINGS[viewType]}

View File

@ -2,13 +2,13 @@ import "./FormList.css";
import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap"; import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap";
import clsx from "clsx"; import clsx from "clsx";
import { ComponentChildren } from "preact"; import { ComponentChildren, RefObject } from "preact";
import { type CSSProperties,useEffect, useMemo, useRef, useState } from "preact/compat"; import { type CSSProperties,useEffect, useMemo, useRef, useState } from "preact/compat";
import { CommandNames } from "../../components/app_context"; import { CommandNames } from "../../components/app_context";
import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils"; import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils";
import FormToggle from "./FormToggle"; import FormToggle from "./FormToggle";
import { useStaticTooltip } from "./hooks"; import { useStaticTooltip, useSyncedRef } from "./hooks";
import Icon from "./Icon"; import Icon from "./Icon";
interface FormListOpts { interface FormListOpts {
@ -97,6 +97,7 @@ interface FormListItemOpts {
className?: string; className?: string;
rtl?: boolean; rtl?: boolean;
postContent?: ComponentChildren; postContent?: ComponentChildren;
itemRef?: RefObject<HTMLLIElement>;
} }
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = { const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
@ -104,8 +105,8 @@ const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
fallbackPlacements: [ handleRightToLeftPlacement("right") ] fallbackPlacements: [ handleRightToLeftPlacement("right") ]
}; };
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) { export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, itemRef: externalItemRef, ...contentProps }: FormListItemOpts) {
const itemRef = useRef<HTMLLIElement>(null); const itemRef = useSyncedRef<HTMLLIElement>(externalItemRef, null);
if (checked) { if (checked) {
icon = "bx bx-check"; icon = "bx bx-check";

View File

@ -933,11 +933,13 @@ export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: N
const [ isReadOnly, setIsReadOnly ] = useState<boolean | undefined>(undefined); const [ isReadOnly, setIsReadOnly ] = useState<boolean | undefined>(undefined);
const [ readOnlyAttr ] = useNoteLabelBoolean(note, "readOnly"); const [ readOnlyAttr ] = useNoteLabelBoolean(note, "readOnly");
const [ autoReadOnlyDisabledAttr ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled"); const [ autoReadOnlyDisabledAttr ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled");
const [ temporarilyEditable, setTemporarilyEditable ] = useState(false);
const enableEditing = useCallback((enabled = true) => { const enableEditing = useCallback((enabled = true) => {
if (noteContext?.viewScope) { if (noteContext?.viewScope) {
noteContext.viewScope.readOnlyTemporarilyDisabled = enabled; noteContext.viewScope.readOnlyTemporarilyDisabled = enabled;
appContext.triggerEvent("readOnlyTemporarilyDisabled", {noteContext}); appContext.triggerEvent("readOnlyTemporarilyDisabled", {noteContext});
setTemporarilyEditable(enabled);
} }
}, [noteContext]); }, [noteContext]);
@ -945,6 +947,7 @@ export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: N
if (note && noteContext) { if (note && noteContext) {
isNoteReadOnly(note, noteContext).then((readOnly) => { isNoteReadOnly(note, noteContext).then((readOnly) => {
setIsReadOnly(readOnly); setIsReadOnly(readOnly);
setTemporarilyEditable(false);
}); });
} }
}, [ note, noteContext, noteContext?.viewScope, readOnlyAttr, autoReadOnlyDisabledAttr ]); }, [ note, noteContext, noteContext?.viewScope, readOnlyAttr, autoReadOnlyDisabledAttr ]);
@ -952,10 +955,11 @@ export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: N
useTriliumEvent("readOnlyTemporarilyDisabled", ({noteContext: eventNoteContext}) => { useTriliumEvent("readOnlyTemporarilyDisabled", ({noteContext: eventNoteContext}) => {
if (noteContext?.ntxId === eventNoteContext.ntxId) { if (noteContext?.ntxId === eventNoteContext.ntxId) {
setIsReadOnly(!noteContext.viewScope?.readOnlyTemporarilyDisabled); setIsReadOnly(!noteContext.viewScope?.readOnlyTemporarilyDisabled);
setTemporarilyEditable(true);
} }
}); });
return { isReadOnly, enableEditing }; return { isReadOnly, enableEditing, temporarilyEditable };
} }
async function isNoteReadOnly(note: FNote, noteContext: NoteContext) { async function isNoteReadOnly(note: FNote, noteContext: NoteContext) {

View File

@ -49,6 +49,21 @@ export function FixedFormattingToolbar() {
const renderState = useRenderState(noteContext, note); const renderState = useRenderState(noteContext, note);
const [ toolbarToRender, setToolbarToRender ] = useState<HTMLElement | null | undefined>(); const [ toolbarToRender, setToolbarToRender ] = useState<HTMLElement | null | undefined>();
// Keyboard shortcut.
const lastFocusedElement = useRef<Element>(null);
useTriliumEvent("toggleRibbonTabClassicEditor", () => {
if (!toolbarToRender) return;
if (!toolbarToRender.contains(document.activeElement)) {
// Focus to the fixed formatting toolbar.
lastFocusedElement.current = document.activeElement;
toolbarToRender.querySelector<HTMLButtonElement>(".ck-toolbar__items button")?.focus();
} else {
// Focus back to the last selection.
(lastFocusedElement.current as HTMLElement)?.focus();
lastFocusedElement.current = null;
}
});
// Populate the cache with the toolbar of every note context. // Populate the cache with the toolbar of every note context.
useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => { useTriliumEvent("textEditorRefreshed", ({ ntxId: eventNtxId, editor }) => {
if (!eventNtxId) return; if (!eventNtxId) return;

View File

@ -1,5 +1,7 @@
import { ConvertToAttachmentResponse } from "@triliumnext/commons"; import { ConvertToAttachmentResponse } from "@triliumnext/commons";
import { useContext } from "preact/hooks"; import { Dropdown as BootstrapDropdown } from "bootstrap";
import { RefObject } from "preact";
import { useContext, useEffect, useRef } from "preact/hooks";
import appContext, { CommandNames } from "../../components/app_context"; import appContext, { CommandNames } from "../../components/app_context";
import Component from "../../components/component"; import Component from "../../components/component";
@ -20,7 +22,7 @@ import MovePaneButton from "../buttons/move_pane_button";
import ActionButton from "../react/ActionButton"; import ActionButton from "../react/ActionButton";
import Dropdown from "../react/Dropdown"; import Dropdown from "../react/Dropdown";
import { FormDropdownDivider, FormDropdownSubmenu, FormListHeader, FormListItem, FormListToggleableItem } from "../react/FormList"; import { FormDropdownDivider, FormDropdownSubmenu, FormListHeader, FormListItem, FormListToggleableItem } from "../react/FormList";
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumOption } from "../react/hooks"; import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
import { NoteTypeDropdownContent, useNoteBookmarkState, useShareState } from "./BasicPropertiesTab"; import { NoteTypeDropdownContent, useNoteBookmarkState, useShareState } from "./BasicPropertiesTab";
import NoteActionsCustom from "./NoteActionsCustom"; import NoteActionsCustom from "./NoteActionsCustom";
@ -59,7 +61,10 @@ function RevisionsButton({ note }: { note: FNote }) {
); );
} }
type ItemToFocus = "basic-properties";
function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) { function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
const dropdownRef = useRef<BootstrapDropdown>(null);
const parentComponent = useContext(ParentComponent); const parentComponent = useContext(ParentComponent);
const noteType = useNoteProperty(note, "type") ?? ""; const noteType = useNoteProperty(note, "type") ?? "";
const [viewType] = useNoteLabel(note, "viewType"); const [viewType] = useNoteLabel(note, "viewType");
@ -77,14 +82,25 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
const [syncServerHost] = useTriliumOption("syncServerHost"); const [syncServerHost] = useTriliumOption("syncServerHost");
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext); const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
const isNormalViewMode = noteContext?.viewScope?.viewMode === "default"; const isNormalViewMode = noteContext?.viewScope?.viewMode === "default";
const itemToFocusRef = useRef<ItemToFocus>(null);
// Keyboard shortcuts.
useTriliumEvent("toggleRibbonTabBasicProperties", () => {
if (!isNewLayout) return;
itemToFocusRef.current = "basic-properties";
dropdownRef.current?.toggle();
});
return ( return (
<Dropdown <Dropdown
dropdownRef={dropdownRef}
buttonClassName={ isNewLayout ? "bx bx-dots-horizontal-rounded" : "bx bx-dots-vertical-rounded" } buttonClassName={ isNewLayout ? "bx bx-dots-horizontal-rounded" : "bx bx-dots-vertical-rounded" }
className="note-actions" className="note-actions"
hideToggleArrow hideToggleArrow
noSelectButtonStyle noSelectButtonStyle
iconAction> iconAction
onHidden={() => itemToFocusRef.current = null }
>
{isReadOnly && <> {isReadOnly && <>
<CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")} <CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")}
@ -99,7 +115,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
<FormDropdownDivider /> <FormDropdownDivider />
{isNewLayout && isNormalViewMode && !isHelpPage && <> {isNewLayout && isNormalViewMode && !isHelpPage && <>
<NoteBasicProperties note={note} /> <NoteBasicProperties note={note} focus={itemToFocusRef} />
<FormDropdownDivider /> <FormDropdownDivider />
</>} </>}
@ -148,12 +164,22 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
); );
} }
function NoteBasicProperties({ note }: { note: FNote }) { function NoteBasicProperties({ note, focus }: {
note: FNote;
focus: RefObject<ItemToFocus>;
}) {
const itemToFocusRef = useRef<HTMLLIElement>(null);
const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note); const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note);
const [ isShared, switchShareState ] = useShareState(note); const [ isShared, switchShareState ] = useShareState(note);
const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template"); const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template");
const isProtected = useNoteProperty(note, "isProtected"); const isProtected = useNoteProperty(note, "isProtected");
useEffect(() => {
if (focus.current === "basic-properties") {
itemToFocusRef.current?.focus();
}
}, [ focus ]);
return <> return <>
<FormListToggleableItem <FormListToggleableItem
icon="bx bx-share-alt" icon="bx bx-share-alt"
@ -161,6 +187,7 @@ function NoteBasicProperties({ note }: { note: FNote }) {
currentValue={isShared} onChange={switchShareState} currentValue={isShared} onChange={switchShareState}
helpPage="R9pX4DGra2Vt" helpPage="R9pX4DGra2Vt"
disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")} disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")}
itemRef={itemToFocusRef}
/> />
<FormListToggleableItem <FormListToggleableItem
icon="bx bx-lock-alt" icon="bx bx-lock-alt"

View File

@ -1,5 +1,5 @@
import { NoteType } from "@triliumnext/commons"; import { NoteType } from "@triliumnext/commons";
import { useContext, useEffect, useState } from "preact/hooks"; import { useContext, useEffect, useRef, useState } from "preact/hooks";
import Component from "../../components/component"; import Component from "../../components/component";
import NoteContext from "../../components/note_context"; import NoteContext from "../../components/note_context";
@ -12,7 +12,7 @@ import { ViewTypeOptions } from "../collections/interface";
import { buildSaveSqlToNoteHandler } from "../FloatingButtonsDefinitions"; import { buildSaveSqlToNoteHandler } from "../FloatingButtonsDefinitions";
import ActionButton from "../react/ActionButton"; import ActionButton from "../react/ActionButton";
import { FormFileUploadActionButton } from "../react/FormFileUpload"; import { FormFileUploadActionButton } from "../react/FormFileUpload";
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks"; import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption } from "../react/hooks";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
import { buildUploadNewFileRevisionListener } from "./FilePropertiesTab"; import { buildUploadNewFileRevisionListener } from "./FilePropertiesTab";
import { buildUploadNewImageRevisionListener } from "./ImagePropertiesTab"; import { buildUploadNewImageRevisionListener } from "./ImagePropertiesTab";
@ -38,6 +38,7 @@ interface NoteActionsCustomInnerProps extends NoteActionsCustomProps {
*/ */
export default function NoteActionsCustom(props: NoteActionsCustomProps) { export default function NoteActionsCustom(props: NoteActionsCustomProps) {
const { note } = props; const { note } = props;
const containerRef = useRef<HTMLDivElement>(null);
const noteType = useNoteProperty(note, "type"); const noteType = useNoteProperty(note, "type");
const noteMime = useNoteProperty(note, "mime"); const noteMime = useNoteProperty(note, "mime");
const [ viewType ] = useNoteLabel(note, "viewType"); const [ viewType ] = useNoteLabel(note, "viewType");
@ -53,8 +54,15 @@ export default function NoteActionsCustom(props: NoteActionsCustomProps) {
isReadOnly isReadOnly
}; };
useTriliumEvents([ "toggleRibbonTabFileProperties", "toggleRibbonTabImageProperties" ], () => {
(containerRef.current?.firstElementChild as HTMLElement)?.focus();
});
return (innerProps && return (innerProps &&
<div className="note-actions-custom"> <div
ref={containerRef}
className="note-actions-custom"
>
<AddChildButton {...innerProps} /> <AddChildButton {...innerProps} />
<RunActiveNoteButton {...innerProps } /> <RunActiveNoteButton {...innerProps } />
<OpenTriliumApiDocsButton {...innerProps} /> <OpenTriliumApiDocsButton {...innerProps} />

View File

@ -1,25 +1,26 @@
import { MutableRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "preact/hooks";
import { AttributeEditor as CKEditorAttributeEditor, MentionFeed, ModelElement, ModelNode, ModelPosition } from "@triliumnext/ckeditor5"; import { AttributeEditor as CKEditorAttributeEditor, MentionFeed, ModelElement, ModelNode, ModelPosition } from "@triliumnext/ckeditor5";
import { AttributeType } from "@triliumnext/commons";
import { MutableRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "preact/hooks";
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
import FAttribute from "../../../entities/fattribute";
import FNote from "../../../entities/fnote";
import contextMenu from "../../../menus/context_menu";
import attribute_parser, { Attribute } from "../../../services/attribute_parser";
import attribute_renderer from "../../../services/attribute_renderer";
import attributes from "../../../services/attributes";
import froca from "../../../services/froca";
import { t } from "../../../services/i18n"; import { t } from "../../../services/i18n";
import server from "../../../services/server"; import link from "../../../services/link";
import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete"; import note_autocomplete, { Suggestion } from "../../../services/note_autocomplete";
import note_create from "../../../services/note_create";
import server from "../../../services/server";
import { isIMEComposing } from "../../../services/shortcuts";
import { escapeQuotes, getErrorMessage } from "../../../services/utils";
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
import ActionButton from "../../react/ActionButton";
import CKEditor, { CKEditorApi } from "../../react/CKEditor"; import CKEditor, { CKEditorApi } from "../../react/CKEditor";
import { useLegacyImperativeHandlers, useLegacyWidget, useTooltip, useTriliumEvent, useTriliumOption } from "../../react/hooks"; import { useLegacyImperativeHandlers, useLegacyWidget, useTooltip, useTriliumEvent, useTriliumOption } from "../../react/hooks";
import FAttribute from "../../../entities/fattribute";
import attribute_renderer from "../../../services/attribute_renderer";
import FNote from "../../../entities/fnote";
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
import attribute_parser, { Attribute } from "../../../services/attribute_parser";
import ActionButton from "../../react/ActionButton";
import { escapeQuotes, getErrorMessage } from "../../../services/utils";
import link from "../../../services/link";
import { isIMEComposing } from "../../../services/shortcuts";
import froca from "../../../services/froca";
import contextMenu from "../../../menus/context_menu";
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
import { AttributeType } from "@triliumnext/commons";
import attributes from "../../../services/attributes";
import note_create from "../../../services/note_create";
type AttributeCommandNames = FilteredCommandNames<CommandData>; type AttributeCommandNames = FilteredCommandNames<CommandData>;
@ -52,7 +53,7 @@ const mentionSetup: MentionFeed[] = [
return names.map((name) => { return names.map((name) => {
return { return {
id: `#${name}`, id: `#${name}`,
name: name name
}; };
}); });
}, },
@ -66,7 +67,7 @@ const mentionSetup: MentionFeed[] = [
return names.map((name) => { return names.map((name) => {
return { return {
id: `~${name}`, id: `~${name}`,
name: name name
}; };
}); });
}, },
@ -85,9 +86,10 @@ interface AttributeEditorProps {
} }
export interface AttributeEditorImperativeHandlers { export interface AttributeEditorImperativeHandlers {
save: () => Promise<void>; save(): Promise<void>;
refresh: () => void; refresh(): void;
renderOwnedAttributes: (ownedAttributes: FAttribute[]) => Promise<void>; focus(): void;
renderOwnedAttributes(ownedAttributes: FAttribute[]): Promise<void>;
} }
export default function AttributeEditor({ api, note, componentId, notePath, ntxId, hidden }: AttributeEditorProps) { export default function AttributeEditor({ api, note, componentId, notePath, ntxId, hidden }: AttributeEditorProps) {
@ -124,7 +126,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
// attrs are not resorted if position changes after the initial load // attrs are not resorted if position changes after the initial load
ownedAttributes.sort((a, b) => a.position - b.position); ownedAttributes.sort((a, b) => a.position - b.position);
let htmlAttrs = ("<p>" + (await attribute_renderer.renderAttributes(ownedAttributes, true)).html() + "</p>"); let htmlAttrs = (`<p>${(await attribute_renderer.renderAttributes(ownedAttributes, true)).html()}</p>`);
if (saved) { if (saved) {
lastSavedContent.current = htmlAttrs; lastSavedContent.current = htmlAttrs;
@ -162,7 +164,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
wrapperRef.current.style.opacity = "0"; wrapperRef.current.style.opacity = "0";
setTimeout(() => { setTimeout(() => {
if (wrapperRef.current) { if (wrapperRef.current) {
wrapperRef.current.style.opacity = "1" wrapperRef.current.style.opacity = "1";
} }
}, 100); }, 100);
} }
@ -252,7 +254,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
if (notePath) { if (notePath) {
result = await note_create.createNoteWithTypePrompt(notePath, { result = await note_create.createNoteWithTypePrompt(notePath, {
activate: false, activate: false,
title: title title
}); });
} }
@ -274,7 +276,8 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
useImperativeHandle(api, () => ({ useImperativeHandle(api, () => ({
save, save,
refresh, refresh,
renderOwnedAttributes: (attributes) => renderOwnedAttributes(attributes as FAttribute[], false) renderOwnedAttributes: (attributes) => renderOwnedAttributes(attributes as FAttribute[], false),
focus: () => editorRef.current?.focus()
}), [ save, refresh, renderOwnedAttributes ]); }), [ save, refresh, renderOwnedAttributes ]);
return ( return (
@ -404,7 +407,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
{attributeDetailWidgetEl} {attributeDetailWidgetEl}
</> </>
) );
} }
function getPreprocessedData(currentValue: string) { function getPreprocessedData(currentValue: string) {

View File

@ -22,12 +22,13 @@
margin-bottom: 20px; margin-bottom: 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
.attachment-title { .attachment-title {
font-size: 1.1rem; font-size: 1.1rem;
margin: 0; margin: 0;
a { a {
color: inherit !important; color: inherit !important;
} }
@ -72,8 +73,8 @@
.attachment-detail-wrapper.list-view { .attachment-detail-wrapper.list-view {
border-radius: 12px; border-radius: 12px;
background-color: var(--card-background-color); background-color: var(--card-background-color);
padding: 0 6px; padding: 6px 6px 0 6px;
box-shadow: var(--card-box-shadow); box-shadow: var(--card-box-shadow);
} }
@ -126,15 +127,6 @@
/* #endregion */ /* #endregion */
/* #region Attachment actions */ /* #region Attachment actions */
.attachment-actions {
width: 35px;
height: 35px;
}
.attachment-actions .select-button {
position: relative;
top: 3px;
}
.attachment-actions .dropdown-menu { .attachment-actions .dropdown-menu {
width: 20em; width: 20em;
@ -152,4 +144,4 @@
background-color: transparent !important; background-color: transparent !important;
pointer-events: none; /* makes it unclickable */ pointer-events: none; /* makes it unclickable */
} }
/* #endregion */ /* #endregion */

View File

@ -0,0 +1,114 @@
.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;
filter: blur(1px);
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
}
.main {
display: flex;
flex-direction: column;
flex-grow: 1;
font-size: 8px;
.tab-bar {
height: 10px;
flex-shrink: 0;
}
.content {
background-color: var(--main-background-color);
flex-grow: 1;
border-top-left-radius: 6px;
display: flex;
flex-direction: column;
min-height: 0;
.title-bar {
display: flex;
align-items: center;
font-size: 14px;
padding: 5px;
.title {
flex-grow: 1;
}
}
.ribbon {
padding: 0 5px;
.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;
overflow: hidden;
padding: 5px;
opacity: 0.5;
filter: blur(1px);
}
.status-bar {
background-color: var(--left-pane-background-color);
flex-shrink: 0;
padding: 0 2px;
display: flex;
&> .status-bar-breadcrumb {
flex-grow: 1;
}
}
}
}
}

View File

@ -1,18 +1,24 @@
import "./appearance.css";
import { FontFamily, OptionNames } from "@triliumnext/commons";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n"; 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 server from "../../../services/server";
import { isElectron, isMobile, reloadFrontendApp, restartDesktopApp } from "../../../services/utils";
import { VerticalLayoutIcon } from "../../buttons/global_menu";
import Button from "../../react/Button";
import Column from "../../react/Column";
import FormCheckbox from "../../react/FormCheckbox"; import FormCheckbox from "../../react/FormCheckbox";
import FormGroup from "../../react/FormGroup"; import FormGroup from "../../react/FormGroup";
import { FontFamily, OptionNames } from "@triliumnext/commons"; import FormRadioGroup from "../../react/FormRadioGroup";
import FormTextBox, { FormTextBoxWithUnit } from "../../react/FormTextBox"; import FormSelect, { FormSelectWithGroups } from "../../react/FormSelect";
import FormText from "../../react/FormText"; 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"; import RelatedSettings from "./components/RelatedSettings";
const MIN_CONTENT_WIDTH = 640; const MIN_CONTENT_WIDTH = 640;
@ -30,7 +36,7 @@ const BUILTIN_THEMES: Theme[] = [
{ val: "auto", title: t("theme.auto_theme") }, { val: "auto", title: t("theme.auto_theme") },
{ val: "light", title: t("theme.light_theme") }, { val: "light", title: t("theme.light_theme") },
{ val: "dark", title: t("theme.dark_theme") } { val: "dark", title: t("theme.dark_theme") }
] ];
interface FontFamilyEntry { interface FontFamilyEntry {
value: FontFamily; value: FontFamily;
@ -84,6 +90,7 @@ export default function AppearanceSettings() {
return ( return (
<div> <div>
{!isMobile() && <LayoutSwitcher />}
{!isMobile() && <LayoutOrientation />} {!isMobile() && <LayoutOrientation />}
<ApplicationTheme /> <ApplicationTheme />
{overrideThemeFonts === "true" && <Fonts />} {overrideThemeFonts === "true" && <Fonts />}
@ -102,7 +109,99 @@ export default function AppearanceSettings() {
} }
]} /> ]} />
</div> </div>
) );
}
function LayoutSwitcher() {
const [ newLayout, setNewLayout ] = useTriliumOptionBool("newLayout");
return (
<OptionsSection title={t("settings_appearance.ui")}>
<RadioWithIllustration
currentValue={newLayout ? "new-layout" : "old-layout"}
onChange={async newValue => {
await setNewLayout(newValue === "new-layout");
reloadFrontendApp();
}}
values={[
{ key: "old-layout", text: t("settings_appearance.ui_old_layout"), illustration: <LayoutIllustration /> },
{ key: "new-layout", text: t("settings_appearance.ui_new_layout"), illustration: <LayoutIllustration isNewLayout /> }
]}
/>
</OptionsSection>
);
}
function LayoutIllustration({ isNewLayout }: { isNewLayout?: boolean }) {
return (
<div className="old-layout-illustration">
<div className="launcher-pane">
<VerticalLayoutIcon />
<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>}
{isNewLayout && <div className="note-title-actions">
<Icon icon="bx bx-chevron-down" />{" "}Promoted attributes
</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>
{isNewLayout && <div className="status-bar">
<div className="status-bar-breadcrumb">
<Icon icon="bx bx-home" />
<Icon icon="bx bx-chevron-right" />
Note
<Icon icon="bx bx-chevron-right" />
Note
</div>
<div className="status-bar-actions">
<Icon icon="bx bx-list-check" />
<Icon icon="bx bx-info-circle" />
</div>
</div>}
</div>
</div>
</div>
);
} }
function LayoutOrientation() { function LayoutOrientation() {
@ -141,7 +240,7 @@ function ApplicationTheme() {
setThemes([ setThemes([
...BUILTIN_THEMES, ...BUILTIN_THEMES,
...userThemes ...userThemes
]) ]);
}); });
}, []); }, []);
@ -162,7 +261,7 @@ function ApplicationTheme() {
</FormGroup> </FormGroup>
</div> </div>
</OptionsSection> </OptionsSection>
) );
} }
function Fonts() { function Fonts() {
@ -245,7 +344,7 @@ function ElectronIntegration() {
<Button text={t("electron_integration.restart-app-button")} onClick={restartDesktopApp} /> <Button text={t("electron_integration.restart-app-button")} onClick={restartDesktopApp} />
</OptionsSection> </OptionsSection>
) );
} }
function Performance() { function Performance() {
@ -271,7 +370,7 @@ function Performance() {
{isElectron() && <SmoothScrollEnabledOption />} {isElectron() && <SmoothScrollEnabledOption />}
</OptionsSection> </OptionsSection>;
} }
function SmoothScrollEnabledOption() { function SmoothScrollEnabledOption() {
@ -280,7 +379,7 @@ function SmoothScrollEnabledOption() {
return <FormCheckbox return <FormCheckbox
label={`${t("ui-performance.enable-smooth-scroll")} ${t("ui-performance.app-restart-required")}`} label={`${t("ui-performance.enable-smooth-scroll")} ${t("ui-performance.app-restart-required")}`}
currentValue={smoothScrollEnabled} onChange={setSmoothScrollEnabled} currentValue={smoothScrollEnabled} onChange={setSmoothScrollEnabled}
/> />;
} }
function MaxContentWidth() { function MaxContentWidth() {
@ -302,10 +401,10 @@ function MaxContentWidth() {
</Column> </Column>
<FormCheckbox label={t("max_content_width.centerContent")} <FormCheckbox label={t("max_content_width.centerContent")}
currentValue={centerContent} currentValue={centerContent}
onChange={setCenterContent} /> onChange={setCenterContent} />
</OptionsSection> </OptionsSection>
) );
} }
function RibbonOptions() { function RibbonOptions() {
@ -318,5 +417,5 @@ function RibbonOptions() {
currentValue={editedNotesOpenInRibbon} onChange={setEditedNotesOpenInRibbon} currentValue={editedNotesOpenInRibbon} onChange={setEditedNotesOpenInRibbon}
/> />
</OptionsSection> </OptionsSection>
) );
} }

View File

@ -8,6 +8,7 @@
.option-row > label { .option-row > label {
width: 40%; width: 40%;
margin-bottom: 0 !important; margin-bottom: 0 !important;
flex-shrink: 0;
} }
.option-row > select, .option-row > select,
@ -26,4 +27,4 @@
.option-row.centered { .option-row.centered {
justify-content: center; justify-content: center;
} }

View File

@ -0,0 +1,28 @@
.options-section .radio-with-illustration {
list-style-type: none;
margin-bottom: 0;
padding: 0;
display: flex;
gap: 1.5em;
justify-content: center;
figure {
figcaption {
margin-top: 0.25em;
text-align: center;
}
margin-bottom: 0;
&> .illustration {
border-radius: 6px;
padding: 3px;
cursor: pointer;
}
}
&> .selected figure > .illustration {
outline: 3px solid var(--input-focus-outline-color);
}
}

View File

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

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,218 @@
<p>The <em>New layout</em> is a series of UI/UX changes that were introduced
in v0.101.0 that heavily change both existing UI elements, as well as adding
some new ones. The goal of this new layout is to modernize the application
and to make it more intuitive but at the same time to reduce clutter.</p>
<h2>Newly introduced features</h2>
<h3>Status bar</h3>
<p>At the bottom of the window there is a new bar called the <em>Status bar</em>.
This bar houses multiple items such as the Breadcrumb navigation and information
and settings about the current note, such as the <a href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/iPIMuisry3hd/_help_veGu4faJErEM">content language</a> and&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/tC7s2alapj8V/_help_zEY4DaJG4YT5">Attributes</a>.</p>
<p>For more information, consult the <a href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/IjZS7iK5EXtb/_help_AlJ73vBCjWDw">dedicated page</a>.</p>
<figure
class="image">
<img style="aspect-ratio:1150/27;" src="4_New Layout_image.png"
width="1150" height="27">
</figure>
<h3>Inline title</h3>
<p>In previous versions of Trilium, the title bar was fixed at all times.
In the new layout, there is both a fixed title bar and one that scrolls
with the text. The newly introduced title is called the <em>Inline title</em> and
it displays the title in a larger font, while also displaying additional
information such as the creation and the modification date.</p>
<p>Whenever the title is scrolled past, the fixed title is shown instead.</p>
<p>This only affects&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_iPIMuisry3hd">Text</a>&nbsp;and&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_6f9hih2hXXZk">Code</a>&nbsp;notes. Note types that take the entirety of the screen such
as&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_grjYqerjn243">Canvas</a>&nbsp;will
always have only the fixed title bar.</p>
<p>Depending on the note type, the inline title will also present some more
interactive options such as being able to switch the note type (see below).</p>
<figure
class="image">
<img style="aspect-ratio:899/122;" src="New Layout_image.png"
width="899" height="122">
<figcaption>The <em>Inline title</em>, which is displayed at the top of the note and
can be scrolled past.</figcaption>
</figure>
<figure class="image">
<img style="aspect-ratio:910/104;" src="3_New Layout_image.png"
width="910" height="104">
<figcaption>The fixed title bar. The title only appears after scrolling past the <em>Inline title</em>.</figcaption>
</figure>
<h3>New note type switcher</h3>
<p>When a new&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_iPIMuisry3hd">Text</a>&nbsp;or&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_6f9hih2hXXZk">Code</a>&nbsp;note is created, a note type switcher will appear below
the <em>Inline title</em>. Apart from changing the note type, it's also
possible to apply a <a href="#root/pOsGYCXsbNQG/tC7s2alapj8V/_help_KC1HB96bqqHX">template</a>.</p>
<p>The switcher will disappear as soon as a text is entered.</p>
<p>
<img src="5_New Layout_image.png" width="735" height="143">
</p>
<h3>Note badges</h3>
<p>Note badges appear near the fixed note title and indicate important information
about the note such as whether it is read-only. Some of the badges are
also interactive.</p>
<figure class="image">
<img style="aspect-ratio:910/49;" src="2_New Layout_image.png"
width="910" height="49">
</figure>
<p>The following badges are available:</p>
<ul>
<li data-list-item-id="e681fff45ecdebbf3a5c6fc8398b4e8b1"><strong>Read-only badge</strong>, which will be shown if the note is not
editable due to either automatic read-only or manual read-only. Clicking
on the badge will temporarily edit the note (similar to the Edit <a href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_XpOYSgsLkTJy">floating button</a>).</li>
<li
data-list-item-id="ee8bd349f3feb3df5069b31345e627985"><strong>Share badge</strong>, which will indicate that the current note
is shared. The badge will also indicate if the share is on the local network
(for the desktop application without&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/Otzi9La2YAUX/_help_cbkrhQjrkKrh">Synchronization</a>&nbsp;set
up) or publicly accessible (for the server).&nbsp;</li>
<li data-list-item-id="e8f5a122ba42c453bf36eb4f0064d570d"><strong>Web clip badge</strong>, which will indicate if the note was clipped
using the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/Otzi9La2YAUX/_help_MtPxeAWVAzMg">Web Clipper</a>.
The badge acts as a link, so it can be clicked on to navigate to the page
or right clicked for more options.</li>
<li data-list-item-id="e4f52532a9ae7b5ff88cc081da81049ad"><strong>Execute badge</strong>, for <a href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">scripts</a> or
<a
href="#root/pOsGYCXsbNQG/tC7s2alapj8V/wX4HbRucYSDD/oyIAJ9PvvwHX/_help_YKWqdJhzi2VY">saved SQL queries</a>which have an execute button or a description.</li>
</ul>
<p>Some of these badges replace the dedicated panels at the top of the note.</p>
<h3>Collapsible sections</h3>
<figure class="image">
<img style="aspect-ratio:496/265;" src="1_New Layout_image.png"
width="496" height="265">
</figure>
<p>The following sections have been made collapsible:</p>
<ul>
<li class="ck-list-marker-italic" data-list-item-id="e1e7527a923c6adc4b25a81c836aa54bb"><em>Promoted Attributes</em>
<ul>
<li data-list-item-id="e81342adbccbbbe0c186887eb0c96f6c7">For full-height notes such as&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_grjYqerjn243">Canvas</a>,
the promoted attributes are collapsed by default to make room.</li>
<li
data-list-item-id="e6c14c0f102892bc42cc6f503d901b1f0">The keyboard shortcut previously used to trigger the promoted attributes
ribbon tab (which was no longer working) has been repurposed to toggle
the promoted attributes instead.</li>
</ul>
</li>
<li data-list-item-id="e11bfe716c8b0b09f68d9c7cc99c93e03"><em>Edited Notes</em>, which appears for&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/tC7s2alapj8V/5668rwcirq1t/_help_l0tKav7yLHGF">Day Notes</a>&nbsp;is
now shown underneath the title.
<ul>
<li data-list-item-id="e24a6e624c699432c4d812b34f25a137a">Whether the section is collapsed or not depends on the choice in&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_4TIF1oA4VQRO">Options</a>&nbsp;→ Appearance.</li>
</ul>
</li>
<li data-list-item-id="e16420a44b06dbe4810f6ab5087bb7f09"><em>Search Properties</em>, which appears for the full&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_eIg8jdvaoNNd">Search</a>&nbsp;and&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_m523cpzocqaD">Saved Search</a>.</li>
</ul>
<h2>Changing to the existing layout</h2>
<h3>Removal of the ribbon</h3>
<p>The most significant change is the removal of the ribbon. All the actions
and options from the ribbon were integrated in other places in the application.</p>
<p>Here's how all the different tabs that were once part of the ribbon are
now available in the new layout:</p>
<ul>
<li data-list-item-id="e23a8f5ef92bc84b978897596a0f311d5">“Formatting toolbar” was relocated to the top of the page.
<ul>
<li data-list-item-id="eaff03a54c5d839b1cf491b873e4ac417">Instead of having one per split, now there is a single formatting toolbar
per tab. This allows more space for the toolbar items.</li>
</ul>
</li>
<li data-list-item-id="e55efa7b7fa563c7a792952d0a00f69c7">“Owned attributes” and “Inherited attributes” were merged and moved to
the status bar region (displayed one above the other).</li>
<li data-list-item-id="eaf2afbfe470834eca4dc7269b7310964">“Basic Properties” were integrated in the&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_8YBEPzcpUgxw">Note buttons</a>&nbsp;menu.
<ul>
<li data-list-item-id="e44ec78344d9a6c8966b0c9c68ae79d79">The only exception here is the Language combo box which can now be found
in the status bar (top-right of the screen).</li>
</ul>
</li>
<li data-list-item-id="ea5cf9c168f3291f8247ce89924472c33">“File” and “Image” tabs
<ul>
<li data-list-item-id="e32291890baede8270361747c20724271">The buttons were moved to the right of the note title, as dedicated entries
in&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_8YBEPzcpUgxw">Note buttons</a>.</li>
<li
data-list-item-id="e4e0b374eeaa3d5cee3ec10c2f0f51e1c">The info section has been merged into the <em>Note info </em>section of
the status bar.</li>
</ul>
</li>
<li data-list-item-id="ea542c02972f94d40cd87ed5f971db880">Edited notes
<ul>
<li data-list-item-id="e628eec12558a51865f1b4f14e9f3fec8">Moved underneath the title, displayed under a collapsible area and the
notes are represented as badges/chips.</li>
<li data-list-item-id="ed819af0e25679551eb56d66bd81cf431">Whether the section is expanded or collapsed depends on the “Edited Notes
ribbon tab will automatically open on day notes” setting from Options →
Appearance.</li>
</ul>
</li>
<li data-list-item-id="eb5f31f734338d9f6ada1329849ac9652">Search definition tab
<ul>
<li data-list-item-id="ee00a5cb8f4d7920a236fb95f34d93dbf">Moved underneath the title under a collapsible area.</li>
<li data-list-item-id="e310963b6aafce5d8e75fd65810087d0b">Expanded by default for new searches, collapsed for saved searches.</li>
</ul>
</li>
<li data-list-item-id="e7713cf206015ff83cdc8569fe22301a4">The Note map is now available in the Note actions menu.
<ul>
<li data-list-item-id="e507702eef9a13e0fc6b88da4e8b4b280">Instead of opening into a panel in the ribbon, the note map now opens
in a side split (similar to the in-app help).</li>
</ul>
</li>
<li data-list-item-id="e36fac289364571fe38384d913a9b82f6">“Note info” tab was moved to a small (i) icon in the status bar.</li>
<li
data-list-item-id="ec77db0f7e17da59142a53738bd6e5dde">“Similar notes” tab
<ul>
<li data-list-item-id="e82f29a4705ba7ef67cfa7871d09d55ec">Moved to the status bar, by going to the “Note info” section and pressing
the button to show similar notes.</li>
<li data-list-item-id="e4824fa59a2e7b078c235fdf191809693">Displayed as a fixed panel, similar to the attributes.</li>
</ul>
</li>
<li data-list-item-id="e51662a87308396697ebe51f8dad842f9">The Collection properties tab were relocated under the note title and
grouped into:
<ul>
<li data-list-item-id="e5355d95afa06871493dd0633eeb21342">A combo box to quickly switch between views.</li>
<li data-list-item-id="eebd3911c5dd2fc770534ae8fc7d2b47e">Individual settings for the current view in a submenu.</li>
</ul>
</li>
<li data-list-item-id="eae9dae6a2a5b74aaea84d818d1334fc9">Some smaller ribbon tabs were converted to badges that appear near the
note title in the breadcrumb section:
<ul>
<li data-list-item-id="ea7f24c544b22fda5ef7d513410454738">Original URL indicator for clipped web pages (<code spellcheck="false">#pageUrl</code>).</li>
<li
data-list-item-id="e43d835ca69e35e71203f64efd2fd12c2">SQL and script execute buttons.</li>
</ul>
</li>
</ul>
<aside class="admonition note">
<p>The ribbon keyboard shortcuts (e.g. <code spellcheck="false">toggleRibbonTabClassicEditor</code>)
have been repurposed to work on the new layout, where they will toggle
the appropriate panel.</p>
</aside>
<h3>Removal of the floating buttons</h3>
<p>Most of the buttons were relocated to the right of the note title, in
the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_8YBEPzcpUgxw">Note buttons</a>&nbsp;area,
with the exception of:</p>
<ul>
<li data-list-item-id="ec0623b0ce81f563d1c58d0d17de7c517">The Edit button is displayed near the note title, as a badge.</li>
<li
data-list-item-id="e0f1d6b12f8ebaf09d070aa9c86850726"><em>Backlinks</em> is displayed in the status bar. When clicked, the same
list of backlinks is displayed.</li>
<li data-list-item-id="e9693ac78b6cb054e6e492e464abd7c16">Relation map zoom buttons are now part of the relation map itself.</li>
<li
data-list-item-id="e4f2e79a6ea41e31ffe58e6c25a75216a">Export image to PNG/SVG are now in the Note actions menu, in the <em>Export as image</em> option.</li>
</ul>
<h2>How to toggle the new layout</h2>
<p>Starting with v0.101.0, this new layout is enabled by default. It is possible
to fall back to the old layout by going to&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_4TIF1oA4VQRO">Options</a>&nbsp;
Appearance and selecting <em>Old layout</em>.</p>
<aside class="admonition important">
<p>Since a new layout was introduced, this becomes the standard one. The <em>Old layout</em> is
considered deprecated and will not receive new features (for example, the
breadcrumb) as we focus on the new one. At some point the old layout will
be removed entirely, as maintaining two layouts with major differences
creates a maintenance burden.</p>
</aside>

View File

@ -0,0 +1,55 @@
<figure class="image">
<img style="aspect-ratio:1150/27;" src="Breadcrumb_image.png"
width="1150" height="27">
</figure>
<p>The breadcrumb allows quickly viewing the note hierarchy of the current
note and navigating through it.</p>
<p>It is part of the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/IjZS7iK5EXtb/_help_AlJ73vBCjWDw">Status bar</a>,
displayed in the bottom-left of the screen.</p>
<h2>Layout and Interaction</h2>
<ul>
<li data-list-item-id="eeca71aa25c6fc24ccea32b3fd91c7d03">If a note or workspace is hoisted, a badge will appear on the left-most
side.
<ul>
<li data-list-item-id="e0c5e4b89d1a4f0fccfd64fd5a5c401de">Clicking on the badge will un-hoist the note/workspace.</li>
</ul>
</li>
<li data-list-item-id="e3f4bcf3d23cc23094991e4b982fab65d">The left-most icon represents the root note, or the hoisted note or workspace.
<ul>
<li data-list-item-id="e0acc69822ac800e00f15286d69150011">Clicking the icon will jump to the root note.</li>
<li data-list-item-id="ea7ad114455b8e77f527cab5064b24f62">Right clicking the icon will display a menu that allows opening the note
in a new tab, split, etc.</li>
</ul>
</li>
<li data-list-item-id="e1e81c0cc1c8ca8ae94e3861e2e7ba104">Each segment shows the title of a note in the current note hierarchy.
<ul>
<li data-list-item-id="e42e1001c318c8b55ec25d44649f116f6">Clicking the icon will jump to that note.</li>
<li data-list-item-id="ebd05c3d7be7f994f229d48131ccd79bb">Right clicking will open a menu with multiple options such as opening
the note in a different tab/split/window, hoisting, moving/cloning the
note, duplicating as well as changing the color of the note.</li>
</ul>
</li>
<li data-list-item-id="ed8111f04712d97db418a8ba507cf1cf4">Clicking the arrow next to each segment will reveal the child notes of
the segment on the left.
<ul>
<li data-list-item-id="e9689b35aa5224f861cf61e63fc3abd96">Clicking on an icon will navigate to that particular note.</li>
<li data-list-item-id="ea008f6126b2d2dc03e9795fa89a8fe0c">It's also possible to create a new child note from here.</li>
<li data-list-item-id="e5d56879e41e3eba28d89999ac208cf63">The menu can optionally hide the archived notes.</li>
</ul>
</li>
<li data-list-item-id="ed65ebfd673ab4cc6eb2e2a227f0caa95">If the current note is deep within a hierarchy, the segments will collapse
into a […] button in order not to occupy too much space.
<ul>
<li data-list-item-id="e3081db77d1acea4568da8d69d199171b">Clicking this button will display each collapsed entry as a menu item.
Clicking on it will navigate to that particular note.</li>
</ul>
</li>
<li data-list-item-id="e17205300d9c0018d65cf0f0fc92343bf">Right clicking on an empty space to the right of the breadcrumb (before
the other status bar items) will reveal another menu that allows:
<ul>
<li data-list-item-id="e80899972041248ee12d26fb7065772a3">Toggling whether archived notes are displayed in the breadcrumb and in
the note tree.</li>
<li data-list-item-id="ef692f737f9df3df6d77b180ade9ecd45">Copying the current note path to clipboard.</li>
</ul>
</li>
</ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,53 @@
<p>The status bar displays information about the current note and allows
changing settings related to it such as configuring the language or attributes.</p>
<h2>Layout and interaction</h2>
<p>On the left side, the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/IjZS7iK5EXtb/_help_I6p2a06hdnL6">Breadcrumb</a>&nbsp;is
displayed which indicates the current note as well as its parent notes
and allows for quick navigation throughout the hierarchy.</p>
<p>On the right side, specific sections will show depending on the type of
the current note.</p>
<ol>
<li data-list-item-id="ef3e251512bf6573cca80079d8efd08ec">For code notes, the language mode of the note is indicated (e.g. JavaScript,
plain text), as well as allowing easy switching to another mode.</li>
<li
data-list-item-id="eb5c80ff8ad9e92fd95cae059af10f1b9">For text notes, the content language is displayed and can be changed,
thus configuring the spell-check and the right-to-left support.
<ol>
<li data-list-item-id="eb8a685a73d822de440eacbf53b361d80">Note that this applies to the entire note and not the selection, unlike
some text editors.</li>
</ol>
</li>
<li data-list-item-id="eec1c0804323b8b841538dd493654e6ac">If a note is placed in multiple places in the tree (cloned), the number
of the note paths will be displayed.
<ol>
<li data-list-item-id="e5975fc63b51ceaee4639a4185025c912">Clicking it will reveal the full list of note paths and a button to place
it somewhere else.</li>
</ol>
</li>
<li data-list-item-id="e9644f8d1759a085172efe05ca9de454e">If a note has attachments, their number will be displayed.
<ol>
<li data-list-item-id="e1f84e230de4009fc971a999330eee2d2">Clicking on it will reveal the list of attachments in a new tab.</li>
</ol>
</li>
<li data-list-item-id="eae7faacee8099500ffeed87bcd10d48c">If a note is linked from other text notes (backlinks), the number of backlinks
will be displayed.
<ol>
<li data-list-item-id="e23765b409994cae53cc3085abdb8c6f4">Clicking on it will show the list of notes that link to this note, as
well as an excerpt of where the note is referenced.</li>
</ol>
</li>
</ol>
<p>Regardless of note type, the following items will always be displayed
if there is a note:</p>
<ol>
<li data-list-item-id="eeba7911e7138db1df76d76712b360b45">Note info, which displays:
<ol>
<li data-list-item-id="ea184596335824cc6cdf737949ee4be0e">The creation/modification date of the note.</li>
<li data-list-item-id="e785134c29b68229dd42afab43413cd70">The type and MIME of the note.</li>
<li data-list-item-id="e06b3ee88bd4ae7da4182e3747264223e">The note ID.</li>
<li data-list-item-id="e896b429faa3a7b9e4ddff8abb2ff0795">An estimation of the note size of the note itself and its children.</li>
<li
data-list-item-id="e2749abcfbc46d6d8cab5b7f0b3fbb161">A button to show Similar notes.</li>
</ol>
</li>
</ol>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -3,10 +3,15 @@
<img src="Note buttons_image.png"> <img src="Note buttons_image.png">
</p> </p>
<ul> <ul>
<li>The Note Revisions button displays the&nbsp;<a href="#root/_help_vZWERwf8U3nx">Note Revisions</a>&nbsp;for <li data-list-item-id="e9bebdee3f029e4352aaa066ccc75f3cc">The Note Revisions button displays the&nbsp;<a href="#root/_help_vZWERwf8U3nx">Note Revisions</a>&nbsp;for
that particular note.</li> that particular note.</li>
<li>The contextual menu offers commands for the note or its subtree, such <li data-list-item-id="ed4fd4715e47307a0808ccb0481660303">The contextual menu offers commands for the note or its subtree, such
as import, export, viewing the&nbsp;<a href="#root/_help_4FahAwuGTAwC">Note source code</a>&nbsp;or&nbsp; as import, export, viewing the&nbsp;<a href="#root/_help_4FahAwuGTAwC">Note source code</a>&nbsp;or&nbsp;
<a <a
href="#root/_help_0vhv7lsOLy82">Attachments</a>.</li> href="#root/_help_0vhv7lsOLy82">Attachments</a>.</li>
</ul> </ul>
<p>On the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_IjZS7iK5EXtb">New Layout</a>,
the button area is populated by some more buttons that are specific to
the current note. For example, for Image and&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_W8vYD3Q1zjCR">File</a>&nbsp;notes,
the download and copy buttons were relocated there.</p>

View File

@ -7,98 +7,94 @@
adjusted.</p> adjusted.</p>
<h2>How it works</h2> <h2>How it works</h2>
<p>When first creating a collection of <em>Board</em> type, a few subnotes <p>When first creating a collection of <em>Board</em> type, a few subnotes
will be created, each having a <code spellcheck="false">#status</code> label will be created, each having a <code>#status</code> label set. The board
set. The board then groups each note by the value of the status attribute.</p> then groups each note by the value of the status attribute.</p>
<p>Notes are displayed recursively, so even the child notes of the child <p>Notes are displayed recursively, so even the child notes of the child
notes will be displayed. However, unlike the&nbsp;<a class="reference-link" notes will be displayed. However, unlike the&nbsp;<a class="reference-link"
href="#root/_help_2FvYrpmOXm29">Table</a>, the notes are not displayed in a hierarchy.</p> href="#root/_help_2FvYrpmOXm29">Table</a>, the notes are not displayed in a hierarchy.</p>
<h2>Interaction</h2> <h2>Interaction</h2>
<h3>Working with columns</h3> <h3>Working with columns</h3>
<ul> <ul>
<li data-list-item-id="eed75fe2d70ba712457da18d13b91fa16">Create a new column by pressing <em>Add Column</em> near the last column. <li>Create a new column by pressing <em>Add Column</em> near the last column.
<ul> <ul>
<li data-list-item-id="ef2cf2654ff26b4f04aabe7aea447ba04">Once pressed, a text box will be displayed to set the name of the column. <li>Once pressed, a text box will be displayed to set the name of the column.
Press <kbd>Enter</kbd> to confirm, or <kbd>Escape</kbd> to dismiss.</li> Press <kbd>Enter</kbd> to confirm, or <kbd>Escape</kbd> to dismiss.</li>
</ul> </ul>
</li> </li>
<li data-list-item-id="ecf404437de7a7126ec6e74428386c644">To reorder a column, simply hold the mouse over the title and drag it <li>To reorder a column, simply hold the mouse over the title and drag it
to the desired position.</li> to the desired position.</li>
<li data-list-item-id="ebe673a03d3e54db84bad22ac06caf1bc">To delete a column, right click on its title and select <em>Delete column</em>.</li> <li>To delete a column, right click on its title and select <em>Delete column</em>.</li>
<li <li>To rename a column, click on the note title.
data-list-item-id="e7c4c69d4c1ef862d13c886c29fccc0e2">To rename a column, click on the note title.
<ul> <ul>
<li data-list-item-id="ea8ee1c0d6783c0c64fde427a7334f703">Press Enter to confirm.</li> <li>Press Enter to confirm.</li>
<li data-list-item-id="e85724e2b224df1670b08a418e560b05d">Upon renaming a column, the corresponding status attribute of all its <li>Upon renaming a column, the corresponding status attribute of all its
notes will be changed in bulk.</li> notes will be changed in bulk.</li>
</ul> </ul>
</li> </li>
<li data-list-item-id="e2836e9a5f492795750c949f1f060f548">If there are many columns, use the mouse wheel to scroll.</li> <li>If there are many columns, use the mouse wheel to scroll.</li>
</ul> </ul>
<h3>Working with notes</h3> <h3>Working with notes</h3>
<ul> <ul>
<li data-list-item-id="efb7df180d7edb7e6c070bcfee5aa6595">Create a new note in any column by pressing <em>New item</em> <li>Create a new note in any column by pressing <em>New item</em>
<ul> <ul>
<li data-list-item-id="e1cba0534a1bd351bad9a8eab629a2267">Enter the name of the note and press <kbd>Enter</kbd> or click away. To <li>Enter the name of the note and press <kbd>Enter</kbd> or click away. To
dismiss the creation of a new note, simply press <kbd>Escape</kbd> or leave dismiss the creation of a new note, simply press <kbd>Escape</kbd> or leave
the name empty.</li> the name empty.</li>
<li data-list-item-id="ecd0181ef17ca7eea5c4f5864472bb4b3">Once created, the new note will have an attribute (<code spellcheck="false">status</code> label <li>Once created, the new note will have an attribute (<code>status</code> label
by default) set to the name of the column.</li> by default) set to the name of the column.</li>
</ul> </ul>
</li> </li>
<li data-list-item-id="ee23b9bd9fedee464cb70079e80379b12">To open the note, simply click on it.</li> <li>To open the note, simply click on it.</li>
<li data-list-item-id="ef6b267a604ab3dedcce739bda9928c36">To change the title of the note directly from the board, hover the mouse <li>To change the title of the note directly from the board, hover the mouse
over its card and press the edit button on the right.</li> over its card and press the edit button on the right.</li>
<li data-list-item-id="ebbe903cc36e703cf676e988a5b58d950">To change the state of a note, simply drag a note from one column to the <li>To change the state of a note, simply drag a note from one column to the
other to change its state.</li> other to change its state.</li>
<li data-list-item-id="e41960c677e9041fcf07692b44e70b5af">The order of the notes in each column corresponds to their position in <li>The order of the notes in each column corresponds to their position in
the tree. the tree.
<ul> <ul>
<li data-list-item-id="e0868bc9825cdef09da3fc03e873c47d7">It's possible to reorder notes simply by dragging them to the desired <li>It's possible to reorder notes simply by dragging them to the desired
position within the same columns.</li> position within the same columns.</li>
<li data-list-item-id="e0706409ea69226ec62e3364d85001697">It's also possible to drag notes across columns, at the desired position.</li> <li>It's also possible to drag notes across columns, at the desired position.</li>
</ul> </ul>
</li> </li>
<li data-list-item-id="e5ad1e7155fc0100222f460ef9feb6b41">For more options, right click on a note to display a context menu with <li>For more options, right click on a note to display a context menu with
the following options: the following options:
<ul> <ul>
<li data-list-item-id="e1ce2217942aaa4c6b78b8c76a7f38dbb">Open the note in a new tab/split/window or quick edit.</li> <li>Open the note in a new tab/split/window or quick edit.</li>
<li data-list-item-id="e707e47f9eede68e4546eb6b8a83e8a17">Move the note to any column.</li> <li>Move the note to any column.</li>
<li data-list-item-id="e3125b7d4859803621bad3d679339dab9">Insert a new note above/below the current one.</li> <li>Insert a new note above/below the current one.</li>
<li data-list-item-id="ea16c75aba80bbcb50b3dd416cec09753">Archive/unarchive the current note.</li> <li>Archive/unarchive the current note.</li>
<li data-list-item-id="e5ea20c999a4f17c69ed4094d24a706f3">Delete the current note.</li> <li>Delete the current note.</li>
</ul> </ul>
</li> </li>
<li data-list-item-id="e568d17582bdddc191dfe89bb9aca89d5">If there are many notes within the column, move the mouse over the column <li>If there are many notes within the column, move the mouse over the column
and use the mouse wheel to scroll.</li> and use the mouse wheel to scroll.</li>
</ul> </ul>
<h3>Working with the note tree</h3> <h3>Working with the note tree</h3>
<p>It's also possible to add items on the board using the&nbsp;<a class="reference-link" <p>It's also possible to add items on the board using the&nbsp;<a class="reference-link"
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</p> href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</p>
<ol> <ol>
<li data-list-item-id="e921887a37d6dba75eb00782ac7c7e118">Select the desired note in the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li> <li>Select the desired note in the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li>
<li <li>Hold the mouse on the note and drag it to the to the desired column.</li>
data-list-item-id="efeec7811e055ff6465ed839bbba2f776">Hold the mouse on the note and drag it to the to the desired column.</li>
</ol> </ol>
<p>This works for:</p> <p>This works for:</p>
<ul> <ul>
<li data-list-item-id="e31561dca83d4b9598775e33d9c4a8396">Notes that are not children of the board, case in which a <a href="#root/_help_IakOLONlIfGI">clone</a> will <li>Notes that are not children of the board, case in which a <a href="#root/_help_IakOLONlIfGI">clone</a> will
be created.</li> be created.</li>
<li data-list-item-id="e74f3978f78cb119c8ae4b0ec167f8683">Notes that are children of the board, but not yet assigned on the board.</li> <li>Notes that are children of the board, but not yet assigned on the board.</li>
<li <li>Notes that are children of the board, case in which they will be moved
data-list-item-id="e581a876b974f0e5f0381651496400d48">Notes that are children of the board, case in which they will be moved
to the new column.</li> to the new column.</li>
</ul> </ul>
<h3>Keyboard interaction</h3> <h3>Keyboard interaction</h3>
<p>The board view has mild support for keyboard-based navigation:</p> <p>The board view has mild support for keyboard-based navigation:</p>
<ul> <ul>
<li data-list-item-id="e956369914e25568591f1d3dda5ae970e">Use <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> to navigate between <li>Use <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> to navigate between
column titles, notes and the “New item” button for each of the columns, column titles, notes and the “New item” button for each of the columns,
in sequential order.</li> in sequential order.</li>
<li data-list-item-id="e5211444d02da23ac54a370bc1cced494">To rename a column or a note, press <kbd>F2</kbd> while it is focused.</li> <li>To rename a column or a note, press <kbd>F2</kbd> while it is focused.</li>
<li <li>To open a specific note or create a new item, press <kbd>Enter</kbd> while
data-list-item-id="e11a91b88f8a12237ef40ab9c32d9f8f8">To open a specific note or create a new item, press <kbd>Enter</kbd> while
it is focused.</li> it is focused.</li>
<li data-list-item-id="e34aabe77a5bce273c353c2400da91cee">To dismiss a rename of a note or a column, press <kbd>Escape</kbd>.</li> <li>To dismiss a rename of a note or a column, press <kbd>Escape</kbd>.</li>
</ul> </ul>
<h2>Configuration</h2> <h2>Configuration</h2>
<h3>Displaying custom attributes</h3> <h3>Displaying custom attributes</h3>
@ -112,37 +108,33 @@
href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>). The easiest way to href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>). The easiest way to
add these is:</p> add these is:</p>
<ol> <ol>
<li data-list-item-id="e63c976202ef9f633570f685d391b5e50">Go to board note.</li> <li>Go to board note.</li>
<li data-list-item-id="ea821edb6d448b1bde26f9d756b4b0fec">In the ribbon select <em>Owned Attributes</em> → plus button → <em>Add new label/relation definition</em>.</li> <li>In the ribbon select <em>Owned Attributes</em> → plus button → <em>Add new label/relation definition</em>.</li>
<li <li>Configure the attribute as desired.</li>
data-list-item-id="e94dd8b134591fe71e2dcea3506247df6">Configure the attribute as desired.</li> <li>Check <em>Inheritable</em> to make it applicable to child notes automatically.</li>
<li data-list-item-id="ea08e72fd3555837459105e19c12c1e33">Check <em>Inheritable</em> to make it applicable to child notes automatically.</li>
</ol> </ol>
<p>After creating the attribute, click on a note and fill in the promoted <p>After creating the attribute, click on a note and fill in the promoted
attributes which should then reflect inside the board.</p> attributes which should then reflect inside the board.</p>
<p>Of note:</p> <p>Of note:</p>
<ul> <ul>
<li data-list-item-id="eb9e8b5e12e3dc2f5410bc10ba091d552">Both promoted and non-promoted attribute definitions are supported. The <li>Both promoted and non-promoted attribute definitions are supported. The
only difference is that non-promoted attributes don't have an “Alias” for only difference is that non-promoted attributes don't have an “Alias” for
assigning a custom name.</li> assigning a custom name.</li>
<li data-list-item-id="ee41e79b02872367b2c5f2c4f88c3da41">Both “Single value” and “Multi value” attributes are supported. In case <li>Both “Single value” and “Multi value” attributes are supported. In case
of multi-value, a badge is displayed for every instance of the attribute.</li> of multi-value, a badge is displayed for every instance of the attribute.</li>
<li <li>All label types are supported, including dates, booleans and URLs.</li>
data-list-item-id="eb4d6fdbc5f52e70263706b3f51d929e3">All label types are supported, including dates, booleans and URLs.</li> <li>Relation attributes are also supported as well, showing a link with the
<li target note title and icon.</li>
data-list-item-id="ec141a7d5aca14f33f58abb3fdb1ab2e3">Relation attributes are also supported as well, showing a link with the <li>Currently, it's not possible to adjust which promoted attributes are displayed,
target note title and icon.</li> since all promoted attributes will be displayed (except the <code>board:groupBy</code> one).
<li data-list-item-id="ef13f98f321ca5b0b0ea881966e7a8ce9">Currently, it's not possible to adjust which promoted attributes are displayed, There are plans to improve upon this being able to hide promoted attributes
since all promoted attributes will be displayed (except the <code spellcheck="false">board:groupBy</code> one). individually.</li>
There are plans to improve upon this being able to hide promoted attributes
individually.</li>
</ul> </ul>
<h3>Grouping by another label</h3> <h3>Grouping by another label</h3>
<p>By default, the label used to group the notes is <code spellcheck="false">#status</code>. <p>By default, the label used to group the notes is <code>#status</code>.
It is possible to use a different label if needed by defining a label named It is possible to use a different label if needed by defining a label named <code>#board:groupBy</code> with
<code the value being the attribute to use (with or without <code>#</code> attribute
spellcheck="false">#board:groupBy</code>with the value being the attribute to use (with or prefix).</p>
without <code spellcheck="false">#</code> attribute prefix).</p>
<h3>Grouping by relations</h3> <h3>Grouping by relations</h3>
<figure class="image image-style-align-right"> <figure class="image image-style-align-right">
<img style="aspect-ratio:535/245;" src="1_Kanban Board_image.png" <img style="aspect-ratio:535/245;" src="1_Kanban Board_image.png"
@ -151,35 +143,35 @@
<p>A more advanced use-case is grouping by <a href="#root/_help_Cq5X6iKQop6R">Relations</a>.</p> <p>A more advanced use-case is grouping by <a href="#root/_help_Cq5X6iKQop6R">Relations</a>.</p>
<p>During this mode:</p> <p>During this mode:</p>
<ul> <ul>
<li data-list-item-id="e8b12352beeecdc5fbba7ab2cc8c17ebf">The columns represent the <em>target notes</em> of a relation.</li> <li>The columns represent the <em>target notes</em> of a relation.</li>
<li data-list-item-id="e93e74a0b91864714971460d4f0c70d7c">When creating a new column, a note is selected instead of a column name.</li> <li>When creating a new column, a note is selected instead of a column name.</li>
<li <li>The column icon will match the target note.</li>
data-list-item-id="ef5f8b62aea5f50ce16fd0eb7ec2da8a9">The column icon will match the target note.</li> <li>Moving notes between columns will change its relation.</li>
<li data-list-item-id="e1a406b952707601c17f59b79b428fcc4">Moving notes between columns will change its relation.</li> <li>Renaming an existing column will change the target note of all the notes
<li data-list-item-id="e26ad186c049342b5071dbd7e288c34a5">Renaming an existing column will change the target note of all the notes in that column.</li>
in that column.</li>
</ul> </ul>
<p>Using relations instead of labels has some benefits:</p> <p>Using relations instead of labels has some benefits:</p>
<ul> <ul>
<li data-list-item-id="e023fc2ef6ec6e976dc81d3e2b0fd0c46">The status/grouping of the notes is visible outside the Kanban board, <li>The status/grouping of the notes is visible outside the Kanban board,
for example on the&nbsp;<a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>.</li> for example on the&nbsp;<a class="reference-link" href="#root/_help_bdUJEHsAPYQR">Note Map</a>.</li>
<li <li>Columns can have icons.</li>
data-list-item-id="e3b0b352a62b78f4ec2cf335e054f1cac">Columns can have icons.</li> <li>Renaming columns is less intensive since it simply involves changing the
<li data-list-item-id="e5646f801237c13c7ddd600eecffea714">Renaming columns is less intensive since it simply involves changing the note title of the target note instead of having to do a bulk rename.</li>
note title of the target note instead of having to do a bulk rename.</li>
</ul> </ul>
<p>To do so:</p> <p>To do so:</p>
<ol> <ol>
<li data-list-item-id="ecd59797f1fe062de6118a1020034f761">First, create a Kanban board from scratch and not a template:</li> <li>
<li <p>First, create a Kanban board from scratch and not a template:</p>
data-list-item-id="ea3e28bea94fae0e37b0319a1e2c0f48c">Assign <code spellcheck="false">#viewType=board #hidePromotedAttributes</code> to </li>
emulate the default template.</li> <li>
<li data-list-item-id="e063e32f35af1b206b060779666db84e5">Set <code spellcheck="false">#board:groupBy</code> to the name of a relation <p>Assign <code>#viewType=board #hidePromotedAttributes</code> to emulate the
to group by, <strong>including the</strong> <code spellcheck="false">~</code> <strong>prefix</strong> (e.g. default template.</p>
<code </li>
spellcheck="false">~status</code>).</li> <li>
<li data-list-item-id="e079874488794f7fa65ea90c7aa6a71a9"> <p>Set <code>#board:groupBy</code> to the name of a relation to group by, <strong>including the</strong> <code>~</code> <strong>prefix</strong> (e.g. <code>~status</code>).</p>
<p>Optionally, use&nbsp;<a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>&nbsp;for </li>
easy status change within the note:</p><pre><code class="language-text-x-trilium-auto">#relation:status(inheritable)="promoted,alias=Status,single"</code></pre> <li>
</li> <p>Optionally, use&nbsp;<a class="reference-link" href="#root/_help_OFXdgB2nNk1F">Promoted Attributes</a>&nbsp;for
easy status change within the note:</p><pre><code class="language-text-x-trilium-auto">#relation:status(inheritable)="promoted,alias=Status,single"</code></pre>
</li>
</ol> </ol>

View File

@ -3,81 +3,93 @@
of brevity, beta versions are skipped and the features gathered to the of brevity, beta versions are skipped and the features gathered to the
nearest stable version.</p> nearest stable version.</p>
<ul> <ul>
<li>v0.97.0: <li data-list-item-id="ef19f93f07430e9fca2b18faeccdb8541">v0.101.0:
<ul> <ul>
<li>Books are now&nbsp;<a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>.</li> <li data-list-item-id="e966cf04b1d3e2d4c25a0804e1300fce1">A&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_IjZS7iK5EXtb">New Layout</a>&nbsp;has
<li><a class="reference-link" href="#root/_help_2FvYrpmOXm29">Table View</a>&nbsp;is been introduced, making significant modifications to the UI/UX such as
integrating the ribbon and the floating buttons into other UI elements
and introducing new functionality such as the&nbsp;<a class="reference-link"
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/IjZS7iK5EXtb/_help_I6p2a06hdnL6">Breadcrumb</a>.</li>
</ul>
</li>
<li data-list-item-id="e5e5a3e380946c4a9c20c195c4c2a67aa">v0.97.0:
<ul>
<li data-list-item-id="ebbabd91a31cefad600f9158e0d8ed8a6">Books are now&nbsp;<a class="reference-link" href="#root/_help_GTwFsgaA0lCt">Collections</a>.</li>
<li
data-list-item-id="e5d450d9e3671fcaaddf7de7d4189cce9"><a class="reference-link" href="#root/_help_2FvYrpmOXm29">Table</a>&nbsp;is
a new collection type displaying notes and attributes in an editable grid.</li> a new collection type displaying notes and attributes in an editable grid.</li>
<li><a class="reference-link" href="#root/_help_ZjLYv08Rp3qC">Quick edit</a>&nbsp;is <li
introduced, adding a new way to edit notes in a popup instead of opening data-list-item-id="ec1854c492a549ddaf435991b6600932f"><a class="reference-link" href="#root/_help_ZjLYv08Rp3qC">Quick edit</a>&nbsp;is
a new tab. It also integrates well with&nbsp;<a class="reference-link" introduced, adding a new way to edit notes in a popup instead of opening
href="#root/_help_GTwFsgaA0lCt">Collections</a>.</li> a new tab. It also integrates well with&nbsp;<a class="reference-link"
href="#root/_help_GTwFsgaA0lCt">Collections</a>.</li>
</ul> </ul>
</li> </li>
<li>v0.96.0: <li data-list-item-id="e669f8c64a650e888f7ab9ad44811b3fe">v0.96.0:
<ul> <ul>
<li><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;gain <li data-list-item-id="e9edc8470eceb197a762ea99a56990b04"><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;gain
premium features thanks to a collaboration with the CKEditor team: premium features thanks to a collaboration with the CKEditor team:
<ul> <ul>
<li><a class="reference-link" href="#root/_help_ZlN4nump6EbW">Slash Commands</a> <li data-list-item-id="eed1f858832c5484cc05c5206af7deb8c"><a class="reference-link" href="#root/_help_ZlN4nump6EbW">Slash Commands</a>
</li> </li>
<li><a class="reference-link" href="#root/_help_pwc194wlRzcH">Text Snippets</a> <li data-list-item-id="e423ca44abcf1c492bca3326c68a4dd02"><a class="reference-link" href="#root/_help_pwc194wlRzcH">Text Snippets</a>
</li> </li>
</ul> </ul>
</li> </li>
</ul>
</li>
<li data-list-item-id="edd5bb4ebdcbc1b78eab317432920681e">v0.95.0:
<ul>
<li data-list-item-id="e96ab356645dc2872d00ef26303c3c243">A more friendly theme was introduced for&nbsp;<a class="reference-link"
href="#root/_help_R9pX4DGra2Vt">Sharing</a>, with search, expandable tree, night
mode and more.</li>
</ul>
</li>
<li data-list-item-id="eb0db41819961cceb421d0080ebe393a0">v0.94.0:
<ul>
<li data-list-item-id="ef5d455dc8f704ff4ed8dbaa643680b4f">Added integration with&nbsp;<a class="reference-link" href="#root/_help_LMAv4Uy3Wk6J">[missing note]</a>&nbsp;(using
self-hosted LLMs such as Ollama or industry standards such as ChatGPT).</li>
</ul>
</li>
<li data-list-item-id="eb02f63d990d33fb68e5c5ef4cb4d98bc">v0.92.5:
<ul>
<li data-list-item-id="eb7667b18dbb62602cb808e66391dc419">Windows binaries are now signed.</li>
<li data-list-item-id="eb2eb9833623a29c74170e96c8354fb58"><a class="reference-link" href="#root/_help_7DAiwaf8Z7Rz">Multi-Factor Authentication</a>&nbsp;was
introduced.</li>
</ul>
</li>
<li data-list-item-id="e72d4cc6c767dc3d84b26a106ec7c68b5">v0.92.4:
<ul>
<li data-list-item-id="ee1f5a03462a47645dbb59199872e1872">macOS binaries are now signed.</li>
<li data-list-item-id="eae09e921f976e97d897d36c907dec049"><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes
can now have adjustable&nbsp;<a class="reference-link" href="#root/_help_veGu4faJErEM">Content language &amp; Right-to-left support</a>.</li>
<li
data-list-item-id="e12e70f61de823364911a9312f5a531e3"><a class="reference-link" href="#root/_help_NRnIZmSMc5sj">Printing &amp; Exporting as PDF</a>
</li>
<li data-list-item-id="e4dc6fa7783021624c2c792eed2a8d444"><a class="reference-link" href="#root/_help_rC3pL2aptaRE">Zen mode</a>
</li>
<li data-list-item-id="e089e6f72a09ee8fe6d12a9c5a774aa78"><a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar</a>, allowing
notes to be displayed in a monthly grid based on start and end dates.</li>
</ul> </ul>
</li> </li>
<li>v0.95.0: <li data-list-item-id="e5acf0fb776a98a6f22fc1e7d8baa1239">v0.91.5:
<ul> <ul>
<li>A more friendly theme was introduced for&nbsp;<a class="reference-link" <li data-list-item-id="eff56df616edfcfbacd72082aa8400caa">Significant improvements for mobile.</li>
href="#root/_help_R9pX4DGra2Vt">Sharing</a>, with search, expandable tree, night <li data-list-item-id="e60bb9702a5cadd5806140a188716ec8e"><a class="reference-link" href="#root/_help_AgjCISero73a">Footnotes</a>&nbsp;are
mode and more.</li> now supported in&nbsp;<a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes.</li>
<li
data-list-item-id="e9214e1433aa79a600f283168945fd4ba">Mermaid diagrams can now be inserted inline within&nbsp;<a class="reference-link"
href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes.</li>
<li data-list-item-id="e0369b6c1b97c47dcb47a0f46a003c8ed">The TriliumNext theme is introduced, bringing a more modern design to
the application.</li>
<li data-list-item-id="ef5ee87f813b6d7b4ac89f6f3b9e72964"><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map</a>, displaying
notes as markers on a geographical map for easy trip planning.</li>
</ul> </ul>
</li> </li>
<li>v0.94.0: <li data-list-item-id="ec24ee9c5a93cac613b12ed9632dbd5d1">v0.90.8:
<ul> <ul>
<li>Added integration with&nbsp;<a class="reference-link" href="#root/_help_LMAv4Uy3Wk6J">AI</a>&nbsp;(using <li data-list-item-id="eef68f9a9f5de860bbf95fb520aa499e3">A new note type was introduced:&nbsp;<a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
self-hosted LLMs such as Ollama or industry standards such as ChatGPT).</li> </li>
</ul> </ul>
</li> </li>
<li>v0.92.5:
<ul>
<li>Windows binaries are now signed.</li>
<li><a class="reference-link" href="#root/_help_7DAiwaf8Z7Rz">Multi-Factor Authentication</a>&nbsp;was
introduced.</li>
</ul>
</li>
<li>v0.92.4:
<ul>
<li>macOS binaries are now signed.</li>
<li><a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes
can now have adjustable&nbsp;<a class="reference-link" href="#root/_help_veGu4faJErEM">Content language &amp; Right-to-left support</a>.</li>
<li><a class="reference-link" href="#root/_help_NRnIZmSMc5sj">Export as PDF</a>
</li>
<li><a class="reference-link" href="#root/_help_rC3pL2aptaRE">Zen mode</a>
</li>
<li><a class="reference-link" href="#root/_help_xWbu3jpNWapp">Calendar View</a>,
allowing notes to be displayed in a monthly grid based on start and end
dates.</li>
</ul>
</li>
<li>v0.91.5:
<ul>
<li>Significant improvements for mobile.</li>
<li><a class="reference-link" href="#root/_help_AgjCISero73a">Footnotes</a>&nbsp;are
now supported in&nbsp;<a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes.</li>
<li>Mermaid diagrams can now be inserted inline within&nbsp;<a class="reference-link"
href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;notes.</li>
<li>The TriliumNext theme is introduced, bringing a more modern design to
the application.</li>
<li><a class="reference-link" href="#root/_help_81SGnPGMk7Xc">Geo Map View</a>,
displaying notes as markers on a geographical map for easy trip planning.</li>
</ul>
</li>
<li>v0.90.8:
<ul>
<li>A new note type was introduced:&nbsp;<a class="reference-link" href="#root/_help_gBbsAeiuUxI5">Mind Map</a>
</li>
</ul>
</li>
</ul> </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 { OptionNames } from "@triliumnext/commons";
import type { Request } from "express";
import ValidationError from "../../errors/validation_error.js";
import config from "../../services/config.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 { interface UserTheme {
val: string; // value of the theme, used in the URL val: string; // value of the theme, used in the URL
@ -100,6 +101,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"splitEditorOrientation", "splitEditorOrientation",
"seenCallToActions", "seenCallToActions",
"experimentalFeatures", "experimentalFeatures",
"newLayout",
// AI/LLM integration options // AI/LLM integration options
"aiEnabled", "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 appInfo from "./app_info.js";
import { randomSecureToken, isWindows } from "./utils.js";
import log from "./log.js";
import dateUtils from "./date_utils.js"; import dateUtils from "./date_utils.js";
import keyboardActions from "./keyboard_actions.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() { function initDocumentOptions() {
optionService.createOption("documentId", randomSecureToken(16), false); optionService.createOption("documentId", randomSecureToken(16), false);
@ -156,6 +157,7 @@ const defaultOptions: DefaultOption[] = [
{ name: "shadowsEnabled", value: "true", isSynced: false }, { name: "shadowsEnabled", value: "true", isSynced: false },
{ name: "backdropEffectsEnabled", value: "true", isSynced: false }, { name: "backdropEffectsEnabled", value: "true", isSynced: false },
{ name: "smoothScrollEnabled", value: "true", isSynced: false }, { name: "smoothScrollEnabled", value: "true", isSynced: false },
{ name: "newLayout", value: "true", isSynced: true },
// Internationalization // Internationalization
{ name: "locale", value: "en", isSynced: true }, { name: "locale", value: "en", isSynced: true },
@ -171,9 +173,9 @@ const defaultOptions: DefaultOption[] = [
value: (optionsMap) => { value: (optionsMap) => {
if (optionsMap.theme === "light") { if (optionsMap.theme === "light") {
return "default:stackoverflow-light"; return "default:stackoverflow-light";
} else {
return "default:stackoverflow-dark";
} }
return "default:stackoverflow-dark";
}, },
isSynced: false isSynced: false
}, },

View File

@ -1,5 +1,5 @@
# Documentation # Documentation
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/lONK7oKNhIgu/Documentation_image.png" width="205" height="162"> There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/Y3urZDbSOH6u/Documentation_image.png" width="205" height="162">
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>. * The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.

View File

@ -237,6 +237,20 @@
"value": "feature-highlights", "value": "feature-highlights",
"isInheritable": false, "isInheritable": false,
"position": 170 "position": 170
},
{
"type": "relation",
"name": "internalLink",
"value": "IjZS7iK5EXtb",
"isInheritable": false,
"position": 180
},
{
"type": "relation",
"name": "internalLink",
"value": "I6p2a06hdnL6",
"isInheritable": false,
"position": 190
} }
], ],
"format": "markdown", "format": "markdown",
@ -2851,6 +2865,20 @@
"value": "note-buttons", "value": "note-buttons",
"isInheritable": false, "isInheritable": false,
"position": 60 "position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "IjZS7iK5EXtb",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
"value": "W8vYD3Q1zjCR",
"isInheritable": false,
"position": 80
} }
], ],
"format": "markdown", "format": "markdown",
@ -3436,6 +3464,286 @@
"dataFileName": "Note Tooltip_image.png" "dataFileName": "Note Tooltip_image.png"
} }
] ]
},
{
"isClone": false,
"noteId": "IjZS7iK5EXtb",
"notePath": [
"pOsGYCXsbNQG",
"gh7bpGYxajRS",
"Vc8PjrjAGuOp",
"IjZS7iK5EXtb"
],
"title": "New Layout",
"notePosition": 220,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bx-layout",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "4TIF1oA4VQRO",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "iPIMuisry3hd",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "KC1HB96bqqHX",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
"value": "8YBEPzcpUgxw",
"isInheritable": false,
"position": 80
},
{
"type": "relation",
"name": "internalLink",
"value": "grjYqerjn243",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
"value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 100
},
{
"type": "relation",
"name": "internalLink",
"value": "cbkrhQjrkKrh",
"isInheritable": false,
"position": 110
},
{
"type": "relation",
"name": "internalLink",
"value": "MtPxeAWVAzMg",
"isInheritable": false,
"position": 120
},
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 140
},
{
"type": "relation",
"name": "internalLink",
"value": "YKWqdJhzi2VY",
"isInheritable": false,
"position": 150
},
{
"type": "relation",
"name": "internalLink",
"value": "veGu4faJErEM",
"isInheritable": false,
"position": 160
},
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"isInheritable": false,
"position": 170
},
{
"type": "relation",
"name": "internalLink",
"value": "AlJ73vBCjWDw",
"isInheritable": false,
"position": 180
},
{
"type": "relation",
"name": "internalLink",
"value": "l0tKav7yLHGF",
"isInheritable": false,
"position": 190
},
{
"type": "relation",
"name": "internalLink",
"value": "eIg8jdvaoNNd",
"isInheritable": false,
"position": 200
},
{
"type": "relation",
"name": "internalLink",
"value": "m523cpzocqaD",
"isInheritable": false,
"position": 210
}
],
"format": "markdown",
"dataFileName": "New Layout.md",
"attachments": [
{
"attachmentId": "3DFGaMiTTHQ1",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "New Layout_image.png"
},
{
"attachmentId": "6iN5nrmdwG6z",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_New Layout_image.png"
},
{
"attachmentId": "KvNAEoJjRhyr",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "2_New Layout_image.png"
},
{
"attachmentId": "lEKxf6dYMG6u",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "3_New Layout_image.png"
},
{
"attachmentId": "SYOTVGCyx749",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "4_New Layout_image.png"
},
{
"attachmentId": "wCwzwfGspejR",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "5_New Layout_image.png"
}
],
"dirFileName": "New Layout",
"children": [
{
"isClone": false,
"noteId": "I6p2a06hdnL6",
"notePath": [
"pOsGYCXsbNQG",
"gh7bpGYxajRS",
"Vc8PjrjAGuOp",
"IjZS7iK5EXtb",
"I6p2a06hdnL6"
],
"title": "Breadcrumb",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "AlJ73vBCjWDw",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-chevron-right",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",
"dataFileName": "Breadcrumb.md",
"attachments": [
{
"attachmentId": "CjYmaJD0L1D4",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Breadcrumb_image.png"
}
]
},
{
"isClone": false,
"noteId": "AlJ73vBCjWDw",
"notePath": [
"pOsGYCXsbNQG",
"gh7bpGYxajRS",
"Vc8PjrjAGuOp",
"IjZS7iK5EXtb",
"AlJ73vBCjWDw"
],
"title": "Status bar",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "I6p2a06hdnL6",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-dock-bottom",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",
"dataFileName": "Status bar.md",
"attachments": []
}
]
} }
] ]
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,114 @@
# New Layout
The _New layout_ is a series of UI/UX changes that were introduced in v0.101.0 that heavily change both existing UI elements, as well as adding some new ones. The goal of this new layout is to modernize the application and to make it more intuitive but at the same time to reduce clutter.
## Newly introduced features
### Status bar
At the bottom of the window there is a new bar called the _Status bar_. This bar houses multiple items such as the Breadcrumb navigation and information and settings about the current note, such as the [content language](../../Note%20Types/Text/Content%20language%20%26%20Right-to-le.md) and <a class="reference-link" href="../../Advanced%20Usage/Attributes.md">Attributes</a>.
For more information, consult the [dedicated page](New%20Layout/Status%20bar.md).
<figure class="image"><img style="aspect-ratio:1150/27;" src="4_New Layout_image.png" width="1150" height="27"></figure>
### Inline title
In previous versions of Trilium, the title bar was fixed at all times. In the new layout, there is both a fixed title bar and one that scrolls with the text. The newly introduced title is called the _Inline title_ and it displays the title in a larger font, while also displaying additional information such as the creation and the modification date.
Whenever the title is scrolled past, the fixed title is shown instead.
This only affects <a class="reference-link" href="../../Note%20Types/Text.md">Text</a> and <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> notes. Note types that take the entirety of the screen such as <a class="reference-link" href="../../Note%20Types/Canvas.md">Canvas</a> will always have only the fixed title bar.
Depending on the note type, the inline title will also present some more interactive options such as being able to switch the note type (see below).
<figure class="image"><img style="aspect-ratio:899/122;" src="New Layout_image.png" width="899" height="122"><figcaption>The <em>Inline title</em>, which is displayed at the top of the note and can be scrolled past.</figcaption></figure><figure class="image"><img style="aspect-ratio:910/104;" src="3_New Layout_image.png" width="910" height="104"><figcaption>The fixed title bar. The title only appears after scrolling past the <em>Inline title</em>.</figcaption></figure>
### New note type switcher
When a new <a class="reference-link" href="../../Note%20Types/Text.md">Text</a> or <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> note is created, a note type switcher will appear below the _Inline title_. Apart from changing the note type, it's also possible to apply a [template](../../Advanced%20Usage/Templates.md).
The switcher will disappear as soon as a text is entered.
<img src="5_New Layout_image.png" width="735" height="143">
### Note badges
Note badges appear near the fixed note title and indicate important information about the note such as whether it is read-only. Some of the badges are also interactive.
<figure class="image"><img style="aspect-ratio:910/49;" src="2_New Layout_image.png" width="910" height="49"></figure>
The following badges are available:
* **Read-only badge**, which will be shown if the note is not editable due to either automatic read-only or manual read-only. Clicking on the badge will temporarily edit the note (similar to the Edit [floating button](Floating%20buttons.md)).
* **Share badge**, which will indicate that the current note is shared. The badge will also indicate if the share is on the local network (for the desktop application without <a class="reference-link" href="../../Installation%20%26%20Setup/Synchronization.md">Synchronization</a> set up) or publicly accessible (for the server).
* **Web clip badge**, which will indicate if the note was clipped using the <a class="reference-link" href="../../Installation%20%26%20Setup/Web%20Clipper.md">Web Clipper</a>. The badge acts as a link, so it can be clicked on to navigate to the page or right clicked for more options.
* **Execute badge**, for [scripts](../../Scripting.md) or [saved SQL queries](../../Advanced%20Usage/Database/Manually%20altering%20the%20database/SQL%20Console.md) which have an execute button or a description.
Some of these badges replace the dedicated panels at the top of the note.
### Collapsible sections
<figure class="image"><img style="aspect-ratio:496/265;" src="1_New Layout_image.png" width="496" height="265"></figure>
The following sections have been made collapsible:
* _Promoted Attributes_
* For full-height notes such as <a class="reference-link" href="../../Note%20Types/Canvas.md">Canvas</a>, the promoted attributes are collapsed by default to make room.
* The keyboard shortcut previously used to trigger the promoted attributes ribbon tab (which was no longer working) has been repurposed to toggle the promoted attributes instead.
* _Edited Notes_, which appears for <a class="reference-link" href="../../Advanced%20Usage/Advanced%20Showcases/Day%20Notes.md">Day Notes</a> is now shown underneath the title.
* Whether the section is collapsed or not depends on the choice in <a class="reference-link" href="Options.md">Options</a> → Appearance.
* _Search Properties_, which appears for the full <a class="reference-link" href="../Navigation/Search.md">Search</a> and <a class="reference-link" href="../../Note%20Types/Saved%20Search.md">Saved Search</a>.
## Changing to the existing layout
### Removal of the ribbon
The most significant change is the removal of the ribbon. All the actions and options from the ribbon were integrated in other places in the application.
Here's how all the different tabs that were once part of the ribbon are now available in the new layout:
* “Formatting toolbar” was relocated to the top of the page.
* Instead of having one per split, now there is a single formatting toolbar per tab. This allows more space for the toolbar items.
* “Owned attributes” and “Inherited attributes” were merged and moved to the status bar region (displayed one above the other).
* “Basic Properties” were integrated in the <a class="reference-link" href="Note%20buttons.md">Note buttons</a> menu.
* The only exception here is the Language combo box which can now be found in the status bar (top-right of the screen).
* “File” and “Image” tabs
* The buttons were moved to the right of the note title, as dedicated entries in <a class="reference-link" href="Note%20buttons.md">Note buttons</a>.
* The info section has been merged into the _Note info_ section of the status bar.
* Edited notes
* Moved underneath the title, displayed under a collapsible area and the notes are represented as badges/chips.
* Whether the section is expanded or collapsed depends on the “Edited Notes ribbon tab will automatically open on day notes” setting from Options → Appearance.
* Search definition tab
* Moved underneath the title under a collapsible area.
* Expanded by default for new searches, collapsed for saved searches.
* The Note map is now available in the Note actions menu.
* Instead of opening into a panel in the ribbon, the note map now opens in a side split (similar to the in-app help).
* “Note info” tab was moved to a small (i) icon in the status bar.
* “Similar notes” tab
* Moved to the status bar, by going to the “Note info” section and pressing the button to show similar notes.
* Displayed as a fixed panel, similar to the attributes.
* The Collection properties tab were relocated under the note title and grouped into:
* A combo box to quickly switch between views.
* Individual settings for the current view in a submenu.
* Some smaller ribbon tabs were converted to badges that appear near the note title in the breadcrumb section:
* Original URL indicator for clipped web pages (`#pageUrl`).
* SQL and script execute buttons.
> [!NOTE]
> The ribbon keyboard shortcuts (e.g. `toggleRibbonTabClassicEditor`) have been repurposed to work on the new layout, where they will toggle the appropriate panel.
### Removal of the floating buttons
Most of the buttons were relocated to the right of the note title, in the <a class="reference-link" href="Note%20buttons.md">Note buttons</a> area, with the exception of:
* The Edit button is displayed near the note title, as a badge.
* _Backlinks_ is displayed in the status bar. When clicked, the same list of backlinks is displayed.
* Relation map zoom buttons are now part of the relation map itself.
* Export image to PNG/SVG are now in the Note actions menu, in the _Export as image_ option.
## How to toggle the new layout
Starting with v0.101.0, this new layout is enabled by default. It is possible to fall back to the old layout by going to <a class="reference-link" href="Options.md">Options</a> → Appearance and selecting _Old layout_.
> [!IMPORTANT]
> Since a new layout was introduced, this becomes the standard one. The _Old layout_ is considered deprecated and will not receive new features (for example, the breadcrumb) as we focus on the new one. At some point the old layout will be removed entirely, as maintaining two layouts with major differences creates a maintenance burden.

View File

@ -0,0 +1,26 @@
# Breadcrumb
<figure class="image"><img style="aspect-ratio:1150/27;" src="Breadcrumb_image.png" width="1150" height="27"></figure>
The breadcrumb allows quickly viewing the note hierarchy of the current note and navigating through it.
It is part of the <a class="reference-link" href="Status%20bar.md">Status bar</a>, displayed in the bottom-left of the screen.
## Layout and Interaction
* If a note or workspace is hoisted, a badge will appear on the left-most side.
* Clicking on the badge will un-hoist the note/workspace.
* The left-most icon represents the root note, or the hoisted note or workspace.
* Clicking the icon will jump to the root note.
* Right clicking the icon will display a menu that allows opening the note in a new tab, split, etc.
* Each segment shows the title of a note in the current note hierarchy.
* Clicking the icon will jump to that note.
* Right clicking will open a menu with multiple options such as opening the note in a different tab/split/window, hoisting, moving/cloning the note, duplicating as well as changing the color of the note.
* Clicking the arrow next to each segment will reveal the child notes of the segment on the left.
* Clicking on an icon will navigate to that particular note.
* It's also possible to create a new child note from here.
* The menu can optionally hide the archived notes.
* If the current note is deep within a hierarchy, the segments will collapse into a \[…\] button in order not to occupy too much space.
* Clicking this button will display each collapsed entry as a menu item. Clicking on it will navigate to that particular note.
* Right clicking on an empty space to the right of the breadcrumb (before the other status bar items) will reveal another menu that allows:
* Toggling whether archived notes are displayed in the breadcrumb and in the note tree.
* Copying the current note path to clipboard.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,27 @@
# Status bar
The status bar displays information about the current note and allows changing settings related to it such as configuring the language or attributes.
## Layout and interaction
On the left side, the <a class="reference-link" href="Breadcrumb.md">Breadcrumb</a> is displayed which indicates the current note as well as its parent notes and allows for quick navigation throughout the hierarchy.
On the right side, specific sections will show depending on the type of the current note.
1. For code notes, the language mode of the note is indicated (e.g. JavaScript, plain text), as well as allowing easy switching to another mode.
2. For text notes, the content language is displayed and can be changed, thus configuring the spell-check and the right-to-left support.
1. Note that this applies to the entire note and not the selection, unlike some text editors.
3. If a note is placed in multiple places in the tree (cloned), the number of the note paths will be displayed.
1. Clicking it will reveal the full list of note paths and a button to place it somewhere else.
4. If a note has attachments, their number will be displayed.
1. Clicking on it will reveal the list of attachments in a new tab.
5. If a note is linked from other text notes (backlinks), the number of backlinks will be displayed.
1. Clicking on it will show the list of notes that link to this note, as well as an excerpt of where the note is referenced.
Regardless of note type, the following items will always be displayed if there is a note:
1. Note info, which displays:
1. The creation/modification date of the note.
2. The type and MIME of the note.
3. The note ID.
4. An estimation of the note size of the note itself and its children.
5. A button to show Similar notes.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -2,4 +2,6 @@
To the right of the [Ribbon](Ribbon.md) there are a few more buttons: ![](Note%20buttons_image.png) To the right of the [Ribbon](Ribbon.md) there are a few more buttons: ![](Note%20buttons_image.png)
* The Note Revisions button displays the [Note Revisions](../Notes/Note%20Revisions.md) for that particular note. * The Note Revisions button displays the [Note Revisions](../Notes/Note%20Revisions.md) for that particular note.
* The contextual menu offers commands for the note or its subtree, such as import, export, viewing the [Note source code](../../Advanced%20Usage/Note%20source.md) or [Attachments](../Notes/Attachments.md). * The contextual menu offers commands for the note or its subtree, such as import, export, viewing the [Note source code](../../Advanced%20Usage/Note%20source.md) or [Attachments](../Notes/Attachments.md).
On the <a class="reference-link" href="New%20Layout.md">New Layout</a>, the button area is populated by some more buttons that are specific to the current note. For example, for Image and <a class="reference-link" href="../../Note%20Types/File.md">File</a> notes, the download and copy buttons were relocated there.

View File

@ -1,9 +1,11 @@
# Feature Highlights # Feature Highlights
This section presents the most important changes by version. For a full set of changes, please consult the change log of each release. For purposes of brevity, beta versions are skipped and the features gathered to the nearest stable version. This section presents the most important changes by version. For a full set of changes, please consult the change log of each release. For purposes of brevity, beta versions are skipped and the features gathered to the nearest stable version.
* v0.101.0:
* A <a class="reference-link" href="Basic%20Concepts%20and%20Features/UI%20Elements/New%20Layout.md">New Layout</a> has been introduced, making significant modifications to the UI/UX such as integrating the ribbon and the floating buttons into other UI elements and introducing new functionality such as the <a class="reference-link" href="Basic%20Concepts%20and%20Features/UI%20Elements/New%20Layout/Breadcrumb.md">Breadcrumb</a>.
* v0.97.0: * v0.97.0:
* Books are now <a class="reference-link" href="Collections.md">Collections</a>. * Books are now <a class="reference-link" href="Collections.md">Collections</a>.
* <a class="reference-link" href="Collections/Table.md">Table View</a> is a new collection type displaying notes and attributes in an editable grid. * <a class="reference-link" href="Collections/Table.md">Table</a> is a new collection type displaying notes and attributes in an editable grid.
* <a class="reference-link" href="Basic%20Concepts%20and%20Features/Navigation/Quick%20edit.md">Quick edit</a> is introduced, adding a new way to edit notes in a popup instead of opening a new tab. It also integrates well with <a class="reference-link" href="Collections.md">Collections</a>. * <a class="reference-link" href="Basic%20Concepts%20and%20Features/Navigation/Quick%20edit.md">Quick edit</a> is introduced, adding a new way to edit notes in a popup instead of opening a new tab. It also integrates well with <a class="reference-link" href="Collections.md">Collections</a>.
* v0.96.0: * v0.96.0:
* <a class="reference-link" href="Note%20Types/Text.md">Text</a> gain premium features thanks to a collaboration with the CKEditor team: * <a class="reference-link" href="Note%20Types/Text.md">Text</a> gain premium features thanks to a collaboration with the CKEditor team:
@ -12,21 +14,21 @@ This section presents the most important changes by version. For a full set of c
* v0.95.0: * v0.95.0:
* A more friendly theme was introduced for <a class="reference-link" href="Advanced%20Usage/Sharing.md">Sharing</a>, with search, expandable tree, night mode and more. * A more friendly theme was introduced for <a class="reference-link" href="Advanced%20Usage/Sharing.md">Sharing</a>, with search, expandable tree, night mode and more.
* v0.94.0: * v0.94.0:
* Added integration with <a class="reference-link" href="#root/LMAv4Uy3Wk6J">AI</a> (using self-hosted LLMs such as Ollama or industry standards such as ChatGPT). * Added integration with <a class="reference-link" href="#root/LMAv4Uy3Wk6J">[missing note]</a> (using self-hosted LLMs such as Ollama or industry standards such as ChatGPT).
* v0.92.5: * v0.92.5:
* Windows binaries are now signed. * Windows binaries are now signed.
* <a class="reference-link" href="Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md">Multi-Factor Authentication</a> was introduced. * <a class="reference-link" href="Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md">Multi-Factor Authentication</a> was introduced.
* v0.92.4: * v0.92.4:
* macOS binaries are now signed. * macOS binaries are now signed.
* <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes can now have adjustable <a class="reference-link" href="Note%20Types/Text/Content%20language%20%26%20Right-to-le.md">Content language &amp; Right-to-left support</a>. * <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes can now have adjustable <a class="reference-link" href="Note%20Types/Text/Content%20language%20%26%20Right-to-le.md">Content language &amp; Right-to-left support</a>.
* <a class="reference-link" href="Basic%20Concepts%20and%20Features/Notes/Printing%20%26%20Exporting%20as%20PDF.md">Export as PDF</a> * <a class="reference-link" href="Basic%20Concepts%20and%20Features/Notes/Printing%20%26%20Exporting%20as%20PDF.md">Printing &amp; Exporting as PDF</a>
* <a class="reference-link" href="Basic%20Concepts%20and%20Features/Zen%20mode.md">Zen mode</a> * <a class="reference-link" href="Basic%20Concepts%20and%20Features/Zen%20mode.md">Zen mode</a>
* <a class="reference-link" href="Collections/Calendar.md">Calendar View</a>, allowing notes to be displayed in a monthly grid based on start and end dates. * <a class="reference-link" href="Collections/Calendar.md">Calendar</a>, allowing notes to be displayed in a monthly grid based on start and end dates.
* v0.91.5: * v0.91.5:
* Significant improvements for mobile. * Significant improvements for mobile.
* <a class="reference-link" href="Note%20Types/Text/Footnotes.md">Footnotes</a> are now supported in <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes. * <a class="reference-link" href="Note%20Types/Text/Footnotes.md">Footnotes</a> are now supported in <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes.
* Mermaid diagrams can now be inserted inline within <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes. * Mermaid diagrams can now be inserted inline within <a class="reference-link" href="Note%20Types/Text.md">Text</a> notes.
* The TriliumNext theme is introduced, bringing a more modern design to the application. * The TriliumNext theme is introduced, bringing a more modern design to the application.
* <a class="reference-link" href="Collections/Geo%20Map.md">Geo Map View</a>, displaying notes as markers on a geographical map for easy trip planning. * <a class="reference-link" href="Collections/Geo%20Map.md">Geo Map</a>, displaying notes as markers on a geographical map for easy trip planning.
* v0.90.8: * v0.90.8:
* A new note type was introduced: <a class="reference-link" href="Note%20Types/Mind%20Map.md">Mind Map</a> * A new note type was introduced: <a class="reference-link" href="Note%20Types/Mind%20Map.md">Mind Map</a>

View File

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