mirror of
https://github.com/zadam/trilium.git
synced 2025-11-11 14:20:36 +08:00
Merge branch 'main' into renovate/major-vitest-monorepo
This commit is contained in:
commit
ae5c898537
10 changed files with 60 additions and 21 deletions
|
|
@ -270,6 +270,7 @@ export type CommandMappings = {
|
||||||
closeThisNoteSplit: CommandData;
|
closeThisNoteSplit: CommandData;
|
||||||
moveThisNoteSplit: CommandData & { isMovingLeft: boolean };
|
moveThisNoteSplit: CommandData & { isMovingLeft: boolean };
|
||||||
jumpToNote: CommandData;
|
jumpToNote: CommandData;
|
||||||
|
openTodayNote: CommandData;
|
||||||
commandPalette: CommandData;
|
commandPalette: CommandData;
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,16 @@ export default class Entrypoints extends Component {
|
||||||
this.openInWindowCommand({ notePath: "", hoistedNoteId: "root" });
|
this.openInWindowCommand({ notePath: "", hoistedNoteId: "root" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openTodayNoteCommand() {
|
||||||
|
const todayNote = await dateNoteService.getTodayNote();
|
||||||
|
if (!todayNote) {
|
||||||
|
console.warn("Missing today note.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await appContext.tabManager.openInSameTab(todayNote.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
async runActiveNoteCommand() {
|
async runActiveNoteCommand() {
|
||||||
const noteContext = appContext.tabManager.getActiveContext();
|
const noteContext = appContext.tabManager.getActiveContext();
|
||||||
if (!noteContext) {
|
if (!noteContext) {
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ export default class FNote {
|
||||||
return notePaths;
|
return notePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSortedNotePathRecords(hoistedNoteId = "root"): NotePathRecord[] {
|
getSortedNotePathRecords(hoistedNoteId = "root", activeNotePath: string | null = null): NotePathRecord[] {
|
||||||
const isHoistedRoot = hoistedNoteId === "root";
|
const isHoistedRoot = hoistedNoteId === "root";
|
||||||
|
|
||||||
const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({
|
const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({
|
||||||
|
|
@ -428,7 +428,23 @@ export default class FNote {
|
||||||
isHidden: path.includes("_hidden")
|
isHidden: path.includes("_hidden")
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Calculate the length of the prefix match between two arrays
|
||||||
|
const prefixMatchLength = (path: string[], target: string[]) => {
|
||||||
|
const diffIndex = path.findIndex((seg, i) => seg !== target[i]);
|
||||||
|
return diffIndex === -1 ? Math.min(path.length, target.length) : diffIndex;
|
||||||
|
};
|
||||||
|
|
||||||
notePaths.sort((a, b) => {
|
notePaths.sort((a, b) => {
|
||||||
|
if (activeNotePath) {
|
||||||
|
const activeSegments = activeNotePath.split('/');
|
||||||
|
const aOverlap = prefixMatchLength(a.notePath, activeSegments);
|
||||||
|
const bOverlap = prefixMatchLength(b.notePath, activeSegments);
|
||||||
|
// Paths with more matching prefix segments are prioritized
|
||||||
|
// when the match count is equal, other criteria are used for sorting
|
||||||
|
if (bOverlap !== aOverlap) {
|
||||||
|
return bOverlap - aOverlap;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
|
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
|
||||||
return a.isInHoistedSubTree ? -1 : 1;
|
return a.isInHoistedSubTree ? -1 : 1;
|
||||||
} else if (a.isArchived !== b.isArchived) {
|
} else if (a.isArchived !== b.isArchived) {
|
||||||
|
|
@ -449,10 +465,11 @@ export default class FNote {
|
||||||
* Returns the note path considered to be the "best"
|
* Returns the note path considered to be the "best"
|
||||||
*
|
*
|
||||||
* @param {string} [hoistedNoteId='root']
|
* @param {string} [hoistedNoteId='root']
|
||||||
|
* @param {string|null} [activeNotePath=null]
|
||||||
* @return {string[]} array of noteIds constituting the particular note path
|
* @return {string[]} array of noteIds constituting the particular note path
|
||||||
*/
|
*/
|
||||||
getBestNotePath(hoistedNoteId = "root") {
|
getBestNotePath(hoistedNoteId = "root", activeNotePath: string | null = null) {
|
||||||
return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
|
return this.getSortedNotePathRecords(hoistedNoteId, activeNotePath)[0]?.notePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,12 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = notePath.split("/").reverse();
|
const path = notePath.split("/").reverse();
|
||||||
|
|
||||||
if (!path.includes("root")) {
|
|
||||||
path.push("root");
|
|
||||||
}
|
|
||||||
|
|
||||||
const effectivePathSegments: string[] = [];
|
const effectivePathSegments: string[] = [];
|
||||||
let childNoteId: string | null = null;
|
let childNoteId: string | null = null;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
while (true) {
|
for (let i = 0; i < path.length; i++) {
|
||||||
if (i >= path.length) {
|
const parentNoteId = path[i];
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentNoteId = path[i++];
|
|
||||||
|
|
||||||
if (childNoteId !== null) {
|
if (childNoteId !== null) {
|
||||||
const child = await froca.getNote(childNoteId, !logErrors);
|
const child = await froca.getNote(childNoteId, !logErrors);
|
||||||
|
|
@ -65,7 +56,7 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parents.some((p) => p.noteId === parentNoteId)) {
|
if (!parents.some(p => p.noteId === parentNoteId) || (i === path.length - 1 && parentNoteId !== 'root')) {
|
||||||
if (logErrors) {
|
if (logErrors) {
|
||||||
const parent = froca.getNoteFromCache(parentNoteId);
|
const parent = froca.getNoteFromCache(parentNoteId);
|
||||||
|
|
||||||
|
|
@ -77,7 +68,8 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bestNotePath = child.getBestNotePath(hoistedNoteId);
|
const activeNotePath = appContext.tabManager.getActiveContextNotePath();
|
||||||
|
const bestNotePath = child.getBestNotePath(hoistedNoteId, activeNotePath);
|
||||||
|
|
||||||
if (bestNotePath) {
|
if (bestNotePath) {
|
||||||
const pathToRoot = bestNotePath.reverse().slice(1);
|
const pathToRoot = bestNotePath.reverse().slice(1);
|
||||||
|
|
@ -108,7 +100,9 @@ async function resolveNotePathToSegments(notePath: string, hoistedNoteId = "root
|
||||||
if (!note) {
|
if (!note) {
|
||||||
throw new Error(`Unable to find note: ${notePath}.`);
|
throw new Error(`Unable to find note: ${notePath}.`);
|
||||||
}
|
}
|
||||||
const bestNotePath = note.getBestNotePath(hoistedNoteId);
|
|
||||||
|
const activeNotePath = appContext.tabManager.getActiveContextNotePath();
|
||||||
|
const bestNotePath = note.getBestNotePath(hoistedNoteId, activeNotePath);
|
||||||
|
|
||||||
if (!bestNotePath) {
|
if (!bestNotePath) {
|
||||||
throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);
|
throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,11 @@ export function reloadFrontendApp(reason?: string) {
|
||||||
logInfo(`Frontend app reload: ${reason}`);
|
logInfo(`Frontend app reload: ${reason}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isElectron()) {
|
||||||
|
dynamicRequire("@electron/remote").BrowserWindow.getFocusedWindow()?.reload();
|
||||||
|
} else {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function restartDesktopApp() {
|
export function restartDesktopApp() {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import type { Application } from "express";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { type SQLiteSessionStore } from "./session_parser.js";
|
import { type SQLiteSessionStore } from "./session_parser.js";
|
||||||
import { SessionData } from "express-session";
|
import { SessionData } from "express-session";
|
||||||
|
import cls from "../services/cls.js";
|
||||||
|
|
||||||
let app: Application;
|
let app: Application;
|
||||||
let sessionStore: SQLiteSessionStore;
|
let sessionStore: SQLiteSessionStore;
|
||||||
|
|
@ -106,7 +107,7 @@ describe("Login Route test", () => {
|
||||||
expect(expiry).toBeTruthy();
|
expect(expiry).toBeTruthy();
|
||||||
|
|
||||||
vi.setSystemTime(expiry!);
|
vi.setSystemTime(expiry!);
|
||||||
vi.advanceTimersByTime(CLEAN_UP_INTERVAL);
|
cls.init(() => vi.advanceTimersByTime(CLEAN_UP_INTERVAL));
|
||||||
({ session } = await getSessionFromCookie(setCookieHeader));
|
({ session } = await getSessionFromCookie(setCookieHeader));
|
||||||
expect(session).toBeFalsy();
|
expect(session).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,14 @@ function getDefaultKeyboardActions() {
|
||||||
scope: "window",
|
scope: "window",
|
||||||
ignoreFromCommandPalette: true
|
ignoreFromCommandPalette: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
actionName: "openTodayNote",
|
||||||
|
friendlyName: t("hidden-subtree.open-today-journal-note-title"),
|
||||||
|
iconClass: "bx bx-calendar",
|
||||||
|
defaultShortcuts: [],
|
||||||
|
description: t("hidden-subtree.open-today-journal-note-title"),
|
||||||
|
scope: "window"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
actionName: "commandPalette",
|
actionName: "commandPalette",
|
||||||
friendlyName: t("keyboard_action_names.command-palette"),
|
friendlyName: t("keyboard_action_names.command-palette"),
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
"screenshot_alt": "Captura de ecrã da aplicação Trilium Notes para computador"
|
"screenshot_alt": "Captura de ecrã da aplicação Trilium Notes para computador"
|
||||||
},
|
},
|
||||||
"organization_benefits": {
|
"organization_benefits": {
|
||||||
"title": "Organização"
|
"title": "Organização",
|
||||||
|
"note_structure_description": "As notas podem ser organizadas de forma hierárquica. Não há necessidade de pastas, pois cada nota pode conter sub notas. Uma única nota pode ser adicionada em vários locais da hierarquia.",
|
||||||
|
"attributes_description": "Utiliza relações entre notas ou adiciona etiquetas para uma categorização fácil. Usa atributos promovidos para inserir informação estruturada, que pode ser utilizada em tabelas ou quadros.",
|
||||||
|
"hoisting_description": "Separa facilmente as tuas notas pessoais e de trabalho agrupando-as num espaço de trabalho, que focaliza a árvore de notas para mostrar apenas um conjunto específico de notas."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
docs/README-pt.md
vendored
2
docs/README-pt.md
vendored
|
|
@ -54,7 +54,7 @@ A nossa documentação está disponível em múltiplos formatos:
|
||||||
- **GitHub**: Navigate through the [User
|
- **GitHub**: Navigate through the [User
|
||||||
Guide](./docs/User%20Guide/User%20Guide/) in this repository
|
Guide](./docs/User%20Guide/User%20Guide/) in this repository
|
||||||
|
|
||||||
### Quick Links
|
### Links rápidos
|
||||||
- [Getting Started Guide](https://docs.triliumnotes.org/)
|
- [Getting Started Guide](https://docs.triliumnotes.org/)
|
||||||
- [Installation
|
- [Installation
|
||||||
Instructions](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
|
Instructions](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ const enum KeyboardActionNamesEnum {
|
||||||
activateNextTab,
|
activateNextTab,
|
||||||
activatePreviousTab,
|
activatePreviousTab,
|
||||||
openNewWindow,
|
openNewWindow,
|
||||||
|
openTodayNote,
|
||||||
toggleTray,
|
toggleTray,
|
||||||
toggleZenMode,
|
toggleZenMode,
|
||||||
firstTab,
|
firstTab,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue