Compare commits

...

72 Commits

Author SHA1 Message Date
zadam
41d47c5d33 release 0.36.1-beta 2019-10-21 21:59:17 +02:00
zadam
132360b46b expose note hoisting to frontend API, closes #663 2019-10-21 21:22:53 +02:00
zadam
f0496cb42c generating simple frame based index page in html export 2019-10-21 21:00:27 +02:00
zadam
1522297700 generate navigation file in the tar export 2019-10-20 19:02:48 +02:00
zadam
2a5ab3a5e1 script can wait until the sync data has been applied 2019-10-20 17:49:58 +02:00
zadam
358fd13c8d small refactorings 2019-10-20 13:09:00 +02:00
zadam
2305ad7405 reloading notes after script changes 2019-10-20 12:29:34 +02:00
zadam
78f5b7b288 rename info service to toast service 2019-10-20 10:00:18 +02:00
zadam
1903c59163 scripts should have different sourceId 2019-10-19 15:12:25 +02:00
zadam
b16c2d19b6 duplicate (single) note 2019-10-19 12:36:16 +02:00
zadam
00bb1236ce protect/unprotect tree reports progress via notifications 2019-10-19 09:58:18 +02:00
zadam
82bbf4173b fixes to delete notifications 2019-10-19 00:11:07 +02:00
zadam
9f4ca279aa delete progress 2019-10-18 23:19:16 +02:00
zadam
b890859025 further refactoring of export notifications 2019-10-18 22:44:03 +02:00
zadam
56e4f4f5ac refactor exportcontext to taskcontext 2019-10-18 22:27:38 +02:00
zadam
157bd3816d add hideTabRowForOneTab if missing, fixes #660 2019-10-18 21:04:20 +02:00
zadam
21588829c7 correctly use taskId in toasts 2019-10-17 21:15:27 +02:00
zadam
9689029c4b ImportContext generalized to TaskContext 2019-10-17 21:11:35 +02:00
zadam
992d174b23 import uses persistent toasts 2019-10-17 20:44:51 +02:00
zadam
8886e95847 replaced bootstrap-notify.min.js with bootstrap toasts 2019-10-17 20:03:05 +02:00
zadam
6d5762fac8 open update to 7.0 2019-10-16 19:42:42 +02:00
zadam
e9ab044e46 fix docker build 2019-10-15 21:53:46 +02:00
zadam
fda219d070 release 0.36.0-beta 2019-10-15 20:42:17 +02:00
zadam
2be1aca2f3 build fixes 2019-10-15 20:32:43 +02:00
zadam
1b318d6a30 fix tab cleanup 2019-10-15 19:42:39 +02:00
zadam
22c4859d42 grey out archived items in the tree 2019-10-15 19:16:44 +02:00
zadam
80a6361cf1 update to demo db 2019-10-14 12:11:27 +02:00
zadam
8439effeeb Merge remote-tracking branch 'origin/master' into master2 2019-10-14 12:06:18 +02:00
zadam
fafab95a07 auto-book has variable number of columns based on the right pane width 2019-10-14 12:06:10 +02:00
Johannes Wünsch
24c8e8fc2b fix gitpod (#658) 2019-10-14 11:55:12 +02:00
zadam
1923bf7dda added some basic book examples to demo document 2019-10-14 11:15:38 +02:00
zadam
2ee94a3a69 changed import progress notification so it shows up for drag & drop as well 2019-10-14 10:31:58 +02:00
zadam
2fb3a3eff9 force note sync will sync also note content 2019-10-11 21:24:49 +02:00
zadam
bcbbf4dc3e improvements to build process 2019-10-11 21:22:59 +02:00
zadam
7dc793920f fix spellcheck build on windows 2019-10-10 23:24:25 +02:00
zadam
0b43eceb2d Merge branch 'stable'
# Conflicts:
#	package-lock.json
#	package.json
2019-10-10 21:26:57 +02:00
zadam
85f736139b fix opening note revisions from the sidebar 2019-10-10 20:00:06 +02:00
zadam
98a6670cb4 prevent default context menu on tab right click, closes #656 2019-10-10 19:34:55 +02:00
zadam
a15be82f00 release 0.35.2 2019-10-09 23:09:38 +02:00
zadam
6c3809e1db calendar widget styling for dark themes
(cherry picked from commit c9432990b7)
2019-10-09 23:07:15 +02:00
zadam
33a2cd21a3 fix double import of auto generated link relations
(cherry picked from commit 144e75da9e)
2019-10-09 23:05:52 +02:00
zadam
3eebce22e7 fix incorrect import of relations from tar
(cherry picked from commit 8d14a0d687)
2019-10-09 23:05:51 +02:00
zadam
adae0625b9 rename var so it's not misleading
(cherry picked from commit dd147a7209)
2019-10-09 23:05:14 +02:00
Arne
5063cfb979 BackendAPI: Return Note created within createNoteAndRefresh (#647)
(cherry picked from commit 334a38c493)
2019-10-09 23:05:14 +02:00
zadam
0835930a8a fix creating child in relation map, closes #655 2019-10-09 23:04:01 +02:00
zadam
c9432990b7 calendar widget styling for dark themes 2019-10-08 20:25:54 +02:00
zadam
9ad521822d mac spellchecker build 2019-10-07 22:17:22 +02:00
zadam
824fb08511 Merge remote-tracking branch 'origin/master' 2019-10-07 20:58:58 +02:00
zadam
cc4c15daf0 spellchecker binaries for windows 2019-10-07 20:58:48 +02:00
Logan Gorence
7718778013 Fix typo in note export screen. (#654) 2019-10-07 08:49:53 +02:00
zadam
a25260353d spellcheck binaries for linux-x64 2019-10-06 22:33:19 +02:00
zadam
c1e8a4b384 spell check support + small options tabs reorganization 2019-10-06 21:35:26 +02:00
zadam
3f2229d9e1 better handling of not detected mime type 2019-10-06 18:28:53 +02:00
FliegendeWurst
8561227622 Import: use upload mime type if mime cannot be detected from filename (#651) 2019-10-06 18:23:39 +02:00
zadam
8859e2ac40 fix opening links from book 2019-10-06 12:33:47 +02:00
zadam
7423b2f4fd book handling of protected notes 2019-10-06 11:21:12 +02:00
zadam
d23e9f1bc4 tar import will sort notes if there is no meta file 2019-10-06 09:49:47 +02:00
zadam
516277a478 added "auto book" displayed on the empty text pages as a replacement for children overview 2019-10-05 20:27:30 +02:00
zadam
cbc7710d81 removed children overview (will be replaced by book) 2019-10-05 12:06:06 +02:00
zadam
ea71e96f72 ability to set book zoom level via label 2019-10-05 12:01:00 +02:00
zadam
59d1cb1833 expand all children button 2019-10-05 11:22:42 +02:00
zadam
7c54ba63ce render notes in book 2019-10-05 10:55:29 +02:00
zadam
5892b5b851 nested note rendering in book 2019-10-05 09:33:31 +02:00
zadam
02eb737b9d book has now zoom 2019-10-04 22:21:14 +02:00
zadam
144e75da9e fix double import of auto generated link relations 2019-10-02 23:28:29 +02:00
zadam
8d14a0d687 fix incorrect import of relations from tar 2019-10-02 23:22:58 +02:00
zadam
dec2c218f7 basic book rendering of code and image notes 2019-10-02 19:40:22 +02:00
zadam
dd147a7209 rename var so it's not misleading 2019-10-01 21:42:36 +02:00
zadam
c3fabcb666 Merge remote-tracking branch 'origin/master' 2019-10-01 21:41:26 +02:00
zadam
35e825b376 fix tooltips 2019-10-01 21:41:20 +02:00
Arne
334a38c493 BackendAPI: Return Note created within createNoteAndRefresh (#647) 2019-10-01 21:40:57 +02:00
zadam
8ec01c73cd skeleton implementation of new "book" note type 2019-10-01 21:11:11 +02:00
151 changed files with 4537 additions and 3800 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ yarn-error.log
config.ini
cert.key
cert.crt
server-package.json

15
.gitpod.dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM gitpod/workspace-full
RUN sudo apt-get update \
&& sudo apt-get install -yq --no-install-recommends \
libpng16-16 \
libpng-dev \
pkg-config \
autoconf \
libtool \
build-essential \
nasm \
libx11-dev \
libxkbfile-dev \
&& sudo rm -rf /var/lib/apt/lists/*

View File

@@ -1,7 +1,11 @@
image:
file: .gitpod.dockerfile
tasks:
- before: nvm install 10 && nvm use 10
init: npm install
command: npm run start
command: npm run start-server
ports:
- port: 8080
onOpen: open-preview

View File

@@ -57,7 +57,6 @@
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>apiTokenId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="25" parent="6">
@@ -126,25 +125,20 @@
<index id="37" parent="7" name="sqlite_autoindex_attributes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>attributeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="38" parent="7" name="IDX_attributes_noteId_index">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="39" parent="7" name="IDX_attributes_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="40" parent="7" name="IDX_attributes_name_index">
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="41" parent="7" name="IDX_attributes_value_index">
<ColNames>value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="42" parent="7">
<ColNames>attributeId</ColNames>
@@ -204,21 +198,17 @@ value</ColNames>
<index id="53" parent="8" name="sqlite_autoindex_branches_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="54" parent="8" name="IDX_branches_noteId_parentNoteId">
<ColNames>noteId
parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="55" parent="8" name="IDX_branches_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="56" parent="8" name="IDX_branches_parentNoteId">
<ColNames>parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="57" parent="8">
<ColNames>branchId</ColNames>
@@ -246,7 +236,6 @@ parentNoteId</ColNames>
<index id="62" parent="9" name="sqlite_autoindex_event_log_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>eventId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="63" parent="9">
@@ -278,7 +267,6 @@ parentNoteId</ColNames>
<index id="68" parent="10" name="sqlite_autoindex_note_contents_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="69" parent="10">
@@ -351,20 +339,16 @@ parentNoteId</ColNames>
<index id="82" parent="11" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="83" parent="11" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="84" parent="11" name="IDX_note_revisions_dateModifiedFrom">
<ColNames>utcDateModifiedFrom</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="85" parent="11" name="IDX_note_revisions_dateModifiedTo">
<ColNames>utcDateModifiedTo</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="86" parent="11">
<ColNames>noteRevisionId</ColNames>
@@ -435,7 +419,6 @@ parentNoteId</ColNames>
<index id="98" parent="12" name="sqlite_autoindex_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="99" parent="12">
@@ -477,7 +460,6 @@ parentNoteId</ColNames>
<index id="106" parent="13" name="sqlite_autoindex_options_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="107" parent="13">
@@ -513,7 +495,6 @@ parentNoteId</ColNames>
<index id="113" parent="14" name="sqlite_autoindex_recent_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="114" parent="14">
@@ -534,7 +515,6 @@ parentNoteId</ColNames>
<index id="117" parent="15" name="sqlite_autoindex_source_ids_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>sourceId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="118" parent="15">
@@ -597,12 +577,10 @@ parentNoteId</ColNames>
<index id="131" parent="18" name="IDX_sync_entityName_entityId">
<ColNames>entityName
entityId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="132" parent="18" name="IDX_sync_utcSyncDate">
<ColNames>utcSyncDate</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="133" parent="18">
<ColNames>id</ColNames>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{@types/jquery}" />
</component>
</project>

View File

@@ -1,11 +1,9 @@
FROM node:12.10.0-alpine
FROM node:12.12.0-alpine
# Create app directory
WORKDIR /usr/src/app
# Copy both package.json and package-lock.json
# where available (npm@5+)
COPY package.json package-lock.json ./
COPY server-package.json package.json
# Install app dependencies
RUN set -x \

View File

@@ -46,5 +46,5 @@ Use a browser based dev environment
Or clone locally and run
```
npm install
npm run start
npm run start-server
```

View File

@@ -3,6 +3,8 @@
VERSION=`jq -r ".version" package.json`
SERIES=${VERSION:0:4}-latest
cat package.json | grep -v electron > server-package.json
sudo docker build -t zadam/trilium:$VERSION -t zadam/trilium:$SERIES .
if [[ $VERSION != *"beta"* ]]; then

View File

@@ -1,20 +1,26 @@
#!/usr/bin/env bash
BUILD_DIR=./dist/trilium-linux-x64
rm -rf $BUILD_DIR
SRC_DIR=./dist/trilium-linux-x64-src
if [ "$1" != "DONTCOPY" ]
then
./bin/copy-trilium.sh $SRC_DIR
fi
echo "Copying required linux-x64 binaries"
rm -r node_modules/sqlite3/lib/binding/*
rm -r node_modules/pngquant-bin/vendor/*
rm -r $SRC_DIR/node_modules/sqlite3/lib/binding/*
rm -r $SRC_DIR/node_modules/pngquant-bin/vendor/*
rm -r $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/*
cp -r bin/deps/linux-x64/sqlite/* node_modules/sqlite3/lib/binding/
cp bin/deps/linux-x64/image/pngquant node_modules/pngquant-bin/vendor/
cp -r bin/deps/linux-x64/sqlite/* $SRC_DIR/node_modules/sqlite3/lib/binding/
cp bin/deps/linux-x64/image/pngquant $SRC_DIR/node_modules/pngquant-bin/vendor/
cp bin/deps/linux-x64/spellchecker/* $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/
# rebuild binaries for image operations (pngquant ...)
npm rebuild
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
BUILD_DIR=./dist/trilium-linux-x64
rm -rf $BUILD_DIR
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
@@ -29,3 +35,7 @@ VERSION=`jq -r ".version" package.json`
cd dist
tar cJf trilium-linux-x64-${VERSION}.tar.xz trilium-linux-x64
cd ..
bin/build-debian.sh

View File

@@ -1,27 +1,36 @@
#!/usr/bin/env bash
SRC_DIR=./dist/trilium-mac-x64-src
if [ "$1" != "DONTCOPY" ]
then
./bin/copy-trilium.sh $SRC_DIR
fi
echo "Copying required mac binaries"
rm -r $SRC_DIR/node_modules/sqlite3/lib/binding/*
rm -r $SRC_DIR/node_modules/mozjpeg/vendor/*
rm -r $SRC_DIR/node_modules/pngquant-bin/vendor/*
rm -r $SRC_DIR/node_modules/giflossy/vendor/*
rm -r $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/*
rm -r $SRC_DIR/node_modules/keyboard-layout/build/Release/*
cp -r bin/deps/mac-x64/sqlite/* $SRC_DIR/node_modules/sqlite3/lib/binding/
cp bin/deps/mac-x64/image/cjpeg $SRC_DIR/node_modules/mozjpeg/vendor/
cp bin/deps/mac-x64/image/pngquant $SRC_DIR/node_modules/pngquant-bin/vendor/
cp bin/deps/mac-x64/image/gifsicle $SRC_DIR/node_modules/giflossy/vendor/
cp bin/deps/mac-x64/spellchecker/* $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/
cp bin/deps/mac-x64/keyboard-layout-manager.node $SRC_DIR/node_modules/keyboard-layout/build/Release/
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=images/app-icons/mac/icon.icns
BUILD_DIR=./dist/trilium-mac-x64
rm -rf $BUILD_DIR
echo "Copying required mac binaries"
rm -r node_modules/sqlite3/lib/binding/*
rm -r node_modules/mozjpeg/vendor/*
rm -r node_modules/pngquant-bin/vendor/*
rm -r node_modules/giflossy/vendor/*
cp -r bin/deps/mac-x64/sqlite/* node_modules/sqlite3/lib/binding/
cp bin/deps/mac-x64/image/cjpeg node_modules/mozjpeg/vendor/
cp bin/deps/mac-x64/image/pngquant node_modules/pngquant-bin/vendor/
cp bin/deps/mac-x64/image/gifsicle node_modules/giflossy/vendor/
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=images/app-icons/mac/icon.icns
# Mac build has by default useless directory level
mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR
./bin/reset-local.sh
echo "Zipping mac x64 electron distribution..."
VERSION=`jq -r ".version" package.json`

View File

@@ -1,40 +1,32 @@
#!/usr/bin/env bash
PKG_DIR=dist/trilium-linux-x64-server
NODE_VERSION=12.10.0
NODE_VERSION=12.12.0
rm -r $PKG_DIR
mkdir $PKG_DIR
cd $PKG_DIR
if [ "$1" != "DONTCOPY" ]
then
./bin/copy-trilium.sh $PKG_DIR
fi
cd dist
wget https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz
tar xvfJ node-v${NODE_VERSION}-linux-x64.tar.xz
rm node-v${NODE_VERSION}-linux-x64.tar.xz
mv node-v${NODE_VERSION}-linux-x64 node
cp -r ../../node_modules/ ./
cp -r ../../images/ ./
cp -r ../../libraries/ ./
cp -r ../../src/ ./
cp -r ../../db/ ./
cp -r ../../package.json ./
cp -r ../../package-lock.json ./
cp -r ../../README.md ./
cp -r ../../LICENSE ./
cp -r ../../config-sample.ini ./
rm -r ./node_modules/electron*
rm -r ./node_modules/sqlite3/lib/binding/*
cp -r ../../bin/deps/linux-x64/sqlite/node* ./node_modules/sqlite3/lib/binding/
printf "#!/bin/sh\n./node/bin/node src/www" > trilium.sh
chmod 755 trilium.sh
cd ..
VERSION=`jq -r ".version" ../package.json`
mv dist/node-v${NODE_VERSION}-linux-x64 $PKG_DIR/node
rm -r $PKG_DIR/node_modules/electron*
rm -r $PKG_DIR/node_modules/sqlite3/lib/binding/*
cp -r ./bin/deps/linux-x64/sqlite/node* $PKG_DIR/node_modules/sqlite3/lib/binding/
printf "#!/bin/sh\n./node/bin/node src/www" > $PKG_DIR/trilium.sh
chmod 755 $PKG_DIR/trilium.sh
VERSION=`jq -r ".version" package.json`
cd dist
tar cJf trilium-linux-x64-server-${VERSION}.tar.xz trilium-linux-x64-server

View File

@@ -1,29 +1,38 @@
#!/usr/bin/env bash
BUILD_DIR=./dist/trilium-windows-x64
rm -rf $BUILD_DIR
SRC_DIR=./dist/trilium-windows-x64-src
if [ "$1" != "DONTCOPY" ]
then
./bin/copy-trilium.sh $SRC_DIR
fi
echo "Copying required windows binaries"
rm -r node_modules/sqlite3/lib/binding/*
rm -r node_modules/mozjpeg/vendor/*
rm -r node_modules/pngquant-bin/vendor/*
rm -r node_modules/giflossy/vendor/*
rm -r $SRC_DIR/node_modules/sqlite3/lib/binding/*
rm -r $SRC_DIR/node_modules/mozjpeg/vendor/*
rm -r $SRC_DIR/node_modules/pngquant-bin/vendor/*
rm -r $SRC_DIR/node_modules/giflossy/vendor/*
rm -r $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/*
rm -r $SRC_DIR/node_modules/keyboard-layout/build/Release/*
cp -r bin/deps/win-x64/sqlite/* node_modules/sqlite3/lib/binding/
cp bin/deps/win-x64/image/cjpeg.exe node_modules/mozjpeg/vendor/
cp bin/deps/win-x64/image/pngquant.exe node_modules/pngquant-bin/vendor/
cp bin/deps/win-x64/image/gifsicle.exe node_modules/giflossy/vendor/
cp -r bin/deps/win-x64/sqlite/* $SRC_DIR/node_modules/sqlite3/lib/binding/
cp bin/deps/win-x64/image/cjpeg.exe $SRC_DIR/node_modules/mozjpeg/vendor/
cp bin/deps/win-x64/image/pngquant.exe $SRC_DIR/node_modules/pngquant-bin/vendor/
cp bin/deps/win-x64/image/gifsicle.exe $SRC_DIR/node_modules/giflossy/vendor/
cp bin/deps/win-x64/spellchecker/* $SRC_DIR/node_modules/@felixrieseberg/spellchecker/build/Release/
cp bin/deps/win-x64/keyboard-layout-manager.node $SRC_DIR/node_modules/keyboard-layout/build/Release/
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=win32 --arch=x64 --overwrite --icon=images/app-icons/win/icon.ico
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=win32 --arch=x64 --overwrite --icon=images/app-icons/win/icon.ico
BUILD_DIR=./dist/trilium-windows-x64
rm -rf $BUILD_DIR
mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
# removing software WebGL binaries because they are pretty huge and not necessary
rm -r $BUILD_DIR/swiftshader
./bin/reset-local.sh
echo "Zipping windows x64 electron distribution..."
VERSION=`jq -r ".version" package.json`

View File

@@ -1,21 +1,23 @@
#!/usr/bin/env bash
rm -r node_modules
npm install
echo "Deleting existing builds"
rm -r dist/*
bin/build-win-x64.sh
SRC_DIR=dist/trilium-src
bin/build-mac-x64.sh
bin/copy-trilium.sh $SRC_DIR
# building X64 linux as the last so electron-rebuild will prepare X64 binaries for local development
bin/build-linux-x64.sh
# we'll just copy the same SRC dir to all the builds so we don't have to do npm install in each separately
cp -r $SRC_DIR ./dist/trilium-linux-x64-src
cp -r $SRC_DIR ./dist/trilium-linux-x64-server
cp -r $SRC_DIR ./dist/trilium-windows-x64-src
cp -r $SRC_DIR ./dist/trilium-mac-x64-src
# this needs to be run after linux build
bin/build-debian.sh
bin/build-win-x64.sh DONTCOPY
bin/build-server.sh
bin/build-mac-x64.sh DONTCOPY
bin/build-linux-x64.sh DONTCOPY
bin/build-server.sh DONTCOPY

32
bin/copy-trilium.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
if [[ $# -eq 0 ]] ; then
echo "Missing argument of target directory"
exit 1
fi
DIR=$1
rm -rf $DIR
mkdir $DIR
echo "Copying Trilium to build directory $DIR"
cp -r images $DIR/
cp -r libraries $DIR/
cp -r src $DIR/
cp -r db $DIR/
cp -r package.json $DIR/
cp -r package-lock.json $DIR/
cp -r README.md $DIR/
cp -r LICENSE $DIR/
cp -r config-sample.ini $DIR/
cp -r electron.js $DIR/
# run in subshell (so we return to original dir)
(cd $DIR && npm install --only=prod)
rm -r $DIR/node_modules/cld/deps
find $DIR/libraries -name "*.map" -type f -delete
find $DIR/libraries -name "hunspell.lib" -type f -delete

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env bash
./node_modules/.bin/electron-rebuild --arch=x64

Binary file not shown.

View File

@@ -0,0 +1,5 @@
INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
VALUES ('spellCheckEnabled', 'true', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0);
INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
VALUES ('spellCheckLanguageCode', 'en-US', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0);

View File

@@ -0,0 +1,3 @@
INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
SELECT 'hideTabRowForOneTab', 'false', '2019-05-01T18:31:00.874Z', '2019-05-01T18:31:00.874Z', 0
WHERE NOT EXISTS(SELECT 1 FROM options WHERE name = 'hideTabRowForOneTab');

View File

@@ -0,0 +1,22 @@
CREATE TABLE IF NOT EXISTS "mig_branches" (
`branchId` TEXT NOT NULL,
`noteId` TEXT NOT NULL,
`parentNoteId` TEXT NOT NULL,
`notePosition` INTEGER NOT NULL,
`prefix` TEXT,
`isExpanded` INTEGER NOT NULL DEFAULT 0,
`isDeleted` INTEGER NOT NULL DEFAULT 0,
`utcDateModified` TEXT NOT NULL,
utcDateCreated TEXT NOT NULL,
hash TEXT DEFAULT "" NOT NULL,
PRIMARY KEY(`branchId`));
INSERT INTO mig_branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash)
SELECT branchId, noteId, parentNoteId, notePosition, prefix, COALESCE(isExpanded, 0), isDeleted, utcDateModified, utcDateCreated, hash FROM branches;
DROP TABLE branches;
ALTER TABLE mig_branches RENAME TO branches;
CREATE INDEX `IDX_branches_noteId` ON `branches` (`noteId`);
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);

View File

@@ -0,0 +1,2 @@
UPDATE branches SET notePosition = notePosition * 10;
UPDATE attributes SET position = position * 10;

View File

@@ -29,25 +29,6 @@ CREATE TABLE IF NOT EXISTS "api_tokens"
utcDateCreated TEXT NOT NULL,
isDeleted INT NOT NULL DEFAULT 0,
hash TEXT DEFAULT "" NOT NULL);
CREATE TABLE IF NOT EXISTS "branches" (
`branchId` TEXT NOT NULL,
`noteId` TEXT NOT NULL,
`parentNoteId` TEXT NOT NULL,
`notePosition` INTEGER NOT NULL,
`prefix` TEXT,
`isExpanded` BOOLEAN,
`isDeleted` INTEGER NOT NULL DEFAULT 0,
`utcDateModified` TEXT NOT NULL,
utcDateCreated TEXT NOT NULL,
hash TEXT DEFAULT "" NOT NULL,
PRIMARY KEY(`branchId`)
);
CREATE TABLE IF NOT EXISTS "event_log" (
`eventId` TEXT NOT NULL PRIMARY KEY,
`noteId` TEXT,
`comment` TEXT,
`utcDateCreated` TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "options"
(
name TEXT not null PRIMARY KEY,
@@ -84,21 +65,6 @@ CREATE TABLE IF NOT EXISTS "notes" (
`utcDateModified` TEXT NOT NULL,
PRIMARY KEY(`noteId`)
);
CREATE TABLE IF NOT EXISTS "note_contents" (
`noteId` TEXT NOT NULL,
`content` TEXT NULL DEFAULT NULL,
`hash` TEXT DEFAULT "" NOT NULL,
`utcDateModified` TEXT NOT NULL,
PRIMARY KEY(`noteId`)
);
CREATE TABLE recent_notes
(
noteId TEXT not null primary key,
notePath TEXT not null,
hash TEXT default "" not null,
utcDateCreated TEXT not null,
isDeleted INT
);
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
`entityName`,
`entityId`
@@ -115,14 +81,6 @@ CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` (
CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` (
`utcDateModifiedTo`
);
CREATE INDEX `IDX_branches_noteId` ON `branches` (
`noteId`
);
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (
`noteId`,
`parentNoteId`
);
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
CREATE INDEX IDX_attributes_name_value
on attributes (name, value);
CREATE INDEX IDX_attributes_name_index
@@ -131,3 +89,33 @@ CREATE INDEX IDX_attributes_noteId_index
on attributes (noteId);
CREATE INDEX IDX_attributes_value_index
on attributes (value);
CREATE TABLE IF NOT EXISTS "note_contents" (
`noteId` TEXT NOT NULL,
`content` TEXT NULL DEFAULT NULL,
`hash` TEXT DEFAULT "" NOT NULL,
`utcDateModified` TEXT NOT NULL,
PRIMARY KEY(`noteId`)
);
CREATE TABLE recent_notes
(
noteId TEXT not null primary key,
notePath TEXT not null,
hash TEXT default "" not null,
utcDateCreated TEXT not null,
isDeleted INT
);
CREATE TABLE IF NOT EXISTS "branches" (
`branchId` TEXT NOT NULL,
`noteId` TEXT NOT NULL,
`parentNoteId` TEXT NOT NULL,
`notePosition` INTEGER NOT NULL,
`prefix` TEXT,
`isExpanded` INTEGER NOT NULL DEFAULT 0,
`isDeleted` INTEGER NOT NULL DEFAULT 0,
`utcDateModified` TEXT NOT NULL,
utcDateCreated TEXT NOT NULL,
hash TEXT DEFAULT "" NOT NULL,
PRIMARY KEY(`branchId`));
CREATE INDEX `IDX_branches_noteId` ON `branches` (`noteId`);
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);

View File

@@ -396,7 +396,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line310">line 310</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line312">line 312</a>
</li></ul></dd>
@@ -1533,7 +1533,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line315">line 315</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line317">line 317</a>
</li></ul></dd>
@@ -1997,7 +1997,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line239">line 239</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line241">line 241</a>
</li></ul></dd>
@@ -2765,7 +2765,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line258">line 258</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line260">line 260</a>
</li></ul></dd>
@@ -3418,7 +3418,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line230">line 230</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line232">line 232</a>
</li></ul></dd>
@@ -3596,7 +3596,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line249">line 249</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line251">line 251</a>
</li></ul></dd>
@@ -3751,7 +3751,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line267">line 267</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line269">line 269</a>
</li></ul></dd>
@@ -3901,7 +3901,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line222">line 222</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line224">line 224</a>
</li></ul></dd>
@@ -4427,7 +4427,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line287">line 287</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line289">line 289</a>
</li></ul></dd>
@@ -4560,7 +4560,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line274">line 274</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line276">line 276</a>
</li></ul></dd>
@@ -4935,7 +4935,7 @@ transactional by default.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line300">line 300</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line302">line 302</a>
</li></ul></dd>

View File

@@ -70,7 +70,7 @@ class Branch extends Entity {
async beforeSaving() {
if (this.notePosition === undefined) {
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]);
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1;
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10;
}
if (!this.isDeleted) {

View File

@@ -237,9 +237,11 @@ function BackendScriptApi(currentNote, apiParams) {
* @returns {Promise&lt;{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/
this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
await noteService.createNote(parentNoteId, title, content, extraOptions);
const ret = await noteService.createNote(parentNoteId, title, content, extraOptions);
ws.refreshTree();
return ret;
};
/**
@@ -343,7 +345,8 @@ function BackendScriptApi(currentNote, apiParams) {
this.getAppInfo = () => appInfo
}
module.exports = BackendScriptApi;</code></pre>
module.exports = BackendScriptApi;
</code></pre>
</article>
</section>

View File

@@ -81,7 +81,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line19">line 19</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line21">line 21</a>
</li></ul></dd>
@@ -131,6 +131,116 @@
<h4 class="name" id="$container"><span class="type-signature"></span>$container<span class="type-signature"></span></h4>
<h5 class="subsection-title">Properties:</h5>
<table class="props">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>container</code></td>
<td class="type">
<span class="param-type">jQuery</span>
</td>
<td class="description last">of all the rendered script content</td>
</tr>
</tbody>
</table>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line25">line 25</a>
</li></ul></dd>
</dl>
<h4 class="name" id="currentNote"><span class="type-signature"></span>currentNote<span class="type-signature"></span></h4>
@@ -223,7 +333,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line25">line 25</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line30">line 30</a>
</li></ul></dd>
@@ -336,7 +446,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line27">line 27</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line32">line 32</a>
</li></ul></dd>
@@ -442,7 +552,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line36">line 36</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line41">line 41</a>
</li></ul></dd>
@@ -552,7 +662,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line23">line 23</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line28">line 28</a>
</li></ul></dd>
@@ -661,7 +771,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line33">line 33</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line38">line 38</a>
</li></ul></dd>
@@ -790,7 +900,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line61">line 61</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line66">line 66</a>
</li></ul></dd>
@@ -945,7 +1055,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line45">line 45</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line50">line 50</a>
</li></ul></dd>
@@ -1100,7 +1210,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line80">line 80</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line85">line 85</a>
</li></ul></dd>
@@ -1280,7 +1390,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line269">line 269</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line279">line 279</a>
</li></ul></dd>
@@ -1413,7 +1523,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line229">line 229</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line239">line 239</a>
</li></ul></dd>
@@ -1471,7 +1581,7 @@
<h4 class="name" id="getActiveNote"><span class="type-signature"></span>getActiveNote<span class="signature">()</span><span class="type-signature"> &rarr; {<a href="NoteFull.html">NoteFull</a>}</span></h4>
<h4 class="name" id="getActiveTabNote"><span class="type-signature"></span>getActiveTabNote<span class="signature">()</span><span class="type-signature"> &rarr; {<a href="NoteFull.html">NoteFull</a>}</span></h4>
@@ -1519,7 +1629,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line275">line 275</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line285">line 285</a>
</li></ul></dd>
@@ -1577,7 +1687,7 @@
<h4 class="name" id="getActiveNotePath"><span class="type-signature"></span>getActiveNotePath<span class="signature">()</span><span class="type-signature"> &rarr; {Promise.&lt;(string|null)>}</span></h4>
<h4 class="name" id="getActiveTabNotePath"><span class="type-signature"></span>getActiveTabNotePath<span class="signature">()</span><span class="type-signature"> &rarr; {Promise.&lt;(string|null)>}</span></h4>
@@ -1625,7 +1735,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line281">line 281</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line291">line 291</a>
</li></ul></dd>
@@ -1784,7 +1894,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line332">line 332</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line338">line 338</a>
</li></ul></dd>
@@ -1891,7 +2001,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line222">line 222</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line232">line 232</a>
</li></ul></dd>
@@ -2046,7 +2156,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line341">line 341</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line347">line 347</a>
</li></ul></dd>
@@ -2202,7 +2312,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line190">line 190</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line200">line 200</a>
</li></ul></dd>
@@ -2403,7 +2513,7 @@ otherwise (by e.g. createNoteLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line202">line 202</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line212">line 212</a>
</li></ul></dd>
@@ -2509,7 +2619,7 @@ otherwise (by e.g. createNoteLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line323">line 323</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line329">line 329</a>
</li></ul></dd>
@@ -2664,7 +2774,7 @@ otherwise (by e.g. createNoteLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line350">line 350</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line356">line 356</a>
</li></ul></dd>
@@ -2773,7 +2883,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line296">line 296</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line302">line 302</a>
</li></ul></dd>
@@ -2928,7 +3038,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line304">line 304</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line310">line 310</a>
</li></ul></dd>
@@ -3061,7 +3171,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line236">line 236</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line246">line 246</a>
</li></ul></dd>
@@ -3167,7 +3277,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line315">line 315</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line321">line 321</a>
</li></ul></dd>
@@ -3255,7 +3365,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line260">line 260</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line270">line 270</a>
</li></ul></dd>
@@ -3309,7 +3419,7 @@ note.
<h4 class="name" id="reloadChildren"><span class="type-signature"></span>reloadChildren<span class="signature">(noteId)</span><span class="type-signature"></span></h4>
<h4 class="name" id="reloadNotesAndTheirChildren"><span class="type-signature"></span>reloadNotesAndTheirChildren<span class="signature">(noteId)</span><span class="type-signature"></span></h4>
@@ -3406,7 +3516,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line208">line 208</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line218">line 218</a>
</li></ul></dd>
@@ -3539,7 +3649,7 @@ note.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line214">line 214</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line224">line 224</a>
</li></ul></dd>
@@ -3700,7 +3810,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line130">line 130</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line135">line 135</a>
</li></ul></dd>
@@ -3860,7 +3970,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line178">line 178</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line188">line 188</a>
</li></ul></dd>
@@ -4016,7 +4126,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line160">line 160</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line170">line 170</a>
</li></ul></dd>
@@ -4070,6 +4180,161 @@ Internally this serializes the anonymous function into string and sends it to ba
<h4 class="name" id="setHoistedNoteId"><span class="type-signature"></span>setHoistedNoteId<span class="signature">(noteId)</span><span class="type-signature"> &rarr; {Promise}</span></h4>
<div class="description">
Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
</div>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>noteId</code></td>
<td class="type">
<span class="param-type">string</span>
</td>
<td class="description last">set hoisted note. 'root' will effectively unhoist</td>
</tr>
</tbody>
</table>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line365">line 365</a>
</li></ul></dd>
</dl>
<h5>Returns:</h5>
<dl>
<dt>
Type
</dt>
<dd>
<span class="param-type">Promise</span>
</dd>
</dl>
<h4 class="name" id="setupElementTooltip"><span class="type-signature"></span>setupElementTooltip<span class="signature">($el)</span><span class="type-signature"></span></h4>
@@ -4167,7 +4432,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line310">line 310</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line316">line 316</a>
</li></ul></dd>
@@ -4304,7 +4569,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line252">line 252</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line262">line 262</a>
</li></ul></dd>
@@ -4441,7 +4706,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line244">line 244</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line254">line 254</a>
</li></ul></dd>

View File

@@ -30,7 +30,10 @@
<h2><span class="attribs"><span class="type-signature"></span></span>NoteShort<span class="signature">()</span><span class="type-signature"></span></h2>
<div class="class-description">This note's representation is used in note tree and is kept in TreeCache.</div>
<div class="class-description">FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache.
Attribute cache should be limited to "transaction".
This note's representation is used in note tree and is kept in TreeCache.</div>
</header>
@@ -93,7 +96,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line13">line 13</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line16">line 16</a>
</li></ul></dd>
@@ -183,7 +186,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line26">line 26</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line29">line 29</a>
</li></ul></dd>
@@ -241,7 +244,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line28">line 28</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line31">line 31</a>
</li></ul></dd>
@@ -299,7 +302,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line20">line 20</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line23">line 23</a>
</li></ul></dd>
@@ -357,7 +360,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line24">line 24</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line27">line 27</a>
</li></ul></dd>
@@ -415,7 +418,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line16">line 16</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line19">line 19</a>
</li></ul></dd>
@@ -473,7 +476,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line18">line 18</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line21">line 21</a>
</li></ul></dd>
@@ -531,7 +534,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line22">line 22</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line25">line 25</a>
</li></ul></dd>
@@ -679,7 +682,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line166">line 166</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line169">line 169</a>
</li></ul></dd>
@@ -846,7 +849,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line106">line 106</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line109">line 109</a>
</li></ul></dd>
@@ -1020,7 +1023,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line177">line 177</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line180">line 180</a>
</li></ul></dd>
@@ -1126,7 +1129,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line57">line 57</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line60">line 60</a>
</li></ul></dd>
@@ -1228,7 +1231,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line71">line 71</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line74">line 74</a>
</li></ul></dd>
@@ -1330,7 +1333,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line93">line 93</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line96">line 96</a>
</li></ul></dd>
@@ -1432,7 +1435,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line98">line 98</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line101">line 101</a>
</li></ul></dd>
@@ -1583,7 +1586,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line199">line 199</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line202">line 202</a>
</li></ul></dd>
@@ -1750,7 +1753,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line132">line 132</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line135">line 135</a>
</li></ul></dd>
@@ -1917,7 +1920,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line124">line 124</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line127">line 127</a>
</li></ul></dd>
@@ -2072,7 +2075,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line211">line 211</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line214">line 214</a>
</li></ul></dd>
@@ -2178,7 +2181,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line83">line 83</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line86">line 86</a>
</li></ul></dd>
@@ -2280,7 +2283,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line88">line 88</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line91">line 91</a>
</li></ul></dd>
@@ -2431,7 +2434,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line205">line 205</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line208">line 208</a>
</li></ul></dd>
@@ -2598,7 +2601,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line148">line 148</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line151">line 151</a>
</li></ul></dd>
@@ -2765,7 +2768,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line140">line 140</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line143">line 143</a>
</li></ul></dd>
@@ -2920,7 +2923,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line223">line 223</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line226">line 226</a>
</li></ul></dd>
@@ -3090,7 +3093,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line233">line 233</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line236">line 236</a>
</li></ul></dd>
@@ -3241,7 +3244,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line217">line 217</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line220">line 220</a>
</li></ul></dd>
@@ -3351,7 +3354,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line257">line 257</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line260">line 260</a>
</li></ul></dd>
@@ -3525,7 +3528,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line157">line 157</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line160">line 160</a>
</li></ul></dd>
@@ -3631,7 +3634,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line65">line 65</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line68">line 68</a>
</li></ul></dd>
@@ -3782,7 +3785,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line187">line 187</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line190">line 190</a>
</li></ul></dd>
@@ -3937,7 +3940,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line193">line 193</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line196">line 196</a>
</li></ul></dd>
@@ -4048,7 +4051,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line248">line 248</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line251">line 251</a>
</li></ul></dd>
@@ -4132,7 +4135,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line32">line 32</a>
<a href="entities_note_short.js.html">entities/note_short.js</a>, <a href="entities_note_short.js.html#line35">line 35</a>
</li></ul></dd>

View File

@@ -35,6 +35,9 @@ const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition';
/**
* FIXME: rethink how attributes are cached in Note entities since they are long lived inside the cache.
* Attribute cache should be limited to "transaction".
*
* This note's representation is used in note tree and is kept in TreeCache.
*/
class NoteShort {

View File

@@ -303,7 +303,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line67">line 67</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line72">line 72</a>
</li></ul></dd>

View File

@@ -29,7 +29,7 @@
<pre class="prettyprint source linenums"><code>import treeService from './tree.js';
import server from './server.js';
import utils from './utils.js';
import infoService from './info.js';
import toastService from './toast.js';
import linkService from './link.js';
import treeCache from './tree_cache.js';
import noteDetailService from './note_detail.js';
@@ -37,6 +37,8 @@ import noteTooltipService from './note_tooltip.js';
import protectedSessionService from './protected_session.js';
import dateNotesService from './date_notes.js';
import StandardWidget from '../widgets/standard_widget.js';
import ws from "./ws.js";
import hoistedNoteService from "./hoisted_note.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -44,9 +46,12 @@ import StandardWidget from '../widgets/standard_widget.js';
* @constructor
* @hideconstructor
*/
function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null) {
function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null, $container = null) {
const $pluginButtons = $("#plugin-buttons");
/** @property {jQuery} container of all the rendered script content */
this.$container = $container;
/** @property {object} note where script started executing */
this.startNote = startNote;
/** @property {object} note where script is currently executing */
@@ -167,9 +172,14 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
currentNoteId: currentNote.noteId,
originEntityName: "notes", // currently there's no other entity on frontend which can trigger event
originEntityId: originEntity ? originEntity.noteId : null
}, {
'trilium-source-id': "script"
});
if (ret.success) {
// wait until all the changes done in the script has been synced to frontend before continuing
await ws.waitForSyncId(ret.maxSyncId);
return ret.executionResult;
}
else {
@@ -233,7 +243,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @param {string} noteId
* @method
*/
this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId);
this.reloadNotesAndTheirChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId);
/**
* @param {string} noteId
@@ -269,7 +279,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @param {string} message
*/
this.showMessage = infoService.showMessage;
this.showMessage = toastService.showMessage;
/**
* Show error message to the user.
@@ -277,7 +287,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @param {string} message
*/
this.showError = infoService.showError;
this.showError = toastService.showError;
/**
* Refresh tree
@@ -300,17 +310,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @returns {NoteFull} active note (loaded into right pane)
*/
this.getActiveNote = noteDetailService.getActiveNote;
this.getActiveTabNote = noteDetailService.getActiveTabNote;
/**
* @method
* @returns {Promise&lt;string|null>} returns note path of active note or null if there isn't active note
*/
this.getActiveNotePath = () => {
const activeTabContext = noteDetailService.getActiveTabContext();
return activeTabContext ? activeTabContext.notePath : null;
};
this.getActiveTabNotePath = noteDetailService.getActiveTabNotePath;
/**
* This method checks whether user navigated away from the note from which the scripts has been started.
@@ -322,7 +328,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {boolean} returns true if the original note is still loaded, false if user switched to another
*/
this.isNoteStillActive = () => {
return this.originEntity.noteId === tabContext.noteId;
return tabContext.note &amp;&amp; this.originEntity.noteId === tabContext.note.noteId;
};
/**
@@ -376,6 +382,15 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {Promise&lt;NoteShort>}
*/
this.getYearNote = dateNotesService.getYearNote;
/**
* Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
*
* @method
* @param {string} noteId - set hoisted note. 'root' will effectively unhoist
* @return {Promise}
*/
this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
}
export default FrontendScriptApi;</code></pre>

View File

@@ -1,6 +1,6 @@
'use strict';
const electron = require('electron');
const {app, globalShortcut, BrowserWindow} = require('electron');
const path = require('path');
const log = require('./src/services/log');
const sqlInit = require('./src/services/sql_init');
@@ -10,10 +10,6 @@ const port = require('./src/services/port');
const env = require('./src/services/env');
const appIconService = require('./src/services/app_icon');
const windowStateKeeper = require('electron-window-state');
const contextMenu = require('electron-context-menu');
const app = electron.app;
const globalShortcut = electron.globalShortcut;
// Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
@@ -25,25 +21,25 @@ let mainWindow;
require('electron-dl')({ saveAs: true });
contextMenu({
menu: (actions, params, browserWindow) => [
actions.cut(),
actions.copy(),
actions.copyLink(),
actions.paste(),
{
label: 'Search DuckDuckGo for “{selection}”',
// Only show it when right-clicking text
visible: params.selectionText.trim().length > 0,
click: () => {
const {shell} = require('electron');
shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`);
}
},
actions.inspect()
]
});
// contextMenu({
// menu: (actions, params, browserWindow) => [
// actions.cut(),
// actions.copy(),
// actions.copyLink(),
// actions.paste(),
// {
// label: 'Search DuckDuckGo for “{selection}”',
// // Only show it when right-clicking text
// visible: params.selectionText.trim().length > 0,
// click: () => {
// const {shell} = require('electron');
//
// shell.openExternal(`https://duckduckgo.com?q=${encodeURIComponent(params.selectionText)}`);
// }
// },
// actions.inspect()
// ]
// });
function onClosed() {
// Dereference the window
@@ -66,7 +62,7 @@ async function createMainWindow() {
defaultHeight: 800
});
const win = new electron.BrowserWindow({
const win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,

File diff suppressed because one or more lines are too long

View File

@@ -29,8 +29,8 @@
async function validatorJavaScript(text, options) {
if (glob.isMobile()
|| glob.getActiveNote() == null
|| glob.getActiveNote().mime === 'application/json') {
|| glob.getActiveTabNote() == null
|| glob.getActiveTabNote().mime === 'application/json') {
// eslint doesn't seem to validate pure JSON well
return [];
}

4674
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.35.1",
"version": "0.36.1-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -17,8 +17,7 @@
"start-electron": "TRILIUM_ENV=dev electron . --disable-gpu",
"build-backend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
"build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
"postinstall": "electron-builder install-app-deps"
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs"
},
"dependencies": {
"async-mutex": "0.1.4",
@@ -31,32 +30,32 @@
"dayjs": "1.8.16",
"debug": "4.1.1",
"ejs": "2.7.1",
"electron-context-menu": "0.15.0",
"electron-debug": "3.0.1",
"electron-dl": "1.14.0",
"electron-find": "1.0.6",
"electron-spellchecker": "2.2.0",
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.16.2",
"express-session": "1.17.0",
"file-type": "12.3.0",
"fs-extra": "8.1.0",
"helmet": "3.21.1",
"html": "1.0.0",
"html2plaintext": "2.1.2",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.2",
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "3.0.0",
"image-type": "4.1.0",
"imagemin": "7.0.0",
"imagemin-giflossy": "5.1.10",
"imagemin-mozjpeg": "8.0.0",
"imagemin-pngquant": "8.0.0",
"ini": "1.3.5",
"jimp": "0.8.4",
"jimp": "0.8.5",
"mime-types": "2.1.24",
"moment": "2.24.0",
"multer": "1.4.2",
"node-abi": "2.11.0",
"open": "6.4.0",
"open": "7.0.0",
"pngjs": "3.4.0",
"portscanner": "2.2.0",
"rand-token": "0.4.0",
@@ -70,22 +69,22 @@
"simple-node-logger": "18.12.23",
"sqlite": "3.0.3",
"sqlite3": "4.1.0",
"string-similarity": "^3.0.0",
"string-similarity": "3.0.0",
"tar-stream": "2.1.0",
"turndown": "5.0.3",
"turndown-plugin-gfm": "1.0.2",
"unescape": "1.0.1",
"ws": "7.1.2",
"ws": "7.2.0",
"xml2js": "0.4.22"
},
"devDependencies": {
"electron": "6.0.10",
"electron": "6.0.12",
"electron-builder": "21.2.0",
"electron-compile": "6.4.4",
"electron-installer-debian": "2.0.1",
"electron-packager": "14.0.6",
"electron-rebuild": "1.8.6",
"jsdoc": "^3.6.3",
"jsdoc": "3.6.3",
"lorem-ipsum": "2.0.3",
"xo": "0.25.3"
},

View File

@@ -88,7 +88,10 @@ app.use((req, res, next) => {
// error handler
app.use((err, req, res, next) => {
if (err && err.message && err.message.includes("Invalid package")) {
if (err && err.message && (
err.message.includes("Invalid package")
|| (err.message.includes("Router not found for request") && err.message.includes("node_modules"))
)) {
// electron 6 outputs a lot of such errors which do not seem important
}
else {

View File

@@ -42,7 +42,7 @@ class Branch extends Entity {
async beforeSaving() {
if (this.notePosition === undefined) {
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]);
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1;
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10;
}
if (!this.isDeleted) {

View File

@@ -29,6 +29,7 @@ import macInit from './services/mac_init.js';
import cssLoader from './services/css_loader.js';
import dateNoteService from './services/date_notes.js';
import sidebarService from './services/sidebar.js';
import importService from './services/import.js';
window.glob.isDesktop = utils.isDesktop;
window.glob.isMobile = utils.isMobile;
@@ -42,7 +43,7 @@ window.glob.noteChanged = noteDetailService.noteChanged;
window.glob.refreshTree = treeService.reload;
// required for ESLint plugin
window.glob.getActiveNote = noteDetailService.getActiveNote;
window.glob.getActiveTabNote = noteDetailService.getActiveTabNote;
window.glob.requireLibrary = libraryLoader.requireLibrary;
window.glob.ESLINT = libraryLoader.ESLINT;
@@ -177,3 +178,7 @@ entrypoints.registerEntrypoints();
noteTooltipService.setupGlobalTooltip();
noteAutocompleteService.init();
if (utils.isElectron()) {
import("./services/spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck());
}

View File

@@ -33,7 +33,7 @@ export async function showDialog(linkType) {
glob.activeDialog = $dialog;
if (noteDetailService.getActiveNoteType() === 'text') {
if (noteDetailService.getActiveTabNoteType() === 'text') {
$linkTypeHtml.prop('disabled', false);
setLinkType('html');
@@ -110,14 +110,14 @@ $form.submit(() => {
else if (linkType === 'selected-to-active') {
const prefix = $clonePrefix.val();
cloningService.cloneNoteTo(noteId, noteDetailService.getActiveNoteId(), prefix);
cloningService.cloneNoteTo(noteId, noteDetailService.getActiveTabNoteId(), prefix);
$dialog.modal('hide');
}
else if (linkType === 'active-to-selected') {
const prefix = $clonePrefix.val();
cloningService.cloneNoteTo(noteDetailService.getActiveNoteId(), noteId, prefix);
cloningService.cloneNoteTo(noteDetailService.getActiveTabNoteId(), noteId, prefix);
$dialog.modal('hide');
}

View File

@@ -1,6 +1,6 @@
import noteDetailService from '../services/note_detail.js';
import server from '../services/server.js';
import infoService from "../services/info.js";
import toastService from "../services/toast.js";
import treeUtils from "../services/tree_utils.js";
import attributeAutocompleteService from "../services/attribute_autocomplete.js";
import utils from "../services/utils.js";
@@ -47,14 +47,15 @@ function AttributesModel() {
};
this.updateAttributePositions = function() {
let position = 0;
let position = 10;
// we need to update positions by searching in the DOM, because order of the
// attributes in the viewmodel (self.ownedAttributes()) stays the same
$ownedAttributesBody.find('input[name="position"]').each(function() {
const attribute = self.getTargetAttribute(this);
attribute().position = position++;
attribute().position = position;
position += 10;
});
};
@@ -91,7 +92,7 @@ function AttributesModel() {
}
this.loadAttributes = async function() {
const noteId = noteDetailService.getActiveNoteId();
const noteId = noteDetailService.getActiveTabNoteId();
const attributes = await server.get('notes/' + noteId + '/attributes');
@@ -137,7 +138,7 @@ function AttributesModel() {
self.updateAttributePositions();
const noteId = noteDetailService.getActiveNoteId();
const noteId = noteDetailService.getActiveTabNoteId();
const attributesToSave = self.ownedAttributes()
.map(attribute => attribute())
@@ -167,7 +168,7 @@ function AttributesModel() {
await showAttributes(attributes);
infoService.showMessage("Attributes have been saved.");
toastService.showMessage("Attributes have been saved.");
const ctx = noteDetailService.getActiveTabContext();

View File

@@ -2,7 +2,7 @@ import treeService from '../services/tree.js';
import server from '../services/server.js';
import treeCache from "../services/tree_cache.js";
import treeUtils from "../services/tree_utils.js";
import infoService from "../services/info.js";
import toastService from "../services/toast.js";
import utils from "../services/utils.js";
const $dialog = $("#branch-prefix-dialog");
@@ -38,7 +38,7 @@ async function savePrefix() {
$dialog.modal('hide');
infoService.showMessage("Branch prefix has been saved.");
toastService.showMessage("Branch prefix has been saved.");
}
$form.submit(() => {

View File

@@ -1,7 +1,7 @@
import treeUtils from "../services/tree_utils.js";
import utils from "../services/utils.js";
import ws from "../services/ws.js";
import infoService from "../services/info.js";
import toastService from "../services/toast.js";
const $dialog = $("#export-dialog");
const $form = $("#export-form");
@@ -10,22 +10,18 @@ const $subtreeFormats = $("#export-subtree-formats");
const $singleFormats = $("#export-single-formats");
const $subtreeType = $("#export-type-subtree");
const $singleType = $("#export-type-single");
const $exportProgressWrapper = $("#export-progress-count-wrapper");
const $exportProgressCount = $("#export-progress-count");
const $exportButton = $("#export-button");
const $opmlVersions = $("#opml-versions");
let exportId = '';
let taskId = '';
let branchId = null;
export async function showDialog(node, defaultType) {
utils.closeActiveDialog();
// each opening of the dialog resets the exportId so we don't associate it with previous exports anymore
exportId = '';
// each opening of the dialog resets the taskId so we don't associate it with previous exports anymore
taskId = '';
$exportButton.removeAttr("disabled");
$exportProgressWrapper.hide();
$exportProgressCount.text('0');
if (defaultType === 'subtree') {
$subtreeType.prop("checked", true).change();
@@ -54,8 +50,7 @@ export async function showDialog(node, defaultType) {
}
$form.submit(() => {
// disabling so export can't be triggered again
$exportButton.attr("disabled", "disabled");
$dialog.modal('hide');
const exportType = $dialog.find("input[name='export-type']:checked").val();
@@ -77,9 +72,9 @@ $form.submit(() => {
});
function exportBranch(branchId, type, format, version) {
exportId = utils.randomString(10);
taskId = utils.randomString(10);
const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${exportId}`;
const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`;
utils.download(url);
}
@@ -112,26 +107,31 @@ $('input[name=export-subtree-format]').change(function () {
}
});
function makeToast(id, message) {
return {
id: id,
title: "Export status",
message: message,
icon: "arrow-square-up-right"
};
}
ws.subscribeToMessages(async message => {
if (message.type === 'export-error') {
infoService.showError(message.message);
$dialog.modal('hide');
if (message.taskType !== 'export') {
return;
}
if (!message.exportId || message.exportId !== exportId) {
// incoming messages must correspond to this export instance
return;
if (message.type === 'task-error') {
toastService.closePersistent(message.taskId);
toastService.showError(message.message);
}
if (message.type === 'export-progress-count') {
$exportProgressWrapper.slideDown();
$exportProgressCount.text(message.progressCount);
else if (message.type === 'task-progress-count') {
toastService.showPersistent(makeToast(message.taskId, "Export in progress: " + message.progressCount));
}
else if (message.type === 'export-finished') {
$dialog.modal('hide');
else if (message.type === 'task-succeeded') {
const toast = makeToast(message.taskId, "Import finished successfully.");
toast.closeAfter = 5000;
infoService.showMessage("Export finished successfully.");
toastService.showPersistent(toast);
}
});

View File

@@ -1,16 +1,11 @@
import treeService from '../services/tree.js';
import utils from '../services/utils.js';
import treeUtils from "../services/tree_utils.js";
import server from "../services/server.js";
import infoService from "../services/info.js";
import ws from "../services/ws.js";
import importService from "../services/import.js";
const $dialog = $("#import-dialog");
const $form = $("#import-form");
const $noteTitle = $dialog.find(".import-note-title");
const $fileUploadInput = $("#import-file-upload-input");
const $importProgressCountWrapper = $("#import-progress-count-wrapper");
const $importProgressCount = $("#import-progress-count");
const $importButton = $("#import-button");
const $safeImportCheckbox = $("#safe-import-checkbox");
const $shrinkImagesCheckbox = $("#shrink-images-checkbox");
@@ -18,16 +13,11 @@ const $textImportedAsTextCheckbox = $("#text-imported-as-text-checkbox");
const $codeImportedAsCodeCheckbox = $("#code-imported-as-code-checkbox");
const $explodeArchivesCheckbox = $("#explode-archives-checkbox");
let importId;
let importIntoNoteId = null;
let parentNoteId = null;
export async function showDialog(node) {
utils.closeActiveDialog();
// each opening of the dialog resets the importId so we don't associate it with previous imports anymore
importId = '';
$importProgressCountWrapper.hide();
$importProgressCount.text('0');
$fileUploadInput.val('').change(); // to trigger Import button disabling listener below
$safeImportCheckbox.prop("checked", true);
@@ -38,9 +28,9 @@ export async function showDialog(node) {
glob.activeDialog = $dialog;
importIntoNoteId = node.data.noteId;
parentNoteId = node.data.noteId;
$noteTitle.text(await treeUtils.getNoteTitle(importIntoNoteId));
$noteTitle.text(await treeUtils.getNoteTitle(parentNoteId));
$dialog.modal();
}
@@ -49,18 +39,14 @@ $form.submit(() => {
// disabling so that import is not triggered again.
$importButton.attr("disabled", "disabled");
importIntoNote(importIntoNoteId);
importIntoNote(parentNoteId);
return false;
});
async function importIntoNote(importNoteId) {
async function importIntoNote(parentNoteId) {
const files = Array.from($fileUploadInput[0].files); // shallow copy since we're resetting the upload button below
// we generate it here (and not on opening) for the case when you try to import multiple times from the same
// dialog (which shouldn't happen, but still ...)
importId = utils.randomString(10);
const options = {
safeImport: boolToString($safeImportCheckbox),
shrinkImages: boolToString($shrinkImagesCheckbox),
@@ -69,73 +55,15 @@ async function importIntoNote(importNoteId) {
explodeArchives: boolToString($explodeArchivesCheckbox)
};
await uploadFiles(importNoteId, files, options);
$dialog.modal('hide');
}
export async function uploadFiles(importNoteId, files, options) {
if (files.length === 0) {
return;
}
let noteId;
for (const file of files) {
const formData = new FormData();
formData.append('upload', file);
formData.append('importId', importId);
for (const key in options) {
formData.append(key, options[key]);
}
({noteId} = await $.ajax({
url: baseApiUrl + 'notes/' + importNoteId + '/import',
headers: server.getHeaders(),
data: formData,
dataType: 'json',
type: 'POST',
timeout: 60 * 60 * 1000,
contentType: false, // NEEDED, DON'T REMOVE THIS
processData: false, // NEEDED, DON'T REMOVE THIS
}));
}
infoService.showMessage("Import finished successfully.");
await treeService.reloadNote(importNoteId);
if (noteId) {
const node = await treeService.activateNote(noteId);
node.setExpanded(true);
}
await importService.uploadFiles(parentNoteId, files, options);
}
function boolToString($el) {
return $el.is(":checked") ? "true" : "false";
}
ws.subscribeToMessages(async message => {
if (message.type === 'import-error') {
infoService.showError(message.message);
$dialog.modal('hide');
return;
}
if (!message.importId || message.importId !== importId) {
// incoming messages must correspond to this import instance
return;
}
if (message.type === 'import-progress-count') {
$importProgressCountWrapper.slideDown();
$importProgressCount.text(message.progressCount);
}
});
$fileUploadInput.change(() => {
if ($fileUploadInput.val()) {
$importButton.removeAttr("disabled");

View File

@@ -23,7 +23,7 @@ export async function showDialog() {
// set default settings
$maxNotesInput.val(20);
const note = noteDetailService.getActiveNote();
const note = noteDetailService.getActiveTabNote();
if (!note) {
return;

View File

@@ -1,5 +1,5 @@
import libraryLoader from "../services/library_loader.js";
import infoService from "../services/info.js";
import toastService from "../services/toast.js";
import utils from "../services/utils.js";
import noteDetailService from "../services/note_detail.js";
@@ -22,7 +22,7 @@ async function convertMarkdownToHtml(text) {
textEditor.model.insertContent(modelFragment, textEditor.model.document.selection);
infoService.showMessage("Markdown content has been imported into the document.");
toastService.showMessage("Markdown content has been imported into the document.");
}
export async function importMarkdownInline() {

View File

@@ -16,7 +16,7 @@ export function showDialog() {
$dialog.modal();
const activeNote = noteDetailService.getActiveNote();
const activeNote = noteDetailService.getActiveTabNote();
$noteId.text(activeNote.noteId);
$dateCreated.text(activeNote.dateCreated);

View File

@@ -11,7 +11,7 @@ let revisionItems = [];
let note;
export async function showCurrentNoteRevisions() {
await showNoteRevisionsDialog(noteDetailService.getActiveNoteId());
await showNoteRevisionsDialog(noteDetailService.getActiveTabNoteId());
}
export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
@@ -24,7 +24,7 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
$list.empty();
$content.empty();
note = noteDetailService.getActiveNote();
note = noteDetailService.getActiveTabNote();
revisionItems = await server.get('notes/' + noteId + '/revisions');
for (const item of revisionItems) {
@@ -63,13 +63,3 @@ $list.on('change', () => {
$content.text("Preview isn't available for this note type.");
}
});
$(document).on('click', "a[data-action='note-revision']", event => {
const linkEl = $(event.target);
const noteId = linkEl.attr('data-note-path');
const noteRevisionId = linkEl.attr('data-note-revision-id');
showNoteRevisionsDialog(noteId, noteRevisionId);
return false;
});

View File

@@ -11,7 +11,7 @@ export function showDialog() {
$dialog.modal();
const noteText = noteDetailService.getActiveNote().content;
const noteText = noteDetailService.getActiveTabNote().content;
$noteSource.text(formatHtml(noteText));
}

View File

@@ -19,8 +19,7 @@ export async function showDialog() {
import('./options/appearance.js'),
import('./options/code_notes.js'),
import('./options/change_password.js'),
import('./options/note_revisions.js'),
import('./options/protected_session.js'),
import('./options/other.js'),
import('./options/sidebar.js'),
import('./options/sync.js'),
]))

View File

@@ -1,5 +1,5 @@
import server from "../../services/server.js";
import infoService from "../../services/info.js";
import toastService from "../../services/toast.js";
export default class AdvancedOptions {
constructor() {
@@ -13,26 +13,26 @@ export default class AdvancedOptions {
this.$forceFullSyncButton.click(async () => {
await server.post('sync/force-full-sync');
infoService.showMessage("Full sync triggered");
toastService.showMessage("Full sync triggered");
});
this.$fillSyncRowsButton.click(async () => {
await server.post('sync/fill-sync-rows');
infoService.showMessage("Sync rows filled successfully");
toastService.showMessage("Sync rows filled successfully");
});
this.$anonymizeButton.click(async () => {
await server.post('anonymization/anonymize');
infoService.showMessage("Created anonymized database");
toastService.showMessage("Created anonymized database");
});
this.$cleanupSoftDeletedButton.click(async () => {
if (confirm("Do you really want to clean up soft-deleted items?")) {
await server.post('cleanup/cleanup-soft-deleted-items');
infoService.showMessage("Soft deleted items have been cleaned up");
toastService.showMessage("Soft deleted items have been cleaned up");
}
});
@@ -40,14 +40,14 @@ export default class AdvancedOptions {
if (confirm("Do you really want to clean up unused images?")) {
await server.post('cleanup/cleanup-unused-images');
infoService.showMessage("Unused images have been cleaned up");
toastService.showMessage("Unused images have been cleaned up");
}
});
this.$vacuumDatabaseButton.click(async () => {
await server.post('cleanup/vacuum-database');
infoService.showMessage("Database has been vacuumed");
toastService.showMessage("Database has been vacuumed");
});
}
}

View File

@@ -1,6 +1,6 @@
import server from "../../services/server.js";
import protectedSessionHolder from "../../services/protected_session_holder.js";
import infoService from "../../services/info.js";
import toastService from "../../services/toast.js";
export default class ChangePasswordOptions {
constructor() {
@@ -39,7 +39,7 @@ export default class ChangePasswordOptions {
protectedSessionHolder.resetProtectedSession();
}
else {
infoService.showError(result.message);
toastService.showError(result.message);
}
});

View File

@@ -1,20 +0,0 @@
import server from "../../services/server.js";
import infoService from "../../services/info.js";
export default class NoteRevisionsOptions {
constructor() {
this.$form = $("#note-revision-snapshot-time-interval-form");
this.$timeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
this.$form.submit(() => {
const opts = { 'noteRevisionSnapshotTimeInterval': this.$timeInterval.val() };
server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
return false;
});
}
optionsLoaded(options) {
this.$timeInterval.val(options['noteRevisionSnapshotTimeInterval']);
}
}

View File

@@ -0,0 +1,54 @@
import optionsService from "../../services/options.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
export default class ProtectedSessionOptions {
constructor() {
this.$spellCheckEnabled = $("#spell-check-enabled");
this.$spellCheckLanguageCode = $("#spell-check-language-code");
this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
this.$noteRevisionsTimeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
this.$spellCheckEnabled.change(() => {
const opts = { 'spellCheckEnabled': this.$spellCheckEnabled.is(":checked") ? "true" : "false" };
server.put('options', opts).then(() => toastService.showMessage("Options change have been saved."));
return false;
});
this.$spellCheckLanguageCode.change(() => {
const opts = { 'spellCheckLanguageCode': this.$spellCheckLanguageCode.val() };
server.put('options', opts).then(() => toastService.showMessage("Options change have been saved."));
return false;
});
this.$protectedSessionTimeout.change(() => {
const protectedSessionTimeout = this.$protectedSessionTimeout.val();
server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => {
optionsService.reloadOptions();
toastService.showMessage("Options change have been saved.");
});
return false;
});
this.$noteRevisionsTimeInterval.change(() => {
const opts = { 'noteRevisionSnapshotTimeInterval': this.$noteRevisionsTimeInterval.val() };
server.put('options', opts).then(() => toastService.showMessage("Options change have been saved."));
return false;
});
}
optionsLoaded(options) {
this.$spellCheckEnabled.prop("checked", options['spellCheckEnabled'] === 'true');
this.$spellCheckLanguageCode.val(options['spellCheckLanguageCode']);
this.$protectedSessionTimeout.val(options['protectedSessionTimeout']);
this.$noteRevisionsTimeInterval.val(options['noteRevisionSnapshotTimeInterval']);
}
}

View File

@@ -1,28 +0,0 @@
import optionsService from "../../services/options.js";
import server from "../../services/server.js";
import infoService from "../../services/info.js";
export default class ProtectedSessionOptions {
constructor() {
this.$form = $("#protected-session-timeout-form");
this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
this.$form.submit(() => this.save());
}
optionsLoaded(options) {
this.$protectedSessionTimeout.val(options['protectedSessionTimeout']);
}
save() {
const protectedSessionTimeout = this.$protectedSessionTimeout.val();
server.put('options', { 'protectedSessionTimeout': protectedSessionTimeout }).then(() => {
optionsService.reloadOptions();
infoService.showMessage("Options change have been saved.");
});
return false;
}
}

View File

@@ -1,5 +1,5 @@
import server from "../../services/server.js";
import infoService from "../../services/info.js";
import toastService from "../../services/toast.js";
export default class SyncOptions {
constructor() {
@@ -15,10 +15,10 @@ export default class SyncOptions {
const result = await server.post('sync/test');
if (result.success) {
infoService.showMessage(result.message);
toastService.showMessage(result.message);
}
else {
infoService.showError("Sync server handshake failed, error: " + result.message);
toastService.showError("Sync server handshake failed, error: " + result.message);
}
});
}
@@ -36,7 +36,7 @@ export default class SyncOptions {
'syncProxy': this.$syncProxy.val()
};
server.put('options', opts).then(() => infoService.showMessage("Options change have been saved."));
server.put('options', opts).then(() => toastService.showMessage("Options change have been saved."));
return false;
}

View File

@@ -1,6 +1,6 @@
import libraryLoader from '../services/library_loader.js';
import server from '../services/server.js';
import infoService from "../services/info.js";
import toastService from "../services/toast.js";
import utils from "../services/utils.js";
const $dialog = $("#sql-console-dialog");
@@ -63,11 +63,11 @@ async function execute() {
});
if (!result.success) {
infoService.showError(result.error);
toastService.showError(result.error);
return;
}
else {
infoService.showMessage("Query was executed successfully.");
toastService.showMessage("Query was executed successfully.");
}
const rows = result.rows;

View File

@@ -1,11 +1,12 @@
import treeService from './tree.js';
import utils from './utils.js';
import server from './server.js';
import infoService from "./info.js";
import toastService from "./toast.js";
import treeCache from "./tree_cache.js";
import treeUtils from "./tree_utils.js";
import hoistedNoteService from "./hoisted_note.js";
import noteDetailService from "./note_detail.js";
import ws from "./ws.js";
async function moveBeforeNode(nodesToMove, beforeNode) {
nodesToMove = await filterRootNote(nodesToMove);
@@ -105,14 +106,23 @@ async function deleteNodes(nodes) {
const deleteClones = $deleteClonesCheckbox.find("input").is(":checked");
const taskId = utils.randomString(10);
let counter = 0;
for (const node of nodes) {
counter++;
const last = counter === nodes.length;
const query = `?taskId=${taskId}&last=${last ? 'true' : 'false'}`;
if (deleteClones) {
await server.remove('notes/' + node.data.noteId);
await server.remove(`notes/${node.data.noteId}` + query);
noteDetailService.noteDeleted(node.data.noteId);
}
else {
const {noteDeleted} = await server.remove('branches/' + node.data.branchId);
const {noteDeleted} = await server.remove(`branches/${node.data.branchId}` + query);
if (noteDeleted) {
noteDetailService.noteDeleted(node.data.noteId);
@@ -152,9 +162,7 @@ async function deleteNodes(nodes) {
node.remove();
}
for (const parentNoteId of parentNoteIds) {
await treeService.reloadNote(parentNoteId);
}
await treeService.reloadNotes(parentNoteIds);
// activate after all the reloading
if (activeNotePath) {
@@ -164,8 +172,6 @@ async function deleteNodes(nodes) {
node.setFocus(true);
}
infoService.showMessage("Note(s) has been deleted.");
return true;
}
@@ -249,6 +255,33 @@ async function filterRootNote(nodes) {
&& node.data.noteId !== hoistedNoteId);
}
function makeToast(id, message) {
return {
id: id,
title: "Delete status",
message: message,
icon: "trash"
};
}
ws.subscribeToMessages(async message => {
if (message.taskType !== 'delete-notes') {
return;
}
if (message.type === 'task-error') {
toastService.closePersistent(message.taskId);
toastService.showError(message.message);
} else if (message.type === 'task-progress-count') {
toastService.showPersistent(makeToast(message.taskId, "Delete notes in progress: " + message.progressCount));
} else if (message.type === 'task-succeeded') {
const toast = makeToast(message.taskId, "Delete finished successfully.");
toast.closeAfter = 5000;
toastService.showPersistent(toast);
}
});
export default {
moveBeforeNode,
moveAfterNode,

View File

@@ -1,6 +1,6 @@
import ScriptContext from "./script_context.js";
import server from "./server.js";
import infoService from "./info.js";
import toastService from "./toast.js";
async function getAndExecuteBundle(noteId, originEntity = null) {
const bundle = await server.get('script/bundle/' + noteId);
@@ -8,8 +8,8 @@ async function getAndExecuteBundle(noteId, originEntity = null) {
return await executeBundle(bundle, originEntity);
}
async function executeBundle(bundle, originEntity, tabContext) {
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, tabContext);
async function executeBundle(bundle, originEntity, tabContext, $container) {
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, tabContext, $container);
try {
return await (function () {
@@ -17,7 +17,7 @@ async function executeBundle(bundle, originEntity, tabContext) {
}.call(apiContext));
}
catch (e) {
infoService.showAndLogError(`Execution of ${bundle.noteId} failed with error: ${e.message}`);
toastService.showAndLogError(`Execution of ${bundle.noteId} failed with error: ${e.message}`);
}
}

View File

@@ -1,7 +1,7 @@
import treeUtils from "./tree_utils.js";
import treeChangesService from "./branches.js";
import cloningService from "./cloning.js";
import infoService from "./info.js";
import toastService from "./toast.js";
let clipboardIds = [];
let clipboardMode = null;
@@ -26,7 +26,7 @@ async function pasteAfter(node) {
// just do nothing
}
else {
infoService.throwError("Unrecognized clipboard mode=" + clipboardMode);
toastService.throwError("Unrecognized clipboard mode=" + clipboardMode);
}
}
@@ -54,7 +54,7 @@ async function pasteInto(node) {
// just do nothing
}
else {
infoService.throwError("Unrecognized clipboard mode=" + mode);
toastService.throwError("Unrecognized clipboard mode=" + mode);
}
}
@@ -62,14 +62,14 @@ function copy(nodes) {
clipboardIds = nodes.map(node => node.data.noteId);
clipboardMode = 'copy';
infoService.showMessage("Note(s) have been copied into clipboard.");
toastService.showMessage("Note(s) have been copied into clipboard.");
}
function cut(nodes) {
clipboardIds = nodes.map(node => node.key);
clipboardMode = 'cut';
infoService.showMessage("Note(s) have been cut into clipboard.");
toastService.showMessage("Note(s) have been cut into clipboard.");
}
function isEmpty() {

View File

@@ -14,7 +14,7 @@ async function cloneNoteTo(childNoteId, parentNoteId, prefix) {
treeCache.addBranchRelationship(resp.branchId, childNoteId, parentNoteId);
await treeService.reloadNote(parentNoteId);
await treeService.reloadNotes([parentNoteId]);
}
// beware that first arg is noteId and second is branchId!
@@ -30,7 +30,7 @@ async function cloneNoteAfter(noteId, afterBranchId) {
treeCache.addBranchRelationship(resp.branchId, noteId, afterBranch.parentNoteId);
await treeService.reloadNote(afterBranch.parentNoteId);
await treeService.reloadNotes([afterBranch.parentNoteId]);
}
export default {

View File

@@ -30,9 +30,9 @@ const dragAndDropSetup = {
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
const importDialog = await import('../dialogs/import.js');
const importService = await import('./import.js');
importDialog.uploadFiles(node.data.noteId, files, {
importService.uploadFiles(node.data.noteId, files, {
safeImport: true,
shrinkImages: true,
textImportedAsText: true,

View File

@@ -172,6 +172,19 @@ function registerEntrypoints() {
utils.bindGlobalShortcut('ctrl+-', zoomService.decreaseZoomFactor);
utils.bindGlobalShortcut('ctrl+=', zoomService.increaseZoomFactor);
}
$(document).on('click', "a[data-action='note-revision']", async event => {
const linkEl = $(event.target);
const noteId = linkEl.attr('data-note-path');
const noteRevisionId = linkEl.attr('data-note-revision-id');
const attributesDialog = await import("../dialogs/note_revisions.js");
attributesDialog.showNoteRevisionsDialog(noteId, noteRevisionId);
return false;
});
}
export default {

View File

@@ -1,7 +1,7 @@
import treeService from './tree.js';
import server from './server.js';
import utils from './utils.js';
import infoService from './info.js';
import toastService from './toast.js';
import linkService from './link.js';
import treeCache from './tree_cache.js';
import noteDetailService from './note_detail.js';
@@ -9,6 +9,8 @@ import noteTooltipService from './note_tooltip.js';
import protectedSessionService from './protected_session.js';
import dateNotesService from './date_notes.js';
import StandardWidget from '../widgets/standard_widget.js';
import ws from "./ws.js";
import hoistedNoteService from "./hoisted_note.js";
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -16,9 +18,12 @@ import StandardWidget from '../widgets/standard_widget.js';
* @constructor
* @hideconstructor
*/
function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null) {
function FrontendScriptApi(startNote, currentNote, originEntity = null, tabContext = null, $container = null) {
const $pluginButtons = $("#plugin-buttons");
/** @property {jQuery} container of all the rendered script content */
this.$container = $container;
/** @property {object} note where script started executing */
this.startNote = startNote;
/** @property {object} note where script is currently executing */
@@ -139,9 +144,14 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
currentNoteId: currentNote.noteId,
originEntityName: "notes", // currently there's no other entity on frontend which can trigger event
originEntityId: originEntity ? originEntity.noteId : null
}, {
'trilium-source-id': "script"
});
if (ret.success) {
// wait until all the changes done in the script has been synced to frontend before continuing
await ws.waitForSyncId(ret.maxSyncId);
return ret.executionResult;
}
else {
@@ -205,7 +215,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @param {string} noteId
* @method
*/
this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId);
this.reloadNotesAndTheirChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId);
/**
* @param {string} noteId
@@ -241,7 +251,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @param {string} message
*/
this.showMessage = infoService.showMessage;
this.showMessage = toastService.showMessage;
/**
* Show error message to the user.
@@ -249,7 +259,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @param {string} message
*/
this.showError = infoService.showError;
this.showError = toastService.showError;
/**
* Refresh tree
@@ -272,17 +282,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @method
* @returns {NoteFull} active note (loaded into right pane)
*/
this.getActiveNote = noteDetailService.getActiveNote;
this.getActiveTabNote = noteDetailService.getActiveTabNote;
/**
* @method
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
*/
this.getActiveNotePath = () => {
const activeTabContext = noteDetailService.getActiveTabContext();
return activeTabContext ? activeTabContext.notePath : null;
};
this.getActiveTabNotePath = noteDetailService.getActiveTabNotePath;
/**
* This method checks whether user navigated away from the note from which the scripts has been started.
@@ -348,6 +354,15 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
* @return {Promise<NoteShort>}
*/
this.getYearNote = dateNotesService.getYearNote;
/**
* Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
*
* @method
* @param {string} noteId - set hoisted note. 'root' will effectively unhoist
* @return {Promise}
*/
this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
}
export default FrontendScriptApi;

View File

@@ -0,0 +1,78 @@
import toastService from "./toast.js";
import treeService from "./tree.js";
import server from "./server.js";
import ws from "./ws.js";
import utils from "./utils.js";
export async function uploadFiles(parentNoteId, files, options) {
if (files.length === 0) {
return;
}
const taskId = utils.randomString(10);
let noteId;
let counter = 0;
for (const file of files) {
counter++;
const formData = new FormData();
formData.append('upload', file);
formData.append('taskId', taskId);
formData.append('last', counter === files.length ? "true" : "false");
for (const key in options) {
formData.append(key, options[key]);
}
({noteId} = await $.ajax({
url: baseApiUrl + 'notes/' + parentNoteId + '/import',
headers: server.getHeaders(),
data: formData,
dataType: 'json',
type: 'POST',
timeout: 60 * 60 * 1000,
contentType: false, // NEEDED, DON'T REMOVE THIS
processData: false, // NEEDED, DON'T REMOVE THIS
}));
}
}
function makeToast(id, message) {
return {
id: id,
title: "Import status",
message: message,
icon: "plus"
};
}
ws.subscribeToMessages(async message => {
if (message.taskType !== 'import') {
return;
}
if (message.type === 'task-error') {
toastService.closePersistent(message.taskId);
toastService.showError(message.message);
} else if (message.type === 'task-progress-count') {
toastService.showPersistent(makeToast(message.taskId, "Import in progress: " + message.progressCount));
} else if (message.type === 'task-succeeded') {
const toast = makeToast(message.taskId, "Import finished successfully.");
toast.closeAfter = 5000;
toastService.showPersistent(toast);
await treeService.reloadNotes([message.parentNoteId]);
if (message.result.importedNoteId) {
const node = await treeService.activateNote(message.result.importedNoteId);
node.setExpanded(true);
}
}
});
export default {
uploadFiles
}

View File

@@ -1,57 +0,0 @@
import ws from "./ws.js";
import utils from "./utils.js";
function showMessage(message) {
console.debug(utils.now(), "message: ", message);
$.notify({
icon: 'jam jam-check',
message: message
}, getNotifySettings('success', 3000));
}
function showAndLogError(message, delay = 10000) {
showError(message, delay);
ws.logError(message);
}
function showError(message, delay = 10000) {
console.log(utils.now(), "error: ", message);
$.notify({
// options
icon: 'jam jam-alert',
message: message
}, getNotifySettings('danger', delay));
}
function getNotifySettings(type, delay) {
return {
element: 'body',
type: type,
z_index: 90000,
placement: {
from: "top",
align: "center"
},
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
delay: delay
};
}
function throwError(message) {
ws.logError(message);
throw new Error(message);
}
export default {
showMessage,
showError,
showAndLogError,
throwError
}

View File

@@ -14,20 +14,24 @@ function getNotePathFromUrl(url) {
}
}
async function createNoteLink(notePath, noteTitle = null) {
async function createNoteLink(notePath, noteTitle = null, tooltip = true) {
if (!noteTitle) {
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
noteTitle = await treeUtils.getNoteTitle(noteId);
}
const noteLink = $("<a>", {
const $noteLink = $("<a>", {
href: 'javascript:',
text: noteTitle
}).attr('data-action', 'note')
.attr('data-note-path', notePath);
return noteLink;
if (!tooltip) {
$noteLink.addClass("no-tooltip-preview");
}
return $noteLink;
}
async function createNoteLinkWithPath(notePath, noteTitle = null) {
@@ -113,7 +117,7 @@ function addTextToEditor(text) {
}
}
function tabContextMenu(e) {
function newTabContextMenu(e) {
const $link = $(e.target);
const notePath = getNotePathFromLink($link);
@@ -138,9 +142,9 @@ function tabContextMenu(e) {
});
}
$(document).on('contextmenu', '.note-detail-text a', tabContextMenu);
$(document).on('contextmenu', "a[data-action='note']", tabContextMenu);
$(document).on('contextmenu', ".note-detail-render a", tabContextMenu);
$(document).on('contextmenu', '.note-detail-text a', newTabContextMenu);
$(document).on('contextmenu', "a[data-action='note']", newTabContextMenu);
$(document).on('contextmenu', ".note-detail-render a", newTabContextMenu);
// when click on link popup, in case of internal link, just go the the referenced note instead of default behavior
// of opening the link in new window/tab
@@ -159,6 +163,7 @@ $(document).on('mousedown', '.note-detail-text a', function (e) {
}
});
$(document).on('mousedown', '.note-detail-book a', goToLink);
$(document).on('mousedown', '.note-detail-render a', goToLink);
$(document).on('mousedown', '.note-detail-text.ck-read-only a', goToLink);
$(document).on('mousedown', 'span.ck-button__label', e => {

View File

@@ -8,7 +8,7 @@ const SELECTED_PATH_KEY = "data-note-path";
async function autocompleteSource(term, cb) {
const result = await server.get('autocomplete'
+ '?query=' + encodeURIComponent(term)
+ '&activeNoteId=' + noteDetailService.getActiveNoteId());
+ '&activeNoteId=' + noteDetailService.getActiveTabNoteId());
if (result.length === 0) {
result.push({

View File

@@ -90,6 +90,7 @@ async function activateOrOpenNote(noteId) {
});
}
/** @return {TabContext[]} */
function getTabContexts() {
return tabContexts;
}
@@ -107,20 +108,28 @@ function getActiveTabContext() {
return tabContexts.find(tc => tc.tabId === tabId);
}
/** @returns {string|null} */
function getActiveTabNotePath() {
const activeContext = getActiveTabContext();
return activeContext ? activeContext.notePath : null;
}
/** @return {NoteFull} */
function getActiveNote() {
function getActiveTabNote() {
const activeContext = getActiveTabContext();
return activeContext ? activeContext.note : null;
}
function getActiveNoteId() {
const activeNote = getActiveNote();
/** @return {string|null} */
function getActiveTabNoteId() {
const activeNote = getActiveTabNote();
return activeNote ? activeNote.noteId : null;
}
function getActiveNoteType() {
const activeNote = getActiveNote();
/** @return {string|null} */
function getActiveTabNoteType() {
const activeNote = getActiveTabNote();
return activeNote ? activeNote.type : null;
}
@@ -300,7 +309,7 @@ function addDetailLoadedListener(noteId, callback) {
function fireDetailLoaded() {
for (const {noteId, callback} of detailLoadedListeners) {
if (noteId === getActiveNoteId()) {
if (noteId === getActiveTabNoteId()) {
callback();
}
}
@@ -337,7 +346,7 @@ $tabContentsContainer.on("dragover", e => e.preventDefault());
$tabContentsContainer.on("dragleave", e => e.preventDefault());
$tabContentsContainer.on("drop", async e => {
const activeNote = getActiveNote();
const activeNote = getActiveTabNote();
if (!activeNote) {
return;
@@ -345,9 +354,9 @@ $tabContentsContainer.on("drop", async e => {
const files = [...e.originalEvent.dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
const importDialog = await import("../dialogs/import.js");
const importService = await import("./import.js");
importDialog.uploadFiles(activeNote.noteId, files, {
importService.uploadFiles(activeNote.noteId, files, {
safeImport: true,
shrinkImages: true,
textImportedAsText: true,
@@ -385,6 +394,8 @@ tabRow.addListener('tabRemove', async ({ detail }) => {
});
$(tabRow.el).on('contextmenu', '.note-tab', e => {
e.preventDefault();
const tab = $(e.target).closest(".note-tab");
contextMenuService.initContextMenu(e, {
@@ -495,9 +506,6 @@ export default {
switchToNote,
loadNote,
loadNoteDetail,
getActiveNote,
getActiveNoteType,
getActiveNoteId,
focusOnTitle,
focusAndSelectTitle,
saveNotesIfChanged,
@@ -506,6 +514,10 @@ export default {
switchToTab,
getTabContexts,
getActiveTabContext,
getActiveTabNotePath,
getActiveTabNote,
getActiveTabNoteType,
getActiveTabNoteId,
getActiveEditor,
activateOrOpenNote,
clearOpenTabsTask,

View File

@@ -0,0 +1,292 @@
import server from "./server.js";
import linkService from "./link.js";
import utils from "./utils.js";
import treeCache from "./tree_cache.js";
import renderService from "./render.js";
import protectedSessionHolder from "./protected_session_holder.js";
import protectedSessionService from "./protected_session.js";
const MIN_ZOOM_LEVEL = 1;
const MAX_ZOOM_LEVEL = 6;
const ZOOMS = {
1: {
width: "100%",
height: "100%"
},
2: {
width: "49%",
height: "350px"
},
3: {
width: "32%",
height: "250px"
},
4: {
width: "24%",
height: "200px"
},
5: {
width: "19%",
height: "175px"
},
6: {
width: "16%",
height: "150px"
}
};
class NoteDetailBook {
/**
* @param {TabContext} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.$component = ctx.$tabContent.find('.note-detail-book');
this.$content = this.$component.find('.note-detail-book-content');
this.$zoomInButton = this.$component.find('.book-zoom-in-button');
this.$zoomOutButton = this.$component.find('.book-zoom-out-button');
this.$expandChildrenButton = this.$component.find('.expand-children-button');
this.$zoomInButton.click(() => this.setZoom(this.zoomLevel - 1));
this.$zoomOutButton.click(() => this.setZoom(this.zoomLevel + 1));
this.$expandChildrenButton.click(async () => {
for (let i = 1; i < 30; i++) { // protection against infinite cycle
const $unexpandedLinks = this.$content.find('.note-book-open-children-button:visible');
if ($unexpandedLinks.length === 0) {
break;
}
for (const link of $unexpandedLinks) {
const $card = $(link).closest(".note-book-card");
await this.expandCard($card);
}
}
});
this.$content.on('click', '.note-book-open-children-button', async ev => {
const $card = $(ev.target).closest('.note-book-card');
await this.expandCard($card);
});
this.$content.on('click', '.note-book-hide-children-button', async ev => {
const $card = $(ev.target).closest('.note-book-card');
$card.find('.note-book-open-children-button').show();
$card.find('.note-book-hide-children-button').hide();
$card.find('.note-book-children-content').empty();
});
}
async expandCard($card) {
const noteId = $card.attr('data-note-id');
const note = await treeCache.getNote(noteId);
$card.find('.note-book-open-children-button').hide();
$card.find('.note-book-hide-children-button').show();
await this.renderIntoElement(note, $card.find('.note-book-children-content'));
}
setZoom(zoomLevel) {
if (!(zoomLevel in ZOOMS)) {
zoomLevel = this.getDefaultZoomLevel();
}
this.zoomLevel = zoomLevel;
this.$zoomInButton.prop("disabled", zoomLevel === MIN_ZOOM_LEVEL);
this.$zoomOutButton.prop("disabled", zoomLevel === MAX_ZOOM_LEVEL);
this.$content.find('.note-book-card').css("flex-basis", ZOOMS[zoomLevel].width);
this.$content.find('.note-book-content').css("max-height", ZOOMS[zoomLevel].height);
}
async render() {
this.$content.empty();
if (this.isAutoBook()) {
const $addTextLink = $('<a href="javascript:">here</a>').click(() => {
this.ctx.renderComponent(true);
});
this.$content.append($('<div class="note-book-auto-message"></div>')
.append(`This note doesn't have any content so we display its children. Click `)
.append($addTextLink)
.append(' if you want to add some text.'))
}
const zoomLevel = parseInt(await this.ctx.note.getLabelValue('bookZoomLevel')) || this.getDefaultZoomLevel();
this.setZoom(zoomLevel);
await this.renderIntoElement(this.ctx.note, this.$content);
}
async renderIntoElement(note, $container) {
for (const childNote of await note.getChildNotes()) {
const type = this.getRenderingType(childNote);
const $card = $('<div class="note-book-card">')
.attr('data-note-id', childNote.noteId)
.css("flex-basis", ZOOMS[this.zoomLevel].width)
.addClass("type-" + type)
.append($('<h5 class="note-book-title">').append(await linkService.createNoteLink(childNote.noteId, null, false)))
.append($('<div class="note-book-content">')
.css("max-height", ZOOMS[this.zoomLevel].height)
.append(await this.getNoteContent(type, childNote)));
const childCount = childNote.getChildNoteIds().length;
if (childCount > 0) {
const label = `${childCount} child${childCount > 1 ? 'ren' : ''}`;
$card.append($('<div class="note-book-children">')
.append($(`<a class="note-book-open-children-button" href="javascript:">+ Show ${label}</a>`))
.append($(`<a class="note-book-hide-children-button" href="javascript:">- Hide ${label}</a>`).hide())
.append($('<div class="note-book-children-content">'))
);
}
$container.append($card);
}
}
async getNoteContent(type, note) {
if (type === 'text') {
const fullNote = await server.get('notes/' + note.noteId);
const $content = $("<div>").html(fullNote.content);
if (utils.isHtmlEmpty(fullNote.content)) {
return "";
}
else {
return $content;
}
}
else if (type === 'code') {
const fullNote = await server.get('notes/' + note.noteId);
if (fullNote.content.trim() === "") {
return "";
}
return $("<pre>").text(fullNote.content);
}
else if (type === 'image') {
return $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
}
else if (type === 'file') {
function getFileUrl() {
// electron needs absolute URL so we extract current host, port, protocol
return utils.getHost() + "/api/notes/" + note.noteId + "/download";
}
const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
$downloadButton.click(() => utils.download(getFileUrl()));
$openButton.click(() => {
if (utils.isElectron()) {
const open = require("open");
open(getFileUrl(), {url: true});
}
else {
window.location.href = getFileUrl();
}
});
// open doesn't work for protected notes since it works through browser which isn't in protected session
$openButton.toggle(!note.isProtected);
return $('<div>')
.append($downloadButton)
.append(' &nbsp; ')
.append($openButton);
}
else if (type === 'render') {
const $el = $('<div>');
await renderService.render(note, $el, this.ctx);
return $el;
}
else if (type === 'protected-session') {
const $button = $(`<button class="btn btn-sm"><span class="jam jam-door"></span> Enter protected session</button>`)
.click(protectedSessionService.enterProtectedSession);
return $("<div>")
.append("<div>This note is protected and to access it you need to enter password.</div>")
.append("<br/>")
.append($button);
}
else {
return "<em>Content of this note cannot be displayed in the book format</em>";
}
}
/** @return {boolean} true if this is "auto book" activated (empty text note) and not explicit book note */
isAutoBook() {
return this.ctx.note.type !== 'book';
}
getDefaultZoomLevel() {
if (this.isAutoBook()) {
const w = this.$component.width();
if (w <= 600) {
return 1;
} else if (w <= 900) {
return 2;
} else if (w <= 1300) {
return 3;
} else {
return 4;
}
}
else {
return 1;
}
}
getRenderingType(childNote) {
let type = childNote.type;
if (childNote.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
protectedSessionHolder.touchProtectedSession();
}
else {
type = 'protected-session';
}
}
return type;
}
getContent() {}
show() {
this.$component.show();
}
focus() {}
onNoteChange() {}
cleanup() {
this.$content.empty();
}
scrollToTop() {
this.$component.scrollTop(0);
}
}
export default NoteDetailBook;

View File

@@ -1,6 +1,6 @@
import libraryLoader from "./library_loader.js";
import bundleService from "./bundle.js";
import infoService from "./info.js";
import toastService from "./toast.js";
import server from "./server.js";
import noteDetailService from "./note_detail.js";
import utils from "./utils.js";
@@ -102,7 +102,7 @@ class NoteDetailCode {
await server.post('script/run/' + this.ctx.note.noteId);
}
infoService.showMessage("Note executed");
toastService.showMessage("Note executed");
}
onNoteChange(func) {

View File

@@ -23,7 +23,7 @@ class NoteDetailFile {
if (utils.isElectron()) {
const open = require("open");
open(this.getFileUrl());
open(this.getFileUrl(), {url: true});
}
else {
window.location.href = this.getFileUrl();

View File

@@ -1,5 +1,5 @@
import utils from "./utils.js";
import infoService from "./info.js";
import toastService from "./toast.js";
import server from "./server.js";
class NoteDetailImage {
@@ -28,10 +28,10 @@ class NoteDetailImage {
const success = document.execCommand('copy');
if (success) {
infoService.showMessage("Image copied to the clipboard");
toastService.showMessage("Image copied to the clipboard");
}
else {
infoService.showAndLogError("Could not copy the image to clipboard.");
toastService.showAndLogError("Could not copy the image to clipboard.");
}
}
finally {

View File

@@ -4,7 +4,7 @@ import linkService from "./link.js";
import libraryLoader from "./library_loader.js";
import treeService from "./tree.js";
import contextMenuWidget from "./context_menu.js";
import infoService from "./info.js";
import toastService from "./toast.js";
import attributeAutocompleteService from "./attribute_autocomplete.js";
const uniDirectionalOverlays = [
@@ -122,7 +122,7 @@ class NoteDetailRelationMap {
this.clipboard = null;
this.$createChildNote.click(async () => {
const promptDialog = await import('"../dialogs/prompt.js"');
const promptDialog = await import('../dialogs/prompt.js');
const title = await promptDialog.ask({ message: "Enter title of new note", defaultValue: "new note" });
if (!title.trim()) {
@@ -134,7 +134,7 @@ class NoteDetailRelationMap {
target: 'into'
});
infoService.showMessage("Click on canvas to place new note");
toastService.showMessage("Click on canvas to place new note");
// reloading tree so that the new note appears there
// no need to wait for it to finish
@@ -576,7 +576,7 @@ class NoteDetailRelationMap {
const exists = this.mapData.notes.some(n => n.noteId === note.noteId);
if (exists) {
infoService.showError(`Note "${note.title}" is already in the diagram.`);
toastService.showError(`Note "${note.title}" is already in the diagram.`);
continue;
}

View File

@@ -1,5 +1,4 @@
import bundleService from "./bundle.js";
import server from "./server.js";
import renderService from "./render.js";
class NoteDetailRender {
/**
@@ -16,29 +15,10 @@ class NoteDetailRender {
}
async render() {
const attributes = await this.ctx.attributes.getAttributes();
const renderNotes = attributes.filter(attr =>
attr.type === 'relation'
&& attr.name === 'renderNote'
&& !!attr.value);
this.$component.show();
this.$noteDetailRenderHelp.hide();
this.$noteDetailRenderContent.empty();
this.$noteDetailRenderContent.toggle(renderNotes.length > 0);
this.$noteDetailRenderHelp.toggle(renderNotes.length === 0);
for (const renderNote of renderNotes) {
const bundle = await server.get('script/bundle/' + renderNote.value);
this.$noteDetailRenderContent.append(bundle.html);
const $result = await bundleService.executeBundle(bundle, this.ctx.note, this.ctx);
if ($result) {
this.$noteDetailRenderContent.append($result);
}
}
await renderService.render(this.ctx.note, this.$noteDetailRenderContent, this.ctx);
}
getContent() {}

View File

@@ -11,6 +11,7 @@ const NOTE_TYPES = [
{ type: "text", mime: "text/html", title: "Text", selectable: true },
{ type: "relation-map", mime: "application/json", title: "Relation Map", selectable: true },
{ type: "render", mime: '', title: "Render Note", selectable: true },
{ type: "book", mime: '', title: "Book", selectable: true },
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
];

View File

@@ -51,7 +51,11 @@ function reloadOptions() {
return optionsReady;
}
/** just waits for some options without triggering reload */
/**
* just waits for some options without triggering reload
*
* @return {Options}
*/
async function waitForOptions() {
return await optionsReady;
}

View File

@@ -3,7 +3,8 @@ import noteDetailService from './note_detail.js';
import utils from './utils.js';
import server from './server.js';
import protectedSessionHolder from './protected_session_holder.js';
import infoService from "./info.js";
import toastService from "./toast.js";
import ws from "./ws.js";
const $enterProtectedSessionButton = $("#enter-protected-session-button");
const $leaveProtectedSessionButton = $("#leave-protected-session-button");
@@ -37,7 +38,7 @@ async function setupProtectedSession(password) {
const response = await enterProtectedSessionOnServer(password);
if (!response.success) {
infoService.showError("Wrong password.", 3000);
toastService.showError("Wrong password.", 3000);
return;
}
@@ -46,8 +47,7 @@ async function setupProtectedSession(password) {
await treeService.reload();
// it's important that tree has been already reloaded at this point since detail also uses tree cache (for children overview)
// children overview is the reason why we need to reload all tabs
// it's important that tree has been already reloaded at this point since detail also uses tree cache (for book)
await noteDetailService.reloadAllTabs();
if (protectedSessionDeferred !== null) {
@@ -60,7 +60,7 @@ async function setupProtectedSession(password) {
$enterProtectedSessionButton.hide();
$leaveProtectedSessionButton.show();
infoService.showMessage("Protected session has been started.");
toastService.showMessage("Protected session has been started.");
}
async function enterProtectedSessionOnServer(password) {
@@ -70,13 +70,13 @@ async function enterProtectedSessionOnServer(password) {
}
async function protectNoteAndSendToServer() {
if (!noteDetailService.getActiveNote() || noteDetailService.getActiveNote().isProtected) {
if (!noteDetailService.getActiveTabNote() || noteDetailService.getActiveTabNote().isProtected) {
return;
}
await enterProtectedSession();
const note = noteDetailService.getActiveNote();
const note = noteDetailService.getActiveTabNote();
note.isProtected = true;
await noteDetailService.getActiveTabContext().saveNote();
@@ -87,10 +87,10 @@ async function protectNoteAndSendToServer() {
}
async function unprotectNoteAndSendToServer() {
const activeNote = noteDetailService.getActiveNote();
const activeNote = noteDetailService.getActiveTabNote();
if (!activeNote.isProtected) {
infoService.showAndLogError(`Note ${activeNote.noteId} is not protected`);
toastService.showAndLogError(`Note ${activeNote.noteId} is not protected`);
return;
}
@@ -118,12 +118,39 @@ async function protectSubtree(noteId, protect) {
await server.put('notes/' + noteId + "/protect/" + (protect ? 1 : 0));
infoService.showMessage("Request to un/protect sub tree has finished successfully");
treeService.reload();
noteDetailService.reload();
}
function makeToast(message, protectingLabel, text) {
return {
id: message.taskId,
title: protectingLabel + " status",
message: text,
icon: message.data.protect ? "shield-check" : "shield-close"
};
}
ws.subscribeToMessages(async message => {
if (message.taskType !== 'protect-notes') {
return;
}
const protectingLabel = message.data.protect ? "Protecting" : "Unprotecting";
if (message.type === 'task-error') {
toastService.closePersistent(message.taskId);
toastService.showError(message.message);
} else if (message.type === 'task-progress-count') {
toastService.showPersistent(makeToast(message, protectingLabel,protectingLabel + " in progress: " + message.progressCount));
} else if (message.type === 'task-succeeded') {
const toast = makeToast(message, protectingLabel, protectingLabel + " finished successfully.");
toast.closeAfter = 3000;
toastService.showPersistent(toast);
}
});
export default {
protectSubtree,
enterProtectedSession,

View File

@@ -0,0 +1,33 @@
import server from "./server.js";
import bundleService from "./bundle.js";
async function render(note, $el, ctx) {
const attributes = await note.getAttributes();
const renderNoteIds = attributes.filter(attr =>
attr.type === 'relation'
&& attr.name === 'renderNote'
&& !!attr.value).map(rel => rel.value);
$el.empty().toggle(renderNoteIds.length > 0);
for (const renderNoteId of renderNoteIds) {
const bundle = await server.get('script/bundle/' + renderNoteId);
const $scriptContainer = $('<div>');
$el.append($scriptContainer);
$scriptContainer.append(bundle.html);
const $result = await bundleService.executeBundle(bundle, note, ctx, $scriptContainer);
if ($result) {
$scriptContainer.append($result);
}
}
return renderNoteIds.length > 0;
}
export default {
render
}

View File

@@ -2,7 +2,7 @@ import FrontendScriptApi from './frontend_script_api.js';
import utils from './utils.js';
import treeCache from './tree_cache.js';
async function ScriptContext(startNoteId, allNoteIds, originEntity = null, tabContext = null) {
async function ScriptContext(startNoteId, allNoteIds, originEntity = null, tabContext = null, $container = null) {
const modules = {};
const startNote = await treeCache.getNote(startNoteId);
@@ -11,7 +11,7 @@ async function ScriptContext(startNoteId, allNoteIds, originEntity = null, tabCo
return {
modules: modules,
notes: utils.toObject(allNotes, note => [note.noteId, note]),
apis: utils.toObject(allNotes, note => [note.noteId, new FrontendScriptApi(startNote, note, originEntity, tabContext)]),
apis: utils.toObject(allNotes, note => [note.noteId, new FrontendScriptApi(startNote, note, originEntity, tabContext, $container)]),
require: moduleNoteIds => {
return moduleName => {
const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId));

View File

@@ -1,7 +1,7 @@
import treeService from './tree.js';
import treeCache from "./tree_cache.js";
import server from './server.js';
import infoService from "./info.js";
import toastService from "./toast.js";
const $searchInput = $("input[name='search-text']");
const $resetSearchButton = $("#reset-search-button");
@@ -73,7 +73,7 @@ async function doSearch(searchText) {
}
if (searchText.trim().length === 0) {
infoService.showMessage("Please enter search criteria first.");
toastService.showMessage("Please enter search criteria first.");
$searchInput.focus();
@@ -85,7 +85,7 @@ async function doSearch(searchText) {
const response = await server.get('search/' + encodeURIComponent(searchText));
if (!response.success) {
infoService.showError("Search failed.", 3000);
toastService.showError("Search failed.", 3000);
return;
}
@@ -105,7 +105,7 @@ async function doSearch(searchText) {
// have at least some feedback which is good especially in situations
// when the result list does not change with a query
infoService.showMessage("Search finished successfully.");
toastService.showMessage("Search finished successfully.");
}
async function saveSearch() {
@@ -139,7 +139,7 @@ async function refreshSearch() {
activeNode.load(true);
activeNode.setExpanded(true);
infoService.showMessage("Saved search note refreshed.");
toastService.showMessage("Saved search note refreshed.");
}
function init() {

View File

@@ -1,45 +1,48 @@
import utils from './utils.js';
import infoService from "./info.js";
import toastService from "./toast.js";
const REQUEST_LOGGING_ENABLED = false;
function getHeaders() {
function getHeaders(headers) {
// headers need to be lowercase because node.js automatically converts them to lower case
// so hypothetical protectedSessionId becomes protectedsessionid on the backend
// also avoiding using underscores instead of dashes since nginx filters them out by default
const headers = {
const allHeaders = {
...{
'trilium-source-id': glob.sourceId,
'x-csrf-token': glob.csrfToken
},
...headers
};
if (utils.isElectron()) {
// passing it explicitely here because of the electron HTTP bypass
headers.cookie = document.cookie;
allHeaders.cookie = document.cookie;
}
return headers;
return allHeaders;
}
async function get(url) {
return await call('GET', url);
async function get(url, headers = {}) {
return await call('GET', url, null, headers);
}
async function post(url, data) {
return await call('POST', url, data);
async function post(url, data, headers = {}) {
return await call('POST', url, data, headers);
}
async function put(url, data) {
return await call('PUT', url, data);
async function put(url, data, headers = {}) {
return await call('PUT', url, data, headers);
}
async function remove(url) {
return await call('DELETE', url);
async function remove(url, headers = {}) {
return await call('DELETE', url, null, headers);
}
let i = 1;
const reqResolves = {};
async function call(method, url, data) {
async function call(method, url, data, headers = {}) {
if (utils.isElectron()) {
const ipc = require('electron').ipcRenderer;
const requestId = i++;
@@ -53,7 +56,7 @@ async function call(method, url, data) {
ipc.send('server-request', {
requestId: requestId,
headers: getHeaders(),
headers: getHeaders(headers),
method: method,
url: "/" + baseApiUrl + url,
data: data
@@ -84,8 +87,8 @@ async function ajax(url, method, data) {
return await $.ajax(options).catch(e => {
const message = "Error when calling " + method + " " + url + ": " + e.status + " - " + e.statusText;
infoService.showError(message);
infoService.throwError(message);
toastService.showError(message);
toastService.throwError(message);
});
}

View File

@@ -0,0 +1,48 @@
import optionsService from "./options.js";
export async function initSpellCheck() {
const options = await optionsService.waitForOptions();
if (!options.is('spellCheckEnabled')) {
return;
}
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
const {remote, shell} = require('electron');
const spellCheckHandler = new SpellCheckHandler();
spellCheckHandler.attachToInput();
spellCheckHandler.switchLanguage(options.get('spellCheckLanguageCode'));
spellCheckHandler.currentSpellcheckerChanged.subscribe(() => {
console.debug(`Detected language is ${spellCheckHandler.currentSpellcheckerLanguage}`);
spellCheckHandler.currentSpellchecker.add("trilium");
spellCheckHandler.currentSpellchecker.add("https");
spellCheckHandler.currentSpellchecker.add("github");
spellCheckHandler.currentSpellchecker.add("unordered");
});
const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler, null, true, (menu, menuInfo) => {
// There's no menu.remove(id) so this is a convoluted way of removing the 'Search with Google' menu item
const oldItems = menu.items;
menu.clear();
oldItems.forEach(oldItem => {
if (!oldItem.label.includes('Google')) {
menu.append(oldItem);
} else {
menu.append(new remote.MenuItem({
label: 'Search with DuckDuckGo',
click: () => {
shell.openExternal(`https://duckduckgo.com/?q=${encodeURIComponent(menuInfo.selectionText)}`);
}
}));
}
});
});
new ContextMenuListener(async (info) => {
await contextMenuBuilder.showPopupMenu(info);
});
}

View File

@@ -1,18 +1,18 @@
import server from './server.js';
import infoService from "./info.js";
import toastService from "./toast.js";
async function syncNow() {
const result = await server.post('sync/now');
if (result.success) {
infoService.showMessage("Sync finished successfully.");
toastService.showMessage("Sync finished successfully.");
}
else {
if (result.message.length > 50) {
result.message = result.message.substr(0, 50);
}
infoService.showError("Sync failed: " + result.message);
toastService.showError("Sync failed: " + result.message);
}
}
@@ -21,7 +21,7 @@ $("#sync-now-button").click(syncNow);
async function forceNoteSync(noteId) {
await server.post('sync/force-note-sync/' + noteId);
infoService.showMessage("Note added to sync queue.");
toastService.showMessage("Note added to sync queue.");
}
export default {

View File

@@ -23,7 +23,8 @@ const componentClasses = {
'search': "./note_detail_search.js",
'render': "./note_detail_render.js",
'relation-map': "./note_detail_relation_map.js",
'protected-session': "./note_detail_protected_session.js"
'protected-session': "./note_detail_protected_session.js",
'book': "./note_detail_book.js"
};
let showSidebarInNewTab = true;
@@ -64,7 +65,6 @@ class TabContext {
this.$notePathList = this.$tabContent.find(".note-path-list");
this.$notePathCount = this.$tabContent.find(".note-path-count");
this.$noteDetailComponents = this.$tabContent.find(".note-detail-component");
this.$childrenOverview = this.$tabContent.find(".children-overview");
this.$scriptArea = this.$tabContent.find(".note-detail-script-area");
this.$savedIndicator = this.$tabContent.find(".saved-indicator");
this.noteChangeDisabled = false;
@@ -115,13 +115,13 @@ class TabContext {
await this.initComponent();
}
async initComponent() {
const type = this.getComponentType();
async initComponent(disableAutoBook = false) {
this.type = this.getComponentType(disableAutoBook);
if (!(type in this.components)) {
const clazz = await import(componentClasses[type]);
if (!(this.type in this.components)) {
const clazz = await import(componentClasses[this.type]);
this.components[type] = new clazz.default(this);
this.components[this.type] = new clazz.default(this);
}
}
@@ -163,7 +163,7 @@ class TabContext {
this.setTitleBar();
this.closeAutocomplete(); // esp. on windows autocomplete is not getting closed automatically
this.cleanup(); // esp. on windows autocomplete is not getting closed automatically
setTimeout(async () => {
// we include the note into recent list only if the user stayed on the note at least 5 seconds
@@ -179,8 +179,6 @@ class TabContext {
if (utils.isDesktop()) {
this.noteType.update();
this.showChildrenOverview();
}
if (this.sidebar) {
@@ -210,11 +208,11 @@ class TabContext {
this.setTitleBar();
}
async renderComponent() {
await this.initComponent();
async renderComponent(disableAutoBook = false) {
await this.initComponent(disableAutoBook);
for (const componentType in this.components) {
if (componentType !== this.getComponentType()) {
if (componentType !== this.type) {
this.components[componentType].cleanup();
}
}
@@ -283,16 +281,19 @@ class TabContext {
}
getComponent() {
const type = this.getComponentType();
return this.components[type];
return this.components[this.type];
}
getComponentType() {
let type;
getComponentType(disableAutoBook) {
if (!this.note) {
return "empty";
}
if (this.note) {
type = this.note.type;
let type = this.note.type;
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(this.note.content) && this.note.hasChildren()) {
type = 'book';
}
if (this.note.isProtected) {
if (protectedSessionHolder.isProtectedSessionAvailable()) {
@@ -304,9 +305,7 @@ class TabContext {
this.$noteTitle.prop("readonly", true);
}
}
} else {
type = 'empty';
}
return type;
}
@@ -365,33 +364,6 @@ class TabContext {
this.$savedIndicator.fadeOut();
}
async showChildrenOverview() {
const attributes = await this.attributes.getAttributes();
const hideChildrenOverview = attributes.some(attr => attr.type === 'label' && attr.name === 'hideChildrenOverview')
|| this.note.type === 'relation-map'
|| this.note.type === 'image'
|| this.note.type === 'file';
if (hideChildrenOverview) {
this.$childrenOverview.hide();
return;
}
this.$childrenOverview.empty();
for (const childBranch of await this.note.getChildBranches()) {
const link = $('<a>', {
href: 'javascript:',
text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
}).attr('data-action', 'note').attr('data-note-path', this.notePath + '/' + childBranch.noteId);
const childEl = $('<div class="child-overview-item">').html(link);
this.$childrenOverview.append(childEl);
}
this.$childrenOverview.show();
}
async addPath(notePath, isCurrent) {
const title = await treeUtils.getNotePathTitle(notePath);
@@ -439,16 +411,20 @@ class TabContext {
}
async remove() {
if (this.$tabContent) {
// sometimes there are orphan autocompletes after closing the tab
this.closeAutocomplete();
this.cleanup();
await this.saveNoteIfChanged();
this.$tabContent.remove();
}
}
closeAutocomplete() {
if (utils.isDesktop()) {
cleanup() {
if (this.$tabContent && utils.isDesktop()) {
this.$tabContent.find('.aa-input').autocomplete('close');
$('.note-tooltip').remove();
}
}

View File

@@ -0,0 +1,99 @@
import ws from "./ws.js";
import utils from "./utils.js";
function toast(options) {
const $toast = $(`<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="mr-auto"><span class="jam jam-${options.icon}"></span> ${options.title}</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">
${options.message}
</div>
</div>`);
if (options.id) {
$toast.attr("id", "toast-" + options.id);
}
$("#toast-container").append($toast);
$toast.toast({
delay: options.delay || 3000,
autohide: !!options.autohide
});
$toast.on('hidden.bs.toast', e => e.target.remove());
$toast.toast("show");
return $toast;
}
function showPersistent(options) {
let $toast = $("#toast-" + options.id);
if ($toast.length > 0) {
$toast.find('.toast-body').html(options.message);
}
else {
options.autohide = false;
$toast = toast(options);
}
if (options.closeAfter) {
setTimeout(() => $toast.toast('dispose'), options.closeAfter);
}
}
function closePersistent(id) {
$("#toast-persistent-" + id).toast("dispose");
}
function showMessage(message, delay = 2000) {
console.debug(utils.now(), "message: ", message);
toast({
title: "Info",
icon: "check",
message: message,
autohide: true,
delay
});
}
function showAndLogError(message, delay = 10000) {
showError(message, delay);
ws.logError(message);
}
function showError(message, delay = 10000) {
console.log(utils.now(), "error: ", message);
toast({
title: "Error",
icon: 'alert',
message: message,
autohide: true,
delay
});
}
function throwError(message) {
ws.logError(message);
throw new Error(message);
}
export default {
showMessage,
showError,
showAndLogError,
throwError,
showPersistent,
closePersistent
}

View File

@@ -7,7 +7,7 @@ import treeUtils from './tree_utils.js';
import utils from './utils.js';
import server from './server.js';
import treeCache from './tree_cache.js';
import infoService from "./info.js";
import toastService from "./toast.js";
import treeBuilder from "./tree_builder.js";
import treeKeyBindings from "./tree_keybindings.js";
import Branch from '../entities/branch.js';
@@ -25,19 +25,26 @@ const $scrollToActiveNoteButton = $("#scroll-to-active-note-button");
let setFrontendAsLoaded;
const frontendLoaded = new Promise(resolve => { setFrontendAsLoaded = resolve; });
// focused & not active node can happen during multiselection where the node is selected but not activated
// (its content is not displayed in the detail)
/**
* focused & not active node can happen during multiselection where the node is selected but not activated
* (its content is not displayed in the detail)
* @return {FancytreeNode|null}
*/
function getFocusedNode() {
const tree = $tree.fancytree("getTree");
return tree.getFocusNode();
}
// note that if you want to access data like noteId or isProtected, you need to go into "data" property
/**
* note that if you want to access data like noteId or isProtected, you need to go into "data" property
* @return {FancytreeNode|null}
*/
function getActiveNode() {
return $tree.fancytree("getActiveNode");
}
/** @return {FancytreeNode[]} */
async function getNodesByBranchId(branchId) {
utils.assertArguments(branchId);
@@ -46,6 +53,7 @@ async function getNodesByBranchId(branchId) {
return getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branchId);
}
/** @return {FancytreeNode[]} */
function getNodesByNoteId(noteId) {
utils.assertArguments(noteId);
@@ -76,10 +84,12 @@ async function setNodeTitleWithPrefix(node) {
node.setTitle(utils.escapeHtml(title));
}
/** @return {FancytreeNode} */
async function expandToNote(notePath, expandOpts) {
return await getNodeFromPath(notePath, true, expandOpts);
}
/** @return {FancytreeNode} */
function findChildNode(parentNode, childNoteId) {
let foundChildNode = null;
@@ -89,16 +99,26 @@ function findChildNode(parentNode, childNoteId) {
break;
}
}
return foundChildNode;
}
/** @return {FancytreeNode} */
async function getNodeFromPath(notePath, expand = false, expandOpts = {}) {
utils.assertArguments(notePath);
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
/** @var {FancytreeNode} */
let parentNode = null;
for (const childNoteId of await getRunPath(notePath)) {
const runPath = await getRunPath(notePath);
if (!runPath) {
console.error("Could not find run path for notePath:", notePath);
return;
}
for (const childNoteId of runPath) {
if (childNoteId === hoistedNoteId) {
// there must be exactly one node with given hoistedNoteId
parentNode = getNodesByNoteId(childNoteId)[0];
@@ -138,6 +158,7 @@ async function getNodeFromPath(notePath, expand = false, expandOpts = {}) {
return parentNode;
}
/** @return {FancytreeNode} */
async function activateNote(notePath, noteLoadedListener) {
utils.assertArguments(notePath);
@@ -183,6 +204,7 @@ async function activateNote(notePath, noteLoadedListener) {
/**
* Accepts notePath which might or might not be valid and returns an existing path as close to the original
* notePath as possible.
* @return {string|null}
*/
async function resolveNotePath(notePath) {
const runPath = await getRunPath(notePath);
@@ -193,6 +215,8 @@ async function resolveNotePath(notePath) {
/**
* Accepts notePath and tries to resolve it. Part of the path might not be valid because of note moving (which causes
* path change) or other corruption, in that case this will try to get some other valid path to the correct note.
*
* @return {string[]}
*/
async function getRunPath(notePath) {
utils.assertArguments(notePath);
@@ -288,7 +312,7 @@ async function getSomeNotePath(note) {
const parents = await cur.getParentNotes();
if (!parents.length) {
infoService.throwError(`Can't find parents for note ${cur.noteId}`);
toastService.throwError(`Can't find parents for note ${cur.noteId}`);
return;
}
@@ -306,10 +330,12 @@ async function setExpandedToServer(branchId, isExpanded) {
await server.put('branches/' + branchId + '/expanded/' + expandedNum);
}
/** @return {FancytreeNode[]} */
function getSelectedNodes(stopOnParents = false) {
return getTree().getSelectedNodes(stopOnParents);
}
/** @return {FancytreeNode[]} */
function getSelectedOrActiveNodes(node) {
let notes = getSelectedNodes(true);
@@ -499,6 +525,7 @@ function initFancyTree(tree) {
});
}
/** @return {Fancytree} */
function getTree() {
return $tree.fancytree('getTree');
}
@@ -573,14 +600,11 @@ async function scrollToActiveNote() {
}
}
function setBranchBackgroundBasedOnProtectedStatus(noteId) {
getNodesByNoteId(noteId).map(node => node.toggleClass("protected", node.data.isProtected));
}
function setProtected(noteId, isProtected) {
getNodesByNoteId(noteId).map(node => node.data.isProtected = isProtected);
setBranchBackgroundBasedOnProtectedStatus(noteId);
getNodesByNoteId(noteId).map(node => {
node.data.isProtected = isProtected;
node.toggleClass("protected", isProtected);
});
}
async function setNoteTitle(noteId, title) {
@@ -612,7 +636,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
extraOptions.isProtected = false;
}
if (noteDetailService.getActiveNoteType() !== 'text') {
if (noteDetailService.getActiveTabNoteType() !== 'text') {
extraOptions.saveSelection = false;
}
else {
@@ -688,7 +712,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
node.renderTitle();
}
else {
infoService.throwError("Unrecognized target: " + target);
toastService.throwError("Unrecognized target: " + target);
}
clearSelectedNodes(); // to unmark previously active node
@@ -754,13 +778,20 @@ ws.subscribeToMessages(message => {
}
});
ws.subscribeToOutsideSyncMessages(syncData => {
if (syncData.some(sync => sync.entityName === 'branches')
|| syncData.some(sync => sync.entityName === 'notes')) {
// this is a synchronous handler - it returns only once the data has been updated
ws.subscribeToOutsideSyncMessages(async syncData => {
const noteIdsToRefresh = new Set();
console.log(utils.now(), "Reloading tree because of background changes");
// this has the problem that the former parentNoteId might not be invalidated
// and the former location of the branch/note won't be removed.
syncData.filter(sync => sync.entityName === 'branches').forEach(sync => noteIdsToRefresh.add(sync.parentNoteId));
reload();
syncData.filter(sync => sync.entityName === 'notes').forEach(sync => noteIdsToRefresh.add(sync.entityId));
syncData.filter(sync => sync.entityName === 'note_reordering').forEach(sync => noteIdsToRefresh.add(sync.entityId));
if (noteIdsToRefresh.size > 0) {
await reloadNotes(Array.from(noteIdsToRefresh));
}
});
@@ -798,14 +829,32 @@ async function checkFolderStatus(node) {
node.renderTitle();
}
async function reloadNote(noteId) {
await treeCache.reloadChildren(noteId);
async function reloadNotes(noteIds) {
if (noteIds.length === 0) {
return;
}
console.debug("Reloading notes", noteIds);
await treeCache.reloadNotesAndTheirChildren(noteIds);
const activeNotePath = noteDetailService.getActiveTabNotePath();
for (const noteId of noteIds) {
for (const node of getNodesByNoteId(noteId)) {
await node.load(true);
await checkFolderStatus(node);
}
}
if (activeNotePath) {
const node = await getNodeFromPath(activeNotePath);
if (node) {
await node.setActive(true, {noEvents: true}); // this node has been already active so no need to fire events again
}
}
}
window.glob.createNoteInto = createNoteInto;
@@ -838,6 +887,18 @@ $tree.on('mousedown', '.fancytree-title', e => {
}
});
async function duplicateNote(noteId, parentNoteId) {
const {note} = await server.post(`notes/${noteId}/duplicate/${parentNoteId}`);
await reload();
await activateNote(note.noteId);
const origNote = await treeCache.getNote(noteId);
toastService.showMessage(`Note "${origNote.title}" has been duplicated`);
}
utils.bindGlobalShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
$collapseTreeButton.click(() => collapseTree());
@@ -849,7 +910,6 @@ frontendLoaded.then(bundle.executeStartupBundles);
export default {
reload,
collapseTree,
setBranchBackgroundBasedOnProtectedStatus,
setProtected,
activateNote,
getFocusedNode,
@@ -868,12 +928,13 @@ export default {
setExpandedToServer,
getNodesByNoteId,
checkFolderStatus,
reloadNote,
reloadNotes,
loadTreeCache,
expandToNote,
getNodeFromPath,
resolveNotePath,
getSomeNotePath,
focusTree,
scrollToActiveNote
scrollToActiveNote,
duplicateNote
};

View File

@@ -1,4 +1,3 @@
import noteDetailService from "./note_detail.js";
import utils from "./utils.js";
import Branch from "../entities/branch.js";
import server from "./server.js";
@@ -31,6 +30,16 @@ async function prepareBranch(note) {
}
}
const NOTE_TYPE_ICONS = {
"file": "jam jam-attachment",
"image": "jam jam-picture",
"code": "jam jam-terminal",
"render": "jam jam-play",
"search": "jam jam-search-folder",
"relation-map": "jam jam-map",
"book": "jam jam-book"
};
async function getIcon(note) {
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
@@ -48,23 +57,8 @@ async function getIcon(note) {
return "jam jam-file";
}
}
else if (note.type === 'file') {
return "jam jam-attachment"
}
else if (note.type === 'image') {
return "jam jam-picture"
}
else if (note.type === 'code') {
return "jam jam-terminal"
}
else if (note.type === 'render') {
return "jam jam-play"
}
else if (note.type === 'search') {
return "jam jam-search-folder"
}
else if (note.type === 'relation-map') {
return "jam jam-map"
else {
return NOTE_TYPE_ICONS[note.type];
}
}
@@ -161,6 +155,10 @@ async function getExtraClasses(note) {
extraClasses.push(utils.getMimeTypeClass(note.mime));
}
if (note.archived) {
extraClasses.push("archived");
}
return extraClasses.join(" ");
}

View File

@@ -1,7 +1,7 @@
import utils from "./utils.js";
import Branch from "../entities/branch.js";
import NoteShort from "../entities/note_short.js";
import infoService from "./info.js";
import toastService from "./toast.js";
import ws from "./ws.js";
import server from "./server.js";
@@ -54,11 +54,13 @@ class TreeCache {
}
/**
* Reload children of given noteId.
* Reload notes and their children.
*/
async reloadChildren(noteId) {
const resp = await server.post('tree/load', { noteIds: [noteId] });
async reloadNotesAndTheirChildren(noteIds) {
// first load the data before clearing the cache
const resp = await server.post('tree/load', { noteIds });
for (const noteId of noteIds) {
for (const childNoteId of this.children[noteId] || []) {
this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId);
@@ -71,6 +73,7 @@ class TreeCache {
this.children[noteId] = [];
delete this.notes[noteId];
}
this.addResp(resp.notes, resp.branches, resp.relations);
}
@@ -83,12 +86,10 @@ class TreeCache {
// to be able to find parents we need first to make sure it is actually loaded
await this.getNote(noteId);
for (const parentNoteId of this.parents[noteId] || []) {
await this.reloadChildren(parentNoteId);
}
await this.reloadNotesAndTheirChildren(this.parents[noteId] || []);
// this is done to load the new parents for the noteId
await this.reloadChildren(noteId);
await this.reloadNotesAndTheirChildren([noteId]);
}
/** @return {Promise<NoteShort[]>} */
@@ -197,7 +198,7 @@ class TreeCache {
const branchId = this.childParentToBranch[key];
if (!branchId) {
infoService.throwError("Cannot find branch for child-parent=" + key);
toastService.throwError("Cannot find branch for child-parent=" + key);
}
return branchId;

View File

@@ -8,6 +8,7 @@ import syncService from "./sync.js";
import hoistedNoteService from './hoisted_note.js';
import noteDetailService from './note_detail.js';
import clipboard from './clipboard.js';
import protectedSessionHolder from "./protected_session_holder.js";
class TreeContextMenu {
constructor(node) {
@@ -68,6 +69,8 @@ class TreeContextMenu {
enabled: !clipboard.isEmpty() && note.type !== 'search' && noSelectedNotes },
{ title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard",
enabled: !clipboard.isEmpty() && isNotRoot && parentNote.type !== 'search' && noSelectedNotes },
{ title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty",
enabled: noSelectedNotes && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
{ title: "----" },
{ title: "Export", cmd: "export", uiIcon: "empty",
enabled: note.type !== 'search' && noSelectedNotes },
@@ -152,6 +155,11 @@ class TreeContextMenu {
else if (cmd === "unhoist") {
hoistedNoteService.unhoist();
}
else if (cmd === "duplicateNote") {
const branch = await treeCache.getBranch(this.node.data.branchId);
treeService.duplicateNote(this.node.data.noteId, branch.parentNoteId);
}
else {
ws.logError("Unknown command: " + cmd);
}

View File

@@ -201,6 +201,10 @@ function closeActiveDialog() {
}
}
function isHtmlEmpty(html) {
return $("<div>").html(html).text().trim().length === 0 && !html.toLowerCase().includes('<img');
}
export default {
reloadApp,
parseDate,
@@ -231,5 +235,6 @@ export default {
getCookie,
getNoteTypeClass,
getMimeTypeClass,
closeActiveDialog
closeActiveDialog,
isHtmlEmpty
};

View File

@@ -1,5 +1,6 @@
import utils from './utils.js';
import infoService from "./info.js";
import toastService from "./toast.js";
import treeService from "./tree.js";
const $outstandingSyncsCount = $("#outstanding-syncs-count");
@@ -8,8 +9,9 @@ const outsideSyncMessageHandlers = [];
const messageHandlers = [];
let ws;
let lastSyncId;
let lastSyncId = window.glob.maxSyncIdAtLoad;
let lastPingTs;
let syncDataQueue = [];
function logError(message) {
console.log(utils.now(), message); // needs to be separate from .trace()
@@ -35,7 +37,10 @@ function subscribeToAllSyncMessages(messageHandler) {
allSyncMessageHandlers.push(messageHandler);
}
function handleMessage(event) {
// used to serialize sync operations
let consumeQueuePromise = null;
async function handleMessage(event) {
const message = JSON.parse(event.data);
for (const messageHandler of messageHandlers) {
@@ -45,32 +50,76 @@ function handleMessage(event) {
if (message.type === 'sync') {
lastPingTs = Date.now();
$outstandingSyncsCount.html(message.outstandingSyncs);
if (message.data.length > 0) {
console.debug(utils.now(), "Sync data: ", message.data);
lastSyncId = message.data[message.data.length - 1].id;
syncDataQueue.push(...message.data);
// first wait for all the preceding consumers to finish
while (consumeQueuePromise) {
await consumeQueuePromise;
}
for (const syncMessageHandler of allSyncMessageHandlers) {
syncMessageHandler(message.data);
// it's my turn so start it up
consumeQueuePromise = consumeSyncData();
await consumeQueuePromise;
// finish and set to null to signal somebody else can pick it up
consumeQueuePromise = null;
}
const syncData = message.data.filter(sync => sync.sourceId !== glob.sourceId);
for (const syncMessageHandler of outsideSyncMessageHandlers) {
syncMessageHandler(syncData);
}
$outstandingSyncsCount.html(message.outstandingSyncs);
}
else if (message.type === 'sync-hash-check-failed') {
infoService.showError("Sync check failed!", 60000);
toastService.showError("Sync check failed!", 60000);
}
else if (message.type === 'consistency-checks-failed') {
infoService.showError("Consistency checks failed! See logs for details.", 50 * 60000);
toastService.showError("Consistency checks failed! See logs for details.", 50 * 60000);
}
}
let syncIdReachedListeners = [];
function waitForSyncId(desiredSyncId) {
console.log("Waiting for ", desiredSyncId);
if (desiredSyncId <= lastSyncId) {
return Promise.resolve();
}
return new Promise((res, rej) => {
syncIdReachedListeners.push({
desiredSyncId,
resolvePromise: res
})
});
}
async function consumeSyncData() {
if (syncDataQueue.length >= 0) {
const allSyncData = syncDataQueue;
syncDataQueue = [];
const outsideSyncData = allSyncData.filter(sync => sync.sourceId !== glob.sourceId);
// the update process should be synchronous as a whole but individual handlers can run in parallel
await Promise.all([
...allSyncMessageHandlers.map(syncHandler => syncHandler(allSyncData)),
...outsideSyncMessageHandlers.map(syncHandler => syncHandler(outsideSyncData))
]);
lastSyncId = allSyncData[allSyncData.length - 1].id;
}
syncIdReachedListeners
.filter(l => l.desiredSyncId <= lastSyncId)
.forEach(l => l.resolvePromise());
syncIdReachedListeners = syncIdReachedListeners
.filter(l => l.desiredSyncId > lastSyncId);
}
function connectWebSocket() {
const protocol = document.location.protocol === 'https:' ? 'wss' : 'ws';
@@ -112,5 +161,6 @@ export default {
logError,
subscribeToMessages,
subscribeToAllSyncMessages,
subscribeToOutsideSyncMessages
subscribeToOutsideSyncMessages,
waitForSyncId
};

View File

@@ -8,21 +8,13 @@ import server from "../services/server.js";
const TPL = `
<div class="calendar-widget">
<div class="calendar-header">
<button class="calendar-btn" data-calendar-toggle="previous">
<svg height="24" version="1.1" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"></path>
</svg>
</button>
<button class="calendar-btn jam jam-arrow-left" data-calendar-toggle="previous"></button>
<div class="calendar-header__label" data-calendar-label="month">
March 2017
</div>
<button class="calendar-btn" data-calendar-toggle="next">
<svg height="24" version="1.1" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z"></path>
</svg>
</button>
<button class="calendar-btn jam jam-arrow-right" data-calendar-toggle="next"></button>
</div>
<div class="calendar-week">

Some files were not shown because too many files have changed in this diff Show More