mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 19:12:27 +08:00
Merge develop
into test_server-utils
This commit is contained in:
commit
892734bce3
@ -1,12 +1,17 @@
|
||||
[Desktop Entry]
|
||||
<% if (productName) { %>Name=<%= productName %>
|
||||
<% } %><% if (description) { %>Comment=<%= description %>
|
||||
<% } %><% if (genericName) { %>GenericName=<%= genericName %>
|
||||
<% } %><% if (name) { %>Exec=<%= name %> %U
|
||||
Icon=<%= name %>
|
||||
<% } %>Type=Application
|
||||
StartupNotify=true
|
||||
<% if (productName) { %>StartupWMClass=<%= productName %>
|
||||
<% } if (categories && categories.length) { %>Categories=<%= categories.join(';') %>;
|
||||
<% } %><% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>;
|
||||
<% } %>
|
||||
<%=
|
||||
Object.entries({
|
||||
"Name": productName,
|
||||
"Comment": description,
|
||||
"GenericName": genericName,
|
||||
"Exec": name ? `${name} %U` : undefined,
|
||||
"Icon": name,
|
||||
"Type": "Application",
|
||||
"StartupNotify": "true",
|
||||
"StartupWMClass": productName,
|
||||
"Categories": categories?.length ? `${categories.join(";")};` : undefined,
|
||||
"MimeType": mimeType?.length ? `${mimeType.join(";")};` : undefined
|
||||
})
|
||||
.map(line => line[1] ? line.join("=") : undefined)
|
||||
.filter(line => !!line)
|
||||
.join("\n")%>
|
@ -26,6 +26,8 @@ electronDl({ saveAs: true });
|
||||
// needed for excalidraw export https://github.com/zadam/trilium/issues/4271
|
||||
electron.app.commandLine.appendSwitch("enable-experimental-web-platform-features");
|
||||
|
||||
electron.app.userAgentFallback = `${electron.app.getName()} ${electron.app.getVersion()}`;
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
|
165
package-lock.json
generated
165
package-lock.json
generated
@ -69,6 +69,7 @@
|
||||
"katex": "0.16.21",
|
||||
"knockout": "3.5.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.1.2",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "15.0.6",
|
||||
"mermaid": "11.4.1",
|
||||
@ -85,7 +86,6 @@
|
||||
"sanitize-filename": "1.6.3",
|
||||
"sanitize-html": "2.14.0",
|
||||
"sax": "1.4.1",
|
||||
"semver": "7.7.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"session-file-store": "1.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
@ -112,7 +112,7 @@
|
||||
"@electron-forge/maker-zip": "7.6.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.6.1",
|
||||
"@electron/rebuild": "3.7.1",
|
||||
"@playwright/test": "1.50.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/better-sqlite3": "7.6.12",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
@ -132,14 +132,14 @@
|
||||
"@types/jasmine": "5.1.5",
|
||||
"@types/jquery": "3.5.32",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/multer": "1.4.12",
|
||||
"@types/node": "22.12.0",
|
||||
"@types/node": "22.13.1",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/safe-compare": "1.1.2",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/sax": "1.2.7",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"@types/session-file-store": "1.2.5",
|
||||
"@types/source-map-support": "0.5.10",
|
||||
@ -149,7 +149,7 @@
|
||||
"@types/ws": "8.5.14",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.0.4",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "34.0.2",
|
||||
"esm": "3.2.25",
|
||||
@ -164,7 +164,7 @@
|
||||
"tsx": "4.19.2",
|
||||
"typedoc": "0.27.6",
|
||||
"typescript": "5.7.3",
|
||||
"vitest": "3.0.4",
|
||||
"vitest": "3.0.5",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-middleware": "7.4.2"
|
||||
@ -2878,13 +2878,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.0.tgz",
|
||||
"integrity": "sha512-ZGNXbt+d65EGjBORQHuYKj+XhCewlwpnSd/EDuLPZGSiEWmgOJB5RmMCCYGy5aMfTs9wx61RivfDKi8H/hcMvw==",
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz",
|
||||
"integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.50.0"
|
||||
"playwright": "1.50.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -3839,6 +3839,16 @@
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/leaflet-gpx": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet-gpx/-/leaflet-gpx-1.3.7.tgz",
|
||||
"integrity": "sha512-IDshIOLZ7dUUjRiCE3DuJcAGavgUCw0xQ93dc/3YvsA6jrFc+nx8eXr0tqZIf2SaWMgqiDj/n7N24WWNh/898g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/leaflet": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
@ -3897,9 +3907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz",
|
||||
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
|
||||
"version": "22.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
|
||||
"integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
@ -3990,13 +4000,6 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
@ -4151,9 +4154,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.4.tgz",
|
||||
"integrity": "sha512-f0twgRCHgbs24Dp8cLWagzcObXMcuKtAwgxjJV/nnysPAJJk1JiKu/W0gIehZLmkljhJXU/E0/dmuQzsA/4jhA==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.5.tgz",
|
||||
"integrity": "sha512-zOOWIsj5fHh3jjGwQg+P+J1FW3s4jBu1Zqga0qW60yutsBtqEqNEJKWYh7cYn1yGD+1bdPsPdC/eL4eVK56xMg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4174,8 +4177,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "3.0.4",
|
||||
"vitest": "3.0.4"
|
||||
"@vitest/browser": "3.0.5",
|
||||
"vitest": "3.0.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@ -4184,14 +4187,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.4.tgz",
|
||||
"integrity": "sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.5.tgz",
|
||||
"integrity": "sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.4",
|
||||
"@vitest/utils": "3.0.4",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@ -4200,13 +4203,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.4.tgz",
|
||||
"integrity": "sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.5.tgz",
|
||||
"integrity": "sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.4",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
@ -4227,9 +4230,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.4.tgz",
|
||||
"integrity": "sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.5.tgz",
|
||||
"integrity": "sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4240,13 +4243,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.4.tgz",
|
||||
"integrity": "sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.5.tgz",
|
||||
"integrity": "sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.0.4",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
@ -4261,13 +4264,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.4.tgz",
|
||||
"integrity": "sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.5.tgz",
|
||||
"integrity": "sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.4",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.2"
|
||||
},
|
||||
@ -4283,9 +4286,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.4.tgz",
|
||||
"integrity": "sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.5.tgz",
|
||||
"integrity": "sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4296,13 +4299,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.4.tgz",
|
||||
"integrity": "sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.5.tgz",
|
||||
"integrity": "sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.4",
|
||||
"@vitest/pretty-format": "3.0.5",
|
||||
"loupe": "^3.1.2",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@ -11431,6 +11434,12 @@
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/leaflet-gpx": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-gpx/-/leaflet-gpx-2.1.2.tgz",
|
||||
"integrity": "sha512-lKoEPlAWel9KXn9keg6Dmyt7gmj5IYyD8CKuxivN+77GpZr2bpKliwFvZJxLUHmNu4fICmCySyxhm5qjZuvvQg==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/limiter": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||
@ -13302,13 +13311,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.0.tgz",
|
||||
"integrity": "sha512-+GinGfGTrd2IfX1TA4N2gNmeIksSb+IAe589ZH+FlmpV3MYTx6+buChGIuDLQwrGNCw2lWibqV50fU510N7S+w==",
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
|
||||
"integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.50.0"
|
||||
"playwright-core": "1.50.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
@ -13321,9 +13330,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.50.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.0.tgz",
|
||||
"integrity": "sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==",
|
||||
"version": "1.50.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz",
|
||||
"integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@ -14703,9 +14712,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz",
|
||||
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@ -16838,9 +16847,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.4.tgz",
|
||||
"integrity": "sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz",
|
||||
"integrity": "sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -17332,19 +17341,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.4.tgz",
|
||||
"integrity": "sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.5.tgz",
|
||||
"integrity": "sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.0.4",
|
||||
"@vitest/mocker": "3.0.4",
|
||||
"@vitest/pretty-format": "^3.0.4",
|
||||
"@vitest/runner": "3.0.4",
|
||||
"@vitest/snapshot": "3.0.4",
|
||||
"@vitest/spy": "3.0.4",
|
||||
"@vitest/utils": "3.0.4",
|
||||
"@vitest/expect": "3.0.5",
|
||||
"@vitest/mocker": "3.0.5",
|
||||
"@vitest/pretty-format": "^3.0.5",
|
||||
"@vitest/runner": "3.0.5",
|
||||
"@vitest/snapshot": "3.0.5",
|
||||
"@vitest/spy": "3.0.5",
|
||||
"@vitest/utils": "3.0.5",
|
||||
"chai": "^5.1.2",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.1.0",
|
||||
@ -17356,7 +17365,7 @@
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.0.4",
|
||||
"vite-node": "3.0.5",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@ -17372,8 +17381,8 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.0.4",
|
||||
"@vitest/ui": "3.0.4",
|
||||
"@vitest/browser": "3.0.5",
|
||||
"@vitest/ui": "3.0.5",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
12
package.json
12
package.json
@ -114,6 +114,7 @@
|
||||
"katex": "0.16.21",
|
||||
"knockout": "3.5.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.1.2",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "15.0.6",
|
||||
"mermaid": "11.4.1",
|
||||
@ -130,7 +131,6 @@
|
||||
"sanitize-filename": "1.6.3",
|
||||
"sanitize-html": "2.14.0",
|
||||
"sax": "1.4.1",
|
||||
"semver": "7.7.0",
|
||||
"serve-favicon": "2.5.0",
|
||||
"session-file-store": "1.5.0",
|
||||
"source-map-support": "0.5.21",
|
||||
@ -154,7 +154,7 @@
|
||||
"@electron-forge/maker-zip": "7.6.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.6.1",
|
||||
"@electron/rebuild": "3.7.1",
|
||||
"@playwright/test": "1.50.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/better-sqlite3": "7.6.12",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
@ -174,14 +174,14 @@
|
||||
"@types/jasmine": "5.1.5",
|
||||
"@types/jquery": "3.5.32",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/multer": "1.4.12",
|
||||
"@types/node": "22.12.0",
|
||||
"@types/node": "22.13.1",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/safe-compare": "1.1.2",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/sax": "1.2.7",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/serve-favicon": "2.5.7",
|
||||
"@types/session-file-store": "1.2.5",
|
||||
"@types/source-map-support": "0.5.10",
|
||||
@ -191,7 +191,7 @@
|
||||
"@types/ws": "8.5.14",
|
||||
"@types/xml2js": "0.4.14",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.0.4",
|
||||
"@vitest/coverage-v8": "3.0.5",
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "34.0.2",
|
||||
"esm": "3.2.25",
|
||||
@ -206,7 +206,7 @@
|
||||
"tsx": "4.19.2",
|
||||
"typedoc": "0.27.6",
|
||||
"typescript": "5.7.3",
|
||||
"vitest": "3.0.4",
|
||||
"vitest": "3.0.5",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-middleware": "7.4.2"
|
||||
|
@ -83,7 +83,7 @@ export type CommandMappings = {
|
||||
};
|
||||
showExportDialog: CommandData & {
|
||||
notePath: string;
|
||||
defaultType: "single";
|
||||
defaultType: "single" | "subtree";
|
||||
};
|
||||
showDeleteNotesDialog: CommandData & {
|
||||
branchIdsToDelete: string[];
|
||||
|
@ -4,7 +4,6 @@ import noteTooltipService from "./services/note_tooltip.js";
|
||||
import bundleService from "./services/bundle.js";
|
||||
import toastService from "./services/toast.js";
|
||||
import noteAutocompleteService from "./services/note_autocomplete.js";
|
||||
import macInit from "./services/mac_init.js";
|
||||
import electronContextMenu from "./menus/electron_context_menu.js";
|
||||
import glob from "./services/glob.js";
|
||||
import { t } from "./services/i18n.js";
|
||||
@ -35,8 +34,6 @@ if (utils.isElectron()) {
|
||||
initOnElectron();
|
||||
}
|
||||
|
||||
macInit.init();
|
||||
|
||||
noteTooltipService.setupGlobalTooltip();
|
||||
|
||||
noteAutocompleteService.init();
|
||||
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Mac specific initialization
|
||||
*/
|
||||
import utils from "./utils.js";
|
||||
import shortcutService from "./shortcuts.js";
|
||||
|
||||
function init() {
|
||||
if (utils.isElectron() && utils.isMac()) {
|
||||
shortcutService.bindGlobalShortcut("meta+c", () => exec("copy"));
|
||||
shortcutService.bindGlobalShortcut("meta+v", () => exec("paste"));
|
||||
shortcutService.bindGlobalShortcut("meta+x", () => exec("cut"));
|
||||
shortcutService.bindGlobalShortcut("meta+a", () => exec("selectAll"));
|
||||
shortcutService.bindGlobalShortcut("meta+z", () => exec("undo"));
|
||||
shortcutService.bindGlobalShortcut("meta+y", () => exec("redo"));
|
||||
}
|
||||
}
|
||||
|
||||
function exec(cmd: string) {
|
||||
document.execCommand(cmd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export default {
|
||||
init
|
||||
};
|
@ -1,9 +1,6 @@
|
||||
import utils from "./services/utils.js";
|
||||
import macInit from "./services/mac_init.js";
|
||||
import ko from "knockout";
|
||||
|
||||
macInit.init();
|
||||
|
||||
// TriliumNextTODO: properly make use of below types
|
||||
// type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | "";
|
||||
// type SetupModelStep = "sync-in-progress" | "setup-type" | "new-document-in-progress" | "sync-from-desktop";
|
||||
|
1
src/public/app/types.d.ts
vendored
1
src/public/app/types.d.ts
vendored
@ -39,6 +39,7 @@ interface CustomGlobals {
|
||||
maxEntityChangeIdAtLoad: number;
|
||||
maxEntityChangeSyncIdAtLoad: number;
|
||||
assetPath: string;
|
||||
appPath: string;
|
||||
instanceName: string;
|
||||
appCssNoteIds: string[];
|
||||
triliumVersion: string;
|
||||
|
@ -67,6 +67,10 @@ const TPL = `
|
||||
.attr-detail input[readonly] {
|
||||
background-color: var(--accented-background-color) !important;
|
||||
}
|
||||
|
||||
.attr-edit-table td {
|
||||
padding: 4px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
|
||||
@ -97,8 +101,13 @@ const TPL = `
|
||||
</tr>
|
||||
<tr class="attr-row-promoted"
|
||||
title="${t("attribute_detail.promoted_title")}">
|
||||
<th>${t("attribute_detail.promoted")}</th>
|
||||
<td><input type="checkbox" class="attr-input-promoted form-check" /></td>
|
||||
<th></th>
|
||||
<td>
|
||||
<label class="tn-checkbox">
|
||||
<input type="checkbox" class="attr-input-promoted form-check" />
|
||||
${t("attribute_detail.promoted")}
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="attr-row-promoted-alias">
|
||||
<th title="${t("attribute_detail.promoted_alias_title")}">${t("attribute_detail.promoted_alias")}</th>
|
||||
@ -149,8 +158,13 @@ const TPL = `
|
||||
</td>
|
||||
</tr>
|
||||
<tr title="${t("attribute_detail.inheritable_title")}">
|
||||
<th>${t("attribute_detail.inheritable")}</th>
|
||||
<td><input type="checkbox" class="attr-input-inheritable form-check" /></td>
|
||||
<th></th>
|
||||
<td>
|
||||
<label class="tn-checkbox">
|
||||
<input type="checkbox" class="attr-input-inheritable form-check" />
|
||||
${t("attribute_detail.inheritable")}
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -242,7 +256,9 @@ const ATTR_HELP: Record<string, Record<string, string>> = {
|
||||
executeDescription: t("attribute_detail.execute_description"),
|
||||
excludeFromNoteMap: t("attribute_detail.exclude_from_note_map"),
|
||||
newNotesOnTop: t("attribute_detail.new_notes_on_top"),
|
||||
hideHighlightWidget: t("attribute_detail.hide_highlight_widget")
|
||||
hideHighlightWidget: t("attribute_detail.hide_highlight_widget"),
|
||||
printLandscape: t("attribute_detail.print_landscape"),
|
||||
printPageSize: t("attribute_detail.print_page_size")
|
||||
},
|
||||
relation: {
|
||||
runOnNoteCreation: t("attribute_detail.run_on_note_creation"),
|
||||
|
@ -77,8 +77,8 @@ const TPL = `
|
||||
|
||||
<div class="attribute-list-editor" tabindex="200"></div>
|
||||
|
||||
<div class="bx bx-save save-attributes-button" title="${escapeQuotes(t("attribute_editor.save_attributes"))}"></div>
|
||||
<div class="bx bx-plus add-new-attribute-button" title="${escapeQuotes(t("attribute_editor.add_a_new_attribute"))}"></div>
|
||||
<div class="bx bx-save save-attributes-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.save_attributes"))}"></div>
|
||||
<div class="bx bx-plus add-new-attribute-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.add_a_new_attribute"))}"></div>
|
||||
|
||||
<div class="attribute-errors" style="display: none;"></div>
|
||||
</div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
import treeService from "../../services/tree.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import ws from "../../services/ws.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
import toastService, { type ToastOptions } from "../../services/toast.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import openService from "../../services/open.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="export-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||
@ -105,6 +106,13 @@ const TPL = `
|
||||
${t("export.format_markdown")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="form-check-label tn-radio">
|
||||
<input class="form-check-input" type="radio" name="export-single-format" value="pdf">
|
||||
${t("export.format_pdf")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@ -116,6 +124,19 @@ const TPL = `
|
||||
</div>`;
|
||||
|
||||
export default class ExportDialog extends BasicWidget {
|
||||
|
||||
private taskId: string;
|
||||
private branchId: string | null;
|
||||
private modal?: bootstrap.Modal;
|
||||
private $form!: JQuery<HTMLElement>;
|
||||
private $noteTitle!: JQuery<HTMLElement>;
|
||||
private $subtreeFormats!: JQuery<HTMLElement>;
|
||||
private $singleFormats!: JQuery<HTMLElement>;
|
||||
private $subtreeType!: JQuery<HTMLElement>;
|
||||
private $singleType!: JQuery<HTMLElement>;
|
||||
private $exportButton!: JQuery<HTMLElement>;
|
||||
private $opmlVersions!: JQuery<HTMLElement>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@ -125,6 +146,8 @@ export default class ExportDialog extends BasicWidget {
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
// Remove once bootstrap is fixed.
|
||||
// @ts-ignore
|
||||
this.modal = bootstrap.Modal.getOrCreateInstance(this.$widget);
|
||||
this.$form = this.$widget.find(".export-form");
|
||||
this.$noteTitle = this.$widget.find(".export-note-title");
|
||||
@ -136,7 +159,7 @@ export default class ExportDialog extends BasicWidget {
|
||||
this.$opmlVersions = this.$widget.find(".opml-versions");
|
||||
|
||||
this.$form.on("submit", () => {
|
||||
this.modal.hide();
|
||||
this.modal?.hide();
|
||||
|
||||
const exportType = this.$widget.find("input[name='export-type']:checked").val();
|
||||
|
||||
@ -149,13 +172,15 @@ export default class ExportDialog extends BasicWidget {
|
||||
|
||||
const exportVersion = exportFormat === "opml" ? this.$widget.find("input[name='opml-version']:checked").val() : "1.0";
|
||||
|
||||
this.exportBranch(this.branchId, exportType, exportFormat, exportVersion);
|
||||
if (this.branchId) {
|
||||
this.exportBranch(this.branchId, String(exportType), String(exportFormat), String(exportVersion));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$widget.find("input[name=export-type]").on("change", (e) => {
|
||||
if (e.currentTarget.value === "subtree") {
|
||||
if ((e.currentTarget as HTMLInputElement).value === "subtree") {
|
||||
if (this.$widget.find("input[name=export-subtree-format]:checked").length === 0) {
|
||||
this.$widget.find("input[name=export-subtree-format]:first").prop("checked", true);
|
||||
}
|
||||
@ -173,7 +198,7 @@ export default class ExportDialog extends BasicWidget {
|
||||
});
|
||||
|
||||
this.$widget.find("input[name=export-subtree-format]").on("change", (e) => {
|
||||
if (e.currentTarget.value === "opml") {
|
||||
if ((e.currentTarget as HTMLInputElement).value === "opml") {
|
||||
this.$opmlVersions.slideDown();
|
||||
} else {
|
||||
this.$opmlVersions.slideUp();
|
||||
@ -181,7 +206,7 @@ export default class ExportDialog extends BasicWidget {
|
||||
});
|
||||
}
|
||||
|
||||
async showExportDialogEvent({ notePath, defaultType }) {
|
||||
async showExportDialogEvent({ notePath, defaultType }: EventData<"showExportDialog">) {
|
||||
this.taskId = "";
|
||||
this.$exportButton.removeAttr("disabled");
|
||||
|
||||
@ -201,11 +226,15 @@ export default class ExportDialog extends BasicWidget {
|
||||
|
||||
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
|
||||
this.branchId = await froca.getBranchId(parentNoteId, noteId);
|
||||
this.$noteTitle.text(await treeService.getNoteTitle(noteId));
|
||||
if (parentNoteId) {
|
||||
this.branchId = await froca.getBranchId(parentNoteId, noteId);
|
||||
}
|
||||
if (noteId) {
|
||||
this.$noteTitle.text(await treeService.getNoteTitle(noteId));
|
||||
}
|
||||
}
|
||||
|
||||
exportBranch(branchId, type, format, version) {
|
||||
exportBranch(branchId: string, type: string, format: string, version: string) {
|
||||
this.taskId = utils.randomString(10);
|
||||
|
||||
const url = openService.getUrlForDownload(`api/branches/${branchId}/export/${type}/${format}/${version}/${this.taskId}`);
|
||||
@ -215,12 +244,14 @@ export default class ExportDialog extends BasicWidget {
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(async (message) => {
|
||||
const makeToast = (id, message) => ({
|
||||
id: id,
|
||||
title: t("export.export_status"),
|
||||
message: message,
|
||||
icon: "arrow-square-up-right"
|
||||
});
|
||||
function makeToast(id: string, message: string): ToastOptions {
|
||||
return {
|
||||
id: id,
|
||||
title: t("export.export_status"),
|
||||
message: message,
|
||||
icon: "arrow-square-up-right"
|
||||
};
|
||||
};
|
||||
|
||||
if (message.taskType !== "export") {
|
||||
return;
|
@ -25,16 +25,22 @@ const TPL = `
|
||||
${t("include_note.box_size_prompt")}
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="small">
|
||||
<label class="form-check-label">${t("include_note.box_size_small")}</label>
|
||||
<label class="form-check-label tn-radio">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="small">
|
||||
${t("include_note.box_size_small")}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="medium" checked>
|
||||
<label class="form-check-label">${t("include_note.box_size_medium")}</label>
|
||||
<label class="form-check-label tn-radio">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="medium" checked>
|
||||
${t("include_note.box_size_medium")}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="full">
|
||||
<label class="form-check-label">${t("include_note.box_size_full")}</label>
|
||||
<label class="form-check-label tn-radio">
|
||||
<input class="form-check-input" type="radio" name="include-note-box-size" value="full">
|
||||
${t("include_note.box_size_full")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -33,11 +33,15 @@ const TPL = `
|
||||
}
|
||||
|
||||
.find-widget-found-wrapper {
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
min-width: 60px;
|
||||
padding: 0 4px;
|
||||
font-size: .85em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.find-widget-search-term-input-group, .replace-widget-replacetext-input {
|
||||
max-width: 300px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.find-widget-spacer {
|
||||
@ -49,6 +53,13 @@ const TPL = `
|
||||
<div class="input-group find-widget-search-term-input-group">
|
||||
<input type="text" class="form-control find-widget-search-term-input" placeholder="${t("find.find_placeholder")}">
|
||||
<button class="btn btn-outline-secondary bx bxs-chevron-up find-widget-previous-button" type="button"></button>
|
||||
<div class="find-widget-found-wrapper input-group-text">
|
||||
<span>
|
||||
<span class="find-widget-current-found">0</span>
|
||||
/
|
||||
<span class="find-widget-total-found">0</span>
|
||||
<span>
|
||||
</div>
|
||||
<button class="btn btn-outline-secondary bx bxs-chevron-down find-widget-next-button" type="button"></button>
|
||||
</div>
|
||||
|
||||
@ -66,11 +77,7 @@ const TPL = `
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="find-widget-found-wrapper">
|
||||
<span class="find-widget-current-found">0</span>
|
||||
/
|
||||
<span class="find-widget-total-found">0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="find-widget-spacer"></div>
|
||||
|
||||
|
@ -261,6 +261,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
||||
const { ipcRenderer } = utils.dynamicRequire("electron");
|
||||
ipcRenderer.send("export-as-pdf", {
|
||||
title: this.note.title,
|
||||
pageSize: this.note.getAttributeValue("label", "printPageSize") ?? "Letter",
|
||||
landscape: this.note.hasAttribute("label", "printLandscape")
|
||||
});
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ const TPL = `
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="links-wrapper"></div>
|
||||
<div class="links-wrapper use-tn-links"></div>
|
||||
|
||||
<div class="attachment-wrapper"></div>
|
||||
</div>`;
|
||||
@ -57,7 +57,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
|
||||
|
||||
this.$linksWrapper.empty().append(
|
||||
t("attachment_detail.owning_note"),
|
||||
await linkService.createLink(this.noteId),
|
||||
(await linkService.createLink(this.noteId)),
|
||||
t("attachment_detail.you_can_also_open"),
|
||||
await linkService.createLink(this.noteId, {
|
||||
title: t("attachment_detail.list_of_all_attachments"),
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import TypeWidget from "./type_widget.js";
|
||||
|
||||
const TPL = `<div class="note-detail-doc note-detail-printable">
|
||||
@ -18,6 +19,9 @@ const TPL = `<div class="note-detail-doc note-detail-printable">
|
||||
</div>`;
|
||||
|
||||
export default class DocTypeWidget extends TypeWidget {
|
||||
|
||||
private $content!: JQuery<HTMLElement>;
|
||||
|
||||
static getType() {
|
||||
return "doc";
|
||||
}
|
||||
@ -29,7 +33,7 @@ export default class DocTypeWidget extends TypeWidget {
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
async doRefresh(note) {
|
||||
async doRefresh(note: FNote) {
|
||||
const docName = note.getLabelValue("docName");
|
||||
|
||||
if (docName) {
|
@ -1,4 +1,4 @@
|
||||
import { Marker, type LatLng, type LeafletMouseEvent } from "leaflet";
|
||||
import { GPX, Marker, type LatLng, type LeafletMouseEvent } from "leaflet";
|
||||
import type FNote from "../../entities/fnote.js";
|
||||
import GeoMapWidget, { type InitCallback, type Leaflet } from "../geo_map.js";
|
||||
import TypeWidget from "./type_widget.js"
|
||||
@ -91,8 +91,6 @@ interface CreateChildResponse {
|
||||
}
|
||||
}
|
||||
|
||||
type MarkerData = Record<string, Marker>;
|
||||
|
||||
enum State {
|
||||
Normal,
|
||||
NewNote
|
||||
@ -103,7 +101,9 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
private geoMapWidget: GeoMapWidget;
|
||||
private _state: State;
|
||||
private L!: Leaflet;
|
||||
private currentMarkerData: MarkerData;
|
||||
private currentMarkerData: Record<string, Marker>;
|
||||
private currentTrackData: Record<string, GPX>;
|
||||
private gpxLoaded?: boolean;
|
||||
|
||||
static getType() {
|
||||
return "geoMap";
|
||||
@ -114,6 +114,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
|
||||
this.geoMapWidget = new GeoMapWidget("type", (L: Leaflet) => this.#onMapInitialized(L));
|
||||
this.currentMarkerData = {};
|
||||
this.currentTrackData = {};
|
||||
this._state = State.Normal;
|
||||
|
||||
this.child(this.geoMapWidget);
|
||||
@ -159,9 +160,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
async #reloadMarkers() {
|
||||
const map = this.geoMapWidget.map;
|
||||
|
||||
if (!this.note || !map) {
|
||||
if (!this.note) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -170,51 +169,91 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
marker.remove();
|
||||
}
|
||||
|
||||
// Delete all existing tracks
|
||||
for (const track of Object.values(this.currentTrackData)) {
|
||||
track.remove();
|
||||
}
|
||||
|
||||
// Add the new markers.
|
||||
this.currentMarkerData = {};
|
||||
const childNotes = await this.note.getChildNotes();
|
||||
const L = this.L;
|
||||
for (const childNote of childNotes) {
|
||||
const latLng = childNote.getAttributeValue("label", LOCATION_ATTRIBUTE);
|
||||
if (!latLng) {
|
||||
if (childNote.mime === "application/gpx+xml") {
|
||||
this.#processNoteWithGpxTrack(childNote);
|
||||
continue;
|
||||
}
|
||||
|
||||
const [ lat, lng ] = latLng.split(",", 2).map((el) => parseFloat(el));
|
||||
const icon = 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 ${childNote.getIcon()}"></span>
|
||||
<span class="title-label">${childNote.title}</span>`,
|
||||
iconSize: [ 25, 41 ],
|
||||
iconAnchor: [ 12, 41 ]
|
||||
})
|
||||
const latLng = childNote.getAttributeValue("label", LOCATION_ATTRIBUTE);
|
||||
if (latLng) {
|
||||
this.#processNoteWithMarker(childNote, latLng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const marker = L.marker(L.latLng(lat, lng), {
|
||||
icon,
|
||||
draggable: true,
|
||||
autoPan: true,
|
||||
autoPanSpeed: 5,
|
||||
})
|
||||
.addTo(map)
|
||||
.on("moveend", e => {
|
||||
this.moveMarker(childNote.noteId, (e.target as Marker).getLatLng());
|
||||
});
|
||||
async #processNoteWithGpxTrack(note: FNote) {
|
||||
if (!this.L || !this.geoMapWidget.map) {
|
||||
return;
|
||||
}
|
||||
|
||||
marker.on("contextmenu", (e) => {
|
||||
openContextMenu(childNote.noteId, e.originalEvent);
|
||||
if (!this.gpxLoaded) {
|
||||
await import("leaflet-gpx");
|
||||
this.gpxLoaded = true;
|
||||
}
|
||||
|
||||
// TODO: This is not very efficient as it's probably a string response that is parsed and then converted back to string and parsed again.
|
||||
const xmlResponse = await server.get<XMLDocument>(`notes/${note.noteId}/open`);
|
||||
const stringResponse = new XMLSerializer().serializeToString(xmlResponse);
|
||||
|
||||
const track = new this.L.GPX(stringResponse, {});
|
||||
track.addTo(this.geoMapWidget.map);
|
||||
this.currentTrackData[note.noteId] = track;
|
||||
}
|
||||
|
||||
#processNoteWithMarker(note: FNote, latLng: string) {
|
||||
const map = this.geoMapWidget.map;
|
||||
if (!map) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [ lat, lng ] = latLng.split(",", 2).map((el) => parseFloat(el));
|
||||
const L = this.L;
|
||||
const icon = this.#buildIcon(note.getIcon(), note.title);
|
||||
|
||||
const marker = L.marker(L.latLng(lat, lng), {
|
||||
icon,
|
||||
draggable: true,
|
||||
autoPan: true,
|
||||
autoPanSpeed: 5,
|
||||
})
|
||||
.addTo(map)
|
||||
.on("moveend", e => {
|
||||
this.moveMarker(note.noteId, (e.target as Marker).getLatLng());
|
||||
});
|
||||
|
||||
const el = marker.getElement();
|
||||
if (el) {
|
||||
const $el = $(el);
|
||||
$el.attr("data-href", `#${childNote.noteId}`);
|
||||
note_tooltip.setupElementTooltip($($el));
|
||||
}
|
||||
marker.on("contextmenu", (e) => {
|
||||
openContextMenu(note.noteId, e.originalEvent);
|
||||
});
|
||||
|
||||
this.currentMarkerData[childNote.noteId] = marker;
|
||||
const el = marker.getElement();
|
||||
if (el) {
|
||||
const $el = $(el);
|
||||
$el.attr("data-href", `#${note.noteId}`);
|
||||
note_tooltip.setupElementTooltip($($el));
|
||||
}
|
||||
|
||||
this.currentMarkerData[note.noteId] = marker;
|
||||
}
|
||||
|
||||
#buildIcon(bxIconClass: 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="title-label">${title}</span>`,
|
||||
iconSize: [ 25, 41 ],
|
||||
iconAnchor: [ 12, 41 ]
|
||||
})
|
||||
}
|
||||
|
||||
#changeState(newState: State) {
|
||||
@ -299,6 +338,14 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
// If any of the children branches are altered.
|
||||
if (loadResults.getBranchRows().find((branch) => branch.parentNoteId === this.noteId)) {
|
||||
this.#reloadMarkers();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
this.#reloadMarkers();
|
||||
|
@ -9,6 +9,12 @@ const TPL = `
|
||||
width: 300px;
|
||||
margin: 30px auto auto;
|
||||
}
|
||||
|
||||
.protected-session-password-component input,
|
||||
.protected-session-password-component button {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<form class="protected-session-password-form">
|
||||
|
@ -4,6 +4,21 @@
|
||||
--launcher-pane-background-color: var(--main-background-color);
|
||||
--main-text-color: black;
|
||||
--input-text-color: var(--main-text-color);
|
||||
|
||||
--print-font-size: 11pt;
|
||||
}
|
||||
|
||||
@page {
|
||||
margin: 2cm;
|
||||
}
|
||||
|
||||
.ck-content {
|
||||
font-size: var(--print-font-size);
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.note-detail-readonly-text {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.no-print,
|
||||
@ -141,13 +156,6 @@ span[style] {
|
||||
outline: unset !important;
|
||||
}
|
||||
|
||||
/* Fix visibility of checkbox checkmarks
|
||||
see https://github.com/TriliumNext/Notes/issues/901 */
|
||||
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable="false"] > input[checked]::after {
|
||||
/* fallback to default ck-editor green */
|
||||
border-color: hsl(126, 64%, 41%);
|
||||
}
|
||||
|
||||
.include-note .include-note-content {
|
||||
max-height: unset !important;
|
||||
overflow: unset !important;
|
||||
@ -160,3 +168,138 @@ span[style] {
|
||||
border: unset !important;
|
||||
border-radius: unset !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Links
|
||||
*/
|
||||
|
||||
.note-detail-printable a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.note-detail-printable a:not([href^="#root/"]) {
|
||||
text-decoration: underline;
|
||||
color: #374a75;
|
||||
}
|
||||
|
||||
.note-detail-printable a::after {
|
||||
/* Hide the external link trailing arrow */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO list check boxes
|
||||
*/
|
||||
|
||||
.note-detail-printable .todo-list__label * {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@supports selector(.todo-list__label__description:has(*)) and (height: 1lh) {
|
||||
.note-detail-printable .todo-list__label__description {
|
||||
|
||||
/* The percentage of the line height that the check box occupies */
|
||||
--box-ratio: .75;
|
||||
/* The size of the gap between the check box and the caption */
|
||||
--box-text-gap: 0.25em;
|
||||
|
||||
--box-size: calc(1lh * var(--box-ratio));
|
||||
--box-vert-offset: calc((1lh - var(--box-size)) / 2);
|
||||
|
||||
display: inline-block;
|
||||
padding-left: calc(var(--box-size) + var(--box-text-gap));
|
||||
/* Source: https://pictogrammers.com/library/mdi/icon/checkbox-blank-outline/ */
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3e%3cpath d='M19%2c3H5C3.89%2c3 3%2c3.89 3%2c5V19A2%2c2 0 0%2c0 5%2c21H19A2%2c2 0 0%2c0 21%2c19V5C21%2c3.89 20.1%2c3 19%2c3M19%2c5V19H5V5H19Z' /%3e%3c/svg%3e");
|
||||
background-position: 0 var(--box-vert-offset);
|
||||
background-size: var(--box-size);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.note-detail-printable .todo-list__label:has(input[type="checkbox"]:checked) .todo-list__label__description {
|
||||
/* Source: https://pictogrammers.com/library/mdi/icon/checkbox-outline/ */
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3e%3cpath d='M19%2c3H5A2%2c2 0 0%2c0 3%2c5V19A2%2c2 0 0%2c0 5%2c21H19A2%2c2 0 0%2c0 21%2c19V5A2%2c2 0 0%2c0 19%2c3M19%2c5V19H5V5H19M10%2c17L6%2c13L7.41%2c11.58L10%2c14.17L16.59%2c7.58L18%2c9' /%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.note-detail-printable .todo-list__label input[type="checkbox"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Blockquotes
|
||||
*/
|
||||
|
||||
.note-detail-printable blockquote {
|
||||
box-shadow: unset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Figures
|
||||
*/
|
||||
|
||||
.note-detail-printable figcaption {
|
||||
--accented-background-color: transparent;
|
||||
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/*
|
||||
* Footnotes
|
||||
*/
|
||||
|
||||
.note-detail-printable .footnote-reference a,
|
||||
.footnote-back-link a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Make the "^" link cover the whole area of the footnote item */
|
||||
|
||||
.footnote-section {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.note-detail-printable li.footnote-item {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.note-detail-printable .footnote-back-link,
|
||||
.note-detail-printable .footnote-back-link *,
|
||||
.note-detail-printable .footnote-back-link a {
|
||||
display: block;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.note-detail-printable .footnote-back-link a {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.note-detail-printable .footnote-content {
|
||||
display: inline-block;
|
||||
width: unset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Widows and orphans
|
||||
*/
|
||||
p,
|
||||
blockquote {
|
||||
widows: 4;
|
||||
orphans: 4;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
widows: 6;
|
||||
orphans: 6;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-after: avoid;
|
||||
break-after: avoid;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
@import url(./forms.css);
|
||||
@import url(./shell.css);
|
||||
@import url(./settings.css);
|
||||
@import url(./notes/empty.css);
|
||||
@import url(./notes/text.css);
|
||||
|
||||
@font-face {
|
||||
@ -64,3 +65,29 @@
|
||||
/* Theme capabilities */
|
||||
--tab-note-icons: true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note search suggestions
|
||||
*/
|
||||
|
||||
/* List body */
|
||||
.jump-to-note-dialog .jump-to-note-results .aa-suggestions,
|
||||
.note-detail-empty .aa-suggestions {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* List item */
|
||||
.jump-to-note-dialog .aa-suggestions div,
|
||||
.note-detail-empty .aa-suggestions div {
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
color: var(--menu-text-color);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Selected list item */
|
||||
.jump-to-note-dialog .aa-suggestions div.aa-cursor,
|
||||
.note-detail-empty .aa-suggestions div.aa-cursor {
|
||||
background: var(--hover-item-background-color);
|
||||
color: var(--hover-item-text-color);
|
||||
}
|
@ -78,10 +78,11 @@ button.btn.btn-success kbd {
|
||||
* Icon buttons
|
||||
*/
|
||||
|
||||
:root .icon-action:not(.global-menu-button) {
|
||||
:root .icon-action:not(.global-menu-button),
|
||||
:root .tn-tool-button {
|
||||
width: var(--icon-button-size);
|
||||
height: var(--icon-button-size);
|
||||
border: unset;
|
||||
border: unset !important;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
@ -89,7 +90,8 @@ button.btn.btn-success kbd {
|
||||
}
|
||||
|
||||
/* The "x" icon button */
|
||||
:root .icon-action.bx-x {
|
||||
:root .icon-action.bx-x,
|
||||
:root .tn-tool-button.bx-x {
|
||||
--icon-button-hover-background: var(--tab-close-button-hover-background);
|
||||
--icon-button-hover-color: var(--tab-close-button-hover-color);
|
||||
--icon-button-size: 24px;
|
||||
@ -99,23 +101,28 @@ button.btn.btn-success kbd {
|
||||
}
|
||||
|
||||
/* The icon */
|
||||
:root .icon-action:not(.global-menu-button)::before {
|
||||
:root .icon-action:not(.global-menu-button)::before,
|
||||
:root .tn-tool-button::before {
|
||||
display: block;
|
||||
line-height: var(--icon-button-size);
|
||||
font-size: calc(var(--icon-button-size) * var(--icon-button-icon-ratio));
|
||||
}
|
||||
|
||||
:root .icon-action:not(.global-menu-button):hover,
|
||||
:root .icon-action:not(.global-menu-button).show {
|
||||
:root .icon-action:not(.global-menu-button).show,
|
||||
:root .tn-tool-button:hover,
|
||||
:root .tn-tool-button.show {
|
||||
background: var(--icon-button-hover-background);
|
||||
color: var(--icon-button-hover-color);
|
||||
}
|
||||
|
||||
:root .icon-action:not(.global-menu-button):active::before {
|
||||
:root .icon-action:not(.global-menu-button):active::before,
|
||||
:root .tn-tool-button:active::before {
|
||||
transform: scale(.85);
|
||||
}
|
||||
|
||||
:root .icon-action:not(.global-menu-button):focus-visible {
|
||||
:root .icon-action:not(.global-menu-button):focus-visible,
|
||||
:root .tn-tool-button:focus-visible {
|
||||
outline: 2px solid var(--input-focus-outline-color);
|
||||
}
|
||||
|
||||
@ -216,6 +223,7 @@ input::selection,
|
||||
.input-group button,
|
||||
.input-group a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
--bs-border-width: 0;
|
||||
--accented-background-color: transparent;
|
||||
background: transparent;
|
||||
@ -228,15 +236,21 @@ input::selection,
|
||||
.input-group button:hover,
|
||||
.input-group a:hover {
|
||||
--muted-text-color: var(--input-action-button-hover);
|
||||
color: var(--input-action-button-hover);
|
||||
}
|
||||
|
||||
.input-group button:focus-visible,
|
||||
.input-group a:focus-visible {
|
||||
box-shadow: unset;
|
||||
outline: transparent;
|
||||
border: transparent;
|
||||
text-shadow: 0 0 3px var(--input-action-button-hover);
|
||||
}
|
||||
|
||||
.input-group button:active {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.input-group a.disabled {
|
||||
opacity: .5;
|
||||
/* Workaround to set the "background" property. */
|
||||
@ -247,7 +261,7 @@ input::selection,
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.input-group .input-group-text {
|
||||
.input-group .input-group-text:not(button):not(a) {
|
||||
/* Background color hijack */
|
||||
--accented-background-color: transparent;
|
||||
|
||||
@ -273,6 +287,7 @@ input::selection,
|
||||
|
||||
select,
|
||||
select.form-select,
|
||||
select.form-control,
|
||||
.select-button.dropdown-toggle.btn {
|
||||
outline: 3px solid transparent;
|
||||
outline-offset: 6px;
|
||||
@ -285,6 +300,7 @@ select.form-select,
|
||||
|
||||
select:hover,
|
||||
select.form-select:hover,
|
||||
select.form-control:hover,
|
||||
.select-button.dropdown-toggle.btn:hover {
|
||||
background-color: var(--input-hover-background);
|
||||
color: var(--input-hover-color);
|
||||
@ -297,6 +313,7 @@ button.select-button.dropdown-toggle.btn:active {
|
||||
|
||||
select:focus,
|
||||
select.form-select:focus,
|
||||
select.form-control:focus,
|
||||
.select-button.dropdown-toggle.btn:focus {
|
||||
box-shadow: unset;
|
||||
outline: 3px solid var(--input-focus-outline-color);
|
||||
|
11
src/public/stylesheets/theme-next/notes/empty.css
Normal file
11
src/public/stylesheets/theme-next/notes/empty.css
Normal file
@ -0,0 +1,11 @@
|
||||
/* The container */
|
||||
div.note-detail-empty {
|
||||
max-width: 70%;
|
||||
margin: 50px auto;
|
||||
}
|
||||
|
||||
/* The search results list */
|
||||
.note-detail-empty span.aa-dropdown-menu {
|
||||
margin-top: 1em;
|
||||
border: unset;
|
||||
}
|
@ -126,3 +126,34 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
||||
border: 0 !important;
|
||||
border-top: 1px solid var(--main-border-color) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search in text panel
|
||||
*/
|
||||
|
||||
.find-replace-widget > div {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.find-replace-widget > div + div {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
div.find-replace-widget div.find-widget-found-wrapper {
|
||||
min-width: 50px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* The up / down buttons of the "Find in text" input */
|
||||
.find-replace-widget .input-group button {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.find-replace-widget .form-check {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.find-replace-widget .form-check .form-check-input {
|
||||
margin-left: 0;
|
||||
}
|
@ -227,6 +227,12 @@ body.layout-horizontal > .horizontal {
|
||||
--hover-item-background-color: transparent;
|
||||
}
|
||||
|
||||
#launcher-pane.horizontal .global-menu-button .global-menu-button-update-available {
|
||||
right: -23px;
|
||||
bottom: -22px;
|
||||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
.tooltip .tooltip-arrow {
|
||||
display: none;
|
||||
}
|
||||
@ -1237,25 +1243,6 @@ body .calendar-dropdown-widget .calendar-body a:hover {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* List body */
|
||||
.jump-to-note-dialog .jump-to-note-results .aa-suggestions {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* List item */
|
||||
.jump-to-note-dialog .aa-suggestions div {
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
color: var(--menu-text-color);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Selected list item */
|
||||
.jump-to-note-dialog .aa-suggestions div.aa-cursor {
|
||||
background: var(--hover-item-background-color);
|
||||
color: var(--hover-item-text-color);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recent changes list
|
||||
*/
|
||||
@ -1656,3 +1643,8 @@ body .calendar-dropdown-widget .calendar-body a:hover {
|
||||
border-radius: 8px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
/* Promoted attributes */
|
||||
.promoted-attribute-cell div.input-group {
|
||||
margin: 1px 0;
|
||||
}
|
@ -109,7 +109,8 @@
|
||||
"choose_export_type": "Choose export type first please",
|
||||
"export_status": "Export status",
|
||||
"export_in_progress": "Export in progress: {{progressCount}}",
|
||||
"export_finished_successfully": "Export finished successfully."
|
||||
"export_finished_successfully": "Export finished successfully.",
|
||||
"format_pdf": "PDF - for printing or sharing purposes."
|
||||
},
|
||||
"help": {
|
||||
"fullDocumentation": "Help (full documentation is available <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
|
||||
@ -437,7 +438,9 @@
|
||||
"share_favicon": "Favicon note to be set in the shared page. Typically you want to set it to share root and make it inheritable. Favicon note must be in the shared sub-tree as well. Consider using 'share_hidden_from_tree'.",
|
||||
"is_owned_by_note": "is owned by note",
|
||||
"other_notes_with_name": "Other notes with {{attributeType}} name \"{{attributeName}}\"",
|
||||
"and_more": "... and {{count}} more."
|
||||
"and_more": "... and {{count}} more.",
|
||||
"print_landscape": "When exporting to PDF, changes the orientation of the page to landscape instead of portrait.",
|
||||
"print_page_size": "When exporting to PDF, changes the size of the page. Supported values: <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": "To add label, just type e.g. <code>#rock</code> or if you want to add also value then e.g. <code>#year = 2020</code>",
|
||||
|
@ -31,6 +31,7 @@ async function register(app: express.Application) {
|
||||
target: productionConfig.target
|
||||
});
|
||||
|
||||
app.use(`/${assetPath}/app/doc_notes`, persistentCacheStatic(path.join(srcRoot, "public/app/doc_notes")));
|
||||
app.use(`/${assetPath}/app`, webpackMiddleware(frontendCompiler));
|
||||
} else {
|
||||
app.use(`/${assetPath}/app`, persistentCacheStatic(path.join(srcRoot, "public/app")));
|
||||
|
@ -72,6 +72,9 @@ export default [
|
||||
{ type: "label", name: "webViewSrc", isDangerous: true },
|
||||
{ type: "label", name: "hideHighlightWidget" },
|
||||
|
||||
{ type: "label", name: "printLandscape" },
|
||||
{ type: "label", name: "printPageSize" },
|
||||
|
||||
// relation names
|
||||
{ type: "relation", name: "internalLink" },
|
||||
{ type: "relation", name: "imageLink" },
|
||||
|
0
src/services/export/pdf.ts
Normal file
0
src/services/export/pdf.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { processMindmapContent } from "./note_content_fulltext.js";
|
||||
|
||||
describe("processMindmapContent", () => {
|
||||
it("supports empty JSON", () => {
|
||||
expect(processMindmapContent("{}")).toEqual("");
|
||||
});
|
||||
|
||||
it("supports blank text / invalid JSON", () => {
|
||||
expect(processMindmapContent("")).toEqual("");
|
||||
expect(processMindmapContent(`{ "node": " }`)).toEqual("");
|
||||
});
|
||||
});
|
@ -131,52 +131,7 @@ class NoteContentFulltextExp extends Expression {
|
||||
|
||||
content = content.replace(/ /g, " ");
|
||||
} else if (type === "mindMap" && mime === "application/json") {
|
||||
let mindMapcontent = JSON.parse(content);
|
||||
|
||||
// Define interfaces for the JSON structure
|
||||
interface MindmapNode {
|
||||
id: string;
|
||||
topic: string;
|
||||
children: MindmapNode[]; // Recursive structure
|
||||
direction?: number;
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
interface MindmapData {
|
||||
nodedata: MindmapNode;
|
||||
arrows: any[]; // If you know the structure, replace `any` with the correct type
|
||||
summaries: any[];
|
||||
direction: number;
|
||||
theme: {
|
||||
name: string;
|
||||
type: string;
|
||||
palette: string[];
|
||||
cssvar: Record<string, string>; // Object with string keys and string values
|
||||
};
|
||||
}
|
||||
|
||||
// Recursive function to collect all topics
|
||||
function collectTopics(node: MindmapNode): string[] {
|
||||
// Collect the current node's topic
|
||||
let topics = [node.topic];
|
||||
|
||||
// If the node has children, collect topics recursively
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
topics = topics.concat(collectTopics(child));
|
||||
}
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
// Start extracting from the root node
|
||||
const topicsArray = collectTopics(mindMapcontent.nodedata);
|
||||
|
||||
// Combine topics into a single string
|
||||
const topicsString = topicsArray.join(", ");
|
||||
|
||||
content = normalize(topicsString.toString());
|
||||
content = processMindmapContent(content);
|
||||
} else if (type === "canvas" && mime === "application/json") {
|
||||
interface Element {
|
||||
type: string;
|
||||
@ -215,4 +170,63 @@ class NoteContentFulltextExp extends Expression {
|
||||
}
|
||||
}
|
||||
|
||||
export function processMindmapContent(content: string) {
|
||||
let mindMapcontent;
|
||||
|
||||
try {
|
||||
mindMapcontent = JSON.parse(content);
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Define interfaces for the JSON structure
|
||||
interface MindmapNode {
|
||||
id: string;
|
||||
topic: string;
|
||||
children: MindmapNode[]; // Recursive structure
|
||||
direction?: number;
|
||||
expanded?: boolean;
|
||||
}
|
||||
|
||||
interface MindmapData {
|
||||
nodedata: MindmapNode;
|
||||
arrows: any[]; // If you know the structure, replace `any` with the correct type
|
||||
summaries: any[];
|
||||
direction: number;
|
||||
theme: {
|
||||
name: string;
|
||||
type: string;
|
||||
palette: string[];
|
||||
cssvar: Record<string, string>; // Object with string keys and string values
|
||||
};
|
||||
}
|
||||
|
||||
// Recursive function to collect all topics
|
||||
function collectTopics(node?: MindmapNode): string[] {
|
||||
if (!node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Collect the current node's topic
|
||||
let topics = [node.topic];
|
||||
|
||||
// If the node has children, collect topics recursively
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
topics = topics.concat(collectTopics(child));
|
||||
}
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
// Start extracting from the root node
|
||||
const topicsArray = collectTopics(mindMapcontent.nodedata);
|
||||
|
||||
// Combine topics into a single string
|
||||
const topicsString = topicsArray.join(", ");
|
||||
|
||||
return normalize(topicsString.toString());
|
||||
}
|
||||
|
||||
export default NoteContentFulltextExp;
|
||||
|
@ -282,7 +282,50 @@ export function getResourceDir() {
|
||||
return join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
||||
}
|
||||
|
||||
// TODO: Deduplicate with src/public/app/services/utils.ts
|
||||
/**
|
||||
* Compares two semantic version strings.
|
||||
* Returns:
|
||||
* 1 if v1 is greater than v2
|
||||
* 0 if v1 is equal to v2
|
||||
* -1 if v1 is less than v2
|
||||
*
|
||||
* @param v1 First version string
|
||||
* @param v2 Second version string
|
||||
* @returns
|
||||
*/
|
||||
function compareVersions(v1: string, v2: string): number {
|
||||
// Remove 'v' prefix and everything after dash if present
|
||||
v1 = v1.replace(/^v/, "").split("-")[0];
|
||||
v2 = v2.replace(/^v/, "").split("-")[0];
|
||||
|
||||
const v1parts = v1.split(".").map(Number);
|
||||
const v2parts = v2.split(".").map(Number);
|
||||
|
||||
// Pad shorter version with zeros
|
||||
while (v1parts.length < 3) v1parts.push(0);
|
||||
while (v2parts.length < 3) v2parts.push(0);
|
||||
|
||||
// Compare major version
|
||||
if (v1parts[0] !== v2parts[0]) {
|
||||
return v1parts[0] > v2parts[0] ? 1 : -1;
|
||||
}
|
||||
|
||||
// Compare minor version
|
||||
if (v1parts[1] !== v2parts[1]) {
|
||||
return v1parts[1] > v2parts[1] ? 1 : -1;
|
||||
}
|
||||
|
||||
// Compare patch version
|
||||
if (v1parts[2] !== v2parts[2]) {
|
||||
return v1parts[2] > v2parts[2] ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export default {
|
||||
compareVersions,
|
||||
crash,
|
||||
deferred,
|
||||
envToBoolean,
|
||||
|
@ -51,6 +51,7 @@ ipcMain.on("create-extra-window", (event, arg) => {
|
||||
interface ExportAsPdfOpts {
|
||||
title: string;
|
||||
landscape: boolean;
|
||||
pageSize: "A0" | "A1" | "A2" | "A3" | "A4" | "A5" | "A6" | "Legal" | "Letter" | "Tabloid" | "Ledger";
|
||||
}
|
||||
|
||||
ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
|
||||
@ -76,11 +77,14 @@ ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
|
||||
try {
|
||||
buffer = await browserWindow.webContents.printToPDF({
|
||||
landscape: opts.landscape,
|
||||
pageSize: opts.pageSize,
|
||||
generateDocumentOutline: true,
|
||||
generateTaggedPDF: true,
|
||||
printBackground: true,
|
||||
displayHeaderFooter: true,
|
||||
headerTemplate: `<div></div>`,
|
||||
footerTemplate: `
|
||||
<div style="width: 100%; text-align: center; font-size: 10pt;">
|
||||
<span class="pageNumber"></span>
|
||||
<div class="pageNumber" style="width: 100%; text-align: center; font-size: 10pt;">
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
@ -56,18 +56,26 @@
|
||||
<div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;">
|
||||
<form data-bind="submit: selectSetupType">
|
||||
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType"">
|
||||
<%= t("setup.new-document") %></label>
|
||||
</div>
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
|
||||
<%= t("setup.sync-from-desktop") %></label>
|
||||
</div>
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType"">
|
||||
<%= t("setup.sync-from-server") %></label>
|
||||
</div>
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label class="tn-radio">
|
||||
<input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
|
||||
<%= t("setup.new-document") %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label class="tn-radio">
|
||||
<input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
|
||||
<%= t("setup.sync-from-desktop") %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label class="tn-radio">
|
||||
<input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
|
||||
<%= t("setup.sync-from-server") %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" data-bind="disable: !setupTypeSelected()" class="btn btn-primary"><%= t("setup.next") %></button>
|
||||
</form>
|
||||
|
@ -12,7 +12,8 @@ import ws from "./services/ws.js";
|
||||
import utils from "./services/utils.js";
|
||||
import port from "./services/port.js";
|
||||
import host from "./services/host.js";
|
||||
import semver from "semver";
|
||||
|
||||
const MINIMUM_NODE_VERSION = "22.0.0";
|
||||
|
||||
// setup basic error handling even before requiring dependencies, since those can produce errors as well
|
||||
|
||||
@ -32,8 +33,8 @@ function exit() {
|
||||
process.on("SIGINT", exit);
|
||||
process.on("SIGTERM", exit);
|
||||
|
||||
if (!semver.satisfies(process.version, ">=10.5.0")) {
|
||||
console.error("Trilium only supports node.js 10.5 and later");
|
||||
if (utils.compareVersions(process.version, MINIMUM_NODE_VERSION) < 0) {
|
||||
console.error(`\nTrilium requires Node.js ${MINIMUM_NODE_VERSION} and later.\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user