diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index a33d24283..68e102a65 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - hotfix paths-ignore: - "apps/website/**" pull_request: @@ -13,8 +14,24 @@ permissions: contents: read jobs: - main: - runs-on: ubuntu-latest + e2e: + strategy: + fail-fast: false + matrix: + include: + - name: linux-x64 + os: ubuntu-22.04 + arch: x64 + - name: linux-arm64 + os: ubuntu-24.04-arm + arch: arm64 + runs-on: ${{ matrix.os }} + name: E2E tests on ${{ matrix.name }} + env: + TRILIUM_DOCKER: 1 + TRILIUM_PORT: 8082 + TRILIUM_DATA_DIR: "${{ github.workspace }}/apps/server/spec/db" + TRILIUM_INTEGRATION_TEST: memory steps: - uses: actions/checkout@v5 with: @@ -29,9 +46,34 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - run: pnpm exec playwright install --with-deps - - run: pnpm --filter server-e2e e2e + - name: Install Playwright browsers + run: pnpm exec playwright install --with-deps + + - name: Build the server + uses: ./.github/actions/build-server + with: + os: linux + arch: ${{ matrix.arch }} + + - name: Unpack and start the server + run: | + version=$(node --eval "console.log(require('./package.json').version)") + file=$(find ./upload -name '*.tar.xz' -print -quit) + name=$(basename "$file" .tar.xz) + mkdir -p ./server-dist + tar -xvf "$file" -C ./server-dist + server_dir="./server-dist/TriliumNotes-Server-$version-linux-${{ matrix.arch }}" + if [ ! -d "$server_dir" ]; then + echo Missing dir. + exit 1 + fi + cd "$server_dir" + "./trilium.sh" & + sleep 10 + + - name: Server end-to-end tests + run: pnpm --filter server-e2e e2e - name: Upload test report if: failure() @@ -39,3 +81,7 @@ jobs: with: name: e2e report path: apps/server-e2e/test-output + + - name: Kill the server + if: always() + run: pkill -f trilium || true diff --git a/apps/build-docs/package.json b/apps/build-docs/package.json index 00196de82..d4a84dfd1 100644 --- a/apps/build-docs/package.json +++ b/apps/build-docs/package.json @@ -9,9 +9,9 @@ "keywords": [], "author": "Elian Doran ", "license": "AGPL-3.0-only", - "packageManager": "pnpm@10.20.0", + "packageManager": "pnpm@10.21.0", "devDependencies": { - "@redocly/cli": "2.11.0", + "@redocly/cli": "2.11.1", "archiver": "7.0.1", "fs-extra": "11.3.2", "react": "19.2.0", diff --git a/apps/client/package.json b/apps/client/package.json index a84bda4c3..2397c386c 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/client", - "version": "0.99.4", + "version": "0.99.5", "description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)", "private": true, "license": "AGPL-3.0-only", @@ -43,7 +43,7 @@ "draggabilly": "3.0.0", "force-graph": "1.51.0", "globals": "16.5.0", - "i18next": "25.6.1", + "i18next": "25.6.2", "i18next-http-backend": "3.0.2", "jquery": "3.7.1", "jquery.fancytree": "2.38.5", diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index 23924edcb..c73fe5a42 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -329,6 +329,7 @@ export type CommandMappings = { exportAsPdf: CommandData; openNoteExternally: CommandData; openNoteCustom: CommandData; + openNoteOnServer: CommandData; renderActiveNote: CommandData; unhoist: CommandData; reloadFrontendApp: CommandData; diff --git a/apps/client/src/components/root_command_executor.ts b/apps/client/src/components/root_command_executor.ts index a2d62eddd..4a1c987f7 100644 --- a/apps/client/src/components/root_command_executor.ts +++ b/apps/client/src/components/root_command_executor.ts @@ -66,6 +66,13 @@ export default class RootCommandExecutor extends Component { } } + openNoteOnServerCommand() { + const noteId = appContext.tabManager.getActiveContextNoteId(); + if (noteId) { + openService.openNoteOnServer(noteId); + } + } + enterProtectedSessionCommand() { protectedSessionService.enterProtectedSession(); } diff --git a/apps/client/src/services/open.ts b/apps/client/src/services/open.ts index 783fcee48..1e5513c86 100644 --- a/apps/client/src/services/open.ts +++ b/apps/client/src/services/open.ts @@ -1,4 +1,5 @@ import utils from "./utils.js"; +import options from "./options.js"; import server from "./server.js"; type ExecFunction = (command: string, cb: (err: string, stdout: string, stderror: string) => void) => void; @@ -171,6 +172,21 @@ function getHost() { return `${url.protocol}//${url.hostname}:${url.port}`; } +async function openNoteOnServer(noteId: string) { + // Get the sync server host from options + const syncServerHost = options.get("syncServerHost"); + + if (!syncServerHost) { + console.error("No sync server host configured"); + return; + } + + const url = new URL(`#root/${noteId}`, syncServerHost).toString(); + + // Use window.open to ensure link opens in external browser in Electron + window.open(url, '_blank', 'noopener,noreferrer'); +} + async function openDirectory(directory: string) { try { if (utils.isElectron()) { @@ -198,5 +214,6 @@ export default { openAttachmentExternally, openNoteCustom, openAttachmentCustom, + openNoteOnServer, openDirectory }; diff --git a/apps/client/src/services/syntax_highlight.ts b/apps/client/src/services/syntax_highlight.ts index 89dc7c94e..dd2bd48b4 100644 --- a/apps/client/src/services/syntax_highlight.ts +++ b/apps/client/src/services/syntax_highlight.ts @@ -24,7 +24,9 @@ export async function formatCodeBlocks($container: JQuery) { continue; } - applyCopyToClipboardButton($(codeBlock)); + if (glob.device !== "print") { + applyCopyToClipboardButton($(codeBlock)); + } if (syntaxHighlightingEnabled) { applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType); diff --git a/apps/client/src/stylesheets/theme-next/ribbon.css b/apps/client/src/stylesheets/theme-next/ribbon.css index dc1465d17..3718602cf 100644 --- a/apps/client/src/stylesheets/theme-next/ribbon.css +++ b/apps/client/src/stylesheets/theme-next/ribbon.css @@ -42,7 +42,7 @@ div.promoted-attributes-container { */ /* The property label */ -.note-info-widget-table th, +.note-info-item > span:first-child, .file-properties-widget .file-table th, .image-properties > div:first-child > span > strong { opacity: 0.65; @@ -50,7 +50,6 @@ div.promoted-attributes-container { vertical-align: top; } -.note-info-widget-table td, .file-properties-widget .file-table td { vertical-align: top; } diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 4d2be0ef9..ef9605d5e 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -682,6 +682,7 @@ "open_note_externally": "Open note externally", "open_note_externally_title": "File will be open in an external application and watched for changes. You'll then be able to upload the modified version back to Trilium.", "open_note_custom": "Open note custom", + "open_note_on_server": "Open note on server", "import_files": "Import files", "export_note": "Export note", "delete_note": "Delete note", diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json index 82f59d0a4..007ea176b 100644 --- a/apps/client/src/translations/es/translation.json +++ b/apps/client/src/translations/es/translation.json @@ -39,7 +39,10 @@ "help_on_tree_prefix": "Ayuda sobre el prefijo del árbol", "prefix": "Prefijo: ", "save": "Guardar", - "branch_prefix_saved": "Se ha guardado el prefijo de rama." + "branch_prefix_saved": "Se ha guardado el prefijo de rama.", + "edit_branch_prefix_multiple": "Editar prefijo de rama para {{count}} ramas", + "branch_prefix_saved_multiple": "El prefijo de rama se ha guardado para {{count}} ramas.", + "affected_branches": "Ramas afectadas ({{count}}):" }, "bulk_actions": { "bulk_actions": "Acciones en bloque", @@ -1107,7 +1110,8 @@ "title": "Ancho del contenido", "default_description": "Trilium limita de forma predeterminada el ancho máximo del contenido para mejorar la legibilidad de ventanas maximizadas en pantallas anchas.", "max_width_label": "Ancho máximo del contenido en píxeles", - "max_width_unit": "píxeles" + "max_width_unit": "píxeles", + "centerContent": "Mantener el contenido centrado" }, "native_title_bar": { "title": "Barra de título nativa (requiere reiniciar la aplicación)", @@ -2079,5 +2083,14 @@ }, "collections": { "rendering_error": "No se puede mostrar contenido debido a un error." + }, + "read-only-info": { + "read-only-note": "Actualmente, está viendo una nota de solo lectura.", + "auto-read-only-note": "Esta nota se muestra en modo de solo lectura para una carga más rápida.", + "auto-read-only-learn-more": "Para saber más", + "edit-note": "Editar nota" + }, + "calendar_view": { + "delete_note": "Eliminar nota..." } } diff --git a/apps/client/src/translations/it/translation.json b/apps/client/src/translations/it/translation.json index a06fe08f5..60a848e39 100644 --- a/apps/client/src/translations/it/translation.json +++ b/apps/client/src/translations/it/translation.json @@ -39,7 +39,10 @@ "help_on_tree_prefix": "Aiuto sui prefissi dell'Albero", "prefix": "Prefisso: ", "save": "Salva", - "branch_prefix_saved": "Il prefisso del ramo è stato salvato." + "branch_prefix_saved": "Il prefisso del ramo è stato salvato.", + "edit_branch_prefix_multiple": "Modifica prefisso ramo per {{count}} rami", + "branch_prefix_saved_multiple": "Il prefisso del ramo è stato salvato per {{count}} rami.", + "affected_branches": "Rami interessati ({{count}}):" }, "bulk_actions": { "bulk_actions": "Azioni massive", @@ -1499,7 +1502,7 @@ }, "protected_session": { "enter_password_instruction": "Per visualizzare la nota protetta è necessario inserire la password:", - "start_session_button": "Avvia sessione protetta invio", + "start_session_button": "Avvia sessione protetta", "started": "La sessione protetta è stata avviata.", "wrong_password": "Password errata.", "protecting-finished-successfully": "Protezione completata con successo.", @@ -1570,7 +1573,8 @@ "title": "Larghezza del contenuto", "default_description": "Per impostazione predefinita, Trilium limita la larghezza massima del contenuto per migliorare la leggibilità sugli schermi più grandi.", "max_width_label": "Larghezza massima del contenuto", - "max_width_unit": "pixel" + "max_width_unit": "pixel", + "centerContent": "Mantieni il contenuto centrato" }, "native_title_bar": { "title": "Barra del titolo nativa (richiede il riavvio dell'app)", @@ -2080,5 +2084,14 @@ }, "collections": { "rendering_error": "Impossibile mostrare il contenuto a causa di un errore." + }, + "read-only-info": { + "read-only-note": "Stai visualizzando una nota di sola lettura.", + "auto-read-only-note": "Questa nota viene visualizzata in modalità di sola lettura per un caricamento più rapido.", + "auto-read-only-learn-more": "Per saperne di più", + "edit-note": "Modifica nota" + }, + "calendar_view": { + "delete_note": "Eliminazione nota..." } } diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json index dc6bce560..4986ccdcf 100644 --- a/apps/client/src/translations/ja/translation.json +++ b/apps/client/src/translations/ja/translation.json @@ -836,7 +836,8 @@ "title": "コンテンツ幅", "default_description": "Triliumは、ワイドスクリーンで最大化された画面での可読性を向上させるために、デフォルトでコンテンツの最大幅を制限しています。", "max_width_label": "最大コンテンツ幅", - "max_width_unit": "ピクセル" + "max_width_unit": "ピクセル", + "centerContent": "コンテンツを中央に配置" }, "theme": { "title": "アプリのテーマ", @@ -1783,7 +1784,7 @@ }, "protected_session": { "enter_password_instruction": "保護されたノートを表示するにはパスワードを入力する必要があります:", - "start_session_button": "保護されたセッションを開始 enter", + "start_session_button": "保護されたセッションを開始", "started": "保護されたセッションが開始されました。", "wrong_password": "パスワードが間違っています。", "protecting-finished-successfully": "保護が正常に完了しました。", @@ -2082,5 +2083,11 @@ }, "calendar_view": { "delete_note": "ノートを削除..." + }, + "read-only-info": { + "read-only-note": "現在、読み取り専用のノートを表示しています。", + "auto-read-only-note": "このノートは読み込みを高速化するために読み取り専用モードで表示されています。", + "auto-read-only-learn-more": "さらに詳しく", + "edit-note": "ノートを編集" } } diff --git a/apps/client/src/widgets/collections/calendar/api.ts b/apps/client/src/widgets/collections/calendar/api.ts index 934edcb2e..eef391108 100644 --- a/apps/client/src/widgets/collections/calendar/api.ts +++ b/apps/client/src/widgets/collections/calendar/api.ts @@ -58,8 +58,6 @@ export async function changeEvent(note: FNote, { startDate, endDate, startTime, startAttribute = note.getAttributes("label").filter(attr => attr.name == "calendar:startTime").shift()?.value||"startTime"; endAttribute = note.getAttributes("label").filter(attr => attr.name == "calendar:endTime").shift()?.value||"endTime"; - if (startTime && endTime) { - setAttribute(note, "label", startAttribute, startTime); - setAttribute(note, "label", endAttribute, endTime); - } + setAttribute(note, "label", startAttribute, startTime); + setAttribute(note, "label", endAttribute, endTime); } diff --git a/apps/client/src/widgets/dialogs/popup_editor.ts b/apps/client/src/widgets/dialogs/popup_editor.ts index 63462f082..80f8f5915 100644 --- a/apps/client/src/widgets/dialogs/popup_editor.ts +++ b/apps/client/src/widgets/dialogs/popup_editor.ts @@ -7,6 +7,11 @@ import Container from "../containers/container.js"; const TPL = /*html*/`\