Merge branch 'develop' into ai-llm-integration
| @@ -34,6 +34,7 @@ npm-debug.log | ||||
|  | ||||
| # exceptions | ||||
| !/bin/copy-dist.ts | ||||
| !/bin/electron-forge/sign-windows.cjs | ||||
|  | ||||
| # temporary exception to make copy-dist inside Docker build not fail | ||||
| # TriliumNextTODO: make copy-dist *not* requiring to copy this file for builds other than electron-forge | ||||
|   | ||||
							
								
								
									
										82
									
								
								.github/actions/build-electron/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -8,8 +8,11 @@ inputs: | ||||
|   arch: | ||||
|     description: "The architecture to build for: x64, arm64" | ||||
|     required: true | ||||
|   extension: | ||||
|     description: "Platform specific extensions to copy in the output: dmg, deb, rpm, exe, zip" | ||||
|   shell: | ||||
|     description: "Which shell to use" | ||||
|     required: true | ||||
|   forge_platform: | ||||
|     description: "The --platform to pass to Electron Forge" | ||||
|     required: true | ||||
|  | ||||
| runs: | ||||
| @@ -38,21 +41,21 @@ runs: | ||||
|  | ||||
|     - name: Verify certificates | ||||
|       if: inputs.os == 'macos' | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: | | ||||
|         echo "Available signing identities:" | ||||
|         security find-identity -v -p codesigning build.keychain | ||||
|  | ||||
|     - name: Set up Python and other macOS dependencies | ||||
|       if: ${{ inputs.os == 'macos' }} | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: | | ||||
|         brew install python-setuptools | ||||
|         brew install create-dmg | ||||
|  | ||||
|     - name: Install dependencies for RPM and Flatpak package building | ||||
|       if: ${{ inputs.os == 'linux' }} | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: | | ||||
|         sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils | ||||
|         flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo | ||||
| @@ -62,39 +65,32 @@ runs: | ||||
|  | ||||
|     # Build setup | ||||
|     - name: Install dependencies | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: npm ci | ||||
|  | ||||
|     - name: Update build info | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: npm run chore:update-build-info | ||||
|  | ||||
|     # Critical debugging configuration | ||||
|     - name: Run electron-forge build with enhanced logging | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       env: | ||||
|         # Pass through required environment variables for signing and notarization | ||||
|         APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }} | ||||
|         APPLE_ID: ${{ env.APPLE_ID }} | ||||
|         APPLE_ID_PASSWORD: ${{ env.APPLE_ID_PASSWORD }} | ||||
|         WINDOWS_SIGN_EXECUTABLE: ${{ env.WINDOWS_SIGN_EXECUTABLE }} | ||||
|         TRILIUM_ARTIFACT_NAME_HINT: TriliumNextNotes ${{ inputs.os }} ${{ inputs.arch }} | ||||
|       run: | | ||||
|         # Map OS names to Electron Forge platform names | ||||
|         if [ "${{ inputs.os }}" = "macos" ]; then | ||||
|           PLATFORM="darwin" | ||||
|         elif [ "${{ inputs.os }}" = "windows" ]; then | ||||
|           PLATFORM="win32" | ||||
|         else | ||||
|           PLATFORM="${{ inputs.os }}" | ||||
|         fi | ||||
|  | ||||
|         npm run electron-forge:make -- \ | ||||
|           --arch=${{ inputs.arch }} \ | ||||
|           --platform=$PLATFORM | ||||
|           --platform=${{ inputs.forge_platform }} | ||||
|  | ||||
|     # Add DMG signing step | ||||
|     - name: Sign DMG | ||||
|       if: inputs.os == 'macos' | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: | | ||||
|         echo "Signing DMG file..." | ||||
|         dmg_file=$(find ./dist -name "*.dmg" -print -quit) | ||||
| @@ -119,7 +115,7 @@ runs: | ||||
|  | ||||
|     - name: Verify code signing | ||||
|       if: inputs.os == 'macos' | ||||
|       shell: bash | ||||
|       shell: ${{ inputs.shell }} | ||||
|       run: | | ||||
|         echo "Verifying code signing for all artifacts..." | ||||
|  | ||||
| @@ -165,49 +161,3 @@ runs: | ||||
|           echo "Found ZIP: $zip_file" | ||||
|           echo "Note: ZIP files are not code signed, but their contents should be" | ||||
|         fi | ||||
|  | ||||
|     - name: Prepare artifacts | ||||
|       shell: bash | ||||
|       run: | | ||||
|         mkdir -p upload | ||||
|  | ||||
|         if [ "${{ inputs.os }}" = "macos" ]; then | ||||
|           # For macOS, we need to look in specific directories based on the maker | ||||
|           echo "Collecting macOS artifacts..." | ||||
|  | ||||
|           # Look for DMG files recursively | ||||
|           echo "Looking for DMG files..." | ||||
|           dmg_file=$(find ./dist -name "*.dmg" -print -quit) | ||||
|           if [ -n "$dmg_file" ]; then | ||||
|             echo "Found DMG: $dmg_file" | ||||
|             cp "$dmg_file" "upload/TriliumNextNotes-${{ github.ref_name }}-macos-${{ inputs.arch }}.dmg" | ||||
|           else | ||||
|             echo "Warning: No DMG file found" | ||||
|           fi | ||||
|  | ||||
|           # Look for ZIP files recursively | ||||
|           echo "Looking for ZIP files..." | ||||
|           zip_file=$(find ./dist -name "*.zip" -print -quit) | ||||
|           if [ -n "$zip_file" ]; then | ||||
|             echo "Found ZIP: $zip_file" | ||||
|             cp "$zip_file" "upload/TriliumNextNotes-${{ github.ref_name }}-macos-${{ inputs.arch }}.zip" | ||||
|           else | ||||
|             echo "Warning: No ZIP file found" | ||||
|           fi | ||||
|         else | ||||
|           # For other platforms, use the existing logic but with better error handling | ||||
|           echo "Collecting artifacts for ${{ inputs.os }}..." | ||||
|           for ext in ${{ inputs.extension }}; do | ||||
|             echo "Looking for .$ext files..." | ||||
|             file=$(find ./dist -name "*.$ext" -print -quit) | ||||
|             if [ -n "$file" ]; then | ||||
|               echo "Found $file for extension $ext" | ||||
|               cp "$file" "upload/TriliumNextNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.$ext" | ||||
|             else | ||||
|               echo "Warning: No file found with extension .$ext" | ||||
|             fi | ||||
|           done | ||||
|         fi | ||||
|  | ||||
|         echo "Final contents of upload directory:" | ||||
|         ls -la upload/ | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/actions/build-server/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -11,7 +11,7 @@ runs: | ||||
|     - name: Set up node & dependencies | ||||
|       uses: actions/setup-node@v4 | ||||
|       with: | ||||
|         node-version: 20 | ||||
|         node-version: 22 | ||||
|         cache: "npm" | ||||
|     - name: Install dependencies | ||||
|       shell: bash | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/dev.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -26,7 +26,7 @@ jobs: | ||||
|       - name: Set up node & dependencies | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|           node-version: 22 | ||||
|           cache: "npm" | ||||
|  | ||||
|       - run: npm ci | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/workflows/main-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -45,7 +45,7 @@ jobs: | ||||
|       - name: Set up node & dependencies | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|           node-version: 22 | ||||
|           cache: "npm" | ||||
|  | ||||
|       - name: Install npm dependencies | ||||
|   | ||||
							
								
								
									
										93
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,93 +0,0 @@ | ||||
| name: Main | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - "feature/update**" | ||||
|       - "feature/server_esm**" | ||||
|     paths-ignore: | ||||
|       - "docs/**" | ||||
|       - ".github/workflows/main-docker.yml" | ||||
|   workflow_dispatch: | ||||
|  | ||||
| concurrency: | ||||
|   group: ${{ github.workflow }}-${{ github.ref }} | ||||
|   cancel-in-progress: true | ||||
|  | ||||
| jobs: | ||||
|   make-electron: | ||||
|     name: Make Electron | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         arch: [x64, arm64] | ||||
|         os: | ||||
|           - name: macos | ||||
|             image: macos-latest | ||||
|             extension: [dmg, zip] | ||||
|           - name: linux | ||||
|             image: ubuntu-latest | ||||
|             extension: [deb, rpm, zip, flatpak] | ||||
|           - name: windows | ||||
|             image: windows-latest | ||||
|             extension: [exe, zip] | ||||
|     runs-on: ${{ matrix.os.image }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Set up node & dependencies | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|       - name: Run the build | ||||
|         uses: ./.github/actions/build-electron | ||||
|         with: | ||||
|           os: ${{ matrix.os.name }} | ||||
|           arch: ${{ matrix.arch }} | ||||
|           extension: ${{ matrix.os.extension }} | ||||
|         env: | ||||
|           APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} | ||||
|           APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} | ||||
|           APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }} | ||||
|           APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }} | ||||
|           APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | ||||
|           APPLE_ID: ${{ secrets.APPLE_ID }} | ||||
|           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | ||||
|  | ||||
|       # Clean up keychain after build | ||||
|       - name: Clean up keychain | ||||
|         if: matrix.os.name == 'macos' && always() | ||||
|         run: | | ||||
|           security delete-keychain build.keychain | ||||
|  | ||||
|       - name: Publish artifacts | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}.zip | ||||
|           path: upload/*.zip | ||||
|       - name: Publish installer artifacts | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}.${{matrix.os.extension}} | ||||
|           path: upload/*.${{ matrix.os.extension }} | ||||
|  | ||||
|   build_linux_server: | ||||
|     name: Build Linux Server | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         arch: [x64, arm64] | ||||
|         include: | ||||
|           - arch: x64 | ||||
|             runs-on: ubuntu-latest | ||||
|           - arch: arm64 | ||||
|             runs-on: ubuntu-24.04-arm | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Run the build | ||||
|         uses: ./.github/actions/build-server | ||||
|         with: | ||||
|           arch: ${{ matrix.arch }} | ||||
|       - uses: actions/upload-artifact@v4 | ||||
|         with: | ||||
|           name: TriliumNextNotes linux server ${{ matrix.arch }} | ||||
|           path: upload/TriliumNextNotes-linux-${{ matrix.arch }}-${{ github.ref_name }}.tar.xz | ||||
							
								
								
									
										57
									
								
								.github/workflows/nightly.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -5,6 +5,9 @@ on: | ||||
|     - cron: "0 2 * * *" # run at 2 AM UTC | ||||
|   # This can be used to allow manually triggering nightlies from the web interface | ||||
|   workflow_dispatch: | ||||
|   push: | ||||
|     branches: | ||||
|       - renovate/electron-forge* | ||||
|   pull_request: | ||||
|     paths: | ||||
|       - .github/actions/build-electron/* | ||||
| @@ -23,22 +26,24 @@ jobs: | ||||
|         os: | ||||
|           - name: macos | ||||
|             image: macos-latest | ||||
|             extension: [dmg, zip] | ||||
|             shell: bash | ||||
|             forge_platform: darwin | ||||
|           - name: linux | ||||
|             image: ubuntu-latest | ||||
|             extension: [deb, rpm, zip, flatpak] | ||||
|             shell: bash | ||||
|             forge_platform: linux | ||||
|           - name: windows | ||||
|             image: windows-latest | ||||
|             extension: [exe, zip] | ||||
|             image: win-signing | ||||
|             shell: cmd | ||||
|             forge_platform: win32 | ||||
|     runs-on: ${{ matrix.os.image }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Set up node & dependencies | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|           node-version: 22 | ||||
|       - name: Install dependencies | ||||
|         shell: bash | ||||
|         run: npm ci | ||||
|       - name: Update nightly version | ||||
|         run: npm run chore:ci-update-nightly-version | ||||
| @@ -47,7 +52,8 @@ jobs: | ||||
|         with: | ||||
|           os: ${{ matrix.os.name }} | ||||
|           arch: ${{ matrix.arch }} | ||||
|           extension: ${{ join(matrix.os.extension, ' ') }} | ||||
|           shell: ${{ matrix.os.shell }} | ||||
|           forge_platform: ${{ matrix.os.forge_platform }} | ||||
|         env: | ||||
|           APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} | ||||
|           APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} | ||||
| @@ -56,6 +62,7 @@ jobs: | ||||
|           APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | ||||
|           APPLE_ID: ${{ secrets.APPLE_ID }} | ||||
|           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | ||||
|           WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }} | ||||
|  | ||||
|       - name: Publish release | ||||
|         uses: softprops/action-gh-release@v2 | ||||
| @@ -69,35 +76,9 @@ jobs: | ||||
|           tag_name: nightly | ||||
|           name: Nightly Build | ||||
|  | ||||
|   nightly-server: | ||||
|     name: Deploy server nightly | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         arch: [x64, arm64] | ||||
|         include: | ||||
|           - arch: x64 | ||||
|             runs-on: ubuntu-latest | ||||
|           - arch: arm64 | ||||
|             runs-on: ubuntu-24.04-arm | ||||
|     runs-on: ${{ matrix.runs-on }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|  | ||||
|       - name: Run the build | ||||
|         uses: ./.github/actions/build-server | ||||
|       - name: Publish artifacts | ||||
|         uses: actions/upload-artifact@v4 | ||||
|         if: ${{ github.event_name == 'pull_request' }} | ||||
|         with: | ||||
|           os: linux | ||||
|           arch: ${{ matrix.arch }} | ||||
|  | ||||
|       - name: Publish release | ||||
|         uses: softprops/action-gh-release@v2 | ||||
|         if: ${{ github.event_name != 'pull_request' }} | ||||
|         with: | ||||
|           make_latest: false | ||||
|           prerelease: true | ||||
|           draft: false | ||||
|           fail_on_unmatched_files: true | ||||
|           files: upload/*.* | ||||
|           tag_name: nightly | ||||
|           name: Nightly Build | ||||
|           name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }} | ||||
|           path: upload | ||||
|   | ||||
							
								
								
									
										17
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -20,26 +20,30 @@ jobs: | ||||
|         os: | ||||
|           - name: macos | ||||
|             image: macos-latest | ||||
|             extension: [dmg, zip] | ||||
|             shell: bash | ||||
|             forge_platform: darwin | ||||
|           - name: linux | ||||
|             image: ubuntu-latest | ||||
|             extension: [deb, rpm, zip, flatpak] | ||||
|             shell: bash | ||||
|             forge_platform: linux | ||||
|           - name: windows | ||||
|             image: windows-latest | ||||
|             extension: [exe, zip] | ||||
|             image: win-signing | ||||
|             shell: cmd | ||||
|             forge_platform: win32 | ||||
|     runs-on: ${{ matrix.os.image }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Set up node & dependencies | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
|           node-version: 20 | ||||
|           node-version: 22 | ||||
|       - name: Run the build | ||||
|         uses: ./.github/actions/build-electron | ||||
|         with: | ||||
|           os: ${{ matrix.os.name }} | ||||
|           arch: ${{ matrix.arch }} | ||||
|           extension: ${{ join(matrix.os.extension, ' ') }} | ||||
|           shell: ${{ matrix.os.shell }} | ||||
|           forge_platform: ${{ matrix.os.forge_platform }} | ||||
|         env: | ||||
|           APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} | ||||
|           APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} | ||||
| @@ -48,6 +52,7 @@ jobs: | ||||
|           APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | ||||
|           APPLE_ID: ${{ secrets.APPLE_ID }} | ||||
|           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | ||||
|           WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }} | ||||
|  | ||||
|       - name: Publish release | ||||
|         uses: softprops/action-gh-release@v2 | ||||
|   | ||||
							
								
								
									
										3
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -2,6 +2,7 @@ | ||||
|   "recommendations": [ | ||||
|     "lokalise.i18n-ally", | ||||
|     "editorconfig.editorconfig", | ||||
|     "vitest.explorer" | ||||
|     "vitest.explorer", | ||||
|     "ms-playwright.playwright" | ||||
|   ] | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ fi | ||||
| echo "Selected Arch: $ARCH" | ||||
|  | ||||
| # Set Node.js version and architecture-specific filename | ||||
| NODE_VERSION=20.15.1 | ||||
| NODE_VERSION=22.14.0 | ||||
|  | ||||
| BUILD_DIR="./build" | ||||
| DIST_DIR="./dist" | ||||
|   | ||||
| @@ -21,6 +21,7 @@ function copyNodeModuleFileOrFolder(source: string) { | ||||
| try { | ||||
|  | ||||
|     const assetsToCopy = new Set([ | ||||
|         "./node_modules", | ||||
|         "./images", | ||||
|         "./libraries", | ||||
|         "./translations", | ||||
| @@ -33,6 +34,7 @@ try { | ||||
|         "./forge.config.cjs", | ||||
|         "./bin/tpl/", | ||||
|         "./bin/electron-forge/desktop.ejs", | ||||
|         "./bin/electron-forge/sign-windows.cjs", | ||||
|         "./src/views/", | ||||
|         "./src/etapi/etapi.openapi.yaml", | ||||
|         "./src/routes/api/openapi.json", | ||||
|   | ||||
							
								
								
									
										14
									
								
								bin/electron-forge/sign-windows.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | ||||
| const child_process = require("child_process"); | ||||
|  | ||||
| module.exports = function (filePath) { | ||||
|     const { WINDOWS_SIGN_EXECUTABLE } = process.env; | ||||
|  | ||||
|     if (!WINDOWS_SIGN_EXECUTABLE) { | ||||
|         console.warn("[Sign] Skip signing due to missing environment variable."); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const command = `${WINDOWS_SIGN_EXECUTABLE} --executable "${filePath}"`; | ||||
|     console.log(`[Sign] ${command}`); | ||||
|     child_process.execSync(command); | ||||
| } | ||||
| @@ -47,6 +47,10 @@ test("User can change language from settings", async ({ page, context }) => { | ||||
|  | ||||
|     // Select Chinese and ensure the translation is set. | ||||
|     await languageCombobox.selectOption("cn"); | ||||
|  | ||||
|     // Press the refresh button. | ||||
|     await app.currentNoteSplit.getByRole("button", { name: "Restart the application" }).click(); | ||||
|  | ||||
|     await expect(app.currentNoteSplit).toContainText("一周的第一天", { timeout: 15000 }); | ||||
|     await expect(languageCombobox).toHaveValue("cn"); | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import electronDl from "electron-dl"; | ||||
| import sqlInit from "./src/services/sql_init.js"; | ||||
| import windowService from "./src/services/window.js"; | ||||
| import tray from "./src/services/tray.js"; | ||||
| import options from "./src/services/options.js"; | ||||
|  | ||||
| import sourceMapSupport from "source-map-support"; | ||||
| sourceMapSupport.install(); | ||||
| @@ -22,6 +23,7 @@ electronDl({ saveAs: true }); | ||||
|  | ||||
| // needed for excalidraw export https://github.com/zadam/trilium/issues/4271 | ||||
| electron.app.commandLine.appendSwitch("enable-experimental-web-platform-features"); | ||||
| electron.app.commandLine.appendSwitch("lang", options.getOptionOrNull("formattingLocale") ?? "en"); | ||||
|  | ||||
| electron.app.userAgentFallback = `${electron.app.getName()} ${electron.app.getVersion()}`; | ||||
|  | ||||
|   | ||||
| @@ -2,13 +2,17 @@ const path = require("path"); | ||||
| const fs = require("fs-extra"); | ||||
|  | ||||
| const APP_NAME = "TriliumNext Notes"; | ||||
| const BIN_PATH = path.normalize("./bin/electron-forge"); | ||||
|  | ||||
| const extraResourcesForPlatform = getExtraResourcesForPlatform(); | ||||
| const baseLinuxMakerConfigOptions = { | ||||
|   icon: "./images/app-icons/png/128x128.png", | ||||
|   desktopTemplate: path.resolve("./bin/electron-forge/desktop.ejs"), | ||||
|   desktopTemplate: path.resolve(path.join(BIN_PATH, "desktop.ejs")), | ||||
|   categories: ["Office", "Utility"] | ||||
| }; | ||||
| const windowsSignConfiguration = process.env.WINDOWS_SIGN_EXECUTABLE ? { | ||||
|     hookModulePath: path.join(BIN_PATH, "sign-windows.cjs") | ||||
| } : undefined; | ||||
|  | ||||
| module.exports = { | ||||
|     // we run electron-forge inside the ./build folder, | ||||
| @@ -26,6 +30,7 @@ module.exports = { | ||||
|             appleIdPassword: process.env.APPLE_ID_PASSWORD, | ||||
|             teamId: process.env.APPLE_TEAM_ID | ||||
|         }, | ||||
|         windowsSign: windowsSignConfiguration, | ||||
|         extraResource: [ | ||||
|             // All resources should stay in Resources directory for macOS | ||||
|             ...(process.platform === "darwin" ? [] : extraResourcesForPlatform), | ||||
| @@ -105,7 +110,8 @@ module.exports = { | ||||
|             config: { | ||||
|                 iconUrl: "https://raw.githubusercontent.com/TriliumNext/Notes/develop/images/app-icons/icon.ico", | ||||
|                 setupIcon: "./images/app-icons/win/setup.ico", | ||||
|                 loadingGif: "./images/app-icons/win/setup-banner.gif" | ||||
|                 loadingGif: "./images/app-icons/win/setup-banner.gif", | ||||
|                 windowsSign: windowsSignConfiguration | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
| @@ -129,7 +135,33 @@ module.exports = { | ||||
|             name: "@electron-forge/plugin-auto-unpack-natives", | ||||
|             config: {} | ||||
|         } | ||||
|     ] | ||||
|     ], | ||||
|     hooks: { | ||||
|         postMake(_, makeResults) { | ||||
|             const outputDir = path.join(__dirname, "..", "upload"); | ||||
|             fs.mkdirp(outputDir); | ||||
|             for (const makeResult of makeResults) { | ||||
|                 for (const artifactPath of makeResult.artifacts) { | ||||
|                     // Ignore certain artifacts. | ||||
|                     let fileName = path.basename(artifactPath); | ||||
|                     const extension = path.extname(fileName); | ||||
|                     if (fileName === "RELEASES" || extension === ".nupkg") { | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     // Override the extension for the CI. | ||||
|                     const { TRILIUM_ARTIFACT_NAME_HINT } = process.env; | ||||
|                     if (TRILIUM_ARTIFACT_NAME_HINT) { | ||||
|                         fileName = TRILIUM_ARTIFACT_NAME_HINT + extension; | ||||
|                     } | ||||
|          | ||||
|                     const outputPath = path.join(outputDir, fileName); | ||||
|                     console.log(`[Artifact] ${artifactPath} -> ${outputPath}`); | ||||
|                     fs.copyFile(artifactPath, outputPath); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| function getExtraResourcesForPlatform() { | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate-inverted.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 348 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate-inverted@1.25x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 427 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate-inverted@1.5x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 514 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate-inverted@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 649 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 331 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate@1.25x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 409 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate@1.5x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 481 B | 
							
								
								
									
										
											BIN
										
									
								
								images/app-icons/tray/new-windowTemplate@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 626 B | 
							
								
								
									
										389
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						| @@ -68,7 +68,7 @@ | ||||
|         "leaflet-gpx": "2.1.2", | ||||
|         "mark.js": "8.11.1", | ||||
|         "marked": "15.0.7", | ||||
|         "mermaid": "11.5.0", | ||||
|         "mermaid": "11.6.0", | ||||
|         "mime-types": "2.1.35", | ||||
|         "multer": "1.4.5-lts.2", | ||||
|         "normalize-strings": "1.1.1", | ||||
| @@ -100,14 +100,14 @@ | ||||
|         "trilium": "src/main.js" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@electron-forge/cli": "7.7.0", | ||||
|         "@electron-forge/maker-deb": "7.7.0", | ||||
|         "@electron-forge/maker-dmg": "7.7.0", | ||||
|         "@electron-forge/maker-flatpak": "7.7.0", | ||||
|         "@electron-forge/maker-rpm": "7.7.0", | ||||
|         "@electron-forge/maker-squirrel": "7.7.0", | ||||
|         "@electron-forge/maker-zip": "7.7.0", | ||||
|         "@electron-forge/plugin-auto-unpack-natives": "7.7.0", | ||||
|         "@electron-forge/cli": "7.8.0", | ||||
|         "@electron-forge/maker-deb": "7.8.0", | ||||
|         "@electron-forge/maker-dmg": "7.8.0", | ||||
|         "@electron-forge/maker-flatpak": "7.8.0", | ||||
|         "@electron-forge/maker-rpm": "7.8.0", | ||||
|         "@electron-forge/maker-squirrel": "7.8.0", | ||||
|         "@electron-forge/maker-zip": "7.8.0", | ||||
|         "@electron-forge/plugin-auto-unpack-natives": "7.8.0", | ||||
|         "@electron/rebuild": "3.7.1", | ||||
|         "@eslint/js": "9.23.0", | ||||
|         "@fullcalendar/core": "6.1.15", | ||||
| @@ -142,8 +142,8 @@ | ||||
|         "@types/leaflet-gpx": "1.3.7", | ||||
|         "@types/mime-types": "2.1.4", | ||||
|         "@types/multer": "1.4.12", | ||||
|         "@types/node": "22.13.11", | ||||
|         "@types/react": "18.3.19", | ||||
|         "@types/node": "22.13.13", | ||||
|         "@types/react": "18.3.20", | ||||
|         "@types/react-dom": "18.3.5", | ||||
|         "@types/safe-compare": "1.1.2", | ||||
|         "@types/sanitize-html": "2.13.0", | ||||
| @@ -152,7 +152,7 @@ | ||||
|         "@types/session-file-store": "1.2.5", | ||||
|         "@types/source-map-support": "0.5.10", | ||||
|         "@types/stream-throttle": "0.1.4", | ||||
|         "@types/supertest": "6.0.2", | ||||
|         "@types/supertest": "6.0.3", | ||||
|         "@types/swagger-ui-express": "4.1.8", | ||||
|         "@types/tmp": "0.2.6", | ||||
|         "@types/turndown": "5.0.5", | ||||
| @@ -192,7 +192,7 @@ | ||||
|         "tsx": "4.19.3", | ||||
|         "typedoc": "0.28.1", | ||||
|         "typescript": "5.8.2", | ||||
|         "typescript-eslint": "8.27.0", | ||||
|         "typescript-eslint": "8.28.0", | ||||
|         "vitest": "3.0.9", | ||||
|         "webpack": "5.98.0", | ||||
|         "webpack-cli": "6.0.1", | ||||
| @@ -569,9 +569,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/cli": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.7.0.tgz", | ||||
|       "integrity": "sha512-QfnjghmlHMb7dyArR5cbPA+MP9ff/ulWZi6R/a5MkHlKyhrysRfjzDtZDsmkEv9mGQgRwylssgXrZrKHGlxFkw==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.8.0.tgz", | ||||
|       "integrity": "sha512-XZ+Hg7pxeE9pgrahqcpMlND+VH0l0UTZLyO5wkI+YfanNyBQksB2mw24XeEtCA6x8F2IaEYdIGgijmPF6qpjzA==", | ||||
|       "dev": true, | ||||
|       "funding": [ | ||||
|         { | ||||
| @@ -585,15 +585,16 @@ | ||||
|       ], | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/core": "7.7.0", | ||||
|         "@electron-forge/core-utils": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/core": "7.8.0", | ||||
|         "@electron-forge/core-utils": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron/get": "^3.0.0", | ||||
|         "chalk": "^4.0.0", | ||||
|         "commander": "^11.1.0", | ||||
|         "debug": "^4.3.1", | ||||
|         "fs-extra": "^10.0.0", | ||||
|         "listr2": "^7.0.2", | ||||
|         "log-symbols": "^4.0.0", | ||||
|         "semver": "^7.2.1" | ||||
|       }, | ||||
|       "bin": { | ||||
| @@ -634,9 +635,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/core": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.7.0.tgz", | ||||
|       "integrity": "sha512-BWhg1Zw1bhpDuZowGH3lXDiL9zZBsYFNjtqyMqmkjcEm5xf9Dzs8mpRpNjtkpf3jit3LB4PNGMLj3c8ix0h4vQ==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.8.0.tgz", | ||||
|       "integrity": "sha512-7byf660ECZND+irOhGxvpmRXjk1bMrsTWh5J2AZMEvaXI8tub9OrZY9VSbi5fcDt0lpHPKmgVk7NRf/ZjJ+beQ==", | ||||
|       "dev": true, | ||||
|       "funding": [ | ||||
|         { | ||||
| @@ -650,17 +651,17 @@ | ||||
|       ], | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/core-utils": "7.7.0", | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/plugin-base": "7.7.0", | ||||
|         "@electron-forge/publisher-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/template-base": "7.7.0", | ||||
|         "@electron-forge/template-vite": "7.7.0", | ||||
|         "@electron-forge/template-vite-typescript": "7.7.0", | ||||
|         "@electron-forge/template-webpack": "7.7.0", | ||||
|         "@electron-forge/template-webpack-typescript": "7.7.0", | ||||
|         "@electron-forge/tracer": "7.7.0", | ||||
|         "@electron-forge/core-utils": "7.8.0", | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/plugin-base": "7.8.0", | ||||
|         "@electron-forge/publisher-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron-forge/template-base": "7.8.0", | ||||
|         "@electron-forge/template-vite": "7.8.0", | ||||
|         "@electron-forge/template-vite-typescript": "7.8.0", | ||||
|         "@electron-forge/template-webpack": "7.8.0", | ||||
|         "@electron-forge/template-webpack-typescript": "7.8.0", | ||||
|         "@electron-forge/tracer": "7.8.0", | ||||
|         "@electron/get": "^3.0.0", | ||||
|         "@electron/packager": "^18.3.5", | ||||
|         "@electron/rebuild": "^3.7.0", | ||||
| @@ -689,13 +690,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/core-utils": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.7.0.tgz", | ||||
|       "integrity": "sha512-kgOkiLzqnySkcpt26rBg8AoZsI1ID3f6s/dQlzfRJisWZTKTu4ryiMcaC0F07DVjaYFnEl9SQ86IvkTcyS97mQ==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.8.0.tgz", | ||||
|       "integrity": "sha512-ZioRzqkXVOGuwkfvXN/FPZxcssJ9AkOZx6RvxomQn90F77G2KfEbw4ZwAxVTQ+jWNUzydTic5qavWle++Y5IeA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron/rebuild": "^3.7.0", | ||||
|         "@malept/cross-spawn-promise": "^2.0.0", | ||||
|         "chalk": "^4.0.0", | ||||
| @@ -766,13 +767,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-base": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.7.0.tgz", | ||||
|       "integrity": "sha512-9u+mmBLBAUHuH0+IGw94EGVTDD4CPKX05h5pp5/PIaijy16ss5dymK4vEp3s2XJMFlza2PsCgLLYBgDcAE2Dqg==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.0.tgz", | ||||
|       "integrity": "sha512-yGRvz70w+NnKO7PhzNFRgYM+x6kxYFgpbChJIQBs3WChd9bGjL+MZLrwYqmxOFLpWNwRAJ6PEi4E/8U5GgV6AQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "fs-extra": "^10.0.0", | ||||
|         "which": "^2.0.2" | ||||
|       }, | ||||
| @@ -809,14 +810,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-deb": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.7.0.tgz", | ||||
|       "integrity": "sha512-yMT0TWpCwXaC9+AYpSr9PBIhcZR297wdJUk5PnEnIROsvOW2y1sh7ny7YdHXTxkvhWdbqY8sLQruL3BE+CyE8w==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.8.0.tgz", | ||||
|       "integrity": "sha512-9jjhLm/1IBIo0UuRdELgvBhUkNjK3tHNlUsrqeb8EJwWJZShbPwHYZJj+VbgjQfJFFzhHwBBDJViBXJ/4ePv+g==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0" | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 16.4.0" | ||||
| @@ -826,14 +827,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-dmg": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-dmg/-/maker-dmg-7.7.0.tgz", | ||||
|       "integrity": "sha512-Hq4nsY6eOdtigN4RLc9i2SbLEr46J6FfbdU+r39R/EVIJ82WRk7JPrCqbckEM7KY1TjhmrnhPWeviWoGAjbXhA==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-dmg/-/maker-dmg-7.8.0.tgz", | ||||
|       "integrity": "sha512-ml6GpHvUyhOapIF1ALEM4zCqXiAf2+t+3FqKnjNtiVbH5fnV2CW//SWWozrvAGTrYGi/6V4s9TL/rIek0BHOPA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -872,14 +873,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-flatpak": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-flatpak/-/maker-flatpak-7.7.0.tgz", | ||||
|       "integrity": "sha512-WDNmGTulgPe4FEgxq128TI58EtVyS2Fq3loXhfirNuzoXpBtvt0LHK447cmtKHAvZZ+R802uriTcj28L+Iub3A==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-flatpak/-/maker-flatpak-7.8.0.tgz", | ||||
|       "integrity": "sha512-tnOWQLVvNZVO9xWmhUHK4OsQgYUpEIn0DX1M8FkgQCYSDXcPg/CZaZ66zqj/gu1KzGAOWg1m5KlTbITRY5Jmcg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -918,14 +919,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-rpm": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.7.0.tgz", | ||||
|       "integrity": "sha512-M9cFO6bCnOuA6BwBUUw35FrnWciWeFyLuByO0KOn3zEYeBDqG2fbBgXXS6OKcRHrpnJVTwiLY5S7eviyO1DJzQ==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.0.tgz", | ||||
|       "integrity": "sha512-oTH951NE39LOX2wYMg+C06vBZDWUP/0dsK01PlXEl5e5YfQM5Cifsk3E7BzE6BpZdWRJL3k/ETqpyYeIGNb1jw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0" | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 16.4.0" | ||||
| @@ -935,14 +936,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-squirrel": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.7.0.tgz", | ||||
|       "integrity": "sha512-9Gcq8e6+1MuXGeE7bEpk7VTd86c0riXTsDFKW8OqwVozeBYhoCd6GU59RmI3b7mcAIKOY1cBY97B+/5bnZ8ZYg==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.0.tgz", | ||||
|       "integrity": "sha512-On8WIyjNtNlWf8NJRRVToighGCCU+wcxytFM0F8Zx/pLszgc01bt7wIarOiAIzuIT9Z8vshAYA0iG1U099jfeA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -981,14 +982,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/maker-zip": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.7.0.tgz", | ||||
|       "integrity": "sha512-/rRFiPcojk0hcn+NOfHv2SlMNvghBk1RN0nuLHbbe8r+C0vG4LJV9ee/Y0HhVKkcpapJOQ+MasXJ86fzAp5uAg==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.8.0.tgz", | ||||
|       "integrity": "sha512-7MLD7GkZdlGecC9GvgBu0sWYt48p3smYvr+YCwlpdH1CTeLmWhvCqeH33a2AB0XI5CY8U8jnkG2jgdTkzr/EQw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/maker-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/maker-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "cross-zip": "^4.0.0", | ||||
|         "fs-extra": "^10.0.0", | ||||
|         "got": "^11.8.5" | ||||
| @@ -1026,53 +1027,53 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/plugin-auto-unpack-natives": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.7.0.tgz", | ||||
|       "integrity": "sha512-cYeD4x2oQXUyK4+DtIR6wMxcIHvyPgsJq1diEuBoQ+MFTh+s6DXZl6JvwtI3scD0XieCkzcmUmf8ygzqs124+w==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.8.0.tgz", | ||||
|       "integrity": "sha512-JGal5ltZmbTQ5rNq67OgGC4MJ2zjjFW0fqykHy8X9J8cgaH7SRdKkT4yYZ8jH01IAF1J57FD2zIob1MvcBqjcg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/plugin-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0" | ||||
|         "@electron-forge/plugin-base": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 16.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/plugin-base": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.7.0.tgz", | ||||
|       "integrity": "sha512-6wisQ4ZKOWey48wFF+JHzih7AuQuVma5KauwNEju2Dh2ibwDMJmPy0FWVolMSg7XUIMbGKLADGilxX6XRv8qNQ==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.8.0.tgz", | ||||
|       "integrity": "sha512-rDeeChRWIp5rQVo3Uc1q0ncUvA+kWWURW7tMuQjPvy2qVSgX+jIf5krk+T1Dp06+D4YZzEIrkibRaamAaIcR1w==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0" | ||||
|         "@electron-forge/shared-types": "7.8.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 16.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/publisher-base": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.7.0.tgz", | ||||
|       "integrity": "sha512-jHKvUc1peBBSl2t5d1x6M3CNyCMyNB+NnTO9LmA1dWFQ3oRDFwromIH5KjRqPJj6l4AyH0/XJogdO7Nn4Eyn6Q==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.8.0.tgz", | ||||
|       "integrity": "sha512-wrZyptJ0Uqvlh2wYzDZfIu2HgCQ+kdGiBlcucmLY4W+GUqf043O8cbYso3D9NXQxOow55QC/1saCQkgLphprPA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0" | ||||
|         "@electron-forge/shared-types": "7.8.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 16.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/shared-types": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.7.0.tgz", | ||||
|       "integrity": "sha512-1zQsmudkAuHv0HnJtSJY3pvTeuN3fnSa9BR6cbeUlcpOfrnG4OTG03FqerHfyIWaBRVy7jGgif0NhKKE9azKyg==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.0.tgz", | ||||
|       "integrity": "sha512-Ul+7HPvAZiAirqpZm0vc9YvlkAE+2bcrI10p3t50mEtuxn5VO/mB72NXiEKfWzHm8F31JySIe9bUV6s1MHQcCw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/tracer": "7.7.0", | ||||
|         "@electron-forge/tracer": "7.8.0", | ||||
|         "@electron/packager": "^18.3.5", | ||||
|         "@electron/rebuild": "^3.7.0", | ||||
|         "listr2": "^7.0.2" | ||||
| @@ -1082,14 +1083,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/template-base": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.7.0.tgz", | ||||
|       "integrity": "sha512-jwnhEHNIyQfbwJ6R8SuZIJApHKBykDr/rSgUF3km9nr2qAUSoUUV7RaJa/uiQJMtvamXenuo5K84C2NzumzS3A==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.8.0.tgz", | ||||
|       "integrity": "sha512-hc8NwoDqEEmZFH/p0p3MK/7xygMmI+cm8Gavoj2Mr2xS7VUUu4r3b5PwIGKvkLfPG34uwsiVwtid2t1rWGF4UA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/core-utils": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/core-utils": "7.8.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@malept/cross-spawn-promise": "^2.0.0", | ||||
|         "debug": "^4.3.1", | ||||
|         "fs-extra": "^10.0.0", | ||||
| @@ -1128,14 +1129,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/template-vite": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.7.0.tgz", | ||||
|       "integrity": "sha512-6p+U6FDWrmF7XgSLkrO07OOgJcrrrArbnExSckGJdBnupxmIDf1Y+exwfHHKdxX6/FfkA6JST5nRGjgA5CFqcw==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.8.0.tgz", | ||||
|       "integrity": "sha512-bf/jd8WzD0gU7Jet+WSi0Lm0SQmseb08WY27ZfJYEs2EVNMiwDfPicgQnOaqP++2yTrXhj1OY/rolZCP9CUyVw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/template-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron-forge/template-base": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -1143,14 +1144,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/template-vite-typescript": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.7.0.tgz", | ||||
|       "integrity": "sha512-32C/+PF+hIloTdbRx7OutvqnTkkC7BHeQxNw4/zG2TfQ3cjl7JUD6A2UvTUHtv5KHkK2hDw6ZdahPwpJO41YSA==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.8.0.tgz", | ||||
|       "integrity": "sha512-kW3CaVxKHUYuVfY+rT3iepeZ69frBRGh3YZOngLY2buCvGIqNEx+VCgrFBRDDbOKGmwQtwO1E9wp2rtC8q6Ztg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/template-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron-forge/template-base": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -1214,14 +1215,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/template-webpack": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.7.0.tgz", | ||||
|       "integrity": "sha512-7Hb1wejKqtvPXqhelubUNAh39FtClB/4JDtWzyAsL2iC3XeB5qh6pITz8+nW/rF2qW/JAepc/lnreqKn34P2ig==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.8.0.tgz", | ||||
|       "integrity": "sha512-AdLGC6NVgrd7Q0SaaeiwJKmSBjN6C2EHxZgLMy1yxNSpazU9m3DtYQilDjXqmCWfxkeNzdke0NaeDvLgdJSw5A==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/template-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron-forge/template-base": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -1229,14 +1230,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/template-webpack-typescript": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.7.0.tgz", | ||||
|       "integrity": "sha512-w1vRAjGy0MjjdEDYPpZcpkMo2e3z5uEwfJdwVOpBeha7p2WM/Y6go21K+7pSqGp8Xmq4zlE20hq5MEx8Bs8eZg==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.8.0.tgz", | ||||
|       "integrity": "sha512-Pl8l+gv3HzqCfFIMLxlEsoAkNd0VEWeZZ675SYyqs0/kBQUifn0bKNhVE4gUZwKGgQCcG1Gvb23KdVGD3H3XmA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron-forge/shared-types": "7.7.0", | ||||
|         "@electron-forge/template-base": "7.7.0", | ||||
|         "@electron-forge/shared-types": "7.8.0", | ||||
|         "@electron-forge/template-base": "7.8.0", | ||||
|         "fs-extra": "^10.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -1300,9 +1301,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron-forge/tracer": { | ||||
|       "version": "7.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.7.0.tgz", | ||||
|       "integrity": "sha512-R/JiGFzWhwfVyc6ioT4l5FFChRLS4Z2tWPeQfPcyoemdpzKpI1rvMHti42gzWXFW8GdzkhG0G3ZWfKiF3y3x/Q==", | ||||
|       "version": "7.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.0.tgz", | ||||
|       "integrity": "sha512-t4fIATZEX6/7PJNfyh6tLzKEsNMpO01Nz/rgHWBxeRvjCw5UNul9OOxoM7b43vfFAO9Jv++34oI3VJ09LeVQ2Q==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
| @@ -1313,9 +1314,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron/asar": { | ||||
|       "version": "3.2.17", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.17.tgz", | ||||
|       "integrity": "sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==", | ||||
|       "version": "3.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.3.1.tgz", | ||||
|       "integrity": "sha512-WtpC/+34p0skWZiarRjLAyqaAX78DofhDxnREy/V5XHfu1XEXbFCSSMcDQ6hNCPJFaPy8/NnUgYuf9uiCkvKPg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
| @@ -1511,9 +1512,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron/osx-sign": { | ||||
|       "version": "1.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.2.tgz", | ||||
|       "integrity": "sha512-KqVlm9WMWq19lBpCXQoThC/Koaiji2zotUDYwZDaZlZZym+FXY9mQW8wN6sUQ93nkVc42f3TQ1S/XN9S1kjM5Q==", | ||||
|       "version": "1.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", | ||||
|       "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", | ||||
|       "dev": true, | ||||
|       "license": "BSD-2-Clause", | ||||
|       "dependencies": { | ||||
| @@ -1677,13 +1678,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@electron/universal": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", | ||||
|       "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", | ||||
|       "version": "2.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.2.tgz", | ||||
|       "integrity": "sha512-mqY1szx5/d5YLvfCDWWoJdkSIjIz+NdWN4pN0r78lYiE7De+slLpuF3lVxIT+hlJnwk5sH2wFRMl6/oUgUVO3A==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@electron/asar": "^3.2.7", | ||||
|         "@electron/asar": "^3.3.1", | ||||
|         "@malept/cross-spawn-promise": "^2.0.0", | ||||
|         "debug": "^4.3.1", | ||||
|         "dir-compare": "^4.2.0", | ||||
| @@ -3976,12 +3977,12 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mermaid-js/parser": { | ||||
|       "version": "0.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", | ||||
|       "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", | ||||
|       "version": "0.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", | ||||
|       "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "langium": "3.0.0" | ||||
|         "langium": "3.3.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@mind-elixir/node-menu": { | ||||
| @@ -5925,9 +5926,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/node": { | ||||
|       "version": "22.13.11", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz", | ||||
|       "integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==", | ||||
|       "version": "22.13.13", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz", | ||||
|       "integrity": "sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "undici-types": "~6.20.0" | ||||
| @@ -5955,9 +5956,9 @@ | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/@types/react": { | ||||
|       "version": "18.3.19", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.19.tgz", | ||||
|       "integrity": "sha512-fcdJqaHOMDbiAwJnXv6XCzX0jDW77yI3tJqYh1Byn8EL5/S628WRx9b/y3DnNe55zTukUQKrfYxiZls2dHcUMw==", | ||||
|       "version": "18.3.20", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", | ||||
|       "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", | ||||
|       "devOptional": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
| @@ -6106,9 +6107,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/supertest": { | ||||
|       "version": "6.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", | ||||
|       "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", | ||||
|       "version": "6.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", | ||||
|       "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
| @@ -6210,17 +6211,17 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/eslint-plugin": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.27.0.tgz", | ||||
|       "integrity": "sha512-4henw4zkePi5p252c8ncBLzLce52SEUz2Ebj8faDnuUXz2UuHEONYcJ+G0oaCF+bYCWVZtrGzq3FD7YXetmnSA==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz", | ||||
|       "integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@eslint-community/regexpp": "^4.10.0", | ||||
|         "@typescript-eslint/scope-manager": "8.27.0", | ||||
|         "@typescript-eslint/type-utils": "8.27.0", | ||||
|         "@typescript-eslint/utils": "8.27.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.27.0", | ||||
|         "@typescript-eslint/scope-manager": "8.28.0", | ||||
|         "@typescript-eslint/type-utils": "8.28.0", | ||||
|         "@typescript-eslint/utils": "8.28.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.28.0", | ||||
|         "graphemer": "^1.4.0", | ||||
|         "ignore": "^5.3.1", | ||||
|         "natural-compare": "^1.4.0", | ||||
| @@ -6240,16 +6241,16 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/parser": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.27.0.tgz", | ||||
|       "integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz", | ||||
|       "integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/scope-manager": "8.27.0", | ||||
|         "@typescript-eslint/types": "8.27.0", | ||||
|         "@typescript-eslint/typescript-estree": "8.27.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.27.0", | ||||
|         "@typescript-eslint/scope-manager": "8.28.0", | ||||
|         "@typescript-eslint/types": "8.28.0", | ||||
|         "@typescript-eslint/typescript-estree": "8.28.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.28.0", | ||||
|         "debug": "^4.3.4" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -6265,14 +6266,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/scope-manager": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.27.0.tgz", | ||||
|       "integrity": "sha512-8oI9GwPMQmBryaaxG1tOZdxXVeMDte6NyJA4i7/TWa4fBwgnAXYlIQP+uYOeqAaLJ2JRxlG9CAyL+C+YE9Xknw==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz", | ||||
|       "integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/types": "8.27.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.27.0" | ||||
|         "@typescript-eslint/types": "8.28.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.28.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^18.18.0 || ^20.9.0 || >=21.1.0" | ||||
| @@ -6283,14 +6284,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/type-utils": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.27.0.tgz", | ||||
|       "integrity": "sha512-wVArTVcz1oJOIEJxui/nRhV0TXzD/zMSOYi/ggCfNq78EIszddXcJb7r4RCp/oBrjt8n9A0BSxRMKxHftpDxDA==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz", | ||||
|       "integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/typescript-estree": "8.27.0", | ||||
|         "@typescript-eslint/utils": "8.27.0", | ||||
|         "@typescript-eslint/typescript-estree": "8.28.0", | ||||
|         "@typescript-eslint/utils": "8.28.0", | ||||
|         "debug": "^4.3.4", | ||||
|         "ts-api-utils": "^2.0.1" | ||||
|       }, | ||||
| @@ -6307,9 +6308,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/types": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.27.0.tgz", | ||||
|       "integrity": "sha512-/6cp9yL72yUHAYq9g6DsAU+vVfvQmd1a8KyA81uvfDE21O2DwQ/qxlM4AR8TSdAu+kJLBDrEHKC5/W2/nxsY0A==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz", | ||||
|       "integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
| @@ -6321,14 +6322,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/typescript-estree": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.27.0.tgz", | ||||
|       "integrity": "sha512-BnKq8cqPVoMw71O38a1tEb6iebEgGA80icSxW7g+kndx0o6ot6696HjG7NdgfuAVmVEtwXUr3L8R9ZuVjoQL6A==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz", | ||||
|       "integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/types": "8.27.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.27.0", | ||||
|         "@typescript-eslint/types": "8.28.0", | ||||
|         "@typescript-eslint/visitor-keys": "8.28.0", | ||||
|         "debug": "^4.3.4", | ||||
|         "fast-glob": "^3.3.2", | ||||
|         "is-glob": "^4.0.3", | ||||
| @@ -6374,16 +6375,16 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/utils": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.27.0.tgz", | ||||
|       "integrity": "sha512-njkodcwH1yvmo31YWgRHNb/x1Xhhq4/m81PhtvmRngD8iHPehxffz1SNCO+kwaePhATC+kOa/ggmvPoPza5i0Q==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz", | ||||
|       "integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@eslint-community/eslint-utils": "^4.4.0", | ||||
|         "@typescript-eslint/scope-manager": "8.27.0", | ||||
|         "@typescript-eslint/types": "8.27.0", | ||||
|         "@typescript-eslint/typescript-estree": "8.27.0" | ||||
|         "@typescript-eslint/scope-manager": "8.28.0", | ||||
|         "@typescript-eslint/types": "8.28.0", | ||||
|         "@typescript-eslint/typescript-estree": "8.28.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^18.18.0 || ^20.9.0 || >=21.1.0" | ||||
| @@ -6398,13 +6399,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/visitor-keys": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.27.0.tgz", | ||||
|       "integrity": "sha512-WsXQwMkILJvffP6z4U3FYJPlbf/j07HIxmDjZpbNvBJkMfvwXj5ACRkkHwBDvLBbDbtX5TdU64/rcvKJ/vuInQ==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz", | ||||
|       "integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/types": "8.27.0", | ||||
|         "@typescript-eslint/types": "8.28.0", | ||||
|         "eslint-visitor-keys": "^4.2.0" | ||||
|       }, | ||||
|       "engines": { | ||||
| @@ -14418,9 +14419,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/langium": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", | ||||
|       "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", | ||||
|       "version": "3.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", | ||||
|       "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "chevrotain": "~11.0.3", | ||||
| @@ -15104,14 +15105,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/mermaid": { | ||||
|       "version": "11.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.5.0.tgz", | ||||
|       "integrity": "sha512-IYhyukID3zzDj1EihKiN1lp+PXNImoJ3Iyz73qeDAgnus4BNGsJV1n471P4PyeGxPVONerZxignwGxGTSwZnlg==", | ||||
|       "version": "11.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", | ||||
|       "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@braintree/sanitize-url": "^7.0.4", | ||||
|         "@iconify/utils": "^2.1.33", | ||||
|         "@mermaid-js/parser": "^0.3.0", | ||||
|         "@mermaid-js/parser": "^0.4.0", | ||||
|         "@types/d3": "^7.4.3", | ||||
|         "cytoscape": "^3.29.3", | ||||
|         "cytoscape-cose-bilkent": "^4.1.0", | ||||
| @@ -20681,15 +20682,15 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/typescript-eslint": { | ||||
|       "version": "8.27.0", | ||||
|       "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.27.0.tgz", | ||||
|       "integrity": "sha512-ZZ/8+Y0rRUMuW1gJaPtLWe4ryHbsPLzzibk5Sq+IFa2aOH1Vo0gPr1fbA6pOnzBke7zC2Da4w8AyCgxKXo3lqA==", | ||||
|       "version": "8.28.0", | ||||
|       "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.28.0.tgz", | ||||
|       "integrity": "sha512-jfZtxJoHm59bvoCMYCe2BM0/baMswRhMmYhy+w6VfcyHrjxZ0OJe0tGasydCpIpA+A/WIJhTyZfb3EtwNC/kHQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@typescript-eslint/eslint-plugin": "8.27.0", | ||||
|         "@typescript-eslint/parser": "8.27.0", | ||||
|         "@typescript-eslint/utils": "8.27.0" | ||||
|         "@typescript-eslint/eslint-plugin": "8.28.0", | ||||
|         "@typescript-eslint/parser": "8.28.0", | ||||
|         "@typescript-eslint/utils": "8.28.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^18.18.0 || ^20.9.0 || >=21.1.0" | ||||
|   | ||||
							
								
								
									
										30
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -38,9 +38,9 @@ | ||||
|     "electron:switch": "electron-rebuild", | ||||
|     "docs:edit": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_ENV=dev TRILIUM_PORT=37741 electron ./electron-docs-main.ts .", | ||||
|     "docs:edit-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_PORT=37741 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-docs-main.ts .\"", | ||||
|     "electron-forge:prepare": "npm run build:prepare-dist && cp -r node_modules ./build", | ||||
|     "electron-forge:prepare": "npm run build:prepare-dist", | ||||
|     "electron-forge:start": "npm run electron-forge:prepare && cd ./build && electron-forge start", | ||||
|     "electron-forge:make": "npm run electron-forge:prepare && cd ./build && electron-forge make", | ||||
|     "electron-forge:make": "npm run electron-forge:prepare && cross-env DEBUG=electron-windows-installer:* electron-forge make ./build", | ||||
|     "electron-forge:package": "npm run electron-forge:prepare && cd ./build && electron-forge package", | ||||
|     "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", | ||||
|     "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", | ||||
| @@ -128,7 +128,7 @@ | ||||
|     "leaflet-gpx": "2.1.2", | ||||
|     "mark.js": "8.11.1", | ||||
|     "marked": "15.0.7", | ||||
|     "mermaid": "11.5.0", | ||||
|     "mermaid": "11.6.0", | ||||
|     "mime-types": "2.1.35", | ||||
|     "multer": "1.4.5-lts.2", | ||||
|     "normalize-strings": "1.1.1", | ||||
| @@ -157,14 +157,14 @@ | ||||
|     "yauzl": "3.2.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@electron-forge/cli": "7.7.0", | ||||
|     "@electron-forge/maker-deb": "7.7.0", | ||||
|     "@electron-forge/maker-dmg": "7.7.0", | ||||
|     "@electron-forge/maker-flatpak": "7.7.0", | ||||
|     "@electron-forge/maker-rpm": "7.7.0", | ||||
|     "@electron-forge/maker-squirrel": "7.7.0", | ||||
|     "@electron-forge/maker-zip": "7.7.0", | ||||
|     "@electron-forge/plugin-auto-unpack-natives": "7.7.0", | ||||
|     "@electron-forge/cli": "7.8.0", | ||||
|     "@electron-forge/maker-deb": "7.8.0", | ||||
|     "@electron-forge/maker-dmg": "7.8.0", | ||||
|     "@electron-forge/maker-flatpak": "7.8.0", | ||||
|     "@electron-forge/maker-rpm": "7.8.0", | ||||
|     "@electron-forge/maker-squirrel": "7.8.0", | ||||
|     "@electron-forge/maker-zip": "7.8.0", | ||||
|     "@electron-forge/plugin-auto-unpack-natives": "7.8.0", | ||||
|     "@electron/rebuild": "3.7.1", | ||||
|     "@eslint/js": "9.23.0", | ||||
|     "@fullcalendar/core": "6.1.15", | ||||
| @@ -199,8 +199,8 @@ | ||||
|     "@types/leaflet-gpx": "1.3.7", | ||||
|     "@types/mime-types": "2.1.4", | ||||
|     "@types/multer": "1.4.12", | ||||
|     "@types/node": "22.13.11", | ||||
|     "@types/react": "18.3.19", | ||||
|     "@types/node": "22.13.13", | ||||
|     "@types/react": "18.3.20", | ||||
|     "@types/react-dom": "18.3.5", | ||||
|     "@types/safe-compare": "1.1.2", | ||||
|     "@types/sanitize-html": "2.13.0", | ||||
| @@ -209,7 +209,7 @@ | ||||
|     "@types/session-file-store": "1.2.5", | ||||
|     "@types/source-map-support": "0.5.10", | ||||
|     "@types/stream-throttle": "0.1.4", | ||||
|     "@types/supertest": "6.0.2", | ||||
|     "@types/supertest": "6.0.3", | ||||
|     "@types/swagger-ui-express": "4.1.8", | ||||
|     "@types/tmp": "0.2.6", | ||||
|     "@types/turndown": "5.0.5", | ||||
| @@ -249,7 +249,7 @@ | ||||
|     "tsx": "4.19.3", | ||||
|     "typedoc": "0.28.1", | ||||
|     "typescript": "5.8.2", | ||||
|     "typescript-eslint": "8.27.0", | ||||
|     "typescript-eslint": "8.28.0", | ||||
|     "vitest": "3.0.9", | ||||
|     "webpack": "5.98.0", | ||||
|     "webpack-cli": "6.0.1", | ||||
|   | ||||
| @@ -10,6 +10,17 @@ function reloadFrontendApp(reason?: string) { | ||||
|     window.location.reload(); | ||||
| } | ||||
|  | ||||
| function restartDesktopApp() { | ||||
|     if (!isElectron()) { | ||||
|         reloadFrontendApp(); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const app = dynamicRequire("@electron/remote").app; | ||||
|     app.relaunch(); | ||||
|     app.exit(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Triggers the system tray to update its menu items, i.e. after a change in dynamic content such as bookmarks or recent notes. | ||||
|  * | ||||
| @@ -739,6 +750,7 @@ function isLaunchBarConfig(noteId: string) { | ||||
|  | ||||
| export default { | ||||
|     reloadFrontendApp, | ||||
|     restartDesktopApp, | ||||
|     reloadTray, | ||||
|     parseDate, | ||||
|     getMonthsInDateRange, | ||||
|   | ||||
| @@ -61,11 +61,7 @@ export default class ElectronIntegrationOptions extends OptionsWidget { | ||||
|         this.$backgroundEffects.on("change", () => this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects)); | ||||
|  | ||||
|         const restartAppButton = this.$widget.find(".restart-app-button"); | ||||
|         restartAppButton.on("click", () => { | ||||
|             const app = utils.dynamicRequire("@electron/remote").app; | ||||
|             app.relaunch(); | ||||
|             app.exit(); | ||||
|         }); | ||||
|         restartAppButton.on("click", utils.restartDesktopApp); | ||||
|     } | ||||
|  | ||||
|     isEnabled() { | ||||
|   | ||||
| @@ -3,20 +3,26 @@ import server from "../../../../services/server.js"; | ||||
| import utils from "../../../../services/utils.js"; | ||||
| import { getAvailableLocales, t } from "../../../../services/i18n.js"; | ||||
| import type { OptionMap } from "../../../../../../services/options_interface.js"; | ||||
| import type { Locale } from "../../../../../../services/i18n.js"; | ||||
|  | ||||
| const TPL = ` | ||||
| <div class="options-section"> | ||||
|     <h4>${t("i18n.title")}</h4> | ||||
|  | ||||
|     <div class="form-group row"> | ||||
|         <div class="col-6"> | ||||
|     <div class="locale-options-container"> | ||||
|         <div class="option-row"> | ||||
|             <label for="locale-select">${t("i18n.language")}</label> | ||||
|             <select id="locale-select" class="locale-select form-select"></select> | ||||
|         </div> | ||||
|  | ||||
|         <div class="col-6"> | ||||
|         <div class="option-row electron-only"> | ||||
|             <label for="formatting-locale-select">${t("i18n.formatting-locale")}</label> | ||||
|             <select id="formatting-locale-select" class="formatting-locale-select form-select"></select> | ||||
|         </div> | ||||
|  | ||||
|         <div class="option-row"> | ||||
|             <label id="first-day-of-week-label">${t("i18n.first-day-of-the-week")}</label> | ||||
|             <div role="group" aria-labelledby="first-day-of-week-label" style="margin-top: .33em;"> | ||||
|             <div role="group" aria-labelledby="first-day-of-week-label"> | ||||
|                 <label class="tn-radio"> | ||||
|                     <input name="first-day-of-week" type="radio" value="0" /> | ||||
|                     ${t("i18n.sunday")} | ||||
| @@ -28,13 +34,44 @@ const TPL = ` | ||||
|                 </label> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="option-row centered"> | ||||
|             <button class="btn btn-secondary btn-micro restart-app-button">${t("electron_integration.restart-app-button")}</button> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <style> | ||||
|         .locale-options-container .option-row { | ||||
|             border-bottom: 1px solid var(--main-border-color); | ||||
|             display: flex; | ||||
|             align-items: center; | ||||
|             padding: 0.5em 0; | ||||
|         } | ||||
|  | ||||
|         .locale-options-container .option-row > label { | ||||
|             width: 40%; | ||||
|             margin-bottom: 0 !important; | ||||
|         } | ||||
|  | ||||
|         .locale-options-container .option-row > select { | ||||
|             width: 60%; | ||||
|         } | ||||
|  | ||||
|         .locale-options-container .option-row:last-of-type { | ||||
|             border-bottom: unset; | ||||
|         } | ||||
|  | ||||
|         .locale-options-container .option-row.centered { | ||||
|             justify-content: center; | ||||
|         } | ||||
|     </style> | ||||
| </div> | ||||
| `; | ||||
|  | ||||
| export default class LocalizationOptions extends OptionsWidget { | ||||
|  | ||||
|     private $localeSelect!: JQuery<HTMLElement>; | ||||
|     private $formattingLocaleSelect!: JQuery<HTMLElement>; | ||||
|  | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
| @@ -43,24 +80,44 @@ export default class LocalizationOptions extends OptionsWidget { | ||||
|         this.$localeSelect.on("change", async () => { | ||||
|             const newLocale = this.$localeSelect.val(); | ||||
|             await server.put(`options/locale/${newLocale}`); | ||||
|             utils.reloadFrontendApp("locale change"); | ||||
|         }); | ||||
|  | ||||
|         this.$formattingLocaleSelect = this.$widget.find(".formatting-locale-select"); | ||||
|         this.$formattingLocaleSelect.on("change", async () => { | ||||
|             const newLocale = this.$formattingLocaleSelect.val(); | ||||
|             await server.put(`options/formattingLocale/${newLocale}`); | ||||
|         }); | ||||
|  | ||||
|         this.$widget.find(`input[name="first-day-of-week"]`).on("change", () => { | ||||
|             const firstDayOfWeek = String(this.$widget.find(`input[name="first-day-of-week"]:checked`).val()); | ||||
|             this.updateOption("firstDayOfWeek", firstDayOfWeek); | ||||
|         }); | ||||
|         this.$widget.find(".restart-app-button").on("click", utils.restartDesktopApp); | ||||
|     } | ||||
|  | ||||
|     async optionsLoaded(options: OptionMap) { | ||||
|         const availableLocales = getAvailableLocales().filter(l => !l.contentOnly); | ||||
|         this.$localeSelect.empty(); | ||||
|         const allLocales = getAvailableLocales(); | ||||
|  | ||||
|         for (const locale of availableLocales) { | ||||
|             this.$localeSelect.append($("<option>").attr("value", locale.id).text(locale.name)); | ||||
|         function buildLocaleItem(locale: Locale, value: string) { | ||||
|             return $("<option>") | ||||
|                 .attr("value", value) | ||||
|                 .text(locale.name) | ||||
|         } | ||||
|  | ||||
|         // Build list of UI locales. | ||||
|         this.$localeSelect.empty(); | ||||
|         for (const locale of allLocales.filter(l => !l.contentOnly)) { | ||||
|             this.$localeSelect.append(buildLocaleItem(locale, locale.id)); | ||||
|         } | ||||
|         this.$localeSelect.val(options.locale); | ||||
|  | ||||
|         // Build list of Electron locales. | ||||
|         this.$formattingLocaleSelect.empty(); | ||||
|         for (const locale of allLocales.filter(l => l.electronLocale)) { | ||||
|             this.$formattingLocaleSelect.append(buildLocaleItem(locale, locale.electronLocale as string)); | ||||
|         } | ||||
|         this.$formattingLocaleSelect.val(options.formattingLocale); | ||||
|  | ||||
|         this.$widget.find(`input[name="first-day-of-week"][value="${options.firstDayOfWeek}"]`) | ||||
|                     .prop("checked", "true"); | ||||
|     } | ||||
|   | ||||
| @@ -49,7 +49,8 @@ body { | ||||
|     --tab-bar-height: 40px; | ||||
| } | ||||
|  | ||||
| body.mobile .desktop-only { | ||||
| body.mobile .desktop-only, | ||||
| body:not(.electron) .electron-only { | ||||
|     display: none !important; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -190,8 +190,11 @@ | ||||
|     --right-pane-item-hover-background: #ffffff26; | ||||
|     --right-pane-item-hover-color: white; | ||||
|  | ||||
|     --scrollbar-border-color: #666; | ||||
|     --scrollbar-background-color: #333; | ||||
|     --scrollbar-thumb-color: #fdfdfd5c; | ||||
|     --scrollbar-thumb-color-hover: #ffffff7d; | ||||
|     --scrollbar-border-color: unset; /* Deprecated */ | ||||
|     --scrollbar-background-color: unset; /* Deprecated */ | ||||
|  | ||||
|     --link-color: lightskyblue; | ||||
|  | ||||
|     --mermaid-theme: dark; | ||||
|   | ||||
| @@ -189,8 +189,11 @@ | ||||
|     --right-pane-item-hover-background: #ececec; | ||||
|     --right-pane-item-hover-color: inherit; | ||||
|  | ||||
|     --scrollbar-border-color: #ddd; | ||||
|     --scrollbar-background-color: #ddd; | ||||
|     --scrollbar-thumb-color: #0000005c; | ||||
|     --scrollbar-thumb-color-hover: #00000066; | ||||
|     --scrollbar-border-color: unset; /* Deprecated */ | ||||
|     --scrollbar-background-color: unset; /* Deprecated */ | ||||
|  | ||||
|     --link-color: blue; | ||||
|  | ||||
|     --mermaid-theme: default; | ||||
|   | ||||
| @@ -660,3 +660,83 @@ a.tn-link:hover[href^="https://"]:not(.no-arrow)::after, | ||||
| input[type="range"] { | ||||
|     background: transparent; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * WebKit scrollbars | ||||
|  */ | ||||
|  | ||||
| :root { | ||||
|     --scrollbar-thickness: 10px; | ||||
|     --scrollbar-thumb-thickness: 3px; | ||||
|     --scrollbar-thumb-hover-thickness: 6px; | ||||
|     --scrollbar-start-end-gap: 8px; | ||||
| } | ||||
|  | ||||
| /* Scrollbar's body */ | ||||
|  | ||||
| ::-webkit-scrollbar:vertical { | ||||
|     width: var(--scrollbar-thickness) !important; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar:horizontal { | ||||
|     height: var(--scrollbar-thickness) !important; | ||||
| } | ||||
|  | ||||
| /* Scrollbar's thumb */ | ||||
|  | ||||
| ::-webkit-scrollbar-thumb { | ||||
|     --s-thumb-thickness: var(--scrollbar-thumb-thickness); | ||||
|     --s-thumb-color: var(--scrollbar-thumb-color); | ||||
|  | ||||
|     --s-gradient-angle: 90deg; | ||||
|     --s-gradient-p1: calc((var(--scrollbar-thickness) - var(--s-thumb-thickness)) / 2); | ||||
|     --s-gradient-p2: calc(var(--s-gradient-p1) + var(--s-thumb-thickness)); | ||||
|  | ||||
|     border: none !important; | ||||
|     background: linear-gradient(var(--s-gradient-angle), | ||||
|                                 transparent, transparent var(--s-gradient-p1), | ||||
|                                 var(--s-thumb-color) 0px, var(--s-thumb-color) var(--s-gradient-p2), | ||||
|                                 transparent 0) !important; | ||||
|  | ||||
|     border-radius: calc(var(--scrollbar-thickness) / 2) !important; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-thumb:horizontal { | ||||
|     --s-gradient-angle: 0deg; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-thumb:hover { | ||||
|     --s-thumb-thickness: var(--scrollbar-thumb-hover-thickness); | ||||
|     --s-thumb-color: var(--scrollbar-thumb-color-hover); | ||||
| } | ||||
|  | ||||
| /* Scrollbar's increment/decrement buttons (repurposed as a scrollbar start/end gap) */ | ||||
|  | ||||
| ::-webkit-scrollbar-button:vertical { | ||||
|     height: var(--scrollbar-start-end-gap); | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-button:horizontal { | ||||
|     width: var(--scrollbar-start-end-gap); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Firefox scrollbars | ||||
|  * | ||||
|  * Unsupported features: --scrollbar-thumb-thickness, --scrollbar-thumb-hover-thickness, | ||||
|  *                       --scrollbar-start-end-gap, --scrollbar-thumb-color-hover. | ||||
|  */ | ||||
|  | ||||
| :root { | ||||
|     scrollbar-color: var(--scrollbar-thumb-color) transparent; | ||||
|     scrollbar-width: var(--scrollbar-thickness); | ||||
| } | ||||
|  | ||||
| @supports selector(::-webkit-scrollbar) { | ||||
|     /* Prevent the scrollbar-color and scrollbar-width properties to override the custom styles | ||||
|      * defined using ::-webkit-scrollbar. */ | ||||
|     :root { | ||||
|         scrollbar-color: unset; | ||||
|         scrollbar-width: unset; | ||||
|     } | ||||
| } | ||||
| @@ -1359,7 +1359,8 @@ | ||||
|     "language": "Language", | ||||
|     "first-day-of-the-week": "First day of the week", | ||||
|     "sunday": "Sunday", | ||||
|     "monday": "Monday" | ||||
|     "monday": "Monday", | ||||
|     "formatting-locale": "Date & number format" | ||||
|   }, | ||||
|   "backup": { | ||||
|     "automatic_backup": "Automatic backup", | ||||
|   | ||||
| @@ -267,7 +267,8 @@ | ||||
|   "basic_properties": { | ||||
|     "basic_properties": "Proprietăți de bază", | ||||
|     "editable": "Editabil", | ||||
|     "note_type": "Tipul notiței" | ||||
|     "note_type": "Tipul notiței", | ||||
|     "language": "Limbă" | ||||
|   }, | ||||
|   "book": { | ||||
|     "no_children_help": "Această notiță de tip Carte nu are nicio subnotiță așadar nu este nimic de afișat. Vedeți <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> pentru detalii." | ||||
| @@ -678,7 +679,8 @@ | ||||
|     "language": "Limbă", | ||||
|     "monday": "Luni", | ||||
|     "sunday": "Duminică", | ||||
|     "title": "Localizare" | ||||
|     "title": "Localizare", | ||||
|     "formatting-locale": "Format dată și numere" | ||||
|   }, | ||||
|   "image_properties": { | ||||
|     "copy_reference_to_clipboard": "Copiază referință în clipboard", | ||||
| @@ -1692,5 +1694,30 @@ | ||||
|   }, | ||||
|   "content_widget": { | ||||
|     "unknown_widget": "Nu s-a putut găsi widget-ul corespunzător pentru „{{id}}”." | ||||
|   }, | ||||
|   "code-editor-options": { | ||||
|     "title": "Editor" | ||||
|   }, | ||||
|   "content_language": { | ||||
|     "description": "Selectați una sau mai multe limbi ce vor apărea în selecția limbii din cadrul secțiunii „Proprietăți de bază” pentru notițele de tip text (editabile sau doar în citire).", | ||||
|     "title": "Limbi pentru conținutul notițelor" | ||||
|   }, | ||||
|   "hidden-subtree": { | ||||
|     "localization": "Limbă și regiune" | ||||
|   }, | ||||
|   "note_language": { | ||||
|     "configure-languages": "Configurează limbile...", | ||||
|     "not_set": "Nedefinită" | ||||
|   }, | ||||
|   "png_export_button": { | ||||
|     "button_title": "Exportă diagrama ca PNG" | ||||
|   }, | ||||
|   "switch_layout_button": { | ||||
|     "title_horizontal": "Mută panoul de editare la stânga", | ||||
|     "title_vertical": "Mută panoul de editare în jos" | ||||
|   }, | ||||
|   "toggle_read_only_button": { | ||||
|     "lock-editing": "Blochează editarea", | ||||
|     "unlock-editing": "Deblochează editarea" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -70,6 +70,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([ | ||||
|     "promotedAttributesOpenInRibbon", | ||||
|     "editedNotesOpenInRibbon", | ||||
|     "locale", | ||||
|     "formattingLocale", | ||||
|     "firstDayOfWeek", | ||||
|     "languages", | ||||
|     "textNoteEditorType", | ||||
|   | ||||
| @@ -76,6 +76,10 @@ async function backupNow(name: string) { | ||||
|     return await syncMutexService.doExclusively(async () => { | ||||
|         const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`; | ||||
|  | ||||
|         if (!fs.existsSync(dataDir.BACKUP_DIR)) { | ||||
|             fs.mkdirSync(dataDir.BACKUP_DIR, 0o700); | ||||
|         } | ||||
|  | ||||
|         await sql.copyDatabase(backupFile); | ||||
|  | ||||
|         log.info(`Created backup at ${backupFile}`); | ||||
| @@ -83,11 +87,6 @@ async function backupNow(name: string) { | ||||
|         return backupFile; | ||||
|     }); | ||||
| } | ||||
|  | ||||
| if (!fs.existsSync(dataDir.BACKUP_DIR)) { | ||||
|     fs.mkdirSync(dataDir.BACKUP_DIR, 0o700); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     getExistingBackups, | ||||
|     backupNow, | ||||
|   | ||||
| @@ -13,36 +13,45 @@ export interface Locale { | ||||
|     rtl?: boolean; | ||||
|     /** `true` if the language is not supported by the application as a display language, but it is selectable by the user for the content. */ | ||||
|     contentOnly?: boolean; | ||||
|     /** The value to pass to `--lang` for the Electron instance in order to set it as a locale. Not setting it will hide it from the list of supported locales. */ | ||||
|     electronLocale?: string; | ||||
| } | ||||
|  | ||||
| const LOCALES: Locale[] = [ | ||||
|     { | ||||
|         id: "en", | ||||
|         name: "English" | ||||
|         name: "English", | ||||
|         electronLocale: "en" | ||||
|     }, | ||||
|     { | ||||
|         id: "de", | ||||
|         name: "Deutsch" | ||||
|         name: "Deutsch", | ||||
|         electronLocale: "de" | ||||
|     }, | ||||
|     { | ||||
|         id: "es", | ||||
|         name: "Español" | ||||
|         name: "Español", | ||||
|         electronLocale: "es" | ||||
|     }, | ||||
|     { | ||||
|         id: "fr", | ||||
|         name: "Français" | ||||
|         name: "Français", | ||||
|         electronLocale: "fr" | ||||
|     }, | ||||
|     { | ||||
|         id: "cn", | ||||
|         name: "简体中文" | ||||
|         name: "简体中文", | ||||
|         electronLocale: "zh_CN" | ||||
|     }, | ||||
|     { | ||||
|         id: "tw", | ||||
|         name: "繁體中文" | ||||
|         name: "繁體中文", | ||||
|         electronLocale: "zh_TW" | ||||
|     }, | ||||
|     { | ||||
|         id: "ro", | ||||
|         name: "Română" | ||||
|         name: "Română", | ||||
|         electronLocale: "ro" | ||||
|     }, | ||||
|  | ||||
|     /* | ||||
|   | ||||
| @@ -25,7 +25,12 @@ function getOptionOrNull(name: OptionNames): string | null { | ||||
|         option = becca.getOption(name); | ||||
|     } else { | ||||
|         // e.g. in initial sync becca is not loaded because DB is not initialized | ||||
|         option = sql.getRow<OptionRow>("SELECT * FROM options WHERE name = ?", [name]); | ||||
|         try { | ||||
|             option = sql.getRow<OptionRow>("SELECT * FROM options WHERE name = ?", [name]); | ||||
|         } catch (e: unknown) { | ||||
|             // DB is not initialized. | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return option ? option.value : null; | ||||
|   | ||||
| @@ -137,6 +137,7 @@ const defaultOptions: DefaultOption[] = [ | ||||
|  | ||||
|     // Internationalization | ||||
|     { name: "locale", value: "en", isSynced: true }, | ||||
|     { name: "formattingLocale", value: "en", isSynced: true }, | ||||
|     { name: "firstDayOfWeek", value: "1", isSynced: true }, | ||||
|     { name: "languages", value: "[]", isSynced: true }, | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi | ||||
|     customSearchEngineName: string; | ||||
|     customSearchEngineUrl: string; | ||||
|     locale: string; | ||||
|     formattingLocale: string; | ||||
|     codeBlockTheme: string; | ||||
|     textNoteEditorType: string; | ||||
|     layoutOrientation: string; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Menu, Tray } from "electron"; | ||||
| import { Menu, Tray, BrowserWindow } from "electron"; | ||||
| import path from "path"; | ||||
| import windowService from "./window.js"; | ||||
| import optionService from "./options.js"; | ||||
| @@ -17,7 +17,7 @@ import cls from "./cls.js"; | ||||
| let tray: Tray; | ||||
| // `mainWindow.isVisible` doesn't work with `mainWindow.show` and `mainWindow.hide` - it returns `false` when the window | ||||
| // is minimized | ||||
| let isVisible = true; | ||||
| let windowVisibilityMap: Record<number, boolean> = {};; // Dictionary for storing window ID and its visibility status | ||||
|  | ||||
| function getTrayIconPath() { | ||||
|     let name: string; | ||||
| @@ -37,53 +37,93 @@ function getIconPath(name: string) { | ||||
|     return path.join(path.dirname(fileURLToPath(import.meta.url)), "../..", "images", "app-icons", "tray", `${name}Template${suffix}.png`); | ||||
| } | ||||
|  | ||||
| function registerVisibilityListener() { | ||||
|     const mainWindow = windowService.getMainWindow(); | ||||
|     if (!mainWindow) { | ||||
| function registerVisibilityListener(window: BrowserWindow) { | ||||
|     if (!window) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // They need to be registered before the tray updater is registered | ||||
|     mainWindow.on("show", () => { | ||||
|         isVisible = true; | ||||
|     window.on("show", () => { | ||||
|         windowVisibilityMap[window.id] = true; | ||||
|         updateTrayMenu(); | ||||
|     }); | ||||
|     mainWindow.on("hide", () => { | ||||
|         isVisible = false; | ||||
|     window.on("hide", () => { | ||||
|         windowVisibilityMap[window.id] = false; | ||||
|         updateTrayMenu(); | ||||
|     }); | ||||
|  | ||||
|     mainWindow.on("minimize", updateTrayMenu); | ||||
|     mainWindow.on("maximize", updateTrayMenu); | ||||
|     if (!isMac) { | ||||
|         // macOS uses template icons which work great on dark & light themes. | ||||
|         nativeTheme.on("updated", updateTrayMenu); | ||||
|     } | ||||
|     ipcMain.on("reload-tray", updateTrayMenu); | ||||
|     i18next.on("languageChanged", updateTrayMenu); | ||||
|     window.on("minimize", updateTrayMenu); | ||||
|     window.on("maximize", updateTrayMenu); | ||||
| } | ||||
|  | ||||
| function updateTrayMenu() { | ||||
|     const mainWindow = windowService.getMainWindow(); | ||||
|     if (!mainWindow) { | ||||
| function getWindowTitle(window: BrowserWindow | null) { | ||||
|     if (!window) { | ||||
|         return; | ||||
|     } | ||||
|     const title = window.getTitle(); | ||||
|     const titleWithoutAppName = title.replace(/\s-\s[^-]+$/, ''); // Remove the name of the app | ||||
|  | ||||
|     function ensureVisible() { | ||||
|         if (mainWindow) { | ||||
|             mainWindow.show(); | ||||
|             mainWindow.focus(); | ||||
|     // Limit title maximum length to 17 | ||||
|     if (titleWithoutAppName.length > 20) { | ||||
|         return titleWithoutAppName.substring(0, 17) + '...'; | ||||
|     } | ||||
|  | ||||
|     return titleWithoutAppName; | ||||
| } | ||||
|  | ||||
| function updateWindowVisibilityMap(allWindows: BrowserWindow[]) { | ||||
|     const currentWindowIds: number[] = allWindows.map(window => window.id); | ||||
|  | ||||
|     // Deleting closed windows from windowVisibilityMap | ||||
|     for (const [id, visibility] of Object.entries(windowVisibilityMap)) { | ||||
|         const windowId = Number(id); | ||||
|         if (!currentWindowIds.includes(windowId)) { | ||||
|             delete windowVisibilityMap[windowId]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Iterate through allWindows to make sure the ID of each window exists in windowVisibilityMap | ||||
|     allWindows.forEach(window => { | ||||
|         const windowId = window.id; | ||||
|         if (!(windowId in windowVisibilityMap)) { | ||||
|             // If it does not exist, it is the newly created window | ||||
|             windowVisibilityMap[windowId] = true; | ||||
|             registerVisibilityListener(window); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| function updateTrayMenu() { | ||||
|     const lastFocusedWindow = windowService.getLastFocusedWindow(); | ||||
|     const allWindows = windowService.getAllWindows(); | ||||
|     updateWindowVisibilityMap(allWindows); | ||||
|  | ||||
|     function ensureVisible(win: BrowserWindow) { | ||||
|         if (win) { | ||||
|             win.show(); | ||||
|             win.focus(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function openNewWindow() { | ||||
|         if (lastFocusedWindow){ | ||||
|             lastFocusedWindow.webContents.send("globalShortcut", "openNewWindow"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function triggerKeyboardAction(actionName: KeyboardActionNames) { | ||||
|         mainWindow?.webContents.send("globalShortcut", actionName); | ||||
|         ensureVisible(); | ||||
|         if (lastFocusedWindow){ | ||||
|             lastFocusedWindow.webContents.send("globalShortcut", actionName); | ||||
|             ensureVisible(lastFocusedWindow); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function openInSameTab(note: BNote | BRecentNote) { | ||||
|         mainWindow?.webContents.send("openInSameTab", note.noteId); | ||||
|         ensureVisible(); | ||||
|         if (lastFocusedWindow){ | ||||
|             lastFocusedWindow.webContents.send("openInSameTab", note.noteId); | ||||
|             ensureVisible(lastFocusedWindow); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function buildBookmarksMenu() { | ||||
| @@ -144,20 +184,44 @@ function updateTrayMenu() { | ||||
|         return menuItems; | ||||
|     } | ||||
|  | ||||
|     const contextMenu = Menu.buildFromTemplate([ | ||||
|         { | ||||
|             label: t("tray.show-windows"), | ||||
|     const windowVisibilityMenuItems: Electron.MenuItemConstructorOptions[] = []; | ||||
|  | ||||
|     // Only call getWindowTitle if windowVisibilityMap has more than one window | ||||
|     const showTitle = Object.keys(windowVisibilityMap).length > 1; | ||||
|  | ||||
|     for (const idStr in windowVisibilityMap) { | ||||
|         const id = parseInt(idStr, 10); // Get the ID of the window and make sure it is a number | ||||
|         const isVisible = windowVisibilityMap[id];  | ||||
|         const win = allWindows.find(w => w.id === id); | ||||
|         if (!win) { | ||||
|             continue; | ||||
|         } | ||||
|         windowVisibilityMenuItems.push({ | ||||
|             label: showTitle ? `${t("tray.show-windows")}: ${getWindowTitle(win)}` : t("tray.show-windows"), | ||||
|             type: "checkbox", | ||||
|             checked: isVisible, | ||||
|             click: () => { | ||||
|                 if (isVisible) { | ||||
|                     mainWindow.hide(); | ||||
|                     win.hide(); | ||||
|                     windowVisibilityMap[id] = false; | ||||
|                 } else { | ||||
|                     ensureVisible(); | ||||
|                     ensureVisible(win); | ||||
|                     windowVisibilityMap[id] = true; | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         }); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     const contextMenu = Menu.buildFromTemplate([ | ||||
|         ...windowVisibilityMenuItems,  | ||||
|         { type: "separator" }, | ||||
|         { | ||||
|             label: t("tray.open_new_window"), | ||||
|             type: "normal", | ||||
|             icon: getIconPath("new-window"), | ||||
|             click: () => openNewWindow() | ||||
|         }, | ||||
|         { | ||||
|             label: t("tray.new-note"), | ||||
|             type: "normal", | ||||
| @@ -188,7 +252,10 @@ function updateTrayMenu() { | ||||
|             type: "normal", | ||||
|             icon: getIconPath("close"), | ||||
|             click: () => { | ||||
|                 mainWindow.close(); | ||||
|                 const windows = BrowserWindow.getAllWindows(); | ||||
|                 windows.forEach(window => { | ||||
|                     window.close(); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     ]); | ||||
| @@ -197,16 +264,18 @@ function updateTrayMenu() { | ||||
| } | ||||
|  | ||||
| function changeVisibility() { | ||||
|     const window = windowService.getMainWindow(); | ||||
|     if (!window) { | ||||
|     const lastFocusedWindow = windowService.getLastFocusedWindow(); | ||||
|      | ||||
|     if (!lastFocusedWindow) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (isVisible) { | ||||
|         window.hide(); | ||||
|     // If the window is visible, hide it | ||||
|     if (windowVisibilityMap[lastFocusedWindow.id]) { | ||||
|         lastFocusedWindow.hide(); | ||||
|     } else { | ||||
|         window.show(); | ||||
|         window.focus(); | ||||
|         lastFocusedWindow.show(); | ||||
|         lastFocusedWindow.focus(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -221,9 +290,15 @@ function createTray() { | ||||
|     tray.on("click", changeVisibility); | ||||
|     updateTrayMenu(); | ||||
|  | ||||
|     registerVisibilityListener(); | ||||
|     if (!isMac) { | ||||
|         // macOS uses template icons which work great on dark & light themes. | ||||
|         nativeTheme.on("updated", updateTrayMenu); | ||||
|     } | ||||
|     ipcMain.on("reload-tray", updateTrayMenu); | ||||
|     i18next.on("languageChanged", updateTrayMenu); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     createTray | ||||
|     createTray, | ||||
|     updateTrayMenu | ||||
| }; | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import remoteMain from "@electron/remote/main/index.js"; | ||||
| import { BrowserWindow, shell, type App, type BrowserWindowConstructorOptions, type WebContents } from "electron"; | ||||
| import { dialog, ipcMain } from "electron"; | ||||
| import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js"; | ||||
| import tray from "./tray.js"; | ||||
|  | ||||
| import { fileURLToPath } from "url"; | ||||
| import { dirname } from "path"; | ||||
| @@ -19,6 +20,26 @@ import { t } from "i18next"; | ||||
| // Prevent the window being garbage collected | ||||
| let mainWindow: BrowserWindow | null; | ||||
| let setupWindow: BrowserWindow | null; | ||||
| let allWindows: BrowserWindow[] = []; // // Used to store all windows, sorted by the order of focus. | ||||
|  | ||||
| function trackWindowFocus(win: BrowserWindow) { | ||||
|     // We need to get the last focused window from allWindows. If the last window is closed, we return the previous window. | ||||
|     // Therefore, we need to push the window into the allWindows array every time it gets focused. | ||||
|     win.on("focus", () => { | ||||
|         allWindows = allWindows.filter(w => !w.isDestroyed() && w !== win); | ||||
|         allWindows.push(win); | ||||
|         if (!optionService.getOptionBool("disableTray")) { | ||||
|             tray.updateTrayMenu(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     win.on("closed", () => { | ||||
|         allWindows = allWindows.filter(w => !w.isDestroyed()); | ||||
|         if (!optionService.getOptionBool("disableTray")) { | ||||
|             tray.updateTrayMenu(); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function createExtraWindow(extraWindowHash: string) { | ||||
|     const spellcheckEnabled = optionService.getOptionBool("spellCheckEnabled"); | ||||
| @@ -42,6 +63,8 @@ async function createExtraWindow(extraWindowHash: string) { | ||||
|     win.loadURL(`http://127.0.0.1:${port}/?extraWindow=1${extraWindowHash}`); | ||||
|  | ||||
|     configureWebContents(win.webContents, spellcheckEnabled); | ||||
|  | ||||
|     trackWindowFocus(win); | ||||
| } | ||||
|  | ||||
| ipcMain.on("create-extra-window", (event, arg) => { | ||||
| @@ -154,18 +177,21 @@ async function createMainWindow(app: App) { | ||||
|     configureWebContents(mainWindow.webContents, spellcheckEnabled); | ||||
|  | ||||
|     app.on("second-instance", (event, commandLine) => { | ||||
|         const lastFocusedWindow = getLastFocusedWindow(); | ||||
|         if (commandLine.includes("--new-window")) { | ||||
|             createExtraWindow(""); | ||||
|         } else if (mainWindow) { | ||||
|         } else if (lastFocusedWindow) { | ||||
|             // Someone tried to run a second instance, we should focus our window. | ||||
|             // see www.ts "requestSingleInstanceLock" for the rest of this logic with explanation | ||||
|             if (mainWindow.isMinimized()) { | ||||
|                 mainWindow.restore(); | ||||
|             if (lastFocusedWindow.isMinimized()) { | ||||
|                 lastFocusedWindow.restore(); | ||||
|             } | ||||
|  | ||||
|             mainWindow.focus(); | ||||
|             lastFocusedWindow.show();  | ||||
|             lastFocusedWindow.focus(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     trackWindowFocus(mainWindow); | ||||
| } | ||||
|  | ||||
| function getWindowExtraOpts() { | ||||
| @@ -296,10 +322,20 @@ function getMainWindow() { | ||||
|     return mainWindow; | ||||
| } | ||||
|  | ||||
| function getLastFocusedWindow() { | ||||
|     return allWindows.length > 0 ? allWindows[allWindows.length - 1] : null; | ||||
| } | ||||
|  | ||||
| function getAllWindows(){ | ||||
|     return allWindows; | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     createMainWindow, | ||||
|     createSetupWindow, | ||||
|     closeSetupWindow, | ||||
|     registerGlobalShortcuts, | ||||
|     getMainWindow | ||||
|     getMainWindow, | ||||
|     getLastFocusedWindow, | ||||
|     getAllWindows | ||||
| }; | ||||
|   | ||||
| @@ -271,7 +271,8 @@ | ||||
|     "bookmarks": "书签", | ||||
|     "today": "打开今天的日记笔记", | ||||
|     "new-note": "新建笔记", | ||||
|     "show-windows": "显示窗口" | ||||
|     "show-windows": "显示窗口", | ||||
|     "open_new_window": "打开新窗口" | ||||
|   }, | ||||
|   "migration": { | ||||
|     "old_version": "由您当前版本的直接迁移不被支持。请先升级到最新的 v0.60.4 然后再到这个版本。", | ||||
|   | ||||
| @@ -274,7 +274,8 @@ | ||||
|     "bookmarks": "Bookmarks", | ||||
|     "today": "Open today's journal note", | ||||
|     "new-note": "New note", | ||||
|     "show-windows": "Show windows" | ||||
|     "show-windows": "Show windows", | ||||
|     "open_new_window": "Open new window" | ||||
|   }, | ||||
|   "migration": { | ||||
|     "old_version": "Direct migration from your current version is not supported. Please upgrade to the latest v0.60.4 first and only then to this version.", | ||||
|   | ||||