diff --git a/.dockerignore b/.dockerignore index 64bcb6983..1ca391a43 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,44 @@ -.git -.idea +# ignored Files +.dockerignore +.editorconfig +.git* +.prettier* +electron* +entitlements.plist +nodemon.json +renovate.json +trilium.iml +Dockerfile +Dockerfile.* +npm-debug.log +/src/**/*.spec.ts + +# ignored folders +/.cache +/.git +/.github +/.idea +/.vscode /bin +/build /dist /docs -/npm-debug.log -node_modules +/dump-db +/e2e +/integration-tests +/spec +/test +/test-etapi +/node_modules -src/**/*.ts -!src/services/asset_path.ts \ No newline at end of file + +# exceptions +!/bin/copy-dist.ts +!/bin/cleanupNodeModules.ts + +# temporary exception to make copy-dist inside Docker build not fail +# TriliumNextTODO: make copy-dist *not* requiring to copy these file for builds other than electron-forge +!forge.config.cjs +!/bin/tpl +!/bin/electron-forge/desktop.ejs +!/bin/electron-forge/sign-windows.cjs \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 9f1ce2646..c2de311c3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,13 @@ package-lock.json linguist-generated=true **/package-lock.json linguist-generated=true +src/public/app/doc_notes/en/User[[:space:]]Guide/** linguist-generated=true +src/public/app/doc_notes/en/User[[:space:]]Guide/**/*.md eol=lf + +demo/**/*.html eol=lf +demo/**/*.json eol=lf +demo/**/*.svg eol=lf +demo/**/*.txt eol=lf +demo/**/*.js eol=lf +demo/**/*.css eol=lf + libraries/** linguist-vendored \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6203d87bc..75c9d0dca 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms -github: [zadam] -custom: ["https://paypal.me/za4am"] +github: [eliandoran] +custom: ["https://paypal.me/eliandoran"] diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index dca9bc3b8..d5003ecdd 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -8,206 +8,153 @@ 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: using: composite steps: - # Certificate setup - - name: Import Apple certificates - if: inputs.os == 'macos' - uses: apple-actions/import-codesign-certs@v2 - with: - p12-file-base64: ${{ env.APPLE_APP_CERTIFICATE_BASE64 }} - p12-password: ${{ env.APPLE_APP_CERTIFICATE_PASSWORD }} - keychain: build - keychain-password: ${{ github.run_id }} + # Certificate setup + - name: Import Apple certificates + if: inputs.os == 'macos' + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ env.APPLE_APP_CERTIFICATE_BASE64 }} + p12-password: ${{ env.APPLE_APP_CERTIFICATE_PASSWORD }} + keychain: build + keychain-password: ${{ github.run_id }} - - name: Install Installer certificate - if: inputs.os == 'macos' - uses: apple-actions/import-codesign-certs@v2 - with: - p12-file-base64: ${{ env.APPLE_INSTALLER_CERTIFICATE_BASE64 }} - p12-password: ${{ env.APPLE_INSTALLER_CERTIFICATE_PASSWORD }} - keychain: build - keychain-password: ${{ github.run_id }} - # We don't need to create a keychain here because we're using the build keychain that was created in the previous step - create-keychain: false + - name: Install Installer certificate + if: inputs.os == 'macos' + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ env.APPLE_INSTALLER_CERTIFICATE_BASE64 }} + p12-password: ${{ env.APPLE_INSTALLER_CERTIFICATE_PASSWORD }} + keychain: build + keychain-password: ${{ github.run_id }} + # We don't need to create a keychain here because we're using the build keychain that was created in the previous step + create-keychain: false - - name: Verify certificates - if: inputs.os == 'macos' - shell: bash - run: | - echo "Available signing identities:" - security find-identity -v -p codesigning build.keychain + - name: Verify certificates + if: inputs.os == 'macos' + 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 - run: | - brew install python-setuptools - brew install create-dmg + - name: Set up Python and other macOS dependencies + if: ${{ inputs.os == 'macos' }} + 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 - 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 - FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) - FLATPAK_VERSION='24.08' - flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION + - name: Install dependencies for RPM and Flatpak package building + if: ${{ inputs.os == 'linux' }} + 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 + FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) + FLATPAK_VERSION='24.08' + flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION - # Build setup - - name: Install dependencies - shell: bash - run: npm ci + # Build setup + - name: Install dependencies + shell: ${{ inputs.shell }} + run: npm ci - - name: Update build info - shell: bash - run: npm run chore:update-build-info + - name: Update build info + shell: ${{ inputs.shell }} + run: npm run chore:update-build-info - # Critical debugging configuration - - name: Run electron-forge build with enhanced logging - shell: bash - 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 }} - 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 }}" + # Critical debugging configuration + - name: Run electron-forge build with enhanced logging + 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-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }} + run: npm run electron-forge:make -- --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }} + + # Add DMG signing step + - name: Sign DMG + if: inputs.os == 'macos' + shell: ${{ inputs.shell }} + run: | + echo "Signing DMG file..." + dmg_file=$(find ./dist -name "*.dmg" -print -quit) + if [ -n "$dmg_file" ]; then + echo "Found DMG: $dmg_file" + # Get the first valid signing identity from the keychain + SIGNING_IDENTITY=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -1 | sed -E 's/.*"([^"]+)".*/\1/') + if [ -z "$SIGNING_IDENTITY" ]; then + echo "Error: No valid Developer ID Application certificate found in keychain" + exit 1 fi + echo "Using signing identity: $SIGNING_IDENTITY" + # Sign the DMG + codesign --force --sign "$SIGNING_IDENTITY" --options runtime --timestamp "$dmg_file" + # Notarize the DMG + xcrun notarytool submit "$dmg_file" --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait + # Staple the notarization ticket + xcrun stapler staple "$dmg_file" + else + echo "No DMG found to sign" + fi - npm run electron-forge:make -- \ - --arch=${{ inputs.arch }} \ - --platform=$PLATFORM + - name: Verify code signing + if: inputs.os == 'macos' + shell: ${{ inputs.shell }} + run: | + echo "Verifying code signing for all artifacts..." - # Add DMG signing step - - name: Sign DMG - if: inputs.os == 'macos' - shell: bash - run: | - echo "Signing DMG file..." - dmg_file=$(find out -name "*.dmg" -print -quit) - if [ -n "$dmg_file" ]; then - echo "Found DMG: $dmg_file" - # Get the first valid signing identity from the keychain - SIGNING_IDENTITY=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -1 | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$SIGNING_IDENTITY" ]; then - echo "Error: No valid Developer ID Application certificate found in keychain" - exit 1 - fi - echo "Using signing identity: $SIGNING_IDENTITY" - # Sign the DMG - codesign --force --sign "$SIGNING_IDENTITY" --options runtime --timestamp "$dmg_file" - # Notarize the DMG - xcrun notarytool submit "$dmg_file" --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD" --team-id "$APPLE_TEAM_ID" --wait - # Staple the notarization ticket - xcrun stapler staple "$dmg_file" - else - echo "No DMG found to sign" - fi + # First check the .app bundle + echo "Looking for .app bundle..." + app_bundle=$(find ./dist -name "*.app" -print -quit) + if [ -n "$app_bundle" ]; then + echo "Found app bundle: $app_bundle" + echo "Verifying app bundle signing..." + codesign --verify --deep --strict --verbose=2 "$app_bundle" + echo "Displaying app bundle signing info..." + codesign --display --verbose=2 "$app_bundle" - - name: Verify code signing - if: inputs.os == 'macos' - shell: bash - run: | - echo "Verifying code signing for all artifacts..." + echo "Checking entitlements..." + codesign --display --entitlements :- "$app_bundle" - # First check the .app bundle - echo "Looking for .app bundle..." - app_bundle=$(find out -name "*.app" -print -quit) - if [ -n "$app_bundle" ]; then - echo "Found app bundle: $app_bundle" - echo "Verifying app bundle signing..." - codesign --verify --deep --strict --verbose=2 "$app_bundle" - echo "Displaying app bundle signing info..." - codesign --display --verbose=2 "$app_bundle" + echo "Checking notarization status..." + xcrun stapler validate "$app_bundle" || echo "Warning: App bundle not notarized yet" + else + echo "No .app bundle found to verify" + fi - echo "Checking entitlements..." - codesign --display --entitlements :- "$app_bundle" + # Then check DMG if it exists + echo "Looking for DMG..." + dmg_file=$(find ./dist -name "*.dmg" -print -quit) + if [ -n "$dmg_file" ]; then + echo "Found DMG: $dmg_file" + echo "Verifying DMG signing..." + codesign --verify --deep --strict --verbose=2 "$dmg_file" + echo "Displaying DMG signing info..." + codesign --display --verbose=2 "$dmg_file" - echo "Checking notarization status..." - xcrun stapler validate "$app_bundle" || echo "Warning: App bundle not notarized yet" - else - echo "No .app bundle found to verify" - fi + echo "Checking DMG notarization..." + xcrun stapler validate "$dmg_file" || echo "Warning: DMG not notarized yet" + else + echo "No DMG found to verify" + fi - # Then check DMG if it exists - echo "Looking for DMG..." - dmg_file=$(find out -name "*.dmg" -print -quit) - if [ -n "$dmg_file" ]; then - echo "Found DMG: $dmg_file" - echo "Verifying DMG signing..." - codesign --verify --deep --strict --verbose=2 "$dmg_file" - echo "Displaying DMG signing info..." - codesign --display --verbose=2 "$dmg_file" - - echo "Checking DMG notarization..." - xcrun stapler validate "$dmg_file" || echo "Warning: DMG not notarized yet" - else - echo "No DMG found to verify" - fi - - # Finally check ZIP if it exists - echo "Looking for ZIP..." - zip_file=$(find out -name "*.zip" -print -quit) - if [ -n "$zip_file" ]; then - 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 out -name "*.dmg" -print -quit) - if [ -n "$dmg_file" ]; then - echo "Found DMG: $dmg_file" - cp "$dmg_file" "upload/TriliumNextNotes-${{ github.ref_name }}-darwin-${{ inputs.arch }}.dmg" - else - echo "Warning: No DMG file found" - fi - - # Look for ZIP files recursively - echo "Looking for ZIP files..." - zip_file=$(find out -name "*.zip" -print -quit) - if [ -n "$zip_file" ]; then - echo "Found ZIP: $zip_file" - cp "$zip_file" "upload/TriliumNextNotes-${{ github.ref_name }}-darwin-${{ 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 out -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/ + # Finally check ZIP if it exists + echo "Looking for ZIP..." + zip_file=$(find ./dist -name "*.zip" -print -quit) + if [ -n "$zip_file" ]; then + echo "Found ZIP: $zip_file" + echo "Note: ZIP files are not code signed, but their contents should be" + fi diff --git a/.github/actions/build-server/action.yml b/.github/actions/build-server/action.yml index 7423b8c4a..13984e2ce 100644 --- a/.github/actions/build-server/action.yml +++ b/.github/actions/build-server/action.yml @@ -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 @@ -28,4 +28,5 @@ runs: run: | mkdir -p upload file=$(find dist -name '*.tar.xz' -print -quit) - cp "$file" "upload/TriliumNextNotes-Server-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.tar.xz" + name=${{ github.ref_name }} + cp "$file" "upload/TriliumNextNotes-Server-${name//\//-}-${{ inputs.os }}-${{ inputs.arch }}.tar.xz" diff --git a/.github/actions/report-size/action.yml b/.github/actions/report-size/action.yml new file mode 100644 index 000000000..e6bc477ca --- /dev/null +++ b/.github/actions/report-size/action.yml @@ -0,0 +1,79 @@ +name: 'Bundle size reporter' +description: 'Post bundle size difference compared to another branch' +inputs: + branch: + description: 'Branch to compare to' + required: true + default: 'main' + paths: + description: + 'Paths to json file bundle size report or folder containing bundles' + required: true + default: '/' + onlyDiff: + description: 'Report only different sizes' + required: false + default: 'false' + filter: + description: 'Regex filter based on file path' + required: false + unit: + description: 'Size unit' + required: false + default: 'KB' + + # Comment inputs + comment: + description: 'Post comment' + required: false + default: 'true' + header: + description: 'Comment header' + required: false + default: 'Bundle size report' + append: + description: 'Append comment' + required: false + default: 'false' + ghToken: + description: 'Github token' + required: false + +runs: + using: 'composite' + steps: + # Checkout branch to compare to [required] + - name: Checkout base branch + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + path: br-base + token: ${{ inputs.ghToken }} + + # Generate the bundle size difference report [required] + - name: Generate report + id: bundleSize + uses: nejcm/bundle-size-reporter-action@v1.4.1 + with: + paths: ${{ inputs.paths }} + onlyDiff: ${{ inputs.onlyDiff }} + filter: ${{ inputs.filter }} + unit: ${{ inputs.unit }} + + # Post github action summary + - name: Post summary + if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes + run: | + echo '${{ steps.bundleSize.outputs.summary }}' >> $GITHUB_STEP_SUMMARY + shell: bash + + # Post github action comment + - name: Post comment + uses: marocchino/sticky-pull-request-comment@v2 + if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes + with: + number: ${{ github.event.pull_request.number }} + header: ${{ inputs.header }} + append: ${{ inputs.append }} + message: '${{ steps.bundleSize.outputs.summary }}' + GITHUB_TOKEN: ${{ inputs.ghToken }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..25cd18724 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,100 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + schedule: + - cron: '20 7 * * 0' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 3afb9811c..c12db78c9 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -1,9 +1,9 @@ name: Dev on: push: - branches-ignore: - - 'develop' - - 'feature/update**' + branches: [ develop ] + pull_request: + branches: [ develop ] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -15,7 +15,73 @@ env: IMAGE_NAME: ${{ github.repository_owner }}/notes TEST_TAG: ${{ github.repository_owner }}/notes:test +permissions: + pull-requests: write # for PR comments + jobs: + report-electron-size: + name: Report Electron size + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "npm" + + - run: npm ci + + - name: Run the build + uses: ./.github/actions/build-electron + with: + os: linux + arch: x64 + shell: bash + forge_platform: linux + + - name: Run the Electron size report + uses: ./.github/actions/report-size + with: + paths: 'upload/**/*' + onlyDiff: 'true' + branch: 'develop' + header: 'Electron size report' + unit: "MB" + ghToken: ${{ secrets.GITHUB_TOKEN }} + report-server-size: + name: Report server size + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Set up node & dependencies + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "npm" + + - run: npm ci + + - name: Run the build + uses: ./.github/actions/build-server + with: + os: linux + arch: x64 + + - name: Run the server size report + uses: ./.github/actions/report-size + with: + paths: 'upload/**/*' + onlyDiff: 'true' + branch: 'develop' + header: 'Server size report' + unit: "MB" + ghToken: ${{ secrets.GITHUB_TOKEN }} + test_dev: name: Test development runs-on: ubuntu-latest @@ -26,13 +92,17 @@ jobs: - name: Set up node & dependencies uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: "npm" - run: npm ci - name: Run the TypeScript build run: npx tsc + + - name: Run the unit tests + run: npm run test + build_docker: name: Build Docker image runs-on: ubuntu-latest @@ -40,22 +110,16 @@ jobs: - test_dev steps: - uses: actions/checkout@v4 - - name: Set up node & dependencies - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "npm" - - run: npm ci - - name: Run the TypeScript build - run: npx tsc - - name: Create server-package.json - run: cat package.json | grep -v electron > server-package.json + - name: Install dependencies + run: npm ci + - name: Update build info + run: npm run chore:update-build-info - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 with: context: . cache-from: type=gha - cache-to: type=gha,mode=max + cache-to: type=gha,mode=max test_docker: name: Check Docker build runs-on: ubuntu-latest @@ -69,7 +133,13 @@ jobs: steps: - name: Checkout the repository uses: actions/checkout@v4 - + + - name: Install dependencies + run: npm ci + + - name: Update build info + run: npm run chore:update-build-info + - name: Set IMAGE_NAME to lowercase run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV - name: Set TEST_TAG to lowercase @@ -78,20 +148,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Set up node & dependencies - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "npm" - - - run: npm ci - - - name: Run the TypeScript build - run: npx tsc - - - name: Create server-package.json - run: cat package.json | grep -v electron > server-package.json - - name: Build and export to Docker uses: docker/build-push-action@v6 with: @@ -101,12 +157,12 @@ jobs: tags: ${{ env.TEST_TAG }} cache-from: type=gha cache-to: type=gha,mode=max - + - name: Validate container run output run: | CONTAINER_ID=$(docker run -d --log-driver=journald --rm --name trilium_local ${{ env.TEST_TAG }}) echo "Container ID: $CONTAINER_ID" - + - name: Wait for the healthchecks to pass uses: stringbean/docker-healthcheck-action@v3 with: diff --git a/.github/workflows/main-docker.yml b/.github/workflows/main-docker.yml index 0c1be531a..755956fb1 100644 --- a/.github/workflows/main-docker.yml +++ b/.github/workflows/main-docker.yml @@ -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 @@ -57,9 +57,6 @@ jobs: - name: Run the TypeScript build run: npx tsc - - name: Create server-package.json - run: cat package.json | grep -v electron > server-package.json - - name: Build and export to Docker uses: docker/build-push-action@v6 with: @@ -134,6 +131,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + + - name: Install dependencies + run: npm ci + + - name: Update build info + run: npm run chore:update-build-info + - name: Docker meta id: meta uses: docker/metadata-action@v5 @@ -154,18 +158,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - - name: Set up node & dependencies - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "npm" - - run: npm ci - - name: Run the TypeScript build - run: npx tsc - - name: Create server-package.json - run: cat package.json | grep -v electron > server-package.json - - name: Login to GHCR uses: docker/login-action@v3 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index b469ac79b..000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,119 +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 - - # Set up certificates and keychain for macOS - - name: Install Apple Certificates - if: matrix.os.name == 'macos' - env: - APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} - APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} - INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }} - INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }} - KEYCHAIN_PASSWORD: ${{ github.run_id }} - run: | - # Create keychain - security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain - security default-keychain -s build.keychain - security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain - security set-keychain-settings -t 3600 -u build.keychain - - # Import application certificate - echo "$APP_CERTIFICATE_BASE64" | base64 --decode > application.p12 - security import application.p12 -k build.keychain -P "$APP_CERTIFICATE_PASSWORD" -T /usr/bin/codesign - rm application.p12 - - # Import installer certificate - echo "$INSTALLER_CERTIFICATE_BASE64" | base64 --decode > installer.p12 - security import installer.p12 -k build.keychain -P "$INSTALLER_CERTIFICATE_PASSWORD" -T /usr/bin/codesign - rm installer.p12 - - # Update keychain settings - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain - - - 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_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 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c339fd37d..83b9c436c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -2,9 +2,16 @@ name: Nightly Release on: # This can be used to automatically publish nightlies at UTC nighttime schedule: - - cron: '0 2 * * *' # run at 2 AM UTC + - 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/* + - forge.config.cjs env: GITHUB_UPLOAD_URL: https://uploads.github.com/repos/TriliumNext/Notes/releases/179589950/assets{?name,label} GITHUB_RELEASE_ID: 179589950 @@ -20,22 +27,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 @@ -44,10 +53,21 @@ 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 }} + 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 }} + WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }} - name: Publish release uses: softprops/action-gh-release@v2 + if: ${{ github.event_name != 'pull_request' }} with: make_latest: false prerelease: true @@ -57,6 +77,13 @@ jobs: tag_name: nightly name: Nightly Build + - name: Publish artifacts + uses: actions/upload-artifact@v4 + if: ${{ github.event_name == 'pull_request' }} + with: + name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }} + path: upload + nightly-server: name: Deploy server nightly strategy: @@ -80,6 +107,7 @@ jobs: - name: Publish release uses: softprops/action-gh-release@v2 + if: ${{ github.event_name != 'pull_request' }} with: make_latest: false prerelease: true diff --git a/.github/workflows/release-winget.yml b/.github/workflows/release-winget.yml new file mode 100644 index 000000000..27b57e19d --- /dev/null +++ b/.github/workflows/release-winget.yml @@ -0,0 +1,20 @@ +name: Release to winget +on: + release: + types: [ published ] + workflow_dispatch: + inputs: + release_tag: + description: 'Git tag to release from' + type: string + required: true +jobs: + release-winget: + runs-on: ubuntu-latest + steps: + - name: Publish to WinGet + uses: vedantmgoyal9/winget-releaser@main + with: + identifier: TriliumNext.Notes + token: ${{ secrets.token }} + release-tag: ${{ github.event.inputs.release_tag || github.event.release.tag_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc523893b..458861c9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,6 @@ on: push: tags: - "v*" - workflow_dispatch: permissions: contents: write concurrency: @@ -20,26 +19,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,15 +51,15 @@ 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 + - name: Upload the artifact + uses: actions/upload-artifact@v4 with: - draft: true - fail_on_unmatched_files: true - files: upload/*.* + name: release-desktop-${{ matrix.os.name }}-${{ matrix.arch }} + path: upload/*.* - build_linux_server-x64: + build_server: name: Build Linux Server strategy: fail-fast: false @@ -77,9 +80,32 @@ jobs: os: linux arch: ${{ matrix.arch }} + - name: Upload the artifact + uses: actions/upload-artifact@v4 + with: + name: release-server-linux-${{ matrix.arch }} + path: upload/*.* + + publish_release: + name: Publish release + runs-on: ubuntu-latest + needs: + - make-electron + - build_server + steps: + - run: mkdir upload + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + pattern: release-* + path: upload + - name: Publish release uses: softprops/action-gh-release@v2 with: - draft: true + draft: false + body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md fail_on_unmatched_files: true files: upload/*.* diff --git a/.gitignore b/.gitignore index bc1575665..b8b091d80 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .DS_Store node_modules/ dist/ +upload/ build/ coverage/ src/public/app-dist/ @@ -11,6 +12,7 @@ po-*/ .flatpak-builder/ *.db +!test/**/*.db !integration-tests/db/document.db !integration-tests/db/config.ini integration-tests/db/log @@ -25,6 +27,7 @@ server-package.json .idea/shelf/ data/ data-test/ +data-demo/ tmp/ .eslintcache @@ -35,4 +38,13 @@ images/app-icons/mac/*.png /playwright-report/ /blob-report/ /playwright/.cache/ -/playwright/.auth/ \ No newline at end of file +/playwright/.auth/ + +data-docs/backup +data-docs/log +data-docs/session +data-docs/session_secret.txt +data-docs/document.* + +# Webpack +webpack-stats.json \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 42a133ba8..000000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -*.html -*.md -*.yml -libraries/* -docs/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 036c81de1..000000000 --- a/.prettierrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "printWidth": 200, - "tabWidth": 4, - "useTabs": false, - "semi": true, - "singleQuote": false, - "quoteProps": "as-needed", - "trailingComma": "none", - "bracketSpacing": true, - "arrowParens": "always", - "proseWrap": "preserve", - "htmlWhitespaceSensitivity": "css", - "endOfLine": "lf", - "overrides": [ - { - "files": ["*.json"], - "options": { - "tabWidth": 2 - } - } - ] -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7fc3d9975..8823f3cdd 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,11 @@ { - "recommendations": ["lokalise.i18n-ally", "editorconfig.editorconfig"] + "recommendations": [ + "lokalise.i18n-ally", + "editorconfig.editorconfig", + "vitest.explorer", + "ms-playwright.playwright", + "tobermory.es6-string-html", + "dbaeumer.vscode-eslint", + "yzhang.markdown-all-in-one" + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index cf21b9ce1..f8d4780a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,8 +5,8 @@ { "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "name": "nodemon server:start", - "program": "${workspaceFolder}/src/main", + "name": "nodemon start-server", + "program": "${workspaceFolder}/src/www", "request": "launch", "restart": true, "runtimeExecutable": "nodemon", diff --git a/.vscode/settings.json b/.vscode/settings.json index 0733583d4..dd0be9432 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,5 +19,12 @@ "[css]": { "editor.defaultFormatter": "vscode.css-language-features" }, - "npm.exclude": ["**/build", "**/dist", "**/out/**"] + "npm.exclude": [ + "**/build", + "**/dist", + "**/out/**" + ], + "[xml]": { + "editor.defaultFormatter": "redhat.vscode-xml" + } } diff --git a/Dockerfile b/Dockerfile index 365e4d07f..635a44d3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,62 +1,49 @@ # Build stage FROM node:22.14.0-bullseye-slim AS builder -# Configure build dependencies in a single layer -RUN apt-get update && apt-get install -y --no-install-recommends \ - autoconf \ - automake \ - g++ \ - gcc \ - libtool \ - make \ - nasm \ - libpng-dev \ - python3 \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /usr/src/app +WORKDIR /usr/src/app/build # Copy only necessary files for build COPY . . -COPY server-package.json package.json # Build and cleanup in a single layer -RUN cp -R build/src/* src/. && \ - cp build/docker_healthcheck.js . && \ - rm docker_healthcheck.ts && \ - npm install && \ - npm run build:webpack && \ - npm prune --omit=dev && \ +RUN npm ci && \ + npm run build:prepare-dist && \ npm cache clean --force && \ - cp -r src/public/app/doc_notes src/public/app-dist/. && \ - rm -rf src/public/app/* && \ - mkdir -p src/public/app/services && \ - cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \ - rm src/services/asset_path.ts && \ - rm -r build + rm -rf build/node_modules && \ + mv build/* \ + start-docker.sh \ + /usr/src/app/ && \ + rm -rf \ + /usr/src/app/build \ + /tmp/node-compile-cache + +#TODO: improve node_modules handling in copy-dist/Dockerfile -> remove duplicated work +# currently copy-dist will copy certain node_module folders, but in the Dockerfile we delete them again (to keep image size down), +# as we install necessary dependencies in runtime buildstage anyways # Runtime stage FROM node:22.14.0-bullseye-slim -# Install only runtime dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - gosu \ - && rm -rf /var/lib/apt/lists/* && \ - rm -rf /var/cache/apt/* - WORKDIR /usr/src/app -# Copy only necessary files from builder -COPY --from=builder /usr/src/app/node_modules ./node_modules -COPY --from=builder /usr/src/app/src ./src -COPY --from=builder /usr/src/app/db ./db -COPY --from=builder /usr/src/app/docker_healthcheck.js . -COPY --from=builder /usr/src/app/start-docker.sh . -COPY --from=builder /usr/src/app/package.json . -COPY --from=builder /usr/src/app/config-sample.ini . -COPY --from=builder /usr/src/app/images ./images -COPY --from=builder /usr/src/app/translations ./translations -COPY --from=builder /usr/src/app/libraries ./libraries +# Install only runtime dependencies +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + gosu && \ + rm -rf \ + /var/lib/apt/lists/* \ + /var/cache/apt/* + +COPY --from=builder /usr/src/app ./ + +RUN sed -i "/electron/d" package.json && \ + npm ci --omit=dev && \ + node --experimental-strip-types ./bin/cleanupNodeModules.ts . --skip-prune-dev-deps && \ + npm cache clean --force && \ + rm -rf \ + /tmp/node-compile-cache \ + /usr/src/app/bin/cleanupNodeModules.ts # Configure container EXPOSE 8080 diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 36d6f0b7b..88a450723 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,38 +1,26 @@ # Build stage FROM node:22.14.0-alpine AS builder -# Configure build dependencies -RUN apk add --no-cache --virtual .build-dependencies \ - autoconf \ - automake \ - g++ \ - gcc \ - libtool \ - make \ - nasm \ - libpng-dev \ - python3 - -WORKDIR /usr/src/app +WORKDIR /usr/src/app/build # Copy only necessary files for build COPY . . -COPY server-package.json package.json # Build and cleanup in a single layer -RUN cp -R build/src/* src/. && \ - cp build/docker_healthcheck.js . && \ - rm docker_healthcheck.ts && \ - npm install && \ - npm run build:webpack && \ - npm prune --omit=dev && \ +RUN npm ci && \ + npm run build:prepare-dist && \ npm cache clean --force && \ - cp -r src/public/app/doc_notes src/public/app-dist/. && \ - rm -rf src/public/app && \ - mkdir -p src/public/app/services && \ - cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \ - rm src/services/asset_path.ts && \ - rm -r build + rm -rf build/node_modules && \ + mv build/* \ + start-docker.sh \ + /usr/src/app/ && \ + rm -rf \ + /usr/src/app/build \ + /tmp/node-compile-cache + +#TODO: improve node_modules handling in copy-dist/Dockerfile -> remove duplicated work +# currently copy-dist will copy certain node_module folders, but in the Dockerfile we delete them again (to keep image size down), +# as we install necessary dependencies in runtime buildstage anyways # Runtime stage FROM node:22.14.0-alpine @@ -42,17 +30,15 @@ RUN apk add --no-cache su-exec shadow WORKDIR /usr/src/app -# Copy only necessary files from builder -COPY --from=builder /usr/src/app/node_modules ./node_modules -COPY --from=builder /usr/src/app/src ./src -COPY --from=builder /usr/src/app/db ./db -COPY --from=builder /usr/src/app/docker_healthcheck.js . -COPY --from=builder /usr/src/app/start-docker.sh . -COPY --from=builder /usr/src/app/package.json . -COPY --from=builder /usr/src/app/config-sample.ini . -COPY --from=builder /usr/src/app/images ./images -COPY --from=builder /usr/src/app/translations ./translations -COPY --from=builder /usr/src/app/libraries ./libraries +COPY --from=builder /usr/src/app ./ + +RUN sed -i "/electron/d" package.json && \ + npm ci --omit=dev && \ + node --experimental-strip-types ./bin/cleanupNodeModules.ts . --skip-prune-dev-deps && \ + npm cache clean --force && \ + rm -rf \ + /tmp/node-compile-cache \ + /usr/src/app/bin/cleanupNodeModules.ts # Add application user RUN adduser -s /bin/false node; exit 0 diff --git a/README.md b/README.md index 0170c8135..bb6e45836 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TriliumNext Notes -![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total) +![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran) ![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total) [English](./README.md) | [Chinese](./README-ZH_CN.md) | [Russian](./README.ru.md) | [Japanese](./README.ja.md) | [Italian](./README.it.md) | [Spanish](./README.es.md) @@ -16,7 +16,7 @@ See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for q ### Migrating from Trilium? -There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Notes instance. Just upgrade your Trilium instance to the latest version and [install TriliumNext/Notes as usual](#-installation) +There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Notes instance. Simply [install TriliumNext/Notes](#-installation) as usual and it will use your existing database. Versions up to and including [v0.90.4](https://github.com/TriliumNext/Notes/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext have their sync versions incremented. @@ -37,6 +37,7 @@ Feel free to join our official conversations. We would love to hear what feature * Fast and easy [navigation between notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text search and [note hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting) * Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions) * Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts) +* Direct OpenID and TOTP integration for more secure login * [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server * there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting) * [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet @@ -65,24 +66,15 @@ To use TriliumNext on your desktop machine (Linux, MacOS, and Windows) you have * Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the ```trilium``` executable. * Access TriliumNext via the web interface of a server installation (see below) * Currently only the latest versions of Chrome & Firefox are supported (and tested). -* (Coming Soon) TriliumNext will also be provided as a Flatpak - -#### MacOS -Currently when running TriliumNext/Notes on MacOS, you may get the following error: -> Apple could not verify "TriliumNext Notes" is free of malware and may harm your Mac or compromise your privacy. - -You will need to run the command on your shell to resolve the error (documented [here](https://github.com/TriliumNext/Notes/issues/329#issuecomment-2287164137)): - -```bash -xattr -c "/path/to/Trilium Next.app" -``` +* TriliumNext is also provided as a Flatpak, but not yet published on FlatHub. ### Mobile -To use TriliumNext on a mobile device: +To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below). -* Use a mobile web browser to access the mobile interface of a server installation (see below) -* Use of a mobile app is not yet supported ([see here](https://github.com/TriliumNext/Notes/issues/72)) to track mobile improvements. +If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid). Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid). + +See issue https://github.com/TriliumNext/Notes/issues/72 for more information on mobile app support. ### Server @@ -105,9 +97,11 @@ npm install npm run server:start ``` +For more details, see the [development docs](https://github.com/TriliumNext/Notes/blob/develop/docs/Developer%20Guide/Developer%20Guide/Building%20and%20deployment/Running%20a%20development%20build.md). + ### Documentation -Head on over to our [Docs repo](https://github.com/TriliumNext/Docs) +See the [documentation guide](https://github.com/TriliumNext/Notes/blob/develop/docs/Developer%20Guide/Developer%20Guide/Documentation.md) for details. ## 👏 Shoutouts diff --git a/_check_ts_progress.sh b/_check_ts_progress.sh deleted file mode 100755 index 7332a6054..000000000 --- a/_check_ts_progress.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -cd src/public -echo Summary -cloc HEAD \ - --git --md \ - --include-lang=javascript,typescript - -echo By file -cloc HEAD \ - --git --md \ - --include-lang=javascript,typescript \ - --by-file | grep \.js\| \ No newline at end of file diff --git a/bin/build-docker.sh b/bin/build-docker.sh index a765930db..d95c289d4 100755 --- a/bin/build-docker.sh +++ b/bin/build-docker.sh @@ -5,11 +5,6 @@ set -e # Fail on any command error VERSION=`jq -r ".version" package.json` SERIES=${VERSION:0:4}-latest -cat package.json | grep -v electron > server-package.json - -echo "Compiling typescript..." -npx tsc - sudo docker build -t triliumnext/notes:$VERSION --network host -t triliumnext/notes:$SERIES . if [[ $VERSION != *"beta"* ]]; then diff --git a/bin/build-server.sh b/bin/build-server.sh index 47bfe9397..93102737c 100755 --- a/bin/build-server.sh +++ b/bin/build-server.sh @@ -21,56 +21,53 @@ fi echo "Selected Arch: $ARCH" # Set Node.js version and architecture-specific filename -NODE_VERSION=20.15.1 -NODE_ARCH=$ARCH +NODE_VERSION=22.14.0 -# Debug output -echo "Node arch: $NODE_ARCH" +BUILD_DIR="./build" +DIST_DIR="./dist" +CLEANUP_SCRIPT="./bin/cleanupNodeModules.ts" -# Special case for x64 in Node.js downloads -if [ "$NODE_ARCH" = "x64" ]; then - NODE_FILENAME="x64" -elif [ "$NODE_ARCH" = "arm64" ]; then - NODE_FILENAME="arm64" -fi -# Debug output -echo "Node filename: $NODE_FILENAME" +# Trigger the build +echo "Build start" +npm run build:prepare-dist +echo "Build finished" -PKG_DIR=dist/trilium-linux-${ARCH}-server -echo "Package directory: $PKG_DIR" +# pruning of unnecessary files and devDeps in node_modules +node --experimental-strip-types $CLEANUP_SCRIPT $BUILD_DIR -if [ "$1" != "DONTCOPY" ] -then - # Need to modify copy-trilium.sh to accept the target directory - ./bin/copy-trilium.sh "$PKG_DIR" -fi +NODE_FILENAME=node-v${NODE_VERSION}-linux-${ARCH} -cd dist -wget https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_FILENAME}.tar.xz -tar xfJ node-v${NODE_VERSION}-linux-${NODE_FILENAME}.tar.xz -rm node-v${NODE_VERSION}-linux-${NODE_FILENAME}.tar.xz +echo "Downloading Node.js runtime $NODE_FILENAME..." +cd $BUILD_DIR +wget -qO- https://nodejs.org/dist/v${NODE_VERSION}/${NODE_FILENAME}.tar.xz | tar xfJ - +mv $NODE_FILENAME node cd .. -mv dist/node-v${NODE_VERSION}-linux-${NODE_FILENAME} $PKG_DIR/node -rm -r $PKG_DIR/node/lib/node_modules/npm -rm -r $PKG_DIR/node/include/node +rm -r $BUILD_DIR/node/lib/node_modules/{npm,corepack} \ + $BUILD_DIR/node/bin/{npm,npx,corepack} \ + $BUILD_DIR/node/CHANGELOG.md \ + $BUILD_DIR/node/include/node \ + $BUILD_DIR/node_modules/electron* \ + $BUILD_DIR/electron*.{js,map} -rm -r $PKG_DIR/node_modules/electron* -rm -r $PKG_DIR/electron*.js +printf "#!/bin/sh\n./node/bin/node src/main\n" > $BUILD_DIR/trilium.sh +chmod 755 $BUILD_DIR/trilium.sh -printf "#!/bin/sh\n./node/bin/node src/main" > $PKG_DIR/trilium.sh -chmod 755 $PKG_DIR/trilium.sh - -cp bin/tpl/anonymize-database.sql $PKG_DIR/ - -cp -r translations $PKG_DIR/ -cp -r dump-db $PKG_DIR/ -rm -rf $PKG_DIR/dump-db/node_modules +# TriliumNextTODO: is this still required? If yes → move to copy-dist/copy-trilium +cp bin/tpl/anonymize-database.sql $BUILD_DIR/ VERSION=`jq -r ".version" package.json` -cd dist -tar cJf trilium-linux-${ARCH}-server-${VERSION}.tar.xz trilium-linux-${ARCH}-server +ARCHIVE_NAME="TriliumNextNotes-Server-${VERSION}-linux-${ARCH}" +echo "Creating Archive $ARCHIVE_NAME..." + +mkdir $DIST_DIR +cp -r "$BUILD_DIR" "$DIST_DIR/$ARCHIVE_NAME" +cd $DIST_DIR +tar cJf "$ARCHIVE_NAME.tar.xz" "$ARCHIVE_NAME" +rm -rf "$ARCHIVE_NAME" + +echo "Server Build Completed!" \ No newline at end of file diff --git a/bin/build.sh b/bin/build.sh deleted file mode 100755 index 7fb41cfcb..000000000 --- a/bin/build.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -set -e # Fail on any command error - -if ! command -v jq &> /dev/null; then - echo "Missing command: jq" - exit 1 -fi - -if ! command -v fakeroot &> /dev/null; then - echo "Missing command: fakeroot" - exit 1 -fi - -if ! command -v dpkg-deb &> /dev/null; then - echo "Missing command: dpkg-deb" - exit 1 -fi - -if dpkg-deb 2>&1 | grep BusyBox &> /dev/null; then - echo "The dpkg-deb binary provided by BusyBox is not compatible. The Debian tool needs to be used instead." - exit 1 -fi - -if ! command -v wine &> /dev/null; then - echo "Missing command: wine" - exit 1 -fi - -echo "Deleting existing builds" - -rm -rf dist/* - -SRC_DIR=dist/trilium-src - -bin/copy-trilium.sh $SRC_DIR - -# we'll just copy the same SRC dir to all the builds so we don't have to do npm install in each separately -cp -r $SRC_DIR ./dist/trilium-linux-x64-src -cp -r $SRC_DIR ./dist/trilium-linux-x64-server -cp -r $SRC_DIR ./dist/trilium-windows-x64-src -cp -r $SRC_DIR ./dist/trilium-mac-x64-src -cp -r $SRC_DIR ./dist/trilium-mac-arm64-src - -bin/build-win-x64.sh DONTCOPY - -bin/build-mac-x64.sh DONTCOPY - -bin/build-mac-arm64.sh DONTCOPY - -bin/build-linux-x64.sh DONTCOPY - -bin/build-server.sh DONTCOPY diff --git a/bin/cleanupNodeModules.ts b/bin/cleanupNodeModules.ts new file mode 100644 index 000000000..bb3e90331 --- /dev/null +++ b/bin/cleanupNodeModules.ts @@ -0,0 +1,109 @@ +import fs from "fs-extra"; +import path from "path"; +import type { Dirent } from "fs-extra"; +import { execSync } from "node:child_process"; + +/** + * Example usage with node >= v22: + * node --experimental-strip-types bin/cleanupNodeModules.ts /path/to/build/folder [--skip-prune-dev-deps] + * Example usage with tsx: + * tsx bin/cleanupNodeModules.ts /path/to/build/folder [--skip-prune-dev-deps] + */ +function main() { + + if (process.argv.length > 4 || process.argv.length < 3) { + console.error("Usage: cleanupNodeModules.ts [path-to-build-folder] [--skip-prune-dev-deps]"); + process.exit(1); + } + + const basePath = process.argv[2]; + const pruneDevDeps = process.argv[3] !== "--skip-prune-dev-deps"; + + if (!fs.existsSync(basePath)) { + console.error(`Supplied path '${basePath}' does not exist. Aborting.`); + process.exit(1); + } + + console.log(`Starting pruning of node_modules ${!pruneDevDeps ? '(skipping npm pruning)' : ''} in '${basePath}'...`); + cleanupNodeModules(basePath, pruneDevDeps); + console.log("Successfully pruned node_modules."); +} + +function cleanupNodeModules(basePath: string, pruneDevDeps: boolean = true) { + + // This needs to run for the server and Docker build, + // but needs to be skipped for electron-forge: its + // built-in pruning takes care of it already + if (pruneDevDeps) { + execSync(`npm ci --omit=dev --prefix ${basePath}`); + } + + const nodeModulesDirPath = path.join(basePath, "node_modules"); + const nodeModulesContent = fs.readdirSync(nodeModulesDirPath, { recursive: true, withFileTypes: true }); + //const libDir = fs.readdirSync(path.join(basePath, "./libraries"), { recursive: true, withFileTypes: true }); + + /** + * Delete unnecessary folders + */ + const filterableDirs = new Set([ + "demo", + "demos", + "doc", + "docs", + "example", + "examples", + "test", + "tests" + ]); + + nodeModulesContent + .filter(el => el.isDirectory() && filterableDirs.has(el.name)) + .forEach(dir => removeDirent(dir)); + + /** + * Delete unnecessary files based on file extension + * TODO filter out useless (README).md files + */ + const filterableFileExt = new Set([ + "ts", + "map" + ]); + + nodeModulesContent + // TriliumNextTODO: check if we can improve this naive file ext matching, without introducing any additional dependency + .filter(el => el.isFile() && filterableFileExt.has(el.name.split(".").at(-1) || "")) + .forEach(dir => removeDirent(dir)); + + + /** + * Delete specific unnecessary folders + * TODO: check if we want removeSync to throw an error, if path does not exist anymore -> currently it will silently fail + */ + const extraFoldersDelete = new Set([ + path.join(nodeModulesDirPath, ".bin"), + path.join(nodeModulesDirPath, "@excalidraw", "excalidraw", "dist", "dev"), + path.join(nodeModulesDirPath, "boxicons", "svg"), + path.join(nodeModulesDirPath, "boxicons", "node_modules"), + path.join(nodeModulesDirPath, "boxicons", "src"), + path.join(nodeModulesDirPath, "boxicons", "iconjar"), + path.join(nodeModulesDirPath, "@jimp", "plugin-print", "fonts"), + path.join(nodeModulesDirPath, "jimp", "dist", "browser") // missing "@" in front of jimp is not a typo here + ]); + + nodeModulesContent + .filter(el => el.isDirectory() && extraFoldersDelete.has(path.join(el.parentPath, el.name))) + .forEach(dir => removeDirent(dir)) +} + + +function removeDirent(el: Dirent) { + const elementToDelete = path.join(el.parentPath, el.name); + fs.removeSync(elementToDelete); + + if (process.env.VERBOSE) { + console.log(`Deleted ${elementToDelete}`); + } + +} + +main() \ No newline at end of file diff --git a/bin/copy-dist.ts b/bin/copy-dist.ts index 2a2b75d56..404cf0991 100644 --- a/bin/copy-dist.ts +++ b/bin/copy-dist.ts @@ -1,9 +1,7 @@ import fs from "fs-extra"; import path from "path"; -const DEST_DIR = "./dist"; -const DEST_DIR_SRC = path.join(DEST_DIR, "src"); -const DEST_DIR_NODE_MODULES = path.join(DEST_DIR, "node_modules"); +const DEST_DIR = "./build"; const VERBOSE = process.env.VERBOSE; @@ -13,43 +11,40 @@ function log(...args: any[]) { } } -async function copyNodeModuleFileOrFolder(source: string) { - const adjustedSource = source.substring(13); - const destination = path.join(DEST_DIR_NODE_MODULES, adjustedSource); +try { - log(`Copying ${source} to ${destination}`); - await fs.ensureDir(path.dirname(destination)); - await fs.copy(source, destination); -} - -const copy = async () => { - for (const srcFile of fs.readdirSync("build")) { - const destFile = path.join(DEST_DIR, path.basename(srcFile)); - log(`Copying source ${srcFile} -> ${destFile}.`); - fs.copySync(path.join("build", srcFile), destFile, { recursive: true }); - } - - const filesToCopy = [ - "config-sample.ini", - "tsconfig.webpack.json", + const assetsToCopy = new Set([ + // copy node_module, to avoid downloading packages a 2nd time during pruning + "./node_modules", + "./images", + "./libraries", + "./translations", + "./db", + "./config-sample.ini", + "./package-lock.json", + "./package.json", + "./LICENSE", + "./README.md", + "./forge.config.cjs", + "./bin/tpl/", + "./bin/cleanupNodeModules.ts", + "./bin/electron-forge/desktop.ejs", + "./bin/electron-forge/sign-windows.cjs", + "./src/views/", "./src/etapi/etapi.openapi.yaml", - "./src/routes/api/openapi.json" - ]; - for (const file of filesToCopy) { - log(`Copying ${file}`); - await fs.copy(file, path.join(DEST_DIR, file)); - } + "./src/routes/api/openapi.json", + "./src/public/icon.png", + "./src/public/manifest.webmanifest", + "./src/public/robots.txt", + "./src/public/fonts", + "./src/public/stylesheets", + "./src/public/translations", + "./packages/turndown-plugin-gfm/src" + ]); - const dirsToCopy = ["images", "libraries", "translations", "db"]; - for (const dir of dirsToCopy) { - log(`Copying ${dir}`); - await fs.copy(dir, path.join(DEST_DIR, dir)); - } - - const srcDirsToCopy = ["./src/public", "./src/views", "./build"]; - for (const dir of srcDirsToCopy) { - log(`Copying ${dir}`); - await fs.copy(dir, path.join(DEST_DIR_SRC, path.basename(dir))); + for (const asset of assetsToCopy) { + log(`Copying ${asset}`); + fs.copySync(asset, path.join(DEST_DIR, asset)); } /** @@ -58,59 +53,13 @@ const copy = async () => { const publicDirsToCopy = ["./src/public/app/doc_notes"]; const PUBLIC_DIR = path.join(DEST_DIR, "src", "public", "app-dist"); for (const dir of publicDirsToCopy) { - await fs.copy(dir, path.join(PUBLIC_DIR, path.basename(dir))); + fs.copySync(dir, path.join(PUBLIC_DIR, path.basename(dir))); } - const nodeModulesFile = [ - "node_modules/react/umd/react.production.min.js", - "node_modules/react/umd/react.development.js", - "node_modules/react-dom/umd/react-dom.production.min.js", - "node_modules/react-dom/umd/react-dom.development.js", - "node_modules/katex/dist/katex.min.js", - "node_modules/katex/dist/contrib/mhchem.min.js", - "node_modules/katex/dist/contrib/auto-render.min.js", - "node_modules/@highlightjs/cdn-assets/highlight.min.js", - "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs" - ]; + console.log("Copying complete!") - for (const file of nodeModulesFile) { - await copyNodeModuleFileOrFolder(file); - } +} catch(err) { + console.error("Error during copy:", err) + process.exit(1) +} - const nodeModulesFolder = [ - "node_modules/@excalidraw/excalidraw/dist/", - "node_modules/katex/dist/", - "node_modules/dayjs/", - "node_modules/boxicons/css/", - "node_modules/boxicons/fonts/", - "node_modules/mermaid/dist/", - "node_modules/jquery/dist/", - "node_modules/jquery-hotkeys/", - "node_modules/split.js/dist/", - "node_modules/panzoom/dist/", - "node_modules/i18next/", - "node_modules/i18next-http-backend/", - "node_modules/jsplumb/dist/", - "node_modules/vanilla-js-wheel-zoom/dist/", - "node_modules/mark.js/dist/", - "node_modules/normalize.css/", - "node_modules/jquery.fancytree/dist/", - "node_modules/autocomplete.js/dist/", - "node_modules/codemirror/lib/", - "node_modules/codemirror/addon/", - "node_modules/codemirror/mode/", - "node_modules/codemirror/keymap/", - "node_modules/mind-elixir/dist/", - "node_modules/@highlightjs/cdn-assets/languages", - "node_modules/@highlightjs/cdn-assets/styles", - "node_modules/leaflet/dist" - ]; - - for (const folder of nodeModulesFolder) { - await copyNodeModuleFileOrFolder(folder); - } -}; - -copy() - .then(() => console.log("Copying complete!")) - .catch((err) => console.error("Error during copy:", err)); diff --git a/bin/copy-trilium.sh b/bin/copy-trilium.sh deleted file mode 100755 index e1d0e197f..000000000 --- a/bin/copy-trilium.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -set -e # Fail on any command error -shopt -s globstar - -if [[ $# -eq 0 ]] ; then - echo "Missing argument of target directory" - exit 1 -fi -if ! [[ $(which npm) ]]; then - echo "Missing npm" - exit 1 -fi - -# Trigger the TypeScript build -echo TypeScript build start -npx tsc -echo TypeScript build finished - -# Copy the TypeScript artifacts -DIR="$1" -rm -rf "$DIR" -mkdir -pv "$DIR" - -echo Webpack start -npm run build:webpack -echo Webpack finish - -echo "Copying Trilium to build directory $DIR" - -for d in 'images' 'libraries' 'src' 'db'; do - cp -r "$d" "$DIR"/ -done - -for f in 'package.json' 'package-lock.json' 'README.md' 'LICENSE' 'config-sample.ini'; do - cp "$f" "$DIR"/ -done - -# Patch package.json main -sed -i 's/.\/dist\/electron-main.js/electron-main.js/g' "$DIR/package.json" - -script_dir=$(realpath $(dirname $0)) -cp -R "$script_dir/../build/src" "$DIR" -cp "$script_dir/../build/electron-main.js" "$DIR" - -# run in subshell (so we return to original dir) -(cd $DIR && npm install --omit=dev --legacy-peer-deps) - -if [[ -d "$DIR"/node_modules ]]; then - # cleanup of useless files in dependencies - for d in 'image-q/demo' \ - '@excalidraw/excalidraw/dist/excalidraw-assets-dev' '@excalidraw/excalidraw/dist/excalidraw.development.js' '@excalidraw/excalidraw/dist/excalidraw-with-preact.development.js' \ - 'mermaid/dist/mermaid.js' \ - 'boxicons/svg' 'boxicons/node_modules/react'/* \ - '@jimp/plugin-print/fonts' 'jimp/browser' 'jimp/fonts'; do - [[ -e "$DIR"/node_modules/"$d" ]] && rm -r "$DIR"/node_modules/"$d" - done - - # delete all tests (there are often large images as test file for jimp etc.) - for d in 'test' 'docs' 'demo' 'example'; do - find "$DIR"/node_modules -name "$d" -exec rm -rf {} + - done -fi - -find $DIR/libraries -name "*.map" -type f -delete -find $DIR/node_modules -name "*.map" -type f -delete -find $DIR -name "*.ts" -type f -delete - -d="$DIR"/src/public -[[ -d "$d"/app-dist ]] || mkdir -pv "$d"/app-dist -cp -r "$d"/app/doc_notes "$d"/app-dist/ - -rm -rf "$d"/app -unset f d DIR diff --git a/bin/docs/.env.example b/bin/docs/.env.example deleted file mode 100644 index 03006fb86..000000000 --- a/bin/docs/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -SHARE_PROTOCOL=http -SHARE_HOST=notes.johnsmith.me -ROOT_NOTE_ID=4yYHqKbLovVX \ No newline at end of file diff --git a/bin/docs/.gitignore b/bin/docs/.gitignore deleted file mode 100644 index 5ad213baa..000000000 --- a/bin/docs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -output -.env \ No newline at end of file diff --git a/bin/docs/assets/v0.63.6/app-dist/share.js b/bin/docs/assets/v0.63.6/app-dist/share.js deleted file mode 100644 index 3c3282497..000000000 --- a/bin/docs/assets/v0.63.6/app-dist/share.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Fetch note with given ID from backend - * - * @param noteId of the given note to be fetched. If false, fetches current note. - */ -async function fetchNote(noteId = null) { - if (!noteId) { - noteId = document.body.getAttribute("data-note-id"); - } - - const resp = await fetch(`api/notes/${noteId}`); - - return await resp.json(); -} - -document.addEventListener( - "DOMContentLoaded", - () => { - const toggleMenuButton = document.getElementById("toggleMenuButton"); - const layout = document.getElementById("layout"); - - if (toggleMenuButton && layout) { - toggleMenuButton.addEventListener("click", () => layout.classList.toggle("showMenu")); - } - }, - false -); diff --git a/bin/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css b/bin/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css deleted file mode 100644 index 41d0ab466..000000000 --- a/bin/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css +++ /dev/null @@ -1,554 +0,0 @@ -/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */ - -.printed-content .ck-widget__selection-handle, -.printed-content .ck-widget__type-around { - /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */ - display: none; -} - -/* - * CKEditor 5 (v41.0.0) content styles. - * Generated on Fri, 26 Jan 2024 10:23:49 GMT. - * For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html - */ - -:root { - --ck-color-image-caption-background: hsl(0, 0%, 97%); - --ck-color-image-caption-text: hsl(0, 0%, 20%); - --ck-color-mention-background: hsla(341, 100%, 30%, 0.1); - --ck-color-mention-text: hsl(341, 100%, 30%); - --ck-color-selector-caption-background: hsl(0, 0%, 97%); - --ck-color-selector-caption-text: hsl(0, 0%, 20%); - --ck-highlight-marker-blue: hsl(201, 97%, 72%); - --ck-highlight-marker-green: hsl(120, 93%, 68%); - --ck-highlight-marker-pink: hsl(345, 96%, 73%); - --ck-highlight-marker-yellow: hsl(60, 97%, 73%); - --ck-highlight-pen-green: hsl(112, 100%, 27%); - --ck-highlight-pen-red: hsl(0, 85%, 49%); - --ck-image-style-spacing: 1.5em; - --ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2); - --ck-todo-list-checkmark-size: 16px; -} - -/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ -.ck-content .table .ck-table-resized { - table-layout: fixed; -} -/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ -.ck-content .table table { - overflow: hidden; -} -/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */ -.ck-content .table td, -.ck-content .table th { - overflow-wrap: break-word; - position: relative; -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content .table { - margin: 0.9em auto; - display: table; -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content .table table { - border-collapse: collapse; - border-spacing: 0; - width: 100%; - height: 100%; - border: 1px double hsl(0, 0%, 70%); -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content .table table td, -.ck-content .table table th { - min-width: 2em; - padding: 0.4em; - border: 1px solid hsl(0, 0%, 75%); -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content .table table th { - font-weight: bold; - background: hsla(0, 0%, 0%, 5%); -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content[dir="rtl"] .table th { - text-align: right; -} -/* @ckeditor/ckeditor5-table/theme/table.css */ -.ck-content[dir="ltr"] .table th { - text-align: left; -} -/* @ckeditor/ckeditor5-table/theme/tablecaption.css */ -.ck-content .table > figcaption { - display: table-caption; - caption-side: top; - word-break: break-word; - text-align: center; - color: var(--ck-color-selector-caption-text); - background-color: var(--ck-color-selector-caption-background); - padding: 0.6em; - font-size: 0.75em; - outline-offset: -1px; -} -/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */ -.ck-content .page-break { - position: relative; - clear: both; - padding: 5px 0; - display: flex; - align-items: center; - justify-content: center; -} -/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */ -.ck-content .page-break::after { - content: ""; - position: absolute; - border-bottom: 2px dashed hsl(0, 0%, 77%); - width: 100%; -} -/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */ -.ck-content .page-break__label { - position: relative; - z-index: 1; - padding: 0.3em 0.6em; - display: block; - text-transform: uppercase; - border: 1px solid hsl(0, 0%, 77%); - border-radius: 2px; - font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif; - font-size: 0.75em; - font-weight: bold; - color: hsl(0, 0%, 20%); - background: hsl(0, 0%, 100%); - box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */ -.ck-content .media { - clear: both; - margin: 0.9em 0; - display: block; - min-width: 15em; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list { - list-style: none; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list li { - position: relative; - margin-bottom: 5px; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list li .todo-list { - margin-top: 5px; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label > input { - -webkit-appearance: none; - display: inline-block; - position: relative; - width: var(--ck-todo-list-checkmark-size); - height: var(--ck-todo-list-checkmark-size); - vertical-align: middle; - border: 0; - left: -25px; - margin-right: -15px; - right: 0; - margin-left: 0; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content[dir="rtl"] .todo-list .todo-list__label > input { - left: 0; - margin-right: 0; - right: -25px; - margin-left: -15px; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label > input::before { - display: block; - position: absolute; - box-sizing: border-box; - content: ""; - width: 100%; - height: 100%; - border: 1px solid hsl(0, 0%, 20%); - border-radius: 2px; - transition: 250ms ease-in-out box-shadow; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label > input::after { - display: block; - position: absolute; - box-sizing: content-box; - pointer-events: none; - content: ""; - left: calc(var(--ck-todo-list-checkmark-size) / 3); - top: calc(var(--ck-todo-list-checkmark-size) / 5.3); - width: calc(var(--ck-todo-list-checkmark-size) / 5.3); - height: calc(var(--ck-todo-list-checkmark-size) / 2.6); - border-style: solid; - border-color: transparent; - border-width: 0 calc(var(--ck-todo-list-checkmark-size) / 8) calc(var(--ck-todo-list-checkmark-size) / 8) 0; - transform: rotate(45deg); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label > input[checked]::before { - background: hsl(126, 64%, 41%); - border-color: hsl(126, 64%, 41%); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label > input[checked]::after { - border-color: hsl(0, 0%, 100%); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label .todo-list__label__description { - vertical-align: middle; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-content .todo-list .todo-list__label.todo-list__label_without-description input[type="checkbox"] { - position: absolute; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > input, -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input { - cursor: pointer; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > input:hover::before, -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input:hover::before { - box-shadow: 0 0 0 5px hsla(0, 0%, 0%, 0.1); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input { - -webkit-appearance: none; - display: inline-block; - position: relative; - width: var(--ck-todo-list-checkmark-size); - height: var(--ck-todo-list-checkmark-size); - vertical-align: middle; - border: 0; - left: -25px; - margin-right: -15px; - right: 0; - margin-left: 0; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content[dir="rtl"] .todo-list .todo-list__label > span[contenteditable="false"] > input { - left: 0; - margin-right: 0; - right: -25px; - margin-left: -15px; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input::before { - display: block; - position: absolute; - box-sizing: border-box; - content: ""; - width: 100%; - height: 100%; - border: 1px solid hsl(0, 0%, 20%); - border-radius: 2px; - transition: 250ms ease-in-out box-shadow; -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input::after { - display: block; - position: absolute; - box-sizing: content-box; - pointer-events: none; - content: ""; - left: calc(var(--ck-todo-list-checkmark-size) / 3); - top: calc(var(--ck-todo-list-checkmark-size) / 5.3); - width: calc(var(--ck-todo-list-checkmark-size) / 5.3); - height: calc(var(--ck-todo-list-checkmark-size) / 2.6); - border-style: solid; - border-color: transparent; - border-width: 0 calc(var(--ck-todo-list-checkmark-size) / 8) calc(var(--ck-todo-list-checkmark-size) / 8) 0; - transform: rotate(45deg); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input[checked]::before { - background: hsl(126, 64%, 41%); - border-color: hsl(126, 64%, 41%); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input[checked]::after { - border-color: hsl(0, 0%, 100%); -} -/* @ckeditor/ckeditor5-list/theme/todolist.css */ -.ck-editor__editable.ck-content .todo-list .todo-list__label.todo-list__label_without-description input[type="checkbox"] { - position: absolute; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ol { - list-style-type: decimal; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ol ol { - list-style-type: lower-latin; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ol ol ol { - list-style-type: lower-roman; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ol ol ol ol { - list-style-type: upper-latin; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ol ol ol ol ol { - list-style-type: upper-roman; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ul { - list-style-type: disc; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ul ul { - list-style-type: circle; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ul ul ul { - list-style-type: square; -} -/* @ckeditor/ckeditor5-list/theme/list.css */ -.ck-content ul ul ul ul { - list-style-type: square; -} -/* @ckeditor/ckeditor5-image/theme/image.css */ -.ck-content .image { - display: table; - clear: both; - text-align: center; - margin: 0.9em auto; - min-width: 50px; -} -/* @ckeditor/ckeditor5-image/theme/image.css */ -.ck-content .image img { - display: block; - margin: 0 auto; - max-width: 100%; - min-width: 100%; - height: auto; -} -/* @ckeditor/ckeditor5-image/theme/image.css */ -.ck-content .image-inline { - /* - * Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).; - * Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root. - * This strange behavior does not happen with inline-flex. - */ - display: inline-flex; - max-width: 100%; - align-items: flex-start; -} -/* @ckeditor/ckeditor5-image/theme/image.css */ -.ck-content .image-inline picture { - display: flex; -} -/* @ckeditor/ckeditor5-image/theme/image.css */ -.ck-content .image-inline picture, -.ck-content .image-inline img { - flex-grow: 1; - flex-shrink: 1; - max-width: 100%; -} -/* @ckeditor/ckeditor5-image/theme/imageresize.css */ -.ck-content img.image_resized { - height: auto; -} -/* @ckeditor/ckeditor5-image/theme/imageresize.css */ -.ck-content .image.image_resized { - max-width: 100%; - display: block; - box-sizing: border-box; -} -/* @ckeditor/ckeditor5-image/theme/imageresize.css */ -.ck-content .image.image_resized img { - width: 100%; -} -/* @ckeditor/ckeditor5-image/theme/imageresize.css */ -.ck-content .image.image_resized > figcaption { - display: block; -} -/* @ckeditor/ckeditor5-image/theme/imagecaption.css */ -.ck-content .image > figcaption { - display: table-caption; - caption-side: bottom; - word-break: break-word; - color: var(--ck-color-image-caption-text); - background-color: var(--ck-color-image-caption-background); - padding: 0.6em; - font-size: 0.75em; - outline-offset: -1px; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-block-align-left, -.ck-content .image-style-block-align-right { - max-width: calc(100% - var(--ck-image-style-spacing)); -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-align-left, -.ck-content .image-style-align-right { - clear: none; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-side { - float: right; - margin-left: var(--ck-image-style-spacing); - max-width: 50%; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-align-left { - float: left; - margin-right: var(--ck-image-style-spacing); -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-align-center { - margin-left: auto; - margin-right: auto; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-align-right { - float: right; - margin-left: var(--ck-image-style-spacing); -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-block-align-right { - margin-right: 0; - margin-left: auto; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-style-block-align-left { - margin-left: 0; - margin-right: auto; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content p + .image-style-align-left, -.ck-content p + .image-style-align-right, -.ck-content p + .image-style-side { - margin-top: 0; -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-inline.image-style-align-left, -.ck-content .image-inline.image-style-align-right { - margin-top: var(--ck-inline-image-style-spacing); - margin-bottom: var(--ck-inline-image-style-spacing); -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-inline.image-style-align-left { - margin-right: var(--ck-inline-image-style-spacing); -} -/* @ckeditor/ckeditor5-image/theme/imagestyle.css */ -.ck-content .image-inline.image-style-align-right { - margin-left: var(--ck-inline-image-style-spacing); -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .marker-yellow { - background-color: var(--ck-highlight-marker-yellow); -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .marker-green { - background-color: var(--ck-highlight-marker-green); -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .marker-pink { - background-color: var(--ck-highlight-marker-pink); -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .marker-blue { - background-color: var(--ck-highlight-marker-blue); -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .pen-red { - color: var(--ck-highlight-pen-red); - background-color: transparent; -} -/* @ckeditor/ckeditor5-highlight/theme/highlight.css */ -.ck-content .pen-green { - color: var(--ck-highlight-pen-green); - background-color: transparent; -} -/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */ -.ck-content blockquote { - overflow: hidden; - padding-right: 1.5em; - padding-left: 1.5em; - margin-left: 0; - margin-right: 0; - font-style: italic; - border-left: solid 5px hsl(0, 0%, 80%); -} -/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */ -.ck-content[dir="rtl"] blockquote { - border-left: 0; - border-right: solid 5px hsl(0, 0%, 80%); -} -/* @ckeditor/ckeditor5-basic-styles/theme/code.css */ -.ck-content code { - background-color: hsla(0, 0%, 78%, 0.3); - padding: 0.15em; - border-radius: 2px; -} -/* @ckeditor/ckeditor5-font/theme/fontsize.css */ -.ck-content .text-tiny { - font-size: 0.7em; -} -/* @ckeditor/ckeditor5-font/theme/fontsize.css */ -.ck-content .text-small { - font-size: 0.85em; -} -/* @ckeditor/ckeditor5-font/theme/fontsize.css */ -.ck-content .text-big { - font-size: 1.4em; -} -/* @ckeditor/ckeditor5-font/theme/fontsize.css */ -.ck-content .text-huge { - font-size: 1.8em; -} -/* @ckeditor/ckeditor5-mention/theme/mention.css */ -.ck-content .mention { - background: var(--ck-color-mention-background); - color: var(--ck-color-mention-text); -} -/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */ -.ck-content hr { - margin: 15px 0; - height: 4px; - background: hsl(0, 0%, 87%); - border: 0; -} -/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */ -.ck-content pre { - padding: 1em; - color: hsl(0, 0%, 20.8%); - background: hsla(0, 0%, 78%, 0.3); - border: 1px solid hsl(0, 0%, 77%); - border-radius: 2px; - text-align: left; - direction: ltr; - tab-size: 4; - white-space: pre-wrap; - font-style: normal; - min-width: 200px; -} -/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */ -.ck-content pre code { - background: unset; - padding: 0; - border-radius: 0; -} -@media print { - /* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */ - .ck-content .page-break { - padding: 0; - } - /* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */ - .ck-content .page-break::after { - display: none; - } -} diff --git a/bin/docs/assets/v0.63.6/libraries/normalize.min.css b/bin/docs/assets/v0.63.6/libraries/normalize.min.css deleted file mode 100644 index 3d24d023d..000000000 --- a/bin/docs/assets/v0.63.6/libraries/normalize.min.css +++ /dev/null @@ -1,148 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -html { - line-height: 1.15; - -webkit-text-size-adjust: 100%; -} -body { - margin: 0; -} -main { - display: block; -} -h1 { - font-size: 2em; - margin: 0.67em 0; -} -hr { - box-sizing: content-box; - height: 0; - overflow: visible; -} -pre { - font-family: monospace, monospace; - font-size: 1em; -} -a { - background-color: transparent; -} -abbr[title] { - border-bottom: none; - text-decoration: underline; - text-decoration: underline dotted; -} -b, -strong { - font-weight: bolder; -} -code, -kbd, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -small { - font-size: 80%; -} -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sub { - bottom: -0.25em; -} -sup { - top: -0.5em; -} -img { - border-style: none; -} -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - font-size: 100%; - line-height: 1.15; - margin: 0; -} -button, -input { - overflow: visible; -} -button, -select { - text-transform: none; -} -[type="button"], -[type="reset"], -[type="submit"], -button { - -webkit-appearance: button; -} -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner, -button::-moz-focus-inner { - border-style: none; - padding: 0; -} -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring, -button:-moz-focusring { - outline: 1px dotted ButtonText; -} -fieldset { - padding: 0.35em 0.75em 0.625em; -} -legend { - box-sizing: border-box; - color: inherit; - display: table; - max-width: 100%; - padding: 0; - white-space: normal; -} -progress { - vertical-align: baseline; -} -textarea { - overflow: auto; -} -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; - padding: 0; -} -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} -[type="search"] { - -webkit-appearance: textfield; - outline-offset: -2px; -} -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} -details { - display: block; -} -summary { - display: list-item; -} -template { - display: none; -} -[hidden] { - display: none; -} -/*# sourceMappingURL=normalize.min.css.map */ diff --git a/bin/docs/assets/v0.63.6/stylesheets/share.css b/bin/docs/assets/v0.63.6/stylesheets/share.css deleted file mode 100644 index 457898d8f..000000000 --- a/bin/docs/assets/v0.63.6/stylesheets/share.css +++ /dev/null @@ -1,165 +0,0 @@ -body { - font-family: "Lucida Grande", "Lucida Sans Unicode", arial, sans-serif; - line-height: 1.5; -} - -#layout { - max-width: 1200px; - margin: 0 auto; - display: flex; - flex-direction: row-reverse; -} - -#menu { - padding: 25px; - flex-basis: 0; - flex-grow: 1; - overflow: auto; -} - -#menu p { - margin: 0; -} - -#menu > p { - font-weight: bold; - font-size: 110%; -} - -#menu ul { - padding-left: 20px; -} - -#main { - flex-basis: 0; - flex-grow: 3; - overflow: auto; - padding: 10px 20px 20px 20px; -} - -#parentLink { - float: right; - margin-top: 20px; -} - -#title { - margin: 0; - padding-top: 10px; -} - -img { - max-width: 100%; -} - -pre { - white-space: pre-wrap; - word-wrap: anywhere; -} - -iframe.pdf-view { - width: 100%; - height: 800px; -} - -#toggleMenuButton { - display: none; - position: fixed; - top: 8px; - left: 5px; - width: 1.4em; - border-radius: 5px; - border: 1px solid #aaa; - font-size: 2rem; - z-index: 10; - height: auto; - color: black; - cursor: pointer; -} - -#childLinks.grid ul { - list-style-type: none; - display: flex; - flex-wrap: wrap; - padding: 0; -} - -#childLinks.grid ul li { - width: 180px; - height: 140px; - padding: 10px; -} - -#childLinks.grid ul li a { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - border: 1px solid #ddd; - border-radius: 5px; - justify-content: center; - align-content: center; - text-align: center; - font-size: large; -} - -#childLinks.grid ul li a:hover { - background: #eee; -} - -#childLinks.list ul { - list-style-type: none; - display: inline-flex; - flex-wrap: wrap; - padding: 0; - margin-top: 5px; -} - -#childLinks.list ul li { - margin-right: 20px; -} - -#noteClippedFrom { - padding: 10px 0 10px 0; - margin: 20px 0 20px 0; - color: #666; - border: 1px solid #ddd; - border-left: 0; - border-right: 0; -} - -#toggleMenuButton::after { - position: relative; - top: -2px; - left: 1px; -} - -@media (max-width: 48em) { - #layout.showMenu #menu { - display: block; - margin-top: 40px; - } - - #toggleMenuButton { - display: block; - } - - #layout.showMenu #main { - display: none; - } - - #title { - padding-left: 60px; - } - - #layout.showMenu #toggleMenuButton::after { - content: "«"; - } - - #toggleMenuButton::after { - content: "»"; - } - - #menu { - display: none; - } -} diff --git a/bin/docs/index.template.html b/bin/docs/index.template.html deleted file mode 100644 index b2580cb88..000000000 --- a/bin/docs/index.template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/bin/docs/prepare.sh b/bin/docs/prepare.sh deleted file mode 100755 index 35e761b6d..000000000 --- a/bin/docs/prepare.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -script_dir=$(realpath $(dirname $0)) - -cd "$script_dir" - -env_file="$script_dir/.env" - -if [ ! -f "$env_file" ]; then - echo "Missing .env file, cannot proceed." - exit 1 -fi - -output_dir="$script_dir/../../docs" -mkdir -p "$output_dir" -rm -f "$output_dir"/* -rm -rf "$output_dir"/{assets,share} - -source "$env_file" - -# Download everything in output/notes.example.com/share/... -share_url="$SHARE_PROTOCOL://$SHARE_HOST/share/$ROOT_NOTE_ID" -wget -rpEk -e robots=off "$share_url" -P "$output_dir" -if [ $? -ne 0 ]; then - echo -e \\nDownloading failed, make sure you are using the real wget package and not the busybox one. - exit 1 -fi - -# Get rid of the domain in the output folder -mv "$output_dir/$SHARE_HOST"/* "$output_dir/" -rmdir "$output_dir/$SHARE_HOST" - -# Create home page with redirect -index_dest_path="$output_dir/index.html" -cp index.template.html "$index_dest_path" -sed -i "s/{{ROOT_NOTE_ID}}/$ROOT_NOTE_ID/g" "$index_dest_path" - -# Rewrite links to get rid of the share folder -sed -i "s/ { - const sql = require("../../src/services/sql"); - const utils = require("../../src/services/utils"); +import sql from "../../src/services/sql"; +import utils from "../../src/services/utils"; +interface NoteContentsRow { + noteId: string; + content: string | Buffer; + dateModified: string; + utcDateModified: string; +} + +interface NoteRevisionContents { + noteRevisionId: string; + content: string | Buffer; + utcDateModified: string; +} + +export default () => { const existingBlobIds = new Set(); - for (const noteId of sql.getColumn(`SELECT noteId FROM note_contents`)) { - const row = sql.getRow(`SELECT noteId, content, dateModified, utcDateModified FROM note_contents WHERE noteId = ?`, [noteId]); + for (const noteId of sql.getColumn(/*sql*/`SELECT noteId FROM note_contents`)) { + const row = sql.getRow(/*sql*/`SELECT noteId, content, dateModified, utcDateModified FROM note_contents WHERE noteId = ?`, [noteId]); const blobId = utils.hashedBlobId(row.content); if (!existingBlobIds.has(blobId)) { @@ -27,8 +40,8 @@ module.exports = () => { sql.execute("UPDATE notes SET blobId = ? WHERE noteId = ?", [blobId, row.noteId]); } - for (const noteRevisionId of sql.getColumn(`SELECT noteRevisionId FROM note_revision_contents`)) { - const row = sql.getRow(`SELECT noteRevisionId, content, utcDateModified FROM note_revision_contents WHERE noteRevisionId = ?`, [noteRevisionId]); + for (const noteRevisionId of sql.getColumn(/*sql*/`SELECT noteRevisionId FROM note_revision_contents`)) { + const row = sql.getRow(/*sql*/`SELECT noteRevisionId, content, utcDateModified FROM note_revision_contents WHERE noteRevisionId = ?`, [noteRevisionId]); const blobId = utils.hashedBlobId(row.content); if (!existingBlobIds.has(blobId)) { @@ -44,7 +57,7 @@ module.exports = () => { sql.execute("UPDATE entity_changes SET entityName = 'blobs', entityId = ? WHERE entityName = 'note_revision_contents' AND entityId = ?", [blobId, row.noteRevisionId]); } else { // duplicates - sql.execute("DELETE FROM entity_changes WHERE entityName = 'note_revision_contents' AND entityId = ?", [row.noteId]); + sql.execute("DELETE FROM entity_changes WHERE entityName = 'note_revision_contents' AND entityId = ?", [row.noteRevisionId]); } sql.execute("UPDATE note_revisions SET blobId = ? WHERE noteRevisionId = ?", [blobId, row.noteRevisionId]); diff --git a/db/migrations/0220__migrate_images_to_attachments.js b/db/migrations/0220__migrate_images_to_attachments.ts similarity index 60% rename from db/migrations/0220__migrate_images_to_attachments.js rename to db/migrations/0220__migrate_images_to_attachments.ts index a93e2028d..f54f4a956 100644 --- a/db/migrations/0220__migrate_images_to_attachments.js +++ b/db/migrations/0220__migrate_images_to_attachments.ts @@ -1,15 +1,15 @@ -module.exports = () => { - const beccaLoader = require("../../src/becca/becca_loader"); - const becca = require("../../src/becca/becca"); - const cls = require("../../src/services/cls"); - const log = require("../../src/services/log"); - const sql = require("../../src/services/sql"); +import becca from "../../src/becca/becca"; +import becca_loader from "../../src/becca/becca_loader"; +import cls from "../../src/services/cls"; +import log from "../../src/services/log"; +import sql from "../../src/services/sql"; +export default () => { cls.init(() => { // emergency disabling of image compression since it appears to make problems in migration to 0.61 - sql.execute(`UPDATE options SET value = 'false' WHERE name = 'compressImages'`); + sql.execute(/*sql*/`UPDATE options SET value = 'false' WHERE name = 'compressImages'`); - beccaLoader.load(); + becca_loader.load(); for (const note of Object.values(becca.notes)) { try { diff --git a/db/migrations/0229__add_oauth_user_data_table.sql b/db/migrations/0229__add_oauth_user_data_table.sql new file mode 100644 index 000000000..ea2db4ef9 --- /dev/null +++ b/db/migrations/0229__add_oauth_user_data_table.sql @@ -0,0 +1,14 @@ +-- Add the oauth user data table +CREATE TABLE IF NOT EXISTS "user_data" +( + tmpID INT, + username TEXT, + email TEXT, + userIDEncryptedDataKey TEXT, + userIDVerificationHash TEXT, + salt TEXT, + derivedKey TEXT, + isSetup TEXT DEFAULT "false", + UNIQUE (tmpID), + PRIMARY KEY (tmpID) +); \ No newline at end of file diff --git a/db/migrations/0229__tasks.sql b/db/migrations/0229__tasks.sql deleted file mode 100644 index 4d0381db2..000000000 --- a/db/migrations/0229__tasks.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE IF NOT EXISTS "tasks" -( - "taskId" TEXT NOT NULL PRIMARY KEY, - "parentNoteId" TEXT NOT NULL, - "title" TEXT NOT NULL DEFAULT "", - "dueDate" INTEGER, - "isDone" INTEGER NOT NULL DEFAULT 0, - "isDeleted" INTEGER NOT NULL DEFAULT 0, - "utcDateModified" TEXT NOT NULL -); \ No newline at end of file diff --git a/db/schema.sql b/db/schema.sql index 77361f5ed..8bf9db1e7 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -126,20 +126,22 @@ CREATE TABLE IF NOT EXISTS "attachments" utcDateScheduledForErasureSince TEXT DEFAULT NULL, isDeleted INT not null, deleteId TEXT DEFAULT NULL); +CREATE TABLE IF NOT EXISTS "user_data" +( + tmpID INT, + username TEXT, + email TEXT, + userIDEncryptedDataKey TEXT, + userIDVerificationHash TEXT, + salt TEXT, + derivedKey TEXT, + isSetup TEXT DEFAULT "false", + UNIQUE (tmpID), + PRIMARY KEY (tmpID) +); CREATE INDEX IDX_attachments_ownerId_role on attachments (ownerId, role); CREATE INDEX IDX_notes_blobId on notes (blobId); CREATE INDEX IDX_revisions_blobId on revisions (blobId); CREATE INDEX IDX_attachments_blobId on attachments (blobId); - -CREATE TABLE IF NOT EXISTS "tasks" -( - "taskId" TEXT NOT NULL PRIMARY KEY, - "parentNoteId" TEXT NOT NULL, - "title" TEXT NOT NULL DEFAULT "", - "dueDate" INTEGER, - "isDone" INTEGER NOT NULL DEFAULT 0, - "isDeleted" INTEGER NOT NULL DEFAULT 0, - "utcDateModified" TEXT NOT NULL -); \ No newline at end of file diff --git a/demo/!!!meta.json b/demo/!!!meta.json new file mode 100644 index 000000000..4899c4606 --- /dev/null +++ b/demo/!!!meta.json @@ -0,0 +1,6088 @@ +{ + "formatVersion": 2, + "appVersion": "0.92.5-beta", + "files": [ + { + "isClone": false, + "noteId": "root", + "notePath": [ + "root" + ], + "title": "root", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "root", + "children": [ + { + "isClone": false, + "noteId": "sxsosDSEMa8u", + "notePath": [ + "root", + "sxsosDSEMa8u" + ], + "title": "Journal", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "book", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "calendarRoot", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-calendar", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "viewType", + "value": "calendar", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "calendar:view", + "value": "dayGridMonth", + "isInheritable": false, + "position": 40 + } + ], + "dataFileName": "Journal.dat", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Fb7eMY46lT6k", + "notePath": [ + "root", + "Fb7eMY46lT6k" + ], + "title": "Trilium Demo", + "notePosition": 20, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "qP9UEZ4kQ0NK", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "8DORylN1bL6J", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "rIP0ngPPeia6", + "isInheritable": false, + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "TIKj1aLdHCzw", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "xd22mlgxfdWw", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-book-reader", + "isInheritable": false, + "position": 70 + } + ], + "format": "html", + "dataFileName": "Trilium Demo.html", + "attachments": [ + { + "attachmentId": "96nFd7uePoSp", + "title": "icon-color.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Trilium Demo_icon-color.svg" + } + ], + "dirFileName": "Trilium Demo", + "children": [ + { + "isClone": false, + "noteId": "B08ZaJdYTK5v", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "B08ZaJdYTK5v" + ], + "title": "Inbox", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-inbox", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "Inbox.html", + "attachments": [], + "dirFileName": "Inbox", + "children": [ + { + "isClone": false, + "noteId": "nMiFslI2hks0", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "B08ZaJdYTK5v", + "nMiFslI2hks0" + ], + "title": "Grocery list for today", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Grocery list for today.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "RsIQGAjjnfgv", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "B08ZaJdYTK5v", + "RsIQGAjjnfgv" + ], + "title": "Book to read", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Book to read.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "47q1VMmTjJtx", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "B08ZaJdYTK5v", + "47q1VMmTjJtx" + ], + "title": "The Last Question", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "includeNoteLink", + "value": "ZWCYra81yOFO", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "_help_nBAXQFj20hS1", + "isInheritable": false, + "position": 20 + } + ], + "format": "html", + "dataFileName": "The Last Question.html", + "attachments": [], + "dirFileName": "The Last Question", + "children": [ + { + "isClone": false, + "noteId": "ZWCYra81yOFO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "B08ZaJdYTK5v", + "47q1VMmTjJtx", + "ZWCYra81yOFO" + ], + "title": "The Last Question by Issac Asimov.pdf", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "file", + "mime": "application/pdf", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "the_last_question_-_issac_asimov.pdf", + "isInheritable": false, + "position": 1 + } + ], + "dataFileName": "The Last Question by Issac.pdf", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "vHpMd49rAUWn", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn" + ], + "title": "Formatting examples", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "", + "attributes": [], + "attachments": [], + "dirFileName": "Formatting examples", + "children": [ + { + "isClone": false, + "noteId": "qP9UEZ4kQ0NK", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn", + "qP9UEZ4kQ0NK" + ], + "title": "School schedule", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "School schedule.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "8DORylN1bL6J", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn", + "8DORylN1bL6J" + ], + "title": "Checkbox lists", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-check", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "Checkbox lists.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "rIP0ngPPeia6", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn", + "rIP0ngPPeia6" + ], + "title": "Highlighting", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-pencil", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "Highlighting.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "TIKj1aLdHCzw", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn", + "TIKj1aLdHCzw" + ], + "title": "Code blocks", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "IPifAXM3cyRS", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-code-alt", + "isInheritable": false, + "position": 2 + } + ], + "format": "html", + "dataFileName": "Code blocks.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "xd22mlgxfdWw", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "vHpMd49rAUWn", + "xd22mlgxfdWw" + ], + "title": "Math", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-calculator", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "Math.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "eYIqnBvdAmaE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE" + ], + "title": "Journal", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "sorted", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "child:child:child:template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-calendar", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Journal.html", + "attachments": [], + "dirFileName": "Journal", + "children": [ + { + "isClone": false, + "noteId": "3H4RMc45XIpI", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI" + ], + "title": "2021", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "sorted", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "yearNote", + "value": "2021", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "child:child:template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "2021", + "children": [ + { + "isClone": false, + "noteId": "oNJGQb0t2SGb", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "oNJGQb0t2SGb" + ], + "title": "11 - November", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "sorted", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "monthNote", + "value": "2021-11", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "child:template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "11 - November", + "children": [ + { + "isClone": false, + "noteId": "JvGlcvRf2Lu8", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "oNJGQb0t2SGb", + "JvGlcvRf2Lu8" + ], + "title": "28 - Tuesday", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "dateNote", + "value": "2021-11-28", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 20 + } + ], + "format": "html", + "dataFileName": "28 - Tuesday.html", + "attachments": [], + "dirFileName": "28 - Tuesday", + "children": [ + { + "isClone": false, + "noteId": "k11dU4eL5Teq", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "oNJGQb0t2SGb", + "JvGlcvRf2Lu8", + "k11dU4eL5Teq" + ], + "title": "Phone call about work project", + "notePosition": 0, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Phone call about work project.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "RNf8bX9CdR1k", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "oNJGQb0t2SGb", + "JvGlcvRf2Lu8", + "RNf8bX9CdR1k" + ], + "title": "Christmas gift ideas", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Christmas gift ideas.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "AaxrGGwyIPxO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "oNJGQb0t2SGb", + "JvGlcvRf2Lu8", + "AaxrGGwyIPxO" + ], + "title": "Trusted timestamping", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Trusted timestamping.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "BkmGEUBkAvZC", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC" + ], + "title": "12 - December", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "sorted", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "monthNote", + "value": "2021-12", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "child:template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "12 - December", + "children": [ + { + "isClone": false, + "noteId": "S9e1O6walQlq", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq" + ], + "title": "18 - Monday", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-18", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "74.9", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "18 - Monday.html", + "attachments": [], + "dirFileName": "18 - Monday", + "children": [ + { + "isClone": false, + "noteId": "aqelIyWn57Aa", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "aqelIyWn57Aa" + ], + "title": "Meeting minutes", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Meeting minutes.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "xqGIjsmQPRPq", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq" + ], + "title": "Photos from the trip", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "bookZoomLevel", + "value": "2", + "isInheritable": false, + "position": 0 + } + ], + "attachments": [], + "dirFileName": "Photos from the trip", + "children": [ + { + "isClone": false, + "noteId": "1EdiWGOQB4RK", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "1EdiWGOQB4RK" + ], + "title": "01.jpeg", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "01.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "16881", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "01.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "NUeVuPpK1N7f", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "NUeVuPpK1N7f" + ], + "title": "02.jpeg", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "02.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "31697", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "02.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "u3Cb1NkuHVFu", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "u3Cb1NkuHVFu" + ], + "title": "03.jpeg", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "03.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "72522", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "03.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "RMk5ZL0mw2vh", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "RMk5ZL0mw2vh" + ], + "title": "04.jpeg", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "04.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "43670", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "04.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "lA77h6Gld6Af", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "lA77h6Gld6Af" + ], + "title": "05.jpeg", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "05.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "22327", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "05.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "5bKca24TwvXi", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "5bKca24TwvXi" + ], + "title": "06.jpeg", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "06.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "79751", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "06.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Dw80f8XRUEkb", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "Dw80f8XRUEkb" + ], + "title": "07.jpeg", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "07.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "30223", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "07.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "3B4wY3ysFzUL", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "3B4wY3ysFzUL" + ], + "title": "08.jpeg", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "08.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "39928", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "08.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "8rJBABNUlLlJ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "8rJBABNUlLlJ" + ], + "title": "09.jpeg", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "09.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "48918", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "09.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "2mkSjqnJhj9i", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "2mkSjqnJhj9i" + ], + "title": "10.jpeg", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "10.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "44150", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "10.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "oPcnET6xqcz1", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "oPcnET6xqcz1" + ], + "title": "11.jpeg", + "notePosition": 100, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "11.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "76231", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "11.jpeg", + "attachments": [] + }, + { + "isClone": false, + "noteId": "V1OwJwsPuBzj", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "xqGIjsmQPRPq", + "V1OwJwsPuBzj" + ], + "title": "12.jpeg", + "notePosition": 110, + "prefix": null, + "isExpanded": false, + "type": "image", + "mime": "image/jpg", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "12.jpeg", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "fileSize", + "value": "44286", + "isInheritable": false, + "position": 2 + } + ], + "dataFileName": "12.jpeg", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "vH389HhCX3to", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "S9e1O6walQlq", + "vH389HhCX3to" + ], + "title": "Send invites for christmas party", + "notePosition": 20, + "prefix": "TODO", + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "location", + "value": "work", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-18", + "isInheritable": false, + "position": 60 + } + ], + "format": "html", + "dataFileName": "TODO - Send invites for christ.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "Xa15h4cRL6O8", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "Xa15h4cRL6O8" + ], + "title": "19 - Tuesday", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-19", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "75.4", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "19 - Tuesday.html", + "attachments": [], + "dirFileName": "19 - Tuesday", + "children": [ + { + "isClone": false, + "noteId": "KaNVFkFBdkvZ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "Xa15h4cRL6O8", + "KaNVFkFBdkvZ" + ], + "title": "Dentist appointment", + "notePosition": 0, + "prefix": "DONE", + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "tag", + "value": "health", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "done", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-19", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "doneDate", + "value": "2021-12-19", + "isInheritable": false, + "position": 60 + } + ], + "format": "html", + "dataFileName": "DONE - Dentist appointment.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "mMQKGLEGtRHn", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "mMQKGLEGtRHn" + ], + "title": "20 - Wednesday", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-20", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "75.2", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "20 - Wednesday.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ZZz9RKYGLcK7", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "ZZz9RKYGLcK7" + ], + "title": "21 - Thursday", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "dateNote", + "value": "2021-12-21", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "76", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "21 - Thursday.html", + "attachments": [], + "dirFileName": "21 - Thursday", + "children": [ + { + "isClone": false, + "noteId": "hXwLRiT7g4dE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "ZZz9RKYGLcK7", + "hXwLRiT7g4dE" + ], + "title": "Christmas shopping", + "notePosition": 0, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Christmas shopping.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "jpcqxgiEtJkn", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "ZZz9RKYGLcK7", + "jpcqxgiEtJkn" + ], + "title": "Office party", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Office party.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "KgE3kYIT24Sb", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "KgE3kYIT24Sb" + ], + "title": "22 - Friday", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "dateNote", + "value": "2021-12-22", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "75.9", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "22 - Friday.html", + "attachments": [], + "dirFileName": "22 - Friday", + "children": [ + { + "isClone": false, + "noteId": "AEa2Sy5UwdT4", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "KgE3kYIT24Sb", + "AEa2Sy5UwdT4" + ], + "title": "Christmas shopping", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Christmas shopping.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "NhSSbIObzEsM", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "KgE3kYIT24Sb", + "NhSSbIObzEsM" + ], + "title": "The Mechanical", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "ODEzxhfMalvQ", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "link", + "value": "https://www.amazon.com/Mechanical-Alchemy-Wars-Ian-Tregillis/dp/0316248002", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "author", + "value": "Ian Tregillis", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "readingStart", + "value": "2018-05-05", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "readingEnd", + "value": "2018-05-23", + "isInheritable": false, + "position": 50 + } + ], + "format": "html", + "dataFileName": "The Mechanical.html", + "attachments": [], + "dirFileName": "The Mechanical", + "children": [ + { + "isClone": false, + "noteId": "QGIhTr2UNF8V", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "KgE3kYIT24Sb", + "NhSSbIObzEsM", + "QGIhTr2UNF8V" + ], + "title": "Highlights", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Highlights.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "uDulJuuQUQV9", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "uDulJuuQUQV9" + ], + "title": "23 - Saturday", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-23", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "75.6", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "23 - Saturday.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "cG0jN1I9tYPs", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "cG0jN1I9tYPs" + ], + "title": "24 - Sunday - Christmas Eve!", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-24", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "weight", + "value": "76.1", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "24 - Sunday - Christmas Eve!.html", + "attachments": [], + "dirFileName": "24 - Sunday - Christmas Eve!", + "children": [ + { + "isClone": false, + "noteId": "xybXfFXh4kbk", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "cG0jN1I9tYPs", + "xybXfFXh4kbk" + ], + "title": "Buy a board game for Alice", + "notePosition": 0, + "prefix": "DONE", + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "location", + "value": "mall", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "done", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "tag", + "value": "christmas", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "tag", + "value": "shopping", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-20", + "isInheritable": false, + "position": 70 + }, + { + "type": "label", + "name": "doneDate", + "value": "2021-12-24", + "isInheritable": false, + "position": 80 + } + ], + "format": "html", + "dataFileName": "DONE - Buy a board game for Al.html", + "attachments": [ + { + "attachmentId": "pZ0Azmg2ivRC", + "title": "codenames.jpg", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "DONE - Buy a board game fo.jpg" + } + ] + }, + { + "isClone": false, + "noteId": "HnU0m7ECI7G5", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "cG0jN1I9tYPs", + "HnU0m7ECI7G5" + ], + "title": "Buy milk", + "notePosition": 10, + "prefix": "TODO", + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 1 + }, + { + "type": "label", + "name": "location", + "value": "tesco", + "isInheritable": false, + "position": 3 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 4 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 4 + }, + { + "type": "label", + "name": "tag", + "value": "groceries", + "isInheritable": false, + "position": 5 + }, + { + "type": "label", + "name": "tag", + "value": "shopping", + "isInheritable": false, + "position": 7 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-24", + "isInheritable": false, + "position": 6 + } + ], + "format": "html", + "dataFileName": "TODO - Buy milk.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "l0Hn9dXX4wJ7", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "BkmGEUBkAvZC", + "l0Hn9dXX4wJ7" + ], + "title": "30 - Thursday", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "sINmXkCzncB4", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "dateNote", + "value": "2021-12-30", + "isInheritable": false, + "position": 31 + } + ], + "format": "html", + "dataFileName": "30 - Thursday.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "2kH7aQSosO9C", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C" + ], + "title": "Epics", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Epics.html", + "attachments": [], + "dirFileName": "Epics", + "children": [ + { + "isClone": false, + "noteId": "BH0WNWPyihkF", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "BH0WNWPyihkF" + ], + "title": "Christmas", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Christmas.html", + "attachments": [], + "dirFileName": "Christmas", + "children": [ + { + "isClone": false, + "noteId": "PDRNLWTHfOi0", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "BH0WNWPyihkF", + "PDRNLWTHfOi0" + ], + "title": "Vacation days", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Vacation days.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "r45qXBox4mts", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "BH0WNWPyihkF", + "r45qXBox4mts" + ], + "title": "Christmas dinner", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Christmas dinner.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "5t7iAUBdEndw", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "BH0WNWPyihkF", + "5t7iAUBdEndw" + ], + "title": "Shopping", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "Shopping", + "children": [ + { + "isClone": true, + "noteId": "RNf8bX9CdR1k", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "BH0WNWPyihkF", + "5t7iAUBdEndw", + "RNf8bX9CdR1k" + ], + "title": "Christmas gift ideas", + "prefix": "28. 11. 2017", + "dataFileName": "28. 11. 2017 - Christmas gift ideas.clone.html", + "type": "text", + "format": "html" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "2p3IczsCyfFH", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "3H4RMc45XIpI", + "2kH7aQSosO9C", + "2p3IczsCyfFH" + ], + "title": "Vacation", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Vacation.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "sINmXkCzncB4", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "eYIqnBvdAmaE", + "sINmXkCzncB4" + ], + "title": "Day template", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "label:weight", + "value": "promoted,number,single,precision=1", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-notepad", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "excludeFromNoteMap", + "value": "", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Day template.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "HGcABfDS6a51", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51" + ], + "title": "Tech", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-desktop", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "Tech.html", + "attachments": [], + "dirFileName": "Tech", + "children": [ + { + "isClone": false, + "noteId": "1wgEZXIXYS9H", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "1wgEZXIXYS9H" + ], + "title": "Security", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-lock-alt", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Security", + "children": [ + { + "isClone": true, + "noteId": "AaxrGGwyIPxO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "1wgEZXIXYS9H", + "AaxrGGwyIPxO" + ], + "title": "Trusted timestamping", + "prefix": null, + "dataFileName": "Trusted timestamping.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "7Sbzd7Qd7nSn", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn" + ], + "title": "Linux", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-tux", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Linux", + "children": [ + { + "isClone": false, + "noteId": "zLD6PepcrYhv", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "zLD6PepcrYhv" + ], + "title": "History", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "History.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "rwXwXJUFWswH", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "rwXwXJUFWswH" + ], + "title": "Bash scripting", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Bash scripting.html", + "attachments": [], + "dirFileName": "Bash scripting", + "children": [ + { + "isClone": false, + "noteId": "F7QT7w1jFQ4S", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "rwXwXJUFWswH", + "F7QT7w1jFQ4S" + ], + "title": "While loop", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "While loop.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Xllm9YiBRXqN", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "rwXwXJUFWswH", + "Xllm9YiBRXqN" + ], + "title": "Bash startup modes", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Bash startup modes.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "E8inmLohu8XE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "E8inmLohu8XE" + ], + "title": "Ubuntu", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Ubuntu.html", + "attachments": [], + "dirFileName": "Ubuntu", + "children": [ + { + "isClone": false, + "noteId": "elwY4igN6EGl", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "7Sbzd7Qd7nSn", + "E8inmLohu8XE", + "elwY4igN6EGl" + ], + "title": "Unity shortcuts", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Unity shortcuts.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "VpNAHIDDlcLh", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "VpNAHIDDlcLh" + ], + "title": "Programming", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-code-alt", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Programming", + "children": [ + { + "isClone": false, + "noteId": "penrSU9H9j0z", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "VpNAHIDDlcLh", + "penrSU9H9j0z" + ], + "title": "Java", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-java", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "Java.html", + "attachments": [] + }, + { + "isClone": true, + "noteId": "rwXwXJUFWswH", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "VpNAHIDDlcLh", + "rwXwXJUFWswH" + ], + "title": "Bash scripting", + "prefix": null, + "dataFileName": "Bash scripting.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "B16t4MPcxaAs", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs" + ], + "title": "Node.js", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-nodejs", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Node.js", + "children": [ + { + "isClone": false, + "noteId": "UDLLOhsgyVLk", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "UDLLOhsgyVLk" + ], + "title": "Intro", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Intro.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "LWpxsRzR1WbA", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "LWpxsRzR1WbA" + ], + "title": "Overview", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Overview.html", + "attachments": [], + "dirFileName": "Overview", + "children": [ + { + "isClone": false, + "noteId": "PHD3wfCknzIJ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "LWpxsRzR1WbA", + "PHD3wfCknzIJ" + ], + "title": "History", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "History.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ZBPDxGMrNx7O", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "LWpxsRzR1WbA", + "ZBPDxGMrNx7O" + ], + "title": "Platform architecture", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Platform architecture.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "dXjFj3SB4m1I", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "LWpxsRzR1WbA", + "dXjFj3SB4m1I" + ], + "title": "Industry support", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Industry support.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "TRT99FK2qOzt", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "HGcABfDS6a51", + "B16t4MPcxaAs", + "TRT99FK2qOzt" + ], + "title": "Releases", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Releases.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "k7rcoITZRsi6", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6" + ], + "title": "Note Types", + "notePosition": 60, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-file", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Note Types", + "children": [ + { + "isClone": false, + "noteId": "P1OnNHRhFn9u", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "P1OnNHRhFn9u" + ], + "title": "Canvas", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "canvas", + "mime": "application/json", + "attributes": [], + "dataFileName": "Canvas.json", + "attachments": [ + { + "attachmentId": "LrYmWTdvBYpX", + "title": "canvas-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 0, + "dataFileName": "Canvas_canvas-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "lZlMer9EkwF8", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8" + ], + "title": "Mermaid Diagrams", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-selection", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Mermaid Diagrams", + "children": [ + { + "isClone": false, + "noteId": "DALQSU4fng4F", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "DALQSU4fng4F" + ], + "title": "Flow", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/mermaid", + "attributes": [], + "dataFileName": "Flow.txt", + "attachments": [ + { + "attachmentId": "6u2TW8zweuBN", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Flow_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "g7rjYwoleBlR", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "g7rjYwoleBlR" + ], + "title": "Flow (ELK)", + "notePosition": 11, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/mermaid", + "attributes": [], + "dataFileName": "Flow (ELK).txt", + "attachments": [ + { + "attachmentId": "9TKNS3IHYYQq", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Flow (ELK)_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "t73XNJNTTwQp", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "t73XNJNTTwQp" + ], + "title": "Sequence", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/mermaid", + "attributes": [], + "dataFileName": "Sequence.txt", + "attachments": [ + { + "attachmentId": "XfWi1TqleVbL", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Sequence_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "6wdYxWSVuXof", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "6wdYxWSVuXof" + ], + "title": "Gantt", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Gantt.txt", + "attachments": [ + { + "attachmentId": "zCjOBL5UP9ii", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Gantt_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "WKL3yssKyoth", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "WKL3yssKyoth" + ], + "title": "Class", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Class.txt", + "attachments": [ + { + "attachmentId": "sRADq0QJpaYr", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Class_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "v9TM7cCXBILP", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "v9TM7cCXBILP" + ], + "title": "State", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "State.txt", + "attachments": [ + { + "attachmentId": "NhPv2lQln1g4", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "State_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "1EDhd9QJyulq", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "1EDhd9QJyulq" + ], + "title": "Mind Map", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/mermaid", + "attributes": [], + "dataFileName": "Mind Map.txt", + "attachments": [ + { + "attachmentId": "Q8EircLk65HH", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Mind Map_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "n32hpxSA2fP8", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "n32hpxSA2fP8" + ], + "title": "Pie", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Pie.txt", + "attachments": [ + { + "attachmentId": "6xhRWdFidyIg", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Pie_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "T0QEYaEv7PmE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "T0QEYaEv7PmE" + ], + "title": "Journey", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Journey.txt", + "attachments": [ + { + "attachmentId": "sxLqDNmjlbK4", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Journey_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "P9QvwWzLZnXJ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "P9QvwWzLZnXJ" + ], + "title": "Git", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Git.txt", + "attachments": [ + { + "attachmentId": "tIoCBIaY50Jm", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Git_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "19O4nsY7dkPt", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "19O4nsY7dkPt" + ], + "title": "Entity Relationship", + "notePosition": 100, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "Entity Relationship.txt", + "attachments": [ + { + "attachmentId": "AyKZhKQIE5w9", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Entity Relationship_mermai.svg" + } + ] + }, + { + "isClone": false, + "noteId": "2izpY6SPbmhp", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "2izpY6SPbmhp" + ], + "title": "Bar chart", + "notePosition": 110, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/mermaid", + "attributes": [], + "dataFileName": "Bar chart.txt", + "attachments": [ + { + "attachmentId": "r1wMURlU52l8", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Bar chart_mermaid-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "hwx0dffwqIGg", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "lZlMer9EkwF8", + "hwx0dffwqIGg" + ], + "title": "C4", + "notePosition": 120, + "prefix": null, + "isExpanded": false, + "type": "mermaid", + "mime": "text/plain", + "attributes": [], + "dataFileName": "C4.txt", + "attachments": [ + { + "attachmentId": "6v2qO6zsygVC", + "title": "mermaid-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "C4_mermaid-export.svg" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "CN3CCLggUqf4", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "CN3CCLggUqf4" + ], + "title": "Mind Map", + "notePosition": 30, + "prefix": null, + "isExpanded": true, + "type": "mindMap", + "mime": "application/json", + "attributes": [], + "dataFileName": "Mind Map.json", + "attachments": [ + { + "attachmentId": "xKxyCN02w5d2", + "title": "mindmap-export.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 0, + "dataFileName": "Mind Map_mindmap-export.svg" + } + ] + }, + { + "isClone": false, + "noteId": "OdCVNGzsvWuv", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv" + ], + "title": "Geo Map (The Seven Wonders of the World)", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "geoMap", + "mime": "application/json", + "attributes": [ + { + "type": "label", + "name": "label:geolocation", + "value": "promoted,alias=Geolocation,single,text", + "isInheritable": true, + "position": 10 + }, + { + "type": "label", + "name": "hidePromotedAttributes", + "value": "", + "isInheritable": false, + "position": 20 + } + ], + "dataFileName": "Geo Map (The Seven Wonders of .json", + "attachments": [], + "dirFileName": "Geo Map (The Seven Wonders of the World)", + "children": [ + { + "isClone": false, + "noteId": "IdHyf4rlLmlO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "IdHyf4rlLmlO" + ], + "title": "The Colosseum, Rome, Italy", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "41.89024211851462, 12.492263083403595", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-circle", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "The Colosseum, Rome, Italy.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Wyp6vjVo0vuV", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "Wyp6vjVo0vuV" + ], + "title": "The Great Wall of China", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "40.431907671437244, 116.57035343915216", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-selection", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "The Great Wall of China.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VsO5E97Y2mPy", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "VsO5E97Y2mPy" + ], + "title": "The Taj Mahal, India", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "27.175173410074475, 78.04213146744753", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-arch", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "The Taj Mahal, India.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "8nRYOMoDdWs3", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "8nRYOMoDdWs3" + ], + "title": "Christ the Redeemer, Brazil", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "-22.951993968508837, -43.21044464113274", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-church", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Christ the Redeemer, Brazil.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "nZxMTxkP8AQ4", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "nZxMTxkP8AQ4" + ], + "title": "Machu Picchu, Peru", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "-13.163198787170078, -72.54528356174288", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-castle", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Machu Picchu, Peru.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "DtMcvaYKen9C", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "DtMcvaYKen9C" + ], + "title": "Chichén Itzá, Mexico", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "20.678882007143176, -88.56836961554815", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-component", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Chichén Itzá, Mexico.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ZkR3GvsSQ0Xe", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "k7rcoITZRsi6", + "OdCVNGzsvWuv", + "ZkR3GvsSQ0Xe" + ], + "title": "Petra, Jordan", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "geolocation", + "value": "30.32084750671952, 35.481009100454926", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-castle", + "isInheritable": false, + "position": 30 + } + ], + "format": "html", + "dataFileName": "Petra, Jordan.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "L2NrzQACw0in", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in" + ], + "title": "Books", + "notePosition": 130, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "child:template", + "value": "ODEzxhfMalvQ", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "wordCount", + "value": "", + "isInheritable": true, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-book-open", + "isInheritable": false, + "position": 21 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Books", + "children": [ + { + "isClone": false, + "noteId": "Mzby9GxzBFl1", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in", + "Mzby9GxzBFl1" + ], + "title": "To read", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "To read.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ODEzxhfMalvQ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in", + "ODEzxhfMalvQ" + ], + "title": "Book template", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-book-reader", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "label:readingEnd", + "value": "promoted,single,date", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "label:readingStart", + "value": "promoted,single,date", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "label:author", + "value": "promoted,single,text", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "template", + "value": "", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "label:link", + "value": "promoted,single,url", + "isInheritable": false, + "position": 40 + } + ], + "format": "html", + "dataFileName": "Book template.html", + "attachments": [], + "dirFileName": "Book template", + "children": [ + { + "isClone": false, + "noteId": "MFVR8MlrXQDQ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in", + "ODEzxhfMalvQ", + "MFVR8MlrXQDQ" + ], + "title": "Highlights", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Highlights.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "IWFmoH3KgkH2", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in", + "IWFmoH3KgkH2" + ], + "title": "Reviews", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "child:template", + "value": "ODEzxhfMalvQ", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Reviews", + "children": [ + { + "isClone": true, + "noteId": "NhSSbIObzEsM", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "L2NrzQACw0in", + "IWFmoH3KgkH2", + "NhSSbIObzEsM" + ], + "title": "The Mechanical", + "prefix": null, + "dataFileName": "The Mechanical.clone.html", + "type": "text", + "format": "html" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "EBROzsTV1jh4", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "EBROzsTV1jh4" + ], + "title": "Work", + "notePosition": 150, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-briefcase-alt", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Work", + "children": [ + { + "isClone": false, + "noteId": "WgVZEuZC0Hv8", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "EBROzsTV1jh4", + "WgVZEuZC0Hv8" + ], + "title": "HR", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "HR.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "oHu27s9uc20i", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "EBROzsTV1jh4", + "oHu27s9uc20i" + ], + "title": "Processes", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Processes.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "mrSqOUvKrvVS", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "EBROzsTV1jh4", + "mrSqOUvKrvVS" + ], + "title": "Projects", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Projects.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "6RKJSJ65qIn5", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "6RKJSJ65qIn5" + ], + "title": "Steel Blue", + "notePosition": 160, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/css", + "attributes": [ + { + "type": "label", + "name": "appTheme", + "value": "steel-blue", + "isInheritable": false, + "position": 0 + } + ], + "dataFileName": "Steel Blue.css", + "attachments": [], + "dirFileName": "Steel Blue", + "children": [ + { + "isClone": false, + "noteId": "83Ywz5JXl9pw", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "6RKJSJ65qIn5", + "83Ywz5JXl9pw" + ], + "title": "eb-garamond-v9-latin-regular.woff2", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "file", + "mime": "application/octet-stream", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "eb-garamond-v9-latin-regular.woff2", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "fileSize", + "value": "27608", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "customResourceProvider", + "value": "fonts/garamond.woff2", + "isInheritable": false, + "position": 30 + } + ], + "dataFileName": "eb-garamond-v9-latin-reg.woff2", + "attachments": [] + }, + { + "isClone": false, + "noteId": "K2IK5OccY70o", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "6RKJSJ65qIn5", + "K2IK5OccY70o" + ], + "title": "raleway-v12-latin-regular.woff2", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "file", + "mime": "application/octet-stream", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "raleway-v12-latin-regular.woff2", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "fileSize", + "value": "20444", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "customResourceProvider", + "value": "fonts/raleway.woff2", + "isInheritable": false, + "position": 30 + } + ], + "dataFileName": "raleway-v12-latin-regula.woff2", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "G8k9CPUVtDeE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE" + ], + "title": "Scripting examples", + "notePosition": 350, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-javascript", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Scripting examples", + "children": [ + { + "isClone": false, + "noteId": "zQpMt8tYmYPL", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL" + ], + "title": "Task manager", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-task", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "Task manager.html", + "attachments": [], + "dirFileName": "Task manager", + "children": [ + { + "isClone": false, + "noteId": "749vDoR12ulD", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD" + ], + "title": "Locations", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskLocationRoot", + "value": "", + "isInheritable": false, + "position": 0 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-map", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Locations", + "children": [ + { + "isClone": false, + "noteId": "ps7IPoCpurK3", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "ps7IPoCpurK3" + ], + "title": "gym", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskLocationNote", + "value": "gym", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "gym.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "gvgFDSKxYYRr", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "gvgFDSKxYYRr" + ], + "title": "work", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskLocationNote", + "value": "work", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "work", + "children": [ + { + "isClone": false, + "noteId": "Evz7c4Npu4PO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "gvgFDSKxYYRr", + "Evz7c4Npu4PO" + ], + "title": "Send invites for christmas party", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "location", + "value": "work", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-18", + "isInheritable": false, + "position": 60 + } + ], + "format": "html", + "dataFileName": "Send invites for christmas par.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "druYfHaAacBo", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "druYfHaAacBo" + ], + "title": "tesco", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskLocationNote", + "value": "tesco", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "tesco", + "children": [ + { + "isClone": false, + "noteId": "hu6fwfUGqnZR", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "druYfHaAacBo", + "hu6fwfUGqnZR" + ], + "title": "Buy milk", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 1 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 2 + }, + { + "type": "label", + "name": "location", + "value": "tesco", + "isInheritable": false, + "position": 3 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 4 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 4 + }, + { + "type": "label", + "name": "tag", + "value": "groceries", + "isInheritable": false, + "position": 5 + }, + { + "type": "label", + "name": "tag", + "value": "shopping", + "isInheritable": false, + "position": 7 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-24", + "isInheritable": false, + "position": 6 + } + ], + "format": "html", + "dataFileName": "Buy milk.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "GrPlXB0Xk9k0", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "GrPlXB0Xk9k0" + ], + "title": "mall", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskLocationNote", + "value": "mall", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "mall", + "children": [ + { + "isClone": false, + "noteId": "4uh4j70LaKiG", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "GrPlXB0Xk9k0", + "4uh4j70LaKiG" + ], + "title": "Buy some book for Bob", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "location", + "value": "mall", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "tag", + "value": "christmas", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "todoDate", + "value": "", + "isInheritable": false, + "position": 70 + }, + { + "type": "label", + "name": "tag", + "value": "shopping", + "isInheritable": false, + "position": 80 + } + ], + "format": "html", + "dataFileName": "Buy some book for Bob.html", + "attachments": [], + "dirFileName": "Buy some book for Bob", + "children": [ + { + "isClone": false, + "noteId": "DWjd4cV0s4YG", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "749vDoR12ulD", + "GrPlXB0Xk9k0", + "4uh4j70LaKiG", + "DWjd4cV0s4YG" + ], + "title": "Maybe Black Swan?", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Maybe Black Swan.html", + "attachments": [] + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "74H7PQ7nSsUf", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "74H7PQ7nSsUf" + ], + "title": "Done", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskDoneRoot", + "value": "", + "isInheritable": false, + "position": 0 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-check-square", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Done", + "children": [ + { + "isClone": false, + "noteId": "PbOr6Ka8Q1pu", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "74H7PQ7nSsUf", + "PbOr6Ka8Q1pu" + ], + "title": "Buy a board game for Alice", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "location", + "value": "mall", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "done", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "tag", + "value": "christmas", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "tag", + "value": "shopping", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-20", + "isInheritable": false, + "position": 70 + }, + { + "type": "label", + "name": "doneDate", + "value": "2021-12-24", + "isInheritable": false, + "position": 80 + } + ], + "format": "html", + "dataFileName": "Buy a board game for Alice.html", + "attachments": [ + { + "attachmentId": "gQqtPMhL3mA0", + "title": "codenames.jpg", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "Buy a board game for Alice.jpg" + } + ] + }, + { + "isClone": false, + "noteId": "oLis5ntGu1GE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "74H7PQ7nSsUf", + "oLis5ntGu1GE" + ], + "title": "Dentist appointment", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "tag", + "value": "health", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "done", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-19", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "doneDate", + "value": "2021-12-19", + "isInheritable": false, + "position": 60 + } + ], + "format": "html", + "dataFileName": "Dentist appointment.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "MkD3XMHCTSOr", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "74H7PQ7nSsUf", + "MkD3XMHCTSOr" + ], + "title": "Get a gym membership", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "location", + "value": "gym", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "cssClass", + "value": "done", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "todoDate", + "value": "2021-12-28", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "doneDate", + "value": "2021-12-30", + "isInheritable": false, + "position": 60 + } + ], + "format": "html", + "dataFileName": "Get a gym membership.html", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "o1sKXDXzZRwS", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "o1sKXDXzZRwS" + ], + "title": "TODO", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTodoRoot", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "child:task", + "value": "", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "child:template", + "value": "7qiHS46jApK2", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "child:cssClass", + "value": "todo", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "bookmarkFolder", + "value": "", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-plane-take-off", + "isInheritable": false, + "position": 51 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "TODO", + "children": [ + { + "isClone": true, + "noteId": "Evz7c4Npu4PO", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "o1sKXDXzZRwS", + "Evz7c4Npu4PO" + ], + "title": "Send invites for christmas party", + "prefix": null, + "dataFileName": "Send invites for christmas party.clone.html", + "type": "text", + "format": "html" + }, + { + "isClone": true, + "noteId": "hu6fwfUGqnZR", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "o1sKXDXzZRwS", + "hu6fwfUGqnZR" + ], + "title": "Buy milk", + "prefix": null, + "dataFileName": "Buy milk.clone.html", + "type": "text", + "format": "html" + }, + { + "isClone": true, + "noteId": "4uh4j70LaKiG", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "o1sKXDXzZRwS", + "4uh4j70LaKiG" + ], + "title": "Buy some book for Bob", + "prefix": null, + "dataFileName": "Buy some book for Bob.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "8fyQ8ULFg2w5", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5" + ], + "title": "Implementation", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-code-alt", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Implementation", + "children": [ + { + "isClone": false, + "noteId": "FvShZ1dQD0WH", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5", + "FvShZ1dQD0WH" + ], + "title": "attribute changed", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=backend", + "attributes": [], + "dataFileName": "attribute changed.js", + "attachments": [], + "dirFileName": "attribute changed", + "children": [ + { + "isClone": false, + "noteId": "g3wKmIEgGR9n", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5", + "FvShZ1dQD0WH", + "g3wKmIEgGR9n" + ], + "title": "reconcileAssignments", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=backend", + "attributes": [], + "dataFileName": "reconcileAssignments.js", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "g4vuWDH3pfQB", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5", + "g4vuWDH3pfQB" + ], + "title": "CSS", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/css", + "attributes": [ + { + "type": "label", + "name": "appCss", + "value": "", + "isInheritable": false, + "position": 0 + } + ], + "dataFileName": "CSS.css", + "attachments": [] + }, + { + "isClone": false, + "noteId": "7qiHS46jApK2", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5", + "7qiHS46jApK2" + ], + "title": "task template", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "label:location", + "value": "promoted,text,single", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "cssClass", + "value": "todo", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "label:tag", + "value": "promoted,text,multi", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "label:todoDate", + "value": "promoted,date,single", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "label:doneDate", + "value": "promoted,date,single", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-task", + "isInheritable": false, + "position": 70 + }, + { + "type": "label", + "name": "task", + "value": "", + "isInheritable": false, + "position": 80 + }, + { + "type": "relation", + "name": "runOnAttributeChange", + "value": "FvShZ1dQD0WH", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "task template.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "SI8AwI2KJqip", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "8fyQ8ULFg2w5", + "SI8AwI2KJqip" + ], + "title": "createNewTask", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "createNewTask.js", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "ZLW5vUDmnj7c", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c" + ], + "title": "Tags", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTagRoot", + "value": "", + "isInheritable": false, + "position": 0 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-purchase-tag", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "Tags", + "children": [ + { + "isClone": false, + "noteId": "L1NgccjlH70Q", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "L1NgccjlH70Q" + ], + "title": "health", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTagNote", + "value": "health", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "dataFileName": "health.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "bRwEJbMouAJh", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "bRwEJbMouAJh" + ], + "title": "shopping", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTagNote", + "value": "shopping", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "shopping", + "children": [ + { + "isClone": true, + "noteId": "4uh4j70LaKiG", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "bRwEJbMouAJh", + "4uh4j70LaKiG" + ], + "title": "Buy some book for Bob", + "prefix": null, + "dataFileName": "Buy some book for Bob.clone.html", + "type": "text", + "format": "html" + }, + { + "isClone": true, + "noteId": "hu6fwfUGqnZR", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "bRwEJbMouAJh", + "hu6fwfUGqnZR" + ], + "title": "Buy milk", + "prefix": null, + "dataFileName": "Buy milk.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "wxvh8gxBAFTr", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "wxvh8gxBAFTr" + ], + "title": "groceries", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTagNote", + "value": "groceries", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "groceries", + "children": [ + { + "isClone": true, + "noteId": "hu6fwfUGqnZR", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "wxvh8gxBAFTr", + "hu6fwfUGqnZR" + ], + "title": "Buy milk", + "prefix": null, + "dataFileName": "Buy milk.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "H8AbesQ2DsJV", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "H8AbesQ2DsJV" + ], + "title": "christmas", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "taskTagNote", + "value": "christmas", + "isInheritable": false, + "position": 1 + } + ], + "format": "html", + "attachments": [], + "dirFileName": "christmas", + "children": [ + { + "isClone": true, + "noteId": "4uh4j70LaKiG", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "ZLW5vUDmnj7c", + "H8AbesQ2DsJV", + "4uh4j70LaKiG" + ], + "title": "Buy some book for Bob", + "prefix": null, + "dataFileName": "Buy some book for Bob.clone.html", + "type": "text", + "format": "html" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "qv7UJuErc16Q", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "zQpMt8tYmYPL", + "qv7UJuErc16Q" + ], + "title": "Create Launcher", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=backend", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-sidebar", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "createNewTask", + "value": "SI8AwI2KJqip", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "executeButton", + "value": "Create Launcher", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "executeDescription", + "value": "This script creates a launcher on the left side bar which can be used to quickly create a new task", + "isInheritable": false, + "position": 40 + } + ], + "dataFileName": "Create Launcher.js", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "uEhizdFWgTIC", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uEhizdFWgTIC" + ], + "title": "Word count widget", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [ + { + "type": "label", + "name": "widget", + "value": "", + "isInheritable": false, + "position": 10 + } + ], + "dataFileName": "Word count widget.js", + "attachments": [] + }, + { + "isClone": false, + "noteId": "IYO5iEWuPCbj", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "IYO5iEWuPCbj" + ], + "title": "Weight Tracker", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "hideChildrenOverview", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "bookmarked", + "value": "", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-tachometer", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "renderNote", + "value": "iFlQcEYLIVWB", + "isInheritable": false, + "position": 20 + } + ], + "attachments": [], + "dirFileName": "Weight Tracker", + "children": [ + { + "isClone": false, + "noteId": "iFlQcEYLIVWB", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "IYO5iEWuPCbj", + "iFlQcEYLIVWB" + ], + "title": "Implementation", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "Implementation.html", + "attachments": [], + "dirFileName": "Implementation", + "children": [ + { + "isClone": false, + "noteId": "fdpRmy4olJ9D", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "IYO5iEWuPCbj", + "iFlQcEYLIVWB", + "fdpRmy4olJ9D" + ], + "title": "JS code", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "JS code.js", + "attachments": [], + "dirFileName": "JS code", + "children": [ + { + "isClone": false, + "noteId": "iS5BBNx0i74J", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "IYO5iEWuPCbj", + "iFlQcEYLIVWB", + "fdpRmy4olJ9D", + "iS5BBNx0i74J" + ], + "title": "chart.js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "file", + "mime": "text/javascript", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "chart.min.js", + "isInheritable": false, + "position": 1 + } + ], + "dataFileName": "chart.js", + "attachments": [] + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "uK5ckLvFPUUX", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX" + ], + "title": "Statistics", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "bookZoomLevel", + "value": "2", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Statistics", + "children": [ + { + "isClone": false, + "noteId": "BsUewm6yHngC", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC" + ], + "title": "Attribute count", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "AiKGUNbMJeOi", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Attribute count", + "children": [ + { + "isClone": false, + "noteId": "AiKGUNbMJeOi", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi" + ], + "title": "template", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "fYAbi7MctpJ1", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [], + "dirFileName": "js", + "children": [ + { + "isClone": false, + "noteId": "TsdykeHU5RJ9", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1", + "TsdykeHU5RJ9" + ], + "title": "renderPieChart", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "renderPieChart.js", + "attachments": [], + "dirFileName": "renderPieChart", + "children": [ + { + "isClone": false, + "noteId": "AQV0tCouCZpT", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1", + "TsdykeHU5RJ9", + "AQV0tCouCZpT" + ], + "title": "chartjs-plugin-datalabels.min.js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "file", + "mime": "text/javascript", + "attributes": [ + { + "type": "label", + "name": "originalFileName", + "value": "chartjs-plugin-datalabels.min.js", + "isInheritable": false, + "position": 1 + } + ], + "dataFileName": "chartjs-plugin-datalabe.min.js", + "attachments": [], + "dirFileName": "chartjs-plugin-datalabels.min.js", + "children": [ + { + "isClone": true, + "noteId": "iS5BBNx0i74J", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1", + "TsdykeHU5RJ9", + "AQV0tCouCZpT", + "iS5BBNx0i74J" + ], + "title": "chart.js", + "prefix": null, + "dataFileName": "chart.js.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": true, + "noteId": "iS5BBNx0i74J", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1", + "TsdykeHU5RJ9", + "iS5BBNx0i74J" + ], + "title": "chart.js", + "prefix": null, + "dataFileName": "chart.js.clone.html", + "type": "text", + "format": "html" + } + ] + }, + { + "isClone": false, + "noteId": "hfRME6bdRU2R", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "BsUewm6yHngC", + "AiKGUNbMJeOi", + "fYAbi7MctpJ1", + "hfRME6bdRU2R" + ], + "title": "renderTable", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "renderTable.js", + "attachments": [] + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "H0nm1lyI4jCY", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "H0nm1lyI4jCY" + ], + "title": "Largest notes", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "1ZUtxvW1qeyZ", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Largest notes", + "children": [ + { + "isClone": false, + "noteId": "1ZUtxvW1qeyZ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "H0nm1lyI4jCY", + "1ZUtxvW1qeyZ" + ], + "title": "template", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "jsM6edxHyM0C", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "H0nm1lyI4jCY", + "1ZUtxvW1qeyZ", + "jsM6edxHyM0C" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "AJ2eejoELNhk", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "AJ2eejoELNhk" + ], + "title": "Most edited notes", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "VNUKOvrPKC76", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Most edited notes", + "children": [ + { + "isClone": false, + "noteId": "VNUKOvrPKC76", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "AJ2eejoELNhk", + "VNUKOvrPKC76" + ], + "title": "template", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "JJyoW0AUEa3E", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "AJ2eejoELNhk", + "VNUKOvrPKC76", + "JJyoW0AUEa3E" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "7W53TQFpBujr", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "7W53TQFpBujr" + ], + "title": "Most linked notes", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "1zt0MOCyJEVI", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Most linked notes", + "children": [ + { + "isClone": false, + "noteId": "1zt0MOCyJEVI", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "7W53TQFpBujr", + "1zt0MOCyJEVI" + ], + "title": "template", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "76s2u6NeK7IE", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "7W53TQFpBujr", + "1zt0MOCyJEVI", + "76s2u6NeK7IE" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "5xtL10PYc4bf", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "5xtL10PYc4bf" + ], + "title": "Note type count", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "fmbJsCyc7oOc", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Note type count", + "children": [ + { + "isClone": false, + "noteId": "fmbJsCyc7oOc", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "5xtL10PYc4bf", + "fmbJsCyc7oOc" + ], + "title": "template", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "fqsDkKujJc3w", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "5xtL10PYc4bf", + "fmbJsCyc7oOc", + "fqsDkKujJc3w" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [], + "dirFileName": "js", + "children": [ + { + "isClone": false, + "noteId": "iStjuccma54t", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "5xtL10PYc4bf", + "fmbJsCyc7oOc", + "fqsDkKujJc3w", + "iStjuccma54t" + ], + "title": "renderTable", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "renderTable.js", + "attachments": [] + }, + { + "isClone": true, + "noteId": "TsdykeHU5RJ9", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "5xtL10PYc4bf", + "fmbJsCyc7oOc", + "fqsDkKujJc3w", + "TsdykeHU5RJ9" + ], + "title": "renderPieChart", + "prefix": null, + "dataFileName": "renderPieChart.clone.html", + "type": "text", + "format": "html" + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "JkMXQ0r5bRnI", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "JkMXQ0r5bRnI" + ], + "title": "Most cloned notes", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "render", + "mime": "", + "attributes": [ + { + "type": "relation", + "name": "renderNote", + "value": "VeQJSgKIkaUQ", + "isInheritable": false, + "position": 10 + } + ], + "attachments": [], + "dirFileName": "Most cloned notes", + "children": [ + { + "isClone": false, + "noteId": "VeQJSgKIkaUQ", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "JkMXQ0r5bRnI", + "VeQJSgKIkaUQ" + ], + "title": "template", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "text/html", + "attributes": [], + "dataFileName": "template.html", + "attachments": [], + "dirFileName": "template", + "children": [ + { + "isClone": false, + "noteId": "NI8zZ7IznTZT", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "uK5ckLvFPUUX", + "JkMXQ0r5bRnI", + "VeQJSgKIkaUQ", + "NI8zZ7IznTZT" + ], + "title": "js", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "js.js", + "attachments": [] + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "IPifAXM3cyRS", + "notePath": [ + "root", + "Fb7eMY46lT6k", + "G8k9CPUVtDeE", + "IPifAXM3cyRS" + ], + "title": "Custom request handler", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=backend", + "attributes": [ + { + "type": "relation", + "name": "targetNote", + "value": "B08ZaJdYTK5v", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "customRequestHandler", + "value": "create-note", + "isInheritable": false, + "position": 20 + } + ], + "dataFileName": "Custom request handler.js", + "attachments": [] + } + ] + } + ] + } + ] + }, + { + "noImport": true, + "dataFileName": "navigation.html" + }, + { + "noImport": true, + "dataFileName": "index.html" + }, + { + "noImport": true, + "dataFileName": "style.css" + } + ] +} \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/index.html b/demo/index.html similarity index 81% rename from src/public/app/doc_notes/en/User Guide/index.html rename to demo/index.html index c5c17da96..638b4e3bf 100644 --- a/src/public/app/doc_notes/en/User Guide/index.html +++ b/demo/index.html @@ -6,6 +6,6 @@ - + \ No newline at end of file diff --git a/demo/navigation.html b/demo/navigation.html new file mode 100644 index 000000000..55bba6d58 --- /dev/null +++ b/demo/navigation.html @@ -0,0 +1,646 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Advanced topics/REST API/ETAPI/API Reference.dat b/demo/root/Journal.dat similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Advanced topics/REST API/ETAPI/API Reference.dat rename to demo/root/Journal.dat diff --git a/demo/root/Trilium Demo.html b/demo/root/Trilium Demo.html new file mode 100644 index 000000000..59ef348b7 --- /dev/null +++ b/demo/root/Trilium Demo.html @@ -0,0 +1,81 @@ + + + + + + + + Trilium Demo + + + +
+

Trilium Demo

+ +
+
+ +
+

Welcome to TriliumNext Notes! + +

+

This is initial "demo" document provided by TriliumNext by default to + showcase some of its features and also give you some ideas how you might + structure your notes. You can play with it, modify note content and tree + structure as you wish.

+

If you need any help, visit TriliumNext website: https://github.com/TriliumNext + +

+

Cleanup

+ +

Once you're finished with experimenting and want to cleanup these pages, + you can simply delete them all.

+

Formatting

+ +

TriliumNext supports classic formatting like italic, bold, bold and italic. + Of course you can add links like this one pointing to google.com + +

+

Lists

+

Ordered: + +

+
    +
  1. First Item
  2. +
  3. Second item +
      +
    1. First sub-item +
        +
      1. sub-sub-item
      2. +
      +
    2. +
    +
  4. +
+

Unordered: + +

+
    +
  • Item
  • +
  • Another item +
      +
    • Sub-item
    • +
    +
  • +
+

Block quotes

+
+

Whereof one cannot speak, thereof one must be silent”

+

– Ludwig Wittgenstein

+
+

Checkout also other examples like tables, + checkbox lists, highlighting, + code blocksand math examples.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Books/Book template.html b/demo/root/Trilium Demo/Books/Book template.html new file mode 100644 index 000000000..b6ece231e --- /dev/null +++ b/demo/root/Trilium Demo/Books/Book template.html @@ -0,0 +1,40 @@ + + + + + + + + Book template + + + +
+

Book template

+ +
+

Main characters

+ +

… here put main characters …

+

 

+

Plot

+ +

… describe main plot lines …

+

 

+

Tone

+ +

 

+

Genre

+ +

scifi / drama / romance

+

 

+

Similar books

+ +
    +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Books/Book template/Highlights.html b/demo/root/Trilium Demo/Books/Book template/Highlights.html new file mode 100644 index 000000000..2339b4a4d --- /dev/null +++ b/demo/root/Trilium Demo/Books/Book template/Highlights.html @@ -0,0 +1,24 @@ + + + + + + + + Highlights + + + +
+

Highlights

+ +
+
+

highlght 1

+
+

my comment

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Books/Reviews/The Mechanical.clone.html b/demo/root/Trilium Demo/Books/Reviews/The Mechanical.clone.html new file mode 100644 index 000000000..cbaf13765 --- /dev/null +++ b/demo/root/Trilium Demo/Books/Reviews/The Mechanical.clone.html @@ -0,0 +1,21 @@ + + + + + + + + The Mechanical + + + +
+

The Mechanical

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Books/To read.html b/demo/root/Trilium Demo/Books/To read.html new file mode 100644 index 000000000..30cf1d0d1 --- /dev/null +++ b/demo/root/Trilium Demo/Books/To read.html @@ -0,0 +1,31 @@ + + + + + + + + To read + + + +
+

To read

+ +
+

Checkout Kindle daily deals: https://www.amazon.com/gp/feature.html?docId=1000677541 + +

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Formatting examples/Checkbox lists.html b/demo/root/Trilium Demo/Formatting examples/Checkbox lists.html new file mode 100644 index 000000000..1a581a9d0 --- /dev/null +++ b/demo/root/Trilium Demo/Formatting examples/Checkbox lists.html @@ -0,0 +1,47 @@ + + + + + + + + Checkbox lists + + + +
+

Checkbox lists

+ +
+

Create easy TODO-lists with checkboxes:

+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Formatting examples/Code blocks.html b/demo/root/Trilium Demo/Formatting examples/Code blocks.html new file mode 100644 index 000000000..5bb2da12c --- /dev/null +++ b/demo/root/Trilium Demo/Formatting examples/Code blocks.html @@ -0,0 +1,33 @@ + + + + + + + + Code blocks + + + +
+

Code blocks

+ +
+

Code blocks are useful to add short snippets of code blocks inside text + notes. Depending on your preference, it's possible to enable or disable + word wrapping for these code blocks.

+

We added syntax highlighting to code blocks as well. When a code block + is first created it will try to automatically determine the programming + language, should that fail it is possible to manually adjust it. The color + scheme for the syntax highlighting is adjustable in settings. 

function helloWorld() {
+	alert("Hello world");
+}
+ +

For larger pieces of code it is better to use a code note, which uses + a fully-fledged code editor (CodeMirror). For an example of a code note, + see Custom request handler.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Formatting examples/Highlighting.html b/demo/root/Trilium Demo/Formatting examples/Highlighting.html new file mode 100644 index 000000000..6fb5ac5b6 --- /dev/null +++ b/demo/root/Trilium Demo/Formatting examples/Highlighting.html @@ -0,0 +1,35 @@ + + + + + + + + Highlighting + + + +
+

Highlighting

+ +
+

Space Shuttle Discovery (Orbiter Vehicle + Designation: OV-103) is one of the orbiters from NASA's Space + Shuttle program and the third of five fully operational orbiters to be + built. Its first mission, STS-41-D, flew from August 30 to September 5, + 1984. Over 27 years of service it launched and landed 39 times, + gathering more spaceflights than any other spacecraft to date. The shuttle + has three main components: the Space Shuttle orbiter, a central fuel tank, + and two rocket boosters. Nearly 25,000 heat resistant tiles cover + the orbiter to protect it from high temperatures on re-entry.

+

Discovery became the third operational orbiter to enter service, preceded + by Columbia and Challenger. It embarked on its last mission, STS-133, on February 24, 2011 and + touched down for the final time at Kennedy Space Center on March 9, having + spent a cumulative total of almost a full year in space. Discovery performed + both research and International Space Station (ISS) assembly missions, + and also carried the Hubble Space Telescope into orbit.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Formatting examples/Math.html b/demo/root/Trilium Demo/Formatting examples/Math.html new file mode 100644 index 000000000..e646e00b4 --- /dev/null +++ b/demo/root/Trilium Demo/Formatting examples/Math.html @@ -0,0 +1,27 @@ + + + + + + + + Math + + + +
+

Math

+ +
+

\(% \f is defined as #1f(#2) using the macro \f\relax{x} = \int_{-\infty}^\infty     \f\hat\xi\,e^{2 \pi i \xi x}     \,d\xi\)Some + math examples:

\[\displaystyle \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }\] + +

Another:

\[\displaystyle \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)\] + +

Inline math is also possible: \(c^2 = a^2 + b^2\) 

+

 

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Formatting examples/School schedule.html b/demo/root/Trilium Demo/Formatting examples/School schedule.html new file mode 100644 index 000000000..f07da5807 --- /dev/null +++ b/demo/root/Trilium Demo/Formatting examples/School schedule.html @@ -0,0 +1,69 @@ + + + + + + + + School schedule + + + +
+

School schedule

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 MondayTuesdayWednesdayThursdayFriday
9:00-10:30P.E. MathComputer Science 
11:00-12:30HistoryEnglish PhysicsMath
13:00-14:30 Computer ScienceChemistryPhysics LabGeography
15:00-16:30Computer Science Latin  
+
School schedule
+
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Inbox.html b/demo/root/Trilium Demo/Inbox.html new file mode 100644 index 000000000..070f07448 --- /dev/null +++ b/demo/root/Trilium Demo/Inbox.html @@ -0,0 +1,25 @@ + + + + + + + + Inbox + + + +
+

Inbox

+ +
+
+
+

This is a place I use to put notes waiting for better categorization

+
+
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Inbox/Book to read.html b/demo/root/Trilium Demo/Inbox/Book to read.html new file mode 100644 index 000000000..24a600a6f --- /dev/null +++ b/demo/root/Trilium Demo/Inbox/Book to read.html @@ -0,0 +1,22 @@ + + + + + + + + Book to read + + + + + + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Inbox/Grocery list for today.html b/demo/root/Trilium Demo/Inbox/Grocery list for today.html new file mode 100644 index 000000000..53b8f74d2 --- /dev/null +++ b/demo/root/Trilium Demo/Inbox/Grocery list for today.html @@ -0,0 +1,25 @@ + + + + + + + + Grocery list for today + + + +
+

Grocery list for today

+ +
+
    +
  • cucumber
  • +
  • cheese
  • +
  • beer
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Inbox/The Last Question.html b/demo/root/Trilium Demo/Inbox/The Last Question.html new file mode 100644 index 000000000..57cc8dff5 --- /dev/null +++ b/demo/root/Trilium Demo/Inbox/The Last Question.html @@ -0,0 +1,32 @@ + + + + + + + + The Last Question + + + +
+

The Last Question

+ +
+

"The Last Question" is a science fiction + short storyby American writer Isaac Asimov. + It first appeared in the November 1956 issue of Science Fiction Quarterly.

+
 
+

This page demonstrates two things:

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Inbox/The Last Question/The Last Question by Issac.pdf b/demo/root/Trilium Demo/Inbox/The Last Question/The Last Question by Issac.pdf new file mode 100644 index 000000000..717a69943 Binary files /dev/null and b/demo/root/Trilium Demo/Inbox/The Last Question/The Last Question by Issac.pdf differ diff --git a/demo/root/Trilium Demo/Journal.html b/demo/root/Trilium Demo/Journal.html new file mode 100644 index 000000000..629b4870f --- /dev/null +++ b/demo/root/Trilium Demo/Journal.html @@ -0,0 +1,23 @@ + + + + + + + + Journal + + + +
+

Journal

+ +
+

You can read some explanation on how this journal works here: https://github.com/zadam/trilium/wiki/Day-notes + +

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday.html b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday.html new file mode 100644 index 000000000..e4a99c109 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday.html @@ -0,0 +1,29 @@ + + + + + + + + 28 - Tuesday + + + +
+

28 - Tuesday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Christmas gift ideas.html b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Christmas gift ideas.html new file mode 100644 index 000000000..01ad36c16 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Christmas gift ideas.html @@ -0,0 +1,28 @@ + + + + + + + + Christmas gift ideas + + + +
+

Christmas gift ideas

+ +
+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Phone call about work project.html b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Phone call about work project.html new file mode 100644 index 000000000..11b751054 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Phone call about work project.html @@ -0,0 +1,21 @@ + + + + + + + + Phone call about work project + + + +
+

Phone call about work project

+ +
+

Bla bla bla ...

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Trusted timestamping.html b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Trusted timestamping.html new file mode 100644 index 000000000..bae5a1ed0 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/11 - November/28 - Tuesday/Trusted timestamping.html @@ -0,0 +1,33 @@ + + + + + + + + Trusted timestamping + + + +
+

Trusted timestamping

+ +
+

Wiki: https://en.wikipedia.org/wiki/Trusted_timestamping + +

+

Bozho: https://techblog.bozho.net/using-trusted-timestamping-java/ + +

+

Trusted timestamping is the process of securely keeping + track of the creation and modification time of a document. Security here + means that no one—not even the owner of the document—should be able to + change it once it has been recorded provided that the timestamper's integrity + is never compromised.

+

The administrative aspect involves setting up a publicly available, trusted + timestamp management infrastructure to collect, process and renew timestamps.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday.html new file mode 100644 index 000000000..3722a62c0 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday.html @@ -0,0 +1,27 @@ + + + + + + + + 18 - Monday + + + +
+

18 - Monday

+ +
+

Miscellaneous notes done on monday ...

+

 

+

Interesting video: https://www.youtube.com/watch?v=_eSAF_qT_FY&feature=youtu.be + +

+

 

+

 

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Meeting minutes.html b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Meeting minutes.html new file mode 100644 index 000000000..477456a5c --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Meeting minutes.html @@ -0,0 +1,21 @@ + + + + + + + + Meeting minutes + + + +
+

Meeting minutes

+ +
+

bla bla bla...

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/01.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/01.jpeg new file mode 100644 index 000000000..526922538 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/01.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/02.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/02.jpeg new file mode 100644 index 000000000..a6bd590e4 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/02.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/03.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/03.jpeg new file mode 100644 index 000000000..8d6ee7003 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/03.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/04.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/04.jpeg new file mode 100644 index 000000000..65dcff9f0 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/04.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/05.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/05.jpeg new file mode 100644 index 000000000..56dad904f Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/05.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/06.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/06.jpeg new file mode 100644 index 000000000..afae674d8 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/06.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/07.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/07.jpeg new file mode 100644 index 000000000..1a4177f45 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/07.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/08.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/08.jpeg new file mode 100644 index 000000000..eaa57bfbf Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/08.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/09.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/09.jpeg new file mode 100644 index 000000000..3266325f2 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/09.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/10.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/10.jpeg new file mode 100644 index 000000000..0ecdcc360 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/10.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/11.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/11.jpeg new file mode 100644 index 000000000..24fb1fc9e Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/11.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/12.jpeg b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/12.jpeg new file mode 100644 index 000000000..ccded29c7 Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/Photos from the trip/12.jpeg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/TODO - Send invites for christ.html b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/TODO - Send invites for christ.html new file mode 100644 index 000000000..fe8e14b7b --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/18 - Monday/TODO - Send invites for christ.html @@ -0,0 +1,19 @@ + + + + + + + + Send invites for christmas party + + + +
+

Send invites for christmas party

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday.html new file mode 100644 index 000000000..487b71530 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday.html @@ -0,0 +1,29 @@ + + + + + + + + 19 - Tuesday + + + +
+

19 - Tuesday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday/DONE - Dentist appointment.html b/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday/DONE - Dentist appointment.html new file mode 100644 index 000000000..e3477864c --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/19 - Tuesday/DONE - Dentist appointment.html @@ -0,0 +1,19 @@ + + + + + + + + Dentist appointment + + + +
+

Dentist appointment

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/20 - Wednesday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/20 - Wednesday.html new file mode 100644 index 000000000..ae559b3c1 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/20 - Wednesday.html @@ -0,0 +1,29 @@ + + + + + + + + 20 - Wednesday + + + +
+

20 - Wednesday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday.html new file mode 100644 index 000000000..28b77ec56 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday.html @@ -0,0 +1,29 @@ + + + + + + + + 21 - Thursday + + + +
+

21 - Thursday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Christmas shopping.html b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Christmas shopping.html new file mode 100644 index 000000000..3e3e61d8c --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Christmas shopping.html @@ -0,0 +1,21 @@ + + + + + + + + Christmas shopping + + + +
+

Christmas shopping

+ +
+

Bought a book!

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Office party.html b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Office party.html new file mode 100644 index 000000000..f4706849b --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/21 - Thursday/Office party.html @@ -0,0 +1,21 @@ + + + + + + + + Office party + + + +
+

Office party

+ +
+

That was fun!

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday.html new file mode 100644 index 000000000..3545fd3d7 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday.html @@ -0,0 +1,29 @@ + + + + + + + + 22 - Friday + + + +
+

22 - Friday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/Christmas shopping.html b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/Christmas shopping.html new file mode 100644 index 000000000..e72ad2ab8 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/Christmas shopping.html @@ -0,0 +1,19 @@ + + + + + + + + Christmas shopping + + + +
+

Christmas shopping

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical.html b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical.html new file mode 100644 index 000000000..9fcb3de3b --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical.html @@ -0,0 +1,29 @@ + + + + + + + + The Mechanical + + + +
+

The Mechanical

+ +
+

I enjoyed this book a lot. It's slow moving at times with the author taking + his time with conversations and descriptions of them. The premise is very + interesting, but I'm sad that it wasn't elaborated more deeply - e.g. the + history and development of the clakker technology with Huygens and how + Spinoza comes into the picture. Maybe the author saves it for the next + two parts of the book.

+

Language can be intimidating at first for non-native english speakers + - author uses wide range of vocabulary. Fortunately it gets better after + a while as reader adjusts.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical/Highlights.html b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical/Highlights.html new file mode 100644 index 000000000..394ec3cca --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/22 - Friday/The Mechanical/Highlights.html @@ -0,0 +1,44 @@ + + + + + + + + Highlights + + + +
+

Highlights

+ +
+
+

Like a raindrop rolling down dry valleys to the sea, his body sensed the + contours of agony and helplessly followed their gradient. Impelled by alchemical + compulsion rather than gravity, Jax became an unstoppable boulder careering + along gullies of human whim.

+
+
+

Free Will was a vacuum, a negative space. It was the absence of coercion, + the absence of compulsion, the absence of agony.

+
+
+

Overwhelming: he could do anything he wanted. But the grand sum of anything-at-all + was nothing-at-all. The topology of freedom offered no gradients to nudge + him, no landmarks to guide him. How did humans guide themselves? How did + they know what to do and what not to do? How did they know when to do anything + without the benefit of geasa and metageasa to prioritize every single action + of their waking lives? How did they order their daily existence without + somebody to tell them what to do?

+
+
+

Life as a slave was unspeakable; life as a slave who had briefly tasted + freedom was unthinkable. Clakkers carried complex geasa by dint of alchemy; + humans carried heavy obligations, too, but called them culture. Society.

+
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/23 - Saturday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/23 - Saturday.html new file mode 100644 index 000000000..912b715c0 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/23 - Saturday.html @@ -0,0 +1,29 @@ + + + + + + + + 23 - Saturday + + + +
+

23 - Saturday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!.html b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!.html new file mode 100644 index 000000000..c243a035c --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!.html @@ -0,0 +1,29 @@ + + + + + + + + 24 - Sunday - Christmas Eve! + + + +
+

24 - Sunday - Christmas Eve!

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game fo.jpg b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game fo.jpg new file mode 100644 index 000000000..f0135ec7b Binary files /dev/null and b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game fo.jpg differ diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game for Al.html b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game for Al.html new file mode 100644 index 000000000..e7367a06c --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/DONE - Buy a board game for Al.html @@ -0,0 +1,27 @@ + + + + + + + + Buy a board game for Alice + + + +
+

Buy a board game for Alice

+ + +
+ + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/TODO - Buy milk.html similarity index 63% rename from src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html rename to demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/TODO - Buy milk.html index bbe5d76cc..61d000446 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/24 - Sunday - Christmas Eve!/TODO - Buy milk.html @@ -3,14 +3,14 @@ - + - Mermaid Diagram + Buy milk
-

Mermaid Diagram

+

Buy milk

diff --git a/demo/root/Trilium Demo/Journal/2021/12 - December/30 - Thursday.html b/demo/root/Trilium Demo/Journal/2021/12 - December/30 - Thursday.html new file mode 100644 index 000000000..133fae97b --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/12 - December/30 - Thursday.html @@ -0,0 +1,29 @@ + + + + + + + + 30 - Thursday + + + +
+

30 - Thursday

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics.html b/demo/root/Trilium Demo/Journal/2021/Epics.html new file mode 100644 index 000000000..45e345a52 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics.html @@ -0,0 +1,23 @@ + + + + + + + + Epics + + + +
+

Epics

+ +
+

Epic is kind of medium-term events or projects spread over days or months.

+

Remember that Trilium is all free form so you can organise your stuff + in whatever way you'd like.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics/Christmas.html b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas.html new file mode 100644 index 000000000..6aa070d40 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas.html @@ -0,0 +1,21 @@ + + + + + + + + Christmas + + + +
+

Christmas

+ +
+

This christmas is going to be awesome!

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Christmas dinner.html b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Christmas dinner.html new file mode 100644 index 000000000..41af08b44 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Christmas dinner.html @@ -0,0 +1,21 @@ + + + + + + + + Christmas dinner + + + +
+

Christmas dinner

+ +
+

Carp of course!

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Shopping/28. 11. 2017 - Christmas gift ideas.clone.html b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Shopping/28. 11. 2017 - Christmas gift ideas.clone.html new file mode 100644 index 000000000..044fe3e8f --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Shopping/28. 11. 2017 - Christmas gift ideas.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Christmas gift ideas + + + +
+

Christmas gift ideas

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Vacation days.html b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Vacation days.html new file mode 100644 index 000000000..c6d39244f --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics/Christmas/Vacation days.html @@ -0,0 +1,22 @@ + + + + + + + + Vacation days + + + +
+

Vacation days

+ +
+

25. 12., 26. 12., 1. 1. - statutory holidays

+

27. 12. - 29. 12., 2. 1. - vacation days

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/2021/Epics/Vacation.html b/demo/root/Trilium Demo/Journal/2021/Epics/Vacation.html new file mode 100644 index 000000000..5d82e56d8 --- /dev/null +++ b/demo/root/Trilium Demo/Journal/2021/Epics/Vacation.html @@ -0,0 +1,21 @@ + + + + + + + + Vacation + + + +
+

Vacation

+ +
+

Planning stuff etc.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Journal/Day template.html b/demo/root/Trilium Demo/Journal/Day template.html new file mode 100644 index 000000000..44bd101ae --- /dev/null +++ b/demo/root/Trilium Demo/Journal/Day template.html @@ -0,0 +1,29 @@ + + + + + + + + Day template + + + +
+

Day template

+ +
+

TODO:

+
    +
  • + +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Canvas.json b/demo/root/Trilium Demo/Note Types/Canvas.json new file mode 100644 index 000000000..7b57adb55 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Canvas.json @@ -0,0 +1 @@ +{"type":"excalidraw","version":2,"elements":[{"type":"rectangle","version":235,"versionNonce":92121392,"isDeleted":false,"id":"Nx_6ACJaaueNh0fTsVZeh","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":14.66796875,"y":8.13671875,"strokeColor":"#000000","backgroundColor":"#fa5252","width":247,"height":138,"seed":679323330,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a0"},{"type":"arrow","version":143,"versionNonce":1500561872,"isDeleted":false,"id":"2bmK1AKg9yVMP6CO_U6Ke","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":216,"y":67,"strokeColor":"#000000","backgroundColor":"#fa5252","width":289,"height":46,"seed":1265574494,"groupIds":[],"strokeSharpness":"round","boundElements":[],"updated":1743367114827,"link":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":"arrow","points":[[0,0],[289,46]],"index":"a1"},{"type":"ellipse","version":163,"versionNonce":1146159920,"isDeleted":false,"id":"TOmE_26Ye9b_m_TKfWU45","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":441,"y":31,"strokeColor":"#000000","backgroundColor":"#228be6","width":203,"height":191,"seed":77588894,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a2"},{"type":"text","version":207,"versionNonce":664274896,"isDeleted":false,"id":"K5CjVJa8vYzQ8LDOdH-av","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":319.5,"y":20,"strokeColor":"#000000","backgroundColor":"#228be6","width":71.75,"height":null,"seed":317664926,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"fontSize":20,"fontFamily":1,"text":"convert","baseline":18,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"convert","index":"a3"},{"type":"diamond","version":140,"versionNonce":1135156528,"isDeleted":false,"id":"rZ0DKhdiE-mys9orY2M7Y","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":335,"y":67,"strokeColor":"#000000","backgroundColor":"#228be6","width":31,"height":37,"seed":401619102,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a4"},{"type":"line","version":203,"versionNonce":2095484368,"isDeleted":false,"id":"gWzx0Ka27J2gQYsjliI5u","fillStyle":"solid","strokeWidth":1,"strokeStyle":"dashed","roughness":1,"opacity":100,"angle":0,"x":147,"y":142,"strokeColor":"#000000","backgroundColor":"#228be6","width":124,"height":113,"seed":668475458,"groupIds":[],"strokeSharpness":"round","boundElements":[],"updated":1743367114827,"link":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":null,"points":[[0,0],[124,113]],"index":"a5"},{"type":"rectangle","version":183,"versionNonce":351139632,"isDeleted":false,"id":"vPXlTlCV6XiWRo8z596tY","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"dotted","roughness":1,"opacity":100,"angle":0,"x":269,"y":240,"strokeColor":"#2b8a3e","backgroundColor":"#e6ffc8","width":144,"height":115,"seed":1313295006,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a6"},{"type":"text","version":317,"versionNonce":1920226256,"isDeleted":false,"id":"NlWoiu7QFiUGoGhxxFDQz","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":290,"y":247,"strokeColor":"#2b8a3e","backgroundColor":"#228be6","width":53.36666488647461,"height":null,"seed":146698370,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"fontSize":20,"fontFamily":1,"text":"List :\n- one\n- two","baseline":70,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"List :\n- one\n- two","index":"a7"},{"type":"diamond","version":221,"versionNonce":1552388400,"isDeleted":false,"id":"4tqMpG7kcfLvu81ba12UQ","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":5.899784582143685,"x":283.37319488867206,"y":123.6942301852414,"strokeColor":"#000000","backgroundColor":"#fa5252","width":104.0405522208337,"height":74.00000000000001,"seed":1677737711,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a8"},{"type":"rectangle","version":278,"versionNonce":1046043088,"isDeleted":false,"id":"79D3L3LhxNNySkbKD3Bia","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0.42451911970089906,"x":479.7715111234793,"y":259.96655309088374,"strokeColor":"#000000","backgroundColor":"#fa5252","width":128.50676566765082,"height":63,"seed":1447260752,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"a9"},{"type":"diamond","version":363,"versionNonce":1910369072,"isDeleted":false,"id":"ffrisBNjK3DyL4MRqDVUM","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0.3893167218331426,"x":21.865547135620886,"y":174.8538070033551,"strokeColor":"#000000","backgroundColor":"#15aabf","width":87.26890572875811,"height":46.29238599328972,"seed":63592112,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"aA"},{"type":"rectangle","version":519,"versionNonce":330145744,"isDeleted":false,"id":"wxvV5tzdo2p3m4nBQf_fI","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":5.997855886997452,"x":90.74789364500919,"y":293.3561760950281,"strokeColor":"#000000","backgroundColor":"#fab005","width":123.50421270998135,"height":35.287647809943806,"seed":1439819952,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"aB"},{"type":"line","version":167,"versionNonce":2136540464,"isDeleted":false,"id":"i2pQf0tOL1I7RZA6fHNXy","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"dotted","roughness":1,"opacity":100,"angle":0.3264344116289468,"x":129,"y":177,"strokeColor":"#a61e4d","backgroundColor":"#fab005","width":71,"height":80,"seed":969735782,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":null,"points":[[0,0],[2,16],[12,40],[20,51],[36,64],[71,80]],"index":"aC"},{"type":"line","version":176,"versionNonce":42542544,"isDeleted":false,"id":"zTcY1DmmmEqtM7iaFOjI2","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":24,"y":282,"strokeColor":"#087f5b","backgroundColor":"#fab005","width":81,"height":33,"seed":1314441594,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":null,"points":[[0,0],[6,-22],[12,-29],[21,-30],[24,-29],[33,-17],[36,1],[37,2],[45,-29],[48,-29],[53,-31],[58,-31],[72,-23],[81,-8],[81,-4]],"index":"aD"},{"type":"rectangle","version":468,"versionNonce":576773936,"isDeleted":false,"id":"UqrOMUkW8i5NCxQf_EDc9","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0.11809202841909539,"x":190.3330910264575,"y":373.66279952417335,"strokeColor":"#000000","backgroundColor":"#be4bdb","width":90.33381794708485,"height":87.67440095165314,"seed":1466118630,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"aE"},{"type":"diamond","version":460,"versionNonce":1476651984,"isDeleted":false,"id":"06wK4k0VHVo3nl3maFgBt","fillStyle":"solid","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":5.137516099543635,"x":353.41738815890335,"y":394.61371445798545,"strokeColor":"#000000","backgroundColor":"#4c6ef5","width":80.89200154670186,"height":77.42001777890613,"seed":1789348282,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"aF"},{"type":"text","version":195,"versionNonce":190675248,"isDeleted":false,"id":"FExeJRXagFlMKEpW4ApvL","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"dotted","roughness":1,"opacity":100,"angle":5.9094584872140326,"x":35.78896794074159,"y":386.2393022458574,"strokeColor":"#d9480f","backgroundColor":"#4c6ef5","width":106.3499984741211,"height":null,"seed":152285754,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"fontSize":20,"fontFamily":2,"text":"Rotated text","baseline":23,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Rotated text","index":"aG"},{"type":"arrow","version":364,"versionNonce":2117464528,"isDeleted":false,"id":"n7nmJZmGCpL1a6l1TdMeX","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":520.1576551164967,"y":338.6957163378038,"strokeColor":"#000000","backgroundColor":"#fa5252","width":78.65765511649664,"height":75.30428366219626,"seed":925954106,"groupIds":[],"strokeSharpness":"round","boundElements":[],"updated":1743367114827,"link":null,"lastCommittedPoint":null,"startArrowhead":null,"endArrowhead":"arrow","points":[[0,0],[-78.65765511649664,75.30428366219626]],"index":"aH"},{"type":"text","version":185,"versionNonce":498520880,"isDeleted":false,"id":"lU3Msnm2CTXBHi8Pzef0C","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"dotted","roughness":1,"opacity":100,"angle":0,"x":509.5,"y":392,"strokeColor":"#000000","backgroundColor":"#4c6ef5","width":128.88333129882812,"height":null,"seed":1839996922,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"fontSize":20,"fontFamily":3,"text":"> some code","baseline":20,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"> some code","index":"aI"},{"type":"text","version":511,"versionNonce":1547122640,"isDeleted":false,"id":"CdlqC6FutWnAhFgUa4-tK","fillStyle":"hachure","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":0,"x":34.326958955223915,"y":31.00810810810811,"strokeColor":"#c92a2a","backgroundColor":"#228be6","width":165.3333282470703,"height":null,"seed":168503609,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"fontSize":39.25486991656494,"fontFamily":1,"text":"Big text","baseline":35,"textAlign":"left","verticalAlign":"top","containerId":null,"originalText":"Big text","index":"aJ"},{"type":"ellipse","version":306,"versionNonce":265612592,"isDeleted":false,"id":"lQb2o3G3uO-OSzJlv7dQf","fillStyle":"cross-hatch","strokeWidth":1,"strokeStyle":"solid","roughness":1,"opacity":100,"angle":5.987365538640589,"x":483.5,"y":118.5,"strokeColor":"#000000","backgroundColor":"#7950f2","width":133.00000000000003,"height":58.00000000000001,"seed":617054574,"groupIds":[],"strokeSharpness":"sharp","boundElements":[],"updated":1743367114827,"link":null,"index":"aK"}],"files":{},"appState":{"scrollX":255.921875,"scrollY":295.91526179206426,"zoom":{"value":1}}} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Canvas_canvas-export.svg b/demo/root/Trilium Demo/Note Types/Canvas_canvas-export.svg new file mode 100644 index 000000000..2bbd5b941 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Canvas_canvas-export.svg @@ -0,0 +1,3 @@ +convertList :- one- twoRotated text> some codeBig text \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of .json b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of .json new file mode 100644 index 000000000..c2bae9b3e --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of .json @@ -0,0 +1 @@ +{"view":{"center":{"lat":5.840169838914697,"lng":14.578571156950112},"zoom":3}} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Chichén Itzá, Mexico.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Chichén Itzá, Mexico.html new file mode 100644 index 000000000..3992ceed9 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Chichén Itzá, Mexico.html @@ -0,0 +1,46 @@ + + + + + + + + Chichén Itzá, Mexico + + + +
+

Chichén Itzá, Mexico

+ +
+

Deep in the Mexican state of Yucatán lies Chichen Itza, + a historic Mayan city + built between the 9th and 12th centuries. Constructed by the pre-Columbian + Mayan tribe Itzá, + the city includes a series of monuments and temples. The most celebrated + is El Castillo, + also known as the Temple of Kukulcan. It is a huge step pyramid in the + center of the city which was built as a devotional temple to the god Kukulkan. + [1] + + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Christ the Redeemer, Brazil.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Christ the Redeemer, Brazil.html new file mode 100644 index 000000000..cbaae70da --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Christ the Redeemer, Brazil.html @@ -0,0 +1,47 @@ + + + + + + + + Christ the Redeemer, Brazil + + + +
+

Christ the Redeemer, Brazil

+ +
+

The totemic statue of Christ the Redeemer stands + over Rio de Janeiro on the top of Mount Corcovado. + At 30 meters tall, this monument is an iconic emblem of Brazil. This huge + public artwork was designed by the Polish-French sculptor Paul Landowski in the 1920s and + completed by Brazilian engineer Heitor da Silva Costa and French engineer + Albert Caquot in 1931. Made from reinforced + concrete clad in over six million soapstone tiles, the Christ the Redeemer + statue is the largest Art Deco sculpture + in the world. Built just after the end of the First World War, the sculpture + was an overpowering symbol of Christianity and hope when the world had + been brought to its knees.[1] + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Machu Picchu, Peru.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Machu Picchu, Peru.html new file mode 100644 index 000000000..929103047 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Machu Picchu, Peru.html @@ -0,0 +1,43 @@ + + + + + + + + Machu Picchu, Peru + + + +
+

Machu Picchu, Peru

+ +
+

Machu Picchu is + a lost treasure of the 15th century and a rare citadel discovered high + in the Andes mountains above the Peruvian Sacred Valley. Astonishingly, + it is one of the only pre-Columbian ruins found nearly intact, featuring + evidence of former plazas, temples, agricultural terraces, and homes. Archaeologists + believe the citadel was built as an estate for the Inca emperor Pachacuti in + around 1450 in polished drystone walls.[1] + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Petra, Jordan.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Petra, Jordan.html new file mode 100644 index 000000000..55e0adafa --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/Petra, Jordan.html @@ -0,0 +1,44 @@ + + + + + + + + Petra, Jordan + + + +
+

Petra, Jordan

+ +
+

Petra, + the ancient city in southern Jordan, is also known as the “rose city” for + its golden hue. It dates as far back as 312 BCE. Set in a remote valley, + this city was founded by the Arab Nabataeans, a sophisticated civilization + that carved stunning architecture and complex waterways out of surrounding + rock faces. The Nabateans also established Petra as a successful trade + hub, earning vast wealth and a booming population before being wiped out + by earthquakes.[1] + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Colosseum, Rome, Italy.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Colosseum, Rome, Italy.html new file mode 100644 index 000000000..c9abd680d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Colosseum, Rome, Italy.html @@ -0,0 +1,48 @@ + + + + + + + + The Colosseum, Rome, Italy + + + +
+

The Colosseum, Rome, Italy

+ +
+

The Colosseum is + the great oval amphitheater in + the center of Rome where gladiators once fought for their lives and + the pleasure of the crowd. The largest amphitheater ever built, it was + constructed from sand and stone over eight years, from 72 to 80 CE. The + colossal structure could hold 80,000 spectators, arranged in a circular + ring around the central stage. Dramatic and sometimes horrifying events + took place here, not just gladiatorial games but also Classical plays, + animal hunts, and executions. Some say water was even pumped into the arena + to enact mock sea battles known as naumachia. + [1] + + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Great Wall of China.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Great Wall of China.html new file mode 100644 index 000000000..ccedb8388 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Great Wall of China.html @@ -0,0 +1,45 @@ + + + + + + + + The Great Wall of China + + + +
+

The Great Wall of China

+ +
+

The Great Wall of China is + a huge barrier that spans thousands of miles along China’s historic northern + border. Created over millennia, the wall began its life as a series of + smaller walls dating back to the 7th century BCE, built as protective barriers against nomadic raids. + In 220 BCE, China’s first Emperor, Qin Shi Huang, + masterminded the unification of all of China’s walls into one almighty + barrier, strengthening and extending the wall to keep out northern invaders. + [1] + + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Taj Mahal, India.html b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Taj Mahal, India.html new file mode 100644 index 000000000..755184be0 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Geo Map (The Seven Wonders of the World)/The Taj Mahal, India.html @@ -0,0 +1,44 @@ + + + + + + + + The Taj Mahal, India + + + +
+

The Taj Mahal, India

+ +
+

India’s renowned Taj Mahal (Persian for Crown of Palaces) is + the stunning white marble mausoleum on the bank of the Yamuna River in + the city of Agra. It has also been selected as one of the seven wonders + of the world. Mughal emperor Shah Jahan built + the temple as a tomb for his beloved wife, Mumtaz Mahal, who died during + childbirth in 1631. A marble tomb in the center is surrounded by 42 acres + of grounds, where gardens, a mosque, a guest house, and a pool complete + the complex.[1] + +

+
    +
  1. ^ + + +
  2. +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart.txt new file mode 100644 index 000000000..b13f7ee52 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart.txt @@ -0,0 +1,15 @@ +gantt + title Git Issues - days since last update + dateFormat X + axisFormat %s + + section Issue19062 + 71 : 0, 71 + section Issue19401 + 36 : 0, 36 + section Issue193 + 34 : 0, 34 + section Issue7441 + 9 : 0, 9 + section Issue1300 + 5 : 0, 5 \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart_mermaid-export.svg new file mode 100644 index 000000000..97489f06b --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Bar chart_mermaid-export.svg @@ -0,0 +1 @@ +051015202530354045505560657071 36 34 9 5 Issue19062Issue19401Issue193Issue7441Issue1300Git Issues - days since last update \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4.txt new file mode 100644 index 000000000..7e4dc961a --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4.txt @@ -0,0 +1,32 @@ +C4Context +title System Context diagram for Internet Banking System + +Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.") +Person(customerB, "Banking Customer B") +Person_Ext(customerC, "Banking Customer C") +System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.") + +Person(customerD, "Banking Customer D", "A customer of the bank,
with personal bank accounts.") + +Enterprise_Boundary(b1, "BankBoundary") { + + SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") + + System_Boundary(b2, "BankBoundary2") { + System(SystemA, "Banking System A") + System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts.") + } + + System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.") + SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.") + + Boundary(b3, "BankBoundary3", "boundary") { + SystemQueue(SystemF, "Banking System F Queue", "A system of the bank, with personal bank accounts.") + SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.") + } +} + +BiRel(customerA, SystemAA, "Uses") +BiRel(SystemAA, SystemE, "Uses") +Rel(SystemAA, SystemC, "Sends e-mails", "SMTP") +Rel(SystemC, customerA, "Sends e-mails to") \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4_mermaid-export.svg new file mode 100644 index 000000000..60f433a49 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/C4_mermaid-export.svg @@ -0,0 +1 @@ +<<person>>Banking Customer AA customer of the bank, with personal bank accounts.<<person>>Banking Customer B<<external_person>>Banking Customer C<<system>>Internet Banking SystemAllows customers to view information about their bank accounts, and make payments.<<person>>Banking Customer DA customer of the bank, with personal bank accounts.<<external_system_db>>Mainframe Banking SystemStores all of the core banking information about customers, accounts, transactions, etc.<<external_system>>E-mail systemThe internal Microsoft Exchange e-mail system.<<system_db>>Banking System D DatabaseA system of the bank, with personal bank accounts.<<system>>Banking System A<<system>>Banking System BA system of the bank, with personal bank accounts.BankBoundary2[ENTERPRISE]<<system_queue>>Banking System F QueueA system of the bank, with personal bank accounts.<<external_system_queue>>Banking System G QueueA system of the bank, with personal bank accounts.BankBoundary3[boundary]BankBoundary[ENTERPRISE]UsesUsesSends e-mails[SMTP]Sends e-mails toSystem Context diagram for Internet Banking System \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class.txt new file mode 100644 index 000000000..f902afadd --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class.txt @@ -0,0 +1,16 @@ +classDiagram +Class01 <|-- AveryLongClass : Cool +<> Class01 +Class09 --> C2 : Where am i? +Class09 --* C3 +Class09 --|> Class07 +Class07 : equals() +Class07 : Object[] elementData +Class01 : size() +Class01 : int chimp +Class01 : int gorilla +class Class10 { + <> + int id + size() +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class_mermaid-export.svg new file mode 100644 index 000000000..9d10efc11 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Class_mermaid-export.svg @@ -0,0 +1 @@ +
Cool
Where am i?
«interface»
Class01
int chimp
int gorilla
size()
AveryLongClass
Class09
C2
C3
Class07
Object[] elementData
equals()
«service»
Class10
int id
size()
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship.txt new file mode 100644 index 000000000..94d6f5196 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship.txt @@ -0,0 +1,10 @@ +erDiagram + CUSTOMER }|..|{ DELIVERY-ADDRESS : has + CUSTOMER ||--o{ ORDER : places + CUSTOMER ||--o{ INVOICE : "liable for" + DELIVERY-ADDRESS ||--o{ ORDER : receives + INVOICE ||--|{ ORDER : covers + ORDER ||--|{ ORDER-ITEM : includes + PRODUCT-CATEGORY ||--|{ PRODUCT : contains + PRODUCT ||--o{ ORDER-ITEM : "ordered in" + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship_mermai.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship_mermai.svg new file mode 100644 index 000000000..d5911752e --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Entity Relationship_mermai.svg @@ -0,0 +1 @@ +CUSTOMERDELIVERY-ADDRESSORDERINVOICEORDER-ITEMPRODUCT-CATEGORYPRODUCThasplacesliable forreceivescoversincludescontainsordered in \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK).txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK).txt new file mode 100644 index 000000000..34d7aedaf --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK).txt @@ -0,0 +1,15 @@ +--- +config: + layout: elk +--- +flowchart LR +A-->|"Guarantee"|B +C-->|"User attributes"|B +C-->|"Master data"|B +C-->|"Exchange Rate"|B +C-->|"Profit Centers"|B +C-->|"Vendor Partners"|B +C-->|"Work Situation"|B +C-->|"Customer"|B +C-->|"Profit Centers"|B +B-->|"Guarantee"|C \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK)_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK)_mermaid-export.svg new file mode 100644 index 000000000..1a133a1c7 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow (ELK)_mermaid-export.svg @@ -0,0 +1 @@ +

A

B

C

Guarantee

User attributes

Master data

Exchange Rate

Profit Centers

Vendor Partners

Work Situation

Customer

Profit Centers

Guarantee

\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow.txt new file mode 100644 index 000000000..a7fcef7f3 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow.txt @@ -0,0 +1,5 @@ +graph TD +A[Hard] -->|Text| B(Round) +B --> C{Decision} +C -->|One| D[Result 1] +C -->|Two| E[Result 2] \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow_mermaid-export.svg new file mode 100644 index 000000000..7941b9ae1 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Flow_mermaid-export.svg @@ -0,0 +1 @@ +

Text

One

Two

Hard

Round

Decision

Result 1

Result 2

\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt.txt new file mode 100644 index 000000000..af32bc27d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt.txt @@ -0,0 +1,8 @@ +gantt +section Section +Completed :done, des1, 2014-01-06,2014-01-08 +Active :active, des2, 2014-01-07, 3d +Parallel 1 : des3, after des1, 1d +Parallel 2 : des4, after des1, 1d +Parallel 3 : des5, after des3, 1d +Parallel 4 : des6, after des4, 1d \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt_mermaid-export.svg new file mode 100644 index 000000000..61ffc0260 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Gantt_mermaid-export.svg @@ -0,0 +1 @@ +2014-01-062014-01-072014-01-072014-01-082014-01-082014-01-092014-01-092014-01-10Completed Parallel 1 Parallel 2 Active Parallel 3 Parallel 4 Section \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git.txt new file mode 100644 index 000000000..f5c89f75d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git.txt @@ -0,0 +1,10 @@ + gitGraph + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git_mermaid-export.svg new file mode 100644 index 000000000..15b9bb454 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Git_mermaid-export.svg @@ -0,0 +1 @@ +maindevelop0-e6e63b91-f6c5e0c2-b62cc0e3-be86c5a4-7021d405-f753c376-3f1a1ef \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey.txt new file mode 100644 index 000000000..8bad1ef77 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey.txt @@ -0,0 +1,9 @@ + journey + title My working day + section Go to work + Make tea: 5: Me + Go upstairs: 3: Me + Do work: 1: Me, Cat + section Go home + Go downstairs: 5: Me + Sit down: 3: Me \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey_mermaid-export.svg new file mode 100644 index 000000000..2b6c1e75d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Journey_mermaid-export.svg @@ -0,0 +1 @@ +CatMe
Go to work
Go to work
Me
Make tea
Make tea
Me
Go upstairs
Go upstairs
MeCat
Do work
Do work
Go home
Go home
Me
Go downstairs
Go downstairs
Me
Sit down
Sit down
My working day
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map.txt new file mode 100644 index 000000000..6cdfad11d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map.txt @@ -0,0 +1,17 @@ +mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + Pen and paper + Mermaid \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map_mermaid-export.svg new file mode 100644 index 000000000..4e0a32214 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Mind Map_mermaid-export.svg @@ -0,0 +1 @@ +mindmapOriginsLong history
PopularisationBritish popular psychologyauthor Tony BuzanResearchOn effectivenessand featuresOn Automatic creationUsesCreative techniquesStrategic planningArgument mappingToolsPen and paperMermaid
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie.txt new file mode 100644 index 000000000..58aeda658 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie.txt @@ -0,0 +1,4 @@ +pie +"Dogs" : 386 +"Cats" : 85 +"Rats" : 15 \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie_mermaid-export.svg new file mode 100644 index 000000000..80909557d --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Pie_mermaid-export.svg @@ -0,0 +1 @@ +79%17%3%DogsCatsRats \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence.txt new file mode 100644 index 000000000..5ed800922 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence.txt @@ -0,0 +1,9 @@ +sequenceDiagram +Alice->>John: Hello John, how are you? +loop Healthcheck + John->>John: Fight against hypochondria +end +Note right of John: Rational thoughts! +John-->>Alice: Great! +John->>Bob: How about you? +Bob-->>John: Jolly good! \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence_mermaid-export.svg new file mode 100644 index 000000000..804d86275 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/Sequence_mermaid-export.svg @@ -0,0 +1 @@ +BobJohnAliceBobJohnAliceloop[Healthcheck]Rational thoughts!Hello John, how are you?Fight against hypochondriaGreat!How about you?Jolly good! \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State.txt b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State.txt new file mode 100644 index 000000000..7b57864f0 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State.txt @@ -0,0 +1,7 @@ +stateDiagram-v2 +[*] --> Still +Still --> [*] +Still --> Moving +Moving --> Still +Moving --> Crash +Crash --> [*] \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State_mermaid-export.svg b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State_mermaid-export.svg new file mode 100644 index 000000000..1e2314d6c --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mermaid Diagrams/State_mermaid-export.svg @@ -0,0 +1 @@ +
Still
Moving
Crash
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mind Map.json b/demo/root/Trilium Demo/Note Types/Mind Map.json new file mode 100644 index 000000000..6a16a83d5 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mind Map.json @@ -0,0 +1 @@ +{"nodeData":{"id":"me-root","topic":"Mind Elixir","root":true,"tags":["Mind Map Core"],"children":[{"topic":"What is Mind Elixir","id":"bd4313fbac40284b","direction":0,"expanded":true,"children":[{"topic":"A mind map core","id":"beeb823afd6d2114"},{"topic":"Free","id":"c1f068377de9f3a0"},{"topic":"Open-Source","id":"c1f06d38a09f23ca"},{"topic":"Use without JavaScript framework","id":"c1f06e4cbcf16463","expanded":true,"children":[]},{"topic":"Use in your own project","id":"c1f1f11a7fbf7550","children":[{"topic":"import MindElixir from 'mind-elixir'","id":"c1f1e245b0a89f9b"},{"topic":"new MindElixir({...}).init(data)","id":"c1f1ebc7072c8928"}]},{"topic":"Easy to use","id":"c1f0723c07b408d7","expanded":true,"children":[{"topic":"Use it like other mind map application","id":"c1f09612fd89920d"}]}]},{"topic":"Basics","id":"bd1b66c4b56754d9","direction":0,"expanded":true,"children":[{"topic":"tab - Create a child node","id":"bd1b6892bcab126a"},{"topic":"enter - Create a sibling node","id":"bd1b6b632a434b27"},{"topic":"del - Remove a node","id":"bd1b983085187c0a"}]},{"topic":"Focus mode","id":"bd1b9b94a9a7a913","direction":1,"expanded":true,"children":[{"topic":"Right click and select Focus Mode","id":"bd1bb2ac4bbab458"},{"topic":"Right click and select Cancel Focus Mode","id":"bd1bb4b14d6697c3"}]},{"topic":"Left menu","id":"bd1b9d1816ede134","direction":0,"expanded":true,"children":[{"topic":"Node distribution","id":"bd1ba11e620c3c1a","expanded":true,"children":[{"topic":"Left","id":"bd1c1cb51e6745d3"},{"topic":"Right","id":"bd1c1e12fd603ff6"},{"topic":"Both l & r","id":"bd1c1f03def5c97b"}]}]},{"topic":"Bottom menu","id":"bd1ba66996df4ba4","direction":1,"expanded":true,"children":[{"topic":"Full screen","id":"bd1ba81d9bc95a7e"},{"topic":"Return to Center","id":"bd1babdd5c18a7a2"},{"topic":"Zoom in","id":"bd1bae68e0ab186e"},{"topic":"Zoom out","id":"bd1bb06377439977"}]},{"topic":"Link","id":"bd1beff607711025","direction":0,"expanded":true,"children":[{"topic":"Right click and select Link","id":"bd1bf320da90046a"},{"topic":"Click the target you want to link","id":"bd1bf6f94ff2e642"},{"topic":"Modify link with control points","id":"bd1c0c4a487bd036"}]},{"topic":"Node style","id":"bd1c217f9d0b20bd","direction":0,"expanded":true,"children":[{"topic":"Font Size","id":"bd1c24420cd2c2f5","style":{"fontSize":"32","color":"#3298db"}},{"topic":"Font Color","id":"bd1c2a59b9a2739c","style":{"color":"#c0392c"}},{"topic":"Background Color","id":"bd1c2de33f057eb4","style":{"color":"#bdc3c7","background":"#2c3e50"}},{"topic":"Add tags","id":"bd1cff58364436d0","tags":["Completed"]},{"topic":"Add icons","id":"bd1d0317f7e8a61a","icons":["😂"],"tags":["www"]},{"topic":"Bolder","id":"bd41fd4ca32322a4","style":{"fontWeight":"bold"}},{"topic":"Hyper link","id":"bd41fd4ca32322a5","hyperLink":"https://github.com/ssshooter/mind-elixir-core"}]},{"topic":"Draggable","id":"bd1f03fee1f63bc6","direction":1,"expanded":true,"children":[{"topic":"Drag a node to another node\nand the former one will become a child node of latter one","id":"bd1f07c598e729dc"}]},{"topic":"Export data","id":"beeb7586973430db","direction":1,"expanded":true,"children":[{"topic":"JSON","id":"beeb784cc189375f"},{"topic":"HTML","id":"beeb7a6bec2d68f5"},{"topic":"SVG","id":"beeb7a6bec2d68e6"}]},{"topic":"Caution","id":"bd42dad21aaf6bae","direction":0,"style":{"background":"#f1c40e"},"expanded":true,"children":[{"topic":"Only save manually","id":"bd42e1d0163ebf04","expanded":true,"children":[{"topic":"Save button in the top-right corner","id":"bd42e619051878b3","branchColor":"green","expanded":true,"children":[]},{"topic":"ctrl + S","id":"bd42e97d7ac35e99"}]}]}],"expanded":true},"arrows":[{"id":"ac5fb1df7345e9c4","label":"Render","from":"beeb784cc189375f","to":"beeb7a6bec2d68f5","delta1":{"x":142.8828125,"y":-57},"delta2":{"x":146.1171875,"y":45}}],"summaries":[{"id":"a5e68e6a2ce1b648","parent":"bd42e1d0163ebf04","start":0,"end":1,"text":"summary"},{"id":"a5e6978f1bc69f4a","parent":"bd4313fbac40284b","start":3,"end":5,"text":"summary"}],"direction":0,"theme":{"name":"Dark","type":"dark","palette":["#848FA0","#748BE9","#D2F9FE","#4145A5","#789AFA","#706CF4","#EF987F","#775DD5","#FCEECF","#DA7FBC"],"cssVar":{"--main-color":"#ffffff","--main-bgcolor":"#4c4f69","--color":"#cccccc","--bgcolor":"#252526","--panel-color":"#ffffff","--panel-bgcolor":"#2d3748","--panel-border-color":"#696969"}}} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Note Types/Mind Map_mindmap-export.svg b/demo/root/Trilium Demo/Note Types/Mind Map_mindmap-export.svg new file mode 100644 index 000000000..6a4b1aa35 --- /dev/null +++ b/demo/root/Trilium Demo/Note Types/Mind Map_mindmap-export.svg @@ -0,0 +1,2 @@ +Rendersummarysummary
What is Mind Elixir
A mind map core
Free
Open-Source
Use without JavaScript framework
Use in your own project
import MindElixir from 'mind-elixir'
new MindElixir({...}).init(data)
Easy to use
Use it like other mind map application
Basics
tab - Create a child node
enter - Create a sibling node
del - Remove a node
Focus mode
Right click and select Focus Mode
Right click and select Cancel Focus Mode
Left menu
Node distribution
Left
Right
Both l & r
Bottom menu
Full screen
Return to Center
Zoom in
Zoom out
Link
Right click and select Link
Click the target you want to link
Modify link with control points
Node style
Font Size
Font Color
Background Color
Add tags
Add icons
Bolder
Hyper link
Draggable
Drag a node to another node +and the former one will become a child node of latter one
Export data
JSON
HTML
SVG
Caution
Only save manually
Save button in the top-right corner
ctrl + S
Mind Elixir
CompletedwwwMind Map Core😂🔗
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Custom request handler.js b/demo/root/Trilium Demo/Scripting examples/Custom request handler.js new file mode 100644 index 000000000..99b71b71c --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Custom request handler.js @@ -0,0 +1,28 @@ +return api.res.send(404); + +/** + * To activate this demo, comment (or remove) the very first line of this code note. + * This is deactivated because custom request handler like this one can be a security risk. + * To test this, execute the following curl request: curl -X POST http://localhost:37740/custom/create-note -H "Content-Type: application/json" -d "{ \"secret\": \"secret-password\", \"title\": \"hello\", \"content\": \"world\" }" + * (host and port might have to be adjusted based on your setup) + * + * See https://github.com/zadam/trilium/wiki/Custom-request-handler for details. + */ + +const {req, res} = api; +const {secret, title, content} = req.body; + +if (req.method == 'POST' && secret === 'secret-password') { + // notes must be saved somewhere in the tree hierarchy specified by a parent note. + // This is defined by a relation from this code note to the "target" parent note + // alternetively you can just use constant noteId for simplicity (get that from "Note Info" dialog of the desired parent note) + const targetParentNoteId = api.currentNote.getRelationValue('targetNote'); + + const {note} = api.createTextNote(targetParentNoteId, title, content); + const notePojo = note.getPojo(); + + res.status(201).json(notePojo); +} +else { + res.send(400); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template.html new file mode 100644 index 000000000..5f5417f5c --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template.html @@ -0,0 +1,12 @@ +
+ + +
+ + + + + + +
Attribute nameCount
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js.js new file mode 100644 index 000000000..4f7d3872b --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js.js @@ -0,0 +1,12 @@ +const attrCounts = await api.runOnBackend(() => { + return api.sql.getRows(` + SELECT + name, COUNT(*) AS count + FROM attributes + WHERE isDeleted = 0 + GROUP BY name + ORDER BY count DESC`); +}); + +renderPieChart(attrCounts.length <= 10 ? attrCounts : attrCounts.splice(0, 10)); +renderTable(attrCounts); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart.js new file mode 100644 index 000000000..db0269cb4 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart.js @@ -0,0 +1,45 @@ +module.exports = data => { + const ctx = api.$container.find('.stats-canvas')[0].getContext("2d"); + + const myPieChart = new Chart(ctx, { + type: 'pie', + data: { + datasets: [{ + data: data.map(nc => nc.count), + backgroundColor: ['#3366CC','#DC3912','#FF9900','#109618','#990099','#3B3EAC','#0099C6','#DD4477','#66AA00','#B82E2E','#316395','#994499','#22AA99','#AAAA11','#6633CC','#E67300','#8B0707','#329262','#5574A6','#3B3EAC'], + datalabels: { + anchor: 'end' + } + }], + labels: data.map(nc => nc.name) + }, + options: { + legend: { + display: false + }, + plugins: { + datalabels: { + backgroundColor: function(context) { + return context.dataset.backgroundColor; + }, + borderColor: 'white', + borderRadius: 25, + borderWidth: 2, + color: 'white', + display: function(context) { + var dataset = context.dataset; + var count = dataset.data.length; + var value = dataset.data[context.dataIndex]; + return value > count * 1.5; + }, + font: { + weight: 'bold' + }, + formatter: function(value, context) { + return context.chart.data.labels[context.dataIndex] + ": " + Math.round(value); + } + } + } + } + }); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chart.js.clone.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chart.js.clone.html new file mode 100644 index 000000000..64e4de7c4 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chart.js.clone.html @@ -0,0 +1,21 @@ + + + + + + + + chart.js + + + +
+

chart.js

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chartjs-plugin-datalabe.min.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chartjs-plugin-datalabe.min.js new file mode 100644 index 000000000..fb45222c7 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderPieChart/chartjs-plugin-datalabe.min.js @@ -0,0 +1,7 @@ +/*! + * chartjs-plugin-datalabels v2.0.0 + * https://chartjs-plugin-datalabels.netlify.app + * (c) 2017-2021 chartjs-plugin-datalabels contributors + * Released under the MIT license + */ +!function(t,e){(t="undefined"!=typeof globalThis?globalThis:t||self).ChartDataLabels=e(t.Chart.helpers,t.Chart)}(this,(function(t,e){"use strict";var r=function(){if("undefined"!=typeof window){if(window.devicePixelRatio)return window.devicePixelRatio;var t=window.screen;if(t)return(t.deviceXDPI||1)/(t.logicalXDPI||1)}return 1}(),a=function(e){var r,a=[];for(e=[].concat(e);e.length;)"string"==typeof(r=e.pop())?a.unshift.apply(a,r.split("\n")):Array.isArray(r)?e.push.apply(e,r):t.isNullOrUndef(e)||a.unshift(""+r);return a},o=function(t,e,r){var a,o=[].concat(e),n=o.length,i=t.font,l=0;for(t.font=r.string,a=0;ar.right&&(a|=2),er.bottom&&(a|=4),a}function u(t,e){var r,a,o=e.anchor,n=t;return e.clamp&&(n=function(t,e){for(var r,a,o,n=t.x0,i=t.y0,l=t.x1,u=t.y1,d=s(n,i,e),c=s(l,u,e);d|c&&!(d&c);)8&(r=d||c)?(a=n+(l-n)*(e.top-i)/(u-i),o=e.top):4&r?(a=n+(l-n)*(e.bottom-i)/(u-i),o=e.bottom):2&r?(o=i+(u-i)*(e.right-n)/(l-n),a=e.right):1&r&&(o=i+(u-i)*(e.left-n)/(l-n),a=e.left),r===d?d=s(n=a,i=o,e):c=s(l=a,u=o,e);return{x0:n,x1:l,y0:i,y1:u}}(n,e.area)),"start"===o?(r=n.x0,a=n.y0):"end"===o?(r=n.x1,a=n.y1):(r=(n.x0+n.x1)/2,a=(n.y0+n.y1)/2),function(t,e,r,a,o){switch(o){case"center":r=a=0;break;case"bottom":r=0,a=1;break;case"right":r=1,a=0;break;case"left":r=-1,a=0;break;case"top":r=0,a=-1;break;case"start":r=-r,a=-a;break;case"end":break;default:o*=Math.PI/180,r=Math.cos(o),a=Math.sin(o)}return{x:t,y:e,vx:r,vy:a}}(r,a,t.vx,t.vy,e.align)}var d=function(t,e){var r=(t.startAngle+t.endAngle)/2,a=Math.cos(r),o=Math.sin(r),n=t.innerRadius,i=t.outerRadius;return u({x0:t.x+a*n,y0:t.y+o*n,x1:t.x+a*i,y1:t.y+o*i,vx:a,vy:o},e)},c=function(t,e){var r=l(t,e.origin),a=r.x*t.options.radius,o=r.y*t.options.radius;return u({x0:t.x-a,y0:t.y-o,x1:t.x+a,y1:t.y+o,vx:r.x,vy:r.y},e)},h=function(t,e){var r=l(t,e.origin),a=t.x,o=t.y,n=0,i=0;return t.horizontal?(a=Math.min(t.x,t.base),n=Math.abs(t.base-t.x)):(o=Math.min(t.y,t.base),i=Math.abs(t.base-t.y)),u({x0:a,y0:o+i,x1:a+n,y1:o,vx:r.x,vy:r.y},e)},f=function(t,e){var r=l(t,e.origin);return u({x0:t.x,y0:t.y,x1:t.x,y1:t.y,vx:r.x,vy:r.y},e)},x=function(t){return Math.round(t*r)/r};function y(t,e){var r=e.chart.getDatasetMeta(e.datasetIndex).vScale;if(!r)return null;if(void 0!==r.xCenter&&void 0!==r.yCenter)return{x:r.xCenter,y:r.yCenter};var a=r.getBasePixel();return t.horizontal?{x:a,y:null}:{x:null,y:a}}function v(t,e,r){var a=r.backgroundColor,o=r.borderColor,n=r.borderWidth;(a||o&&n)&&(t.beginPath(),function(t,e,r,a,o,n){var i=Math.PI/2;if(n){var l=Math.min(n,o/2,a/2),s=e+l,u=r+l,d=e+a-l,c=r+o-l;t.moveTo(e,u),sr.x+r.w+2||t.y>r.y+r.h+2)},intersects:function(t){var e,r,a,o=this._points(),n=t._points(),i=[M(o[0],o[1]),M(o[0],o[3])];for(this._rotation!==t._rotation&&i.push(M(n[0],n[1]),M(n[0],n[3])),e=0;et.getProps([e],!0)[e]}),n=a.geometry(),i=$(l,a.model(),n),o._box.update(i,n,a.rotation()));(function(t,e){var r,a,o,n;for(r=t.length-1;r>=0;--r)for(o=t[r].$layout,a=r-1;a>=0&&o._visible;--a)(n=t[a].$layout)._visible&&o._box.intersects(n._box)&&e(o,n)})(t,(function(t,e){var r=t._hidable,a=e._hidable;r&&a||a?e._visible=!1:r&&(t._visible=!1)}))}(t)},lookup:function(t,e){var r,a;for(r=t.length-1;r>=0;--r)if((a=t[r].$layout)&&a._visible&&a._box.contains(e))return t[r];return null},draw:function(t,e){var r,a,o,n,i,l;for(r=0,a=e.length;r + + + + + + + chart.js + + + +
+

chart.js

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderTable.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderTable.js new file mode 100644 index 000000000..39bfc9b23 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Attribute count/template/js/renderTable.js @@ -0,0 +1,17 @@ +module.exports = counts => { + const $statsTable = api.$container.find('.stats-table'); + + addRow('total', counts.reduce((acc, cur) => acc + cur.count, 0)); + + for (const count of counts) { + addRow(count.name, count.count); + } + + function addRow(name, count) { + $statsTable.append( + $("") + .append($("").text(name)) + .append($("").text(count)) + ); + } +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template.html new file mode 100644 index 000000000..2932d9885 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template.html @@ -0,0 +1,10 @@ +
+

This table shows 100 largest notes, including their revisions and attachments.

+ + + + + + +
NoteSize
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template/js.js new file mode 100644 index 000000000..af10eb40a --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Largest notes/template/js.js @@ -0,0 +1,47 @@ +const notes = await api.runOnBackend(() => { + const blobSizes = api.sql.getMap(`SELECT blobId, LENGTH(content) FROM blobs`); + const noteBlobIds = api.sql.getRows(` + SELECT + notes.noteId, + notes.blobId, + GROUP_CONCAT(revisions.blobId) AS revisions_blobIds, + GROUP_CONCAT(note_attachments.blobId) AS note_attachments_blobIds, + GROUP_CONCAT(revision_attachments.blobId) AS revision_attachments_blobIds + FROM + notes + LEFT JOIN revisions USING (noteId) + LEFT JOIN attachments AS note_attachments ON notes.noteId = note_attachments.ownerId + LEFT JOIN attachments AS revision_attachments ON revisions.revisionId = revision_attachments.ownerId + GROUP BY noteId`); + + let noteSizes = []; + + for (const {noteId, blobId, revisions_blobIds, note_attachments_blobIds, revision_attachments_blobIds} of noteBlobIds) { + const blobIds = new Set(`${blobId},${revisions_blobIds},${note_attachments_blobIds},${revision_attachments_blobIds}`.split(',').filter(blobId => !!blobId)); + + const lengths = [...blobIds].map(blobId => blobSizes[blobId] || 0); + const totalLength = lengths.reduce((partialSum, a) => partialSum + a, 0); + + noteSizes.push({ noteId, size: totalLength }); + } + + noteSizes.sort((a, b) => a.size > b.size ? -1 : 1); + + noteSizes = noteSizes.splice(0, 100); + + return noteSizes; +}); + +const $statsTable = api.$container.find('.stats-table'); + +for (const note of notes) { + $statsTable.append( + $("") + .append( + $("").append(await api.createNoteLink(note.noteId, {showNotePath: true})) + ) + .append( + $("").text(note.size + " bytes") + ) + ); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template.html new file mode 100644 index 000000000..84d8504db --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template.html @@ -0,0 +1,8 @@ +
+ + + + + +
NoteClone count
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template/js.js new file mode 100644 index 000000000..63c6f0ba5 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most cloned notes/template/js.js @@ -0,0 +1,26 @@ +const notes = await api.runOnBackend(() => { + return api.sql.getRows(` + SELECT + notes.noteId, + COUNT(branches.branchId) AS count + FROM notes + JOIN branches USING (noteId) + WHERE notes.isDeleted = 0 + AND branches.isDeleted = 0 + GROUP BY notes.noteId + HAVING count > 1 + ORDER BY count DESC + LIMIT 100`); +}); + +const $statsTable = api.$container.find('.stats-table'); + +for (const note of notes) { + $statsTable.append( + $("") + .append( + $("").append(await api.createNoteLink(note.noteId, {showNotePath: true})) + ) + .append($("").text(note.count)) + ); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template.html new file mode 100644 index 000000000..d60e28d8c --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template.html @@ -0,0 +1,10 @@ +
+

This table shows notes with most revisions

+ + + + + + +
NoteRevision count
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template/js.js new file mode 100644 index 000000000..e36d0e698 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most edited notes/template/js.js @@ -0,0 +1,24 @@ +const notes = await api.runOnBackend(() => { + return api.sql.getRows(` + SELECT + notes.noteId, + COUNT(revisions.revisionId) AS count + FROM notes + JOIN revisions USING (noteId) + WHERE notes.isDeleted = 0 + GROUP BY notes.noteId + ORDER BY count DESC + LIMIT 100`); +}); + +const $statsTable = api.$container.find('.stats-table'); + +for (const note of notes) { + $statsTable.append( + $("") + .append( + $("").append(await api.createNoteLink(note.noteId, {showNotePath: true})) + ) + .append($("").text(note.count)) + ); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template.html new file mode 100644 index 000000000..fad5fae88 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template.html @@ -0,0 +1,10 @@ +
+

This table shows notes which are linked by other notes through relations

+ + + + + + +
NoteRelation count
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template/js.js new file mode 100644 index 000000000..53ab6b1c9 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Most linked notes/template/js.js @@ -0,0 +1,26 @@ +const notes = await api.runOnBackend(() => { + return api.sql.getRows(` + SELECT + notes.noteId, + COUNT(attributes.attributeId) AS count + FROM notes + JOIN attributes ON attributes.value = notes.noteId + WHERE notes.isDeleted = 0 + AND attributes.isDeleted = 0 + AND attributes.type = 'relation' + GROUP BY notes.noteId + ORDER BY count DESC + LIMIT 100`); +}); + +const $statsTable = api.$container.find('.stats-table'); + +for (const note of notes) { + $statsTable.append( + $("") + .append( + $("").append(await api.createLink(note.noteId, {showNotePath: true})) + ) + .append($("").text(note.count)) + ); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template.html new file mode 100644 index 000000000..3cfe66481 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template.html @@ -0,0 +1,13 @@ +
+ + +
+ + + + + + + +
Note typeCount (not deleted)Count (deleted)
+
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js.js new file mode 100644 index 000000000..4a4eecb7b --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js.js @@ -0,0 +1,20 @@ +const noteCounts = await api.runOnBackend(() => { + return api.sql.getRows(` + SELECT + type, + isDeleted, + SUM(CASE WHEN isDeleted=0 THEN 1 ELSE 0 END) AS countNotDeleted, + SUM(CASE WHEN isDeleted=1 THEN 1 ELSE 0 END) AS countDeleted + FROM notes + GROUP BY type + ORDER BY countNotDeleted DESC`); +}); + +renderPieChart(noteCounts.map(nc => { + return { + name: nc.type, + count: nc.countNotDeleted + }; +})); + +renderTable(noteCounts); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderPieChart.clone.html b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderPieChart.clone.html new file mode 100644 index 000000000..d08203fb2 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderPieChart.clone.html @@ -0,0 +1,21 @@ + + + + + + + + renderPieChart + + + +
+

renderPieChart

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderTable.js b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderTable.js new file mode 100644 index 000000000..353653eae --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Statistics/Note type count/template/js/renderTable.js @@ -0,0 +1,21 @@ +module.exports = counts => { + const $statsTable = api.$container.find('.stats-table'); + + addRow('total', + counts.reduce((acc, cur) => acc + cur.countNotDeleted, 0), + counts.reduce((acc, cur) => acc + cur.countDeleted, 0) + ); + + for (const count of counts) { + addRow(count.type, count.countNotDeleted, count.countDeleted); + } + + function addRow(type, countNotDeleted, countDeleted) { + $statsTable.append( + $("") + .append($("").text(type)) + .append($("").text(countNotDeleted)) + .append($("").text(countDeleted)) + ); + } +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager.html b/demo/root/Trilium Demo/Scripting examples/Task manager.html new file mode 100644 index 000000000..0194f9364 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager.html @@ -0,0 +1,26 @@ + + + + + + + + Task manager + + + +
+

Task manager

+ +
+

This is a simple TODO/Task manager. You can see some description and explanation + here: https://github.com/zadam/trilium/wiki/Task-manager + +

+

Please note that this is meant as scripting example only and feature/bug + support is very limited.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Create Launcher.js b/demo/root/Trilium Demo/Scripting examples/Task manager/Create Launcher.js new file mode 100644 index 000000000..1fb4c4430 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Create Launcher.js @@ -0,0 +1,10 @@ +// will add a launcher to the left sidebar +api.createOrUpdateLauncher({ + id: 'taskmanager', + type: 'script', + title: 'New task', + icon: 'bx-task', + keyboardShortcut: 'alt+n', + scriptNoteId: api.currentNote.getRelationValue('createNewTask'), + isVisible: true +}); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.html new file mode 100644 index 000000000..65b1819f2 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.html @@ -0,0 +1,27 @@ + + + + + + + + Buy a board game for Alice + + + +
+

Buy a board game for Alice

+ + +
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.jpg b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.jpg new file mode 100644 index 000000000..f0135ec7b Binary files /dev/null and b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Buy a board game for Alice.jpg differ diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Dentist appointment.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Dentist appointment.html new file mode 100644 index 000000000..c793f1e22 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Dentist appointment.html @@ -0,0 +1,19 @@ + + + + + + + + Dentist appointment + + + +
+

Dentist appointment

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Get a gym membership.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Get a gym membership.html new file mode 100644 index 000000000..3591b09b3 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Done/Get a gym membership.html @@ -0,0 +1,21 @@ + + + + + + + + Get a gym membership + + + +
+

Get a gym membership

+ +
+

Just in time for new years resolution!

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/CSS.css b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/CSS.css new file mode 100644 index 000000000..4d4b43b71 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/CSS.css @@ -0,0 +1,7 @@ +span.fancytree-node.todo .fancytree-title { + color: red !important; +} + +span.fancytree-node.done .fancytree-title { + color: green !important; +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed.js b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed.js new file mode 100644 index 000000000..56292594d --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed.js @@ -0,0 +1,63 @@ +if (!["task", "location", "tag", "todoDate", "doneDate"].includes(api.originEntity.name)) { + return; +} + +const tagRootNote = api.getNoteWithLabel('taskTagRoot'); +const doneRootNote = api.getNoteWithLabel('taskDoneRoot'); +const todoRootNote = api.getNoteWithLabel('taskTodoRoot'); + +if (!tagRootNote || !doneRootNote || !todoRootNote) { + console.log("One of the tagRootNote, doneRootNote or todoRootNote does not exist"); + return; +} + +const note = api.originEntity.getNote(); + +if (note.isDeleted) { + return; +} + +const attributes = note.getAttributes(); + +const todoDate = note.getLabelValue('todoDate'); +const doneDate = note.getLabelValue('doneDate'); + +function isWithinExpectedRange(date) { + if (!date) { + return true; + } + + const year = parseInt(date.substr(0, 4)); + + return year >= 2010 && year < 2050; +} + +if (!isWithinExpectedRange(todoDate) || !isWithinExpectedRange(doneDate)) { + console.log(`One or both dates - ${todoDate}, ${doneDate} - is outside of expected range`); + + return; +} + +const isTaskDone = !!doneDate; + +api.toggleNoteInParent(isTaskDone, note.noteId, doneRootNote.noteId); +api.toggleNoteInParent(!isTaskDone, note.noteId, todoRootNote.noteId); + +const location = note.getLabelValue('location'); +const locationRootNote = api.getNoteWithLabel('taskLocationRoot'); + +reconcileAssignments(note, locationRootNote, location ? [location] : [], 'taskLocationNote', isTaskDone); + +const tags = attributes.filter(attr => attr.type === 'label' && attr.name === 'tag').map(attr => attr.value); + +reconcileAssignments(note, tagRootNote, tags, 'taskTagNote', isTaskDone); + +note.toggleLabel(isTaskDone, "cssClass", "done"); + +const doneTargetNoteId = isTaskDone ? api.getDayNote(doneDate).noteId : null; +api.setNoteToParent(note.noteId, 'DONE', doneTargetNoteId); + +note.toggleLabel(!isTaskDone, "cssClass", "todo"); + +const todoTargetNoteId = (!isTaskDone && todoDate) ? api.getDayNote(todoDate).noteId : null; +api.setNoteToParent(note.noteId, 'TODO', todoTargetNoteId); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed/reconcileAssignments.js b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed/reconcileAssignments.js new file mode 100644 index 000000000..bf3503987 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/attribute changed/reconcileAssignments.js @@ -0,0 +1,25 @@ +module.exports = function (note, categoryRootNote, assignedCategories, labelName, isTaskDone) { + const found = {}; + + for (const categoryNote of categoryRootNote.getChildNotes()) { + const label = categoryNote.getLabel(labelName); + + if (label) { + found[label.value] = !isTaskDone && assignedCategories.includes(label.value); + + api.toggleNoteInParent(found[label.value], note.noteId, categoryNote.noteId); + } + } + + if (!isTaskDone) { + for (const assignedCategory of assignedCategories) { + if (!found[assignedCategory]) { + const categoryNote = api.createTextNote(categoryRootNote.noteId, assignedCategory, "").note; + + categoryNote.addLabel(labelName, assignedCategory); + + api.ensureNoteIsPresentInParent(note.noteId, categoryNote.noteId); + } + } + } +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/createNewTask.js b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/createNewTask.js new file mode 100644 index 000000000..9dd12721a --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/createNewTask.js @@ -0,0 +1,14 @@ +// creating notes is backend (server) responsibility so we need to pass +// the control there +const taskNoteId = await api.runOnBackend(() => { + const todoRootNote = api.getNoteWithLabel('taskTodoRoot'); + const resp = api.createTextNote(todoRootNote.noteId, 'new task', ''); + + return resp.note.noteId; +}); + +// wait until the frontend is fully synced with the changes made on the backend above +await api.waitUntilSynced(); + +// we got an ID of newly created note and we want to immediatelly display it +await api.activateNewNote(taskNoteId); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/task template.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/task template.html new file mode 100644 index 000000000..c8a144a4f --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Implementation/task template.html @@ -0,0 +1,19 @@ + + + + + + + + task template + + + +
+

task template

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/gym.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/gym.html new file mode 100644 index 000000000..175295486 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/gym.html @@ -0,0 +1,19 @@ + + + + + + + + gym + + + +
+

gym

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob.html new file mode 100644 index 000000000..dd9db66c9 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob.html @@ -0,0 +1,21 @@ + + + + + + + + Buy some book for Bob + + + +
+

Buy some book for Bob

+ +
+

Bob likes to read popular science books so something like that…

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob/Maybe Black Swan.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob/Maybe Black Swan.html new file mode 100644 index 000000000..51831b17f --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/mall/Buy some book for Bob/Maybe Black Swan.html @@ -0,0 +1,30 @@ + + + + + + + + Maybe Black Swan? + + + +
+

Maybe Black Swan?

+ +
+

https://en.wikipedia.org/wiki/The_Black_Swan:_The_Impact_of_the_Highly_Improbable + +

+

The Black Swan: The Impact of the Highly Improbable is + a 2007 book by author and former options trader + Nassim Nicholas Taleb. The book focuses on the extreme impact of rare + and unpredictable outlier events + — and the human tendency to find simplistic explanations for these events, + retrospectively. Taleb calls this the Black Swan theory.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/tesco/Buy milk.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/tesco/Buy milk.html new file mode 100644 index 000000000..61d000446 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/tesco/Buy milk.html @@ -0,0 +1,19 @@ + + + + + + + + Buy milk + + + +
+

Buy milk

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/work/Send invites for christmas par.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/work/Send invites for christmas par.html new file mode 100644 index 000000000..fe8e14b7b --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Locations/work/Send invites for christmas par.html @@ -0,0 +1,19 @@ + + + + + + + + Send invites for christmas party + + + +
+

Send invites for christmas party

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy milk.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy milk.clone.html new file mode 100644 index 000000000..bbcd6f3cf --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy milk.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy milk + + + +
+

Buy milk

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy some book for Bob.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy some book for Bob.clone.html new file mode 100644 index 000000000..b6c791688 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Buy some book for Bob.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy some book for Bob + + + +
+

Buy some book for Bob

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Send invites for christmas party.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Send invites for christmas party.clone.html new file mode 100644 index 000000000..fd4df0179 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/TODO/Send invites for christmas party.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Send invites for christmas party + + + +
+

Send invites for christmas party

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/christmas/Buy some book for Bob.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/christmas/Buy some book for Bob.clone.html new file mode 100644 index 000000000..8d8a86979 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/christmas/Buy some book for Bob.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy some book for Bob + + + +
+

Buy some book for Bob

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/groceries/Buy milk.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/groceries/Buy milk.clone.html new file mode 100644 index 000000000..538a712d5 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/groceries/Buy milk.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy milk + + + +
+

Buy milk

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/health.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/health.html new file mode 100644 index 000000000..02f957876 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/health.html @@ -0,0 +1,19 @@ + + + + + + + + health + + + +
+

health

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy milk.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy milk.clone.html new file mode 100644 index 000000000..538a712d5 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy milk.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy milk + + + +
+

Buy milk

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy some book for Bob.clone.html b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy some book for Bob.clone.html new file mode 100644 index 000000000..8d8a86979 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Task manager/Tags/shopping/Buy some book for Bob.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Buy some book for Bob + + + +
+

Buy some book for Bob

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation.html b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation.html new file mode 100644 index 000000000..6966e949a --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation.html @@ -0,0 +1,5 @@ +
+ See explanation here. + + +
\ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code.js b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code.js new file mode 100644 index 000000000..3c514402a --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code.js @@ -0,0 +1,46 @@ +async function getChartData() { + const days = await api.runOnBackend(() => { + const notes = api.getNotesWithLabel('weight'); + const days = []; + + for (const note of notes) { + const date = note.getLabelValue('dateNote'); + const weight = parseFloat(note.getLabelValue('weight')); + + if (date && weight) { + days.push({ date, weight }); + } + } + + days.sort((a, b) => a.date > b.date ? 1 : -1); + + return days; + }); + + const datasets = [ + { + label: "Weight (kg)", + backgroundColor: 'red', + borderColor: 'red', + data: days.map(day => day.weight), + fill: false, + spanGaps: true, + datalabels: { + display: false + }, + tension: 0.3 + } + ]; + + return { + datasets: datasets, + labels: days.map(day => day.date) + }; +} + +const ctx = api.$container.find("canvas")[0].getContext("2d"); + +new chartjs.Chart(ctx, { + type: 'line', + data: await getChartData() +}); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code/chart.js b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code/chart.js new file mode 100644 index 000000000..07b5445e5 --- /dev/null +++ b/demo/root/Trilium Demo/Scripting examples/Weight Tracker/Implementation/JS code/chart.js @@ -0,0 +1,13 @@ +/*! + * Chart.js v3.7.0 + * https://www.chartjs.org + * (c) 2021 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){"use strict";const t="undefined"==typeof window?function(t){return t()}:window.requestAnimationFrame;function e(e,i,s){const n=s||(t=>Array.prototype.slice.call(t));let o=!1,a=[];return function(...s){a=n(s),o||(o=!0,t.call(window,(()=>{o=!1,e.apply(i,a)})))}}function i(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const s=t=>"start"===t?"left":"end"===t?"right":"center",n=(t,e,i)=>"start"===t?e:"end"===t?i:(e+i)/2,o=(t,e,i,s)=>t===(s?"left":"right")?i:"center"===t?(e+i)/2:e;var a=new class{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=t.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,"progress")),n.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){e&&e.length&&this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce(((t,e)=>Math.max(t,e._duration)),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!!(e&&e.running&&e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}; +/*! + * @kurkle/color v0.1.9 + * https://github.com/kurkle/color#readme + * (c) 2020 Jukka Kurkela + * Released under the MIT License + */const r={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},l="0123456789ABCDEF",h=t=>l[15&t],c=t=>l[(240&t)>>4]+l[15&t],d=t=>(240&t)>>4==(15&t);function u(t){var e=function(t){return d(t.r)&&d(t.g)&&d(t.b)&&d(t.a)}(t)?h:c;return t?"#"+e(t.r)+e(t.g)+e(t.b)+(t.a<255?e(t.a):""):t}function f(t){return t+.5|0}const g=(t,e,i)=>Math.max(Math.min(t,i),e);function p(t){return g(f(2.55*t),0,255)}function m(t){return g(f(255*t),0,255)}function x(t){return g(f(t/2.55)/100,0,1)}function b(t){return g(f(100*t),0,100)}const _=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const y=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function v(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function w(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function M(t,e,i){const s=v(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function k(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=n===e?(i-s)/h+(i>16&255,o>>8&255,255&o]}return t}(),T.transparent=[0,0,0,0]);const e=T[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}function R(t,e,i){if(t){let s=k(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=P(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function E(t,e){return t?Object.assign(e||{},t):t}function I(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=m(t[3]))):(e=E(t,{r:0,g:0,b:0,a:1})).a=m(e.a),e}function z(t){return"r"===t.charAt(0)?function(t){const e=_.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=255&(e[8]?p(t):255*t)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?p(i):i),s=255&(e[4]?p(s):s),n=255&(e[6]?p(n):n),{r:i,g:s,b:n,a:o}}}(t):C(t)}class F{constructor(t){if(t instanceof F)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=I(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*r[s[1]],g:255&17*r[s[2]],b:255&17*r[s[3]],a:5===o?17*r[s[4]]:255}:7!==o&&9!==o||(n={r:r[s[1]]<<4|r[s[2]],g:r[s[3]]<<4|r[s[4]],b:r[s[5]]<<4|r[s[6]],a:9===o?r[s[7]]<<4|r[s[8]]:255})),i=n||L(t)||z(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=E(this._rgb);return t&&(t.a=x(t.a)),t}set rgb(t){this._rgb=I(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${x(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):this._rgb;var t}hexString(){return this._valid?u(this._rgb):this._rgb}hslString(){return this._valid?function(t){if(!t)return;const e=k(t),i=e[0],s=b(e[1]),n=b(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${x(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):this._rgb}mix(t,e){const i=this;if(t){const s=i.rgb,n=t.rgb;let o;const a=e===o?.5:e,r=2*a-1,l=s.a-n.a,h=((r*l==-1?r:(r+l)/(1+r*l))+1)/2;o=1-h,s.r=255&h*s.r+o*n.r+.5,s.g=255&h*s.g+o*n.g+.5,s.b=255&h*s.b+o*n.b+.5,s.a=a*s.a+(1-a)*n.a,i.rgb=s}return i}clone(){return new F(this.rgb)}alpha(t){return this._rgb.a=m(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=f(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return R(this._rgb,2,t),this}darken(t){return R(this._rgb,2,-t),this}saturate(t){return R(this._rgb,1,t),this}desaturate(t){return R(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=k(t);i[0]=D(i[0]+e),i=P(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function B(t){return new F(t)}const V=t=>t instanceof CanvasGradient||t instanceof CanvasPattern;function W(t){return V(t)?t:B(t)}function N(t){return V(t)?t:B(t).saturate(.5).darken(.1).hexString()}function H(){}const j=function(){let t=0;return function(){return t++}}();function $(t){return null==t}function Y(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)}function U(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}const X=t=>("number"==typeof t||t instanceof Number)&&isFinite(+t);function q(t,e){return X(t)?t:e}function K(t,e){return void 0===t?e:t}const G=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100:t/e,Z=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function J(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)}function Q(t,e,i,s){let n,o,a;if(Y(t))if(o=t.length,s)for(n=o-1;n>=0;n--)e.call(i,t[n],n);else for(n=0;ni;)t=t[e.substr(i,s-i)],i=s+1,s=rt(e,i);return t}function ht(t){return t.charAt(0).toUpperCase()+t.slice(1)}const ct=t=>void 0!==t,dt=t=>"function"==typeof t,ut=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function ft(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}const gt=Object.create(null),pt=Object.create(null);function mt(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;et.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>N(e.backgroundColor),this.hoverBorderColor=(t,e)=>N(e.borderColor),this.hoverColor=(t,e)=>N(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t)}set(t,e){return xt(this,t,e)}get(t){return mt(this,t)}describe(t,e){return xt(pt,t,e)}override(t,e){return xt(gt,t,e)}route(t,e,i,s){const n=mt(this,t),o=mt(this,i),a="_"+e;Object.defineProperties(n,{[a]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[a],e=o[s];return U(t)?Object.assign({},e,t):K(t,e)},set(t){this[a]=t}}})}}({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}});const _t=Math.PI,yt=2*_t,vt=yt+_t,wt=Number.POSITIVE_INFINITY,Mt=_t/180,kt=_t/2,St=_t/4,Pt=2*_t/3,Dt=Math.log10,Ct=Math.sign;function Ot(t){const e=Math.round(t);t=Lt(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(Dt(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function At(t){const e=[],i=Math.sqrt(t);let s;for(s=1;st-e)).pop(),e}function Tt(t){return!isNaN(parseFloat(t))&&isFinite(t)}function Lt(t,e,i){return Math.abs(t-e)=t}function Et(t,e,i){let s,n,o;for(s=0,n=t.length;sl&&h=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function Ut(t){return!t||$(t.size)||$(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Xt(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function qt(t,e,i,s){let n=(s=s||{}).data=s.data||{},o=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(n=s.data={},o=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let a=0;const r=i.length;let l,h,c,d,u;for(l=0;li.length){for(l=0;l0&&t.stroke()}}function Jt(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.xe.top-i&&t.y0&&""!==o.strokeColor;let l,h;for(t.save(),t.font=n.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]);$(e.rotation)||t.rotate(e.rotation);e.color&&(t.fillStyle=e.color);e.textAlign&&(t.textAlign=e.textAlign);e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,o),l=0;lt[i]1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const re=(t,e,i)=>ae(t,i,(s=>t[s][e]ae(t,i,(s=>t[s][e]>=i));function he(t,e,i){let s=0,n=t.length;for(;ss&&t[n-1]>i;)n--;return s>0||n{const i="_onData"+ht(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{"function"==typeof t[i]&&t[i](...e)})),n}})})))}function ue(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ce.forEach((e=>{delete t[e]})),delete t._chartjs)}function fe(t){const e=new Set;let i,s;for(i=0,s=t.length;iwindow.getComputedStyle(t,null);function be(t,e){return xe(t).getPropertyValue(e)}const _e=["top","right","bottom","left"];function ye(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=_e[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}function ve(t,e){const{canvas:i,currentDevicePixelRatio:s}=e,n=xe(i),o="border-box"===n.boxSizing,a=ye(n,"padding"),r=ye(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.native||t,s=i.touches,n=s&&s.length?s[0]:i,{offsetX:o,offsetY:a}=n;let r,l,h=!1;if(((t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot))(o,a,i.target))r=o,l=a;else{const t=e.getBoundingClientRect();r=n.clientX-t.left,l=n.clientY-t.top,h=!0}return{x:r,y:l,box:h}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const we=t=>Math.round(10*t)/10;function Me(t,e,i,s){const n=xe(t),o=ye(n,"margin"),a=me(n.maxWidth,t,"clientWidth")||wt,r=me(n.maxHeight,t,"clientHeight")||wt,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=pe(t);if(o){const t=o.getBoundingClientRect(),a=xe(o),r=ye(a,"border","width"),l=ye(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=me(a.maxWidth,o,"clientWidth"),n=me(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||wt,maxHeight:n||wt}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=ye(n,"border","width"),e=ye(n,"padding");h-=e.width+t.width,c-=e.height+t.height}return h=Math.max(0,h-o.width),c=Math.max(0,s?Math.floor(h/s):c-o.height),h=we(Math.min(h,a,l.maxWidth)),c=we(Math.min(c,r,l.maxHeight)),h&&!c&&(c=we(h/2)),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=n/s,t.width=o/s;const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};window.addEventListener("test",null,e),window.removeEventListener("test",null,e)}catch(t){}return t}();function Pe(t,e){const i=be(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t,e){return"native"in t?{x:t.x,y:t.y}:ve(t,e)}function Ce(t,e,i,s){const{controller:n,data:o,_sorted:a}=t,r=n._cachedMeta.iScale;if(r&&e===r.axis&&"r"!==e&&a&&o.length){const t=r._reversePixels?le:re;if(!s)return t(o,e,i);if(n._sharedOptions){const s=o[0],n="function"==typeof s.getRange&&s.getRange(e);if(n){const s=t(o,e,i-n),a=t(o,e,i+n);return{lo:s.lo,hi:a.hi}}}}return{lo:0,hi:o.length-1}}function Oe(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t{t[r](n[a],s)&&o.push({element:t,datasetIndex:e,index:i}),t.inRange(n.x,n.y,s)&&(l=!0)})),i.intersect&&!l?[]:o}var Ee={modes:{index(t,e,i,s){const n=De(e,t),o=i.axis||"x",a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s),r=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=a[0].index,i=t.data[e];i&&!i.skip&&r.push({element:i,datasetIndex:t.index,index:e})})),r):[]},dataset(t,e,i,s){const n=De(e,t),o=i.axis||"xy";let a=i.intersect?Ae(t,n,o,s):Le(t,n,o,!1,s);if(a.length>0){const e=a[0].datasetIndex,i=t.getDatasetMeta(e).data;a=[];for(let t=0;tAe(t,De(e,t),i.axis||"xy",s),nearest:(t,e,i,s)=>Le(t,De(e,t),i.axis||"xy",i.intersect,s),x:(t,e,i,s)=>Re(t,e,{axis:"x",intersect:i.intersect},s),y:(t,e,i,s)=>Re(t,e,{axis:"y",intersect:i.intersect},s)}};const Ie=new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/),ze=new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/);function Fe(t,e){const i=(""+t).match(Ie);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}function Be(t,e){const i={},s=U(e),n=s?Object.keys(e):e,o=U(t)?s?i=>K(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=+o(t)||0;return i}function Ve(t){return Be(t,{top:"y",right:"x",bottom:"y",left:"x"})}function We(t){return Be(t,["topLeft","topRight","bottomLeft","bottomRight"])}function Ne(t){const e=Ve(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function He(t,e){t=t||{},e=e||bt.font;let i=K(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=K(t.style,e.style);s&&!(""+s).match(ze)&&(console.warn('Invalid font style specified: "'+s+'"'),s="");const n={family:K(t.family,e.family),lineHeight:Fe(K(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:K(t.weight,e.weight),string:""};return n.string=Ut(n),n}function je(t,e,i,s){let n,o,a,r=!0;for(n=0,o=t.length;ni&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ye(t,e){return Object.assign(Object.create(t),e)}const Ue=["left","top","right","bottom"];function Xe(t,e){return t.filter((t=>t.pos===e))}function qe(t,e){return t.filter((t=>-1===Ue.indexOf(t.pos)&&t.box.axis===e))}function Ke(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function Ge(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Ue.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function ei(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;ot.box.fullSize)),!0),s=Ke(Xe(e,"left"),!0),n=Ke(Xe(e,"right")),o=Ke(Xe(e,"top"),!0),a=Ke(Xe(e,"bottom")),r=qe(e,"x"),l=qe(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Xe(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;Q(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),u=Object.assign({},n);Je(u,Ne(s));const f=Object.assign({maxPadding:u,w:o,h:a,x:n.left,y:n.top},n),g=Ge(l.concat(h),d);ei(r.fullSize,f,d,g),ei(l,f,d,g),ei(h,f,d,g)&&ei(l,f,d,g),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(f),si(r.leftAndTop,f,d,g),f.x+=f.w,f.y+=f.h,si(r.rightAndBottom,f,d,g),t.chartArea={left:f.left,top:f.top,right:f.left+f.w,bottom:f.top+f.h,height:f.h,width:f.w},Q(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(f.w,f.h,{left:0,top:0,right:0,bottom:0})}))}};function oi(t,e=[""],i=t,s,n=(()=>t[0])){ct(s)||(s=mi("_fallback",t));const o={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:i,_fallback:s,_getTarget:n,override:n=>oi([n,...t],e,i,s)};return new Proxy(o,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>ci(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=mi(li(o,t),i),ct(n))return hi(t,n)?gi(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>xi(t).includes(e),ownKeys:t=>xi(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function ai(t,e,i,s){const n={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:ri(t,s),setContext:e=>ai(t,e,i,s),override:n=>ai(t.override(n),e,i,s)};return new Proxy(n,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>ci(t,e,(()=>function(t,e,i){const{_proxy:s,_context:n,_subProxy:o,_descriptors:a}=t;let r=s[e];dt(r)&&a.isScriptable(e)&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t),e=e(o,a||s),r.delete(t),hi(t,e)&&(e=gi(n._scopes,n,t,e));return e}(e,r,t,i));Y(r)&&r.length&&(r=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_descriptors:r}=i;if(ct(o.index)&&s(t))e=e[o.index%e.length];else if(U(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const l of i){const i=gi(s,n,t,l);e.push(ai(i,o,a&&a[t],r))}}return e}(e,r,t,a.isIndexable));hi(e,r)&&(r=ai(r,n,o&&o[e],a));return r}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function ri(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:dt(i)?i:()=>i,isIndexable:dt(s)?s:()=>s}}const li=(t,e)=>t?t+ht(e):e,hi=(t,e)=>U(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function ci(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e))return t[e];const s=i();return t[e]=s,s}function di(t,e,i){return dt(t)?t(e,i):t}const ui=(t,e)=>!0===t?e:"string"==typeof t?lt(e,t):void 0;function fi(t,e,i,s,n){for(const o of e){const e=ui(i,o);if(e){t.add(e);const o=di(e._fallback,i,n);if(ct(o)&&o!==i&&o!==s)return o}else if(!1===e&&ct(s)&&i!==s)return null}return!1}function gi(t,e,i,s){const n=e._rootScopes,o=di(e._fallback,i,s),a=[...t,...n],r=new Set;r.add(s);let l=pi(r,a,i,o||i,s);return null!==l&&((!ct(o)||o===i||(l=pi(r,a,o,l,s),null!==l))&&oi(Array.from(r),[""],n,o,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const n=s[e];if(Y(n)&&U(i))return i;return n}(e,i,s))))}function pi(t,e,i,s,n){for(;i;)i=fi(t,e,i,s,n);return i}function mi(t,e){for(const i of e){if(!i)continue;const e=i[t];if(ct(e))return e}}function xi(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}const bi=Number.EPSILON||1e-14,_i=(t,e)=>e"x"===t?"y":"x";function vi(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=Vt(o,n),l=Vt(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function wi(t,e="x"){const i=yi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=_i(t,0);for(a=0;a!t.skip))),"monotone"===e.cubicInterpolationMode)wi(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o0===t||1===t,Pi=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*yt/i),Di=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*yt/i)+1,Ci={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*kt),easeOutSine:t=>Math.sin(t*kt),easeInOutSine:t=>-.5*(Math.cos(_t*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>Si(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>Si(t)?t:Pi(t,.075,.3),easeOutElastic:t=>Si(t)?t:Di(t,.075,.3),easeInOutElastic(t){const e=.1125;return Si(t)?t:t<.5?.5*Pi(2*t,e,.45):.5+.5*Di(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-Ci.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*Ci.easeInBounce(2*t):.5*Ci.easeOutBounce(2*t-1)+.5};function Oi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function Ai(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function Ti(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=Oi(t,n,i),r=Oi(n,o,i),l=Oi(o,e,i),h=Oi(a,r,i),c=Oi(r,l,i);return Oi(h,c,i)}const Li=new Map;function Ri(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=Li.get(i);return s||(s=new Intl.NumberFormat(t,e),Li.set(i,s)),s}(e,i).format(t)}function Ei(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ii(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function zi(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Fi(t){return"angle"===t?{between:Ht,compare:Wt,normalize:Nt}:{between:Yt,compare:(t,e)=>t-e,normalize:t=>t}}function Bi({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Vi(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Fi(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Fi(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;hb||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Bi({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Bi({start:_,end:d,loop:u,count:a,style:f})),g}function Wi(t,e){const i=[],s=t.segments;for(let n=0;nn&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Hi(t,[{start:a,end:r,loop:o}],i,e);return Hi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,rnull===t||""===t;const Gi=!!Se&&{passive:!0};function Zi(t,e,i){t.canvas.removeEventListener(e,i,Gi)}function Ji(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function Qi(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.addedNodes,s),e=e&&!Ji(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function ts(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||Ji(i.removedNodes,s),e=e&&!Ji(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const es=new Map;let is=0;function ss(){const t=window.devicePixelRatio;t!==is&&(is=t,es.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function ns(t,i,s){const n=t.canvas,o=n&&pe(n);if(!o)return;const a=e(((t,e)=>{const i=o.clientWidth;s(t,e),i{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||a(i,s)}));return r.observe(o),function(t,e){es.size||window.addEventListener("resize",ss),es.set(t,e)}(t,a),r}function os(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){es.delete(t),es.size||window.removeEventListener("resize",ss)}(t)}function as(t,i,s){const n=t.canvas,o=e((e=>{null!==t.ctx&&s(function(t,e){const i=qi[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t,(t=>{const e=t[0];return[e,e.offsetX,e.offsetY]}));return function(t,e,i){t.addEventListener(e,i,Gi)}(n,i,o),o}class rs extends Ui{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t.$chartjs={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",Ki(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(Ki(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e.$chartjs)return!1;const i=e.$chartjs.initial;["height","width"].forEach((t=>{const s=i[t];$(s)?e.removeAttribute(t):e.setAttribute(t,s)}));const s=i.style||{};return Object.keys(s).forEach((t=>{e.style[t]=s[t]})),e.width=e.width,delete e.$chartjs,!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:Qi,detach:ts,resize:ns}[e]||as;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:os,detach:os,resize:os}[e]||Zi)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return Me(t,e,i,s)}isAttached(t){const e=pe(t);return!(!e||!e.isConnected)}}function ls(t){return!ge()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?Xi:rs}var hs=Object.freeze({__proto__:null,_detectPlatform:ls,BasePlatform:Ui,BasicPlatform:Xi,DomPlatform:rs});const cs="transparent",ds={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=W(t||cs),n=s.valid&&W(e||cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class us{constructor(t,e,i,s){const n=e[i];s=je([t.to,s,n,t.from]);const o=je([t.from,n,s]);this._active=!0,this._fn=t.fn||ds[t.type||typeof o],this._easing=Ci[t.easing]||Ci.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=je([t.to,e,s,t.from]),this._from=je([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),bt.set("animations",{colors:{type:"color",properties:["color","borderColor","backgroundColor"]},numbers:{type:"number",properties:["x","y","borderWidth","radius","tension"]}}),bt.describe("animations",{_fallback:"animation"}),bt.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}});class gs{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!U(t))return;const e=this._properties;Object.getOwnPropertyNames(t).forEach((i=>{const s=t[i];if(!U(s))return;const n={};for(const t of fs)n[t]=s[t];(Y(s.properties)&&s.properties||[i]).forEach((t=>{t!==i&&e.has(t)||e.set(t,n)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new us(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(a.add(this._chart,i),!0):void 0}}function ps(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function ms(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n0||!i&&e<0)return n.index}return null}function vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;ti[t].axis===e)).shift()}function Ms(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i]}}}const ks=t=>"reset"===t||"none"===t,Ss=(t,e)=>e?t:Object.assign({},t);class Ps{constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.$context=void 0,this._syncList=[],this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=bs(t.vScale,t),this.addElements()}updateIndex(t){this.index!==t&&Ms(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=K(i.xAxisID,ws(t,"x")),o=e.yAxisID=K(i.yAxisID,ws(t,"y")),a=e.rAxisID=K(i.rAxisID,ws(t,"r")),r=e.indexAxis,l=e.iAxisID=s(r,n,o,a),h=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(l),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&ue(this._data,this),t._stacked&&Ms(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(U(e))this._data=function(t){const e=Object.keys(t),i=new Array(e.length);let s,n,o;for(s=0,n=e.length;s0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,h=s;else{h=Y(s[t])?this.parseArrayData(i,s,t,e):U(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const n=()=>null===l[a]||d&&l[a]t&&!e.hidden&&e._stacked&&{keys:ms(i,!0),values:null})(e,i,this.chart),l={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:h,max:c}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(a);let d,u;function f(){u=s[d];const e=u[a.axis];return!X(u[t.axis])||h>e||c=0;--d)if(!f()){this.updateRangeFromParsed(l,t,u,r);break}return l}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s=0&&tthis.getContext(i,s)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Ss(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new gs(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||ks(t)||this.chart._animationsDisabled}updateElement(t,e,i,s){ks(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!ks(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}Ds.defaults={},Ds.defaultRoutes=void 0;const Cs={values:t=>Y(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=Dt(Math.abs(o)),r=Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),Ri(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=t/Math.pow(10,Math.floor(Dt(t)));return 1===s||2===s||5===s?Cs.numeric.call(this,t,e,i):""}};var Os={formatters:Cs};function As(t,e){const i=t.options.ticks,s=i.maxTicksLimit||function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),n=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;is)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;nn)return e}return Math.max(n,1)}(n,e,s);if(o>0){let t,i;const s=o>1?Math.round((r-a)/(o-1)):null;for(Ts(e,l,h,$(s)?0:a-s,a),t=0,i=o-1;te.lineWidth,tickColor:(t,e)=>e.color,offset:!1,borderDash:[],borderDashOffset:0,borderWidth:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:Os.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),bt.route("scale.ticks","color","","color"),bt.route("scale.grid","color","","borderColor"),bt.route("scale.grid","borderColor","","borderColor"),bt.route("scale.title","color","","color"),bt.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t}),bt.describe("scales",{_fallback:"scale"}),bt.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t});const Ls=(t,e,i)=>"top"===e||"left"===e?t[e]+i:t[e]-i;function Rs(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;oa+r)))return h}function Is(t){return t.drawTicks?t.tickLength:0}function zs(t,e){if(!t.display)return 0;const i=He(t.font,e),s=Ne(t.padding);return(Y(t.text)?t.text.length:1)*i.lineHeight+s.height}function Fs(t,e,i){let n=s(t);return(i&&"right"!==e||!i&&"right"===e)&&(n=(t=>"left"===t?"right":"right"===t?"left":t)(n)),n}class Bs extends Ds{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=q(t,Number.POSITIVE_INFINITY),e=q(e,Number.NEGATIVE_INFINITY),i=q(i,Number.POSITIVE_INFINITY),s=q(s,Number.NEGATIVE_INFINITY),{min:q(t,i),max:q(e,s),minDefined:X(t),maxDefined:X(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;rs?s:i,s=n&&i>s?i:s,{min:q(i,q(s,i)),max:q(s,q(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){J(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=$e(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=jt(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Is(t.grid)-e.padding-zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=zt(Math.min(Math.asin(jt((h.highest.height+6)/o,-1,1)),Math.asin(jt(a/r,-1,1))-Math.asin(jt(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){J(this.options.afterCalculateLabelRotation,[this])}beforeFit(){J(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Is(n)+o):(t.height=this.maxHeight,t.width=Is(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=It(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){J(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n({width:n[t]||0,height:o[t]||0});return{first:v(0),last:v(e-1),widest:v(_),highest:v(y),widths:n,heights:o}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return $t(this._alignToPixels?Kt(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:r/s:r*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:o}=s,a=n.offset,r=this.isHorizontal(),l=this.ticks.length+(a?1:0),h=Is(n),c=[],d=n.setContext(this.getContext()),u=d.drawBorder?d.borderWidth:0,f=u/2,g=function(t){return Kt(i,t,u)};let p,m,x,b,_,y,v,w,M,k,S,P;if("top"===o)p=g(this.bottom),y=this.bottom-h,w=p-f,k=g(t.top)+f,P=t.bottom;else if("bottom"===o)p=g(this.top),k=t.top,P=g(t.bottom)-f,y=p+f,w=this.top+h;else if("left"===o)p=g(this.right),_=this.right-h,v=p-f,M=g(t.left)+f,S=t.right;else if("right"===o)p=g(this.left),M=t.left,S=g(t.right)-f,_=p+f,v=this.left+h;else if("x"===e){if("center"===o)p=g((t.top+t.bottom)/2+.5);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}k=t.top,P=t.bottom,y=p+f,w=y+h}else if("y"===e){if("center"===o)p=g((t.left+t.right)/2);else if(U(o)){const t=Object.keys(o)[0],e=o[t];p=g(this.chart.scales[t].getPixelForValue(e))}_=p-f,v=_-h,M=t.left,S=t.right}const D=K(s.ticks.maxTicksLimit,l),C=Math.max(1,Math.ceil(l/D));for(m=0;me.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:i+1,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");bt.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&bt.describe(e,t.descriptors)}(t,o,i),this.override&&bt.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in bt[s]&&(delete bt[s][i],this.override&&delete gt[i])}}var Ws=new class{constructor(){this.controllers=new Vs(Ps,"datasets",!0),this.elements=new Vs(Ds,"elements"),this.plugins=new Vs(Object,"plugins"),this.scales=new Vs(Bs,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):Q(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=ht(t);J(i["before"+s],[],i),e[t](i),J(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;et.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function Hs(t,e){return e||!1!==t?!0===t?{}:t:null}function js(t,e,i,s){const n=t.pluginScopeKeys(e),o=t.getOptionScopes(i,n);return t.createResolver(o,s,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function $s(t,e){const i=bt.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function Ys(t,e){return"x"===t||"y"===t?t:e.axis||("top"===(i=e.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.charAt(0).toLowerCase();var i}function Us(t){const e=t.options||(t.options={});e.plugins=K(e.plugins,{}),e.scales=function(t,e){const i=gt[t.type]||{scales:{}},s=e.scales||{},n=$s(t.type,e),o=Object.create(null),a=Object.create(null);return Object.keys(s).forEach((t=>{const e=s[t];if(!U(e))return console.error(`Invalid scale configuration for scale: ${t}`);if(e._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${t}`);const r=Ys(t,e),l=function(t,e){return t===e?"_index_":"_value_"}(r,n),h=i.scales||{};o[r]=o[r]||t,a[t]=ot(Object.create(null),[{axis:r},e,h[r],h[l]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,r=i.indexAxis||$s(n,e),l=(gt[n]||{}).scales||{};Object.keys(l).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,r),n=i[e+"AxisID"]||o[e]||e;a[n]=a[n]||Object.create(null),ot(a[n],[{axis:e},s[n],l[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];ot(e,[bt.scales[e.type],bt.scale])})),a}(t,e)}function Xs(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const qs=new Map,Ks=new Set;function Gs(t,e){let i=qs.get(t);return i||(i=e(),qs.set(t,i),Ks.add(i)),i}const Zs=(t,e,i)=>{const s=lt(e,i);void 0!==s&&t.add(s)};class Js{constructor(t){this._config=function(t){return(t=t||{}).data=Xs(t.data),Us(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Xs(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),Us(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return Gs(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return Gs(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return Gs(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return Gs(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>Zs(r,t,e)))),e.forEach((t=>Zs(r,s,t))),e.forEach((t=>Zs(r,gt[n]||{},t))),e.forEach((t=>Zs(r,bt,t))),e.forEach((t=>Zs(r,pt,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),Ks.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,gt[e]||{},bt.datasets[e]||{},{type:e},bt,pt]}resolveNamedOptions(t,e,i,s=[""]){const n={$shared:!0},{resolver:o,subPrefixes:a}=Qs(this._resolverCache,t,s);let r=o;if(function(t,e){const{isScriptable:i,isIndexable:s}=ri(t);for(const n of e){const e=i(n),o=s(n),a=(o||e)&&t[n];if(e&&(dt(a)||tn(a))||o&&Y(a))return!0}return!1}(o,e)){n.$shared=!1;r=ai(o,i=dt(i)?i():i,this.createResolver(t,i,a))}for(const t of e)n[t]=r[t];return n}createResolver(t,e,i=[""],s){const{resolver:n}=Qs(this._resolverCache,t,i);return U(e)?ai(n,e,void 0,s):n}}function Qs(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:oi(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const tn=t=>U(t)&&Object.getOwnPropertyNames(t).reduce(((e,i)=>e||dt(t[i])),!1);const en=["top","bottom","left","right","chartArea"];function sn(t,e){return"top"===t||"bottom"===t||-1===en.indexOf(t)&&"x"===e}function nn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function on(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),J(i&&i.onComplete,[t],e)}function an(t){const e=t.chart,i=e.options.animation;J(i&&i.onProgress,[t],e)}function rn(t){return ge()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const ln={},hn=t=>{const e=rn(t);return Object.values(ln).filter((t=>t.canvas===e)).pop()};function cn(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class dn{constructor(t,e){const s=this.config=new Js(e),n=rn(t),o=hn(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||ls(n)),this.platform.updateConfig(s);const l=this.platform.acquireContext(n,r.aspectRatio),h=l&&l.canvas,c=h&&h.height,d=h&&h.width;this.id=j(),this.ctx=l,this.canvas=h,this.width=d,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Ns,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=i((t=>this.update(t)),r.resizeDelay||0),this._dataChanges=[],ln[this.id]=this,l&&h?(a.listen(this,"complete",on),a.listen(this,"progress",an),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:s,_aspectRatio:n}=this;return $(t)?e&&n?n:s?i/s:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Gt(this.canvas,this.ctx),this}stop(){return a.stop(this),this}resize(t,e){a.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),J(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){Q(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=Ys(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),Q(n,(e=>{const n=e.options,o=n.id,a=Ys(o,n),r=K(n.type,e.dtype);void 0!==n.position&&sn(n.position,a)===sn(e.dposition)||(n.position=e.dposition),s[o]=!0;let l=null;if(o in i&&i[o].type===r)l=i[o];else{l=new(Ws.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[l.id]=l}l.init(n,t)})),Q(s,((t,e)=>{t||delete i[e]})),Q(i,(t=>{ni.configure(this,t,t.options),ni.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(nn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){Q(this.scales,(t=>{ni.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);ut(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){cn(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ni.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],Q(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i=t._clip,s=!i.disabled,n=this.chartArea,o={meta:t,index:t.index,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetDraw",o)&&(s&&Qt(e,{left:!1===i.left?0:n.left-i.left,right:!1===i.right?this.width:n.right+i.right,top:!1===i.top?0:n.top-i.top,bottom:!1===i.bottom?this.height:n.bottom+i.bottom}),t.controller.draw(),s&&te(e),o.cancelable=!1,this.notifyPlugins("afterDatasetDraw",o))}getElementsAtEventForMode(t,e,i,s){const n=Ee.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ye(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);ct(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),a.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};Q(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){Q(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},Q(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!tt(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:Jt(t,this.chartArea,this._minPadding)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=ft(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,J(n.onHover,[t,a,this],this),r&&J(n.onClick,[t,a,this],this));const h=!tt(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}const un=()=>Q(dn.instances,(t=>t._plugins.invalidate())),fn=!0;function gn(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}Object.defineProperties(dn,{defaults:{enumerable:fn,value:bt},instances:{enumerable:fn,value:ln},overrides:{enumerable:fn,value:gt},registry:{enumerable:fn,value:Ws},version:{enumerable:fn,value:"3.7.0"},getChart:{enumerable:fn,value:hn},register:{enumerable:fn,value:(...t)=>{Ws.add(...t),un()}},unregister:{enumerable:fn,value:(...t)=>{Ws.remove(...t),un()}}});class pn{constructor(t){this.options=t||{}}formats(){return gn()}parse(t,e){return gn()}format(t,e){return gn()}add(t,e,i){return gn()}diff(t,e,i){return gn()}startOf(t,e,i){return gn()}endOf(t,e){return gn()}}pn.override=function(t){Object.assign(pn.prototype,t)};var mn={_date:pn};function xn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;et-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(ct(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;sMath.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function _n(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;ht.x,i="left",s="right"):(e=t.base=i?1:-1)}(c,e,o)*n,d===o&&(p-=c/2),h=p+c),p===e.getPixelForValue(o)){const t=Ct(c)*e.getLineWidthForValue(o)/2;p+=t,c-=t}return{size:c,base:p,head:h,center:h+c/2}}_calculateBarIndexPixels(t,e){const i=e.scale,s=this.options,n=s.skipNull,o=K(s.maxBarThickness,1/0);let a,r;if(e.grouped){const i=n?this._getStackCount(t):e.stackCount,l="flex"===s.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,{xScale:i,yScale:s}=e,n=this.getParsed(t),o=i.getLabelForValue(n.x),a=s.getLabelForValue(n.y),r=n._custom;return{label:e.label,value:"("+o+", "+a+(r?", "+r:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,r=this.resolveDataElementOptions(e,s),l=this.getSharedOptions(r),h=this.includeOptions(s,l),c=o.axis,d=a.axis;for(let r=e;r""}}}};class Dn extends Ps{constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,o,a=t=>+i[t];if(U(i[t])){const{key:t="value"}=this._parsing;a=e=>+lt(i[e],t)}for(n=t,o=t+e;nHt(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>Ht(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(kt,c,u),x=g(_t,h,d),b=g(_t+kt,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(c,h,r),p=(i.width-o)/d,m=(i.height-o)/u,x=Math.max(Math.min(p,m)/2,0),b=Z(this.options.radius,x),_=(b-Math.max(b*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=f*b,this.offsetY=g*b,s.total=this.calculateTotal(),this.outerRadius=b-_*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-_*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/yt)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,f=this.resolveDataElementOptions(e,s),g=this.getSharedOptions(f),p=this.includeOptions(s,g);let m,x=this._getRotation();for(m=0;m0&&!isNaN(t)?yt*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s"spacing"!==t,_indexable:t=>"spacing"!==t},Dn.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label(t){let e=t.label;const i=": "+t.formattedValue;return Y(e)?(e=e.slice(),e[0]+=i):e+=i,e}}}}};class Cn extends Ps{initialize(){this.enableOptionSharing=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=function(t,e,i){const s=e.length;let n=0,o=s;if(t._sorted){const{iScale:a,_parsed:r}=t,l=a.axis,{min:h,max:c,minDefined:d,maxDefined:u}=a.getUserBounds();d&&(n=jt(Math.min(re(r,a.axis,h).lo,i?s:re(e,l,a.getPixelForValue(h)).lo),0,s-1)),o=u?jt(Math.max(re(r,a.axis,c).hi+1,i?0:re(e,l,a.getPixelForValue(c)).hi+1),n,s)-n:s-n}return{start:n,count:o}}(e,s,o);this._drawStart=a,this._drawCount=r,function(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a,_stacked:r,_dataset:l}=this._cachedMeta,h=this.resolveDataElementOptions(e,s),c=this.getSharedOptions(h),d=this.includeOptions(s,c),u=o.axis,f=a.axis,{spanGaps:g,segment:p}=this.options,m=Tt(g)?g:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||n||"none"===s;let b=e>0&&this.getParsed(e-1);for(let h=e;h0&&i[u]-b[u]>m,p&&(g.parsed=i,g.raw=l.data[h]),d&&(g.options=c||this.resolveDataElementOptions(h,e.active?"active":s)),x||this.updateElement(e,h,g,s),b=i}this.updateSharedOptions(c,s,h)}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}Cn.id="line",Cn.defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1},Cn.overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};class On extends Ps{constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=Ri(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=this.getDataset(),r=o.options.animation,l=this._cachedMeta.rScale,h=l.xCenter,c=l.yCenter,d=l.getIndexAngle(0)-.5*_t;let u,f=d;const g=360/this.countVisibleElements();for(u=0;u{!isNaN(t.data[s])&&this.chart.getDataVisibility(s)&&i++})),i}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?It(this.resolveDataElementOptions(t,e).angle||i):0}}On.id="polarArea",On.defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0},On.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i}}=t.legend.options;return e.labels.map(((e,s)=>{const n=t.getDatasetMeta(0).controller.getStyle(s);return{text:e,fillStyle:n.backgroundColor,strokeStyle:n.borderColor,lineWidth:n.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(s),index:s}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}},tooltip:{callbacks:{title:()=>"",label:t=>t.chart.data.labels[t.dataIndex]+": "+t.formattedValue}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};class An extends Dn{}An.id="pie",An.defaults={cutout:0,rotation:0,circumference:360,radius:"100%"};class Tn extends Ps{getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this.getDataset(),o=this._cachedMeta.rScale,a="reset"===s;for(let r=e;r"",label:t=>"("+t.label+", "+t.formattedValue+")"}}},scales:{x:{type:"linear"},y:{type:"linear"}}};var Rn=Object.freeze({__proto__:null,BarController:Sn,BubbleController:Pn,DoughnutController:Dn,LineController:Cn,PolarAreaController:On,PieController:An,RadarController:Tn,ScatterController:Ln});function En(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+kt,s-kt),t.closePath(),t.clip()}function In(t,e,i,s){const n=Be(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return jt(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:jt(n.innerStart,0,a),innerEnd:jt(n.innerEnd,0,a)}}function zn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Fn(t,e,i,s,n){const{x:o,y:a,startAngle:r,pixelMargin:l,innerRadius:h}=e,c=Math.max(e.outerRadius+s+i-l,0),d=h>0?h+s+i+l:0;let u=0;const f=n-r;if(s){const t=((h>0?h-s:0)+(c>0?c-s:0))/2;u=(f-(0!==t?f*t/(t+s):f))/2}const g=(f-Math.max(.001,f*c-i/_t)/c)/2,p=r+g+u,m=n-g-u,{outerStart:x,outerEnd:b,innerStart:_,innerEnd:y}=In(e,d,c,m-p),v=c-x,w=c-b,M=p+x/v,k=m-b/w,S=d+_,P=d+y,D=p+_/S,C=m-y/P;if(t.beginPath(),t.arc(o,a,c,M,k),b>0){const e=zn(w,k,o,a);t.arc(e.x,e.y,b,k,m+kt)}const O=zn(P,m,o,a);if(t.lineTo(O.x,O.y),y>0){const e=zn(P,C,o,a);t.arc(e.x,e.y,y,m+kt,C+Math.PI)}if(t.arc(o,a,d,m-y/d,p+_/d,!0),_>0){const e=zn(S,D,o,a);t.arc(e.x,e.y,_,D+Math.PI,p-kt)}const A=zn(v,p,o,a);if(t.lineTo(A.x,A.y),x>0){const e=zn(v,M,o,a);t.arc(e.x,e.y,x,p-kt,M)}t.closePath()}function Bn(t,e,i,s,n){const{options:o}=e,{borderWidth:a,borderJoinStyle:r}=o,l="inner"===o.borderAlign;a&&(l?(t.lineWidth=2*a,t.lineJoin=r||"round"):(t.lineWidth=a,t.lineJoin=r||"bevel"),e.fullCircles&&function(t,e,i){const{x:s,y:n,startAngle:o,pixelMargin:a,fullCircles:r}=e,l=Math.max(e.outerRadius-a,0),h=e.innerRadius+a;let c;for(i&&En(t,e,o+yt),t.beginPath(),t.arc(s,n,h,o+yt,o,!0),c=0;c=yt||Ht(n,a,r),f=Yt(o,l+d,h+d);return u&&f}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius","circumference"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/2,n=(e.spacing||0)/2;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>yt?Math.floor(i/yt):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();let o=0;if(s){o=s/2;const e=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(e)*o,Math.sin(e)*o),this.circumference>=_t&&(o=s)}t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor;const a=function(t,e,i,s){const{fullCircles:n,startAngle:o,circumference:a}=e;let r=e.endAngle;if(n){Fn(t,e,i,s,o+yt);for(let e=0;er&&o>r;return{count:s,start:l,loop:e.loop,ilen:h(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(ig&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function Yn(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?$n:jn}Vn.id="arc",Vn.defaults={borderAlign:"center",borderColor:"#fff",borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0},Vn.defaultRoutes={backgroundColor:"backgroundColor"};const Un="function"==typeof Path2D;function Xn(t,e,i,s){Un&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Wn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=Yn(e);for(const r of n)Wn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class qn extends Ds{constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;ki(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ni(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Wi(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?Ai:t.tension||"monotone"===t.cubicInterpolationMode?Ti:Oi}(i);let l,h;for(l=0,h=o.length;l"borderDash"!==t&&"fill"!==t};class Gn extends Ds{constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps(["x","y"],i);return Math.pow(t-n,2)+Math.pow(e-o,2){oo(t)}))}var ro={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void ao(t);const s=t.width;t.data.datasets.forEach(((e,n)=>{const{_data:o,indexAxis:a}=e,r=t.getDatasetMeta(n),l=o||e.data;if("y"===je([a,t.options.indexAxis]))return;if("line"!==r.type)return;const h=t.scales[r.xAxisID];if("linear"!==h.type&&"time"!==h.type)return;if(t.options.parsing)return;let{start:c,count:d}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=jt(re(e,o.axis,a).lo,0,i-1)),s=h?jt(re(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(r,l);if(d<=(i.threshold||4*s))return void oo(e);let u;switch($(o)&&(e._data=l,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":u=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;cu&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(l,c,d,s,i);break;case"min-max":u=function(t,e,i,s){let n,o,a,r,l,h,c,d,u,f,g=0,p=0;const m=[],x=e+i-1,b=t[e].x,_=t[x].x-b;for(n=e;nf&&(f=r,c=n),g=(p*g+o.x)/++p;else{const i=n-1;if(!$(h)&&!$(c)){const e=Math.min(h,c),s=Math.max(h,c);e!==d&&e!==i&&m.push({...t[e],x:g}),s!==d&&s!==i&&m.push({...t[s],x:g})}n>0&&i!==d&&m.push(t[i]),m.push(o),l=e,p=0,u=f=r,h=c=d=n}}return m}(l,c,d,s);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=u}))},destroy(t){ao(t)}};function lo(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=K(i&&i.target,i);return void 0===s&&(s=!!e.backgroundColor),!1!==s&&null!==s&&(!0===s?"origin":s)}(t);if(U(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return X(n)&&Math.floor(n)===n?("-"!==s[0]&&"+"!==s[0]||(n=e+n),!(n===e||n<0||n>=i)&&n):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}class ho{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:yt},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function co(t){return(t.scale||{}).getPointPositionForValue?function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,o=[],a=s.reverse?e.max:e.min,r=s.reverse?e.min:e.max;let l,h,c;if(c="start"===i?a:"end"===i?r:U(i)?i.value:e.getBaseValue(),s.grid.circular)return h=e.getPointPositionForValue(0,a),new ho({x:h.x,y:h.y,radius:e.getDistanceFromCenterForValue(c)});for(l=0;lt;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function fo(t,e,i){const s=[];for(let n=0;n{e=uo(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new qn({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function xo(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!X(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function bo(t,e,i){t.beginPath(),e.path(t),t.lineTo(e.last().x,i),t.lineTo(e.first().x,i),t.closePath(),t.clip()}function _o(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=Nt(n),o=Nt(o)),{property:t,start:n,end:o}}function yo(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function vo(t,e,i){const{top:s,bottom:n}=e.chart.chartArea,{property:o,start:a,end:r}=i||{};"x"===o&&(t.beginPath(),t.rect(a,s,r-a,n-s),t.clip())}function wo(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}function Mo(t,e){const{line:i,target:s,property:n,color:o,scale:a}=e,r=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=uo(s,r,n);const l=_o(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Wi(e,l);for(const e of h){const s=_o(i,o[e.start],o[e.end],e.loop),r=Vi(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:yo(l,s,"start",Math.max)},end:{[i]:yo(l,s,"end",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:l,start:h,end:c}of r){const{style:{backgroundColor:r=o}={}}=e,d=!0!==s;t.save(),t.fillStyle=r,vo(t,a,d&&_o(n,h,c)),t.beginPath();const u=!!i.pathSegment(t,e);let f;if(d){u?t.closePath():wo(t,s,c,n);const e=!!s.pathSegment(t,l,{move:u,reverse:!0});f=u&&e,f||wo(t,s,h,n)}t.closePath(),t.fill(f?"evenodd":"nonzero"),t.restore()}}function ko(t,e,i){const s=po(e),{line:n,scale:o,axis:a}=e,r=n.options,l=r.fill,h=r.backgroundColor,{above:c=h,below:d=h}=l||{};s&&n.points.length&&(Qt(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r}=e,l=i._loop?"angle":e.axis;t.save(),"x"===l&&o!==n&&(bo(t,s,a.top),Mo(t,{line:i,target:s,color:n,scale:r,property:l}),t.restore(),t.save(),bo(t,s,a.bottom)),Mo(t,{line:i,target:s,color:o,scale:r,property:l}),t.restore()}(t,{line:n,target:s,above:c,below:d,area:i,scale:o,axis:a}),te(t))}var So={id:"filler",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&ko(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;i&&ko(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;s&&!1!==s.fill&&"beforeDatasetDraw"===i.drawTime&&ko(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Po=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class Do extends Ds{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=J(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=He(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=Po(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,n,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const p=i+e/2+n.measureText(t.text).width;o>0&&u+s+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:s},d=Math.max(d,p),u+=s+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:o}}=this,a=Ei(o,this.left,this.width);if(this.isHorizontal()){let o=0,r=n(i,this.left+s,this.right-this.lineWidths[o]);for(const l of e)o!==l.row&&(o=l.row,r=n(i,this.left+s,this.right-this.lineWidths[o])),l.top+=this.top+t+s,l.left=a.leftForLtr(a.x(r),l.width),r+=l.width+s}else{let o=0,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height);for(const l of e)l.col!==o&&(o=l.col,r=n(i,this.top+t+s,this.bottom-this.columnSizes[o].height)),l.top=r,l.left+=this.left+s,l.left=a.leftForLtr(a.x(l.left),l.width),r+=l.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Qt(t,this),this._draw(),te(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:a,labels:r}=t,l=bt.color,h=Ei(t.rtl,this.left,this.width),c=He(r.font),{color:d,padding:u}=r,f=c.size,g=f/2;let p;this.drawTitle(),s.textAlign=h.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=c.string;const{boxWidth:m,boxHeight:x,itemHeight:b}=Po(r,f),_=this.isHorizontal(),y=this._computeTitleHeight();p=_?{x:n(a,this.left+u,this.right-i[0]),y:this.top+u+y,line:0}:{x:this.left+u,y:n(a,this.top+y+u,this.bottom-e[0].height),line:0},Ii(this.ctx,t.textDirection);const v=b+u;this.legendItems.forEach(((w,M)=>{s.strokeStyle=w.fontColor||d,s.fillStyle=w.fontColor||d;const k=s.measureText(w.text).width,S=h.textAlign(w.textAlign||(w.textAlign=r.textAlign)),P=m+g+k;let D=p.x,C=p.y;h.setWidth(this.width),_?M>0&&D+P+u>this.right&&(C=p.y+=v,p.line++,D=p.x=n(a,this.left+u,this.right-i[p.line])):M>0&&C+v>this.bottom&&(D=p.x=D+e[p.line].width+u,p.line++,C=p.y=n(a,this.top+y+u,this.bottom-e[p.line].height));!function(t,e,i){if(isNaN(m)||m<=0||isNaN(x)||x<0)return;s.save();const n=K(i.lineWidth,1);if(s.fillStyle=K(i.fillStyle,l),s.lineCap=K(i.lineCap,"butt"),s.lineDashOffset=K(i.lineDashOffset,0),s.lineJoin=K(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=K(i.strokeStyle,l),s.setLineDash(K(i.lineDash,[])),r.usePointStyle){const o={radius:m*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},a=h.xPlus(t,m/2);Zt(s,o,a,e+g)}else{const o=e+Math.max((f-x)/2,0),a=h.leftForLtr(t,m),r=We(i.borderRadius);s.beginPath(),Object.values(r).some((t=>0!==t))?oe(s,{x:a,y:o,w:m,h:x,radius:r}):s.rect(a,o,m,x),s.fill(),0!==n&&s.stroke()}s.restore()}(h.x(D),C,w),D=o(S,D+m+g,_?D+P:this.right,t.rtl),function(t,e,i){se(s,i.text,t,e+b/2,c,{strikethrough:i.hidden,textAlign:h.textAlign(i.textAlign)})}(h.x(D),C,w),_?p.x+=P+u:p.y+=v})),zi(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=He(e.font),o=Ne(e.padding);if(!e.display)return;const a=Ei(t.rtl,this.left,this.width),r=this.ctx,l=e.position,h=i.size/2,c=o.top+h;let d,u=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),d=this.top+c,u=n(t.align,u,this.right-f);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);d=c+n(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const g=n(l,u,u+f);r.textAlign=a.textAlign(s(l)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=i.string,se(r,e.text,g,d,i)}_computeTitleHeight(){const t=this.options.title,e=He(t.font),i=Ne(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(Yt(t,this.left,this.right)&&Yt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;it.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const a=t.controller.getStyle(i?0:void 0),r=Ne(a.borderWidth);return{text:e[t.index].label,fillStyle:a.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:(r.width+r.height)/4,strokeStyle:a.borderColor,pointStyle:s||a.pointStyle,rotation:a.rotation,textAlign:n||a.textAlign,borderRadius:0,datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class Oo extends Ds{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=Y(i.text)?i.text.length:1;this._padding=Ne(i.padding);const n=s*He(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=n:this.width=n}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:o,options:a}=this,r=a.align;let l,h,c,d=0;return this.isHorizontal()?(h=n(r,i,o),c=e+t,l=o-i):("left"===a.position?(h=i+t,c=n(r,s,e),d=-.5*_t):(h=o-t,c=n(r,e,s),d=.5*_t),l=s-e),{titleX:h,titleY:c,maxWidth:l,rotation:d}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=He(e.font),n=i.lineHeight/2+this._padding.top,{titleX:o,titleY:a,maxWidth:r,rotation:l}=this._drawArgs(n);se(t,e.text,0,0,i,{color:e.color,maxWidth:r,rotation:l,textAlign:s(e.align),textBaseline:"middle",translation:[o,a]})}}var Ao={id:"title",_element:Oo,start(t,e,i){!function(t,e){const i=new Oo({ctx:t.ctx,options:e,chart:t});ni.configure(t,i,e),ni.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ni.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const To=new WeakMap;var Lo={id:"subtitle",start(t,e,i){const s=new Oo({ctx:t.ctx,options:i,chart:t});ni.configure(t,s,i),ni.addBox(t,s),To.set(t,s)},stop(t){ni.removeBox(t,To.get(t)),To.delete(t)},beforeUpdate(t,e,i){const s=To.get(t);ni.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Ro={average(t){if(!t.length)return!1;let e,i,s=0,n=0,o=0;for(e=0,i=t.length;e-1?t.split("\n"):t}function zo(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Fo(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=He(e.bodyFont),h=He(e.titleFont),c=He(e.footerFont),d=o.length,u=n.length,f=s.length,g=Ne(e.padding);let p=g.height,m=0,x=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(x+=t.beforeBody.length+t.afterBody.length,d&&(p+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),x){p+=f*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(x-f)*l.lineHeight+(x-1)*e.bodySpacing}u&&(p+=e.footerMarginTop+u*c.lineHeight+(u-1)*e.footerSpacing);let b=0;const _=function(t){m=Math.max(m,i.measureText(t).width+b)};return i.save(),i.font=h.string,Q(t.title,_),i.font=l.string,Q(t.beforeBody.concat(t.afterBody),_),b=e.displayColors?a+2+e.boxPadding:0,Q(s,(t=>{Q(t.before,_),Q(t.lines,_),Q(t.after,_)})),b=0,i.font=c.string,Q(t.footer,_),i.restore(),m+=g.width,{width:m,height:p}}function Bo(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Vo(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return it.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||Bo(t,e,i,s),yAlign:s}}function Wo(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=We(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:jt(g,0,s.width-e.width),y:jt(p,0,s.height-e.height)}}function No(t,e,i){const s=Ne(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function Ho(t){return Eo([],Io(t))}function jo(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}class $o extends Ds{constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart||t._chart,this._chart=this.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new gs(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ye(t,{tooltip:e,tooltipItems:i,type:"tooltip"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=i.beforeTitle.apply(this,[t]),n=i.title.apply(this,[t]),o=i.afterTitle.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}getBeforeBody(t,e){return Ho(e.callbacks.beforeBody.apply(this,[t]))}getBody(t,e){const{callbacks:i}=e,s=[];return Q(t,(t=>{const e={before:[],lines:[],after:[]},n=jo(i,t);Eo(e.before,Io(n.beforeLabel.call(this,t))),Eo(e.lines,n.label.call(this,t)),Eo(e.after,Io(n.afterLabel.call(this,t))),s.push(e)})),s}getAfterBody(t,e){return Ho(e.callbacks.afterBody.apply(this,[t]))}getFooter(t,e){const{callbacks:i}=e,s=i.beforeFooter.apply(this,[t]),n=i.footer.apply(this,[t]),o=i.afterFooter.apply(this,[t]);let a=[];return a=Eo(a,Io(s)),a=Eo(a,Io(n)),a=Eo(a,Io(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;at.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),Q(l,(e=>{const i=jo(t.callbacks,e);s.push(i.labelColor.call(this,e)),n.push(i.labelPointStyle.call(this,e)),o.push(i.labelTextColor.call(this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Ro[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Fo(this,i),a=Object.assign({},t,e),r=Vo(this.chart,i,a),l=Wo(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=We(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Ei(i.rtl,this.x,this.width);for(t.x=No(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=He(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,oe(t,{x:e,y:g,w:l,h:r,radius:a}),t.fill(),t.stroke(),t.fillStyle=o.backgroundColor,t.beginPath(),oe(t,{x:i,y:g+1,w:l-2,h:r-2,radius:a}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,l,r),t.strokeRect(e,g,l,r),t.fillStyle=o.backgroundColor,t.fillRect(i,g+1,l-2,r-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=He(i.bodyFont);let d=c.lineHeight,u=0;const f=Ei(i.rtl,this.x,this.width),g=function(i){e.fillText(i,f.x(t.x+u),t.y+d/2),t.y+=d+n},p=f.textAlign(o);let m,x,b,_,y,v,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=No(this,p,i),e.fillStyle=i.bodyColor,Q(this.beforeBody,g),u=a&&"right"!==p?"center"===o?l/2+h:l+2+h:0,_=0,v=s.length;_0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Ro[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Fo(this,t),a=Object.assign({},i,this._size),r=Vo(e,t,a),l=Wo(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=Ne(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ii(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),zi(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!tt(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!tt(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e;const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Ro[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}$o.positioners=Ro;var Yo={id:"tooltip",_element:$o,positioners:Ro,afterInit(t,e,i){i&&(t.tooltip=new $o({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip,i={tooltip:e};!1!==t.notifyPlugins("beforeTooltipDraw",i)&&(e&&e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i))},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:{beforeTitle:H,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]},Uo=Object.freeze({__proto__:null,Decimation:ro,Filler:So,Legend:Co,SubTitle:Lo,Title:Ao,Tooltip:Yo});function Xo(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}class qo extends Bs{constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if($(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:jt(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:Xo(i,t,K(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){const e=this.getLabels();return t>=0&&te.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}function Ko(t,e,{horizontal:i,minRotation:s}){const n=It(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(""+t).length;return Math.min(e/o,a)}qo.id="category",qo.defaults={ticks:{callback:qo.prototype.getLabelForValue}};class Go extends Bs{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return $(t)||("number"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=Ct(s),e=Ct(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=1;(n>=Number.MAX_SAFE_INTEGER||s<=Number.MIN_SAFE_INTEGER)&&(e=Math.abs(.05*n)),a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const s=function(t,e){const i=[],{bounds:s,step:n,min:o,max:a,precision:r,count:l,maxTicks:h,maxDigits:c,includeBounds:d}=t,u=n||1,f=h-1,{min:g,max:p}=e,m=!$(o),x=!$(a),b=!$(l),_=(p-g)/(c+1);let y,v,w,M,k=Ot((p-g)/f/u)*u;if(k<1e-14&&!m&&!x)return[{value:g},{value:p}];M=Math.ceil(p/k)-Math.floor(g/k),M>f&&(k=Ot(M*k/f/u)*u),$(r)||(y=Math.pow(10,r),k=Math.ceil(k*y)/y),"ticks"===s?(v=Math.floor(g/k)*k,w=Math.ceil(p/k)*k):(v=g,w=p),m&&x&&n&&Rt((a-o)/n,k/1e3)?(M=Math.round(Math.min((a-o)/k,h)),k=(a-o)/M,v=o,w=a):b?(v=m?o:v,w=x?a:w,M=l-1,k=(w-v)/M):(M=(w-v)/k,M=Lt(M,Math.round(M),k/1e3)?Math.round(M):Math.ceil(M));const S=Math.max(Ft(k),Ft(v));y=Math.pow(10,$(r)?S:r),v=Math.round(v*y)/y,w=Math.round(w*y)/y;let P=0;for(m&&(d&&v!==o?(i.push({value:o}),v0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=X(t)?Math.max(0,t):null,this.max=X(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t,a=(t,e)=>Math.pow(10,Math.floor(Dt(t))+e);i===s&&(i<=0?(n(1),o(10)):(n(a(i,-1)),o(a(s,1)))),i<=0&&n(a(s,-1)),s<=0&&o(a(i,1)),this._zero&&this.min!==this._suggestedMin&&i===a(this.min,0)&&n(a(i,-1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=function(t,e){const i=Math.floor(Dt(e.max)),s=Math.ceil(e.max/Math.pow(10,i)),n=[];let o=q(t.min,Math.pow(10,Math.floor(Dt(e.min)))),a=Math.floor(Dt(o)),r=Math.floor(o/Math.pow(10,a)),l=a<0?Math.pow(10,Math.abs(a)):1;do{n.push({value:o,major:Jo(o)}),++r,10===r&&(r=1,++a,l=a>=0?1:l),o=Math.round(r*Math.pow(10,a)*l)/l}while(an?{start:e-i,end:e}:{start:e,end:e+i}}function ia(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],n=[],o=t._pointLabels.length,a=t.options.pointLabels,r=a.centerPointLabels?_t/o:0;for(let d=0;de.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.starte.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function na(t){return 0===t||180===t?"center":t<180?"left":"right"}function oa(t,e,i){return"right"===i?t-=e:"center"===i&&(t-=e/2),t}function aa(t,e,i){return 90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e),t}function ra(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,yt);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o{const i=J(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?ia(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return Nt(t*(yt/(this._pointLabels.length||1))+It(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if($(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if($(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;n--){const e=s.setContext(t.getPointLabelContext(n)),o=He(e.font),{x:a,y:r,textAlign:l,left:h,top:c,right:d,bottom:u}=t._pointLabelItems[n],{backdropColor:f}=e;if(!$(f)){const t=Ne(e.backdropPadding);i.fillStyle=f,i.fillRect(h-t.left,c-t.top,d-h+t.width,u-c+t.height)}se(i,t._pointLabels[n],a,r+o.lineHeight/2,o,{color:e.color,textAlign:l,textBaseline:"middle"})}}(this,n),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e){a=this.getDistanceFromCenterForValue(t.value);!function(t,e,i,s){const n=t.ctx,o=e.circular,{color:a,lineWidth:r}=e;!o&&!s||!a||!r||i<0||(n.save(),n.strokeStyle=a,n.lineWidth=r,n.setLineDash(e.borderDash),n.lineDashOffset=e.borderDashOffset,n.beginPath(),ra(t,i,o,s),n.closePath(),n.stroke(),n.restore())}(this,s.setContext(this.getContext(e-1)),a,n)}})),i.display){for(t.save(),o=n-1;o>=0;o--){const s=i.setContext(this.getPointLabelContext(o)),{color:n,lineWidth:l}=s;l&&n&&(t.lineWidth=l,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,a=this.getDistanceFromCenterForValue(e.ticks.reverse?this.min:this.max),r=this.getPointPosition(o,a),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(r.x,r.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=He(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=Ne(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}se(t,s.label,0,-n,l,{color:r.color})})),t.restore()}drawTitle(){}}la.id="radialLinear",la.defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:Os.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}},la.defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"},la.descriptors={angleLines:{_fallback:"grid"}};const ha={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ca=Object.keys(ha);function da(t,e){return t-e}function ua(t,e){if($(e))return null;const i=t._adapter,{parser:s,round:n,isoWeekday:o}=t._parseOpts;let a=e;return"function"==typeof s&&(a=s(a)),X(a)||(a="string"==typeof s?i.parse(a,s):i.parse(a)),null===a?null:(n&&(a="week"!==n||!Tt(o)&&!0!==o?i.startOf(a,n):i.startOf(a,"isoWeek",o)),+a)}function fa(t,e,i,s){const n=ca.length;for(let o=ca.indexOf(t);o=e?i[s]:i[n]]=!0}}else t[e]=!0}function pa(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a=0&&(e[l].major=!0);return e}(t,s,n,i):s}class ma extends Bs{constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e){const i=t.time||(t.time={}),s=this._adapter=new mn._date(t.adapters.date);ot(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:ua(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:a}=this.getUserBounds();function r(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),a||isNaN(t.max)||(n=Math.max(n,t.max))}o&&a||(r(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||r(this.getMinMax(!1))),s=X(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=X(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=he(s,n,this.max);return this._unit=e.unit||(i.autoSkip?fa(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=ca.length-1;o>=ca.indexOf(i);o--){const i=ca[o];if(ha[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return ca[i?ca.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=ca.indexOf(t)+1,i=ca.length;e1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const f="data"===s.ticks.source&&this.getDataTimestamps();for(c=u,d=0;ct-e)).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.time.displayFormats,a=this._unit,r=this._majorUnit,l=a&&o[a],h=r&&o[r],c=i[e],d=r&&h&&c&&c.major,u=this._adapter.format(t,s||(d?h:l)),f=n.ticks.callback;return f?J(f,[u,e,i],this):u}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=re(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=re(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}ma.id="time",ma.defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",major:{enabled:!1}}};class ba extends ma{constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=xa(e,this.min),this._tableRange=xa(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o + Word count: + + +   + + Character count: + +`; + +class WordCountWidget extends api.NoteContextAwareWidget { + get position() { return 100; } // higher value means position towards the bottom/right + + get parentWidget() { return 'center-pane'; } + + isEnabled() { + return super.isEnabled() + && this.note.type === 'text' + && this.note.hasLabel('wordCount'); + } + + doRender() { + this.$widget = $(TPL); + this.$wordCount = this.$widget.find('.word-count'); + this.$characterCount = this.$widget.find('.character-count'); + return this.$widget; + } + + async refreshWithNote(note) { + const {content} = await note.getNoteComplement(); + + const text = $(content).text(); // get plain text only + + const counts = this.getCounts(text); + + this.$wordCount.text(counts.words); + this.$characterCount.text(counts.characters); + } + + getCounts(text) { + const chunks = text + .split(/[\s-+:,/\\]+/) + .filter(chunk => chunk !== ''); + + let words; + + if (chunks.length === 1 && chunks[0] === '') { + words = 0; + } + else { + words = chunks.length; + } + + const characters = chunks.join('').length; + + return {words, characters}; + } + + async entitiesReloadedEvent({loadResults}) { + if (loadResults.isNoteContentReloaded(this.noteId)) { + this.refresh(); + } + } +} + +module.exports = new WordCountWidget(); \ No newline at end of file diff --git a/demo/root/Trilium Demo/Steel Blue.css b/demo/root/Trilium Demo/Steel Blue.css new file mode 100644 index 000000000..56f3dec40 --- /dev/null +++ b/demo/root/Trilium Demo/Steel Blue.css @@ -0,0 +1,101 @@ +/* + * This is a demo of how you can create custom theme for Trilium. You can activate it by going + * into options (top-right corner) in first tab "Appearance". + * + * You can read some details on theming here: http://github.com/zadam/trilium/wiki/Themes + */ + +@font-face { /* This will be used as main UI font (see below) */ + font-family: 'Raleway'; + font-style: normal; + font-weight: 400; + src: url('../../../custom/fonts/raleway.woff2') format('woff2'); +} + +@font-face { /* This will be used as text content font (see below) */ + font-family: 'Garamond'; + font-style: normal; + font-weight: 400; + src: url('../../../custom/fonts/garamond.woff2') format('woff2'); +} + +:root { + --theme-style: light; + + --main-font-family: 'Raleway'; + --main-font-size: normal; + + --tree-font-family: 'Raleway'; + --tree-font-size: normal; + + --detail-font-family: 'Raleway'; + --detail-font-size: normal; + + --monospace-font-family: 'Monospace'; + --monospace-font-size: normal; + + --main-background-color: #404552; + --main-text-color: #AFB8C6; + --main-border-color: #AFB8C6; + + --accented-background-color: #383C4A; + --more-accented-background-color: #2F343F; + + --button-text-color: #AFB8C6; + --button-background-color: #2F343F; + --button-disabled-text-color: #86919F; + --button-disabled-background-color: #404552; + --button-border-color: #333; + --button-border-radius: 2px; + + --primary-button-background-color: #6c757d; + --primary-button-text-color: white; + --primary-button-border-color: #6c757d; + + --muted-text-color: #86919F; + + --input-text-color: #AFB8C6; + --input-background-color: #404552; + + --hover-item-text-color: white; + --hover-item-background-color: #4877B1; + + --active-item-text-color: white; + --active-item-background-color: #4877B1; + + --menu-text-color: #AFB8C6; + --menu-background-color: #383C4A; + + --modal-background-color: #404552; + --modal-backdrop-color: black; + + --left-pane-background-color: #2F343F; + --left-pane-text-color: #AFB8C6; + + --launcher-pane-background-color: #2F343F; + --launcher-pane-text-color: #AFB8C6; + + --active-tab-background-color: #2F343F; + --active-tab-text-color: #AFB8C6; + + --inactive-tab-background-color: #404552; + --inactive-tab-text-color: #AFB8C6; + + --scrollbar-border-color: rgba(175, 184, 198, 0.5); + --tooltip-background-color: #383C4A; + --link-color: lightskyblue; + + --mermaid-theme: forest; +} + +body .global-menu-button { + background-image: url("../../../assets/vX/images/icon-grey.png"); +} + +body .note-detail-editable-text, body .note-detail-readonly-text { + font-size: 120%; /* Garamond is subjectively smaller */ +} + +body .CodeMirror { + filter: invert(100%) hue-rotate(180deg); +} \ No newline at end of file diff --git a/demo/root/Trilium Demo/Steel Blue/eb-garamond-v9-latin-reg.woff2 b/demo/root/Trilium Demo/Steel Blue/eb-garamond-v9-latin-reg.woff2 new file mode 100644 index 000000000..c36dcecbb Binary files /dev/null and b/demo/root/Trilium Demo/Steel Blue/eb-garamond-v9-latin-reg.woff2 differ diff --git a/demo/root/Trilium Demo/Steel Blue/raleway-v12-latin-regula.woff2 b/demo/root/Trilium Demo/Steel Blue/raleway-v12-latin-regula.woff2 new file mode 100644 index 000000000..eba29a9ab Binary files /dev/null and b/demo/root/Trilium Demo/Steel Blue/raleway-v12-latin-regula.woff2 differ diff --git a/demo/root/Trilium Demo/Tech.html b/demo/root/Trilium Demo/Tech.html new file mode 100644 index 000000000..f22ea997a --- /dev/null +++ b/demo/root/Trilium Demo/Tech.html @@ -0,0 +1,21 @@ + + + + + + + + Tech + + + +
+

Tech

+ +
+

Expand note on the left pane to see content.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/Bash scripting.html b/demo/root/Trilium Demo/Tech/Linux/Bash scripting.html new file mode 100644 index 000000000..55c1364be --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/Bash scripting.html @@ -0,0 +1,34 @@ + + + + + + + + Bash scripting + + + +
+

Bash scripting

+ +
+

Bash is a Unix shell and + command languagewritten by Brian Fox for + the GNU Project as + a free software replacement + for the Bourne shell. + First released in 1989, it has been distributed widely as the default + loginshell for most Linux distributions + and Apple's macOS (formerly + OS X). A version is also available for Windows 10.

+

Bash on Wikipedia + +

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/Bash scripting/Bash startup modes.html b/demo/root/Trilium Demo/Tech/Linux/Bash scripting/Bash startup modes.html new file mode 100644 index 000000000..2f686d2de --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/Bash scripting/Bash startup modes.html @@ -0,0 +1,105 @@ + + + + + + + + Bash startup modes + + + +
+

Bash startup modes

+ +
+

Login shell

+ +

As a "login shell", Bash reads and sets (executes) the user's profile + from /etc/profile and one of ~/.bash_profile, ~/.bash_login, or ~/.profile + (in that order, using the first one that's readable!).

+

When a login shell exits, Bash reads and executes commands from the file + ~/.bash_logout, if it exists.

+

Why an extra login shell mode? There are many actions and variable sets + that only make sense for the initial user login. That's why all UNIX® shells + have (should have) a "login" mode.

+

Methods to start Bash as a login shell: + +

+
    +
  • the first character of argv[0] is - (a hyphen): traditional UNIX® shells + start from the login binary
  • +
  • Bash is started with the -l option
  • +
  • Bash is started with the --login option
  • +
+

Methods to test for login shell mode: + +

+ +

Related switches: + +

+
    +
  • --noprofile disables reading of all profile files
  • +
+

Interactive shell

+ +

When Bash starts as an interactive non-login shell, it reads and executes + commands from ~/.bashrc. This file should contain, for example, aliases, + since they need to be defined in every shell as they're not inherited from + the parent shell.

+

The feature to have a system-wide /etc/bash.bashrc or a similar system-wide + rc-file is specific to vendors and distributors that ship their own, patched variant of Bash. + The classic way to have a system-wide rc file is to source /etc/bashrc + from every user's ~/.bashrc.

+

Methods to test for interactive-shell mode: + +

+
    +
  • the special parameter $- contains the letter i (lowercase I)
  • +
+

Related switches: + +

+
    +
  • -i forces the interactive mode
  • +
  • --norc disables reading of the startup files (e.g. /etc/bash.bashrc if + supported) and ~/.bashrc
  • +
  • --rcfile defines another startup file (instead of /etc/bash.bashrc and + ~/.bashrc)
  • +
+

SH mode

+ +

When Bash starts in SH compatiblity mode, it tries to mimic the startup + behaviour of historical versions of sh as closely as possible, while conforming + to the POSIX® standard as well. The profile files read are /etc/profile + and ~/.profile, if it's a login shell.

+

If it's not a login shell, the environment variable ENV is + evaluated and the resulting filename is used as the name of the startup + file.

+

After the startup files are read, Bash enters the POSIX(r) compatiblity mode (for running, not for starting!).

+

Bash starts in sh compatiblity mode when: + +

+
    +
  • +

    the base filename in argv[0] is sh (

    +
    + +
    +

     NB: /bin/sh may be linked to /bin/bash, but that doesn't mean it + acts like /bin/bash 

    +
    + +
    +

    )

    +
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/Bash scripting/While loop.html b/demo/root/Trilium Demo/Tech/Linux/Bash scripting/While loop.html new file mode 100644 index 000000000..edf0b35c5 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/Bash scripting/While loop.html @@ -0,0 +1,32 @@ + + + + + + + + While loop + + + +
+

While loop

+ +
+

Documentation: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_02.html + +

#!/bin/bash
+# This script opens 4 terminal windows.
+
+i="0"
+while [ $i -lt 4 ]
+do
+    xterm &
+
+    i=$[$i+1]
+done
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/History.html b/demo/root/Trilium Demo/Tech/Linux/History.html new file mode 100644 index 000000000..0899d7210 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/History.html @@ -0,0 +1,30 @@ + + + + + + + + History + + + +
+

History

+ +
+

The history of Linux began in 1991 with the commencement + of a personal project by Finnish student + Linus Torvaldsto create a new free operating system kernel. Since + then, the resulting Linux kernel has + been marked by constant growth throughout its history. Since the initial + release of its source code in + 1991, it has grown from a small number of C files + under a license prohibiting commercial distribution to the 4.2.3 version + in 2015 with more than 18 million lines of source code under the GNU General Public License v2.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/Ubuntu.html b/demo/root/Trilium Demo/Tech/Linux/Ubuntu.html new file mode 100644 index 000000000..20f896ddd --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/Ubuntu.html @@ -0,0 +1,21 @@ + + + + + + + + Ubuntu + + + +
+

Ubuntu

+ +
+

Expand note on the left pane to see content.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Linux/Ubuntu/Unity shortcuts.html b/demo/root/Trilium Demo/Tech/Linux/Ubuntu/Unity shortcuts.html new file mode 100644 index 000000000..7c9d2c6ed --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Linux/Ubuntu/Unity shortcuts.html @@ -0,0 +1,38 @@ + + + + + + + + Unity shortcuts + + + +
+

Unity shortcuts

+ +
+
    +
  • Hold Super - Invoke the Launcher and display the shortcuts
  • +
  • Hold Super, then press 1 or 2 or 3 and so on until 0 to open or focus + an application. The number corresponds to the location of the icon on the + launcher from top to bottom. +
      +
    • Adding Shift will open a new instance of the application if it's already + open.
    • +
    • Holding the key is also useful when you want to get to the Launcher but + do not want to invoke the Dash.
    • +
    +
  • +
  • Super + T - Open the rubbish bin/trash can.
  • +
  • Alt + F1 - Put keyboard focus on the Launcher, use arrow keys to navigate, + Enter launches an application, Right arrow exposes the quicklists if an + application has them.
  • +
  • Ctrl + Alt + T - Launch a terminal window.
  • +
+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Intro.html b/demo/root/Trilium Demo/Tech/Node.js/Intro.html new file mode 100644 index 000000000..b66270fea --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Intro.html @@ -0,0 +1,29 @@ + + + + + + + + Intro + + + +
+

Intro

+ +
+

Node.js is an open-source, + cross-platform, JavaScript + runtime environmentthat executes JavaScript code outside of a browser. + Node.js lets developers use JavaScript to write command line tools and + for server-side scripting—running + scripts server-side to produce dynamic web page content + before the page is sent to the user's web browser. 

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Overview.html b/demo/root/Trilium Demo/Tech/Node.js/Overview.html new file mode 100644 index 000000000..76c20a2ee --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Overview.html @@ -0,0 +1,44 @@ + + + + + + + + Overview + + + +
+

Overview

+ +
+

Node.js allows the creation of Web servers and + networking tools using JavaScript and + a collection of "modules" that handle various core functionalities.[28] + [31][44] + [45][46] Modules + are provided for file system I/O, + networking (DNS, + HTTP, TCP, + TLS/SSL, or UDP), + binarydata (buffers), cryptography functions, + data streams, and other core functions.[31] + [45][47] Node.js's + modules use an API designed to reduce the complexity of writing server + applications.[31] + [45] +

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Overview/History.html b/demo/root/Trilium Demo/Tech/Node.js/Overview/History.html new file mode 100644 index 000000000..5c51f3b41 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Overview/History.html @@ -0,0 +1,29 @@ + + + + + + + + History + + + +
+

History

+ +
+

Node.js was written initially by Ryan Dahl in + 2009,[24] about + thirteen years after the introduction of the first server-side JavaScript + environment, Netscape's LiveWire Pro Web.[25] The + initial release supported only Linux and Mac OS X. Its development and + maintenance was led by Dahl and later sponsored by Joyent. + [26] +

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Overview/Industry support.html b/demo/root/Trilium Demo/Tech/Node.js/Overview/Industry support.html new file mode 100644 index 000000000..66c525f88 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Overview/Industry support.html @@ -0,0 +1,24 @@ + + + + + + + + Industry support + + + +
+

Industry support

+ +
+

There are thousands of open-source libraries for Node.js, most of them + hosted on the npm website. + The Node.js developer community has two main mailing lists and the IRC channel + #node.js on freenode

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Overview/Platform architecture.html b/demo/root/Trilium Demo/Tech/Node.js/Overview/Platform architecture.html new file mode 100644 index 000000000..4b51ab5be --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Overview/Platform architecture.html @@ -0,0 +1,29 @@ + + + + + + + + Platform architecture + + + +
+

Platform architecture

+ +
+

Node.js brings event-driven programming to + web servers, enabling development of fast web servers in JavaScript. + [31]Developers can create scalable servers without using threading, + by using a simplified model of event-driven programming that + uses callbacks to signal the completion of a task.[31] + +

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Node.js/Releases.html b/demo/root/Trilium Demo/Tech/Node.js/Releases.html new file mode 100644 index 000000000..bfac00b34 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Node.js/Releases.html @@ -0,0 +1,26 @@ + + + + + + + + Releases + + + +
+

Releases

+ +
+

New major releases of Node.js are cut from the GitHub master + branch every six months. Even-numbered versions are cut in April and odd-numbered + versions are cut in October. When a new odd version is released, the previous + even version undergoes transition to Long Term Support (LTS), which gives + that version 18 months of active support from the date it is designated + LTS.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Programming/Bash scripting.clone.html b/demo/root/Trilium Demo/Tech/Programming/Bash scripting.clone.html new file mode 100644 index 000000000..e82fc6a4b --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Programming/Bash scripting.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Bash scripting + + + +
+

Bash scripting

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Programming/Java.html b/demo/root/Trilium Demo/Tech/Programming/Java.html new file mode 100644 index 000000000..37c6df080 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Programming/Java.html @@ -0,0 +1,19 @@ + + + + + + + + Java + + + +
+

Java

+ +
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Tech/Security/Trusted timestamping.clone.html b/demo/root/Trilium Demo/Tech/Security/Trusted timestamping.clone.html new file mode 100644 index 000000000..960727f12 --- /dev/null +++ b/demo/root/Trilium Demo/Tech/Security/Trusted timestamping.clone.html @@ -0,0 +1,21 @@ + + + + + + + + Trusted timestamping + + + +
+

Trusted timestamping

+ +
+

This is a clone of a note. Go to its primary location.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Work/HR.html b/demo/root/Trilium Demo/Work/HR.html new file mode 100644 index 000000000..20a918aeb --- /dev/null +++ b/demo/root/Trilium Demo/Work/HR.html @@ -0,0 +1,21 @@ + + + + + + + + HR + + + +
+

HR

+ +
+

HR stuff

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Work/Processes.html b/demo/root/Trilium Demo/Work/Processes.html new file mode 100644 index 000000000..5bf20e3f4 --- /dev/null +++ b/demo/root/Trilium Demo/Work/Processes.html @@ -0,0 +1,21 @@ + + + + + + + + Processes + + + +
+

Processes

+ +
+

bla bla...

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo/Work/Projects.html b/demo/root/Trilium Demo/Work/Projects.html new file mode 100644 index 000000000..8a0d3a98b --- /dev/null +++ b/demo/root/Trilium Demo/Work/Projects.html @@ -0,0 +1,21 @@ + + + + + + + + Projects + + + +
+

Projects

+ +
+

Here I'd keep various notes related to my work's projects etc.

+
+
+ + + \ No newline at end of file diff --git a/demo/root/Trilium Demo_icon-color.svg b/demo/root/Trilium Demo_icon-color.svg new file mode 100644 index 000000000..173712891 --- /dev/null +++ b/demo/root/Trilium Demo_icon-color.svg @@ -0,0 +1,28 @@ + + + TriliumNext Notes + + + + + + + + + + + + + + + diff --git a/src/public/app/doc_notes/en/User Guide/style.css b/demo/style.css similarity index 96% rename from src/public/app/doc_notes/en/User Guide/style.css rename to demo/style.css index 4243d146a..0ebbae93d 100644 --- a/src/public/app/doc_notes/en/User Guide/style.css +++ b/demo/style.css @@ -17,6 +17,32 @@ margin: 0 !important; } +.admonition { + --accent-color: var(--card-border-color); + border: 1px solid var(--accent-color); + box-shadow: var(--card-box-shadow); + background: var(--card-background-color); + border-radius: 0.5em; + padding: 1em; + margin: 1.25em 0; + position: relative; + overflow: hidden; +} + +.admonition p:last-child { + margin-bottom: 0; +} + +.admonition p, h2 { + margin-top: 0; +} + +.admonition.note { --accent-color: #69c7ff; } +.admonition.tip { --accent-color: #40c025; } +.admonition.important { --accent-color: #9839f7; } +.admonition.caution { --accent-color: #ff2e2e; } +.admonition.warning { --accent-color: #e2aa03; } + /* * CKEditor 5 (v41.0.0) content styles. * Generated on Fri, 26 Jan 2024 10:23:49 GMT. diff --git a/docs/1l1f6WZbaBEZ.html b/docs/1l1f6WZbaBEZ.html deleted file mode 100644 index d2e8c3924..000000000 --- a/docs/1l1f6WZbaBEZ.html +++ /dev/null @@ -1,831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Branch prefixes - - -
-
- - - - -

Branch prefixes

- - - - - - -

This note has no content.

- -
- - - - - - -
- - diff --git a/docs/1pOWnHdGAuWR.html b/docs/1pOWnHdGAuWR.html deleted file mode 100644 index 43efb41f2..000000000 --- a/docs/1pOWnHdGAuWR.html +++ /dev/null @@ -1,866 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Widgets - - -
-
- - - - -

Widgets

- - - - -
-

To create a basic widget, simply create a code note with type “JS frontend”. Add the #widget label in order for it to be loaded at startup.

const template = `<div id="my-widget"><button>Click Me!</button></div>`;
-
-class MyWidget extends api.BasicWidget {
-    get position() { return 1; }
-    get parentWidget() { return "left-pane" }
-    
-    doRender() {
-        this.$widget = $(template);
-        return this.$widget;
-    }
-}
-
-module.exports = new MyWidget();

parentWidget() can be given the following values:

  • left-pane - This renders the widget on the left side of the screen where the note tree lives.
  • center-pane - This renders the widget in the center of the layout in the same location that notes and splits appear.
  • note-detail-pane - This renders the widget with the note in the center pane. This means it can appear multiple times with splits.
  • right-pane - This renders the widget to the right of any opened notes.

Reference:

-
- - - - - -
- - - - - - -
- - diff --git a/docs/3eTu21fjtZkj.html b/docs/3eTu21fjtZkj.html deleted file mode 100644 index b71857435..000000000 --- a/docs/3eTu21fjtZkj.html +++ /dev/null @@ -1,842 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Scripting - - -
-
- - - - -

Scripting

- - - - - - - - -
- - - - - - -
- - diff --git a/docs/3jc1nUXyteo0.html b/docs/3jc1nUXyteo0.html deleted file mode 100644 index 39ee0ed82..000000000 --- a/docs/3jc1nUXyteo0.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Themes - - -
-
- - - - -

Themes

- - - - -
-

Server-side

  • There are two themes embedded in the application:
    • light, located in src\public\stylesheets\theme-light.css
    • dark, located in src\public\stylesheets\theme-dark.css
  • The default theme is set only once, when the database is created and is managed by options_init#initNotSyncedOptions.
    • On Electron, the choice between light and dark is done based on the OS preference.
    • Otherwise, the theme is always dark.
  • The theme is served via src\routes\index.ts, in the getThemeCssUrl method.

Client-side

  • The two predefined themes are hard-coded in the client in src\public\app\widgets\type_widgets\options\appearance\theme.js.
  • The user-defined themes are obtained via a call to the server: options/user-themes.
  • The theme retrieval is done via a request
-
- - - -
- - - - - - -
- - diff --git a/docs/4FXLAtcPhZFo.html b/docs/4FXLAtcPhZFo.html deleted file mode 100644 index e32f4377c..000000000 --- a/docs/4FXLAtcPhZFo.html +++ /dev/null @@ -1,838 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - CSS - - -
-
- - - - -

CSS

- - - - -
-

In doRender():

this.cssBlock(`#my-widget {
-	position: absolute;
-    bottom: 40px;
-    left: 60px;
-    z-index: 1;
-}`)

Reference: https://trilium.rocks/X7pxYpiu0lgU 

-
- - - -
- - - - - - -
- - diff --git a/docs/4yYHqKbLovVX.html b/docs/4yYHqKbLovVX.html deleted file mode 100644 index fca1bfba9..000000000 --- a/docs/4yYHqKbLovVX.html +++ /dev/null @@ -1,857 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Developer's Guide - - -
-
- - -

Developer's Guide

- - - - - - - - -
- - - - - - -
- - diff --git a/docs/6x42mhlfLo0o.html b/docs/6x42mhlfLo0o.html deleted file mode 100644 index 3149fba73..000000000 --- a/docs/6x42mhlfLo0o.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - blobs - - -
-
- - - - -

blobs

- - - - -
-
Column NameData TypeNullityDefault valueDescription
blobIdTextNon-null The unique ID of the blob (e.g. XXbfAJXqWrYnSXcelLFA).
contentTextNullablenull

The content of the blob, can be either:

  • text (for plain text notes or HTML notes).
  • binary (for images and other types of attachments)
dateModifiedTextNon-null Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
-
- - - -
- - - - - - -
- - diff --git a/docs/8jWguCtuKsAt.html b/docs/8jWguCtuKsAt.html deleted file mode 100644 index 6d845c16e..000000000 --- a/docs/8jWguCtuKsAt.html +++ /dev/null @@ -1,855 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Right pane widget - - -
-
- - - - -

Right pane widget

- - - - -
-
  • doRender must not be overridden, instead doRenderBody() has to be overridden.
  • parentWidget() must be set to “rightPane”.
  • widgetTitle() getter can optionally be overriden, otherwise the widget will be displayed as “Untitled widget”.
const template = `<div>Hi</div>`;
-
-class ToDoListWidget extends api.RightPanelWidget {
-    
-    get widgetTitle() {
-        return "Title goes here";
-    }
-        
-    get parentWidget() { return "right-pane" }
-    
-    doRenderBody() {
-        this.$body.empty().append($(template));
-    }   
-    
-    async refreshWithNote(note) {
-        this.toggleInt(false);                
-        this.triggerCommand("reEvaluateRightPaneVisibility");
-        this.toggleInt(true);
-        this.triggerCommand("reEvaluateRightPaneVisibility");
-    }
-}
-
-module.exports = new ToDoListWidget();

The implementation is in src/public/app/widgets/right_panel_widget.js.

-
- - - -
- - - - - - -
- - diff --git a/docs/B8hxg4e66cVL.html b/docs/B8hxg4e66cVL.html deleted file mode 100644 index 2d2e330df..000000000 --- a/docs/B8hxg4e66cVL.html +++ /dev/null @@ -1,907 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Development and architecture - - -
-
- - - - -

Development and architecture

- - - - - - - - -
- - - - - - -
- - diff --git a/docs/BhE2WFknKKHG.html b/docs/BhE2WFknKKHG.html deleted file mode 100644 index f34dcf1f8..000000000 --- a/docs/BhE2WFknKKHG.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Main - - -
-
- - - - -

Main

- - - - -
-

The main workflow of the CI:

  • Builds the Docker image and publishes in the GitHub Docker registry.
  • Builds using a portion of the delivery script artifacts for the following platforms:
    • Windows x86_64 as .zip file
    • Windows x86_64 installer (using Squirrel)
    • macOS x86_64 and aarch64.
    • Linux x86_64
    • Linux server x86_64.

The main workflow of the CI runs on develop branches as well as any branch that starts with feature/update_.

Downloading the artifacts from the main branch

Simply go to the develop branch on GitHub and look at the commit bar:

Press the green checkmark (or red cross if something went bad). Then look at the list of jobs and their status:

Then look for any of the entires that starts with “Main” and press the “Details” link next to it. It doesn't really matter which platform you'll choose as the artifacts are available on the same page.

 

-
- - - -
- - - - - - -
- - diff --git a/docs/C09Dou56ffMe.html b/docs/C09Dou56ffMe.html deleted file mode 100644 index fa3c94613..000000000 --- a/docs/C09Dou56ffMe.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Having a simpler packaging system - - -
-
- - - - -

Having a simpler packaging system

- - - - -
-

The current build scripts are a bit complicated and maintaining them is not easy.

Electron Forge seems more mature and has a boatload of features, including Flatpak, snaps, Windows installers & more.

Have a look also at the Plugins section since there are quite a few interesting things there as well.

Afterwards consider running a new round of Reducing binary size, especially taking into consideration removing of the unnecessary locales.

-
- - - -
- - - - - - -
- - diff --git a/docs/Developer Guide/!!!meta.json b/docs/Developer Guide/!!!meta.json new file mode 100644 index 000000000..af6ae2063 --- /dev/null +++ b/docs/Developer Guide/!!!meta.json @@ -0,0 +1,2636 @@ +{ + "formatVersion": 2, + "appVersion": "0.92.7", + "files": [ + { + "isClone": false, + "noteId": "jdjRLhLV3TtI", + "notePath": [ + "jdjRLhLV3TtI" + ], + "title": "Developer Guide", + "notePosition": 1, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Developer Guide", + "children": [ + { + "isClone": false, + "noteId": "Kqzuchw6MmPm", + "notePath": [ + "jdjRLhLV3TtI", + "Kqzuchw6MmPm" + ], + "title": "Dependency Management", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-package", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "attachments": [], + "dirFileName": "Dependency Management", + "children": [ + { + "isClone": false, + "noteId": "YH5JPX12BYFk", + "notePath": [ + "jdjRLhLV3TtI", + "Kqzuchw6MmPm", + "YH5JPX12BYFk" + ], + "title": "Adding a new client library", + "notePosition": 0, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Adding a new client library.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "C5CNXGgti17i", + "notePath": [ + "jdjRLhLV3TtI", + "Kqzuchw6MmPm", + "C5CNXGgti17i" + ], + "title": "Having a simpler packaging system", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Having a simpler packaging sys.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "sUqOpnrQyEC7", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7" + ], + "title": "Building and deployment", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Building and deployment", + "children": [ + { + "isClone": false, + "noteId": "zdQzavvHDl1k", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "zdQzavvHDl1k" + ], + "title": "Documentation", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Documentation.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "czgXkoEYwclZ", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "czgXkoEYwclZ" + ], + "title": "Running a development build", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Running a development build.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "PXzm2t3sCdsP", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "PXzm2t3sCdsP" + ], + "title": "Build deliveries locally", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Build deliveries locally.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "rLWcPPQi7Eso", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "rLWcPPQi7Eso" + ], + "title": "Releasing a version", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "IxkDdjTogO18", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Releasing a version.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "oqg9OpK8xfcm", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "oqg9OpK8xfcm" + ], + "title": "CI", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "CI", + "children": [ + { + "isClone": false, + "noteId": "IxkDdjTogO18", + "notePath": [ + "jdjRLhLV3TtI", + "sUqOpnrQyEC7", + "oqg9OpK8xfcm", + "IxkDdjTogO18" + ], + "title": "Main", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "PXzm2t3sCdsP", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Main.md", + "attachments": [ + { + "attachmentId": "c3aGEk60ZR2Q", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Main_image.png" + }, + { + "attachmentId": "q9OGTAguCyWf", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Main_image.png" + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "6BWwXzPCph4G", + "notePath": [ + "jdjRLhLV3TtI", + "6BWwXzPCph4G" + ], + "title": "Project maintenance", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Project maintenance", + "children": [ + { + "isClone": false, + "noteId": "fa6hAJ9Ith3A", + "notePath": [ + "jdjRLhLV3TtI", + "6BWwXzPCph4G", + "fa6hAJ9Ith3A" + ], + "title": "Updating dependencies", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "Xfi1ScuBTKJf", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Updating dependencies.md", + "attachments": [], + "dirFileName": "Updating dependencies", + "children": [ + { + "isClone": false, + "noteId": "Xfi1ScuBTKJf", + "notePath": [ + "jdjRLhLV3TtI", + "6BWwXzPCph4G", + "fa6hAJ9Ith3A", + "Xfi1ScuBTKJf" + ], + "title": "bettersqlite binaries", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "PXzm2t3sCdsP", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "bettersqlite binaries.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "v5zBZNLR358v", + "notePath": [ + "jdjRLhLV3TtI", + "6BWwXzPCph4G", + "fa6hAJ9Ith3A", + "v5zBZNLR358v" + ], + "title": "Node.js, Electron and `better-sqlite3`", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Node.js, Electron and `better-.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "aPQ1fSuoBCTC", + "notePath": [ + "jdjRLhLV3TtI", + "6BWwXzPCph4G", + "fa6hAJ9Ith3A", + "aPQ1fSuoBCTC" + ], + "title": "Testing compatibility", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Testing compatibility.md", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "wbVIolLKDhe2", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2" + ], + "title": "Development and architecture", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Development and architecture", + "children": [ + { + "isClone": false, + "noteId": "TLXJwBDo8Rdv", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "TLXJwBDo8Rdv" + ], + "title": "Internationalisation / Translations", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "ky5zpmxXZhhr", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-globe", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Internationalisation Translat.md", + "attachments": [], + "dirFileName": "Internationalisation Translations", + "children": [ + { + "isClone": false, + "noteId": "ky5zpmxXZhhr", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "TLXJwBDo8Rdv", + "ky5zpmxXZhhr" + ], + "title": "Guidelines", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Guidelines.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VTebBD3jZjdp", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "TLXJwBDo8Rdv", + "VTebBD3jZjdp" + ], + "title": "i18n-ally", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "ky5zpmxXZhhr", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "i18n-ally.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "KhDvxPlQQybs", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "TLXJwBDo8Rdv", + "KhDvxPlQQybs" + ], + "title": "Server translations", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Server translations.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "fI16A7NrT713", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "fI16A7NrT713" + ], + "title": "Live reload", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Live reload.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "7BCukQTCm7fv", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "7BCukQTCm7fv" + ], + "title": "Themes", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Themes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "n9wYW9nUTynV", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "n9wYW9nUTynV" + ], + "title": "Synchronisation", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Synchronisation", + "children": [ + { + "isClone": false, + "noteId": "wA6tm9xcWWaB", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "n9wYW9nUTynV", + "wA6tm9xcWWaB" + ], + "title": "Content hashing", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Content hashing.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "aGlhNBEA9wwo", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "aGlhNBEA9wwo" + ], + "title": "Build information", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "PXzm2t3sCdsP", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Build information.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "vNMojjUN76jc", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc" + ], + "title": "Database", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Database", + "children": [ + { + "isClone": false, + "noteId": "e6GnYOXeIWjg", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "e6GnYOXeIWjg" + ], + "title": "attachments", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "UvXpeSqfYc6d", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "VIcWnKGs0sMh", + "isInheritable": false, + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "tM3rIZQzlum4", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "attachments.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ciL84vNBNi9y", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "ciL84vNBNi9y" + ], + "title": "attributes", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "r11Bh3uxFGRj", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "tM3rIZQzlum4", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "attributes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VIcWnKGs0sMh", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "VIcWnKGs0sMh" + ], + "title": "blobs", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "blobs.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "GskLPkgY5n6E", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "GskLPkgY5n6E" + ], + "title": "branches", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "h8AsuFjSD4fB", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "tM3rIZQzlum4", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "branches.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ohhExR078MPU", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "ohhExR078MPU" + ], + "title": "entity_changes", + "notePosition": 51, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "entity_changes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "bRqbIg633nCs", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "bRqbIg633nCs" + ], + "title": "etapi_tokens", + "notePosition": 52, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "tM3rIZQzlum4", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "etapi_tokens.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "DSkl8C325tEC", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "DSkl8C325tEC" + ], + "title": "notes", + "notePosition": 53, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "VIcWnKGs0sMh", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "UvXpeSqfYc6d", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "tM3rIZQzlum4", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "notes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "4oeftEmy77Bt", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "4oeftEmy77Bt" + ], + "title": "options", + "notePosition": 54, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "options.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VyFirdgAOoh5", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "VyFirdgAOoh5" + ], + "title": "recent_notes", + "notePosition": 55, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "recent_notes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "s7ZBiaJVNumK", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "vNMojjUN76jc", + "s7ZBiaJVNumK" + ], + "title": "revisions", + "notePosition": 56, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "VIcWnKGs0sMh", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "UvXpeSqfYc6d", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-table", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "revisions.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "UvXpeSqfYc6d", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "UvXpeSqfYc6d" + ], + "title": "Protected entities", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "e6GnYOXeIWjg", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "s7ZBiaJVNumK", + "isInheritable": false, + "position": 30 + } + ], + "format": "markdown", + "dataFileName": "Protected entities.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "tM3rIZQzlum4", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "tM3rIZQzlum4" + ], + "title": "Deleted notes", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Deleted notes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "r11Bh3uxFGRj", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "r11Bh3uxFGRj" + ], + "title": "Special notes", + "notePosition": 100, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Special notes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "h8AsuFjSD4fB", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "h8AsuFjSD4fB" + ], + "title": "Branch prefixes", + "notePosition": 110, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Branch prefixes.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "qjQNyaYXSNWu", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "qjQNyaYXSNWu" + ], + "title": "Revisions", + "notePosition": 120, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Revisions.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "3mz4ZYhi9Cy8", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "3mz4ZYhi9Cy8" + ], + "title": "Backlinks", + "notePosition": 130, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Backlinks.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ItZRqNGeGSU0", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "ItZRqNGeGSU0" + ], + "title": "Note types", + "notePosition": 140, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "DSkl8C325tEC", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Note types.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "64ZTlUPgEPtW", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "64ZTlUPgEPtW" + ], + "title": "Safe mode", + "notePosition": 150, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Safe mode.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "m2W35hwSDUeh", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "m2W35hwSDUeh" + ], + "title": "Icons", + "notePosition": 160, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "PXzm2t3sCdsP", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "oLhKpfi2kGON", + "isInheritable": false, + "position": 20 + } + ], + "format": "markdown", + "dataFileName": "Icons.md", + "attachments": [], + "dirFileName": "Icons", + "children": [ + { + "isClone": false, + "noteId": "rUkJPiX0sJSk", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "m2W35hwSDUeh", + "rUkJPiX0sJSk" + ], + "title": "Removed icons", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Removed icons.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "GzrBXey1UTUW", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "m2W35hwSDUeh", + "GzrBXey1UTUW" + ], + "title": "Icons on Mac", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Icons on Mac.md", + "attachments": [ + { + "attachmentId": "gMQM37l1tgDc", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Icons on Mac_image.png" + }, + { + "attachmentId": "KBbeDSs1hueu", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Icons on Mac_image.png" + } + ], + "dirFileName": "Icons on Mac", + "children": [ + { + "isClone": false, + "noteId": "0btkkp7llQdO", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "m2W35hwSDUeh", + "GzrBXey1UTUW", + "0btkkp7llQdO" + ], + "title": "Slightly blurry icon on Mac", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Slightly blurry icon on Mac.md", + "attachments": [ + { + "attachmentId": "6USSTMu15E6N", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Slightly blurry icon on Ma.png" + }, + { + "attachmentId": "KEkBj1bOyfQ5", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Slightly blurry icon on Ma.png" + }, + { + "attachmentId": "XaG2VbiqKYtR", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_Slightly blurry icon on Ma.png" + } + ] + }, + { + "isClone": false, + "noteId": "8zAJ5J8SFEp8", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "m2W35hwSDUeh", + "GzrBXey1UTUW", + "8zAJ5J8SFEp8" + ], + "title": "Adaptive icon", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Adaptive icon.md", + "attachments": [ + { + "attachmentId": "38usIA7IJTpY", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Adaptive icon_image.png" + }, + { + "attachmentId": "4eQa9Eqkuekv", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Adaptive icon_image.png" + }, + { + "attachmentId": "gM1I22x3bYDv", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_Adaptive icon_image.png" + }, + { + "attachmentId": "Im2xMquSwizu", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "3_Adaptive icon_image.png" + }, + { + "attachmentId": "l2xu0BHxfPfq", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "4_Adaptive icon_image.png" + }, + { + "attachmentId": "wM6YPPsas7tA", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "5_Adaptive icon_image.png" + }, + { + "attachmentId": "zPLIBv0Xvgwm", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "6_Adaptive icon_image.png" + } + ] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "oLhKpfi2kGON", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "oLhKpfi2kGON" + ], + "title": "Demo document", + "notePosition": 170, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Demo document.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "mXFYlhuEr1mZ", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "mXFYlhuEr1mZ" + ], + "title": "Docker", + "notePosition": 180, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Docker.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "6dC7ha5vjqqS", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS" + ], + "title": "Options", + "notePosition": 190, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Options.md", + "attachments": [], + "dirFileName": "Options", + "children": [ + { + "isClone": false, + "noteId": "Qk5Q0Xty3ITv", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS", + "Qk5Q0Xty3ITv" + ], + "title": "Check box option", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Check box option.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VnqYvYEuMMvb", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS", + "VnqYvYEuMMvb" + ], + "title": "Trigger UI refresh", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Trigger UI refresh.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "3TbiQZODAp6y", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS", + "3TbiQZODAp6y" + ], + "title": "Displaying the option in settings", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Displaying the option in setti.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "1TVWljchsc0t", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS", + "1TVWljchsc0t" + ], + "title": "Refresh widget with option change", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Refresh widget with option cha.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "NcHcYOEn4ol5", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "6dC7ha5vjqqS", + "NcHcYOEn4ol5" + ], + "title": "Creating a new option", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Creating a new option.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "AdNRgGrYeTCy", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy" + ], + "title": "Adding a new note type", + "notePosition": 210, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Adding a new note type", + "children": [ + { + "isClone": false, + "noteId": "UFtOg3sLumZM", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "UFtOg3sLumZM" + ], + "title": "First steps", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "lgFwLJT72mdf", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "oLhKpfi2kGON", + "isInheritable": false, + "position": 20 + } + ], + "format": "markdown", + "dataFileName": "First steps.md", + "attachments": [], + "dirFileName": "First steps", + "children": [ + { + "isClone": false, + "noteId": "aSO1wqK7L1ma", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "UFtOg3sLumZM", + "aSO1wqK7L1ma" + ], + "title": "mind_map.js", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "code", + "mime": "application/javascript;env=frontend", + "attributes": [], + "dataFileName": "mind_map.js", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "lgFwLJT72mdf", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "lgFwLJT72mdf" + ], + "title": "Note type checklist", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "UFtOg3sLumZM", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "OGYpAbrmEXbX", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-list-check", + "isInheritable": false, + "position": 40 + } + ], + "format": "markdown", + "dataFileName": "Note type checklist.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "PoxUNujeKJ7T", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "PoxUNujeKJ7T" + ], + "title": "Saving data via spaced update", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "OGYpAbrmEXbX", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Saving data via spaced update.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "xYVE7qA3EBwb", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "xYVE7qA3EBwb" + ], + "title": "Loading data", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "PoxUNujeKJ7T", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Loading data.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "OGYpAbrmEXbX", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "OGYpAbrmEXbX" + ], + "title": "SVG rendering", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "SVG rendering.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Q8hgB8EEen80", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "Q8hgB8EEen80" + ], + "title": "Copy image reference to the clipboard", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Copy image reference to the cl.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "AwWq8bJRl6XD", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "AdNRgGrYeTCy", + "AwWq8bJRl6XD" + ], + "title": "Export diagram as SVG", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "OGYpAbrmEXbX", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Export diagram as SVG.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "W0msUwLxm40d", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "W0msUwLxm40d" + ], + "title": "Printing", + "notePosition": 220, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Printing.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Usiyzn9C4WFv", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "Usiyzn9C4WFv" + ], + "title": "Launchers", + "notePosition": 230, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Launchers.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "k7RavjuXQt8z", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "k7RavjuXQt8z" + ], + "title": "Syntax highlighting", + "notePosition": 240, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Syntax highlighting.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "UzRirf46Xi46", + "notePath": [ + "jdjRLhLV3TtI", + "wbVIolLKDhe2", + "UzRirf46Xi46" + ], + "title": "Hidden notes", + "notePosition": 250, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Hidden notes.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "re0QTuqiYnVb", + "notePath": [ + "jdjRLhLV3TtI", + "re0QTuqiYnVb" + ], + "title": "Scripting", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Scripting", + "children": [ + { + "isClone": false, + "noteId": "gz6zq5rlHqMa", + "notePath": [ + "jdjRLhLV3TtI", + "re0QTuqiYnVb", + "gz6zq5rlHqMa" + ], + "title": "Widgets", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Widgets.md", + "attachments": [], + "dirFileName": "Widgets", + "children": [ + { + "isClone": false, + "noteId": "M8IppdwVHSjG", + "notePath": [ + "jdjRLhLV3TtI", + "re0QTuqiYnVb", + "gz6zq5rlHqMa", + "M8IppdwVHSjG" + ], + "title": "Right pane widget", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Right pane widget.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VqGQnnPGnqAU", + "notePath": [ + "jdjRLhLV3TtI", + "re0QTuqiYnVb", + "gz6zq5rlHqMa", + "VqGQnnPGnqAU" + ], + "title": "CSS", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "CSS.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "fZ2IGYFXjkEy", + "notePath": [ + "jdjRLhLV3TtI", + "re0QTuqiYnVb", + "fZ2IGYFXjkEy" + ], + "title": "Server-side imports", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Server-side imports.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "Sow7ThJozkzJ", + "notePath": [ + "jdjRLhLV3TtI", + "Sow7ThJozkzJ" + ], + "title": "Documentation", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Documentation.md", + "attachments": [ + { + "attachmentId": "2bUrJyt2yfsd", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Documentation_image.png" + } + ], + "dirFileName": "Documentation", + "children": [ + { + "isClone": false, + "noteId": "LjqM0VUL1CrU", + "notePath": [ + "jdjRLhLV3TtI", + "Sow7ThJozkzJ", + "LjqM0VUL1CrU" + ], + "title": "Documentation references in the application", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Documentation references in th.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "dtKC3FmoWOrv", + "notePath": [ + "jdjRLhLV3TtI", + "dtKC3FmoWOrv" + ], + "title": "Testing", + "notePosition": 110, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Testing.md", + "attachments": [], + "dirFileName": "Testing", + "children": [ + { + "isClone": false, + "noteId": "C5MUQczZ5R9N", + "notePath": [ + "jdjRLhLV3TtI", + "dtKC3FmoWOrv", + "C5MUQczZ5R9N" + ], + "title": "Integration testing", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Integration testing", + "children": [ + { + "isClone": false, + "noteId": "pH4RsxqifVpK", + "notePath": [ + "jdjRLhLV3TtI", + "dtKC3FmoWOrv", + "C5MUQczZ5R9N", + "pH4RsxqifVpK" + ], + "title": "Setting up authentication", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Setting up authentication.md", + "attachments": [ + { + "attachmentId": "aWFXFuXNon7J", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Setting up authentication_.png" + }, + { + "attachmentId": "JRbtB4byzewo", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Setting up authentication_.png" + } + ] + }, + { + "isClone": false, + "noteId": "bIfKwfCnqpeI", + "notePath": [ + "jdjRLhLV3TtI", + "dtKC3FmoWOrv", + "C5MUQczZ5R9N", + "bIfKwfCnqpeI" + ], + "title": "Test database", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Test database.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "w6gMvKh0UAVT", + "notePath": [ + "jdjRLhLV3TtI", + "dtKC3FmoWOrv", + "C5MUQczZ5R9N", + "w6gMvKh0UAVT" + ], + "title": "Running tests", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Running tests.md", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "dHfw0XZE515z", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z" + ], + "title": "Sub-projects", + "notePosition": 120, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Sub-projects", + "children": [ + { + "isClone": false, + "noteId": "JkTy2zz8Zbyq", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq" + ], + "title": "CKEditor", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "CKEditor", + "children": [ + { + "isClone": false, + "noteId": "5yWZVlKPjLCC", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq", + "5yWZVlKPjLCC" + ], + "title": "Environment setup", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "CaInsmrlZhR6", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Environment setup.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "CaInsmrlZhR6", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq", + "CaInsmrlZhR6" + ], + "title": "Building the editor", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "5yWZVlKPjLCC", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Building the editor.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "5gBYmUqiupBl", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq", + "5gBYmUqiupBl" + ], + "title": "Differences from upstream", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "lY19SLxUMj3J", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "fullContentWidth", + "value": "", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Differences from upstream.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "Q9FyKVERd1Lb", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq", + "Q9FyKVERd1Lb" + ], + "title": "Updating to a newer version of CKEditor", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "5yWZVlKPjLCC", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "CaInsmrlZhR6", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "yAFSS6awVbaZ", + "isInheritable": false, + "position": 30 + } + ], + "format": "markdown", + "dataFileName": "Updating to a newer version of.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "yAFSS6awVbaZ", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "JkTy2zz8Zbyq", + "yAFSS6awVbaZ" + ], + "title": "Versions and external plugins", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "lY19SLxUMj3J", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Versions and external plugins.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "lY19SLxUMj3J", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "lY19SLxUMj3J" + ], + "title": "ckeditor5-math", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "JkTy2zz8Zbyq", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "UMN2ABrBU5D7", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "CaInsmrlZhR6", + "isInheritable": false, + "position": 30 + } + ], + "format": "markdown", + "dataFileName": "ckeditor5-math.md", + "attachments": [ + { + "attachmentId": "UlSZYhYX8Kfj", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "ckeditor5-math_image.png" + } + ], + "dirFileName": "ckeditor5-math", + "children": [ + { + "isClone": false, + "noteId": "vpbbBaypScLb", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "lY19SLxUMj3J", + "vpbbBaypScLb" + ], + "title": "Updating with upstream", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "UMN2ABrBU5D7", + "isInheritable": false, + "position": 10 + } + ], + "format": "markdown", + "dataFileName": "Updating with upstream.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "UMN2ABrBU5D7", + "notePath": [ + "jdjRLhLV3TtI", + "dHfw0XZE515z", + "lY19SLxUMj3J", + "UMN2ABrBU5D7" + ], + "title": "Release management & continuous integration", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Release management & continuou.md", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "ibAPHul7Efvr", + "notePath": [ + "jdjRLhLV3TtI", + "ibAPHul7Efvr" + ], + "title": "Notes for old development", + "notePosition": 130, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Notes for old development", + "children": [ + { + "isClone": true, + "noteId": "PXzm2t3sCdsP", + "notePath": [ + "jdjRLhLV3TtI", + "ibAPHul7Efvr", + "PXzm2t3sCdsP" + ], + "title": "Build deliveries locally", + "prefix": null, + "dataFileName": "Build deliveries locally.clone.md", + "type": "text", + "format": "markdown", + "isExpanded": false + }, + { + "isClone": true, + "noteId": "rLWcPPQi7Eso", + "notePath": [ + "jdjRLhLV3TtI", + "ibAPHul7Efvr", + "rLWcPPQi7Eso" + ], + "title": "Releasing a version", + "prefix": null, + "dataFileName": "Releasing a version.clone.md", + "type": "text", + "format": "markdown", + "isExpanded": false + }, + { + "isClone": true, + "noteId": "czgXkoEYwclZ", + "notePath": [ + "jdjRLhLV3TtI", + "ibAPHul7Efvr", + "czgXkoEYwclZ" + ], + "title": "Running a development build", + "prefix": null, + "dataFileName": "Running a development build.clone.md", + "type": "text", + "format": "markdown", + "isExpanded": false + } + ] + }, + { + "isClone": false, + "noteId": "QRLbiDXNxoWN", + "notePath": [ + "jdjRLhLV3TtI", + "QRLbiDXNxoWN" + ], + "title": "Troubleshooting", + "notePosition": 140, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Troubleshooting", + "children": [ + { + "isClone": false, + "noteId": "QUb0fRhbpT8E", + "notePath": [ + "jdjRLhLV3TtI", + "QRLbiDXNxoWN", + "QUb0fRhbpT8E" + ], + "title": "Error [TransformError]: The package \"@esbuild/linux-x64\" could not be found, and is needed by esbuild.", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Error [TransformError] The pac.md", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "x6lgrdztQwVB", + "notePath": [ + "jdjRLhLV3TtI", + "x6lgrdztQwVB" + ], + "title": "Installation", + "notePosition": 150, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "attachments": [], + "dirFileName": "Installation", + "children": [ + { + "isClone": false, + "noteId": "bOjeTrUViwLw", + "notePath": [ + "jdjRLhLV3TtI", + "x6lgrdztQwVB", + "bOjeTrUViwLw" + ], + "title": "Download latest nightly and install it", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "Download latest nightly and in.md", + "attachments": [] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Build deliveries locally.md b/docs/Developer Guide/Developer Guide/Building and deployment/Build deliveries locally.md new file mode 100644 index 000000000..44c92587a --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Building and deployment/Build deliveries locally.md @@ -0,0 +1,26 @@ +# Build deliveries locally +In the project root: + +
PlatformArchitectureApplicationBuild command
macOSx86_64Desktop / Electron app./bin/build-mac-x64.sh
ARM 64Desktop / Electron app./bin/build-mac-arm64.sh
Linuxx86_64Desktop / Electron app./bin/build-linux-x64.sh
Server./bin/build-server.sh
Windowsx86_64Desktop / Electron app./bin/build-win-x64.sh
+ +Under NixOS the following `nix-shell` is needed: + +``` +nix-shell -p jq +``` + +For Linux builds: + +``` +nix-shell -p jq fakeroot dpkg +``` + +The resulting build will be in the `dist` directory under the project root. + +### Testing the Linux builds under NixOS + +
Desktop clientServer
$ NIXPKGS_ALLOW_UNFREE=1 nix-shell -p steam-run
+[nix-shell] cd dist/trilium-linux-x64
+[nix-shell] steam-run ./trilium
$ NIXPKGS_ALLOW_UNFREE=1 nix-shell -p steam-run
+[nix-shell] cd dist/trilium-linux-x64-server
+[nix-shell] steam-run ./trilium.sh
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png b/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png new file mode 100644 index 000000000..5eacee569 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md new file mode 100644 index 000000000..667969496 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md @@ -0,0 +1,24 @@ +# Main +The main workflow of the CI: + +* Builds the Docker image and publishes in the GitHub Docker registry. +* Builds using a portion of the [delivery script](../Build%20deliveries%20locally.md) artifacts for the following platforms: + * Windows `x86_64` as .zip file + * Windows `x86_64` installer (using Squirrel) + * macOS `x86_64` and `aarch64`. + * Linux `x86_64` + * Linux server `x86_64`. + +The main workflow of the CI runs on `develop` branches as well as any branch that starts with `feature/update_`. + +## Downloading the artifacts from the main branch + +Simply go to the [`develop` branch on GitHub](https://github.com/TriliumNext/Notes) and look at the commit bar: + +
+ +Press the green checkmark (or red cross if something went bad). Then look at the list of jobs and their status: + +
+ +Then look for any of the entires that starts with “Main” and press the “Details” link next to it. It doesn't really matter which platform you'll choose as the artifacts are available on the same page. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png new file mode 100644 index 000000000..2f13f1647 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md b/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md new file mode 100644 index 000000000..13c941258 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md @@ -0,0 +1,34 @@ +# Documentation +Development notes are published on [triliumnext.github.io/Notes](https://triliumnext.github.io/Notes) by the CI using GitHub Pages. + +The GitHub Pages deployment works by taking the files from the Notes repository, in the `docs` directory. + +## How it works + +There is a script that uses `wget` to download all the files from a share, that means: + +1. You must have a local instance of Trilium Notes server. +2. You must have the documentation imported, up to date and shared. + +Note that currently the documentation source file is not distributed (the note export), until a way is found to automate this process. Contact `eliandoran` should you require to obtain a copy of the documentation. + +## Setting up `.env` file + +Go to `bin/docs` and copy `.env.example` to `.env` and edit it: + +1. Change the `SHARE_PROTOCOL` to either `http` or `https` depending on your setup. +2. Change `SHARE_HOST` to match the domain name or the URL to the host (without the protocol or any slashes). + +Generally `ROOT_NOTE_ID` should not be changed since the note ID must match if the files were imported correctly. + +## Triggering a build + +Run: + +``` +./bin/docs/prepare.sh +``` + +This will attempt to download all the notes from the share URL and put them in `docs`, rewritten for GitHub Pages. + +Commit the results and follow the normal development process to push them. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md b/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md new file mode 100644 index 000000000..5651afc4d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md @@ -0,0 +1,22 @@ +# Releasing a version +On NixOS: + +``` +nix-shell -p dpkg fakeroot jq nodejs_20 +``` + +Then simply run from project root: + +``` +./bin/release.sh 1.2.3 +``` + +where `1.2.3` is the desired release version. + +If a version ends with `-beta`, it will automatically be marked as pre-release in GitHub. + +This will automatically generate a release in GitHub if everything goes according to plan. + +Note that the Windows installer is not automatically uploaded yet, it has to be taken from the [main workflow of the CI from the `develop` branch](CI/Main.md). + +Make sure to check test the artifacts of the release. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md b/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md new file mode 100644 index 000000000..2a1858e87 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md @@ -0,0 +1,89 @@ +# Running a development build +As always, install the dependencies for the first time (and re-run whenever there are errors about missing dependencies): + +``` +npm install +``` + +## Run server + +Run with default settings: + +``` +npm run start-server +``` + +Run with custom port: + +``` +TRILIUM_PORT=8082 npm run start-server +``` + +## Run Electron + +Rebuild `better-sqlite3` dependency: + +``` +npm run switch-electron +``` + +Then run Electron: + +``` +npm run start-electron +``` + +To run Electron using the same data directory as the production version: + +``` +npm run start-electron-no-dir +``` + +When done, switch back the `better-sqlite3` dependency: + +``` +npm run switch-server +``` + +## Quick switch + +To start Electron without running `switch-electron` first: + +``` +npm run qstart-electron +``` + +Similarly, to start the server without running `switch-server` first: + +``` +npm run qstart-server +``` + +## Safe mode + +Safe mode is off by default, to enable it temporarily on a Unix shell, prepend the environment variable setting: + +``` +TRILIUM_SAFE_MODE=1 npm run start-server +``` + +To have the same behaviour on Windows, we would need to alter `package.json`: + +```diff +-"start-electron": "npm run prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .", ++"start-electron": "npm run prepare-dist && cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .", +``` + +## Running on NixOS + +When doing development, the Electron binary retrieved from NPM is not going to be compatible with NixOS, resulting in errors when trying to run it. To bypass this, there is a special command to run electron using `nix-shell`: + +``` +npm run start-electron-nix +``` + +Similarly to the original command, to use the same data directory as the production version: + +``` +npm run start-electron-no-dir-nix +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md b/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md new file mode 100644 index 000000000..26617f7b6 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md @@ -0,0 +1,116 @@ +# Adding a new client library +In the past some libraries have been copy-pasted (and adapted if needed) to the repository. However, new libraries must be obtained exclusively through npm. + +The first step is to install the desired library. As an example we are going to install `i18next`: + +``` +npm i i18next +``` + +### Step 1. Understanding the structure of the import + +After installing the dependency, it's important to know how it's structured. You can do this by looking at the directory structure of the newly imported dependency: + +``` +$ tree node_modules/i18next +node_modules/i18next +├── dist +│ ├── cjs +│ │ └── i18next.js +│ ├── esm +│ │ ├── i18next.bundled.js +│ │ ├── i18next.js +│ │ └── package.json +│ └── umd +│ ├── i18next.js +│ └── i18next.min.js +├── i18next.js +├── i18next.min.js +├── index.d.mts +├── index.d.ts +├── index.js +├── index.v4.d.ts +├── LICENSE +├── package.json +├── README.md +└── typescript + ├── helpers.d.ts + ├── options.d.ts + ├── t.d.ts + └── t.v4.d.ts +``` + +Generally you should be looking for a `.min.js` file. Note that the `esm` and `cjs` variants generally don't work, we are looking for the classic, no module dependency. + +### Step 2. Exposing the library from the server + +The library must be delivered by the server and this is done via `src/routes/assets.ts`. In the `register` function, add a new entry near the bottom of the function: + +```javascript +app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/'))); +``` + +### Step 3. Adding it to the library loader + +The library loader is a client module which is in charge of downloading the library from the server and importing it. The loader is located in `src/public/app/services/library_loader.js`. + +To add a new library, start by creating a constant for it, with the value pointing to the minified JS identified at the first step: + +```javascript +const I18NEXT = { + js: [ + "node_modules/i18next/i18next.min.js" + ] +}; +``` + +Then add it to the `export default` section: + +```diff + export default { + requireCss, + requireLibrary, + CKEDITOR, + CODE_MIRROR, + ESLINT, + RELATION_MAP, + PRINT_THIS, + CALENDAR_WIDGET, + KATEX, + WHEEL_ZOOM, + FORCE_GRAPH, + MERMAID, + EXCALIDRAW, +- MARKJS ++ MARKJS, ++ I18NEXT + } +``` + +### Step 4. Using the library + +To import the library, simply use the following mechanism: + +```diff +import library_loader from "./library_loader.js"; + +await library_loader.requireLibrary(library_loader.I18NEXT); +``` + +Make sure to replace `I18NEXT` with the library that was created at the previous steps. + +Note that because we are not using a module management mechanism such as ES Modules or Common.js modules, the `requireLibrary` method does not actually return anything.  + +To benefit from the library, it must export on its own an object in `window`. + +In the case of `i18next`, it sets `window.i18next` and that can be used directly: + +```diff +i18next.init({}); +``` + +### Step 5. Adding Electron support + +For Electron, the `node_modules` are copied as a separate step by `bin/copy-dist.ts`. + +Scroll all the way down to the `nodeModulesFolder` and append an entry for the newly added libraries. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md b/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md new file mode 100644 index 000000000..98702fa11 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md @@ -0,0 +1,8 @@ +# Having a simpler packaging system +The current build scripts are a bit complicated and maintaining them is not easy. + +[Electron Forge](https://www.electronforge.io/) seems more mature and has a boatload of features, including Flatpak, snaps, Windows installers & more. + +Have a look also at the [Plugins](https://www.electronforge.io/config/plugins) section since there are quite a few interesting things there as well. + +Afterwards consider running a new round of Reducing binary size, especially taking into consideration removing of the unnecessary locales. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md new file mode 100644 index 000000000..d8282bb3f --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md @@ -0,0 +1,6 @@ +# Copy image reference to the clipboard +This function is handled by `src/public/app/widgets/floating_buttons/copy_image_reference_button.js` and it supports multiple note types out of the box. + +To enable the display of the button, simply modify `isEnabled` to add support for the new note type. + +No other modifications should be necessary. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md new file mode 100644 index 000000000..ad17e39bc --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md @@ -0,0 +1,33 @@ +# Export diagram as SVG +This mechanism is handled by `src/public/app/widgets/floating_buttons/svg_export_button.js`. + +## Step 1. Enable the button + +Modify the  `isEnabled` method in `svg_export_button.js` to add support for the new note type. + +## Step 2. Add support for exporting the image + +The SVG export needs to be handled inside the note type implementation.  + +The first goal is to create a method to handle the SVG rendering. Make sure to deduplicate the code if the SVG rendering is already handled. + +``` +async renderSvg() { + return await this.mind.exportSvg().text(); +} +``` + +Then create an event handler to manage the SVG export: + +``` +async exportSvgEvent({ntxId}) { + if (!this.isNoteContext(ntxId) || this.note.type !== "mindMap") { + return; + } + + const svg = await this.renderSvg(); + utils.downloadSvg(this.note.title, svg); +} +``` + +Make sure to modify the note type assertion at the beginning of the method. This is very important, otherwise there can be errors when navigating through multiple note types that support this button. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md new file mode 100644 index 000000000..7f7df68c8 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md @@ -0,0 +1,54 @@ +# First steps +> **Note**: When adding or updating step titles/order, don't forget to update the corresponding list in Note type checklist. + +## Step 1. Register the note type in the server + +Go to `src\services\note_types.ts` and add a new entry in `noteTypes` with the type ID and the default MIME type. + +## Step 2. Register the note type in the client context menu + +The client lists the available note types in `src\public\app\services\note_types.ts`. + +## Step 3. Create a type widget + +Go to `src\public\app\widgets\type_widgets` directory and create a new file corresponding to the new note type. + +A blank implementation looks something like this: + +## Step 4. Register the type widget + +The type widget needs to go in `src\public\app\widgets\note_detail.ts`where there is a `typeWidgetClasses` map, mapping the type IDs with the corresponding type widget that was created at the previous step. + +## Step 5. Add the default icon mapping + +To set a default icon for this note type, go to `src\public\app\entities\fnote.ts` and add it to `NOTE_TYPE_ICONS`. + +## Step 6. Add to note type selector + +Go to `src/public/app/widgets/note_type.ts` and register the new note type in `NOTE_TYPES`. + +## Step 7. Add the note to server allowed note types + +This is required in order to make imports possible, otherwise they will be imported as plain text. + +Go to `src/becca/entities/rows.ts` and add the new note type to `ALLOWED_NOTE_TYPES`. + +## Additional changes + +* If the widget requires a full height, it must be configured in `src\public\app\widgets\note_detail.ts` (look for `checkFullHeight`) then a `height: 100%` style can be applied to the containers to make them fit. +* To make the note always full width (ignoring the user's content width), go to `note_wrapper` and look for the `refresh` method. There is a `toggleClass` for `full-content-width` based on the note type. +* To allow the note source to be viewed, go to `src/public/app/widgets/buttons/note_actions.ts` and look for `this.toggleDisabled(this.$showSourceButton` in `refreshVisibility`. + +## Final steps + +* Update the Demo document to showcase the new note type. + +## Troubleshooting + +### Content does not appear, however it appears as hidden in the DOM + +Type widgets do a check whenever a note is selected to determine whether the widget needs to be displayed or not, based on the note type. Make sure `getType()` is well implemented in the newly added type widget (take great care that the value is returned but also that the note type ID matches the ones registered in the previous steps): + +``` +static getType() { return "foo"; } +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js new file mode 100644 index 000000000..9e33e54d9 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js @@ -0,0 +1,27 @@ +import TypeWidget from "./type_widget.js"; + +const TPL = ` +
+
+`; + +export default class MindMapWidget extends TypeWidget { + static getType() { return "mindMap"; } + + doRender() { + this.$widget = $(TPL); + + super.doRender(); + } + + async doRefresh(note) { + this.$widget.html("

Hello

"); + this.$widget.show(); + } + + async entitiesReloadedEvent({loadResults}) { + if (loadResults.isNoteReloaded(this.noteId)) { + this.refresh(); + } + } +} \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md new file mode 100644 index 000000000..a7aed7b0f --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md @@ -0,0 +1,9 @@ +# Loading data +Data loading can be done in `doRefresh()` since it gets a reference to the note: + +``` +const blob = await note.getBlob(); +const content = blob.getJsonContent(); +``` + +Note that `doRefresh` can sometimes be called by Saving data via spaced update when the user makes a changes, this has to be accounted for. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md new file mode 100644 index 000000000..9b81ae39c --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md @@ -0,0 +1,49 @@ +# Note type checklist +The goal of this checklist is to ensure a good implementation or re-test of a note type. + +## Implementation checklist + +The note type widget must be created according to First steps: + +* Register the note type in the server +* Register the note type in the client context menu +* Create a type widget +* Register the type widget +* Add the default icon mapping +* Add to note type selector +* Add the note to server allowed note types +* Update demo document to include this new note type +* Increase server sync version (see Mindmap gets turned as file). + +## Validation checklist + +### Ensure that the note renders properly + +* When refreshing to a note that is already displayed +* When going to another note and then going back +* When creating a new note of the given type +* Have two tabs of the same note type and switch between them + +### Ensure data persistence + +* Save data when modifying changes via spaced update + +### Ensure data retrieval + +* Go on a note of this type and refresh the page +* Create a new note of this type while on another note of this type and ensure that the content is set properly. + +### Set up a note preview + +For an implementation reference, see SVG rendering. + +* Note preview rendering (go to parent and see note list). +* Include note +* Share +* Note revisions + +### Import/export + +* Export & Import, making sure no data is lost in the process. +* Remove the data folder entirely to test that the demo document is well imported on first setup. + * Ensure that the preview also works (check the preview in the root note). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md new file mode 100644 index 000000000..da7648c15 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md @@ -0,0 +1,72 @@ +# SVG rendering +For diagrams and similar note types, it makes sense to cache an SVG rendering of the content so that it can be used for: + +* Content preview in note lists (when viewing the list of notes from the parent note). +* Note inclusion +* Share + +## Step 1. Save the SVG content as an attachment + +The first step is to obtain the SVG from the custom widget used. For example, for Mind Elixir there is an `exportSvg` method. + +If the returned value is a `Blob`, then the underlying text can be obtained via `await blob.text()`. + +To save the SVG as an attachment alongside the content, simply modify `getData()`: + +``` +async getData() { + const mind = this.mind; + if (!mind) { + return; + } + + const svgContent = await this.mind.exportSvg().text(); + return { + content: mind.getDataString(), + attachments: [ + { + role: "image", + title: "mindmap-export.svg", + mime: "image/svg+xml", + content: svgContent, + position: 0 + } + ] + }; +} +``` + +You can test this step by making a change to the note and then using the “Note attachments” option from the note menu. + +## Step 2. Adapting the server to serve SVG attachment + +The `src/routes/api/image.ts` route is in charge for serving the image previews of image notes, but also of custom note types such as canvases. + +Alter the `returnImageInt` method as follows: + +1. Add the image type to the guard condition which returns 400 for unsupported note types. +2. Add an `if` statement to render the attachment using the correct name: + +``` +if (image.type === "mindMap") { + renderSvgAttachment(image, res, 'mindmap-export.svg'); +} +``` + +## Step 3. Serve the SVG attachment for note preview + +The client also needs tweaking to allow it to render SVG attachments by calling the previously modified server route. + +The `src/public/app/services/content_renderer.js` file is in charge of handling the previews. To render using the image route, modify `getRenderedContent` to add the new note type to the `if` which calls `renderImage`. + +## Step 4. Serve SVG for share + +By default, `Note type cannot be displayed` will be displayed when trying to access the given note via a share. + +To serve the SVG, open `src/share/content_renderer.ts` and look for `getContent`. Then add to the `if` containing `renderImage` the new note type. + +This is not enough, as attempting to access the shared note will result in a broken image that fails with `Requested note is not a shareable image`. To solve this one, go to `src/share/routes.ts` and add a `renderImageAttachment` statement to `router.get('/share/api/images/[…])`. + +## Step 5. Serve SVG for revisions + +In the revisions list, to display the SVG, go to `src/public/app/widgets/dialogs/revisions.js` and look for the `renderContent` method. Simply add the note type to one of the already existing `if`s, such as the one for `canvas` and `mindMap` or `mermaid` (if the text content of the diagram should also be displayed). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md new file mode 100644 index 000000000..5ad001dca --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md @@ -0,0 +1,27 @@ +# Saving data via spaced update +The data persistence is achieved via the spaced update mechanism which is already present and needs to be integrated within the newly created type widgets. + +First, the class must implement `getData`, in order to retrieve the data from the custom widget in a serialized form. As an example from the mind map implementation: + +``` +async getData() { + const mind = this.mind; + if (!mind) { + return; + } + + return { + content: mind.getDataString() + }; +} +``` + +Here the content is a string containing a JSON. It is also possible to provide attachments here as well, such as SVG rendering to provide a preview of the content. + +Then to trigger an update, register a listener within the custom widget that calls the spaced update, for example: + +``` +mind.bus.addListener("operation", (operation) => { + this.spacedUpdate.scheduleUpdate(); +}); +``` \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Advanced topics/REST API/Internal API/API Reference.dat b/docs/Developer Guide/Developer Guide/Development and architecture/Backlinks.md similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Advanced topics/REST API/Internal API/API Reference.dat rename to docs/Developer Guide/Developer Guide/Development and architecture/Backlinks.md diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Branch prefixes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Branch prefixes.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md b/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md new file mode 100644 index 000000000..509386e08 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md @@ -0,0 +1,4 @@ +# Build information +* Provides context about when the build was made and the corresponding Git revision. +* The information is displayed to the client when going in the about dialog. +* The build information is hard-coded in `src/services/build.ts`. This file is generated automatically via `npm run update-build-info` which itself is run automatically whenever making a build in the CI, or a [local delivery](../Building%20and%20deployment/Build%20deliveries%20locally.md). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md new file mode 100644 index 000000000..1b462d6f0 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md @@ -0,0 +1,2 @@ +# attachments +
Column NameData TypeNullityDefault valueDescription
attachmentIdTextNon-null Unique ID (e.g. qhC1vzU4nwSE)
ownerIdTextNon-null The unique ID of a row in notes.
roleTextNon-null The role of the attachment: image for images that are attached to a note.
mimeTextNon-null The MIME type of the attachment (e.g. image/png)
titleTextNon-null The title of the attachment.
isProtectedIntegerNon-null01 if the entity is protected, 0 otherwise.
positionIntegerNon-null0Not sure where the position is relevant for attachments (saw it with values of 10 and 0).
blobIdTextNullablenullThe corresponding blobId from the blobs table.
dateModifiedTextNon-null Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateScheduledForErasureTextNullablenull 
isDeletedIntegerNon-null 1 if the entity is deleted, 0 otherwise.
deleteIdTextNullablenull 
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md new file mode 100644 index 000000000..5b2604430 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md @@ -0,0 +1,2 @@ +# attributes +
Column NameData TypeNullityDefault valueDescription
attributeIdTextNon-null Unique Id of the attribute (e.g. qhC1vzU4nwSE), can also have a special unique ID for Special notes (e.g. _lbToday_liconClass).
noteIdTextNon-null The ID of the note this atttribute belongs to
typeTextNon-null The type of attribute (label or relation).
nameTextNon-null The name/key of the attribute.
valueTextNon-null""
  • For label attributes, a free-form value of the attribute.
  • For relation attributes, the ID of the note the relation is pointing to.
positionIntegerNon-null0The position of the attribute compared to the other attributes. Some predefined attributes such as originalFileName have a value of 1000.
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
isDeletedIntegerNon-null 1 if the entity is deleted, 0 otherwise.
deleteIdTextNullablenull 
isInheritableIntegerNullable0 
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md new file mode 100644 index 000000000..64a5fdc28 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md @@ -0,0 +1,2 @@ +# blobs +
Column NameData TypeNullityDefault valueDescription
blobIdTextNon-null The unique ID of the blob (e.g. XXbfAJXqWrYnSXcelLFA).
contentTextNullablenull

The content of the blob, can be either:

  • text (for plain text notes or HTML notes).
  • binary (for images and other types of attachments)
dateModifiedTextNon-null Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md new file mode 100644 index 000000000..16be8a81c --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md @@ -0,0 +1,2 @@ +# branches +
Column NameData TypeNullityDefault valueDescription
branchIdTextNon-null The ID of the branch, in the form of a_b where a is the parentNoteId and b is the noteId.
noteIdTextNon-null The ID of the note.
parentNoteIdTextNon-null The ID of the parent note the note belongs to.
notePositionIntegerNon-null The position of the branch within the same level of hierarchy, the value is usually a multiple of 10.
prefixTextNullable The branch prefix if any, or NULL otherwise.
isExpandedIntegerNon-null0Whether the branch should appear expanded (its children shown) to the user.
isDeletedIntegerNon-null01 if the entity is deleted, 0 otherwise.
deleteIdTextNullablenull 
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md new file mode 100644 index 000000000..85a0b134b --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md @@ -0,0 +1,2 @@ +# entity_changes +
Column NameData TypeNullityDefault valueDescription
idIntegerNullable A sequential numeric index of the entity change.
entityNameTextNullable The type of entity being changed (attributes, branches, note_reordering, etc.)
entityIdTextNullable The ID of the entity being changed.
hashTextNullable TODO: Describe how the hash is calculated
isErasedIntegerNullable TODO: What does this do?
changeIdTextNullable TODO: What does this do?
componentIdTextNullable TODO: What does this do?
instanceIdTextNullable TODO: What does this do?
isSyncedIntegerNullable TODO: What does this do?
utcDateChangedTextNullable Date of the entity change in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md new file mode 100644 index 000000000..66e64fb8c --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md @@ -0,0 +1,2 @@ +# etapi_tokens +
Column NameData TypeNullityDefault valueDescription
etapiTokenIdTextNon-null A unique ID of the token (e.g. aHmLr5BywvfJ).
nameTextNon-null The name of the token, as is set by the user.
tokenHashTextNon-null The token itself.
utcDateCreatedTextNon-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
isDeletedIntegerNon-null01 if the entity is deleted, 0 otherwise.
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md new file mode 100644 index 000000000..2a21fdcba --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md @@ -0,0 +1,2 @@ +# notes +
Column NameData TypeNullityDefault valueDescription
noteIdTextNon-null The unique ID of the note (e.g. 2LJrKqIhr0Pe).
titleTextNon-null"note"The title of the note, as defined by the user.
isProtectedIntegerNon-null01 if the entity is protected, 0 otherwise.
typeTextNon-null"text"The type of note (i.e. text, file, code, relationMap, mermaid, canvas).
mimeTextNon-null"text/html"The MIME type of the note (e.g. text/html).. Note that it can be an empty string in some circumstances, but not null.
isDeletedIntegerNullable01 if the entity is deleted, 0 otherwise.
deleteIdTextNon-nullnull 
dateCreatedTextNon-null Localized creation date (e.g. 2023-11-08 18:43:44.204+0200)
dateModifiedTextNon-null Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateCreatedTextNon-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
blobIdTextNullablenullThe corresponding ID from blobs. Although it can theoretically be NULL, haven't found any such note yet.
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md new file mode 100644 index 000000000..ec70482c9 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md @@ -0,0 +1,2 @@ +# options +
Column NameData TypeNullityDefault valueDescription
nameTextNon-null The name of option (e.g. maxContentWidth)
valueTextNon-null The value of the option.
isSyncedIntegerNon-null00 if the option is not synchronized and thus can differ between clients, 1 if the option is synchronized.
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md new file mode 100644 index 000000000..f1897cc1d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md @@ -0,0 +1,2 @@ +# recent_notes +
Column NameData TypeNullityDefault valueDescription
noteIdTextNon-null Unique ID of the note (e.g. yRRTLlqTbGoZ).
notePathTextNon-null The path (IDs) to the note from root to the note itself, separated by slashes.
utcDateCreatedTextNon-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md new file mode 100644 index 000000000..4ea9c893d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md @@ -0,0 +1,2 @@ +# revisions +
Column NameData TypeNullityDefault valueDescription
revisionIdTextTextNon-null Unique ID of the revision (e.g. 0GjgUqnEudI8).
noteIdTextNon-null ID of the note this revision belongs to.
typeTextNon-null""The type of note (i.e. text, file, code, relationMap, mermaid, canvas).
mimeTextNon-null""The MIME type of the note (e.g. text/html).
titleTextNon-null The title of the note, as defined by the user.
isProtectedIntegerNon-null01 if the entity is protected, 0 otherwise.
blobIdTextNullablenullThe corresponding ID from blobs. Although it can theoretically be NULL, haven't found any such note yet.
utcDateLastEditedTextNon-null Not sure how it differs from modification date.
utcDateCreatedTextNon-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
dateLastEditedTextNon-null Not sure how it differs from modification date.
dateCreatedTextNon-null Localized creatino date (e.g. 2023-08-12 15:10:04.045+0300)
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Deleted notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Deleted notes.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md b/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md new file mode 100644 index 000000000..72fb72523 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md @@ -0,0 +1,19 @@ +# Demo document +The demo document is an exported .zip that resides in `db/demo.zip`. + +During on-boarding, if the user selects that they are a new user then the `demo.zip` is imported into the root note. + +## Modifying the document + +On a dev server, remove all your existing notes in order to ensure a clean setup. Right click → Import to note and select the .zip file in `db/demo.zip`. Make sure to disable “Safe import”. + +After making the necessary modifications, simply export the “Trilium Demo” note as “HTML in ZIP archive” and replace `db/demo.zip` with the newly exported one. + +## Testing the changes + +``` +rm -r data +npm run start-server +``` + +And then do the on-boarding again. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md b/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md new file mode 100644 index 000000000..f8ec576fb --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md @@ -0,0 +1,18 @@ +# Docker +To run a Docker build: + +``` +./bin/builder-docker.sh +``` + +To run the built Docker image: + +``` +sudo docker run -p 8081:8080 triliumnext/notes:v0.90.6-beta +``` + +To enter a shell in the Docker container: + +``` +sudo docker run -it --entrypoint=/bin/sh zadam/trilium:0.63-latest +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md new file mode 100644 index 000000000..01f1773f6 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md @@ -0,0 +1,6 @@ +# Hidden notes +## Disallow adding child notes + +1. To enforce at server level go to `services/notes.ts` and look for the `getAndValidateParent` method.  Look for the `params.ignoreForbiddenParents` if statement and add it there. +2. To hide the plus button in the note tree, go to `widgets/note_tree` in the client and look for `enhanceTitle`. Look for the if statement which starts with `!["search", "launcher"].includes(note.type)`. +3. To disable it from the contextual menu, go to `tree_context_menu` and look for the `getMenuItems` method. There look for the `insertNoteAfter` and `insertChildNote` actions and look at their `enabled` conditions. If adding a big note type with lots of child notes, see the pattern of optinos & help (rename and augment the `notOptionsOrHelp` variable. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md new file mode 100644 index 000000000..fe48406a7 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md @@ -0,0 +1,26 @@ +# Icons +Icons are stored in `images` and in `images/app-icons`. + +## Favicon + +The favicon is served dynamically via `serve-favicon`, using the icon in `images/app-icons/win/icon.ico`. + +## Declarative generation of icons + +All the icons are now built off of the SVGs in the `images` directory using the `bin/create-icons.sh` script. + +## Main images + +These are stored in `images`: + +
NameResolutionDescription
icon-black.svg53x40Used by the global menu button when not hovered.
icon-color.svg53x40Used by the global menu when hovered.
icon-grey.svg53x40Used by the dark theme, in place of icon-black.svg.
+ +## App icons + +
NameResolutionDescription
ios/apple-touch-icon.png180x180Used as apple-touch-icon, but only in login.ejs and set_password.ejs for some reason.
mac/icon.icns512x512Provided as --icon to electron-packager for mac-arm64 and mac-x64 builds.
png/128x128.png128x128Used in linux-x64 build, to provide an icon.png.
png/256x256-dev.png256x256Used by the Electron window icon, if in dev mode.
png/256x256.pngUsed by the Electron window icon, if not in dev mode.
win/icon.ico
  • ICO 16x16
  • ICO 32x32
  • ICO 48x48
  • ICO 64x64
  • ICO 128x128
  • PNG 256x256
  • Used by the win-x64 build.
  • Used by Squirrel Windows installer for: setup icon, app icon, control panel icon
  • Used as the favicon.
win/setup-banner.gif640x480Used by the Squirrel Windows installer during the installation process. Has only one frame.
+ +## Additional locations where the branding is used + +* In the client, more specifically in `src/public/app/widgets/buttons/global_menu.js`, where the SVG content of the icon is directly embedded to allow styling via CSS. +* In the Demo document, as an attachment. +* In the CKEditor build, look for `packages/ckeditor5-build-balloon-block/src/icons/trilium.svg`. Make sure not to have any `fill` overrides in the SVG as the wrong color will be used. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png new file mode 100644 index 000000000..4fa4deed1 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md new file mode 100644 index 000000000..ed0a29475 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md @@ -0,0 +1,8 @@ +# Icons on Mac +Looks great in Finder: + +
+ +Looks great in Dock: + +
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png new file mode 100644 index 000000000..4fa4deed1 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png new file mode 100644 index 000000000..1a0232adf Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png new file mode 100644 index 000000000..e42926852 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png new file mode 100644 index 000000000..c30743ece Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png new file mode 100644 index 000000000..99b0ce3d4 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png new file mode 100644 index 000000000..afcfacc11 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png new file mode 100644 index 000000000..e99523df8 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png new file mode 100644 index 000000000..eacecb265 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md new file mode 100644 index 000000000..61f195705 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md @@ -0,0 +1,6 @@ +# Adaptive icon +
Before
After
With new scale
+ +## Scale + +
0.9
0.85
0.8
0.75
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png new file mode 100644 index 000000000..eeec79a01 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png new file mode 100644 index 000000000..c32a45801 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md new file mode 100644 index 000000000..86082bb5c --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md @@ -0,0 +1,50 @@ +# Slightly blurry icon on Mac +Slightly blurry in extended preview on Mac + +
+ +In the screenshot, the icon is around 650px whereas the closest image we have is 512px, so that might explain the blur. Adding an `ic10` (`1024x1024`, aka `512x512@2x` to see what happens). + +Before: + +``` +File: ../images/app-icons/mac/icon.icns + ic09: 62069 bytes, png: 512x512 +``` + +After: + +``` +File: ../images/app-icons/mac/icon.icns + icp4: 1140 bytes, png: 16x16 + icp5: 1868 bytes, png: 32x32 + ic07: 9520 bytes, png: 128x128 + ic09: 62069 bytes, png: 512x512 + ic10: 180442 bytes, png: 512x512@2x +``` + +Even with a 1024x1024 icon, the image is still blurry. + +Comparing the `.icns` file from the Electron build reveals that the `.icns` file has been tampered with: + +
The electron.icns from the resulting buildThe icon source
File: images/app-icons/mac/electron.icns
+  icp4: 1140 bytes, png: 16x16
+  icp5: 1868 bytes, png: 32x32
+  ic07: 9520 bytes, png: 128x128
+  ic09: 62069 bytes, png: 512x512
+  ic10: 180442 bytes, png: 512x512@2x
File: images/app-icons/mac/icon.icns
+  icp4: 1648 bytes, png: 16x16
+  icp5: 4364 bytes, png: 32x32
+  ic07: 26273 bytes, png: 128x128
+  ic09: 206192 bytes, png: 512x512
+  ic10: 716034 bytes, png: 512x512@2x
+ +The bluriness might come from the image itself: [https://stackoverflow.com/questions/54030521/convert-svg-to-png-with-sharp-edges](https://stackoverflow.com/questions/54030521/convert-svg-to-png-with-sharp-edges)  + +Rendering with Inkscape (left) vs ImageMagick (right): + +
+ +Now in macOS it's also rendering quite nicely: + +
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png new file mode 100644 index 000000000..f587c31cf Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md new file mode 100644 index 000000000..6ec6a45eb --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md @@ -0,0 +1,12 @@ +# Removed icons +The following icons were removed: + +## Main images + +These are stored in `images`: + +
NameResolutionDescription
icon-black.png36x36Does not appear to be used.
icon-color.png36x36Used only by some tests in test-etapi.
icon-grey.png36x36Does not appear to be used.
icon.svg210x297Does not appear to be used.
+ +## App icons + +
NameResolutionDescription
png/16x16-bw.png16x16Do not appear to be used.
png/16x16.png
png/24x24.png24x24
png/32x32.png32x32
png/48x48.png48x48
png/64x64.png64x64
png/96x96.png96x96
png/512x512.png512x512Does not appear to be used.
win/setup-banner.xcf GIMP source for win/setup-banner.gif. Provided only for future editing.
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md new file mode 100644 index 000000000..d4026a474 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md @@ -0,0 +1,94 @@ +# Internationalisation / Translations +During the initial development of Trilium Notes, internationalisation was not considered as it was meant to be an English-only product. + +As the application and the user base grows, it makes sense to be able to reach out as many people as possible by providing translations in their native language. + +The library used is [i18next](https://www.i18next.com/). + +## Where are the translations? + +The translations are formatted as JSON files and they are located in `src/public/translations`. For every supported locale, there is a subdirectory in which there is a `translation.json` file (e.g. `src/public/translations/en/translation.json`). + +### Message keys + +One important aspect is the fact that we are using a key-based approach. This means that each message is identified by an ID rather than a natural-language message (such as the default approach in gettext). + +The key-based approach allows a hierarchical structure. For example, a key of `about.title` would be added in `translation.json` as follows: + +```json +{ + "about": { + "title": "About TriliumNext Notes" + } +} +``` + +Follow the Guidelines when creating a new message. + +### Adding a new locale + +To add a new locale, go to `src/public/translations` with your favorite text editor and copy the `en` directory. + +Rename the copy to the ISO code (e.g. `fr`, `ro`) of the language being translated. + +Translations with a country-language combination, using their corresponding ISO code (e.g. `fr_FR`, `fr_BE`), has not been tested yet. + +### Changing the language + +Since the internationalisation process is in its early stages, there is no user-facing way to switch the language. + +To change the language manually, edit `src/public/app/services/i18n.js` and look for the line containing `lng: "en"`. Replace `en` with the desired language code (from the ones available in `src/public/translations`). + +## Client-side translations + +### Component-level translations + +Most of the client translations are present in the various widgets and layouts. + +Translation support has to be added manually for every file. + +The first step is to add the translation import with a relative import. For example, if we are in the `src/public/app/widgets/dialogs` directory, the import would look as follows: + +```javascript +import { t } from "../../services/i18n.js"; +``` + +Afterwards, simply replace the hard-coded message with: + +```javascript +${t("msgid")} +``` + +where `msgid` is the key of the message being translated. + +### Variables + +In the translation, enclose the variables with `{{` and `}}`: + +``` +{ + "key": "{{what}} is {{how}}" +} +``` + +Then pass the arguments when reading the translation: + +``` +t('key', { what: 'i18next', how: 'great' }) +``` + +### Template-level translations + +Templates are `.ejs` files present in `src/views`, these are used to prepare the root layout for desktop, mobile applications as well as setup (onboarding) and the shared notes view. + +Due to using a different approach, it is not possible yet to translate those files. + +## Server-side translations + +Currently the server-side messages are not translatable. They will be added as a separate step. + +## Locale/language selection + +The language is stored as an option which is synchronized across all devices and the user is able to adjust it via Options → Appearance → Locale. + +The options shown to the user are currently hard-coded in `src/routes/api/options.ts`, where there is a `getSupportedLocales()` function. The `id` field must match the corresponding directory in `src/public/translations` and the `name` must be the localized name of the language (so the name must be in that language, not in English). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md new file mode 100644 index 000000000..65bdf9673 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md @@ -0,0 +1,9 @@ +# Guidelines +* Use hierarchy whenever appropriate, try to group the messages by: + * Modals (e.g. `about.foo`, `jump_to_note.foo`) +* Don't duplicate messages that are very widely used. + * One such example is `aria-label="Close"` which should go to a single message such as `modal.close` instead of being duplicated in every modal. +* On the other hand, don't overly generalise messages. A `close` message that is used whenever the “Close” word is encountered is not a good approach since it can potentially cause issues due to lack of context. +* Use [variable interpolation](https://www.i18next.com/translation-function/interpolation) whenever appropriate. + * If you see multiple messages joined together only to apply add a variable such as a user-inputted value, try to join those messages together into a single message containing a variable. + * So instead of `“Number of updates: “ + numUpdates + “.”` use `$(t("number_updates", { numUpdates }))` where the message translation would appear as `Number of updates: {{numUpdates}}.` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md new file mode 100644 index 000000000..f055fce95 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md @@ -0,0 +1,19 @@ +# Server translations +* Server-side translations are managed by the same library as the client, i18next. +* The translation files reside in the `/translations` directory, following the same convention as the client (`translations/{{lng}}/{{ns}}.json`), where the namespace is `server.json`. So for the Spanish translations we have `translations/es/server.json`. +* Loading of translations is managed by [i18next-fs-backend](https://github.com/i18next/i18next-fs-backend) which loads the translations directly from the file system (unlike HTTP requests like the client), at the path mentioned previously (relative to `package.json`). + +## How to translate a string + +Unlike the client which uses a dedicated client service, the i18next library on the server is used directly, as such: + +```javascript +import { t } from "i18next"; + +const translatedString = t("message.id"); +``` + +## What should be translated + +* Avoid translating server-side logs, as those are supposed to be for debugging and as such there is no benefit in translating them. +* Translate any user-facing message that comes from the server, such as error messages shown in the Electron application, or information such as keyboard shortcuts, note titles, etc. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md new file mode 100644 index 000000000..75a8b0cd6 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md @@ -0,0 +1,15 @@ +# i18n-ally +[`i18n-ally`](https://github.com/lokalise/i18n-ally) is a VS Code extension that aids in internationalization. + +It is currently integrated in the project and offers features such as: + +* Highlight, autocomplete translations. +* Display translations inline. +* Extract messages into translation. + +### Extracting messages into translation + +1. Open any .js file and select an untranslated string inside a template (`TPL`). +2. Press Ctrl+P and look for “i18n Ally: Extract text into i18n messages” +3. Select the first template. +4. Select the path of translation, taking into consideration the  Guidelines (e.g. `jump_to_note.search-for-note-by-its-name`). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md b/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md new file mode 100644 index 000000000..f7e5f429e --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md @@ -0,0 +1,13 @@ +# Launchers +Launchers are items that are displayed in the launcher bar (left side of the screen). They are of two different types: + +* Visible launchers: are displayed by default to the user, can be moved to the available launchers section to hide them. +* Available launchers: can be manually added by the user from settings into the list of visible launchers. + +## Adding a new launcher + +Regardless of the type, new launchers are added at server level, inside `hidden_subtree.ts` file. + +* To add a new available launcher, look for `_lbAvailableLaunchers` and add a new item to its `children`. +* Similarly, to add a visible launcher, look for `_lbVisibleLaunchers`. + * If you add a visible launcher, it will be available for both new and old users, since the application will identify that there is a new launcher to be added regardless of the user preference. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md b/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md new file mode 100644 index 000000000..dc428c828 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md @@ -0,0 +1,20 @@ +# Live reload +## Server live reload + +If running the server using `npm run start-server`, the server will watch for changes in `src/public` and trigger a frontend reload if that occurs. + +## Electron live reload + +Similarly, `npm run start-electron` supports live refresh  as well. + +However, a core difference is that Electron watches `dist/src/public` instead of `src/public` since Electron runs on its own copy of the files. + +To ameliorate that, a separate watch script has been implemented which automatically copies files from `src/public` to `dist/src/public` whenever a change is detected. To run it: + +``` +npm run +``` + +## Technical details + +* This mechanism is managed at server level by watching for changes in`services/ws.ts`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md b/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md new file mode 100644 index 000000000..b2388f1dd --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md @@ -0,0 +1,30 @@ +# Note types +The note type is defined by the `type` column in notes. + +Possible types: + +
Note Typetype valueCorresponding MIME typeContent of the note's blobRelevant attributes
Texttext The HTML of the note. 
Relation Map relationMapapplication/json

A JSON describing the note:

{
+    "notes": [
+        {
+            "noteId": "gFQDL11KEm9G",
+            "x": 142,
+            "y": 405
+        },
+        {
+            "noteId": "8GcjEKyrrCgl",
+            "x": 100.10406374385552,
+            "y": 757.0364424520196
+        }
+    ],
+    "transform": {
+        "scale": 0.3,
+        "x": 480.29766098682165,
+        "y": 116.83892021963081
+    }
+}
None
Render Noterendertext/html or blank.An empty blob.~renderNote pointing to the HTML note to render.
Canvascanvasapplication/json
{
+	"appState": {},
+	"elemenets": {},
+	"files": {},
+	"type": "excalidraw",
+	"version": 2
+}
None
Mermaid Diagrammermaidtext/mermaid or text/plainThe plain text content of the Mermaid diagram.None
Bookbooktext/html or blank.An empty blob.
  • #viewType which can be either grid or list.
  • #expanded

both options are shown to the user via the “Book Properties” ribbon widget.

Web ViewwebViewblankAn empty blob.#webViewSrc pointing to an URL to render.
CodecodeDepends on the language (e.g. text/plain, text/x-markdown, text/x-c++src).The plain text content. 
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options.md new file mode 100644 index 000000000..2cfff155d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options.md @@ -0,0 +1,14 @@ +# Options +## Read an option + +Add the import to the service (make sure the relative path is correct): + +```javascript +import options from "../../services/options.js"; +``` + +Them simply read the option: + +```javascript +this.firstDayOfWeek = options.getInt("firstDayOfWeek"); +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md new file mode 100644 index 000000000..c127d8c62 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md @@ -0,0 +1,37 @@ +# Check box option +In the TPL: + +``` +
+

Background effects

+ +

On the desktop application, it's possible to use a semi-transparent background tinted in the colors of the user's wallpaper to add a touch of color.

+ +
+ +
+
+``` + +In `doRender()`: + +``` +doRender() { + this.$backgroundEffects = this.$widget.find("input.background-effects"); + + this.$backgroundEffects.on("change", () => this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects)); +} +``` + +In `optionsLoaded(options)`: + +``` +async optionsLoaded(options) { + + this.setCheckboxState(this.$backgroundEffects, options.backgroundEffects); + +} +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md new file mode 100644 index 000000000..975ef70b3 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md @@ -0,0 +1,5 @@ +# Creating a new option +1. Go to `options_interface.ts` and add the option to `OptionDefinitions`, specifying its intended data type (boolean, string, number). Note that in the end the option will still be stored as a string, but this aids in type safety across the application. +2. To add a new option with a set default, go to `options_init.ts` in the server and add a new entry in the `defaultOptions`. +3. **Make the option adjustable by the client** + By default options are not adjustable or visible to the client. To do so, modify `routes/api/options.ts` to add the newly added option to `ALLOWED_OPTIONS`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md new file mode 100644 index 000000000..71bab7e34 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md @@ -0,0 +1,36 @@ +# Displaying the option in settings +Go to `src/public/app/widgets/type_widgets/options` and select a corresponding category, such as `appearance` and edit one of the JS files. + +For example, to create a select: + +First, modify the template (`TPL`), to add the new widget: + +``` +
+ + +
+``` + +Secondly, create a reference to the new element in `doRender()`: + +``` +this.$firstDayOfWeek = this.$widget.find(".first-day-of-week-select"); +``` + +Then in `optionsLoaded` adjust the value to the one set in the database: + +``` +this.$firstDayOfWeek.val(options.firstDayOfWeek); +``` + +To actually update the option, add a listener in `doRender`: + +``` +this.$firstDayOfWeek.on("change", () => { + this.updateOption("firstDayOfWeek", this.$firstDayOfWeek.val()); +}); +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md new file mode 100644 index 000000000..1650f399d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md @@ -0,0 +1,10 @@ +# Refresh widget with option change +To make a widget react to a change of a given option, simply add the following to the widget: + +```javascript +async entitiesReloadedEvent({loadResults}) { + if (loadResults.getOptionNames().includes("firstDayOfWeek")) { + // Do something. + } +} +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md new file mode 100644 index 000000000..7d85a175c --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md @@ -0,0 +1,12 @@ +# Trigger UI refresh +Call `utils.reloadFrontendApp`, but make sure to wait for the option to be saved first. + +``` +this.$backgroundEffects.on("change", async () => { + + await this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects); + + utils.reloadFrontendApp("background effect change"); + +}); +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md b/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md new file mode 100644 index 000000000..29c081bcb --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md @@ -0,0 +1,15 @@ +# Printing +Note printing is handled by `note_detail.js`, in the `printActiveNoteEvent` method. + +The application uses the [`print-this`](https://www.npmjs.com/package/print-this) library to isolate `.note-detail-printable:visible` and prepare it for printing. + +Some scripts like KaTeX are manually injected in the footer, and the CSS to be used is manually defined. The most important one is `print.css`. + +## Syntax highlighting + +Syntax highlighting for code blocks is supported as well: + +* It works by injecting a Highlight.js stylesheet into the print. +* The theme used is hard-coded (the _Visual Studio Light theme_, at the time of writing) in order not to have a dark background in print. +* The Highlight.js library is not needed since the `.note-detail-printable` which is rendered already has the `.hljs` classes added to it in order to achieve the syntax highlighting. +* The user's choice of whether to enable syntax highlighting is also respected. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md b/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md new file mode 100644 index 000000000..d6042d6fb --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md @@ -0,0 +1,6 @@ +# Protected entities +The following entities can be made protected, via their `isProtected` flag: + +* attachments +* notes +* revisions \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Revisions.md b/docs/Developer Guide/Developer Guide/Development and architecture/Revisions.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md b/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md new file mode 100644 index 000000000..5522f0062 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md @@ -0,0 +1,11 @@ +# Safe mode +Safe mode is triggered by setting the `TRILIUM_SAFE_MODE` environment variable to a truthy value, usually `1`. + +In each artifact there is a `trilium-safe-mode.sh` (or `.bat`) script to enable it. + +What it does: + +* Disables `customWidget` launcher types in `app/widgets/containers/launcher.js`. +* Disables the running of `mobileStartup` or `frontendStartup` scripts. +* Displays the root note instead of the previously saved session. +* Disables the running of `backendStartup`, `hourly`, `daily` scripts and checks for the hidden subtree. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Special notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Special notes.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md b/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md new file mode 100644 index 000000000..4b628a5e3 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md @@ -0,0 +1,17 @@ +# Content hashing +Entity hashing is done in `content_hash#getEntityHashes`. + +* It works by looking at the `entity_changes` table and going through each of the entity names/types: + * `blobs` + * `attributes` + * `revisions` + * `attachments` + * `notes` + * `branches` + * `etapi_tokens` + * `options` +* For some reason `note_reordering` entities are ignored specifically. +* All the rows in `entity_changes` are then ordered alphabetically, based on their `entityId`. +* Every entity row is then grouped by `entityName` and then by sector. The sector is defined as the first character of the `id`. +* The hash is altered to add the `isErased` value as well since the hash of deleted entries is not updated. +* For each sector, the hash is calculated using `utils.hash`, using SHA1 encoded as Base64. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md b/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md new file mode 100644 index 000000000..b04131299 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md @@ -0,0 +1,84 @@ +# Syntax highlighting +## Defining the MIME type + +The first step to supporting a new language for either code blocks or code notes is to define the MIME type. Go to `mime_types.ts` and add a corresponding entry: + +``` +{ title: "Batch file (DOS)", mime: "application/x-bat" } +``` + +## Syntax highlighting for Highlight.js + +### Built-in languages + +Highlight.js supports a lot of languages out of the box, for some of them we just need to enable them by specifying one of the language aliases in the `highlightJs` field in the `mime_types` definition: + +``` +{ title: "Batch file (DOS)", mime: "application/x-bat", highlightJs: "dos" } +``` + +For the full list of supported languages, see [Supported Languages — highlight.js 11.9.0 documentation](https://highlightjs.readthedocs.io/en/latest/supported-languages.html). Look for the “Package” column to see if another library needs to be installed to support it. + +Note that we are using the CDN build which may or may not have all the languages listed as predefined in the “Supported languages” list. To view the real list of supported files, see the `node_modules/@highlightjs/cdn-assets/languages` directory. + +### Custom language + +When the source code for a language is available, one way is to simply copy it to `libraries/highlightjs/{id}.js` where `id` matches the name for `highlightJs`. + +Make sure in the script that the language is registered: + +``` +hljs.registerLanguage('terraform', hljsDefineTerraform); +``` + +Then in `mime_types.ts` make sure to set `highlightJsSource` to `libraries` to load it. + +``` +{ title: "Terraform (HCL)", mime: "text/x-hcl", highlightJs: "terraform", highlightJsSource: "libraries", codeMirrorSource: "libraries/codemirror/hcl.js" }, +``` + +## Syntax highlighting for CodeMirror + +### Custom language + +Generally new languages are not added in the base installation and need to be separately registered. For CodeMirror 5 it seems that (at least for simple languages), the modes are distributed as _simple modes_ and can generally be copy-pasted in `libraries/codemirror`. An example would be: + +``` +(() => { + + CodeMirror.defineSimpleMode("batch", { + + start: [], + + echo: [] + + }); + + + + CodeMirror.defineMIME("application/x-bat", "batch"); + + CodeMirror.modeInfo.push({ + + ext: [ "bat", "cmd" ], + + mime: "application/x-bat", + + mode: "batch", + + name: "Batch file" + + }); + +})(); + + +``` + +Note that changing `modeInfo` is crucial, otherwise syntax highlighting will not work. The `mime` field is mandatory, even if `mimes` is used instead. + +Afterwards, register it in `mime_types.ts`, specifying `codeMirrorSource` to point to the newly created file: + +``` +{ title: "Batch file (DOS)", mime: "application/x-bat", highlightJs: "dos", codeMirrorSource: "libraries/codemirror/batch.js" } +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md new file mode 100644 index 000000000..5d89c2c2e --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md @@ -0,0 +1,17 @@ +# Themes +## Server-side + +* There are three themes embedded in the application: + * `light`, located in `src\public\stylesheets\theme-light.css` + * `dark`, located in `src\public\stylesheets\theme-dark.css` + * `next`, located in `src\public\stylesheets\theme-next.css`. +* The default theme is set only once, when the database is created and is managed by `options_init#initNotSyncedOptions`. + * In the original implementation: On Electron, the choice between `light` and `dark` is done based on the OS preference. Otherwise, the theme is always `dark`. + * Now, we always choose `next` as the default theme. +* The theme is served via `src\routes\index.ts`, in the `getThemeCssUrl` method. + +## Client-side + +* The predefined themes are hard-coded in the client in `src\public\app\widgets\type_widgets\options\appearance\theme.js`. +* The user-defined themes are obtained via a call to the server: `options/user-themes`. +* The theme retrieval is done via a request. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md new file mode 100644 index 000000000..5af483058 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Documentation.md @@ -0,0 +1,73 @@ +# Documentation +
+ +There are multiple types of documentation for Trilium: + +* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1. +* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. +* _Release Notes_, this contains the change log for each released or soon-to-be-released version. The release notes are used automatically by the CI when releasing a version. +* The _Script API_, which is an automatically generated documentation for the front-end and back-end APIs for scripts. + +## Editing documentation + +There are two ways to modify documentation: + +* Using a special mode of Trilium. +* By manually editing the files. + +### Using `docs:edit` + +To edit the documentation using Trilium, set up a working development environment and run the following commands: + +* On most operating systems, `npm run electron:switch` followed by `npm run docs:edit` +* On NixOS, `npm run docs:edit-nix`. + +> [!NOTE] +> `npm run docs:edit` acts very similar to `npm run electron:start` in the sense that you cannot both be editing documentation and starting a server. Using both `npm run electron:start` and `docs:edit` is possible, since they are using the same Electron instance. + +How it works: + +* At startup, the documentation from `docs/` is imported from Markdown into a in-memory session (the initialization of the database is already handled by the application). +* Each modification will trigger after 10s an export from the in-memory Trilium session back to Markdown, including the meta file. + +### Manual editing + +Apart from the User Guide, it's generally feasible to make small modifications directly using a Markdown editor or VS Code, for example. + +When making manual modifications, avoid: + +* Uploading pictures, since images are handled as Trilium attachments which are stored in the meta file. +* Changing the file or directory structure in any way, since that is also handled by the meta file. A missing file will most certainly cause a crash at start-up when attempting to edit the docs using Trilium. + +### Reviewing & committing the changes + +Since the documentation is tracked with Git, after making the manual or automatic modifications (wait at least 10s after making the modification) the changes will reflect in Git. + +Make sure to analyze each modified file and report possible issues. + +Important aspects to consider: + +* The Trilium import/export mechanism is not perfect, so if you make some modifications to the documentation using `docs:edit`, at the next import/export/import cycle some whitespace might get thrown in. It's generally safe to commit the changes as-is. +* Since we are importing Markdown, editing HTML and then exporting the HTML back to Markdown there might be some edge cases where the formatting is not properly preserved. Try to identify such cases and report them in order to get them fixed (this will benefit also the users). + +## Location of the documentation + +All documentation is stored in the [Notes](https://github.com/TriliumNext/Notes) repository: + +* `docs/Developer Guide` contains Markdown documentation that can be modified either externally (using a Markdown editor, or internally using Trilium). +* `docs/Release Notes` is also stored in Markdown format and can be freely edited. +* `docs/Script API` contains auto-generated files and thus must not be modified. +* `docs/User Guide` contains also Markdown-only documentation but must generally not be edited externally. + * The reason is that the `docs:edit` feature will not only import/export this documentation, but also generate the corresponding HTML documentation and meta structure in `src/public/app/doc_notes/en/User Guide`. + * It's theoretically possible to edit the Markdown files externally and then run `docs:edit` and trigger a change in order to build the documentation, but that would not be a very productive workflow. + +## Updating the Script API + +As mentioned previously, the Script API is not manually editable since it is auto-generated using TypeDoc. + +To update the API documentation, simply run `npm run docs:build`. Compare the changes (if any) and commit them. + +Note that in order to simulate the environment a script would have, some fake source files (in the sense that they are only used for documentation) are being used as entrypoints for the documentation: + +* For back-end scripts, the script is located in `src/services/backend_script_entrypoint.ts`. +* For front-end scripts, the script is located in `src/public/app/services/frontend_script_entrypoint.ts`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation/Documentation references in th.md b/docs/Developer Guide/Developer Guide/Documentation/Documentation references in th.md new file mode 100644 index 000000000..61a585f12 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Documentation/Documentation references in th.md @@ -0,0 +1,23 @@ +# Documentation references in the application +## Hard-coded links + +Hard-coded links are present throughout the application, either in dialogs or in the source code as comments. + +You can identify these links by searching for: + +``` +https://triliumnext.github.io/Docs/Wiki/ +``` + +## Help buttons + +There is a pattern of “?” buttons throughout the application which make use of the `data-help-page` attribute. Whenever these buttons are pressed, the user is redirected to the corresponding wiki page by prepending the wiki root URL to the `data-help-page` attribute. + +Since the current wiki has a different structure than the original, for example to link to [https://github.com/TriliumNext/Docs/blob/main/Wiki/tree-concepts.md](https://github.com/TriliumNext/Docs/blob/main/Wiki/tree-concepts.md) the `data-help-page` attribute must be set to `tree-concepts.md`. + +For links to headings, simply add the heading after the `.md`: `tree-concepts.md#prefix` + +You can identify those by looking for: + +* `.attr("data-help-page"` +* `data-help-page="` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation_image.png b/docs/Developer Guide/Developer Guide/Documentation_image.png new file mode 100644 index 000000000..542bb7858 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Documentation_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md b/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md new file mode 100644 index 000000000..4d6b313ac --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md @@ -0,0 +1,12 @@ +# Download latest nightly and install it +On Ubuntu: + +``` +#!/usr/bin/env bash + +name=TriliumNextNotes-linux-x64-nightly.deb +rm -f $name* +wget https://github.com/TriliumNext/Notes/releases/download/nightly/$name +sudo apt-get install ./$name +rm $name +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md new file mode 100644 index 000000000..72ed24768 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md @@ -0,0 +1,2 @@ +# Build deliveries locally +This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Build%20deliveries%20locally.md). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md new file mode 100644 index 000000000..148f6c404 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md @@ -0,0 +1,2 @@ +# Releasing a version +This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Releasing%20a%20version.md). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md new file mode 100644 index 000000000..ca867a1d7 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md @@ -0,0 +1,2 @@ +# Running a development build +This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Running%20a%20development%20build.md). \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md new file mode 100644 index 000000000..606b9c8e6 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md @@ -0,0 +1,2 @@ +# Updating dependencies +
DependencyName in library_loaderThings to check for a basic sanity check Protected by unit tests
better-sqlite3 See bettersqlite binaries.  
jsdom 
  • Note map
  • Clipper
  • Note similarity
Protected by typings, should catch any potential changes in API.Yes
async-mutex 
  • Sync
  
axios 
  • Can't be directly tested, as it's exposed only via the backend script API.
  
sax 
  • EverNote imports
  
  • ws
  • debounce
 
  • Check any action is reported from server to client (e.g. delete a note).
  
ejs 
  • Onboarding / first setup
  
dayjs 
  • Day notes
  
semver 
  • Application should start.
  
https-proxy-agent ???  
sax 
  • EverNote import
  
ini 
  • Affects config, generally if the application starts then it should be OK.
  
jsplumbRELATION_MAP
  • Relation map note type
  
jquery.mark.es6MARKJS
  • In search, when highlighting the text that matched.
  • In search in HTML, which might not actually be used since it seems to have been replaced by CKEditor's own find & replace dialog.
  
knockout.js 
  • Used in rendering the login and main layout of the application.
  
normalize.min.css 
  • Used in shared notes.
  
wheel-zoom.min.jsWHEEL_ZOOM
  • When opening a image that is in attachment.
  • When opening a stand-alone image note.
  • When zooming in a mermaid chart.
  
fancytree 
  • The note tree should be fully functional.
  
bootstrap 
  • Check mostly the on-boarding pages, when there is no database.
  
electron-debug 
  • Run electron using npm run start-electron and check that the debug hotkeys are still working (Ctrl+Shift+I on Windows/Linux, Cmd+Alt+I for dev tools, Cmd/Ctrl+R for reload).
  
electron-dl    
eslint    
marked 
  • Importing a markdown note.
 Yes
force-graph 
  • Note map
  
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md new file mode 100644 index 000000000..1081e5ea9 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md @@ -0,0 +1,8 @@ +# Node.js, Electron and `better-sqlite3` +## Node.js, Electron and `better-sqlite3` + +`better-sqlite3` requires a native module in order to work. In order to ease the installation process, prebuilt binaries are provided by the library developers. + +Trilium Next started with version [8.4.0](https://github.com/WiseLibs/better-sqlite3/releases/tag/v8.4.0) for `better-sqlite3` + +
better-sqlite3 versionSQLite versionNode.js prebuildsElectron.js prebuilds
8.4.0<3.43.0v20???
8.5.0v20v25
8.5.1 v26
8.5.2v20 (macOS + arm64)
8.6.03.43.0 
8.7.03.43.1 
9.0.03.43.2 v27
9.1.03.44.0 
9.1.1macOS + Alpine
9.2.03.44.2 
9.2.1 / 9.2.2 v28
9.3.03.45.0 
9.4.03.45.1 
9.4.1Windows arm, arm64
9.4.2 <v29
9.4.3 <v29
9.4.4 v29
9.4.5Better prebuilds
9.5.03.45.2 
9.6.03.45.3 v30
10.0.0v22
10.1.03.46.0 
11.0.0>21
11.1.0 (prerelease)  v31
11.1.1  
11.1.2  
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md new file mode 100644 index 000000000..dffa2d177 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md @@ -0,0 +1,2 @@ +# Testing compatibility +
better-sqlite3 version
Change log
SQLite version
Change log
Compatibility with upstream Trilium
8.4.0<3.43.0Compatible, same version.
8.6.03.43.0 
8.7.03.43.1 
9.0.03.43.2 
9.1.0 + 9.1.13.44.0 
9.2.0 + 9.2.1 + 9.2.23.44.2 
9.3.03.45.0 
9.4.0, 9.4.1, 9.4.2, 9.4.3, 9.4.4, 9.4.53.45.1 
9.5.03.45.2 
9.6.0 / 10.0.03.45.3 
10.1.0 / 11.0.0 / 11.1.1 / 11.1.2 / 11.2.0 / 11.2.13.46.0 
11.3.03.46.1 
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md new file mode 100644 index 000000000..a9ec99601 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md @@ -0,0 +1,41 @@ +# bettersqlite binaries +### The native node bindings + +`better-sqlite3` has native Node bindings. With updates of `better-sqlite3`, but also of Electron and Node.js versions, these bindings need to be updated. + +Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js. + +During development, `npm install` tries to build or reuse prebuilt natives for the current Node.js version. This makes `npm run start-server` work out of the box. Trying to run `npm run start-electron` with these versions generally causes an error such as this: + +``` +Uncaught Exception: +Error: The module '/Users/elian/Projects/Notes/node_modules/better-sqlite3/build/Release/better_sqlite3.node' +was compiled against a different Node.js version using +NODE_MODULE_VERSION 108. This version of Node.js requires +NODE_MODULE_VERSION 116. Please try re-compiling or re-installing +the module (for instance, using `npm rebuild` or `npm install`). +``` + +### How the natives are handled + +Locally, this can be fixed by rebuilding the binaries, which is what `npm run switch-electron` does, which uses `electron-rebuild` under the hood. + +When the deliveries are built (see Build deliveries locally), it is not feasible to rebuild the dependencies since we are building for multiple platforms. Luckily, `better-sqlite3` provides these prebuilt binaries from us, available as artifacts on [their GitHub releases page](https://github.com/WiseLibs/better-sqlite3/releases/).  + +The build script manages the natives for `better-sqlite3` by keeping a copy of the `.node` file for every platform in `bin/better-sqlite3`. + +Whenever the version of `better-sqlite3` changes, the `.node` files must also be renewed based on their releases page. To simplify this process, a script was created in `bin/better-sqlite3/update.sh`. + +## How to update the natives + +The update script needs to know the version of Electron or Node.js for which to download the prebuilt binaries. + +If you get errors during download, check on the [releases page](https://github.com/WiseLibs/better-sqlite3/releases/) to ensure that this particular combination of Electron/Node actually exists for the given release. + +To determine the `NODE_MODULE_VERSION` that is required, look for `This version of Node.js requires` +`NODE_MODULE_VERSION` in the error when starting Trilium via: + +* `npm run start-electron` (or run any Electron [delivery](../../Building%20and%20deployment/Build%20deliveries%20locally.md)), case in which the `ELECTRON_VERSION` variable needs to be changed. +* `npm run start-server` (or run the Linux server delivery), case in which the `NODE_VERSION` variable needs to be changed. + +Check which files got changed after running the update script and for each platform that got changed, test it locally via Build deliveries locally or via the CI. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md b/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md new file mode 100644 index 000000000..81290602e --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md @@ -0,0 +1,16 @@ +# Server-side imports +Trilium Notes allowed the use of Common.js module imports inside backend scripts, such as: + +``` +const isBetween = require('dayjs/plugin/isBetween') +api.dayjs.extend(isBetween) +``` + +For TriliumNext, the backend has been switched to use ESM which has a slightly more complicated syntax. Instead of `require` we now have `import` but which is asynchronous so it will require an `await`: + +``` +const isBetween = (await import("dayjs/plugin/isBetween")).default; +api.dayjs.extend(isBetween); +``` + +Note that `.default` is also usually needed to obtain the same behaviour as a CJS import. When in doubt, use `console.log` to see the output of the value returned by `await import`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets.md new file mode 100644 index 000000000..cbd08854f --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets.md @@ -0,0 +1,33 @@ +# Widgets +To create a basic widget, simply create a code note with type “JS frontend”. Add the `#widget` label in order for it to be loaded at startup. + +``` +const template = `
`; + +class MyWidget extends api.BasicWidget { + get position() { return 1; } + get parentWidget() { return "left-pane" } + + doRender() { + this.$widget = $(template); + return this.$widget; + } +} + +module.exports = new MyWidget(); +``` + +`parentWidget()` can be given the following values: + +* `left-pane` - This renders the widget on the left side of the screen where the note tree lives. +* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear. +* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits. +* `right-pane` - This renders the widget to the right of any opened notes. + +* * * + +Reference: + +* [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU) +* [https://github.com/zadam/trilium/wiki/Widget-Basics](https://github.com/zadam/trilium/wiki/Widget-Basics) +* [https://github.com/zadam/trilium/wiki/Frontend-Basics](https://github.com/zadam/trilium/wiki/Frontend-Basics) \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md new file mode 100644 index 000000000..e93d63ad7 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md @@ -0,0 +1,15 @@ +# CSS +In `doRender()`: + +``` +this.cssBlock(`#my-widget { + position: absolute; + bottom: 40px; + left: 60px; + z-index: 1; +}`) +``` + +* * * + +Reference: [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU) \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md new file mode 100644 index 000000000..c038765dd --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md @@ -0,0 +1,32 @@ +# Right pane widget +* `doRender` must not be overridden, instead `doRenderBody()` has to be overridden. +* `parentWidget()` must be set to `“rightPane”`. +* `widgetTitle()` getter can optionally be overriden, otherwise the widget will be displayed as “Untitled widget”. + +``` +const template = `
Hi
`; + +class ToDoListWidget extends api.RightPanelWidget { + + get widgetTitle() { + return "Title goes here"; + } + + get parentWidget() { return "right-pane" } + + doRenderBody() { + this.$body.empty().append($(template)); + } + + async refreshWithNote(note) { + this.toggleInt(false); + this.triggerCommand("reEvaluateRightPaneVisibility"); + this.toggleInt(true); + this.triggerCommand("reEvaluateRightPaneVisibility"); + } +} + +module.exports = new ToDoListWidget(); +``` + +The implementation is in `src/public/app/widgets/right_panel_widget.js`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md new file mode 100644 index 000000000..1d51e55d9 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md @@ -0,0 +1,21 @@ +# Building the editor +First, make sure Environment setup is set up. + +## Trigger the build + +``` +cd packages/ckeditor5-build-trilium +yarn build +``` + +This will trigger a change in the `build` directory. + +## Copy the build artifact to the main repo + +Go to `packages/ckeditor5-build-balloon-trilium/build` and copy `ckeditor.js` and `ckeditor.js.map` to `libraries/ckeditor` in the `Notes` repository. + +An example shell command to copy it: + +``` +cp build/ckeditor.* ~/Projects/TriliumNext/Notes/libraries/ckeditor/ +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md new file mode 100644 index 000000000..2d72bd669 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md @@ -0,0 +1,22 @@ +# Differences from upstream +* Embeds [`~~isaul32/ckeditor5-math~~`](https://github.com/isaul32/ckeditor5-math)  ckeditor5-math, which is a third-party plugin for adding math support. CKEditor itself also has a [math plugin](https://ckeditor.com/docs/ckeditor5/latest/features/math-equations.html) with MathType and ChemType but it's premium-only. +* Zadam left a TODO in `findandreplaceUI`: `// FIXME: keyboard shortcut doesn't work:` [`https://github.com/ckeditor/ckeditor5/issues/10645`](https://github.com/ckeditor/ckeditor5/issues/10645) +* `packages\ckeditor5-build-balloon-block\src\mention_customization.js` introduces note insertion via `@` character. + +
Affected fileAffected methodChanged inReason for change
packages/ckeditor5-mention/src/mentionui.tscreateRegExp()6db05043be24bacf9bd51ea46408232b01a1b232 (added back)Allows triggering the autocomplete for labels and attributes in the attribute editor.
init()55a63a1934efb9a520fcc2d69f3ce55ac22aca39Allows dismissing @-mention permanently after pressing ESC, otherwise it would automatically show up as soon as a space was entered.
+ +## Checking the old repo + +Use the following command to identify commits from Zadam: + +``` +git log --oneline --author="adam" --all +``` + +It's best to run the command from zadam's fork of `trilium-ckeditor5` instead of the TriliumNext once since it might not contain all the unmerged branches. + +To show a filtered diff of a commit: + +``` +git show d42e772783 -- ':!*yarn.lock' ':!*packages/ckeditor5-build-balloon-block/build/*' ':!*package.json' +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md new file mode 100644 index 000000000..46bb10792 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md @@ -0,0 +1,26 @@ +# Environment setup +## Clone the repository + +To set up the repository: + +``` +git clone https://github.com/TriliumNext/trilium-ckeditor5.git +``` + +## Install dependencies + +First, install root dependencies: + +``` +cd trilium-ckeditor5 +yarn install +``` + +Secondly, install the Trilium build dependencies: + +``` +cd packages/ckeditor5-build-trilium +yarn install +``` + +To trigger the build, see Building the editor. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md new file mode 100644 index 000000000..ac2ea361d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md @@ -0,0 +1,65 @@ +# Updating to a newer version of CKEditor +## Before updating + +Make sure that all the plugins are compatible with this version:  Versions and external plugins. If not, they will need to be updated to the same version as the one you are updating, by altering their `package.json`. + +If the plugin is external to the Trilium organisation, it needs to be forked first. + +## Environment setup + +The first step is to add the CKEditor source as a remote. This only needs to be done once. + +``` +git remote add upstream ssh://git@github.com/ckeditor/ckeditor5.git +git fetch upstream +``` + +## Update steps + +Due to how the repository is structured, updates to the CKEditor are a bit difficult. + +1. `git fetch upstream` +2. Pick a version and merge with it: `git merge -X theirs v99.2.0` +3. When there are complicated conflicts, sometimes it's easier to take everything from the target version instead, for a given path: `git checkout v99.2.0 -- "packages/ckeditor5-list/**"`. +4. Go in `packages/ckeditor5-build-trilium/package.json` and run `node sync-version.js` to update the `package.json` with the new versions. Review and commit the change. +5. Follow again the dependency setup in Environment setup, as they have changed. +6. [Run the build](Building%20the%20editor.md) and check that it works. + +## Final steps + +1. Start the TriliumNext server +2. If updated to a newer version of CKEditor, check type `CKEDITOR_VERSION` in the browser/Electron console to ensure that the correct version is used. +3. Do a basic sanity check as well. +4. Commit and push the change on both sides (in the `trilium-ckeditor5` repo and in the `Notes` repo). + +## Troubleshooting client side errors + +These errors might show up when testing the Trilium app: + +``` +ReferenceError: CKEditor is not defined +``` + +Usually this is a side effect of another error, check the logs carefully to see if there is any other related error (perhaps a `CKEditorError`). + +* * * + +``` +Uncaught error: Message: CKEditorError: ckeditor-duplicated-modules +``` + +Most likely cause is one of the external plugins is incompatible with this version. + +For example, to disable the Math plugin, go to `packages/ckeditor5-build-trilium/src/config.ts` and modify: + +```diff +-import Math from '@triliumnext/ckeditor5-math/src/math'; +-import AutoformatMath from '@triliumnext/ckeditor5-math/src/autoformatmath'; + +export const COMMON_PLUGINS = [ +- Math, +- AutoformatMath, +] +``` + +In this case, make sure to align the version of all the external plugins with the one you are updating to, usually by forking the external plugin and updating its versions. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md new file mode 100644 index 000000000..14e023654 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md @@ -0,0 +1,4 @@ +# Versions and external plugins +## External plugins + +
trilium-ckeditor543.2.0 
ckeditor5-math See ckeditor5-math.
   
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md new file mode 100644 index 000000000..598042ceb --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md @@ -0,0 +1,30 @@ +# ckeditor5-math +
ckeditor5-math in action.
+ +A fork of [isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math), which is the CKEditor5 plugin which adds the math functionality. The fork was created to handle #297: Insert Math appears to be broken. + +## Development environment + +* Tested on Node.js 20. +* The package manager is yarn 1 (v1.22.22 is known to be working fine for it at the time of writing). +* Committing is protected by `husky` which runs `eslint` to ensure that the code is clean. + +Important commands: + +* To check if the code has any formatting issues: `yarn lint` +* To start a live preview: `yarn start` +* To run the tests: `yarn test` + * Note that this requires Chromium, on NixOS this can be achieved by running a `nix-shell -p chromium`, and running `CHROME_BIN=$(which chromium) yarn test` inside it. + +## 📦 Packages + +The built artifact of the plugin is released by the CI and available on the [GitHub NPM registry](https://github.com/TriliumNext/ckeditor5-math/pkgs/npm/ckeditor5-math). + +Note that due to limitations on GitHub's registry, it is not possible to install this package without setting up a personal access token (even though the package itself is public). See [missing note] for more information. + +## ⬆️ Integrating with CKEditor + +1. Release a new version: Release management & continuous integration +2. In `trilium-ckeditor5`, go to `packages/ckeditor5-build-trilium/package.json` in the CKEditor repository and change the dependency of `@triliumnext/ckeditor5-math` to the newly released version. +3. Run `yarn install`. +4. Proceed with Building the editor to integrate everything into TriliumNext and then commit the change. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md new file mode 100644 index 000000000..e0603cf84 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md @@ -0,0 +1,16 @@ +# Release management & continuous integration +To automate the release process, a GitHub workflow has been added which builds the package and releases it over to GitHub NPM registry. + +The workflow publishes a release whenever a tag with the correct format is pushed. + +The steps are as follows: + +1. Ensure that the source code is clean and ready for a release. +2. Go to `package.json` and bump the `version` field. +3. Commit the changes. +4. Tag the commit with `v1.2.3`, with the correct version number. +5. Push the changes. + +Then follow the CI and it should indicate success. Afterwards, check the [package](https://github.com/TriliumNext/ckeditor5-math/pkgs/npm/ckeditor5-math)section to ensure that the package is in the “Recent Versions” section. + +If the changes could benefit upstream, consider opening a pull request with the changes there as well. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md new file mode 100644 index 000000000..ff322a427 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md @@ -0,0 +1,21 @@ +# Updating with upstream +If there was a change in the upstream repository ([isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math)), it can be integrated as follows: + +1. Add the upstream as remote (`git remote add upstream ssh://git@github.com/isaul32/ckeditor5-math.git`). +2. Fetch the changes: `git fetch upstream` +3. Merge with a tag: `git merge v43.1.2` +4. Solve the conflict in `package.json` by: + 1. Taking the same version as the upcoming one and appending `-hotfix1`. + 2. Keeping the `@triliumnext/ckeditor5-math` name. +5. Install dependencies: `yarn install` +6. Check that the build works via `yarn prepublishOnly`. +7. Commit the changes, push them. +8. Release a version with Release management & continuous integration. + +## CI job not triggered after pushing all the upstream tags + +If the CI job was not triggered, you might have accidentally pushed a lot of tags using `git push --tags`. Manually delete the tag and push it again: + +```diff +git push -d origin v43.1.2-hotfix1 && git push --tags +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png new file mode 100644 index 000000000..51184b130 Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png differ diff --git a/docs/Developer Guide/Developer Guide/Testing.md b/docs/Developer Guide/Developer Guide/Testing.md new file mode 100644 index 000000000..6963f0207 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Testing.md @@ -0,0 +1,33 @@ +# Testing +## Unit testing and integration testing + +Using `vitest`, there are some unit and integration tests done for both the client and the server. + +These tests can be found by looking for the corresponding `.spec.ts` in the same directory as the source file. + +

To run the server-side tests:

npm run server:test

To view the code coverage for the server:

npm run server:coverage

Afterwards, a friendly HTML report can be found in /coverage/index.html.

To run the client-side tests:

npm run client:test

To view the code coverage for the client:

npm run client:coverage

Afterwards, a friendly HTML report can be found in /src/public/app/coverage/index.html.

+ +To run both client and server-side tests: + +``` +npm run test +``` + +Note that some integration tests rely on an in-memory database in order to function.  + +### REST API testing for the server + +Some original work was done by Zadam in `/test-etapi`, using `.http` files. + +New effort using `vitest` and `supertest` to initialize the Express server and run assertions without having to make actual requests to the server. + +An important aspect is that we have access to the Express `app` which allows for interesting assertions such as checking the state of the server, registering debug middleware and so on. + +One example is `src/share/routes.spec.ts`. + +These integration tests are run alongside unit tests. + +## End-to-end testing + +* This tests both the client and the server, by running the server and then using Playwright to query the state of the page. +* These can be found in `/e2e`. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png b/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png new file mode 100644 index 000000000..020472c6d Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png differ diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md new file mode 100644 index 000000000..52663cd80 --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md @@ -0,0 +1,38 @@ +# Running tests +## First-time run + +Before starting Playwright, it has to be installed locally via: + +``` +npx playwright install +``` + +## Starting the integration test server + +There are two types of integration test servers: + +* `npm run integration-mem-db` will run a server with dev mode disabled. + * This is usually what the end user will see when accessing a server instance. + * It will not test the Electron/desktop side of the application. + * Changes to the public scripts will not take effect until running `npm run webpack`. +* `npm run integration-mem-db-dev` will run a server with dev mode enabled. + * This is usually what a dev sees when running `npm run start-server`. + * The difference with the production one is that the assets are loaded directly from files and as such it does not require `npm run webpack` to see changes. + +Either options will open up a server on [localhost:8082](http://localhost:8082) that can be accessed either manually via the browser or via Playwright. + +When asked for a password, the password is `demo1234`. + +## Starting the interactive test runner + +After starting the integration test server, to run the Playwright UI, run in the terminal: + +``` +npx playwright test --ui +``` + +It is also possible to run the interactive code generator instead: + +``` +npx playwright codegen +``` \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md new file mode 100644 index 000000000..fa140503a --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md @@ -0,0 +1,12 @@ +# Setting up authentication +There is a setup test that stores the authentication token so that it can be reused throughout all the tests. + +If tests fail due to being stuck on login, then it must be run. + +To run it manually press “all” near the “Status:” text on top-left of the window + +
+ +Then check “setup” and look for `auth.setup.ts` and press its corresponding Run button: + +
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png new file mode 100644 index 000000000..4b260dfbc Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png differ diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md new file mode 100644 index 000000000..2ad34d2ee --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md @@ -0,0 +1,34 @@ +# Test database +The integration tests do not use the same database as `npm run start-server`. Instead, the database is located `integration-tests/db/document.db`. + +## In-memory database + +Even though we are running our own database, there is still the problem of one test affecting the content for the others or accidentally removing important test notes. + +To avoid this, the integration test server (run via `integration-mem-db`) loads the database from `integration-test` into memory and operates from there. That means that any changes done on the integration test server (port 8082) will not be persisted between restarts of the server. + +Another benefit of having the database in memory for tests is that they can run in parallel without the risk on interfering with each other. + +## How to make changes to the database + +As mentioned previously, the database itself can be edited manually in order to add content that is relevant to the tests. + +In order to do so, run a separate integration test server with: + +``` +npm run integration-edit-db +``` + +This will open up a server on port 8081 which can be used to alter the integration test DB. After finishing the desired changes, it's ideal to close the server to prevent any interferences with further running of tests. + +## The database is tracked by Git + +This is intentional, meaning that any change to the database will mark the file as changed in Git as well. Some tests require a specific note and it would be too wasteful to have to recreate it via Playwright each time. Instead the content is added manually and then the tests operate directly on the said notes. + +In order to make the database easier to track, SQLite WAL was disabled but only for the integration database. This means that only the `.db` file is present and needs to be committed. + +## Cleaning up the database + +It's recommended to clean up any deleted notes to avoid unnecessary changes being committed. To do so go to Recent Changes in the launcher and select “Erase deleted notes now”. + +It's also a good idea to go to Options → Advanced → Vacuum database to clean up it. \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md b/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md new file mode 100644 index 000000000..82158440d --- /dev/null +++ b/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md @@ -0,0 +1,40 @@ +# Error [TransformError]: The package "@esbuild/linux-x64" could not be found, and is needed by esbuild. +Full log: + +``` +> trilium@0.91.1-beta start-server +> cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts + +[nodemon] 3.1.9 +[nodemon] to restart at any time, enter `rs` +[nodemon] watching path(s): src/**/* translations/**/* +[nodemon] watching extensions: ts,js,json +[nodemon] starting `tsx src/main.ts` + +node:internal/process/promises:391 + triggerUncaughtException(err, true /* fromPromise */); + ^ +Error [TransformError]: The package "@esbuild/linux-x64" could not be found, and is needed by esbuild. + +If you are installing esbuild with npm, make sure that you don't specify the +"--no-optional" or "--omit=optional" flags. The "optionalDependencies" feature +of "package.json" is used by esbuild to install the correct binary executable +for your current platform. + at generateBinPath (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1752:15) + at esbuildCommandAndArgs (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1822:33) + at ensureServiceIsRunning (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1979:25) + at transform (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1880:37) + at file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:2755 + at applyTransformers (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:1266) + at transform (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:2702) + at load (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/esm/index.mjs?1734213798404:2:2245) + at async nextLoad (node:internal/modules/esm/hooks:868:22) + at async Hooks.load (node:internal/modules/esm/hooks:451:20) +``` + +The solution is to remove `node_modules` and reinstall all dependencies: + +``` +rm -r node_modules +npm install +``` \ No newline at end of file diff --git a/docs/FJ4VR6G2M6VD.html b/docs/FJ4VR6G2M6VD.html deleted file mode 100644 index 9d7ac0869..000000000 --- a/docs/FJ4VR6G2M6VD.html +++ /dev/null @@ -1,831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Special notes - - -
-
- - - - -

Special notes

- - - - - - -

This note has no content.

- -
- - - - - - -
- - diff --git a/docs/FSZoX3cJlJE7.html b/docs/FSZoX3cJlJE7.html deleted file mode 100644 index 45b704d1e..000000000 --- a/docs/FSZoX3cJlJE7.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - options - - -
-
- - - - -

options

- - - - -
-
Column NameData TypeNullityDefault valueDescription
nameTextNon-null The name of option (e.g. maxContentWidth)
valueTextNon-null The value of the option.
isSyncedIntegerNon-null00 if the option is not synchronized and thus can differ between clients, 1 if the option is synchronized.
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
-
- - - -
- - - - - - -
- - diff --git a/docs/GMta9hBHsXHQ.html b/docs/GMta9hBHsXHQ.html deleted file mode 100644 index f1cee5cd8..000000000 --- a/docs/GMta9hBHsXHQ.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Node.js, Electron and `better-sqlite3` - - -
-
- - - - -

Node.js, Electron and `better-sqlite3`

- - - - -
-

better-sqlite3 requires a native module in order to work. In order to ease the installation process, prebuilt binaries are provided by the library developers.

Trilium Next started with version 8.4.0 for better-sqlite3

better-sqlite3 versionSQLite versionNode.js prebuildsElectron.js prebuilds
8.4.0<3.43.0v20???
8.5.0v20v25
8.5.1 v26
8.5.2v20 (macOS + arm64)
8.6.03.43.0 
8.7.03.43.1 
9.0.03.43.2 v27
9.1.03.44.0 
9.1.1macOS + Alpine
9.2.03.44.2 
9.2.1 / 9.2.2 v28
9.3.03.45.0 
9.4.03.45.1 
9.4.1Windows arm, arm64
9.4.2 <v29
9.4.3 <v29
9.4.4 v29
9.4.5Better prebuilds
9.5.03.45.2 
9.6.03.45.3 v30
10.0.0v22
10.1.03.46.0 
11.0.0>21
11.1.0 (prerelease)  v31
11.1.1  
11.1.2  
-
- - - -
- - - - - - -
- - diff --git a/docs/IuxV242YGaN5.html b/docs/IuxV242YGaN5.html deleted file mode 100644 index c9c418a23..000000000 --- a/docs/IuxV242YGaN5.html +++ /dev/null @@ -1,831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Deleted notes - - -
-
- - - - -

Deleted notes

- - - - - - -

This note has no content.

- -
- - - - - - -
- - diff --git a/docs/KRC2O84LekPz.html b/docs/KRC2O84LekPz.html deleted file mode 100644 index 56a2ea347..000000000 --- a/docs/KRC2O84LekPz.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - i18n-ally - - -
-
- - - - -

i18n-ally

- - - - -
-

i18n-ally is a VS Code extension that aids in internationalization.

It is currently integrated in the project and offers features such as:

  • Highlight, autocomplete translations.
  • Display translations inline.
  • Extract messages into translation.

Extracting messages into translation

  1. Open any .js file and select an untranslated string inside a template (TPL).
  2. Press Ctrl+P and look for “i18n Ally: Extract text into i18n messages”
  3. Select the first template.
  4. Select the path of translation, taking into consideration the Guidelines (e.g. jump_to_note.search-for-note-by-its-name).
-
- - - -
- - - - - - -
- - diff --git a/docs/KbwD5mDpD4CV.html b/docs/KbwD5mDpD4CV.html deleted file mode 100644 index 58b06cdea..000000000 --- a/docs/KbwD5mDpD4CV.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Protected entities - - -
-
- - - - -

Protected entities

- - - - -
-

The following entities can be made protected, via their isProtected flag:

-
- - - -
- - - - - - -
- - diff --git a/docs/MUGBo4n67kBI.html b/docs/MUGBo4n67kBI.html deleted file mode 100644 index 54ce1b705..000000000 --- a/docs/MUGBo4n67kBI.html +++ /dev/null @@ -1,835 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Environment setup - - -
-
- - - - -

Environment setup

- - - - -
-

To set up the repository:

git clone https://github.com/TriliumNext/trilium-ckeditor5.git
-cd trilium-ckeditor5.git
-yarn install

To trigger the build run:

yarn build
-
- - - -
- - - - - - -
- - diff --git a/docs/PHqgH8FCfcod.html b/docs/PHqgH8FCfcod.html deleted file mode 100644 index e7bd1f015..000000000 --- a/docs/PHqgH8FCfcod.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Documentation - - -
-
- - - - -

Documentation

- - - - -
-

Development notes are published on triliumnext.github.io/Notes by the CI using GitHub Pages.

The GitHub Pages deployment works by taking the files from the Notes repository, in the docs directory.

How it works

There is a script that uses wget to download all the files from a share, that means:

  1. You must have a local instance of Trilium Notes server.
  2. You must have the documentation imported, up to date and shared.

Note that currently the documentation source file is not distributed (the note export), until a way is found to automate this process. Contact eliandoran should you require to obtain a copy of the documentation.

Setting up .env file

Go to bin/docs and copy .env.example to .env and edit it:

  1. Change the SHARE_PROTOCOL to either http or https depending on your setup.
  2. Change SHARE_HOST to match the domain name or the URL to the host (without the protocol or any slashes).

Generally ROOT_NOTE_ID should not be changed since the note ID must match if the files were imported correctly.

Triggering a build

Run:

./bin/docs/prepare.sh

This will attempt to download all the notes from the share URL and put them in docs, rewritten for GitHub Pages.

Commit the results and follow the normal development process to push them.

-
- - - -
- - - - - - -
- - diff --git a/docs/QSkfVssHIngA.html b/docs/QSkfVssHIngA.html deleted file mode 100644 index a0b3a9fbd..000000000 --- a/docs/QSkfVssHIngA.html +++ /dev/null @@ -1,831 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Revisions - - -
-
- - - - -

Revisions

- - - - - - -

This note has no content.

- -
- - - - - - -
- - diff --git a/docs/QXCi6Y1SYulw.html b/docs/QXCi6Y1SYulw.html deleted file mode 100644 index 1f760cea8..000000000 --- a/docs/QXCi6Y1SYulw.html +++ /dev/null @@ -1,881 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Adding a new client library - - -
-
- - - - -

Adding a new client library

- - - - -
-

In the past some libraries have been copy-pasted (and adapted if needed) to the repository. However, new libraries must be obtained exclusively through npm.

The first step is to install the desired library. As an example we are going to install i18next:

npm i i18next

Step 1. Understanding the structure of the import

After installing the dependency, it's important to know how it's structured. You can do this by looking at the directory structure of the newly imported dependency:

$ tree node_modules/i18next
-node_modules/i18next
-├── dist
-│   ├── cjs
-│   │   └── i18next.js
-│   ├── esm
-│   │   ├── i18next.bundled.js
-│   │   ├── i18next.js
-│   │   └── package.json
-│   └── umd
-│       ├── i18next.js
-│       └── i18next.min.js
-├── i18next.js
-├── i18next.min.js
-├── index.d.mts
-├── index.d.ts
-├── index.js
-├── index.v4.d.ts
-├── LICENSE
-├── package.json
-├── README.md
-└── typescript
-    ├── helpers.d.ts
-    ├── options.d.ts
-    ├── t.d.ts
-    └── t.v4.d.ts

Generally you should be looking for a .min.js file. Note that the esm and cjs variants generally don't work, we are looking for the classic, no module dependency.

Step 2. Exposing the library from the server

The library must be delivered by the server and this is done via src/routes/assets.ts. In the register function, add a new entry near the bottom of the function:

app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/')));

Step 3. Adding it to the library loader

The library loader is a client module which is in charge of downloading the library from the server and importing it. The loader is located in src/public/app/services/library_loader.js.

To add a new library, start by creating a constant for it, with the value pointing to the minified JS identified at the first step:

const I18NEXT = {
-    js: [
-        "node_modules/i18next/i18next.min.js"
-    ]
-};

Then add it to the export default section:

 export default {
-     requireCss,
-     requireLibrary,
-     CKEDITOR,
-     CODE_MIRROR,
-     ESLINT,
-     RELATION_MAP,
-     PRINT_THIS,
-     CALENDAR_WIDGET,
-     KATEX,
-     WHEEL_ZOOM,
-     FORCE_GRAPH,
-     MERMAID,
-     EXCALIDRAW,
--    MARKJS
-+    MARKJS,
-+    I18NEXT
- }

Step 4. Using the library

To import the library, simply use the following mechanism:

import library_loader from "./library_loader.js";
-
-await library_loader.requireLibrary(library_loader.I18NEXT);

Make sure to replace I18NEXT with the library that was created at the previous steps.

Note that because we are not using a module management mechanism such as ES Modules or Common.js modules, the requireLibrary method does not actually return anything. 

To benefit from the library, it must export on its own an object in window.

In the case of i18next, it sets window.i18next and that can be used directly:

i18next.init({});

Step 5. Adding Electron support

For Electron, the node_modules are copied as a separate step by bin/copy-dist.ts.

Scroll all the way down to the nodeModulesFolder and append an entry for the newly added libraries.

-
- - - -
- - - - - - -
- - diff --git a/docs/QYMncZf5Bu3D.html b/docs/QYMncZf5Bu3D.html deleted file mode 100644 index d57d258a3..000000000 --- a/docs/QYMncZf5Bu3D.html +++ /dev/null @@ -1,833 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - revisions - - -
-
- - - - -

revisions

- - - - -
-
Column NameData TypeNullityDefault valueDescription
revisionIdTextTextNon-null Unique ID of the revision (e.g. 0GjgUqnEudI8).
noteIdTextNon-null ID of the note this revision belongs to.
typeTextNon-null""The type of note (i.e. text, file, code, relationMap, mermaid, canvas).
mimeTextNon-null""The MIME type of the note (e.g. text/html).
titleTextNon-null The title of the note, as defined by the user.
isProtectedIntegerNon-null01 if the entity is protected, 0 otherwise.
blobIdTextNullablenullThe corresponding ID from blobs. Although it can theoretically be NULL, haven't found any such note yet.
utcDateLastEditedTextNon-null Not sure how it differs from modification date.
utcDateCreatedTextNon-null Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModifiedTextNon-null Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
dateLastEditedTextNon-null Not sure how it differs from modification date.
dateCreatedTextNon-null Localized creatino date (e.g. 2023-08-12 15:10:04.045+0300)
-
- - - -
- - - - - - -
- - diff --git a/docs/Release Notes/!!!meta.json b/docs/Release Notes/!!!meta.json new file mode 100644 index 000000000..9a5c041f1 --- /dev/null +++ b/docs/Release Notes/!!!meta.json @@ -0,0 +1,596 @@ +{ + "formatVersion": 2, + "appVersion": "0.92.7", + "files": [ + { + "isClone": false, + "noteId": "hD3V4hiu2VW4", + "notePath": [ + "hD3V4hiu2VW4" + ], + "title": "Release Notes", + "notePosition": 1, + "prefix": null, + "isExpanded": false, + "type": "book", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "sorted", + "value": "", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "expanded", + "value": "", + "isInheritable": false, + "position": 60 + }, + { + "type": "label", + "name": "sortNatural", + "value": "", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "sortDirection", + "value": "desc", + "isInheritable": false, + "position": 20 + }, + { + "type": "label", + "name": "viewType", + "value": "grid", + "isInheritable": false, + "position": 40 + } + ], + "attachments": [], + "dirFileName": "Release Notes", + "children": [ + { + "isClone": false, + "noteId": "VN3xnce1vLkX", + "notePath": [ + "hD3V4hiu2VW4", + "VN3xnce1vLkX" + ], + "title": "v0.93.0", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.93.0.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "WRaBfQqPr6qo", + "notePath": [ + "hD3V4hiu2VW4", + "WRaBfQqPr6qo" + ], + "title": "v0.92.7", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "language", + "value": "en", + "isInheritable": false, + "position": 40 + } + ], + "format": "markdown", + "dataFileName": "v0.92.7.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "a2rwfKNmUFU1", + "notePath": [ + "hD3V4hiu2VW4", + "a2rwfKNmUFU1" + ], + "title": "v0.92.6", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.6.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "fEJ8qErr0BKL", + "notePath": [ + "hD3V4hiu2VW4", + "fEJ8qErr0BKL" + ], + "title": "v0.92.5-beta", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.5-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "kkkZQQGSXjwy", + "notePath": [ + "hD3V4hiu2VW4", + "kkkZQQGSXjwy" + ], + "title": "v0.92.4", + "notePosition": 50, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.4.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "vAroNixiezaH", + "notePath": [ + "hD3V4hiu2VW4", + "vAroNixiezaH" + ], + "title": "v0.92.3-beta", + "notePosition": 60, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.3-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "mHEq1wxAKNZd", + "notePath": [ + "hD3V4hiu2VW4", + "mHEq1wxAKNZd" + ], + "title": "v0.92.2-beta", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.2-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "IykjoAmBpc61", + "notePath": [ + "hD3V4hiu2VW4", + "IykjoAmBpc61" + ], + "title": "v0.92.1-beta", + "notePosition": 80, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.1-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "dq2AJ9vSBX4Y", + "notePath": [ + "hD3V4hiu2VW4", + "dq2AJ9vSBX4Y" + ], + "title": "v0.92.0-beta", + "notePosition": 90, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.92.0-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "3a8aMe4jz4yM", + "notePath": [ + "hD3V4hiu2VW4", + "3a8aMe4jz4yM" + ], + "title": "v0.91.6", + "notePosition": 100, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.6.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "8djQjkiDGESe", + "notePath": [ + "hD3V4hiu2VW4", + "8djQjkiDGESe" + ], + "title": "v0.91.5", + "notePosition": 110, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.5.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "OylxVoVJqNmr", + "notePath": [ + "hD3V4hiu2VW4", + "OylxVoVJqNmr" + ], + "title": "v0.91.4-beta", + "notePosition": 120, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.4-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "tANGQDvnyhrj", + "notePath": [ + "hD3V4hiu2VW4", + "tANGQDvnyhrj" + ], + "title": "v0.91.3-beta", + "notePosition": 130, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.3-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "hMoBfwSoj1SC", + "notePath": [ + "hD3V4hiu2VW4", + "hMoBfwSoj1SC" + ], + "title": "v0.91.2-beta", + "notePosition": 140, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.2-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "a2XMSKROCl9z", + "notePath": [ + "hD3V4hiu2VW4", + "a2XMSKROCl9z" + ], + "title": "v0.91.1-beta", + "notePosition": 150, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.91.1-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "yqXFvWbLkuMD", + "notePath": [ + "hD3V4hiu2VW4", + "yqXFvWbLkuMD" + ], + "title": "v0.90.12", + "notePosition": 160, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.12.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "veS7pg311yJP", + "notePath": [ + "hD3V4hiu2VW4", + "veS7pg311yJP" + ], + "title": "v0.90.11-beta", + "notePosition": 170, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.11-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "sq5W9TQxRqMq", + "notePath": [ + "hD3V4hiu2VW4", + "sq5W9TQxRqMq" + ], + "title": "v0.90.10-beta", + "notePosition": 180, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.10-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "yFEGVCUM9tPx", + "notePath": [ + "hD3V4hiu2VW4", + "yFEGVCUM9tPx" + ], + "title": "v0.90.9-beta", + "notePosition": 190, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.9-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "o4wAGqOQuJtV", + "notePath": [ + "hD3V4hiu2VW4", + "o4wAGqOQuJtV" + ], + "title": "v0.90.8", + "notePosition": 200, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "includeNoteLink", + "value": "i4A5g9iOg9I0", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "includeNoteLink", + "value": "G4PAi554kQUr", + "isInheritable": false, + "position": 20 + } + ], + "format": "markdown", + "dataFileName": "v0.90.8.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "i4A5g9iOg9I0", + "notePath": [ + "hD3V4hiu2VW4", + "i4A5g9iOg9I0" + ], + "title": "v0.90.7-beta", + "notePosition": 210, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.7-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "ThNf2GaKgXUs", + "notePath": [ + "hD3V4hiu2VW4", + "ThNf2GaKgXUs" + ], + "title": "v0.90.6-beta", + "notePosition": 220, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.6-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "G4PAi554kQUr", + "notePath": [ + "hD3V4hiu2VW4", + "G4PAi554kQUr" + ], + "title": "v0.90.5-beta", + "notePosition": 230, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.5-beta.md", + "attachments": [ + { + "attachmentId": "fYtz4wC6u6wN", + "title": "image.png", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "v0.90.5-beta_image.png" + } + ] + }, + { + "isClone": false, + "noteId": "zATRobGRCmBn", + "notePath": [ + "hD3V4hiu2VW4", + "zATRobGRCmBn" + ], + "title": "v0.90.4", + "notePosition": 240, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.4.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "sCDLf8IKn3Iz", + "notePath": [ + "hD3V4hiu2VW4", + "sCDLf8IKn3Iz" + ], + "title": "v0.90.3", + "notePosition": 250, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.3.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "VqqyBu4AuTjC", + "notePath": [ + "hD3V4hiu2VW4", + "VqqyBu4AuTjC" + ], + "title": "v0.90.2-beta", + "notePosition": 260, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.2-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "RX3Nl7wInLsA", + "notePath": [ + "hD3V4hiu2VW4", + "RX3Nl7wInLsA" + ], + "title": "v0.90.1-beta", + "notePosition": 270, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.1-beta.md", + "attachments": [] + }, + { + "isClone": false, + "noteId": "GyueACukPWjk", + "notePath": [ + "hD3V4hiu2VW4", + "GyueACukPWjk" + ], + "title": "v0.90.0-beta", + "notePosition": 280, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "markdown", + "dataFileName": "v0.90.0-beta.md", + "attachments": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.0-beta.md b/docs/Release Notes/Release Notes/v0.90.0-beta.md new file mode 100644 index 000000000..00daf4c0c --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.0-beta.md @@ -0,0 +1,36 @@ +# v0.90.0-beta +**Note:** This version is currently not meant for public use, but for internal testing. The reason is that it might be quite unstable. Nevertheless, feel free to test if you understand the risks. + +### What's new compared to the latest version of Trilium Notes (0.63.7)? + +Theoretically, nothing. This version is based on Trilium Notes 0.63.7, but it introduces a significant refactoring of the code: the server has been ported from JavaScript to TypeScript. + +To the end user, nothing should change, except that some things might have been broken in the process. + +For the first release of TriliumNext, we simply need to check if there are any regressions by using it in day-to-day activities. Feel free to [report any issues](https://github.com/TriliumNext/Notes/issues/new/choose). + +### What do I need to do in order to start? + +For the desktop application, simply download it from the releases page and extract it in a similar fashion to how the original Trilium Notes is installed. + +Regarding data, please note that **TriliumNext Notes shares the same database as Trilium Notes.** A manual database backup is strongly encouraged: + +* On Windows, open Windows Explorer and type `%appdata%` in the address bar. Look for `trilium-data` and simply copy and paste it. +* On Linux, the data directory is in `~/.local/share/trilium-data`. + +Generally your existing notes should be quite safe (there are no database migrations or API changes, only the consistency checks could have been impacted), haven't noticed any issues when testing. + +Additionally, since the sync version has not changed either, you should be able to mix the client and the server between Trilium Notes and TriliumNext Notes. + +The server is also packaged and available in the release. For the Docker build, the Docker image is built automatically and is available on GitHub's Docker registry. + +### Why v0.90.0 + +The current release from [zadam/trilium](https://github.com/zadam/trilium/releases/tag/v0.63.7) is 0.63.7. + +If we reset the version number to 0.1, there are a few downsides: + +* This might cause issues with migration and updates checking. +* The fact that TriliumNext 0.1.0 is based off of Trilium 0.63.7 can be rather confusing. + +At the same time we would not want to bump directly to the next version as we would directly enter in conflict with future releases of Trilium. As such, we are bumping it to 0.90 instead. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.1-beta.md b/docs/Release Notes/Release Notes/v0.90.1-beta.md new file mode 100644 index 000000000..3e89c7bbb --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.1-beta.md @@ -0,0 +1,36 @@ +# v0.90.1-beta +The key highlight of this version is the large number of library updates, bringing them to the latest version. + +## ⚙️ Windows Installer + +A Squirrel-based installer for the Windows 64-bit platform is now provided with each release. + +Simply download the installer and run it and the application should be installed automatically. + +## 🐞 Bugfixes + +The following regressions due to the conversion to TypeScript has been solved, compared to v0.90.0: + +* [Searching note content fails](https://github.com/TriliumNext/Notes/issues/211) +* [Canvas: "Copy image reference to clipboard" no longer working](https://github.com/TriliumNext/Notes/pull/227) +* [v.0.90.0-beta random errors when loading notes](https://github.com/TriliumNext/Notes/issues/238) +* [Frontend JS code exeution error](https://github.com/TriliumNext/Notes/issues/244) +* [Sync failed: Cannot read properties of undefined (reading 'utcDateChanged')](https://github.com/TriliumNext/Notes/issues/210) + +## ⬆️ Library updates + +### Client-side library updates + +
LibraryOld versionNew version
axios1.6.71.7.2
excalidraw0.17.30.17.6
katex0.16.90.16.11
mermaid10.9.010.9.1
react, react-dom18.2.018.3.1
+ +### Server-side library updates + +
LibraryOld versionNew version
sanitize-url6.0.47.1.0
archiver7.0.07.0.1
marked12.0.013.0.2
sanitize-html1.6.71.7.2
turndown7.1.27.2.0
yauzl3.1.23.1.3
express4.18.34.19.2
express-rate-limit7.2.07.3.1
jsdom24.0.024.1.0
ws8.16.08.18.0
ejs3.1.93.1.10
dayjs1.11.101.11.12
semver7.6.07.6.3
async-mutex0.4.10.5.0
https-proxy-agent7.0.47.0.5
sax1.3.01.4.1
ini3.0.14.1.3
debounce1.2.12.1.0
+ +## ✨ Technical improvements + +* Updated from Node 18.8.2 to 20.15.1. +* Reduced the binary size (see [#252](https://github.com/TriliumNext/Notes/pull/252)). +* Removed redundant `open` dependency. +* Updated internal tooling (`rimraf`, `webpack`, `nodemon`, `jsdoc`). +* Updated TypeScript to latest. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.10-beta.md b/docs/Release Notes/Release Notes/v0.90.10-beta.md new file mode 100644 index 000000000..bf228c040 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.10-beta.md @@ -0,0 +1,35 @@ +# v0.90.10-beta +## 💡 Key highlights + +* Syntax highlight in code blocks in editable and read-only text notes (via Highlight.js), heavily based on [antoniotejada](https://github.com/antoniotejada)'s [Trilium-SyntaxHighlightWidget](https://github.com/antoniotejada/Trilium-SyntaxHighlightWidget). + +## 🐞 Bugfixes + +* Code note overlapping with note list. +* Fix error when running a script in 0.90.9-beta. +* Font section in Appearance settings blinking when settings were being updated. +* [Crop fileName and prevent cutting into the extension](https://github.com/TriliumNext/Notes/pull/541) / Canvas note exported to html can't be view due to title name is 13-15 digits long by @SiriusXT +* Duplicate note count in delete relation modal. +* Docnotes (e.g. launcher configuration descriptions) not showing on Electron/desktop builds. + +## ✨ Improvements + +* Adjustable word-wrap for code blocks in text notes. +* Adjustable theme for code blocks in text notes, plus slightly improved design. +* The application should now be more highly tolerant to errors caused by custom widgets and scripts, not causing the UI to also crash (see [#511](https://github.com/TriliumNext/Notes/issues/511)). +* [Close tabs to the right](https://github.com/TriliumNext/Notes/pull/542) by @SiriusXT +* [Automatically trigger autocomplete on focus](https://github.com/TriliumNext/Notes/pull/534) by @SiriusXT + +## 🌍 Internationalization + +* Translate weekday and month names for daily notes. +* Update server translations when switching language. +* Translate search note prefix. +* Translate sync test messages. + +## ⬆️ Library updates + +* mind-elixir: 4.2.3 → 4.2.4 +* mermaid: 11.3.0 → 11.4.0 +* i18next: 23.16.2 -> 23.16.4 +* and other internal development tooling updates. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.11-beta.md b/docs/Release Notes/Release Notes/v0.90.11-beta.md new file mode 100644 index 000000000..d1d00ba0e --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.11-beta.md @@ -0,0 +1,32 @@ +# v0.90.11-beta +## 💡 Key highlights + +* Now it is possible to have a fixed toolbar for editing text notes instead of the standard floating one, see [Classic editor for text notes (with fixed toolbar)](https://github.com/TriliumNext/Notes/pull/571) for more information. + +## 🐞 Bugfixes + +* [Double input after spaces on Android](https://github.com/TriliumNext/Notes/issues/568) +* [Hyperlinks with custom protocols not working](https://github.com/TriliumNext/Notes/issues/122) by @SiriusXT +* [Internal Link: Two Tooltips are displayed](https://github.com/TriliumNext/Notes/issues/525) +* Fixed loading of syntax highlighting themes for server and Docker builds. + +## ✨ Improvements + +* [Insert inline images in text notes](https://github.com/TriliumNext/Notes/issues/531) (also grouped the image alignment buttons in subcategories since there were too many of them). +* [Explicitly manage the "latest" tag, and have it point to the same tag as "stable"](https://github.com/TriliumNext/Notes/pull/545) by @perfectra1n +* [Make attachments open in a new tab/browser](https://github.com/TriliumNext/Notes/pull/559) by @SiriusXT +* [mouse scroll wheel direction for zoom level](https://github.com/TriliumNext/Notes/pull/555) by @rom1dep +* Apply syntax highlight to preview of code notes as well. +* [More reliably check for version updates](https://github.com/TriliumNext/Notes/pull/574) by @perfectra1n +* README improvements by @perfectra1n + +## 🌍 Internationalization + +* Preliminary support for the German language by @j13055 +* Improved French translations by @Potjoe-97 +* Improved Spanish translations by @hasecilu + +## ⬆️ Library updates + +* mind-elixir: 4.2.4 -> 4.3.1 +* client: force-graph: 1.45.0 -> 1.46.0 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.12.md b/docs/Release Notes/Release Notes/v0.90.12.md new file mode 100644 index 000000000..ff9727771 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.12.md @@ -0,0 +1,131 @@ +# v0.90.12 +## 💡 Key highlights + +* Now it is possible to have a fixed toolbar for editing text notes instead of the standard floating one, see [Classic editor for text notes (with fixed toolbar)](https://github.com/TriliumNext/Notes/pull/571) for more information. +* Syntax highlight in code blocks in editable and read-only text notes (via Highlight.js), heavily based on [antoniotejada](https://github.com/antoniotejada)'s [Trilium-SyntaxHighlightWidget](https://github.com/antoniotejada/Trilium-SyntaxHighlightWidget). + +## Changes since the last beta release + +### 🐞 Bugfixes + +* [Shell (Bash) Syntax Highlight](https://github.com/TriliumNext/Notes/issues/583) +* [0 Max content width causes editor issues](https://github.com/TriliumNext/Notes/issues/593) +* [Share icon not visible in note icon picker](https://github.com/TriliumNext/Notes/issues/603) + +### ✨ Improvements + +* Add find & replace button to fixed toolbar +* [Add more link protocol support](https://github.com/TriliumNext/Notes/pull/589) by @SiriusXT +* [Add a text replacement feature to the find\_widget](https://github.com/TriliumNext/Notes/pull/576) by @SiriusXT +* [Replace the editor type combo box with radio buttons](https://github.com/TriliumNext/Notes/pull/587) by @adoriandoran +* [Add box icons to note menu & attachments](https://github.com/TriliumNext/Notes/pull/609) by @SiriusXT +* [Improve the "Existing backups" section](https://github.com/TriliumNext/Notes/pull/615) by @adoriandoran +* [Trigger full text search when Ctrl + Enter is pressed in note autocomplete](https://github.com/TriliumNext/Notes/pull/585) by @SiriusXT +* [Improve context menus](https://github.com/TriliumNext/Notes/pull/618) (+ [Replace (?) with information icon](https://github.com/TriliumNext/Notes/issues/613)) by @adoriandoran +* [Add Options Launcher](https://github.com/TriliumNext/Notes/issues/619) +* [Change note clone icon indicator from star to link icon](https://github.com/TriliumNext/Notes/issues/565) +* [Add "Reopen last tab" and "Copy tab to new window" to tab management](https://github.com/TriliumNext/Notes/pull/651) by @SiriusXT + +### 🌍 Internationalization + +* Improved Spanish translations by @hasecilu +* Improved German translations by @j13055 +* [Improve note revision wording and consistency](https://github.com/TriliumNext/Notes/pull/612) by @meichthys + +### ⬆️ Library updates and technical improvements + +* [Add renovate GitHub Action and JSON config](https://github.com/TriliumNext/Notes/pull/607) by @perfectra1n + +## From `v0.90.11-beta` + +### 🐞 Bugfixes + +* [Double input after spaces on Android](https://github.com/TriliumNext/Notes/issues/568) +* [Hyperlinks with custom protocols not working](https://github.com/TriliumNext/Notes/issues/122) by @SiriusXT +* [Internal Link: Two Tooltips are displayed](https://github.com/TriliumNext/Notes/issues/525) +* Fixed loading of syntax highlighting themes for server and Docker builds. + +### ✨ Improvements + +* [Insert inline images in text notes](https://github.com/TriliumNext/Notes/issues/531) (also grouped the image alignment buttons in subcategories since there were too many of them). +* [Explicitly manage the "latest" tag, and have it point to the same tag as "stable"](https://github.com/TriliumNext/Notes/pull/545) by @perfectra1n +* [Make attachments open in a new tab/browser](https://github.com/TriliumNext/Notes/pull/559) by @SiriusXT +* [mouse scroll wheel direction for zoom level](https://github.com/TriliumNext/Notes/pull/555) by @rom1dep +* Apply syntax highlight to preview of code notes as well. +* [More reliably check for version updates](https://github.com/TriliumNext/Notes/pull/574) by @perfectra1n +* README improvements by @perfectra1n + +### 🌍 Internationalization + +* Preliminary support for the German language by @j13055 +* Improved French translations by @Potjoe-97 +* Improved Spanish translations by @hasecilu + +### ⬆️ Library updates + +* mind-elixir: 4.2.4 -> 4.3.1 +* client: force-graph: 1.45.0 -> 1.46.0 + +## From `v0.90.10-beta` + +### 🐞 Bugfixes + +* Code note overlapping with note list. +* Fix error when running a script in 0.90.9-beta. +* Font section in Appearance settings blinking when settings were being updated. +* [Crop fileName and prevent cutting into the extension](https://github.com/TriliumNext/Notes/pull/541) / Canvas note exported to html can't be view due to title name is 13-15 digits long by @SiriusXT +* Duplicate note count in delete relation modal. +* Docnotes (e.g. launcher configuration descriptions) not showing on Electron/desktop builds. + +### ✨ Improvements + +* Adjustable word-wrap for code blocks in text notes. +* Adjustable theme for code blocks in text notes, plus slightly improved design. +* The application should now be more highly tolerant to errors caused by custom widgets and scripts, not causing the UI to also crash (see [#511](https://github.com/TriliumNext/Notes/issues/511)). +* [Close tabs to the right](https://github.com/TriliumNext/Notes/pull/542) by @SiriusXT +* [Automatically trigger autocomplete on focus](https://github.com/TriliumNext/Notes/pull/534) by @SiriusXT + +### 🌍 Internationalization + +* Translate weekday and month names for daily notes. +* Update server translations when switching language. +* Translate search note prefix. +* Translate sync test messages. + +### ⬆️ Library updates + +* mind-elixir: 4.2.3 → 4.2.4 +* mermaid: 11.3.0 → 11.4.0 +* i18next: 23.16.2 -> 23.16.4 +* and other internal development tooling updates. + +## From `v0.90.9-beta` + +### 🐞 Bugfixes + +* [Promoted boolean attributes no long showing checkbox when the value is true](https://github.com/TriliumNext/Notes/issues/503) +* [Import of .htm keeps htm in title while html does not](https://github.com/TriliumNext/Notes/issues/500) + +### ✨ Improvements + +* [Syntax highlighting in read-only mode code notes](https://github.com/TriliumNext/Notes/issues/504) (also for “View source”). +* [Server 404 logs are now one-line instead of a full stack trace](https://github.com/TriliumNext/Notes/issues/505) + +### 🌍 Internationalization + +* Translated the toast notifications, errors and confirmation boxes. +* Translated the launcher context menu. + +### ⬆️ Library updates + +* i18next: 23.16.0 -> 23.16.2 +* mind-elixir: 4.2.0 -> 4.2.2 +* vanilla-js-wheel-zoom: 9.0.2 -> 9.0.4 +* cookie-parser: 1.4.6 → 1.4.7 +* image-type: 4.1.0 → 5.2.0 +* express, express-rate-limit, express-session updated to latest. +* cookie-parser: 1.4.6 → 1.4.7 +* marked: 14.1.2 → 14.1.3 +* sanitize-html: 2.13.0 → 2.13.1 +* force-graph: 1.43.5 → 1.45.0 +* and others (jasmine, debounce) \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.2-beta.md b/docs/Release Notes/Release Notes/v0.90.2-beta.md new file mode 100644 index 000000000..9c0af0417 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.2-beta.md @@ -0,0 +1,20 @@ +# v0.90.2-beta +## 🐞 Bugfixes + +* [(Bug report) Initial sync doesn't finish](https://github.com/TriliumNext/Notes/issues/266) + +## ✨ Technical Improvements + +Important changes: + +* The biggest change is that the server has been ported from Common.js to ES Modules. Note that a change of this proportion is bound to cause some quirks throughout the application, bug reports are welcome. +* Updated Electron from 25.9.8 to 31.2.1 ([#231](https://github.com/TriliumNext/Notes/pull/231)) + +Additionally: + +* Updated CKEditor from 41.0.0 to 41.4.2. +* Started adding support for internationalization ([#248](https://github.com/TriliumNext/Notes/pull/248)). The application will soon be able to be translated into multiple languages. +* Improved error management for scripting: + * If a critical unexpected error occurs from a widget, a toast is shown explaining the situation and advising to enter safe mode, as opposed to a blank page. + * If a basic widget fails to render, a toast is shown instead of crashing the application. +* Toast messages now support newlines (by adding a `\n` to the message) for slightly better formatting. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.3.md b/docs/Release Notes/Release Notes/v0.90.3.md new file mode 100644 index 000000000..aafb68ea5 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.3.md @@ -0,0 +1,50 @@ +# v0.90.3 +This is the first public release of TriliumNext considered stable. + +## ❔ Why TriliumNext? + +TriliumNext has started as a fork of [Trilium Notes](https://github.com/zadam/trilium) at the beginning of 2024. The reason for the work is that the upstream project has entered [maintenance phase](https://github.com/zadam/trilium/issues/4620) and we would like to extend the application. + +The work so far has focused more on the technical aspects because most of the work has been done by @zadam and handing over a project of this size is non-trivial. Some more technical work will be done in the upcoming versions after which the project can focus on improving the user experience as much as possible. + +As a short overview of what's planned next from a user-facing point of view: + +* Support for multiple languages. +* Improving the existing theme and decluttering the UI. +* Mobile improvements. +* Exploring additional editors such as a MarkDown-based editor. + +## ⬆️ Porting from Trilium Notes? + +There is no change in the database structure. + +TriliumNext Notes can be run instead of the original Trilium Notes and it should work out of the box, since it will reuse the same database. + +It should also be possible to downgrade back to Trilium Notes if required, without any changes or loss of data. + +Similarly goes for the server, it should work out of the box. It is possible to mix and match between Trilium Notes and TriliumNext Notes. + +## 🐞 How stable is the version + +Generally you should not encounter any breaking bugs as the prior versions have been tested and daily-driven for a few weeks now. + +Should you encounter any issue, feel free to report them on [our GitHub issues](https://github.com/TriliumNext/Notes/issues). + +## ✨ Key highlights + +* Fixed (from v0.90.2-beta) + * Fixed [Error importing zip file](https://github.com/TriliumNext/Notes/issues/281) + * Fixed Alt+Left and Alt+Right navigation would not work under Electron. + * Added a fresh new icon to represent our ongoing effort to improve Trilium. +* v0.90.2-beta + * Fixed some issues with the sync. + * Ported the server from Common.js to ES modules. + * Updated the CKEditor from 41.0.0 to 41.4.2. + * Updated Electron from 25.9.8 (marked as end-of-life) to 31.2.1. + * Started adding support for internationalization ([#248](https://github.com/TriliumNext/Notes/pull/248)). The application will soon be able to be translated into multiple languages. + * Improved error management for scripting +* v0.90.1-beta + * Introduced a Windows installer instead of the .zip installation. + * Bug fixes related to the TypeScript port of the server. +* v0.90.0-beta + * On a technical side, the server was rewritten in TypeScript. This should improve the stability of both current and future developments thanks to the language's type safety. It will also make the development slightly easier. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.4.md b/docs/Release Notes/Release Notes/v0.90.4.md new file mode 100644 index 000000000..cb49828a0 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.4.md @@ -0,0 +1,21 @@ +# v0.90.4 +This release focuses mostly on improving the experience with the Docker containers. + +## Docker ARM builds + +* [ARM builds for Docker were reintroduced](https://github.com/TriliumNext/Notes/issues/302) and are available on both [Docker Hub](https://hub.docker.com/r/triliumnext/notes) and [GHCR](https://github.com/TriliumNext/Notes/pkgs/container/notes)starting with this release. + * Note that only `ARMv7` and `ARM64/v8` architectures are supported for now. + * `ARMv6` support was dropped due to incompatibility issues with Node 20. + +## 🐞 Bugfixes + +* [Docker container marked as not healthy](https://github.com/TriliumNext/Notes/issues/296) +* [Find/Replace dialog doesn't match theme](https://github.com/TriliumNext/Notes/issues/304) +* [Tray icon is missing on windows](https://github.com/TriliumNext/Notes/issues/311) +* [Error when Duplicating subtree of note that contains broken internal trilium link](https://github.com/TriliumNext/Notes/issues/308) +* [Update available points to Trilium download instead of TriliumNext](https://github.com/TriliumNext/Notes/issues/313) + +## Additional changes + +* [Update `docker-compose.yml`](https://github.com/TriliumNext/Notes/pull/309) by slashtechno +* Updated links throughout the application to point to the rendered [TriliumNext Wiki](https://triliumnext.github.io/Docs) \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.5-beta.md b/docs/Release Notes/Release Notes/v0.90.5-beta.md new file mode 100644 index 000000000..5634ce676 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.5-beta.md @@ -0,0 +1,66 @@ +# v0.90.5-beta +This release brings a few quality-of-life improvements, as well as bugfixes. The main highlight, however, is the increased support for localization as well as a new note type. + +## 🌍 Internationalization + +Preliminary support for internationalization has been added, thanks to the efforts of `@Nriver`. More specifically, the following languages now have a partial translation: + +* Spanish by `@hasecilu` +* Chinese by `@Nriver` +* Romanian. + +Note that only the client has been translated so far and there are still a lot of tasks to handle before the application is fully translated. + +## 💡 New note type: Mind map + +Mind maps are usually a fast method to write down ideas and do brainstorming with a team. TriliumNext introduces this new note type with the help of [Mind Elixir](https://mind-elixir.com/), an open-source library. + +Note that the integration is still in its infancy, so there might be bugs and a lack of feature until it is stabilized. The library itself supports quite a few features that are not implemented yet in Trilium: math expressions using KaTeX, images, links. Those might be implemented along the way, provided they are needed by users. + +
+ +> **Note**: For the note type to fully work (such as shared notes), it is ideal to update the server instance as well, if you are using one. + +## ⚙️ Builds + +The internal build tooling has been updated, resulting in the following changes: + +* For macOS users there is now a `.dmg` installation method as well. `@JYC333` + * There is also an ARM-native version for macOS that can be used, however it will require [additional steps to get it running](https://github.com/TriliumNext/Notes/issues/329) due to the lack of notarization (to bypass the “TriliumNext Notes.app” is damaged error). +* For both Windows and Linux we now have `amd64` builds as well. + * Please note these versions are not being actively tested due to lack of equipment, so feel free to raise any issues you might be encountering. + +On the Docker side, [brought back Alpine-based Docker containers for amd64](https://github.com/TriliumNext/Notes/pull/366) by `@perfectrain`. + +### 🐞 Bugfixes + +* [v0.90.4 docker does not read USER\_UID and USER\_GID from environment](https://github.com/TriliumNext/Notes/issues/331) +* [Invalid CSRF token on Android phone](https://github.com/TriliumNext/Notes/issues/318) +* [Excess spacing in lists](https://github.com/TriliumNext/Notes/issues/341) +* [scrollbar-color makes the scrollbar appear natively styled](https://github.com/TriliumNext/Notes/issues/350) +* Invisible scrollbar on Firefox +* [Issue with note title text box border](https://github.com/TriliumNext/Notes/issues/358) +* [Focus not set to input field when clicking `Include Note` from Block Editor](https://github.com/TriliumNext/Notes/issues/365) +* [Fix a bug of find widget](https://github.com/TriliumNext/Notes/pull/377) by `@SiriusXT` +* ['A JavaScript error occurred in the main process' when launching Trilium](https://github.com/TriliumNext/Notes/issues/368) (improved error handling). +* [Note Tooltip isn't removed when clicking on internal trilium link in read-only mode](https://github.com/TriliumNext/Notes/issues/375) +* [Calendar dropdown won't close if click/right-click other button that open notes from launcher bar](https://github.com/TriliumNext/Notes/issues/384) + +### ✨ Improvements + +* [Improved the calendar button in the launch bar](https://github.com/TriliumNext/Notes/issues/374), adding support for easier month and year selection. +* [Make first day of week configurable](https://github.com/TriliumNext/Notes/issues/247) (supports Sunday and Monday) + * Can be adjusted in Options → Appearance → Localization → First day of the week. + * The option is synced with the server and the client updates immediately. +* Removed hard-coded libraries such as FancyTree and Bootstrap. This will later allow us to upgrade to the latest versions. `@JYC333` +* [Hide Electron-specific settings in the web version](https://github.com/TriliumNext/Notes/issues/345) +* [Add a toggle to promote the current note to a template](https://github.com/TriliumNext/Notes/issues/348) +* Disable share toggle when in options +* [Open New window from taskbar](https://github.com/TriliumNext/Notes/pull/373) by `@SiriusXT` +* Pressing F2 to edit the branch prefix will now work only in the note tree to avoid issues with other interactive elements such as the mind map, but also since the option itself is not the most used ones to require a global shortcut. + +## New Contributors + +* [@JYC333](https://github.com/JYC333) made their first contribution in [#294](https://github.com/TriliumNext/Notes/pull/294) +* [@hasecilu](https://github.com/hasecilu) made their first contribution in [#349](https://github.com/TriliumNext/Notes/pull/349) +* [@SiriusXT](https://github.com/SiriusXT) made their first contribution in [#377](https://github.com/TriliumNext/Notes/pull/377) \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.5-beta_image.png b/docs/Release Notes/Release Notes/v0.90.5-beta_image.png new file mode 100644 index 000000000..b4963d431 Binary files /dev/null and b/docs/Release Notes/Release Notes/v0.90.5-beta_image.png differ diff --git a/docs/Release Notes/Release Notes/v0.90.6-beta.md b/docs/Release Notes/Release Notes/v0.90.6-beta.md new file mode 100644 index 000000000..423c0d6ec --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.6-beta.md @@ -0,0 +1,2 @@ +# v0.90.6-beta +This is a Docker-only re-release of [v0.90.5-beta](https://github.com/TriliumNext/Notes/releases/tag/v0.90.6-beta) that **adds back the amd64 container image** which was previously not available due to a race condition on our build mechanism. For now, this means that the Alpine container mentioned in the previous release is no longer available, as we have fallen back to Debian Slim. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.7-beta.md b/docs/Release Notes/Release Notes/v0.90.7-beta.md new file mode 100644 index 000000000..79a3be12a --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.7-beta.md @@ -0,0 +1,52 @@ +# v0.90.7-beta +## ⚠️ Important notes + +* 0.90.5-beta & 0.90.6-beta have an incompatibility with older sync servers if using mind maps, causing them to get turned to a file. If you are impacted by this, see [mindmap note suddenly show with file note](https://github.com/TriliumNext/Notes/issues/467#issuecomment-2402853189). +* Sync version has been increased from 32 to 33 to prevent issues with mindmap notes. This means that the server also needs to be updated to this version, otherwise sync will be refused. + +## 🐞 Bugfixes + +* [Insert Math appears to be broken](https://github.com/TriliumNext/Notes/issues/297) +* [Override the z-index of Bootstrap tooltips](https://github.com/TriliumNext/Notes/pull/472) by [dousha](https://github.com/dousha) +* [Clicking an entry in the calendar opens the note twice](https://github.com/TriliumNext/Notes/issues/417) +* [mindmap note suddenly show with file note](https://github.com/TriliumNext/Notes/issues/467) +* [Import md with jpeg: broken jpeg](https://github.com/TriliumNext/Notes/pull/458) by [`perfectra1n`](https://github.com/perfectra1n) + +## ✨ Improvements + +* [Icon improvements](https://github.com/TriliumNext/Notes/pull/392) by `@SiriusXT`. +* [Open in file manager of Trilium's data directory](https://github.com/TriliumNext/Notes/pull/389) by `@SiriusXT`. +* [i18n Fallback to English for null characters](https://github.com/TriliumNext/Notes/pull/414) by `@SiriusXT`. +* [Use matrix strategy for CI/CD](https://github.com/TriliumNext/Notes/pull/402#top) (Alpine is back!) by `perfectra1n`. +* [Floating buttons can be displayed again after being closed](https://github.com/TriliumNext/Notes/pull/432) by `@SiriusXT`. +* [Revisions number limit](https://github.com/TriliumNext/Notes/pull/391) by [SiriusXT](https://github.com/SiriusXT). +* [Searching with orderBy doesn't work with dated attributes](https://github.com/TriliumNext/Notes/pull/451) by [jaimeferj](https://github.com/jaimeferj). +* [Use the electron Clipboard module when using "Copy image to clipboard"](https://github.com/TriliumNext/Notes/pull/452) by @perfectra1n. +* [MIME type not being used when creating new note correctly, upon zip import](https://github.com/TriliumNext/Notes/pull/458) by @perfectra1n. +* [fix blurry PWA icon](https://github.com/TriliumNext/Notes/pull/465) by [quantum5](https://github.com/quantum5) + +## 🌍 Internationalization + +* Translated more of the client (e.g. note types, tree context menu, switches in the ribbon). +* Started translating the server (e.g. first setup, keyboard actions, shared pages). +* Improved coverage for Spanish by [hasecilu](https://github.com/hasecilu). +* Improved coverage for Chinese by [Nriver](https://github.com/Nriver/). +* Improved coverage for Romanian. + +## Library updates + +* [Upgrade bootstrap from v4 to v5](https://github.com/TriliumNext/Notes/pull/381) by @JYC333 + * This is a heavy UI library update, so there might be quirks here and there. + * Various improvements and bugfixes by @JYC333 and @SiriusXT +* Mermaid: 10.9.1 → 11.3.0 +* Mind Elixir: 4.0.5 → 4.1.5 +* JSDOM: 24.1.1 → 25.0.0 +* Day.js: 1.11.12 → 1.11.13 +* ESLint: 9.9.0 → 9.10.0 +* Marked: 13.0.2 → 14.1.2 +* Ini: 4.1.3 -> 5.0.0 +* Axios: 1.7.2 → 1.7.7 +* i18next: 23.14.0 → 23.15.2 +* express 4.19.2 → 4.21.0 +* better-sqlite3: 11.1.2 → 11.3.0 +* and other development tools. \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.8.md b/docs/Release Notes/Release Notes/v0.90.8.md new file mode 100644 index 000000000..4d0dba238 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.8.md @@ -0,0 +1,41 @@ +# v0.90.8 +## 💡 Key highlights + +* A new note type was introduced called “Mind Map” using the MindElixir library. +* Sync version has been increased from 32 to 33 to prevent issues with mindmap notes. This means that the server also needs to be updated to this version, otherwise sync will be refused. +* Internationalization support (Spanish, Chinese, Romanian, French). +* Various bugfixes (see below). + +## New to v0.90.8 + +## 🐞 Bugfixes + +* [There are horizontal lines under all hyperlinks](https://github.com/TriliumNext/Notes/issues/485) + +## ✨ Improvements + +* [Introduce `stable` tag on containers](https://github.com/TriliumNext/Notes/pull/489) by @perfectra1n + +### 🌍 Internationalization + +* Added French translations by @Potjoe-97 +* Translation improvements for Spanish by @hasecilu +* Translation improvements for Chinese by @Nriver +* Improved coverage for Romanian. + +### ⬆️ Library updates + +* mind-elixir: 4.1.5 -> 4.2.0 +* i18next: 23.15.2 -> 23.16.0 + +## From v0.90.7-beta + +
 
+ +## From v0.90.6-beta + +* Adds back the `amd64` container image + +## From v0.90-5-beta + +
 
\ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.90.9-beta.md b/docs/Release Notes/Release Notes/v0.90.9-beta.md new file mode 100644 index 000000000..8819f7944 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.90.9-beta.md @@ -0,0 +1,29 @@ +# v0.90.9-beta +## 🐞 Bugfixes + +* [Promoted boolean attributes no long showing checkbox when the value is true](https://github.com/TriliumNext/Notes/issues/503) +* [Import of .htm keeps htm in title while html does not](https://github.com/TriliumNext/Notes/issues/500) + +## ✨ Improvements + +* [Syntax highlighting in read-only mode code notes](https://github.com/TriliumNext/Notes/issues/504) (also for “View source”). +* [Server 404 logs are now one-line instead of a full stack trace](https://github.com/TriliumNext/Notes/issues/505) + +## 🌍 Internationalization + +* Translated the toast notifications, errors and confirmation boxes. +* Translated the launcher context menu. + +## ⬆️ Library updates + +* i18next: 23.16.0 -> 23.16.2 +* mind-elixir: 4.2.0 -> 4.2.2 +* vanilla-js-wheel-zoom: 9.0.2 -> 9.0.4 +* cookie-parser: 1.4.6 → 1.4.7 +* image-type: 4.1.0 → 5.2.0 +* express, express-rate-limit, express-session updated to latest. +* cookie-parser: 1.4.6 → 1.4.7 +* marked: 14.1.2 → 14.1.3 +* sanitize-html: 2.13.0 → 2.13.1 +* force-graph: 1.43.5 → 1.45.0 +* and others (jasmine, debounce) \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.1-beta.md b/docs/Release Notes/Release Notes/v0.91.1-beta.md new file mode 100644 index 000000000..b1375a286 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.1-beta.md @@ -0,0 +1,85 @@ +# v0.91.1-beta +## 💡 Key highlights + +* The launcher bar can now be placed at the top instead of the left of the screen, with a full-width tab bar. See [#654](https://github.com/TriliumNext/Notes/pull/654) for more information. +* [A new theme is introduced called “TriliumNext"](https://github.com/TriliumNext/Notes/pull/661) by @adoriandoran and @eliandoran. Settings on this theme are now displayed in a card layout. Redesigned tree action buttons. +* [Native title bar buttons](https://github.com/TriliumNext/Notes/pull/702) when native title bar is off for Windows and macOS. +* On Windows, on the TriliumNext theme there are window transparency effects if running under Windows, see [Mica (Windows transparency effects)](https://github.com/TriliumNext/Notes/pull/717) for screenshots. Unfortunately, it is plagued by a few bugs with Electron (the library we are using for the desktop application), see the link for more information. +* Updated Docker builds to Node 22. **Be on the lookout for any potential issues, especially for less-tested platforms such as ARM.** + +## 🐞 Bugfixes + +* [Syntax highlight in code blocks only in edit mode](https://github.com/TriliumNext/Notes/issues/668) +* To do lists not preserved on safe import. +* [File type video with emoji in name will break the preview](https://github.com/TriliumNext/Notes/issues/757) +* [Attribute "jump to" for internal links not working anymore](https://github.com/TriliumNext/Notes/issues/732) + +## ✨ Improvements + +* [Add label definition type for standalone time](https://github.com/TriliumNext/Notes/pull/653) by @mm21 +* [Remove resizer from backend log text area](https://github.com/TriliumNext/Notes/pull/670) by @meichthys +* Use CodeMirror for backend log +* Support for ELK layout in Mermaid diagrams (sponsored by @perfectra1n). See the relevant “ELK layout engine” in Mermaid diagrams for more information. +* [Make exact matches rise higher up in search](https://github.com/TriliumNext/Notes/pull/678) by @perfectra1n +* [Change layout of promoted attributes](https://github.com/TriliumNext/Notes/pull/538) +* [Add full text search in autocomplete](https://github.com/TriliumNext/Notes/pull/659) by @SiriusXT +* [Importing single HTML file: prefer html title over filename](https://github.com/TriliumNext/Notes/pull/598) by @maphew +* [Support page breaks for printing](https://github.com/TriliumNext/Notes/pull/692) +* [user configurable list of allowed html tags in import](https://github.com/TriliumNext/Notes/pull/601) by @maphew +* General HTML support; now the editor supports more HTML tags and displays them appropriately. + + * Currently the import function is the only way to benefit from them, or modifying the source code manually by changing the note type. +* Change default icons for “Go to Previous Note” and “Go to next note” launcher buttons. +* Move toggle sidebar button near tab bar on horizontal layout. +* Mind map notes now are full-width by default (same as Canvas, for example). +* Tabs can now display the icon notes instead of only workspace icons (this is the default behaviour of the TrilumNext theme, can be added to any other theme via `--tab-note-icons: true;` to `:root`). +* [New Features for note map](https://github.com/TriliumNext/Notes/pull/700) by @CobriMediaJulien +* [Add supported link protocols](https://github.com/TriliumNext/Notes/pull/694) by @SiriusXT +* [add Mind Elixir plugin @mind-elixir/node-menu](https://github.com/TriliumNext/Notes/issues/723) +* [Format dates and times](https://github.com/TriliumNext/Notes/pull/741) by @adoriandoran +* [Expose cheerio in backend script api](https://github.com/TriliumNext/Notes/pull/758) by @kleutzinger +* [Format note paths](https://github.com/TriliumNext/Notes/pull/759) by @adoriandoran +* [add remark about UFW issues](https://github.com/TriliumNext/Notes/pull/767) by @pano9000 +* [Code Block language support for Terraform (HCL)](https://github.com/TriliumNext/Notes/issues/739) +* [Display the fixed toolbar on multiple lines](https://github.com/TriliumNext/Notes/issues/729) (adjustable in settings) +* [Hide `internalLink` from `Jump To Note` dialog](https://github.com/TriliumNext/Notes/issues/713) + +## 🌍 Internationalization + +* [Chinese translation improvements (& some new messages translated)](https://github.com/TriliumNext/Notes/pull/673) by @Nriver +* [Add translation for doc notes](https://github.com/TriliumNext/Notes/pull/677) by @Nriver +* [Spanish improvements](https://github.com/TriliumNext/Notes/pull/695/files) by @hasecilu +* [Traditional Chinese support](https://github.com/TriliumNext/Notes/pull/698) by @dwong33 +* Translate import toast messages. +* Translate hidden notes (including options). +* Romanian improvements. +* [Brazillian Portuguese](https://github.com/TriliumNext/Notes/pull/740) by @Nertonm work in progress (only server translations for now) +* Translate note autocomplete. + +## ⬆️ Library updates + +* i18next, i18next-http-backend, i18next-fs-backend +* force-graph v1.47.0 +* express 4.2.12 +* mermaid 11.4.1 +* axios 1.7.9 +* katex 0.16.14 +* https-proxy-agent 7.0.6 +* better-sqlite3 11.6.0 +* electron 31.3.1 -> 33.2.1 +* jimp 0.22.12 -> 1.6.0 +* mind-elixir 4.3.3 +* Development tools + * electron-forge + * Docker Node.js updated to v20.18.1 + * GitHub Actions workflow actions + * webpack v5.97.1 + * TypeScript + types + * yargs + * helmet + * marked 15.0.3 + * is-svg 5.1.0 + * image-type 5.2.0 + * eslint v9.16.0 + * jsdom v25.0.1 + * compression v1.7.5 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.2-beta.md b/docs/Release Notes/Release Notes/v0.91.2-beta.md new file mode 100644 index 000000000..7347c41d2 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.2-beta.md @@ -0,0 +1,36 @@ +# v0.91.2-beta +## 🐞 Bugfixes + +* [Can not toggle notes by clicking on the note's icon in the navigation bar](https://github.com/TriliumNext/Notes/issues/812) +* [Cannot arrow down to "full search" when no search results are returned in quick search](https://github.com/TriliumNext/Notes/issues/798) +* [Toolbar tooltips no longer shown](https://github.com/TriliumNext/Notes/issues/795) +* [Two help pages will be opened when the help button is clicked](https://github.com/TriliumNext/Notes/issues/570) + +## ✨ Improvements + +* [fix typo Trillium](https://github.com/TriliumNext/Notes/pull/799) by @pano9000 +* [Libraries in Excalidraw are broken](https://github.com/TriliumNext/Notes/pull/787) by @CobriMediaJulien +* [Color scheme selection](https://github.com/TriliumNext/Notes/pull/800) by @adoriandoran +* [Style Next: Restyle the "Jump to Note" dialog](https://github.com/TriliumNext/Notes/pull/802) by @adoriandoran +* Exporting a Markdown file will now try to preserve syntax highlighting +* [Auto-show left panel when configuring launchbar](https://github.com/TriliumNext/Notes/issues/779) +* [Accessibility: explicitly associate label and input elements](https://github.com/TriliumNext/Notes/pull/813) by @pano9000 + +## 🌍 Internationalization + +* Spanish improvements by @hasecilu +* [localize hardcoded English aria-labels](https://github.com/TriliumNext/Notes/pull/801) by @pano9000 +* Translate new note title + +## ⬆️ Library updates + +* typedoc: v0.27.5 +* highlightjs: v11.11.0 +* marked: v15.0.4 +* express-rate-limit to v7.5.0 +* chokidar: 4.0.3 +* katex: v0.16.18 +* sanitize-html: v2.14.0 +* webpack-cli: v5.1.4 → v6.0.1 +* i18next: v24.2.0 +* @braintree/sanitize-url: v7.1.1 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.3-beta.md b/docs/Release Notes/Release Notes/v0.91.3-beta.md new file mode 100644 index 000000000..cb1cb2b2b --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.3-beta.md @@ -0,0 +1,101 @@ +# v0.91.3-beta +## 💡 Key highlights + +* Quite a few mobile improvements have been done (**kindly waiting for bug reports**) + * On mobile the horizontal layout (with the launcher bar displayed at the top of the screen instead of on the left side) is now enforced. + * This allows for more real estate for the content of the note and simplifies the mobile layout. + * The launch bar has also been moved at the bottom, to be inline with modern application layouts. + * Restructured the tree as a sidebar which is triggered via a button on the left side of the title bar. + * The sidebar can also be triggered by swiping to the right on the left side of the screen. _Still some quirks to address on both iOS and Android due to their weird back button gesture_. + * Improved the positioning of the bottom bar and of the editor toolbar. + * Tabs have been enabled. Currently we are using the same implementation as on desktop, which might have a few quirks on mobile. + * Jump to note is now available. + * The mobile view now has its own launch bar configuration with its own launch bar buttons. For now only a few have been enabled. **Feel free to request any button from the desktop should you require it.** + * Now it's possible to insert footnotes in CKEditor, thanks to a fork of [ThomasAitken/ckeditor5-footnotes](https://github.com/ThomasAitken/ckeditor5-footnotes). + * It's also possible to create inline Mermaid diagrams into text notes. + +## Known issues + +* On mobile, opening in a new tab activates the new tab but does not refresh the tab bar. + +## 🐞 Bugfixes + +* Prevent HTML from rendering in launcher bar. +* [Calendar overflows off right of view when in mobile view](https://github.com/TriliumNext/Notes/issues/783) +* Note tree context menu not shown on long press on iOS. +* [Advanced tree menu is only accessible in the mobile view via long-press](https://github.com/TriliumNext/Notes/issues/785) +* [NotFoundError after trying to paste filepath into note, restart required](https://github.com/TriliumNext/Notes/issues/881) +* login: fix "flash of unstyled content" by @pano9000 +* [Limit search function doesn't work](https://github.com/TriliumNext/Notes/issues/920) +* [fix ck-editor checkboxes not showing checkmark while printing](https://github.com/TriliumNext/Notes/pull/908) by @pano9000 + +## ✨ Improvements + +* [Syntax Highlighting for Batch scripts](https://github.com/TriliumNext/Notes/issues/830) +* Mobile improvements + + * Translucent top bar mobile layout on iOS + * Display formatting toolbar above keyboard. +* Fonts + + * Grouped the font selection by font type (sans-serif, monospace, etc.). + * Added an option to use the system font (e.g. “Segoe UI” for Windows). +* Display the content of JSON attachments and improve the style of attachment code blocks. +* Translucent top bar on desktop layout for iPad +* ['Show Help' and 'About Trillium Notes' menu options are now available in the mobile view](https://github.com/TriliumNext/Notes/issues/666) +* Back/forward buttons have been enabled for web builds as well, mostly for mobile view. +* [support for RFC 5870 geo-URIs](https://github.com/TriliumNext/Notes/pull/891) by @0Raptor +* login: add Trilium icon and improve style by @pano9000 +* allow disabling mention autocomplete by pressing escape (forward port of @zadam’s work) +* [Restyle the help dialog](https://github.com/TriliumNext/Notes/pull/903) by @adoriandoran +* Added back ESLint support for backend notes +* Exporting JavaScript code blocks in text notes to Markdown now sets the right language tag. + +## 🌍 Internationalization + +* Spanish translation improvements by @hasecilu +* Chinese translation improvements by @Nriver +* [make tags translatable](https://github.com/TriliumNext/Notes/pull/857) by @pano9000 +* [make duplicate notes suffix translatable](https://github.com/TriliumNext/Notes/pull/859) by @pano9000 +* improve translatability of database\_backed\_up\_to string by @pano9000 +* translate some buttons in the note tree +* translate a few note map buttons + +## ⬆️ Technical Improvements + +* Library updates + + * force-graph to v1.47.2 + * @highlightjs/cdn-assets to v11.11.1 + * typedoc to v0.27.6 + * jquery.fancytree to v2.38.4 + * katex to v0.16.19 + * @types/node to v22.10.5 + * marked to v15.0.5 + * mind-elixir to v4.3.6 + * better-sqlite3 to v11.8.0 + * CKEditor to 41.3.2 + * jsdom to v26 + * i18next to v24.2.1 + * draggabilly to v3.0.0 + * electron to 33.3.1. + * Node.js for Docker containers: v22.13.0 + * ts-loader: v9.5.2 +* [use existing randomSecureToken function](https://github.com/TriliumNext/Notes/pull/866) by @pano9000 +* use named exports for the utils functions by @pano9000 +* use Set instead of Arrays for faster lookups by @pano9000 +* remove unused 'request' by @pano9000 +* utils/formatDownloadTitle: simplify function by @pano9000 +* remove unused tree-kill dependency by @pano9000 +* [Add timezone and localtime mounts to docker-compose](https://github.com/TriliumNext/Notes/pull/892) by @perfectra1n +* login: simplify JS by @pano9000 +* set password: various smaller fixes by @pano9000 +* use ejs partial for injecting window.glob by @pano9000 +* add prettier as devDep and scripts by @pano9000 +* add override for \*.json tab width to match .editorconfig by @pano9000 +* update deprecated electron packages by @pano9000 +* Add server logging for CKEditor state changes by @process +* refactor(data\_dir): simplify logic and make code robust and testable by @pano9000 +* replace csurf with csrf-csrf by @pano9000 +* refactor(backend\_log): improve getBackendLog by @pano9000 +* fix(views): replace deprecated meta tag by @pano9000 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.4-beta.md b/docs/Release Notes/Release Notes/v0.91.4-beta.md new file mode 100644 index 000000000..7ca46aea0 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.4-beta.md @@ -0,0 +1,60 @@ +# v0.91.4-beta +To do before release: + +* Ensure Excalidraw works on desktop build. + +## 💡 Key highlights + +* We now provide binaries to run the server on Linux on ARM without Docker (e.g. on a Raspberry Pi, thanks to @perfectra1n ). +* [Introducing a map note type](https://github.com/TriliumNext/Notes/pull/1017) +* Add ability to set Trilium configuration variables via ENV variables by @pano9000 and @perfectra1n + +## 🐞 Bugfixes + +* [Share: Fix going from child note to parent share root](https://github.com/TriliumNext/Notes/pull/963) +* [Context Menu Keyboard Commands wrap to next line](https://github.com/TriliumNext/Notes/issues/976) +* [Keyboard shortcuts in tree context menu are not in line with menu item](https://github.com/TriliumNext/Notes/issues/685) +* [Canvas/Excalidraw note slows down considerably with many images](https://github.com/TriliumNext/Notes/issues/967) +* [tab context menu on mobile is not accessible](https://github.com/TriliumNext/Notes/issues/969) +* [Code Notes Settings: the "Available MIME Type" list gets corrupted](https://github.com/TriliumNext/Notes/issues/988) +* [Tree Panel: the preferred width resets under certain conditions](https://github.com/TriliumNext/Notes/issues/990) +* [Mind map: the branching side preference is not persisted](https://github.com/TriliumNext/Notes/issues/986) +* HTML Import Strips away "valid" h1 tags by @pano9000 +* [share.js broken on share pages](https://github.com/TriliumNext/Notes/issues/1029) by @pano9000 + +## ✨ Improvements + +* [Add space between "delete all revisions" and ? buttons on Note revision dialog](https://github.com/TriliumNext/Notes/issues/974) +* Improved launch bar on mobile: context menu to move between available and visible, or reset the configuration. +* Enable fixed editing toolbar by default for new users. + +## 🌍 Internationalization + +* Translated some missing messages in Romanian. + +## ⬆️ Technical improvements + +* **Reached zero vulnerabilities in dependencies according to** `**npm audit**` +* Update Electron to 34 +* update dependency fs-extra to v11.3.0 +* update dependency force-graph to v1.49.0 +* update dependency katex to v0.16.21 +* update dependency better-sqlite3 to v11.8.1 +* [set more secure csrf related settings](https://github.com/TriliumNext/Notes/pull/961) by @pano9000 +* get rid of ts-node by @pano9000 +* Fix default ivLength in dump-db tool by @Nriver +* add vitest as test framework and port current tests (& various test improvements) by @pano9000 +* get rid of Webpack Critical dependency warning by @pano9000 +* fix flaky getPlatformAppDataDir test on Windows by @pano9000 +* refactor and add tests for \`services/import/mime\` by @pano9000 +* refactor: compress images by @j9t +* Bump Electron to v34.0.1 +* Bump i18next-http-backend to v3.0.2 +* Bump i18next to v24.2.2 +* Bump electron-forge monorepo to v7.6.1 +* Bump @mind-elixir/node-menu to v1.0.4 +* Docker: update node.js to v22.13.1 +* webpack: add Configuration type +* webpack: add missing share.js entry point +* npm audit fix by @pano9000 +* port desktop and mobile entrypoints by @pano9000 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.5.md b/docs/Release Notes/Release Notes/v0.91.5.md new file mode 100644 index 000000000..40a15152a --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.5.md @@ -0,0 +1,301 @@ +# v0.91.5 +## 💡 Key highlights + +* We now provide binaries to run the server on Linux on ARM without Docker (e.g. on a Raspberry Pi, thanks to @perfectra1n ). +* [Introducing a map note type](https://github.com/TriliumNext/Notes/pull/1017) +* Add ability to set Trilium configuration variables via ENV variables by @pano9000 and @perfectra1n +* Quite a few mobile improvements have been done (**kindly waiting for bug reports**) + + * On mobile the horizontal layout (with the launcher bar displayed at the top of the screen instead of on the left side) is now enforced. + * This allows for more real estate for the content of the note and simplifies the mobile layout. + * The launch bar has also been moved at the bottom, to be inline with modern application layouts. + * Restructured the tree as a sidebar which is triggered via a button on the left side of the title bar. + * The sidebar can also be triggered by swiping to the right on the left side of the screen. _Still some quirks to address on both iOS and Android due to their weird back button gesture_. + * Improved the positioning of the bottom bar and of the editor toolbar. + * Tabs have been enabled. Currently we are using the same implementation as on desktop, which might have a few quirks on mobile. + * Jump to note is now available. + * The mobile view now has its own launch bar configuration with its own launch bar buttons. For now only a few have been enabled. **Feel free to request any button from the desktop should you require it.** + * Now it's possible to insert footnotes in CKEditor, thanks to a fork of [ThomasAitken/ckeditor5-footnotes](https://github.com/ThomasAitken/ckeditor5-footnotes). + * It's also possible to create inline Mermaid diagrams into text notes. +* The launcher bar can now be placed at the top instead of the left of the screen, with a full-width tab bar. See [#654](https://github.com/TriliumNext/Notes/pull/654) for more information. +* [A new theme is introduced called “TriliumNext"](https://github.com/TriliumNext/Notes/pull/661) by @adoriandoran and @eliandoran. Settings on this theme are now displayed in a card layout. Redesigned tree action buttons. +* [Native title bar buttons](https://github.com/TriliumNext/Notes/pull/702) when native title bar is off for Windows and macOS. +* On Windows, on the TriliumNext theme there are window transparency effects if running under Windows, see [Mica (Windows transparency effects)](https://github.com/TriliumNext/Notes/pull/717) for screenshots. Unfortunately, it is plagued by a few bugs with Electron (the library we are using for the desktop application), see the link for more information. +* Updated Docker builds to Node 22. **Be on the lookout for any potential issues, especially for less-tested platforms such as ARM.** + +## New in this release (v0.91.5) + +### 🐞 Bugfixes + +* [Import Dialog "Safe Import" Tooltip is cut off](https://github.com/TriliumNext/Notes/issues/1003) +* [Backend Log is not showing on mobile view](https://github.com/TriliumNext/Notes/issues/1058) +* [initial server setup form cannot be sent with enter key](https://github.com/TriliumNext/Notes/issues/94) by @pano9000 + +### ✨ Improvements + +* [Preserve highlighted text's background color when printing](https://github.com/TriliumNext/Notes/issues/905) + +### 🌍 Internationalization + +* … + +### 🛠️ Technical updates + +* update dependency @types/node to v22.12.0 +* Use new type field for GitHub issues by @pano9000 +* chore(client/ts): port setup entrypoint by @pano9000 +* refactor(server/utils): turn isMac/isWin/isElectron/isDev into boolean by @pano9000 +* chore(issue\_templates): add simple Task template by @pano9000 + +## From v0.91.4-beta + +### 🐞 Bugfixes + +* [Share: Fix going from child note to parent share root](https://github.com/TriliumNext/Notes/pull/963) +* [Context Menu Keyboard Commands wrap to next line](https://github.com/TriliumNext/Notes/issues/976) +* [Keyboard shortcuts in tree context menu are not in line with menu item](https://github.com/TriliumNext/Notes/issues/685) +* [Canvas/Excalidraw note slows down considerably with many images](https://github.com/TriliumNext/Notes/issues/967) +* [tab context menu on mobile is not accessible](https://github.com/TriliumNext/Notes/issues/969) +* [Code Notes Settings: the "Available MIME Type" list gets corrupted](https://github.com/TriliumNext/Notes/issues/988) +* [Tree Panel: the preferred width resets under certain conditions](https://github.com/TriliumNext/Notes/issues/990) +* [Mind map: the branching side preference is not persisted](https://github.com/TriliumNext/Notes/issues/986) +* HTML Import Strips away "valid" h1 tags by @pano9000 +* [share.js broken on share pages](https://github.com/TriliumNext/Notes/issues/1029) by @pano9000 + +### ✨ Improvements + +* [Add space between "delete all revisions" and ? buttons on Note revision dialog](https://github.com/TriliumNext/Notes/issues/974) +* Improved launch bar on mobile: context menu to move between available and visible, or reset the configuration. +* Enable fixed editing toolbar by default for new users. + +### 🌍 Internationalization + +* Translated some missing messages in Romanian. + +### ⬆️ Technical improvements + +* **Reached zero vulnerabilities in dependencies according to** `**npm audit**` +* Update Electron to 34 +* update dependency fs-extra to v11.3.0 +* update dependency force-graph to v1.49.0 +* update dependency katex to v0.16.21 +* update dependency better-sqlite3 to v11.8.1 +* [set more secure csrf related settings](https://github.com/TriliumNext/Notes/pull/961) by @pano9000 +* get rid of ts-node by @pano9000 +* Fix default ivLength in dump-db tool by @Nriver +* add vitest as test framework and port current tests (& various test improvements) by @pano9000 +* get rid of Webpack Critical dependency warning by @pano9000 +* fix flaky getPlatformAppDataDir test on Windows by @pano9000 +* refactor and add tests for \`services/import/mime\` by @pano9000 +* refactor: compress images by @j9t +* Bump Electron to v34.0.1 +* Bump i18next-http-backend to v3.0.2 +* Bump i18next to v24.2.2 +* Bump electron-forge monorepo to v7.6.1 +* Bump @mind-elixir/node-menu to v1.0.4 +* Docker: update node.js to v22.13.1 +* webpack: add Configuration type +* webpack: add missing share.js entry point +* npm audit fix by @pano9000 +* port desktop and mobile entrypoints by @pano9000 + +## From v0.91.3-beta + +### 🐞 Bugfixes + +* Prevent HTML from rendering in launcher bar. +* [Calendar overflows off right of view when in mobile view](https://github.com/TriliumNext/Notes/issues/783) +* Note tree context menu not shown on long press on iOS. +* [Advanced tree menu is only accessible in the mobile view via long-press](https://github.com/TriliumNext/Notes/issues/785) +* [NotFoundError after trying to paste filepath into note, restart required](https://github.com/TriliumNext/Notes/issues/881) +* login: fix "flash of unstyled content" by @pano9000 +* [Limit search function doesn't work](https://github.com/TriliumNext/Notes/issues/920) +* [fix ck-editor checkboxes not showing checkmark while printing](https://github.com/TriliumNext/Notes/pull/908) by @pano9000 + +### ✨ Improvements + +* [Syntax Highlighting for Batch scripts](https://github.com/TriliumNext/Notes/issues/830) +* Mobile improvements + + * Translucent top bar mobile layout on iOS + * Display formatting toolbar above keyboard. +* Fonts + + * Grouped the font selection by font type (sans-serif, monospace, etc.). + * Added an option to use the system font (e.g. “Segoe UI” for Windows). +* Display the content of JSON attachments and improve the style of attachment code blocks. +* Translucent top bar on desktop layout for iPad +* ['Show Help' and 'About Trillium Notes' menu options are now available in the mobile view](https://github.com/TriliumNext/Notes/issues/666) +* Back/forward buttons have been enabled for web builds as well, mostly for mobile view. +* [support for RFC 5870 geo-URIs](https://github.com/TriliumNext/Notes/pull/891) by @0Raptor +* login: add Trilium icon and improve style by @pano9000 +* allow disabling mention autocomplete by pressing escape (forward port of @zadam’s work) +* [Restyle the help dialog](https://github.com/TriliumNext/Notes/pull/903) by @adoriandoran +* Added back ESLint support for backend notes +* Exporting JavaScript code blocks in text notes to Markdown now sets the right language tag. + +### 🌍 Internationalization + +* Spanish translation improvements by @hasecilu +* Chinese translation improvements by @Nriver +* [make tags translatable](https://github.com/TriliumNext/Notes/pull/857) by @pano9000 +* [make duplicate notes suffix translatable](https://github.com/TriliumNext/Notes/pull/859) by @pano9000 +* improve translatability of database\_backed\_up\_to string by @pano9000 +* translate some buttons in the note tree +* translate a few note map buttons + +### ⬆️ Technical Improvements + +* Library updates + + * force-graph to v1.47.2 + * @highlightjs/cdn-assets to v11.11.1 + * typedoc to v0.27.6 + * jquery.fancytree to v2.38.4 + * katex to v0.16.19 + * @types/node to v22.10.5 + * marked to v15.0.5 + * mind-elixir to v4.3.6 + * better-sqlite3 to v11.8.0 + * CKEditor to 41.3.2 + * jsdom to v26 + * i18next to v24.2.1 + * draggabilly to v3.0.0 + * electron to 33.3.1. + * Node.js for Docker containers: v22.13.0 + * ts-loader: v9.5.2 +* [use existing randomSecureToken function](https://github.com/TriliumNext/Notes/pull/866) by @pano9000 +* use named exports for the utils functions by @pano9000 +* use Set instead of Arrays for faster lookups by @pano9000 +* remove unused 'request' by @pano9000 +* utils/formatDownloadTitle: simplify function by @pano9000 +* remove unused tree-kill dependency by @pano9000 +* [Add timezone and localtime mounts to docker-compose](https://github.com/TriliumNext/Notes/pull/892) by @perfectra1n +* login: simplify JS by @pano9000 +* set password: various smaller fixes by @pano9000 +* use ejs partial for injecting window.glob by @pano9000 +* add prettier as devDep and scripts by @pano9000 +* add override for \*.json tab width to match .editorconfig by @pano9000 +* update deprecated electron packages by @pano9000 +* Add server logging for CKEditor state changes by @process +* refactor(data\_dir): simplify logic and make code robust and testable by @pano9000 +* replace csurf with csrf-csrf by @pano9000 +* refactor(backend\_log): improve getBackendLog by @pano9000 +* fix(views): replace deprecated meta tag by @pano9000 + +## From v0.91.2-beta + +### 🐞 Bugfixes + +* [Can not toggle notes by clicking on the note's icon in the navigation bar](https://github.com/TriliumNext/Notes/issues/812) +* [Cannot arrow down to "full search" when no search results are returned in quick search](https://github.com/TriliumNext/Notes/issues/798) +* [Toolbar tooltips no longer shown](https://github.com/TriliumNext/Notes/issues/795) +* [Two help pages will be opened when the help button is clicked](https://github.com/TriliumNext/Notes/issues/570) + +### ✨ Improvements + +* [fix typo Trillium](https://github.com/TriliumNext/Notes/pull/799) by @pano9000 +* [Libraries in Excalidraw are broken](https://github.com/TriliumNext/Notes/pull/787) by @CobriMediaJulien +* [Color scheme selection](https://github.com/TriliumNext/Notes/pull/800) by @adoriandoran +* [Style Next: Restyle the "Jump to Note" dialog](https://github.com/TriliumNext/Notes/pull/802) by @adoriandoran +* Exporting a Markdown file will now try to preserve syntax highlighting +* [Auto-show left panel when configuring launchbar](https://github.com/TriliumNext/Notes/issues/779) +* [Accessibility: explicitly associate label and input elements](https://github.com/TriliumNext/Notes/pull/813) by @pano9000 + +### 🌍 Internationalization + +* Spanish improvements by @hasecilu +* [localize hardcoded English aria-labels](https://github.com/TriliumNext/Notes/pull/801) by @pano9000 +* Translate new note title + +### ⬆️ Library updates + +* typedoc: v0.27.5 +* highlightjs: v11.11.0 +* marked: v15.0.4 +* express-rate-limit to v7.5.0 +* chokidar: 4.0.3 +* katex: v0.16.18 +* sanitize-html: v2.14.0 +* webpack-cli: v5.1.4 → v6.0.1 +* i18next: v24.2.0 +* @braintree/sanitize-url: v7.1.1 + +## From v0.91.1-beta + +### 🐞 Bugfixes + +* [Syntax highlight in code blocks only in edit mode](https://github.com/TriliumNext/Notes/issues/668) +* To do lists not preserved on safe import. +* [File type video with emoji in name will break the preview](https://github.com/TriliumNext/Notes/issues/757) +* [Attribute "jump to" for internal links not working anymore](https://github.com/TriliumNext/Notes/issues/732) + +### ✨ Improvements + +* [Add label definition type for standalone time](https://github.com/TriliumNext/Notes/pull/653) by @mm21 +* [Remove resizer from backend log text area](https://github.com/TriliumNext/Notes/pull/670) by @meichthys +* Use CodeMirror for backend log +* Support for ELK layout in Mermaid diagrams (sponsored by @perfectra1n). See the relevant “ELK layout engine” in [missing note] for more information. +* [Make exact matches rise higher up in search](https://github.com/TriliumNext/Notes/pull/678) by @perfectra1n +* [Change layout of promoted attributes](https://github.com/TriliumNext/Notes/pull/538) +* [Add full text search in autocomplete](https://github.com/TriliumNext/Notes/pull/659) by @SiriusXT +* [Importing single HTML file: prefer html title over filename](https://github.com/TriliumNext/Notes/pull/598) by @maphew +* [Support page breaks for printing](https://github.com/TriliumNext/Notes/pull/692) +* [user configurable list of allowed html tags in import](https://github.com/TriliumNext/Notes/pull/601) by @maphew +* General HTML support; now the editor supports more HTML tags and displays them appropriately. + + * Currently the import function is the only way to benefit from them, or modifying the source code manually by changing the note type. +* Change default icons for “Go to Previous Note” and “Go to next note” launcher buttons. +* Move toggle sidebar button near tab bar on horizontal layout. +* Mind map notes now are full-width by default (same as Canvas, for example). +* Tabs can now display the icon notes instead of only workspace icons (this is the default behaviour of the TrilumNext theme, can be added to any other theme via `--tab-note-icons: true;` to `:root`). +* [New Features for note map](https://github.com/TriliumNext/Notes/pull/700) by @CobriMediaJulien +* [Add supported link protocols](https://github.com/TriliumNext/Notes/pull/694) by @SiriusXT +* [add Mind Elixir plugin @mind-elixir/node-menu](https://github.com/TriliumNext/Notes/issues/723) +* [Format dates and times](https://github.com/TriliumNext/Notes/pull/741) by @adoriandoran +* [Expose cheerio in backend script api](https://github.com/TriliumNext/Notes/pull/758) by @kleutzinger +* [Format note paths](https://github.com/TriliumNext/Notes/pull/759) by @adoriandoran +* [add remark about UFW issues](https://github.com/TriliumNext/Notes/pull/767) by @pano9000 +* [Code Block language support for Terraform (HCL)](https://github.com/TriliumNext/Notes/issues/739) +* [Display the fixed toolbar on multiple lines](https://github.com/TriliumNext/Notes/issues/729) (adjustable in settings) +* [Hide `internalLink` from `Jump To Note` dialog](https://github.com/TriliumNext/Notes/issues/713) + +### 🌍 Internationalization + +* [Chinese translation improvements (& some new messages translated)](https://github.com/TriliumNext/Notes/pull/673) by @Nriver +* [Add translation for doc notes](https://github.com/TriliumNext/Notes/pull/677) by @Nriver +* [Spanish improvements](https://github.com/TriliumNext/Notes/pull/695/files) by @hasecilu +* [Traditional Chinese support](https://github.com/TriliumNext/Notes/pull/698) by @dwong33 +* Translate import toast messages. +* Translate hidden notes (including options). +* Romanian improvements. +* [Brazillian Portuguese](https://github.com/TriliumNext/Notes/pull/740) by @Nertonm work in progress (only server translations for now) +* Translate note autocomplete. + +### ⬆️ Library updates + +* i18next, i18next-http-backend, i18next-fs-backend +* force-graph v1.47.0 +* express 4.2.12 +* mermaid 11.4.1 +* axios 1.7.9 +* katex 0.16.14 +* https-proxy-agent 7.0.6 +* better-sqlite3 11.6.0 +* electron 31.3.1 -> 33.2.1 +* jimp 0.22.12 -> 1.6.0 +* mind-elixir 4.3.3 +* Development tools + * electron-forge + * Docker Node.js updated to v20.18.1 + * GitHub Actions workflow actions + * webpack v5.97.1 + * TypeScript + types + * yargs + * helmet + * marked 15.0.3 + * is-svg 5.1.0 + * image-type 5.2.0 + * eslint v9.16.0 + * jsdom v25.0.1 + * compression v1.7.5 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.91.6.md b/docs/Release Notes/Release Notes/v0.91.6.md new file mode 100644 index 000000000..27e831bef --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.91.6.md @@ -0,0 +1,5 @@ +# v0.91.6 +## 🐞 Bugfixes + +* [Full text search not working if there are empty mindmaps](https://github.com/TriliumNext/Notes/issues/1107) +* [Shortcut for Clipboard Pasting not working on Mac](https://github.com/TriliumNext/Notes/issues/1087) \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.92.0-beta.md b/docs/Release Notes/Release Notes/v0.92.0-beta.md new file mode 100644 index 000000000..5d350f3b3 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.92.0-beta.md @@ -0,0 +1,83 @@ +# v0.92.0-beta +## 💡 Key highlights + +* [Trilium can now export notes as PDF directly, on the desktop version.](https://github.com/TriliumNext/Notes/pull/1091) +* RPM packages for Linux by @pano9000 +* [Flatpak packages for Linux](https://github.com/TriliumNext/Notes/pull/1130) (not yet published to FlatHub) by @pano9000 +* There is now an in-app help that is still work in progress, but it can be triggered by pressing F1. On some note types (e.g. geo map) there is a contextual help button. +* Zen Mode (minimal UI). See in-app help for more details. + +## 🐞 Bugfixes + +* [js error Parsing error: The keyword 'const' is reserved](https://github.com/zadam/trilium/issues/4696) by @lly-c232733 +* [Full Text Search button in quick launch search bar does not work](https://github.com/TriliumNext/Notes/issues/973) by @SiriusXT +* An obscure bug when expanding a note with custom sort where some of the notes did not have the attribute would cause notes to jump around. +* [Weight Tracker doesn't work on new install](https://github.com/zadam/trilium/issues/4892) +* [Markdown export: Fix strikethrough](https://github.com/TriliumNext/Notes/issues/117) +* [TypeScript code files are detected as video/mp2t](https://github.com/TriliumNext/Notes/issues/1142) files by @pano9000 +* disallow text selection in dropdown-menus by @pano9000 +* [Password window remains after opening encrypted notes](https://github.com/TriliumNext/Notes/issues/1078) by @dousha +* [Error when using code blocks in Markdown code notes](https://github.com/TriliumNext/Notes/issues/1164) +* [Numbered bullet is different in Note Revision view](https://github.com/TriliumNext/Notes/issues/1177) +* Missing `etapi.openapi.yaml`. +* [Pressing Enter to "Add link" does not work anymore](https://github.com/TriliumNext/Notes/issues/1100) (thanks to @St0rml) +* [Cannot cancel sharing the root note or the root of a workspace when it's hoisted](https://github.com/TriliumNext/Notes/issues/518) by @dousha +* [Enabling "Enable background effects" does not properly work with dark themes](https://github.com/TriliumNext/Notes/issues/1209) + +## ✨ Improvements + +* [Better styling for various widgets (buttons, input boxes) on the TriliumNext theme](https://github.com/TriliumNext/Notes/pull/1074) & various TriliumNext theme improvements by @adoriandoran +* System tray icon improvements: translations, new buttons ("New note", “Open today's journal note”, “Bookmarks” and “Recent Notes”), and icons. +* Custom Windows installer icon by @Sarah-Hussein +* [Geo map: it's now possible to add .gpx notes as children for the map. Look for the help button in the geo map for more information.](https://github.com/TriliumNext/Notes/issues/1095) +* Geo map: Enable Retina detection for sharper tiles on supported devices. +* Electron: Add "Office" as category for Desktop file by @pano9000 +* Improvements to the note tooltip, including [making the note title clickable](https://github.com/TriliumNext/Notes/issues/1063). +* Render PDFs in full width by default. +* allow setting custom cookiePath by @pano9000 +* [Swagger UI endpoint](https://github.com/TriliumNext/Notes/pull/1144) by @perfectra1n +* [Add support for \`mid:\` URI](https://github.com/TriliumNext/Notes/issues/1160) +* Raw files can now be served in shared tree by changing the URL instead of setting an attribute to the note. See documentation on Shared Notes in the in-app help (press F1). +* [allow setting custom session cookie expiration](https://github.com/TriliumNext/Notes/pull/1156) by @pano9000 +* [friendly number handling for note erasure timeouts](https://github.com/TriliumNext/Notes/pull/1163) by @pano9000 + +## Mobile improvements + +* Configure launch bar now has an always visible tree instead of the sidebar. It's far from perfect, but it should be more usable than the previous implementation. +* The note title is now smaller. +* Added back a few launcher bar items: + * New Note + * Calendar + * [Open Today's Journal Note](https://github.com/TriliumNext/Notes/issues/1105) +* [Recent changes are now available as well as a launch bar item](https://github.com/TriliumNext/Notes/issues/1161). +* Fixed switching and dragging tabs not working. + +## 🌍 Internationalization + +* Spanish improvements by @hasecilu + +## 🛠️ Technical improvements + +* Remove dependency on `semver`. +* Update test dependencies. +* make desktop.ejs easier to read by @pano9000 +* server/utils: add tests and smaller refactors / fixes by @pano9000 +* refactor: electron forge config by @pano9000 +* improve server build release file names by @pano9000 +* Improved the development experience by setting up caches (server & Electron) & auto-reload for Electron +* protected\_session: input improvements by @pano9000 +* remove unused jasmine by @pano9000 +* update dependency electron to v34.2.0 +* update dependency prettier to v3.5.1 +* update dependency mind-elixir to v4.3.7 +* update dependency marked to v15.0.7 +* update dependency typedoc to v0.27.7 +* update dependency compression to v1.8.0 +* update dependency webpack to v5.98.0 +* update node.js to v22.14.0 +* simplify login function and get rid of deprecated usage of expires by @pano9000 +* Automated OpenAPI spec generation by @FliegendeWurst +* remove leftover @types/jasmine by @pano9000 +* fix and enable ex-flaky tests in services/search.spec.ts by @pano9000 +* npm scripts namespacing by @pano9000 +* "unpack" log messages before printing by @pano9000 \ No newline at end of file diff --git a/docs/Release Notes/Release Notes/v0.92.1-beta.md b/docs/Release Notes/Release Notes/v0.92.1-beta.md new file mode 100644 index 000000000..3e79112bc --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.92.1-beta.md @@ -0,0 +1,51 @@ +# v0.92.1-beta +## 💡 Key highlights + +* There are now stricter rules when it comes to importing HTML, where we only allow very few styles (colors, borders, etc.). Should you encounter any issues when importing, try disabling “Safe import” and report any issues you might have. + +## 🐞 Bugfixes + +* Geomap: Viewport and zoom not restored properly when switching between two geomaps. +* [Imported Evernote Enex file overlaps Trilium UI](https://github.com/TriliumNext/Notes/issues/931) + +## ✨ Improvements + +* Web view now renders in browser using an `