Merge branch 'develop' into ai-llm-integration
| @@ -1,10 +1,37 @@ | |||||||
| .git | # ignored Files | ||||||
| .idea | .dockerignore | ||||||
|  | .editorconfig | ||||||
|  | .git* | ||||||
|  | .prettier* | ||||||
|  | electron* | ||||||
|  | entitlements.plist | ||||||
|  | forge.config.cjs | ||||||
|  | nodemon.json | ||||||
|  | renovate.json | ||||||
|  | trilium.iml | ||||||
|  | Dockerfile | ||||||
|  | Dockerfile.* | ||||||
|  | npm-debug.log | ||||||
|  | /src/**/*.spec.ts | ||||||
|  |  | ||||||
|  | # ignored folders | ||||||
|  | /.cache | ||||||
|  | /.git | ||||||
|  | /.github | ||||||
|  | /.idea | ||||||
|  | /.vscode | ||||||
| /bin | /bin | ||||||
|  | /build | ||||||
| /dist | /dist | ||||||
| /docs | /docs | ||||||
| /npm-debug.log | /dump-db | ||||||
| node_modules | /e2e | ||||||
|  | /integration-tests | ||||||
|  | /spec | ||||||
|  | /test | ||||||
|  | /test-etapi | ||||||
|  | /node_modules | ||||||
|  |  | ||||||
| src/**/*.ts |  | ||||||
| !src/services/asset_path.ts | # exceptions | ||||||
|  | !/bin/copy-dist.ts | ||||||
							
								
								
									
										24
									
								
								.github/workflows/dev.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -44,16 +44,6 @@ jobs: | |||||||
|       - test_dev |       - test_dev | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v4 |       - 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 |  | ||||||
|       - uses: docker/setup-buildx-action@v3 |       - uses: docker/setup-buildx-action@v3 | ||||||
|       - uses: docker/build-push-action@v6 |       - uses: docker/build-push-action@v6 | ||||||
|         with: |         with: | ||||||
| @@ -82,20 +72,6 @@ jobs: | |||||||
|       - name: Set up Docker Buildx |       - name: Set up Docker Buildx | ||||||
|         uses: docker/setup-buildx-action@v3 |         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 |       - name: Build and export to Docker | ||||||
|         uses: docker/build-push-action@v6 |         uses: docker/build-push-action@v6 | ||||||
|         with: |         with: | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								.github/workflows/main-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -57,9 +57,6 @@ jobs: | |||||||
|       - name: Run the TypeScript build |       - name: Run the TypeScript build | ||||||
|         run: npx tsc |         run: npx tsc | ||||||
|  |  | ||||||
|       - name: Create server-package.json |  | ||||||
|         run: cat package.json | grep -v electron > server-package.json |  | ||||||
|  |  | ||||||
|       - name: Build and export to Docker |       - name: Build and export to Docker | ||||||
|         uses: docker/build-push-action@v6 |         uses: docker/build-push-action@v6 | ||||||
|         with: |         with: | ||||||
| @@ -154,18 +151,6 @@ jobs: | |||||||
|       - name: Set up Docker Buildx |       - name: Set up Docker Buildx | ||||||
|         uses: docker/setup-buildx-action@v3 |         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 |       - name: Login to GHCR | ||||||
|         uses: docker/login-action@v3 |         uses: docker/login-action@v3 | ||||||
|         with: |         with: | ||||||
|   | |||||||
| @@ -3,3 +3,4 @@ | |||||||
| *.yml | *.yml | ||||||
| libraries/* | libraries/* | ||||||
| docs/* | docs/* | ||||||
|  | src/public/app/doc_notes/**/* | ||||||
							
								
								
									
										6
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,3 +1,7 @@ | |||||||
| { | { | ||||||
|   "recommendations": ["lokalise.i18n-ally", "editorconfig.editorconfig"] |   "recommendations": [ | ||||||
|  |     "lokalise.i18n-ally", | ||||||
|  |     "editorconfig.editorconfig", | ||||||
|  |     "vitest.explorer" | ||||||
|  |   ] | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -5,8 +5,8 @@ | |||||||
|     { |     { | ||||||
|       "console": "integratedTerminal", |       "console": "integratedTerminal", | ||||||
|       "internalConsoleOptions": "neverOpen", |       "internalConsoleOptions": "neverOpen", | ||||||
|       "name": "nodemon server:start", |       "name": "nodemon start-server", | ||||||
|       "program": "${workspaceFolder}/src/main", |       "program": "${workspaceFolder}/src/www", | ||||||
|       "request": "launch", |       "request": "launch", | ||||||
|       "restart": true, |       "restart": true, | ||||||
|       "runtimeExecutable": "nodemon", |       "runtimeExecutable": "nodemon", | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -19,5 +19,12 @@ | |||||||
|   "[css]": { |   "[css]": { | ||||||
|     "editor.defaultFormatter": "vscode.css-language-features" |     "editor.defaultFormatter": "vscode.css-language-features" | ||||||
|   }, |   }, | ||||||
|   "npm.exclude": ["**/build", "**/dist", "**/out/**"] |   "npm.exclude": [ | ||||||
|  |     "**/build", | ||||||
|  |     "**/dist", | ||||||
|  |     "**/out/**" | ||||||
|  |   ], | ||||||
|  |   "[xml]": { | ||||||
|  |     "editor.defaultFormatter": "redhat.vscode-xml" | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						| @@ -1,62 +1,46 @@ | |||||||
| # Build stage | # Build stage | ||||||
| FROM node:22.14.0-bullseye-slim AS builder | FROM node:22.14.0-bullseye-slim AS builder | ||||||
|  |  | ||||||
| # Configure build dependencies in a single layer | WORKDIR /usr/src/app/build | ||||||
| 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 |  | ||||||
|  |  | ||||||
| # Copy only necessary files for build | # Copy only necessary files for build | ||||||
| COPY . . | COPY . . | ||||||
| COPY server-package.json package.json |  | ||||||
|  |  | ||||||
| # Build and cleanup in a single layer | # Build and cleanup in a single layer | ||||||
| RUN cp -R build/src/* src/. && \ | RUN npm ci && \ | ||||||
|     cp build/docker_healthcheck.js . && \     |     npm run build:prepare-dist && \ | ||||||
|     rm docker_healthcheck.ts && \ |  | ||||||
|     npm install && \ |  | ||||||
|     npm run build:webpack && \ |  | ||||||
|     npm prune --omit=dev && \ |  | ||||||
|     npm cache clean --force && \ |     npm cache clean --force && \ | ||||||
|     cp -r src/public/app/doc_notes src/public/app-dist/. && \ |     rm -rf dist/node_modules && \ | ||||||
|     rm -rf src/public/app/* && \ |     mv dist/* \ | ||||||
|     mkdir -p src/public/app/services && \ |       start-docker.sh \ | ||||||
|     cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \ |       /usr/src/app/ && \ | ||||||
|     rm src/services/asset_path.ts && \ |     rm -rf \ | ||||||
|     rm -r build |       /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 | # Runtime stage | ||||||
| FROM node:22.14.0-bullseye-slim | 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 | WORKDIR /usr/src/app | ||||||
|  |  | ||||||
| # Copy only necessary files from builder | # Install only runtime dependencies | ||||||
| COPY --from=builder /usr/src/app/node_modules ./node_modules | RUN apt-get update && \ | ||||||
| COPY --from=builder /usr/src/app/src ./src |     apt-get install -y --no-install-recommends \ | ||||||
| COPY --from=builder /usr/src/app/db ./db |       gosu && \ | ||||||
| COPY --from=builder /usr/src/app/docker_healthcheck.js . |     rm -rf \ | ||||||
| COPY --from=builder /usr/src/app/start-docker.sh . |       /var/lib/apt/lists/* \ | ||||||
| COPY --from=builder /usr/src/app/package.json . |       /var/cache/apt/* | ||||||
| COPY --from=builder /usr/src/app/config-sample.ini . |  | ||||||
| COPY --from=builder /usr/src/app/images ./images | COPY --from=builder /usr/src/app ./ | ||||||
| COPY --from=builder /usr/src/app/translations ./translations |  | ||||||
| COPY --from=builder /usr/src/app/libraries ./libraries | RUN sed -i "/electron/d" package.json && \ | ||||||
|  |     npm ci --omit=dev && \ | ||||||
|  |     npm cache clean --force && \ | ||||||
|  |     rm -rf /tmp/node-compile-cache | ||||||
|  |  | ||||||
| # Configure container | # Configure container | ||||||
| EXPOSE 8080 | EXPOSE 8080 | ||||||
|   | |||||||
| @@ -1,38 +1,26 @@ | |||||||
| # Build stage | # Build stage | ||||||
| FROM node:22.14.0-alpine AS builder | FROM node:22.14.0-alpine AS builder | ||||||
|  |  | ||||||
| # Configure build dependencies | WORKDIR /usr/src/app/build | ||||||
| RUN apk add --no-cache --virtual .build-dependencies \ |  | ||||||
|     autoconf \ |  | ||||||
|     automake \ |  | ||||||
|     g++ \ |  | ||||||
|     gcc \ |  | ||||||
|     libtool \ |  | ||||||
|     make \ |  | ||||||
|     nasm \ |  | ||||||
|     libpng-dev \ |  | ||||||
|     python3 |  | ||||||
|  |  | ||||||
| WORKDIR /usr/src/app |  | ||||||
|  |  | ||||||
| # Copy only necessary files for build | # Copy only necessary files for build | ||||||
| COPY . . | COPY . . | ||||||
| COPY server-package.json package.json |  | ||||||
|  |  | ||||||
| # Build and cleanup in a single layer | # Build and cleanup in a single layer | ||||||
| RUN cp -R build/src/* src/. && \ | RUN npm ci && \ | ||||||
|     cp build/docker_healthcheck.js . && \ |     npm run build:prepare-dist && \ | ||||||
|     rm docker_healthcheck.ts && \ |  | ||||||
|     npm install && \ |  | ||||||
|     npm run build:webpack && \ |  | ||||||
|     npm prune --omit=dev && \ |  | ||||||
|     npm cache clean --force && \ |     npm cache clean --force && \ | ||||||
|     cp -r src/public/app/doc_notes src/public/app-dist/. && \ |     rm -rf dist/node_modules && \ | ||||||
|     rm -rf src/public/app && \ |     mv dist/* \ | ||||||
|     mkdir -p src/public/app/services && \ |       start-docker.sh \ | ||||||
|     cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \ |       /usr/src/app/ && \ | ||||||
|     rm src/services/asset_path.ts && \ |     rm -rf \ | ||||||
|     rm -r build |       /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 | # Runtime stage | ||||||
| FROM node:22.14.0-alpine | FROM node:22.14.0-alpine | ||||||
| @@ -42,17 +30,12 @@ RUN apk add --no-cache su-exec shadow | |||||||
|  |  | ||||||
| WORKDIR /usr/src/app | WORKDIR /usr/src/app | ||||||
|  |  | ||||||
| # Copy only necessary files from builder | COPY --from=builder /usr/src/app ./ | ||||||
| COPY --from=builder /usr/src/app/node_modules ./node_modules |  | ||||||
| COPY --from=builder /usr/src/app/src ./src | RUN sed -i "/electron/d" package.json && \ | ||||||
| COPY --from=builder /usr/src/app/db ./db |     npm ci --omit=dev && \ | ||||||
| COPY --from=builder /usr/src/app/docker_healthcheck.js . |     npm cache clean --force && \ | ||||||
| COPY --from=builder /usr/src/app/start-docker.sh . |     rm -rf /tmp/node-compile-cache | ||||||
| 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 |  | ||||||
|  |  | ||||||
| # Add application user | # Add application user | ||||||
| RUN adduser -s /bin/false node; exit 0 | RUN adduser -s /bin/false node; exit 0 | ||||||
|   | |||||||
| @@ -5,11 +5,6 @@ set -e  # Fail on any command error | |||||||
| VERSION=`jq -r ".version" package.json` | VERSION=`jq -r ".version" package.json` | ||||||
| SERIES=${VERSION:0:4}-latest | 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 . | sudo docker build -t triliumnext/notes:$VERSION --network host -t triliumnext/notes:$SERIES . | ||||||
|  |  | ||||||
| if [[ $VERSION != *"beta"* ]]; then | if [[ $VERSION != *"beta"* ]]; then | ||||||
|   | |||||||
| @@ -66,8 +66,6 @@ chmod 755 $PKG_DIR/trilium.sh | |||||||
| cp bin/tpl/anonymize-database.sql $PKG_DIR/ | cp bin/tpl/anonymize-database.sql $PKG_DIR/ | ||||||
|  |  | ||||||
| cp -r translations $PKG_DIR/ | cp -r translations $PKG_DIR/ | ||||||
| cp -r dump-db $PKG_DIR/ |  | ||||||
| rm -rf $PKG_DIR/dump-db/node_modules |  | ||||||
|  |  | ||||||
| VERSION=`jq -r ".version" package.json` | VERSION=`jq -r ".version" package.json` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,8 +2,6 @@ import fs from "fs-extra"; | |||||||
| import path from "path"; | import path from "path"; | ||||||
|  |  | ||||||
| const DEST_DIR = "./dist"; | 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 VERBOSE = process.env.VERBOSE; | const VERBOSE = process.env.VERBOSE; | ||||||
|  |  | ||||||
| @@ -13,43 +11,37 @@ function log(...args: any[]) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function copyNodeModuleFileOrFolder(source: string) { | function copyNodeModuleFileOrFolder(source: string) { | ||||||
|     const adjustedSource = source.substring(13); |     const destination = path.join(DEST_DIR, source); | ||||||
|     const destination = path.join(DEST_DIR_NODE_MODULES, adjustedSource); |  | ||||||
|  |  | ||||||
|     log(`Copying ${source} to ${destination}`); |     log(`Copying ${source} to ${destination}`); | ||||||
|     await fs.ensureDir(path.dirname(destination)); |     fs.ensureDirSync(path.dirname(destination)); | ||||||
|     await fs.copy(source, destination); |     fs.copySync(source, destination); | ||||||
| } | } | ||||||
|  |  | ||||||
| const copy = async () => { | try { | ||||||
|     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 = [ |     const assetsToCopy = new Set([ | ||||||
|         "config-sample.ini", |         "./images", | ||||||
|         "tsconfig.webpack.json", |         "./libraries", | ||||||
|  |         "./translations", | ||||||
|  |         "./db", | ||||||
|  |         "./config-sample.ini", | ||||||
|  |         "./package-lock.json", | ||||||
|  |         "./package.json", | ||||||
|  |         "./src/views/", | ||||||
|         "./src/etapi/etapi.openapi.yaml", |         "./src/etapi/etapi.openapi.yaml", | ||||||
|         "./src/routes/api/openapi.json" |         "./src/routes/api/openapi.json", | ||||||
|     ]; |         "./src/public/icon.png", | ||||||
|     for (const file of filesToCopy) { |         "./src/public/manifest.webmanifest", | ||||||
|         log(`Copying ${file}`); |         "./src/public/robots.txt", | ||||||
|         await fs.copy(file, path.join(DEST_DIR, file)); |         "./src/public/fonts", | ||||||
|     } |         "./src/public/stylesheets", | ||||||
|  |         "./src/public/translations" | ||||||
|  |     ]); | ||||||
|  |  | ||||||
|     const dirsToCopy = ["images", "libraries", "translations", "db"]; |     for (const asset of assetsToCopy) { | ||||||
|     for (const dir of dirsToCopy) { |         log(`Copying ${asset}`); | ||||||
|         log(`Copying ${dir}`); |         fs.copySync(asset, path.join(DEST_DIR, asset)); | ||||||
|         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))); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -58,10 +50,10 @@ const copy = async () => { | |||||||
|     const publicDirsToCopy = ["./src/public/app/doc_notes"]; |     const publicDirsToCopy = ["./src/public/app/doc_notes"]; | ||||||
|     const PUBLIC_DIR = path.join(DEST_DIR, "src", "public", "app-dist"); |     const PUBLIC_DIR = path.join(DEST_DIR, "src", "public", "app-dist"); | ||||||
|     for (const dir of publicDirsToCopy) { |     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 = [ |     const nodeModulesFile = new Set([ | ||||||
|         "node_modules/react/umd/react.production.min.js", |         "node_modules/react/umd/react.production.min.js", | ||||||
|         "node_modules/react/umd/react.development.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.production.min.js", | ||||||
| @@ -71,13 +63,9 @@ const copy = async () => { | |||||||
|         "node_modules/katex/dist/contrib/auto-render.min.js", |         "node_modules/katex/dist/contrib/auto-render.min.js", | ||||||
|         "node_modules/@highlightjs/cdn-assets/highlight.min.js", |         "node_modules/@highlightjs/cdn-assets/highlight.min.js", | ||||||
|         "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs" |         "node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs" | ||||||
|     ]; |     ]); | ||||||
|  |  | ||||||
|     for (const file of nodeModulesFile) { |     const nodeModulesFolder = new Set([ | ||||||
|         await copyNodeModuleFileOrFolder(file); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const nodeModulesFolder = [ |  | ||||||
|         "node_modules/@excalidraw/excalidraw/dist/", |         "node_modules/@excalidraw/excalidraw/dist/", | ||||||
|         "node_modules/katex/dist/", |         "node_modules/katex/dist/", | ||||||
|         "node_modules/dayjs/", |         "node_modules/dayjs/", | ||||||
| @@ -104,13 +92,15 @@ const copy = async () => { | |||||||
|         "node_modules/@highlightjs/cdn-assets/languages", |         "node_modules/@highlightjs/cdn-assets/languages", | ||||||
|         "node_modules/@highlightjs/cdn-assets/styles", |         "node_modules/@highlightjs/cdn-assets/styles", | ||||||
|         "node_modules/leaflet/dist" |         "node_modules/leaflet/dist" | ||||||
|     ]; |     ]); | ||||||
|  |  | ||||||
|     for (const folder of nodeModulesFolder) { |  | ||||||
|         await copyNodeModuleFileOrFolder(folder); |  | ||||||
|  |     for (const nodeModuleItem of [...nodeModulesFile, ...nodeModulesFolder]) { | ||||||
|  |         copyNodeModuleFileOrFolder(nodeModuleItem); | ||||||
|     } |     } | ||||||
| }; |     console.log("Copying complete!") | ||||||
|  |  | ||||||
| copy() | } catch(err) { | ||||||
|     .then(() => console.log("Copying complete!")) |     console.error("Error during copy:", err) | ||||||
|     .catch((err) => console.error("Error during copy:", err)); | } | ||||||
| @@ -14,7 +14,7 @@ fi | |||||||
|  |  | ||||||
| # Trigger the TypeScript build | # Trigger the TypeScript build | ||||||
| echo TypeScript build start | echo TypeScript build start | ||||||
| npx tsc | npm run build:ts | ||||||
| echo TypeScript build finished | echo TypeScript build finished | ||||||
|  |  | ||||||
| # Copy the TypeScript artifacts | # Copy the TypeScript artifacts | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { fileURLToPath } from "url"; | import { fileURLToPath } from "url"; | ||||||
| import { dirname, join } from "path"; | import { dirname, join } from "path"; | ||||||
| import swaggerJsdoc from 'swagger-jsdoc'; | import swaggerJsdoc from "swagger-jsdoc"; | ||||||
| import fs from "fs"; | import fs from "fs"; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| @@ -11,28 +11,30 @@ import fs from "fs"; | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| const options = { | const options = { | ||||||
|   definition: { |     definition: { | ||||||
|     openapi: '3.1.1', |         openapi: "3.1.1", | ||||||
|     info: { |         info: { | ||||||
|       title: 'Trilium Notes - Sync server API', |             title: "Trilium Notes - Sync server API", | ||||||
|       version: '0.96.6', |             version: "0.96.6", | ||||||
|       description: "This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).", |             description: | ||||||
|       contact: { |                 "This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).", | ||||||
|         name: "TriliumNext issue tracker", |             contact: { | ||||||
|         url: "https://github.com/TriliumNext/Notes/issues", |                 name: "TriliumNext issue tracker", | ||||||
|       }, |                 url: "https://github.com/TriliumNext/Notes/issues" | ||||||
|       license: { |             }, | ||||||
|         name: "GNU Free Documentation License 1.3 (or later)", |             license: { | ||||||
|         url: "https://www.gnu.org/licenses/fdl-1.3", |                 name: "GNU Free Documentation License 1.3 (or later)", | ||||||
|       }, |                 url: "https://www.gnu.org/licenses/fdl-1.3" | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     }, |     }, | ||||||
|   }, |     apis: [ | ||||||
|   apis: [ |         // Put individual files here to have them ordered first. | ||||||
|     // Put individual files here to have them ordered first. |         "./src/routes/api/setup.ts", | ||||||
|     './src/routes/api/setup.ts', |         // all other files | ||||||
|     // all other files |         "./src/routes/api/*.ts", | ||||||
|     './src/routes/api/*.ts', './bin/generate-openapi.js' |         "./bin/generate-openapi.js" | ||||||
|   ], |     ] | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const openapiSpecification = swaggerJsdoc(options); | const openapiSpecification = swaggerJsdoc(options); | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| #!/usr/bin/env bash | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | set -e | ||||||
|  |  | ||||||
| if [[ $# -eq 0 ]] ; then | if [[ $# -eq 0 ]] ; then | ||||||
|     echo "Missing argument of new version" |     echo "Missing argument of new version" | ||||||
|     exit 1 |     exit 1 | ||||||
|   | |||||||
| @@ -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 |  | ||||||
| ); |  | ||||||
| @@ -132,14 +132,3 @@ CREATE INDEX IDX_attachments_ownerId_role | |||||||
| CREATE INDEX IDX_notes_blobId on notes (blobId); | CREATE INDEX IDX_notes_blobId on notes (blobId); | ||||||
| CREATE INDEX IDX_revisions_blobId on revisions (blobId); | CREATE INDEX IDX_revisions_blobId on revisions (blobId); | ||||||
| CREATE INDEX IDX_attachments_blobId on attachments (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 |  | ||||||
| ); |  | ||||||
| @@ -14,7 +14,7 @@ npm install | |||||||
|  |  | ||||||
| ## Running | ## Running | ||||||
|  |  | ||||||
| See output of `npx esrun dump.ts --help`: | See output of `npx tsx dump.ts --help`: | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| dump-db.ts <path_to_document> <target_directory> | dump-db.ts <path_to_document> <target_directory> | ||||||
|   | |||||||
							
								
								
									
										858
									
								
								dump-db/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						| @@ -18,9 +18,9 @@ | |||||||
|   "homepage": "https://github.com/TriliumNext/Notes/blob/master/dump-db/README.md", |   "homepage": "https://github.com/TriliumNext/Notes/blob/master/dump-db/README.md", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "better-sqlite3": "^11.1.2", |     "better-sqlite3": "^11.1.2", | ||||||
|     "esrun": "^3.2.26", |  | ||||||
|     "mime-types": "^2.1.34", |     "mime-types": "^2.1.34", | ||||||
|     "sanitize-filename": "^1.6.3", |     "sanitize-filename": "^1.6.3", | ||||||
|  |     "tsx": "^4.19.3", | ||||||
|     "yargs": "^17.3.1" |     "yargs": "^17.3.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|   | |||||||
| @@ -11,16 +11,14 @@ test("Displays translation on desktop", async ({ page, context }) => { | |||||||
|     const app = new App(page, context); |     const app = new App(page, context); | ||||||
|     await app.goto(); |     await app.goto(); | ||||||
|  |  | ||||||
|     await expect(page.locator("#left-pane .quick-search input")) |     await expect(page.locator("#left-pane .quick-search input")).toHaveAttribute("placeholder", "Quick search"); | ||||||
|         .toHaveAttribute("placeholder", "Quick search"); |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
| test("Displays translation on mobile", async ({ page, context }) => { | test("Displays translation on mobile", async ({ page, context }) => { | ||||||
|     const app = new App(page, context); |     const app = new App(page, context); | ||||||
|     await app.goto({ isMobile: true }); |     await app.goto({ isMobile: true }); | ||||||
|  |  | ||||||
|     await expect(page.locator("#mobile-sidebar-wrapper .quick-search input")) |     await expect(page.locator("#mobile-sidebar-wrapper .quick-search input")).toHaveAttribute("placeholder", "Quick search"); | ||||||
|         .toHaveAttribute("placeholder", "Quick search"); |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
| test("Displays translations in Settings", async ({ page, context }) => { | test("Displays translations in Settings", async ({ page, context }) => { | ||||||
| @@ -44,14 +42,16 @@ test("User can change language from settings", async ({ page, context }) => { | |||||||
|  |  | ||||||
|     // Check that the default value (English) is set. |     // Check that the default value (English) is set. | ||||||
|     await expect(app.currentNoteSplit).toContainText("Theme"); |     await expect(app.currentNoteSplit).toContainText("Theme"); | ||||||
|     const languageCombobox = await app.currentNoteSplit.getByRole("combobox").first(); |     const languageCombobox = app.currentNoteSplit.getByRole("combobox").first(); | ||||||
|     await expect(languageCombobox).toHaveValue("en"); |     await expect(languageCombobox).toHaveValue("en"); | ||||||
|  |  | ||||||
|     // Select Chinese and ensure the translation is set. |     // Select Chinese and ensure the translation is set. | ||||||
|     await languageCombobox.selectOption("cn"); |     await languageCombobox.selectOption("cn"); | ||||||
|     await expect(app.currentNoteSplit).toContainText("主题", { timeout: 15000 }); |     await expect(app.currentNoteSplit).toContainText("主题", { timeout: 15000 }); | ||||||
|  |     await expect(languageCombobox).toHaveValue("cn"); | ||||||
|  |  | ||||||
|     // Select English again. |     // Select English again. | ||||||
|     await languageCombobox.selectOption("en"); |     await languageCombobox.selectOption("en"); | ||||||
|     await expect(app.currentNoteSplit).toContainText("Language", { timeout: 15000 }); |     await expect(app.currentNoteSplit).toContainText("Language", { timeout: 15000 }); | ||||||
|  |     await expect(languageCombobox).toHaveValue("en"); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -19,13 +19,13 @@ test("Can drag tabs around", async ({ page, context }) => { | |||||||
|     let tab = app.getTab(0); |     let tab = app.getTab(0); | ||||||
|  |  | ||||||
|     // Drag the first tab at the end |     // Drag the first tab at the end | ||||||
|     await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 }}); |     await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 } }); | ||||||
|  |  | ||||||
|     tab = app.getTab(2); |     tab = app.getTab(2); | ||||||
|     await expect(tab).toContainText(NOTE_TITLE); |     await expect(tab).toContainText(NOTE_TITLE); | ||||||
|  |  | ||||||
|     // Drag the tab to the left |     // Drag the tab to the left | ||||||
|     await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 }}); |     await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 } }); | ||||||
|     await expect(app.getTab(0)).toContainText(NOTE_TITLE); |     await expect(app.getTab(0)).toContainText(NOTE_TITLE); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,7 +39,9 @@ test("Displays lint errors for backend script", async ({ page, context }) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| async function expectTooltip(page: Page, tooltip: string) { | async function expectTooltip(page: Page, tooltip: string) { | ||||||
|     await expect(page.locator(".CodeMirror-lint-tooltip:visible", { |     await expect( | ||||||
|         "hasText": tooltip |         page.locator(".CodeMirror-lint-tooltip:visible", { | ||||||
|     })).toBeVisible(); |             hasText: tooltip | ||||||
|  |         }) | ||||||
|  |     ).toBeVisible(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,8 @@ import App from "../support/app"; | |||||||
|  |  | ||||||
| test("renders ELK flowchart", async ({ page, context }) => { | test("renders ELK flowchart", async ({ page, context }) => { | ||||||
|     await testAriaSnapshot({ |     await testAriaSnapshot({ | ||||||
|         page, context, |         page, | ||||||
|  |         context, | ||||||
|         noteTitle: "Flowchart ELK on", |         noteTitle: "Flowchart ELK on", | ||||||
|         snapshot: ` |         snapshot: ` | ||||||
|             - document: |             - document: | ||||||
| @@ -22,12 +23,13 @@ test("renders ELK flowchart", async ({ page, context }) => { | |||||||
|                 - paragraph: Guarantee |                 - paragraph: Guarantee | ||||||
|                 - text: Interfaces for B |                 - text: Interfaces for B | ||||||
|         ` |         ` | ||||||
|     }) |     }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| test("renders standard flowchart", async ({ page, context }) => { | test("renders standard flowchart", async ({ page, context }) => { | ||||||
|     await testAriaSnapshot({ |     await testAriaSnapshot({ | ||||||
|         page, context, |         page, | ||||||
|  |         context, | ||||||
|         noteTitle: "Flowchart ELK off", |         noteTitle: "Flowchart ELK off", | ||||||
|         snapshot: ` |         snapshot: ` | ||||||
|             - document: |             - document: | ||||||
| @@ -46,7 +48,7 @@ test("renders standard flowchart", async ({ page, context }) => { | |||||||
|                 - paragraph: C |                 - paragraph: C | ||||||
|                 - text: Interfaces for B |                 - text: Interfaces for B | ||||||
|         ` |         ` | ||||||
|     }) |     }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| interface AriaTestOpts { | interface AriaTestOpts { | ||||||
|   | |||||||
| @@ -44,8 +44,8 @@ test("Highlights list is displayed", async ({ page, context }) => { | |||||||
|  |  | ||||||
|     await expect(app.sidebar).toContainText("Highlights List"); |     await expect(app.sidebar).toContainText("Highlights List"); | ||||||
|     const rootList = app.sidebar.locator(".highlights-list ol"); |     const rootList = app.sidebar.locator(".highlights-list ol"); | ||||||
|     let index=0; |     let index = 0; | ||||||
|     for (const highlightedEl of [ "Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2" ]) { |     for (const highlightedEl of ["Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2"]) { | ||||||
|         await expect(rootList.locator("li").nth(index++)).toContainText(highlightedEl); |         await expect(rootList.locator("li").nth(index++)).toContainText(highlightedEl); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
| @@ -54,7 +54,7 @@ test("Displays math popup", async ({ page, context }) => { | |||||||
|     const app = new App(page, context); |     const app = new App(page, context); | ||||||
|     await app.goto(); |     await app.goto(); | ||||||
|     await app.goToNoteInNewTab("Empty text"); |     await app.goToNoteInNewTab("Empty text"); | ||||||
|     const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor") |     const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor"); | ||||||
|     await noteContent.fill("Hello world"); |     await noteContent.fill("Hello world"); | ||||||
|     await noteContent.press("ControlOrMeta+M"); |     await noteContent.press("ControlOrMeta+M"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ export default class App { | |||||||
|         this.tabBar = page.locator(".tab-row-widget-container"); |         this.tabBar = page.locator(".tab-row-widget-container"); | ||||||
|         this.noteTree = page.locator(".tree-wrapper"); |         this.noteTree = page.locator(".tree-wrapper"); | ||||||
|         this.launcherBar = page.locator("#launcher-container"); |         this.launcherBar = page.locator("#launcher-container"); | ||||||
|         this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)") |         this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)"); | ||||||
|         this.sidebar = page.locator("#right-pane"); |         this.sidebar = page.locator("#right-pane"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -42,12 +42,11 @@ export default class App { | |||||||
|             url = "/"; |             url = "/"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await this.page.goto(url, { waitUntil: "networkidle" }); |         await this.page.goto(url, { waitUntil: "networkidle", timeout: 30_000 }); | ||||||
|  |  | ||||||
|         // Wait for the page to load. |         // Wait for the page to load. | ||||||
|         if (url === "/") { |         if (url === "/") { | ||||||
|             await expect(this.page.locator(".tree")) |             await expect(this.page.locator(".tree")).toContainText("Trilium Integration Test"); | ||||||
|                 .toContainText("Trilium Integration Test"); |  | ||||||
|             await this.closeAllTabs(); |             await this.closeAllTabs(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -109,11 +108,12 @@ export default class App { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         expect(csrfToken).toBeTruthy(); |         expect(csrfToken).toBeTruthy(); | ||||||
|         await expect(await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, { |         await expect( | ||||||
|             headers: { |             await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, { | ||||||
|                 "x-csrf-token": csrfToken |                 headers: { | ||||||
|             } |                     "x-csrf-token": csrfToken | ||||||
|         })).toBeOK(); |                 } | ||||||
|  |             }) | ||||||
|  |         ).toBeOK(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,43 @@ | |||||||
|  | import eslint from "@eslint/js"; | ||||||
|  | import tseslint from "typescript-eslint"; | ||||||
|  |  | ||||||
|  | export default tseslint.config( | ||||||
|  |     eslint.configs.recommended, | ||||||
|  |     tseslint.configs.recommended, | ||||||
|  |     // consider using rules below, once we have a full TS codebase and can be more strict | ||||||
|  |     // tseslint.configs.strictTypeChecked, | ||||||
|  |     // tseslint.configs.stylisticTypeChecked, | ||||||
|  |     // tseslint.configs.recommendedTypeChecked, | ||||||
|  |     { | ||||||
|  |         languageOptions: { | ||||||
|  |             parserOptions: { | ||||||
|  |                 projectService: true, | ||||||
|  |                 tsconfigRootDir: import.meta.dirname | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         rules: { | ||||||
|  |             // add rule overrides here | ||||||
|  |             "no-undef": "off", | ||||||
|  |             "no-unused-vars": "off", | ||||||
|  |             "@typescript-eslint/no-unused-vars": [ | ||||||
|  |                 "error", | ||||||
|  |                 { | ||||||
|  |                     "argsIgnorePattern": "^_", | ||||||
|  |                     "varsIgnorePattern": "^_", | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         ignores: [ | ||||||
|  |             "build/*", | ||||||
|  |             "dist/*", | ||||||
|  |             "docs/*", | ||||||
|  |             "libraries/*", | ||||||
|  |             "src/public/app-dist/*", | ||||||
|  |             "src/public/app/doc_notes/*" | ||||||
|  |         ] | ||||||
|  |     } | ||||||
|  | ); | ||||||
							
								
								
									
										28
									
								
								libraries/codemirror/eslint.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -35,39 +35,13 @@ | |||||||
|             return []; |             return []; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await glob.requireLibrary(glob.ESLINT); |  | ||||||
|  |  | ||||||
|         if (text.length > 20000) { |         if (text.length > 20000) { | ||||||
|             console.log("Skipping linting because of large size: ", text.length); |             console.log("Skipping linting because of large size: ", text.length); | ||||||
|  |  | ||||||
|             return []; |             return []; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const errors = new eslint().verify(text, { |         const errors = await glob.linter(text); | ||||||
|             root: true, |  | ||||||
|             parserOptions: { |  | ||||||
|                 ecmaVersion: "2019" |  | ||||||
|             }, |  | ||||||
|             extends: ['eslint:recommended', 'airbnb-base'], |  | ||||||
|             env: { |  | ||||||
|                 'browser': true, |  | ||||||
|                 'node': true |  | ||||||
|             }, |  | ||||||
|             rules: { |  | ||||||
|                 'import/no-unresolved': 'off', |  | ||||||
|                 'func-names': 'off', |  | ||||||
|                 'comma-dangle': ['warn'], |  | ||||||
|                 'padded-blocks': 'off', |  | ||||||
|                 'linebreak-style': 'off', |  | ||||||
|                 'class-methods-use-this': 'off', |  | ||||||
|                 'no-unused-vars': ['warn', { vars: 'local', args: 'after-used' }], |  | ||||||
|                 'no-nested-ternary': 'off', |  | ||||||
|                 'no-underscore-dangle': ['error', {'allow': ['_super', '_lookupFactory']}] |  | ||||||
|             }, |  | ||||||
|             globals: { |  | ||||||
|                 "api": "readonly" |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         console.log(errors); |         console.log(errors); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										112883
									
								
								libraries/eslint/eslint.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1107
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
							
								
								
									
										41
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -2,7 +2,7 @@ | |||||||
|   "name": "trilium", |   "name": "trilium", | ||||||
|   "productName": "TriliumNext Notes", |   "productName": "TriliumNext Notes", | ||||||
|   "description": "Build your personal knowledge base with TriliumNext Notes", |   "description": "Build your personal knowledge base with TriliumNext Notes", | ||||||
|   "version": "0.92.2-beta", |   "version": "0.92.3-beta", | ||||||
|   "license": "AGPL-3.0-only", |   "license": "AGPL-3.0-only", | ||||||
|   "main": "./dist/electron-main.js", |   "main": "./dist/electron-main.js", | ||||||
|   "author": { |   "author": { | ||||||
| @@ -43,20 +43,24 @@ | |||||||
|     "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", |     "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", | ||||||
|     "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", |     "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", | ||||||
|     "docs:build": "npm run docs:build-backend && npm run docs:build-frontend", |     "docs:build": "npm run docs:build-backend && npm run docs:build-frontend", | ||||||
|     "build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts", |     "build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts --progress", | ||||||
|     "build:prepare-dist": "npm run build:webpack && rimraf ./dist && tsc && tsx ./bin/copy-dist.ts", |     "build:ts": "tsc -p tsconfig.build.json", | ||||||
|  |     "build:clean": "rimraf ./dist ./build", | ||||||
|  |     "build:prepare-dist": "npm run build:clean && npm run build:ts && npm run build:webpack && tsx ./bin/copy-dist.ts", | ||||||
|     "test": "npm run client:test && npm run server:test", |     "test": "npm run client:test && npm run server:test", | ||||||
|     "server:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest", |     "server:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest", | ||||||
|     "server:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --coverage", |     "server:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --coverage", | ||||||
|     "client:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app", |     "client:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app", | ||||||
|     "client:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app --coverage", |     "client:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app --coverage", | ||||||
|     "test:playwright": "playwright test", |     "test:playwright": "playwright test --workers 1", | ||||||
|     "test:integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", |     "test:integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", | ||||||
|     "test:integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", |     "test:integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", | ||||||
|     "test:integration-mem-db-dev": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", |     "test:integration-mem-db-dev": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", | ||||||
|     "dev:watch-dist": "tsx ./bin/watch-dist.ts", |     "dev:watch-dist": "tsx ./bin/watch-dist.ts", | ||||||
|     "dev:prettier-check": "prettier . --check", |     "dev:prettier-check": "prettier . --check", | ||||||
|     "dev:prettier-fix": "prettier . --write", |     "dev:prettier-fix": "prettier . --write", | ||||||
|  |     "dev:linter-check": "eslint .", | ||||||
|  |     "dev:linter-fix": "eslint . --fix", | ||||||
|     "chore:update-build-info": "tsx bin/update-build-info.ts", |     "chore:update-build-info": "tsx bin/update-build-info.ts", | ||||||
|     "chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts", |     "chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts", | ||||||
|     "chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000", |     "chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000", | ||||||
| @@ -77,7 +81,7 @@ | |||||||
|     "archiver": "7.0.1", |     "archiver": "7.0.1", | ||||||
|     "async-mutex": "0.5.0", |     "async-mutex": "0.5.0", | ||||||
|     "autocomplete.js": "0.38.1", |     "autocomplete.js": "0.38.1", | ||||||
|     "axios": "1.8.1", |     "axios": "1.8.2", | ||||||
|     "better-sqlite3": "11.8.1", |     "better-sqlite3": "11.8.1", | ||||||
|     "boxicons": "2.1.4", |     "boxicons": "2.1.4", | ||||||
|     "chardet": "2.1.0", |     "chardet": "2.1.0", | ||||||
| @@ -98,12 +102,12 @@ | |||||||
|     "electron-squirrel-startup": "1.0.1", |     "electron-squirrel-startup": "1.0.1", | ||||||
|     "electron-window-state": "5.0.3", |     "electron-window-state": "5.0.3", | ||||||
|     "escape-html": "1.0.3", |     "escape-html": "1.0.3", | ||||||
|  |     "eslint-linter-browserify": "9.22.0", | ||||||
|     "express": "4.21.2", |     "express": "4.21.2", | ||||||
|     "express-rate-limit": "7.5.0", |     "express-rate-limit": "7.5.0", | ||||||
|     "express-session": "1.18.1", |     "express-session": "1.18.1", | ||||||
|     "force-graph": "1.49.2", |     "force-graph": "1.49.3", | ||||||
|     "fs-extra": "11.3.0", |     "fs-extra": "11.3.0", | ||||||
|     "happy-dom": "17.1.8", |  | ||||||
|     "helmet": "8.0.0", |     "helmet": "8.0.0", | ||||||
|     "html": "1.0.0", |     "html": "1.0.0", | ||||||
|     "html2plaintext": "2.1.4", |     "html2plaintext": "2.1.4", | ||||||
| @@ -123,7 +127,6 @@ | |||||||
|     "jsdom": "26.0.0", |     "jsdom": "26.0.0", | ||||||
|     "jsplumb": "2.15.6", |     "jsplumb": "2.15.6", | ||||||
|     "katex": "0.16.21", |     "katex": "0.16.21", | ||||||
|     "knockout": "3.5.1", |  | ||||||
|     "leaflet": "1.9.4", |     "leaflet": "1.9.4", | ||||||
|     "leaflet-gpx": "2.1.2", |     "leaflet-gpx": "2.1.2", | ||||||
|     "mark.js": "8.11.1", |     "mark.js": "8.11.1", | ||||||
| @@ -150,7 +153,6 @@ | |||||||
|     "striptags": "3.2.0", |     "striptags": "3.2.0", | ||||||
|     "swagger-ui-express": "5.0.1", |     "swagger-ui-express": "5.0.1", | ||||||
|     "tmp": "0.2.3", |     "tmp": "0.2.3", | ||||||
|     "ts-loader": "9.5.2", |  | ||||||
|     "turndown": "7.2.0", |     "turndown": "7.2.0", | ||||||
|     "unescape": "1.0.1", |     "unescape": "1.0.1", | ||||||
|     "vanilla-js-wheel-zoom": "9.0.4", |     "vanilla-js-wheel-zoom": "9.0.4", | ||||||
| @@ -168,7 +170,8 @@ | |||||||
|     "@electron-forge/maker-zip": "7.7.0", |     "@electron-forge/maker-zip": "7.7.0", | ||||||
|     "@electron-forge/plugin-auto-unpack-natives": "7.7.0", |     "@electron-forge/plugin-auto-unpack-natives": "7.7.0", | ||||||
|     "@electron/rebuild": "3.7.1", |     "@electron/rebuild": "3.7.1", | ||||||
|     "@playwright/test": "1.50.1", |     "@eslint/js": "9.22.0", | ||||||
|  |     "@playwright/test": "1.51.0", | ||||||
|     "@popperjs/core": "2.11.8", |     "@popperjs/core": "2.11.8", | ||||||
|     "@types/archiver": "6.0.3", |     "@types/archiver": "6.0.3", | ||||||
|     "@types/better-sqlite3": "7.6.12", |     "@types/better-sqlite3": "7.6.12", | ||||||
| @@ -193,7 +196,7 @@ | |||||||
|     "@types/leaflet-gpx": "1.3.7", |     "@types/leaflet-gpx": "1.3.7", | ||||||
|     "@types/mime-types": "2.1.4", |     "@types/mime-types": "2.1.4", | ||||||
|     "@types/multer": "1.4.12", |     "@types/multer": "1.4.12", | ||||||
|     "@types/node": "22.13.8", |     "@types/node": "22.13.9", | ||||||
|     "@types/react": "18.3.18", |     "@types/react": "18.3.18", | ||||||
|     "@types/react-dom": "18.3.5", |     "@types/react-dom": "18.3.5", | ||||||
|     "@types/safe-compare": "1.1.2", |     "@types/safe-compare": "1.1.2", | ||||||
| @@ -207,23 +210,27 @@ | |||||||
|     "@types/swagger-ui-express": "4.1.8", |     "@types/swagger-ui-express": "4.1.8", | ||||||
|     "@types/tmp": "0.2.6", |     "@types/tmp": "0.2.6", | ||||||
|     "@types/turndown": "5.0.5", |     "@types/turndown": "5.0.5", | ||||||
|     "@types/ws": "8.5.14", |     "@types/ws": "8.18.0", | ||||||
|     "@types/xml2js": "0.4.14", |     "@types/xml2js": "0.4.14", | ||||||
|     "@types/yargs": "17.0.33", |     "@types/yargs": "17.0.33", | ||||||
|     "@vitest/coverage-v8": "3.0.7", |     "@vitest/coverage-v8": "3.0.8", | ||||||
|     "autoprefixer": "10.4.20", |     "autoprefixer": "10.4.20", | ||||||
|     "bootstrap": "5.3.3", |     "bootstrap": "5.3.3", | ||||||
|     "cross-env": "7.0.3", |     "cross-env": "7.0.3", | ||||||
|     "css-loader": "7.1.2", |     "css-loader": "7.1.2", | ||||||
|     "electron": "34.3.0", |     "electron": "34.3.1", | ||||||
|  |     "eslint": "9.22.0", | ||||||
|     "esm": "3.2.25", |     "esm": "3.2.25", | ||||||
|  |     "globals": "16.0.0", | ||||||
|  |     "happy-dom": "17.4.0", | ||||||
|     "i18next-http-backend": "3.0.2", |     "i18next-http-backend": "3.0.2", | ||||||
|     "jsdoc": "4.0.4", |     "jsdoc": "4.0.4", | ||||||
|  |     "knockout": "3.5.1", | ||||||
|     "lorem-ipsum": "2.0.8", |     "lorem-ipsum": "2.0.8", | ||||||
|     "mini-css-extract-plugin": "2.9.2", |     "mini-css-extract-plugin": "2.9.2", | ||||||
|     "nodemon": "3.1.9", |     "nodemon": "3.1.9", | ||||||
|     "postcss-loader": "8.1.1", |     "postcss-loader": "8.1.1", | ||||||
|     "prettier": "3.5.2", |     "prettier": "3.5.3", | ||||||
|     "rcedit": "4.0.1", |     "rcedit": "4.0.1", | ||||||
|     "rimraf": "6.0.1", |     "rimraf": "6.0.1", | ||||||
|     "sass": "1.85.1", |     "sass": "1.85.1", | ||||||
| @@ -231,11 +238,13 @@ | |||||||
|     "split.js": "1.6.5", |     "split.js": "1.6.5", | ||||||
|     "supertest": "7.0.0", |     "supertest": "7.0.0", | ||||||
|     "swagger-jsdoc": "6.2.8", |     "swagger-jsdoc": "6.2.8", | ||||||
|  |     "ts-loader": "9.5.2", | ||||||
|     "tslib": "2.8.1", |     "tslib": "2.8.1", | ||||||
|     "tsx": "4.19.3", |     "tsx": "4.19.3", | ||||||
|     "typedoc": "0.27.9", |     "typedoc": "0.27.9", | ||||||
|     "typescript": "5.8.2", |     "typescript": "5.8.2", | ||||||
|     "vitest": "3.0.7", |     "typescript-eslint": "8.26.0", | ||||||
|  |     "vitest": "3.0.8", | ||||||
|     "webpack": "5.98.0", |     "webpack": "5.98.0", | ||||||
|     "webpack-cli": "6.0.1", |     "webpack-cli": "6.0.1", | ||||||
|     "webpack-dev-middleware": "7.4.2" |     "webpack-dev-middleware": "7.4.2" | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { defineConfig, devices } from '@playwright/test'; | import { defineConfig, devices } from "@playwright/test"; | ||||||
|  |  | ||||||
| const SERVER_URL = 'http://127.0.0.1:8082'; | const SERVER_URL = "http://127.0.0.1:8082"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Read environment variables from file. |  * Read environment variables from file. | ||||||
| @@ -14,68 +14,70 @@ const SERVER_URL = 'http://127.0.0.1:8082'; | |||||||
|  * See https://playwright.dev/docs/test-configuration. |  * See https://playwright.dev/docs/test-configuration. | ||||||
|  */ |  */ | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|   testDir: './e2e', |     testDir: "./e2e", | ||||||
|   /* Run tests in files in parallel */ |     /* Run tests in files in parallel */ | ||||||
|   fullyParallel: true, |     fullyParallel: true, | ||||||
|   /* Fail the build on CI if you accidentally left test.only in the source code. */ |     /* Fail the build on CI if you accidentally left test.only in the source code. */ | ||||||
|   forbidOnly: !!process.env.CI, |     forbidOnly: !!process.env.CI, | ||||||
|   /* Retry on CI only */ |     /* Retry on CI only */ | ||||||
|   retries: process.env.CI ? 2 : 0, |     retries: process.env.CI ? 2 : 0, | ||||||
|   /* Opt out of parallel tests on CI. */ |     /* Opt out of parallel tests on CI. */ | ||||||
|   workers: process.env.CI ? 1 : undefined, |     workers: process.env.CI ? 1 : undefined, | ||||||
|   /* Reporter to use. See https://playwright.dev/docs/test-reporters */ |     /* Reporter to use. See https://playwright.dev/docs/test-reporters */ | ||||||
|   reporter: 'html', |     reporter: "html", | ||||||
|   /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ |     /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ | ||||||
|   use: { |     use: { | ||||||
|     /* Base URL to use in actions like `await page.goto('/')`. */ |         /* Base URL to use in actions like `await page.goto('/')`. */ | ||||||
|     baseURL: SERVER_URL, |         baseURL: SERVER_URL, | ||||||
|  |  | ||||||
|     /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ |         /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ | ||||||
|     trace: 'on-first-retry', |         trace: "on-first-retry" | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   /* Configure projects for major browsers */ |  | ||||||
|   projects: [ |  | ||||||
|     { |  | ||||||
|       name: 'chromium', |  | ||||||
|       use: { ...devices['Desktop Chrome'] }, |  | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     // { |     /* Configure projects for major browsers */ | ||||||
|     //   name: 'firefox', |     projects: [ | ||||||
|     //   use: { ...devices['Desktop Firefox'] }, |         { | ||||||
|     // }, |             name: "chromium", | ||||||
|  |             use: { ...devices["Desktop Chrome"] } | ||||||
|  |         } | ||||||
|  |  | ||||||
|     // { |         // { | ||||||
|     //   name: 'webkit', |         //   name: 'firefox', | ||||||
|     //   use: { ...devices['Desktop Safari'] }, |         //   use: { ...devices['Desktop Firefox'] }, | ||||||
|     // }, |         // }, | ||||||
|  |  | ||||||
|     /* Test against mobile viewports. */ |         // { | ||||||
|     // { |         //   name: 'webkit', | ||||||
|     //   name: 'Mobile Chrome', |         //   use: { ...devices['Desktop Safari'] }, | ||||||
|     //   use: { ...devices['Pixel 5'] }, |         // }, | ||||||
|     // }, |  | ||||||
|     // { |  | ||||||
|     //   name: 'Mobile Safari', |  | ||||||
|     //   use: { ...devices['iPhone 12'] }, |  | ||||||
|     // }, |  | ||||||
|  |  | ||||||
|     /* Test against branded browsers. */ |         /* Test against mobile viewports. */ | ||||||
|     // { |         // { | ||||||
|     //   name: 'Microsoft Edge', |         //   name: 'Mobile Chrome', | ||||||
|     //   use: { ...devices['Desktop Edge'], channel: 'msedge' }, |         //   use: { ...devices['Pixel 5'] }, | ||||||
|     // }, |         // }, | ||||||
|     // { |         // { | ||||||
|     //   name: 'Google Chrome', |         //   name: 'Mobile Safari', | ||||||
|     //   use: { ...devices['Desktop Chrome'], channel: 'chrome' }, |         //   use: { ...devices['iPhone 12'] }, | ||||||
|     // }, |         // }, | ||||||
|   ], |  | ||||||
|  |  | ||||||
|   /* Run your local dev server before starting the tests */ |         /* Test against branded browsers. */ | ||||||
|   webServer: !process.env.TRILIUM_DOCKER ? { |         // { | ||||||
|     command: 'npm run test:integration-mem-db-dev', |         //   name: 'Microsoft Edge', | ||||||
|     url: SERVER_URL, |         //   use: { ...devices['Desktop Edge'], channel: 'msedge' }, | ||||||
|     reuseExistingServer: !process.env.CI, |         // }, | ||||||
|   } : undefined, |         // { | ||||||
|  |         //   name: 'Google Chrome', | ||||||
|  |         //   use: { ...devices['Desktop Chrome'], channel: 'chrome' }, | ||||||
|  |         // }, | ||||||
|  |     ], | ||||||
|  |  | ||||||
|  |     /* Run your local dev server before starting the tests */ | ||||||
|  |     webServer: !process.env.TRILIUM_DOCKER | ||||||
|  |         ? { | ||||||
|  |               command: "npm run test:integration-mem-db-dev", | ||||||
|  |               url: SERVER_URL, | ||||||
|  |               reuseExistingServer: !process.env.CI | ||||||
|  |           } | ||||||
|  |         : undefined | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ import type { AttachmentRow, BlobRow, RevisionRow } from "./entities/rows.js"; | |||||||
| import BBlob from "./entities/bblob.js"; | import BBlob from "./entities/bblob.js"; | ||||||
| import BRecentNote from "./entities/brecent_note.js"; | import BRecentNote from "./entities/brecent_note.js"; | ||||||
| import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; | import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; | ||||||
| import type BTask from "./entities/btask.js"; |  | ||||||
|  |  | ||||||
| interface AttachmentOpts { | interface AttachmentOpts { | ||||||
|     includeContentLength?: boolean; |     includeContentLength?: boolean; | ||||||
| @@ -33,7 +32,6 @@ export default class Becca { | |||||||
|     attributeIndex!: Record<string, BAttribute[]>; |     attributeIndex!: Record<string, BAttribute[]>; | ||||||
|     options!: Record<string, BOption>; |     options!: Record<string, BOption>; | ||||||
|     etapiTokens!: Record<string, BEtapiToken>; |     etapiTokens!: Record<string, BEtapiToken>; | ||||||
|     tasks!: Record<string, BTask>; |  | ||||||
|  |  | ||||||
|     allNoteSetCache: NoteSet | null; |     allNoteSetCache: NoteSet | null; | ||||||
|  |  | ||||||
| @@ -50,7 +48,6 @@ export default class Becca { | |||||||
|         this.attributeIndex = {}; |         this.attributeIndex = {}; | ||||||
|         this.options = {}; |         this.options = {}; | ||||||
|         this.etapiTokens = {}; |         this.etapiTokens = {}; | ||||||
|         this.tasks = {}; |  | ||||||
|  |  | ||||||
|         this.dirtyNoteSetCache(); |         this.dirtyNoteSetCache(); | ||||||
|  |  | ||||||
| @@ -216,14 +213,6 @@ export default class Becca { | |||||||
|         return this.etapiTokens[etapiTokenId]; |         return this.etapiTokens[etapiTokenId]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     getTasks(): BTask[] { |  | ||||||
|         return Object.values(this.tasks); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     getTask(taskId: string): BTask | null { |  | ||||||
|         return this.tasks[taskId]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     getEntity<T extends AbstractBeccaEntity<T>>(entityName: string, entityId: string): AbstractBeccaEntity<T> | null { |     getEntity<T extends AbstractBeccaEntity<T>>(entityName: string, entityId: string): AbstractBeccaEntity<T> | null { | ||||||
|         if (!entityName || !entityId) { |         if (!entityName || !entityId) { | ||||||
|             return null; |             return null; | ||||||
|   | |||||||
| @@ -11,10 +11,9 @@ import BOption from "./entities/boption.js"; | |||||||
| import BEtapiToken from "./entities/betapi_token.js"; | import BEtapiToken from "./entities/betapi_token.js"; | ||||||
| import cls from "../services/cls.js"; | import cls from "../services/cls.js"; | ||||||
| import entityConstructor from "../becca/entity_constructor.js"; | import entityConstructor from "../becca/entity_constructor.js"; | ||||||
| import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow, TaskRow } from "./entities/rows.js"; | import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from "./entities/rows.js"; | ||||||
| import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; | import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js"; | ||||||
| import ws from "../services/ws.js"; | import ws from "../services/ws.js"; | ||||||
| import BTask from "./entities/btask.js"; |  | ||||||
|  |  | ||||||
| const beccaLoaded = new Promise<void>(async (res, rej) => { | const beccaLoaded = new Promise<void>(async (res, rej) => { | ||||||
|     const sqlInit = (await import("../services/sql_init.js")).default; |     const sqlInit = (await import("../services/sql_init.js")).default; | ||||||
| @@ -64,17 +63,6 @@ function load() { | |||||||
|         for (const row of sql.getRows<EtapiTokenRow>(`SELECT etapiTokenId, name, tokenHash, utcDateCreated, utcDateModified FROM etapi_tokens WHERE isDeleted = 0`)) { |         for (const row of sql.getRows<EtapiTokenRow>(`SELECT etapiTokenId, name, tokenHash, utcDateCreated, utcDateModified FROM etapi_tokens WHERE isDeleted = 0`)) { | ||||||
|             new BEtapiToken(row); |             new BEtapiToken(row); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |  | ||||||
|             for (const row of sql.getRows<TaskRow>(`SELECT taskId, parentNoteId, title, dueDate, isDone, isDeleted FROM tasks WHERE isDeleted = 0`)) { |  | ||||||
|                 new BTask(row); |  | ||||||
|             } |  | ||||||
|         } catch (e: any) { |  | ||||||
|             // Some older migrations trigger becca which would fail since the "tasks" table is not yet defined (didn't reach the right migration). |  | ||||||
|             if (!(e.message.includes("no such table"))) { |  | ||||||
|                 throw e; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     for (const noteId in becca.notes) { |     for (const noteId in becca.notes) { | ||||||
|   | |||||||
| @@ -1618,7 +1618,7 @@ class BNote extends AbstractBeccaEntity<BNote> { | |||||||
|      * @param matchBy - choose by which property we detect if to update an existing attachment. |      * @param matchBy - choose by which property we detect if to update an existing attachment. | ||||||
|      *                      Supported values are either 'attachmentId' (default) or 'title' |      *                      Supported values are either 'attachmentId' (default) or 'title' | ||||||
|      */ |      */ | ||||||
|     saveAttachment({ attachmentId, role, mime, title, content, position }: AttachmentRow, matchBy = "attachmentId") { |     saveAttachment({ attachmentId, role, mime, title, content, position }: AttachmentRow, matchBy: "attachmentId" | "title" | undefined = "attachmentId") { | ||||||
|         if (!["attachmentId", "title"].includes(matchBy)) { |         if (!["attachmentId", "title"].includes(matchBy)) { | ||||||
|             throw new Error(`Unsupported value '${matchBy}' for matchBy param, has to be either 'attachmentId' or 'title'.`); |             throw new Error(`Unsupported value '${matchBy}' for matchBy param, has to be either 'attachmentId' or 'title'.`); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import becca from "../becca.js"; | |||||||
| import AbstractBeccaEntity from "./abstract_becca_entity.js"; | import AbstractBeccaEntity from "./abstract_becca_entity.js"; | ||||||
| import sql from "../../services/sql.js"; | import sql from "../../services/sql.js"; | ||||||
| import BAttachment from "./battachment.js"; | import BAttachment from "./battachment.js"; | ||||||
| import type { AttachmentRow, RevisionRow } from "./rows.js"; | import type { AttachmentRow, NoteType, RevisionRow } from "./rows.js"; | ||||||
| import eraseService from "../../services/erase.js"; | import eraseService from "../../services/erase.js"; | ||||||
|  |  | ||||||
| interface ContentOpts { | interface ContentOpts { | ||||||
| @@ -36,7 +36,7 @@ class BRevision extends AbstractBeccaEntity<BRevision> { | |||||||
|  |  | ||||||
|     revisionId?: string; |     revisionId?: string; | ||||||
|     noteId!: string; |     noteId!: string; | ||||||
|     type!: string; |     type!: NoteType; | ||||||
|     mime!: string; |     mime!: string; | ||||||
|     title!: string; |     title!: string; | ||||||
|     dateLastEdited?: string; |     dateLastEdited?: string; | ||||||
|   | |||||||
| @@ -1,84 +0,0 @@ | |||||||
| import date_utils from "../../services/date_utils.js"; |  | ||||||
| import AbstractBeccaEntity from "./abstract_becca_entity.js"; |  | ||||||
| import type BOption from "./boption.js"; |  | ||||||
| import type { TaskRow } from "./rows.js"; |  | ||||||
|  |  | ||||||
| export default class BTask extends AbstractBeccaEntity<BOption> { |  | ||||||
|  |  | ||||||
|     static get entityName() { |  | ||||||
|         return "tasks"; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get primaryKeyName() { |  | ||||||
|         return "taskId"; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static get hashedProperties() { |  | ||||||
|         return [ "taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted" ]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     taskId?: string; |  | ||||||
|     parentNoteId!: string; |  | ||||||
|     title!: string; |  | ||||||
|     dueDate?: string; |  | ||||||
|     isDone!: boolean; |  | ||||||
|     private _isDeleted?: boolean; |  | ||||||
|  |  | ||||||
|     constructor(row?: TaskRow) { |  | ||||||
|         super(); |  | ||||||
|  |  | ||||||
|         if (!row) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         this.updateFromRow(row); |  | ||||||
|         this.init(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     get isDeleted() { |  | ||||||
|         return !!this._isDeleted; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     updateFromRow(row: TaskRow) { |  | ||||||
|         this.taskId = row.taskId; |  | ||||||
|         this.parentNoteId = row.parentNoteId; |  | ||||||
|         this.title = row.title; |  | ||||||
|         this.dueDate = row.dueDate; |  | ||||||
|         this.isDone = !!row.isDone; |  | ||||||
|         this._isDeleted = !!row.isDeleted; |  | ||||||
|         this.utcDateModified = row.utcDateModified; |  | ||||||
|  |  | ||||||
|         if (this.taskId) { |  | ||||||
|             this.becca.tasks[this.taskId] = this; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     init() { |  | ||||||
|         if (this.taskId) { |  | ||||||
|             this.becca.tasks[this.taskId] = this; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected beforeSaving(opts?: {}): void { |  | ||||||
|         super.beforeSaving(); |  | ||||||
|  |  | ||||||
|         this.utcDateModified = date_utils.utcNowDateTime(); |  | ||||||
|  |  | ||||||
|         if (this.taskId) { |  | ||||||
|             this.becca.tasks[this.taskId] = this; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     getPojo() { |  | ||||||
|         return { |  | ||||||
|             taskId: this.taskId, |  | ||||||
|             parentNoteId: this.parentNoteId, |  | ||||||
|             title: this.title, |  | ||||||
|             dueDate: this.dueDate, |  | ||||||
|             isDone: this.isDone, |  | ||||||
|             isDeleted: this.isDeleted, |  | ||||||
|             utcDateModified: this.utcDateModified |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -22,7 +22,7 @@ export interface AttachmentRow { | |||||||
| export interface RevisionRow { | export interface RevisionRow { | ||||||
|     revisionId?: string; |     revisionId?: string; | ||||||
|     noteId: string; |     noteId: string; | ||||||
|     type: string; |     type: NoteType; | ||||||
|     mime: string; |     mime: string; | ||||||
|     isProtected?: boolean; |     isProtected?: boolean; | ||||||
|     title: string; |     title: string; | ||||||
| @@ -139,13 +139,3 @@ export interface NoteRow { | |||||||
|     utcDateModified: string; |     utcDateModified: string; | ||||||
|     content?: string | Buffer; |     content?: string | Buffer; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface TaskRow { |  | ||||||
|     taskId?: string; |  | ||||||
|     parentNoteId: string; |  | ||||||
|     title: string; |  | ||||||
|     dueDate?: string; |  | ||||||
|     isDone?: boolean; |  | ||||||
|     isDeleted?: boolean; |  | ||||||
|     utcDateModified?: string; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import BNote from "./entities/bnote.js"; | |||||||
| import BOption from "./entities/boption.js"; | import BOption from "./entities/boption.js"; | ||||||
| import BRecentNote from "./entities/brecent_note.js"; | import BRecentNote from "./entities/brecent_note.js"; | ||||||
| import BRevision from "./entities/brevision.js"; | import BRevision from "./entities/brevision.js"; | ||||||
| import BTask from "./entities/btask.js"; |  | ||||||
|  |  | ||||||
| type EntityClass = new (row?: any) => AbstractBeccaEntity<any>; | type EntityClass = new (row?: any) => AbstractBeccaEntity<any>; | ||||||
|  |  | ||||||
| @@ -22,8 +21,7 @@ const ENTITY_NAME_TO_ENTITY: Record<string, ConstructorData<any> & EntityClass> | |||||||
|     notes: BNote, |     notes: BNote, | ||||||
|     options: BOption, |     options: BOption, | ||||||
|     recent_notes: BRecentNote, |     recent_notes: BRecentNote, | ||||||
|     revisions: BRevision, |     revisions: BRevision | ||||||
|     tasks: BTask |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| function getEntityFromEntityName(entityName: keyof typeof ENTITY_NAME_TO_ENTITY) { | function getEntityFromEntityName(entityName: keyof typeof ENTITY_NAME_TO_ENTITY) { | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								src/errors/forbidden_error.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | |||||||
|  | import HttpError from "./http_error.js"; | ||||||
|  |  | ||||||
|  | class ForbiddenError extends HttpError { | ||||||
|  |  | ||||||
|  |     constructor(message: string) { | ||||||
|  |         super(message, 403); | ||||||
|  |         this.name = "ForbiddenError"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default ForbiddenError; | ||||||
							
								
								
									
										13
									
								
								src/errors/http_error.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | |||||||
|  | class HttpError extends Error { | ||||||
|  |  | ||||||
|  |     statusCode: number; | ||||||
|  |  | ||||||
|  |     constructor(message: string, statusCode: number) { | ||||||
|  |         super(message); | ||||||
|  |         this.name = "HttpError"; | ||||||
|  |         this.statusCode = statusCode; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default HttpError; | ||||||
| @@ -1,9 +1,12 @@ | |||||||
| class NotFoundError { | import HttpError from "./http_error.js"; | ||||||
|     message: string; |  | ||||||
|  | class NotFoundError extends HttpError { | ||||||
|  |  | ||||||
|     constructor(message: string) { |     constructor(message: string) { | ||||||
|         this.message = message; |         super(message, 404); | ||||||
|  |         this.name = "NotFoundError"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export default NotFoundError; | export default NotFoundError; | ||||||
|   | |||||||
| @@ -1,9 +1,12 @@ | |||||||
| class ValidationError { | import HttpError from "./http_error.js"; | ||||||
|     message: string; |  | ||||||
|  | class ValidationError extends HttpError { | ||||||
|  |  | ||||||
|     constructor(message: string) { |     constructor(message: string) { | ||||||
|         this.message = message; |         super(message, 400) | ||||||
|  |         this.name = "ValidationError"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export default ValidationError; | export default ValidationError; | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ import type NoteTreeWidget from "../widgets/note_tree.js"; | |||||||
| import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js"; | import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js"; | ||||||
| import type { ContextMenuEvent } from "../menus/context_menu.js"; | import type { ContextMenuEvent } from "../menus/context_menu.js"; | ||||||
| import type TypeWidget from "../widgets/type_widgets/type_widget.js"; | import type TypeWidget from "../widgets/type_widgets/type_widget.js"; | ||||||
|  | import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js"; | ||||||
|  |  | ||||||
| interface Layout { | interface Layout { | ||||||
|     getRootWidget: (appContext: AppContext) => RootWidget; |     getRootWidget: (appContext: AppContext) => RootWidget; | ||||||
| @@ -62,7 +63,7 @@ export interface NoteCommandData extends CommandData { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface ExecuteCommandData<T> extends CommandData { | export interface ExecuteCommandData<T> extends CommandData { | ||||||
|     resolve: (data: T) => void |     resolve: (data: T) => void; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -70,7 +71,7 @@ export interface ExecuteCommandData<T> extends CommandData { | |||||||
|  */ |  */ | ||||||
| export type CommandMappings = { | export type CommandMappings = { | ||||||
|     "api-log-messages": CommandData; |     "api-log-messages": CommandData; | ||||||
|     focusTree: CommandData, |     focusTree: CommandData; | ||||||
|     focusOnTitle: CommandData; |     focusOnTitle: CommandData; | ||||||
|     focusOnDetail: CommandData; |     focusOnDetail: CommandData; | ||||||
|     focusOnSearchDefinition: Required<CommandData>; |     focusOnSearchDefinition: Required<CommandData>; | ||||||
| @@ -108,7 +109,7 @@ export type CommandMappings = { | |||||||
|     showInfoDialog: ConfirmWithMessageOptions; |     showInfoDialog: ConfirmWithMessageOptions; | ||||||
|     showConfirmDialog: ConfirmWithMessageOptions; |     showConfirmDialog: ConfirmWithMessageOptions; | ||||||
|     showRecentChanges: CommandData & { ancestorNoteId: string }; |     showRecentChanges: CommandData & { ancestorNoteId: string }; | ||||||
|     showImportDialog: CommandData & { noteId: string; }; |     showImportDialog: CommandData & { noteId: string }; | ||||||
|     openNewNoteSplit: NoteCommandData; |     openNewNoteSplit: NoteCommandData; | ||||||
|     openInWindow: NoteCommandData; |     openInWindow: NoteCommandData; | ||||||
|     openNoteInNewTab: CommandData; |     openNoteInNewTab: CommandData; | ||||||
| @@ -131,8 +132,10 @@ export type CommandMappings = { | |||||||
|     editNoteTitle: ContextMenuCommandData; |     editNoteTitle: ContextMenuCommandData; | ||||||
|     protectSubtree: ContextMenuCommandData; |     protectSubtree: ContextMenuCommandData; | ||||||
|     unprotectSubtree: ContextMenuCommandData; |     unprotectSubtree: ContextMenuCommandData; | ||||||
|     openBulkActionsDialog: ContextMenuCommandData | { |     openBulkActionsDialog: | ||||||
|         selectedOrActiveNoteIds?: string[] |     | ContextMenuCommandData | ||||||
|  |     | { | ||||||
|  |         selectedOrActiveNoteIds?: string[]; | ||||||
|     }; |     }; | ||||||
|     editBranchPrefix: ContextMenuCommandData; |     editBranchPrefix: ContextMenuCommandData; | ||||||
|     convertNoteToAttachment: ContextMenuCommandData; |     convertNoteToAttachment: ContextMenuCommandData; | ||||||
| @@ -221,11 +224,11 @@ export type CommandMappings = { | |||||||
|     moveTabToNewWindow: CommandData; |     moveTabToNewWindow: CommandData; | ||||||
|     copyTabToNewWindow: CommandData; |     copyTabToNewWindow: CommandData; | ||||||
|     closeActiveTab: CommandData & { |     closeActiveTab: CommandData & { | ||||||
|         $el: JQuery<HTMLElement> |         $el: JQuery<HTMLElement>; | ||||||
|     }, |     }; | ||||||
|     setZoomFactorAndSave: { |     setZoomFactorAndSave: { | ||||||
|         zoomFactor: string; |         zoomFactor: string; | ||||||
|     } |     }; | ||||||
|  |  | ||||||
|     reEvaluateRightPaneVisibility: CommandData; |     reEvaluateRightPaneVisibility: CommandData; | ||||||
|     runActiveNote: CommandData; |     runActiveNote: CommandData; | ||||||
| @@ -234,18 +237,18 @@ export type CommandMappings = { | |||||||
|     }; |     }; | ||||||
|     scrollToEnd: CommandData; |     scrollToEnd: CommandData; | ||||||
|     closeThisNoteSplit: CommandData; |     closeThisNoteSplit: CommandData; | ||||||
|     moveThisNoteSplit: CommandData & { isMovingLeft: boolean; }; |     moveThisNoteSplit: CommandData & { isMovingLeft: boolean }; | ||||||
|  |  | ||||||
|     // Geomap |     // Geomap | ||||||
|     deleteFromMap: { noteId: string }, |     deleteFromMap: { noteId: string }; | ||||||
|     openGeoLocation: { noteId: string, event: JQuery.MouseDownEvent } |     openGeoLocation: { noteId: string; event: JQuery.MouseDownEvent }; | ||||||
|  |  | ||||||
|     toggleZenMode: CommandData; |     toggleZenMode: CommandData; | ||||||
|  |  | ||||||
|     updateAttributeList: CommandData & { attributes: Attribute[] }; |     updateAttributeList: CommandData & { attributes: Attribute[] }; | ||||||
|     saveAttributes: CommandData; |     saveAttributes: CommandData; | ||||||
|     reloadAttributes: CommandData; |     reloadAttributes: CommandData; | ||||||
|     refreshNoteList: CommandData & { noteId: string; }; |     refreshNoteList: CommandData & { noteId: string }; | ||||||
|  |  | ||||||
|     refreshResults: {}; |     refreshResults: {}; | ||||||
|     refreshSearchDefinition: {}; |     refreshSearchDefinition: {}; | ||||||
| @@ -348,7 +351,7 @@ type EventMappings = { | |||||||
|         ntxId: string | null | undefined; // TODO: deduplicate ntxId |         ntxId: string | null | undefined; // TODO: deduplicate ntxId | ||||||
|     }; |     }; | ||||||
|     tabReorder: { |     tabReorder: { | ||||||
|         ntxIdsInOrder: string[] |         ntxIdsInOrder: string[]; | ||||||
|     }; |     }; | ||||||
|     refreshNoteList: { |     refreshNoteList: { | ||||||
|         noteId: string; |         noteId: string; | ||||||
| @@ -359,6 +362,12 @@ type EventMappings = { | |||||||
|     relationMapResetPanZoom: { ntxId: string | null | undefined }; |     relationMapResetPanZoom: { ntxId: string | null | undefined }; | ||||||
|     relationMapResetZoomIn: { ntxId: string | null | undefined }; |     relationMapResetZoomIn: { ntxId: string | null | undefined }; | ||||||
|     relationMapResetZoomOut: { ntxId: string | null | undefined }; |     relationMapResetZoomOut: { ntxId: string | null | undefined }; | ||||||
|  |     activeNoteChangedEvent: {}; | ||||||
|  |     showAddLinkDialog: { | ||||||
|  |         textTypeWidget: EditableTextTypeWidget; | ||||||
|  |         text: string; | ||||||
|  |     }; | ||||||
|  |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export type EventListener<T extends EventNames> = { | export type EventListener<T extends EventNames> = { | ||||||
| @@ -542,10 +551,12 @@ $(window).on("beforeunload", () => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| $(window).on("hashchange", function () { | $(window).on("hashchange", function () { | ||||||
|     const { notePath, ntxId, viewScope } = linkService.parseNavigationStateFromUrl(window.location.href); |     const { notePath, ntxId, viewScope, searchString } = linkService.parseNavigationStateFromUrl(window.location.href); | ||||||
|  |  | ||||||
|     if (notePath || ntxId) { |     if (notePath || ntxId) { | ||||||
|         appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope); |         appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope); | ||||||
|  |     } else if (searchString) { | ||||||
|  |         appContext.triggerCommand("searchNotes", { searchString }); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> { | |||||||
|     children: ChildT[]; |     children: ChildT[]; | ||||||
|     initialized: Promise<void> | null; |     initialized: Promise<void> | null; | ||||||
|     parent?: TypedComponent<any>; |     parent?: TypedComponent<any>; | ||||||
|     position!: number; |     _position!: number; | ||||||
|  |  | ||||||
|     constructor() { |     constructor() { | ||||||
|         this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`; |         this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`; | ||||||
| @@ -31,6 +31,14 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> { | |||||||
|         return this.constructor.name.replace(/[^A-Z0-9]/gi, "_"); |         return this.constructor.name.replace(/[^A-Z0-9]/gi, "_"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     get position() { | ||||||
|  |         return this._position; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     set position(newPosition: number) { | ||||||
|  |         this._position = newPosition; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     setParent(parent: TypedComponent<any>) { |     setParent(parent: TypedComponent<any>) { | ||||||
|         this.parent = parent; |         this.parent = parent; | ||||||
|         return this; |         return this; | ||||||
|   | |||||||
| @@ -369,7 +369,8 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded"> | |||||||
|  |  | ||||||
|         const { note, viewScope } = this; |         const { note, viewScope } = this; | ||||||
|  |  | ||||||
|         let title = viewScope?.viewMode === "default" ? note.title : `${note.title}: ${viewScope?.viewMode}`; |         const isNormalView = (viewScope?.viewMode === "default" || viewScope?.viewMode === "contextual-help"); | ||||||
|  |         let title = (isNormalView ? note.title : `${note.title}: ${viewScope?.viewMode}`); | ||||||
|  |  | ||||||
|         if (viewScope?.attachmentId) { |         if (viewScope?.attachmentId) { | ||||||
|             // assuming the attachment has been already loaded |             // assuming the attachment has been already loaded | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ export default class ShortcutComponent extends Component implements EventListene | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     bindNoteShortcutHandler(labelOrRow: AttributeRow) { |     bindNoteShortcutHandler(labelOrRow: AttributeRow) { | ||||||
|         const handler = () => appContext.tabManager.getActiveContext().setNote(labelOrRow.noteId); |         const handler = () => appContext.tabManager.getActiveContext()?.setNote(labelOrRow.noteId); | ||||||
|         const namespace = labelOrRow.attributeId; |         const namespace = labelOrRow.attributeId; | ||||||
|  |  | ||||||
|         if (labelOrRow.isDeleted) { |         if (labelOrRow.isDeleted) { | ||||||
|   | |||||||
| @@ -248,7 +248,7 @@ export default class TabManager extends Component { | |||||||
|         await noteContext.setEmpty(); |         await noteContext.setEmpty(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId = null) { |     async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId) { | ||||||
|         const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId); |         const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId); | ||||||
|  |  | ||||||
|         const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId); |         const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
| 	"formatVersion": 2, | 	"formatVersion": 2, | ||||||
| 	"appVersion": "0.92.0-beta", | 	"appVersion": "0.92.2-beta", | ||||||
| 	"files": [ | 	"files": [ | ||||||
| 		{ | 		{ | ||||||
| 			"isClone": false, | 			"isClone": false, | ||||||
| @@ -34,7 +34,7 @@ | |||||||
| 						"OkOZllzB3fqN", | 						"OkOZllzB3fqN", | ||||||
| 						"yoAe4jV2yzbd" | 						"yoAe4jV2yzbd" | ||||||
| 					], | 					], | ||||||
| 					"title": "Features", | 					"title": "New Features", | ||||||
| 					"notePosition": 40, | 					"notePosition": 40, | ||||||
| 					"prefix": null, | 					"prefix": null, | ||||||
| 					"isExpanded": false, | 					"isExpanded": false, | ||||||
| @@ -47,53 +47,91 @@ | |||||||
| 							"value": "bx bx-star", | 							"value": "bx bx-star", | ||||||
| 							"isInheritable": false, | 							"isInheritable": false, | ||||||
| 							"position": 10 | 							"position": 10 | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							"type": "label", | ||||||
|  | 							"name": "sorted", | ||||||
|  | 							"value": "dateCreated", | ||||||
|  | 							"isInheritable": false, | ||||||
|  | 							"position": 20 | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							"type": "label", | ||||||
|  | 							"name": "sortDirection", | ||||||
|  | 							"value": "desc", | ||||||
|  | 							"isInheritable": false, | ||||||
|  | 							"position": 30 | ||||||
| 						} | 						} | ||||||
| 					], | 					], | ||||||
| 					"format": "html", | 					"format": "html", | ||||||
| 					"attachments": [], | 					"attachments": [], | ||||||
| 					"dirFileName": "Features", | 					"dirFileName": "New Features", | ||||||
| 					"children": [ | 					"children": [ | ||||||
| 						{ | 						{ | ||||||
| 							"isClone": false, | 							"isClone": false, | ||||||
| 							"noteId": "13D1lOc9sqmZ", | 							"noteId": "3I277VKYxWDH", | ||||||
| 							"notePath": [ | 							"notePath": [ | ||||||
| 								"OkOZllzB3fqN", | 								"OkOZllzB3fqN", | ||||||
| 								"yoAe4jV2yzbd", | 								"yoAe4jV2yzbd", | ||||||
| 								"13D1lOc9sqmZ" | 								"3I277VKYxWDH" | ||||||
| 							], | 							], | ||||||
| 							"title": "Export as PDF", | 							"title": "Right-to-left text notes", | ||||||
| 							"notePosition": 20, | 							"notePosition": 10, | ||||||
| 							"prefix": null, | 							"prefix": null, | ||||||
| 							"isExpanded": false, | 							"isExpanded": false, | ||||||
| 							"type": "text", | 							"type": "text", | ||||||
| 							"mime": "text/html", | 							"mime": "text/html", | ||||||
| 							"attributes": [], | 							"attributes": [ | ||||||
|  | 								{ | ||||||
|  | 									"type": "label", | ||||||
|  | 									"name": "iconClass", | ||||||
|  | 									"value": "bx bx-align-right", | ||||||
|  | 									"isInheritable": false, | ||||||
|  | 									"position": 10 | ||||||
|  | 								} | ||||||
|  | 							], | ||||||
| 							"format": "html", | 							"format": "html", | ||||||
| 							"dataFileName": "Export as PDF.html", | 							"dataFileName": "Right-to-left text notes.html", | ||||||
| 							"attachments": [ | 							"attachments": [ | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "xsGM34t8ssKV", | 									"attachmentId": "PSBNAvDyj5Vy", | ||||||
| 									"title": "image.png", | 									"title": "image.png", | ||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "Export as PDF_image.png" | 									"dataFileName": "Right-to-left text notes_i.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "cvyes4f1Vhmm", | 									"attachmentId": "YXYIJznak915", | ||||||
| 									"title": "image.png", | 									"title": "image.png", | ||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "1_Export as PDF_image.png" | 									"dataFileName": "1_Right-to-left text notes_i.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "b3v1pLE6TF1Y", | 									"attachmentId": "Do0S17lDl7uu", | ||||||
| 									"title": "image.png", | 									"title": "image.png", | ||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "2_Export as PDF_image.png" | 									"dataFileName": "2_Right-to-left text notes_i.png" | ||||||
|  | 								}, | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "D3lyhPvPvocb", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/png", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "3_Right-to-left text notes_i.png" | ||||||
|  | 								}, | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "Tu7llk3GgRkA", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/png", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "4_Right-to-left text notes_i.png" | ||||||
| 								} | 								} | ||||||
| 							] | 							] | ||||||
| 						}, | 						}, | ||||||
| @@ -106,12 +144,20 @@ | |||||||
| 								"B3YLYM4erjnW" | 								"B3YLYM4erjnW" | ||||||
| 							], | 							], | ||||||
| 							"title": "Zen mode", | 							"title": "Zen mode", | ||||||
| 							"notePosition": 30, | 							"notePosition": 20, | ||||||
| 							"prefix": null, | 							"prefix": null, | ||||||
| 							"isExpanded": false, | 							"isExpanded": false, | ||||||
| 							"type": "text", | 							"type": "text", | ||||||
| 							"mime": "text/html", | 							"mime": "text/html", | ||||||
| 							"attributes": [], | 							"attributes": [ | ||||||
|  | 								{ | ||||||
|  | 									"type": "label", | ||||||
|  | 									"name": "iconClass", | ||||||
|  | 									"value": "bx bxs-yin-yang", | ||||||
|  | 									"isInheritable": false, | ||||||
|  | 									"position": 10 | ||||||
|  | 								} | ||||||
|  | 							], | ||||||
| 							"format": "html", | 							"format": "html", | ||||||
| 							"dataFileName": "Zen mode.html", | 							"dataFileName": "Zen mode.html", | ||||||
| 							"attachments": [ | 							"attachments": [ | ||||||
| @@ -180,6 +226,50 @@ | |||||||
| 									"dataFileName": "7_Zen mode_image.png" | 									"dataFileName": "7_Zen mode_image.png" | ||||||
| 								} | 								} | ||||||
| 							] | 							] | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							"isClone": false, | ||||||
|  | 							"noteId": "13D1lOc9sqmZ", | ||||||
|  | 							"notePath": [ | ||||||
|  | 								"OkOZllzB3fqN", | ||||||
|  | 								"yoAe4jV2yzbd", | ||||||
|  | 								"13D1lOc9sqmZ" | ||||||
|  | 							], | ||||||
|  | 							"title": "Export as PDF", | ||||||
|  | 							"notePosition": 30, | ||||||
|  | 							"prefix": null, | ||||||
|  | 							"isExpanded": false, | ||||||
|  | 							"type": "text", | ||||||
|  | 							"mime": "text/html", | ||||||
|  | 							"attributes": [ | ||||||
|  | 								{ | ||||||
|  | 									"type": "label", | ||||||
|  | 									"name": "iconClass", | ||||||
|  | 									"value": "bx bxs-file-pdf", | ||||||
|  | 									"isInheritable": false, | ||||||
|  | 									"position": 30 | ||||||
|  | 								} | ||||||
|  | 							], | ||||||
|  | 							"format": "html", | ||||||
|  | 							"dataFileName": "Export as PDF.html", | ||||||
|  | 							"attachments": [ | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "xsGM34t8ssKV", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/png", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "Export as PDF_image.png" | ||||||
|  | 								}, | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "b3v1pLE6TF1Y", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/png", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "1_Export as PDF_image.png" | ||||||
|  | 								} | ||||||
|  | 							] | ||||||
| 						} | 						} | ||||||
| 					] | 					] | ||||||
| 				}, | 				}, | ||||||
| @@ -233,8 +323,47 @@ | |||||||
| 								} | 								} | ||||||
| 							], | 							], | ||||||
| 							"format": "html", | 							"format": "html", | ||||||
| 							"dataFileName": "Text.html", | 							"attachments": [], | ||||||
| 							"attachments": [] | 							"dirFileName": "Text", | ||||||
|  | 							"children": [ | ||||||
|  | 								{ | ||||||
|  | 									"isClone": false, | ||||||
|  | 									"noteId": "B0lcI9xz1r8K", | ||||||
|  | 									"notePath": [ | ||||||
|  | 										"OkOZllzB3fqN", | ||||||
|  | 										"wmegHv51MJMd", | ||||||
|  | 										"crJtzsol4olb", | ||||||
|  | 										"B0lcI9xz1r8K" | ||||||
|  | 									], | ||||||
|  | 									"title": "Content language", | ||||||
|  | 									"notePosition": 10, | ||||||
|  | 									"prefix": null, | ||||||
|  | 									"isExpanded": false, | ||||||
|  | 									"type": "text", | ||||||
|  | 									"mime": "text/html", | ||||||
|  | 									"attributes": [ | ||||||
|  | 										{ | ||||||
|  | 											"type": "relation", | ||||||
|  | 											"name": "internalLink", | ||||||
|  | 											"value": "3I277VKYxWDH", | ||||||
|  | 											"isInheritable": false, | ||||||
|  | 											"position": 10 | ||||||
|  | 										} | ||||||
|  | 									], | ||||||
|  | 									"format": "html", | ||||||
|  | 									"dataFileName": "Content language.html", | ||||||
|  | 									"attachments": [ | ||||||
|  | 										{ | ||||||
|  | 											"attachmentId": "OpIv6CnYCLVa", | ||||||
|  | 											"title": "image.png", | ||||||
|  | 											"role": "image", | ||||||
|  | 											"mime": "image/png", | ||||||
|  | 											"position": 10, | ||||||
|  | 											"dataFileName": "Content language_image.png" | ||||||
|  | 										} | ||||||
|  | 									] | ||||||
|  | 								} | ||||||
|  | 							] | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							"isClone": false, | 							"isClone": false, | ||||||
| @@ -382,7 +511,7 @@ | |||||||
| 							"title": "Book", | 							"title": "Book", | ||||||
| 							"notePosition": 70, | 							"notePosition": 70, | ||||||
| 							"prefix": null, | 							"prefix": null, | ||||||
| 							"isExpanded": true, | 							"isExpanded": false, | ||||||
| 							"type": "text", | 							"type": "text", | ||||||
| 							"mime": "text/html", | 							"mime": "text/html", | ||||||
| 							"attributes": [ | 							"attributes": [ | ||||||
| @@ -576,6 +705,14 @@ | |||||||
| 											"mime": "image/png", | 											"mime": "image/png", | ||||||
| 											"position": 10, | 											"position": 10, | ||||||
| 											"dataFileName": "18_Calendar View_image.png" | 											"dataFileName": "18_Calendar View_image.png" | ||||||
|  | 										}, | ||||||
|  | 										{ | ||||||
|  | 											"attachmentId": "JM6AU8N4MIgB", | ||||||
|  | 											"title": "image.png", | ||||||
|  | 											"role": "image", | ||||||
|  | 											"mime": "image/png", | ||||||
|  | 											"position": 10, | ||||||
|  | 											"dataFileName": "19_Calendar View_image.png" | ||||||
| 										} | 										} | ||||||
| 									] | 									] | ||||||
| 								} | 								} | ||||||
| @@ -697,7 +834,7 @@ | |||||||
| 								"wmegHv51MJMd", | 								"wmegHv51MJMd", | ||||||
| 								"foPEtsL51pD2" | 								"foPEtsL51pD2" | ||||||
| 							], | 							], | ||||||
| 							"title": "Geo Map", | 							"title": "Geo map", | ||||||
| 							"notePosition": 120, | 							"notePosition": 120, | ||||||
| 							"prefix": null, | 							"prefix": null, | ||||||
| 							"isExpanded": false, | 							"isExpanded": false, | ||||||
| @@ -713,23 +850,15 @@ | |||||||
| 								} | 								} | ||||||
| 							], | 							], | ||||||
| 							"format": "html", | 							"format": "html", | ||||||
| 							"dataFileName": "Geo Map.html", | 							"dataFileName": "Geo map.html", | ||||||
| 							"attachments": [ | 							"attachments": [ | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "J0baLTpafs7C", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "Geo Map_image.png" |  | ||||||
| 								}, |  | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "kcYjOvJDFkbS", | 									"attachmentId": "kcYjOvJDFkbS", | ||||||
| 									"title": "image.png", | 									"title": "image.png", | ||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "1_Geo Map_image.png" | 									"dataFileName": "Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "FDP3JzIVSnuJ", | 									"attachmentId": "FDP3JzIVSnuJ", | ||||||
| @@ -737,7 +866,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "2_Geo Map_image.png" | 									"dataFileName": "1_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "eUrcqc8RRuZG", | 									"attachmentId": "eUrcqc8RRuZG", | ||||||
| @@ -745,7 +874,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "3_Geo Map_image.png" | 									"dataFileName": "2_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "1quk4yxJpeHZ", | 									"attachmentId": "1quk4yxJpeHZ", | ||||||
| @@ -753,7 +882,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "4_Geo Map_image.png" | 									"dataFileName": "3_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "iSpyhQ5Ya6Nk", | 									"attachmentId": "iSpyhQ5Ya6Nk", | ||||||
| @@ -761,7 +890,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "5_Geo Map_image.png" | 									"dataFileName": "4_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "ut6vm2aXVfXI", | 									"attachmentId": "ut6vm2aXVfXI", | ||||||
| @@ -769,7 +898,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "6_Geo Map_image.png" | 									"dataFileName": "5_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "uYdb9wWf5Nuv", | 									"attachmentId": "uYdb9wWf5Nuv", | ||||||
| @@ -777,15 +906,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "7_Geo Map_image.png" | 									"dataFileName": "6_Geo map_image.png" | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "GhHYO2LteDmZ", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "8_Geo Map_image.png" |  | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "viN50n5G4kB0", | 									"attachmentId": "viN50n5G4kB0", | ||||||
| @@ -793,7 +914,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "9_Geo Map_image.png" | 									"dataFileName": "7_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "mgwGrtQZjxxb", | 									"attachmentId": "mgwGrtQZjxxb", | ||||||
| @@ -801,7 +922,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "10_Geo Map_image.png" | 									"dataFileName": "8_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "PMqmCbNLlZOG", | 									"attachmentId": "PMqmCbNLlZOG", | ||||||
| @@ -809,7 +930,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "11_Geo Map_image.png" | 									"dataFileName": "9_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "0AwaQMqt3FVA", | 									"attachmentId": "0AwaQMqt3FVA", | ||||||
| @@ -817,7 +938,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "12_Geo Map_image.png" | 									"dataFileName": "10_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "gR2c2Thmfy3I", | 									"attachmentId": "gR2c2Thmfy3I", | ||||||
| @@ -825,7 +946,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "13_Geo Map_image.png" | 									"dataFileName": "11_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "JULizn130rVI", | 									"attachmentId": "JULizn130rVI", | ||||||
| @@ -833,7 +954,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "14_Geo Map_image.png" | 									"dataFileName": "12_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "MdC0DpifJwu4", | 									"attachmentId": "MdC0DpifJwu4", | ||||||
| @@ -841,7 +962,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "15_Geo Map_image.png" | 									"dataFileName": "13_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "gFR2Izzp18LQ", | 									"attachmentId": "gFR2Izzp18LQ", | ||||||
| @@ -849,7 +970,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "16_Geo Map_image.png" | 									"dataFileName": "14_Geo map_image.png" | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "42AncDs7SSAf", | 									"attachmentId": "42AncDs7SSAf", | ||||||
| @@ -857,15 +978,7 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "17_Geo Map_image.png" | 									"dataFileName": "15_Geo map_image.png" | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "pKdtiq4r0eFY", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "18_Geo Map_image.png" |  | ||||||
| 								}, | 								}, | ||||||
| 								{ | 								{ | ||||||
| 									"attachmentId": "FXRVvYpOxWyR", | 									"attachmentId": "FXRVvYpOxWyR", | ||||||
| @@ -873,7 +986,23 @@ | |||||||
| 									"role": "image", | 									"role": "image", | ||||||
| 									"mime": "image/png", | 									"mime": "image/png", | ||||||
| 									"position": 10, | 									"position": 10, | ||||||
| 									"dataFileName": "19_Geo Map_image.png" | 									"dataFileName": "16_Geo map_image.png" | ||||||
|  | 								}, | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "qudP7UCtwIq3", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/jpg", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "17_Geo map_image.png" | ||||||
|  | 								}, | ||||||
|  | 								{ | ||||||
|  | 									"attachmentId": "utecGxWk08QY", | ||||||
|  | 									"title": "image.png", | ||||||
|  | 									"role": "image", | ||||||
|  | 									"mime": "image/png", | ||||||
|  | 									"position": 10, | ||||||
|  | 									"dataFileName": "18_Geo map_image.png" | ||||||
| 								} | 								} | ||||||
| 							] | 							] | ||||||
| 						} | 						} | ||||||
| @@ -943,173 +1072,6 @@ | |||||||
| 						} | 						} | ||||||
| 					] | 					] | ||||||
| 				}, | 				}, | ||||||
| 				{ |  | ||||||
| 					"isClone": false, |  | ||||||
| 					"noteId": "DtJJ20yEozPA", |  | ||||||
| 					"notePath": [ |  | ||||||
| 						"OkOZllzB3fqN", |  | ||||||
| 						"DtJJ20yEozPA" |  | ||||||
| 					], |  | ||||||
| 					"title": "Theme development", |  | ||||||
| 					"notePosition": 130, |  | ||||||
| 					"prefix": null, |  | ||||||
| 					"isExpanded": false, |  | ||||||
| 					"type": "text", |  | ||||||
| 					"mime": "text/html", |  | ||||||
| 					"attributes": [ |  | ||||||
| 						{ |  | ||||||
| 							"type": "label", |  | ||||||
| 							"name": "iconClass", |  | ||||||
| 							"value": "bx bx-palette", |  | ||||||
| 							"isInheritable": false, |  | ||||||
| 							"position": 10 |  | ||||||
| 						} |  | ||||||
| 					], |  | ||||||
| 					"format": "html", |  | ||||||
| 					"attachments": [], |  | ||||||
| 					"dirFileName": "Theme development", |  | ||||||
| 					"children": [ |  | ||||||
| 						{ |  | ||||||
| 							"isClone": false, |  | ||||||
| 							"noteId": "5HH79ztN0fZA", |  | ||||||
| 							"notePath": [ |  | ||||||
| 								"OkOZllzB3fqN", |  | ||||||
| 								"DtJJ20yEozPA", |  | ||||||
| 								"5HH79ztN0fZA" |  | ||||||
| 							], |  | ||||||
| 							"title": "Creating a custom theme", |  | ||||||
| 							"notePosition": 10, |  | ||||||
| 							"prefix": null, |  | ||||||
| 							"isExpanded": false, |  | ||||||
| 							"type": "text", |  | ||||||
| 							"mime": "text/html", |  | ||||||
| 							"attributes": [ |  | ||||||
| 								{ |  | ||||||
| 									"type": "relation", |  | ||||||
| 									"name": "internalLink", |  | ||||||
| 									"value": "aH8Dk5aMiq7R", |  | ||||||
| 									"isInheritable": false, |  | ||||||
| 									"position": 10 |  | ||||||
| 								} |  | ||||||
| 							], |  | ||||||
| 							"format": "html", |  | ||||||
| 							"dataFileName": "Creating a custom theme.html", |  | ||||||
| 							"attachments": [ |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "AJHVfQtIQgJ7", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "Creating a custom theme_im.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "gXLyv5KXjfxg", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "1_Creating a custom theme_im.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "on1gD7BzCWdN", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "2_Creating a custom theme_im.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "17p6z24yW5eP", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "3_Creating a custom theme_im.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "K3cdwj8f90m0", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "4_Creating a custom theme_im.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "bn93hwF7C8sR", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "5_Creating a custom theme_im.png" |  | ||||||
| 								} |  | ||||||
| 							] |  | ||||||
| 						}, |  | ||||||
| 						{ |  | ||||||
| 							"isClone": false, |  | ||||||
| 							"noteId": "aH8Dk5aMiq7R", |  | ||||||
| 							"notePath": [ |  | ||||||
| 								"OkOZllzB3fqN", |  | ||||||
| 								"DtJJ20yEozPA", |  | ||||||
| 								"aH8Dk5aMiq7R" |  | ||||||
| 							], |  | ||||||
| 							"title": "Customize the Next theme", |  | ||||||
| 							"notePosition": 20, |  | ||||||
| 							"prefix": null, |  | ||||||
| 							"isExpanded": false, |  | ||||||
| 							"type": "text", |  | ||||||
| 							"mime": "text/html", |  | ||||||
| 							"attributes": [], |  | ||||||
| 							"format": "html", |  | ||||||
| 							"dataFileName": "Customize the Next theme.html", |  | ||||||
| 							"attachments": [ |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "5z4bC0x0eH0P", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "Customize the Next theme_i.png" |  | ||||||
| 								}, |  | ||||||
| 								{ |  | ||||||
| 									"attachmentId": "u0zkXkD7rGXA", |  | ||||||
| 									"title": "image.png", |  | ||||||
| 									"role": "image", |  | ||||||
| 									"mime": "image/png", |  | ||||||
| 									"position": 10, |  | ||||||
| 									"dataFileName": "1_Customize the Next theme_i.png" |  | ||||||
| 								} |  | ||||||
| 							] |  | ||||||
| 						}, |  | ||||||
| 						{ |  | ||||||
| 							"isClone": false, |  | ||||||
| 							"noteId": "pMq6N1oBV9oo", |  | ||||||
| 							"notePath": [ |  | ||||||
| 								"OkOZllzB3fqN", |  | ||||||
| 								"DtJJ20yEozPA", |  | ||||||
| 								"pMq6N1oBV9oo" |  | ||||||
| 							], |  | ||||||
| 							"title": "Reference", |  | ||||||
| 							"notePosition": 30, |  | ||||||
| 							"prefix": null, |  | ||||||
| 							"isExpanded": false, |  | ||||||
| 							"type": "text", |  | ||||||
| 							"mime": "text/html", |  | ||||||
| 							"attributes": [ |  | ||||||
| 								{ |  | ||||||
| 									"type": "relation", |  | ||||||
| 									"name": "internalLink", |  | ||||||
| 									"value": "po38jIc0LD2H", |  | ||||||
| 									"isInheritable": false, |  | ||||||
| 									"position": 10 |  | ||||||
| 								} |  | ||||||
| 							], |  | ||||||
| 							"format": "html", |  | ||||||
| 							"dataFileName": "Reference.html", |  | ||||||
| 							"attachments": [] |  | ||||||
| 						} |  | ||||||
| 					] |  | ||||||
| 				}, |  | ||||||
| 				{ | 				{ | ||||||
| 					"isClone": false, | 					"isClone": false, | ||||||
| 					"noteId": "LTnkDnYmmZ7s", | 					"noteId": "LTnkDnYmmZ7s", | ||||||
| @@ -1283,7 +1245,7 @@ | |||||||
| 									"title": "ETAPI", | 									"title": "ETAPI", | ||||||
| 									"notePosition": 10, | 									"notePosition": 10, | ||||||
| 									"prefix": null, | 									"prefix": null, | ||||||
| 									"isExpanded": true, | 									"isExpanded": false, | ||||||
| 									"type": "text", | 									"type": "text", | ||||||
| 									"mime": "text/html", | 									"mime": "text/html", | ||||||
| 									"attributes": [], | 									"attributes": [], | ||||||
| @@ -1333,7 +1295,7 @@ | |||||||
| 									"title": "Internal API", | 									"title": "Internal API", | ||||||
| 									"notePosition": 20, | 									"notePosition": 20, | ||||||
| 									"prefix": null, | 									"prefix": null, | ||||||
| 									"isExpanded": true, | 									"isExpanded": false, | ||||||
| 									"type": "text", | 									"type": "text", | ||||||
| 									"mime": "text/html", | 									"mime": "text/html", | ||||||
| 									"attributes": [], | 									"attributes": [], | ||||||
|   | |||||||
| Before Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 340 B | 
| After Width: | Height: | Size: 89 KiB | 
| Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB | 
| After Width: | Height: | Size: 115 KiB | 
| Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB | 
| After Width: | Height: | Size: 116 KiB | 
| Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB | 
| After Width: | Height: | Size: 86 KiB | 
| Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB | 
| Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB | 
| Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB | 
| Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB | 
| @@ -23,7 +23,7 @@ | |||||||
|           as PDF. On the server or PWA (mobile), the option is not available due |           as PDF. On the server or PWA (mobile), the option is not available due | ||||||
|           to technical constraints and it will be hidden.</p> |           to technical constraints and it will be hidden.</p> | ||||||
|         <p>To print a note, select the |         <p>To print a note, select the | ||||||
|           <img src="2_Export as PDF_image.png" width="29" |           <img src="1_Export as PDF_image.png" width="29" | ||||||
|           height="31">button to the right of the note and select <i>Export as PDF</i>.</p> |           height="31">button to the right of the note and select <i>Export as PDF</i>.</p> | ||||||
|         <p>Afterwards you will be prompted to select where to save the PDF file. |         <p>Afterwards you will be prompted to select where to save the PDF file. | ||||||
|           Upon confirmation, the resulting PDF will be opened automatically using |           Upon confirmation, the resulting PDF will be opened automatically using | ||||||
| @@ -33,7 +33,7 @@ | |||||||
|           <a |           <a | ||||||
|           href="#root/OeKBfN6JbMIq/jRV1MPt4mNSP/hrC6xn7hnDq5">report the issue</a>. In this case, it's best to offer a sample note (click |           href="#root/OeKBfN6JbMIq/jRV1MPt4mNSP/hrC6xn7hnDq5">report the issue</a>. In this case, it's best to offer a sample note (click | ||||||
|             on the |             on the | ||||||
|             <img src="2_Export as PDF_image.png" width="29" height="31">button, select Export note → This note and all of its descendants → HTML |             <img src="1_Export as PDF_image.png" width="29" height="31">button, select Export note → This note and all of its descendants → HTML | ||||||
|             in ZIP archive). Make sure not to accidentally leak any personal information.</p> |             in ZIP archive). Make sure not to accidentally leak any personal information.</p> | ||||||
|         <h2>Landscape mode</h2> |         <h2>Landscape mode</h2> | ||||||
|         <p>When exporting to PDF, there are no customizable settings such as page |         <p>When exporting to PDF, there are no customizable settings such as page | ||||||
| Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB | 
| @@ -0,0 +1,56 @@ | |||||||
|  | <html> | ||||||
|  |    | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <link rel="stylesheet" href="../../style.css"> | ||||||
|  |     <base target="_parent"> | ||||||
|  |     <title data-trilium-title>Right-to-left text notes</title> | ||||||
|  |   </head> | ||||||
|  |    | ||||||
|  |   <body> | ||||||
|  |     <div class="content"> | ||||||
|  |        <h1 data-trilium-h1>Right-to-left text notes</h1> | ||||||
|  |  | ||||||
|  |       <div class="ck-content"> | ||||||
|  |         <p>Trilium now has basic support for right-to-left text, at note level.</p> | ||||||
|  |         <figure | ||||||
|  |         class="table"> | ||||||
|  |           <table> | ||||||
|  |             <tbody> | ||||||
|  |               <tr> | ||||||
|  |                 <td> | ||||||
|  |                   <figure class="image"> | ||||||
|  |                     <img style="aspect-ratio:906/557;" src="3_Right-to-left text notes_i.png" | ||||||
|  |                     width="906" height="557"> | ||||||
|  |                   </figure> | ||||||
|  |                 </td> | ||||||
|  |                 <td> | ||||||
|  |                   <figure class="image"> | ||||||
|  |                     <img style="aspect-ratio:906/557;" src="2_Right-to-left text notes_i.png" | ||||||
|  |                     width="906" height="557"> | ||||||
|  |                   </figure> | ||||||
|  |                 </td> | ||||||
|  |               </tr> | ||||||
|  |             </tbody> | ||||||
|  |           </table> | ||||||
|  |           </figure> | ||||||
|  |           <p>Note that only the Text note type supports this.</p> | ||||||
|  |           <p>The list of languages is configurable via the a new dedicated settings | ||||||
|  |             page:</p> | ||||||
|  |           <figure class="image"> | ||||||
|  |             <img style="aspect-ratio:1248/635;" src="4_Right-to-left text notes_i.png" | ||||||
|  |             width="1248" height="635"> | ||||||
|  |           </figure> | ||||||
|  |           <p>To select the corresponding language of the text, go to “Basic Properties” | ||||||
|  |             and select your desired language.</p> | ||||||
|  |           <p> | ||||||
|  |             <img src="1_Right-to-left text notes_i.png" width="635" height="492"> | ||||||
|  |           </p> | ||||||
|  |           <p>Feel free to report any issues regarding right to left support.</p> | ||||||
|  |           <p> </p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </body> | ||||||
|  |  | ||||||
|  | </html> | ||||||
| After Width: | Height: | Size: 100 KiB | 
| Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB | 
| Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 323 KiB | 
| Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB | 
| Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB | 
| Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB | 
| Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB | 
| After Width: | Height: | Size: 31 KiB | 
| Before Width: | Height: | Size: 36 KiB | 
| After Width: | Height: | Size: 100 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB | 
| Before Width: | Height: | Size: 515 KiB After Width: | Height: | Size: 515 KiB | 
| Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB | 
| Before Width: | Height: | Size: 397 KiB After Width: | Height: | Size: 397 KiB | 
| Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB | 
| Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 260 KiB | 
| Before Width: | Height: | Size: 6.9 KiB | 
| Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB | 
| After Width: | Height: | Size: 4.6 KiB | 
| @@ -118,6 +118,12 @@ | |||||||
|                 <td>When present (regardless of value), it will show the number of the week |                 <td>When present (regardless of value), it will show the number of the week | ||||||
|                   on the calendar.</td> |                   on the calendar.</td> | ||||||
|               </tr> |               </tr> | ||||||
|  |               <tr> | ||||||
|  |                 <td><code>~child:template</code> | ||||||
|  |                 </td> | ||||||
|  |                 <td>Defines the template for newly created notes in the calendar (via dragging | ||||||
|  |                   or clicking).</td> | ||||||
|  |               </tr> | ||||||
|             </tbody> |             </tbody> | ||||||
|           </table> |           </table> | ||||||
|         </figure> |         </figure> | ||||||
| @@ -175,6 +181,36 @@ | |||||||
|                   than the title, either a label (e.g. <code>#assignee</code>) or a relation |                   than the title, either a label (e.g. <code>#assignee</code>) or a relation | ||||||
|                   (e.g. <code>~for</code>). See <i>Advanced use-cases</i> for more information.</td> |                   (e.g. <code>~for</code>). See <i>Advanced use-cases</i> for more information.</td> | ||||||
|               </tr> |               </tr> | ||||||
|  |               <tr> | ||||||
|  |                 <td><code>#calendar:promotedAttributes</code> | ||||||
|  |                 </td> | ||||||
|  |                 <td> | ||||||
|  |                   <p>Allows displaying the value of one or more promoted attributes in the | ||||||
|  |                     calendar like this: | ||||||
|  |                     <img src="19_Calendar View_image.png" width="131" height="113"> | ||||||
|  |                   </p><pre><code class="language-text-x-trilium-auto">#label:weight="promoted,number,single,precision=1" | ||||||
|  | #label:mood="promoted,alias=Mood,single,text" | ||||||
|  | #calendar:promotedAttributes="label:weight,label:mood" </code></pre> | ||||||
|  |                   <p>It can also be used with relations, case in which it will display the | ||||||
|  |                     title of the target note:</p><pre><code class="language-text-x-trilium-auto">#relation:assignee="promoted,alias=Assignee,single,text" | ||||||
|  | #calendar:promotedAttributes="relation:assignee"  | ||||||
|  | ~assignee=@My assignee </code></pre> | ||||||
|  |                 </td> | ||||||
|  |               </tr> | ||||||
|  |               <tr> | ||||||
|  |                 <td><code>#calendar:startDate</code> | ||||||
|  |                 </td> | ||||||
|  |                 <td>Allows using a different label to represent the start date, other than <code>#startDate</code> (e.g. <code>#expiryDate</code>). | ||||||
|  |                   The label name must be prefixed with <code>#</code>. If the label is not | ||||||
|  |                   defined for a note, the default will be used instead.</td> | ||||||
|  |               </tr> | ||||||
|  |               <tr> | ||||||
|  |                 <td><code>#calendar:endDate</code> | ||||||
|  |                 </td> | ||||||
|  |                 <td>Allows using a different label to represent the start date, other than <code>#endDate</code>. | ||||||
|  |                   The label name must be prefixed with <code>#</code>. If the label is not | ||||||
|  |                   defined for a note, the default will be used instead.</td> | ||||||
|  |               </tr> | ||||||
|             </tbody> |             </tbody> | ||||||
|           </table> |           </table> | ||||||
|           </figure> |           </figure> | ||||||
|   | |||||||
| Before Width: | Height: | Size: 6.5 KiB | 
| @@ -5,12 +5,12 @@ | |||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|     <link rel="stylesheet" href="../../style.css"> |     <link rel="stylesheet" href="../../style.css"> | ||||||
|     <base target="_parent"> |     <base target="_parent"> | ||||||
|     <title data-trilium-title>Geo Map</title> |     <title data-trilium-title>Geo map</title> | ||||||
|   </head> |   </head> | ||||||
|    |    | ||||||
|   <body> |   <body> | ||||||
|     <div class="content"> |     <div class="content"> | ||||||
|        <h1 data-trilium-h1>Geo Map</h1> |        <h1 data-trilium-h1>Geo map</h1> | ||||||
| 
 | 
 | ||||||
|       <div class="ck-content"> |       <div class="ck-content"> | ||||||
|         <h2>Creating a new geo map</h2> |         <h2>Creating a new geo map</h2> | ||||||
| @@ -26,7 +26,7 @@ | |||||||
|                 <th>1</th> |                 <th>1</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:1256/1044;" src="9_Geo Map_image.png" width="1256" |                     <img style="aspect-ratio:1256/1044;" src="7_Geo map_image.png" width="1256" | ||||||
|                     height="1044"> |                     height="1044"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -36,7 +36,7 @@ | |||||||
|                 <th>2</th> |                 <th>2</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:1720/1396;" src="3_Geo Map_image.png" width="1720" |                     <img style="aspect-ratio:1720/1396;" src="2_Geo map_image.png" width="1720" | ||||||
|                     height="1396"> |                     height="1396"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -69,18 +69,18 @@ | |||||||
|                   <p>To create a marker, first navigate to the desired point on the map. Then |                   <p>To create a marker, first navigate to the desired point on the map. Then | ||||||
|                     press the |                     press the | ||||||
|                     <img class="image_resized" style="aspect-ratio:72/66;width:7.37%;" |                     <img class="image_resized" style="aspect-ratio:72/66;width:7.37%;" | ||||||
|                     src="4_Geo Map_image.png" width="72" height="66">button on the top-right of the map.</p> |                     src="3_Geo map_image.png" width="72" height="66">button on the top-right of the map.</p> | ||||||
|                   <p>If the button is not visible, make sure the button section is visible |                   <p>If the button is not visible, make sure the button section is visible | ||||||
|                     by pressing the chevron button ( |                     by pressing the chevron button ( | ||||||
|                     <img class="image_resized" style="aspect-ratio:72/66;width:7.51%;" |                     <img class="image_resized" style="aspect-ratio:72/66;width:7.51%;" | ||||||
|                     src="10_Geo Map_image.png" width="72" height="66">) in the top-right of the map.</p> |                     src="8_Geo map_image.png" width="72" height="66">) in the top-right of the map.</p> | ||||||
|                 </td> |                 </td> | ||||||
|               </tr> |               </tr> | ||||||
|               <tr> |               <tr> | ||||||
|                 <th>2</th> |                 <th>2</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:1730/416;" src="14_Geo Map_image.png" width="1730" |                     <img style="aspect-ratio:1730/416;" src="12_Geo map_image.png" width="1730" | ||||||
|                     height="416"> |                     height="416"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                   <p> </p> |                   <p> </p> | ||||||
| @@ -96,7 +96,7 @@ | |||||||
|                 <th>3</th> |                 <th>3</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image"> |                   <figure class="image"> | ||||||
|                     <img style="aspect-ratio:1586/404;" src="1_Geo Map_image.png" width="1586" |                     <img style="aspect-ratio:1586/404;" src="Geo map_image.png" width="1586" | ||||||
|                     height="404"> |                     height="404"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                   <p> </p> |                   <p> </p> | ||||||
| @@ -107,7 +107,7 @@ | |||||||
|                 <th>4</th> |                 <th>4</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image"> |                   <figure class="image"> | ||||||
|                     <img style="aspect-ratio:1696/608;" src="6_Geo Map_image.png" width="1696" |                     <img style="aspect-ratio:1696/608;" src="5_Geo map_image.png" width="1696" | ||||||
|                     height="608"> |                     height="608"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                   <p> </p> |                   <p> </p> | ||||||
| @@ -122,7 +122,7 @@ | |||||||
|         <p>The location of a marker is stored in the <code>#geolocation</code> attribute |         <p>The location of a marker is stored in the <code>#geolocation</code> attribute | ||||||
|           of the child notes:</p> |           of the child notes:</p> | ||||||
|         <figure class="image"> |         <figure class="image"> | ||||||
|           <img style="aspect-ratio:1288/278;" src="12_Geo Map_image.png" width="1288" |           <img style="aspect-ratio:1288/278;" src="10_Geo map_image.png" width="1288" | ||||||
|           height="278"> |           height="278"> | ||||||
|         </figure> |         </figure> | ||||||
|         <p>This value can be added manually if needed. The value of the attribute |         <p>This value can be added manually if needed. The value of the attribute | ||||||
| @@ -155,6 +155,13 @@ | |||||||
|             </ul> |             </ul> | ||||||
|           </li> |           </li> | ||||||
|         </ul> |         </ul> | ||||||
|  |         <h2>Icon and color of the markers</h2> | ||||||
|  |         <p> | ||||||
|  |           <img src="18_Geo map_image.png" alt="image" width="523" height="295"> | ||||||
|  |         </p> | ||||||
|  |         <p>The markers will have the same icon as the note.</p> | ||||||
|  |         <p>It's possible to add a custom color to a marker by assigning them a <code>#color</code> attribute | ||||||
|  |           such as <code>#color=green</code>.</p> | ||||||
|         <h2>Adding the coordinates manually</h2> |         <h2>Adding the coordinates manually</h2> | ||||||
|         <p>In a nutshell, create a child note and set the <code>#geolocation</code> attribute |         <p>In a nutshell, create a child note and set the <code>#geolocation</code> attribute | ||||||
|           to the coordinates.</p> |           to the coordinates.</p> | ||||||
| @@ -168,7 +175,7 @@ | |||||||
|                 <th>1</th> |                 <th>1</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image-style-align-center image_resized" style="width:100%;"> |                   <figure class="image image-style-align-center image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:732/918;" src="16_Geo Map_image.png" width="732" |                     <img style="aspect-ratio:732/918;" src="14_Geo map_image.png" width="732" | ||||||
|                     height="918"> |                     height="918"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -185,7 +192,7 @@ | |||||||
|                 <th>2</th> |                 <th>2</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:518/84;" src="19_Geo Map_image.png" width="518" |                     <img style="aspect-ratio:518/84;" src="16_Geo map_image.png" width="518" | ||||||
|                     height="84"> |                     height="84"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -199,7 +206,7 @@ | |||||||
|                 <th>3</th> |                 <th>3</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:1074/276;" src="11_Geo Map_image.png" width="1074" |                     <img style="aspect-ratio:1074/276;" src="9_Geo map_image.png" width="1074" | ||||||
|                     height="276"> |                     height="276"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -225,7 +232,7 @@ | |||||||
|                 <th>1</th> |                 <th>1</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:562/454;" src="17_Geo Map_image.png" width="562" |                     <img style="aspect-ratio:562/454;" src="15_Geo map_image.png" width="562" | ||||||
|                     height="454"> |                     height="454"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -236,7 +243,7 @@ | |||||||
|                 <th>2</th> |                 <th>2</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:696/480;" src="13_Geo Map_image.png" width="696" |                     <img style="aspect-ratio:696/480;" src="11_Geo map_image.png" width="696" | ||||||
|                     height="480"> |                     height="480"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -250,7 +257,7 @@ | |||||||
|                 <th>3</th> |                 <th>3</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image"> |                   <figure class="image"> | ||||||
|                     <img style="aspect-ratio:640/276;" src="2_Geo Map_image.png" width="640" |                     <img style="aspect-ratio:640/276;" src="1_Geo map_image.png" width="640" | ||||||
|                     height="276"> |                     height="276"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -275,7 +282,7 @@ | |||||||
|                 <th>1</th> |                 <th>1</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image"> |                   <figure class="image"> | ||||||
|                     <img style="aspect-ratio:226/74;" src="7_Geo Map_image.png" width="226" |                     <img style="aspect-ratio:226/74;" src="6_Geo map_image.png" width="226" | ||||||
|                     height="74"> |                     height="74"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -286,7 +293,7 @@ | |||||||
|                 <th>2</th> |                 <th>2</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image"> |                   <figure class="image"> | ||||||
|                     <img style="aspect-ratio:322/222;" src="5_Geo Map_image.png" width="322" |                     <img style="aspect-ratio:322/222;" src="4_Geo map_image.png" width="322" | ||||||
|                     height="222"> |                     height="222"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -297,7 +304,7 @@ | |||||||
|                 <th>3</th> |                 <th>3</th> | ||||||
|                 <td> |                 <td> | ||||||
|                   <figure class="image image_resized" style="width:100%;"> |                   <figure class="image image_resized" style="width:100%;"> | ||||||
|                     <img style="aspect-ratio:620/530;" src="15_Geo Map_image.png" width="620" |                     <img style="aspect-ratio:620/530;" src="13_Geo map_image.png" width="620" | ||||||
|                     height="530"> |                     height="530"> | ||||||
|                   </figure> |                   </figure> | ||||||
|                 </td> |                 </td> | ||||||
| @@ -310,9 +317,16 @@ | |||||||
|             </tbody> |             </tbody> | ||||||
|           </table> |           </table> | ||||||
|           </figure> |           </figure> | ||||||
|           <p> </p> |           <h2>Troubleshooting</h2> | ||||||
|           <p> </p> |           <h3>Grid-like artifacts on the map</h3> | ||||||
|           <p> </p> |           <p> | ||||||
|  |             <img class="image_resized" style="aspect-ratio:678/499;width:58%;" src="17_Geo map_image.png" | ||||||
|  |             width="678" height="499"> | ||||||
|  |           </p> | ||||||
|  |           <p>This occurs if the application is not at 100% zoom which causes the pixels | ||||||
|  |             of the map to not render correctly due to fractional scaling. The only | ||||||
|  |             possible solution i to set the UI zoom at 100% (default keyboard shortcut | ||||||
|  |             is Ctrl+0).</p> | ||||||
|           <p> </p> |           <p> </p> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB | 
| @@ -1,19 +0,0 @@ | |||||||
| <html> |  | ||||||
|    |  | ||||||
|   <head> |  | ||||||
|     <meta charset="utf-8"> |  | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> |  | ||||||
|     <link rel="stylesheet" href="../../style.css"> |  | ||||||
|     <base target="_parent"> |  | ||||||
|     <title data-trilium-title>Text</title> |  | ||||||
|   </head> |  | ||||||
|    |  | ||||||
|   <body> |  | ||||||
|     <div class="content"> |  | ||||||
|        <h1 data-trilium-h1>Text</h1> |  | ||||||
|  |  | ||||||
|       <div class="ck-content"></div> |  | ||||||
|     </div> |  | ||||||
|   </body> |  | ||||||
|  |  | ||||||
| </html> |  | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | <html> | ||||||
|  |    | ||||||
|  |   <head> | ||||||
|  |     <meta charset="utf-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <link rel="stylesheet" href="../../../style.css"> | ||||||
|  |     <base target="_parent"> | ||||||
|  |     <title data-trilium-title>Content language</title> | ||||||
|  |   </head> | ||||||
|  |    | ||||||
|  |   <body> | ||||||
|  |     <div class="content"> | ||||||
|  |        <h1 data-trilium-h1>Content language</h1> | ||||||
|  |  | ||||||
|  |       <div class="ck-content"> | ||||||
|  |         <p>A language hint can be provided for text notes. This option informs the | ||||||
|  |           browser or the desktop application about the language the note is written | ||||||
|  |           in (for example this might help with spellchecking), and it also determines | ||||||
|  |           whether the text is displayed from right-to-left for languages such as | ||||||
|  |           Arabic, Hebrew, etc.</p> | ||||||
|  |         <p>For more information about right-to-left support, see <a class="reference-link" | ||||||
|  |           href="../../New%20Features/Right-to-left%20text%20notes.html">Right-to-left text notes</a>.</p> | ||||||
|  |         <p>To set the language of the content, go to “Basic Properties” and look | ||||||
|  |           for the “Language” field. By default there will be no content languages | ||||||
|  |           set, they can be configured by going to settings or by selecting the “Configure | ||||||
|  |           languages” item in the list.</p> | ||||||
|  |         <p> | ||||||
|  |           <img src="Content language_image.png" width="635" height="492"> | ||||||
|  |         </p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </body> | ||||||
|  |  | ||||||
|  | </html> | ||||||
| After Width: | Height: | Size: 89 KiB | 
| Before Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 57 KiB |