Compare commits
147 Commits
v0.92.0-be
...
v0.92.1-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bea6af20e | ||
|
|
3c83112240 | ||
|
|
4e876ed24d | ||
|
|
94ce01bbc2 | ||
|
|
411e3dfa0e | ||
|
|
a1bfc6aae7 | ||
|
|
97bc103e76 | ||
|
|
9da1f55409 | ||
|
|
017fba518d | ||
|
|
63584c153c | ||
|
|
a4a2e55415 | ||
|
|
4a1691ac31 | ||
|
|
5a8d5c59f5 | ||
|
|
a9cebe312f | ||
|
|
43f79ca813 | ||
|
|
5c1db3cab2 | ||
|
|
bc4d820cb0 | ||
|
|
f4e6edd19e | ||
|
|
530340f753 | ||
|
|
fcc1068b06 | ||
|
|
6d19e315f4 | ||
|
|
7269c1b0aa | ||
|
|
8e69cf79a6 | ||
|
|
46f543ad54 | ||
|
|
38dbf6efcd | ||
|
|
8e68ddafd5 | ||
|
|
0c43b387ce | ||
|
|
8aa560eb82 | ||
|
|
3a2b8e9791 | ||
|
|
468b3b6027 | ||
|
|
f872073f65 | ||
|
|
ef4fc0a180 | ||
|
|
1d47df5f28 | ||
|
|
77264b5385 | ||
|
|
41e925dc94 | ||
|
|
789178061b | ||
|
|
8011969b9d | ||
|
|
9ab2fe85bd | ||
|
|
6c818427fc | ||
|
|
7dab171a0c | ||
|
|
c680c3476b | ||
|
|
bf0b6ce554 | ||
|
|
bedc61c3d0 | ||
|
|
c925ae5f15 | ||
|
|
77ee7f96c1 | ||
|
|
cadd78524c | ||
|
|
fd4f35e879 | ||
|
|
39f00bd568 | ||
|
|
ee2d4c6830 | ||
|
|
e93d47f664 | ||
|
|
0c88c4c3ee | ||
|
|
81bdd57398 | ||
|
|
fe5182ebc6 | ||
|
|
42d46bdb72 | ||
|
|
710cf68c06 | ||
|
|
300bb561bb | ||
|
|
96961898ca | ||
|
|
1520913686 | ||
|
|
6fae7a98f5 | ||
|
|
57dc168c26 | ||
|
|
946d9aee40 | ||
|
|
16b16927ef | ||
|
|
c2e4def523 | ||
|
|
1e11625f14 | ||
|
|
5495677fc2 | ||
|
|
2cefdf8b9f | ||
|
|
57b3035559 | ||
|
|
4f84ad8b81 | ||
|
|
1626767f30 | ||
|
|
91003af092 | ||
|
|
860de346a7 | ||
|
|
324a3d0d8b | ||
|
|
f9e4ae7210 | ||
|
|
bdd6395a76 | ||
|
|
1c118f2aa9 | ||
|
|
4010cb2789 | ||
|
|
f83beafd76 | ||
|
|
366264f3a9 | ||
|
|
ba91ed1855 | ||
|
|
2734e230ab | ||
|
|
ca1d5207d8 | ||
|
|
f6b6b2e740 | ||
|
|
c255af67c9 | ||
|
|
34b4e6d069 | ||
|
|
4e01534d76 | ||
|
|
fa05f15753 | ||
|
|
68c7df797d | ||
|
|
587a051430 | ||
|
|
04a6175630 | ||
|
|
bf6c5dfb20 | ||
|
|
19816493d6 | ||
|
|
cecde349b7 | ||
|
|
1a80a379dc | ||
|
|
739eaf9fc0 | ||
|
|
7f173b287a | ||
|
|
5a6c3ae426 | ||
|
|
6b5d905ebe | ||
|
|
61f2e35717 | ||
|
|
4a34d5b2df | ||
|
|
549917c1f1 | ||
|
|
4ed3a28e29 | ||
|
|
c261bf7f7a | ||
|
|
62c9e865f5 | ||
|
|
bd75a26803 | ||
|
|
9a1d26e129 | ||
|
|
bf41c54bd0 | ||
|
|
28148b32d2 | ||
|
|
eb08a976dd | ||
|
|
eeb99cf37c | ||
|
|
a0c2715980 | ||
|
|
e35ff07b9b | ||
|
|
ce1f418aa7 | ||
|
|
fbc4206908 | ||
|
|
bb4c3ae6ff | ||
|
|
0332ade13c | ||
|
|
2d968b8e9c | ||
|
|
fd2c65dcc0 | ||
|
|
aab35955bf | ||
|
|
bcb40b531f | ||
|
|
32bb43f9c1 | ||
|
|
86ab2d4008 | ||
|
|
657638ee54 | ||
|
|
50d37bbcb1 | ||
|
|
6706332be3 | ||
|
|
2ec2d784ec | ||
|
|
16caae191e | ||
|
|
c7d75b759c | ||
|
|
b837c57d06 | ||
|
|
ef3a75d58e | ||
|
|
59b474df35 | ||
|
|
fd47412d51 | ||
|
|
237f2ead73 | ||
|
|
558bee72e9 | ||
|
|
ed082f34d5 | ||
|
|
fabafeac86 | ||
|
|
d26d668741 | ||
|
|
fc8f805b28 | ||
|
|
ed8b8e50a4 | ||
|
|
bc66e98533 | ||
|
|
7af4e52766 | ||
|
|
ccbed7bbc3 | ||
|
|
10ba467202 | ||
|
|
793b0c9fe8 | ||
|
|
35a3d326f7 | ||
|
|
f672054441 | ||
|
|
e0e530b219 | ||
|
|
c0714a92d5 |
3
.github/actions/build-server/action.yml
vendored
@@ -1,4 +1,7 @@
|
||||
inputs:
|
||||
os:
|
||||
description: "One of the supported platforms: windows"
|
||||
required: true
|
||||
arch:
|
||||
description: "The architecture to build for: x64, arm64"
|
||||
required: true
|
||||
|
||||
25
.github/workflows/main-docker.yml
vendored
@@ -100,7 +100,20 @@ jobs:
|
||||
|
||||
build:
|
||||
name: Build Docker images
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- dockerfile: Dockerfile.alpine
|
||||
platform: linux/amd64
|
||||
image: ubuntu-latest
|
||||
- dockerfile: Dockerfile
|
||||
platform: linux/arm64
|
||||
image: ubuntu-24.04-arm
|
||||
- dockerfile: Dockerfile
|
||||
platform: linux/arm/v7
|
||||
image: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.image }}
|
||||
needs:
|
||||
- test_docker
|
||||
permissions:
|
||||
@@ -108,16 +121,6 @@ jobs:
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- dockerfile: Dockerfile.alpine
|
||||
platform: linux/amd64
|
||||
- dockerfile: Dockerfile
|
||||
platform: linux/arm64
|
||||
- dockerfile: Dockerfile
|
||||
platform: linux/arm/v7
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
|
||||
2
.github/workflows/main.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
extension: [deb, rpm, zip, flatpak]
|
||||
- name: windows
|
||||
image: windows-latest
|
||||
extension: exe
|
||||
extension: [exe, zip]
|
||||
runs-on: ${{ matrix.os.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
3
.github/workflows/nightly.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
extension: [deb, rpm, zip, flatpak]
|
||||
- name: windows
|
||||
image: windows-latest
|
||||
extension: exe
|
||||
extension: [exe, zip]
|
||||
runs-on: ${{ matrix.os.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -75,6 +75,7 @@ jobs:
|
||||
- name: Run the build
|
||||
uses: ./.github/actions/build-server
|
||||
with:
|
||||
os: linux
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
- name: Publish release
|
||||
|
||||
1
.github/workflows/release.yml
vendored
@@ -65,6 +65,7 @@ jobs:
|
||||
- name: Run the build
|
||||
uses: ./.github/actions/build-server
|
||||
with:
|
||||
os: linux
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
- name: Publish release
|
||||
|
||||
900
package-lock.json
generated
44
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "TriliumNext Notes",
|
||||
"description": "Build your personal knowledge base with TriliumNext Notes",
|
||||
"version": "0.92.0-beta",
|
||||
"version": "0.92.1-beta",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "./dist/electron-main.js",
|
||||
"author": {
|
||||
@@ -26,7 +26,6 @@
|
||||
"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 .\"",
|
||||
@@ -37,30 +36,23 @@
|
||||
"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:prepare-dist && electron-forge make",
|
||||
"electron-forge:package": "npm run build:prepare-dist && 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": "npm run build:webpack && rimraf ./dist && tsc && tsx ./bin/copy-dist.ts",
|
||||
|
||||
"test": "cross-env TRILIUM_DATA_DIR=./integration-tests/db vitest",
|
||||
"test": "cross-env TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory 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",
|
||||
@@ -81,7 +73,7 @@
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/leaflet": "1.9.16",
|
||||
"@types/react-dom": "18.3.5",
|
||||
"@types/swagger-ui-express": "4.1.7",
|
||||
"@types/swagger-ui-express": "4.1.8",
|
||||
"archiver": "7.0.1",
|
||||
"async-mutex": "0.5.0",
|
||||
"autocomplete.js": "0.38.1",
|
||||
@@ -89,6 +81,7 @@
|
||||
"better-sqlite3": "11.8.1",
|
||||
"bootstrap": "5.3.3",
|
||||
"boxicons": "2.1.4",
|
||||
"chardet": "2.0.0",
|
||||
"cheerio": "1.0.0",
|
||||
"chokidar": "4.0.3",
|
||||
"cls-hooked": "4.2.2",
|
||||
@@ -155,6 +148,7 @@
|
||||
"source-map-support": "0.5.21",
|
||||
"split.js": "1.6.5",
|
||||
"stream-throttle": "0.1.3",
|
||||
"strip-bom": "5.0.0",
|
||||
"striptags": "3.2.0",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"tmp": "0.2.3",
|
||||
@@ -162,19 +156,19 @@
|
||||
"turndown": "7.2.0",
|
||||
"unescape": "1.0.1",
|
||||
"vanilla-js-wheel-zoom": "9.0.4",
|
||||
"ws": "8.18.0",
|
||||
"ws": "8.18.1",
|
||||
"xml2js": "0.6.2",
|
||||
"yauzl": "3.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "7.6.1",
|
||||
"@electron-forge/maker-deb": "7.6.1",
|
||||
"@electron-forge/maker-dmg": "7.6.1",
|
||||
"@electron-forge/maker-flatpak": "7.6.1",
|
||||
"@electron-forge/maker-rpm": "7.6.1",
|
||||
"@electron-forge/maker-squirrel": "7.6.1",
|
||||
"@electron-forge/maker-zip": "7.6.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.6.1",
|
||||
"@electron-forge/cli": "7.7.0",
|
||||
"@electron-forge/maker-deb": "7.7.0",
|
||||
"@electron-forge/maker-dmg": "7.7.0",
|
||||
"@electron-forge/maker-flatpak": "7.7.0",
|
||||
"@electron-forge/maker-rpm": "7.7.0",
|
||||
"@electron-forge/maker-squirrel": "7.7.0",
|
||||
"@electron-forge/maker-zip": "7.7.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
|
||||
"@electron/rebuild": "3.7.1",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@types/archiver": "6.0.3",
|
||||
@@ -198,7 +192,7 @@
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/multer": "1.4.12",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/node": "22.13.5",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/safe-compare": "1.1.2",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
@@ -212,7 +206,7 @@
|
||||
"@types/ws": "8.5.14",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"@vitest/coverage-v8": "3.0.6",
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "34.2.0",
|
||||
"esm": "3.2.25",
|
||||
@@ -224,10 +218,10 @@
|
||||
"rimraf": "6.0.1",
|
||||
"swagger-jsdoc": "6.2.8",
|
||||
"tslib": "2.8.1",
|
||||
"tsx": "4.19.2",
|
||||
"typedoc": "0.27.7",
|
||||
"tsx": "4.19.3",
|
||||
"typedoc": "0.27.8",
|
||||
"typescript": "5.7.3",
|
||||
"vitest": "3.0.5",
|
||||
"vitest": "3.0.6",
|
||||
"webpack": "5.98.0",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-middleware": "7.4.2"
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
/**
|
||||
* Reads the level of indentation of the first line and trims the identation for all the text by that amount.
|
||||
*
|
||||
* For example, for:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "hello": "world"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* it results in:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "hello": "world"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This is meant to be used as a template string, where it allows the indentation of the template without affecting whitespace changes.
|
||||
*
|
||||
* @example const html = trimIndentation`\
|
||||
* <h1>Heading 1</h1>
|
||||
* <h2>Heading 2</h2>
|
||||
* <h3>Heading 3</h3>
|
||||
* <h4>Heading 4</h4>
|
||||
* <h5>Heading 5</h5>
|
||||
* <h6>Heading 6</h6>
|
||||
* `;
|
||||
* @param strings
|
||||
* @returns
|
||||
*/
|
||||
export function trimIndentation(strings: TemplateStringsArray) {
|
||||
const str = strings.toString();
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 323 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 515 KiB After Width: | Height: | Size: 515 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 397 KiB After Width: | Height: | Size: 397 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 26 KiB |
@@ -72,7 +72,7 @@
|
||||
<li>It's possible to drag across multiple days to set both the start and end
|
||||
date of a particular note.
|
||||
<br>
|
||||
<img src="4_Calendar View_image.png" width="425" height="91">
|
||||
<img src="3_Calendar View_image.png" width="425" height="91">
|
||||
</li>
|
||||
<li>Creating new notes from the calendar will respect the <code>~child:template</code> relation
|
||||
if set on the book note.</li>
|
||||
@@ -81,7 +81,7 @@
|
||||
<ul>
|
||||
<li>Hovering the mouse over an event will display information about the note.
|
||||
<br>
|
||||
<img src="5_Calendar View_image.png" width="323" height="160">
|
||||
<img src="4_Calendar View_image.png" width="323" height="160">
|
||||
</li>
|
||||
<li>Left clicking the event will go to that note. Middle clicking will open
|
||||
the note in a new tab and right click will offer more options including
|
||||
@@ -91,10 +91,93 @@
|
||||
edge of the event and dragging the mouse around.</li>
|
||||
</ul>
|
||||
<h2>Configuring the calendar</h2>
|
||||
<ul>
|
||||
<li>The first day of the week can be either Sunday or Monday and can be adjusted
|
||||
from the application settings.</li>
|
||||
</ul>
|
||||
<p>The following attributes can be added to the book type:</p>
|
||||
<figure class="table"
|
||||
style="width:100%;">
|
||||
<table class="ck-table-resized">
|
||||
<colgroup>
|
||||
<col style="width:29.49%;">
|
||||
<col style="width:70.51%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#calendar:hideWeekends</code>
|
||||
</td>
|
||||
<td>When present (regardless of value), it will hide Saturday and Sundays
|
||||
from the calendar.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:weekNumbers</code>
|
||||
</td>
|
||||
<td>When present (regardless of value), it will show the number of the week
|
||||
on the calendar.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>In addition, the first day of the week can be either Sunday or Monday
|
||||
and can be adjusted from the application settings.</p>
|
||||
<h2>Configuring the calendar events</h2>
|
||||
<p>For each note of the calendar, the following attributes can be used:</p>
|
||||
<figure
|
||||
class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>#startDate</code>
|
||||
</td>
|
||||
<td>The date the event starts, which will display it in the calendar. The
|
||||
format is <code>YYYY-MM-DD</code> (year, month and day separated by a minus
|
||||
sign).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#endDate</code>
|
||||
</td>
|
||||
<td>Similar to <code>startDate</code>, mentions the end date if the event spans
|
||||
across multiple days. The date is inclusive, so the end day is also considered.
|
||||
The attribute can be missing for single-day events.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#color</code>
|
||||
</td>
|
||||
<td>Displays the event with a specified color (named such as <code>red</code>, <code>gray</code> or
|
||||
hex such as <code>#FF0000</code>). This will also change the color of the
|
||||
note in other places such as the note tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:color</code>
|
||||
</td>
|
||||
<td>Similar to <code>#color</code>, but applies the color only for the event
|
||||
in the calendar and not for other places such as the note tree.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#iconClass</code>
|
||||
</td>
|
||||
<td>If present, the icon of the note will be displayed to the left of the
|
||||
event title.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>#calendar:title</code>
|
||||
</td>
|
||||
<td>Changes the title of an event to point to an attribute of the note other
|
||||
than the title, either a label (e.g. <code>#assignee</code>) or a relation
|
||||
(e.g. <code>~for</code>). See <i>Advanced use-cases</i> for more information.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<h2>How the calendar works</h2>
|
||||
<p>
|
||||
<img class="image-style-align-left" src="7_Calendar View_image.png" width="329"
|
||||
@@ -106,7 +189,32 @@
|
||||
<p>
|
||||
<img src="9_Calendar View_image.png" width="264" height="164">
|
||||
</p>
|
||||
<h2>Advanced use-cases</h2>
|
||||
<p>When not used in a Journal, the calendar is recursive. That is, it will
|
||||
look for events not just in its child notes but also in the children of
|
||||
these child notes.</p>
|
||||
<h2>Use-cases</h2>
|
||||
<h3>Using with the Journal / calendar</h3>
|
||||
<p>It is possible to integrate the calendar view into the Journal with day
|
||||
notes. In order to do so change the note type of the Journal note (calendar
|
||||
root) to Book and then select the Calendar View.</p>
|
||||
<p>Based on the <code>#calendarRoot</code> (or <code>#workspaceCalendarRoot</code>)
|
||||
attribute, the calendar will know that it's in a calendar and apply the
|
||||
following:</p>
|
||||
<ul>
|
||||
<li>The calendar events are now rendered based on their <code>dateNote</code> attribute
|
||||
rather than <code>startDate</code>.</li>
|
||||
<li>Interactive editing such as dragging over an empty era or resizing an
|
||||
event is no longer possible.</li>
|
||||
<li>Clicking on the empty space on a date will automatically open that day's
|
||||
note or create it if it does not exist.</li>
|
||||
<li>Direct children of a day note will be displayed on the calendar despite
|
||||
not having a <code>dateNote</code> attribute. Children of the child notes
|
||||
will not be displayed.</li>
|
||||
</ul>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1217/724;" src="18_Calendar View_image.png" width="1217"
|
||||
height="724">
|
||||
</figure>
|
||||
<h3>Using a different attribute as event title</h3>
|
||||
<p>By default, events are displayed on the calendar by their note title.
|
||||
However, it is possible to configure a different attribute to be displayed
|
||||
@@ -154,7 +262,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:294/151;" src="14_Calendar View_image.png" width="294"
|
||||
<img style="aspect-ratio:294/151;" src="13_Calendar View_image.png" width="294"
|
||||
height="151">
|
||||
</figure>
|
||||
</td>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Canvas</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Canvas</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Code</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Code</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -5,12 +5,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Geo map</title>
|
||||
<title data-trilium-title>Geo Map</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Geo map</h1>
|
||||
<h1 data-trilium-h1>Geo Map</h1>
|
||||
|
||||
<div class="ck-content">
|
||||
<h2>Creating a new geo map</h2>
|
||||
@@ -26,7 +26,7 @@
|
||||
<th>1</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:1256/1044;" src="10_Geo map_image.png" width="1256"
|
||||
<img style="aspect-ratio:1256/1044;" src="9_Geo Map_image.png" width="1256"
|
||||
height="1044">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -36,7 +36,7 @@
|
||||
<th>2</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:1720/1396;" src="3_Geo map_image.png" width="1720"
|
||||
<img style="aspect-ratio:1720/1396;" src="3_Geo Map_image.png" width="1720"
|
||||
height="1396">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -69,18 +69,18 @@
|
||||
<p>To create a marker, first navigate to the desired point on the map. Then
|
||||
press the
|
||||
<img class="image_resized" style="aspect-ratio:72/66;width:7.37%;"
|
||||
src="5_Geo map_image.png" width="72" height="66">button on the top-right of the map.</p>
|
||||
src="4_Geo Map_image.png" width="72" height="66">button on the top-right of the map.</p>
|
||||
<p>If the button is not visible, make sure the button section is visible
|
||||
by pressing the chevron button (
|
||||
<img class="image_resized" style="aspect-ratio:72/66;width:7.51%;"
|
||||
src="11_Geo map_image.png" width="72" height="66">) in the top-right of the map.</p>
|
||||
src="10_Geo Map_image.png" width="72" height="66">) in the top-right of the map.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>2</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:1730/416;" src="14_Geo map_image.png" width="1730"
|
||||
<img style="aspect-ratio:1730/416;" src="14_Geo Map_image.png" width="1730"
|
||||
height="416">
|
||||
</figure>
|
||||
<p> </p>
|
||||
@@ -96,7 +96,7 @@
|
||||
<th>3</th>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1586/404;" src="1_Geo map_image.png" width="1586"
|
||||
<img style="aspect-ratio:1586/404;" src="1_Geo Map_image.png" width="1586"
|
||||
height="404">
|
||||
</figure>
|
||||
<p> </p>
|
||||
@@ -107,7 +107,7 @@
|
||||
<th>4</th>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1696/608;" src="7_Geo map_image.png" width="1696"
|
||||
<img style="aspect-ratio:1696/608;" src="6_Geo Map_image.png" width="1696"
|
||||
height="608">
|
||||
</figure>
|
||||
<p> </p>
|
||||
@@ -122,7 +122,7 @@
|
||||
<p>The location of a marker is stored in the <code>#geolocation</code> attribute
|
||||
of the child notes:</p>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1288/278;" src="4_Geo map_image.png" width="1288"
|
||||
<img style="aspect-ratio:1288/278;" src="12_Geo Map_image.png" width="1288"
|
||||
height="278">
|
||||
</figure>
|
||||
<p>This value can be added manually if needed. The value of the attribute
|
||||
@@ -168,7 +168,7 @@
|
||||
<th>1</th>
|
||||
<td>
|
||||
<figure class="image image-style-align-center image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:732/918;" src="16_Geo map_image.png" width="732"
|
||||
<img style="aspect-ratio:732/918;" src="16_Geo Map_image.png" width="732"
|
||||
height="918">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -185,7 +185,7 @@
|
||||
<th>2</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:518/84;" src="19_Geo map_image.png" width="518"
|
||||
<img style="aspect-ratio:518/84;" src="19_Geo Map_image.png" width="518"
|
||||
height="84">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -199,7 +199,7 @@
|
||||
<th>3</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:1074/276;" src="12_Geo map_image.png" width="1074"
|
||||
<img style="aspect-ratio:1074/276;" src="11_Geo Map_image.png" width="1074"
|
||||
height="276">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -225,7 +225,7 @@
|
||||
<th>1</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:562/454;" src="17_Geo map_image.png" width="562"
|
||||
<img style="aspect-ratio:562/454;" src="17_Geo Map_image.png" width="562"
|
||||
height="454">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -236,7 +236,7 @@
|
||||
<th>2</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:696/480;" src="13_Geo map_image.png" width="696"
|
||||
<img style="aspect-ratio:696/480;" src="13_Geo Map_image.png" width="696"
|
||||
height="480">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -250,7 +250,7 @@
|
||||
<th>3</th>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:640/276;" src="2_Geo map_image.png" width="640"
|
||||
<img style="aspect-ratio:640/276;" src="2_Geo Map_image.png" width="640"
|
||||
height="276">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -275,7 +275,7 @@
|
||||
<th>1</th>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:226/74;" src="8_Geo map_image.png" width="226"
|
||||
<img style="aspect-ratio:226/74;" src="7_Geo Map_image.png" width="226"
|
||||
height="74">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -286,7 +286,7 @@
|
||||
<th>2</th>
|
||||
<td>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:322/222;" src="6_Geo map_image.png" width="322"
|
||||
<img style="aspect-ratio:322/222;" src="5_Geo Map_image.png" width="322"
|
||||
height="222">
|
||||
</figure>
|
||||
</td>
|
||||
@@ -297,7 +297,7 @@
|
||||
<th>3</th>
|
||||
<td>
|
||||
<figure class="image image_resized" style="width:100%;">
|
||||
<img style="aspect-ratio:620/530;" src="15_Geo map_image.png" width="620"
|
||||
<img style="aspect-ratio:620/530;" src="15_Geo Map_image.png" width="620"
|
||||
height="530">
|
||||
</figure>
|
||||
</td>
|
||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Mind Map</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Mind Map</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Note Map</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Note Map</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Relation Map</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Relation Map</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Render Note</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Render Note</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Saved Search</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Saved Search</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Text</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Text</h1>
|
||||
|
||||
<div class="ck-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,45 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Web View</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Web View</h1>
|
||||
|
||||
<div class="ck-content">
|
||||
<h2>Configuration</h2>
|
||||
<p>A webview needs to know which URL to render, and it can be provided by
|
||||
setting the <code>webViewSrc</code> attribute, such as:</p><pre><code class="language-text-x-trilium-auto">#webViewSrc="https://www.wikipedia.org"</code></pre>
|
||||
<h2>Web view on the server vs. Electron</h2>
|
||||
<p>When accessing Trilium via a browser instead of the desktop application,
|
||||
the web view will still try to render the content of the desired webpage.
|
||||
However, since it's running in a browser there are quite a few limitations
|
||||
as opposed to the desktop one.</p>
|
||||
<p>More specifically, quite a few websites oppose being embedded in another
|
||||
website (technically they have a non-permisive <code>X-Frame-Options</code> header).
|
||||
This is not bypassable by Trilium so the page will simply fail to render.</p>
|
||||
<p>You can diagnose this by right clicking the Trilium web page → Inspect
|
||||
(element) and looking in the “Console” tab for errors such as:</p>
|
||||
<ul>
|
||||
<li><code>Refused to display 'https://www.google.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.</code>
|
||||
</li>
|
||||
<li><code>Refused to frame 'https://duckduckgo.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self' https://html.duckduckgo.com".</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>There are a few websites that do render such as <code>wikipedia.org</code>.</p>
|
||||
<p>Do note that we are also applying some sandboxing constraints on the server
|
||||
side, so if you have any issues other than the unresolvable <code>X-Frame-Options</code> described
|
||||
above, feel free to report them.</p>
|
||||
<p>On the desktop side, a different technology is used which bypasses the
|
||||
constraints of an <code>iframe</code> (<code>webview</code>).</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,60 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Using promoted attributes to configure scripts</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<h1 data-trilium-h1>Using promoted attributes to configure scripts</h1>
|
||||
|
||||
<div class="ck-content">
|
||||
<p>A good use case of promoted attributes is to easily define the various
|
||||
parameters a script might need, for example an input and output note if
|
||||
it's processing data, or a checkbox to define a particular change in behavior
|
||||
for the script.</p>
|
||||
<p>
|
||||
<img src="Using promoted attributes .png" width="425" height="179">
|
||||
</p>
|
||||
<h2>Using check boxes to toggle flags</h2>
|
||||
<p>Instead of asking the user to modify a boolean value in the script, it's
|
||||
much more intuitive to use a checkbox for it as a promoted attribute.</p>
|
||||
<p>To do so, first define the promoted attribute:</p><pre><code class="language-text-x-trilium-auto">#label:groupByExtension="promoted,alias=Group by extension,single,boolean"</code></pre>
|
||||
<p>Then use it:</p><pre><code class="language-application-javascript-env-frontend">const byExtension = api.currentNote.getLabelValue("groupByExtension") === "true";
|
||||
if (byExtension) {
|
||||
// Do something.
|
||||
}</code></pre>
|
||||
<p>This will work equally well in both front-end and back-end scripts.</p>
|
||||
<h2>Using relations to select notes</h2>
|
||||
<p>One common use case for a script is to read data from another note and
|
||||
perhaps output its result in another note. To do so we need to define the
|
||||
following promoted attributes:</p><pre><code class="language-text-x-trilium-auto">#relation:input="promoted,alias=Input,single" #relation:output="promoted,alias=Output,single"</code></pre>
|
||||
<p>Once we have this, we can add some basic error handling to ensure that
|
||||
the fields are completed by the user:</p><pre><code class="language-text-x-trilium-auto">const inputNoteId = api.currentNote.getRelationValue("input");
|
||||
if (!inputNoteId) {
|
||||
api.showError("Missing input.");
|
||||
return;
|
||||
}
|
||||
|
||||
const outputNoteId = api.currentNote.getRelationValue("output");
|
||||
if (!outputNoteId) {
|
||||
api.showError("Missing output.");
|
||||
return;
|
||||
}</code></pre>
|
||||
<p>Note that here we are using <code>api.showError</code> which is only available
|
||||
for frontend notes. If you are writing a backend note, simply remove <code>api.showError</code> but
|
||||
the user will no feedback on why the script did not execute properly.</p>
|
||||
<p>Afterwards we can simply read the note and do something with it:</p><pre><code class="language-text-x-trilium-auto">const note = api.getNote(inputNoteId);
|
||||
if (!note) {
|
||||
return;
|
||||
}
|
||||
const content = note.getContent().toString("utf-8");</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../../style.css">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Creating a custom theme</title>
|
||||
</head>
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../../style.css">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Customize the Next theme</title>
|
||||
</head>
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../../../style.css">
|
||||
<link rel="stylesheet" href="../../style.css">
|
||||
<base target="_parent">
|
||||
<title data-trilium-title>Reference</title>
|
||||
</head>
|
||||
@@ -155,7 +155,7 @@ body.electron:not(.native-titlebar) {
|
||||
}</code></pre>
|
||||
<h2>Custom fonts</h2>
|
||||
<p>Currently the only way to include a custom font is to use <a class="reference-link"
|
||||
href="../Custom%20resource%20providers.html">Custom resource providers</a>.
|
||||
href="../Advanced%20topics/Custom%20resource%20providers.html">Custom resource providers</a>.
|
||||
Basically import a font into Trilium and assign it <code>#customResourceProvider=fonts/myfont.ttf</code> and
|
||||
then import the font in CSS via <code>/custom/fonts/myfont.ttf</code>.</p>
|
||||
<h2>Dark and light themes</h2>
|
||||
@@ -19,9 +19,17 @@
|
||||
</li>
|
||||
<li>Note Types
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Note%20Types/Mermaid%20Diagram.html" target="detail">Mermaid Diagram</a>
|
||||
<li><a href="User%20Guide/Note%20Types/Text.html" target="detail">Text</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Geo%20map.html" target="detail">Geo map</a>
|
||||
<li><a href="User%20Guide/Note%20Types/Code.html" target="detail">Code</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Saved%20Search.html" target="detail">Saved Search</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Relation%20Map.html" target="detail">Relation Map</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Note%20Map.html" target="detail">Note Map</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Render%20Note.html" target="detail">Render Note</a>
|
||||
</li>
|
||||
<li>Book
|
||||
<ul>
|
||||
@@ -29,6 +37,16 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Mermaid%20Diagram.html" target="detail">Mermaid Diagram</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Canvas.html" target="detail">Canvas</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Web%20View.html" target="detail">Web View</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Mind%20Map.html" target="detail">Mind Map</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Note%20Types/Geo%20Map.html" target="detail">Geo Map</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Shared notes
|
||||
@@ -38,6 +56,18 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Theme development
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Theme%20development/Creating%20a%20custom%20theme.html"
|
||||
target="detail">Creating a custom theme</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Theme%20development/Customize%20the%20Next%20theme.html"
|
||||
target="detail">Customize the Next theme</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Theme%20development/Reference.html" target="detail">Reference</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Scripting
|
||||
<ul>
|
||||
<li>Examples
|
||||
@@ -47,38 +77,28 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Advanced usage
|
||||
<ul>
|
||||
<li>Theme development
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Advanced%20usage/Theme%20development/Creating%20a%20custom%20theme.html"
|
||||
target="detail">Creating a custom theme</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Advanced%20usage/Theme%20development/Customize%20the%20Next%20theme.html"
|
||||
target="detail">Customize the Next theme</a>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Advanced%20usage/Theme%20development/Reference.html"
|
||||
target="detail">Reference</a>
|
||||
<li><a href="User%20Guide/Scripting/Using%20promoted%20attributes%20to%20c.html"
|
||||
target="detail">Using promoted attributes to configure scripts</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="User%20Guide/Advanced%20usage/Custom%20resource%20providers.html"
|
||||
<li>Advanced topics
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Advanced%20topics/Custom%20resource%20providers.html"
|
||||
target="detail">Custom resource providers</a>
|
||||
</li>
|
||||
<li>REST API
|
||||
<ul>
|
||||
<li>ETAPI
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Advanced%20usage/REST%20API/ETAPI/API%20Reference.dat"
|
||||
<li><a href="User%20Guide/Advanced%20topics/REST%20API/ETAPI/API%20Reference.dat"
|
||||
target="detail">API Reference</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Internal API
|
||||
<ul>
|
||||
<li><a href="User%20Guide/Advanced%20usage/REST%20API/Internal%20API/API%20Reference.dat"
|
||||
<li><a href="User%20Guide/Advanced%20topics/REST%20API/Internal%20API/API%20Reference.dat"
|
||||
target="detail">API Reference</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -40,6 +40,10 @@ export default class NoteListRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
get isFullHeight() {
|
||||
return this.viewMode?.isFullHeight;
|
||||
}
|
||||
|
||||
async renderList() {
|
||||
if (!this.viewMode) {
|
||||
return null;
|
||||
|
||||
@@ -30,6 +30,27 @@ function parseDate(str: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/a/30465299/4898894
|
||||
function getMonthsInDateRange(startDate: string, endDate: string) {
|
||||
const start = startDate.split('-');
|
||||
const end = endDate.split('-');
|
||||
const startYear = parseInt(start[0]);
|
||||
const endYear = parseInt(end[0]);
|
||||
const dates = [];
|
||||
|
||||
for (let i = startYear; i <= endYear; i++) {
|
||||
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
||||
const startMon = i === startYear ? parseInt(start[1])-1 : 0;
|
||||
|
||||
for(let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j+1) {
|
||||
const month = j+1;
|
||||
const displayMonth = month < 10 ? '0'+month : month;
|
||||
dates.push([i, displayMonth].join('-'));
|
||||
}
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
function padNum(num: number) {
|
||||
return `${num <= 9 ? "0" : ""}${num}`;
|
||||
}
|
||||
@@ -621,6 +642,7 @@ export default {
|
||||
reloadFrontendApp,
|
||||
reloadTray,
|
||||
parseDate,
|
||||
getMonthsInDateRange,
|
||||
formatDateISO,
|
||||
formatDateTime,
|
||||
formatTimeInterval,
|
||||
|
||||
@@ -15,6 +15,11 @@ const TPL = `
|
||||
.note-list-widget .note-list {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.note-list-widget.full-height,
|
||||
.note-list-widget.full-height .note-list-widget-content {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="note-list-widget-content">
|
||||
@@ -68,6 +73,7 @@ export default class NoteListWidget extends NoteContextAwareWidget {
|
||||
|
||||
async renderNoteList(note: FNote) {
|
||||
const noteListRenderer = new NoteListRenderer(this.$content, note, note.getChildNoteIds());
|
||||
this.$widget.toggleClass("full-height", noteListRenderer.isFullHeight);
|
||||
await noteListRenderer.renderList();
|
||||
this.viewMode = noteListRenderer.viewMode;
|
||||
}
|
||||
@@ -111,8 +117,10 @@ export default class NoteListWidget extends NoteContextAwareWidget {
|
||||
this.checkRenderStatus();
|
||||
}
|
||||
|
||||
if (this.viewMode) {
|
||||
this.viewMode.entitiesReloadedEvents(e);
|
||||
// Inform the view mode of changes and refresh if needed.
|
||||
if (this.viewMode && this.viewMode.onEntitiesReloaded(e)) {
|
||||
this.refresh();
|
||||
this.checkRenderStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,19 @@ import mimeTypesService from "../services/mime_types.js";
|
||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
import dialogService from "../services/dialog.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import type FNote from "../entities/fnote.js";
|
||||
import type { NoteType } from "../entities/fnote.js";
|
||||
import type { EventData } from "../components/app_context.js";
|
||||
|
||||
const NOTE_TYPES = [
|
||||
interface NoteTypeMapping {
|
||||
type: NoteType;
|
||||
mime?: string;
|
||||
title: string;
|
||||
isBeta?: boolean;
|
||||
selectable: boolean;
|
||||
}
|
||||
|
||||
const NOTE_TYPES: NoteTypeMapping[] = [
|
||||
// The suggested note type ordering method: insert the item into the corresponding group,
|
||||
// then ensure the items within the group are ordered alphabetically.
|
||||
|
||||
@@ -67,9 +78,16 @@ const TPL = `
|
||||
`;
|
||||
|
||||
export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
|
||||
private dropdown!: bootstrap.Dropdown;
|
||||
private $noteTypeDropdown!: JQuery<HTMLElement>;
|
||||
private $noteTypeButton!: JQuery<HTMLElement>;
|
||||
private $noteTypeDesc!: JQuery<HTMLElement>;
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
//@ts-ignore
|
||||
this.dropdown = bootstrap.Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']"));
|
||||
|
||||
this.$widget.on("show.bs.dropdown", () => this.renderDropdown());
|
||||
@@ -81,7 +99,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
this.$widget.on("click", ".dropdown-item", () => this.dropdown.toggle());
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
async refreshWithNote(note: FNote) {
|
||||
this.$noteTypeButton.prop("disabled", () => NOT_SELECTABLE_NOTE_TYPES.includes(note.type));
|
||||
|
||||
this.$noteTypeDesc.text(await this.findTypeTitle(note.type, note.mime));
|
||||
@@ -93,8 +111,12 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
async renderDropdown() {
|
||||
this.$noteTypeDropdown.empty();
|
||||
|
||||
if (!this.note) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const noteType of NOTE_TYPES.filter((nt) => nt.selectable)) {
|
||||
let $typeLink;
|
||||
let $typeLink: JQuery<HTMLElement>;
|
||||
|
||||
const $title = $("<span>").text(noteType.title);
|
||||
if (noteType.isBeta) {
|
||||
@@ -110,7 +132,9 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
const type = $typeLink.attr("data-note-type");
|
||||
const noteType = NOTE_TYPES.find((nt) => nt.type === type);
|
||||
|
||||
if (noteType) {
|
||||
this.save(noteType.type, noteType.mime);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$noteTypeDropdown.append('<div class="dropdown-divider"></div>');
|
||||
@@ -136,7 +160,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
.on("click", (e) => {
|
||||
const $link = $(e.target).closest(".dropdown-item");
|
||||
|
||||
this.save("code", $link.attr("data-mime-type"));
|
||||
this.save("code", $link.attr("data-mime-type") ?? "");
|
||||
});
|
||||
|
||||
if (this.note.type === "code" && this.note.mime === mimeType.mime) {
|
||||
@@ -149,7 +173,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
async findTypeTitle(type, mime) {
|
||||
async findTypeTitle(type: NoteType, mime: string) {
|
||||
if (type === "code") {
|
||||
const mimeTypes = mimeTypesService.getMimeTypes();
|
||||
const found = mimeTypes.find((mt) => mt.mime === mime);
|
||||
@@ -162,12 +186,12 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
async save(type, mime) {
|
||||
if (type === this.note.type && mime === this.note.mime) {
|
||||
async save(type: NoteType, mime?: string) {
|
||||
if (type === this.note?.type && mime === this.note?.mime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type !== this.note.type && !(await this.confirmChangeIfContent())) {
|
||||
if (type !== this.note?.type && !(await this.confirmChangeIfContent())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,16 +199,20 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
|
||||
async confirmChangeIfContent() {
|
||||
if (!this.note) {
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await this.note.getBlob();
|
||||
|
||||
if (!blob.content || !blob.content.trim().length) {
|
||||
if (!blob?.content || !blob.content.trim().length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return await dialogService.confirm(t("note_types.confirm-change"));
|
||||
}
|
||||
|
||||
async entitiesReloadedEvent({ loadResults }) {
|
||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
if (loadResults.isNoteReloaded(this.noteId)) {
|
||||
this.refresh();
|
||||
}
|
||||
@@ -155,6 +155,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
.attr("data-attribute-type", valueAttr.type)
|
||||
.attr("data-attribute-name", valueAttr.name)
|
||||
.prop("value", valueAttr.value)
|
||||
.prop("placeholder", t("promoted_attributes.unset-field-placeholder"))
|
||||
.addClass("form-control")
|
||||
.addClass("promoted-attribute-input")
|
||||
.on("change", (event) => this.promotedAttributeChanged(event));
|
||||
@@ -226,6 +227,9 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
} else if (definition.labelType === "boolean") {
|
||||
$input.prop("type", "checkbox");
|
||||
|
||||
$input.wrap($(`<label class="tn-checkbox"></label>`));
|
||||
$wrapper.find(".input-group").removeClass("input-group");
|
||||
|
||||
if (valueAttr.value === "true") {
|
||||
$input.prop("checked", "checked");
|
||||
}
|
||||
@@ -272,7 +276,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
|
||||
if (definition.multiplicity === "multi") {
|
||||
const $addButton = $("<span>")
|
||||
.addClass("bx bx-plus pointer")
|
||||
.addClass("bx bx-plus pointer tn-tool-button")
|
||||
.prop("title", t("promoted_attributes.add_new_attribute"))
|
||||
.on("click", async () => {
|
||||
const $new = await this.createPromotedAttributeCell(
|
||||
@@ -292,7 +296,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
});
|
||||
|
||||
const $removeButton = $("<span>")
|
||||
.addClass("bx bx-trash pointer")
|
||||
.addClass("bx bx-trash pointer tn-tool-button")
|
||||
.prop("title", t("promoted_attributes.remove_this_attribute"))
|
||||
.on("click", async () => {
|
||||
const attributeId = $input.attr("data-attribute-id");
|
||||
|
||||
@@ -14,7 +14,8 @@ import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_onl
|
||||
import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js";
|
||||
import ImageOptions from "./options/images/images.js";
|
||||
import SpellcheckOptions from "./options/spellcheck.js";
|
||||
import PasswordOptions from "./options/password.js";
|
||||
import PasswordOptions from "./options/password/password.js";
|
||||
import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"
|
||||
import EtapiOptions from "./options/etapi.js";
|
||||
import BackupOptions from "./options/backup.js";
|
||||
import SyncOptions from "./options/sync.js";
|
||||
@@ -35,6 +36,7 @@ import RibbonOptions from "./options/appearance/ribbon.js";
|
||||
import LocalizationOptions from "./options/appearance/i18n.js";
|
||||
import CodeBlockOptions from "./options/appearance/code_block.js";
|
||||
import EditorOptions from "./options/text_notes/editor.js";
|
||||
import ShareSettingsOptions from "./options/other/share_settings.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import type NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
|
||||
@@ -64,7 +66,7 @@ const CONTENT_WIDGETS: Record<string, (typeof NoteContextAwareWidget)[]> = {
|
||||
_optionsCodeNotes: [VimKeyBindingsOptions, WrapLinesOptions, CodeAutoReadOnlySizeOptions, CodeMimeTypesOptions],
|
||||
_optionsImages: [ImageOptions],
|
||||
_optionsSpellcheck: [SpellcheckOptions],
|
||||
_optionsPassword: [PasswordOptions],
|
||||
_optionsPassword: [PasswordOptions, ProtectedSessionTimeoutOptions],
|
||||
_optionsEtapi: [EtapiOptions],
|
||||
_optionsBackup: [BackupOptions],
|
||||
_optionsSync: [SyncOptions],
|
||||
@@ -76,14 +78,14 @@ const CONTENT_WIDGETS: Record<string, (typeof NoteContextAwareWidget)[]> = {
|
||||
RevisionsSnapshotIntervalOptions,
|
||||
RevisionSnapshotsLimitOptions,
|
||||
NetworkConnectionsOptions,
|
||||
HtmlImportTagsOptions
|
||||
HtmlImportTagsOptions,
|
||||
ShareSettingsOptions
|
||||
],
|
||||
_optionsAdvanced: [DatabaseIntegrityCheckOptions, DatabaseAnonymizationOptions, AdvancedSyncOptions, VacuumDatabaseOptions],
|
||||
_backendLog: [BackendLogWidget]
|
||||
};
|
||||
|
||||
export default class ContentWidgetTypeWidget extends TypeWidget {
|
||||
|
||||
private $content!: JQuery<HTMLElement>;
|
||||
|
||||
static getType() {
|
||||
|
||||
@@ -223,7 +223,9 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
||||
}
|
||||
|
||||
$classicToolbarWidget.empty();
|
||||
if ($classicToolbarWidget.length) {
|
||||
$classicToolbarWidget[0].appendChild(editor.ui.view.toolbar.element);
|
||||
}
|
||||
|
||||
if (utils.isMobile()) {
|
||||
$classicToolbarWidget.addClass("visible");
|
||||
|
||||
@@ -4,6 +4,8 @@ import { t } from "../../services/i18n.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
|
||||
const TEXT_MAX_NUM_CHARS = 5000;
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-file note-detail-printable">
|
||||
<style>
|
||||
@@ -29,6 +31,10 @@ const TPL = `
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="file-preview-too-big alert alert-info hidden-ext">
|
||||
${t("file.too_big", { maxNumChars: TEXT_MAX_NUM_CHARS })}
|
||||
</div>
|
||||
|
||||
<pre class="file-preview-content"></pre>
|
||||
|
||||
<div class="file-preview-not-available alert alert-info">
|
||||
@@ -46,6 +52,7 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
|
||||
private $previewContent!: JQuery<HTMLElement>;
|
||||
private $previewNotAvailable!: JQuery<HTMLElement>;
|
||||
private $previewTooBig!: JQuery<HTMLElement>;
|
||||
private $pdfPreview!: JQuery<HTMLElement>;
|
||||
private $videoPreview!: JQuery<HTMLElement>;
|
||||
private $audioPreview!: JQuery<HTMLElement>;
|
||||
@@ -58,6 +65,7 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
this.$widget = $(TPL);
|
||||
this.$previewContent = this.$widget.find(".file-preview-content");
|
||||
this.$previewNotAvailable = this.$widget.find(".file-preview-not-available");
|
||||
this.$previewTooBig = this.$widget.find(".file-preview-too-big");
|
||||
this.$pdfPreview = this.$widget.find(".pdf-preview");
|
||||
this.$videoPreview = this.$widget.find(".video-preview");
|
||||
this.$audioPreview = this.$widget.find(".audio-preview");
|
||||
@@ -73,12 +81,17 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
this.$previewContent.empty().hide();
|
||||
this.$pdfPreview.attr("src", "").empty().hide();
|
||||
this.$previewNotAvailable.hide();
|
||||
this.$previewTooBig.addClass("hidden-ext");
|
||||
this.$videoPreview.hide();
|
||||
this.$audioPreview.hide();
|
||||
|
||||
if (blob?.content) {
|
||||
this.$previewContent.show().scrollTop(0);
|
||||
this.$previewContent.text(blob.content);
|
||||
const trimmedContent = blob.content.substring(0, TEXT_MAX_NUM_CHARS);
|
||||
if (trimmedContent.length !== blob.content.length) {
|
||||
this.$previewTooBig.removeClass("hidden-ext");
|
||||
}
|
||||
this.$previewContent.text(trimmedContent);
|
||||
} else if (note.mime === "application/pdf") {
|
||||
this.$pdfPreview.show().attr("src", openService.getUrlForDownload(`api/notes/${this.noteId}/open`));
|
||||
} else if (note.mime.startsWith("video/")) {
|
||||
|
||||
@@ -135,10 +135,22 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
throw new Error(t("geo-map.unable-to-load-map"));
|
||||
}
|
||||
|
||||
if (!this.note) {
|
||||
return;
|
||||
this.#restoreViewportAndZoom();
|
||||
|
||||
// Restore markers.
|
||||
await this.#reloadMarkers();
|
||||
|
||||
const updateFn = () => this.spacedUpdate.scheduleUpdate();
|
||||
map.on("moveend", updateFn);
|
||||
map.on("zoomend", updateFn);
|
||||
map.on("click", (e) => this.#onMapClicked(e));
|
||||
}
|
||||
|
||||
async #restoreViewportAndZoom() {
|
||||
const map = this.geoMapWidget.map;
|
||||
if (!map || !this.note) {
|
||||
return;
|
||||
}
|
||||
const blob = await this.note.getBlob();
|
||||
|
||||
let parsedContent: MapData = {};
|
||||
@@ -150,14 +162,6 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
const center = parsedContent.view?.center ?? DEFAULT_COORDINATES;
|
||||
const zoom = parsedContent.view?.zoom ?? DEFAULT_ZOOM;
|
||||
map.setView(center, zoom);
|
||||
|
||||
// Restore markers.
|
||||
await this.#reloadMarkers();
|
||||
|
||||
const updateFn = () => this.spacedUpdate.scheduleUpdate();
|
||||
map.on("moveend", updateFn);
|
||||
map.on("zoomend", updateFn);
|
||||
map.on("click", (e) => this.#onMapClicked(e));
|
||||
}
|
||||
|
||||
async #reloadMarkers() {
|
||||
@@ -218,7 +222,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
|
||||
const [ lat, lng ] = latLng.split(",", 2).map((el) => parseFloat(el));
|
||||
const L = this.L;
|
||||
const icon = this.#buildIcon(note.getIcon(), note.title);
|
||||
const icon = this.#buildIcon(note.getIcon(), note.getColorClass(), note.title);
|
||||
|
||||
const marker = L.marker(L.latLng(lat, lng), {
|
||||
icon,
|
||||
@@ -253,12 +257,12 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
this.currentMarkerData[note.noteId] = marker;
|
||||
}
|
||||
|
||||
#buildIcon(bxIconClass: string, title: string) {
|
||||
#buildIcon(bxIconClass: string, colorClass: string, title: string) {
|
||||
return this.L.divIcon({
|
||||
html: `\
|
||||
<img class="icon" src="${asset_path}/node_modules/leaflet/dist/images/marker-icon.png" />
|
||||
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
|
||||
<span class="bx ${bxIconClass}"></span>
|
||||
<span class="bx ${bxIconClass} ${colorClass}"></span>
|
||||
<span class="title-label">${title}</span>`,
|
||||
iconSize: [ 25, 41 ],
|
||||
iconAnchor: [ 12, 41 ]
|
||||
@@ -343,6 +347,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
|
||||
async doRefresh(note: FNote) {
|
||||
await this.geoMapWidget.refresh();
|
||||
this.#restoreViewportAndZoom();
|
||||
await this.#reloadMarkers();
|
||||
}
|
||||
|
||||
@@ -356,7 +361,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
// If any of note has its location attribute changed.
|
||||
// TODO: Should probably filter by parent here as well.
|
||||
const attributeRows = loadResults.getAttributeRows();
|
||||
if (attributeRows.find((at) => at.name === LOCATION_ATTRIBUTE)) {
|
||||
if (attributeRows.find((at) => [ LOCATION_ATTRIBUTE, "color" ].includes(at.name ?? ""))) {
|
||||
this.#reloadMarkers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,37 @@ import OptionsWidget from "../options_widget.js";
|
||||
import server from "../../../../services/server.js";
|
||||
import toastService from "../../../../services/toast.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("attachment_erasure_timeout.attachment_erasure_timeout")}</h4>
|
||||
|
||||
<p>${t("attachment_erasure_timeout.attachment_auto_deletion_description")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t("attachment_erasure_timeout.erase_attachments_after_x_seconds")}</label>
|
||||
<input class="erase-unused-attachments-after-time-in-seconds form-control options-number-input" type="number" min="0">
|
||||
</div>
|
||||
|
||||
<div id="time-selector-placeholder"></div>
|
||||
<p>${t("attachment_erasure_timeout.manual_erasing_description")}</p>
|
||||
|
||||
<button class="erase-unused-attachments-now-button btn btn-secondary">${t("attachment_erasure_timeout.erase_unused_attachments_now")}</button>
|
||||
</div>`;
|
||||
|
||||
export default class AttachmentErasureTimeoutOptions extends OptionsWidget {
|
||||
|
||||
private $eraseUnusedAttachmentsAfterTimeInSeconds!: JQuery<HTMLElement>;
|
||||
export default class AttachmentErasureTimeoutOptions extends TimeSelector {
|
||||
private $eraseUnusedAttachmentsNowButton!: JQuery<HTMLElement>;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "erase-unused-attachments-after",
|
||||
widgetLabelId: "attachment_erasure_timeout.erase_attachments_after",
|
||||
optionValueId: "eraseUnusedAttachmentsAfterSeconds",
|
||||
optionTimeScaleId: "eraseUnusedAttachmentsAfterTimeScale"
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
this.$widget = $(TPL);
|
||||
this.$eraseUnusedAttachmentsAfterTimeInSeconds = this.$widget.find(".erase-unused-attachments-after-time-in-seconds");
|
||||
this.$eraseUnusedAttachmentsAfterTimeInSeconds.on("change", () => this.updateOption("eraseUnusedAttachmentsAfterSeconds", this.$eraseUnusedAttachmentsAfterTimeInSeconds.val()));
|
||||
// inject TimeSelector widget template
|
||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
|
||||
|
||||
this.$eraseUnusedAttachmentsNowButton = this.$widget.find(".erase-unused-attachments-now-button");
|
||||
this.$eraseUnusedAttachmentsNowButton.on("click", () => {
|
||||
@@ -37,8 +41,4 @@ export default class AttachmentErasureTimeoutOptions extends OptionsWidget {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
this.$eraseUnusedAttachmentsAfterTimeInSeconds.val(options.eraseUnusedAttachmentsAfterSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,118 +3,42 @@ import server from "../../../../services/server.js";
|
||||
import toastService from "../../../../services/toast.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("note_erasure_timeout.note_erasure_timeout_title")}</h4>
|
||||
|
||||
<p>${t("note_erasure_timeout.note_erasure_description")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="erase-entities-after-time">${t("note_erasure_timeout.erase_notes_after")}</label>
|
||||
<div class="d-flex gap-2">
|
||||
<input id="erase-entities-after-time" class="form-control options-number-input" type="number" min="0" steps="1" required>
|
||||
<!-- TriliumNextTODO: i18n the strings when refactoring this to a standalone widget -->
|
||||
<select id="erase-entities-after-time-scale" class="form-select duration-selector" required>
|
||||
<option value="1">${t("duration.seconds")}</option>
|
||||
<option value="60">${t("duration.minutes")}</option>
|
||||
<option value="3600">${t("duration.hours")}</option>
|
||||
<option value="86400">${t("duration.days")}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="time-selector-placeholder"></div>
|
||||
<p>${t("note_erasure_timeout.manual_erasing_description")}</p>
|
||||
|
||||
<button id="erase-deleted-notes-now-button" class="btn btn-secondary">${t("note_erasure_timeout.erase_deleted_notes_now")}</button>
|
||||
|
||||
<style>
|
||||
.duration-selector {
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
</div>`;
|
||||
|
||||
export default class NoteErasureTimeoutOptions extends OptionsWidget {
|
||||
|
||||
private $eraseEntitiesAfterTime!: JQuery<HTMLInputElement>;
|
||||
private $eraseEntitiesAfterTimeScale!: JQuery<HTMLSelectElement>;
|
||||
export default class NoteErasureTimeoutOptions extends TimeSelector {
|
||||
private $eraseDeletedNotesButton!: JQuery<HTMLButtonElement>;
|
||||
private eraseEntitiesAfterTimeInSeconds!: string | number;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "erase-entities-after",
|
||||
widgetLabelId: "note_erasure_timeout.erase_notes_after",
|
||||
optionValueId: "eraseEntitiesAfterTimeInSeconds",
|
||||
optionTimeScaleId: "eraseEntitiesAfterTimeScale"
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
// inject TimeSelector widget template
|
||||
this.$widget = $(TPL);
|
||||
this.$eraseEntitiesAfterTime = this.$widget.find("#erase-entities-after-time");
|
||||
this.$eraseEntitiesAfterTimeScale = this.$widget.find("#erase-entities-after-time-scale");
|
||||
|
||||
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.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
||||
|
||||
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"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("revisions_snapshot_interval.note_revisions_snapshot_interval_title")}</h4>
|
||||
|
||||
<p class="use-tn-links">${t("revisions_snapshot_interval.note_revisions_snapshot_description")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t("revisions_snapshot_interval.snapshot_time_interval_label")}</label>
|
||||
<input class="revision-snapshot-time-interval-in-seconds form-control options-number-input" type="number" min="10">
|
||||
</div>
|
||||
<div id="time-selector-placeholder"></div>
|
||||
</div>`;
|
||||
|
||||
export default class RevisionsSnapshotIntervalOptions extends OptionsWidget {
|
||||
export default class RevisionsSnapshotIntervalOptions extends TimeSelector {
|
||||
|
||||
private $revisionsTimeInterval!: JQuery<HTMLElement>;
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "revision-snapshot-time-interval",
|
||||
widgetLabelId: "revisions_snapshot_interval.snapshot_time_interval_label",
|
||||
optionValueId: "revisionSnapshotTimeInterval",
|
||||
optionTimeScaleId: "revisionSnapshotTimeIntervalTimeScale",
|
||||
minimumSeconds: 10
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
// inject TimeSelector widget template
|
||||
this.$widget = $(TPL);
|
||||
this.$revisionsTimeInterval = this.$widget.find(".revision-snapshot-time-interval-in-seconds");
|
||||
this.$revisionsTimeInterval.on("change", () => this.updateOption("revisionSnapshotTimeInterval", this.$revisionsTimeInterval.val()));
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
this.$revisionsTimeInterval.val(options.revisionSnapshotTimeInterval);
|
||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import options from "../../../../services/options.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import type { OptionMap, OptionNames } from "../../../../../../services/options_interface.js";
|
||||
import searchService from "../../../../services/search.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("share.title")}</h4>
|
||||
|
||||
<label class="tn-checkbox">
|
||||
<input class="form-check-input" type="checkbox" name="redirectBareDomain" value="true">
|
||||
${t("share.redirect_bare_domain")}
|
||||
</label>
|
||||
<p class="form-text">${t("share.redirect_bare_domain_description")}</p>
|
||||
|
||||
<label class="tn-checkbox">
|
||||
<input class="form-check-input" type="checkbox" name="showLoginInShareTheme" value="true">
|
||||
${t("share.show_login_link")}
|
||||
</label>
|
||||
<p class="form-text">${t("share.show_login_link_description")}</p>
|
||||
</div>`;
|
||||
|
||||
export default class ShareSettingsOptions extends OptionsWidget {
|
||||
private $shareRootCheck!: JQuery<HTMLElement>;
|
||||
private $shareRootStatus!: JQuery<HTMLElement>;
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.contentSized();
|
||||
|
||||
this.$shareRootCheck = this.$widget.find('.share-root-check');
|
||||
this.$shareRootStatus = this.$widget.find('.share-root-status');
|
||||
|
||||
// Add change handlers for both checkboxes
|
||||
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
|
||||
this.save();
|
||||
|
||||
// Show/hide share root status section based on redirectBareDomain checkbox
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.name === 'redirectBareDomain') {
|
||||
this.$shareRootCheck.toggle(target.checked);
|
||||
if (target.checked) {
|
||||
this.checkShareRoot();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add click handler for check share root button
|
||||
this.$widget.find('.check-share-root').on("click", () => this.checkShareRoot());
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
const redirectBareDomain = options.redirectBareDomain === "true";
|
||||
this.$widget.find('input[name="redirectBareDomain"]').prop("checked", redirectBareDomain);
|
||||
this.$shareRootCheck.toggle(redirectBareDomain);
|
||||
if (redirectBareDomain) {
|
||||
await this.checkShareRoot();
|
||||
}
|
||||
|
||||
this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked", options.showLoginInShareTheme === "true");
|
||||
}
|
||||
|
||||
async checkShareRoot() {
|
||||
const $button = this.$widget.find('.check-share-root');
|
||||
$button.prop('disabled', true);
|
||||
|
||||
try {
|
||||
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
|
||||
const sharedShareRootNote = shareRootNotes.find(note => note.isShared());
|
||||
|
||||
if (sharedShareRootNote) {
|
||||
this.$shareRootStatus
|
||||
.removeClass('text-danger')
|
||||
.addClass('text-success')
|
||||
.text(t("share.share_root_found", {noteTitle: sharedShareRootNote.title}));
|
||||
} else {
|
||||
this.$shareRootStatus
|
||||
.removeClass('text-success')
|
||||
.addClass('text-danger')
|
||||
.text(shareRootNotes.length > 0
|
||||
? t("share.share_root_not_shared", {noteTitle: shareRootNotes[0].title})
|
||||
: t("share.share_root_not_found"));
|
||||
}
|
||||
} finally {
|
||||
$button.prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
const redirectBareDomain = this.$widget.find('input[name="redirectBareDomain"]').prop("checked");
|
||||
await this.updateOption<"redirectBareDomain">("redirectBareDomain", redirectBareDomain.toString());
|
||||
|
||||
const showLoginInShareTheme = this.$widget.find('input[name="showLoginInShareTheme"]').prop("checked");
|
||||
await this.updateOption<"showLoginInShareTheme">("showLoginInShareTheme", showLoginInShareTheme.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import server from "../../../services/server.js";
|
||||
import protectedSessionHolder from "../../../services/protected_session_holder.js";
|
||||
import toastService from "../../../services/toast.js";
|
||||
import OptionsWidget from "./options_widget.js";
|
||||
import type { OptionMap } from "../../../../../services/options_interface.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import server from "../../../../services/server.js";
|
||||
import protectedSessionHolder from "../../../../services/protected_session_holder.js";
|
||||
import toastService from "../../../../services/toast.js";
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import type { OptionMap } from "../../../../../../services/options_interface.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
@@ -32,17 +32,7 @@ const TPL = `
|
||||
<button class="save-password-button btn btn-primary">${t("password.change_password")}</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="options-section">
|
||||
<h4>${t("password.protected_session_timeout")}</h4>
|
||||
|
||||
<p>${t("password.protected_session_timeout_description")} <a class="tn-link" href="https://triliumnext.github.io/Docs/Wiki/protected-notes.html" class="external">${t("password.wiki")}</a> ${t("password.for_more_info")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="protected-session-timeout-in-seconds">${t("password.protected_session_timeout_label")}</label>
|
||||
<input id="protected-session-timeout-in-seconds" class="protected-session-timeout-in-seconds form-control options-number-input" type="number" min="60">
|
||||
</div>
|
||||
</div>`;
|
||||
`;
|
||||
|
||||
// TODO: Deduplicate
|
||||
interface ChangePasswordResponse {
|
||||
@@ -0,0 +1,30 @@
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import TimeSelector from "../time_selector.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("password.protected_session_timeout")}</h4>
|
||||
|
||||
<p>${t("password.protected_session_timeout_description")} <a class="tn-link" href="https://triliumnext.github.io/Docs/Wiki/protected-notes.html" class="external">${t("password.wiki")}</a> ${t("password.for_more_info")}</p>
|
||||
<div id="time-selector-placeholder"></div>
|
||||
</div>`;
|
||||
|
||||
export default class ProtectedSessionTimeoutOptions extends TimeSelector {
|
||||
constructor() {
|
||||
super({
|
||||
widgetId: "protected-session-timeout",
|
||||
widgetLabelId: "password.protected_session_timeout_label",
|
||||
optionValueId: "protectedSessionTimeout",
|
||||
optionTimeScaleId: "protectedSessionTimeoutTimeScale",
|
||||
minimumSeconds: 60
|
||||
});
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $timeSelector = this.$widget;
|
||||
// inject TimeSelector widget template
|
||||
this.$widget = $(TPL);
|
||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
||||
}
|
||||
}
|
||||
140
src/public/app/widgets/type_widgets/options/time_selector.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import OptionsWidget from "./options_widget.js";
|
||||
import toastService from "../../../services/toast.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import type { OptionDefinitions, OptionMap } from "../../../../../services/options_interface.js";
|
||||
import optionsService from "../../../services/options.js";
|
||||
|
||||
type TimeSelectorConstructor = {
|
||||
widgetId: string;
|
||||
widgetLabelId: string;
|
||||
optionValueId: keyof OptionDefinitions;
|
||||
optionTimeScaleId: keyof OptionDefinitions;
|
||||
includedTimeScales?: Set<TimeSelectorScale>;
|
||||
minimumSeconds?: number;
|
||||
};
|
||||
|
||||
type TimeSelectorScale = "seconds" | "minutes" | "hours" | "days";
|
||||
|
||||
const TPL = (options: Omit<TimeSelectorConstructor, "optionValueId" | "optionTimeScaleId">) => `
|
||||
<div class="form-group">
|
||||
<label for="${options.widgetId}">${t(options.widgetLabelId)}</label>
|
||||
<div class="d-flex gap-2">
|
||||
<input id="${options.widgetId}" class="form-control options-number-input" type="number" min="0" steps="1" required>
|
||||
<select id="${options.widgetId}-time-scale" class="form-select duration-selector" required>
|
||||
${options.includedTimeScales?.has("seconds") ? `<option value="1">${t("duration.seconds")}</option>` : ""}
|
||||
${options.includedTimeScales?.has("minutes") ? `<option value="60">${t("duration.minutes")}</option>` : ""}
|
||||
${options.includedTimeScales?.has("hours") ? `<option value="3600">${t("duration.hours")}</option>` : ""}
|
||||
${options.includedTimeScales?.has("days") ? `<option value="86400">${t("duration.days")}</option>` : ""}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<style>
|
||||
.duration-selector {
|
||||
width: auto;
|
||||
}
|
||||
</style>`;
|
||||
|
||||
export default class TimeSelector extends OptionsWidget {
|
||||
private $timeValueInput!: JQuery<HTMLInputElement>;
|
||||
private $timeScaleSelect!: JQuery<HTMLSelectElement>;
|
||||
private internalTimeInSeconds!: string | number;
|
||||
private widgetId: string;
|
||||
private widgetLabelId: string;
|
||||
private optionValueId: keyof OptionDefinitions;
|
||||
private optionTimeScaleId: keyof OptionDefinitions;
|
||||
private includedTimeScales: Set<TimeSelectorScale>;
|
||||
private minimumSeconds: number;
|
||||
|
||||
constructor(options: TimeSelectorConstructor) {
|
||||
super();
|
||||
this.widgetId = options.widgetId;
|
||||
this.widgetLabelId = options.widgetLabelId;
|
||||
this.optionValueId = options.optionValueId;
|
||||
this.optionTimeScaleId = options.optionTimeScaleId;
|
||||
this.includedTimeScales = options.includedTimeScales || new Set(["seconds", "minutes", "hours", "days"]);
|
||||
this.minimumSeconds = options.minimumSeconds || 0
|
||||
}
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(
|
||||
TPL({
|
||||
widgetId: this.widgetId,
|
||||
widgetLabelId: this.widgetLabelId,
|
||||
includedTimeScales: this.includedTimeScales
|
||||
})
|
||||
);
|
||||
|
||||
this.$timeValueInput = this.$widget.find(`#${this.widgetId}`);
|
||||
this.$timeScaleSelect = this.$widget.find(`#${this.widgetId}-time-scale`);
|
||||
|
||||
this.$timeValueInput.on("change", () => {
|
||||
const time = this.$timeValueInput.val();
|
||||
const timeScale = this.$timeScaleSelect.val();
|
||||
|
||||
if (!this.handleTimeValidation() || typeof timeScale !== "string" || !time) return;
|
||||
|
||||
this.setInternalTimeInSeconds(this.convertTime(time, timeScale).toOption());
|
||||
|
||||
this.updateOption(this.optionValueId, this.internalTimeInSeconds);
|
||||
});
|
||||
|
||||
this.$timeScaleSelect.on("change", () => {
|
||||
const timeScale = this.$timeScaleSelect.val();
|
||||
|
||||
if (!this.handleTimeValidation() || typeof timeScale !== "string") return;
|
||||
|
||||
//calculate the new displayed value
|
||||
const displayedTime = this.convertTime(this.internalTimeInSeconds, timeScale).toDisplay();
|
||||
|
||||
this.updateOption(this.optionTimeScaleId, timeScale);
|
||||
this.$timeValueInput.val(displayedTime).trigger("change");
|
||||
});
|
||||
}
|
||||
|
||||
async optionsLoaded(options: OptionMap) {
|
||||
const optionValue = optionsService.getInt(this.optionValueId) || 0;
|
||||
const optionTimeScale = optionsService.getInt(this.optionTimeScaleId) || 1;
|
||||
|
||||
this.setInternalTimeInSeconds(optionValue);
|
||||
|
||||
const displayedTime = this.convertTime(optionValue, optionTimeScale).toDisplay();
|
||||
this.$timeValueInput.val(displayedTime);
|
||||
this.$timeScaleSelect.val(optionTimeScale);
|
||||
}
|
||||
|
||||
private 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)
|
||||
};
|
||||
}
|
||||
|
||||
private handleTimeValidation() {
|
||||
if (this.$timeValueInput.is(":invalid")) {
|
||||
toastService.showError(t("time_selector.invalid_input"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private setInternalTimeInSeconds(time: number) {
|
||||
if (time < this.minimumSeconds) {
|
||||
toastService.showError(t("time_selector.minimum_input", {minimumSeconds: this.minimumSeconds}));
|
||||
return this.internalTimeInSeconds = this.minimumSeconds;
|
||||
}
|
||||
return this.internalTimeInSeconds = time;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { t } from "../../services/i18n.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
import attributeService from "../../services/attributes.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
import utils from "../../services/utils.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-web-view note-detail-printable" style="height: 100%">
|
||||
@@ -10,16 +13,24 @@ const TPL = `
|
||||
<p>${t("web_view.embed_websites")}</p>
|
||||
|
||||
<p>${t("web_view.create_label")}</p>
|
||||
|
||||
<h4>${t("web_view.disclaimer")}</h4>
|
||||
|
||||
<p>${t("web_view.experimental_note")}</p>
|
||||
</div>
|
||||
|
||||
<webview class="note-detail-web-view-content"></webview>
|
||||
${buildElement()}
|
||||
</div>`;
|
||||
|
||||
function buildElement() {
|
||||
if (!utils.isElectron()) {
|
||||
return `<iframe class="note-detail-web-view-content" sandbox="allow-same-origin allow-scripts"></iframe>`;
|
||||
} else {
|
||||
return `<webview class="note-detail-web-view-content"></webview>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default class WebViewTypeWidget extends TypeWidget {
|
||||
|
||||
private $noteDetailWebViewHelp!: JQuery<HTMLElement>;
|
||||
private $noteDetailWebViewContent!: JQuery<HTMLElement>;
|
||||
|
||||
static getType() {
|
||||
return "webView";
|
||||
}
|
||||
@@ -34,11 +45,15 @@ export default class WebViewTypeWidget extends TypeWidget {
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
async doRefresh(note) {
|
||||
async doRefresh(note: FNote) {
|
||||
this.$widget.show();
|
||||
this.$noteDetailWebViewHelp.hide();
|
||||
this.$noteDetailWebViewContent.hide();
|
||||
|
||||
if (!this.note) {
|
||||
return;
|
||||
}
|
||||
|
||||
const webViewSrc = this.note.getLabelValue("webViewSrc");
|
||||
|
||||
if (webViewSrc) {
|
||||
@@ -54,17 +69,19 @@ export default class WebViewTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.$noteDetailWebViewContent.removeAttribute("src");
|
||||
this.$noteDetailWebViewContent.removeAttr("src");
|
||||
}
|
||||
|
||||
setDimensions() {
|
||||
const $parent = this.$widget;
|
||||
|
||||
this.$noteDetailWebViewContent.height($parent.height()).width($parent.width());
|
||||
this.$noteDetailWebViewContent
|
||||
.height($parent.height() ?? 0)
|
||||
.width($parent.width() ?? 0);
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({ loadResults }) {
|
||||
if (loadResults.getAttributeRows().find((attr) => attr.name === "webViewSrc" && attributeService.isAffecting(attr, this.noteContext.note))) {
|
||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
if (loadResults.getAttributeRows().find((attr) => attr.name === "webViewSrc" && attributeService.isAffecting(attr, this.noteContext?.note))) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Calendar, DateSelectArg, EventChangeArg, EventDropArg, EventSourceInput, PluginDef } from "@fullcalendar/core";
|
||||
import type { Calendar, DateSelectArg, EventChangeArg, EventDropArg, EventInput, EventSourceFunc, EventSourceFuncArg, EventSourceInput, PluginDef } from "@fullcalendar/core";
|
||||
import froca from "../../services/froca.js";
|
||||
import ViewMode, { type ViewModeArgs } from "./view_mode.js";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
@@ -9,6 +9,9 @@ import options from "../../services/options.js";
|
||||
import dialogService from "../../services/dialog.js";
|
||||
import attributes from "../../services/attributes.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import date_notes from "../../services/date_notes.js";
|
||||
import appContext from "../../components/app_context.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="calendar-view">
|
||||
@@ -66,6 +69,7 @@ export default class CalendarView extends ViewMode {
|
||||
private noteIds: string[];
|
||||
private parentNote: FNote;
|
||||
private calendar?: Calendar;
|
||||
private isCalendarRoot: boolean;
|
||||
|
||||
constructor(args: ViewModeArgs) {
|
||||
super(args);
|
||||
@@ -74,31 +78,66 @@ export default class CalendarView extends ViewMode {
|
||||
this.$calendarContainer = this.$root.find(".calendar-container");
|
||||
this.noteIds = args.noteIds;
|
||||
this.parentNote = args.parentNote;
|
||||
console.log(args);
|
||||
this.isCalendarRoot = false;
|
||||
args.$parent.append(this.$root);
|
||||
}
|
||||
|
||||
get isFullHeight(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async renderList(): Promise<JQuery<HTMLElement> | undefined> {
|
||||
const isEditable = true;
|
||||
this.isCalendarRoot = this.parentNote.hasLabel("calendarRoot") || this.parentNote.hasLabel("workspaceCalendarRoot");
|
||||
const isEditable = !this.isCalendarRoot;
|
||||
|
||||
const { Calendar } = await import("@fullcalendar/core");
|
||||
const plugins: PluginDef[] = [];
|
||||
plugins.push((await import("@fullcalendar/daygrid")).default);
|
||||
|
||||
if (isEditable) {
|
||||
if (isEditable || this.isCalendarRoot) {
|
||||
plugins.push((await import("@fullcalendar/interaction")).default);
|
||||
}
|
||||
|
||||
let eventBuilder: EventSourceFunc;
|
||||
if (!this.isCalendarRoot) {
|
||||
eventBuilder = async () => await this.#buildEvents(this.noteIds)
|
||||
} else {
|
||||
eventBuilder = async (e: EventSourceFuncArg) => await this.#buildEventsForCalendar(e);
|
||||
}
|
||||
|
||||
const calendar = new Calendar(this.$calendarContainer[0], {
|
||||
plugins,
|
||||
initialView: "dayGridMonth",
|
||||
events: async () => await CalendarView.#buildEvents(this.noteIds),
|
||||
events: eventBuilder,
|
||||
editable: isEditable,
|
||||
selectable: isEditable,
|
||||
select: (e) => this.#onCalendarSelection(e),
|
||||
eventChange: (e) => this.#onEventMoved(e),
|
||||
firstDay: options.getInt("firstDayOfWeek") ?? 0,
|
||||
locale: await CalendarView.#getLocale()
|
||||
weekends: !this.parentNote.hasAttribute("label", "calendar:hideWeekends"),
|
||||
weekNumbers: this.parentNote.hasAttribute("label", "calendar:weekNumbers"),
|
||||
locale: await CalendarView.#getLocale(),
|
||||
height: "100%",
|
||||
eventContent: (e => {
|
||||
let html = "";
|
||||
|
||||
const iconClass = e.event.extendedProps.iconClass;
|
||||
if (iconClass) {
|
||||
html += `<span class="${iconClass}"></span> `;
|
||||
}
|
||||
|
||||
html += utils.escapeHtml(e.event.title);
|
||||
return { html };
|
||||
}),
|
||||
dateClick: async (e) => {
|
||||
if (!this.isCalendarRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const note = await date_notes.getDayNote(e.dateStr);
|
||||
if (note) {
|
||||
appContext.tabManager.getActiveContext().setNote(note.noteId);
|
||||
}
|
||||
}
|
||||
});
|
||||
calendar.render();
|
||||
this.calendar = calendar;
|
||||
@@ -174,47 +213,113 @@ export default class CalendarView extends ViewMode {
|
||||
CalendarView.#setAttribute(note, "label", "endDate", endDate);
|
||||
}
|
||||
|
||||
entitiesReloadedEvents({ loadResults }: EventData<"entitiesReloaded">): void {
|
||||
onEntitiesReloaded({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
// Refresh note IDs if they got changed.
|
||||
if (loadResults.getBranchRows().some((branch) => branch.parentNoteId == this.parentNote.noteId)) {
|
||||
if (loadResults.getBranchRows().some((branch) => branch.parentNoteId === this.parentNote.noteId)) {
|
||||
this.noteIds = this.parentNote.getChildNoteIds();
|
||||
}
|
||||
|
||||
// Refresh calendar on attribute change.
|
||||
if (loadResults.getAttributeRows().some((attribute) => attribute.noteId === this.parentNote.noteId && attribute.name?.startsWith("calendar:"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Refresh dataset on subnote change.
|
||||
if (this.calendar && loadResults.getAttributeRows().some((a) => this.noteIds.includes(a.noteId ?? ""))) {
|
||||
this.calendar.refetchEvents();
|
||||
}
|
||||
}
|
||||
|
||||
static async #buildEvents(noteIds: string[]) {
|
||||
async #buildEventsForCalendar(e: EventSourceFuncArg) {
|
||||
const events = [];
|
||||
|
||||
// Gather all the required date note IDs.
|
||||
const dateRange = utils.getMonthsInDateRange(e.startStr, e.endStr);
|
||||
let allDateNoteIds: string[] = [];
|
||||
for (const month of dateRange) {
|
||||
// TODO: Deduplicate get type.
|
||||
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${this.parentNote.noteId}`);
|
||||
const dateNoteIds = Object.values(dateNotesForMonth);
|
||||
allDateNoteIds = [ ...allDateNoteIds, ...dateNoteIds ];
|
||||
}
|
||||
|
||||
// Request all the date notes.
|
||||
const dateNotes = await froca.getNotes(allDateNoteIds);
|
||||
const childNoteToDateMapping: Record<string, string> = {};
|
||||
for (const dateNote of dateNotes) {
|
||||
const startDate = dateNote.getLabelValue("dateNote");
|
||||
if (!startDate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
events.push(await CalendarView.#buildEvent(dateNote, startDate));
|
||||
|
||||
if (dateNote.hasChildren()) {
|
||||
const childNoteIds = dateNote.getChildNoteIds();
|
||||
for (const childNoteId of childNoteIds) {
|
||||
childNoteToDateMapping[childNoteId] = startDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request all child notes of date notes in a single run.
|
||||
const childNoteIds = Object.keys(childNoteToDateMapping);
|
||||
const childNotes = await froca.getNotes(childNoteIds);
|
||||
for (const childNote of childNotes) {
|
||||
const startDate = childNoteToDateMapping[childNote.noteId];
|
||||
const event = await CalendarView.#buildEvent(childNote, startDate);
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
return events.flat();
|
||||
}
|
||||
|
||||
async #buildEvents(noteIds: string[]) {
|
||||
const notes = await froca.getNotes(noteIds);
|
||||
const events: EventSourceInput = [];
|
||||
|
||||
for (const note of notes) {
|
||||
const startDate = note.getAttributeValue("label", "startDate");
|
||||
const customTitle = note.getAttributeValue("label", "calendar:title");
|
||||
let startDate = note.getLabelValue("startDate");
|
||||
|
||||
if (note.hasChildren()) {
|
||||
const childrenEventData = await this.#buildEvents(note.getChildNoteIds());
|
||||
if (childrenEventData.length > 0) {
|
||||
events.push(childrenEventData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!startDate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const endDate = note.getAttributeValue("label", "endDate");
|
||||
events.push(await CalendarView.#buildEvent(note, startDate, endDate));
|
||||
}
|
||||
|
||||
return events.flat();
|
||||
}
|
||||
|
||||
static async #buildEvent(note: FNote, startDate: string, endDate?: string | null) {
|
||||
const customTitle = note.getLabelValue("calendar:title");
|
||||
const titles = await CalendarView.#parseCustomTitle(customTitle, note);
|
||||
const color = note.getLabelValue("calendar:color") ?? note.getLabelValue("color");
|
||||
const events: EventInput[] = [];
|
||||
for (const title of titles) {
|
||||
const eventData: typeof events[0] = {
|
||||
const eventData: EventInput = {
|
||||
title: title,
|
||||
start: startDate,
|
||||
url: `#${note.noteId}`,
|
||||
noteId: note.noteId
|
||||
noteId: note.noteId,
|
||||
color: color ?? undefined,
|
||||
iconClass: note.getLabelValue("iconClass")
|
||||
};
|
||||
|
||||
const endDate = CalendarView.#offsetDate(note.getAttributeValue("label", "endDate") ?? startDate, 1);
|
||||
if (endDate) {
|
||||
eventData.end = CalendarView.#formatDateToLocalISO(endDate);
|
||||
const endDateOffset = CalendarView.#offsetDate(endDate ?? startDate, 1);
|
||||
if (endDateOffset) {
|
||||
eventData.end = CalendarView.#formatDateToLocalISO(endDateOffset);
|
||||
}
|
||||
|
||||
events.push(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,19 @@ export default abstract class ViewMode {
|
||||
|
||||
abstract renderList(): Promise<JQuery<HTMLElement> | undefined>;
|
||||
|
||||
entitiesReloadedEvents(e: EventData<"entitiesReloaded">) {
|
||||
/**
|
||||
* Called whenever an "entitiesReloaded" event has been received by the parent component.
|
||||
*
|
||||
* @param e the event data.
|
||||
* @return {@code true} if the view should be re-rendered, a falsy value otherwise.
|
||||
*/
|
||||
onEntitiesReloaded(e: EventData<"entitiesReloaded">): boolean | void {
|
||||
// Do nothing by default.
|
||||
}
|
||||
|
||||
get isFullHeight() {
|
||||
// Override to change its value.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -144,6 +144,9 @@
|
||||
|
||||
--alert-bar-background: #6b6b6b3b;
|
||||
|
||||
--promoted-attribute-card-background-color: var(--card-background-color);
|
||||
--promoted-attribute-card-shadow-color: #000000b3;
|
||||
|
||||
--right-pane-item-hover-background: #ffffff26;
|
||||
--right-pane-item-hover-color: white;
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@
|
||||
|
||||
--alert-bar-background: #32637b29;
|
||||
|
||||
--promoted-attribute-card-background-color: var(--card-background-color);
|
||||
--promoted-attribute-card-shadow-color: #00000033;
|
||||
|
||||
--new-tab-button-background: #d8d8d8;
|
||||
--new-tab-button-color: #3a3a3a;
|
||||
--new-tab-button-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
|
||||
@@ -140,6 +140,8 @@ input:not([type]),
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
input[type="password"],
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
textarea.form-control,
|
||||
textarea,
|
||||
@@ -156,6 +158,8 @@ input:not([type]):hover,
|
||||
input[type="text"]:hover,
|
||||
input[type="number"]:hover,
|
||||
input[type="password"]:hover,
|
||||
input[type="date"]:hover,
|
||||
input[type="time"]:hover,
|
||||
input[type="datetime-local"]:hover,
|
||||
textarea.form-control:hover,
|
||||
textarea:hover,
|
||||
@@ -168,6 +172,8 @@ input:not([type]):focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="time"]:focus,
|
||||
input[type="datetime-local"]:focus,
|
||||
textarea.form-control:focus,
|
||||
textarea:focus,
|
||||
@@ -193,6 +199,11 @@ input::selection,
|
||||
color: var(--input-selection-text-color);
|
||||
}
|
||||
|
||||
.form-text {
|
||||
color: var(--main-text-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Input groups */
|
||||
|
||||
.input-group {
|
||||
@@ -270,17 +281,20 @@ input::selection,
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.input-group .input-group-text:not(button):not(a) {
|
||||
.input-group .input-group-text {
|
||||
/* Background color hijack */
|
||||
--accented-background-color: transparent;
|
||||
|
||||
border: none;
|
||||
font-style: italic;
|
||||
color: var(--input-placeholder-color);
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.input-group .input-group-text:not(button):not(a):not(.bx) {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Combo box-like dropdown buttons */
|
||||
|
||||
.select-button.dropdown-toggle::after {
|
||||
|
||||
@@ -1836,3 +1836,110 @@ body.background-effects.zen #root-widget {
|
||||
animation: alert-show 300ms ease-in;
|
||||
border-bottom: 2px solid #0000001c !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Promoted attributes
|
||||
*/
|
||||
|
||||
/* The promoted attributes section */
|
||||
div.promoted-attributes-container {
|
||||
display: flex;
|
||||
margin-right: 10%;
|
||||
padding: 6px 0;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
div.promoted-attributes-container,
|
||||
div.promoted-attributes-container input {
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
/* A promoted attribute card */
|
||||
div.promoted-attribute-cell {
|
||||
--pa-card-padding-left: 16px;
|
||||
--pa-card-padding-right: 2px;
|
||||
--input-background-color: transparent;
|
||||
|
||||
box-shadow: 1px 1px 2px var(--promoted-attribute-card-shadow-color);
|
||||
|
||||
display: inline-flex;
|
||||
margin: 0;
|
||||
border-radius: 8px;
|
||||
padding: 2px var(--pa-card-padding-right) 2px var(--pa-card-padding-left);
|
||||
background: var(--promoted-attribute-card-background-color);
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
@container (max-width: 500px) {
|
||||
/* Narrow promoted attributes section */
|
||||
div.promoted-attribute-cell {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* A promoted attribute card (boolean attribute) */
|
||||
div.promoted-attribute-cell:has(input[type="checkbox"]):not(:has(.multiplicity > span)) {
|
||||
/* Checbox attribute, without multiplicity */
|
||||
padding-right: var(--pa-card-padding-left);
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell > label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell:not(:has(input[type="checkbox"])) > label::after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell div.input-group {
|
||||
min-height: auto;
|
||||
padding: 1px 6px;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell input {
|
||||
padding: 2px 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell input[type="text"] {
|
||||
width: 10em !important;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell input[type="number"] {
|
||||
width: 6em !important;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell span.open-external-link-button {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
div.promoted-attribute-cell .tn-checkbox {
|
||||
--box-label-gap: 0;
|
||||
|
||||
height: 1cap;
|
||||
}
|
||||
|
||||
/* The <div> containing the checkbox for a promoted boolean attribute */
|
||||
div.promoted-attribute-cell div:has(input[type="checkbox"]) {
|
||||
order: -1; /* Relocate the checkbox before the label */
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
|
||||
/* The element containing the "new attribute" and "remove this attribute button" */
|
||||
div.promoted-attribute-cell .multiplicity:has(span) {
|
||||
--icon-button-size: 24px;
|
||||
|
||||
margin-left: 8px;
|
||||
margin-right: calc(var(--pa-card-padding-left) - var(--pa-card-padding-right));
|
||||
font-size: 0; /* Prevent whitespaces creating a gap between buttons */
|
||||
}
|
||||
@@ -988,9 +988,7 @@
|
||||
"web_view": {
|
||||
"web_view": "网页视图",
|
||||
"embed_websites": "网页视图类型的笔记允许您将网站嵌入到 Trilium 中。",
|
||||
"create_label": "首先,请创建一个带有您要嵌入的 URL 地址的标签,例如 #webViewSrc=\"https://www.bing.com\"",
|
||||
"disclaimer": "实验性功能免责声明",
|
||||
"experimental_note": "网页视图是一种实验性的笔记类型,将来可能会被移除或大幅更改。网页视图只在桌面端有效。"
|
||||
"create_label": "首先,请创建一个带有您要嵌入的 URL 地址的标签,例如 #webViewSrc=\"https://www.bing.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "刷新"
|
||||
@@ -1122,7 +1120,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "附件清理超时",
|
||||
"attachment_auto_deletion_description": "如果附件在一段时间后不再被笔记引用,它们将自动被删除(并被清理)。",
|
||||
"erase_attachments_after_x_seconds": "在附件在笔记中未被使用 X 秒后清理",
|
||||
"erase_attachments_after": "",
|
||||
"manual_erasing_description": "您还可以手动触发清理(而不考虑上述定义的超时时间):",
|
||||
"erase_unused_attachments_now": "立即清理未使用的附件笔记",
|
||||
"unused_attachments_erased": "未使用的附件已被删除。"
|
||||
@@ -1134,15 +1132,15 @@
|
||||
"note_erasure_timeout": {
|
||||
"note_erasure_timeout_title": "笔记清理超时",
|
||||
"note_erasure_description": "被删除的笔记(以及属性、历史版本等)最初仅被标记为“删除”,可以从“最近修改”对话框中恢复它们。经过一段时间后,已删除的笔记会被“清理”,这意味着它们的内容将无法恢复。此设置允许您配置从删除到清除笔记之间的时间长度。",
|
||||
"erase_notes_after": "Erase notes after",
|
||||
"erase_notes_after": "",
|
||||
"manual_erasing_description": "您还可以手动触发清理(不考虑上述定义的超时):",
|
||||
"erase_deleted_notes_now": "立即清理已删除的笔记",
|
||||
"deleted_notes_erased": "已删除的笔记已被清理。"
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "笔记修改快照间隔",
|
||||
"note_revisions_snapshot_description": "笔记修改快照时间间隔是指经过多少秒后会为笔记创建新的修改历史。更多信息请参见<a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a>。",
|
||||
"snapshot_time_interval_label": "笔记修改快照时间间隔(单位:秒)"
|
||||
"note_revisions_snapshot_interval_title": "",
|
||||
"note_revisions_snapshot_description": "",
|
||||
"snapshot_time_interval_label": ""
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "笔记历史快照限制",
|
||||
@@ -1261,7 +1259,7 @@
|
||||
"protected_session_timeout_description": "保护会话超时是一个时间段,超时后保护会话会从浏览器内存中清除。这是从最后一次与保护笔记的交互开始计时的。更多信息请见",
|
||||
"wiki": "维基",
|
||||
"for_more_info": "更多信息。",
|
||||
"protected_session_timeout_label": "保护会话超时(秒)",
|
||||
"protected_session_timeout_label": "",
|
||||
"reset_confirmation": "重置密码将永久丧失对所有现受保护笔记的访问。您真的要重置密码吗?",
|
||||
"reset_success_message": "密码已重置。请设置新密码",
|
||||
"change_password_heading": "更改密码",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Über TriliumNext Notes",
|
||||
"close": "Schließen",
|
||||
"homepage": "Startseite:",
|
||||
"app_version": "App-Version:",
|
||||
"db_version": "DB-Version:",
|
||||
@@ -52,10 +53,15 @@
|
||||
"chosen_actions": "Ausgewählte Aktionen",
|
||||
"execute_bulk_actions": "Massenaktionen ausführen",
|
||||
"bulk_actions_executed": "Massenaktionen wurden erfolgreich ausgeführt.",
|
||||
"none_yet": "Noch keine ... Füge eine Aktion hinzu, indem du oben auf eine der verfügbaren Aktionen klicken."
|
||||
"none_yet": "Noch keine ... Füge eine Aktion hinzu, indem du oben auf eine der verfügbaren Aktionen klicken.",
|
||||
"labels": "Labels",
|
||||
"relations": "Beziehungen",
|
||||
"notes": "Notizen",
|
||||
"other": "Andere"
|
||||
},
|
||||
"clone_to": {
|
||||
"clone_notes_to": "Notizen klonen nach...",
|
||||
"close":"Schließen",
|
||||
"help_on_links": "Hilfe zu Links",
|
||||
"notes_to_clone": "Notizen zum Klonen",
|
||||
"target_parent_note": "Ziel-Übergeordnetenotiz",
|
||||
@@ -68,6 +74,7 @@
|
||||
},
|
||||
"confirm": {
|
||||
"confirmation": "Bestätigung",
|
||||
"close":"Schließen",
|
||||
"cancel": "Abbrechen",
|
||||
"ok": "OK",
|
||||
"are_you_sure_remove_note": "Bist du sicher, dass du \"{{title}}\" von der Beziehungskarte entfernen möchten? ",
|
||||
@@ -76,6 +83,7 @@
|
||||
},
|
||||
"delete_notes": {
|
||||
"delete_notes_preview": "Vorschau der Notizen löschen",
|
||||
"close": "Schließen",
|
||||
"delete_all_clones_description": "auch alle Klone löschen (kann bei letzte Änderungen rückgängig gemacht werden)",
|
||||
"erase_notes_description": "Beim normalen (vorläufigen) Löschen werden die Notizen nur als gelöscht markiert und sie können innerhalb eines bestimmten Zeitraums (im Dialogfeld „Letzte Änderungen“) wiederhergestellt werden. Wenn du diese Option aktivierst, werden die Notizen sofort gelöscht und es ist nicht möglich, die Notizen wiederherzustellen.",
|
||||
"erase_notes_warning": "Notizen dauerhaft löschen (kann nicht rückgängig gemacht werden), einschließlich aller Klone. Dadurch wird ein Neuladen der Anwendung erzwungen.",
|
||||
@@ -83,12 +91,15 @@
|
||||
"no_note_to_delete": "Es werden keine Notizen gelöscht (nur Klone).",
|
||||
"broken_relations_to_be_deleted": "Folgende Beziehungen werden gelöst und gelöscht (<span class=\"broke-relations-count\"></span>)",
|
||||
"cancel": "Abbrechen",
|
||||
"ok": "OK"
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Notiz {{- note}} (soll gelöscht werden) wird von Beziehung {{- relation}} ausgehend von {{- source}} referenziert."
|
||||
|
||||
},
|
||||
"export": {
|
||||
"export_note_title": "Notiz exportieren",
|
||||
"close": "Schließen",
|
||||
"export_type_subtree": "Diese Notiz und alle ihre Unternotizen",
|
||||
"format_html": "HTML - empfohlen, da dadurch alle Formatierungen erhalten bleiben",
|
||||
"format_html_zip": "HTML im ZIP-Archiv – dies wird empfohlen, da dadurch die gesamte Formatierung erhalten bleibt.",
|
||||
"format_markdown": "Markdown – dadurch bleiben die meisten Formatierungen erhalten.",
|
||||
"format_opml": "OPML – Outliner-Austauschformat nur für Text. Formatierungen, Bilder und Dateien sind nicht enthalten.",
|
||||
@@ -99,7 +110,8 @@
|
||||
"choose_export_type": "Bitte wähle zuerst den Exporttypen aus",
|
||||
"export_status": "Exportstatus",
|
||||
"export_in_progress": "Export läuft: {{progressCount}}",
|
||||
"export_finished_successfully": "Der Export wurde erfolgreich abgeschlossen."
|
||||
"export_finished_successfully": "Der Export wurde erfolgreich abgeschlossen.",
|
||||
"format_pdf": "PDF - für Ausdrucke oder Teilen."
|
||||
},
|
||||
"help": {
|
||||
"fullDocumentation": "Hilfe (gesamte Dokumentation ist <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a> verfügbar)",
|
||||
@@ -171,10 +183,20 @@
|
||||
"codeImportedAsCode": "Importiere erkannte Codedateien (z. B. <code>.json</code>) als Codenotizen, wenn die Metadaten unklar sind",
|
||||
"replaceUnderscoresWithSpaces": "Ersetze Unterstriche in importierten Notiznamen durch Leerzeichen",
|
||||
"import": "Import",
|
||||
"failed": "Import fehlgeschlagen: {{message}}."
|
||||
"failed": "Import fehlgeschlagen: {{message}}.",
|
||||
"html_import_tags": {
|
||||
"title": "HTML Tag Import",
|
||||
"description": "Festlegen, welche HTML tags beim Import von Notizen beibehalten werden sollen. Tags, die nicht in dieser Liste stehen, werden beim Import entfernt. Einige tags (wie bspw. 'script') werden aus Sicherheitsgründen immer entfernt.",
|
||||
"placeholder": "HTML tags eintragen, pro Zeile nur einer pro Zeile",
|
||||
"reset_button": "Zur Standardliste zurücksetzen"
|
||||
},
|
||||
"import-status": "Importstatus",
|
||||
"in-progress": "Import läuft: {{progress}}",
|
||||
"successful": "Import erfolgreich abgeschlossen."
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Notiz beifügen",
|
||||
"close": "Schließen",
|
||||
"label_note": "Notiz",
|
||||
"placeholder_search": "Suche nach einer Notiz anhand ihres Namens",
|
||||
"box_size_prompt": "Kartongröße des beigelegten Zettels:",
|
||||
@@ -190,16 +212,19 @@
|
||||
},
|
||||
"jump_to_note": {
|
||||
"search_placeholder": "Suche nach einer Notiz anhand ihres Namens",
|
||||
"close": "Schließen",
|
||||
"search_button": "Suche im Volltext: <kbd>Strg+Eingabetaste</kbd>"
|
||||
},
|
||||
"markdown_import": {
|
||||
"dialog_title": "Markdown-Import",
|
||||
"close": "Schließen",
|
||||
"modal_body_text": "Aufgrund der Browser-Sandbox ist es nicht möglich, die Zwischenablage direkt aus JavaScript zu lesen. Bitte füge den zu importierenden Markdown in den Textbereich unten ein und klicke auf die Schaltfläche „Importieren“.",
|
||||
"import_button": "Importieren Strg+Eingabe",
|
||||
"import_success": "Markdown-Inhalt wurde in das Dokument importiert."
|
||||
},
|
||||
"move_to": {
|
||||
"dialog_title": "Notizen verschieben nach ...",
|
||||
"close": "Schließen",
|
||||
"notes_to_move": "Notizen zum Verschieben",
|
||||
"target_parent_note": "Ziel-Elternnotiz",
|
||||
"search_placeholder": "Suche nach einer Notiz anhand ihres Namens",
|
||||
@@ -209,16 +234,19 @@
|
||||
},
|
||||
"note_type_chooser": {
|
||||
"modal_title": "Wähle den Notiztyp aus",
|
||||
"close": "Schließen",
|
||||
"modal_body": "Wähle den Notiztyp / die Vorlage der neuen Notiz:",
|
||||
"templates": "Vorlagen:"
|
||||
},
|
||||
"password_not_set": {
|
||||
"title": "Das Passwort ist nicht festgelegt",
|
||||
"close": "Schließen",
|
||||
"body1": "Geschützte Notizen werden mit einem Benutzerpasswort verschlüsselt, es wurde jedoch noch kein Passwort festgelegt.",
|
||||
"body2": "Um Notizen verschlüsseln zu können, klicke <a class=\"open-password-options-button\" href=\"javascript:\">hier</a> um das Optionsmenu zu öffnen und ein Passwort zu setzen."
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Prompt",
|
||||
"close": "Schließen",
|
||||
"ok": "OK <kbd>Eingabe</kbd>",
|
||||
"defaultTitle": "Prompt"
|
||||
},
|
||||
@@ -232,6 +260,7 @@
|
||||
"recent_changes": {
|
||||
"title": "Aktuelle Änderungen",
|
||||
"erase_notes_button": "Jetzt gelöschte Notizen löschen",
|
||||
"close": "Schließen",
|
||||
"deleted_notes_message": "Gelöschte Notizen wurden gelöscht.",
|
||||
"no_changes_message": "Noch keine Änderungen...",
|
||||
"undelete_link": "Wiederherstellen",
|
||||
@@ -242,6 +271,7 @@
|
||||
"delete_all_revisions": "Lösche alle Revisionen dieser Notiz",
|
||||
"delete_all_button": "Alle Revisionen löschen",
|
||||
"help_title": "Hilfe zu Notizrevisionen",
|
||||
"close": "Schließen",
|
||||
"revision_last_edited": "Diese Revision wurde zuletzt am {{date}} bearbeitet",
|
||||
"confirm_delete_all": "Möchtest du alle Revisionen dieser Notiz löschen? Durch diese Aktion werden der Titel und der Inhalt der Revision gelöscht, die Metadaten der Revision bleiben jedoch erhalten.",
|
||||
"no_revisions": "Für diese Notiz gibt es noch keine Revisionen...",
|
||||
@@ -249,9 +279,9 @@
|
||||
"confirm_restore": "Möchtest du diese Revision wiederherstellen? Dadurch werden der aktuelle Titel und Inhalt der Notiz mit dieser Revision überschrieben.",
|
||||
"delete_button": "Lösche diese Revision",
|
||||
"confirm_delete": "Möchtest du diese Revision löschen? Durch diese Aktion werden der Titel und der Inhalt der Revision gelöscht, die Metadaten der Revision bleiben jedoch erhalten.",
|
||||
"revisions_deleted": "Hinweisrevisionen wurden gelöscht.",
|
||||
"revisions_deleted": "Notizrevisionen wurden gelöscht.",
|
||||
"revision_restored": "Die Notizrevision wurde wiederhergestellt.",
|
||||
"revision_deleted": "Hinweisrevision wurde gelöscht.",
|
||||
"revision_deleted": "Notizrevision wurde gelöscht.",
|
||||
"snapshot_interval": "Notizrevisionen-Snapshot Intervall: {{seconds}}s.",
|
||||
"maximum_revisions": "Maximale Revisionen für aktuelle Notiz: {{number}}.",
|
||||
"settings": "Einstellungen für Notizrevisionen",
|
||||
@@ -263,6 +293,7 @@
|
||||
},
|
||||
"sort_child_notes": {
|
||||
"sort_children_by": "Unternotizen sortieren nach...",
|
||||
"close": "Schließen",
|
||||
"sorting_criteria": "Sortierkriterien",
|
||||
"title": "Titel",
|
||||
"date_created": "Erstellungsdatum",
|
||||
@@ -280,6 +311,7 @@
|
||||
},
|
||||
"upload_attachments": {
|
||||
"upload_attachments_to_note": "Lade Anhänge zur Notiz hoch",
|
||||
"close": "Schließen",
|
||||
"choose_files": "Wähle Dateien aus",
|
||||
"files_will_be_uploaded": "Dateien werden als Anhänge in hochgeladen",
|
||||
"options": "Optionen",
|
||||
@@ -338,13 +370,14 @@
|
||||
"disable_inclusion": "Skripte mit dieser Bezeichnung werden nicht in die Ausführung des übergeordneten Skripts einbezogen.",
|
||||
"sorted": "Hält untergeordnete Notizen alphabetisch nach Titel sortiert",
|
||||
"sort_direction": "ASC (Standard) oder DESC",
|
||||
"sort_folders_first": "Ordner (Notizen mit Kindern) sollten oben sortiert werden",
|
||||
"sort_folders_first": "Ordner (Notizen mit Unternotizen) sollten oben sortiert werden",
|
||||
"top": "Behalte die angegebene Notiz oben in der übergeordneten Notiz (gilt nur für sortierte übergeordnete Notizen).",
|
||||
"hide_promoted_attributes": "Heraufgestufte Attribute für diese Notiz ausblenden",
|
||||
"read_only": "Der Editor befindet sich im schreibgeschützten Modus. Funktioniert nur für Text- und Codenotizen.",
|
||||
"auto_read_only_disabled": "Text-/Codenotizen können automatisch in den Lesemodus versetzt werden, wenn sie zu groß sind. Du kannst dieses Verhalten für jede einzelne Notiz deaktivieren, indem du diese Beschriftung zur Notiz hinzufügst",
|
||||
"app_css": "markiert CSS-Notizen, die in die Trilium-Anwendung geladen werden und somit zur Änderung des Aussehens von Trilium verwendet werden können.",
|
||||
"app_theme": "markiert CSS-Notizen, die vollständige Trilium-Themen sind und daher in den Trilium-Optionen verfügbar sind.",
|
||||
"app_theme_base": "markiert Notiz als \"nächste\" in der Reihe für ein Trilium-Theme als Grundlage für ein Custom-Theme. Ersetzt damit das Standard Theme.",
|
||||
"css_class": "Der Wert dieser Bezeichnung wird dann als CSS-Klasse dem Knoten hinzugefügt, der die angegebene Notiz im Baum darstellt. Dies kann für fortgeschrittene Themen nützlich sein. Kann in Vorlagennotizen verwendet werden.",
|
||||
"icon_class": "Der Wert dieser Bezeichnung wird als CSS-Klasse zum Symbol im Baum hinzugefügt, was dabei helfen kann, die Notizen im Baum visuell zu unterscheiden. Beispiel könnte bx bx-home sein – Symbole werden von Boxicons übernommen. Kann in Vorlagennotizen verwendet werden.",
|
||||
"page_size": "Anzahl der Elemente pro Seite in der Notizliste",
|
||||
@@ -406,7 +439,9 @@
|
||||
"share_favicon": "Favicon-Notiz, die auf der freigegebenen Seite festgelegt werden soll. Normalerweise möchtest du es so einstellen, dass es Root teilt und es vererbbar macht. Die Favicon-Notiz muss sich ebenfalls im freigegebenen Unterbaum befinden. Erwäge die Verwendung von „share_hidden_from_tree“.",
|
||||
"is_owned_by_note": "ist Eigentum von Note",
|
||||
"other_notes_with_name": "Other notes with {{attributeType}} name \"{{attributeName}}\"",
|
||||
"and_more": "... und {{count}} mehr."
|
||||
"and_more": "... und {{count}} mehr.",
|
||||
"print_landscape": "Beim Export als PDF, wird die Seitenausrichtung Querformat anstatt Hochformat verwendet.",
|
||||
"print_page_size": "Beim Export als PDF, wird die Größe der Seite angepasst. Unterstützte Größen: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>."
|
||||
},
|
||||
"attribute_editor": {
|
||||
"help_text_body1": "Um ein Label hinzuzufügen, gebe einfach z.B. ein. <code>#rock</code> oder wenn du auch einen Wert hinzufügen möchten, dann z.B. <code>#year = 2024</code>",
|
||||
@@ -491,7 +526,7 @@
|
||||
"example_note": "<code>Notiz</code> – alle übereinstimmenden Notizen werden in „Notiz“ umbenannt.",
|
||||
"example_new_title": "<code>NEU: ${note.title}</code> – Übereinstimmende Notiztitel erhalten das Präfix „NEU:“",
|
||||
"example_date_prefix": "<code>${note.dateCreatedObj.format('MM-DD:')}: ${note.title}</code> – übereinstimmende Notizen werden mit dem Erstellungsmonat und -datum der Notiz vorangestellt",
|
||||
"api_docs": "Siehe API-Dokumente für <a hrefu003d'https://zadam.github.io/trilium/backend_api/Note.html'>Hinweis</a> und seinen <a hrefu003d'https://day.js.org/ docs/en/display/format'>dateCreatedObj / utcDateCreatedObj-Eigenschaften</a> für Details."
|
||||
"api_docs": "Siehe API-Dokumente für <a hrefu003d'https://zadam.github.io/trilium/backend_api/Note.html'>Notiz</a> und seinen <a hrefu003d'https://day.js.org/ docs/en/display/format'>dateCreatedObj / utcDateCreatedObj-Eigenschaften</a> für Details."
|
||||
},
|
||||
"add_relation": {
|
||||
"add_relation": "Beziehung hinzufügen",
|
||||
@@ -596,7 +631,7 @@
|
||||
"zoom_in": "Hineinzoomen",
|
||||
"configure_launchbar": "Konfiguriere die Launchbar",
|
||||
"show_shared_notes_subtree": "Unterbaum „Freigegebene Notizen“ anzeigen",
|
||||
"advanced": "Fortschrittlich",
|
||||
"advanced": "Erweitert",
|
||||
"open_dev_tools": "Öffne die Entwicklungstools",
|
||||
"open_sql_console": "Öffne die SQL-Konsole",
|
||||
"open_sql_console_history": "Öffne den SQL-Konsolenverlauf",
|
||||
@@ -607,7 +642,9 @@
|
||||
"show_hidden_subtree": "Versteckten Teilbaum anzeigen",
|
||||
"show_help": "Hilfe anzeigen",
|
||||
"about": "Über TriliumNext Notes",
|
||||
"logout": "Abmelden"
|
||||
"logout": "Abmelden",
|
||||
"show-cheatsheet": "Cheatsheet anzeigen",
|
||||
"toggle-zen-mode": "Zen Modus"
|
||||
},
|
||||
"sync_status": {
|
||||
"unknown": "<p>Der Synchronisations-Status wird bekannt, sobald der nächste Synchronisierungsversuch gestartet wird.</p><p>Klicke, um eine Synchronisierung jetzt auszulösen.</p>",
|
||||
@@ -641,7 +678,8 @@
|
||||
"save_revision": "Revision speichern",
|
||||
"convert_into_attachment_failed": "Konvertierung der Notiz '{{title}}' fehlgeschlagen.",
|
||||
"convert_into_attachment_successful": "Notiz '{{title}}' wurde als Anhang konvertiert.",
|
||||
"convert_into_attachment_prompt": "Bist du dir sicher, dass du die Notiz '{{title}}' in ein Anhang der übergeordneten Notiz konvertieren möchtest?"
|
||||
"convert_into_attachment_prompt": "Bist du dir sicher, dass du die Notiz '{{title}}' in ein Anhang der übergeordneten Notiz konvertieren möchtest?",
|
||||
"print_pdf": "Export als PDF..."
|
||||
},
|
||||
"onclick_button": {
|
||||
"no_click_handler": "Das Schaltflächen-Widget „{{componentId}}“ hat keinen definierten Klick-Handler"
|
||||
@@ -715,7 +753,8 @@
|
||||
"collapse": "Einklappen",
|
||||
"expand": "Ausklappen",
|
||||
"book_properties": "Bucheigenschaften",
|
||||
"invalid_view_type": "Ungültiger Ansichtstyp „{{type}}“"
|
||||
"invalid_view_type": "Ungültiger Ansichtstyp „{{type}}“",
|
||||
"calendar": "Kalender"
|
||||
},
|
||||
"edited_notes": {
|
||||
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
|
||||
@@ -759,15 +798,17 @@
|
||||
"note_size_info": "Die Notizgröße bietet eine grobe Schätzung des Speicherbedarfs für diese Notiz. Es berücksichtigt den Inhalt der Notiz und den Inhalt ihrer Notizrevisionen.",
|
||||
"calculate": "berechnen",
|
||||
"subtree_size": "(Teilbaumgröße: {{size}} in {{count}} Notizen)",
|
||||
"title": "Hinweisinfo"
|
||||
"title": "Notizinfo"
|
||||
},
|
||||
"note_map": {
|
||||
"open_full": "Vollständig erweitern",
|
||||
"collapse": "Auf normale Größe reduzieren",
|
||||
"title": "Hinweiskarte"
|
||||
"title": "Notizkarte",
|
||||
"fix-nodes": "Knoten fixieren",
|
||||
"link-distance": "Verbindungslänge"
|
||||
},
|
||||
"note_paths": {
|
||||
"title": "Hinweispfade",
|
||||
"title": "Notizpfade",
|
||||
"clone_button": "Notiz an neuen Speicherort klonen...",
|
||||
"intro_placed": "Diese Notiz wird in den folgenden Pfaden abgelegt:",
|
||||
"intro_not_placed": "Diese Notiz ist noch nicht im Notizbaum platziert.",
|
||||
@@ -864,7 +905,7 @@
|
||||
"content_and_attachments_size": "Beachte die Inhaltsgröße einschließlich der Anhänge",
|
||||
"content_and_attachments_and_revisions_size": "Beachte die Inhaltsgröße einschließlich Anhängen und Revisionen",
|
||||
"revision_count": "Anzahl der Revisionen",
|
||||
"children_count": "Anzahl der Kindernotizen",
|
||||
"children_count": "Anzahl der Unternotizen",
|
||||
"parent_count": "Anzahl der Klone",
|
||||
"owned_label_count": "Anzahl der Etiketten",
|
||||
"owned_relation_count": "Anzahl der Beziehungen",
|
||||
@@ -900,14 +941,14 @@
|
||||
},
|
||||
"attachment_detail": {
|
||||
"open_help_page": "Hilfeseite zu Anhängen öffnen",
|
||||
"owning_note": "Hinweis zum Eigentümer:",
|
||||
"owning_note": "Eigentümernotiz: ",
|
||||
"you_can_also_open": ", Du kannst auch das öffnen",
|
||||
"list_of_all_attachments": "Liste aller Anhänge",
|
||||
"attachment_deleted": "Dieser Anhang wurde gelöscht."
|
||||
},
|
||||
"attachment_list": {
|
||||
"open_help_page": "Hilfeseite zu Anhängen öffnen",
|
||||
"owning_note": "Hinweis zum Eigentümer:",
|
||||
"owning_note": "Eigentümernotiz: ",
|
||||
"upload_attachments": "Anhänge hochladen",
|
||||
"no_attachments": "Diese Notiz enthält keine Anhänge."
|
||||
},
|
||||
@@ -951,7 +992,7 @@
|
||||
"specify_new_relation_name": "Gebe den neuen Beziehungsnamen an (erlaubte Zeichen: alphanumerisch, Doppelpunkt und Unterstrich):",
|
||||
"connection_exists": "Die Verbindung „{{name}}“ zwischen diesen Notizen besteht bereits.",
|
||||
"start_dragging_relations": "Beginne hier mit dem Ziehen von Beziehungen und lege sie auf einer anderen Notiz ab.",
|
||||
"note_not_found": "Hinweis {{noteId}} nicht gefunden!",
|
||||
"note_not_found": "Notiz {{noteId}} nicht gefunden!",
|
||||
"cannot_match_transform": "Transformation kann nicht übereinstimmen: {{transform}}",
|
||||
"note_already_in_diagram": "Die Notiz \"{{title}}\" ist schon im Diagram.",
|
||||
"enter_title_of_new_note": "Gebe den Titel der neuen Notiz ein",
|
||||
@@ -959,15 +1000,13 @@
|
||||
"click_on_canvas_to_place_new_note": "Klicke auf den Canvas, um eine neue Notiz zu platzieren"
|
||||
},
|
||||
"render": {
|
||||
"note_detail_render_help_1": "Dieser Hilfehinweis wird angezeigt, da dieser Hinweis vom Typ „HTML rendern“ nicht über die erforderliche Beziehung verfügt, um ordnungsgemäß zu funktionieren.",
|
||||
"note_detail_render_help_1": "Diese Hilfesnotiz wird angezeigt, da diese Notiz vom Typ „HTML rendern“ nicht über die erforderliche Beziehung verfügt, um ordnungsgemäß zu funktionieren.",
|
||||
"note_detail_render_help_2": "Render-HTML-Notiztyp wird benutzt für <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripting</a>. Kurzgesagt, du hast ein HTML-Code-Notiz (optional mit JavaScript) und diese Notiz rendert es. Damit es funktioniert, musst du eine a <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">Beziehung</a> namens \"renderNote\" zeigend auf die HTML-Notiz zum rendern definieren."
|
||||
},
|
||||
"web_view": {
|
||||
"web_view": "Webansicht",
|
||||
"embed_websites": "Hinweis vom Typ Web View ermöglicht das Einbetten von Websites in Trilium.",
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\"",
|
||||
"disclaimer": "Haftungsausschluss zum experimentellen Status",
|
||||
"experimental_note": "Web View ist ein experimenteller Notiztyp und könnte in Zukunft entfernt oder grundlegend geändert werden. Web View funktioniert auch nur im Desktop-Build."
|
||||
"embed_websites": "Notiz vom Typ Web View ermöglicht das Einbetten von Websites in Trilium.",
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "Aktualisieren"
|
||||
@@ -1026,13 +1065,22 @@
|
||||
"main_font": "Handschrift",
|
||||
"font_family": "Schriftfamilie",
|
||||
"size": "Größe",
|
||||
"note_tree_font": "Hinweisbaum-Schriftart",
|
||||
"note_detail_font": "Hinweis-Detail-Schriftart",
|
||||
"note_tree_font": "Notizbaum-Schriftart",
|
||||
"note_detail_font": "Notiz-Detail-Schriftart",
|
||||
"monospace_font": "Minivan (Code) Schriftart",
|
||||
"note_tree_and_detail_font_sizing": "Beachte, dass die Größe der Baum- und Detailschriftarten relativ zur Hauptschriftgrößeneinstellung ist.",
|
||||
"not_all_fonts_available": "Möglicherweise sind nicht alle aufgelisteten Schriftarten auf Ihrem System verfügbar.",
|
||||
"apply_font_changes": "Um Schriftartänderungen zu übernehmen, klicke auf",
|
||||
"reload_frontend": "Frontend neu laden"
|
||||
"reload_frontend": "Frontend neu laden",
|
||||
"generic-fonts": "Generische Schriftarten",
|
||||
"sans-serif-system-fonts": "Sans-serif Systemschriftarten",
|
||||
"serif-system-fonts": "Serif Systemschriftarten",
|
||||
"monospace-system-fonts": "Monospace Systemschriftarten",
|
||||
"handwriting-system-fonts": "Handschrift Systemschriftarten",
|
||||
"serif": "Serif",
|
||||
"sans-serif": "Sans Serif",
|
||||
"monospace": "Monospace",
|
||||
"system-default": "System Standard"
|
||||
},
|
||||
"max_content_width": {
|
||||
"title": "Inhaltsbreite",
|
||||
@@ -1056,8 +1104,17 @@
|
||||
"title": "Thema",
|
||||
"theme_label": "Thema",
|
||||
"override_theme_fonts_label": "Theme-Schriftarten überschreiben",
|
||||
"light_theme": "Licht",
|
||||
"dark_theme": "Dunkel"
|
||||
"auto_theme": "Auto",
|
||||
"light_theme": "Hell",
|
||||
"dark_theme": "Dunkel",
|
||||
"triliumnext": "TriliumNext Beta (Systemfarbschema folgend)",
|
||||
"triliumnext-light": "TriliumNext Beta (Hell)",
|
||||
"triliumnext-dark": "TriliumNext Beta (Dunkel)",
|
||||
"layout": "Layout",
|
||||
"layout-vertical-title": "Vertikal",
|
||||
"layout-horizontal-title": "Horizontal",
|
||||
"layout-vertical-description": "Startleiste ist auf der linken Seite (standard)",
|
||||
"layout-horizontal-description": "Startleiste ist unter der Tableiste. Die Tableiste wird dadurch auf die ganze Breite erweitert."
|
||||
},
|
||||
"zoom_factor": {
|
||||
"title": "Zoomfaktor (nur Desktop-Build)",
|
||||
@@ -1090,7 +1147,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "Zeitüberschreitung beim Löschen von Anhängen",
|
||||
"attachment_auto_deletion_description": "Anhänge werden automatisch gelöscht (und gelöscht), wenn sie nach einer definierten Zeitspanne nicht mehr in ihrer Notiz referenziert werden.",
|
||||
"erase_attachments_after_x_seconds": "Anhänge nach X Sekunden löschen, nachdem sie nicht in der Notiz verwendet wurden",
|
||||
"erase_attachments_after": "Erase unused attachments after:",
|
||||
"manual_erasing_description": "Du kannst das Löschen auch manuell auslösen (ohne Berücksichtigung des oben definierten Timeouts):",
|
||||
"erase_unused_attachments_now": "Lösche jetzt nicht verwendete Anhangnotizen",
|
||||
"unused_attachments_erased": "Nicht verwendete Anhänge wurden gelöscht."
|
||||
@@ -1102,15 +1159,15 @@
|
||||
"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": "Notizen löschen nach",
|
||||
"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."
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Snapshot-Intervall für Notizrevisionen",
|
||||
"note_revisions_snapshot_description": "Das Snapshot-Zeitintervall für Notizrevisionen ist die Zeit in Sekunden, nach der eine neue Notizrevision erstellt wird. Weitere Informationen findest du im <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">Wiki</a>.",
|
||||
"snapshot_time_interval_label": "Zeitintervall für Notiz-Revisions-Snapshot (in Sekunden)"
|
||||
"note_revisions_snapshot_description": "Das Snapshot-Zeitintervall für Notizrevisionen ist die Zeit, nach der eine neue Notizrevision erstellt wird. Weitere Informationen findest du im <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">Wiki</a>.",
|
||||
"snapshot_time_interval_label": "Zeitintervall für Notiz-Revisions-Snapshot:"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "Limit für Notizrevision-Snapshots",
|
||||
@@ -1183,6 +1240,8 @@
|
||||
"backup_now": "Jetzt sichern",
|
||||
"backup_database_now": "Jetzt Datenbank sichern",
|
||||
"existing_backups": "Vorhandene Backups",
|
||||
"date-and-time": "Datum & Uhrzeit",
|
||||
"path": "Pfad",
|
||||
"database_backed_up_to": "Die Datenbank wurde gesichert unter {{backupFilePath}}",
|
||||
"no_backup_yet": "noch kein Backup"
|
||||
},
|
||||
@@ -1190,7 +1249,7 @@
|
||||
"title": "ETAPI",
|
||||
"description": "ETAPI ist eine REST-API, die für den programmgesteuerten Zugriff auf die Trilium-Instanz ohne Benutzeroberfläche verwendet wird.",
|
||||
"see_more": "Weitere Details findest du unter",
|
||||
"wiki": "Woche",
|
||||
"wiki": "Wiki",
|
||||
"and": "und",
|
||||
"openapi_spec": "ETAPI OpenAPI-Spezifikation",
|
||||
"create_token": "Erstelle ein neues ETAPI-Token",
|
||||
@@ -1227,7 +1286,7 @@
|
||||
"protected_session_timeout_description": "Das Zeitlimit für geschützte Sitzungen ist ein Zeitraum, nach dem die geschützte Sitzung aus dem Speicher des Browsers gelöscht wird. Dies wird ab der letzten Interaktion mit geschützten Notizen gemessen. Sehen",
|
||||
"wiki": "Wiki",
|
||||
"for_more_info": "für weitere Informationen.",
|
||||
"protected_session_timeout_label": "Zeitüberschreitung der geschützten Sitzung (in Sekunden)",
|
||||
"protected_session_timeout_label": "Zeitüberschreitung der geschützten Sitzung:",
|
||||
"reset_confirmation": "Durch das Zurücksetzen des Passworts verlierst du für immer den Zugriff auf alle Ihre bestehenden geschützten Notizen. Möchtest du das Passwort wirklich zurücksetzen?",
|
||||
"reset_success_message": "Das Passwort wurde zurückgesetzt. Bitte lege ein neues Passwort fest",
|
||||
"change_password_heading": "Kennwort ändern",
|
||||
@@ -1356,7 +1415,9 @@
|
||||
"launcher": "Launcher",
|
||||
"doc": "Dokument",
|
||||
"widget": "Widget",
|
||||
"confirm-change": "Es is nicht empfehlenswert den Notiz-Typ zu ändern, wenn der Inhalt der Notiz nicht leer ist. Möchtest du dennoch fortfahren?"
|
||||
"confirm-change": "Es is nicht empfehlenswert den Notiz-Typ zu ändern, wenn der Inhalt der Notiz nicht leer ist. Möchtest du dennoch fortfahren?",
|
||||
"geo-map": "Geo Map",
|
||||
"beta-feature": "Beta"
|
||||
},
|
||||
"protect_note": {
|
||||
"toggle-on": "Notiz schützen",
|
||||
@@ -1379,7 +1440,11 @@
|
||||
"open-help-page": "Hilfeseite öffnen",
|
||||
"find": {
|
||||
"case_sensitive": "Groß-/Kleinschreibung beachten",
|
||||
"match_words": "Wörter genau übereinstimmen"
|
||||
"match_words": "Wörter genau übereinstimmen",
|
||||
"find_placeholder": "Finde in Text...",
|
||||
"replace_placeholder": "Ersetze mit...",
|
||||
"replace": "Ersetzen",
|
||||
"replace_all": "Alle Ersetzen"
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"title": "Hervorhebungs-Liste",
|
||||
@@ -1404,7 +1469,11 @@
|
||||
"automatically-collapse-notes-title": "Notizen werden nach einer Inaktivitätsperiode automatisch zusammengeklappt, um den Baum zu entlasten.",
|
||||
"save-changes": "Änderungen speichern und anwenden",
|
||||
"auto-collapsing-notes-after-inactivity": "Automatisches Zusammenklappen von Notizen nach Inaktivität…",
|
||||
"saved-search-note-refreshed": "Gespeicherte Such-Notiz wurde aktualisiert."
|
||||
"saved-search-note-refreshed": "Gespeicherte Such-Notiz wurde aktualisiert.",
|
||||
"hoist-this-note-workspace": "Diese Notiz fokussieren (Arbeitsbereich)",
|
||||
"refresh-saved-search-results": "Gespeicherte Suchergebnisse aktualisieren",
|
||||
"create-child-note": "Unternotiz anlegen",
|
||||
"unhoist": "Entfokussieren"
|
||||
},
|
||||
"title_bar_buttons": {
|
||||
"window-on-top": "Dieses Fenster immer oben halten"
|
||||
@@ -1435,7 +1504,9 @@
|
||||
"close_other_tabs": "Andere Tabs schließen",
|
||||
"close_right_tabs": "Tabs rechts schließen",
|
||||
"close_all_tabs": "Alle Tabs schließen",
|
||||
"reopen_last_tab": "Zuletzt geschlossenen Tab erneut öffnen",
|
||||
"move_tab_to_new_window": "Tab in neues Fenster verschieben",
|
||||
"copy_tab_to_new_window": "Tab in neues Fenster kopieren",
|
||||
"new_tab": "Neuer Tab"
|
||||
},
|
||||
"toc": {
|
||||
@@ -1487,7 +1558,7 @@
|
||||
"confirm_unhoisting": "Die angeforderte Notiz ‚{{requestedNote}}‘ befindet sich außerhalb des hoisted Bereichs der Notiz ‚{{hoistedNote}}‘. Du musst sie unhoisten, um auf die Notiz zuzugreifen. Möchtest du mit dem Unhoisting fortfahren?"
|
||||
},
|
||||
"launcher_context_menu": {
|
||||
"reset_launcher_confirm": "Möchtest du „{{title}}“ wirklich zurücksetzen? Alle Daten / Einstellungen in dieser Notiz (und ihren Kindern) gehen verloren und der Launcher wird an seinen ursprünglichen Standort zurückgesetzt.",
|
||||
"reset_launcher_confirm": "Möchtest du „{{title}}“ wirklich zurücksetzen? Alle Daten / Einstellungen in dieser Notiz (und ihren Unternotizen) gehen verloren und der Launcher wird an seinen ursprünglichen Standort zurückgesetzt.",
|
||||
"add-note-launcher": "Launcher für Notiz hinzufügen",
|
||||
"add-script-launcher": "Launcher für Skript hinzufügen",
|
||||
"add-custom-widget": "Benutzerdefiniertes Widget hinzufügen",
|
||||
@@ -1508,5 +1579,84 @@
|
||||
},
|
||||
"code_block": {
|
||||
"word_wrapping": "Wortumbruch"
|
||||
},
|
||||
"classic_editor_toolbar": {
|
||||
"title": "Format"
|
||||
},
|
||||
"editor": {
|
||||
"title": "Editor"
|
||||
},
|
||||
"editing": {
|
||||
"editor_type": {
|
||||
"label": "Format Toolbar",
|
||||
"floating": {
|
||||
"title": "Schwebend",
|
||||
"description": "Werkzeuge erscheinen in Cursornähe"
|
||||
},
|
||||
"fixed": {
|
||||
"title": "Fixiert",
|
||||
"description": "Werkzeuge erscheinen im \"Format\" Tab"
|
||||
},
|
||||
"multiline-toolbar": "Toolbar wenn nötig in mehreren Zeilen darstellen."
|
||||
}
|
||||
},
|
||||
"electron_context_menu": {
|
||||
"add-term-to-dictionary": "Begriff \"{{term}}\" zum Wörterbuch hinzufügen",
|
||||
"cut": "Ausschneiden",
|
||||
"copy": "Kopieren",
|
||||
"copy-link": "Link opieren",
|
||||
"paste": "Einfügen",
|
||||
"paste-as-plain-text": "Als unformatierten Text einfügen",
|
||||
"search_online": "Suche nach \"{{term}}\" mit {{searchEngine}} starten"
|
||||
},
|
||||
"image_context_menu": {
|
||||
"copy_reference_to_clipboard": "Referenz in Zwischenablage kopieren",
|
||||
"copy_image_to_clipboard": "Bild in die Zwischenablage kopieren"
|
||||
},
|
||||
"link_context_menu": {
|
||||
"open_note_in_new_tab": "Notiz in neuen Tab öffnen",
|
||||
"open_note_in_new_split": "Notiz in neuen geteilten Tab öffnen",
|
||||
"open_note_in_new_window": "Notiz in neuen Fenster öffnen"
|
||||
},
|
||||
"electron_integration": {
|
||||
"desktop-application": "Desktop Anwendung",
|
||||
"native-title-bar": "Native Anwendungsleiste",
|
||||
"native-title-bar-description": "In Windows und macOS, sorgt das Deaktivieren der nativen Anwendungsleiste für ein kompakteres Aussehen. Unter Linux, sorgt das Aktivieren der nativen Anwendungsleiste für eine bessere Integration mit anderen Teilen des Systems.",
|
||||
"background-effects": "Hintergrundeffekte aktivieren (nur Windows 11)",
|
||||
"background-effects-description": "Der Mica Effekt fügt einen unscharfen, stylischen Hintergrund in Anwendungsfenstern ein. Dieser erzeugt Tiefe und ein modernes Auftreten.",
|
||||
"restart-app-button": "Anwendung neustarten um Änderungen anzuwenden",
|
||||
"zoom-factor": "Zoomfaktor"
|
||||
},
|
||||
"note_autocomplete": {
|
||||
"search-for": "Suche nach \"{{term}}\"",
|
||||
"create-note": "Erstelle und verlinke Unternotiz \"{{term}}\"",
|
||||
"insert-external-link": "Einfügen von Externen Link zu \"{{term}}\"",
|
||||
"clear-text-field": "Textfeldinhalt löschen",
|
||||
"show-recent-notes": "Aktuelle Notizen anzeigen",
|
||||
"full-text-search": "Volltextsuche"
|
||||
},
|
||||
"note_tooltip": {
|
||||
"note-has-been-deleted": "Notiz wurde gelöscht."
|
||||
},
|
||||
"geo-map": {
|
||||
"create-child-note-title": "Neue Unternotiz anlegen und zur Karte hinzufügen",
|
||||
"create-child-note-instruction": "Auf die Karte klicken, um eine neue Notiz an der Stelle zu erstellen oder Escape drücken um abzubrechen.",
|
||||
"unable-to-load-map": "Karte konnte nicht geladen werden."
|
||||
},
|
||||
"geo-map-context": {
|
||||
"open-location": "Ort öffnen",
|
||||
"remove-from-map": "Von Karte entfernen"
|
||||
},
|
||||
"help-button": {
|
||||
"title": "Relevante Hilfeseite öffnen"
|
||||
},
|
||||
"duration": {
|
||||
"seconds": "Sekunden",
|
||||
"minutes": "Minuten",
|
||||
"hours": "Stunden",
|
||||
"days": "Tage"
|
||||
},
|
||||
"time_selector": {
|
||||
"invalid_input": "Die eingegebene Zeit ist keine valide Zahl."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,6 +824,7 @@
|
||||
},
|
||||
"promoted_attributes": {
|
||||
"promoted_attributes": "Promoted Attributes",
|
||||
"unset-field-placeholder": "not set",
|
||||
"url_placeholder": "http://website...",
|
||||
"open_external_link": "Open external link",
|
||||
"unknown_label_type": "Unknown label type '{{type}}'",
|
||||
@@ -966,7 +967,8 @@
|
||||
"enter_workspace": "Enter workspace {{title}}"
|
||||
},
|
||||
"file": {
|
||||
"file_preview_not_available": "File preview is not available for this file format."
|
||||
"file_preview_not_available": "File preview is not available for this file format.",
|
||||
"too_big": "The preview only shows the first {{maxNumChars}} characters of the file for performance reasons. Download the file and open it externally to be able to see the entire content."
|
||||
},
|
||||
"protected_session": {
|
||||
"enter_password_instruction": "Showing protected note requires entering your password:",
|
||||
@@ -1005,9 +1007,7 @@
|
||||
"web_view": {
|
||||
"web_view": "Web View",
|
||||
"embed_websites": "Note of type Web View allows you to embed websites into Trilium.",
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\"",
|
||||
"disclaimer": "Disclaimer on the experimental status",
|
||||
"experimental_note": "Web View is an experimental note type, and it might be removed or substantially changed in the future. Web View works also only in the desktop build."
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "Refresh"
|
||||
@@ -1148,7 +1148,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "Attachment Erasure Timeout",
|
||||
"attachment_auto_deletion_description": "Attachments get automatically deleted (and erased) if they are not referenced by their note anymore after a defined time out.",
|
||||
"erase_attachments_after_x_seconds": "Erase attachments after X seconds of not being used in its note",
|
||||
"erase_attachments_after": "Erase unused attachments after:",
|
||||
"manual_erasing_description": "You can also trigger erasing manually (without considering the timeout defined above):",
|
||||
"erase_unused_attachments_now": "Erase unused attachment notes now",
|
||||
"unused_attachments_erased": "Unused attachments have been erased."
|
||||
@@ -1160,15 +1160,15 @@
|
||||
"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": "Erase notes after",
|
||||
"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."
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Note Revision Snapshot Interval",
|
||||
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time in seconds after which a new note revision will be created for the note. See <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> for more info.",
|
||||
"snapshot_time_interval_label": "Note revision snapshot time interval (in seconds):"
|
||||
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> for more info.",
|
||||
"snapshot_time_interval_label": "Note revision snapshot time interval:"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "Note Revision Snapshot Limit",
|
||||
@@ -1287,7 +1287,7 @@
|
||||
"protected_session_timeout_description": "Protected session timeout is a time period after which the protected session is wiped from the browser's memory. This is measured from the last interaction with protected notes. See",
|
||||
"wiki": "wiki",
|
||||
"for_more_info": "for more info.",
|
||||
"protected_session_timeout_label": "Protected session timeout (in seconds)",
|
||||
"protected_session_timeout_label": "Protected session timeout:",
|
||||
"reset_confirmation": "By resetting the password you will forever lose access to all your existing protected notes. Do you really want to reset the password?",
|
||||
"reset_success_message": "Password has been reset. Please set new password",
|
||||
"change_password_heading": "Change Password",
|
||||
@@ -1656,5 +1656,20 @@
|
||||
"minutes": "Minutes",
|
||||
"hours": "Hours",
|
||||
"days": "Days"
|
||||
},
|
||||
"share": {
|
||||
"title": "Share Settings",
|
||||
"redirect_bare_domain": "Redirect bare domain to Share page",
|
||||
"redirect_bare_domain_description": "Redirect anonymous users to the Share page instead of showing Login",
|
||||
"show_login_link": "Show Login link in Share theme",
|
||||
"show_login_link_description": "Add a login link to the Share page footer",
|
||||
"check_share_root": "Check Share Root Status",
|
||||
"share_root_found": "Share root note '{{noteTitle}}' is ready",
|
||||
"share_root_not_found": "No note with #shareRoot label found",
|
||||
"share_root_not_shared": "Note '{{noteTitle}}' has #shareRoot label but is not Shared"
|
||||
},
|
||||
"time_selector": {
|
||||
"invalid_input": "The entered time value is not a valid number.",
|
||||
"minimum_input": "The entered time value needs to be at least {{minimumSeconds}} seconds."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,9 +1003,7 @@
|
||||
"web_view": {
|
||||
"web_view": "Vista web",
|
||||
"embed_websites": "La nota de tipo Web View le permite insertar sitios web en Trilium.",
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\"",
|
||||
"disclaimer": "Descargo de responsabilidad sobre el estado experimental",
|
||||
"experimental_note": "Web View es un tipo de nota experimental y es posible que se elimine o cambie sustancialmente en el futuro. Web View también funciona solo en la versión de escritorio."
|
||||
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "Refrescar"
|
||||
@@ -1146,7 +1144,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "Tiempo de espera para borrar archivos adjuntos",
|
||||
"attachment_auto_deletion_description": "Los archivos adjuntos se eliminan (y borran) automáticamente si ya no se hace referencia a ellos en su nota después de un tiempo de espera definido.",
|
||||
"erase_attachments_after_x_seconds": "Borrar archivos adjuntos después de X segundos de no usarse en su nota",
|
||||
"erase_attachments_after": "Erase unused attachments after:",
|
||||
"manual_erasing_description": "También puede activar el borrado manualmente (sin considerar el tiempo de espera definido anteriormente):",
|
||||
"erase_unused_attachments_now": "Borrar ahora los archivos adjuntos no utilizados en la nota",
|
||||
"unused_attachments_erased": "Los archivos adjuntos no utilizados se han eliminado."
|
||||
@@ -1158,15 +1156,15 @@
|
||||
"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": "Borrar notas después de",
|
||||
"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."
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Intervalo de instantáneas de revisiones de notas",
|
||||
"note_revisions_snapshot_description": "El intervalo de tiempo de la instantánea de revisión de nota es el tiempo en segundos después de lo cual se creará una nueva revisión para la nota. Ver <a href=\"https://triliumnext.github.io/docs/wiki/note-revisions.html\" class=\"external\"> wiki </a> para obtener más información.",
|
||||
"snapshot_time_interval_label": "Intervalo de tiempo de la instantánea de revisión de notas (en segundos)"
|
||||
"note_revisions_snapshot_description": "El intervalo de tiempo de la instantánea de revisión de nota es el tiempo después de lo cual se creará una nueva revisión para la nota. Ver <a href=\"https://triliumnext.github.io/docs/wiki/note-revisions.html\" class=\"external\"> wiki </a> para obtener más información.",
|
||||
"snapshot_time_interval_label": "Intervalo de tiempo de la instantánea de revisión de notas:"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "Límite de respaldos de revisiones de nota",
|
||||
@@ -1285,7 +1283,7 @@
|
||||
"protected_session_timeout_description": "El tiempo de espera de la sesión protegida es el período de tiempo después del cual la sesión protegida se borra de la memoria del navegador. Esto se mide desde la última interacción con notas protegidas. Ver",
|
||||
"wiki": "wiki",
|
||||
"for_more_info": "para más información.",
|
||||
"protected_session_timeout_label": "Tiempo de espera de sesión protegida (en segundos)",
|
||||
"protected_session_timeout_label": "Tiempo de espera de sesión protegida:",
|
||||
"reset_confirmation": "Al restablecer la contraseña, perderá para siempre el acceso a todas sus notas protegidas existentes. ¿Realmente quieres restablecer la contraseña?",
|
||||
"reset_success_message": "La contraseña ha sido restablecida. Por favor establezca una nueva contraseña",
|
||||
"change_password_heading": "Cambiar contraseña",
|
||||
|
||||
@@ -966,9 +966,7 @@
|
||||
"web_view": {
|
||||
"web_view": "Affichage Web",
|
||||
"embed_websites": "Les notes de type Affichage Web vous permet d'intégrer des sites Web dans Trilium.",
|
||||
"create_label": "Pour commencer, veuillez créer un label avec l'adresse URL que vous souhaitez intégrer, par ex. #webViewSrc=\"https://www.google.com\"",
|
||||
"disclaimer": "Avertissement sur le statut expérimental",
|
||||
"experimental_note": "Affichage Web est un type de note expérimental et il pourrait être supprimé ou considérablement modifié à l'avenir. Affichage Web ne fonctionne que dans la version de bureau."
|
||||
"create_label": "Pour commencer, veuillez créer un label avec l'adresse URL que vous souhaitez intégrer, par ex. #webViewSrc=\"https://www.google.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "Rafraîchir"
|
||||
@@ -1091,7 +1089,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "Délai d'effacement des pièces jointes",
|
||||
"attachment_auto_deletion_description": "Les pièces jointes sont automatiquement supprimées (et effacées) si elles ne sont plus référencées par leur note après un certain délai.",
|
||||
"erase_attachments_after_x_seconds": "Effacer les pièces jointes après X secondes sans utilisation dans sa note",
|
||||
"erase_attachments_after": "Erase unused attachments after:",
|
||||
"manual_erasing_description": "Vous pouvez également déclencher l'effacement manuellement (sans tenir compte du délai défini ci-dessus) :",
|
||||
"erase_unused_attachments_now": "Effacez maintenant les pièces jointes inutilisées",
|
||||
"unused_attachments_erased": "Les pièces jointes inutilisées ont été effacées."
|
||||
@@ -1103,15 +1101,15 @@
|
||||
"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": "Effacer les notes après",
|
||||
"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."
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Intervalle d'enregistrement automatique des versions des notes",
|
||||
"note_revisions_snapshot_description": "L'intervalle d'enregistrement automatique des versions de note est le temps en secondes après lequel une nouvelle version de note est créée pour une note. Consultez le <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> pour plus d'informations.",
|
||||
"snapshot_time_interval_label": "Intervalle de temps entre deux enregistrements de version de note (en secondes) :"
|
||||
"note_revisions_snapshot_description": "L'intervalle d'enregistrement automatique des versions de note est le temps après lequel une nouvelle version de note est créée pour une note. Consultez le <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> pour plus d'informations.",
|
||||
"snapshot_time_interval_label": "Intervalle de temps entre deux enregistrements de version de note :"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "Limite des enregistrements de version de note",
|
||||
@@ -1228,7 +1226,7 @@
|
||||
"protected_session_timeout_description": "Le délai d'expiration de la session protégée est une période de temps après laquelle la session protégée est effacée de la mémoire du navigateur. Il est mesuré à partir de la dernière interaction avec des notes protégées. Voir",
|
||||
"wiki": "wiki",
|
||||
"for_more_info": "pour plus d'informations.",
|
||||
"protected_session_timeout_label": "Délai d'expiration de la session protégée (en secondes)",
|
||||
"protected_session_timeout_label": "Délai d'expiration de la session protégée :",
|
||||
"reset_confirmation": "En réinitialisant le mot de passe, vous perdrez à jamais l'accès à toutes vos notes protégées existantes. Voulez-vous vraiment réinitialiser le mot de passe ?",
|
||||
"reset_success_message": "Le mot de passe a été réinitialisé. Veuillez définir un nouveau mot de passe",
|
||||
"change_password_heading": "Changer le mot de passe",
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_auto_deletion_description": "Atașamentele se șterg automat (permanent) dacă nu sunt referențiate de către notița lor părinte după un timp prestabilit de timp.",
|
||||
"attachment_erasure_timeout": "Perioadă de ștergere a atașamentelor",
|
||||
"erase_attachments_after_x_seconds": "Șterge atașamentele după X secunde după ce acestea n-au mai fost folosite într-o notiță",
|
||||
"erase_attachments_after": "Erase unused attachments after:",
|
||||
"erase_unused_attachments_now": "Elimină atașamentele șterse acum",
|
||||
"manual_erasing_description": "Șterge acum toate atașamentele nefolosite din notițe",
|
||||
"unused_attachments_erased": "Atașamentele nefolosite au fost șterse."
|
||||
@@ -836,7 +836,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": "Elimină notițele șterse după",
|
||||
"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"
|
||||
@@ -923,7 +923,7 @@
|
||||
"password_mismatch": "Noile parole nu coincid.",
|
||||
"protected_session_timeout": "Timpul de expirare a sesiunii protejate",
|
||||
"protected_session_timeout_description": "Timpul de expirare a sesiunii protejate este o perioadă de timp după care sesiunea protejată este ștearsă din memoria navigatorului. Aceasta este măsurată de la timpul ultimei interacțiuni cu notițele protejate. Vezi",
|
||||
"protected_session_timeout_label": "Timpul de expirare a sesiunii protejate (în secunde)",
|
||||
"protected_session_timeout_label": "Timpul de expirare a sesiunii protejate:",
|
||||
"reset_confirmation": "Prin resetarea parolei se va pierde pentru totdeauna accesul la notițele protejate existente. Sigur doriți resetarea parolei?",
|
||||
"reset_link": "click aici pentru a o reseta.",
|
||||
"reset_success_message": "Parola a fost resetată. Setați o nouă parolă",
|
||||
@@ -944,7 +944,8 @@
|
||||
"remove_this_attribute": "Elimină acest atribut",
|
||||
"unknown_attribute_type": "Tip de atribut necunoscut „{{type}}”",
|
||||
"unknown_label_type": "Tip de etichetă necunoscut „{{type}}”",
|
||||
"url_placeholder": "http://siteweb..."
|
||||
"url_placeholder": "http://siteweb...",
|
||||
"unset-field-placeholder": "nesetat"
|
||||
},
|
||||
"prompt": {
|
||||
"defaultTitle": "Aviz",
|
||||
@@ -1068,9 +1069,9 @@
|
||||
"note_revisions": "Revizii ale notiței"
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_description": "Intervalul de salvare a reviziilor este timpul în secunde după care se crează o nouă revizie a unei notițe. Vedeți <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki-ul</a> pentru mai multe informații.",
|
||||
"note_revisions_snapshot_description": "Intervalul de salvare a reviziilor este timpul după care se crează o nouă revizie a unei notițe. Vedeți <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki-ul</a> pentru mai multe informații.",
|
||||
"note_revisions_snapshot_interval_title": "Intervalul de salvare a reviziilor",
|
||||
"snapshot_time_interval_label": "Intervalul de salvare a reviziilor (în secunde)"
|
||||
"snapshot_time_interval_label": "Intervalul de salvare a reviziilor:"
|
||||
},
|
||||
"ribbon": {
|
||||
"edited_notes_message": "Tab-ul panglicii „Notițe editate” se va deschide automat pentru notițele zilnice",
|
||||
@@ -1307,9 +1308,7 @@
|
||||
},
|
||||
"web_view": {
|
||||
"create_label": "Pentru a începe, creați o etichetă cu adresa URL de încorporat, e.g. #webViewSrc=\"https://www.google.com\"",
|
||||
"disclaimer": "Avertisment despre statutul experimental",
|
||||
"embed_websites": "Notițele de tip „Vizualizare web” permit încorporarea site-urilor web în Trilium.",
|
||||
"experimental_note": "„Vizualizare web” este un tip experimental de notiță, și poate fi înlăturat sau schimbat substanțial în viitor. De asemenea, Web View funcționeză doar în aplicația desktop.",
|
||||
"web_view": "Vizualizare web"
|
||||
},
|
||||
"wrap_lines": {
|
||||
|
||||
@@ -970,9 +970,7 @@
|
||||
"web_view": {
|
||||
"web_view": "網頁視圖",
|
||||
"embed_websites": "網頁視圖類型的筆記允許您將網站嵌入到 Trilium 中。",
|
||||
"create_label": "首先,請新增一個帶有您要嵌入的 URL 地址的標籤,例如 #webViewSrc=\"https://www.bing.com\"",
|
||||
"disclaimer": "實驗性功能免責聲明",
|
||||
"experimental_note": "網頁視圖是一種實驗性的筆記類型,將來可能會被移除或大幅更改。網頁視圖只在桌面端有效。"
|
||||
"create_label": "首先,請新增一個帶有您要嵌入的 URL 地址的標籤,例如 #webViewSrc=\"https://www.bing.com\""
|
||||
},
|
||||
"backend_log": {
|
||||
"refresh": "刷新"
|
||||
@@ -1100,7 +1098,7 @@
|
||||
"attachment_erasure_timeout": {
|
||||
"attachment_erasure_timeout": "附件清理超時",
|
||||
"attachment_auto_deletion_description": "如果附件在一段時間後不再被筆記引用,它們將自動被刪除(並被清理)。",
|
||||
"erase_attachments_after_x_seconds": "在附件在筆記中未被使用 X 秒後清理",
|
||||
"erase_attachments_after": "",
|
||||
"manual_erasing_description": "您還可以手動觸發清理(而不考慮上述定義的超時時間):",
|
||||
"erase_unused_attachments_now": "立即清理未使用的附件筆記",
|
||||
"unused_attachments_erased": "未使用的附件已被刪除。"
|
||||
@@ -1112,15 +1110,15 @@
|
||||
"note_erasure_timeout": {
|
||||
"note_erasure_timeout_title": "筆記清理超時",
|
||||
"note_erasure_description": "被刪除的筆記(以及屬性、歷史版本等)最初僅被標記為「刪除」,可以從「最近修改」對話框中恢復它們。經過一段時間後,已刪除的筆記會被「清理」,這意味著它們的內容將無法恢復。此設定允許您設定從刪除到清除筆記之間的時間長度。",
|
||||
"erase_notes_after": "Erase notes after",
|
||||
"erase_notes_after": "",
|
||||
"manual_erasing_description": "您還可以手動觸發清理(不考慮上述定義的超時):",
|
||||
"erase_deleted_notes_now": "立即清理已刪除的筆記",
|
||||
"deleted_notes_erased": "已刪除的筆記已被清理。"
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "筆記修改快照間隔",
|
||||
"note_revisions_snapshot_description": "筆記修改快照時間間隔是指經過多少秒後會為筆記新增新的修改歷史。更多資訊請參見<a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a>。",
|
||||
"snapshot_time_interval_label": "筆記修改快照時間間隔(單位:秒)"
|
||||
"note_revisions_snapshot_interval_title": "",
|
||||
"note_revisions_snapshot_description": "",
|
||||
"snapshot_time_interval_label": ""
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "筆記歷史快照限制",
|
||||
@@ -1239,7 +1237,7 @@
|
||||
"protected_session_timeout_description": "保護會話超時是一個時間段,超時後保護會話會從瀏覽器內存中清除。這是從最後一次與保護筆記的交互開始計時的。更多資訊請見",
|
||||
"wiki": "維基",
|
||||
"for_more_info": "更多資訊。",
|
||||
"protected_session_timeout_label": "保護會話超時(秒)",
|
||||
"protected_session_timeout_label": "",
|
||||
"reset_confirmation": "重置密碼將永久喪失對所有現受保護筆記的訪問。您真的要重置密碼嗎?",
|
||||
"reset_success_message": "密碼已重置。請設定新密碼",
|
||||
"change_password_heading": "更改密碼",
|
||||
|
||||
@@ -5,7 +5,7 @@ import log from "../../services/log.js";
|
||||
import searchService from "../../services/search/services/search.js";
|
||||
import ValidationError from "../../errors/validation_error.js";
|
||||
import type { Request } from "express";
|
||||
import { changeLanguage } from "../../services/i18n.js";
|
||||
import { changeLanguage, getLocales } from "../../services/i18n.js";
|
||||
import { listSyntaxHighlightingThemes } from "../../services/code_block_theme.js";
|
||||
import type { OptionNames } from "../../services/options_interface.js";
|
||||
|
||||
@@ -14,7 +14,9 @@ const ALLOWED_OPTIONS = new Set([
|
||||
"eraseEntitiesAfterTimeInSeconds",
|
||||
"eraseEntitiesAfterTimeScale",
|
||||
"protectedSessionTimeout",
|
||||
"protectedSessionTimeoutTimeScale",
|
||||
"revisionSnapshotTimeInterval",
|
||||
"revisionSnapshotTimeIntervalTimeScale",
|
||||
"revisionSnapshotNumberLimit",
|
||||
"zoomFactor",
|
||||
"theme",
|
||||
@@ -61,6 +63,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
"checkForUpdates",
|
||||
"disableTray",
|
||||
"eraseUnusedAttachmentsAfterSeconds",
|
||||
"eraseUnusedAttachmentsAfterTimeScale",
|
||||
"disableTray",
|
||||
"customSearchEngineName",
|
||||
"customSearchEngineUrl",
|
||||
@@ -72,7 +75,9 @@ const ALLOWED_OPTIONS = new Set([
|
||||
"textNoteEditorMultilineToolbar",
|
||||
"layoutOrientation",
|
||||
"backgroundEffects",
|
||||
"allowedHtmlTags" // Allow configuring HTML import tags
|
||||
"allowedHtmlTags",
|
||||
"redirectBareDomain",
|
||||
"showLoginInShareTheme"
|
||||
]);
|
||||
|
||||
function getOptions() {
|
||||
@@ -153,37 +158,7 @@ function getSyntaxHighlightingThemes() {
|
||||
}
|
||||
|
||||
function getSupportedLocales() {
|
||||
// TODO: Currently hardcoded, needs to read the list of available languages.
|
||||
return [
|
||||
{
|
||||
id: "en",
|
||||
name: "English"
|
||||
},
|
||||
{
|
||||
id: "de",
|
||||
name: "Deutsch"
|
||||
},
|
||||
{
|
||||
id: "es",
|
||||
name: "Español"
|
||||
},
|
||||
{
|
||||
id: "fr",
|
||||
name: "Français"
|
||||
},
|
||||
{
|
||||
id: "cn",
|
||||
name: "简体中文"
|
||||
},
|
||||
{
|
||||
id: "tw",
|
||||
name: "繁體中文"
|
||||
},
|
||||
{
|
||||
id: "ro",
|
||||
name: "Română"
|
||||
}
|
||||
];
|
||||
return getLocales();
|
||||
}
|
||||
|
||||
function isAllowed(name: string) {
|
||||
|
||||
@@ -29,8 +29,8 @@ function getYearNote(req: Request) {
|
||||
|
||||
function getDayNotesForMonth(req: Request) {
|
||||
const month = req.params.month;
|
||||
|
||||
return sql.getMap(`
|
||||
const calendarRoot = req.query.calendarRoot;
|
||||
const query = `\
|
||||
SELECT
|
||||
attr.value AS date,
|
||||
notes.noteId
|
||||
@@ -40,7 +40,22 @@ function getDayNotesForMonth(req: Request) {
|
||||
AND attr.isDeleted = 0
|
||||
AND attr.type = 'label'
|
||||
AND attr.name = 'dateNote'
|
||||
AND attr.value LIKE '${month}%'`);
|
||||
AND attr.value LIKE '${month}%'`;
|
||||
|
||||
if (calendarRoot) {
|
||||
const rows = sql.getRows<{ date: string, noteId: string }>(query);
|
||||
const result: Record<string, string> = {};
|
||||
for (const {date, noteId} of rows) {
|
||||
const note = becca.getNote(noteId);
|
||||
if (note?.hasAncestor(String(calendarRoot))) {
|
||||
result[date] = noteId;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return sql.getMap(query);
|
||||
}
|
||||
}
|
||||
|
||||
function saveSqlConsole(req: Request) {
|
||||
|
||||
@@ -7,6 +7,8 @@ import { isElectron } from "./utils.js";
|
||||
import passwordEncryptionService from "./encryption/password_encryption.js";
|
||||
import config from "./config.js";
|
||||
import passwordService from "./encryption/password.js";
|
||||
import options from "./options.js";
|
||||
import attributes from "./attributes.js";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
|
||||
const noAuthentication = config.General && config.General.noAuthentication === true;
|
||||
@@ -15,7 +17,16 @@ function checkAuth(req: Request, res: Response, next: NextFunction) {
|
||||
if (!sqlInit.isDbInitialized()) {
|
||||
res.redirect("setup");
|
||||
} else if (!req.session.loggedIn && !isElectron && !noAuthentication) {
|
||||
res.redirect("login");
|
||||
const redirectToShare = options.getOptionBool("redirectBareDomain");
|
||||
if (redirectToShare) {
|
||||
// Check if any note has the #shareRoot label
|
||||
const shareRootNotes = attributes.getNotesWithLabel("shareRoot");
|
||||
if (shareRootNotes.length === 0) {
|
||||
res.status(404).json({ message: "Share root not found. Please set up a note with #shareRoot label first." });
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.redirect(redirectToShare ? "share" : "login");
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||