From 7e01dfd22096504fb70be5115807707bffe5bc21 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 19:45:01 +0300 Subject: [PATCH 01/13] fix(sort): refresh when sorting notes via dialog --- apps/client/src/widgets/dialogs/sort_child_notes.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/dialogs/sort_child_notes.ts b/apps/client/src/widgets/dialogs/sort_child_notes.ts index 7560476c5..4724cf55c 100644 --- a/apps/client/src/widgets/dialogs/sort_child_notes.ts +++ b/apps/client/src/widgets/dialogs/sort_child_notes.ts @@ -88,7 +88,9 @@ export default class SortChildNotesDialog extends BasicWidget { this.$widget = $(TPL); this.$form = this.$widget.find(".sort-child-notes-form"); - this.$form.on("submit", async () => { + this.$form.on("submit", async (e) => { + e.preventDefault(); + const sortBy = this.$form.find("input[name='sort-by']:checked").val(); const sortDirection = this.$form.find("input[name='sort-direction']:checked").val(); const foldersFirst = this.$form.find("input[name='sort-folders-first']").is(":checked"); From f6e275709f9487535964451ceeb938121383fb06 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 19:47:01 +0300 Subject: [PATCH 02/13] fix(command_palette): sort child notes not working --- apps/client/src/services/command_registry.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/client/src/services/command_registry.ts b/apps/client/src/services/command_registry.ts index a6c1000b7..8f045d57d 100644 --- a/apps/client/src/services/command_registry.ts +++ b/apps/client/src/services/command_registry.ts @@ -260,7 +260,11 @@ class CommandRegistry { } const treeComponent = appContext.getComponentByEl(tree) as NoteTreeWidget; - treeComponent.triggerCommand(actionName, { ntxId: appContext.tabManager.activeNtxId }); + const activeNode = treeComponent.getActiveNode(); + treeComponent.triggerCommand(actionName, { + ntxId: appContext.tabManager.activeNtxId, + node: activeNode + }); } } From 7fc739487fdda37f6446d82f88d4a49294b6c388 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 19:50:07 +0300 Subject: [PATCH 03/13] chore(command_palette): hide jump to note / command palette --- apps/client/src/services/command_registry.ts | 12 ++++++--- apps/client/src/services/keyboard_actions.ts | 26 ++++++------------- apps/server/src/services/keyboard_actions.ts | 8 +++--- .../src/lib/keyboard_actions_interface.ts | 6 ++++- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/client/src/services/command_registry.ts b/apps/client/src/services/command_registry.ts index 8f045d57d..a8721e43f 100644 --- a/apps/client/src/services/command_registry.ts +++ b/apps/client/src/services/command_registry.ts @@ -1,7 +1,8 @@ +import { ActionKeyboardShortcut } from "@triliumnext/commons"; import appContext, { type CommandNames } from "../components/app_context.js"; import type NoteTreeWidget from "../widgets/note_tree.js"; import { t, translationsInitializedPromise } from "./i18n.js"; -import keyboardActions, { Action } from "./keyboard_actions.js"; +import keyboardActions from "./keyboard_actions.js"; import utils from "./utils.js"; export interface CommandDefinition { @@ -15,7 +16,7 @@ export interface CommandDefinition { aliases?: string[]; source?: "manual" | "keyboard-action"; /** Reference to the original keyboard action for scope checking. */ - keyboardAction?: Action; + keyboardAction?: ActionKeyboardShortcut; } class CommandRegistry { @@ -105,7 +106,7 @@ class CommandRegistry { } } - private registerKeyboardActions(actions: Action[]) { + private registerKeyboardActions(actions: ActionKeyboardShortcut[]) { for (const action of actions) { // Skip actions that we've already manually registered if (this.commands.has(action.actionName)) { @@ -122,6 +123,11 @@ class CommandRegistry { continue; } + // Skip actions that should not appear in the command palette + if (action.ignoreFromCommandPalette) { + continue; + } + // Get the primary shortcut (first one in the list) const primaryShortcut = action.effectiveShortcuts?.[0]; diff --git a/apps/client/src/services/keyboard_actions.ts b/apps/client/src/services/keyboard_actions.ts index 820de185c..c72ba29bb 100644 --- a/apps/client/src/services/keyboard_actions.ts +++ b/apps/client/src/services/keyboard_actions.ts @@ -2,25 +2,15 @@ import server from "./server.js"; import appContext, { type CommandNames } from "../components/app_context.js"; import shortcutService from "./shortcuts.js"; import type Component from "../components/component.js"; +import type { ActionKeyboardShortcut } from "@triliumnext/commons"; -const keyboardActionRepo: Record = {}; +const keyboardActionRepo: Record = {}; -// TODO: Deduplicate with server. -export interface Action { - actionName: CommandNames; - effectiveShortcuts: string[]; - scope: string; - friendlyName: string; - description?: string; - iconClass?: string; - isElectronOnly?: boolean; -} - -const keyboardActionsLoaded = server.get("keyboard-actions").then((actions) => { +const keyboardActionsLoaded = server.get("keyboard-actions").then((actions) => { actions = actions.filter((a) => !!a.actionName); // filter out separators for (const action of actions) { - action.effectiveShortcuts = action.effectiveShortcuts.filter((shortcut) => !shortcut.startsWith("global:")); + action.effectiveShortcuts = (action.effectiveShortcuts ?? []).filter((shortcut) => !shortcut.startsWith("global:")); keyboardActionRepo[action.actionName] = action; } @@ -42,7 +32,7 @@ async function setupActionsForElement(scope: string, $el: JQuery, c const actions = await getActionsForScope(scope); for (const action of actions) { - for (const shortcut of action.effectiveShortcuts) { + for (const shortcut of action.effectiveShortcuts ?? []) { shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId })); } } @@ -50,7 +40,7 @@ async function setupActionsForElement(scope: string, $el: JQuery, c getActionsForScope("window").then((actions) => { for (const action of actions) { - for (const shortcut of action.effectiveShortcuts) { + for (const shortcut of action.effectiveShortcuts ?? []) { shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId })); } } @@ -84,7 +74,7 @@ function updateDisplayedShortcuts($container: JQuery) { const action = await getAction(actionName, true); if (action) { - const keyboardActions = action.effectiveShortcuts.join(", "); + const keyboardActions = (action.effectiveShortcuts ?? []).join(", "); if (keyboardActions || $(el).text() !== "not set") { $(el).text(keyboardActions); @@ -103,7 +93,7 @@ function updateDisplayedShortcuts($container: JQuery) { if (action) { const title = $(el).attr("title"); - const shortcuts = action.effectiveShortcuts.join(", "); + const shortcuts = (action.effectiveShortcuts ?? []).join(", "); if (title?.includes(shortcuts)) { return; diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts index bf65121be..5e5807fd0 100644 --- a/apps/server/src/services/keyboard_actions.ts +++ b/apps/server/src/services/keyboard_actions.ts @@ -36,18 +36,18 @@ function getDefaultKeyboardActions() { { actionName: "jumpToNote", friendlyName: t("keyboard_action_names.jump-to-note"), - iconClass: "bx bx-send", defaultShortcuts: ["CommandOrControl+J"], description: t("keyboard_actions.open-jump-to-note-dialog"), - scope: "window" + scope: "window", + ignoreFromCommandPalette: true }, { actionName: "commandPalette", friendlyName: t("keyboard_action_names.command-palette"), - iconClass: "bx bx-terminal", defaultShortcuts: ["CommandOrControl+Shift+J"], description: t("keyboard_actions.open-command-palette"), - scope: "window" + scope: "window", + ignoreFromCommandPalette: true }, { actionName: "scrollToActiveNote", diff --git a/packages/commons/src/lib/keyboard_actions_interface.ts b/packages/commons/src/lib/keyboard_actions_interface.ts index 9b73e48b2..c3de7e0db 100644 --- a/packages/commons/src/lib/keyboard_actions_interface.ts +++ b/packages/commons/src/lib/keyboard_actions_interface.ts @@ -112,7 +112,7 @@ export interface ActionKeyboardShortcut { * An icon describing the action. * This is currently only used in the command palette. */ - iconClass: string; + iconClass?: string; /** * Scope here means on which element the keyboard shortcuts are attached - this means that for the shortcut to work, * the focus has to be inside the element. @@ -127,6 +127,10 @@ export interface ActionKeyboardShortcut { * This is used to hide actions that are not available in the web version. */ isElectronOnly?: boolean; + /** + * If set to true, the action will not be shown in the command palette. + */ + ignoreFromCommandPalette?: boolean; } export type KeyboardShortcut = ActionKeyboardShortcut | KeyboardShortcutSeparator; From 024022299833e95a171819d67f8845560fbb555e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 19:54:19 +0300 Subject: [PATCH 04/13] chore(command_palette): disable two unsupported commands --- apps/server/src/services/keyboard_actions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts index 5e5807fd0..65efaaa66 100644 --- a/apps/server/src/services/keyboard_actions.ts +++ b/apps/server/src/services/keyboard_actions.ts @@ -245,18 +245,18 @@ function getDefaultKeyboardActions() { { actionName: "addNoteAboveToSelection", friendlyName: t("keyboard_action_names.add-note-above-to-selection"), - iconClass: "bx bx-chevron-up-square", defaultShortcuts: ["Shift+Up"], description: t("keyboard_actions.add-note-above-to-the-selection"), - scope: "note-tree" + scope: "note-tree", + ignoreFromCommandPalette: true }, { actionName: "addNoteBelowToSelection", friendlyName: t("keyboard_action_names.add-note-below-to-selection"), - iconClass: "bx bx-chevron-down-square", defaultShortcuts: ["Shift+Down"], description: t("keyboard_actions.add-note-below-to-selection"), - scope: "note-tree" + scope: "note-tree", + ignoreFromCommandPalette: true }, { actionName: "duplicateSubtree", From 0e6b10e4000759e62fb1e704c2f52dc9cb97bca4 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:33:22 +0300 Subject: [PATCH 05/13] feat(command_palette): active tab-related commands on browser --- apps/server/src/services/keyboard_actions.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts index 65efaaa66..4ce5bc2a6 100644 --- a/apps/server/src/services/keyboard_actions.ts +++ b/apps/server/src/services/keyboard_actions.ts @@ -275,7 +275,6 @@ function getDefaultKeyboardActions() { friendlyName: t("keyboard_action_names.open-new-tab"), iconClass: "bx bx-plus", defaultShortcuts: isElectron ? ["CommandOrControl+T"] : [], - isElectronOnly: true, description: t("keyboard_actions.open-new-tab"), scope: "window" }, @@ -284,7 +283,6 @@ function getDefaultKeyboardActions() { friendlyName: t("keyboard_action_names.close-active-tab"), iconClass: "bx bx-minus", defaultShortcuts: isElectron ? ["CommandOrControl+W"] : [], - isElectronOnly: true, description: t("keyboard_actions.close-active-tab"), scope: "window" }, @@ -302,7 +300,6 @@ function getDefaultKeyboardActions() { friendlyName: t("keyboard_action_names.activate-next-tab"), iconClass: "bx bx-skip-next", defaultShortcuts: isElectron ? ["CommandOrControl+Tab", "CommandOrControl+PageDown"] : [], - isElectronOnly: true, description: t("keyboard_actions.activate-next-tab"), scope: "window" }, @@ -311,7 +308,6 @@ function getDefaultKeyboardActions() { friendlyName: t("keyboard_action_names.activate-previous-tab"), iconClass: "bx bx-skip-previous", defaultShortcuts: isElectron ? ["CommandOrControl+Shift+Tab", "CommandOrControl+PageUp"] : [], - isElectronOnly: true, description: t("keyboard_actions.activate-previous-tab"), scope: "window" }, From 11d086ef12a4cdbfbd058affca0bf150e0ef2aa3 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:39:37 +0300 Subject: [PATCH 06/13] fix(command_palette): text editor-based issues not working --- apps/client/src/services/command_registry.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/client/src/services/command_registry.ts b/apps/client/src/services/command_registry.ts index a8721e43f..e3ffb36a4 100644 --- a/apps/client/src/services/command_registry.ts +++ b/apps/client/src/services/command_registry.ts @@ -244,6 +244,8 @@ class CommandRegistry { if (command.keyboardAction && command.commandName) { if (command.keyboardAction.scope === "note-tree") { this.executeWithNoteTreeFocus(command.commandName); + } else if (command.keyboardAction.scope === "text-detail") { + this.executeWithTextDetail(command.commandName); } else { appContext.triggerCommand(command.commandName); } @@ -272,6 +274,17 @@ class CommandRegistry { node: activeNode }); } + + private async executeWithTextDetail(actionName: CommandNames) { + const typeWidget = await appContext.tabManager.getActiveContext()?.getTypeWidget(); + if (!typeWidget) { + return; + } + + typeWidget.triggerCommand(actionName, { + ntxId: appContext.tabManager.activeNtxId + }); + } } const commandRegistry = new CommandRegistry(); From 5b074c2e221ec4bfc38b630f541541814448ff6f Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:45:31 +0300 Subject: [PATCH 07/13] fix(command_palette): some note context-aware commands not working --- apps/client/src/services/command_registry.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/command_registry.ts b/apps/client/src/services/command_registry.ts index e3ffb36a4..f0bb2eef5 100644 --- a/apps/client/src/services/command_registry.ts +++ b/apps/client/src/services/command_registry.ts @@ -247,14 +247,18 @@ class CommandRegistry { } else if (command.keyboardAction.scope === "text-detail") { this.executeWithTextDetail(command.commandName); } else { - appContext.triggerCommand(command.commandName); + appContext.triggerCommand(command.commandName, { + ntxId: appContext.tabManager.activeNtxId + }); } return; } // Fallback for commands without keyboard action reference if (command.commandName) { - appContext.triggerCommand(command.commandName); + appContext.triggerCommand(command.commandName, { + ntxId: appContext.tabManager.activeNtxId + }); return; } From baf341b312ee69adcbe5a5950e3f3ad8c05274ab Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:47:11 +0300 Subject: [PATCH 08/13] fix(command_palette): find in text not shown --- apps/server/src/services/keyboard_actions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts index 4ce5bc2a6..6a11242c4 100644 --- a/apps/server/src/services/keyboard_actions.ts +++ b/apps/server/src/services/keyboard_actions.ts @@ -759,7 +759,6 @@ function getDefaultKeyboardActions() { friendlyName: t("keyboard_action_names.find-in-text"), iconClass: "bx bx-search", defaultShortcuts: isElectron ? ["CommandOrControl+F"] : [], - isElectronOnly: true, description: t("keyboard_actions.find-in-text"), scope: "window" }, From 54e3ab5139853a1b8b9d95df04c4fb2602e8bb56 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:50:08 +0300 Subject: [PATCH 09/13] fix(command_palette): full screen not working on the browser --- apps/client/src/components/entrypoints.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/client/src/components/entrypoints.ts b/apps/client/src/components/entrypoints.ts index 0ad2c76ed..fd81879a7 100644 --- a/apps/client/src/components/entrypoints.ts +++ b/apps/client/src/components/entrypoints.ts @@ -113,7 +113,9 @@ export default class Entrypoints extends Component { if (win.isFullScreenable()) { win.setFullScreen(!win.isFullScreen()); } - } // outside of electron this is handled by the browser + } else { + document.documentElement.requestFullscreen(); + } } reloadFrontendAppCommand() { From a1ac276be5de1027559e6ac75856e4b667e355bf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 22:58:42 +0300 Subject: [PATCH 10/13] feat(web_view): hide attribute from attribute preview --- apps/client/src/services/attribute_renderer.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/client/src/services/attribute_renderer.ts b/apps/client/src/services/attribute_renderer.ts index bbe278056..9432eebc6 100644 --- a/apps/client/src/services/attribute_renderer.ts +++ b/apps/client/src/services/attribute_renderer.ts @@ -79,7 +79,19 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b return $container; } -const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName"]; +const HIDDEN_ATTRIBUTES = [ + "originalFileName", + "fileSize", + "template", + "inherit", + "cssClass", + "iconClass", + "pageSize", + "viewType", + "geolocation", + "docName", + "webViewSrc" +]; async function renderNormalAttributes(note: FNote) { const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes(); From acb16f751b298fc32e024c8f71c4d57e5d807541 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 23:03:02 +0300 Subject: [PATCH 11/13] style(next): improve border for image notes preview --- apps/client/src/stylesheets/theme-next/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/stylesheets/theme-next/base.css b/apps/client/src/stylesheets/theme-next/base.css index 2c4236676..b18e95a07 100644 --- a/apps/client/src/stylesheets/theme-next/base.css +++ b/apps/client/src/stylesheets/theme-next/base.css @@ -454,7 +454,7 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after { font-size: 0.8rem; } -.note-list-wrapper .note-book-card .note-book-content .rendered-content { +.note-list-wrapper .note-book-card .note-book-content:not(.type-image) .rendered-content { padding: 1rem; } From cda8fc71465fcccd71900adf87ac37ec6d7c47a5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 23:05:46 +0300 Subject: [PATCH 12/13] style(next): improve border for pdf notes preview --- apps/client/src/stylesheets/theme-next/base.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/client/src/stylesheets/theme-next/base.css b/apps/client/src/stylesheets/theme-next/base.css index b18e95a07..c992f7ecb 100644 --- a/apps/client/src/stylesheets/theme-next/base.css +++ b/apps/client/src/stylesheets/theme-next/base.css @@ -454,10 +454,15 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after { font-size: 0.8rem; } -.note-list-wrapper .note-book-card .note-book-content:not(.type-image) .rendered-content { +.note-list-wrapper .note-book-card .note-book-content .rendered-content { padding: 1rem; } +.note-list-wrapper .note-book-card .note-book-content.type-image .rendered-content, +.note-list-wrapper .note-book-card .note-book-content.type-pdf .rendered-content { + padding: 0; +} + .note-list-wrapper .note-book-card .note-book-content .rendered-content.text-with-ellipsis { padding: 1rem !important; } From 1dfe27d3df8d64a42126ec6380944aba50b3bfdf Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 30 Jul 2025 23:17:58 +0300 Subject: [PATCH 13/13] feat(web_view): open externally from note preview --- apps/client/src/services/content_renderer.ts | 28 +++++++++++++++++++ apps/client/src/stylesheets/style.css | 6 ++-- .../src/translations/en/translation.json | 3 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts index 40480e073..4f6d12c34 100644 --- a/apps/client/src/services/content_renderer.ts +++ b/apps/client/src/services/content_renderer.ts @@ -65,6 +65,9 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA $renderedContent.append($("
").append("
This note is protected and to access it you need to enter password.
").append("
").append($button)); } else if (entity instanceof FNote) { + $renderedContent + .css("display", "flex") + .css("flex-direction", "column"); $renderedContent.append( $("
") .css("display", "flex") @@ -72,8 +75,33 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA .css("align-items", "center") .css("height", "100%") .css("font-size", "500%") + .css("flex-grow", "1") .append($("").addClass(entity.getIcon())) ); + + if (entity.type === "webView" && entity.hasLabel("webViewSrc")) { + const $footer = $("