mirror of
https://github.com/zadam/trilium.git
synced 2025-11-01 10:55:55 +01:00
Compare commits
56 Commits
v0.48.1-be
...
v0.48.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f60cf1a86 | ||
|
|
930d29d64a | ||
|
|
c345e7031b | ||
|
|
fc46398a3c | ||
|
|
7e41226549 | ||
|
|
b0c0ed8512 | ||
|
|
5978447185 | ||
|
|
ec4d445f97 | ||
|
|
e378435fbe | ||
|
|
e4dca4750f | ||
|
|
311b98ffcb | ||
|
|
126f41ae5e | ||
|
|
4b1678c416 | ||
|
|
da74272f13 | ||
|
|
9ce224d4c5 | ||
|
|
ce57a13002 | ||
|
|
e42357f1f8 | ||
|
|
ab7d121290 | ||
|
|
5209583a73 | ||
|
|
35fc4ba9a4 | ||
|
|
7f9019322b | ||
|
|
8a455e83f0 | ||
|
|
79b8d91025 | ||
|
|
bcabe5786f | ||
|
|
a7d3dafcf1 | ||
|
|
e6af84df39 | ||
|
|
674172f0b8 | ||
|
|
7ec20f9384 | ||
|
|
6135de8507 | ||
|
|
63b0d30e74 | ||
|
|
9e29fba8da | ||
|
|
8910ae92c7 | ||
|
|
3413074235 | ||
|
|
33aa72eb97 | ||
|
|
6e0a65b59c | ||
|
|
56e49cfc19 | ||
|
|
af40d73cee | ||
|
|
50b7063811 | ||
|
|
ee1b377bc2 | ||
|
|
51dfe8bb14 | ||
|
|
e426ee3e4f | ||
|
|
a434aa113d | ||
|
|
3b551e3e4d | ||
|
|
3d98644bf6 | ||
|
|
a22e4d60b6 | ||
|
|
069fbee3a6 | ||
|
|
8b21867c5c | ||
|
|
2cc4367b37 | ||
|
|
241d1b1035 | ||
|
|
449081807e | ||
|
|
dc0d6d24bd | ||
|
|
4111a2f0e8 | ||
|
|
8ef4b2bf50 | ||
|
|
53875d26bc | ||
|
|
f505f9d65a | ||
|
|
6434889cd6 |
53
.github/workflows/docker.yaml
vendored
Normal file
53
.github/workflows/docker.yaml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Publish Docker image
|
||||
on:
|
||||
push:
|
||||
tags: [v*]
|
||||
jobs:
|
||||
push_to_registries:
|
||||
name: Push Docker image to multiple registries
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
zadam/trilium
|
||||
ghcr.io/zadam/trilium
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}-latest
|
||||
type=match,pattern=(\d+.\d+).\d+\-beta,enable=${{ endsWith(github.ref, 'beta') }},group=1,suffix=-latest
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
install: true
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to GitHub Docker Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Create server-package.json
|
||||
run: cat package.json | grep -v electron > server-package.json
|
||||
- name: Build and Push
|
||||
uses: docker/build-push-action@v2.7.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
||||
push: true
|
||||
cache-from: type=registry,ref=zadam/trilium:buildcache
|
||||
cache-to: type=registry,ref=zadam/trilium:buildcache,mode=max
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
@@ -2,10 +2,10 @@ image:
|
||||
file: .gitpod.dockerfile
|
||||
|
||||
tasks:
|
||||
- before: nvm install 14.17.6 && nvm use 14.17.6
|
||||
- before: nvm install 14.18.1 && nvm use 14.18.1
|
||||
init: npm install
|
||||
command: npm run start-server
|
||||
|
||||
ports:
|
||||
- port: 8080
|
||||
onOpen: open-preview
|
||||
onOpen: open-preview
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14.17.6-alpine
|
||||
FROM node:14.18.1-alpine
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -4,4 +4,19 @@ echo "Packaging debian x64 distribution..."
|
||||
|
||||
VERSION=`jq -r ".version" package.json`
|
||||
|
||||
./node_modules/.bin/electron-installer-debian --config bin/deb-options.json --options.version=${VERSION} --arch amd64
|
||||
./node_modules/.bin/electron-installer-debian --config bin/deb-options.json --options.version=${VERSION} --arch amd64
|
||||
|
||||
|
||||
# hacky stop-gag measure to produce debian compatible XZ compressed debs until this is fixed: https://github.com/electron-userland/electron-installer-debian/issues/272
|
||||
cd dist
|
||||
ar x trilium_${VERSION}_amd64.deb
|
||||
rm trilium_${VERSION}_amd64.deb
|
||||
# recompress
|
||||
< control.tar.zst zstd -d | xz > control.tar.xz
|
||||
< data.tar.zst zstd -d | xz > data.tar.xz
|
||||
# create deb archive (I really do not know, what argument "sdsd" is for but something is required for ar to create the archive as desired)
|
||||
ar -m -c -a sdsd trilium_${VERSION}_amd64.deb debian-binary control.tar.xz data.tar.xz
|
||||
|
||||
rm control* data* debian-binary
|
||||
|
||||
echo "Converted to XZ deb"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PKG_DIR=dist/trilium-linux-x64-server
|
||||
NODE_VERSION=14.17.6
|
||||
NODE_VERSION=14.18.1
|
||||
|
||||
if [ "$1" != "DONTCOPY" ]
|
||||
then
|
||||
|
||||
@@ -5,7 +5,7 @@ if [[ $# -eq 0 ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
n exec 14.17.6 npm run webpack
|
||||
n exec 14.18.1 npm run webpack
|
||||
|
||||
DIR=$1
|
||||
|
||||
@@ -27,7 +27,7 @@ cp -r electron.js $DIR/
|
||||
cp webpack-* $DIR/
|
||||
|
||||
# run in subshell (so we return to original dir)
|
||||
(cd $DIR && n exec 14.17.6 npm install --only=prod)
|
||||
(cd $DIR && n exec 14.18.1 npm install --only=prod)
|
||||
|
||||
# cleanup of useless files in dependencies
|
||||
rm -r $DIR/node_modules/image-q/demo
|
||||
|
||||
@@ -69,13 +69,3 @@ gh release create "$TAG" \
|
||||
"dist/$WINDOWS_X64_BUILD" \
|
||||
"dist/$MAC_X64_BUILD" \
|
||||
"dist/$SERVER_BUILD"
|
||||
|
||||
echo "Building docker image"
|
||||
|
||||
bin/build-docker.sh $VERSION
|
||||
|
||||
echo "Pushing docker image to dockerhub"
|
||||
|
||||
bin/push-docker-image.sh $VERSION
|
||||
|
||||
echo "Release finished!"
|
||||
|
||||
67
images/icon.svg
Normal file
67
images/icon.svg
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg2163"
|
||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||
sodipodi:docname="cropped.svg">
|
||||
<defs
|
||||
id="defs2157" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.5099157"
|
||||
inkscape:cx="386.95033"
|
||||
inkscape:cy="314.49555"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1245"
|
||||
inkscape:window-height="846"
|
||||
inkscape:window-x="60"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata2160">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
d="m 139.19968,46.483949 c -5.40456,-0.16722 -8.20491,2.3622 -8.88118,2.5968 0.61524,-1.16523 1.02694,-1.89936 2.60032,-3.53766 -5.35164,-0.0561 -10.31381,1.10208 -14.09982,4.64397 -5.07788,4.75015 -4.68312,8.68574 -4.69335,8.66634 0.001,0.002 -0.80116,-2.56646 -1.04176,-5.31354 -5.52591,7.64928 -7.70784,17.78459 -3.51825,25.34532 2.60068,-6.2738 6.73029,-11.68083 12.0523,-15.9445 0.17533,-4.47393 1.9752,-8.25394 4.82,-11.04477 -2.56716,3.0727 -3.69993,6.57331 -3.58563,10.10144 6.19019,-4.61821 13.77738,-7.6962 22.11141,-8.7062 -6.03391,1.10878 -11.50091,3.51367 -16.27929,6.7938 2.99085,1.36595 6.40645,1.58009 9.99631,0.32984 -3.4417,1.6129 -7.26228,1.87678 -11.12731,0.47202 -3.43041,2.51213 -6.48124,5.4804 -9.10343,8.74254 4.65525,1.52188 9.74725,1.2072 14.81455,-1.37548 -4.65173,3.00461 -10.11661,4.13138 -15.95614,2.85185 -2.32798,3.1242 -4.2792,6.47983 -5.80426,9.93811 1.87855,1.385 10.03089,5.73052 24.01465,-4.58153 -1.9745,-0.58349 -3.5747,-1.40864 -3.57541,-1.41005 0.0445,0.0198 1.4411,0.14817 5.52556,-2.2987 3.61104,-2.11173 6.71724,-5.9369 9.0103,-8.91434 -1.95051,-0.71861 -3.46957,-1.47285 -3.47028,-1.47461 -0.001,-10e-4 2.5206,0.16968 6.4322,-2.8247 3.48897,-2.67088 7.38399,-6.54261 7.39599,-6.43325 0.0247,0.0233 -8.59226,-6.3433 -17.63748,-6.6227"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0352778"
|
||||
id="path1983" />
|
||||
<path
|
||||
d="m 76.908921,60.394149 c 4.10149,-1.19228 6.7131,0.18422 7.27437,0.22948 -0.68748,-0.76706 -1.13907,-1.24548 -2.64709,-2.18406 4.08133,-1.09739 8.09089,-1.19268 11.64521,0.76111 4.7672,2.6204 5.19783,5.69818 5.20227,5.68151 -8.1e-4,0.002 0.13467,-2.11419 -0.19274,-4.25578 5.649429,4.74214 9.204969,12.03797 7.409049,18.6271 -3.15635,-4.27007 -7.320749,-7.57773 -12.183899,-9.77927 -0.96691,-3.37555 -3.04709,-5.90265 -5.74177,-7.46934 2.535,1.83627 4.0527,4.28158 4.62244,6.9933 -5.59297,-2.30039 -11.96698,-3.15163 -18.52727,-2.27895 4.82002,-0.34383 15.0298,0.35455 23.31423,7.22913 -3.27571,2.07767 -7.22761,2.84155 -11.58272,1.87121 4.116,1.37333 8.50418,1.15576 12.73066,-0.97074 2.361629,1.92282 4.478499,4.09616 6.288529,6.4319 -1.1786,1.42613 -6.602629,6.34494 -19.214749,1.23993 1.40117,-0.83377 2.47089,-1.77831 2.47129,-1.7795 -0.0302,0.0238 -1.07415,0.39703 -4.65308,-0.66304 -3.15394,-0.89808 -6.24094,-3.20205 -8.54894,-5.02006 1.35762,-0.93183 2.37896,-1.80609 2.37896,-1.80768 3.9e-4,-8.1e-4 -1.89551,0.62612 -5.44379,-0.88538 -3.16522,-1.34871 -6.86431,-3.53238 -6.85302,-3.44662 -0.0145,0.0226 5.38855,-6.52877 12.25206,-8.52425"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.040011"
|
||||
id="path1985" />
|
||||
<path
|
||||
d="m 106.35028,116.78676 c -2.99089,-2.78316 -3.11891,-5.61706 -3.35465,-6.10128 -0.30328,0.94728 -0.48119,1.55657 -0.5271,3.26459 -2.902599,-2.81363 -4.935969,-6.06629 -5.042799,-9.9638 -0.14303,-5.226251 2.20774,-7.111581 2.19185,-7.106711 0.002,0 -1.82455,0.94068 -3.44692,2.27518 1.19194,-7.00825 5.529279,-13.56279 11.887189,-15.36164 -2.01438,4.72154 -2.73705,9.79389 -2.19847,14.89152 -2.33709,2.474751 -3.42573,5.443121 -3.41602,8.440321 0.29225,-2.99941 1.58705,-5.46485 3.56567,-7.282331 0.81185,5.747411 3.21074,11.417161 7.13399,16.382681 -2.63507,-3.79648 -7.03025,-12.54682 -5.34828,-22.784661 3.32507,1.66359 5.88686,4.53616 7.20241,8.603531 -0.86349,-4.070921 -3.18336,-7.574601 -7.01259,-9.996131 0.44853,-2.89963 1.2255,-5.72253 2.28632,-8.37424 1.76099,0.26123 8.49766,2.27917 10.39682,15.19844 -1.37647,-0.73844 -2.68363,-1.14912 -2.68496,-1.14868 0.0349,0.0129 0.85422,0.68654 1.7164,4.159181 0.78977,3.04292 0.37789,6.72937 -0.0102,9.53311 -1.43739,-0.65417 -2.66244,-1.05988 -2.66332,-1.059 -0.001,0 1.44445,1.24891 1.91637,4.92099 0.42071,3.27585 0.40659,7.40616 0.47236,7.35418 0.026,8.4e-4 -8.0584,-1.18846 -13.06411,-5.84525"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0442482"
|
||||
id="path1987" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
100
package-lock.json
generated
100
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.48.0-beta",
|
||||
"version": "0.48.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1606,16 +1606,16 @@
|
||||
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.17.3",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz",
|
||||
"integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==",
|
||||
"version": "4.17.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.4.tgz",
|
||||
"integrity": "sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001264",
|
||||
"electron-to-chromium": "^1.3.857",
|
||||
"caniuse-lite": "^1.0.30001265",
|
||||
"electron-to-chromium": "^1.3.867",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^1.1.77",
|
||||
"picocolors": "^0.2.1"
|
||||
"node-releases": "^2.0.0",
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
@@ -2188,11 +2188,6 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"colorette": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
|
||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w=="
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
@@ -2855,9 +2850,9 @@
|
||||
}
|
||||
},
|
||||
"electron": {
|
||||
"version": "13.5.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-13.5.2.tgz",
|
||||
"integrity": "sha512-CPakwDpy5m8dL0383F5uJboQcVtn9bT/+6/wdDKo8LuTUO9aER1TF41v7feZgZW2c+UwoGPWa814ElSQ3qta2A==",
|
||||
"version": "13.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-13.6.1.tgz",
|
||||
"integrity": "sha512-rZ6Y7RberigruefQpWOiI4bA9ppyT88GQF8htY6N1MrAgal5RrBc+Mh92CcGU7zT9QO+XO3DarSgZafNTepffQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
@@ -2866,9 +2861,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
||||
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==",
|
||||
"version": "14.17.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.32.tgz",
|
||||
"integrity": "sha512-JcII3D5/OapPGx+eJ+Ik1SQGyt6WvuqdRfh9jUwL6/iHGjmyOriBDciBUu7lEIBTL2ijxwrR70WUnw5AEDmFvQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -3551,9 +3546,9 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.864",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.864.tgz",
|
||||
"integrity": "sha512-v4rbad8GO6/yVI92WOeU9Wgxc4NA0n4f6P1FvZTY+jyY7JHEhw3bduYu60v3Q1h81Cg6eo4ApZrFPuycwd5hGw==",
|
||||
"version": "1.3.867",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.867.tgz",
|
||||
"integrity": "sha512-WbTXOv7hsLhjJyl7jBfDkioaY++iVVZomZ4dU6TMe/SzucV6mUAs2VZn/AehBwuZMiNEQDaPuTGn22YK5o+aDw==",
|
||||
"dev": true
|
||||
},
|
||||
"electron-window-state": {
|
||||
@@ -4944,19 +4939,19 @@
|
||||
}
|
||||
},
|
||||
"jasmine": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.9.0.tgz",
|
||||
"integrity": "sha512-JgtzteG7xnqZZ51fg7N2/wiQmXon09szkALcRMTgCMX4u/m17gVJFjObnvw5FXkZOWuweHPaPRVB6DI2uN0wVA==",
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.10.0.tgz",
|
||||
"integrity": "sha512-2Y42VsC+3CQCTzTwJezOvji4qLORmKIE0kwowWC+934Krn6ZXNQYljiwK5st9V3PVx96BSiDYXSB60VVah3IlQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.6",
|
||||
"jasmine-core": "~3.9.0"
|
||||
"jasmine-core": "~3.10.0"
|
||||
}
|
||||
},
|
||||
"jasmine-core": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.9.0.tgz",
|
||||
"integrity": "sha512-Tv3kVbPCGVrjsnHBZ38NsPU3sDOtNa0XmbG2baiyJqdb5/SPpDO6GVwJYtUryl6KB4q1Ssckwg612ES9Z0dreQ==",
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.10.0.tgz",
|
||||
"integrity": "sha512-XWGaJ25RUdOQnjGiLoQa9QG/R4u1e9Bk4uhLdn9F4JCBco84L4SKM52bxci4vWTSUzhmhuHNAkAHFN/6Cox9wQ==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-worker": {
|
||||
@@ -5782,9 +5777,9 @@
|
||||
}
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.1.25",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
|
||||
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q=="
|
||||
"version": "3.1.30",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
|
||||
"integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ=="
|
||||
},
|
||||
"napi-build-utils": {
|
||||
"version": "1.0.2",
|
||||
@@ -5844,9 +5839,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.77",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz",
|
||||
"integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.0.tgz",
|
||||
"integrity": "sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==",
|
||||
"dev": true
|
||||
},
|
||||
"nopt": {
|
||||
@@ -6308,9 +6303,9 @@
|
||||
"integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
|
||||
"integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
@@ -6380,13 +6375,20 @@
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz",
|
||||
"integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==",
|
||||
"version": "8.3.9",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.9.tgz",
|
||||
"integrity": "sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==",
|
||||
"requires": {
|
||||
"colorette": "^1.2.2",
|
||||
"nanoid": "^3.1.23",
|
||||
"nanoid": "^3.1.28",
|
||||
"picocolors": "^0.2.1",
|
||||
"source-map-js": "^0.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"picocolors": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
|
||||
"integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"prebuild-install": {
|
||||
@@ -6862,9 +6864,9 @@
|
||||
}
|
||||
},
|
||||
"sanitize-html": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.5.1.tgz",
|
||||
"integrity": "sha512-hUITPitQk+eFNLtr4dEkaaiAJndG2YE87IOpcfBSL1XdklWgwcNDJdr9Ppe8QKL/C3jFt1xH/Mbj20e0GZQOfg==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.5.2.tgz",
|
||||
"integrity": "sha512-sJ1rO2YixFIqs2kIcEUb6PTrCjvz8DMq1XqWWuy0kjgjrn58GNLK1DKSIRybFZDO1WNgsEgD+WiEzTEYS8xEug==",
|
||||
"requires": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
@@ -8030,9 +8032,9 @@
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.58.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.58.1.tgz",
|
||||
"integrity": "sha512-4Z/dmbTU+VmkCb2XNgW7wkE5TfEcSooclprn/UEuVeAkwHhn07OcgUsyaKHGtCY/VobjnsYBlyhKeMLiSoOqPg==",
|
||||
"version": "5.58.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.58.2.tgz",
|
||||
"integrity": "sha512-3S6e9Vo1W2ijk4F4PPWRIu6D/uGgqaPmqw+av3W3jLDujuNkdxX5h5c+RQ6GkjVR+WwIPOfgY8av+j5j4tMqJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
|
||||
10
package.json
10
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.48.1-beta",
|
||||
"version": "0.48.5",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -66,7 +66,7 @@
|
||||
"request": "^2.88.2",
|
||||
"rimraf": "3.0.2",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"sanitize-html": "2.5.1",
|
||||
"sanitize-html": "2.5.2",
|
||||
"sax": "1.2.4",
|
||||
"semver": "7.3.5",
|
||||
"serve-favicon": "2.5.0",
|
||||
@@ -81,16 +81,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "13.5.2",
|
||||
"electron": "13.6.1",
|
||||
"electron-builder": "22.13.1",
|
||||
"electron-packager": "15.4.0",
|
||||
"electron-rebuild": "3.2.3",
|
||||
"esm": "3.2.25",
|
||||
"jasmine": "3.9.0",
|
||||
"jasmine": "3.10.0",
|
||||
"jsdoc": "3.6.7",
|
||||
"lorem-ipsum": "2.0.4",
|
||||
"rcedit": "3.0.1",
|
||||
"webpack": "5.58.1",
|
||||
"webpack": "5.58.2",
|
||||
"webpack-cli": "4.9.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -4,7 +4,7 @@ const Attribute = require('../../src/becca/entities/attribute.js');
|
||||
const becca = require('../../src/becca/becca.js');
|
||||
const randtoken = require('rand-token').generator({source: 'crypto'});
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
function findNoteByTitle(searchResults, title) {
|
||||
return searchResults
|
||||
.map(sr => becca.notes[sr.noteId])
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const sql = require("../services/sql.js");
|
||||
const NoteRevision = require("./entities/note_revision.js");
|
||||
const RecentNote = require("./entities/recent_note.js");
|
||||
const NoteSet = require("../services/search/note_set");
|
||||
|
||||
class Becca {
|
||||
@@ -27,7 +25,7 @@ class Becca {
|
||||
this.loaded = false;
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
/** @returns {Attribute[]} */
|
||||
findAttributes(type, name) {
|
||||
name = name.trim().toLowerCase();
|
||||
|
||||
@@ -38,7 +36,7 @@ class Becca {
|
||||
return this.attributeIndex[`${type}-${name}`] || [];
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
/** @returns {Attribute[]} */
|
||||
findAttributesWithPrefix(type, name) {
|
||||
const resArr = [];
|
||||
const key = `${type}-${name}`;
|
||||
@@ -102,6 +100,7 @@ class Becca {
|
||||
getNoteRevision(noteRevisionId) {
|
||||
const row = sql.getRow("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [noteRevisionId]);
|
||||
|
||||
const NoteRevision = require("./entities/note_revision.js"); // avoiding circular dependency problems
|
||||
return row ? new NoteRevision(row) : null;
|
||||
}
|
||||
|
||||
@@ -131,12 +130,14 @@ class Becca {
|
||||
getRecentNotesFromQuery(query, params = []) {
|
||||
const rows = sql.getRows(query, params);
|
||||
|
||||
const RecentNote = require("./entities/recent_note.js"); // avoiding circular dependency problems
|
||||
return rows.map(row => new RecentNote(row));
|
||||
}
|
||||
|
||||
getNoteRevisionsFromQuery(query, params = []) {
|
||||
const rows = sql.getRows(query, params);
|
||||
|
||||
const NoteRevision = require("./entities/note_revision.js"); // avoiding circular dependency problems
|
||||
return rows.map(row => new NoteRevision(row));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,10 @@ function load() {
|
||||
new Option(row);
|
||||
}
|
||||
|
||||
for (const noteId in becca.notes) {
|
||||
becca.notes[noteId].sortParents();
|
||||
}
|
||||
|
||||
becca.loaded = true;
|
||||
|
||||
log.info(`Becca (note cache) load took ${Date.now() - start}ms`);
|
||||
@@ -66,7 +70,7 @@ function postProcessEntityUpdate(entityName, entity) {
|
||||
}
|
||||
}
|
||||
|
||||
eventService.subscribe([eventService.ENTITY_CHANGE_SYNCED], ({entityName, entityRow}) => {
|
||||
eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({entityName, entityRow}) => {
|
||||
if (!becca.loaded) {
|
||||
return;
|
||||
}
|
||||
@@ -89,7 +93,7 @@ eventService.subscribe([eventService.ENTITY_CHANGE_SYNCED], ({entityName, entit
|
||||
postProcessEntityUpdate(entityName, entityRow);
|
||||
});
|
||||
|
||||
eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
|
||||
eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
|
||||
if (!becca.loaded) {
|
||||
return;
|
||||
}
|
||||
@@ -97,7 +101,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
|
||||
postProcessEntityUpdate(entityName, entity);
|
||||
});
|
||||
|
||||
eventService.subscribe([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_SYNCED], ({entityName, entityId}) => {
|
||||
eventService.subscribeBeccaLoader([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_SYNCED], ({entityName, entityId}) => {
|
||||
if (!becca.loaded) {
|
||||
return;
|
||||
}
|
||||
@@ -151,7 +155,7 @@ function branchUpdated(branch) {
|
||||
|
||||
if (childNote) {
|
||||
childNote.flatTextCache = null;
|
||||
childNote.resortParents();
|
||||
childNote.sortParents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +220,7 @@ function noteReorderingUpdated(branchIdList) {
|
||||
}
|
||||
}
|
||||
|
||||
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => {
|
||||
eventService.subscribeBeccaLoader(eventService.ENTER_PROTECTED_SESSION, () => {
|
||||
try {
|
||||
becca.decryptProtectedNotes();
|
||||
}
|
||||
@@ -225,7 +229,7 @@ eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => {
|
||||
}
|
||||
});
|
||||
|
||||
eventService.subscribe(eventService.LEAVE_PROTECTED_SESSION, load);
|
||||
eventService.subscribeBeccaLoader(eventService.LEAVE_PROTECTED_SESSION, load);
|
||||
|
||||
module.exports = {
|
||||
load,
|
||||
|
||||
@@ -125,7 +125,7 @@ function getNoteTitleForPath(notePathArray) {
|
||||
|
||||
/**
|
||||
* Returns notePath for noteId from cache. Note hoisting is respected.
|
||||
* Archived notes are also returned, but non-archived paths are preferred if available
|
||||
* Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
|
||||
* - this means that archived paths is returned only if there's no non-archived path
|
||||
* - you can check whether returned path is archived using isArchived
|
||||
*/
|
||||
@@ -140,10 +140,6 @@ function getSomePathInner(note, path, respectHoisting) {
|
||||
path.push(note.noteId);
|
||||
path.reverse();
|
||||
|
||||
if (path.includes("hidden")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (respectHoisting && !path.includes(cls.getHoistedNoteId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,21 +36,21 @@ class Attribute extends AbstractEntity {
|
||||
}
|
||||
|
||||
update([attributeId, noteId, type, name, value, isInheritable, position, utcDateModified]) {
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.attributeId = attributeId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.type = type;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.name = name;
|
||||
/** @param {int} */
|
||||
/** @type {int} */
|
||||
this.position = position;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.value = value;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isInheritable = !!isInheritable;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateModified = utcDateModified;
|
||||
|
||||
return this;
|
||||
@@ -145,6 +145,10 @@ class Attribute extends AbstractEntity {
|
||||
}
|
||||
}
|
||||
|
||||
get isDeleted() {
|
||||
return !(this.attributeId in this.becca.attributes);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
if (!this.value) {
|
||||
if (this.type === 'relation') {
|
||||
|
||||
@@ -35,19 +35,19 @@ class Branch extends AbstractEntity {
|
||||
}
|
||||
|
||||
update([branchId, noteId, parentNoteId, prefix, notePosition, isExpanded, utcDateModified]) {
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.branchId = branchId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.parentNoteId = parentNoteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.prefix = prefix;
|
||||
/** @param {int} */
|
||||
/** @type {int} */
|
||||
this.notePosition = notePosition;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isExpanded = !!isExpanded;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateModified = utcDateModified;
|
||||
|
||||
return this;
|
||||
@@ -77,7 +77,7 @@ class Branch extends AbstractEntity {
|
||||
this.becca.childParentToBranch[`${this.noteId}-${this.parentNoteId}`] = this;
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
get childNote() {
|
||||
if (!(this.noteId in this.becca.notes)) {
|
||||
// entities can come out of order in sync, create skeleton which will be filled later
|
||||
@@ -91,7 +91,7 @@ class Branch extends AbstractEntity {
|
||||
return this.childNote;
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
get parentNote() {
|
||||
if (!(this.parentNoteId in this.becca.notes)) {
|
||||
// entities can come out of order in sync, create skeleton which will be filled later
|
||||
@@ -101,6 +101,10 @@ class Branch extends AbstractEntity {
|
||||
return this.becca.notes[this.parentNoteId];
|
||||
}
|
||||
|
||||
get isDeleted() {
|
||||
return !(this.branchId in this.becca.branches);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
if (this.notePosition === undefined || this.notePosition === null) {
|
||||
// TODO finding new position can be refactored into becca
|
||||
|
||||
@@ -45,33 +45,33 @@ class Note extends AbstractEntity {
|
||||
update([noteId, title, type, mime, isProtected, dateCreated, dateModified, utcDateCreated, utcDateModified]) {
|
||||
// ------ Database persisted attributes ------
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.title = title;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isProtected = !!isProtected;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.type = type;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.mime = mime;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateCreated = dateCreated || dateUtils.localNowDateTime();
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateModified = dateModified;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateCreated = utcDateCreated || dateUtils.utcNowDateTime();
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateModified = utcDateModified;
|
||||
|
||||
// ------ Derived attributes ------
|
||||
|
||||
/** @param {boolean} */
|
||||
this.isDecrypted = !this.isProtected;
|
||||
/** @type {boolean} */
|
||||
this.isDecrypted = !this.noteId || !this.isProtected;
|
||||
|
||||
this.decrypt();
|
||||
|
||||
/** @param {string|null} */
|
||||
/** @type {string|null} */
|
||||
this.flatTextCache = null;
|
||||
|
||||
return this;
|
||||
@@ -116,26 +116,32 @@ class Note extends AbstractEntity {
|
||||
|| protectedSessionService.isProtectedSessionAvailable()
|
||||
}
|
||||
|
||||
/** @returns {Branch[]} */
|
||||
getParentBranches() {
|
||||
return this.parentBranches;
|
||||
}
|
||||
|
||||
/** @returns {Branch[]} */
|
||||
getBranches() {
|
||||
return this.parentBranches;
|
||||
}
|
||||
|
||||
/** @returns {Note[]} */
|
||||
getParentNotes() {
|
||||
return this.parents;
|
||||
}
|
||||
|
||||
/** @returns {Note[]} */
|
||||
getChildNotes() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
hasChildren() {
|
||||
return this.children && this.children.length > 0;
|
||||
}
|
||||
|
||||
/** @returns {Branch[]} */
|
||||
getChildBranches() {
|
||||
return this.children.map(childNote => this.becca.getBranchFromChildAndParent(childNote.noteId, this.noteId));
|
||||
}
|
||||
@@ -370,7 +376,7 @@ class Note extends AbstractEntity {
|
||||
return this.__attributeCache;
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
/** @returns {Attribute[]} */
|
||||
__getInheritableAttributes(path) {
|
||||
if (path.includes(this.noteId)) {
|
||||
return [];
|
||||
@@ -611,7 +617,7 @@ class Note extends AbstractEntity {
|
||||
|
||||
// will sort the parents so that non-search & non-archived are first and archived at the end
|
||||
// this is done so that non-search & non-archived paths are always explored as first when looking for note path
|
||||
resortParents() {
|
||||
sortParents() {
|
||||
this.parentBranches.sort((a, b) =>
|
||||
a.branchId.startsWith('virt-')
|
||||
|| a.parentNote.hasInheritableOwnedArchivedLabel() ? 1 : -1);
|
||||
@@ -721,28 +727,38 @@ class Note extends AbstractEntity {
|
||||
return !!this.targetRelations.find(rel => rel.name === 'template');
|
||||
}
|
||||
|
||||
/** @return {Note[]} */
|
||||
/** @returns {Note[]} */
|
||||
getSubtreeNotesIncludingTemplated() {
|
||||
const arr = [[this]];
|
||||
const set = new Set();
|
||||
|
||||
for (const childNote of this.children) {
|
||||
arr.push(childNote.getSubtreeNotesIncludingTemplated());
|
||||
}
|
||||
function inner(note) {
|
||||
if (set.has(note)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const targetRelation of this.targetRelations) {
|
||||
if (targetRelation.name === 'template') {
|
||||
const note = targetRelation.note;
|
||||
set.add(note);
|
||||
|
||||
if (note) {
|
||||
arr.push(note.getSubtreeNotesIncludingTemplated());
|
||||
for (const childNote of note.children) {
|
||||
inner(childNote);
|
||||
}
|
||||
|
||||
for (const targetRelation of note.targetRelations) {
|
||||
if (targetRelation.name === 'template') {
|
||||
const targetNote = targetRelation.note;
|
||||
|
||||
if (targetNote) {
|
||||
inner(targetNote);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr.flat();
|
||||
inner(this);
|
||||
|
||||
return Array.from(set);
|
||||
}
|
||||
|
||||
/** @return {Note[]} */
|
||||
/** @returns {Note[]} */
|
||||
getSubtreeNotes(includeArchived = true) {
|
||||
const noteSet = new Set();
|
||||
|
||||
@@ -763,9 +779,9 @@ class Note extends AbstractEntity {
|
||||
return Array.from(noteSet);
|
||||
}
|
||||
|
||||
/** @return {String[]} */
|
||||
getSubtreeNoteIds() {
|
||||
return this.getSubtreeNotes().map(note => note.noteId);
|
||||
/** @returns {String[]} */
|
||||
getSubtreeNoteIds(includeArchived = true) {
|
||||
return this.getSubtreeNotes(includeArchived).map(note => note.noteId);
|
||||
}
|
||||
|
||||
getDescendantNoteIds() {
|
||||
@@ -820,6 +836,7 @@ class Note extends AbstractEntity {
|
||||
return this.getAttributes().length;
|
||||
}
|
||||
|
||||
/** @returns {Note[]} */
|
||||
getAncestors() {
|
||||
if (!this.ancestorCache) {
|
||||
const noteIds = new Set();
|
||||
@@ -843,11 +860,22 @@ class Note extends AbstractEntity {
|
||||
return this.ancestorCache;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
hasAncestor(ancestorNoteId) {
|
||||
for (const ancestorNote of this.getAncestors()) {
|
||||
if (ancestorNote.noteId === ancestorNoteId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getTargetRelations() {
|
||||
return this.targetRelations;
|
||||
}
|
||||
|
||||
/** @return {Note[]} - returns only notes which are templated, does not include their subtrees
|
||||
/** @returns {Note[]} - returns only notes which are templated, does not include their subtrees
|
||||
* in effect returns notes which are influenced by note's non-inheritable attributes */
|
||||
getTemplatedNotes() {
|
||||
const arr = [this];
|
||||
@@ -1084,6 +1112,10 @@ class Note extends AbstractEntity {
|
||||
}
|
||||
}
|
||||
|
||||
get isDeleted() {
|
||||
return !(this.noteId in this.becca.notes);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
|
||||
@@ -19,29 +19,29 @@ class NoteRevision extends AbstractEntity {
|
||||
constructor(row) {
|
||||
super();
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteRevisionId = row.noteRevisionId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = row.noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.type = row.type;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.mime = row.mime;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isProtected = !!row.isProtected;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.title = row.title;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateLastEdited = row.dateLastEdited;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateCreated = row.dateCreated;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateLastEdited = row.utcDateLastEdited;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateCreated = row.utcDateCreated;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateModified = row.utcDateModified;
|
||||
/** @param {number} */
|
||||
/** @type {number} */
|
||||
this.contentLength = row.contentLength;
|
||||
|
||||
if (this.isProtected) {
|
||||
|
||||
@@ -163,7 +163,23 @@ const TPL = `
|
||||
|
||||
<p>
|
||||
To apply font changes, click on
|
||||
<button class="btn btn-micro" id="reload-frontend-button">reload frontend</button>
|
||||
<button class="btn btn-micro reload-frontend-button">reload frontend</button>
|
||||
</p>
|
||||
|
||||
<h4>Content width</h4>
|
||||
|
||||
<p>Trilium by default limits max content width to improve readability for maximized screens on wide screens.</p>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-4">
|
||||
<label for="max-content-width">Max content width in pixels</label>
|
||||
<input type="number" min="200" step="10" class="form-control" id="max-content-width">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
To content width changes, click on
|
||||
<button class="btn btn-micro reload-frontend-button">reload frontend</button>
|
||||
</p>
|
||||
</form>`;
|
||||
|
||||
@@ -192,7 +208,7 @@ export default class ApperanceOptions {
|
||||
this.$monospaceFontSize = $("#monospace-font-size");
|
||||
this.$monospaceFontFamily = $("#monospace-font-family");
|
||||
|
||||
$("#reload-frontend-button").on("click", () => utils.reloadFrontendApp("font changes"));
|
||||
$(".reload-frontend-button").on("click", () => utils.reloadFrontendApp("changes from appearance options"));
|
||||
|
||||
this.$body = $("body");
|
||||
|
||||
@@ -238,6 +254,14 @@ export default class ApperanceOptions {
|
||||
for (const optionName of optionsToSave) {
|
||||
this['$' + optionName].on('change', () => server.put(`options/${optionName}/${this['$' + optionName].val()}`));
|
||||
}
|
||||
|
||||
this.$maxContentWidth = $("#max-content-width");
|
||||
|
||||
this.$maxContentWidth.on('change', async () => {
|
||||
const maxContentWidth = this.$maxContentWidth.val();
|
||||
|
||||
await server.put('options/maxContentWidth/' + maxContentWidth);
|
||||
})
|
||||
}
|
||||
|
||||
toggleBodyClass(prefix, value) {
|
||||
@@ -292,6 +316,8 @@ export default class ApperanceOptions {
|
||||
|
||||
this.$monospaceFontSize.val(options.monospaceFontSize);
|
||||
this.fillFontFamilyOptions(this.$monospaceFontFamily, options.monospaceFontFamily);
|
||||
|
||||
this.$maxContentWidth.val(options.maxContentWidth);
|
||||
}
|
||||
|
||||
fillFontFamilyOptions($select, currentValue) {
|
||||
|
||||
@@ -8,19 +8,19 @@ class Attribute {
|
||||
}
|
||||
|
||||
update(row) {
|
||||
/** @param {string} attributeId */
|
||||
/** @type {string} attributeId */
|
||||
this.attributeId = row.attributeId;
|
||||
/** @param {string} noteId */
|
||||
/** @type {string} noteId */
|
||||
this.noteId = row.noteId;
|
||||
/** @param {string} type */
|
||||
/** @type {string} type */
|
||||
this.type = row.type;
|
||||
/** @param {string} name */
|
||||
/** @type {string} name */
|
||||
this.name = row.name;
|
||||
/** @param {string} value */
|
||||
/** @type {string} value */
|
||||
this.value = row.value;
|
||||
/** @param {int} position */
|
||||
/** @type {int} position */
|
||||
this.position = row.position;
|
||||
/** @param {boolean} isInheritable */
|
||||
/** @type {boolean} isInheritable */
|
||||
this.isInheritable = !!row.isInheritable;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@ class Branch {
|
||||
}
|
||||
|
||||
update(row) {
|
||||
/** @param {string} primary key */
|
||||
/** @type {string} primary key */
|
||||
this.branchId = row.branchId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = row.noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.parentNoteId = row.parentNoteId;
|
||||
/** @param {int} */
|
||||
/** @type {int} */
|
||||
this.notePosition = row.notePosition;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.prefix = row.prefix;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isExpanded = !!row.isExpanded;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.fromSearchNote = !!row.fromSearchNote;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,35 +3,35 @@
|
||||
*/
|
||||
class NoteComplement {
|
||||
constructor(row) {
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = row.noteId;
|
||||
|
||||
/**
|
||||
* @param {string} - can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images)
|
||||
* @type {string} - can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images)
|
||||
*/
|
||||
this.content = row.content;
|
||||
|
||||
/** @param {int} */
|
||||
/** @type {int} */
|
||||
this.contentLength = row.contentLength;
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateCreated = row.dateCreated;
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.dateModified = row.dateModified;
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateCreated = row.utcDateCreated;
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.utcDateModified = row.utcDateModified;
|
||||
|
||||
// "combined" date modified give larger out of note's and note_content's dateModified
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.combinedDateModified = row.combinedDateModified;
|
||||
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.combinedUtcDateModified = row.combinedUtcDateModified;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@ class NoteShort {
|
||||
}
|
||||
|
||||
update(row) {
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.noteId = row.noteId;
|
||||
/** @param {string} */
|
||||
/** @type {string} */
|
||||
this.title = row.title;
|
||||
/** @param {boolean} */
|
||||
/** @type {boolean} */
|
||||
this.isProtected = !!row.isProtected;
|
||||
/** @param {string} one of 'text', 'code', 'file' or 'render' */
|
||||
/** @type {string} one of 'text', 'code', 'file' or 'render' */
|
||||
this.type = row.type;
|
||||
/** @param {string} content-type, e.g. "application/json" */
|
||||
/** @type {string} content-type, e.g. "application/json" */
|
||||
this.mime = row.mime;
|
||||
}
|
||||
|
||||
@@ -644,12 +644,9 @@ class NoteShort {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear note's attributes cache to force fresh reload for next attribute request.
|
||||
* Cache is note instance scoped.
|
||||
* @deprecated NOOP
|
||||
*/
|
||||
invalidateAttributeCache() {
|
||||
this.__attributeCache = null;
|
||||
}
|
||||
invalidateAttributeCache() {}
|
||||
|
||||
/**
|
||||
* Get relations which target this note
|
||||
@@ -681,7 +678,7 @@ class NoteShort {
|
||||
return await this.froca.getNoteComplement(this.noteId);
|
||||
}
|
||||
|
||||
get toString() {
|
||||
toString() {
|
||||
return `Note(noteId=${this.noteId}, title=${this.title})`;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,12 +79,12 @@ class AppContext extends Component {
|
||||
this.triggerEvent('initialRenderComplete');
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
triggerEvent(name, data) {
|
||||
return this.handleEvent(name, data);
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
triggerCommand(name, data = {}) {
|
||||
for (const executor of this.executors) {
|
||||
const fun = executor[name + "Command"];
|
||||
|
||||
@@ -97,7 +97,7 @@ class ContextMenu {
|
||||
.append($link)
|
||||
// important to use mousedown instead of click since the former does not change focus
|
||||
// (especially important for focused text for spell check)
|
||||
.on('mousedown', (e) => {
|
||||
.on('mousedown', e => {
|
||||
e.stopPropagation();
|
||||
|
||||
this.hide();
|
||||
|
||||
@@ -2,19 +2,19 @@ import froca from "./froca.js";
|
||||
import server from "./server.js";
|
||||
import ws from "./ws.js";
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function getInboxNote() {
|
||||
const note = await server.get('special-notes/inbox/' + dayjs().format("YYYY-MM-DD"), "date-note");
|
||||
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function getTodayNote() {
|
||||
return await getDateNote(dayjs().format("YYYY-MM-DD"));
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function getDateNote(date) {
|
||||
const note = await server.get('special-notes/date/' + date, "date-note");
|
||||
|
||||
@@ -23,7 +23,16 @@ async function getDateNote(date) {
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function getWeekNote(date) {
|
||||
const note = await server.get('special-notes/week/' + date, "date-note");
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @returns {NoteShort} */
|
||||
async function getMonthNote(month) {
|
||||
const note = await server.get('special-notes/month/' + month, "date-note");
|
||||
|
||||
@@ -32,7 +41,7 @@ async function getMonthNote(month) {
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function getYearNote(year) {
|
||||
const note = await server.get('special-notes/year/' + year, "date-note");
|
||||
|
||||
@@ -41,7 +50,7 @@ async function getYearNote(year) {
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function createSqlConsole() {
|
||||
const note = await server.post('special-notes/sql-console');
|
||||
|
||||
@@ -50,7 +59,7 @@ async function createSqlConsole() {
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
async function createSearchNote(opts = {}) {
|
||||
const note = await server.post('special-notes/search-note', opts);
|
||||
|
||||
@@ -63,6 +72,7 @@ export default {
|
||||
getInboxNote,
|
||||
getTodayNote,
|
||||
getDateNote,
|
||||
getWeekNote,
|
||||
getMonthNote,
|
||||
getYearNote,
|
||||
createSqlConsole,
|
||||
|
||||
@@ -172,6 +172,12 @@ export default class Entrypoints extends Component {
|
||||
utils.reloadFrontendApp("Switching to desktop version");
|
||||
}
|
||||
|
||||
async switchToMobileVersionCommand() {
|
||||
utils.setCookie('trilium-device', 'mobile');
|
||||
|
||||
utils.reloadFrontendApp("Switching to mobile version");
|
||||
}
|
||||
|
||||
async openInWindowCommand({notePath, hoistedNoteId}) {
|
||||
if (!hoistedNoteId) {
|
||||
hoistedNoteId = 'root';
|
||||
|
||||
@@ -207,7 +207,7 @@ class Froca {
|
||||
froca.notes[note.noteId].searchResultsLoaded = true;
|
||||
}
|
||||
|
||||
/** @return {NoteShort[]} */
|
||||
/** @returns {NoteShort[]} */
|
||||
getNotesFromCache(noteIds, silentNotFoundError = false) {
|
||||
return noteIds.map(noteId => {
|
||||
if (!this.notes[noteId] && !silentNotFoundError) {
|
||||
@@ -221,7 +221,7 @@ class Froca {
|
||||
}).filter(note => !!note);
|
||||
}
|
||||
|
||||
/** @return {Promise<NoteShort[]>} */
|
||||
/** @returns {Promise<NoteShort[]>} */
|
||||
async getNotes(noteIds, silentNotFoundError = false) {
|
||||
const missingNoteIds = noteIds.filter(noteId => !this.notes[noteId]);
|
||||
|
||||
@@ -238,14 +238,14 @@ class Froca {
|
||||
}).filter(note => !!note);
|
||||
}
|
||||
|
||||
/** @return {Promise<boolean>} */
|
||||
/** @returns {Promise<boolean>} */
|
||||
async noteExists(noteId) {
|
||||
const notes = await this.getNotes([noteId], true);
|
||||
|
||||
return notes.length === 1;
|
||||
}
|
||||
|
||||
/** @return {Promise<NoteShort>} */
|
||||
/** @returns {Promise<NoteShort>} */
|
||||
async getNote(noteId, silentNotFoundError = false) {
|
||||
if (noteId === 'none') {
|
||||
console.trace(`No 'none' note.`);
|
||||
@@ -273,7 +273,7 @@ class Froca {
|
||||
.filter(b => !!b);
|
||||
}
|
||||
|
||||
/** @return {Branch} */
|
||||
/** @returns {Branch} */
|
||||
getBranch(branchId, silentNotFoundError = false) {
|
||||
if (!(branchId in this.branches)) {
|
||||
if (!silentNotFoundError) {
|
||||
|
||||
@@ -396,6 +396,15 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
*/
|
||||
this.getDateNote = dateNotesService.getDateNote;
|
||||
|
||||
/**
|
||||
* Returns date-note for the first date of the week of the given date. If it doesn't exist, it is automatically created.
|
||||
*
|
||||
* @method
|
||||
* @param {string} date - e.g. "2019-04-29"
|
||||
* @return {Promise<NoteShort>}
|
||||
*/
|
||||
this.getWeekNote = dateNotesService.getWeekNote;
|
||||
|
||||
/**
|
||||
* Returns month-note. If it doesn't exist, it is automatically created.
|
||||
*
|
||||
|
||||
@@ -65,7 +65,7 @@ export default class LoadResults {
|
||||
this.attributes.push({attributeId, sourceId});
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
/** @returns {Attribute[]} */
|
||||
getAttributes(sourceId = 'none') {
|
||||
return this.attributes
|
||||
.filter(row => row.sourceId !== sourceId)
|
||||
|
||||
@@ -43,7 +43,7 @@ async function autocompleteSource(term, cb, options = {}) {
|
||||
action: 'create-note',
|
||||
noteTitle: term,
|
||||
parentNoteId: activeNoteId || 'root',
|
||||
highlightedNotePathTitle: `Create and link child note "${term}"`
|
||||
highlightedNotePathTitle: `Create and link child note "${utils.escapeHtml(term)}"`
|
||||
}
|
||||
].concat(results);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ async function autocompleteSource(term, cb, options = {}) {
|
||||
{
|
||||
action: 'external-link',
|
||||
externalLink: term,
|
||||
highlightedNotePathTitle: `Insert external link to "${term}"`
|
||||
highlightedNotePathTitle: `Insert external link to "${utils.escapeHtml(term)}"`
|
||||
}
|
||||
].concat(results);
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ function trim(text, doTrim) {
|
||||
return text;
|
||||
}
|
||||
else {
|
||||
return text.substr(0, Math.min(text.length, 1000));
|
||||
return text.substr(0, Math.min(text.length, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ class NoteContext extends Component {
|
||||
}
|
||||
|
||||
setEmpty() {
|
||||
this.notePath = null;
|
||||
this.noteId = null;
|
||||
this.parentNoteId = null;
|
||||
this.hoistedNoteId = 'root';
|
||||
|
||||
this.triggerEvent('noteSwitched', {
|
||||
noteContext: this,
|
||||
notePath: this.notePath
|
||||
@@ -71,7 +76,13 @@ class NoteContext extends Component {
|
||||
|
||||
getMainContext() {
|
||||
if (this.mainNtxId) {
|
||||
return appContext.tabManager.getNoteContextById(this.mainNtxId);
|
||||
try {
|
||||
return appContext.tabManager.getNoteContextById(this.mainNtxId);
|
||||
}
|
||||
catch (e) {
|
||||
this.mainNtxId = null;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return this;
|
||||
@@ -123,7 +134,7 @@ class NoteContext extends Component {
|
||||
return this.notePath ? this.notePath.split('/') : [];
|
||||
}
|
||||
|
||||
/** @return {NoteComplement} */
|
||||
/** @returns {NoteComplement} */
|
||||
async getNoteComplement() {
|
||||
if (!this.noteId) {
|
||||
return null;
|
||||
@@ -155,12 +166,12 @@ class NoteContext extends Component {
|
||||
}
|
||||
|
||||
async setHoistedNoteId(noteIdToHoist) {
|
||||
if (this.notePathArray && !this.notePathArray.includes(noteIdToHoist)) {
|
||||
this.hoistedNoteId = noteIdToHoist;
|
||||
|
||||
if (!this.notePathArray?.includes(noteIdToHoist)) {
|
||||
await this.setNote(noteIdToHoist);
|
||||
}
|
||||
|
||||
this.hoistedNoteId = noteIdToHoist;
|
||||
|
||||
await this.triggerEvent('hoistedNoteChanged', {
|
||||
noteId: noteIdToHoist,
|
||||
ntxId: this.ntxId
|
||||
|
||||
@@ -153,7 +153,7 @@ class NoteListRenderer {
|
||||
this.parentNote = parentNote;
|
||||
const includedNoteIds = this.getIncludedNoteIds();
|
||||
|
||||
this.noteIds = noteIds.filter(noteId => !includedNoteIds.has(noteId));
|
||||
this.noteIds = noteIds.filter(noteId => !includedNoteIds.has(noteId) && noteId !== 'hidden');
|
||||
|
||||
if (this.noteIds.length === 0) {
|
||||
return;
|
||||
@@ -180,7 +180,7 @@ class NoteListRenderer {
|
||||
this.showNotePath = showNotePath;
|
||||
}
|
||||
|
||||
/** @return {Set<string>} list of noteIds included (images, included notes) into a parent note and which
|
||||
/** @returns {Set<string>} list of noteIds included (images, included notes) into a parent note and which
|
||||
* don't have to be shown in the note list. */
|
||||
getIncludedNoteIds() {
|
||||
const includedLinks = this.parentNote
|
||||
|
||||
@@ -139,7 +139,7 @@ export default class TabManager extends Component {
|
||||
this.triggerEvent('activeNoteChanged'); // trigger this even in on popstate event
|
||||
}
|
||||
|
||||
/** @return {NoteContext[]} */
|
||||
/** @returns {NoteContext[]} */
|
||||
getNoteContexts() {
|
||||
return this.noteContexts;
|
||||
}
|
||||
@@ -175,20 +175,20 @@ export default class TabManager extends Component {
|
||||
return activeContext ? activeContext.notePath : null;
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
/** @returns {NoteShort} */
|
||||
getActiveContextNote() {
|
||||
const activeContext = this.getActiveContext();
|
||||
return activeContext ? activeContext.note : null;
|
||||
}
|
||||
|
||||
/** @return {string|null} */
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNoteId() {
|
||||
const activeNote = this.getActiveContextNote();
|
||||
|
||||
return activeNote ? activeNote.noteId : null;
|
||||
}
|
||||
|
||||
/** @return {string|null} */
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNoteType() {
|
||||
const activeNote = this.getActiveContextNote();
|
||||
|
||||
@@ -299,6 +299,17 @@ export default class TabManager extends Component {
|
||||
async removeNoteContext(ntxId) {
|
||||
const noteContextToRemove = this.getNoteContextById(ntxId);
|
||||
|
||||
if (noteContextToRemove.isMainContext()) {
|
||||
// forbid removing last main note context
|
||||
// this was previously allowed (was replaced with empty tab) but this proved to be prone to race conditions
|
||||
const mainNoteContexts = this.getNoteContexts().filter(nc => nc.isMainContext());
|
||||
|
||||
if (mainNoteContexts.length === 1) {
|
||||
mainNoteContexts[0].setEmpty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// close dangling autocompletes after closing the tab
|
||||
$(".aa-input").autocomplete("close");
|
||||
|
||||
@@ -353,7 +364,7 @@ export default class TabManager extends Component {
|
||||
const order = {};
|
||||
let i = 0;
|
||||
|
||||
for (const ntxId in ntxIdsInOrder) {
|
||||
for (const ntxId of ntxIdsInOrder) {
|
||||
order[ntxId] = i++;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
|
||||
|
||||
const someNotePathSegments = getSomeNotePathSegments(note, hoistedNoteId);
|
||||
|
||||
if (!someNotePathSegments) {
|
||||
throw new Error(`Did not find any path segments for ${note.toString()}, hoisted note ${hoistedNoteId}`);
|
||||
}
|
||||
|
||||
// if there isn't actually any note path with hoisted note then return the original resolved note path
|
||||
return someNotePathSegments.includes(hoistedNoteId) ? someNotePathSegments : effectivePathSegments;
|
||||
}
|
||||
|
||||
@@ -68,12 +68,13 @@ async function executeFrontendUpdate(entityChanges) {
|
||||
frontendUpdateDataQueue.push(...entityChanges);
|
||||
|
||||
// we set lastAcceptedEntityChangeId even before frontend update processing and send ping so that backend can start sending more updates
|
||||
lastAcceptedEntityChangeId = Math.max(lastAcceptedEntityChangeId, entityChanges[entityChanges.length - 1].id);
|
||||
|
||||
const lastSyncEntityChange = entityChanges.slice().reverse().find(ec => ec.isSynced);
|
||||
for (const entityChange of entityChanges) {
|
||||
lastAcceptedEntityChangeId = Math.max(lastAcceptedEntityChangeId, entityChange.id);
|
||||
|
||||
if (lastSyncEntityChange) {
|
||||
lastAcceptedEntityChangeSyncId = Math.max(lastAcceptedEntityChangeSyncId, lastSyncEntityChange.id);
|
||||
if (entityChange.isSynced) {
|
||||
lastAcceptedEntityChangeSyncId = Math.max(lastAcceptedEntityChangeSyncId, entityChange.id);
|
||||
}
|
||||
}
|
||||
|
||||
sendPing();
|
||||
|
||||
@@ -334,7 +334,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
this.$relatedNotesList = this.$relatedNotesContainer.find('.related-notes-list');
|
||||
this.$relatedNotesMoreNotes = this.$relatedNotesContainer.find('.related-notes-more-notes');
|
||||
|
||||
$(window).on('mouseup', e => {
|
||||
$(window).on('mousedown', e => {
|
||||
if (!$(e.target).closest(this.$widget[0]).length
|
||||
&& !$(e.target).closest(".algolia-autocomplete").length
|
||||
&& !$(e.target).closest("#context-menu-container").length) {
|
||||
@@ -626,7 +626,9 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
||||
props.push('precision=' + this.$inputNumberPrecision.val());
|
||||
}
|
||||
} else if (this.attrType === 'relation-definition' && this.$inputInverseRelation.val().trim().length > 0) {
|
||||
props.push("inverse=" + this.$inputInverseRelation.val());
|
||||
const inverseRelationName = this.$inputInverseRelation.val();
|
||||
|
||||
props.push("inverse=" + utils.filterAttributeName(inverseRelationName));
|
||||
}
|
||||
|
||||
this.$rowNumberPrecision.toggle(
|
||||
|
||||
@@ -193,7 +193,8 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
|
||||
|
||||
this.$editor.on('keydown', async e => {
|
||||
if (e.which === 13) {
|
||||
await this.save();
|
||||
// allow autocomplete to fill the result textarea
|
||||
setTimeout(() => this.save(), 100);
|
||||
}
|
||||
|
||||
this.attributeDetailWidget.hide();
|
||||
|
||||
@@ -26,9 +26,17 @@ export default class ButtonWidget extends NoteContextAwareWidget {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
if (this.settings.onClick) {
|
||||
this.$widget.on("click", () => this.settings.onClick(this));
|
||||
this.$widget.on("click", e => {
|
||||
this.$widget.tooltip("hide");
|
||||
|
||||
this.settings.onClick(this, e);
|
||||
});
|
||||
} else {
|
||||
this.$widget.on("click", () => this.triggerCommand(this.settings.command));
|
||||
this.$widget.on("click", () => {
|
||||
this.$widget.tooltip("hide");
|
||||
|
||||
this.triggerCommand(this.settings.command);
|
||||
});
|
||||
}
|
||||
|
||||
this.$widget.attr("data-placement", this.settings.titlePlacement);
|
||||
|
||||
@@ -13,6 +13,12 @@ export default class ClosePaneButton extends ButtonWidget {
|
||||
this.icon("bx-x")
|
||||
.title("Close this pane")
|
||||
.titlePlacement("bottom")
|
||||
.onClick(widget => widget.triggerCommand("closeThisNoteSplit", { ntxId: widget.getNtxId() }));
|
||||
.onClick((widget, e) => {
|
||||
// to avoid split pane container detecting click within the pane which would try to activate this
|
||||
// pane (which is being removed)
|
||||
e.stopPropagation();
|
||||
|
||||
widget.triggerCommand("closeThisNoteSplit", { ntxId: widget.getNtxId() });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ const TPL = `
|
||||
<kbd data-command="showBackendLog"></kbd>
|
||||
</a>
|
||||
|
||||
<a class="dropdown-item switch-to-mobile-version-button" data-trigger-command="switchToMobileVersion">
|
||||
<span class="bx bx-empty"></span>
|
||||
Switch to mobile version
|
||||
</a>
|
||||
|
||||
<a class="dropdown-item" data-trigger-command="reloadFrontendApp"
|
||||
title="Reload can help with some visual glitches without restarting the whole app.">
|
||||
<span class="bx bx-empty"></span>
|
||||
@@ -103,9 +108,11 @@ export default class GlobalMenuWidget extends BasicWidget {
|
||||
this.$widget.find(".show-about-dialog-button").on('click',
|
||||
() => import("../../dialogs/about.js").then(d => d.showDialog()));
|
||||
|
||||
this.$widget.find(".logout-button").toggle(!utils.isElectron());
|
||||
const isElectron = utils.isElectron();
|
||||
|
||||
this.$widget.find(".open-dev-tools-button").toggle(utils.isElectron());
|
||||
this.$widget.find(".logout-button").toggle(!isElectron);
|
||||
this.$widget.find(".open-dev-tools-button").toggle(isElectron);
|
||||
this.$widget.find(".switch-to-mobile-version-button").toggle(!isElectron);
|
||||
|
||||
this.$widget.on('click', '.dropdown-item',
|
||||
() => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle'));
|
||||
|
||||
@@ -25,12 +25,12 @@ const TPL = `
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a data-trigger-command="renderActiveNote" class="dropdown-item render-note-button"><kbd data-command="renderActiveNote"></kbd> Re-render note</a>
|
||||
<a data-trigger-command="findInText" class="dropdown-item">Search in note <kbd data-command="findInText"></a>
|
||||
<a data-trigger-command="findInText" class="dropdown-item find-in-text-button">Search in note <kbd data-command="findInText"></a>
|
||||
<a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</a>
|
||||
<a data-trigger-command="openNoteExternally" class="dropdown-item open-note-externally-button"><kbd data-command="openNoteExternally"></kbd> Open note externally</a>
|
||||
<a class="dropdown-item import-files-button">Import files</a>
|
||||
<a class="dropdown-item export-note-button">Export note</a>
|
||||
<a data-trigger-command="printActiveNote" class="dropdown-item print-note-button"><kbd data-command="printActiveNote"></kbd> Print note</a>
|
||||
<a data-trigger-command="printActiveNote" class="dropdown-item print-active-note-button"><kbd data-command="printActiveNote"></kbd> Print note</a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -42,6 +42,8 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.$findInTextButton = this.$widget.find('.find-in-text-button');
|
||||
this.$printActiveNoteButton = this.$widget.find('.print-active-note-button');
|
||||
this.$showSourceButton = this.$widget.find('.show-source-button');
|
||||
this.$renderNoteButton = this.$widget.find('.render-note-button');
|
||||
|
||||
@@ -57,14 +59,18 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
||||
this.$importNoteButton = this.$widget.find('.import-files-button');
|
||||
this.$importNoteButton.on("click", () => import('../../dialogs/import.js').then(d => d.showDialog(this.noteId)));
|
||||
|
||||
this.$widget.on('click', '.dropdown-item', () => this.$widget.find('.dropdown-menu').dropdown('toggle'));
|
||||
this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle'));
|
||||
|
||||
this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button");
|
||||
}
|
||||
|
||||
refreshWithNote(note) {
|
||||
this.toggleDisabled(this.$findInTextButton, ['text', 'code', 'book', 'search'].includes(note.type));
|
||||
|
||||
this.toggleDisabled(this.$showSourceButton, ['text', 'relation-map', 'search', 'code'].includes(note.type));
|
||||
|
||||
this.toggleDisabled(this.$printActiveNoteButton, ['text', 'code'].includes(note.type));
|
||||
|
||||
this.$renderNoteButton.toggle(note.type === 'render');
|
||||
|
||||
this.$openNoteExternallyButton.toggle(utils.isElectron());
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class Component {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
handleEvent(name, data) {
|
||||
return Promise.all([
|
||||
this.initialized.then(() => this.callMethod(this[name + 'Event'], data)),
|
||||
@@ -48,12 +48,12 @@ export default class Component {
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
triggerEvent(name, data) {
|
||||
return this.parent.triggerEvent(name, data);
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
handleEventInChildren(name, data) {
|
||||
const promises = [];
|
||||
|
||||
@@ -64,7 +64,7 @@ export default class Component {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/** @return {Promise} */
|
||||
/** @returns {Promise} */
|
||||
triggerCommand(name, data = {}) {
|
||||
const fun = this[name + 'Command'];
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import keyboardActionsService from "../../services/keyboard_actions.js";
|
||||
import attributeService from "../../services/attributes.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="ribbon-container">
|
||||
@@ -166,7 +167,7 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
||||
});
|
||||
}
|
||||
|
||||
toggleRibbonTab($ribbonTitle) {
|
||||
toggleRibbonTab($ribbonTitle, refreshActiveTab = true) {
|
||||
const activate = !$ribbonTitle.hasClass("active");
|
||||
|
||||
this.$tabContainer.find('.ribbon-tab-title').removeClass("active");
|
||||
@@ -182,7 +183,7 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
||||
|
||||
const activeChild = this.getActiveRibbonWidget();
|
||||
|
||||
if (activeChild) {
|
||||
if (activeChild && refreshActiveTab) {
|
||||
activeChild.handleEvent('noteSwitched', {noteContext: this.noteContext, notePath: this.notePath});
|
||||
}
|
||||
} else {
|
||||
@@ -248,7 +249,7 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
||||
}
|
||||
|
||||
if ($ribbonTabToActivate) {
|
||||
$ribbonTabToActivate.trigger('click');
|
||||
this.toggleRibbonTab($ribbonTabToActivate, false);
|
||||
}
|
||||
else {
|
||||
this.$bodyContainer.find('.ribbon-body').removeClass("active");
|
||||
@@ -261,10 +262,6 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
||||
return $ribbonComponent.hasClass("active");
|
||||
}
|
||||
|
||||
refreshRibbonContainerCommand() {
|
||||
this.refreshWithNote(this.note, true);
|
||||
}
|
||||
|
||||
ensureOwnedAttributesAreOpen(ntxId) {
|
||||
if (this.isNoteContext(ntxId) && !this.isRibbonTabActive('ownedAttributes')) {
|
||||
this.toggleRibbonTabWithName('ownedAttributes', ntxId);
|
||||
@@ -332,6 +329,9 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
else if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) {
|
||||
this.refreshWithNote(this.note, true);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveRibbonWidget() {
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
||||
const $renderedWidget = widget.render();
|
||||
|
||||
$renderedWidget.attr("data-ntx-id", noteContext.ntxId);
|
||||
$renderedWidget.css("flex-basis", "0"); // so that each split has same width
|
||||
$renderedWidget.addClass("note-split");
|
||||
|
||||
$renderedWidget.on('click', () => appContext.tabManager.activateNoteContext(noteContext.ntxId));
|
||||
|
||||
@@ -37,6 +37,12 @@ export default class SplitNoteContainer extends FlexContainer {
|
||||
}
|
||||
|
||||
async openNewNoteSplitEvent({ntxId, notePath}) {
|
||||
if (!ntxId) {
|
||||
logError("empty ntxId!");
|
||||
|
||||
ntxId = appContext.tabManager.getActiveMainContext().ntxId;
|
||||
}
|
||||
|
||||
const noteContext = await appContext.tabManager.openEmptyTab(null, 'root', appContext.tabManager.getActiveMainContext().ntxId);
|
||||
|
||||
// remove the original position of newly created note context
|
||||
@@ -46,7 +52,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
||||
// insert the note context after the originating note context
|
||||
ntxIds.splice(ntxIds.indexOf(ntxId) + 1, 0, noteContext.ntxId);
|
||||
|
||||
this.triggerCommand("noteContextReorder", ntxIds);
|
||||
this.triggerCommand("noteContextReorder", {ntxIdsInOrder: ntxIds});
|
||||
|
||||
// move the note context rendered widget after the originating widget
|
||||
this.$widget.find(`[data-ntx-id="${noteContext.ntxId}"]`)
|
||||
|
||||
@@ -17,7 +17,7 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
z-index: 1000;
|
||||
z-index: 10; /* should be below dropdown (note actions) */
|
||||
}
|
||||
|
||||
.map-type-switcher .bx {
|
||||
@@ -332,9 +332,9 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
||||
|
||||
if (this.widgetMode === 'ribbon') {
|
||||
setTimeout(() => {
|
||||
const node = this.nodes.find(node => node.id === this.noteId);
|
||||
const subGraphNoteIds = this.getSubGraphConnectedToCurrentNote(data);
|
||||
|
||||
this.graph.centerAt(node.x, node.y, 500);
|
||||
this.graph.zoomToFit(400, 50, node => subGraphNoteIds.has(node.id));
|
||||
}, 1000);
|
||||
}
|
||||
else if (this.widgetMode === 'type') {
|
||||
@@ -344,6 +344,39 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
getSubGraphConnectedToCurrentNote(data) {
|
||||
function getGroupedLinksBySource(links) {
|
||||
const map = {};
|
||||
|
||||
for (const link of links) {
|
||||
const key = link.source.id;
|
||||
map[key] = map[key] || [];
|
||||
map[key].push(link);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
const linksBySource = getGroupedLinksBySource(data.links);
|
||||
|
||||
const subGraphNoteIds = new Set();
|
||||
|
||||
function traverseGraph(noteId) {
|
||||
if (subGraphNoteIds.has(noteId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
subGraphNoteIds.add(noteId);
|
||||
|
||||
for (const link of linksBySource[noteId] || []) {
|
||||
traverseGraph(link.target.id);
|
||||
}
|
||||
}
|
||||
|
||||
traverseGraph(this.noteId);
|
||||
return subGraphNoteIds;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.$container.html('');
|
||||
}
|
||||
|
||||
@@ -681,12 +681,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
return extraClasses.join(" ");
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
/** @returns {FancytreeNode[]} */
|
||||
getSelectedNodes(stopOnParents = false) {
|
||||
return this.tree.getSelectedNodes(stopOnParents);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
/** @returns {FancytreeNode[]} */
|
||||
getSelectedOrActiveNodes(node = null) {
|
||||
const nodes = this.getSelectedNodes(true);
|
||||
|
||||
@@ -771,7 +771,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
/** @returns {FancytreeNode} */
|
||||
async getNodeFromPath(notePath, expand = false, logErrors = true) {
|
||||
utils.assertArguments(notePath);
|
||||
/** @let {FancytreeNode} */
|
||||
@@ -838,24 +838,24 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
/** @returns {FancytreeNode} */
|
||||
findChildNode(parentNode, childNoteId) {
|
||||
return parentNode.getChildren().find(childNode => childNode.data.noteId === childNoteId);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
/** @returns {FancytreeNode} */
|
||||
async expandToNote(notePath, logErrors = true) {
|
||||
return this.getNodeFromPath(notePath, true, logErrors);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
/** @returns {FancytreeNode[]} */
|
||||
getNodesByBranch(branch) {
|
||||
utils.assertArguments(branch);
|
||||
|
||||
return this.getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branch.branchId);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
/** @returns {FancytreeNode[]} */
|
||||
getNodesByNoteId(noteId) {
|
||||
utils.assertArguments(noteId);
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
const NOTE_TYPES = [
|
||||
{ type: "file", title: "File", selectable: false },
|
||||
{ type: "image", title: "Image", selectable: false },
|
||||
{ type: "search", title: "Saved search", selectable: false },
|
||||
{ type: "search", title: "Saved Search", selectable: false },
|
||||
{ type: "note-map", mime: '', title: "Note Map", selectable: false },
|
||||
|
||||
{ type: "text", mime: "text/html", title: "Text", selectable: true },
|
||||
{ type: "relation-map", mime: "application/json", title: "Relation Map", selectable: true },
|
||||
@@ -15,6 +16,8 @@ const NOTE_TYPES = [
|
||||
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
|
||||
];
|
||||
|
||||
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter(nt => !nt.selectable).map(nt => nt.type);
|
||||
|
||||
const TPL = `
|
||||
<div class="dropdown note-type-widget">
|
||||
<style>
|
||||
@@ -48,7 +51,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
|
||||
async refreshWithNote(note) {
|
||||
this.$noteTypeButton.prop("disabled",
|
||||
() => ["file", "image", "search"].includes(note.type));
|
||||
() => NOT_SELECTABLE_NOTE_TYPES.includes(note.type));
|
||||
|
||||
this.$noteTypeDesc.text(await this.findTypeTitle(note.type, note.mime));
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ const TPL = `
|
||||
<style>
|
||||
.note-update-status-widget {
|
||||
margin: 10px;
|
||||
contain: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ export default class InheritedAttributesWidget extends NoteContextAwareWidget {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.attributeDetailWidget = new AttributeDetailWidget().setParent(this);
|
||||
/** @type {AttributeDetailWidget} */
|
||||
this.attributeDetailWidget = new AttributeDetailWidget()
|
||||
.contentSized()
|
||||
.setParent(this);
|
||||
|
||||
this.child(this.attributeDetailWidget);
|
||||
}
|
||||
|
||||
@@ -65,18 +69,21 @@ export default class InheritedAttributesWidget extends NoteContextAwareWidget {
|
||||
|
||||
for (const attribute of inheritedAttributes) {
|
||||
const $attr = (await attributeRenderer.renderAttribute(attribute, false))
|
||||
.on('click', e => this.attributeDetailWidget.showAttributeDetail({
|
||||
attribute: {
|
||||
noteId: attribute.noteId,
|
||||
type: attribute.type,
|
||||
name: attribute.name,
|
||||
value: attribute.value,
|
||||
isInheritable: attribute.isInheritable
|
||||
},
|
||||
isOwned: false,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
}));
|
||||
.on('click', e => {
|
||||
setTimeout(() =>
|
||||
this.attributeDetailWidget.showAttributeDetail({
|
||||
attribute: {
|
||||
noteId: attribute.noteId,
|
||||
type: attribute.type,
|
||||
name: attribute.name,
|
||||
value: attribute.value,
|
||||
isInheritable: attribute.isInheritable
|
||||
},
|
||||
isOwned: false,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
}), 100);
|
||||
});
|
||||
|
||||
this.$container
|
||||
.append($attr)
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
this.$notePathList.empty();
|
||||
|
||||
if (this.noteId === 'root') {
|
||||
await this.addPath('root');
|
||||
await this.getRenderedPath('root');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,22 +83,18 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
this.$notePathIntro.text("This note is not yet placed into the note tree.");
|
||||
}
|
||||
|
||||
const printedNotePaths = new Set();
|
||||
const renderedPaths = [];
|
||||
|
||||
for (const notePathRecord of sortedNotePaths) {
|
||||
const notePath = notePathRecord.notePath.join('/');
|
||||
|
||||
if (printedNotePaths.has(notePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printedNotePaths.add(notePath);
|
||||
|
||||
await this.addPath(notePath, notePathRecord);
|
||||
renderedPaths.push(await this.getRenderedPath(notePath, notePathRecord));
|
||||
}
|
||||
|
||||
this.$notePathList.empty().append(...renderedPaths);
|
||||
}
|
||||
|
||||
async addPath(notePath, notePathRecord) {
|
||||
async getRenderedPath(notePath, notePathRecord) {
|
||||
const title = await treeService.getNotePathTitle(notePath);
|
||||
|
||||
const $noteLink = await linkService.createNoteLink(notePath, {title});
|
||||
@@ -136,7 +132,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
$noteLink.append(` ${icons.join(' ')}`);
|
||||
}
|
||||
|
||||
this.$notePathList.append($("<li>").append($noteLink));
|
||||
return $("<li>").append($noteLink);
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({loadResults}) {
|
||||
|
||||
@@ -297,9 +297,6 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
entitiesReloadedEvent({loadResults}) {
|
||||
if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) {
|
||||
this.refresh();
|
||||
|
||||
this.getTitle(this.note);
|
||||
this.triggerCommand('refreshRibbonContainer');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +226,8 @@ div.ui-tooltip {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.CodeMirror * {
|
||||
font-family: var(--monospace-font-family) !important;
|
||||
body .CodeMirror {
|
||||
font-size: var(--monospace-font-size);
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
@@ -950,3 +950,9 @@ input {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.note-split {
|
||||
flex-basis: 0; /* so that each split has same width */
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@@ -125,11 +125,13 @@ function updateNoteAttributes(req) {
|
||||
for (const incAttr of incomingAttributes) {
|
||||
position += 10;
|
||||
|
||||
const value = incAttr.value || "";
|
||||
|
||||
const perfectMatchAttr = existingAttrs.find(attr =>
|
||||
attr.type === incAttr.type &&
|
||||
attr.name === incAttr.name &&
|
||||
attr.isInheritable === incAttr.isInheritable &&
|
||||
attr.value === incAttr.value);
|
||||
attr.value === value);
|
||||
|
||||
if (perfectMatchAttr) {
|
||||
existingAttrs = existingAttrs.filter(attr => attr.attributeId !== perfectMatchAttr.attributeId);
|
||||
@@ -145,7 +147,7 @@ function updateNoteAttributes(req) {
|
||||
if (incAttr.type === 'relation') {
|
||||
const targetNote = becca.getNote(incAttr.value);
|
||||
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
if (!targetNote) {
|
||||
log.error(`Target note of relation ${JSON.stringify(incAttr)} does not exist or is deleted`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ const htmlSanitizer = require('../../services/html_sanitizer');
|
||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
||||
|
||||
function findClippingNote(todayNote, pageUrl) {
|
||||
const notes = todayNote.searchNoteInSubtree(
|
||||
const notes = todayNote.searchNotesInSubtree(
|
||||
formatAttrForSearch({
|
||||
type: 'label',
|
||||
name: "pageUrl",
|
||||
@@ -59,6 +59,7 @@ function addClipping(req) {
|
||||
|
||||
clippingNote.setLabel('clipType', 'clippings');
|
||||
clippingNote.setLabel('pageUrl', pageUrl);
|
||||
clippingNote.setLabel('iconClass', 'bx bx-globe');
|
||||
}
|
||||
|
||||
const rewrittenContent = processContent(images, clippingNote, content);
|
||||
@@ -92,6 +93,7 @@ function createNote(req) {
|
||||
|
||||
if (pageUrl) {
|
||||
note.setLabel('pageUrl', pageUrl);
|
||||
note.setLabel('iconClass', 'bx bx-globe');
|
||||
}
|
||||
|
||||
const rewrittenContent = processContent(images, note, content);
|
||||
|
||||
@@ -27,7 +27,7 @@ function getFontCss(req, res) {
|
||||
style += `--main-font-size: ${optionsMap.mainFontSize}%;`;
|
||||
style += `--tree-font-size: ${optionsMap.treeFontSize}%;`;
|
||||
style += `--detail-font-size: ${optionsMap.detailFontSize}%;`;
|
||||
style += `--monospace-font-size: ${optionsMap.monospaceFontSize};`;
|
||||
style += `--monospace-font-size: ${optionsMap.monospaceFontSize}%;`;
|
||||
|
||||
style += '}';
|
||||
|
||||
|
||||
@@ -24,23 +24,54 @@ function buildDescendantCountMap() {
|
||||
return noteIdToCountMap;
|
||||
}
|
||||
|
||||
function getNeighbors(note, depth) {
|
||||
if (depth === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const retNoteIds = [];
|
||||
|
||||
for (const relation of note.getRelations()) {
|
||||
if (['relationMapLink', 'template', 'image'].includes(relation.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetNote = relation.getTargetNote();
|
||||
retNoteIds.push(targetNote.noteId);
|
||||
|
||||
for (const noteId of getNeighbors(targetNote, depth - 1)) {
|
||||
retNoteIds.push(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
return retNoteIds;
|
||||
}
|
||||
|
||||
function getLinkMap(req) {
|
||||
const mapRootNote = becca.getNote(req.params.noteId);
|
||||
// if the map root itself has ignore (journal typically) then there wouldn't be anything to display so
|
||||
// we'll just ignore it
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap');
|
||||
|
||||
const noteIds = new Set();
|
||||
const noteIds = new Set(
|
||||
mapRootNote.getSubtreeNotes(false)
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.map(note => note.noteId)
|
||||
);
|
||||
|
||||
const notes = mapRootNote.getSubtreeNotes(false)
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.map(note => [
|
||||
for (const noteId of getNeighbors(mapRootNote, 3)) {
|
||||
noteIds.add(noteId);
|
||||
}
|
||||
|
||||
const notes = Array.from(noteIds).map(noteId => {
|
||||
const note = becca.getNote(noteId);
|
||||
|
||||
return [
|
||||
note.noteId,
|
||||
note.isContentAvailable() ? note.title : '[protected]',
|
||||
note.type
|
||||
]);
|
||||
|
||||
notes.forEach(([noteId]) => noteIds.add(noteId));
|
||||
];
|
||||
});
|
||||
|
||||
const links = Object.values(becca.attributes).filter(rel => {
|
||||
if (rel.type !== 'relation' || rel.name === 'relationMapLink' || rel.name === 'template') {
|
||||
|
||||
@@ -54,6 +54,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'dailyBackupEnabled',
|
||||
'weeklyBackupEnabled',
|
||||
'monthlyBackupEnabled',
|
||||
'maxContentWidth'
|
||||
]);
|
||||
|
||||
function getOptions() {
|
||||
|
||||
@@ -5,11 +5,11 @@ const attributeService = require('../../services/attributes');
|
||||
const becca = require('../../becca/becca');
|
||||
const syncService = require('../../services/sync');
|
||||
|
||||
function exec(req) {
|
||||
async function exec(req) {
|
||||
try {
|
||||
const {body} = req;
|
||||
|
||||
const result = scriptService.executeScript(
|
||||
const result = await scriptService.executeScript(
|
||||
body.script,
|
||||
body.params,
|
||||
body.startNoteId,
|
||||
|
||||
@@ -247,6 +247,12 @@ function getRelatedNotes(req) {
|
||||
|
||||
const allResults = matchingNameAndValue.concat(matchingName);
|
||||
|
||||
const allResultNoteIds = new Set();
|
||||
|
||||
for (const record of allResults) {
|
||||
allResultNoteIds.add(record.noteId);
|
||||
}
|
||||
|
||||
for (const record of allResults) {
|
||||
if (results.length >= 20) {
|
||||
break;
|
||||
@@ -260,7 +266,7 @@ function getRelatedNotes(req) {
|
||||
}
|
||||
|
||||
return {
|
||||
count: allResults.length,
|
||||
count: allResultNoteIds.size,
|
||||
results
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ function getDateNote(req) {
|
||||
return dateNoteService.getDateNote(req.params.date);
|
||||
}
|
||||
|
||||
function getWeekNote(req) {
|
||||
return dateNoteService.getWeekNote(req.params.date);
|
||||
}
|
||||
|
||||
function getMonthNote(req) {
|
||||
return dateNoteService.getMonthNote(req.params.month);
|
||||
}
|
||||
@@ -65,6 +69,7 @@ function getHoistedNote() {
|
||||
module.exports = {
|
||||
getInboxNote,
|
||||
getDateNote,
|
||||
getWeekNote,
|
||||
getMonthNote,
|
||||
getYearNote,
|
||||
getDateNotesForMonth,
|
||||
|
||||
@@ -7,12 +7,15 @@ const config = require('../services/config');
|
||||
const optionService = require('../services/options');
|
||||
const log = require('../services/log');
|
||||
const env = require('../services/env');
|
||||
const utils = require('../services/utils');
|
||||
const protectedSessionService = require("../services/protected_session");
|
||||
|
||||
function index(req, res) {
|
||||
const options = optionService.getOptionsMap();
|
||||
|
||||
let view = req.cookies['trilium-device'] === 'mobile' ? 'mobile' : 'desktop';
|
||||
let view = (!utils.isElectron() && req.cookies['trilium-device'] === 'mobile')
|
||||
? 'mobile'
|
||||
: 'desktop';
|
||||
|
||||
const csrfToken = req.csrfToken();
|
||||
log.info(`Generated CSRF token ${csrfToken} with secret ${res.getHeader('set-cookie')}`);
|
||||
@@ -32,7 +35,8 @@ function index(req, res) {
|
||||
isDev: env.isDev(),
|
||||
isMainWindow: !req.query.extra,
|
||||
extraHoistedNoteId: req.query.extraHoistedNoteId,
|
||||
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable()
|
||||
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable(),
|
||||
maxContentWidth: parseInt(options.maxContentWidth)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ function register(app) {
|
||||
|
||||
apiRoute(GET, '/api/special-notes/inbox/:date', specialNotesRoute.getInboxNote);
|
||||
apiRoute(GET, '/api/special-notes/date/:date', specialNotesRoute.getDateNote);
|
||||
apiRoute(GET, '/api/special-notes/week/:date', specialNotesRoute.getWeekNote);
|
||||
apiRoute(GET, '/api/special-notes/month/:month', specialNotesRoute.getMonthNote);
|
||||
apiRoute(GET, '/api/special-notes/year/:year', specialNotesRoute.getYearNote);
|
||||
apiRoute(GET, '/api/special-notes/notes-for-month/:month', specialNotesRoute.getDateNotesForMonth);
|
||||
|
||||
@@ -58,21 +58,21 @@ function BackendScriptApi(currentNote, apiParams) {
|
||||
* @param {string} noteId
|
||||
* @returns {Note|null}
|
||||
*/
|
||||
this.getNote = becca.getNote;
|
||||
this.getNote = noteId => becca.getNote(noteId);
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @param {string} branchId
|
||||
* @returns {Branch|null}
|
||||
*/
|
||||
this.getBranch = becca.getBranch;
|
||||
this.getBranch = branchId => becca.getBranch(branchId);
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @param {string} attributeId
|
||||
* @returns {Attribute|null}
|
||||
*/
|
||||
this.getAttribute = becca.getAttribute;
|
||||
this.getAttribute = attributeId => becca.getAttribute(attributeId);
|
||||
|
||||
/**
|
||||
* This is a powerful search method - you can search by attributes and their values, e.g.:
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2021-10-12T22:42:48+02:00", buildRevision: "64a3a8b561f614a1940551bb3feb9e035f7cb475" };
|
||||
module.exports = { buildDate:"2021-11-01T09:19:28+01:00", buildRevision: "930d29d64adb90707ad8854739a4628b02765bc5" };
|
||||
|
||||
@@ -34,7 +34,7 @@ function getNoteStartingWith(parentNoteId, startsWith) {
|
||||
return becca.getNote(noteId);
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
function getRootCalendarNote() {
|
||||
let rootNote = attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
|
||||
|
||||
@@ -57,7 +57,7 @@ function getRootCalendarNote() {
|
||||
return rootNote;
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
function getYearNote(dateStr, rootNote) {
|
||||
if (!rootNote) {
|
||||
rootNote = getRootCalendarNote();
|
||||
@@ -97,7 +97,7 @@ function getMonthNoteTitle(rootNote, monthNumber, dateObj) {
|
||||
.replace(/{month}/g, monthName);
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
function getMonthNote(dateStr, rootNote) {
|
||||
if (!rootNote) {
|
||||
rootNote = getRootCalendarNote();
|
||||
@@ -152,7 +152,7 @@ function getDateNoteTitle(rootNote, dayNumber, dateObj) {
|
||||
.replace(/{weekDay2}/g, weekDay.substr(0, 2));
|
||||
}
|
||||
|
||||
/** @return {Note} */
|
||||
/** @returns {Note} */
|
||||
function getDateNote(dateStr) {
|
||||
let dateNote = attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
|
||||
|
||||
|
||||
@@ -27,6 +27,19 @@ function subscribe(eventTypes, listener) {
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeBeccaLoader(eventTypes, listener) {
|
||||
if (!Array.isArray(eventTypes)) {
|
||||
eventTypes = [ eventTypes ];
|
||||
}
|
||||
|
||||
for (const eventType of eventTypes) {
|
||||
eventListeners[eventType] = eventListeners[eventType] || [];
|
||||
// becca loader should be the first listener so that other listeners can already work
|
||||
// with updated becca
|
||||
eventListeners[eventType] = [listener, ...eventListeners[eventType]];
|
||||
}
|
||||
}
|
||||
|
||||
function emit(eventType, data) {
|
||||
const listeners = eventListeners[eventType];
|
||||
|
||||
@@ -45,6 +58,7 @@ function emit(eventType, data) {
|
||||
|
||||
module.exports = {
|
||||
subscribe,
|
||||
subscribeBeccaLoader,
|
||||
emit,
|
||||
// event types:
|
||||
NOTE_TITLE_CHANGED,
|
||||
|
||||
@@ -138,7 +138,8 @@ eventService.subscribe(eventService.ENTITY_CHANGED, ({ entityName, entity }) =>
|
||||
isInheritable: entity.isInheritable
|
||||
}).save();
|
||||
|
||||
targetNote.invalidateAttributeCache();
|
||||
// becca will not be updated before we'll check from the other side which would create infinite relation creation (#2269)
|
||||
targetNote.invalidateThisCache();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -150,9 +151,6 @@ eventService.subscribe(eventService.ENTITY_DELETED, ({ entityName, entity }) =>
|
||||
|
||||
for (const relation of relations) {
|
||||
if (relation.value === note.noteId) {
|
||||
note.invalidateAttributeCache();
|
||||
targetNote.invalidateAttributeCache();
|
||||
|
||||
relation.markAsDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {string} path without leading or trailing slash and backslashes converted to forward ones*/
|
||||
/** @returns {string} path without leading or trailing slash and backslashes converted to forward ones*/
|
||||
function normalizeFilePath(filePath) {
|
||||
filePath = filePath.replace(/\\/g, "/");
|
||||
|
||||
|
||||
@@ -89,7 +89,8 @@ const defaultOptions = [
|
||||
{ name: 'autoReadonlySizeCode', value: '30000', isSynced: false },
|
||||
{ name: 'dailyBackupEnabled', value: 'true', isSynced: false },
|
||||
{ name: 'weeklyBackupEnabled', value: 'true', isSynced: false },
|
||||
{ name: 'monthlyBackupEnabled', value: 'true', isSynced: false }
|
||||
{ name: 'monthlyBackupEnabled', value: 'true', isSynced: false },
|
||||
{ name: 'maxContentWidth', value: '1200', isSynced: false },
|
||||
];
|
||||
|
||||
function initStartupOptions() {
|
||||
|
||||
@@ -20,7 +20,7 @@ function parse(value) {
|
||||
else if (token.startsWith('inverse')) {
|
||||
const chunks = token.split('=');
|
||||
|
||||
defObj.inverseRelation = chunks[1];
|
||||
defObj.inverseRelation = chunks[1].replace(/[^\p{L}\p{N}_:]/ug, "")
|
||||
}
|
||||
else {
|
||||
console.log("Unrecognized attribute definition token:", token);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
const lex = require('./lex');
|
||||
const handleParens = require('./handle_parens');
|
||||
const parse = require('./parse');
|
||||
const NoteSet = require("../note_set");
|
||||
const SearchResult = require("../search_result");
|
||||
const SearchContext = require("../search_context");
|
||||
const becca = require('../../../becca/becca');
|
||||
|
||||
@@ -137,7 +137,15 @@ function saveSqlConsole(sqlConsoleNoteId) {
|
||||
attributeService.getNoteWithLabel('sqlConsoleHome')
|
||||
|| dateNoteService.getDateNote(today);
|
||||
|
||||
return sqlConsoleNote.cloneTo(sqlConsoleHome.noteId);
|
||||
const result = sqlConsoleNote.cloneTo(sqlConsoleHome.noteId);
|
||||
|
||||
for (const parentBranch of sqlConsoleNote.getParentBranches()) {
|
||||
if (parentBranch.parentNote.hasAncestor("hidden")) {
|
||||
parentBranch.markAsDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function createSearchNote(searchString, ancestorNoteId) {
|
||||
@@ -158,25 +166,34 @@ function createSearchNote(searchString, ancestorNoteId) {
|
||||
return note;
|
||||
}
|
||||
|
||||
function saveSearchNote(searchNoteId) {
|
||||
const searchNote = becca.getNote(searchNoteId);
|
||||
|
||||
function getSearchHome() {
|
||||
const hoistedNote = getHoistedNote();
|
||||
let searchHome;
|
||||
|
||||
if (!hoistedNote.isRoot()) {
|
||||
searchHome = hoistedNote.searchNoteInSubtree('#hoistedSearchHome')
|
||||
return hoistedNote.searchNoteInSubtree('#hoistedSearchHome')
|
||||
|| hoistedNote.searchNoteInSubtree('#searchHome')
|
||||
|| hoistedNote;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
const today = dateUtils.localNowDate();
|
||||
|
||||
searchHome = hoistedNote.searchNoteInSubtree('#searchHome')
|
||||
return hoistedNote.searchNoteInSubtree('#searchHome')
|
||||
|| dateNoteService.getDateNote(today);
|
||||
}
|
||||
}
|
||||
|
||||
return searchNote.cloneTo(searchHome.noteId);
|
||||
function saveSearchNote(searchNoteId) {
|
||||
const searchNote = becca.getNote(searchNoteId);
|
||||
const searchHome = getSearchHome();
|
||||
|
||||
const result = searchNote.cloneTo(searchHome.noteId);
|
||||
|
||||
for (const parentBranch of searchNote.getParentBranches()) {
|
||||
if (parentBranch.parentNote.hasAncestor("hidden")) {
|
||||
parentBranch.markAsDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getHoistedNote() {
|
||||
|
||||
@@ -22,7 +22,7 @@ class TaskContext {
|
||||
this.increaseProgressCount();
|
||||
}
|
||||
|
||||
/** @return {TaskContext} */
|
||||
/** @returns {TaskContext} */
|
||||
static getInstance(taskId, taskType, data) {
|
||||
if (!taskContexts[taskId]) {
|
||||
taskContexts[taskId] = new TaskContext(taskId, taskType, data);
|
||||
|
||||
@@ -298,6 +298,10 @@ function normalize(str) {
|
||||
return removeDiacritic(str).toLowerCase();
|
||||
}
|
||||
|
||||
function filterAttributeName(name) {
|
||||
return name.replace(/[^\p{L}\p{N}_:]/ug, "");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
randomSecureToken,
|
||||
randomString,
|
||||
@@ -331,5 +335,6 @@ module.exports = {
|
||||
timeLimit,
|
||||
deferred,
|
||||
removeDiacritic,
|
||||
normalize
|
||||
normalize,
|
||||
filterAttributeName
|
||||
};
|
||||
|
||||
@@ -58,6 +58,12 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.note-split {
|
||||
max-width: <%= maxContentWidth %>px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Required for correct loading of scripts in Electron -->
|
||||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<div id="note-revisions-dialog" class="modal fade mx-auto" tabindex="-1" role="dialog">
|
||||
<style>
|
||||
#note-revision-content-wrapper {
|
||||
flex-grow: 1;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#note-revision-content {
|
||||
overflow: auto;
|
||||
word-break: break-word;
|
||||
@@ -9,6 +17,12 @@
|
||||
max-width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#note-revision-content pre {
|
||||
max-width: 100%;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modal-dialog modal-xl" role="document">
|
||||
@@ -34,7 +48,7 @@
|
||||
<div id="note-revision-list" style="position: static; height: 100%; overflow: auto;" class="dropdown-menu"></div>
|
||||
</div>
|
||||
|
||||
<div id="note-revision-content-wrapper" style="flex-grow: 1; margin-left: 20px; display: flex; flex-direction: column;">
|
||||
<div id="note-revision-content-wrapper">
|
||||
<div style="flex-grow: 0; display: flex; justify-content: space-between;">
|
||||
<h3 id="note-revision-title" style="margin: 3px; flex-grow: 100;"></h3>
|
||||
|
||||
|
||||
@@ -134,12 +134,12 @@
|
||||
<p>This setup needs to be initiated from the desktop instance:</p>
|
||||
|
||||
<ol>
|
||||
<li>please open your desktop instance of Trilium Notes</li>
|
||||
<li>click on Options button in the top right</li>
|
||||
<li>click on Sync tab</li>
|
||||
<li>configure server instance address to the: <span id="current-host"></span> and click save.</li>
|
||||
<li>click on "Test sync" button</li>
|
||||
<li>once you've done all this, click <a href="/">here</a></li>
|
||||
<li>Open your desktop instance of Trilium Notes.</li>
|
||||
<li>From the Trilium Menu, click Options.</li>
|
||||
<li>Click on Sync tab.</li>
|
||||
<li>Change server instance address to: <span id="current-host"></span> and click save.</li>
|
||||
<li>Click "Test sync" button to verify connection is successfull.</li>
|
||||
<li>Once you've completed these steps, click <a href="/">here</a>.</li>
|
||||
</ol>
|
||||
|
||||
<button type="button" data-bind="click: back" class="btn btn-secondary">Back</button>
|
||||
|
||||
Reference in New Issue
Block a user