Merge remote-tracking branch 'origin/main' into feature/replace_hotkeys_library

This commit is contained in:
Elian Doran 2025-07-30 23:29:16 +03:00
commit 29b813fa3b
No known key found for this signature in database
11 changed files with 113 additions and 43 deletions

View File

@ -106,7 +106,9 @@ export default class Entrypoints extends Component {
if (win.isFullScreenable()) { if (win.isFullScreenable()) {
win.setFullScreen(!win.isFullScreen()); win.setFullScreen(!win.isFullScreen());
} }
} // outside of electron this is handled by the browser } else {
document.documentElement.requestFullscreen();
}
} }
reloadFrontendAppCommand() { reloadFrontendAppCommand() {

View File

@ -79,7 +79,19 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b
return $container; 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) { async function renderNormalAttributes(note: FNote) {
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes(); const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();

View File

@ -1,7 +1,8 @@
import { ActionKeyboardShortcut } from "@triliumnext/commons";
import appContext, { type CommandNames } from "../components/app_context.js"; import appContext, { type CommandNames } from "../components/app_context.js";
import type NoteTreeWidget from "../widgets/note_tree.js"; import type NoteTreeWidget from "../widgets/note_tree.js";
import { t, translationsInitializedPromise } from "./i18n.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"; import utils from "./utils.js";
export interface CommandDefinition { export interface CommandDefinition {
@ -15,7 +16,7 @@ export interface CommandDefinition {
aliases?: string[]; aliases?: string[];
source?: "manual" | "keyboard-action"; source?: "manual" | "keyboard-action";
/** Reference to the original keyboard action for scope checking. */ /** Reference to the original keyboard action for scope checking. */
keyboardAction?: Action; keyboardAction?: ActionKeyboardShortcut;
} }
class CommandRegistry { class CommandRegistry {
@ -105,7 +106,7 @@ class CommandRegistry {
} }
} }
private registerKeyboardActions(actions: Action[]) { private registerKeyboardActions(actions: ActionKeyboardShortcut[]) {
for (const action of actions) { for (const action of actions) {
// Skip actions that we've already manually registered // Skip actions that we've already manually registered
if (this.commands.has(action.actionName)) { if (this.commands.has(action.actionName)) {
@ -122,6 +123,11 @@ class CommandRegistry {
continue; continue;
} }
// Skip actions that should not appear in the command palette
if (action.ignoreFromCommandPalette) {
continue;
}
// Get the primary shortcut (first one in the list) // Get the primary shortcut (first one in the list)
const primaryShortcut = action.effectiveShortcuts?.[0]; const primaryShortcut = action.effectiveShortcuts?.[0];
@ -238,15 +244,21 @@ class CommandRegistry {
if (command.keyboardAction && command.commandName) { if (command.keyboardAction && command.commandName) {
if (command.keyboardAction.scope === "note-tree") { if (command.keyboardAction.scope === "note-tree") {
this.executeWithNoteTreeFocus(command.commandName); this.executeWithNoteTreeFocus(command.commandName);
} else if (command.keyboardAction.scope === "text-detail") {
this.executeWithTextDetail(command.commandName);
} else { } else {
appContext.triggerCommand(command.commandName); appContext.triggerCommand(command.commandName, {
ntxId: appContext.tabManager.activeNtxId
});
} }
return; return;
} }
// Fallback for commands without keyboard action reference // Fallback for commands without keyboard action reference
if (command.commandName) { if (command.commandName) {
appContext.triggerCommand(command.commandName); appContext.triggerCommand(command.commandName, {
ntxId: appContext.tabManager.activeNtxId
});
return; return;
} }
@ -260,7 +272,22 @@ class CommandRegistry {
} }
const treeComponent = appContext.getComponentByEl(tree) as NoteTreeWidget; 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
});
}
private async executeWithTextDetail(actionName: CommandNames) {
const typeWidget = await appContext.tabManager.getActiveContext()?.getTypeWidget();
if (!typeWidget) {
return;
}
typeWidget.triggerCommand(actionName, {
ntxId: appContext.tabManager.activeNtxId
});
} }
} }

View File

@ -65,6 +65,9 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA
$renderedContent.append($("<div>").append("<div>This note is protected and to access it you need to enter password.</div>").append("<br/>").append($button)); $renderedContent.append($("<div>").append("<div>This note is protected and to access it you need to enter password.</div>").append("<br/>").append($button));
} else if (entity instanceof FNote) { } else if (entity instanceof FNote) {
$renderedContent
.css("display", "flex")
.css("flex-direction", "column");
$renderedContent.append( $renderedContent.append(
$("<div>") $("<div>")
.css("display", "flex") .css("display", "flex")
@ -72,8 +75,33 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA
.css("align-items", "center") .css("align-items", "center")
.css("height", "100%") .css("height", "100%")
.css("font-size", "500%") .css("font-size", "500%")
.css("flex-grow", "1")
.append($("<span>").addClass(entity.getIcon())) .append($("<span>").addClass(entity.getIcon()))
); );
if (entity.type === "webView" && entity.hasLabel("webViewSrc")) {
const $footer = $("<footer>")
.addClass("webview-footer");
const $openButton = $(`
<button class="file-open btn btn-primary" type="button">
<span class="bx bx-link-external"></span>
${t("content_renderer.open_externally")}
</button>
`)
.appendTo($footer)
.on("click", () => {
const webViewSrc = entity.getLabelValue("webViewSrc");
if (webViewSrc) {
if (utils.isElectron()) {
const electron = utils.dynamicRequire("electron");
electron.shell.openExternal(webViewSrc);
} else {
window.open(webViewSrc, '_blank', 'noopener,noreferrer');
}
}
});
$footer.appendTo($renderedContent);
}
} }
if (entity instanceof FNote) { if (entity instanceof FNote) {

View File

@ -2,25 +2,15 @@ import server from "./server.js";
import appContext, { type CommandNames } from "../components/app_context.js"; import appContext, { type CommandNames } from "../components/app_context.js";
import shortcutService from "./shortcuts.js"; import shortcutService from "./shortcuts.js";
import type Component from "../components/component.js"; import type Component from "../components/component.js";
import type { ActionKeyboardShortcut } from "@triliumnext/commons";
const keyboardActionRepo: Record<string, Action> = {}; const keyboardActionRepo: Record<string, ActionKeyboardShortcut> = {};
// TODO: Deduplicate with server. const keyboardActionsLoaded = server.get<ActionKeyboardShortcut[]>("keyboard-actions").then((actions) => {
export interface Action {
actionName: CommandNames;
effectiveShortcuts: string[];
scope: string;
friendlyName: string;
description?: string;
iconClass?: string;
isElectronOnly?: boolean;
}
const keyboardActionsLoaded = server.get<Action[]>("keyboard-actions").then((actions) => {
actions = actions.filter((a) => !!a.actionName); // filter out separators actions = actions.filter((a) => !!a.actionName); // filter out separators
for (const action of actions) { 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; keyboardActionRepo[action.actionName] = action;
} }
@ -42,7 +32,7 @@ async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, c
const actions = await getActionsForScope(scope); const actions = await getActionsForScope(scope);
for (const action of actions) { 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 })); shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
} }
} }
@ -50,7 +40,7 @@ async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, c
getActionsForScope("window").then((actions) => { getActionsForScope("window").then((actions) => {
for (const action of 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 })); shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
} }
} }
@ -84,7 +74,7 @@ function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
const action = await getAction(actionName, true); const action = await getAction(actionName, true);
if (action) { if (action) {
const keyboardActions = action.effectiveShortcuts.join(", "); const keyboardActions = (action.effectiveShortcuts ?? []).join(", ");
if (keyboardActions || $(el).text() !== "not set") { if (keyboardActions || $(el).text() !== "not set") {
$(el).text(keyboardActions); $(el).text(keyboardActions);
@ -103,7 +93,7 @@ function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
if (action) { if (action) {
const title = $(el).attr("title"); const title = $(el).attr("title");
const shortcuts = action.effectiveShortcuts.join(", "); const shortcuts = (action.effectiveShortcuts ?? []).join(", ");
if (title?.includes(shortcuts)) { if (title?.includes(shortcuts)) {
return; return;

View File

@ -1937,12 +1937,14 @@ body.zen .note-title-widget input {
/* Content renderer */ /* Content renderer */
footer.file-footer { footer.file-footer,
footer.webview-footer {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
footer.file-footer button { footer.file-footer button,
footer.webview-footer button {
margin: 5px; margin: 5px;
} }

View File

@ -458,6 +458,11 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
padding: 1rem; 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 { .note-list-wrapper .note-book-card .note-book-content .rendered-content.text-with-ellipsis {
padding: 1rem !important; padding: 1rem !important;
} }

View File

@ -2002,5 +2002,8 @@
"search_history_description": "View previous searches", "search_history_description": "View previous searches",
"configure_launch_bar_title": "Configure Launch Bar", "configure_launch_bar_title": "Configure Launch Bar",
"configure_launch_bar_description": "Open the launch bar configuration, to add or remove items." "configure_launch_bar_description": "Open the launch bar configuration, to add or remove items."
},
"content_renderer": {
"open_externally": "Open externally"
} }
} }

View File

@ -88,7 +88,9 @@ export default class SortChildNotesDialog extends BasicWidget {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$form = this.$widget.find(".sort-child-notes-form"); 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 sortBy = this.$form.find("input[name='sort-by']:checked").val();
const sortDirection = this.$form.find("input[name='sort-direction']: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"); const foldersFirst = this.$form.find("input[name='sort-folders-first']").is(":checked");

View File

@ -36,18 +36,18 @@ function getDefaultKeyboardActions() {
{ {
actionName: "jumpToNote", actionName: "jumpToNote",
friendlyName: t("keyboard_action_names.jump-to-note"), friendlyName: t("keyboard_action_names.jump-to-note"),
iconClass: "bx bx-send",
defaultShortcuts: ["CommandOrControl+J"], defaultShortcuts: ["CommandOrControl+J"],
description: t("keyboard_actions.open-jump-to-note-dialog"), description: t("keyboard_actions.open-jump-to-note-dialog"),
scope: "window" scope: "window",
ignoreFromCommandPalette: true
}, },
{ {
actionName: "commandPalette", actionName: "commandPalette",
friendlyName: t("keyboard_action_names.command-palette"), friendlyName: t("keyboard_action_names.command-palette"),
iconClass: "bx bx-terminal",
defaultShortcuts: ["CommandOrControl+Shift+J"], defaultShortcuts: ["CommandOrControl+Shift+J"],
description: t("keyboard_actions.open-command-palette"), description: t("keyboard_actions.open-command-palette"),
scope: "window" scope: "window",
ignoreFromCommandPalette: true
}, },
{ {
actionName: "scrollToActiveNote", actionName: "scrollToActiveNote",
@ -245,18 +245,18 @@ function getDefaultKeyboardActions() {
{ {
actionName: "addNoteAboveToSelection", actionName: "addNoteAboveToSelection",
friendlyName: t("keyboard_action_names.add-note-above-to-selection"), friendlyName: t("keyboard_action_names.add-note-above-to-selection"),
iconClass: "bx bx-chevron-up-square",
defaultShortcuts: ["Shift+Up"], defaultShortcuts: ["Shift+Up"],
description: t("keyboard_actions.add-note-above-to-the-selection"), description: t("keyboard_actions.add-note-above-to-the-selection"),
scope: "note-tree" scope: "note-tree",
ignoreFromCommandPalette: true
}, },
{ {
actionName: "addNoteBelowToSelection", actionName: "addNoteBelowToSelection",
friendlyName: t("keyboard_action_names.add-note-below-to-selection"), friendlyName: t("keyboard_action_names.add-note-below-to-selection"),
iconClass: "bx bx-chevron-down-square",
defaultShortcuts: ["Shift+Down"], defaultShortcuts: ["Shift+Down"],
description: t("keyboard_actions.add-note-below-to-selection"), description: t("keyboard_actions.add-note-below-to-selection"),
scope: "note-tree" scope: "note-tree",
ignoreFromCommandPalette: true
}, },
{ {
actionName: "duplicateSubtree", actionName: "duplicateSubtree",
@ -275,7 +275,6 @@ function getDefaultKeyboardActions() {
friendlyName: t("keyboard_action_names.open-new-tab"), friendlyName: t("keyboard_action_names.open-new-tab"),
iconClass: "bx bx-plus", iconClass: "bx bx-plus",
defaultShortcuts: isElectron ? ["CommandOrControl+T"] : [], defaultShortcuts: isElectron ? ["CommandOrControl+T"] : [],
isElectronOnly: true,
description: t("keyboard_actions.open-new-tab"), description: t("keyboard_actions.open-new-tab"),
scope: "window" scope: "window"
}, },
@ -284,7 +283,6 @@ function getDefaultKeyboardActions() {
friendlyName: t("keyboard_action_names.close-active-tab"), friendlyName: t("keyboard_action_names.close-active-tab"),
iconClass: "bx bx-minus", iconClass: "bx bx-minus",
defaultShortcuts: isElectron ? ["CommandOrControl+W"] : [], defaultShortcuts: isElectron ? ["CommandOrControl+W"] : [],
isElectronOnly: true,
description: t("keyboard_actions.close-active-tab"), description: t("keyboard_actions.close-active-tab"),
scope: "window" scope: "window"
}, },
@ -302,7 +300,6 @@ function getDefaultKeyboardActions() {
friendlyName: t("keyboard_action_names.activate-next-tab"), friendlyName: t("keyboard_action_names.activate-next-tab"),
iconClass: "bx bx-skip-next", iconClass: "bx bx-skip-next",
defaultShortcuts: isElectron ? ["CommandOrControl+Tab", "CommandOrControl+PageDown"] : [], defaultShortcuts: isElectron ? ["CommandOrControl+Tab", "CommandOrControl+PageDown"] : [],
isElectronOnly: true,
description: t("keyboard_actions.activate-next-tab"), description: t("keyboard_actions.activate-next-tab"),
scope: "window" scope: "window"
}, },
@ -311,7 +308,6 @@ function getDefaultKeyboardActions() {
friendlyName: t("keyboard_action_names.activate-previous-tab"), friendlyName: t("keyboard_action_names.activate-previous-tab"),
iconClass: "bx bx-skip-previous", iconClass: "bx bx-skip-previous",
defaultShortcuts: isElectron ? ["CommandOrControl+Shift+Tab", "CommandOrControl+PageUp"] : [], defaultShortcuts: isElectron ? ["CommandOrControl+Shift+Tab", "CommandOrControl+PageUp"] : [],
isElectronOnly: true,
description: t("keyboard_actions.activate-previous-tab"), description: t("keyboard_actions.activate-previous-tab"),
scope: "window" scope: "window"
}, },
@ -763,7 +759,6 @@ function getDefaultKeyboardActions() {
friendlyName: t("keyboard_action_names.find-in-text"), friendlyName: t("keyboard_action_names.find-in-text"),
iconClass: "bx bx-search", iconClass: "bx bx-search",
defaultShortcuts: isElectron ? ["CommandOrControl+F"] : [], defaultShortcuts: isElectron ? ["CommandOrControl+F"] : [],
isElectronOnly: true,
description: t("keyboard_actions.find-in-text"), description: t("keyboard_actions.find-in-text"),
scope: "window" scope: "window"
}, },

View File

@ -112,7 +112,7 @@ export interface ActionKeyboardShortcut {
* An icon describing the action. * An icon describing the action.
* This is currently only used in the command palette. * 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, * 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. * 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. * This is used to hide actions that are not available in the web version.
*/ */
isElectronOnly?: boolean; isElectronOnly?: boolean;
/**
* If set to true, the action will not be shown in the command palette.
*/
ignoreFromCommandPalette?: boolean;
} }
export type KeyboardShortcut = ActionKeyboardShortcut | KeyboardShortcutSeparator; export type KeyboardShortcut = ActionKeyboardShortcut | KeyboardShortcutSeparator;