mirror of
https://github.com/zadam/trilium.git
synced 2025-10-30 09:56:36 +01:00
Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4266754d8 | ||
|
|
dc288fb18c | ||
|
|
26dfa1ffdb | ||
|
|
153de63f4d | ||
|
|
a89629b3de | ||
|
|
eec850c11f | ||
|
|
e8d63b5647 | ||
|
|
bd8b83898f | ||
|
|
97109efb6c | ||
|
|
3e89855aa3 | ||
|
|
3b148eb6f8 | ||
|
|
4e8d1dac67 | ||
|
|
7779fd1dfe | ||
|
|
960d7dede3 | ||
|
|
224fbdc8cd | ||
|
|
8561201abc | ||
|
|
3c1a809276 | ||
|
|
4b101baf00 | ||
|
|
782127dd91 | ||
|
|
4fc8bace94 | ||
|
|
47a22f6e8d | ||
|
|
17d7ff3ff1 | ||
|
|
3582013a33 | ||
|
|
95bbdb3b6b | ||
|
|
8a57960c6e | ||
|
|
5f4a84d967 | ||
|
|
099e90ed64 | ||
|
|
3d324b954d | ||
|
|
443f389d73 | ||
|
|
08edc521e4 | ||
|
|
f54f6d09b0 | ||
|
|
3219441fdf | ||
|
|
1b0a2b41da | ||
|
|
582429e762 | ||
|
|
238df0fb40 | ||
|
|
89356918f1 | ||
|
|
263b65997c | ||
|
|
2b757bfccd | ||
|
|
c78ca4c9db | ||
|
|
a0395e9866 | ||
|
|
89aa4fbc73 | ||
|
|
74a7802088 | ||
|
|
a89b6711d1 | ||
|
|
b2549b2834 | ||
|
|
959c4cbe64 | ||
|
|
d03d3603d2 | ||
|
|
e1c2573778 | ||
|
|
2af2b45062 | ||
|
|
eabe4775bd | ||
|
|
da9b321aa0 | ||
|
|
c18d8d2d1b | ||
|
|
5f2361ebd5 | ||
|
|
9791dab97d | ||
|
|
85d986534d | ||
|
|
00faf758e8 | ||
|
|
6ba2e5cf73 | ||
|
|
fb3876d28b | ||
|
|
fb975849b9 | ||
|
|
16fef78344 | ||
|
|
e0b4b369dc | ||
|
|
0df7851214 | ||
|
|
5d47c2b23e | ||
|
|
d09b021487 | ||
|
|
910bda860c | ||
|
|
2d92b4931a | ||
|
|
212b719ee9 | ||
|
|
1db892d22f | ||
|
|
535dcb6d12 | ||
|
|
4426362799 | ||
|
|
2c609e8136 | ||
|
|
11b73b79ed | ||
|
|
e70c862e72 | ||
|
|
b3e66d5a83 | ||
|
|
e8cd821e57 | ||
|
|
be7ac74235 | ||
|
|
58fa0832f6 | ||
|
|
1502b9ce66 | ||
|
|
7307ca385f | ||
|
|
c1fd9825aa | ||
|
|
9de7d3fc53 | ||
|
|
3c5db844ba | ||
|
|
e7330c1104 | ||
|
|
ec4586b164 | ||
|
|
91e5f24798 | ||
|
|
38723e0189 | ||
|
|
8c88ce6f65 | ||
|
|
4d22959e28 | ||
|
|
50a28d8c51 | ||
|
|
e25b633ec4 | ||
|
|
75bd395669 | ||
|
|
5e353a5612 | ||
|
|
6b359b7796 | ||
|
|
13f9d037dc | ||
|
|
1911d64c1c | ||
|
|
d4c3f1b3f2 | ||
|
|
3db84daf94 | ||
|
|
2526715aa4 | ||
|
|
04c573e212 | ||
|
|
58f4f5d1e6 | ||
|
|
fa5d982a55 | ||
|
|
108afe8896 | ||
|
|
37da0adb8a | ||
|
|
4f50864ec8 | ||
|
|
30b9ef8604 | ||
|
|
b063b4c528 | ||
|
|
e08b0141a4 | ||
|
|
9d8b8e26a1 | ||
|
|
cb70109ee7 | ||
|
|
e541abbd60 | ||
|
|
940a70adc5 | ||
|
|
88e8eb7e9c | ||
|
|
b365c186a1 | ||
|
|
64c9734f05 | ||
|
|
48c843c087 | ||
|
|
0e4eec10b9 | ||
|
|
a3661cb763 | ||
|
|
115879ec4a | ||
|
|
df11b076bc | ||
|
|
54ecd2ee75 | ||
|
|
2369bcf9fc | ||
|
|
5d8808a2ad | ||
|
|
62b993f06f | ||
|
|
8aa5608085 | ||
|
|
b452d7e5c5 | ||
|
|
9b9d6d86d0 | ||
|
|
7f2755d4a0 | ||
|
|
3b268cc8eb | ||
|
|
6dfe335707 | ||
|
|
c7125d2b50 | ||
|
|
e8a33a5ee7 | ||
|
|
deb0b24c4c | ||
|
|
cafcb67a8a | ||
|
|
4c6e9480e4 | ||
|
|
109bead1c7 | ||
|
|
ae1220b970 | ||
|
|
b89a2df462 | ||
|
|
8b5536ee3a | ||
|
|
647790885d | ||
|
|
227c3e4dcc | ||
|
|
4eb2407c73 | ||
|
|
43e12fbea2 | ||
|
|
2a3091f788 | ||
|
|
742df25bc2 | ||
|
|
9be1d1f697 | ||
|
|
ed52f93bbb | ||
|
|
3466a19397 | ||
|
|
fe53e2351c | ||
|
|
5c35b870eb | ||
|
|
90d091aedb | ||
|
|
0a05a40186 | ||
|
|
7482ed063b | ||
|
|
70e343f2fc | ||
|
|
358f3a7291 | ||
|
|
6161b1c193 | ||
|
|
94b57dadd7 | ||
|
|
81fbefb9cd | ||
|
|
6d6695e3a9 | ||
|
|
4c308ad68f | ||
|
|
989a003d2f | ||
|
|
ccdb41841e | ||
|
|
0a94622413 | ||
|
|
5769587305 | ||
|
|
ffbfccb701 | ||
|
|
56ce23fc36 | ||
|
|
cba7e5a59f | ||
|
|
86cf8f3202 | ||
|
|
907cdd8fcb | ||
|
|
7ea53d468e | ||
|
|
586d6b4557 | ||
|
|
8f68ff1932 | ||
|
|
a1ea2c9115 | ||
|
|
e8ce81a133 | ||
|
|
aff12950f0 | ||
|
|
75c58cbf79 | ||
|
|
87a1e98fa2 | ||
|
|
d1eacbb574 | ||
|
|
71d248cd87 | ||
|
|
ac608b9334 | ||
|
|
32020d78b5 | ||
|
|
ff853c7d0a | ||
|
|
8526cb2315 | ||
|
|
dc89f72e75 | ||
|
|
657ff16267 | ||
|
|
ed759f5585 |
2
.idea/dataSources.xml
generated
2
.idea/dataSources.xml
generated
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
<data-source source="LOCAL" name="document.db" uuid="a2c75661-f9e2-478f-a69f-6a9409e69997">
|
<data-source source="LOCAL" name="document.db" uuid="b0b03187-36c8-4ec1-bdab-fd4273cd692e">
|
||||||
<driver-ref>sqlite.xerial</driver-ref>
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
|||||||
@@ -1,662 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<dataSource name="document.db">
|
|
||||||
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.17">
|
|
||||||
<root id="1">
|
|
||||||
<ServerVersion>3.25.1</ServerVersion>
|
|
||||||
</root>
|
|
||||||
<schema id="2" parent="1" name="main">
|
|
||||||
<Current>1</Current>
|
|
||||||
</schema>
|
|
||||||
<collation id="3" parent="1" name="BINARY"/>
|
|
||||||
<collation id="4" parent="1" name="NOCASE"/>
|
|
||||||
<collation id="5" parent="1" name="RTRIM"/>
|
|
||||||
<table id="6" parent="2" name="api_tokens"/>
|
|
||||||
<table id="7" parent="2" name="attributes"/>
|
|
||||||
<table id="8" parent="2" name="branches"/>
|
|
||||||
<table id="9" parent="2" name="note_contents"/>
|
|
||||||
<table id="10" parent="2" name="note_revision_contents"/>
|
|
||||||
<table id="11" parent="2" name="note_revisions"/>
|
|
||||||
<table id="12" parent="2" name="notes"/>
|
|
||||||
<table id="13" parent="2" name="options"/>
|
|
||||||
<table id="14" parent="2" name="recent_notes"/>
|
|
||||||
<table id="15" parent="2" name="source_ids"/>
|
|
||||||
<table id="16" parent="2" name="sqlite_master">
|
|
||||||
<System>1</System>
|
|
||||||
</table>
|
|
||||||
<table id="17" parent="2" name="sqlite_sequence">
|
|
||||||
<System>1</System>
|
|
||||||
</table>
|
|
||||||
<table id="18" parent="2" name="sync"/>
|
|
||||||
<column id="19" parent="6" name="apiTokenId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="20" parent="6" name="token">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="21" parent="6" name="utcDateCreated">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="22" parent="6" name="isDeleted">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="23" parent="6" name="hash">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>apiTokenId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="25" parent="6">
|
|
||||||
<ColNames>apiTokenId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="26" parent="7" name="attributeId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="27" parent="7" name="noteId">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="28" parent="7" name="type">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="29" parent="7" name="name">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="30" parent="7" name="value">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>''</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="31" parent="7" name="position">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="32" parent="7" name="utcDateCreated">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="33" parent="7" name="utcDateModified">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="34" parent="7" name="isDeleted">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="35" parent="7" name="deleteId">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<DefaultExpression>NULL</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="36" parent="7" name="hash">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="37" parent="7" name="isInheritable">
|
|
||||||
<Position>12</Position>
|
|
||||||
<DataType>int|0s</DataType>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>attributeId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="40" parent="7" name="IDX_attributes_name_value">
|
|
||||||
<ColNames>name
|
|
||||||
value</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="41" parent="7" name="IDX_attributes_value_index">
|
|
||||||
<ColNames>value</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="42" parent="7">
|
|
||||||
<ColNames>attributeId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="43" parent="8" name="branchId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="44" parent="8" name="noteId">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="45" parent="8" name="parentNoteId">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="46" parent="8" name="notePosition">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="47" parent="8" name="prefix">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="48" parent="8" name="isExpanded">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="49" parent="8" name="isDeleted">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="50" parent="8" name="deleteId">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<DefaultExpression>NULL</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="51" parent="8" name="utcDateModified">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="52" parent="8" name="utcDateCreated">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="53" parent="8" name="hash">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>branchId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
|
||||||
<ColNames>noteId
|
|
||||||
parentNoteId</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
|
||||||
<ColNames>parentNoteId</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="57" parent="8">
|
|
||||||
<ColNames>branchId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="58" parent="9" name="noteId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="59" parent="9" name="content">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<DefaultExpression>NULL</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="60" parent="9" name="hash">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="61" parent="9" name="utcDateModified">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="63" parent="9">
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="64" parent="10" name="noteRevisionId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="65" parent="10" name="content">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="66" parent="10" name="hash">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>''</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="67" parent="10" name="utcDateModified">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>noteRevisionId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="69" parent="10">
|
|
||||||
<ColNames>noteRevisionId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_revision_contents_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="70" parent="11" name="noteRevisionId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="71" parent="11" name="noteId">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="72" parent="11" name="title">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="73" parent="11" name="contentLength">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="74" parent="11" name="isErased">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="75" parent="11" name="isProtected">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="76" parent="11" name="utcDateLastEdited">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="77" parent="11" name="utcDateCreated">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="78" parent="11" name="utcDateModified">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="79" parent="11" name="dateLastEdited">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="80" parent="11" name="dateCreated">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="81" parent="11" name="type">
|
|
||||||
<Position>12</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>''</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="82" parent="11" name="mime">
|
|
||||||
<Position>13</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>''</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="83" parent="11" name="hash">
|
|
||||||
<Position>14</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>''</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>noteRevisionId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
|
||||||
<ColNames>utcDateLastEdited</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
|
||||||
<ColNames>utcDateCreated</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
|
||||||
<ColNames>dateLastEdited</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
|
||||||
<ColNames>dateCreated</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="90" parent="11">
|
|
||||||
<ColNames>noteRevisionId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="91" parent="12" name="noteId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="92" parent="12" name="title">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>"note"</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="93" parent="12" name="contentLength">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="94" parent="12" name="isProtected">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="95" parent="12" name="type">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>'text'</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="96" parent="12" name="mime">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>'text/html'</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="97" parent="12" name="hash">
|
|
||||||
<Position>7</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="98" parent="12" name="isDeleted">
|
|
||||||
<Position>8</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="99" parent="12" name="deleteId">
|
|
||||||
<Position>9</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<DefaultExpression>NULL</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="100" parent="12" name="isErased">
|
|
||||||
<Position>10</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="101" parent="12" name="dateCreated">
|
|
||||||
<Position>11</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="102" parent="12" name="dateModified">
|
|
||||||
<Position>12</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="103" parent="12" name="utcDateCreated">
|
|
||||||
<Position>13</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="104" parent="12" name="utcDateModified">
|
|
||||||
<Position>14</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="106" parent="12" name="IDX_notes_title">
|
|
||||||
<ColNames>title</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="107" parent="12" name="IDX_notes_type">
|
|
||||||
<ColNames>type</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
|
||||||
<ColNames>isDeleted</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
|
||||||
<ColNames>dateCreated</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="110" parent="12" name="IDX_notes_dateModified">
|
|
||||||
<ColNames>dateModified</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
|
||||||
<ColNames>utcDateCreated</ColNames>
|
|
||||||
</index>
|
|
||||||
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
|
||||||
<ColNames>utcDateModified</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="113" parent="12">
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="114" parent="13" name="name">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="115" parent="13" name="value">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="116" parent="13" name="isSynced">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="117" parent="13" name="hash">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="118" parent="13" name="utcDateCreated">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="119" parent="13" name="utcDateModified">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>name</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="121" parent="13">
|
|
||||||
<ColNames>name</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="122" parent="14" name="noteId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="123" parent="14" name="notePath">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="124" parent="14" name="hash">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>""</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="125" parent="14" name="utcDateCreated">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="126" parent="14" name="isDeleted">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>INT|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<key id="128" parent="14">
|
|
||||||
<ColNames>noteId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="129" parent="15" name="sourceId">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="130" parent="15" name="utcDateCreated">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
|
||||||
<NameSurrogate>1</NameSurrogate>
|
|
||||||
<ColNames>sourceId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
|
||||||
<ColNames>utcDateCreated</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="133" parent="15">
|
|
||||||
<ColNames>sourceId</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
|
||||||
</key>
|
|
||||||
<column id="134" parent="16" name="type">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="135" parent="16" name="name">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="136" parent="16" name="tbl_name">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="137" parent="16" name="rootpage">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>int|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="138" parent="16" name="sql">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>text|0s</DataType>
|
|
||||||
</column>
|
|
||||||
<column id="139" parent="17" name="name">
|
|
||||||
<Position>1</Position>
|
|
||||||
</column>
|
|
||||||
<column id="140" parent="17" name="seq">
|
|
||||||
<Position>2</Position>
|
|
||||||
</column>
|
|
||||||
<column id="141" parent="18" name="id">
|
|
||||||
<Position>1</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
|
||||||
<column id="142" parent="18" name="entityName">
|
|
||||||
<Position>2</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="143" parent="18" name="entityId">
|
|
||||||
<Position>3</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="144" parent="18" name="sourceId">
|
|
||||||
<Position>4</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<column id="145" parent="18" name="isSynced">
|
|
||||||
<Position>5</Position>
|
|
||||||
<DataType>INTEGER|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
<DefaultExpression>0</DefaultExpression>
|
|
||||||
</column>
|
|
||||||
<column id="146" parent="18" name="utcSyncDate">
|
|
||||||
<Position>6</Position>
|
|
||||||
<DataType>TEXT|0s</DataType>
|
|
||||||
<NotNull>1</NotNull>
|
|
||||||
</column>
|
|
||||||
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
|
||||||
<ColNames>entityName
|
|
||||||
entityId</ColNames>
|
|
||||||
<Unique>1</Unique>
|
|
||||||
</index>
|
|
||||||
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
|
||||||
<ColNames>utcSyncDate</ColNames>
|
|
||||||
</index>
|
|
||||||
<key id="149" parent="18">
|
|
||||||
<ColNames>id</ColNames>
|
|
||||||
<Primary>1</Primary>
|
|
||||||
</key>
|
|
||||||
</database-model>
|
|
||||||
</dataSource>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#n:main
|
|
||||||
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
|
||||||
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +1,7 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="JSUnfilteredForInLoop" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
<option name="processCode" value="true" />
|
<option name="processCode" value="true" />
|
||||||
<option name="processLiterals" value="true" />
|
<option name="processLiterals" value="true" />
|
||||||
|
|||||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:12.16.2-alpine
|
FROM node:12.16.3-alpine
|
||||||
|
|
||||||
# Create app directory
|
# Create app directory
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ cp images/app-icons/png/128x128.png $BUILD_DIR/icon.png
|
|||||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||||
rm -r $BUILD_DIR/swiftshader
|
rm -r $BUILD_DIR/swiftshader
|
||||||
|
|
||||||
|
cp bin/tpl/portable-trilium.sh $BUILD_DIR/
|
||||||
|
chmod 755 $BUILD_DIR/portable-trilium.sh
|
||||||
|
|
||||||
echo "Packaging linux x64 electron distribution..."
|
echo "Packaging linux x64 electron distribution..."
|
||||||
VERSION=`jq -r ".version" package.json`
|
VERSION=`jq -r ".version" package.json`
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PKG_DIR=dist/trilium-linux-x64-server
|
PKG_DIR=dist/trilium-linux-x64-server
|
||||||
NODE_VERSION=12.16.2
|
NODE_VERSION=12.16.3
|
||||||
|
|
||||||
if [ "$1" != "DONTCOPY" ]
|
if [ "$1" != "DONTCOPY" ]
|
||||||
then
|
then
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
|
|||||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||||
rm -r $BUILD_DIR/swiftshader
|
rm -r $BUILD_DIR/swiftshader
|
||||||
|
|
||||||
|
cp bin/tpl/portable-trilium.bat $BUILD_DIR/
|
||||||
|
|
||||||
echo "Zipping windows x64 electron distribution..."
|
echo "Zipping windows x64 electron distribution..."
|
||||||
VERSION=`jq -r ".version" package.json`
|
VERSION=`jq -r ".version" package.json`
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ if [[ $# -eq 0 ]] ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
npm run webpack
|
||||||
|
|
||||||
DIR=$1
|
DIR=$1
|
||||||
|
|
||||||
rm -rf $DIR
|
rm -rf $DIR
|
||||||
@@ -25,11 +27,15 @@ cp -r electron.js $DIR/
|
|||||||
cp webpack-* $DIR/
|
cp webpack-* $DIR/
|
||||||
|
|
||||||
# run in subshell (so we return to original dir)
|
# run in subshell (so we return to original dir)
|
||||||
(cd $DIR && npm install --only=prod && npm run webpack)
|
(cd $DIR && npm install --only=prod)
|
||||||
|
|
||||||
find $DIR/libraries -name "*.map" -type f -delete
|
find $DIR/libraries -name "*.map" -type f -delete
|
||||||
|
|
||||||
rm -r $DIR/src/public/app
|
rm -r $DIR/src/public/app
|
||||||
|
|
||||||
|
rm -r $DIR/node_modules/sqlite3/build
|
||||||
|
rm -r $DIR/node_modules/sqlite3/deps
|
||||||
|
|
||||||
sed -i -e 's/app\/desktop.js/app-dist\/desktop.js/g' $DIR/src/views/desktop.ejs
|
sed -i -e 's/app\/desktop.js/app-dist\/desktop.js/g' $DIR/src/views/desktop.ejs
|
||||||
sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs
|
sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs
|
||||||
|
sed -i -e 's/app\/setup.js/app-dist\/setup.js/g' $DIR/src/views/setup.ejs
|
||||||
|
|||||||
4
bin/tpl/portable-trilium.bat
Normal file
4
bin/tpl/portable-trilium.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SET DIR=%~dp0
|
||||||
|
SET TRILIUM_DATA_DIR=%DIR%\trilium-data
|
||||||
|
cd %DIR%
|
||||||
|
start trilium.exe
|
||||||
7
bin/tpl/portable-trilium.sh
Executable file
7
bin/tpl/portable-trilium.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
DIR=`dirname "$0"`
|
||||||
|
export TRILIUM_DATA_DIR="$DIR/trilium-data"
|
||||||
|
|
||||||
|
"$DIR/trilium"
|
||||||
|
|
||||||
BIN
db/demo.zip
BIN
db/demo.zip
Binary file not shown.
4
db/migrations/0159__fix_isSynced_in_sync_rows.sql
Normal file
4
db/migrations/0159__fix_isSynced_in_sync_rows.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
UPDATE sync SET isSynced = 1 WHERE entityName != 'options' OR (
|
||||||
|
entityName = 'options'
|
||||||
|
AND 1 = (SELECT isSynced FROM options WHERE name = sync.entityId)
|
||||||
|
)
|
||||||
@@ -879,7 +879,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -5483,7 +5483,7 @@ Typical use case is when new note has been created, we should wait until it is s
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -507,7 +507,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -6813,7 +6813,7 @@ Cache is note instance scoped.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default Attribute;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default Branch;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default NoteComplement;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ export default NoteShort;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -95,6 +95,275 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="subsection-title">Methods</h3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="decorateWidget"><span class="type-signature"></span>decorateWidget<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
for overriding
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line93">line 93</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="doRenderBody"><span class="type-signature">(async) </span>doRenderBody<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
for overriding
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line96">line 96</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="widgetCollapsedStateChangedEvent"><span class="type-signature"></span>widgetCollapsedStateChangedEvent<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
|
||||||
|
separately but should behave uniformly for the user.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line86">line 86</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -333,7 +602,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ export default FrontendScriptApi;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
151
docs/frontend_api/widgets_collapsible_widget.js.html
Normal file
151
docs/frontend_api/widgets_collapsible_widget.js.html
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>JSDoc: Source: widgets/collapsible_widget.js</title>
|
||||||
|
|
||||||
|
<script src="scripts/prettify/prettify.js"> </script>
|
||||||
|
<script src="scripts/prettify/lang-css.js"> </script>
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
|
||||||
|
<h1 class="page-title">Source: widgets/collapsible_widget.js</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<article>
|
||||||
|
<pre class="prettyprint source linenums"><code>import TabAwareWidget from "./tab_aware_widget.js";
|
||||||
|
import options from "../services/options.js";
|
||||||
|
|
||||||
|
const WIDGET_TPL = `
|
||||||
|
<div class="card widget">
|
||||||
|
<div class="card-header">
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-sm widget-title" data-toggle="collapse" data-target="#[to be set]">
|
||||||
|
Collapsible Group Item
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="widget-help external no-arrow bx bx-info-circle"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="widget-header-actions"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="[to be set]" class="collapse body-wrapper" style="transition: none; ">
|
||||||
|
<div class="card-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class CollapsibleWidget extends TabAwareWidget {
|
||||||
|
get widgetTitle() { return "Untitled widget"; }
|
||||||
|
|
||||||
|
get headerActions() { return []; }
|
||||||
|
|
||||||
|
get help() { return {}; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(WIDGET_TPL);
|
||||||
|
this.$widget.find('[data-target]').attr('data-target', "#" + this.componentId);
|
||||||
|
|
||||||
|
this.$bodyWrapper = this.$widget.find('.body-wrapper');
|
||||||
|
this.$bodyWrapper.attr('id', this.componentId); // for toggle to work we need id
|
||||||
|
|
||||||
|
this.widgetName = this.constructor.name;
|
||||||
|
|
||||||
|
if (!options.is(this.widgetName + 'Collapsed')) {
|
||||||
|
this.$bodyWrapper.collapse("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
// using immediate variants of the event so that the previous collapse is not caught
|
||||||
|
this.$bodyWrapper.on('hide.bs.collapse', () => this.saveCollapsed(true));
|
||||||
|
this.$bodyWrapper.on('show.bs.collapse', () => this.saveCollapsed(false));
|
||||||
|
|
||||||
|
this.$body = this.$bodyWrapper.find('.card-body');
|
||||||
|
|
||||||
|
this.$title = this.$widget.find('.widget-title');
|
||||||
|
this.$title.text(this.widgetTitle);
|
||||||
|
|
||||||
|
this.$help = this.$widget.find('.widget-help');
|
||||||
|
|
||||||
|
if (this.help.title) {
|
||||||
|
this.$help.attr("title", this.help.title);
|
||||||
|
this.$help.attr("href", this.help.url || "javascript:");
|
||||||
|
|
||||||
|
if (!this.help.url) {
|
||||||
|
this.$help.addClass('no-link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$help.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$headerActions = this.$widget.find('.widget-header-actions');
|
||||||
|
this.$headerActions.append(...this.headerActions);
|
||||||
|
|
||||||
|
this.initialized = this.doRenderBody();
|
||||||
|
|
||||||
|
this.decorateWidget();
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveCollapsed(collapse) {
|
||||||
|
options.save(this.widgetName + 'Collapsed', collapse.toString());
|
||||||
|
|
||||||
|
this.triggerEvent(`widgetCollapsedStateChanged`, {widgetName: this.widgetName, collapse});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
|
||||||
|
* separately but should behave uniformly for the user.
|
||||||
|
*/
|
||||||
|
widgetCollapsedStateChangedEvent({widgetName, collapse}) {
|
||||||
|
if (widgetName === this.widgetName) {
|
||||||
|
this.$bodyWrapper.toggleClass('show', !collapse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** for overriding */
|
||||||
|
decorateWidget() {}
|
||||||
|
|
||||||
|
/** for overriding */
|
||||||
|
async doRenderBody() {}
|
||||||
|
|
||||||
|
isExpanded() {
|
||||||
|
return this.$bodyWrapper.hasClass("show");
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<br class="clear">
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.4</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script> prettyPrint(); </script>
|
||||||
|
<script src="scripts/linenumber.js"> </script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,23 +1,53 @@
|
|||||||
/*
|
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
|
||||||
* !!!!!!! This stylesheet is heavily modified compared to the original for similarity with in-editor look !!!!!!!
|
|
||||||
* This is used for printing and tar HTML export
|
|
||||||
|
|
||||||
* CKEditor 5 (v17.0.0) content styles.
|
.ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
|
||||||
* Generated on Fri, 13 Mar 2020 13:27:10 GMT.
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CKEditor 5 (v21.0.0) content styles.
|
||||||
|
* Generated on Wed, 29 Jul 2020 12:14:43 GMT.
|
||||||
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
|
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--ck-highlight-marker-blue: #72cdfd;
|
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
|
||||||
--ck-highlight-marker-green: #63f963;
|
--ck-color-mention-text: hsl(341, 100%, 30%);
|
||||||
--ck-highlight-marker-pink: #fc7999;
|
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
|
||||||
--ck-highlight-marker-yellow: #fdfd77;
|
--ck-highlight-marker-green: hsl(120, 93%, 68%);
|
||||||
--ck-highlight-pen-green: #118800;
|
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
|
||||||
--ck-highlight-pen-red: #e91313;
|
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
|
||||||
|
--ck-highlight-pen-green: hsl(112, 100%, 27%);
|
||||||
|
--ck-highlight-pen-red: hsl(0, 85%, 49%);
|
||||||
--ck-image-style-spacing: 1.5em;
|
--ck-image-style-spacing: 1.5em;
|
||||||
--ck-todo-list-checkmark-size: 16px;
|
--ck-todo-list-checkmark-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ckeditor5-image/theme/image.css */
|
||||||
|
.ck-content .image {
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
text-align: center;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
/* ckeditor5-image/theme/image.css */
|
||||||
|
.ck-content .image img {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
/* ckeditor5-image/theme/imagecaption.css */
|
||||||
|
.ck-content .image > figcaption {
|
||||||
|
display: table-caption;
|
||||||
|
caption-side: bottom;
|
||||||
|
word-break: break-word;
|
||||||
|
color: hsl(0, 0%, 20%);
|
||||||
|
background-color: hsl(0, 0%, 97%);
|
||||||
|
padding: .6em;
|
||||||
|
font-size: .75em;
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
/* ckeditor5-image/theme/imageresize.css */
|
/* ckeditor5-image/theme/imageresize.css */
|
||||||
.ck-content .image.image_resized {
|
.ck-content .image.image_resized {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -32,37 +62,11 @@
|
|||||||
.ck-content .image.image_resized > figcaption {
|
.ck-content .image.image_resized > figcaption {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
/* ckeditor5-basic-styles/theme/code.css */
|
|
||||||
.ck-content code {
|
|
||||||
background-color: hsla(0, 0%, 78%, 0.3);
|
|
||||||
padding: .15em;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
/* ckeditor5-image/theme/image.css */
|
|
||||||
.ck-content .image {
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
text-align: center;
|
|
||||||
margin: 1em auto;
|
|
||||||
}
|
|
||||||
/* ckeditor5-image/theme/image.css */
|
|
||||||
.ck-content .image > img {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 100%;
|
|
||||||
min-width: 50px;
|
|
||||||
}
|
|
||||||
/* ckeditor5-image/theme/imagestyle.css */
|
|
||||||
.ck-content .image-style-side,
|
|
||||||
.ck-content .image-style-align-left,
|
|
||||||
.ck-content .image-style-align-center,
|
|
||||||
.ck-content .image-style-align-right {
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
/* ckeditor5-image/theme/imagestyle.css */
|
/* ckeditor5-image/theme/imagestyle.css */
|
||||||
.ck-content .image-style-side {
|
.ck-content .image-style-side {
|
||||||
float: right;
|
float: right;
|
||||||
margin-left: var(--ck-image-style-spacing);
|
margin-left: var(--ck-image-style-spacing);
|
||||||
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
/* ckeditor5-image/theme/imagestyle.css */
|
/* ckeditor5-image/theme/imagestyle.css */
|
||||||
.ck-content .image-style-align-left {
|
.ck-content .image-style-align-left {
|
||||||
@@ -79,42 +83,6 @@
|
|||||||
float: right;
|
float: right;
|
||||||
margin-left: var(--ck-image-style-spacing);
|
margin-left: var(--ck-image-style-spacing);
|
||||||
}
|
}
|
||||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
|
||||||
.ck-content .page-break {
|
|
||||||
position: relative;
|
|
||||||
clear: both;
|
|
||||||
padding: 5px 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
|
||||||
.ck-content .page-break::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
border-bottom: 2px dashed hsl(0, 0%, 77%);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
|
||||||
.ck-content .page-break__label {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
padding: .3em .6em;
|
|
||||||
display: block;
|
|
||||||
text-transform: uppercase;
|
|
||||||
border: 1px solid hsl(0, 0%, 77%);
|
|
||||||
border-radius: 2px;
|
|
||||||
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
|
|
||||||
font-size: 0.75em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: hsl(0, 0%, 20%);
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
/* ckeditor5-block-quote/theme/blockquote.css */
|
/* ckeditor5-block-quote/theme/blockquote.css */
|
||||||
.ck-content blockquote {
|
.ck-content blockquote {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -130,38 +98,6 @@
|
|||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-right: solid 5px hsl(0, 0%, 80%);
|
border-right: solid 5px hsl(0, 0%, 80%);
|
||||||
}
|
}
|
||||||
/* ckeditor5-media-embed/theme/mediaembed.css */
|
|
||||||
.ck-content .media {
|
|
||||||
clear: both;
|
|
||||||
margin: 1em 0;
|
|
||||||
display: block;
|
|
||||||
min-width: 15em;
|
|
||||||
}
|
|
||||||
/* ckeditor5-table/theme/table.css */
|
|
||||||
.ck-content .table {
|
|
||||||
margin: 1em auto;
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
/* ckeditor5-table/theme/table.css */
|
|
||||||
.ck-content .table table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1px double hsl(0, 0%, 70%);
|
|
||||||
}
|
|
||||||
/* ckeditor5-table/theme/table.css */
|
|
||||||
.ck-content .table table td,
|
|
||||||
.ck-content .table table th {
|
|
||||||
min-width: 2em;
|
|
||||||
padding: .4em;
|
|
||||||
border-color: hsl(0, 0%, 75%);
|
|
||||||
}
|
|
||||||
/* ckeditor5-table/theme/table.css */
|
|
||||||
.ck-content .table table th {
|
|
||||||
font-weight: bold;
|
|
||||||
background: hsla(0, 0%, 0%, 5%);
|
|
||||||
}
|
|
||||||
/* ckeditor5-list/theme/todolist.css */
|
/* ckeditor5-list/theme/todolist.css */
|
||||||
.ck-content .todo-list {
|
.ck-content .todo-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -229,16 +165,12 @@
|
|||||||
.ck-content .todo-list .todo-list__label .todo-list__label__description {
|
.ck-content .todo-list .todo-list__label .todo-list__label__description {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
/* ckeditor5-image/theme/imagecaption.css */
|
/* ckeditor5-horizontal-line/theme/horizontalline.css */
|
||||||
.ck-content .image > figcaption {
|
.ck-content hr {
|
||||||
display: table-caption;
|
margin: 15px 0;
|
||||||
caption-side: bottom;
|
height: 4px;
|
||||||
word-break: break-word;
|
background: hsl(0, 0%, 87%);
|
||||||
color: hsl(0, 0%, 20%);
|
border: 0;
|
||||||
background-color: hsl(0, 0%, 97%);
|
|
||||||
padding: .6em;
|
|
||||||
font-size: .75em;
|
|
||||||
outline-offset: -1px;
|
|
||||||
}
|
}
|
||||||
/* ckeditor5-highlight/theme/highlight.css */
|
/* ckeditor5-highlight/theme/highlight.css */
|
||||||
.ck-content .marker-yellow {
|
.ck-content .marker-yellow {
|
||||||
@@ -266,17 +198,108 @@
|
|||||||
color: var(--ck-highlight-pen-green);
|
color: var(--ck-highlight-pen-green);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
/* ckeditor5-horizontal-line/theme/horizontalline.css */
|
/* ckeditor5-font/theme/fontsize.css */
|
||||||
.ck-content hr {
|
.ck-content .text-tiny {
|
||||||
border-width: 1px 0 0;
|
font-size: .7em;
|
||||||
border-style: solid;
|
}
|
||||||
border-color: hsl(0, 0%, 37%);
|
/* ckeditor5-font/theme/fontsize.css */
|
||||||
margin: 0;
|
.ck-content .text-small {
|
||||||
|
font-size: .85em;
|
||||||
|
}
|
||||||
|
/* ckeditor5-font/theme/fontsize.css */
|
||||||
|
.ck-content .text-big {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
/* ckeditor5-font/theme/fontsize.css */
|
||||||
|
.ck-content .text-huge {
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
/* ckeditor5-basic-styles/theme/code.css */
|
||||||
|
.ck-content code {
|
||||||
|
background-color: hsla(0, 0%, 78%, 0.3);
|
||||||
|
padding: .15em;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content .table {
|
||||||
|
margin: 1em auto;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content .table table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px double hsl(0, 0%, 70%);
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content .table table td,
|
||||||
|
.ck-content .table table th {
|
||||||
|
min-width: 2em;
|
||||||
|
padding: .4em;
|
||||||
|
border: 1px solid hsl(0, 0%, 75%);
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content .table table th {
|
||||||
|
font-weight: bold;
|
||||||
|
background: hsla(0, 0%, 0%, 5%);
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content[dir="rtl"] .table th {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
/* ckeditor5-table/theme/table.css */
|
||||||
|
.ck-content[dir="ltr"] .table th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||||
|
.ck-content .page-break {
|
||||||
|
position: relative;
|
||||||
|
clear: both;
|
||||||
|
padding: 5px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||||
|
.ck-content .page-break::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
border-bottom: 2px dashed hsl(0, 0%, 77%);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||||
|
.ck-content .page-break__label {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
padding: .3em .6em;
|
||||||
|
display: block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: 1px solid hsl(0, 0%, 77%);
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
|
||||||
|
font-size: 0.75em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: hsl(0, 0%, 20%);
|
||||||
|
background: hsl(0, 0%, 100%);
|
||||||
|
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
/* ckeditor5-media-embed/theme/mediaembed.css */
|
||||||
|
.ck-content .media {
|
||||||
|
clear: both;
|
||||||
|
margin: 1em 0;
|
||||||
|
display: block;
|
||||||
|
min-width: 15em;
|
||||||
}
|
}
|
||||||
/* ckeditor5-code-block/theme/codeblock.css */
|
/* ckeditor5-code-block/theme/codeblock.css */
|
||||||
.ck-content pre {
|
.ck-content pre {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
color: #353535;
|
color: hsl(0, 0%, 20.8%);
|
||||||
background: hsla(0, 0%, 78%, 0.3);
|
background: hsla(0, 0%, 78%, 0.3);
|
||||||
border: 1px solid hsl(0, 0%, 77%);
|
border: 1px solid hsl(0, 0%, 77%);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@@ -293,6 +316,11 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
/* ckeditor5-mention/theme/mention.css */
|
||||||
|
.ck-content .mention {
|
||||||
|
background: var(--ck-color-mention-background);
|
||||||
|
color: var(--ck-color-mention-text);
|
||||||
|
}
|
||||||
@media print {
|
@media print {
|
||||||
/* ckeditor5-page-break/theme/pagebreak.css */
|
/* ckeditor5-page-break/theme/pagebreak.css */
|
||||||
.ck-content .page-break {
|
.ck-content .page-break {
|
||||||
@@ -302,4 +330,4 @@
|
|||||||
.ck-content .page-break::after {
|
.ck-content .page-break::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
libraries/ckeditor/ckeditor.js
vendored
2
libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2986,7 +2986,7 @@ var uniqueId = $.fn.extend( {
|
|||||||
self = this,
|
self = this,
|
||||||
wasExpanded = this.isExpanded();
|
wasExpanded = this.isExpanded();
|
||||||
|
|
||||||
_assert(this.isLazy(), "load() requires a lazy node");
|
//_assert(this.isLazy(), "load() requires a lazy node");
|
||||||
// _assert( forceReload || this.isUndefined(), "Pass forceReload=true to re-load a lazy node" );
|
// _assert( forceReload || this.isUndefined(), "Pass forceReload=true to re-load a lazy node" );
|
||||||
if (!forceReload && !this.isUndefined()) {
|
if (!forceReload && !this.isUndefined()) {
|
||||||
return _getResolvedPromise(this);
|
return _getResolvedPromise(this);
|
||||||
|
|||||||
633
package-lock.json
generated
633
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.41.5",
|
"version": "0.43.4",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"start-server": "TRILIUM_ENV=dev node ./src/www",
|
"start-server": "TRILIUM_ENV=dev node ./src/www",
|
||||||
"start-electron": "TRILIUM_ENV=dev electron .",
|
"start-electron": "TRILIUM_ENV=dev electron .",
|
||||||
"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-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/app/entities/*.js src/public/app/services/frontend_script_api.js",
|
"build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/collapsible_widget.js",
|
||||||
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
|
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
|
||||||
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js"
|
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js"
|
||||||
},
|
},
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"commonmark": "0.29.1",
|
"commonmark": "0.29.1",
|
||||||
"cookie-parser": "1.4.5",
|
"cookie-parser": "1.4.5",
|
||||||
"csurf": "1.11.0",
|
"csurf": "1.11.0",
|
||||||
"dayjs": "1.8.24",
|
"dayjs": "1.8.26",
|
||||||
"debug": "4.1.1",
|
"debug": "4.1.1",
|
||||||
"ejs": "3.0.2",
|
"ejs": "3.1.2",
|
||||||
"electron-debug": "3.0.1",
|
"electron-debug": "3.0.1",
|
||||||
"electron-dl": "3.0.0",
|
"electron-dl": "3.0.0",
|
||||||
"electron-find": "1.0.6",
|
"electron-find": "1.0.6",
|
||||||
"electron-window-state": "5.0.3",
|
"electron-window-state": "5.0.3",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.1",
|
||||||
"file-type": "14.1.4",
|
"file-type": "14.3.0",
|
||||||
"fs-extra": "9.0.0",
|
"fs-extra": "9.0.0",
|
||||||
"helmet": "3.22.0",
|
"helmet": "3.22.0",
|
||||||
"html": "1.0.0",
|
"html": "1.0.0",
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"imagemin-pngquant": "8.0.0",
|
"imagemin-pngquant": "8.0.0",
|
||||||
"ini": "1.3.5",
|
"ini": "1.3.5",
|
||||||
"is-svg": "4.2.1",
|
"is-svg": "4.2.1",
|
||||||
"jimp": "0.10.2",
|
"jimp": "0.10.3",
|
||||||
"mime-types": "2.1.26",
|
"mime-types": "2.1.27",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"node-abi": "2.15.0",
|
"node-abi": "2.18.0",
|
||||||
"open": "7.0.3",
|
"open": "7.0.3",
|
||||||
"portscanner": "2.2.0",
|
"portscanner": "2.2.0",
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
@@ -73,18 +73,18 @@
|
|||||||
"turndown": "6.0.0",
|
"turndown": "6.0.0",
|
||||||
"turndown-plugin-gfm": "1.0.2",
|
"turndown-plugin-gfm": "1.0.2",
|
||||||
"unescape": "1.0.1",
|
"unescape": "1.0.1",
|
||||||
"ws": "7.2.3",
|
"ws": "7.2.5",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0",
|
||||||
"yazl": "^2.5.1"
|
"yazl": "^2.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "9.0.0-beta.18",
|
"electron": "9.0.5",
|
||||||
"electron-builder": "22.5.1",
|
"electron-builder": "22.6.0",
|
||||||
"electron-packager": "14.2.1",
|
"electron-packager": "14.2.1",
|
||||||
"electron-rebuild": "1.10.1",
|
"electron-rebuild": "1.10.1",
|
||||||
"jsdoc": "3.6.4",
|
"jsdoc": "3.6.4",
|
||||||
"lorem-ipsum": "2.0.3",
|
"lorem-ipsum": "2.0.3",
|
||||||
"webpack": "5.0.0-beta.14",
|
"webpack": "5.0.0-beta.16",
|
||||||
"webpack-cli": "4.0.0-beta.8"
|
"webpack-cli": "4.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
const anonymizationService = require('./services/anonymization');
|
const backupService = require('./services/backup');
|
||||||
|
const sqlInit = require('./services/sql_init');
|
||||||
|
require('./entities/entity_constructor');
|
||||||
|
|
||||||
anonymizationService.anonymize().then(filePath => {
|
sqlInit.dbReady.then(async () => {
|
||||||
console.log("Anonymized file has been saved to:", filePath);
|
try {
|
||||||
|
console.log("Starting anonymization...");
|
||||||
|
|
||||||
process.exit(0);
|
const resp = await backupService.anonymize();
|
||||||
});
|
|
||||||
|
if (resp.success) {
|
||||||
|
console.log("Anonymized file has been saved to: " + resp.anonymizedFilePath);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
console.log("Anonymization failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e.message, e.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|||||||
13
src/app.js
13
src/app.js
@@ -28,17 +28,6 @@ app.use((req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
|
||||||
cls.namespace.bindEmitter(req);
|
|
||||||
cls.namespace.bindEmitter(res);
|
|
||||||
|
|
||||||
cls.init(() => {
|
|
||||||
cls.namespace.set("Hi");
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use(bodyParser.json({limit: '500mb'}));
|
app.use(bodyParser.json({limit: '500mb'}));
|
||||||
app.use(bodyParser.urlencoded({extended: false}));
|
app.use(bodyParser.urlencoded({extended: false}));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
@@ -120,4 +109,4 @@ require('./services/scheduler');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
app,
|
app,
|
||||||
sessionParser
|
sessionParser
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ class Attribute extends Entity {
|
|||||||
|
|
||||||
// cannot be static!
|
// cannot be static!
|
||||||
updatePojo(pojo) {
|
updatePojo(pojo) {
|
||||||
delete pojo.isOwned;
|
delete pojo.__note; // FIXME: probably note necessary anymore
|
||||||
delete pojo.__note;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createClone(type, name, value) {
|
createClone(type, name, value) {
|
||||||
@@ -124,4 +123,4 @@ class Attribute extends Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Attribute;
|
module.exports = Attribute;
|
||||||
|
|||||||
@@ -411,10 +411,6 @@ class Note extends Entity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const attr of filteredAttributes) {
|
|
||||||
attr.isOwned = attr.noteId === this.noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.__attributeCache = filteredAttributes;
|
this.__attributeCache = filteredAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,12 +541,13 @@ class Note extends Entity {
|
|||||||
/**
|
/**
|
||||||
* @return {Promise<Attribute>}
|
* @return {Promise<Attribute>}
|
||||||
*/
|
*/
|
||||||
async addAttribute(type, name, value = "") {
|
async addAttribute(type, name, value = "", isInheritable = false) {
|
||||||
const attr = new Attribute({
|
const attr = new Attribute({
|
||||||
noteId: this.noteId,
|
noteId: this.noteId,
|
||||||
type: type,
|
type: type,
|
||||||
name: name,
|
name: name,
|
||||||
value: value
|
value: value,
|
||||||
|
isInheritable: isInheritable
|
||||||
});
|
});
|
||||||
|
|
||||||
await attr.save();
|
await attr.save();
|
||||||
@@ -560,12 +557,12 @@ class Note extends Entity {
|
|||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addLabel(name, value = "") {
|
async addLabel(name, value = "", isInheritable = false) {
|
||||||
return await this.addAttribute(LABEL, name, value);
|
return await this.addAttribute(LABEL, name, value, isInheritable);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addRelation(name, targetNoteId) {
|
async addRelation(name, targetNoteId, isInheritable = false) {
|
||||||
return await this.addAttribute(RELATION, name, targetNoteId);
|
return await this.addAttribute(RELATION, name, targetNoteId, isInheritable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -946,4 +943,4 @@ class Note extends Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Note;
|
module.exports = Note;
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import bundleService from "./services/bundle.js";
|
|||||||
import noteAutocompleteService from './services/note_autocomplete.js';
|
import noteAutocompleteService from './services/note_autocomplete.js';
|
||||||
import macInit from './services/mac_init.js';
|
import macInit from './services/mac_init.js';
|
||||||
import contextMenu from "./services/context_menu.js";
|
import contextMenu from "./services/context_menu.js";
|
||||||
import DesktopLayout from "./widgets/desktop_layout.js";
|
import DesktopMainWindowLayout from "./layouts/desktop_main_window_layout.js";
|
||||||
import glob from "./services/glob.js";
|
import glob from "./services/glob.js";
|
||||||
|
import DesktopExtraWindowLayout from "./layouts/desktop_extra_window_layout.js";
|
||||||
|
import zoomService from './services/zoom.js';
|
||||||
|
|
||||||
glob.setupGlobs();
|
glob.setupGlobs();
|
||||||
|
|
||||||
@@ -23,9 +25,11 @@ $('[data-toggle="tooltip"]').tooltip({
|
|||||||
macInit.init();
|
macInit.init();
|
||||||
|
|
||||||
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
||||||
const desktopLayout = new DesktopLayout(widgetBundles);
|
const layout = window.glob.isMainWindow
|
||||||
|
? new DesktopMainWindowLayout(widgetBundles)
|
||||||
|
: new DesktopExtraWindowLayout(widgetBundles);
|
||||||
|
|
||||||
appContext.setLayout(desktopLayout);
|
appContext.setLayout(layout);
|
||||||
appContext.start();
|
appContext.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -82,7 +86,7 @@ if (utils.isElectron()) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.linkURL.length !== 0 && params.mediaType === 'none') {
|
if (!["", "javascript:", "about:blank#blocked"].includes(params.linkURL) && params.mediaType === 'none') {
|
||||||
items.push({
|
items.push({
|
||||||
title: `Copy link`,
|
title: `Copy link`,
|
||||||
uiIcon: "copy",
|
uiIcon: "copy",
|
||||||
@@ -130,9 +134,11 @@ if (utils.isElectron()) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const zoomLevel = zoomService.getCurrentZoom();
|
||||||
|
|
||||||
contextMenu.show({
|
contextMenu.show({
|
||||||
x: params.x,
|
x: params.x / zoomLevel,
|
||||||
y: params.y,
|
y: params.y / zoomLevel,
|
||||||
items,
|
items,
|
||||||
selectMenuItemHandler: ({command, spellingSuggestion}) => {
|
selectMenuItemHandler: ({command, spellingSuggestion}) => {
|
||||||
if (command === 'replaceMisspelling') {
|
if (command === 'replaceMisspelling') {
|
||||||
@@ -141,4 +147,4 @@ if (utils.isElectron()) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export async function showDialog(widget) {
|
|||||||
|
|
||||||
$addLinkTitleSettings.toggle(!textTypeWidget.hasSelection());
|
$addLinkTitleSettings.toggle(!textTypeWidget.hasSelection());
|
||||||
|
|
||||||
updateTitleFormGroupVisibility();
|
|
||||||
$addLinkTitleSettings.find('input[type=radio]').on('change', updateTitleFormGroupVisibility);
|
$addLinkTitleSettings.find('input[type=radio]').on('change', updateTitleFormGroupVisibility);
|
||||||
|
|
||||||
// with selection hyper link is implied
|
// with selection hyper link is implied
|
||||||
@@ -28,6 +27,8 @@ export async function showDialog(widget) {
|
|||||||
$addLinkTitleSettings.find("input[value='reference-link']").prop("checked", true);
|
$addLinkTitleSettings.find("input[value='reference-link']").prop("checked", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTitleFormGroupVisibility();
|
||||||
|
|
||||||
utils.openDialog($dialog);
|
utils.openDialog($dialog);
|
||||||
|
|
||||||
$autoComplete.val('').trigger('focus');
|
$autoComplete.val('').trigger('focus');
|
||||||
@@ -89,4 +90,4 @@ $form.on('submit', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ function AttributesModel() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
async function showAttributes(attributes) {
|
async function showAttributes(noteId, attributes) {
|
||||||
const ownedAttributes = attributes.filter(attr => attr.isOwned);
|
const ownedAttributes = attributes.filter(attr => attr.noteId === noteId);
|
||||||
|
|
||||||
for (const attr of ownedAttributes) {
|
for (const attr of ownedAttributes) {
|
||||||
attr.labelValue = attr.type === 'label' ? attr.value : '';
|
attr.labelValue = attr.type === 'label' ? attr.value : '';
|
||||||
@@ -86,7 +86,7 @@ function AttributesModel() {
|
|||||||
|
|
||||||
addLastEmptyRow();
|
addLastEmptyRow();
|
||||||
|
|
||||||
const inheritedAttributes = attributes.filter(attr => !attr.isOwned);
|
const inheritedAttributes = attributes.filter(attr => attr.noteId !== noteId);
|
||||||
|
|
||||||
self.inheritedAttributes(inheritedAttributes);
|
self.inheritedAttributes(inheritedAttributes);
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ function AttributesModel() {
|
|||||||
|
|
||||||
const attributes = await server.get('notes/' + noteId + '/attributes');
|
const attributes = await server.get('notes/' + noteId + '/attributes');
|
||||||
|
|
||||||
await showAttributes(attributes);
|
await showAttributes(noteId, attributes);
|
||||||
|
|
||||||
// attribute might not be rendered immediatelly so could not focus
|
// attribute might not be rendered immediatelly so could not focus
|
||||||
setTimeout(() => $(".attribute-type-select:last").trigger('focus'), 1000);
|
setTimeout(() => $(".attribute-type-select:last").trigger('focus'), 1000);
|
||||||
@@ -152,10 +152,10 @@ function AttributesModel() {
|
|||||||
attr.value = treeService.getNoteIdFromNotePath(attr.selectedPath);
|
attr.value = treeService.getNoteIdFromNotePath(attr.selectedPath);
|
||||||
}
|
}
|
||||||
else if (attr.type === 'label-definition') {
|
else if (attr.type === 'label-definition') {
|
||||||
attr.value = attr.labelDefinition;
|
attr.value = JSON.stringify(attr.labelDefinition);
|
||||||
}
|
}
|
||||||
else if (attr.type === 'relation-definition') {
|
else if (attr.type === 'relation-definition') {
|
||||||
attr.value = attr.relationDefinition;
|
attr.value = JSON.stringify(attr.relationDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete attr.labelValue;
|
delete attr.labelValue;
|
||||||
@@ -166,7 +166,7 @@ function AttributesModel() {
|
|||||||
|
|
||||||
const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave);
|
const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave);
|
||||||
|
|
||||||
await showAttributes(attributes);
|
await showAttributes(noteId, attributes);
|
||||||
|
|
||||||
toastService.showMessage("Attributes have been saved.");
|
toastService.showMessage("Attributes have been saved.");
|
||||||
};
|
};
|
||||||
@@ -311,4 +311,4 @@ $dialog.on('focus', '.label-value', function (e) {
|
|||||||
$el: $(this),
|
$el: $(this),
|
||||||
open: true
|
open: true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,13 +39,14 @@ export async function showDialog(noteIds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function cloneNotesTo(notePath) {
|
async function cloneNotesTo(notePath) {
|
||||||
const targetNoteId = treeService.getNoteIdFromNotePath(notePath);
|
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||||
|
const targetBranchId = await treeCache.getBranchId(parentNoteId, noteId);
|
||||||
|
|
||||||
for (const cloneNoteId of clonedNoteIds) {
|
for (const cloneNoteId of clonedNoteIds) {
|
||||||
await branchService.cloneNoteTo(cloneNoteId, targetNoteId, $clonePrefix.val());
|
await branchService.cloneNoteTo(cloneNoteId, targetBranchId, $clonePrefix.val());
|
||||||
|
|
||||||
const clonedNote = await treeCache.getNote(cloneNoteId);
|
const clonedNote = await treeCache.getNote(cloneNoteId);
|
||||||
const targetNote = await treeCache.getNote(targetNoteId);
|
const targetNote = await treeCache.getBranch(targetBranchId).getNote();
|
||||||
|
|
||||||
toastService.showMessage(`Note "${clonedNote.title}" has been cloned into ${targetNote.title}`);
|
toastService.showMessage(`Note "${clonedNote.title}" has been cloned into ${targetNote.title}`);
|
||||||
}
|
}
|
||||||
@@ -64,4 +65,4 @@ $form.on('submit', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ export async function showDialog(branchIds) {
|
|||||||
noteAutocompleteService.showRecentNotes($noteAutoComplete);
|
noteAutocompleteService.showRecentNotes($noteAutoComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveNotesTo(parentNoteId) {
|
async function moveNotesTo(parentBranchId) {
|
||||||
await branchService.moveToParentNote(movedBranchIds, parentNoteId);
|
await branchService.moveToParentNote(movedBranchIds, parentBranchId);
|
||||||
|
|
||||||
const parentNote = await treeCache.getNote(parentNoteId);
|
const parentBranch = treeCache.getBranch(parentBranchId);
|
||||||
|
const parentNote = await parentBranch.getNote();
|
||||||
|
|
||||||
toastService.showMessage(`Selected notes have been moved into ${parentNote.title}`);
|
toastService.showMessage(`Selected notes have been moved into ${parentNote.title}`);
|
||||||
}
|
}
|
||||||
@@ -46,13 +47,12 @@ $form.on('submit', () => {
|
|||||||
if (notePath) {
|
if (notePath) {
|
||||||
$dialog.modal('hide');
|
$dialog.modal('hide');
|
||||||
|
|
||||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||||
|
treeCache.getBranchId(parentNoteId, noteId).then(branchId => moveNotesTo(branchId));
|
||||||
moveNotesTo(noteId);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.error("No path to move to.");
|
console.error("No path to move to.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,14 +37,18 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
|
|||||||
async function loadNoteRevisions(noteId, noteRevId) {
|
async function loadNoteRevisions(noteId, noteRevId) {
|
||||||
$list.empty();
|
$list.empty();
|
||||||
$content.empty();
|
$content.empty();
|
||||||
|
$titleButtons.empty();
|
||||||
|
|
||||||
note = appContext.tabManager.getActiveTabNote();
|
note = appContext.tabManager.getActiveTabNote();
|
||||||
revisionItems = await server.get(`notes/${noteId}/revisions`);
|
revisionItems = await server.get(`notes/${noteId}/revisions`);
|
||||||
|
|
||||||
for (const item of revisionItems) {
|
for (const item of revisionItems) {
|
||||||
$list.append($('<a class="dropdown-item" tabindex="0">')
|
$list.append(
|
||||||
.text(item.dateLastEdited.substr(0, 16) + ` (${item.contentLength} bytes)`)
|
$('<a class="dropdown-item" tabindex="0">')
|
||||||
.attr('data-note-revision-id', item.noteRevisionId));
|
.text(item.dateLastEdited.substr(0, 16) + ` (${item.contentLength} bytes)`)
|
||||||
|
.attr('data-note-revision-id', item.noteRevisionId)
|
||||||
|
.attr('title', 'This revision was last edited on ' + item.dateLastEdited)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$listDropdown.dropdown('show');
|
$listDropdown.dropdown('show');
|
||||||
@@ -59,6 +63,8 @@ async function loadNoteRevisions(noteId, noteRevId) {
|
|||||||
$title.text("No revisions for this note yet...");
|
$title.text("No revisions for this note yet...");
|
||||||
noteRevisionId = null;
|
noteRevisionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$eraseAllRevisionsButton.toggle(revisionItems.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dialog.on('shown.bs.modal', () => {
|
$dialog.on('shown.bs.modal', () => {
|
||||||
@@ -76,6 +82,21 @@ async function setContentPane() {
|
|||||||
|
|
||||||
$title.html(revisionItem.title);
|
$title.html(revisionItem.title);
|
||||||
|
|
||||||
|
const $restoreRevisionButton = $('<button class="btn btn-sm" type="button">Restore this revision</button>');
|
||||||
|
|
||||||
|
$restoreRevisionButton.on('click', async () => {
|
||||||
|
const confirmDialog = await import('../dialogs/confirm.js');
|
||||||
|
const text = 'Do you want to restore this revision? This will overwrite current title/content of the note with this revision.';
|
||||||
|
|
||||||
|
if (await confirmDialog.confirm(text)) {
|
||||||
|
await server.put(`notes/${revisionItem.noteId}/restore-revision/${revisionItem.noteRevisionId}`);
|
||||||
|
|
||||||
|
$dialog.modal('hide');
|
||||||
|
|
||||||
|
toastService.showMessage('Note revision has been restored.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const $eraseRevisionButton = $('<button class="btn btn-sm" type="button">Delete this revision</button>');
|
const $eraseRevisionButton = $('<button class="btn btn-sm" type="button">Delete this revision</button>');
|
||||||
|
|
||||||
$eraseRevisionButton.on('click', async () => {
|
$eraseRevisionButton.on('click', async () => {
|
||||||
@@ -92,6 +113,8 @@ async function setContentPane() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$titleButtons
|
$titleButtons
|
||||||
|
.append($restoreRevisionButton)
|
||||||
|
.append(' ')
|
||||||
.append($eraseRevisionButton)
|
.append($eraseRevisionButton)
|
||||||
.append(' ');
|
.append(' ');
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,18 @@ const TPL = `
|
|||||||
|
|
||||||
<button id="find-and-fix-consistency-issues-button" class="btn">Find and fix consistency issues</button><br/><br/>
|
<button id="find-and-fix-consistency-issues-button" class="btn">Find and fix consistency issues</button><br/><br/>
|
||||||
|
|
||||||
<h4>Debugging</h4>
|
<h4>Anonymize database</h4>
|
||||||
|
|
||||||
|
<p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and some non-sensitive metadata)
|
||||||
|
for sharing online for debugging purposes without fear of leaking your personal data.</p>
|
||||||
|
|
||||||
<button id="anonymize-button" class="btn">Save anonymized database</button><br/><br/>
|
<button id="anonymize-button" class="btn">Save anonymized database</button><br/><br/>
|
||||||
|
|
||||||
<p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and metadata)
|
<h4>Backup database</h4>
|
||||||
for sharing online for debugging purposes without fear of leaking your personal data.</p>
|
|
||||||
|
<p>Trilium has automatic backup (daily, weekly, monthly), but you can also trigger backup manually here.</p>
|
||||||
|
|
||||||
|
<button id="backup-database-button" class="btn">Backup database now</button><br/><br/>
|
||||||
|
|
||||||
<h4>Vacuum database</h4>
|
<h4>Vacuum database</h4>
|
||||||
|
|
||||||
@@ -37,6 +43,7 @@ export default class AdvancedOptions {
|
|||||||
this.$forceFullSyncButton = $("#force-full-sync-button");
|
this.$forceFullSyncButton = $("#force-full-sync-button");
|
||||||
this.$fillSyncRowsButton = $("#fill-sync-rows-button");
|
this.$fillSyncRowsButton = $("#fill-sync-rows-button");
|
||||||
this.$anonymizeButton = $("#anonymize-button");
|
this.$anonymizeButton = $("#anonymize-button");
|
||||||
|
this.$backupDatabaseButton = $("#backup-database-button");
|
||||||
this.$vacuumDatabaseButton = $("#vacuum-database-button");
|
this.$vacuumDatabaseButton = $("#vacuum-database-button");
|
||||||
this.$findAndFixConsistencyIssuesButton = $("#find-and-fix-consistency-issues-button");
|
this.$findAndFixConsistencyIssuesButton = $("#find-and-fix-consistency-issues-button");
|
||||||
|
|
||||||
@@ -53,21 +60,32 @@ export default class AdvancedOptions {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.$anonymizeButton.on('click', async () => {
|
this.$anonymizeButton.on('click', async () => {
|
||||||
await server.post('anonymization/anonymize');
|
const resp = await server.post('database/anonymize');
|
||||||
|
|
||||||
toastService.showMessage("Created anonymized database");
|
if (!resp.success) {
|
||||||
|
toastService.showError("Could not create anonymized database, check backend logs for details");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toastService.showMessage(`Created anonymized database in ${resp.anonymizedFilePath}`, 10000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$backupDatabaseButton.on('click', async () => {
|
||||||
|
const {backupFile} = await server.post('database/backup-database');
|
||||||
|
|
||||||
|
toastService.showMessage("Database has been backed up to " + backupFile, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$vacuumDatabaseButton.on('click', async () => {
|
this.$vacuumDatabaseButton.on('click', async () => {
|
||||||
await server.post('cleanup/vacuum-database');
|
await server.post('database/vacuum-database');
|
||||||
|
|
||||||
toastService.showMessage("Database has been vacuumed");
|
toastService.showMessage("Database has been vacuumed");
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$findAndFixConsistencyIssuesButton.on('click', async () => {
|
this.$findAndFixConsistencyIssuesButton.on('click', async () => {
|
||||||
await server.post('cleanup/find-and-fix-consistency-issues');
|
await server.post('database/find-and-fix-consistency-issues');
|
||||||
|
|
||||||
toastService.showMessage("Consistency issues should be fixed.");
|
toastService.showMessage("Consistency issues should be fixed.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,26 +16,26 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
ancestorNoteId = hoistedNoteService.getHoistedNoteId();
|
ancestorNoteId = hoistedNoteService.getHoistedNoteId();
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await server.get('recent-changes/' + ancestorNoteId);
|
const recentChangesRows = await server.get('recent-changes/' + ancestorNoteId);
|
||||||
|
|
||||||
// preload all notes into cache
|
// preload all notes into cache
|
||||||
await treeCache.getNotes(result.map(r => r.noteId), true);
|
await treeCache.getNotes(recentChangesRows.map(r => r.noteId), true);
|
||||||
|
|
||||||
$content.empty();
|
$content.empty();
|
||||||
|
|
||||||
if (result.length === 0) {
|
if (recentChangesRows.length === 0) {
|
||||||
$content.append("No changes yet ...");
|
$content.append("No changes yet ...");
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedByDate = groupByDate(result);
|
const groupedByDate = groupByDate(recentChangesRows);
|
||||||
|
|
||||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||||
const $changesList = $('<ul>');
|
const $changesList = $('<ul>');
|
||||||
|
|
||||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append($changesList);
|
const dayEl = $('<div>').append($('<b>').text(dateDay)).append($changesList);
|
||||||
|
|
||||||
for (const change of dayChanges) {
|
for (const change of dayChanges) {
|
||||||
const formattedTime = utils.formatTime(utils.parseDate(change.date));
|
const formattedTime = change.date.substr(11, 5);
|
||||||
|
|
||||||
let $noteLink;
|
let $noteLink;
|
||||||
|
|
||||||
@@ -82,7 +82,12 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$changesList.append($('<li>')
|
$changesList.append($('<li>')
|
||||||
.append(formattedTime + ' - ')
|
.append(
|
||||||
|
$("<span>")
|
||||||
|
.text(formattedTime)
|
||||||
|
.attr("title", change.date)
|
||||||
|
)
|
||||||
|
.append(' - ')
|
||||||
.append($noteLink));
|
.append($noteLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,25 +95,11 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupByDate(result) {
|
function groupByDate(rows) {
|
||||||
const groupedByDate = new Map();
|
const groupedByDate = new Map();
|
||||||
const dayCache = {};
|
|
||||||
|
|
||||||
for (const row of result) {
|
for (const row of rows) {
|
||||||
let dateDay = utils.parseDate(row.date);
|
const dateDay = row.date.substr(0, 10);
|
||||||
dateDay.setHours(0);
|
|
||||||
dateDay.setMinutes(0);
|
|
||||||
dateDay.setSeconds(0);
|
|
||||||
dateDay.setMilliseconds(0);
|
|
||||||
|
|
||||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
|
||||||
// reference equality
|
|
||||||
if (dayCache[dateDay]) {
|
|
||||||
dateDay = dayCache[dateDay];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dayCache[dateDay] = dateDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!groupedByDate.has(dateDay)) {
|
if (!groupedByDate.has(dateDay)) {
|
||||||
groupedByDate.set(dateDay, []);
|
groupedByDate.set(dateDay, []);
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ class Attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {NoteShort} */
|
/** @returns {NoteShort} */
|
||||||
async getNote() {
|
getNote() {
|
||||||
return await this.treeCache.getNote(this.noteId);
|
return this.treeCache.notes[this.noteId];
|
||||||
|
}
|
||||||
|
|
||||||
|
get targetNoteId() { // alias
|
||||||
|
return this.type === 'relation' ? this.value : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get jsonValue() {
|
get jsonValue() {
|
||||||
@@ -39,6 +43,44 @@ class Attribute {
|
|||||||
get toString() {
|
get toString() {
|
||||||
return `Attribute(attributeId=${this.attributeId}, type=${this.type}, name=${this.name}, value=${this.value})`;
|
return `Attribute(attributeId=${this.attributeId}, type=${this.type}, name=${this.name}, value=${this.value})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} - returns true if this attribute has the potential to influence the note in the argument.
|
||||||
|
* That can happen in multiple ways:
|
||||||
|
* 1. attribute is owned by the note
|
||||||
|
* 2. attribute is owned by the template of the note
|
||||||
|
* 3. attribute is owned by some note's ancestor and is inheritable
|
||||||
|
*/
|
||||||
|
isAffecting(affectedNote) {
|
||||||
|
if (!affectedNote) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrNote = this.getNote();
|
||||||
|
|
||||||
|
if (!attrNote) {
|
||||||
|
// the note (owner of the attribute) is not even loaded into the cache so it should not affect anything else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const owningNotes = [affectedNote, ...affectedNote.getTemplateNotes()];
|
||||||
|
|
||||||
|
for (const owningNote of owningNotes) {
|
||||||
|
if (owningNote.noteId === attrNote.noteId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isInheritable) {
|
||||||
|
for (const owningNote of owningNotes) {
|
||||||
|
if (owningNote.hasAncestor(attrNote)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Attribute;
|
export default Attribute;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
import Attribute from './attribute.js';
|
import Attribute from './attribute.js';
|
||||||
|
import noteAttributeCache from "../services/note_attribute_cache.js";
|
||||||
|
|
||||||
const LABEL = 'label';
|
const LABEL = 'label';
|
||||||
const LABEL_DEFINITION = 'label-definition';
|
const LABEL_DEFINITION = 'label-definition';
|
||||||
@@ -7,6 +8,8 @@ const RELATION = 'relation';
|
|||||||
const RELATION_DEFINITION = 'relation-definition';
|
const RELATION_DEFINITION = 'relation-definition';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* FIXME: since there's no "full note" anymore we can rename this to Note
|
||||||
|
*
|
||||||
* This note's representation is used in note tree and is kept in TreeCache.
|
* This note's representation is used in note tree and is kept in TreeCache.
|
||||||
*/
|
*/
|
||||||
class NoteShort {
|
class NoteShort {
|
||||||
@@ -156,9 +159,9 @@ class NoteShort {
|
|||||||
getOwnedAttributes(type, name) {
|
getOwnedAttributes(type, name) {
|
||||||
const attrs = this.attributes
|
const attrs = this.attributes
|
||||||
.map(attributeId => this.treeCache.attributes[attributeId])
|
.map(attributeId => this.treeCache.attributes[attributeId])
|
||||||
.filter(attr => !!attr);
|
.filter(Boolean); // filter out nulls;
|
||||||
|
|
||||||
return this.__filterAttrs(attrs, type, name)
|
return this.__filterAttrs(attrs, type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,48 +170,62 @@ class NoteShort {
|
|||||||
* @returns {Attribute[]} all note's attributes, including inherited ones
|
* @returns {Attribute[]} all note's attributes, including inherited ones
|
||||||
*/
|
*/
|
||||||
getAttributes(type, name) {
|
getAttributes(type, name) {
|
||||||
const ownedAttributes = this.getOwnedAttributes();
|
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
|
||||||
|
}
|
||||||
|
|
||||||
const attrArrs = [
|
__getCachedAttributes(path) {
|
||||||
ownedAttributes
|
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
|
||||||
];
|
// when template instance is a parent of template itself
|
||||||
|
if (path.includes(this.noteId)) {
|
||||||
for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) {
|
return [];
|
||||||
const templateNote = this.treeCache.getNoteFromCache(templateAttr.value);
|
|
||||||
|
|
||||||
if (templateNote) {
|
|
||||||
attrArrs.push(templateNote.getAttributes());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.noteId !== 'root') {
|
if (!(this.noteId in noteAttributeCache)) {
|
||||||
for (const parentNote of this.getParentNotes()) {
|
const ownedAttributes = this.getOwnedAttributes();
|
||||||
// these virtual parent-child relationships are also loaded into frontend tree cache
|
|
||||||
if (parentNote.type !== 'search') {
|
const attrArrs = [
|
||||||
attrArrs.push(parentNote.getInheritableAttributes());
|
ownedAttributes
|
||||||
|
];
|
||||||
|
|
||||||
|
const newPath = [...path, this.noteId];
|
||||||
|
|
||||||
|
for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) {
|
||||||
|
const templateNote = this.treeCache.notes[templateAttr.value];
|
||||||
|
|
||||||
|
if (templateNote && templateNote.noteId !== this.noteId) {
|
||||||
|
attrArrs.push(templateNote.__getCachedAttributes(newPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.noteId !== 'root') {
|
||||||
|
for (const parentNote of this.getParentNotes()) {
|
||||||
|
// these virtual parent-child relationships are also loaded into frontend tree cache
|
||||||
|
if (parentNote.type !== 'search') {
|
||||||
|
attrArrs.push(parentNote.__getInheritableAttributes(newPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noteAttributeCache.attributes[this.noteId] = attrArrs.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributes = attrArrs.flat();
|
return noteAttributeCache.attributes[this.noteId];
|
||||||
|
|
||||||
return this.__filterAttrs(attributes, type, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__filterAttrs(attributes, type, name) {
|
__filterAttrs(attributes, type, name) {
|
||||||
if (type && name) {
|
if (!type && !name) {
|
||||||
|
return attributes;
|
||||||
|
} else if (type && name) {
|
||||||
return attributes.filter(attr => attr.type === type && attr.name === name);
|
return attributes.filter(attr => attr.type === type && attr.name === name);
|
||||||
} else if (type) {
|
} else if (type) {
|
||||||
return attributes.filter(attr => attr.type === type);
|
return attributes.filter(attr => attr.type === type);
|
||||||
} else if (name) {
|
} else if (name) {
|
||||||
return attributes.filter(attr => attr.name === name);
|
return attributes.filter(attr => attr.name === name);
|
||||||
} else {
|
|
||||||
return attributes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getInheritableAttributes() {
|
__getInheritableAttributes(path) {
|
||||||
const attrs = this.getAttributes();
|
const attrs = this.__getCachedAttributes(path);
|
||||||
|
|
||||||
return attrs.filter(attr => attr.isInheritable);
|
return attrs.filter(attr => attr.isInheritable);
|
||||||
}
|
}
|
||||||
@@ -420,6 +437,35 @@ class NoteShort {
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {NoteShort[]}
|
||||||
|
*/
|
||||||
|
getTemplateNotes() {
|
||||||
|
const relations = this.getRelations('template');
|
||||||
|
|
||||||
|
return relations.map(rel => this.treeCache.notes[rel.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAncestor(ancestorNote) {
|
||||||
|
if (this.noteId === ancestorNote.noteId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const templateNote of this.getTemplateNotes()) {
|
||||||
|
if (templateNote.hasAncestor(ancestorNote)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const parentNote of this.getParentNotes()) {
|
||||||
|
if (parentNote.hasAncestor(ancestorNote)) {console.log(parentNote);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear note's attributes cache to force fresh reload for next attribute request.
|
* Clear note's attributes cache to force fresh reload for next attribute request.
|
||||||
* Cache is note instance scoped.
|
* Cache is note instance scoped.
|
||||||
@@ -438,6 +484,15 @@ class NoteShort {
|
|||||||
.map(attributeId => this.treeCache.attributes[attributeId]);
|
.map(attributeId => this.treeCache.attributes[attributeId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return note complement which is most importantly note's content
|
||||||
|
*
|
||||||
|
* @return {Promise<NoteComplement>}
|
||||||
|
*/
|
||||||
|
async getNoteComplement() {
|
||||||
|
return await this.treeCache.getNoteComplement(this.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
get toString() {
|
get toString() {
|
||||||
return `Note(noteId=${this.noteId}, title=${this.title})`;
|
return `Note(noteId=${this.noteId}, title=${this.title})`;
|
||||||
}
|
}
|
||||||
@@ -455,4 +510,4 @@ class NoteShort {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteShort;
|
export default NoteShort;
|
||||||
|
|||||||
46
src/public/app/layouts/desktop_extra_window_layout.js
Normal file
46
src/public/app/layouts/desktop_extra_window_layout.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
|
import GlobalMenuWidget from "../widgets/global_menu.js";
|
||||||
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
|
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
|
||||||
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
|
import TabCachingWidget from "../widgets/tab_caching_widget.js";
|
||||||
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
|
import RunScriptButtonsWidget from "../widgets/run_script_buttons.js";
|
||||||
|
import NoteTypeWidget from "../widgets/note_type.js";
|
||||||
|
import NoteActionsWidget from "../widgets/note_actions.js";
|
||||||
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
|
|
||||||
|
export default class DesktopExtraWindowLayout {
|
||||||
|
constructor(customWidgets) {
|
||||||
|
this.customWidgets = customWidgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRootWidget(appContext) {
|
||||||
|
appContext.mainTreeWidget = new NoteTreeWidget();
|
||||||
|
|
||||||
|
return new FlexContainer('column')
|
||||||
|
.setParent(appContext)
|
||||||
|
.id('root-widget')
|
||||||
|
.css('height', '100vh')
|
||||||
|
.child(new FlexContainer('row')
|
||||||
|
.child(new GlobalMenuWidget())
|
||||||
|
.child(new TabRowWidget())
|
||||||
|
.child(new TitleBarButtonsWidget()))
|
||||||
|
.child(new FlexContainer('row')
|
||||||
|
.collapsible()
|
||||||
|
.child(new FlexContainer('column').id('center-pane').css('flex-grow', '1')
|
||||||
|
.child(new FlexContainer('row').class('title-row')
|
||||||
|
.cssBlock('.title-row > * { margin: 5px; }')
|
||||||
|
.child(new NoteTitleWidget())
|
||||||
|
.child(new RunScriptButtonsWidget().hideInZenMode())
|
||||||
|
.child(new NoteTypeWidget().hideInZenMode())
|
||||||
|
.child(new NoteActionsWidget().hideInZenMode())
|
||||||
|
)
|
||||||
|
.child(new TabCachingWidget(() => new PromotedAttributesWidget()))
|
||||||
|
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
||||||
|
.child(...this.customWidgets.get('center-pane'))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,30 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
import GlobalMenuWidget from "./global_menu.js";
|
import GlobalMenuWidget from "../widgets/global_menu.js";
|
||||||
import TabRowWidget from "./tab_row.js";
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
import TitleBarButtonsWidget from "./title_bar_buttons.js";
|
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
|
||||||
import StandardTopWidget from "./standard_top_widget.js";
|
import StandardTopWidget from "../widgets/standard_top_widget.js";
|
||||||
import SidePaneContainer from "./side_pane_container.js";
|
import SidePaneContainer from "../widgets/side_pane_container.js";
|
||||||
import GlobalButtonsWidget from "./global_buttons.js";
|
import GlobalButtonsWidget from "../widgets/global_buttons.js";
|
||||||
import SearchBoxWidget from "./search_box.js";
|
import SearchBoxWidget from "../widgets/search_box.js";
|
||||||
import SearchResultsWidget from "./search_results.js";
|
import SearchResultsWidget from "../widgets/search_results.js";
|
||||||
import NoteTreeWidget from "./note_tree.js";
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import TabCachingWidget from "./tab_caching_widget.js";
|
import TabCachingWidget from "../widgets/tab_caching_widget.js";
|
||||||
import NotePathsWidget from "./note_paths.js";
|
import NotePathsWidget from "../widgets/note_paths.js";
|
||||||
import NoteTitleWidget from "./note_title.js";
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
import RunScriptButtonsWidget from "./run_script_buttons.js";
|
import RunScriptButtonsWidget from "../widgets/run_script_buttons.js";
|
||||||
import ProtectedNoteSwitchWidget from "./protected_note_switch.js";
|
import NoteTypeWidget from "../widgets/note_type.js";
|
||||||
import NoteTypeWidget from "./note_type.js";
|
import NoteActionsWidget from "../widgets/note_actions.js";
|
||||||
import NoteActionsWidget from "./note_actions.js";
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
import PromotedAttributesWidget from "./promoted_attributes.js";
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteDetailWidget from "./note_detail.js";
|
import NoteInfoWidget from "../widgets/collapsible_widgets/note_info.js";
|
||||||
import NoteInfoWidget from "./note_info.js";
|
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
|
||||||
import CalendarWidget from "./calendar.js";
|
import AttributesWidget from "../widgets/collapsible_widgets/attributes.js";
|
||||||
import AttributesWidget from "./attributes.js";
|
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
|
||||||
import LinkMapWidget from "./link_map.js";
|
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
|
||||||
import NoteRevisionsWidget from "./note_revisions.js";
|
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
|
||||||
import SimilarNotesWidget from "./similar_notes.js";
|
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
|
||||||
import WhatLinksHereWidget from "./what_links_here.js";
|
import SidePaneToggles from "../widgets/side_pane_toggles.js";
|
||||||
import SidePaneToggles from "./side_pane_toggles.js";
|
import EditedNotesWidget from "../widgets/collapsible_widgets/edited_notes.js";
|
||||||
import appContext from "../services/app_context.js";
|
|
||||||
|
|
||||||
const RIGHT_PANE_CSS = `
|
const RIGHT_PANE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
@@ -98,13 +97,13 @@ const RIGHT_PANE_CSS = `
|
|||||||
}
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopMainWindowLayout {
|
||||||
constructor(customWidgets) {
|
constructor(customWidgets) {
|
||||||
this.customWidgets = customWidgets;
|
this.customWidgets = customWidgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRootWidget(appContext) {
|
getRootWidget(appContext) {
|
||||||
appContext.mainTreeWidget = new NoteTreeWidget();
|
appContext.mainTreeWidget = new NoteTreeWidget("main");
|
||||||
|
|
||||||
return new FlexContainer('column')
|
return new FlexContainer('column')
|
||||||
.setParent(appContext)
|
.setParent(appContext)
|
||||||
@@ -118,6 +117,7 @@ export default class DesktopLayout {
|
|||||||
.hideInZenMode())
|
.hideInZenMode())
|
||||||
.child(new FlexContainer('row')
|
.child(new FlexContainer('row')
|
||||||
.collapsible()
|
.collapsible()
|
||||||
|
.filling()
|
||||||
.child(new SidePaneContainer('left')
|
.child(new SidePaneContainer('left')
|
||||||
.hideInZenMode()
|
.hideInZenMode()
|
||||||
.child(new GlobalButtonsWidget())
|
.child(new GlobalButtonsWidget())
|
||||||
@@ -132,7 +132,6 @@ export default class DesktopLayout {
|
|||||||
.cssBlock('.title-row > * { margin: 5px; }')
|
.cssBlock('.title-row > * { margin: 5px; }')
|
||||||
.child(new NoteTitleWidget())
|
.child(new NoteTitleWidget())
|
||||||
.child(new RunScriptButtonsWidget().hideInZenMode())
|
.child(new RunScriptButtonsWidget().hideInZenMode())
|
||||||
.child(new ProtectedNoteSwitchWidget().hideInZenMode())
|
|
||||||
.child(new NoteTypeWidget().hideInZenMode())
|
.child(new NoteTypeWidget().hideInZenMode())
|
||||||
.child(new NoteActionsWidget().hideInZenMode())
|
.child(new NoteActionsWidget().hideInZenMode())
|
||||||
)
|
)
|
||||||
@@ -145,6 +144,7 @@ export default class DesktopLayout {
|
|||||||
.hideInZenMode()
|
.hideInZenMode()
|
||||||
.child(new NoteInfoWidget())
|
.child(new NoteInfoWidget())
|
||||||
.child(new TabCachingWidget(() => new CalendarWidget()))
|
.child(new TabCachingWidget(() => new CalendarWidget()))
|
||||||
|
.child(new TabCachingWidget(() => new EditedNotesWidget()))
|
||||||
.child(new TabCachingWidget(() => new AttributesWidget()))
|
.child(new TabCachingWidget(() => new AttributesWidget()))
|
||||||
.child(new TabCachingWidget(() => new LinkMapWidget()))
|
.child(new TabCachingWidget(() => new LinkMapWidget()))
|
||||||
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
||||||
@@ -155,4 +155,4 @@ export default class DesktopLayout {
|
|||||||
.child(new SidePaneToggles().hideInZenMode())
|
.child(new SidePaneToggles().hideInZenMode())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
import NoteTitleWidget from "./note_title.js";
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
import NoteDetailWidget from "./note_detail.js";
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteTreeWidget from "./note_tree.js";
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import MobileGlobalButtonsWidget from "./mobile_global_buttons.js";
|
import MobileGlobalButtonsWidget from "../widgets/mobile_widgets/mobile_global_buttons.js";
|
||||||
import CloseDetailButtonWidget from "./close_detail_button.js";
|
import CloseDetailButtonWidget from "../widgets/mobile_widgets/close_detail_button.js";
|
||||||
import MobileDetailMenuWidget from "./mobile_detail_menu.js";
|
import MobileDetailMenuWidget from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
||||||
import ScreenContainer from "./screen_container.js";
|
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
|
||||||
|
|
||||||
const MOBILE_CSS = `
|
const MOBILE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
@@ -73,7 +73,7 @@ export default class MobileLayout {
|
|||||||
.child(new ScreenContainer("tree", 'column')
|
.child(new ScreenContainer("tree", 'column')
|
||||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4")
|
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4")
|
||||||
.child(new MobileGlobalButtonsWidget())
|
.child(new MobileGlobalButtonsWidget())
|
||||||
.child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS)))
|
.child(new NoteTreeWidget("main").cssBlock(FANCYTREE_CSS)))
|
||||||
.child(new ScreenContainer("detail", "column")
|
.child(new ScreenContainer("detail", "column")
|
||||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-8")
|
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-8")
|
||||||
.child(new FlexContainer('row')
|
.child(new FlexContainer('row')
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import appContext from "./services/app_context.js";
|
import appContext from "./services/app_context.js";
|
||||||
import MobileLayout from "./widgets/mobile_layout.js";
|
import MobileLayout from "./layouts/mobile_layout.js";
|
||||||
import glob from "./services/glob.js";
|
import glob from "./services/glob.js";
|
||||||
|
|
||||||
glob.setupGlobs();
|
glob.setupGlobs();
|
||||||
|
|||||||
@@ -4,15 +4,22 @@ import DialogCommandExecutor from "./dialog_command_executor.js";
|
|||||||
import Entrypoints from "./entrypoints.js";
|
import Entrypoints from "./entrypoints.js";
|
||||||
import options from "./options.js";
|
import options from "./options.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import ZoomService from "./zoom.js";
|
import zoomService from "./zoom.js";
|
||||||
import TabManager from "./tab_manager.js";
|
import TabManager from "./tab_manager.js";
|
||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import Component from "../widgets/component.js";
|
import Component from "../widgets/component.js";
|
||||||
import keyboardActionsService from "./keyboard_actions.js";
|
import keyboardActionsService from "./keyboard_actions.js";
|
||||||
import MobileScreenSwitcherExecutor from "../widgets/mobile_screen_switcher.js";
|
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js";
|
||||||
import MainTreeExecutors from "./main_tree_executors.js";
|
import MainTreeExecutors from "./main_tree_executors.js";
|
||||||
|
|
||||||
class AppContext extends Component {
|
class AppContext extends Component {
|
||||||
|
constructor(isMainWindow) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.isMainWindow = isMainWindow;
|
||||||
|
this.executors = [];
|
||||||
|
}
|
||||||
|
|
||||||
setLayout(layout) {
|
setLayout(layout) {
|
||||||
this.layout = layout;
|
this.layout = layout;
|
||||||
}
|
}
|
||||||
@@ -39,10 +46,12 @@ class AppContext extends Component {
|
|||||||
|
|
||||||
$("body").append($renderedWidget);
|
$("body").append($renderedWidget);
|
||||||
|
|
||||||
$renderedWidget.on('click', "[data-trigger-command]", e => {
|
$renderedWidget.on('click', "[data-trigger-command]", function() {
|
||||||
const commandName = $(e.target).attr('data-trigger-command');
|
const commandName = $(this).attr('data-trigger-command');
|
||||||
|
const $component = $(this).closest(".component");
|
||||||
|
const component = $component.prop("component");
|
||||||
|
|
||||||
this.triggerCommand(commandName);
|
component.triggerCommand(commandName, {$el: $(this)});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tabManager = new TabManager();
|
this.tabManager = new TabManager();
|
||||||
@@ -65,7 +74,7 @@ class AppContext extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
this.child(new ZoomService());
|
this.child(zoomService);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.triggerEvent('initialRenderComplete');
|
this.triggerEvent('initialRenderComplete');
|
||||||
@@ -86,6 +95,8 @@ class AppContext extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this might hint at error but sometimes this is used by components which are at different places
|
||||||
|
// in the component tree to communicate with each other
|
||||||
console.debug(`Unhandled command ${name}, converting to event.`);
|
console.debug(`Unhandled command ${name}, converting to event.`);
|
||||||
|
|
||||||
return this.triggerEvent(name, data);
|
return this.triggerEvent(name, data);
|
||||||
@@ -94,15 +105,9 @@ class AppContext extends Component {
|
|||||||
getComponentByEl(el) {
|
getComponentByEl(el) {
|
||||||
return $(el).closest(".component").prop('component');
|
return $(el).closest(".component").prop('component');
|
||||||
}
|
}
|
||||||
|
|
||||||
async protectedSessionStartedEvent() {
|
|
||||||
await treeCache.loadInitialTree();
|
|
||||||
|
|
||||||
this.triggerEvent('treeCacheReloaded');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const appContext = new AppContext();
|
const appContext = new AppContext(window.glob.isMainWindow);
|
||||||
|
|
||||||
// we should save all outstanding changes before the page/app is closed
|
// we should save all outstanding changes before the page/app is closed
|
||||||
$(window).on('beforeunload', () => {
|
$(window).on('beforeunload', () => {
|
||||||
@@ -130,4 +135,4 @@ $(window).on('hashchange', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default appContext;
|
export default appContext;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ async function moveAfterBranch(branchIdsToMove, afterBranchId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveToParentNote(branchIdsToMove, newParentNoteId) {
|
async function moveToParentNote(branchIdsToMove, newParentBranchId) {
|
||||||
branchIdsToMove = filterRootNote(branchIdsToMove);
|
branchIdsToMove = filterRootNote(branchIdsToMove);
|
||||||
|
|
||||||
for (const branchIdToMove of branchIdsToMove) {
|
for (const branchIdToMove of branchIdsToMove) {
|
||||||
@@ -56,7 +56,7 @@ async function moveToParentNote(branchIdsToMove, newParentNoteId) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentNoteId}`);
|
const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentBranchId}`);
|
||||||
|
|
||||||
if (!resp.success) {
|
if (!resp.success) {
|
||||||
alert(resp.message);
|
alert(resp.message);
|
||||||
@@ -198,8 +198,8 @@ ws.subscribeToMessages(async message => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function cloneNoteTo(childNoteId, parentNoteId, prefix) {
|
async function cloneNoteTo(childNoteId, parentBranchId, prefix) {
|
||||||
const resp = await server.put('notes/' + childNoteId + '/clone-to/' + parentNoteId, {
|
const resp = await server.put(`notes/${childNoteId}/clone-to/${parentBranchId}`, {
|
||||||
prefix: prefix
|
prefix: prefix
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -225,4 +225,4 @@ export default {
|
|||||||
moveNodeUpInHierarchy,
|
moveNodeUpInHierarchy,
|
||||||
cloneNoteAfter,
|
cloneNoteAfter,
|
||||||
cloneNoteTo
|
cloneNoteTo
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import ScriptContext from "./script_context.js";
|
import ScriptContext from "./script_context.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import treeCache from "./tree_cache.js";
|
|
||||||
|
|
||||||
async function getAndExecuteBundle(noteId, originEntity = null) {
|
async function getAndExecuteBundle(noteId, originEntity = null) {
|
||||||
const bundle = await server.get('script/bundle/' + noteId);
|
const bundle = await server.get('script/bundle/' + noteId);
|
||||||
@@ -77,4 +76,4 @@ export default {
|
|||||||
getAndExecuteBundle,
|
getAndExecuteBundle,
|
||||||
executeStartupBundles,
|
executeStartupBundles,
|
||||||
getWidgetBundlesByParent
|
getWidgetBundlesByParent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ async function pasteAfter(afterBranchId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pasteInto(parentNoteId) {
|
async function pasteInto(parentBranchId) {
|
||||||
if (isClipboardEmpty()) {
|
if (isClipboardEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clipboardMode === 'cut') {
|
if (clipboardMode === 'cut') {
|
||||||
await branchService.moveToParentNote(clipboardBranchIds, parentNoteId);
|
await branchService.moveToParentNote(clipboardBranchIds, parentBranchId);
|
||||||
|
|
||||||
clipboardBranchIds = [];
|
clipboardBranchIds = [];
|
||||||
clipboardMode = null;
|
clipboardMode = null;
|
||||||
@@ -50,7 +50,7 @@ async function pasteInto(parentNoteId) {
|
|||||||
for (const clipboardBranch of clipboardBranches) {
|
for (const clipboardBranch of clipboardBranches) {
|
||||||
const clipboardNote = await clipboardBranch.getNote();
|
const clipboardNote = await clipboardBranch.getNote();
|
||||||
|
|
||||||
await branchService.cloneNoteTo(clipboardNote.noteId, parentNoteId);
|
await branchService.cloneNoteTo(clipboardNote.noteId, parentBranchId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy will keep clipboardBranchIds and clipboardMode so it's possible to paste into multiple places
|
// copy will keep clipboardBranchIds and clipboardMode so it's possible to paste into multiple places
|
||||||
@@ -89,4 +89,4 @@ export default {
|
|||||||
cut,
|
cut,
|
||||||
copy,
|
copy,
|
||||||
isClipboardEmpty
|
isClipboardEmpty
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Component from "../widgets/component.js";
|
|||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import noteCreateService from "./note_create.js";
|
import noteCreateService from "./note_create.js";
|
||||||
import ws from "./ws.js";
|
import ws from "./ws.js";
|
||||||
|
import bundleService from "./bundle.js";
|
||||||
|
|
||||||
export default class Entrypoints extends Component {
|
export default class Entrypoints extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -182,4 +183,40 @@ export default class Entrypoints extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createTopLevelNoteCommand() { noteCreateService.createNewTopLevelNote(); }
|
createTopLevelNoteCommand() { noteCreateService.createNewTopLevelNote(); }
|
||||||
|
|
||||||
|
async openInWindowCommand({notePath}) {
|
||||||
|
if (utils.isElectron()) {
|
||||||
|
const {ipcRenderer} = utils.dynamicRequire('electron');
|
||||||
|
|
||||||
|
ipcRenderer.send('create-extra-window', {notePath});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?extra=1#' + notePath;
|
||||||
|
|
||||||
|
window.open(url, '', 'width=1000,height=800');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async openNewWindowCommand() {
|
||||||
|
this.openInWindowCommand({notePath: ''});
|
||||||
|
}
|
||||||
|
|
||||||
|
async runActiveNoteCommand() {
|
||||||
|
const note = appContext.tabManager.getActiveTabNote();
|
||||||
|
|
||||||
|
// ctrl+enter is also used elsewhere so make sure we're running only when appropriate
|
||||||
|
if (!note || note.type !== 'code') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.mime.endsWith("env=frontend")) {
|
||||||
|
await bundleService.getAndExecuteBundle(note.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.mime.endsWith("env=backend")) {
|
||||||
|
await server.post('script/run/' + note.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
toastService.showMessage("Note executed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,6 +403,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
|||||||
* @method
|
* @method
|
||||||
*/
|
*/
|
||||||
this.waitUntilSynced = ws.waitForMaxKnownSyncId;
|
this.waitUntilSynced = ws.waitForMaxKnownSyncId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will refresh all currently opened notes which have included note specified in the parameter
|
||||||
|
*
|
||||||
|
* @param includedNoteId - noteId of the included note
|
||||||
|
*/
|
||||||
|
this.refreshIncludedNote = includedNoteId => appContext.triggerEvent('refreshIncludedNote', {noteId: includedNoteId});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FrontendScriptApi;
|
export default FrontendScriptApi;
|
||||||
|
|||||||
@@ -49,11 +49,7 @@ function setupGlobs() {
|
|||||||
|
|
||||||
let message = "Uncaught error: ";
|
let message = "Uncaught error: ";
|
||||||
|
|
||||||
if (string.includes("Cannot read property 'defaultView' of undefined")) {
|
if (string.includes("script error")) {
|
||||||
// ignore this specific error which is very common but we don't know where it comes from
|
|
||||||
// and it seems to be harmless
|
|
||||||
return true;
|
|
||||||
} else if (string.includes("script error")) {
|
|
||||||
message += 'No details available';
|
message += 'No details available';
|
||||||
} else {
|
} else {
|
||||||
message += [
|
message += [
|
||||||
@@ -61,8 +57,9 @@ function setupGlobs() {
|
|||||||
'URL: ' + url,
|
'URL: ' + url,
|
||||||
'Line: ' + lineNo,
|
'Line: ' + lineNo,
|
||||||
'Column: ' + columnNo,
|
'Column: ' + columnNo,
|
||||||
'Error object: ' + JSON.stringify(error)
|
'Error object: ' + JSON.stringify(error),
|
||||||
].join(' - ');
|
'Stack: ' + error && error.stack
|
||||||
|
].join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.logError(message);
|
ws.logError(message);
|
||||||
@@ -86,9 +83,11 @@ function setupGlobs() {
|
|||||||
|
|
||||||
$("body").on("click", "a.external", function () {
|
$("body").on("click", "a.external", function () {
|
||||||
window.open($(this).attr("href"), '_blank');
|
window.open($(this).attr("href"), '_blank');
|
||||||
|
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setupGlobs
|
setupGlobs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ function updateDisplayedShortcuts($container) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$container.find('button[data-command],a.icon-action[data-command],.kb-in-title').each(async (i, el) => {
|
$container.find('[data-trigger-command]').each(async (i, el) => {
|
||||||
const actionName = $(el).attr('data-command');
|
const actionName = $(el).attr('data-trigger-command');
|
||||||
const action = await getAction(actionName, true);
|
const action = await getAction(actionName, true);
|
||||||
|
|
||||||
if (action) {
|
if (action) {
|
||||||
|
|||||||
@@ -81,24 +81,29 @@ function goToLink(e) {
|
|||||||
}
|
}
|
||||||
else if (e.which === 1) {
|
else if (e.which === 1) {
|
||||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||||
activeTabContext.setNote(notePath)
|
activeTabContext.setNote(notePath);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const address = $link.attr('href');
|
if (e.which === 1) {
|
||||||
|
const address = $link.attr('href');
|
||||||
|
|
||||||
if (address && address.startsWith('http')) {
|
if (address && address.startsWith('http')) {
|
||||||
window.open(address, '_blank');
|
window.open(address, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newTabContextMenu(e) {
|
function linkContextMenu(e) {
|
||||||
const $link = $(e.target).closest("a");
|
const $link = $(e.target).closest("a");
|
||||||
|
|
||||||
const notePath = getNotePathFromLink($link);
|
const notePath = getNotePathFromLink($link);
|
||||||
@@ -113,12 +118,16 @@ function newTabContextMenu(e) {
|
|||||||
x: e.pageX,
|
x: e.pageX,
|
||||||
y: e.pageY,
|
y: e.pageY,
|
||||||
items: [
|
items: [
|
||||||
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"}
|
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "empty"},
|
||||||
|
{title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "window-open"}
|
||||||
],
|
],
|
||||||
selectMenuItemHandler: ({command}) => {
|
selectMenuItemHandler: ({command}) => {
|
||||||
if (command === 'openNoteInNewTab') {
|
if (command === 'openNoteInNewTab') {
|
||||||
appContext.tabManager.openTabWithNote(notePath);
|
appContext.tabManager.openTabWithNote(notePath);
|
||||||
}
|
}
|
||||||
|
else if (command === 'openNoteInNewWindow') {
|
||||||
|
appContext.openInNewWindow(notePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -128,7 +137,7 @@ function newTabContextMenu(e) {
|
|||||||
$(document).on('mousedown', "a[data-action='note']", goToLink);
|
$(document).on('mousedown', "a[data-action='note']", goToLink);
|
||||||
$(document).on('mousedown', 'div.popover-content a, div.ui-tooltip-content a', goToLink);
|
$(document).on('mousedown', 'div.popover-content a, div.ui-tooltip-content a', goToLink);
|
||||||
$(document).on('dblclick', '.note-detail-text a', goToLink);
|
$(document).on('dblclick', '.note-detail-text a', goToLink);
|
||||||
$(document).on('mousedown', '.note-detail-text a', function (e) {
|
$(document).on('mousedown', '.note-detail-text a:not(.reference-link)', function (e) {
|
||||||
const $link = $(e.target).closest("a");
|
const $link = $(e.target).closest("a");
|
||||||
const notePath = getNotePathFromLink($link);
|
const notePath = getNotePathFromLink($link);
|
||||||
|
|
||||||
@@ -151,21 +160,26 @@ $(document).on('mousedown', '.note-detail-text a', function (e) {
|
|||||||
|
|
||||||
$(document).on('mousedown', '.note-detail-book a', goToLink);
|
$(document).on('mousedown', '.note-detail-book a', goToLink);
|
||||||
$(document).on('mousedown', '.note-detail-render a', goToLink);
|
$(document).on('mousedown', '.note-detail-render a', goToLink);
|
||||||
$(document).on('mousedown', '.note-detail-text.ck-read-only a,.note-detail-text a.reference-link', goToLink);
|
$(document).on('mousedown', '.note-detail-text a.reference-link', goToLink);
|
||||||
|
$(document).on('mousedown', '.note-detail-readonly-text a.reference-link', goToLink);
|
||||||
|
$(document).on('mousedown', '.note-detail-readonly-text a', goToLink);
|
||||||
$(document).on('mousedown', 'a.ck-link-actions__preview', goToLink);
|
$(document).on('mousedown', 'a.ck-link-actions__preview', goToLink);
|
||||||
|
$(document).on('click', 'section.include-note a', goToLink);
|
||||||
$(document).on('click', 'a.ck-link-actions__preview', e => {
|
$(document).on('click', 'a.ck-link-actions__preview', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('contextmenu', 'a.ck-link-actions__preview', newTabContextMenu);
|
$(document).on('contextmenu', 'a.ck-link-actions__preview', linkContextMenu);
|
||||||
$(document).on('contextmenu', '.note-detail-text a', newTabContextMenu);
|
$(document).on('contextmenu', '.note-detail-text a', linkContextMenu);
|
||||||
$(document).on('contextmenu', "a[data-action='note']", newTabContextMenu);
|
$(document).on('contextmenu', '.note-detail-readonly-text a', linkContextMenu);
|
||||||
$(document).on('contextmenu', ".note-detail-render a", newTabContextMenu);
|
$(document).on('contextmenu', "a[data-action='note']", linkContextMenu);
|
||||||
$(document).on('contextmenu', ".note-paths-widget a", newTabContextMenu);
|
$(document).on('contextmenu', ".note-detail-render a", linkContextMenu);
|
||||||
|
$(document).on('contextmenu', ".note-paths-widget a", linkContextMenu);
|
||||||
|
$(document).on('contextmenu', "section.include-note a", linkContextMenu);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getNotePathFromUrl,
|
getNotePathFromUrl,
|
||||||
createNoteLink,
|
createNoteLink,
|
||||||
goToLink
|
goToLink
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default class LoadResults {
|
|||||||
|
|
||||||
this.noteIdToSourceId = {};
|
this.noteIdToSourceId = {};
|
||||||
this.sourceIdToNoteIds = {};
|
this.sourceIdToNoteIds = {};
|
||||||
|
|
||||||
this.branches = [];
|
this.branches = [];
|
||||||
|
|
||||||
this.attributes = [];
|
this.attributes = [];
|
||||||
@@ -54,8 +54,9 @@ export default class LoadResults {
|
|||||||
this.attributes.push({attributeId, sourceId});
|
this.attributes.push({attributeId, sourceId});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttributes() {
|
getAttributes(sourceId = 'none') {
|
||||||
return this.attributes
|
return this.attributes
|
||||||
|
.filter(row => row.sourceId !== sourceId)
|
||||||
.map(row => this.treeCache.attributes[row.attributeId])
|
.map(row => this.treeCache.attributes[row.attributeId])
|
||||||
.filter(attr => !!attr);
|
.filter(attr => !!attr);
|
||||||
}
|
}
|
||||||
@@ -101,6 +102,15 @@ export default class LoadResults {
|
|||||||
this.options.includes(name);
|
this.options.includes(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} true if there are changes which could affect the attributes (including inherited ones)
|
||||||
|
* notably changes in note itself should not have any effect on attributes
|
||||||
|
*/
|
||||||
|
hasAttributeRelatedChanges() {
|
||||||
|
return this.branches.length > 0
|
||||||
|
|| this.attributes.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
isEmpty() {
|
isEmpty() {
|
||||||
return Object.keys(this.noteIdToSourceId).length === 0
|
return Object.keys(this.noteIdToSourceId).length === 0
|
||||||
&& this.branches.length === 0
|
&& this.branches.length === 0
|
||||||
@@ -110,4 +120,4 @@ export default class LoadResults {
|
|||||||
&& this.contentNoteIdToSourceId.length === 0
|
&& this.contentNoteIdToSourceId.length === 0
|
||||||
&& this.options.length === 0;
|
&& this.options.length === 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,16 +34,10 @@ export default class MainTreeExecutors extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {note} = await noteCreateService.createNote(activeNote.noteId, {
|
await noteCreateService.createNote(activeNote.noteId, {
|
||||||
isProtected: activeNote.isProtected,
|
isProtected: activeNote.isProtected,
|
||||||
saveSelection: false
|
saveSelection: false
|
||||||
});
|
});
|
||||||
|
|
||||||
await ws.waitForMaxKnownSyncId();
|
|
||||||
|
|
||||||
appContext.tabManager.getActiveTabContext().setNote(note.noteId);
|
|
||||||
|
|
||||||
appContext.triggerCommand('focusAndSelectTitle');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNoteAfterCommand() {
|
async createNoteAfterCommand() {
|
||||||
@@ -55,17 +49,11 @@ export default class MainTreeExecutors extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {note} = await noteCreateService.createNote(parentNoteId, {
|
await noteCreateService.createNote(parentNoteId, {
|
||||||
target: 'after',
|
target: 'after',
|
||||||
targetBranchId: node.data.branchId,
|
targetBranchId: node.data.branchId,
|
||||||
isProtected: isProtected,
|
isProtected: isProtected,
|
||||||
saveSelection: true
|
saveSelection: false
|
||||||
});
|
});
|
||||||
|
|
||||||
await ws.waitForMaxKnownSyncId();
|
|
||||||
|
|
||||||
appContext.tabManager.getActiveTabContext().setNote(note.noteId);
|
|
||||||
|
|
||||||
appContext.triggerCommand('focusAndSelectTitle');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
export default class Mutex {
|
|
||||||
constructor() {
|
|
||||||
this.queue = [];
|
|
||||||
this.pending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isLocked() {
|
|
||||||
return this.pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquire() {
|
|
||||||
const ticket = new Promise(resolve => this.queue.push(resolve));
|
|
||||||
|
|
||||||
if (!this.pending) {
|
|
||||||
this.dispatchNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ticket;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchNext() {
|
|
||||||
if (this.queue.length > 0) {
|
|
||||||
this.pending = true;
|
|
||||||
this.queue.shift()(this.dispatchNext.bind(this));
|
|
||||||
} else {
|
|
||||||
this.pending = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
src/public/app/services/note_attribute_cache.js
Normal file
20
src/public/app/services/note_attribute_cache.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Purpose of this class is to cache list of attributes for notes.
|
||||||
|
*
|
||||||
|
* Cache invalidation granularity is global - whenever a write operation is detected to notes, branches or attributes
|
||||||
|
* we invalidate the whole cache. That's OK, since the purpose for this is to speed up batch read-only operations, such
|
||||||
|
* as loading the tree which uses attributes heavily.
|
||||||
|
*/
|
||||||
|
class NoteAttributeCache {
|
||||||
|
constructor() {
|
||||||
|
this.attributes = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate() {
|
||||||
|
this.attributes = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteAttributeCache = new NoteAttributeCache();
|
||||||
|
|
||||||
|
export default noteAttributeCache;
|
||||||
@@ -24,7 +24,7 @@ async function getRenderedContent(note) {
|
|||||||
.attr("src", `api/images/${note.noteId}/${note.title}`)
|
.attr("src", `api/images/${note.noteId}/${note.title}`)
|
||||||
.css("max-width", "100%");
|
.css("max-width", "100%");
|
||||||
}
|
}
|
||||||
else if (type === 'file') {
|
else if (type === 'file' || type === 'pdf') {
|
||||||
function getFileUrl() {
|
function getFileUrl() {
|
||||||
return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
|
return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
|
||||||
}
|
}
|
||||||
@@ -47,19 +47,21 @@ async function getRenderedContent(note) {
|
|||||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||||
$openButton.toggle(!note.isProtected);
|
$openButton.toggle(!note.isProtected);
|
||||||
|
|
||||||
$rendered = $('<div>');
|
$rendered = $('<div style="display: flex; flex-direction: column; height: 100%;">');
|
||||||
|
|
||||||
if (note.mime === 'application/pdf' && utils.isElectron()) {
|
if (type === 'pdf') {
|
||||||
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; height: 100%; flex-grow: 100;"></iframe>');
|
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; flex-grow: 100;"></iframe>');
|
||||||
$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + note.noteId + "/open"));
|
$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + note.noteId + "/open"));
|
||||||
|
|
||||||
$rendered.append($pdfPreview);
|
$rendered.append($pdfPreview);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rendered
|
$rendered.append(
|
||||||
.append($downloadButton)
|
$("<div>")
|
||||||
.append(' ')
|
.append($downloadButton)
|
||||||
.append($openButton);
|
.append(' ')
|
||||||
|
.append($openButton)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else if (type === 'render') {
|
else if (type === 'render') {
|
||||||
$rendered = $('<div>');
|
$rendered = $('<div>');
|
||||||
@@ -90,6 +92,10 @@ async function getRenderedContent(note) {
|
|||||||
function getRenderingType(note) {
|
function getRenderingType(note) {
|
||||||
let type = note.type;
|
let type = note.type;
|
||||||
|
|
||||||
|
if (type === 'file' && note.mime === 'application/pdf') {
|
||||||
|
type = 'pdf';
|
||||||
|
}
|
||||||
|
|
||||||
if (note.isProtected) {
|
if (note.isProtected) {
|
||||||
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
protectedSessionHolder.touchProtectedSession();
|
protectedSessionHolder.touchProtectedSession();
|
||||||
@@ -104,4 +110,4 @@ function getRenderingType(note) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
getRenderedContent
|
getRenderedContent
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,8 +48,12 @@ async function createNote(parentNoteId, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.activate) {
|
if (options.activate) {
|
||||||
|
await ws.waitForMaxKnownSyncId();
|
||||||
|
|
||||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||||
activeTabContext.setNote(note.noteId);
|
await activeTabContext.setNote(note.noteId);
|
||||||
|
|
||||||
|
appContext.triggerCommand('focusAndSelectTitle');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {note, branch};
|
return {note, branch};
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import treeService from './tree.js';
|
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import ws from "./ws.js";
|
import ws from "./ws.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
|
import treeCache from "./tree_cache.js";
|
||||||
const $enterProtectedSessionButton = $("#enter-protected-session-button");
|
|
||||||
const $leaveProtectedSessionButton = $("#leave-protected-session-button");
|
|
||||||
|
|
||||||
let protectedSessionDeferred = null;
|
let protectedSessionDeferred = null;
|
||||||
|
|
||||||
async function leaveProtectedSession() {
|
async function leaveProtectedSession() {
|
||||||
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
utils.reloadApp();
|
protectedSessionHolder.resetProtectedSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +31,15 @@ function enterProtectedSession() {
|
|||||||
return dfd.promise();
|
return dfd.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function reloadData() {
|
||||||
|
const allNoteIds = Object.keys(treeCache.notes);
|
||||||
|
|
||||||
|
await treeCache.loadInitialTree();
|
||||||
|
|
||||||
|
// make sure that all notes used in the application are loaded, including the ones not shown in the tree
|
||||||
|
await treeCache.reloadNotes(allNoteIds, true);
|
||||||
|
}
|
||||||
|
|
||||||
async function setupProtectedSession(password) {
|
async function setupProtectedSession(password) {
|
||||||
const response = await enterProtectedSessionOnServer(password);
|
const response = await enterProtectedSessionOnServer(password);
|
||||||
|
|
||||||
@@ -45,6 +51,10 @@ async function setupProtectedSession(password) {
|
|||||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
||||||
protectedSessionHolder.touchProtectedSession();
|
protectedSessionHolder.touchProtectedSession();
|
||||||
|
|
||||||
|
await reloadData();
|
||||||
|
|
||||||
|
await appContext.triggerEvent('treeCacheReloaded');
|
||||||
|
|
||||||
appContext.triggerEvent('protectedSessionStarted');
|
appContext.triggerEvent('protectedSessionStarted');
|
||||||
|
|
||||||
if (protectedSessionDeferred !== null) {
|
if (protectedSessionDeferred !== null) {
|
||||||
@@ -54,9 +64,6 @@ async function setupProtectedSession(password) {
|
|||||||
protectedSessionDeferred = null;
|
protectedSessionDeferred = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$enterProtectedSessionButton.hide();
|
|
||||||
$leaveProtectedSessionButton.show();
|
|
||||||
|
|
||||||
toastService.showMessage("Protected session has been started.");
|
toastService.showMessage("Protected session has been started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,4 +113,4 @@ export default {
|
|||||||
enterProtectedSession,
|
enterProtectedSession,
|
||||||
leaveProtectedSession,
|
leaveProtectedSession,
|
||||||
setupProtectedSession
|
setupProtectedSession
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,9 +39,16 @@ function touchProtectedSession() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function touchProtectedSessionIfNecessary(note) {
|
||||||
|
if (note && note.isProtected && isProtectedSessionAvailable()) {
|
||||||
|
touchProtectedSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setProtectedSessionId,
|
setProtectedSessionId,
|
||||||
resetProtectedSession,
|
resetProtectedSession,
|
||||||
isProtectedSessionAvailable,
|
isProtectedSessionAvailable,
|
||||||
touchProtectedSession
|
touchProtectedSession,
|
||||||
|
touchProtectedSessionIfNecessary
|
||||||
};
|
};
|
||||||
@@ -8,6 +8,7 @@ function getHeaders(headers) {
|
|||||||
// also avoiding using underscores instead of dashes since nginx filters them out by default
|
// also avoiding using underscores instead of dashes since nginx filters them out by default
|
||||||
const allHeaders = {
|
const allHeaders = {
|
||||||
'trilium-source-id': glob.sourceId,
|
'trilium-source-id': glob.sourceId,
|
||||||
|
'trilium-local-now-datetime': utils.localNowDateTime(),
|
||||||
'x-csrf-token': glob.csrfToken
|
'x-csrf-token': glob.csrfToken
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ async function syncNow() {
|
|||||||
toastService.showMessage("Sync finished successfully.");
|
toastService.showMessage("Sync finished successfully.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (result.message.length > 50) {
|
if (result.message.length > 100) {
|
||||||
result.message = result.message.substr(0, 50);
|
result.message = result.message.substr(0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
toastService.showError("Sync failed: " + result.message);
|
toastService.showError("Sync failed: " + result.message);
|
||||||
@@ -25,4 +25,4 @@ async function forceNoteSync(noteId) {
|
|||||||
export default {
|
export default {
|
||||||
syncNow,
|
syncNow,
|
||||||
forceNoteSync
|
forceNoteSync
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class TabContext extends Component {
|
|||||||
|
|
||||||
this.autoBookDisabled = false;
|
this.autoBookDisabled = false;
|
||||||
this.textPreviewDisabled = false;
|
this.textPreviewDisabled = false;
|
||||||
|
this.codePreviewDisabled = false;
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
@@ -69,13 +70,10 @@ class TabContext extends Component {
|
|||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) {
|
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||||
// FIXME: there are probably more places where this should be done
|
|
||||||
protectedSessionHolder.touchProtectedSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triggerSwitchEvent) {
|
if (triggerSwitchEvent) {
|
||||||
this.triggerEvent('tabNoteSwitched', {
|
await this.triggerEvent('tabNoteSwitched', {
|
||||||
tabContext: this,
|
tabContext: this,
|
||||||
notePath: this.notePath
|
notePath: this.notePath
|
||||||
});
|
});
|
||||||
@@ -129,4 +127,4 @@ class TabContext extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabContext;
|
export default TabContext;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import treeCache from "./tree_cache.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import TabContext from "./tab_context.js";
|
import TabContext from "./tab_context.js";
|
||||||
|
import appContext from "./app_context.js";
|
||||||
|
|
||||||
export default class TabManager extends Component {
|
export default class TabManager extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -14,6 +15,10 @@ export default class TabManager extends Component {
|
|||||||
this.activeTabId = null;
|
this.activeTabId = null;
|
||||||
|
|
||||||
this.tabsUpdate = new SpacedUpdate(async () => {
|
this.tabsUpdate = new SpacedUpdate(async () => {
|
||||||
|
if (!appContext.isMainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const openTabs = this.tabContexts
|
const openTabs = this.tabContexts
|
||||||
.map(tc => tc.getTabState())
|
.map(tc => tc.getTabState())
|
||||||
.filter(t => !!t);
|
.filter(t => !!t);
|
||||||
@@ -30,7 +35,9 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadTabs() {
|
async loadTabs() {
|
||||||
const openTabs = options.getJson('openTabs') || [];
|
const tabsToOpen = appContext.isMainWindow
|
||||||
|
? (options.getJson('openTabs') || [])
|
||||||
|
: [];
|
||||||
|
|
||||||
// if there's notePath in the URL, make sure it's open and active
|
// if there's notePath in the URL, make sure it's open and active
|
||||||
// (useful, among others, for opening clipped notes from clipper)
|
// (useful, among others, for opening clipped notes from clipper)
|
||||||
@@ -39,17 +46,17 @@ export default class TabManager extends Component {
|
|||||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
if (noteId && await treeCache.noteExists(noteId)) {
|
if (noteId && await treeCache.noteExists(noteId)) {
|
||||||
for (const tab of openTabs) {
|
for (const tab of tabsToOpen) {
|
||||||
tab.active = false;
|
tab.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundTab = openTabs.find(tab => noteId === treeService.getNoteIdFromNotePath(tab.notePath));
|
const foundTab = tabsToOpen.find(tab => noteId === treeService.getNoteIdFromNotePath(tab.notePath));
|
||||||
|
|
||||||
if (foundTab) {
|
if (foundTab) {
|
||||||
foundTab.active = true;
|
foundTab.active = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
openTabs.push({
|
tabsToOpen.push({
|
||||||
notePath: notePath,
|
notePath: notePath,
|
||||||
active: true
|
active: true
|
||||||
});
|
});
|
||||||
@@ -59,7 +66,7 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
let filteredTabs = [];
|
let filteredTabs = [];
|
||||||
|
|
||||||
for (const openTab of openTabs) {
|
for (const openTab of tabsToOpen) {
|
||||||
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
||||||
|
|
||||||
if (await treeCache.noteExists(noteId)) {
|
if (await treeCache.noteExists(noteId)) {
|
||||||
@@ -75,7 +82,7 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
if (filteredTabs.length === 0) {
|
if (filteredTabs.length === 0) {
|
||||||
filteredTabs.push({
|
filteredTabs.push({
|
||||||
notePath: 'root',
|
notePath: this.isMainWindow ? 'root' : '',
|
||||||
active: true
|
active: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -189,12 +196,14 @@ export default class TabManager extends Component {
|
|||||||
async openTabWithNote(notePath, activate, tabId = null) {
|
async openTabWithNote(notePath, activate, tabId = null) {
|
||||||
const tabContext = await this.openEmptyTab(tabId);
|
const tabContext = await this.openEmptyTab(tabId);
|
||||||
|
|
||||||
await tabContext.setNote(notePath, !activate); // if activate is false then send normal noteSwitched event
|
if (notePath) {
|
||||||
|
await tabContext.setNote(notePath, !activate); // if activate is false then send normal noteSwitched event
|
||||||
|
}
|
||||||
|
|
||||||
if (activate) {
|
if (activate) {
|
||||||
this.activateTab(tabContext.tabId, false);
|
this.activateTab(tabContext.tabId, false);
|
||||||
|
|
||||||
this.triggerEvent('tabNoteSwitchedAndActivated', {
|
await this.triggerEvent('tabNoteSwitchedAndActivated', {
|
||||||
tabContext,
|
tabContext,
|
||||||
notePath: tabContext.notePath // resolved note path
|
notePath: tabContext.notePath // resolved note path
|
||||||
});
|
});
|
||||||
@@ -229,7 +238,7 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
|
|
||||||
this.setCurrentNotePathToHash();
|
this.setCurrentNotePathToHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +249,9 @@ export default class TabManager extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close dangling autocompletes after closing the tab
|
||||||
|
$(".aa-input").autocomplete("close");
|
||||||
|
|
||||||
await this.triggerEvent('beforeTabRemove', {tabId});
|
await this.triggerEvent('beforeTabRemove', {tabId});
|
||||||
|
|
||||||
if (this.tabContexts.length <= 1) {
|
if (this.tabContexts.length <= 1) {
|
||||||
@@ -315,6 +327,14 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveTabToNewWindowCommand({tabId}) {
|
||||||
|
const notePath = this.getTabContextById(tabId).notePath;
|
||||||
|
|
||||||
|
this.removeTab(tabId);
|
||||||
|
|
||||||
|
this.triggerCommand('openInWindow', {notePath});
|
||||||
|
}
|
||||||
|
|
||||||
async hoistedNoteChangedEvent({hoistedNoteId}) {
|
async hoistedNoteChangedEvent({hoistedNoteId}) {
|
||||||
if (hoistedNoteId === 'root') {
|
if (hoistedNoteId === 'root') {
|
||||||
return;
|
return;
|
||||||
@@ -326,4 +346,4 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async function resolveNotePath(notePath) {
|
|||||||
*
|
*
|
||||||
* @return {string[]}
|
* @return {string[]}
|
||||||
*/
|
*/
|
||||||
async function getRunPath(notePath) {
|
async function getRunPath(notePath, logErrors = true) {
|
||||||
utils.assertArguments(notePath);
|
utils.assertArguments(notePath);
|
||||||
|
|
||||||
notePath = notePath.split("-")[0].trim();
|
notePath = notePath.split("-")[0].trim();
|
||||||
@@ -66,10 +66,14 @@ async function getRunPath(notePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!parents.some(p => p.noteId === parentNoteId)) {
|
if (!parents.some(p => p.noteId === parentNoteId)) {
|
||||||
console.debug(utils.now(), "Did not find parent " + parentNoteId + " for child " + childNoteId);
|
if (logErrors) {
|
||||||
|
console.log(utils.now(), "Did not find parent " + parentNoteId + " for child " + childNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
if (parents.length > 0) {
|
if (parents.length > 0) {
|
||||||
console.debug(utils.now(), "Available parents:", parents);
|
if (logErrors) {
|
||||||
|
console.log(utils.now(), "Available parents:", parents);
|
||||||
|
}
|
||||||
|
|
||||||
const someNotePath = await getSomeNotePath(parents[0]);
|
const someNotePath = await getSomeNotePath(parents[0]);
|
||||||
|
|
||||||
@@ -86,7 +90,10 @@ async function getRunPath(notePath) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("No parents so no run path.");
|
if (logErrors) {
|
||||||
|
console.log("No parents so no run path.");
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,6 +169,13 @@ function getNoteIdFromNotePath(notePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNoteIdAndParentIdFromNotePath(notePath) {
|
function getNoteIdAndParentIdFromNotePath(notePath) {
|
||||||
|
if (notePath === 'root') {
|
||||||
|
return {
|
||||||
|
noteId: 'root',
|
||||||
|
parentNoteId: 'none'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let parentNoteId = 'root';
|
let parentNoteId = 'root';
|
||||||
let noteId = '';
|
let noteId = '';
|
||||||
|
|
||||||
@@ -286,4 +300,4 @@ export default {
|
|||||||
getNotePathTitle,
|
getNotePathTitle,
|
||||||
getHashValueFromAddress,
|
getHashValueFromAddress,
|
||||||
parseNotePath
|
parseNotePath
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
import utils from "./utils.js";
|
|
||||||
import treeCache from "./tree_cache.js";
|
|
||||||
import ws from "./ws.js";
|
|
||||||
import hoistedNoteService from "./hoisted_note.js";
|
|
||||||
|
|
||||||
async function prepareRootNode() {
|
|
||||||
await treeCache.initializedPromise;
|
|
||||||
|
|
||||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
|
||||||
|
|
||||||
let hoistedBranch;
|
|
||||||
|
|
||||||
if (hoistedNoteId === 'root') {
|
|
||||||
hoistedBranch = treeCache.getBranch('root');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const hoistedNote = await treeCache.getNote(hoistedNoteId);
|
|
||||||
hoistedBranch = (await hoistedNote.getBranches())[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return await prepareNode(hoistedBranch);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function prepareBranch(note) {
|
|
||||||
if (note.type === 'search') {
|
|
||||||
return await prepareSearchBranch(note);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return await prepareRealBranch(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const NOTE_TYPE_ICONS = {
|
|
||||||
"file": "bx bx-file",
|
|
||||||
"image": "bx bx-image",
|
|
||||||
"code": "bx bx-code",
|
|
||||||
"render": "bx bx-extension",
|
|
||||||
"search": "bx bx-file-find",
|
|
||||||
"relation-map": "bx bx-map-alt",
|
|
||||||
"book": "bx bx-book"
|
|
||||||
};
|
|
||||||
|
|
||||||
function getIconClass(note) {
|
|
||||||
const labels = note.getLabels('iconClass');
|
|
||||||
|
|
||||||
return labels.map(l => l.value).join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIcon(note) {
|
|
||||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
|
||||||
|
|
||||||
const iconClass = getIconClass(note);
|
|
||||||
|
|
||||||
if (iconClass) {
|
|
||||||
return iconClass;
|
|
||||||
}
|
|
||||||
else if (note.noteId === 'root') {
|
|
||||||
return "bx bx-chevrons-right";
|
|
||||||
}
|
|
||||||
else if (note.noteId === hoistedNoteId) {
|
|
||||||
return "bx bxs-arrow-from-bottom";
|
|
||||||
}
|
|
||||||
else if (note.type === 'text') {
|
|
||||||
if (note.hasChildren()) {
|
|
||||||
return "bx bx-folder";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "bx bx-note";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return NOTE_TYPE_ICONS[note.type];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function prepareNode(branch) {
|
|
||||||
const note = await branch.getNote();
|
|
||||||
|
|
||||||
if (!note) {
|
|
||||||
throw new Error(`Branch has no note ` + branch.noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
|
||||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
|
||||||
|
|
||||||
const node = {
|
|
||||||
noteId: note.noteId,
|
|
||||||
parentNoteId: branch.parentNoteId,
|
|
||||||
branchId: branch.branchId,
|
|
||||||
isProtected: note.isProtected,
|
|
||||||
noteType: note.type,
|
|
||||||
title: utils.escapeHtml(title),
|
|
||||||
extraClasses: getExtraClasses(note),
|
|
||||||
icon: getIcon(note),
|
|
||||||
refKey: note.noteId,
|
|
||||||
expanded: branch.isExpanded || hoistedNoteId === note.noteId,
|
|
||||||
lazy: true,
|
|
||||||
key: utils.randomString(12) // this should prevent some "duplicate key" errors
|
|
||||||
};
|
|
||||||
|
|
||||||
if (note.hasChildren() || note.type === 'search') {
|
|
||||||
node.folder = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function prepareRealBranch(parentNote) {
|
|
||||||
utils.assertArguments(parentNote);
|
|
||||||
|
|
||||||
const childBranches = await parentNote.getChildBranches();
|
|
||||||
|
|
||||||
if (!childBranches) {
|
|
||||||
ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteList = [];
|
|
||||||
|
|
||||||
for (const branch of childBranches) {
|
|
||||||
const node = await prepareNode(branch);
|
|
||||||
|
|
||||||
noteList.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return noteList;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function prepareSearchBranch(note) {
|
|
||||||
await treeCache.reloadNotes([note.noteId]);
|
|
||||||
|
|
||||||
const newNote = await treeCache.getNote(note.noteId);
|
|
||||||
|
|
||||||
return await prepareRealBranch(newNote);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getExtraClasses(note) {
|
|
||||||
utils.assertArguments(note);
|
|
||||||
|
|
||||||
const extraClasses = [];
|
|
||||||
|
|
||||||
if (note.isProtected) {
|
|
||||||
extraClasses.push("protected");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (note.getParentNoteIds().length > 1) {
|
|
||||||
extraClasses.push("multiple-parents");
|
|
||||||
}
|
|
||||||
|
|
||||||
const cssClass = note.getCssClass();
|
|
||||||
|
|
||||||
if (cssClass) {
|
|
||||||
extraClasses.push(cssClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
extraClasses.push(utils.getNoteTypeClass(note.type));
|
|
||||||
|
|
||||||
if (note.mime) { // some notes should not have mime type (e.g. render)
|
|
||||||
extraClasses.push(utils.getMimeTypeClass(note.mime));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (note.hasLabel('archived')) {
|
|
||||||
extraClasses.push("archived");
|
|
||||||
}
|
|
||||||
|
|
||||||
return extraClasses.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
prepareRootNode,
|
|
||||||
prepareBranch,
|
|
||||||
getExtraClasses,
|
|
||||||
getIcon
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,8 @@ class TreeCache {
|
|||||||
async loadInitialTree() {
|
async loadInitialTree() {
|
||||||
const resp = await server.get('tree');
|
const resp = await server.get('tree');
|
||||||
|
|
||||||
|
await this.loadParents(resp, false);
|
||||||
|
|
||||||
// clear the cache only directly before adding new content which is important for e.g. switching to protected session
|
// clear the cache only directly before adding new content which is important for e.g. switching to protected session
|
||||||
|
|
||||||
/** @type {Object.<string, NoteShort>} */
|
/** @type {Object.<string, NoteShort>} */
|
||||||
@@ -34,24 +36,28 @@ class TreeCache {
|
|||||||
/** @type {Object.<string, Promise<NoteComplement>>} */
|
/** @type {Object.<string, Promise<NoteComplement>>} */
|
||||||
this.noteComplementPromises = {};
|
this.noteComplementPromises = {};
|
||||||
|
|
||||||
await this.loadParents(resp);
|
|
||||||
this.addResp(resp);
|
this.addResp(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadParents(resp) {
|
async loadParents(resp, additiveLoad) {
|
||||||
const noteIds = new Set(resp.notes.map(note => note.noteId));
|
const noteIds = new Set(resp.notes.map(note => note.noteId));
|
||||||
const missingNoteIds = [];
|
const missingNoteIds = [];
|
||||||
|
const existingNotes = additiveLoad ? this.notes : {};
|
||||||
|
|
||||||
for (const branch of resp.branches) {
|
for (const branch of resp.branches) {
|
||||||
if (!(branch.parentNoteId in this.notes) && !noteIds.has(branch.parentNoteId) && branch.parentNoteId !== 'none') {
|
if (!(branch.parentNoteId in existingNotes) && !noteIds.has(branch.parentNoteId) && branch.parentNoteId !== 'none') {
|
||||||
missingNoteIds.push(branch.parentNoteId);
|
missingNoteIds.push(branch.parentNoteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const attr of resp.attributes) {
|
for (const attr of resp.attributes) {
|
||||||
if (attr.type === 'relation' && attr.name === 'template' && !(attr.value in this.notes) && !noteIds.has(attr.value)) {
|
if (attr.type === 'relation' && attr.name === 'template' && !(attr.value in existingNotes) && !noteIds.has(attr.value)) {
|
||||||
missingNoteIds.push(attr.value);
|
missingNoteIds.push(attr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(attr.noteId in existingNotes) && !noteIds.has(attr.noteId)) {
|
||||||
|
missingNoteIds.push(attr.noteId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingNoteIds.length > 0) {
|
if (missingNoteIds.length > 0) {
|
||||||
@@ -61,7 +67,7 @@ class TreeCache {
|
|||||||
resp.branches = resp.branches.concat(newResp.branches);
|
resp.branches = resp.branches.concat(newResp.branches);
|
||||||
resp.attributes = resp.attributes.concat(newResp.attributes);
|
resp.attributes = resp.attributes.concat(newResp.attributes);
|
||||||
|
|
||||||
await this.loadParents(resp);
|
await this.loadParents(resp, additiveLoad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +160,7 @@ class TreeCache {
|
|||||||
|
|
||||||
const resp = await server.post('tree/load', { noteIds });
|
const resp = await server.post('tree/load', { noteIds });
|
||||||
|
|
||||||
await this.loadParents(resp);
|
await this.loadParents(resp, true);
|
||||||
this.addResp(resp);
|
this.addResp(resp);
|
||||||
|
|
||||||
for (const note of resp.notes) {
|
for (const note of resp.notes) {
|
||||||
@@ -231,7 +237,7 @@ class TreeCache {
|
|||||||
/** @return {Promise<NoteShort>} */
|
/** @return {Promise<NoteShort>} */
|
||||||
async getNote(noteId, silentNotFoundError = false) {
|
async getNote(noteId, silentNotFoundError = false) {
|
||||||
if (noteId === 'none') {
|
if (noteId === 'none') {
|
||||||
console.log(`No 'none' note.`);
|
console.trace(`No 'none' note.`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (!noteId) {
|
else if (!noteId) {
|
||||||
@@ -246,10 +252,10 @@ class TreeCache {
|
|||||||
return this.notes[noteId];
|
return this.notes[noteId];
|
||||||
}
|
}
|
||||||
|
|
||||||
getBranches(branchIds) {
|
getBranches(branchIds, silentNotFoundError = false) {
|
||||||
return branchIds
|
return branchIds
|
||||||
.map(branchId => this.getBranch(branchId))
|
.map(branchId => this.getBranch(branchId, silentNotFoundError))
|
||||||
.filter(b => b !== null);
|
.filter(b => !!b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {Branch} */
|
/** @return {Branch} */
|
||||||
@@ -267,12 +273,28 @@ class TreeCache {
|
|||||||
async getBranchId(parentNoteId, childNoteId) {
|
async getBranchId(parentNoteId, childNoteId) {
|
||||||
const child = await this.getNote(childNoteId);
|
const child = await this.getNote(childNoteId);
|
||||||
|
|
||||||
|
if (!child) {
|
||||||
|
console.error(`Could not find branchId for parent=${parentNoteId}, child=${childNoteId} since child does not exist`);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return child.parentToBranch[parentNoteId];
|
return child.parentToBranch[parentNoteId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<NoteComplement>}
|
||||||
|
*/
|
||||||
async getNoteComplement(noteId) {
|
async getNoteComplement(noteId) {
|
||||||
if (!this.noteComplementPromises[noteId]) {
|
if (!this.noteComplementPromises[noteId]) {
|
||||||
this.noteComplementPromises[noteId] = server.get('notes/' + noteId).then(row => new NoteComplement(row));
|
this.noteComplementPromises[noteId] = server.get('notes/' + noteId).then(row => new NoteComplement(row));
|
||||||
|
|
||||||
|
// we don't want to keep large payloads forever in memory so we clean that up quite quickly
|
||||||
|
// this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components)
|
||||||
|
// this is also a work around for missing invalidation after change
|
||||||
|
this.noteComplementPromises[noteId].then(
|
||||||
|
() => setTimeout(() => this.noteComplementPromises[noteId] = null, 1000)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.noteComplementPromises[noteId];
|
return await this.noteComplementPromises[noteId];
|
||||||
@@ -281,4 +303,4 @@ class TreeCache {
|
|||||||
|
|
||||||
const treeCache = new TreeCache();
|
const treeCache = new TreeCache();
|
||||||
|
|
||||||
export default treeCache;
|
export default treeCache;
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ class TreeContextMenu {
|
|||||||
async getMenuItems() {
|
async getMenuItems() {
|
||||||
const note = await treeCache.getNote(this.node.data.noteId);
|
const note = await treeCache.getNote(this.node.data.noteId);
|
||||||
const branch = treeCache.getBranch(this.node.data.branchId);
|
const branch = treeCache.getBranch(this.node.data.branchId);
|
||||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
|
||||||
const isNotRoot = note.noteId !== 'root';
|
const isNotRoot = note.noteId !== 'root';
|
||||||
const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId();
|
const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId();
|
||||||
|
const parentNote = isNotRoot ? await treeCache.getNote(branch.parentNoteId) : null;
|
||||||
|
|
||||||
// some actions don't support multi-note so they are disabled when notes are selected
|
// some actions don't support multi-note so they are disabled when notes are selected
|
||||||
// the only exception is when the only selected note is the one that was right-clicked, then
|
// the only exception is when the only selected note is the one that was right-clicked, then
|
||||||
@@ -56,7 +56,8 @@ class TreeContextMenu {
|
|||||||
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: 'Open in new tab', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
|
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
|
||||||
|
{ title: 'Open in a new window', command: "openInWindow", uiIcon: "window-open", enabled: noSelectedNotes },
|
||||||
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus",
|
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus",
|
||||||
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
|
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
|
||||||
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
||||||
@@ -73,7 +74,8 @@ class TreeContextMenu {
|
|||||||
{ title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "empty",
|
{ title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "empty",
|
||||||
enabled: isNotRoot && parentNotSearch && noSelectedNotes},
|
enabled: isNotRoot && parentNotSearch && noSelectedNotes},
|
||||||
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
||||||
{ title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
|
{ title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "expand", enabled: noSelectedNotes },
|
||||||
|
{ title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "collapse", enabled: noSelectedNotes },
|
||||||
{ title: "Force note sync", command: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes },
|
{ title: "Force note sync", command: "forceNoteSync", uiIcon: "refresh", enabled: noSelectedNotes },
|
||||||
{ title: 'Sort alphabetically <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch },
|
{ title: 'Sort alphabetically <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "empty", enabled: noSelectedNotes && notSearch },
|
||||||
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "history", enabled: noSelectedNotes }
|
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "history", enabled: noSelectedNotes }
|
||||||
@@ -129,7 +131,7 @@ class TreeContextMenu {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.treeWidget.triggerCommand(command, {node: this.node});
|
this.treeWidget.triggerCommand(command, {node: this.node, notePath: notePath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ function formatDateTime(date) {
|
|||||||
return formatDate(date) + " " + formatTime(date);
|
return formatDate(date) + " " + formatTime(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function localNowDateTime() {
|
||||||
|
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||||
|
}
|
||||||
|
|
||||||
function now() {
|
function now() {
|
||||||
return formatTimeWithSeconds(new Date());
|
return formatTimeWithSeconds(new Date());
|
||||||
}
|
}
|
||||||
@@ -60,8 +64,19 @@ function assertArguments() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const entityMap = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
"'": ''',
|
||||||
|
'/': '/',
|
||||||
|
'`': '`',
|
||||||
|
'=': '='
|
||||||
|
};
|
||||||
|
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return $('<div/>').text(str).html();
|
return str.replace(/[&<>"'`=\/]/g, s => entityMap[s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopWatch(what, func) {
|
async function stopWatch(what, func) {
|
||||||
@@ -99,7 +114,7 @@ function download(url) {
|
|||||||
url += '?' + Date.now(); // don't use cache
|
url += '?' + Date.now(); // don't use cache
|
||||||
|
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
const remote = utils.dynamicRequire('electron').remote;
|
const remote = dynamicRequire('electron').remote;
|
||||||
|
|
||||||
remote.getCurrentWebContents().downloadURL(url);
|
remote.getCurrentWebContents().downloadURL(url);
|
||||||
}
|
}
|
||||||
@@ -273,7 +288,7 @@ function isHtmlEmpty(html) {
|
|||||||
|
|
||||||
async function clearBrowserCache() {
|
async function clearBrowserCache() {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
const win = utils.dynamicRequire('electron').remote.getCurrentWindow();
|
const win = dynamicRequire('electron').remote.getCurrentWindow();
|
||||||
await win.webContents.session.clearCache();
|
await win.webContents.session.clearCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,6 +327,27 @@ function dynamicRequire(moduleName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function timeLimit(promise, limitMs) {
|
||||||
|
// better stack trace if created outside of promise
|
||||||
|
const error = new Error('Process exceeded time limit ' + limitMs);
|
||||||
|
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
let resolved = false;
|
||||||
|
|
||||||
|
promise.then(result => {
|
||||||
|
resolved = true;
|
||||||
|
|
||||||
|
res(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!resolved) {
|
||||||
|
rej(error);
|
||||||
|
}
|
||||||
|
}, limitMs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
reloadApp,
|
reloadApp,
|
||||||
parseDate,
|
parseDate,
|
||||||
@@ -321,6 +357,7 @@ export default {
|
|||||||
formatDate,
|
formatDate,
|
||||||
formatDateISO,
|
formatDateISO,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
|
localNowDateTime,
|
||||||
now,
|
now,
|
||||||
isElectron,
|
isElectron,
|
||||||
isMac,
|
isMac,
|
||||||
@@ -350,5 +387,6 @@ export default {
|
|||||||
normalizeShortcut,
|
normalizeShortcut,
|
||||||
copySelectionToClipboard,
|
copySelectionToClipboard,
|
||||||
isCKEditorInitialized,
|
isCKEditorInitialized,
|
||||||
dynamicRequire
|
dynamicRequire,
|
||||||
};
|
timeLimit
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Branch from "../entities/branch.js";
|
|||||||
import Attribute from "../entities/attribute.js";
|
import Attribute from "../entities/attribute.js";
|
||||||
import options from "./options.js";
|
import options from "./options.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
|
import noteAttributeCache from "./note_attribute_cache.js";
|
||||||
|
|
||||||
const $outstandingSyncsCount = $("#outstanding-syncs-count");
|
const $outstandingSyncsCount = $("#outstanding-syncs-count");
|
||||||
|
|
||||||
@@ -24,7 +25,8 @@ function logError(message) {
|
|||||||
if (ws && ws.readyState === 1) {
|
if (ws && ws.readyState === 1) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: 'log-error',
|
type: 'log-error',
|
||||||
error: message
|
error: message,
|
||||||
|
stack: new Error().stack
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,21 @@ function subscribeToMessages(messageHandler) {
|
|||||||
// used to serialize sync operations
|
// used to serialize sync operations
|
||||||
let consumeQueuePromise = null;
|
let consumeQueuePromise = null;
|
||||||
|
|
||||||
|
// most sync events are sent twice - once immediatelly after finishing the transaction and once during the scheduled ping
|
||||||
|
// but we want to process only once
|
||||||
|
const processedSyncIds = new Set();
|
||||||
|
|
||||||
|
function logRows(syncRows) {
|
||||||
|
const filteredRows = syncRows.filter(row =>
|
||||||
|
!processedSyncIds.has(row.id)
|
||||||
|
&& row.entityName !== 'recent_notes'
|
||||||
|
&& (row.entityName !== 'options' || row.entityId !== 'openTabs'));
|
||||||
|
|
||||||
|
if (filteredRows.length > 0) {
|
||||||
|
console.debug(utils.now(), "Sync data: ", filteredRows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleMessage(event) {
|
async function handleMessage(event) {
|
||||||
const message = JSON.parse(event.data);
|
const message = JSON.parse(event.data);
|
||||||
|
|
||||||
@@ -50,13 +67,7 @@ async function handleMessage(event) {
|
|||||||
$outstandingSyncsCount.html(message.outstandingSyncs);
|
$outstandingSyncsCount.html(message.outstandingSyncs);
|
||||||
|
|
||||||
if (syncRows.length > 0) {
|
if (syncRows.length > 0) {
|
||||||
const filteredRows = syncRows.filter(row =>
|
logRows(syncRows);
|
||||||
row.entityName !== 'recent_notes'
|
|
||||||
&& (row.entityName !== 'options' || row.entityId !== 'openTabs'));
|
|
||||||
|
|
||||||
if (filteredRows.length > 0) {
|
|
||||||
console.debug(utils.now(), "Sync data: ", filteredRows);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncDataQueue.push(...syncRows);
|
syncDataQueue.push(...syncRows);
|
||||||
|
|
||||||
@@ -132,13 +143,21 @@ async function runSafely(syncHandler, syncData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: we should rethink the fact that each sync row is sent twice (once at the end of transaction, once periodically)
|
||||||
|
* and we keep both lastProcessedSyncId and processedSyncIds
|
||||||
|
* it even seems incorrect that when transaction sync rows are received, we incorrectly increase lastProcessedSyncId
|
||||||
|
* and then some syncs might lost (or are *all* sync rows sent from transactions?)
|
||||||
|
*/
|
||||||
async function consumeSyncData() {
|
async function consumeSyncData() {
|
||||||
if (syncDataQueue.length > 0) {
|
if (syncDataQueue.length > 0) {
|
||||||
const allSyncData = syncDataQueue;
|
const allSyncRows = syncDataQueue;
|
||||||
syncDataQueue = [];
|
syncDataQueue = [];
|
||||||
|
|
||||||
|
const nonProcessedSyncRows = allSyncRows.filter(sync => !processedSyncIds.has(sync.id));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await processSyncRows(allSyncData);
|
await utils.timeLimit(processSyncRows(nonProcessedSyncRows), 5000);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
|
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
|
||||||
@@ -147,7 +166,11 @@ async function consumeSyncData() {
|
|||||||
utils.reloadApp();
|
utils.reloadApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncData[allSyncData.length - 1].id);
|
for (const syncRow of nonProcessedSyncRows) {
|
||||||
|
processedSyncIds.add(syncRow.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncRows[allSyncRows.length - 1].id);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSyncIdListeners();
|
checkSyncIdListeners();
|
||||||
@@ -169,7 +192,7 @@ function connectWebSocket() {
|
|||||||
|
|
||||||
async function sendPing() {
|
async function sendPing() {
|
||||||
if (Date.now() - lastPingTs > 30000) {
|
if (Date.now() - lastPingTs > 30000) {
|
||||||
console.log(utils.now(), "Lost websocket connection to the backend");
|
console.log(utils.now(), "Lost websocket connection to the backend. If you keep having this issue repeatedly, you might want to check your reverse proxy (nginx, apache) configuration and allow/unblock WebSocket.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws.readyState === ws.OPEN) {
|
if (ws.readyState === ws.OPEN) {
|
||||||
@@ -211,18 +234,18 @@ subscribeToMessages(message => {
|
|||||||
async function processSyncRows(syncRows) {
|
async function processSyncRows(syncRows) {
|
||||||
const missingNoteIds = [];
|
const missingNoteIds = [];
|
||||||
|
|
||||||
syncRows.forEach(({entityName, entity}) => {
|
for (const {entityName, entity} of syncRows) {
|
||||||
if (entityName === 'branches' && !(entity.parentNoteId in treeCache.notes)) {
|
if (entityName === 'branches' && !(entity.parentNoteId in treeCache.notes)) {
|
||||||
missingNoteIds.push(entity.parentNoteId);
|
missingNoteIds.push(entity.parentNoteId);
|
||||||
}
|
}
|
||||||
else if (entityName === 'attributes'
|
else if (entityName === 'attributes'
|
||||||
&& entity.type === 'relation'
|
&& entity.type === 'relation'
|
||||||
&& entity.name === 'template'
|
&& entity.name === 'template'
|
||||||
&& !(entity.noteId in treeCache.notes)) {
|
&& !(entity.noteId in treeCache.notes)) {
|
||||||
|
|
||||||
missingNoteIds.push(entity.value);
|
missingNoteIds.push(entity.value);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
if (missingNoteIds.length > 0) {
|
if (missingNoteIds.length > 0) {
|
||||||
await treeCache.reloadNotes(missingNoteIds);
|
await treeCache.reloadNotes(missingNoteIds);
|
||||||
@@ -230,16 +253,16 @@ async function processSyncRows(syncRows) {
|
|||||||
|
|
||||||
const loadResults = new LoadResults(treeCache);
|
const loadResults = new LoadResults(treeCache);
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'notes').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'notes')) {
|
||||||
const note = treeCache.notes[sync.entityId];
|
const note = treeCache.notes[sync.entityId];
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
note.update(sync.entity);
|
note.update(sync.entity);
|
||||||
loadResults.addNote(sync.entityId, sync.sourceId);
|
loadResults.addNote(sync.entityId, sync.sourceId);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'branches').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'branches')) {
|
||||||
let branch = treeCache.branches[sync.entityId];
|
let branch = treeCache.branches[sync.entityId];
|
||||||
const childNote = treeCache.notes[sync.entity.noteId];
|
const childNote = treeCache.notes[sync.entity.noteId];
|
||||||
const parentNote = treeCache.notes[sync.entity.parentNoteId];
|
const parentNote = treeCache.notes[sync.entity.parentNoteId];
|
||||||
@@ -285,9 +308,9 @@ async function processSyncRows(syncRows) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'note_reordering').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'note_reordering')) {
|
||||||
for (const branchId in sync.positions) {
|
for (const branchId in sync.positions) {
|
||||||
const branch = treeCache.branches[branchId];
|
const branch = treeCache.branches[branchId];
|
||||||
|
|
||||||
@@ -297,10 +320,10 @@ async function processSyncRows(syncRows) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadResults.addNoteReordering(sync.entityId, sync.sourceId);
|
loadResults.addNoteReordering(sync.entityId, sync.sourceId);
|
||||||
});
|
}
|
||||||
|
|
||||||
// missing reloading the relation target note
|
// missing reloading the relation target note
|
||||||
syncRows.filter(sync => sync.entityName === 'attributes').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'attributes')) {
|
||||||
let attribute = treeCache.attributes[sync.entityId];
|
let attribute = treeCache.attributes[sync.entityId];
|
||||||
const sourceNote = treeCache.notes[sync.entity.noteId];
|
const sourceNote = treeCache.notes[sync.entity.noteId];
|
||||||
const targetNote = sync.entity.type === 'relation' && treeCache.notes[sync.entity.value];
|
const targetNote = sync.entity.type === 'relation' && treeCache.notes[sync.entity.value];
|
||||||
@@ -336,29 +359,33 @@ async function processSyncRows(syncRows) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'note_contents').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'note_contents')) {
|
||||||
delete treeCache.noteComplementPromises[sync.entityId];
|
delete treeCache.noteComplementPromises[sync.entityId];
|
||||||
|
|
||||||
loadResults.addNoteContent(sync.entityId, sync.sourceId);
|
loadResults.addNoteContent(sync.entityId, sync.sourceId);
|
||||||
});
|
}
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'note_revisions').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'note_revisions')) {
|
||||||
loadResults.addNoteRevision(sync.entityId, sync.noteId, sync.sourceId);
|
loadResults.addNoteRevision(sync.entityId, sync.noteId, sync.sourceId);
|
||||||
});
|
}
|
||||||
|
|
||||||
syncRows.filter(sync => sync.entityName === 'options').forEach(sync => {
|
for (const sync of syncRows.filter(sync => sync.entityName === 'options')) {
|
||||||
if (sync.entity.name === 'openTabs') {
|
if (sync.entity.name === 'openTabs') {
|
||||||
return; // only noise
|
continue; // only noise
|
||||||
}
|
}
|
||||||
|
|
||||||
options.set(sync.entity.name, sync.entity.value);
|
options.set(sync.entity.name, sync.entity.value);
|
||||||
|
|
||||||
loadResults.addOption(sync.entity.name);
|
loadResults.addOption(sync.entity.name);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!loadResults.isEmpty()) {
|
if (!loadResults.isEmpty()) {
|
||||||
|
if (loadResults.hasAttributeRelatedChanges()) {
|
||||||
|
noteAttributeCache.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
const appContext = (await import("./app_context.js")).default;
|
const appContext = (await import("./app_context.js")).default;
|
||||||
await appContext.triggerEvent('entitiesReloaded', {loadResults});
|
await appContext.triggerEvent('entitiesReloaded', {loadResults});
|
||||||
}
|
}
|
||||||
@@ -369,4 +396,4 @@ export default {
|
|||||||
subscribeToMessages,
|
subscribeToMessages,
|
||||||
waitForSyncId,
|
waitForSyncId,
|
||||||
waitForMaxKnownSyncId
|
waitForMaxKnownSyncId
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,31 +5,35 @@ import utils from "../services/utils.js";
|
|||||||
const MIN_ZOOM = 0.5;
|
const MIN_ZOOM = 0.5;
|
||||||
const MAX_ZOOM = 2.0;
|
const MAX_ZOOM = 2.0;
|
||||||
|
|
||||||
export default class ZoomService extends Component {
|
class ZoomService extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.setZoomFactor(options.getFloat('zoomFactor'));
|
if (utils.isElectron()) {
|
||||||
|
options.initializedPromise.then(() => {
|
||||||
|
this.setZoomFactor(options.getFloat('zoomFactor'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setZoomFactor(zoomFactor) {
|
setZoomFactor(zoomFactor) {
|
||||||
zoomFactor = parseFloat(zoomFactor);
|
zoomFactor = parseFloat(zoomFactor);
|
||||||
|
|
||||||
const webFrame = utils.dynamicRequire('electron').webFrame;
|
const webFrame = utils.dynamicRequire('electron').webFrame;
|
||||||
webFrame.setZoomFactor(zoomFactor);
|
webFrame.setZoomFactor(zoomFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setZoomFactorAndSave(zoomFactor) {
|
async setZoomFactorAndSave(zoomFactor) {
|
||||||
if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
|
if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
|
||||||
this.setZoomFactor(zoomFactor);
|
this.setZoomFactor(zoomFactor);
|
||||||
|
|
||||||
await options.save('zoomFactor', zoomFactor);
|
await options.save('zoomFactor', zoomFactor);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
|
console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentZoom() {
|
getCurrentZoom() {
|
||||||
return utils.dynamicRequire('electron').webFrame.getZoomFactor();
|
return utils.dynamicRequire('electron').webFrame.getZoomFactor();
|
||||||
}
|
}
|
||||||
@@ -45,4 +49,8 @@ export default class ZoomService extends Component {
|
|||||||
setZoomFactorAndSaveEvent({zoomFactor}) {
|
setZoomFactorAndSaveEvent({zoomFactor}) {
|
||||||
this.setZoomFactorAndSave(zoomFactor);
|
this.setZoomFactorAndSave(zoomFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const zoomService = new ZoomService();
|
||||||
|
|
||||||
|
export default zoomService;
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ class BasicWidget extends Component {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filling() {
|
||||||
|
this.css('flex-grow', '1');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
hideInZenMode() {
|
hideInZenMode() {
|
||||||
this.class('hide-in-zen-mode');
|
this.class('hide-in-zen-mode');
|
||||||
return this;
|
return this;
|
||||||
@@ -57,7 +62,10 @@ class BasicWidget extends Component {
|
|||||||
for (const key in this.attrs) {
|
for (const key in this.attrs) {
|
||||||
if (key === 'style') {
|
if (key === 'style') {
|
||||||
if (this.attrs[key]) {
|
if (this.attrs[key]) {
|
||||||
$widget.attr(key, $widget.attr('style') + ';' + this.attrs[key]);
|
let style = $widget.attr('style');
|
||||||
|
style = style ? `${style}; ${this.attrs[key]}` : this.attrs[key];
|
||||||
|
|
||||||
|
$widget.attr(key, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -106,4 +114,4 @@ class BasicWidget extends Component {
|
|||||||
cleanup() {}
|
cleanup() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BasicWidget;
|
export default BasicWidget;
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ export default class CollapsibleWidget extends TabAwareWidget {
|
|||||||
this.$bodyWrapper = this.$widget.find('.body-wrapper');
|
this.$bodyWrapper = this.$widget.find('.body-wrapper');
|
||||||
this.$bodyWrapper.attr('id', this.componentId); // for toggle to work we need id
|
this.$bodyWrapper.attr('id', this.componentId); // for toggle to work we need id
|
||||||
|
|
||||||
this.widgetName = this.constructor.name;
|
// not using constructor name because of webpack mangling class names ...
|
||||||
|
this.widgetName = this.widgetTitle.replace(/[^[a-zA-Z0-9]/g, "_");
|
||||||
|
|
||||||
if (!options.is(this.widgetName + 'Collapsed')) {
|
if (!options.is(this.widgetName + 'Collapsed')) {
|
||||||
this.$bodyWrapper.collapse("show");
|
this.$bodyWrapper.collapse("show");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import ws from "../services/ws.js";
|
import ws from "../../services/ws.js";
|
||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
export default class AttributesWidget extends CollapsibleWidget {
|
export default class AttributesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Attributes"; }
|
get widgetTitle() { return "Attributes"; }
|
||||||
@@ -16,7 +16,7 @@ export default class AttributesWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const attributesDialog = await import("../dialogs/attributes.js");
|
const attributesDialog = await import("../../dialogs/attributes.js");
|
||||||
attributesDialog.showDialog();
|
attributesDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import libraryLoader from "../services/library_loader.js";
|
import libraryLoader from "../../services/library_loader.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import dateNoteService from "../services/date_notes.js";
|
import dateNoteService from "../../services/date_notes.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../../services/app_context.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="calendar-widget">
|
<div class="calendar-widget">
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import treeCache from "../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
|
|
||||||
export default class EditedNotesWidget extends CollapsibleWidget {
|
export default class EditedNotesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Edited notes on this day"; }
|
get widgetTitle() { return "Edited notes on this day"; }
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
import treeCache from "../../services/tree_cache.js";
|
||||||
|
|
||||||
let linkMapContainerIdCtr = 1;
|
let linkMapContainerIdCtr = 1;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||||
linkMapDialog.showDialog();
|
linkMapDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
|||||||
const $linkMapContainer = this.$body.find('.link-map-container');
|
const $linkMapContainer = this.$body.find('.link-map-container');
|
||||||
$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
|
$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
|
||||||
|
|
||||||
const LinkMapServiceClass = (await import('../services/link_map.js')).default;
|
const LinkMapServiceClass = (await import('../../services/link_map.js')).default;
|
||||||
|
|
||||||
this.linkMapService = new LinkMapServiceClass(note, $linkMapContainer, {
|
this.linkMapService = new LinkMapServiceClass(note, $linkMapContainer, {
|
||||||
maxDepth: 1,
|
maxDepth: 1,
|
||||||
@@ -89,5 +90,19 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
|||||||
if (loadResults.getAttributes().find(attr => attr.type === 'relation' && (attr.noteId === this.noteId || attr.value === this.noteId))) {
|
if (loadResults.getAttributes().find(attr => attr.type === 'relation' && (attr.noteId === this.noteId || attr.value === this.noteId))) {
|
||||||
this.noteSwitched();
|
this.noteSwitched();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changedNoteIds = loadResults.getNoteIds();
|
||||||
|
|
||||||
|
if (changedNoteIds.length > 0) {
|
||||||
|
const $linkMapContainer = this.$body.find('.link-map-container');
|
||||||
|
|
||||||
|
for (const noteId of changedNoteIds) {
|
||||||
|
const note = treeCache.notes[noteId];
|
||||||
|
|
||||||
|
if (note) {
|
||||||
|
$linkMapContainer.find(`a[data-note-path="${noteId}"]`).text(note.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<table class="note-info-widget-table">
|
<table class="note-info-widget-table">
|
||||||
<style>
|
<style>
|
||||||
.note-info-widget-table {
|
.note-info-widget-table {
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,23 +20,24 @@ const TPL = `
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th nowrap>Note ID:</th>
|
<th>Note ID:</th>
|
||||||
<td nowrap colspan="3" class="note-info-note-id"></td>
|
<td class="note-info-note-id"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th nowrap>Created:</th>
|
<th>Created:</th>
|
||||||
<td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-created"></td>
|
<td class="note-info-date-created"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th nowrap>Modified:</th>
|
<th>Modified:</th>
|
||||||
<td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-modified"></td>
|
<td class="note-info-date-modified"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Type:</th>
|
<th>Type:</th>
|
||||||
<td class="note-info-type"></td>
|
<td>
|
||||||
|
<span class="note-info-type"></span>
|
||||||
<th>MIME:</th>
|
|
||||||
<td class="note-info-mime"></td>
|
<span class="note-info-mime"></span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
`;
|
`;
|
||||||
@@ -60,18 +60,21 @@ export default class NoteInfoWidget extends CollapsibleWidget {
|
|||||||
|
|
||||||
this.$noteId.text(note.noteId);
|
this.$noteId.text(note.noteId);
|
||||||
this.$dateCreated
|
this.$dateCreated
|
||||||
.text(noteComplement.dateCreated)
|
.text(noteComplement.dateCreated.substr(0, 16))
|
||||||
.attr("title", noteComplement.dateCreated);
|
.attr("title", noteComplement.dateCreated);
|
||||||
|
|
||||||
this.$dateModified
|
this.$dateModified
|
||||||
.text(noteComplement.dateModified)
|
.text(noteComplement.dateModified.substr(0, 16))
|
||||||
.attr("title", noteComplement.dateCreated);
|
.attr("title", noteComplement.dateCreated);
|
||||||
|
|
||||||
this.$type.text(note.type);
|
this.$type.text(note.type);
|
||||||
|
|
||||||
this.$mime
|
if (note.mime) {
|
||||||
.text(note.mime)
|
this.$mime.text('(' + note.mime + ')');
|
||||||
.attr("title", note.mime);
|
}
|
||||||
|
else {
|
||||||
|
this.$mime.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({loadResults}) {
|
entitiesReloadedEvent({loadResults}) {
|
||||||
@@ -79,4 +82,4 @@ export default class NoteInfoWidget extends CollapsibleWidget {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
|
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
|
||||||
@@ -19,7 +19,7 @@ class NoteRevisionsWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const attributesDialog = await import("../dialogs/note_revisions.js");
|
const attributesDialog = await import("../../dialogs/note_revisions.js");
|
||||||
attributesDialog.showCurrentNoteRevisions(this.noteId);
|
attributesDialog.showCurrentNoteRevisions(this.noteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ class NoteRevisionsWidget extends CollapsibleWidget {
|
|||||||
'data-action': 'note-revision',
|
'data-action': 'note-revision',
|
||||||
'data-note-path': note.noteId,
|
'data-note-path': note.noteId,
|
||||||
'data-note-revision-id': item.noteRevisionId,
|
'data-note-revision-id': item.noteRevisionId,
|
||||||
|
title: 'This revision was last edited on ' + item.dateLastEdited,
|
||||||
href: 'javascript:'
|
href: 'javascript:'
|
||||||
}).text(item.dateLastEdited.substr(0, 16)));
|
}).text(item.dateLastEdited.substr(0, 16)));
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import treeCache from "../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
|
|
||||||
export default class SimilarNotesWidget extends CollapsibleWidget {
|
export default class SimilarNotesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Similar notes"; }
|
get widgetTitle() { return "Similar notes"; }
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
|
|
||||||
export default class WhatLinksHereWidget extends CollapsibleWidget {
|
export default class WhatLinksHereWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "What links here"; }
|
get widgetTitle() { return "What links here"; }
|
||||||
@@ -13,7 +13,7 @@ export default class WhatLinksHereWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show link map").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show link map").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||||
linkMapDialog.showDialog();
|
linkMapDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
import utils from '../services/utils.js';
|
import utils from '../services/utils.js';
|
||||||
import Mutex from "../services/mutex.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for all components in the Trilium's frontend.
|
* Abstract class for all components in the Trilium's frontend.
|
||||||
*
|
*
|
||||||
* Contains also event implementation with following properties:
|
* Contains also event implementation with following properties:
|
||||||
* - event / command distribution is synchronous which among others mean that events are well ordered - event
|
* - event / command distribution is synchronous which among others mean that events are well ordered - event
|
||||||
* which was sent out first will also be processed first by the component since it was added to the mutex queue
|
* which was sent out first will also be processed first by the component
|
||||||
* as the first one
|
|
||||||
* - execution of the event / command is asynchronous - each component executes the event on its own without regard for
|
* - execution of the event / command is asynchronous - each component executes the event on its own without regard for
|
||||||
* other components.
|
* other components.
|
||||||
* - although the execution is async, we are collecting all the promises and therefore it is possible to wait until the
|
* - although the execution is async, we are collecting all the promises and therefore it is possible to wait until the
|
||||||
@@ -15,11 +13,10 @@ import Mutex from "../services/mutex.js";
|
|||||||
*/
|
*/
|
||||||
export default class Component {
|
export default class Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.componentId = `comp-${this.constructor.name}-` + utils.randomString(6);
|
this.componentId = `comp-` + utils.randomString(8);
|
||||||
/** @type Component[] */
|
/** @type Component[] */
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.initialized = Promise.resolve();
|
this.initialized = Promise.resolve();
|
||||||
this.mutex = new Mutex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setParent(parent) {
|
setParent(parent) {
|
||||||
@@ -79,18 +76,8 @@ export default class Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let release;
|
await fun.call(this, data);
|
||||||
|
|
||||||
try {
|
return true;
|
||||||
release = await this.mutex.acquire();
|
|
||||||
|
|
||||||
await fun.call(this, data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
if (release) {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,14 @@ const WIDGET_TPL = `
|
|||||||
|
|
||||||
<a data-trigger-command="collapseTree"
|
<a data-trigger-command="collapseTree"
|
||||||
title="Collapse note tree"
|
title="Collapse note tree"
|
||||||
data-command="collapseTree"
|
|
||||||
class="icon-action bx bx-layer-minus"></a>
|
class="icon-action bx bx-layer-minus"></a>
|
||||||
|
|
||||||
<a data-trigger-command="scrollToActiveNote"
|
<a data-trigger-command="scrollToActiveNote"
|
||||||
title="Scroll to active note"
|
title="Scroll to active note"
|
||||||
data-command="scrollToActiveNote"
|
|
||||||
class="icon-action bx bx-crosshair"></a>
|
class="icon-action bx bx-crosshair"></a>
|
||||||
|
|
||||||
<a data-trigger-command="searchNotes"
|
<a data-trigger-command="searchNotes"
|
||||||
title="Search in notes"
|
title="Search in notes"
|
||||||
data-command="searchNotes"
|
|
||||||
class="icon-action bx bx-search"></a>
|
class="icon-action bx bx-search"></a>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "./basic_widget.js";
|
||||||
import keyboardActionService from "../services/keyboard_actions.js";
|
|
||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import syncService from "../services/sync.js";
|
import syncService from "../services/sync.js";
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ const TPL = `
|
|||||||
.global-menu button {
|
.global-menu button {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
height: 33px;
|
height: 33px;
|
||||||
border-bottom: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.global-menu .dropdown-menu {
|
.global-menu .dropdown-menu {
|
||||||
@@ -36,7 +35,13 @@ const TPL = `
|
|||||||
|
|
||||||
<a class="dropdown-item sync-now-button" title="Trigger sync">
|
<a class="dropdown-item sync-now-button" title="Trigger sync">
|
||||||
<span class="bx bx-refresh"></span>
|
<span class="bx bx-refresh"></span>
|
||||||
Sync (<span id="outstanding-syncs-count">0</span>)
|
Sync now (<span id="outstanding-syncs-count">0</span>)
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="dropdown-item" data-trigger-command="openNewWindow">
|
||||||
|
<span class="bx bx-window-open"></span>
|
||||||
|
Open new window
|
||||||
|
<kbd data-command="openNewWindow"></kbd>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools">
|
<a class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools">
|
||||||
@@ -111,4 +116,4 @@ export default class GlobalMenuWidget extends BasicWidget {
|
|||||||
|
|
||||||
return this.$widget;
|
return this.$widget;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../../services/app_context.js";
|
||||||
import contextMenu from "../services/context_menu.js";
|
import contextMenu from "../../services/context_menu.js";
|
||||||
import noteCreateService from "../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import branchService from "../services/branches.js";
|
import branchService from "../../services/branches.js";
|
||||||
|
|
||||||
const TPL = `<button type="button" class="action-button bx bx-menu"></button>`;
|
const TPL = `<button type="button" class="action-button bx bx-menu"></button>`;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
const WIDGET_TPL = `
|
const WIDGET_TPL = `
|
||||||
<div id="global-buttons">
|
<div id="global-buttons">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Component from "./component.js";
|
import Component from "../component.js";
|
||||||
|
|
||||||
export default class MobileScreenSwitcherExecutor extends Component {
|
export default class MobileScreenSwitcherExecutor extends Component {
|
||||||
setActiveScreenCommand({screen}) {
|
setActiveScreenCommand({screen}) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../flex_container.js";
|
||||||
|
|
||||||
export default class ScreenContainer extends FlexContainer {
|
export default class ScreenContainer extends FlexContainer {
|
||||||
constructor(screenName, direction) {
|
constructor(screenName, direction) {
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import TabAwareWidget from "./tab_aware_widget.js";
|
import TabAwareWidget from "./tab_aware_widget.js";
|
||||||
|
import protectedSessionService from "../services/protected_session.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="dropdown note-actions">
|
<div class="dropdown note-actions">
|
||||||
@@ -12,6 +13,49 @@ const TPL = `
|
|||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
pointer-events: none; /* makes it unclickable */
|
pointer-events: none; /* makes it unclickable */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The switch - the box around the slider */
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 50px;
|
||||||
|
height: 24px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The slider */
|
||||||
|
.slider {
|
||||||
|
border-radius: 24px;
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--more-accented-background-color);
|
||||||
|
transition: .4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
-webkit-transition: .4s;
|
||||||
|
transition: .4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.checked {
|
||||||
|
background-color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider.checked:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle">
|
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle">
|
||||||
@@ -19,6 +63,23 @@ const TPL = `
|
|||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<div class="dropdown-menu dropdown-menu-right">
|
||||||
|
<div class="dropdown-item protect-button">
|
||||||
|
Protect the note
|
||||||
|
|
||||||
|
<span title="Note is not protected, click to make it protected">
|
||||||
|
<label class="switch">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-item unprotect-button">
|
||||||
|
Unprotect the note
|
||||||
|
|
||||||
|
<span title="Note is protected, click to make it unprotected">
|
||||||
|
<label class="switch">
|
||||||
|
<span class="slider checked"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a data-trigger-command="findInText" class="dropdown-item">Search in note <kbd data-command="findInText"></a>
|
||||||
<a data-trigger-command="showNoteRevisions" class="dropdown-item show-note-revisions-button">Revisions</a>
|
<a data-trigger-command="showNoteRevisions" class="dropdown-item show-note-revisions-button">Revisions</a>
|
||||||
<a data-trigger-command="showAttributes" class="dropdown-item show-attributes-button"><kbd data-command="showAttributes"></kbd> Attributes</a>
|
<a data-trigger-command="showAttributes" class="dropdown-item show-attributes-button"><kbd data-command="showAttributes"></kbd> Attributes</a>
|
||||||
<a data-trigger-command="showLinkMap" class="dropdown-item show-link-map-button"><kbd data-command="showLinkMap"></kbd> Link map</a>
|
<a data-trigger-command="showLinkMap" class="dropdown-item show-link-map-button"><kbd data-command="showLinkMap"></kbd> Link map</a>
|
||||||
@@ -48,6 +109,12 @@ export default class NoteActionsWidget extends TabAwareWidget {
|
|||||||
this.$importNoteButton = this.$widget.find('.import-files-button');
|
this.$importNoteButton = this.$widget.find('.import-files-button');
|
||||||
this.$importNoteButton.on("click", () => import('../dialogs/import.js').then(d => d.showDialog(this.noteId)));
|
this.$importNoteButton.on("click", () => import('../dialogs/import.js').then(d => d.showDialog(this.noteId)));
|
||||||
|
|
||||||
|
this.$protectButton = this.$widget.find(".protect-button");
|
||||||
|
this.$protectButton.on('click', () => protectedSessionService.protectNote(this.noteId, true, false));
|
||||||
|
|
||||||
|
this.$unprotectButton = this.$widget.find(".unprotect-button");
|
||||||
|
this.$unprotectButton.on('click', () => protectedSessionService.protectNote(this.noteId, false, false));
|
||||||
|
|
||||||
return this.$widget;
|
return this.$widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,5 +131,14 @@ export default class NoteActionsWidget extends TabAwareWidget {
|
|||||||
else {
|
else {
|
||||||
this.$exportNoteButton.attr('disabled', 'disabled');
|
this.$exportNoteButton.attr('disabled', 'disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$protectButton.toggle(!note.isProtected);
|
||||||
|
this.$unprotectButton.toggle(!!note.isProtected);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
entitiesReloadedEvent({loadResults}) {
|
||||||
|
if (loadResults.isNoteReloaded(this.noteId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import server from "../services/server.js";
|
|||||||
import libraryLoader from "../services/library_loader.js";
|
import libraryLoader from "../services/library_loader.js";
|
||||||
import EmptyTypeWidget from "./type_widgets/empty.js";
|
import EmptyTypeWidget from "./type_widgets/empty.js";
|
||||||
import EditableTextTypeWidget from "./type_widgets/editable_text.js";
|
import EditableTextTypeWidget from "./type_widgets/editable_text.js";
|
||||||
import CodeTypeWidget from "./type_widgets/code.js";
|
import EditableCodeTypeWidget from "./type_widgets/editable_code.js";
|
||||||
import FileTypeWidget from "./type_widgets/file.js";
|
import FileTypeWidget from "./type_widgets/file.js";
|
||||||
import ImageTypeWidget from "./type_widgets/image.js";
|
import ImageTypeWidget from "./type_widgets/image.js";
|
||||||
import SearchTypeWidget from "./type_widgets/search.js";
|
import SearchTypeWidget from "./type_widgets/search.js";
|
||||||
@@ -19,6 +19,7 @@ import keyboardActionsService from "../services/keyboard_actions.js";
|
|||||||
import noteCreateService from "../services/note_create.js";
|
import noteCreateService from "../services/note_create.js";
|
||||||
import DeletedTypeWidget from "./type_widgets/deleted.js";
|
import DeletedTypeWidget from "./type_widgets/deleted.js";
|
||||||
import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js";
|
import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js";
|
||||||
|
import ReadOnlyCodeTypeWidget from "./type_widgets/read_only_code.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-detail">
|
<div class="note-detail">
|
||||||
@@ -38,7 +39,8 @@ const typeWidgetClasses = {
|
|||||||
'deleted': DeletedTypeWidget,
|
'deleted': DeletedTypeWidget,
|
||||||
'editable-text': EditableTextTypeWidget,
|
'editable-text': EditableTextTypeWidget,
|
||||||
'read-only-text': ReadOnlyTextTypeWidget,
|
'read-only-text': ReadOnlyTextTypeWidget,
|
||||||
'code': CodeTypeWidget,
|
'editable-code': EditableCodeTypeWidget,
|
||||||
|
'read-only-code': ReadOnlyCodeTypeWidget,
|
||||||
'file': FileTypeWidget,
|
'file': FileTypeWidget,
|
||||||
'image': ImageTypeWidget,
|
'image': ImageTypeWidget,
|
||||||
'search': SearchTypeWidget,
|
'search': SearchTypeWidget,
|
||||||
@@ -61,6 +63,8 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
const dto = note.dto;
|
const dto = note.dto;
|
||||||
dto.content = this.getTypeWidget().getContent();
|
dto.content = this.getTypeWidget().getContent();
|
||||||
|
|
||||||
|
protectedSessionHolder.touchProtectedSessionIfNecessary(note);
|
||||||
|
|
||||||
await server.put('notes/' + noteId, dto, this.componentId);
|
await server.put('notes/' + noteId, dto, this.componentId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -182,15 +186,32 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
const noteComplement = await this.tabContext.getNoteComplement();
|
const noteComplement = await this.tabContext.getNoteComplement();
|
||||||
|
|
||||||
if (note.hasLabel('readOnly') ||
|
if (note.hasLabel('readOnly') ||
|
||||||
(noteComplement.content && noteComplement.content.length > 10000)) {
|
(noteComplement.content
|
||||||
|
&& noteComplement.content.length > 10000)
|
||||||
|
&& !note.hasLabel('autoReadOnlyDisabled')) {
|
||||||
type = 'read-only-text';
|
type = 'read-only-text';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'code' && !this.tabContext.codePreviewDisabled) {
|
||||||
|
const noteComplement = await this.tabContext.getNoteComplement();
|
||||||
|
|
||||||
|
if (note.hasLabel('readOnly') ||
|
||||||
|
(noteComplement.content
|
||||||
|
&& noteComplement.content.length > 30000)
|
||||||
|
&& !note.hasLabel('autoReadOnlyDisabled')) {
|
||||||
|
type = 'read-only-code';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'text') {
|
if (type === 'text') {
|
||||||
type = 'editable-text';
|
type = 'editable-text';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'code') {
|
||||||
|
type = 'editable-code';
|
||||||
|
}
|
||||||
|
|
||||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
type = 'protected-session';
|
type = 'protected-session';
|
||||||
}
|
}
|
||||||
@@ -249,13 +270,30 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async entitiesReloadedEvent({loadResults}) {
|
async entitiesReloadedEvent({loadResults}) {
|
||||||
// FIXME: we should test what happens when the loaded note is deleted
|
|
||||||
|
|
||||||
if (loadResults.isNoteContentReloaded(this.noteId, this.componentId)
|
if (loadResults.isNoteContentReloaded(this.noteId, this.componentId)
|
||||||
|| (loadResults.isNoteReloaded(this.noteId, this.componentId) && (this.type !== await this.getWidgetType() || this.mime !== this.note.mime))) {
|
|| (loadResults.isNoteReloaded(this.noteId, this.componentId) && (this.type !== await this.getWidgetType() || this.mime !== this.note.mime))) {
|
||||||
|
|
||||||
this.handleEvent('noteTypeMimeChanged', {noteId: this.noteId});
|
this.handleEvent('noteTypeMimeChanged', {noteId: this.noteId});
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const attrs = loadResults.getAttributes();
|
||||||
|
|
||||||
|
const label = attrs.find(attr =>
|
||||||
|
attr.type === 'label'
|
||||||
|
&& ['readOnly', 'autoReadOnlyDisabled', 'cssClass', 'bookZoomLevel'].includes(attr.name)
|
||||||
|
&& attr.isAffecting(this.note));
|
||||||
|
|
||||||
|
const relation = attrs.find(attr =>
|
||||||
|
attr.type === 'relation'
|
||||||
|
&& ['template', 'renderNote'].includes(attr.name)
|
||||||
|
&& attr.isAffecting(this.note));
|
||||||
|
|
||||||
|
if (label || relation) {
|
||||||
|
// probably incorrect event
|
||||||
|
// calling this.refresh() is not enough since the event needs to be propagated to children as well
|
||||||
|
this.handleEvent('noteTypeMimeChanged', {noteId: this.noteId});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeUnloadEvent() {
|
beforeUnloadEvent() {
|
||||||
@@ -274,6 +312,12 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codePreviewDisabledEvent({tabContext}) {
|
||||||
|
if (this.isTab(tabContext.tabId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async cutIntoNoteCommand() {
|
async cutIntoNoteCommand() {
|
||||||
const note = appContext.tabManager.getActiveTabNote();
|
const note = appContext.tabManager.getActiveTabNote();
|
||||||
|
|
||||||
@@ -281,9 +325,15 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await noteCreateService.createNote(note.noteId, {
|
// without await as this otherwise causes deadlock through component mutex
|
||||||
|
noteCreateService.createNote(note.noteId, {
|
||||||
isProtected: note.isProtected,
|
isProtected: note.isProtected,
|
||||||
saveSelection: true
|
saveSelection: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// used by cutToNote in CKEditor build
|
||||||
|
async saveNoteDetailNowCommand() {
|
||||||
|
await this.spacedUpdate.updateNowIfNecessary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ const TPL = `
|
|||||||
min-width: 5em;
|
min-width: 5em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-title-container input.note-title.protected {
|
||||||
|
text-shadow: 4px 4px 4px var(--muted-text-color);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<input autocomplete="off" value="" class="note-title" tabindex="1">
|
<input autocomplete="off" value="" class="note-title" tabindex="1">
|
||||||
@@ -29,6 +33,8 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
|||||||
this.spacedUpdate = new SpacedUpdate(async () => {
|
this.spacedUpdate = new SpacedUpdate(async () => {
|
||||||
const title = this.$noteTitle.val();
|
const title = this.$noteTitle.val();
|
||||||
|
|
||||||
|
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||||
|
|
||||||
await server.put(`notes/${this.noteId}/change-title`, {title});
|
await server.put(`notes/${this.noteId}/change-title`, {title});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -50,6 +56,12 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
|||||||
this.$noteTitle.val(note.title);
|
this.$noteTitle.val(note.title);
|
||||||
|
|
||||||
this.$noteTitle.prop("readonly", note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable());
|
this.$noteTitle.prop("readonly", note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable());
|
||||||
|
|
||||||
|
this.setProtectedStatus(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
setProtectedStatus(note) {
|
||||||
|
this.$noteTitle.toggleClass("protected", !!note.isProtected);
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeNoteSwitchEvent({tabContext}) {
|
async beforeNoteSwitchEvent({tabContext}) {
|
||||||
@@ -78,7 +90,14 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entitiesReloadedEvent({loadResults}) {
|
||||||
|
if (loadResults.isNoteReloaded(this.noteId)) {
|
||||||
|
// not updating the title specifically since the synced title might be older than what the user is currently typing
|
||||||
|
this.setProtectedStatus(this.note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beforeUnloadEvent() {
|
beforeUnloadEvent() {
|
||||||
this.spacedUpdate.updateNowIfNecessary();
|
this.spacedUpdate.updateNowIfNecessary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user