diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index b55ffea4b..76e8161b5 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -33,10 +33,10 @@ runs: run: sed -e "s/case 'armv7l'/case 'arm64'/g" -e "s/return 'arm'/return 'aarch64'/g" -i node_modules/@electron-forge/maker-flatpak/dist/MakerFlatpak.js - name: Update build info shell: bash - run: npm run update-build-info + run: npm run chore:update-build-info - name: Run electron-forge shell: bash - run: npm run make-electron -- --arch=${{ inputs.arch }} + run: npm run electron-forge:make -- --arch=${{ inputs.arch }} - name: Prepare artifacts shell: bash run: | diff --git a/.github/actions/build-server/action.yml b/.github/actions/build-server/action.yml index 694005c1b..cf8057c70 100644 --- a/.github/actions/build-server/action.yml +++ b/.github/actions/build-server/action.yml @@ -18,7 +18,7 @@ runs: MATRIX_ARCH: ${{ inputs.arch }} shell: bash run: | - npm run update-build-info + npm run chore:update-build-info ./bin/build-server.sh - name: Prepare artifacts shell: bash diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2b1fbe57e..f7cee700a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -38,7 +38,7 @@ jobs: shell: bash run: npm ci - name: Update nightly version - run: npm run ci-update-nightly-version + run: npm run chore:ci-update-nightly-version - name: Run the build uses: ./.github/actions/build-electron with: diff --git a/.gitpod.yml b/.gitpod.yml index 2b77be7b7..f87a115d0 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -4,7 +4,7 @@ image: tasks: - before: nvm install 20.15.1 && nvm use 20.15.1 init: npm install - command: npm run start-server + command: npm run server:start ports: - port: 8080 diff --git a/Dockerfile b/Dockerfile index 959c244a9..365e4d07f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM node:22.13.1-bullseye-slim AS builder +FROM node:22.14.0-bullseye-slim AS builder # Configure build dependencies in a single layer RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -25,7 +25,7 @@ RUN cp -R build/src/* src/. && \ cp build/docker_healthcheck.js . && \ rm docker_healthcheck.ts && \ npm install && \ - npm run webpack && \ + npm run build:webpack && \ npm prune --omit=dev && \ npm cache clean --force && \ cp -r src/public/app/doc_notes src/public/app-dist/. && \ @@ -36,7 +36,7 @@ RUN cp -R build/src/* src/. && \ rm -r build # Runtime stage -FROM node:22.13.1-bullseye-slim +FROM node:22.14.0-bullseye-slim # Install only runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/Dockerfile.alpine b/Dockerfile.alpine index f7b49b94c..36d6f0b7b 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # Build stage -FROM node:22.13.1-alpine AS builder +FROM node:22.14.0-alpine AS builder # Configure build dependencies RUN apk add --no-cache --virtual .build-dependencies \ @@ -24,7 +24,7 @@ RUN cp -R build/src/* src/. && \ cp build/docker_healthcheck.js . && \ rm docker_healthcheck.ts && \ npm install && \ - npm run webpack && \ + npm run build:webpack && \ npm prune --omit=dev && \ npm cache clean --force && \ cp -r src/public/app/doc_notes src/public/app-dist/. && \ @@ -35,7 +35,7 @@ RUN cp -R build/src/* src/. && \ rm -r build # Runtime stage -FROM node:22.13.1-alpine +FROM node:22.14.0-alpine # Install runtime dependencies RUN apk add --no-cache su-exec shadow diff --git a/README-ZH_CN.md b/README-ZH_CN.md index f6aa860b1..1f6bbaf55 100644 --- a/README-ZH_CN.md +++ b/README-ZH_CN.md @@ -78,7 +78,7 @@ Trilium 也提供 Flatpak: ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 致谢 diff --git a/README.es.md b/README.es.md index a2ea94144..bfa57fe45 100644 --- a/README.es.md +++ b/README.es.md @@ -86,7 +86,7 @@ Clone localmente y ejecute ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Reconocimientos diff --git a/README.it.md b/README.it.md index a778230c7..750027c1a 100644 --- a/README.it.md +++ b/README.it.md @@ -73,7 +73,7 @@ Clona localmente ed esegui ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Riconoscimenti diff --git a/README.ja.md b/README.ja.md index 110b779c5..757638770 100644 --- a/README.ja.md +++ b/README.ja.md @@ -54,7 +54,7 @@ Trilium は Flatpak としても提供されます: ```shell npm install -npm run start-server +npm run server:start ``` ## 📢 シャウトアウト diff --git a/README.md b/README.md index e2a81aa4c..0170c8135 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ You can also read [Patterns of personal knowledge base](https://triliumnext.gith git clone https://github.com/TriliumNext/Notes.git cd Notes npm install -npm run start-server +npm run server:start ``` ### Documentation diff --git a/README.ru.md b/README.ru.md index d78738915..e2336efb3 100644 --- a/README.ru.md +++ b/README.ru.md @@ -44,7 +44,7 @@ Trilium предоставляется в виде десктопного при ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Благодарности diff --git a/bin/copy-dist.ts b/bin/copy-dist.ts index 6ffc2cf98..6087a69bd 100644 --- a/bin/copy-dist.ts +++ b/bin/copy-dist.ts @@ -7,9 +7,9 @@ const DEST_DIR_NODE_MODULES = path.join(DEST_DIR, "node_modules"); const VERBOSE = process.env.VERBOSE; -function log(...args) { +function log(...args: any[]) { if (VERBOSE) { - console.log(args); + console.log(...args); } } @@ -29,7 +29,12 @@ const copy = async () => { fs.copySync(path.join("build", srcFile), destFile, { recursive: true }); } - const filesToCopy = ["config-sample.ini", "tsconfig.webpack.json"]; + const filesToCopy = [ + "config-sample.ini", + "tsconfig.webpack.json", + "./src/etapi/etapi.openapi.yaml", + "./src/routes/api/openapi.json" + ]; for (const file of filesToCopy) { log(`Copying ${file}`); await fs.copy(file, path.join(DEST_DIR, file)); diff --git a/bin/copy-trilium.sh b/bin/copy-trilium.sh index 221e8a0ea..e1d0e197f 100755 --- a/bin/copy-trilium.sh +++ b/bin/copy-trilium.sh @@ -23,7 +23,7 @@ rm -rf "$DIR" mkdir -pv "$DIR" echo Webpack start -npm run webpack +npm run build:webpack echo Webpack finish echo "Copying Trilium to build directory $DIR" diff --git a/bin/generate-openapi.ts b/bin/generate-openapi.ts new file mode 100644 index 000000000..4bd97a76f --- /dev/null +++ b/bin/generate-openapi.ts @@ -0,0 +1,189 @@ +import { fileURLToPath } from "url"; +import { dirname, join } from "path"; +import swaggerJsdoc from 'swagger-jsdoc'; +import fs from "fs"; + +/* + * Usage: npm run generate-openapi | tail -n1 > x.json + * + * Inspect generated file by opening it in https://editor-next.swagger.io/ + * + */ + +const options = { + definition: { + openapi: '3.1.1', + info: { + title: 'Trilium Notes - Sync server API', + 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).", + contact: { + name: "TriliumNext issue tracker", + url: "https://github.com/TriliumNext/Notes/issues", + }, + license: { + name: "GNU Free Documentation License 1.3 (or later)", + url: "https://www.gnu.org/licenses/fdl-1.3", + }, + }, + }, + apis: [ + // Put individual files here to have them ordered first. + './src/routes/api/setup.ts', + // all other files + './src/routes/api/*.ts', './bin/generate-openapi.js' + ], +}; + +const openapiSpecification = swaggerJsdoc(options); +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const outputPath = join(scriptDir, "..", "src", "routes", "api", "openapi.json"); +fs.writeFileSync(outputPath, JSON.stringify(openapiSpecification)); +console.log("Saved to ", outputPath); + +/** + * @swagger + * tags: + * - name: auth + * description: Authentication + * - name: sync + * description: Synchronization + * - name: data + */ + +/** + * @swagger + * components: + * schemas: + * Attribute: + * type: object + * properties: + * attributeId: + * type: string + * example: "4G1DPrI58PAb" + * noteId: + * $ref: "#/components/schemas/NoteId" + * type: + * type: string + * enum: ["attribute", "relation"] + * name: + * type: string + * example: "internalLink" + * value: + * type: string + * example: "hA8aHSpTRdZ6" + * description: "If type = \"relation\", a note ID. Otherwise, the attribute content." + * position: + * type: integer + * example: 20 + * isInheritable: + * type: boolean + * Blob: + * type: object + * properties: + * blobId: + * type: string + * example: "8iqMIB8eiY1tPYmElfjm" + * content: + * type: + * - string + * - 'null' + * description: "`null` if not text." + * contentLength: + * type: integer + * dateModified: + * $ref: "#/components/schemas/DateTime" + * utcDateModified: + * $ref: "#/components/schemas/UtcDateTime" + * Branch: + * type: object + * properties: + * branchId: + * $ref: "#/components/schemas/BranchId" + * noteId: + * $ref: "#/components/schemas/NoteId" + * parentNoteId: + * $ref: "#/components/schemas/NoteId" + * notePosition: + * type: integer + * example: 20 + * prefix: + * type: + * - string + * - 'null' + * isExpanded: + * type: boolean + * BranchId: + * type: string + * example: "WUjhaGp4EKah_ur11rSfHkzeV" + * description: Equal to `{parentNoteId}_{noteId}` + * DateTime: + * type: string + * example: "2025-02-14 08:19:59.203+0100" + * EntityChange: + * type: object + * properties: + * entityChange: + * type: object + * properties: + * entityName: + * type: string + * example: "notes" + * description: Database table for this entity. + * changeId: + * type: string + * example: "changeId9630" + * description: ID, referenced in `entity_changes` table. + * entity: + * type: object + * description: Encoded entity data. Object has one property for each database column. + * Note: + * type: object + * properties: + * noteId: + * $ref: "#/components/schemas/NoteId" + * title: + * type: string + * isProtected: + * type: boolean + * type: + * type: string + * example: "text" + * enum: ["text", "code", "render", "file", "image", "search", "relationMap", "book", "noteMap", "mermaid", "canvas", "webView", "launcher", "doc", "contentWidget", "mindMap", "geoMap"] + * description: "[Reference list](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/note_types.ts)" + * mime: + * type: string + * example: "text/html" + * blobId: + * type: string + * example: "z4PhNX7vuL3xVChQ1m2A" + * NoteId: + * type: string + * example: "ur11rSfHkzeV" + * description: "12-character note ID. Special values: \"none\"`, `\"root\"." + * Timestamps: + * type: object + * properties: + * dateCreated: + * $ref: "#/components/schemas/DateTime" + * dateModified: + * $ref: "#/components/schemas/DateTime" + * utcDateCreated: + * $ref: "#/components/schemas/UtcDateTime" + * utcDateModified: + * $ref: "#/components/schemas/UtcDateTime" + * UtcDateTime: + * type: string + * example: "2025-02-13T07:42:47.698Z" + * description: "Result of `new Date().toISOString().replace('T', ' ')`" + * securitySchemes: + * user-password: + * type: apiKey + * name: trilium-cred + * in: header + * description: "Username and password, formatted as `user:password`" + * session: + * type: apiKey + * in: cookie + * name: trilium.sid + */ diff --git a/bin/release.sh b/bin/release.sh index fea1e030f..74559a88c 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -32,7 +32,7 @@ mv package.json.tmp package.json git add package.json -npm run update-build-info +npm run chore:update-build-info git add src/services/build.ts diff --git a/config-sample.ini b/config-sample.ini index 939eaa7a5..baa026730 100644 --- a/config-sample.ini +++ b/config-sample.ini @@ -30,13 +30,19 @@ trustedReverseProxy=false [Session] -# Use this setting to constrain the current instance's "Path" value for the set cookies +# Use this setting to set a custom value for the "Path" Attribute value of the session cookie. # This can be useful, when you have several instances running on the same domain, under different paths (e.g. by using a reverse proxy). -# It prevents your instances from overwriting each others' cookies. -# e.g. if you have https://your-domain.com/triliumNext/instanceA and https://your-domain.com/triliumNext/instanceB +# It prevents your instances from overwriting each others' cookies, allowing you to stay logged in multiple instances simultanteously. +# E.g. if you have instances running under https://your-domain.com/triliumNext/instanceA and https://your-domain.com/triliumNext/instanceB # you would want to set the cookiePath value to "/triliumNext/instanceA" for your first and "/triliumNext/instanceB" for your second instance cookiePath=/ +# Use this setting to set a custom value for the "Max-Age" Attribute of the session cookie. +# This controls how long your session will be valid, before it expires and you need to log in again, when you use the "Remember Me" option. +# Value needs to be entered in Seconds. +# Default value is 1814400 Seconds, which is 21 Days. +cookieMaxAge=1814400 + [Sync] #syncServerHost= #syncServerTimeout= diff --git a/docs/X4N03xLYEWnN.html b/docs/X4N03xLYEWnN.html index 2b686480d..b8e844cba 100644 --- a/docs/X4N03xLYEWnN.html +++ b/docs/X4N03xLYEWnN.html @@ -38,12 +38,12 @@
-

The native node bindings

better-sqlite3 has native Node bindings. With updates of better-sqlite3, but also of Electron and Node.js versions, these bindings need to be updated.

Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js.

During development, npm install tries to build or reuse prebuilt natives for the current Node.js version. This makes npm run start-server work out of the box. Trying to run npm run start-electron with these versions generally causes an error such as this:

Uncaught Exception:
+                

The native node bindings

better-sqlite3 has native Node bindings. With updates of better-sqlite3, but also of Electron and Node.js versions, these bindings need to be updated.

Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js.

During development, npm install tries to build or reuse prebuilt natives for the current Node.js version. This makes npm run server:start work out of the box. Trying to run npm run electron:start with these versions generally causes an error such as this:

Uncaught Exception:
 Error: The module '/Users/elian/Projects/Notes/node_modules/better-sqlite3/build/Release/better_sqlite3.node'
 was compiled against a different Node.js version using
 NODE_MODULE_VERSION 108. This version of Node.js requires
 NODE_MODULE_VERSION 116. Please try re-compiling or re-installing
-the module (for instance, using `npm rebuild` or `npm install`).

How the natives are handled

Locally, this can be fixed by rebuilding the binaries, which is what npm run switch-electron does, which uses electron-rebuild under the hood.

When the deliveries are built (see Build deliveries locally), it is not feasible to rebuild the dependencies since we are building for multiple platforms. Luckily, better-sqlite3 provides these prebuilt binaries from us, available as artifacts on their GitHub releases page

The build script manages the natives for better-sqlite3 by keeping a copy of the .node file for every platform in bin/better-sqlite3.

Whenever the version of better-sqlite3 changes, the .node files must also be renewed based on their releases page. To simplify this process, a script was created in bin/better-sqlite3/update.sh.

How to update the natives

The update script needs to know the version of Electron or Node.js for which to download the prebuilt binaries.

If you get errors during download, check on the releases page to ensure that this particular combination of Electron/Node actually exists for the given release.

To determine the NODE_MODULE_VERSION that is required, look for This version of Node.js requires
NODE_MODULE_VERSION in the error when starting Trilium via:

  • npm run start-electron (or run any Electron delivery), case in which the ELECTRON_VERSION variable needs to be changed.
  • npm run start-server (or run the Linux server delivery), case in which the NODE_VERSION variable needs to be changed.

Check which files got changed after running the update script and for each platform that got changed, test it locally via Build deliveries locally or via the CI.

+the module (for instance, using `npm rebuild` or `npm install`).

How the natives are handled

Locally, this can be fixed by rebuilding the binaries, which is what npm run electron:switch does, which uses electron-rebuild under the hood.

When the deliveries are built (see Build deliveries locally), it is not feasible to rebuild the dependencies since we are building for multiple platforms. Luckily, better-sqlite3 provides these prebuilt binaries from us, available as artifacts on their GitHub releases page

The build script manages the natives for better-sqlite3 by keeping a copy of the .node file for every platform in bin/better-sqlite3.

Whenever the version of better-sqlite3 changes, the .node files must also be renewed based on their releases page. To simplify this process, a script was created in bin/better-sqlite3/update.sh.

How to update the natives

The update script needs to know the version of Electron or Node.js for which to download the prebuilt binaries.

If you get errors during download, check on the releases page to ensure that this particular combination of Electron/Node actually exists for the given release.

To determine the NODE_MODULE_VERSION that is required, look for This version of Node.js requires
NODE_MODULE_VERSION in the error when starting Trilium via:

Check which files got changed after running the update script and for each platform that got changed, test it locally via Build deliveries locally or via the CI.

diff --git a/docs/XxqZW6JjkW2g.html b/docs/XxqZW6JjkW2g.html index 3307c8c4f..25b34176c 100644 --- a/docs/XxqZW6JjkW2g.html +++ b/docs/XxqZW6JjkW2g.html @@ -38,7 +38,7 @@
-

Server live reload

If running the server using npm run start-server, the server will watch for changes in src/public and trigger a frontend reload if that occurs.

Electron live reload

Similarly, npm run start-electron supports live refresh  as well.

However, a core difference is that Electron watches dist/src/public instead of src/public since Electron runs on its own copy of the files.

To ameliorate that, a separate watch script has been implemented which automatically copies files from src/public to dist/src/public whenever a change is detected. To run it:

npm run 

Technical details

+

Server live reload

If running the server using npm run server:start, the server will watch for changes in src/public and trigger a frontend reload if that occurs.

Electron live reload

Similarly, npm run electron:start supports live refresh  as well.

However, a core difference is that Electron watches dist/src/public instead of src/public since Electron runs on its own copy of the files.

To ameliorate that, a separate watch script has been implemented which automatically copies files from src/public to dist/src/public whenever a change is detected. To run it:

npm run 

Technical details

diff --git a/docs/backend_api/index.html b/docs/backend_api/index.html index 612058e6f..6c27bd07a 100644 --- a/docs/backend_api/index.html +++ b/docs/backend_api/index.html @@ -71,7 +71,7 @@

To install TriliumNext on your own server (including via Docker from Dockerhub) follow the server installation docs.

See wiki for complete list of documentation pages.

You can also read Patterns of personal knowledge base to get some inspiration on how you might use TriliumNext.

-
git clone https://github.com/TriliumNext/Notes.git
cd Notes
npm install
npm run start-server +
git clone https://github.com/TriliumNext/Notes.git
cd Notes
npm install
npm run server:start

Head on over to our Docs repo

diff --git a/docs/backend_api/media/README-ZH_CN.md b/docs/backend_api/media/README-ZH_CN.md index f6aa860b1..1f6bbaf55 100644 --- a/docs/backend_api/media/README-ZH_CN.md +++ b/docs/backend_api/media/README-ZH_CN.md @@ -78,7 +78,7 @@ Trilium 也提供 Flatpak: ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 致谢 diff --git a/docs/backend_api/media/README.es.md b/docs/backend_api/media/README.es.md index a2ea94144..bfa57fe45 100644 --- a/docs/backend_api/media/README.es.md +++ b/docs/backend_api/media/README.es.md @@ -86,7 +86,7 @@ Clone localmente y ejecute ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Reconocimientos diff --git a/docs/backend_api/media/README.it.md b/docs/backend_api/media/README.it.md index a778230c7..750027c1a 100644 --- a/docs/backend_api/media/README.it.md +++ b/docs/backend_api/media/README.it.md @@ -73,7 +73,7 @@ Clona localmente ed esegui ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Riconoscimenti diff --git a/docs/backend_api/media/README.ja.md b/docs/backend_api/media/README.ja.md index 110b779c5..757638770 100644 --- a/docs/backend_api/media/README.ja.md +++ b/docs/backend_api/media/README.ja.md @@ -54,7 +54,7 @@ Trilium は Flatpak としても提供されます: ```shell npm install -npm run start-server +npm run server:start ``` ## 📢 シャウトアウト diff --git a/docs/backend_api/media/README.md b/docs/backend_api/media/README.md index 1800f667e..c427d98d0 100644 --- a/docs/backend_api/media/README.md +++ b/docs/backend_api/media/README.md @@ -102,7 +102,7 @@ You can also read [Patterns of personal knowledge base](https://triliumnext.gith git clone https://github.com/TriliumNext/Notes.git cd Notes npm install -npm run start-server +npm run server:start ``` ### Documentation diff --git a/docs/backend_api/media/README.ru.md b/docs/backend_api/media/README.ru.md index d78738915..e2336efb3 100644 --- a/docs/backend_api/media/README.ru.md +++ b/docs/backend_api/media/README.ru.md @@ -44,7 +44,7 @@ Trilium предоставляется в виде десктопного при ```shell npm install -npm run start-server +npm run server:start ``` ## 👏 Благодарности diff --git a/docs/btM6L9JtG301.html b/docs/btM6L9JtG301.html index a16f503c9..f8a6b4701 100644 --- a/docs/btM6L9JtG301.html +++ b/docs/btM6L9JtG301.html @@ -38,7 +38,7 @@
-

Run server

Run with default settings:

npm run start-server

Run with custom port:

TRILIUM_PORT=8082 npm run start-server

Run Electron

Rebuild better-sqlite3 dependency:

npm run switch-electron

Then run Electron:

npm run start-electron

To run Electron using the same data directory as the production version:

npm run start-electron-no-dir

When done, switch back the better-sqlite3 dependency:

npm run switch-server
+

Run server

Run with default settings:

npm run server:start

Run with custom port:

TRILIUM_PORT=8082 npm run server:start

Run Electron

Rebuild better-sqlite3 dependency:

npm run electron:switch

Then run Electron:

npm run electron:start

To run Electron using the same data directory as the production version:

npm run electron:start-no-dir

When done, switch back the better-sqlite3 dependency:

npm run server:switch
diff --git a/docs/mPGbEmYGitWe.html b/docs/mPGbEmYGitWe.html index a66680a97..017d198b7 100644 --- a/docs/mPGbEmYGitWe.html +++ b/docs/mPGbEmYGitWe.html @@ -38,7 +38,7 @@
-
  • Provides context about when the build was made and the corresponding Git revision.
  • The information is displayed to the client when going in the about dialog.
  • The build information is hard-coded in src/services/build.ts. This file is generated automatically via npm run update-build-info which itself is run automatically whenever making a build in the CI, or a local delivery.
+
  • Provides context about when the build was made and the corresponding Git revision.
  • The information is displayed to the client when going in the about dialog.
  • The build information is hard-coded in src/services/build.ts. This file is generated automatically via npm run chore:update-build-info which itself is run automatically whenever making a build in the CI, or a local delivery.
diff --git a/libraries/codemirror/hcl.js b/libraries/codemirror/hcl.js index cb4243889..04c0ea749 100644 --- a/libraries/codemirror/hcl.js +++ b/libraries/codemirror/hcl.js @@ -198,6 +198,7 @@ ext: [ "hcl " ], mime: "text/x-hcl", mode: "hcl", + name: "Terraform (HCL)" }); }); diff --git a/package-lock.json b/package-lock.json index d633ccfe2..a32cc9615 100644 --- a/package-lock.json +++ b/package-lock.json @@ -135,13 +135,12 @@ "@types/fs-extra": "11.0.4", "@types/html": "1.0.4", "@types/ini": "4.1.1", - "@types/jasmine": "5.1.5", "@types/jquery": "3.5.32", "@types/jsdom": "21.1.7", "@types/leaflet-gpx": "1.3.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.13.1", + "@types/node": "22.13.4", "@types/react": "18.3.18", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", @@ -157,20 +156,21 @@ "@types/yargs": "17.0.33", "@vitest/coverage-v8": "3.0.5", "cross-env": "7.0.3", - "electron": "34.1.1", + "electron": "34.2.0", "esm": "3.2.25", "jsdoc": "4.0.4", "lorem-ipsum": "2.0.8", "nodemon": "3.1.9", - "prettier": "3.5.0", + "prettier": "3.5.1", "rcedit": "4.0.1", "rimraf": "6.0.1", + "swagger-jsdoc": "6.2.8", "tslib": "2.8.1", "tsx": "4.19.2", "typedoc": "0.27.7", "typescript": "5.7.3", "vitest": "3.0.5", - "webpack": "5.97.1", + "webpack": "5.98.0", "webpack-cli": "6.0.1", "webpack-dev-middleware": "7.4.2" } @@ -222,6 +222,54 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.2.tgz", @@ -2715,6 +2763,13 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "license": "MIT" }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jsdoc/salty": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", @@ -4048,13 +4103,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/jasmine": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.5.tgz", - "integrity": "sha512-SaCZ3kM5NjOiJqMRYwHpLbTfUC2Dyk1KS3QanNFsUYPGTk70CWVK/J9ueun6zNhw/UkgV7xl8V4ZLQZNRbfnNw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/jquery": { "version": "3.5.32", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.32.tgz", @@ -4183,9 +4231,9 @@ } }, "node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -4914,15 +4962,15 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -4946,35 +4994,16 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", "peerDependencies": { - "ajv": "^6.9.1" + "ajv": "^8.8.2" } }, "node_modules/amator": { @@ -6021,6 +6050,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/caniuse-lite": { "version": "1.0.30001689", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001689.tgz", @@ -7799,6 +7835,19 @@ "p-limit": "^3.1.0 " } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -7928,9 +7977,9 @@ } }, "node_modules/electron": { - "version": "34.1.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-34.1.1.tgz", - "integrity": "sha512-1aDYk9Gsv1/fFeClMrxWGoVMl7uCUgl1pe26BiTnLXmAoqEXCa3f3sCKFWV+cuDzUjQGAZcpkWhGYTgWUSQrLA==", + "version": "34.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-34.2.0.tgz", + "integrity": "sha512-SYwBJNeXBTm1q/ErybQMUBZAYqEreBUqBwTrNkw1rV4YatDZk5Aittpcus3PPeC4UoI/tqmJ946uG8AKHTd6CA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -8649,9 +8698,9 @@ } }, "node_modules/electron/node_modules/@types/node": { - "version": "20.17.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", - "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", "license": "MIT", "dependencies": { "undici-types": "~6.19.2" @@ -8951,6 +9000,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -8992,6 +9063,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -9461,12 +9542,6 @@ "node": ">= 6" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, "node_modules/fast-uri": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", @@ -11621,9 +11696,9 @@ "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -11962,6 +12037,21 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -13101,6 +13191,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -13891,9 +13989,9 @@ } }, "node_modules/prettier": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz", - "integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", + "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "bin": { @@ -15106,14 +15204,15 @@ } }, "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -16088,6 +16187,82 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/swagger-ui-dist": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.18.3.tgz", @@ -16317,59 +16492,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -17110,15 +17232,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/username": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", @@ -17187,6 +17300,16 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -17954,9 +18077,9 @@ } }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -17977,9 +18100,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -18082,63 +18205,6 @@ } } }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/webpack-merge": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", @@ -18163,28 +18229,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/whatwg-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -18629,6 +18673,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/zip-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", diff --git a/package.json b/package.json index 2555c4ed3..bd65afbd8 100644 --- a/package.json +++ b/package.json @@ -20,42 +20,51 @@ }, "type": "module", "scripts": { - "start-server": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", - "start-server-safe": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", - "start-server-no-dir": "cross-env TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", - "start-test-server": "npm run switch-server && rimraf ./data-test && cross-env TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 nodemon src/main.ts", - "qstart-server": "npm run switch-server && npm run start-server", - "start-electron": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./electron-main.ts --inspect=5858 .", - "start-electron-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"", - "start-electron-no-dir": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev electron --inspect=5858 .", - "start-electron-no-dir-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"", - "start-electron-prod": "npm run prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .", - "start-electron-prod-nix": "electron-rebuild --version 33.3.1 && npm run prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"", - "start-electron-prod-no-dir": "npm run prepare-dist && cross-env TRILIUM_ENV=dev electron --inspect=5858 .", - "start-electron-prod-no-dir-nix": "electron-rebuild --version 33.3.1 && npm run prepare-dist && cross-env TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"", - "qstart-electron": "npm run switch-electron && npm run start-electron", - "switch-server": "rimraf ./node_modules/better-sqlite3 && npm install", - "switch-electron": "electron-rebuild", - "build-backend-docs": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", - "build-frontend-docs": "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", - "build-docs": "npm run build-backend-docs && npm run build-frontend-docs", - "webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts", - "test-playwright": "playwright test", - "test": "cross-env TRILIUM_DATA_DIR=./data-test vitest", - "test-coverage": "cross-env TRILIUM_DATA_DIR=./data-test vitest --coverage", - "start-electron-forge": "npm run prepare-dist && electron-forge start", - "make-electron": "npm run webpack && npm run prepare-dist && electron-forge make", - "package-electron": "electron-forge package", - "prepare-dist": "rimraf ./dist && tsc && tsx ./bin/copy-dist.ts", - "watch-dist": "tsx ./bin/watch-dist.ts", - "update-build-info": "tsx bin/update-build-info.ts", - "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", - "integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts", - "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", - "generate-document": "cross-env nodemon ./bin/generate_document.ts 1000", - "ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts", - "prettier-check": "prettier . --check", - "prettier-fix": "prettier . --write" + "server:start": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", + "server:start-safe": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", + "server:start-no-dir": "cross-env TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts", + "server:start-test": "npm run server:switch && rimraf ./data-test && cross-env TRILIUM_DATA_DIR=./data-test TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev TRILIUM_PORT=9999 nodemon src/main.ts", + "server:qstart": "npm run server:switch && npm run server:start", + "server:switch": "rimraf ./node_modules/better-sqlite3 && npm install", + + "electron:start": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./electron-main.ts --inspect=5858 .", + "electron:start-no-dir": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev electron --inspect=5858 .", + "electron:start-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"", + "electron:start-nix-no-dir": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-main.ts --inspect=5858 .\"", + "electron:start-prod": "npm run build:prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .", + "electron:start-prod-no-dir": "npm run build:prepare-dist && cross-env TRILIUM_ENV=dev electron --inspect=5858 .", + "electron:start-prod-nix": "electron-rebuild --version 33.3.1 && npm run build:prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"", + "electron:start-prod-nix-no-dir": "electron-rebuild --version 33.3.1 && npm run build:prepare-dist && cross-env TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./dist/electron-main.js --inspect=5858 .\"", + "electron:qstart": "npm run electron:switch && npm run electron:start", + "electron:switch": "electron-rebuild", + + "electron-forge:start": "npm run build:prepare-dist && electron-forge start", + "electron-forge:make": "npm run build:webpack && npm run build:prepare-dist && electron-forge make", + "electron-forge:package": "electron-forge package", + + "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", + "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", + "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:prepare-dist": "rimraf ./dist && tsc && tsx ./bin/copy-dist.ts", + + "test": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest", + "test:coverage": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest --coverage", + "test:playwright": "playwright test", + + "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-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:prettier-check": "prettier . --check", + "dev:prettier-fix": "prettier . --write", + + "chore:update-build-info": "tsx bin/update-build-info.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-openapi": "tsx bin/generate-openapi.js" }, "dependencies": { "@braintree/sanitize-url": "7.1.1", @@ -181,13 +190,12 @@ "@types/fs-extra": "11.0.4", "@types/html": "1.0.4", "@types/ini": "4.1.1", - "@types/jasmine": "5.1.5", "@types/jquery": "3.5.32", "@types/jsdom": "21.1.7", "@types/leaflet-gpx": "1.3.7", "@types/mime-types": "2.1.4", "@types/multer": "1.4.12", - "@types/node": "22.13.1", + "@types/node": "22.13.4", "@types/react": "18.3.18", "@types/safe-compare": "1.1.2", "@types/sanitize-html": "2.13.0", @@ -203,20 +211,21 @@ "@types/yargs": "17.0.33", "@vitest/coverage-v8": "3.0.5", "cross-env": "7.0.3", - "electron": "34.1.1", + "electron": "34.2.0", "esm": "3.2.25", "jsdoc": "4.0.4", "lorem-ipsum": "2.0.8", "nodemon": "3.1.9", - "prettier": "3.5.0", + "prettier": "3.5.1", "rcedit": "4.0.1", "rimraf": "6.0.1", + "swagger-jsdoc": "6.2.8", "tslib": "2.8.1", "tsx": "4.19.2", "typedoc": "0.27.7", "typescript": "5.7.3", "vitest": "3.0.5", - "webpack": "5.97.1", + "webpack": "5.98.0", "webpack-cli": "6.0.1", "webpack-dev-middleware": "7.4.2" } diff --git a/playwright.config.ts b/playwright.config.ts index 9015af28e..e5377db4b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -74,7 +74,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: !process.env.TRILIUM_DOCKER ? { - command: 'npm run integration-mem-db-dev', + command: 'npm run test:integration-mem-db-dev', url: SERVER_URL, reuseExistingServer: !process.env.CI, } : undefined, diff --git a/spec/etapi/app_info.ts b/spec/etapi/app_info.ts index 4bb8c4064..08aade29b 100644 --- a/spec/etapi/app_info.ts +++ b/spec/etapi/app_info.ts @@ -1,8 +1,9 @@ import etapi from "../support/etapi.js"; - +/* TriliumNextTODO: port to Vitest etapi.describeEtapi("app_info", () => { it("get", async () => { const appInfo = await etapi.getEtapi("app-info"); expect(appInfo.clipperProtocolVersion).toEqual("1.0"); }); }); +*/ \ No newline at end of file diff --git a/spec/etapi/backup.ts b/spec/etapi/backup.ts index 78aa01d7f..88e487f9e 100644 --- a/spec/etapi/backup.ts +++ b/spec/etapi/backup.ts @@ -1,8 +1,10 @@ import etapi from "../support/etapi.js"; +/* TriliumNextTODO: port to Vitest etapi.describeEtapi("backup", () => { it("create", async () => { const response = await etapi.putEtapiContent("backup/etapi_test"); expect(response.status).toEqual(204); }); }); +*/ \ No newline at end of file diff --git a/spec/etapi/import.ts b/spec/etapi/import.ts index d36a01c58..fc88a9ef5 100644 --- a/spec/etapi/import.ts +++ b/spec/etapi/import.ts @@ -3,6 +3,7 @@ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; +/* TriliumNextTODO: port to Vitest etapi.describeEtapi("import", () => { // temporarily skip this test since test-export.zip is missing xit("import", async () => { @@ -22,3 +23,4 @@ etapi.describeEtapi("import", () => { expect(content).toContain("test export content"); }); }); +*/ \ No newline at end of file diff --git a/spec/etapi/notes.ts b/spec/etapi/notes.ts index 6efcd88a1..dccb668df 100644 --- a/spec/etapi/notes.ts +++ b/spec/etapi/notes.ts @@ -1,6 +1,7 @@ import crypto from "crypto"; import etapi from "../support/etapi.js"; +/* TriliumNextTODO: port to Vitest etapi.describeEtapi("notes", () => { it("create", async () => { const { note, branch } = await etapi.postEtapi("create-note", { @@ -99,3 +100,4 @@ etapi.describeEtapi("notes", () => { expect(error.message).toEqual(`Note '${note.noteId}' not found.`); }); }); +*/ \ No newline at end of file diff --git a/spec/support/etapi.ts b/spec/support/etapi.ts index d653707d5..307868d7d 100644 --- a/spec/support/etapi.ts +++ b/spec/support/etapi.ts @@ -1,4 +1,5 @@ import type child_process from "child_process"; +import { describe, beforeAll, afterAll } from "vitest"; let etapiAuthToken: string | undefined; diff --git a/src/becca/entities/bbranch.ts b/src/becca/entities/bbranch.ts index 0160ef61b..018f8f2f7 100644 --- a/src/becca/entities/bbranch.ts +++ b/src/becca/entities/bbranch.ts @@ -159,7 +159,7 @@ class BBranch extends AbstractBeccaEntity { } } - if (this.noteId === "root" || this.noteId === cls.getHoistedNoteId()) { + if ((this.noteId === "root" || this.noteId === cls.getHoistedNoteId()) && !this.isWeak) { throw new Error("Can't delete root or hoisted branch/note"); } diff --git a/src/public/app/components/app_context.ts b/src/public/app/components/app_context.ts index 63e3144c2..f54fd88fa 100644 --- a/src/public/app/components/app_context.ts +++ b/src/public/app/components/app_context.ts @@ -223,6 +223,8 @@ export type CommandMappings = { // Geomap deleteFromMap: { noteId: string }, openGeoLocation: { noteId: string, event: JQuery.MouseDownEvent } + + toggleZenMode: CommandData; }; type EventMappings = { @@ -325,6 +327,7 @@ type EventMappings = { }; scrollToEnd: { ntxId: string }; noteTypeMimeChanged: { noteId: string }; + zenModeChanged: { isEnabled: boolean }; }; export type EventListener = { diff --git a/src/public/app/components/root_command_executor.ts b/src/public/app/components/root_command_executor.ts index d93143a5f..eb46e3139 100644 --- a/src/public/app/components/root_command_executor.ts +++ b/src/public/app/components/root_command_executor.ts @@ -178,6 +178,13 @@ export default class RootCommandExecutor extends Component { for (const window of windows) window[action](); } + toggleZenModeCommand() { + const $body = $("body"); + $body.toggleClass("zen"); + const isEnabled = $body.hasClass("zen"); + appContext.triggerEvent("zenModeChanged", { isEnabled }); + } + firstTabCommand() { this.#goToTab(1); } diff --git a/src/public/app/doc_notes/en/User Guide/!!!meta.json b/src/public/app/doc_notes/en/User Guide/!!!meta.json index ffee6a973..f7ee2b545 100644 --- a/src/public/app/doc_notes/en/User Guide/!!!meta.json +++ b/src/public/app/doc_notes/en/User Guide/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.91.5", + "appVersion": "0.91.6-test-250214-024424", "files": [ { "isClone": false, @@ -11,7 +11,7 @@ "title": "User Guide", "notePosition": 20, "prefix": null, - "isExpanded": false, + "isExpanded": true, "type": "text", "mime": "text/html", "attributes": [ @@ -29,22 +29,326 @@ "children": [ { "isClone": false, - "noteId": "wmegHv51MJMd", + "noteId": "jrai60LsOhdk", "notePath": [ "OkOZllzB3fqN", - "wmegHv51MJMd" + "jrai60LsOhdk" ], - "title": "Types of notes", + "title": "Installation", "notePosition": 20, "prefix": null, "isExpanded": false, "type": "text", "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "KPeRqBU7YSAY", + "isInheritable": false, + "position": 10 + }, + { + "type": "label", + "name": "hideChildrenOverview", + "value": "", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "Installation.html", + "attachments": [ + { + "attachmentId": "Mp9RaDeLtETz", + "title": "Fedora_logo.svg", + "role": "image", + "mime": "image/svg+xml", + "position": 10, + "dataFileName": "Installation_Fedora_logo.svg" + } + ], + "dirFileName": "Installation", + "children": [ + { + "isClone": false, + "noteId": "KPeRqBU7YSAY", + "notePath": [ + "OkOZllzB3fqN", + "jrai60LsOhdk", + "KPeRqBU7YSAY" + ], + "title": "On Fedora Linux", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-tux", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "On Fedora Linux.html", + "attachments": [ + { + "attachmentId": "YHD8kyEhgkyZ", + "title": "Screenshot From 2025-02-05 19-30-50.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "On Fedora Linux_Screenshot.png" + }, + { + "attachmentId": "0CpZ5v5xUMia", + "title": "Screenshot From 2025-02-05 19-35-45.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_On Fedora Linux_Screenshot.png" + }, + { + "attachmentId": "9u7nBYvUbXJW", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "On Fedora Linux_image.png" + }, + { + "attachmentId": "ipGBq0moRvF3", + "title": "Screenshot From 2025-02-05 19-36-27.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_On Fedora Linux_Screenshot.png" + }, + { + "attachmentId": "fa83WbDUIB4G", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_On Fedora Linux_image.png" + }, + { + "attachmentId": "kcCWr0YXytOU", + "title": "Screenshot From 2025-02-05 19-30-30.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "3_On Fedora Linux_Screenshot.png" + }, + { + "attachmentId": "YF3JZy1qz7Fq", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_On Fedora Linux_image.png" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "yoAe4jV2yzbd", + "notePath": [ + "OkOZllzB3fqN", + "yoAe4jV2yzbd" + ], + "title": "Features", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", "attributes": [], "format": "html", "attachments": [], - "dirFileName": "Types of notes", + "dirFileName": "Features", "children": [ + { + "isClone": false, + "noteId": "13D1lOc9sqmZ", + "notePath": [ + "OkOZllzB3fqN", + "yoAe4jV2yzbd", + "13D1lOc9sqmZ" + ], + "title": "Export as PDF", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "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": "cvyes4f1Vhmm", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Export as PDF_image.png" + }, + { + "attachmentId": "b3v1pLE6TF1Y", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "2_Export as PDF_image.png" + } + ] + }, + { + "isClone": false, + "noteId": "B3YLYM4erjnW", + "notePath": [ + "OkOZllzB3fqN", + "yoAe4jV2yzbd", + "B3YLYM4erjnW" + ], + "title": "Zen mode", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Zen mode.html", + "attachments": [ + { + "attachmentId": "TS5MS8fQ8Rfr", + "title": "image.png", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "Zen mode_image.png" + }, + { + "attachmentId": "cswryNtIFZHy", + "title": "image.png", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "1_Zen mode_image.png" + }, + { + "attachmentId": "UDk4M7uiTE7w", + "title": "image.png", + "role": "image", + "mime": "image/jpg", + "position": 10, + "dataFileName": "2_Zen mode_image.png" + }, + { + "attachmentId": "sQldbByAmE0k", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "3_Zen mode_image.png" + }, + { + "attachmentId": "DzrJD3hXJwXJ", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "4_Zen mode_image.png" + }, + { + "attachmentId": "HeGfp8wObFO5", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "5_Zen mode_image.png" + }, + { + "attachmentId": "uWsrFwgfypsS", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "6_Zen mode_image.png" + }, + { + "attachmentId": "hX8xmbxgSNFh", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "7_Zen mode_image.png" + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "wmegHv51MJMd", + "notePath": [ + "OkOZllzB3fqN", + "wmegHv51MJMd" + ], + "title": "Note Types", + "notePosition": 70, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "Note Types", + "children": [ + { + "isClone": false, + "noteId": "TTWESa9YFyB4", + "notePath": [ + "OkOZllzB3fqN", + "wmegHv51MJMd", + "TTWESa9YFyB4" + ], + "title": "Mermaid Diagram", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "iconClass", + "value": "bx bx-shape-square", + "isInheritable": false, + "position": 10 + } + ], + "format": "html", + "dataFileName": "Mermaid Diagram.html", + "attachments": [] + }, { "isClone": false, "noteId": "foPEtsL51pD2", @@ -54,7 +358,7 @@ "foPEtsL51pD2" ], "title": "Geo map", - "notePosition": 10, + "notePosition": 20, "prefix": null, "isExpanded": false, "type": "text", @@ -72,7 +376,7 @@ "dataFileName": "Geo map.html", "attachments": [ { - "attachmentId": "viN50n5G4kB0", + "attachmentId": "J0baLTpafs7C", "title": "image.png", "role": "image", "mime": "image/png", @@ -80,7 +384,7 @@ "dataFileName": "Geo map_image.png" }, { - "attachmentId": "eUrcqc8RRuZG", + "attachmentId": "kcYjOvJDFkbS", "title": "image.png", "role": "image", "mime": "image/png", @@ -88,7 +392,7 @@ "dataFileName": "1_Geo map_image.png" }, { - "attachmentId": "1quk4yxJpeHZ", + "attachmentId": "FDP3JzIVSnuJ", "title": "image.png", "role": "image", "mime": "image/png", @@ -96,7 +400,7 @@ "dataFileName": "2_Geo map_image.png" }, { - "attachmentId": "mgwGrtQZjxxb", + "attachmentId": "eUrcqc8RRuZG", "title": "image.png", "role": "image", "mime": "image/png", @@ -104,7 +408,7 @@ "dataFileName": "3_Geo map_image.png" }, { - "attachmentId": "JULizn130rVI", + "attachmentId": "0AwaQMqt3FVA", "title": "image.png", "role": "image", "mime": "image/png", @@ -112,7 +416,7 @@ "dataFileName": "4_Geo map_image.png" }, { - "attachmentId": "kcYjOvJDFkbS", + "attachmentId": "1quk4yxJpeHZ", "title": "image.png", "role": "image", "mime": "image/png", @@ -120,7 +424,7 @@ "dataFileName": "5_Geo map_image.png" }, { - "attachmentId": "ut6vm2aXVfXI", + "attachmentId": "iSpyhQ5Ya6Nk", "title": "image.png", "role": "image", "mime": "image/png", @@ -128,7 +432,7 @@ "dataFileName": "6_Geo map_image.png" }, { - "attachmentId": "0AwaQMqt3FVA", + "attachmentId": "ut6vm2aXVfXI", "title": "image.png", "role": "image", "mime": "image/png", @@ -136,7 +440,7 @@ "dataFileName": "7_Geo map_image.png" }, { - "attachmentId": "gFR2Izzp18LQ", + "attachmentId": "uYdb9wWf5Nuv", "title": "image.png", "role": "image", "mime": "image/png", @@ -144,7 +448,7 @@ "dataFileName": "8_Geo map_image.png" }, { - "attachmentId": "PMqmCbNLlZOG", + "attachmentId": "GhHYO2LteDmZ", "title": "image.png", "role": "image", "mime": "image/png", @@ -152,7 +456,7 @@ "dataFileName": "9_Geo map_image.png" }, { - "attachmentId": "pKdtiq4r0eFY", + "attachmentId": "viN50n5G4kB0", "title": "image.png", "role": "image", "mime": "image/png", @@ -160,7 +464,7 @@ "dataFileName": "10_Geo map_image.png" }, { - "attachmentId": "FXRVvYpOxWyR", + "attachmentId": "mgwGrtQZjxxb", "title": "image.png", "role": "image", "mime": "image/png", @@ -168,7 +472,7 @@ "dataFileName": "11_Geo map_image.png" }, { - "attachmentId": "42AncDs7SSAf", + "attachmentId": "PMqmCbNLlZOG", "title": "image.png", "role": "image", "mime": "image/png", @@ -184,7 +488,7 @@ "dataFileName": "13_Geo map_image.png" }, { - "attachmentId": "FDP3JzIVSnuJ", + "attachmentId": "JULizn130rVI", "title": "image.png", "role": "image", "mime": "image/png", @@ -192,7 +496,7 @@ "dataFileName": "14_Geo map_image.png" }, { - "attachmentId": "GhHYO2LteDmZ", + "attachmentId": "MdC0DpifJwu4", "title": "image.png", "role": "image", "mime": "image/png", @@ -200,7 +504,7 @@ "dataFileName": "15_Geo map_image.png" }, { - "attachmentId": "J0baLTpafs7C", + "attachmentId": "gFR2Izzp18LQ", "title": "image.png", "role": "image", "mime": "image/png", @@ -208,7 +512,7 @@ "dataFileName": "16_Geo map_image.png" }, { - "attachmentId": "uYdb9wWf5Nuv", + "attachmentId": "42AncDs7SSAf", "title": "image.png", "role": "image", "mime": "image/png", @@ -216,7 +520,7 @@ "dataFileName": "17_Geo map_image.png" }, { - "attachmentId": "iSpyhQ5Ya6Nk", + "attachmentId": "pKdtiq4r0eFY", "title": "image.png", "role": "image", "mime": "image/png", @@ -224,7 +528,7 @@ "dataFileName": "18_Geo map_image.png" }, { - "attachmentId": "MdC0DpifJwu4", + "attachmentId": "FXRVvYpOxWyR", "title": "image.png", "role": "image", "mime": "image/png", @@ -237,13 +541,13 @@ }, { "isClone": false, - "noteId": "BDEpqZHDS51s", + "noteId": "BhLd0mxKn0gY", "notePath": [ "OkOZllzB3fqN", - "BDEpqZHDS51s" + "BhLd0mxKn0gY" ], - "title": "Working with notes", - "notePosition": 30, + "title": "Shared notes", + "notePosition": 100, "prefix": null, "isExpanded": false, "type": "text", @@ -251,17 +555,17 @@ "attributes": [], "format": "html", "attachments": [], - "dirFileName": "Working with notes", + "dirFileName": "Shared notes", "children": [ { "isClone": false, - "noteId": "13D1lOc9sqmZ", + "noteId": "1DtQ2mHreeOI", "notePath": [ "OkOZllzB3fqN", - "BDEpqZHDS51s", - "13D1lOc9sqmZ" + "BhLd0mxKn0gY", + "1DtQ2mHreeOI" ], - "title": "Exporting as PDF", + "title": "Serving directly the content of a note", "notePosition": 10, "prefix": null, "isExpanded": false, @@ -269,31 +573,23 @@ "mime": "text/html", "attributes": [], "format": "html", - "dataFileName": "Exporting as PDF.html", + "dataFileName": "Serving directly the content o.html", "attachments": [ { - "attachmentId": "b3v1pLE6TF1Y", + "attachmentId": "2zgbdi7zMieM", "title": "image.png", "role": "image", - "mime": "image/png", + "mime": "image/jpg", "position": 10, - "dataFileName": "Exporting as PDF_image.png" + "dataFileName": "Serving directly the conte.png" }, { - "attachmentId": "xsGM34t8ssKV", + "attachmentId": "DsKDaFOcumH6", "title": "image.png", "role": "image", - "mime": "image/png", + "mime": "image/jpg", "position": 10, - "dataFileName": "1_Exporting as PDF_image.png" - }, - { - "attachmentId": "cvyes4f1Vhmm", - "title": "image.png", - "role": "image", - "mime": "image/png", - "position": 10, - "dataFileName": "2_Exporting as PDF_image.png" + "dataFileName": "1_Serving directly the conte.png" } ] } @@ -301,28 +597,89 @@ }, { "isClone": false, - "noteId": "XUG1egT28FBk", + "noteId": "LTnkDnYmmZ7s", "notePath": [ "OkOZllzB3fqN", - "XUG1egT28FBk" + "LTnkDnYmmZ7s" ], - "title": "Power users", - "notePosition": 50, + "title": "Scripting", + "notePosition": 110, "prefix": null, - "isExpanded": true, + "isExpanded": false, "type": "text", "mime": "text/html", "attributes": [], "format": "html", "attachments": [], - "dirFileName": "Power users", + "dirFileName": "Scripting", + "children": [ + { + "isClone": false, + "noteId": "cTWlUHkiv1fB", + "notePath": [ + "OkOZllzB3fqN", + "LTnkDnYmmZ7s", + "cTWlUHkiv1fB" + ], + "title": "Examples", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "Examples", + "children": [ + { + "isClone": false, + "noteId": "g5Vta7jt3aq3", + "notePath": [ + "OkOZllzB3fqN", + "LTnkDnYmmZ7s", + "cTWlUHkiv1fB", + "g5Vta7jt3aq3" + ], + "title": "Downloading responses from Google Forms", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Downloading responses from Goo.html", + "attachments": [] + } + ] + } + ] + }, + { + "isClone": false, + "noteId": "m4Paddd5qnnQ", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ" + ], + "title": "Advanced usage", + "notePosition": 120, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "Advanced usage", "children": [ { "isClone": false, "noteId": "DtJJ20yEozPA", "notePath": [ "OkOZllzB3fqN", - "XUG1egT28FBk", + "m4Paddd5qnnQ", "DtJJ20yEozPA" ], "title": "Theme development", @@ -349,7 +706,7 @@ "noteId": "5HH79ztN0fZA", "notePath": [ "OkOZllzB3fqN", - "XUG1egT28FBk", + "m4Paddd5qnnQ", "DtJJ20yEozPA", "5HH79ztN0fZA" ], @@ -372,7 +729,7 @@ "dataFileName": "Creating a custom theme.html", "attachments": [ { - "attachmentId": "bn93hwF7C8sR", + "attachmentId": "AJHVfQtIQgJ7", "title": "image.png", "role": "image", "mime": "image/png", @@ -380,7 +737,7 @@ "dataFileName": "Creating a custom theme_im.png" }, { - "attachmentId": "17p6z24yW5eP", + "attachmentId": "gXLyv5KXjfxg", "title": "image.png", "role": "image", "mime": "image/png", @@ -388,7 +745,7 @@ "dataFileName": "1_Creating a custom theme_im.png" }, { - "attachmentId": "gXLyv5KXjfxg", + "attachmentId": "on1gD7BzCWdN", "title": "image.png", "role": "image", "mime": "image/png", @@ -396,7 +753,7 @@ "dataFileName": "2_Creating a custom theme_im.png" }, { - "attachmentId": "AJHVfQtIQgJ7", + "attachmentId": "17p6z24yW5eP", "title": "image.png", "role": "image", "mime": "image/png", @@ -404,7 +761,7 @@ "dataFileName": "3_Creating a custom theme_im.png" }, { - "attachmentId": "on1gD7BzCWdN", + "attachmentId": "K3cdwj8f90m0", "title": "image.png", "role": "image", "mime": "image/png", @@ -412,7 +769,7 @@ "dataFileName": "4_Creating a custom theme_im.png" }, { - "attachmentId": "K3cdwj8f90m0", + "attachmentId": "bn93hwF7C8sR", "title": "image.png", "role": "image", "mime": "image/png", @@ -426,11 +783,11 @@ "noteId": "aH8Dk5aMiq7R", "notePath": [ "OkOZllzB3fqN", - "XUG1egT28FBk", + "m4Paddd5qnnQ", "DtJJ20yEozPA", "aH8Dk5aMiq7R" ], - "title": "Theme base (legacy vs. next)", + "title": "Customize the Next theme", "notePosition": 20, "prefix": null, "isExpanded": false, @@ -438,23 +795,23 @@ "mime": "text/html", "attributes": [], "format": "html", - "dataFileName": "Theme base (legacy vs. next).html", + "dataFileName": "Customize the Next theme.html", "attachments": [ - { - "attachmentId": "u0zkXkD7rGXA", - "title": "image.png", - "role": "image", - "mime": "image/png", - "position": 10, - "dataFileName": "Theme base (legacy vs. nex.png" - }, { "attachmentId": "5z4bC0x0eH0P", "title": "image.png", "role": "image", "mime": "image/png", "position": 10, - "dataFileName": "1_Theme base (legacy vs. nex.png" + "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" } ] }, @@ -463,7 +820,7 @@ "noteId": "pMq6N1oBV9oo", "notePath": [ "OkOZllzB3fqN", - "XUG1egT28FBk", + "m4Paddd5qnnQ", "DtJJ20yEozPA", "pMq6N1oBV9oo" ], @@ -473,12 +830,160 @@ "isExpanded": false, "type": "text", "mime": "text/html", - "attributes": [], + "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "po38jIc0LD2H", + "isInheritable": false, + "position": 10 + } + ], "format": "html", "dataFileName": "Reference.html", "attachments": [] } ] + }, + { + "isClone": false, + "noteId": "po38jIc0LD2H", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "po38jIc0LD2H" + ], + "title": "Custom resource providers", + "notePosition": 20, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "dataFileName": "Custom resource providers.html", + "attachments": [] + }, + { + "isClone": false, + "noteId": "rGq9oI9hWwGf", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "rGq9oI9hWwGf" + ], + "title": "REST API", + "notePosition": 30, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "REST API", + "children": [ + { + "isClone": false, + "noteId": "sztusxU10ADE", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "rGq9oI9hWwGf", + "sztusxU10ADE" + ], + "title": "ETAPI", + "notePosition": 10, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "ETAPI", + "children": [ + { + "isClone": false, + "noteId": "f3xpgx6H01PW", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "rGq9oI9hWwGf", + "sztusxU10ADE", + "f3xpgx6H01PW" + ], + "title": "API Reference", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "webView", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "webViewSrc", + "value": "/etapi/docs", + "isInheritable": false, + "position": 10 + } + ], + "dataFileName": "API Reference.dat", + "attachments": [] + } + ] + }, + { + "isClone": false, + "noteId": "9OiEC6pf3Wfv", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "rGq9oI9hWwGf", + "9OiEC6pf3Wfv" + ], + "title": "Internal API", + "notePosition": 20, + "prefix": null, + "isExpanded": true, + "type": "text", + "mime": "text/html", + "attributes": [], + "format": "html", + "attachments": [], + "dirFileName": "Internal API", + "children": [ + { + "isClone": false, + "noteId": "7uB5k0iCmOtZ", + "notePath": [ + "OkOZllzB3fqN", + "m4Paddd5qnnQ", + "rGq9oI9hWwGf", + "9OiEC6pf3Wfv", + "7uB5k0iCmOtZ" + ], + "title": "API Reference", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "webView", + "mime": "", + "attributes": [ + { + "type": "label", + "name": "webViewSrc", + "value": "/api/docs", + "isInheritable": false, + "position": 10 + } + ], + "dataFileName": "API Reference.dat", + "attachments": [] + } + ] + } + ] } ] } diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Custom resource providers.html b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Custom resource providers.html new file mode 100644 index 000000000..e847d0808 --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Custom resource providers.html @@ -0,0 +1,56 @@ + + + + + + + + Custom resource providers + + + +
+

Custom resource providers

+ +
+

A custom resource provider allows any file imported into Trilium (images, + fonts, stylesheets) to be publicly accessible via a URL.

+

A potential use case for this is to add embed a custom font alongside + a theme.

+

Steps for creating a custom resource provider

+
    +
  1. Import a file such as an image or a font into Trilium by drag & drop.
  2. +
  3. Select the file and go to the Owned Attributes section.
  4. +
  5. Add the label #customResourceProvider=hello.
  6. +
  7. To test if it is working, use a browser to navigate to <protocol>://<host>/custom/hello (where <protocol> is + either http or https based on your setup, and <host> is + the host or IP to your Trilium server instance). If you are running the + TriliumNext application without a server, use http://localhost:37840 as + the base URL.
  8. +
  9. If everything went well, at the previous step the browser should have + downloaded the file uploaded in the first step.
  10. +
+

Instead of hello, the name can be:

+
    +
  • A path, such as fonts/Roboto.ttf, which would be accessible + via <host>/custom/fonts/Roboto.ttf.
  • +
  • As a more advanced use case, a regular expression to match multiple routes, + such as hello/.* which will be accessible via /custom/hello/1, /custom/hello/2, /custom/hello/world, + etc.
  • +
+

Using it in a theme

+

For example, if you have a custom font to be imported by the theme, first + upload a font file into Trilium and assign it the #customResourceProvider=fonts/myfont.ttf attribute.

+

Then modify the theme CSS to point to:

@font-face {
+	font-family: customFont;
+	src: url("/custom/fonts/myfont.ttf");
+}
+
+div {
+	font-family: customFont;
+}
+
+
+ + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/REST API/ETAPI/API Reference.dat b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/REST API/ETAPI/API Reference.dat new file mode 100644 index 000000000..e69de29bb diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/REST API/Internal API/API Reference.dat b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/REST API/Internal API/API Reference.dat new file mode 100644 index 000000000..e69de29bb diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/2_Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/1_Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/2_Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/1_Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Theme base (legacy vs. nex.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/1_Customize the Next theme_i.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Theme base (legacy vs. nex.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/1_Customize the Next theme_i.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/4_Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/2_Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/4_Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/2_Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/1_Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/3_Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/1_Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/3_Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/5_Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/4_Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/5_Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/4_Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/5_Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/5_Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Creating a custom theme.html b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Creating a custom theme.html similarity index 87% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Creating a custom theme.html rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Creating a custom theme.html index e4d1eeda4..d2dc5fea7 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Creating a custom theme.html +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Creating a custom theme.html @@ -19,7 +19,7 @@ keep them into one place.

As such, the first step is to create a new note to gather all the themes.

- +

Step 2. Create the theme

@@ -32,7 +32,7 @@
-
@@ -42,7 +42,7 @@
-
@@ -51,7 +51,7 @@
-
@@ -72,15 +72,15 @@

Refresh the application (Ctrl+Shift+R is a good way to do so) and go to settings. You should see the newly created theme:

- +

Afterwards the application will refresh itself with the new theme:

- +

Do note that the theme will be based off of the legacy theme. To override that and base the theme on the new TriliumNext theme, see: Theme base (legacy vs. next) + href="Customize%20the%20Next%20theme.html">Theme base (legacy vs. next)

Step 5. Making changes

Simply go back to the note and change according to needs. To apply the diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/3_Creating a custom theme_im.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Creating a custom theme_im.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/3_Creating a custom theme_im.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Creating a custom theme_im.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Theme base (legacy vs. next).html b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Customize the Next theme.html similarity index 78% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Theme base (legacy vs. next).html rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Customize the Next theme.html index d392deed4..cd0a47f0b 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Theme base (legacy vs. next).html +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Customize the Next theme.html @@ -5,20 +5,20 @@ - Theme base (legacy vs. next) + Customize the Next theme

-

Theme base (legacy vs. next)

+

Customize the Next theme

By default, any custom theme will be based on the legacy light theme. - To change the TriliumNext theme instead, add the #appThemeBase=next attribute + To use the TriliumNext theme instead, add the #appThemeBase=next attribute onto the existing theme. The appTheme attribute must also be present.

- +

When appThemeBase is set to next it will use the “TriliumNext (auto)” theme. Any other value is ignored and will use the diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/1_Theme base (legacy vs. nex.png b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Customize the Next theme_i.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/1_Theme base (legacy vs. nex.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Customize the Next theme_i.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Reference.html b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Reference.html similarity index 93% rename from src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Reference.html rename to src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Reference.html index 13af6494e..e858513c1 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Power users/Theme development/Reference.html +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Advanced usage/Theme development/Reference.html @@ -122,6 +122,11 @@ body.electron:not(.native-titlebar) { height: 3px; background-color: var(--workspace-tab-background-color); }

+

Custom fonts

+

Currently the only way to include a custom font is to use Custom resource providers. + Basically import a font into Trilium and assign it #customResourceProvider=fonts/myfont.ttf and + then import the font in CSS via /custom/fonts/myfont.ttf.

diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/2_Exporting as PDF_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/1_Export as PDF_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/2_Exporting as PDF_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Features/1_Export as PDF_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/1_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/1_Zen mode_image.png new file mode 100644 index 000000000..c1a3e8124 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/1_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/Exporting as PDF_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/2_Export as PDF_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/Exporting as PDF_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Features/2_Export as PDF_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/2_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/2_Zen mode_image.png new file mode 100644 index 000000000..0c9f26de0 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/2_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/3_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/3_Zen mode_image.png new file mode 100644 index 000000000..530d76afb Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/3_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/4_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/4_Zen mode_image.png new file mode 100644 index 000000000..d2698b715 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/4_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/5_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/5_Zen mode_image.png new file mode 100644 index 000000000..9f693475f Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/5_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/6_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/6_Zen mode_image.png new file mode 100644 index 000000000..c2543960a Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/6_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/7_Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/7_Zen mode_image.png new file mode 100644 index 000000000..d23b671da Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/7_Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/Exporting as PDF.html b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Export as PDF.html similarity index 78% rename from src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/Exporting as PDF.html rename to src/public/app/doc_notes/en/User Guide/User Guide/Features/Export as PDF.html index 045180d5f..1c4337360 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/Exporting as PDF.html +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Export as PDF.html @@ -5,32 +5,35 @@ - Exporting as PDF + Export as PDF
-

Exporting as PDF

+

Export as PDF

-
- +
+ +
Screenshot of the note contextual menu indicating the “Export as PDF” + option.

On the desktop application of Trilium it is possible to export a note as PDF. On the server or PWA (mobile), the option is not available due to technical constraints and it will be hidden.

To print a note, select the - button to the right of the note and select Export as PDF.

Afterwards you will be prompted to select where to save the PDF file. - Upon confirmation, the resulting PDF will be opened automatically.

+ Upon confirmation, the resulting PDF will be opened automatically using + the default/system application configured for PDFs.

Should you encounter any visual issues in the resulting PDF file (e.g. a table does not fit properly, there is cut off text, etc.) feel free to report the issue. In this case, it's best to offer a sample note (click on the - button, select Export note → This note and all of its descendants → HTML + button, select Export note → This note and all of its descendants → HTML in ZIP archive). Make sure not to accidentally leak any personal information.

Landscape mode

When exporting to PDF, there are no customizable settings such as page diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/1_Exporting as PDF_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Export as PDF_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Working with notes/1_Exporting as PDF_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Features/Export as PDF_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode.html b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode.html new file mode 100644 index 000000000..9792a7d32 --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode.html @@ -0,0 +1,73 @@ + + + + + + + + Zen mode + + + +

+

Zen mode

+ +
+
+ +
Screenshot of Zen Mode activated on a Windows 11 system with native title + bar off and background effects on.
+
+

When Zen Mode is activated (pictured on the side), most of the user interface + of Trilium is hidden away in order to be able to focus on the content, + whether it's for reading or writing.

+
+ +
Screenshot of the Zen Mode option in the global menu.
+
+

Activating & deactivating

+

The Zen Mode can be activated by accessing the global menu and selecting + the “Zen Mode” option:

+

Aside from the global menu, it's also possible to activate this mode by + using a keyboard shortcut which is Alt+Z by default. Look for toggleZenMode in + the shortcut configuration.

+

Once Zen Mode is activated, all the UI elements of the application will + be hidden away, including the global menu. In that case, the Zen Mode can + be deactivated either by pressing the + icon in the top-right corner of the window or by pressing the keyboard + combination again.

+

Do note that, by design, activating or deactivating the Zen Mode applies + only to the current window. Restarting the application will also disable + the Zen Mode.

+

Moving the window around

+

If “Native title bar” is activated, then the operating system's default + title bar can be used to drag the window around. If deactivated, the window + can still be moved by dragging the mouse across the top part of the window + where the note titles are.

+
+ +
Screenshot of two notes side-by-side while Zen Mode is active, on Windows + 11 with background effects off.
+
+

Split windows and tabs

+

Tabs are completely hidden, however it's still possible to use keyboard + shortcuts such as firstTab (Ctrl+1 by default), secondTab (Ctrl+2 + by default). There are also some newer shortcuts such as activateNextTab (Ctrl+Tab) + or activatePreviousTab (Ctrl+Shift+Tab) that allow easy navigation, + however make sure that they are configured properly in the settings.

+

For the split view of notes, there are no keyboard shortcuts at the time + of writing, but it's still possible to have them in Zen Mode by creating + the split while the Zen Mode is off and then reactivating it afterwards.

+

 

+

 

+
+
+ + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode_image.png new file mode 100644 index 000000000..675a068aa Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Features/Zen mode_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html b/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html new file mode 100644 index 000000000..058f84feb --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Installation.html @@ -0,0 +1,38 @@ + + + + + + + + Installation + + + +
+

Installation

+ +
+

Desktop application

+
+ + + + + + +
+
+ +
+

Fedora +

+
+
+

Self-hosted server

+
+
+ + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_Screenshot.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_Screenshot.png new file mode 100644 index 000000000..d519f9b75 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_Screenshot.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_image.png new file mode 100644 index 000000000..f3fcbfcb5 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/1_On Fedora Linux_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_Screenshot.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_Screenshot.png new file mode 100644 index 000000000..d7370a422 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_Screenshot.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_image.png new file mode 100644 index 000000000..b69b71d3c Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/2_On Fedora Linux_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/3_On Fedora Linux_Screenshot.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/3_On Fedora Linux_Screenshot.png new file mode 100644 index 000000000..6c3589ca4 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/3_On Fedora Linux_Screenshot.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux.html b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux.html new file mode 100644 index 000000000..88ad36272 --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux.html @@ -0,0 +1,82 @@ + + + + + + + + On Fedora Linux + + + +
+

On Fedora Linux

+ +
+

First, download a release from GitHub.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1 +
+ +
+
In your file explorer, look for the .rpm file of TriliumNext.
2 +
+ +
+
Right click the file and select Open With Software Install.
3 +
+ +
+
+

GNOME Software will appear. Press the + .

+

You will be asked to confirm the action by entering your password.

+

After confirmation the software will start installing.

+

Once it's done the “Install” button will turn into + .

+
4 +
+ +
+
After installation, the application will be available in the GNOME overview + section.
+
+
+
+ + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_Screenshot.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_Screenshot.png new file mode 100644 index 000000000..fb0ce66ef Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_Screenshot.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_image.png new file mode 100644 index 000000000..5791f83ef Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Installation/On Fedora Linux_image.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Installation_Fedora_logo.svg b/src/public/app/doc_notes/en/User Guide/User Guide/Installation_Fedora_logo.svg new file mode 100644 index 000000000..1ad9c15c8 --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Installation_Fedora_logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/10_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/10_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/3_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/11_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/3_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/11_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/9_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/12_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/9_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/12_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/13_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/13_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/13_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/13_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/4_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/14_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/4_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/14_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/19_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/15_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/19_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/15_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/8_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/16_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/8_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/16_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/12_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/17_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/12_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/17_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/10_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/18_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/10_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/18_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/11_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/19_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/11_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/19_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/5_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/1_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/5_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/1_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/14_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/2_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/14_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/2_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/1_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/3_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/1_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/3_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/7_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/4_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/7_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/4_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/2_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/5_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/2_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/5_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/18_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/6_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/18_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/6_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/6_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/7_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/6_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/7_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/17_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/8_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/17_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/8_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/15_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/9_Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/15_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/9_Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/Geo map.html b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Geo map.html similarity index 80% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/Geo map.html rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Geo map.html index bcaf6d7b7..65d8ee517 100644 --- a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/Geo map.html +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Geo map.html @@ -26,7 +26,7 @@ 1
-
@@ -36,7 +36,7 @@ 2
-
@@ -51,8 +51,8 @@
  • Use the mouse wheel, two-finger gesture on a touchpad or the +/- buttons on the top-left to adjust the zoom.
  • -

    The position on the map and the zoom are saved inside the map note. When - visting again the note it will restore this position.

    +

    The position on the map and the zoom are saved inside the map note and + restored when visiting again the note.

    Adding a marker using the map

    @@ -69,18 +69,18 @@

    To create a marker, first navigate to the desired point on the map. Then press the button on the top-right of the map.

    + src="5_Geo map_image.png" width="72" height="66">button on the top-right of the map.

    If the button is not visible, make sure the button section is visible by pressing the chevron button ( ) in the top-right of the map.

    + src="11_Geo map_image.png" width="72" height="66">) in the top-right of the map.

    2
    -

     

    @@ -96,7 +96,7 @@
    3
    -

     

    @@ -107,7 +107,7 @@
    4
    -

     

    @@ -118,6 +118,15 @@
    +

    How the location of the markers is stored

    +

    The location of a marker is stored in the #geolocation attribute + of the child notes:

    +
    + +
    +

    This value can be added manually if needed. The value of the attribute + is made up of the latitude and longitude separated by a comma.

    Repositioning markers

    It's possible to reposition existing markers by simply drag and dropping them to the new destination.

    @@ -125,13 +134,30 @@

    If moved by mistake, there is currently no way to undo the change. If the mouse was not yet released, it's possible to force a refresh of the page (Ctrl+R or Meta+R) to cancel it.

    -

    Adding the geolocation manually

    -

    The location of a marker is stored in the #geolocation attribute - of the child notes:

    -
    - -
    +

    Interaction with the markers

    +
      +
    • Hovering over a marker will display the content of the note it belongs + to. +
        +
      • Clicking on the note title in the tooltip will navigate to the note in + the current view.
      • +
      +
    • +
    • Middle-clicking the marker will open the note in a new tab.
    • +
    • Right-clicking the marker will open a contextual menu allowing: +
        +
      • Opening the note in a new tab, split or window.
      • +
      • Opening the location using an external application (if the operating system + supports it).
      • +
      • Removing the marker from the map, which will remove the #geolocation attribute + of the note. To add it back again, the coordinates have to be manually + added back in.
      • +
      +
    • +
    +

    Adding the coordinates manually

    +

    In a nutshell, create a child note and set the #geolocation attribute + to the coordinates.

    The value of the attribute is made up of the latitude and longitude separated by a comma.

    Adding from Google Maps

    @@ -142,7 +168,7 @@ 1
    -
    @@ -159,7 +185,7 @@ 2
    -
    @@ -173,7 +199,7 @@ 3
    -
    @@ -199,7 +225,7 @@ 1
    -
    @@ -224,7 +250,7 @@ 3
    -
    @@ -249,7 +275,7 @@ 1
    -
    @@ -260,7 +286,7 @@ 2
    -
    @@ -271,7 +297,7 @@ 3
    -
    diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/16_Geo map_image.png b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Geo map_image.png similarity index 100% rename from src/public/app/doc_notes/en/User Guide/User Guide/Types of notes/16_Geo map_image.png rename to src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Geo map_image.png diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html new file mode 100644 index 000000000..bbe5d76cc --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagram.html @@ -0,0 +1,19 @@ + + + + + + + + Mermaid Diagram + + + +
    +

    Mermaid Diagram

    + +
    +
    + + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Scripting/Examples/Downloading responses from Goo.html b/src/public/app/doc_notes/en/User Guide/User Guide/Scripting/Examples/Downloading responses from Goo.html new file mode 100644 index 000000000..7e4955655 --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Scripting/Examples/Downloading responses from Goo.html @@ -0,0 +1,19 @@ + + + + + + + + Downloading responses from Google Forms + + + +
    +

    Downloading responses from Google Forms

    + +
    +
    + + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/1_Serving directly the conte.png b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/1_Serving directly the conte.png new file mode 100644 index 000000000..bab0f97f5 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/1_Serving directly the conte.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the conte.png b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the conte.png new file mode 100644 index 000000000..981a43146 Binary files /dev/null and b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the conte.png differ diff --git a/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the content o.html b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the content o.html new file mode 100644 index 000000000..d64d24e2e --- /dev/null +++ b/src/public/app/doc_notes/en/User Guide/User Guide/Shared notes/Serving directly the content o.html @@ -0,0 +1,60 @@ + + + + + + + + Serving directly the content of a note + + + +
    +

    Serving directly the content of a note

    + +
    +

    When accessing a shared note, Trilium will render it as a web page. Sometimes + it's desirable to serve the content directly so that it can be used in + a script or downloaded by the user.

    +
    + + + + + + + + + + + +
    +
    + +
    A note displayed as a web page (HTML)
    +
    +
    +
    + +
    A note displayed as a raw format
    +
    +
    +
    +

    By adding an attribute to the note

    +

    Simply add the #shareRaw attribute and the note will always + be rendered raw when accessed from the share URL.

    +

    By altering the URL

    +

    Append ?raw to the URL to display a note in its raw format + regardless of whether the #shareRaw attribute is added on the + note.

    +

    + +

    +

     

    +
    +
    + + + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/index.html b/src/public/app/doc_notes/en/User Guide/index.html index bac353ead..fc49dc742 100644 --- a/src/public/app/doc_notes/en/User Guide/index.html +++ b/src/public/app/doc_notes/en/User Guide/index.html @@ -6,6 +6,6 @@ - + \ No newline at end of file diff --git a/src/public/app/doc_notes/en/User Guide/navigation.html b/src/public/app/doc_notes/en/User Guide/navigation.html index ddf57d958..77cf9ca53 100644 --- a/src/public/app/doc_notes/en/User Guide/navigation.html +++ b/src/public/app/doc_notes/en/User Guide/navigation.html @@ -9,34 +9,82 @@
    `; +interface RevisionItem { + noteId: string; + revisionId: string; + dateLastEdited: string; + contentLength: number; + type: NoteType; + title: string; + isProtected: boolean; + mime: string; +} + +interface FullRevision { + content: string; + mime: string; +} + export default class RevisionsDialog extends BasicWidget { + + private revisionItems: RevisionItem[]; + private note: FNote | null; + private revisionId: string | null; + + //@ts-ignore + private modal: bootstrap.Modal; + //@ts-ignore + private listDropdown: bootstrap.Dropdown; + + private $list!: JQuery; + private $listDropdown!: JQuery; + private $content!: JQuery; + private $title!: JQuery; + private $titleButtons!: JQuery; + private $eraseAllRevisionsButton!: JQuery; + private $maximumRevisions!: JQuery; + private $snapshotInterval!: JQuery; + private $revisionSettingsButton!: JQuery; + constructor() { super(); @@ -87,11 +125,13 @@ export default class RevisionsDialog extends BasicWidget { doRender() { this.$widget = $(TPL); + //@ts-ignore this.modal = bootstrap.Modal.getOrCreateInstance(this.$widget); this.$list = this.$widget.find(".revision-list"); this.$listDropdown = this.$widget.find(".revision-list-dropdown"); - this.listDropdown = bootstrap.Dropdown.getOrCreateInstance(this.$listDropdown); + //@ts-ignore + this.listDropdown = bootstrap.Dropdown.getOrCreateInstance(this.$listDropdown, { autoClose: false }); this.$content = this.$widget.find(".revision-content"); this.$title = this.$widget.find(".revision-title"); this.$titleButtons = this.$widget.find(".revision-title-buttons"); @@ -102,26 +142,18 @@ export default class RevisionsDialog extends BasicWidget { this.listDropdown.show(); this.$listDropdown.parent().on("hide.bs.dropdown", (e) => { - // Prevent closing dropdown by pressing ESC and clicking outside - e.preventDefault(); + this.modal.hide(); }); - document.addEventListener( - "keydown", - (e) => { - // Close the revision dialog when revision element is focused and ESC is pressed - if (e.key === "Escape" || e.target.classList.contains(["dropdown-item", "active"])) { - this.modal.hide(); - } - }, - true - ); - this.$widget.on("shown.bs.modal", () => { this.$list.find(`[data-revision-id="${this.revisionId}"]`).trigger("focus"); }); this.$eraseAllRevisionsButton.on("click", async () => { + if (!this.note) { + return; + } + const text = t("revisions.confirm_delete_all"); if (await dialogService.confirm(text)) { @@ -147,18 +179,22 @@ export default class RevisionsDialog extends BasicWidget { } async showRevisionsEvent({ noteId = appContext.tabManager.getActiveContextNoteId() }) { + if (!noteId) { + return; + } + utils.openDialog(this.$widget); await this.loadRevisions(noteId); } - async loadRevisions(noteId) { + async loadRevisions(noteId: string) { this.$list.empty(); this.$content.empty(); this.$titleButtons.empty(); this.note = appContext.tabManager.getActiveContextNote(); - this.revisionItems = await server.get(`notes/${noteId}/revisions`); + this.revisionItems = await server.get(`notes/${noteId}/revisions`); for (const item of this.revisionItems) { this.$list.append( @@ -184,9 +220,9 @@ export default class RevisionsDialog extends BasicWidget { // Show the footer of the revisions dialog this.$snapshotInterval.text(t("revisions.snapshot_interval", { seconds: options.getInt("revisionSnapshotTimeInterval") })); - let revisionsNumberLimit = parseInt(this.note.getLabelValue("versioningLimit") ?? ""); + let revisionsNumberLimit: number | string = parseInt(this.note?.getLabelValue("versioningLimit") ?? ""); if (!Number.isInteger(revisionsNumberLimit)) { - revisionsNumberLimit = parseInt(options.getInt("revisionSnapshotNumberLimit")); + revisionsNumberLimit = options.getInt("revisionSnapshotNumberLimit") ?? 0; } if (revisionsNumberLimit === -1) { revisionsNumberLimit = "∞"; @@ -198,6 +234,9 @@ export default class RevisionsDialog extends BasicWidget { const revisionId = this.$list.find(".active").attr("data-revision-id"); const revisionItem = this.revisionItems.find((r) => r.revisionId === revisionId); + if (!revisionItem) { + return; + } this.$title.html(revisionItem.title); @@ -206,7 +245,7 @@ export default class RevisionsDialog extends BasicWidget { await this.renderContent(revisionItem); } - renderContentButtons(revisionItem) { + renderContentButtons(revisionItem: RevisionItem) { this.$titleButtons.empty(); const $restoreRevisionButton = $(``); @@ -252,13 +291,13 @@ export default class RevisionsDialog extends BasicWidget { } } - async renderContent(revisionItem) { + async renderContent(revisionItem: RevisionItem) { this.$content.empty(); - const fullRevision = await server.get(`revisions/${revisionItem.revisionId}`); + const fullRevision = await server.get(`revisions/${revisionItem.revisionId}`); if (revisionItem.type === "text") { - this.$content.html(fullRevision.content); + this.$content.html(`
    ${fullRevision.content}
    `); if (this.$content.find("span.math-tex").length > 0) { await libraryLoader.requireLibrary(libraryLoader.KATEX); @@ -266,11 +305,15 @@ export default class RevisionsDialog extends BasicWidget { renderMathInElement(this.$content[0], { trust: true }); } } else if (revisionItem.type === "code") { - this.$content.html($("
    ").text(fullRevision.content));
    +            this.$content.html($("
    ")
    +                .text(fullRevision.content).html());
             } else if (revisionItem.type === "image") {
                 if (fullRevision.mime === "image/svg+xml") {
                     let encodedSVG = encodeURIComponent(fullRevision.content); //Base64 of other format images may be embedded in svg
    -                this.$content.html($("").attr("src", `data:${fullRevision.mime};utf8,${encodedSVG}`).css("max-width", "100%").css("max-height", "100%"));
    +                this.$content.html($("")
    +                    .attr("src", `data:${fullRevision.mime};utf8,${encodedSVG}`)
    +                    .css("max-width", "100%")
    +                    .css("max-height", "100%").html());
                 } else {
                     this.$content.html(
                         $("")
    @@ -278,13 +321,16 @@ export default class RevisionsDialog extends BasicWidget {
                             // as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be uploaded as a new note
                             .attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
                             .css("max-width", "100%")
    -                        .css("max-height", "100%")
    +                        .css("max-height", "100%").html()
                     );
                 }
             } else if (revisionItem.type === "file") {
                 const $table = $("")
    -                .append($("").append($("").append($("")
    +                    .append(
    +                        $("").append($("
    ").text(t("revisions.mime")), $("").text(revisionItem.mime))) - .append($("
    ").text(t("revisions.file_size")), $("").text(utils.formatSize(revisionItem.contentLength)))); + .append($("
    ").text(t("revisions.mime")), + $("").text(revisionItem.mime))) + .append($("
    ").text(t("revisions.file_size")), $("").text(utils.formatSize(revisionItem.contentLength)))); if (fullRevision.content) { $table.append( @@ -294,15 +340,23 @@ export default class RevisionsDialog extends BasicWidget { ); } - this.$content.html($table); + this.$content.html($table.html()); } else if (["canvas", "mindMap"].includes(revisionItem.type)) { const encodedTitle = encodeURIComponent(revisionItem.title); - this.$content.html($("").attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`).css("max-width", "100%")); + this.$content.html( + $("") + .attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`) + .css("max-width", "100%") + .html()); } else if (revisionItem.type === "mermaid") { const encodedTitle = encodeURIComponent(revisionItem.title); - this.$content.html($("").attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`).css("max-width", "100%")); + this.$content.html( + $("") + .attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`) + .css("max-width", "100%") + .html()); this.$content.append($("
    ").text(fullRevision.content));
             } else {
    diff --git a/src/public/app/widgets/ribbon_widgets/book_properties.js b/src/public/app/widgets/ribbon_widgets/book_properties.ts
    similarity index 80%
    rename from src/public/app/widgets/ribbon_widgets/book_properties.js
    rename to src/public/app/widgets/ribbon_widgets/book_properties.ts
    index efc49d142..6bb66dd1f 100644
    --- a/src/public/app/widgets/ribbon_widgets/book_properties.js
    +++ b/src/public/app/widgets/ribbon_widgets/book_properties.ts
    @@ -1,6 +1,8 @@
     import NoteContextAwareWidget from "../note_context_aware_widget.js";
     import attributeService from "../../services/attributes.js";
     import { t } from "../../services/i18n.js";
    +import type FNote from "../../entities/fnote.js";
    +import type { EventData } from "../../components/app_context.js";
     
     const TPL = `
     
    @@ -9,7 +11,7 @@ const TPL = ` padding: 12px 12px 6px 12px; display: flex; } - + .book-properties-widget > * { margin-right: 15px; } @@ -17,19 +19,19 @@ const TPL = `
    ${t("book_properties.view_type")}:    - +
    - + @@ -37,13 +39,18 @@ const TPL = ` class="expand-children-button btn btn-sm" title="${t("book_properties.expand_all_children")}"> - + ${t("book_properties.expand")}
    `; export default class BookPropertiesWidget extends NoteContextAwareWidget { + + private $viewTypeSelect!: JQuery; + private $expandChildrenButton!: JQuery; + private $collapseAllButton!: JQuery; + get name() { return "bookProperties"; } @@ -70,11 +77,15 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { this.contentSized(); this.$viewTypeSelect = this.$widget.find(".view-type-select"); - this.$viewTypeSelect.on("change", () => this.toggleViewType(this.$viewTypeSelect.val())); + this.$viewTypeSelect.on("change", () => this.toggleViewType(String(this.$viewTypeSelect.val()))); this.$expandChildrenButton = this.$widget.find(".expand-children-button"); this.$expandChildrenButton.on("click", async () => { - if (!this.note.isLabelTruthy("expanded")) { + if (!this.noteId || !this.note) { + return; + } + + if (!this.note?.isLabelTruthy("expanded")) { await attributeService.addLabel(this.noteId, "expanded"); } @@ -83,6 +94,10 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { this.$collapseAllButton = this.$widget.find(".collapse-all-button"); this.$collapseAllButton.on("click", async () => { + if (!this.noteId || !this.note) { + return; + } + // owned is important - we shouldn't remove inherited expanded labels for (const expandedAttr of this.note.getOwnedLabels("expanded")) { await attributeService.removeAttributeById(this.noteId, expandedAttr.attributeId); @@ -92,7 +107,11 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { }); } - async refreshWithNote(note) { + async refreshWithNote(note: FNote) { + if (!this.note) { + return; + } + const viewType = this.note.getLabelValue("viewType") || "grid"; this.$viewTypeSelect.val(viewType); @@ -101,7 +120,11 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { this.$collapseAllButton.toggle(viewType === "list"); } - async toggleViewType(type) { + async toggleViewType(type: string) { + if (!this.noteId) { + return; + } + if (type !== "list" && type !== "grid") { throw new Error(t("book_properties.invalid_view_type", { type })); } @@ -109,7 +132,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { await attributeService.setLabel(this.noteId, "viewType", type); } - entitiesReloadedEvent({ loadResults }) { + entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { if (loadResults.getAttributeRows().find((attr) => attr.noteId === this.noteId && attr.name === "viewType")) { this.refresh(); } diff --git a/src/public/app/widgets/type_widgets/editable_code.js b/src/public/app/widgets/type_widgets/editable_code.ts similarity index 82% rename from src/public/app/widgets/type_widgets/editable_code.js rename to src/public/app/widgets/type_widgets/editable_code.ts index 78a266a57..389eb7ae3 100644 --- a/src/public/app/widgets/type_widgets/editable_code.js +++ b/src/public/app/widgets/type_widgets/editable_code.ts @@ -1,3 +1,5 @@ +import type { EventData } from "../../components/app_context.js"; +import type FNote from "../../entities/fnote.js"; import { t } from "../../services/i18n.js"; import keyboardActionService from "../../services/keyboard_actions.js"; import options from "../../services/options.js"; @@ -10,7 +12,7 @@ const TPL = ` position: relative; height: 100%; } - + .note-detail-code-editor { min-height: 50px; height: 100%; @@ -21,6 +23,9 @@ const TPL = ` `; export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget { + + private $editor!: JQuery; + static getType() { return "editableCode"; } @@ -50,11 +55,11 @@ export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget { this.codeEditor.on("change", () => this.spacedUpdate.scheduleUpdate()); } - async doRefresh(note) { - const blob = await this.note.getBlob(); + async doRefresh(note: FNote) { + const blob = await this.note?.getBlob(); await this.spacedUpdate.allowUpdateWithoutChange(() => { - this._update(note, blob.content); + this._update(note, blob?.content); }); this.show(); @@ -66,7 +71,7 @@ export default class EditableCodeTypeWidget extends AbstractCodeTypeWidget { }; } - async executeWithCodeEditorEvent({ resolve, ntxId }) { + async executeWithCodeEditorEvent({ resolve, ntxId }: EventData<"executeWithCodeEditor">) { if (!this.isNoteContext(ntxId)) { return; } diff --git a/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts b/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts index 876cf36d3..99161f9a9 100644 --- a/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts +++ b/src/public/app/widgets/type_widgets/options/other/note_erasure_timeout.ts @@ -11,26 +11,68 @@ const TPL = `

    ${t("note_erasure_timeout.note_erasure_description")}

    - - + +
    + + + +

    ${t("note_erasure_timeout.manual_erasing_description")}

    - + + + `; export default class NoteErasureTimeoutOptions extends OptionsWidget { - private $eraseEntitiesAfterTimeInSeconds!: JQuery; - private $eraseDeletedNotesButton!: JQuery; + private $eraseEntitiesAfterTime!: JQuery; + private $eraseEntitiesAfterTimeScale!: JQuery; + private $eraseDeletedNotesButton!: JQuery; + private eraseEntitiesAfterTimeInSeconds!: string | number; doRender() { this.$widget = $(TPL); - this.$eraseEntitiesAfterTimeInSeconds = this.$widget.find(".erase-entities-after-time-in-seconds"); - this.$eraseEntitiesAfterTimeInSeconds.on("change", () => this.updateOption("eraseEntitiesAfterTimeInSeconds", this.$eraseEntitiesAfterTimeInSeconds.val())); + this.$eraseEntitiesAfterTime = this.$widget.find("#erase-entities-after-time"); + this.$eraseEntitiesAfterTimeScale = this.$widget.find("#erase-entities-after-time-scale"); - this.$eraseDeletedNotesButton = this.$widget.find(".erase-deleted-notes-now-button"); + this.$eraseEntitiesAfterTime.on("change", () => { + const time = this.$eraseEntitiesAfterTime.val(); + const timeScale = this.$eraseEntitiesAfterTimeScale.val(); + + if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return; + + this.eraseEntitiesAfterTimeInSeconds = this.convertTime(time, timeScale).toOption(); + this.updateOption("eraseEntitiesAfterTimeInSeconds", this.eraseEntitiesAfterTimeInSeconds); + + }); + + this.$eraseEntitiesAfterTimeScale.on("change", () => { + + const timeScale = this.$eraseEntitiesAfterTimeScale.val(); + + if (!this.handleTimeValidation() || typeof timeScale !== "string") return; + + //calculate the new displayed value + const displayedTime = this.convertTime(this.eraseEntitiesAfterTimeInSeconds, timeScale).toDisplay(); + + this.updateOption("eraseEntitiesAfterTimeScale", timeScale); + this.$eraseEntitiesAfterTime.val(displayedTime).trigger("change"); + + }); + + this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button"); this.$eraseDeletedNotesButton.on("click", () => { server.post("notes/erase-deleted-notes-now").then(() => { toastService.showMessage(t("note_erasure_timeout.deleted_notes_erased")); @@ -39,6 +81,40 @@ export default class NoteErasureTimeoutOptions extends OptionsWidget { } async optionsLoaded(options: OptionMap) { - this.$eraseEntitiesAfterTimeInSeconds.val(options.eraseEntitiesAfterTimeInSeconds); + this.eraseEntitiesAfterTimeInSeconds = options.eraseEntitiesAfterTimeInSeconds; + + const displayedTime = this.convertTime(options.eraseEntitiesAfterTimeInSeconds, options.eraseEntitiesAfterTimeScale).toDisplay(); + this.$eraseEntitiesAfterTime.val(displayedTime); + this.$eraseEntitiesAfterTimeScale.val(options.eraseEntitiesAfterTimeScale); } + + + convertTime(time: string | number, timeScale: string | number) { + + const value = typeof time === "number" ? time : parseInt(time); + if (Number.isNaN(value)) { + throw new Error(`Time needs to be a valid integer, but received: ${time}`); + } + + const operand = typeof timeScale === "number" ? timeScale : parseInt(timeScale); + if (Number.isNaN(operand) || operand < 1) { + throw new Error(`TimeScale needs to be a valid integer >= 1, but received: ${timeScale}`); + } + + return { + toOption: () => Math.ceil(value * operand), + toDisplay: () => Math.ceil(value / operand), + } + + } + + handleTimeValidation() { + if (this.$eraseEntitiesAfterTime.is(":invalid")) { + // TriliumNextTODO: i18n + toastService.showMessage("The entered time value is not a valid number."); + return false + } + return true; + } + } diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 57520621e..7d6a6501c 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -1643,4 +1643,45 @@ body.electron.platform-darwin:not(.native-titlebar) .tab-row-container { background: var(--main-background-color); box-shadow: 0px 10px 20px rgba(0, 0, 0, var(--dropdown-shadow-opacity)); border-radius: 4px; +} + +body.zen { + --tab-bar-height: 0; +} + +body.zen .gutter, +body.zen #launcher-container, +body.zen #launcher-pane, +body.zen #left-pane, +body.zen #right-pane, +body.zen .tab-row-container, +body.zen .tab-row-widget, +body.zen .ribbon-container, +body.zen .note-icon-widget, +body.zen .title-row .button-widget, +body.zen .floating-buttons { + display: none !important; +} + +body.zen #launcher-pane { + position: absolute !important; + top: 0 !important; + right: 0 !important; + width: 64px !important; + height: 64px !important; + background: transparent !important; + border: 0 !important; +} + +body.zen .title-row { + display: block !important; + height: unset !important; + -webkit-app-region: drag; + padding-left: env(titlebar-area-x); +} + +body.zen .note-title-widget, +body.zen .note-title-widget input { + font-size: 1rem !important; + background: transparent !important; } \ No newline at end of file diff --git a/src/public/stylesheets/theme-next/shell.css b/src/public/stylesheets/theme-next/shell.css index 68751a534..e35972cf2 100644 --- a/src/public/stylesheets/theme-next/shell.css +++ b/src/public/stylesheets/theme-next/shell.css @@ -1808,6 +1808,12 @@ div.bookmark-folder-widget .note-link .bx { color: var(--hover-item-text-color); } +body.background-effects.zen #root-widget { + --main-background-color: transparent; + --root-background: transparent; +} + + /* Alert bar */ @keyframes alert-show { diff --git a/src/public/translations/cn/translation.json b/src/public/translations/cn/translation.json index 1d844f21d..3525f7a27 100644 --- a/src/public/translations/cn/translation.json +++ b/src/public/translations/cn/translation.json @@ -1134,7 +1134,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "笔记清理超时", "note_erasure_description": "被删除的笔记(以及属性、历史版本等)最初仅被标记为“删除”,可以从“最近修改”对话框中恢复它们。经过一段时间后,已删除的笔记会被“清理”,这意味着它们的内容将无法恢复。此设置允许您配置从删除到清除笔记之间的时间长度。", - "erase_notes_after_x_seconds": "在笔记删除 X 秒后清理", + "erase_notes_after": "Erase notes after", "manual_erasing_description": "您还可以手动触发清理(不考虑上述定义的超时):", "erase_deleted_notes_now": "立即清理已删除的笔记", "deleted_notes_erased": "已删除的笔记已被清理。" diff --git a/src/public/translations/de/translation.json b/src/public/translations/de/translation.json index b0c9a91c6..9d318b7c2 100644 --- a/src/public/translations/de/translation.json +++ b/src/public/translations/de/translation.json @@ -1102,7 +1102,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "Beachte das Zeitlimit für die Löschung", "note_erasure_description": "Deleted notes (and attributes, revisions...) are at first only marked as deleted and it is possible to recover them from Recent Notes dialog. After a period of time, deleted notes are \"erased\" which means their content is not recoverable anymore. This setting allows you to configure the length of the period between deleting and erasing the note.", - "erase_notes_after_x_seconds": "Notizen nach X Sekunden löschen", + "erase_notes_after": "Notizen löschen nach", "manual_erasing_description": "Du kannst das Löschen auch manuell auslösen (ohne Berücksichtigung des oben definierten Timeouts):", "erase_deleted_notes_now": "Jetzt gelöschte Notizen löschen", "deleted_notes_erased": "Gelöschte Notizen wurden gelöscht." diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 36fdccb4d..392ea89ae 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -642,7 +642,8 @@ "show_help": "Show Help", "about": "About TriliumNext Notes", "logout": "Logout", - "show-cheatsheet": "Show Cheatsheet" + "show-cheatsheet": "Show Cheatsheet", + "toggle-zen-mode": "Zen Mode" }, "sync_status": { "unknown": "

    Sync status will be known once the next sync attempt starts.

    Click to trigger sync now.

    ", @@ -1158,7 +1159,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "Note Erasure Timeout", "note_erasure_description": "Deleted notes (and attributes, revisions...) are at first only marked as deleted and it is possible to recover them from Recent Notes dialog. After a period of time, deleted notes are \"erased\" which means their content is not recoverable anymore. This setting allows you to configure the length of the period between deleting and erasing the note.", - "erase_notes_after_x_seconds": "Erase notes after X seconds", + "erase_notes_after": "Erase notes after", "manual_erasing_description": "You can also trigger erasing manually (without considering the timeout defined above):", "erase_deleted_notes_now": "Erase deleted notes now", "deleted_notes_erased": "Deleted notes have been erased." @@ -1648,5 +1649,11 @@ }, "help-button": { "title": "Open the relevant help page" + }, + "duration": { + "seconds": "Seconds", + "minutes": "Minutes", + "hours": "Hours", + "days": "Days" } } diff --git a/src/public/translations/es/translation.json b/src/public/translations/es/translation.json index 8499975a8..f23b90972 100644 --- a/src/public/translations/es/translation.json +++ b/src/public/translations/es/translation.json @@ -109,7 +109,8 @@ "choose_export_type": "Por favor, elija primero el tipo de exportación", "export_status": "Estado de exportación", "export_in_progress": "Exportación en curso: {{progressCount}}", - "export_finished_successfully": "La exportación finalizó exitosamente." + "export_finished_successfully": "La exportación finalizó exitosamente.", + "format_pdf": "PDF - para propósitos de impresión o compartición." }, "help": { "fullDocumentation": "Ayuda (la documentación completa está disponible online)", @@ -437,7 +438,9 @@ "share_favicon": "La nota de favicon se configurará en la página compartida. Por lo general, se desea configurarlo para que comparta la raíz y lo haga heredable. La nota de Favicon también debe estar en el subárbol compartido. Considere usar 'share_hidden_from_tree'.", "is_owned_by_note": "es propiedad de una nota", "other_notes_with_name": "Otras notas con nombre de {{attributeType}} \"{{attributeName}}\"", - "and_more": "... y {{count}} más." + "and_more": "... y {{count}} más.", + "print_landscape": "Al exportar a PDF, cambia la orientación de la página a paisaje en lugar de retrato.", + "print_page_size": "Al exportar a PDF, cambia el tamaño de la página. Valores soportados: A0, A1, A2, A3, A4, A5, A6, Legal, Letter, Tabloid, Ledger." }, "attribute_editor": { "help_text_body1": "Para agregar una etiqueta, simplemente escriba, por ejemplo. #rock o si desea agregar también valor, p.e. #año = 2020", @@ -638,7 +641,8 @@ "show_hidden_subtree": "Mostrar subárbol oculto", "show_help": "Mostrar ayuda", "about": "Acerca de TriliumNext Notes", - "logout": "Cerrar sesión" + "logout": "Cerrar sesión", + "show-cheatsheet": "Mostrar hoja de trucos" }, "sync_status": { "unknown": "

    El estado de sincronización será conocido una vez que el siguiente intento de sincronización comience.

    Dé clic para activar la sincronización ahora

    ", @@ -672,7 +676,8 @@ "save_revision": "Guardar revisión", "convert_into_attachment_failed": "La conversión de nota '{{title}}' falló.", "convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.", - "convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?" + "convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?", + "print_pdf": "Exportar como PDF..." }, "onclick_button": { "no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido" @@ -1153,7 +1158,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "Tiempo de espera de borrado de notas", "note_erasure_description": "Las notas eliminadas (y los atributos, las revisiones ...) en principio solo están marcadas como eliminadas y es posible recuperarlas del diálogo de Notas recientes. Después de un período de tiempo, las notas eliminadas son \" borradas\", lo que significa que su contenido ya no es recuperable. Esta configuración le permite configurar la longitud del período entre eliminar y borrar la nota.", - "erase_notes_after_x_seconds": "Borrar notas después de X segundos", + "erase_notes_after": "Borrar notas después de", "manual_erasing_description": "También puede activar el borrado manualmente (sin considerar el tiempo de espera definido anteriormente):", "erase_deleted_notes_now": "Borrar notas eliminadas ahora", "deleted_notes_erased": "Las notas eliminadas han sido borradas." @@ -1409,7 +1414,9 @@ "launcher": "Lanzador", "doc": "Doc", "widget": "Widget", - "confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?" + "confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?", + "geo-map": "Mapa Geo", + "beta-feature": "Beta" }, "protect_note": { "toggle-on": "Proteger la nota", @@ -1629,5 +1636,17 @@ }, "note_tooltip": { "note-has-been-deleted": "La nota ha sido eliminada." + }, + "geo-map": { + "create-child-note-title": "Crear una nueva subnota y agregarla al mapa", + "create-child-note-instruction": "Dé clic en el mapa para crear una nueva nota en esa ubicación o presione Escape para cancelar.", + "unable-to-load-map": "No se puede cargar el mapa." + }, + "geo-map-context": { + "open-location": "Abrir ubicación", + "remove-from-map": "Eliminar del mapa" + }, + "help-button": { + "title": "Abrir la página de ayuda relevante" } } diff --git a/src/public/translations/fr/translation.json b/src/public/translations/fr/translation.json index 7a680560d..9e13ba005 100644 --- a/src/public/translations/fr/translation.json +++ b/src/public/translations/fr/translation.json @@ -1103,7 +1103,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "Délai d'effacement des notes", "note_erasure_description": "Les notes supprimées (et les attributs, versions...) sont seulement marquées comme supprimées et il est possible de les récupérer à partir de la boîte de dialogue Notes récentes. Après un certain temps, les notes supprimées sont « effacées », ce qui signifie que leur contenu n'est plus récupérable. Ce paramètre vous permet de configurer la durée entre la suppression et l'effacement de la note.", - "erase_notes_after_x_seconds": "Effacer les notes après X secondes", + "erase_notes_after": "Effacer les notes après", "manual_erasing_description": "Vous pouvez également déclencher l'effacement manuellement (sans tenir compte de la durée définie ci-dessus) :", "erase_deleted_notes_now": "Effacer les notes supprimées maintenant", "deleted_notes_erased": "Les notes supprimées ont été effacées." diff --git a/src/public/translations/ro/translation.json b/src/public/translations/ro/translation.json index 9f4c1ade9..f751350a1 100644 --- a/src/public/translations/ro/translation.json +++ b/src/public/translations/ro/translation.json @@ -230,7 +230,9 @@ "workspace_search_home": "notițele de căutare vor fi create sub această notiță", "workspace_tab_background_color": "Culoare CSS ce va fi folosită în tab-urile ce aparțin spațiului de lucru", "workspace_template": "Această notița va apărea în lista de șabloane când se crează o nouă notiță, dar doar când spațiul de lucru în care se află notița este focalizat", - "app_theme_base": "setați valoarea la „next” pentru a folosi drept temă de bază „TriliumNext” în loc de cea clasică." + "app_theme_base": "setați valoarea la „next” pentru a folosi drept temă de bază „TriliumNext” în loc de cea clasică.", + "print_landscape": "Schimbă orientarea paginii din portret în vedere atunci când se exportă în PDF.", + "print_page_size": "Schimbă dimensiunea paginii când se exportă în PDF. Valori suportate: A0, A1, A2, A3, A4, A5, A6, Legal, Letter, Tabloid, Ledger." }, "attribute_editor": { "add_a_new_attribute": "Adaugă un nou attribut", @@ -520,7 +522,8 @@ "format_opml": "OPML - format de interschimbare pentru editoare cu structură ierarhică (outline). Formatarea, imaginile și fișierele nu vor fi incluse.", "opml_version_1": "OPML v1.0 - text simplu", "opml_version_2": "OPML v2.0 - permite și HTML", - "format_html": "HTML - recomandat deoarece păstrează toata formatarea" + "format_html": "HTML - recomandat deoarece păstrează toata formatarea", + "format_pdf": "PDF - cu scopul de printare sau partajare." }, "fast_search": { "description": "Căutarea rapidă dezactivează căutarea la nivel de conținut al notițelor cu scopul de a îmbunătăți performanța de căutare pentru baze de date mari.", @@ -588,7 +591,9 @@ "toggle_fullscreen": "Comută mod ecran complet", "zoom": "Zoom", "zoom_in": "Mărește", - "zoom_out": "Micșorează" + "zoom_out": "Micșorează", + "show-cheatsheet": "Afișează ghidul rapid", + "toggle-zen-mode": "Mod zen" }, "heading_style": { "markdown": "Stil Markdown", @@ -830,7 +835,7 @@ "note_erasure_timeout": { "deleted_notes_erased": "Notițele șterse au fost eliminate permanent.", "erase_deleted_notes_now": "Elimină notițele șterse acum", - "erase_notes_after_x_seconds": "Elimină notițele șterse după X secunde", + "erase_notes_after": "Elimină notițele șterse după", "manual_erasing_description": "Se poate rula o eliminare manuală (fără a lua în considerare timpul definit mai sus):", "note_erasure_description": "Notițele șterse (precum și atributele, reviziile) sunt prima oară doar marcate drept șterse și este posibil să fie recuperate din ecranul Notițe recente. După o perioadă de timp, notițele șterse vor fi „eliminate”, caz în care conținutul lor nu se poate recupera. Această setare permite configurarea duratei de timp dintre ștergerea și eliminarea notițelor.", "note_erasure_timeout_title": "Timpul de eliminare automată a notițelor șterse" @@ -1644,5 +1649,14 @@ "geo-map": { "create-child-note-title": "Crează o notiță nouă și adaug-o pe hartă", "unable-to-load-map": "Nu s-a putut încărca harta." + }, + "duration": { + "days": "zile", + "hours": "ore", + "minutes": "minute", + "seconds": "secunde" + }, + "help-button": { + "title": "Deschide ghidul relevant" } } diff --git a/src/public/translations/tw/translation.json b/src/public/translations/tw/translation.json index e80761cb2..463ec858f 100644 --- a/src/public/translations/tw/translation.json +++ b/src/public/translations/tw/translation.json @@ -1112,7 +1112,7 @@ "note_erasure_timeout": { "note_erasure_timeout_title": "筆記清理超時", "note_erasure_description": "被刪除的筆記(以及屬性、歷史版本等)最初僅被標記為「刪除」,可以從「最近修改」對話框中恢復它們。經過一段時間後,已刪除的筆記會被「清理」,這意味著它們的內容將無法恢復。此設定允許您設定從刪除到清除筆記之間的時間長度。", - "erase_notes_after_x_seconds": "在筆記刪除 X 秒後清理", + "erase_notes_after": "Erase notes after", "manual_erasing_description": "您還可以手動觸發清理(不考慮上述定義的超時):", "erase_deleted_notes_now": "立即清理已刪除的筆記", "deleted_notes_erased": "已刪除的筆記已被清理。" diff --git a/src/routes/api/app_info.ts b/src/routes/api/app_info.ts index 87501e804..fb2f84aec 100644 --- a/src/routes/api/app_info.ts +++ b/src/routes/api/app_info.ts @@ -2,6 +2,52 @@ import appInfo from "../../services/app_info.js"; +/** + * @swagger + * /api/app-info: + * get: + * summary: Get installation info + * operationId: app-info + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/app_info.ts + * responses: + * '200': + * description: Installation info + * content: + * application/json: + * schema: + * type: object + * properties: + * appVersion: + * type: string + * example: "0.91.6" + * dbVersion: + * type: integer + * example: 228 + * nodeVersion: + * type: string + * description: "value of process.version" + * syncVersion: + * type: integer + * example: 34 + * buildDate: + * type: string + * example: "2024-09-07T18:36:34Z" + * buildRevision: + * type: string + * example: "7c0d6930fa8f20d269dcfbcbc8f636a25f6bb9a7" + * dataDirectory: + * type: string + * example: "/var/lib/trilium" + * clipperProtocolVersion: + * type: string + * example: "1.0" + * utcDateTime: + * $ref: '#/components/schemas/UtcDateTime' + * security: + * - session: [] + */ function getAppInfo() { return appInfo; } diff --git a/src/routes/api/branches.ts b/src/routes/api/branches.ts index b9c5f751d..b81d0cfc0 100644 --- a/src/routes/api/branches.ts +++ b/src/routes/api/branches.ts @@ -186,6 +186,51 @@ function setExpandedForSubtree(req: Request) { }; } +/** + * @swagger + * /api/branches/{branchId}: + * delete: + * summary: Delete branch (note clone) + * operationId: branches-delete + * parameters: + * - name: branchId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/BranchId" + * - name: taskId + * in: query + * required: true + * schema: + * type: string + * description: Task group identifier + * - name: eraseNotes + * in: query + * schema: + * type: boolean + * required: false + * description: Whether to erase the note immediately + * - name: last + * in: query + * schema: + * type: boolean + * required: true + * description: Whether this is the last request of this task group + * responses: + * '200': + * description: Branch successfully deleted + * content: + * application/json: + * schema: + * type: object + * properties: + * noteDeleted: + * type: boolean + * description: Whether the last note clone was deleted + * security: + * - session: [] + * tags: ["data"] + */ function deleteBranch(req: Request) { const last = req.query.last === "true"; const eraseNotes = req.query.eraseNotes === "true"; diff --git a/src/routes/api/login.ts b/src/routes/api/login.ts index 3a6d43e99..1f190aec2 100644 --- a/src/routes/api/login.ts +++ b/src/routes/api/login.ts @@ -14,6 +14,68 @@ import ws from "../../services/ws.js"; import etapiTokenService from "../../services/etapi_tokens.js"; import type { Request } from "express"; +/** + * @swagger + * /api/login/sync: + * post: + * tags: + * - auth + * summary: Log in using documentSecret + * description: The `hash` parameter is computed using a HMAC of the `documentSecret` and `timestamp`. + * operationId: login-sync + * externalDocs: + * description: HMAC calculation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/utils.ts#L62-L66 + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * timestamp: + * $ref: '#/components/schemas/UtcDateTime' + * hash: + * type: string + * syncVersion: + * type: integer + * example: 34 + * responses: + * '200': + * description: Successful operation + * content: + * application/json: + * schema: + * type: object + * properties: + * syncVersion: + * type: integer + * example: 34 + * options: + * type: object + * properties: + * documentSecret: + * type: string + * '400': + * description: Sync version / document secret mismatch + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: "Non-matching sync versions, local is version ${server syncVersion}, remote is ${requested syncVersion}. It is recommended to run same version of Trilium on both sides of sync" + * '401': + * description: Timestamp mismatch + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: "Auth request time is out of sync, please check that both client and server have correct time. The difference between clocks has to be smaller than 5 minutes" + */ function loginSync(req: Request) { if (!sqlInit.schemaExists()) { return [500, { message: "DB schema does not exist, can't sync." }]; diff --git a/src/routes/api/notes.ts b/src/routes/api/notes.ts index 9f1dc5e41..853032938 100644 --- a/src/routes/api/notes.ts +++ b/src/routes/api/notes.ts @@ -14,14 +14,85 @@ import type { Request } from "express"; import type BBranch from "../../becca/entities/bbranch.js"; import type { AttributeRow } from "../../becca/entities/rows.js"; +/** + * @swagger + * /api/notes/{noteId}: + * get: + * summary: Retrieve note metadata + * operationId: notes-get + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '200': + * description: Note metadata + * content: + * application/json: + * schema: + * allOf: + * - $ref: '#/components/schemas/Note' + * - $ref: "#/components/schemas/Timestamps" + * security: + * - session: [] + * tags: ["data"] + */ function getNote(req: Request) { return becca.getNoteOrThrow(req.params.noteId); } +/** + * @swagger + * /api/notes/{noteId}/blob: + * get: + * summary: Retrieve note content + * operationId: notes-blob + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '304': + * description: Note content + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Blob' + * security: + * - session: [] + * tags: ["data"] + */ function getNoteBlob(req: Request) { return blobService.getBlobPojo("notes", req.params.noteId); } +/** + * @swagger + * /api/notes/{noteId}/metadata: + * get: + * summary: Retrieve note metadata (limited to timestamps) + * operationId: notes-metadata + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * responses: + * '200': + * description: Note metadata + * content: + * application/json: + * schema: + * $ref: "#/components/schemas/Timestamps" + * security: + * - session: [] + * tags: ["data"] + */ function getNoteMetadata(req: Request) { const note = becca.getNoteOrThrow(req.params.noteId); @@ -62,6 +133,43 @@ function updateNoteData(req: Request) { return noteService.updateNoteData(noteId, content, attachments); } +/** + * @swagger + * /api/notes/{noteId}: + * delete: + * summary: Delete note + * operationId: notes-delete + * parameters: + * - name: noteId + * in: path + * required: true + * schema: + * $ref: "#/components/schemas/NoteId" + * - name: taskId + * in: query + * required: true + * schema: + * type: string + * description: Task group identifier + * - name: eraseNotes + * in: query + * schema: + * type: boolean + * required: false + * description: Whether to erase the note immediately + * - name: last + * in: query + * schema: + * type: boolean + * required: true + * description: Whether this is the last request of this task group + * responses: + * '200': + * description: Note successfully deleted + * security: + * - session: [] + * tags: ["data"] + */ function deleteNote(req: Request) { const noteId = req.params.noteId; const taskId = req.query.taskId; diff --git a/src/routes/api/openapi.json b/src/routes/api/openapi.json new file mode 100644 index 000000000..3034c7e13 --- /dev/null +++ b/src/routes/api/openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.1","info":{"title":"Trilium Notes - Sync server API","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).","contact":{"name":"TriliumNext issue tracker","url":"https://github.com/TriliumNext/Notes/issues"},"license":{"name":"GNU Free Documentation License 1.3 (or later)","url":"https://www.gnu.org/licenses/fdl-1.3"}},"paths":{"/api/setup/sync-seed":{"get":{"tags":["auth"],"summary":"Sync documentSecret value","description":"First step to logging in.","operationId":"setup-sync-seed","responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"syncVersion":{"type":"integer","example":34},"options":{"type":"object","properties":{"documentSecret":{"type":"string"}}}}}}}}},"security":[{"user-password":[]}]}},"/api/app-info":{"get":{"summary":"Get installation info","operationId":"app-info","externalDocs":{"description":"Server implementation","url":"https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/app_info.ts"},"responses":{"200":{"description":"Installation info","content":{"application/json":{"schema":{"type":"object","properties":{"appVersion":{"type":"string","example":"0.91.6"},"dbVersion":{"type":"integer","example":228},"nodeVersion":{"type":"string","description":"value of process.version"},"syncVersion":{"type":"integer","example":34},"buildDate":{"type":"string","example":"2024-09-07T18:36:34Z"},"buildRevision":{"type":"string","example":"7c0d6930fa8f20d269dcfbcbc8f636a25f6bb9a7"},"dataDirectory":{"type":"string","example":"/var/lib/trilium"},"clipperProtocolVersion":{"type":"string","example":"1.0"},"utcDateTime":{"$ref":"#/components/schemas/UtcDateTime"}}}}}}},"security":[{"session":[]}]}},"/api/branches/{branchId}":{"delete":{"summary":"Delete branch (note clone)","operationId":"branches-delete","parameters":[{"name":"branchId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/BranchId"}},{"name":"taskId","in":"query","required":true,"schema":{"type":"string"},"description":"Task group identifier"},{"name":"eraseNotes","in":"query","schema":{"type":"boolean"},"required":false,"description":"Whether to erase the note immediately"},{"name":"last","in":"query","schema":{"type":"boolean"},"required":true,"description":"Whether this is the last request of this task group"}],"responses":{"200":{"description":"Branch successfully deleted","content":{"application/json":{"schema":{"type":"object","properties":{"noteDeleted":{"type":"boolean","description":"Whether the last note clone was deleted"}}}}}}},"security":[{"session":[]}],"tags":["data"]}},"/api/login/sync":{"post":{"tags":["auth"],"summary":"Log in using documentSecret","description":"The `hash` parameter is computed using a HMAC of the `documentSecret` and `timestamp`.","operationId":"login-sync","externalDocs":{"description":"HMAC calculation","url":"https://github.com/TriliumNext/Notes/blob/v0.91.6/src/services/utils.ts#L62-L66"},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"timestamp":{"$ref":"#/components/schemas/UtcDateTime"},"hash":{"type":"string"},"syncVersion":{"type":"integer","example":34}}}}}},"responses":{"200":{"description":"Successful operation","content":{"application/json":{"schema":{"type":"object","properties":{"syncVersion":{"type":"integer","example":34},"options":{"type":"object","properties":{"documentSecret":{"type":"string"}}}}}}}},"400":{"description":"Sync version / document secret mismatch","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Non-matching sync versions, local is version ${server syncVersion}, remote is ${requested syncVersion}. It is recommended to run same version of Trilium on both sides of sync"}}}}}},"401":{"description":"Timestamp mismatch","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Auth request time is out of sync, please check that both client and server have correct time. The difference between clocks has to be smaller than 5 minutes"}}}}}}}}},"/api/notes/{noteId}":{"get":{"summary":"Retrieve note metadata","operationId":"notes-get","parameters":[{"name":"noteId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/NoteId"}}],"responses":{"200":{"description":"Note metadata","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Note"},{"$ref":"#/components/schemas/Timestamps"}]}}}}},"security":[{"session":[]}],"tags":["data"]},"delete":{"summary":"Delete note","operationId":"notes-delete","parameters":[{"name":"noteId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/NoteId"}},{"name":"taskId","in":"query","required":true,"schema":{"type":"string"},"description":"Task group identifier"},{"name":"eraseNotes","in":"query","schema":{"type":"boolean"},"required":false,"description":"Whether to erase the note immediately"},{"name":"last","in":"query","schema":{"type":"boolean"},"required":true,"description":"Whether this is the last request of this task group"}],"responses":{"200":{"description":"Note successfully deleted"}},"security":[{"session":[]}],"tags":["data"]}},"/api/notes/{noteId}/blob":{"get":{"summary":"Retrieve note content","operationId":"notes-blob","parameters":[{"name":"noteId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/NoteId"}}],"responses":{"304":{"description":"Note content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Blob"}}}}},"security":[{"session":[]}],"tags":["data"]}},"/api/notes/{noteId}/metadata":{"get":{"summary":"Retrieve note metadata (limited to timestamps)","operationId":"notes-metadata","parameters":[{"name":"noteId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/NoteId"}}],"responses":{"200":{"description":"Note metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Timestamps"}}}}},"security":[{"session":[]}],"tags":["data"]}},"/api/sync/changed":{"get":{"summary":"Pull sync changes","operationId":"sync-changed","externalDocs":{"description":"Server implementation","url":"https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts"},"parameters":[{"in":"query","name":"instanceId","required":true,"schema":{"type":"string"},"description":"Local instance ID"},{"in":"query","name":"lastEntityChangeId","required":true,"schema":{"type":"integer"},"description":"Last locally present change ID"},{"in":"query","name":"logMarkerId","required":true,"schema":{"type":"string"},"description":"Marker to identify this request in server log"}],"responses":{"200":{"description":"Sync changes, limited to approximately one megabyte.","content":{"application/json":{"schema":{"type":"object","properties":{"entityChanges":{"type":"list","items":{"$ref":"#/components/schemas/EntityChange"}},"lastEntityChangeId":{"type":"integer","description":"If `outstandingPullCount > 0`, pass this as parameter in your next request to continue."},"outstandingPullCount":{"type":"int","example":42,"description":"Number of changes not yet returned by the remote."}}}}}}},"security":[{"session":[]}],"tags":["sync"]}},"/api/sync/update":{"put":{"summary":"Push sync changes","description":"Basic usage: set `pageCount = 1`, `pageIndex = 0`, and omit `requestId`. Supply your entity changes in the request body.","operationId":"sync-update","externalDocs":{"description":"Server implementation","url":"https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts"},"parameters":[{"in":"header","name":"pageCount","required":true,"schema":{"type":"integer"}},{"in":"header","name":"pageIndex","required":true,"schema":{"type":"integer"}},{"in":"header","name":"requestId","schema":{"type":"string","description":"ID to identify paginated requests"}},{"in":"query","name":"logMarkerId","required":true,"schema":{"type":"string"},"description":"Marker to identify this request in server log"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"instanceId":{"type":"string","description":"Local instance ID"},"entities":{"type":"list","items":{"$ref":"#/components/schemas/EntityChange"}}}}}}},"responses":{"200":{"description":"Changes processed successfully"}},"security":[{"session":[]}],"tags":["sync"]}},"/api/tree":{"get":{"summary":"Retrieve tree data","operationId":"tree","externalDocs":{"description":"Server implementation","url":"https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/tree.ts"},"parameters":[{"in":"query","name":"subTreeNoteId","required":false,"schema":{"type":"string"},"description":"Limit tree data to this note and descendants"}],"responses":{"200":{"description":"Notes, branches and attributes","content":{"application/json":{"schema":{"type":"object","properties":{"branches":{"type":"list","items":{"$ref":"#/components/schemas/Branch"}},"notes":{"type":"list","items":{"$ref":"#/components/schemas/Note"}},"attributes":{"type":"list","items":{"$ref":"#/components/schemas/Attribute"}}}}}}}},"security":[{"session":[]}],"tags":["data"]}}},"components":{},"tags":[]} \ No newline at end of file diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index 257344cab..14cb7ec42 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -12,6 +12,7 @@ import type { OptionNames } from "../../services/options_interface.js"; // options allowed to be updated directly in the Options dialog const ALLOWED_OPTIONS = new Set([ "eraseEntitiesAfterTimeInSeconds", + "eraseEntitiesAfterTimeScale", "protectedSessionTimeout", "revisionSnapshotTimeInterval", "revisionSnapshotNumberLimit", diff --git a/src/routes/api/setup.ts b/src/routes/api/setup.ts index e4e5a6729..718bc37a7 100644 --- a/src/routes/api/setup.ts +++ b/src/routes/api/setup.ts @@ -45,6 +45,34 @@ function saveSyncSeed(req: Request) { sqlInit.createDatabaseForSync(options); } +/** + * @swagger + * /api/setup/sync-seed: + * get: + * tags: + * - auth + * summary: Sync documentSecret value + * description: First step to logging in. + * operationId: setup-sync-seed + * responses: + * '200': + * description: Successful operation + * content: + * application/json: + * schema: + * type: object + * properties: + * syncVersion: + * type: integer + * example: 34 + * options: + * type: object + * properties: + * documentSecret: + * type: string + * security: + * - user-password: [] + */ function getSyncSeed() { log.info("Serving sync seed."); diff --git a/src/routes/api/sync.ts b/src/routes/api/sync.ts index d157fb4ed..736e1e97b 100644 --- a/src/routes/api/sync.ts +++ b/src/routes/api/sync.ts @@ -86,6 +86,58 @@ function forceFullSync() { syncService.sync(); } +/** + * @swagger + * /api/sync/changed: + * get: + * summary: Pull sync changes + * operationId: sync-changed + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts + * parameters: + * - in: query + * name: instanceId + * required: true + * schema: + * type: string + * description: Local instance ID + * - in: query + * name: lastEntityChangeId + * required: true + * schema: + * type: integer + * description: Last locally present change ID + * - in: query + * name: logMarkerId + * required: true + * schema: + * type: string + * description: Marker to identify this request in server log + * responses: + * '200': + * description: Sync changes, limited to approximately one megabyte. + * content: + * application/json: + * schema: + * type: object + * properties: + * entityChanges: + * type: list + * items: + * $ref: '#/components/schemas/EntityChange' + * lastEntityChangeId: + * type: integer + * description: If `outstandingPullCount > 0`, pass this as parameter in your next request to continue. + * outstandingPullCount: + * type: int + * example: 42 + * description: Number of changes not yet returned by the remote. + * security: + * - session: [] + * tags: + * - sync + */ function getChanged(req: Request) { const startTime = Date.now(); @@ -151,6 +203,60 @@ const partialRequests: Record< } > = {}; +/** + * @swagger + * /api/sync/update: + * put: + * summary: Push sync changes + * description: + * "Basic usage: set `pageCount = 1`, `pageIndex = 0`, and omit `requestId`. Supply your entity changes in the request body." + * operationId: sync-update + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/sync.ts + * parameters: + * - in: header + * name: pageCount + * required: true + * schema: + * type: integer + * - in: header + * name: pageIndex + * required: true + * schema: + * type: integer + * - in: header + * name: requestId + * schema: + * type: string + * description: ID to identify paginated requests + * - in: query + * name: logMarkerId + * required: true + * schema: + * type: string + * description: Marker to identify this request in server log + * requestBody: + * content: + * application/json: + * schema: + * type: object + * properties: + * instanceId: + * type: string + * description: Local instance ID + * entities: + * type: list + * items: + * $ref: '#/components/schemas/EntityChange' + * responses: + * '200': + * description: Changes processed successfully + * security: + * - session: [] + * tags: + * - sync + */ function update(req: Request) { let { body } = req; diff --git a/src/routes/api/tree.ts b/src/routes/api/tree.ts index d90470993..610c82fde 100644 --- a/src/routes/api/tree.ts +++ b/src/routes/api/tree.ts @@ -127,6 +127,46 @@ function getNotesAndBranchesAndAttributes(_noteIds: string[] | Set) { }; } +/** + * @swagger + * /api/tree: + * get: + * summary: Retrieve tree data + * operationId: tree + * externalDocs: + * description: Server implementation + * url: https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/api/tree.ts + * parameters: + * - in: query + * name: subTreeNoteId + * required: false + * schema: + * type: string + * description: Limit tree data to this note and descendants + * responses: + * '200': + * description: Notes, branches and attributes + * content: + * application/json: + * schema: + * type: object + * properties: + * branches: + * type: list + * items: + * $ref: '#/components/schemas/Branch' + * notes: + * type: list + * items: + * $ref: '#/components/schemas/Note' + * attributes: + * type: list + * items: + * $ref: '#/components/schemas/Attribute' + * security: + * - session: [] + * tags: ["data"] + */ function getTree(req: Request) { const subTreeNoteId = typeof req.query.subTreeNoteId === "string" ? req.query.subTreeNoteId : "root"; const collectedNoteIds = new Set([subTreeNoteId]); diff --git a/src/routes/api_docs.ts b/src/routes/api_docs.ts index 1535265c3..3df230feb 100644 --- a/src/routes/api_docs.ts +++ b/src/routes/api_docs.ts @@ -1,4 +1,4 @@ -import type { Router } from "express"; +import type { Application, Router } from "express"; import swaggerUi from "swagger-ui-express"; import { readFile } from "fs/promises"; import { fileURLToPath } from "url"; @@ -7,19 +7,29 @@ import yaml from "js-yaml"; import type { JsonObject } from "swagger-ui-express"; const __dirname = dirname(fileURLToPath(import.meta.url)); -const swaggerDocument = yaml.load( +const etapiDocument = yaml.load( await readFile(join(__dirname, "../etapi/etapi.openapi.yaml"), "utf8") ) as JsonObject; +const apiDocument = JSON.parse(await readFile(join(__dirname, "api", "openapi.json"), "utf-8")); -function register(router: Router) { - router.use( - "/etapi", - swaggerUi.serve, - swaggerUi.setup(swaggerDocument, { +function register(app: Application) { + app.use( + "/etapi/docs/", + swaggerUi.serveFiles(etapiDocument), + swaggerUi.setup(etapiDocument, { explorer: true, customSiteTitle: "TriliumNext ETAPI Documentation" }) ); + + app.use( + "/api/docs/", + swaggerUi.serveFiles(apiDocument), + swaggerUi.setup(apiDocument, { + explorer: true, + customSiteTitle: "TriliumNext Internal API Documentation" + }) + ); } export default { diff --git a/src/routes/login.ts b/src/routes/login.ts index 21ebaf280..68b98e893 100644 --- a/src/routes/login.ts +++ b/src/routes/login.ts @@ -70,14 +70,16 @@ function login(req: Request, res: Response) { } req.session.regenerate(() => { - const sessionMaxAge = 21 * 24 * 3600000 // 3 weeks in Milliseconds + if (!rememberMe) { + // unset default maxAge set by sessionParser + // Cookie becomes non-persistent and expires after current browser session (e.g. when browser is closed) + req.session.cookie.maxAge = undefined; + } - req.session.cookie.maxAge = (rememberMe) ? sessionMaxAge : undefined; req.session.loggedIn = true; res.redirect("."); }); - } function verifyPassword(guessedPassword: string) { diff --git a/src/routes/session_parser.ts b/src/routes/session_parser.ts index eaaf0ebe9..89df0e037 100644 --- a/src/routes/session_parser.ts +++ b/src/routes/session_parser.ts @@ -12,11 +12,11 @@ const sessionParser = session({ cookie: { path: config.Session.cookiePath, httpOnly: true, - maxAge: 24 * 60 * 60 * 1000 // in milliseconds + maxAge: config.Session.cookieMaxAge * 1000 // needs value in milliseconds }, name: "trilium.sid", store: new FileStore({ - ttl: 30 * 24 * 3600, + ttl: config.Session.cookieMaxAge, path: `${dataDir.TRILIUM_DATA_DIR}/sessions` }) }); diff --git a/src/services/code_block_theme.spec.ts b/src/services/code_block_theme.spec.ts new file mode 100644 index 000000000..84482b416 --- /dev/null +++ b/src/services/code_block_theme.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vitest"; +import { getStylesDirectory, readThemesFromFileSystem } from "./code_block_theme.js"; + +import themeNames from "./code_block_theme_names.json" with { type: "json" }; + +describe("Code block theme", () => { + it("all themes are mapped", () => { + const themes = readThemesFromFileSystem(getStylesDirectory()); + + const mappedThemeNames = new Set(Object.values(themeNames)); + const unmappedThemeNames = new Set(); + + for (const theme of themes) { + if (!mappedThemeNames.has(theme.title)) { + unmappedThemeNames.add(theme.title); + } + } + + expect(unmappedThemeNames.size, `Unmapped themes: ${Array.from(unmappedThemeNames).join(", ")}`).toBe(0); + }); +}); diff --git a/src/services/code_block_theme.ts b/src/services/code_block_theme.ts index 8351b2519..8a3964fcd 100644 --- a/src/services/code_block_theme.ts +++ b/src/services/code_block_theme.ts @@ -44,7 +44,7 @@ export function listSyntaxHighlightingThemes() { }; } -function getStylesDirectory() { +export function getStylesDirectory() { if (isElectron && !isDev) { return "styles"; } @@ -60,7 +60,7 @@ function getStylesDirectory() { * @param path the path to read from. Usually this is the highlight.js `styles` directory. * @returns the list of themes. */ -function readThemesFromFileSystem(path: string): ColorTheme[] { +export function readThemesFromFileSystem(path: string): ColorTheme[] { return fs .readdirSync(path) .filter((el) => el.endsWith(".min.css")) diff --git a/src/services/code_block_theme_names.json b/src/services/code_block_theme_names.json index 0981092f2..4bfa6a290 100644 --- a/src/services/code_block_theme_names.json +++ b/src/services/code_block_theme_names.json @@ -14,6 +14,10 @@ "brown paper": "Brown Paper (Light)", "codepen embed": "CodePen Embed (Dark)", "color brewer": "Color Brewer (Light)", + "cybertopia cherry": "Cybertopia Cherry (Dark)", + "cybertopia dimmer": "Cybertopia Dimmer (Dark)", + "cybertopia icecap": "Cybertopia Icecap (Dark)", + "cybertopia saturated": "Cybertopia Saturated (Dark)", "dark": "Dark", "default": "Original highlight.js Theme (Light)", "devibeans": "devibeans (Dark)", @@ -58,6 +62,9 @@ "qtcreator light": "Qt Creator (Light)", "rainbow": "Rainbow (Dark)", "routeros": "RouterOS Script (Light)", + "rose pine dawn": "Rose Pine Dawn (Light)", + "rose pine moon": "Rose Pine Moon (Dark)", + "rose pine": "Rose Pine (Dark)", "school book": "School Book (Light)", "shades of purple": "Shades of Purple (Dark)", "srcery": "Srcery (Dark)", diff --git a/src/services/config.ts b/src/services/config.ts index b529d4792..b10015aa7 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -34,6 +34,7 @@ export interface TriliumConfig { }; Session: { cookiePath: string; + cookieMaxAge: number; } Sync: { syncServerHost: string; @@ -81,7 +82,10 @@ const config: TriliumConfig = { Session: { cookiePath: - process.env.TRILIUM_SESSION_COOKIEPATH || iniConfig?.Session?.cookiePath || "/" + process.env.TRILIUM_SESSION_COOKIEPATH || iniConfig?.Session?.cookiePath || "/", + + cookieMaxAge: + parseInt(String(process.env.TRILIUM_SESSION_COOKIEMAXAGE)) || parseInt(iniConfig?.Session?.cookieMaxAge) || 21 * 24 * 60 * 60 // 21 Days in Seconds }, Sync: { diff --git a/src/services/in_app_help.ts b/src/services/in_app_help.ts index 0af911684..fffbf7812 100644 --- a/src/services/in_app_help.ts +++ b/src/services/in_app_help.ts @@ -44,6 +44,15 @@ function parseNoteMeta(noteMeta: NoteMeta, docNameRoot: string): HiddenSubtreeIt for (const attribute of noteMeta.attributes ?? []) { if (attribute.name === "iconClass") { iconClass = attribute.value; + continue; + } + + if (attribute.name === "webViewSrc") { + item.attributes?.push({ + type: "label", + name: attribute.name, + value: attribute.value + }); } } @@ -64,6 +73,11 @@ function parseNoteMeta(noteMeta: NoteMeta, docNameRoot: string): HiddenSubtreeIt }); } + // Handle web views + if (noteMeta.type === "webView") { + item.type = "webView"; + } + // Handle children if (noteMeta.children) { const children: HiddenSubtreeItem[] = []; diff --git a/src/services/keyboard_actions.ts b/src/services/keyboard_actions.ts index 21745878d..1df14a264 100644 --- a/src/services/keyboard_actions.ts +++ b/src/services/keyboard_actions.ts @@ -238,6 +238,12 @@ function getDefaultKeyboardActions() { description: t("keyboard_actions.toggle-tray"), scope: "window" }, + { + actionName: "toggleZenMode", + defaultShortcuts: ["Alt+Z"], + description: t("keyboard_actions.toggle-zen-mode"), + scope: "window" + }, { actionName: "firstTab", defaultShortcuts: ["CommandOrControl+1"], diff --git a/src/services/keyboard_actions_interface.ts b/src/services/keyboard_actions_interface.ts index 9eb3539bd..2fc96665f 100644 --- a/src/services/keyboard_actions_interface.ts +++ b/src/services/keyboard_actions_interface.ts @@ -35,6 +35,7 @@ const enum KeyboardActionNamesEnum { activatePreviousTab, openNewWindow, toggleTray, + toggleZenMode, firstTab, secondTab, thirdTab, diff --git a/src/services/meta/note_meta.ts b/src/services/meta/note_meta.ts index 0aaafb6b5..681ca4f8c 100644 --- a/src/services/meta/note_meta.ts +++ b/src/services/meta/note_meta.ts @@ -1,3 +1,4 @@ +import type { NoteType } from "../../becca/entities/rows.js"; import type AttachmentMeta from "./attachment_meta.js"; import type AttributeMeta from "./attribute_meta.js"; @@ -15,7 +16,7 @@ export default interface NoteMeta { notePosition?: number; prefix?: string | null; isExpanded?: boolean; - type?: string; + type?: NoteType; mime?: string; /** 'html' or 'markdown', applicable to text notes only */ format?: "html" | "markdown"; diff --git a/src/services/options_init.ts b/src/services/options_init.ts index dedd1a896..bb962835d 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -105,6 +105,7 @@ const defaultOptions: DefaultOption[] = [ { name: "rightPaneVisible", value: "true", isSynced: false }, { name: "nativeTitleBarVisible", value: "false", isSynced: false }, { name: "eraseEntitiesAfterTimeInSeconds", value: "604800", isSynced: true }, // default is 7 days + { name: "eraseEntitiesAfterTimeScale", value: "86400", isSynced: true }, // default 86400 seconds = Day { name: "hideArchivedNotes_main", value: "false", isSynced: false }, { name: "debugModeEnabled", value: "false", isSynced: false }, { name: "headingStyle", value: "underline", isSynced: true }, diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts index 2632b7b2e..f3a92383b 100644 --- a/src/services/options_interface.ts +++ b/src/services/options_interface.ts @@ -61,6 +61,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions { }); }); - it.skip("simple path match", () => { + it("simple path match", () => { rootNote.child(note("Europe").child(note("Austria"))); const searchContext = new SearchContext(); @@ -32,7 +32,7 @@ describe("Search", () => { expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy(); }); - it.skip("normal search looks also at attributes", () => { + it("normal search looks also at attributes", () => { const austria = note("Austria"); const vienna = note("Vienna"); @@ -50,7 +50,7 @@ describe("Search", () => { expect(findNoteByTitle(searchResults, "Vienna")).toBeTruthy(); }); - it.skip("normal search looks also at type and mime", () => { + it("normal search looks also at type and mime", () => { rootNote.child(note("Effective Java", { type: "book", mime: "" })).child(note("Hello World.java", { type: "code", mime: "text/x-java" })); const searchContext = new SearchContext(); @@ -69,7 +69,7 @@ describe("Search", () => { expect(searchResults.length).toEqual(2); }); - it.skip("only end leafs are results", () => { + it("only end leafs are results", () => { rootNote.child(note("Europe").child(note("Austria"))); const searchContext = new SearchContext(); @@ -79,7 +79,7 @@ describe("Search", () => { expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy(); }); - it.skip("only end leafs are results", () => { + it("only end leafs are results", () => { rootNote.child(note("Europe").child(note("Austria").label("capital", "Vienna"))); const searchContext = new SearchContext(); @@ -132,7 +132,7 @@ describe("Search", () => { expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy(); }); - it.skip("inherited label comparison", () => { + it("inherited label comparison", () => { rootNote.child(note("Europe").label("country", "", true).child(note("Austria")).child(note("Czech Republic"))); const searchContext = new SearchContext(); @@ -527,7 +527,7 @@ describe("Search", () => { expect(becca.notes[searchResults[0].noteId].title).toEqual("Europe"); }); - it.skip("test note.text *=* something", () => { + it("test note.text *=* something", () => { const italy = note("Italy").label("capital", "Rome"); const slovakia = note("Slovakia").label("capital", "Bratislava"); @@ -540,7 +540,7 @@ describe("Search", () => { expect(becca.notes[searchResults[0].noteId].title).toEqual("Slovakia"); }); - it.skip("test that fulltext does not match archived notes", () => { + it("test that fulltext does not match archived notes", () => { const italy = note("Italy").label("capital", "Rome"); const slovakia = note("Slovakia").label("capital", "Bratislava"); diff --git a/src/share/routes.ts b/src/share/routes.ts index fedec7abb..c77a41fc0 100644 --- a/src/share/routes.ts +++ b/src/share/routes.ts @@ -143,7 +143,7 @@ function register(router: Router) { addNoIndexHeader(note, res); - if (note.isLabelTruthy("shareRaw")) { + if (note.isLabelTruthy("shareRaw") || typeof req.query.raw !== "undefined") { res.setHeader("Content-Type", note.mime).send(note.getContent()); return; diff --git a/translations/en/server.json b/translations/en/server.json index 0560c5976..49e08bb99 100644 --- a/translations/en/server.json +++ b/translations/en/server.json @@ -92,7 +92,8 @@ "toggle-book-properties": "Toggle Book Properties", "toggle-classic-editor-toolbar": "Toggle the Formatting tab for the editor with fixed toolbar", "export-as-pdf": "Exports the current note as a PDF", - "show-cheatsheet": "Shows a modal with common keyboard operations" + "show-cheatsheet": "Shows a modal with common keyboard operations", + "toggle-zen-mode": "Enables/disables the zen mode (minimal UI for more focused editing)" }, "login": { "title": "Login", diff --git a/translations/es/server.json b/translations/es/server.json index 3bf1a0aef..655c2aba2 100644 --- a/translations/es/server.json +++ b/translations/es/server.json @@ -90,7 +90,9 @@ "force-save-revision": "Forzar la creación/guardado de una nueva revisión de nota de la nota activa", "show-help": "Muestra ayuda/hoja de referencia integrada", "toggle-book-properties": "Alternar propiedades del libro", - "toggle-classic-editor-toolbar": "Alternar la pestaña de formato por el editor con barra de herramientas fija" + "toggle-classic-editor-toolbar": "Alternar la pestaña de formato por el editor con barra de herramientas fija", + "export-as-pdf": "Exporta la nota actual como un PDF", + "show-cheatsheet": "Muestra un modal con operaciones de teclado comunes" }, "login": { "title": "Iniciar sesión", @@ -240,7 +242,8 @@ "sync-title": "Sincronizar", "other": "Otros", "advanced-title": "Avanzado", - "visible-launchers-title": "Lanzadores visibles" + "visible-launchers-title": "Lanzadores visibles", + "user-guide": "Guía de Usuario" }, "notes": { "new-note": "Nueva nota", @@ -250,5 +253,23 @@ "backend_log": { "log-does-not-exist": "El archivo de registro del backend '{{fileName}}' no existe (aún).", "reading-log-failed": "Leer el archivo de registro del backend '{{fileName}}' falló." + }, + "content_renderer": { + "note-cannot-be-displayed": "Este tipo de nota no puede ser mostrado." + }, + "pdf": { + "export_filter": "Documento PDF (*.pdf)", + "unable-to-export-message": "La nota actual no pudo ser exportada como PDF.", + "unable-to-export-title": "No es posible exportar como PDF", + "unable-to-save-message": "No se pudo escribir en el archivo seleccionado. Intente de nuevo o seleccione otro destino." + }, + "tray": { + "tooltip": "TriliumNext Notes", + "close": "Cerrar Trilium", + "recents": "Notas recientes", + "bookmarks": "Marcadores", + "today": "Abrir nota del diario de hoy", + "new-note": "Nueva nota", + "show-windows": "Mostrar ventanas" } } diff --git a/translations/ro/server.json b/translations/ro/server.json index 6ff05b095..ebf99082e 100644 --- a/translations/ro/server.json +++ b/translations/ro/server.json @@ -91,7 +91,8 @@ "zoom-in": "Mărește zoom-ul", "zoom-out": "Micșorează zoom-ul", "toggle-classic-editor-toolbar": "Comută tab-ul „Formatare” pentru editorul cu bară fixă", - "export-as-pdf": "Exportă notița curentă ca PDF" + "export-as-pdf": "Exportă notița curentă ca PDF", + "show-cheatsheet": "Afișează o fereastră cu scurtături de la tastatură comune" }, "login": { "button": "Autentifică", @@ -241,7 +242,8 @@ "sql-console-history-title": "Istoricul consolei SQL", "go-to-next-note-title": "Mergi la notița următoare", "launch-bar-templates-title": "Șabloane bară de lansare", - "visible-launchers-title": "Lansatoare vizibile" + "visible-launchers-title": "Lansatoare vizibile", + "user-guide": "Ghidul de utilizare" }, "notes": { "new-note": "Notiță nouă"