Compare commits

...

46 Commits

Author SHA1 Message Date
zadam 38723e0189 release 0.42.5 2020-05-31 23:33:30 +02:00
zadam 8c88ce6f65 fix moving/cloning notes broken in 0.42.4, closes #1066 2020-05-31 22:33:02 +02:00
zadam 4d22959e28 release 0.42.4 2020-05-31 10:33:12 +02:00
zadam 50a28d8c51 the node you start dragging should be included even if not selected 2020-05-31 10:32:35 +02:00
zadam e25b633ec4 better error logging in backup 2020-05-31 10:24:59 +02:00
zadam 75bd395669 fix extra refresh because of duplicated sync event, closes #1063 2020-05-30 22:35:18 +02:00
zadam 5e353a5612 improved drag & drop 2020-05-30 10:30:21 +02:00
zadam 6b359b7796 return 401 when auth request is out of sync, closes #1056 2020-05-29 22:06:36 +02:00
zadam 13f9d037dc safer backup to file using VACUUM INTO + possibility to explicitly ask for backup now 2020-05-29 21:55:08 +02:00
zadam 1911d64c1c fix long filename overflowing, closes #1052 2020-05-29 20:36:48 +02:00
zadam d4c3f1b3f2 upgrade to ckeditor 19.1.0 2020-05-27 22:08:06 +02:00
zadam 3db84daf94 fix hiding autocompletes after closing tab, closes #1034 2020-05-22 19:30:21 +02:00
zadam 2526715aa4 release 0.42.3 2020-05-20 08:54:55 +02:00
zadam 04c573e212 set default executors 2020-05-20 08:54:37 +02:00
zadam 58f4f5d1e6 fix deadlock after "cut to note", closes #1030 2020-05-19 22:58:08 +02:00
zadam fa5d982a55 fix exporting root note, closes #1024 2020-05-17 21:07:54 +02:00
zadam 108afe8896 fix incorrect processing of sync rows, closes #1019 2020-05-14 13:08:06 +02:00
zadam 37da0adb8a release 0.42.2 2020-05-12 16:46:45 +02:00
zadam 4f50864ec8 better UX when deleting notes - focus in note tree is moved to the next/previous note 2020-05-12 13:40:42 +02:00
zadam 30b9ef8604 fix tab title of deleted note 2020-05-12 12:45:32 +02:00
zadam b063b4c528 read only view images should not overflow 2020-05-12 12:28:59 +02:00
zadam e08b0141a4 when expanding/collpasing, set the flag also to the tree cache 2020-05-12 10:52:07 +02:00
zadam 9d8b8e26a1 attach extension to download file if note present 2020-05-12 10:28:31 +02:00
zadam cb70109ee7 fix creating new promoted attributes, closes #1008
(cherry picked from commit 2e0fb8aaf1)
2020-05-12 00:00:59 +02:00
zadam e541abbd60 disable hiding the body to not hide the noscript element 2020-05-11 22:44:10 +02:00
Naveen M V 940a70adc5 Fix regex bug (#1005)
(cherry picked from commit 9a662f76da)
2020-05-11 22:06:39 +02:00
zadam 88e8eb7e9c fixed context menu positioning when scaling is active 2020-05-11 20:08:55 +02:00
zadam b365c186a1 fixed clicking on links in read only view 2020-05-11 19:38:14 +02:00
zadam 64c9734f05 transform setup.js to the webpacked version in the build 2020-05-08 20:50:53 +02:00
zadam 48c843c087 fix setup on server edition 2020-05-08 10:24:57 +02:00
zadam 0e4eec10b9 added "restore this revision" button 2020-05-07 23:34:13 +02:00
zadam a3661cb763 fix display of buttons for revisions when there is none 2020-05-07 23:14:21 +02:00
zadam 115879ec4a fix note revisions displaying wrong tooltip 2020-05-07 23:02:46 +02:00
zadam df11b076bc release 0.42.1 2020-05-06 23:24:13 +02:00
MeIchthys 54ecd2ee75 Prevent td text from overlapping th text (#1002)
This makes all of the Note Info sections more consistent with each other. It prevents overlapping of text when the window is displayed in a small-width environment.
2020-05-06 23:12:28 +02:00
zadam 2369bcf9fc fixes for image download 2020-05-06 23:11:34 +02:00
zadam 5d8808a2ad fix renaming existing attributes + added new label autoReadOnlyDisabled to control automatic setting to readOnly mode 2020-05-06 21:41:14 +02:00
zadam 62b993f06f fix closing tab by mouse 2020-05-06 21:24:51 +02:00
zadam 8aa5608085 fix "Render note" and "Execute script" buttons + refactoring around data-trigger-command handling 2020-05-06 00:00:14 +02:00
zadam b452d7e5c5 Merge remote-tracking branch 'origin/stable' 2020-05-06 00:00:01 +02:00
zadam 9b9d6d86d0 remove debugging console.log 2020-05-05 22:51:53 +02:00
zadam 7f2755d4a0 refresh button state change on note update 2020-05-05 22:18:09 +02:00
zadam 3b268cc8eb fix selecting note title after creation, closes #997 2020-05-05 21:42:18 +02:00
zadam 6dfe335707 added global menu item to open new empty window + some refactoring 2020-05-05 19:30:03 +02:00
zadam c7125d2b50 createNoteAfter should have saveSelection: false 2020-05-05 19:09:01 +02:00
zadam e8a33a5ee7 fix note title not updating when changing the title 2020-05-05 18:56:12 +02:00
69 changed files with 783 additions and 1209 deletions
+1 -15
View File
@@ -1,25 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<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>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url>
</data-source>
<data-source source="LOCAL" name="document" uuid="066dc5f4-4097-429e-8cf1-3adc0a9d648a">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/dist/trilium linux x64/trilium-data/document.db</jdbc-url>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.16.1/xerial-sqlite-license.txt</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.16.1/sqlite-jdbc-3.16.1.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>
-692
View File
@@ -1,692 +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.18">
<root id="1">
<ServerVersion>3.16.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>&quot;&quot;</DefaultExpression>
</column>
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>apiTokenId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="25" parent="6">
<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>&apos;&apos;</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>&quot;&quot;</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>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="39" parent="7" name="IDX_attributes_noteId_index">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="40" parent="7" name="IDX_attributes_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="41" parent="7" name="IDX_attributes_value_index">
<ColNames>value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="42" parent="7">
<ColNames>attributeId</ColNames>
<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>&quot;&quot;</DefaultExpression>
</column>
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
<ColNames>noteId
parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="56" parent="8" name="IDX_branches_parentNoteId">
<ColNames>parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="57" parent="8">
<ColNames>branchId</ColNames>
<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>&quot;&quot;</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>
<ColumnCollations></ColumnCollations>
<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>&apos;&apos;</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>
<ColumnCollations></ColumnCollations>
<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>&apos;&apos;</DefaultExpression>
</column>
<column id="82" parent="11" name="mime">
<Position>13</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="83" parent="11" name="hash">
<Position>14</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="85" parent="11" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
<ColNames>utcDateLastEdited</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
<ColNames>dateLastEdited</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
<ColNames>dateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</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>&quot;note&quot;</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>&apos;text&apos;</DefaultExpression>
</column>
<column id="96" parent="12" name="mime">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text/html&apos;</DefaultExpression>
</column>
<column id="97" parent="12" name="hash">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</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>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="106" parent="12" name="IDX_notes_title">
<ColNames>title</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="107" parent="12" name="IDX_notes_type">
<ColNames>type</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="108" parent="12" name="IDX_notes_isDeleted">
<ColNames>isDeleted</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="109" parent="12" name="IDX_notes_dateCreated">
<ColNames>dateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="110" parent="12" name="IDX_notes_dateModified">
<ColNames>dateModified</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="112" parent="12" name="IDX_notes_utcDateModified">
<ColNames>utcDateModified</ColNames>
<ColumnCollations></ColumnCollations>
</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>&quot;&quot;</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>
<ColumnCollations></ColumnCollations>
<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>&quot;&quot;</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>
<ColumnCollations></ColumnCollations>
<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>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</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>integer|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>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
<ColNames>utcSyncDate</ColNames>
<ColumnCollations></ColumnCollations>
</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 -1
View File
@@ -1,4 +1,4 @@
FROM node:12.16.2-alpine
FROM node:12.16.3-alpine
# Create app directory
WORKDIR /usr/src/app
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
PKG_DIR=dist/trilium-linux-x64-server
NODE_VERSION=12.16.2
NODE_VERSION=12.16.3
if [ "$1" != "DONTCOPY" ]
then
+2 -1
View File
@@ -34,4 +34,5 @@ find $DIR/libraries -name "*.map" -type f -delete
rm -r $DIR/src/public/app
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
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+131 -123
View File
@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.41.6",
"version": "0.42.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1192,38 +1192,38 @@
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
},
"app-builder-bin": {
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.6.tgz",
"integrity": "sha512-gY9ABoV5jh67IrPEwF81R8l9LwE3RqHUyU3rIKitwqMpKhplN5OZC6WEHOXO3XhwiLCIlr9LLI6OPhr3bmtQIg==",
"version": "3.5.8",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.8.tgz",
"integrity": "sha512-ni3q7QTfQNWHNWuyn5x3FZu6GnQZv+TFnfgk5++svqleKEhHGqS1mIaKsh7x5pBX6NFXU3/+ktk98wA/AW4EXw==",
"dev": true
},
"app-builder-lib": {
"version": "22.5.1",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.5.1.tgz",
"integrity": "sha512-VtB/PD8actR1317D/0uGzuJIYbpw4pRrfMB6IyTLwGynUd3ihqiCFjejVWHjCwopgCct2kE0MvLwo8P49xHIeQ==",
"version": "22.6.0",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.6.0.tgz",
"integrity": "sha512-ky2aLYy92U+Gh6dKq/e8/bNmCotp6/GMhnX8tDZPv9detLg9WuBnWWi1ktBPlpbl1DREusy+TIh+9rgvfduQoA==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@develar/schema-utils": "~2.6.5",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "22.5.1",
"builder-util": "22.6.0",
"builder-util-runtime": "8.7.0",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.1.1",
"ejs": "^3.0.2",
"electron-publish": "22.5.1",
"ejs": "^3.1.2",
"electron-publish": "22.6.0",
"fs-extra": "^9.0.0",
"hosted-git-info": "^3.0.4",
"is-ci": "^2.0.0",
"isbinaryfile": "^4.0.5",
"isbinaryfile": "^4.0.6",
"js-yaml": "^3.13.1",
"lazy-val": "^1.0.4",
"minimatch": "^3.0.4",
"normalize-package-data": "^2.5.0",
"read-config-file": "6.0.0",
"sanitize-filename": "^1.6.3",
"semver": "^7.1.3",
"semver": "^7.3.2",
"temp-file": "^3.3.7"
},
"dependencies": {
@@ -1263,7 +1263,7 @@
"dependencies": {
"file-type": {
"version": "3.9.0",
"resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
}
}
@@ -1539,7 +1539,7 @@
},
"uuid": {
"version": "2.0.3",
"resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
}
}
@@ -1573,7 +1573,7 @@
"dependencies": {
"semver": {
"version": "4.3.6",
"resolved": "http://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
"integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto="
}
}
@@ -1593,7 +1593,7 @@
},
"bl": {
"version": "1.2.2",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "^2.3.5",
@@ -1853,34 +1853,34 @@
"dependencies": {
"file-type": {
"version": "3.9.0",
"resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
},
"uuid": {
"version": "2.0.3",
"resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
}
}
},
"builder-util": {
"version": "22.5.1",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.5.1.tgz",
"integrity": "sha512-CelDTP3+fvDfZfbwy3PXif7mudPaWankJ8vrRg/NtCGvL+hXnwycnJZr46d5EQL7AgQcpJ27o9LTdfu61cxTFw==",
"version": "22.6.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.6.0.tgz",
"integrity": "sha512-jgdES2ExJYkuXC3DEaGAjFctKNA81C4QDy8zdoc+rqdSqheTizuDNtZg02uMFklmUES4V4fggmqds+Y7wraqng==",
"dev": true,
"requires": {
"7zip-bin": "~5.0.3",
"@types/debug": "^4.1.5",
"@types/fs-extra": "^8.1.0",
"app-builder-bin": "3.5.6",
"app-builder-bin": "3.5.8",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "8.7.0",
"chalk": "^3.0.0",
"chalk": "^4.0.0",
"debug": "^4.1.1",
"fs-extra": "^9.0.0",
"is-ci": "^2.0.0",
"js-yaml": "^3.13.1",
"source-map-support": "^0.5.16",
"source-map-support": "^0.5.19",
"stat-mode": "^1.0.0",
"temp-file": "^3.3.7"
},
@@ -1896,9 +1896,9 @@
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -1920,6 +1920,16 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"stat-mode": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
@@ -1963,7 +1973,7 @@
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
@@ -2138,7 +2148,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
@@ -2455,7 +2465,7 @@
},
"commander": {
"version": "2.8.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"requires": {
"graceful-readlink": ">= 1.0.0"
@@ -3118,7 +3128,7 @@
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
@@ -3143,13 +3153,13 @@
}
},
"dmg-builder": {
"version": "22.5.1",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.5.1.tgz",
"integrity": "sha512-AwIiyGwgqhA8Ty/YnEU20aSzfrWZns6suOBTqddD+rLDI4jEASKGQadfvcXRSWgaK/VQW0GrhheXrhJpzZzt3g==",
"version": "22.6.0",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.6.0.tgz",
"integrity": "sha512-rJxuGhHIpcuDGBtWZMM8aLxkbZNgYO2MO5dUerDIBXebhX1K8DA23iz/uZ8ahcRNgWEv57b8GDqJbXKEfr5T0A==",
"dev": true,
"requires": {
"app-builder-lib": "22.5.1",
"builder-util": "22.5.1",
"app-builder-lib": "22.6.0",
"builder-util": "22.6.0",
"fs-extra": "^9.0.0",
"iconv-lite": "^0.5.1",
"js-yaml": "^3.13.1",
@@ -3335,9 +3345,9 @@
}
},
"electron": {
"version": "9.0.0-beta.18",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.18.tgz",
"integrity": "sha512-mOdPB4nPc4kO2uwpvvbNZz0RDrRDIko/C8XFmOZmz7k3JaU7r0h/tLxdeypiqfLwxW/Bu1n+uwqG1X34i3fTEw==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0.tgz",
"integrity": "sha512-JsaSQNPh+XDYkLj8APtVKTtvpb86KIG57W5OOss4TNrn8L3isC9LsCITwfnVmGIXHhvX6oY/weCtN5hAAytjVg==",
"dev": true,
"requires": {
"@electron/get": "^1.0.1",
@@ -3346,18 +3356,18 @@
}
},
"electron-builder": {
"version": "22.5.1",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.5.1.tgz",
"integrity": "sha512-7gnHN8Ml5zecDerN8/ljAwUKtE+hhGLuT/X2/zO0FJM2q2hlLx/6ZgzESFILKqnPQFEBRxQ8SL1OxjdIY0HIrw==",
"version": "22.6.0",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.6.0.tgz",
"integrity": "sha512-aLHlB6DTfjJ3MI4AUIFeWnwIozNgNlbOk2c2sTHxB10cAKp0dBVSPZ7xF5NK0uwDhElvRzJQubnHtJD6zKg42Q==",
"dev": true,
"requires": {
"@types/yargs": "^15.0.4",
"app-builder-lib": "22.5.1",
"app-builder-lib": "22.6.0",
"bluebird-lst": "^1.0.9",
"builder-util": "22.5.1",
"builder-util": "22.6.0",
"builder-util-runtime": "8.7.0",
"chalk": "^3.0.0",
"dmg-builder": "22.5.1",
"chalk": "^4.0.0",
"dmg-builder": "22.6.0",
"fs-extra": "^9.0.0",
"is-ci": "^2.0.0",
"lazy-val": "^1.0.4",
@@ -3378,9 +3388,9 @@
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -3724,16 +3734,16 @@
}
},
"electron-publish": {
"version": "22.5.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.5.1.tgz",
"integrity": "sha512-g5bwLAHZT6A++yU1+Et+fncnFAdXXgkRao9rzTFAvhQ0QJBsmLiyOd0Ta2RI/EQcVoy6jyHtxFs7CWIXE5aZOA==",
"version": "22.6.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.6.0.tgz",
"integrity": "sha512-+v05SBf9qR7Os5au+fifloNHy5QxHQkUGudBj68YaTb43Pn37UkwRxSc49Lf13s4wW32ohM45g8BOVInPJEdnA==",
"dev": true,
"requires": {
"@types/fs-extra": "^8.1.0",
"bluebird-lst": "^1.0.9",
"builder-util": "22.5.1",
"builder-util": "22.6.0",
"builder-util-runtime": "8.7.0",
"chalk": "^3.0.0",
"chalk": "^4.0.0",
"fs-extra": "^9.0.0",
"lazy-val": "^1.0.4",
"mime": "^2.4.4"
@@ -3750,9 +3760,9 @@
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
@@ -4438,9 +4448,9 @@
}
},
"file-type": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-14.2.0.tgz",
"integrity": "sha512-CAkX5G5jq8LIgFu++dpM3giMZadYdU+QVQoPLajjNboo8IzaR4cKpBCVEuz+suhd/vHqoAJeSWhEubKjRPQHJg==",
"version": "14.3.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-14.3.0.tgz",
"integrity": "sha512-s71v6jMkbfwVdj87csLeNpL5K93mv4lN+lzgzifoICtPHhnXokDwBa3jrzfg+z6FK872iYJ0vS0i74v8XmoFDA==",
"requires": {
"readable-web-to-node-stream": "^2.0.0",
"strtok3": "^6.0.0",
@@ -4947,7 +4957,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"getpass": {
@@ -5211,7 +5221,7 @@
},
"got": {
"version": "5.7.1",
"resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz",
"resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
"integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
"requires": {
"create-error-class": "^3.0.1",
@@ -5859,7 +5869,7 @@
},
"into-stream": {
"version": "3.1.0",
"resolved": "http://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
"integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
"requires": {
"from2": "^2.1.1",
@@ -6011,7 +6021,7 @@
},
"is-obj": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
},
"is-object": {
@@ -6207,9 +6217,9 @@
}
},
"jest-worker": {
"version": "25.4.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz",
"integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz",
"integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
@@ -6611,7 +6621,7 @@
},
"load-json-file": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"requires": {
"graceful-fs": "^4.1.2",
@@ -7120,7 +7130,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
@@ -7220,7 +7230,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
@@ -7228,7 +7238,7 @@
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
@@ -7422,7 +7432,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"got": {
@@ -7458,7 +7468,7 @@
},
"p-cancelable": {
"version": "0.4.1",
"resolved": "http://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz",
"integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ=="
},
"p-event": {
@@ -7582,7 +7592,7 @@
"dependencies": {
"file-type": {
"version": "3.9.0",
"resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
}
}
@@ -7607,7 +7617,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"pify": {
@@ -7664,7 +7674,7 @@
},
"get-stream": {
"version": "2.3.1",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
"requires": {
"object-assign": "^4.0.1",
@@ -7694,7 +7704,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
}
@@ -7734,7 +7744,7 @@
},
"pify": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"prepend-http": {
@@ -7839,7 +7849,7 @@
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"requires": {
"core-util-is": "~1.0.0",
@@ -8217,7 +8227,7 @@
},
"onetime": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
},
"open": {
@@ -8369,7 +8379,7 @@
},
"p-is-promise": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4="
},
"p-limit": {
@@ -8850,7 +8860,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
}
@@ -9134,7 +9144,7 @@
"dependencies": {
"file-type": {
"version": "3.9.0",
"resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
}
}
@@ -9159,7 +9169,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"pify": {
@@ -9197,7 +9207,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
}
@@ -9249,7 +9259,7 @@
},
"get-stream": {
"version": "2.3.1",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
"integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=",
"requires": {
"object-assign": "^4.0.1",
@@ -9279,7 +9289,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}
}
@@ -9467,7 +9477,7 @@
},
"query-string": {
"version": "5.1.1",
"resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
@@ -9606,7 +9616,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -9715,7 +9725,6 @@
"version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"dev": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
@@ -9742,14 +9751,12 @@
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@@ -10110,9 +10117,9 @@
}
},
"serialize-javascript": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz",
"integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==",
"dev": true
},
"serve-favicon": {
@@ -10265,9 +10272,9 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
@@ -10341,12 +10348,13 @@
"integrity": "sha512-1bBO+me3gXRfqwRR3K9aNDoSbTkQ87o6fSjj/BE2gSHHsK3qIDR+LoFZHgZ6kSPdFBoLTsy5/w/+8PBBaK+lvg=="
},
"sqlite3": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.2.0.tgz",
"integrity": "sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz",
"integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==",
"requires": {
"nan": "^2.12.1",
"node-pre-gyp": "^0.11.0"
"node-pre-gyp": "^0.11.0",
"request": "^2.87.0"
}
},
"squeak": {
@@ -10476,7 +10484,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -10501,7 +10509,7 @@
},
"strip-dirs": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz",
"integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=",
"requires": {
"chalk": "^1.0.0",
@@ -10701,9 +10709,9 @@
"dev": true
},
"terser": {
"version": "4.6.11",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz",
"integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==",
"version": "4.6.13",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz",
"integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==",
"dev": true,
"requires": {
"commander": "^2.20.0",
@@ -10720,19 +10728,19 @@
}
},
"terser-webpack-plugin": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz",
"integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==",
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.6.tgz",
"integrity": "sha512-I8IDsQwZrqjdmOicNeE8L/MhwatAap3mUrtcAKJuilsemUNcX+Hier/eAzwStVqhlCxq0aG3ni9bK/0BESXkTg==",
"dev": true,
"requires": {
"cacache": "^13.0.1",
"find-cache-dir": "^3.2.0",
"jest-worker": "^25.1.0",
"p-limit": "^2.2.2",
"schema-utils": "^2.6.4",
"serialize-javascript": "^2.1.2",
"find-cache-dir": "^3.3.1",
"jest-worker": "^25.4.0",
"p-limit": "^2.3.0",
"schema-utils": "^2.6.6",
"serialize-javascript": "^3.0.0",
"source-map": "^0.6.1",
"terser": "^4.4.3",
"terser": "^4.6.12",
"webpack-sources": "^1.4.3"
},
"dependencies": {
@@ -10759,7 +10767,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"through2": {
@@ -10778,7 +10786,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"requires": {
"core-util-is": "~1.0.0",
@@ -11458,9 +11466,9 @@
"integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="
},
"webpack": {
"version": "5.0.0-beta.15",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.15.tgz",
"integrity": "sha512-nT+l7LteKTIzB3lmroEGL4qcCBqgHMpa3EJUvhQdfXRWjxCfWnnWdBARhp/To61omZhyNPz2Ye2J1ZEf070kWA==",
"version": "5.0.0-beta.16",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.16.tgz",
"integrity": "sha512-O6YzI5H7XDPoXFrdC338P0GsSdhmYvz0//HL8LxVFHuRSbtHcV8mfx5U8ouWihFqwyvbfy27Bqoz2KY62kME9Q==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.9.0",
@@ -11482,7 +11490,7 @@
"pkg-dir": "^4.2.0",
"schema-utils": "^2.5.0",
"tapable": "2.0.0-beta.9",
"terser-webpack-plugin": "^2.3.1",
"terser-webpack-plugin": "^2.3.6",
"watchpack": "2.0.0-beta.13",
"webpack-sources": "2.0.0-beta.8"
}
+4 -4
View File
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.42.0-beta",
"version": "0.42.5",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -37,7 +37,7 @@
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.17.1",
"file-type": "14.2.0",
"file-type": "14.3.0",
"fs-extra": "9.0.0",
"helmet": "3.22.0",
"html": "1.0.0",
@@ -78,13 +78,13 @@
"yazl": "^2.5.1"
},
"devDependencies": {
"electron": "9.0.0-beta.22",
"electron": "9.0.0",
"electron-builder": "22.6.0",
"electron-packager": "14.2.1",
"electron-rebuild": "1.10.1",
"jsdoc": "3.6.4",
"lorem-ipsum": "2.0.3",
"webpack": "5.0.0-beta.15",
"webpack": "5.0.0-beta.16",
"webpack-cli": "4.0.0-beta.8"
},
"optionalDependencies": {
+1 -2
View File
@@ -105,7 +105,6 @@ class Attribute extends Entity {
// cannot be static!
updatePojo(pojo) {
delete pojo.isOwned;
delete pojo.__note;
}
@@ -124,4 +123,4 @@ class Attribute extends Entity {
}
}
module.exports = Attribute;
module.exports = Attribute;
+1 -5
View File
@@ -411,10 +411,6 @@ class Note extends Entity {
}
});
for (const attr of filteredAttributes) {
attr.isOwned = attr.noteId === this.noteId;
}
this.__attributeCache = filteredAttributes;
}
@@ -946,4 +942,4 @@ class Note extends Entity {
}
}
module.exports = Note;
module.exports = Note;
+6 -3
View File
@@ -8,6 +8,7 @@ import contextMenu from "./services/context_menu.js";
import DesktopMainWindowLayout from "./layouts/desktop_main_window_layout.js";
import glob from "./services/glob.js";
import DesktopExtraWindowLayout from "./layouts/desktop_extra_window_layout.js";
import zoomService from './services/zoom.js';
glob.setupGlobs();
@@ -133,9 +134,11 @@ if (utils.isElectron()) {
return;
}
const zoomLevel = zoomService.getCurrentZoom();
contextMenu.show({
x: params.x,
y: params.y,
x: params.x / zoomLevel,
y: params.y / zoomLevel,
items,
selectMenuItemHandler: ({command, spellingSuggestion}) => {
if (command === 'replaceMisspelling') {
@@ -144,4 +147,4 @@ if (utils.isElectron()) {
}
});
});
}
}
+6 -6
View File
@@ -59,8 +59,8 @@ function AttributesModel() {
});
};
async function showAttributes(attributes) {
const ownedAttributes = attributes.filter(attr => attr.isOwned);
async function showAttributes(noteId, attributes) {
const ownedAttributes = attributes.filter(attr => attr.noteId === noteId);
for (const attr of ownedAttributes) {
attr.labelValue = attr.type === 'label' ? attr.value : '';
@@ -86,7 +86,7 @@ function AttributesModel() {
addLastEmptyRow();
const inheritedAttributes = attributes.filter(attr => !attr.isOwned);
const inheritedAttributes = attributes.filter(attr => attr.noteId !== noteId);
self.inheritedAttributes(inheritedAttributes);
}
@@ -96,7 +96,7 @@ function AttributesModel() {
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
setTimeout(() => $(".attribute-type-select:last").trigger('focus'), 1000);
@@ -166,7 +166,7 @@ function AttributesModel() {
const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave);
await showAttributes(attributes);
await showAttributes(noteId, attributes);
toastService.showMessage("Attributes have been saved.");
};
@@ -311,4 +311,4 @@ $dialog.on('focus', '.label-value', function (e) {
$el: $(this),
open: true
})
});
});
+5 -4
View File
@@ -39,13 +39,14 @@ export async function showDialog(noteIds) {
}
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) {
await branchService.cloneNoteTo(cloneNoteId, targetNoteId, $clonePrefix.val());
await branchService.cloneNoteTo(cloneNoteId, targetBranchId, $clonePrefix.val());
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}`);
}
@@ -64,4 +65,4 @@ $form.on('submit', () => {
}
return false;
});
});
+7 -7
View File
@@ -32,10 +32,11 @@ export async function showDialog(branchIds) {
noteAutocompleteService.showRecentNotes($noteAutoComplete);
}
async function moveNotesTo(parentNoteId) {
await branchService.moveToParentNote(movedBranchIds, parentNoteId);
async function moveNotesTo(parentBranchId) {
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}`);
}
@@ -46,13 +47,12 @@ $form.on('submit', () => {
if (notePath) {
$dialog.modal('hide');
const noteId = treeService.getNoteIdFromNotePath(notePath);
moveNotesTo(noteId);
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
treeCache.getBranchId(parentNoteId, noteId).then(branchId => moveNotesTo(branchId));
}
else {
console.error("No path to move to.");
}
return false;
});
});
+26 -4
View File
@@ -37,15 +37,18 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
async function loadNoteRevisions(noteId, noteRevId) {
$list.empty();
$content.empty();
$titleButtons.empty();
note = appContext.tabManager.getActiveTabNote();
revisionItems = await server.get(`notes/${noteId}/revisions`);
for (const item of revisionItems) {
$list.append($('<a class="dropdown-item" tabindex="0">')
.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);
$list.append(
$('<a class="dropdown-item" tabindex="0">')
.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');
@@ -60,6 +63,8 @@ async function loadNoteRevisions(noteId, noteRevId) {
$title.text("No revisions for this note yet...");
noteRevisionId = null;
}
$eraseAllRevisionsButton.toggle(revisionItems.length > 0);
}
$dialog.on('shown.bs.modal', () => {
@@ -77,6 +82,21 @@ async function setContentPane() {
$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>');
$eraseRevisionButton.on('click', async () => {
@@ -93,6 +113,8 @@ async function setContentPane() {
});
$titleButtons
.append($restoreRevisionButton)
.append(' &nbsp; ')
.append($eraseRevisionButton)
.append(' &nbsp; ');
+17 -3
View File
@@ -24,6 +24,13 @@ const TPL = `
<p>This action will create a new copy of the database and anonymise it (remove all note content and leave only structure and metadata)
for sharing online for debugging purposes without fear of leaking your personal data.</p>
<h4>Backup database</h4>
<button id="backup-database-button" class="btn">Backup database</button>
<br/>
<br/>
<h4>Vacuum database</h4>
<p>This will rebuild database which will typically result in smaller database file. No data will be actually changed.</p>
@@ -37,6 +44,7 @@ export default class AdvancedOptions {
this.$forceFullSyncButton = $("#force-full-sync-button");
this.$fillSyncRowsButton = $("#fill-sync-rows-button");
this.$anonymizeButton = $("#anonymize-button");
this.$backupDatabaseButton = $("#backup-database-button");
this.$vacuumDatabaseButton = $("#vacuum-database-button");
this.$findAndFixConsistencyIssuesButton = $("#find-and-fix-consistency-issues-button");
@@ -58,16 +66,22 @@ export default class AdvancedOptions {
toastService.showMessage("Created anonymized database");
});
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 () => {
await server.post('cleanup/vacuum-database');
await server.post('database/vacuum-database');
toastService.showMessage("Database has been vacuumed");
});
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.");
});
}
}
}
@@ -24,7 +24,6 @@ import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.j
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
import SidePaneToggles from "../widgets/side_pane_toggles.js";
import appContext from "../services/app_context.js";
const RIGHT_PANE_CSS = `
<style>
@@ -117,6 +116,7 @@ export default class DesktopMainWindowLayout {
.hideInZenMode())
.child(new FlexContainer('row')
.collapsible()
.filling()
.child(new SidePaneContainer('left')
.hideInZenMode()
.child(new GlobalButtonsWidget())
@@ -153,4 +153,4 @@ export default class DesktopMainWindowLayout {
.child(new SidePaneToggles().hideInZenMode())
);
}
}
}
+11 -19
View File
@@ -4,7 +4,7 @@ import DialogCommandExecutor from "./dialog_command_executor.js";
import Entrypoints from "./entrypoints.js";
import options from "./options.js";
import utils from "./utils.js";
import ZoomService from "./zoom.js";
import zoomService from "./zoom.js";
import TabManager from "./tab_manager.js";
import treeService from "./tree.js";
import Component from "../widgets/component.js";
@@ -17,6 +17,7 @@ class AppContext extends Component {
super();
this.isMainWindow = isMainWindow;
this.executors = [];
}
setLayout(layout) {
@@ -45,10 +46,12 @@ class AppContext extends Component {
$("body").append($renderedWidget);
$renderedWidget.on('click', "[data-trigger-command]", e => {
const commandName = $(e.target).attr('data-trigger-command');
$renderedWidget.on('click', "[data-trigger-command]", function() {
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();
@@ -71,7 +74,7 @@ class AppContext extends Component {
}
if (utils.isElectron()) {
this.child(new ZoomService());
this.child(zoomService);
}
this.triggerEvent('initialRenderComplete');
@@ -92,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.`);
return this.triggerEvent(name, data);
@@ -100,19 +105,6 @@ class AppContext extends Component {
getComponentByEl(el) {
return $(el).closest(".component").prop('component');
}
async openInNewWindow(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');
}
}
}
const appContext = new AppContext(window.glob.isMainWindow);
@@ -143,4 +135,4 @@ $(window).on('hashchange', function() {
}
});
export default appContext;
export default appContext;
+5 -5
View File
@@ -45,7 +45,7 @@ async function moveAfterBranch(branchIdsToMove, afterBranchId) {
}
}
async function moveToParentNote(branchIdsToMove, newParentNoteId) {
async function moveToParentNote(branchIdsToMove, newParentBranchId) {
branchIdsToMove = filterRootNote(branchIdsToMove);
for (const branchIdToMove of branchIdsToMove) {
@@ -56,7 +56,7 @@ async function moveToParentNote(branchIdsToMove, newParentNoteId) {
continue;
}
const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentNoteId}`);
const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentBranchId}`);
if (!resp.success) {
alert(resp.message);
@@ -198,8 +198,8 @@ ws.subscribeToMessages(async message => {
}
});
async function cloneNoteTo(childNoteId, parentNoteId, prefix) {
const resp = await server.put('notes/' + childNoteId + '/clone-to/' + parentNoteId, {
async function cloneNoteTo(childNoteId, parentBranchId, prefix) {
const resp = await server.put(`notes/${childNoteId}/clone-to/${parentBranchId}`, {
prefix: prefix
});
@@ -225,4 +225,4 @@ export default {
moveNodeUpInHierarchy,
cloneNoteAfter,
cloneNoteTo
};
};
+4 -4
View File
@@ -33,13 +33,13 @@ async function pasteAfter(afterBranchId) {
}
}
async function pasteInto(parentNoteId) {
async function pasteInto(parentBranchId) {
if (isClipboardEmpty()) {
return;
}
if (clipboardMode === 'cut') {
await branchService.moveToParentNote(clipboardBranchIds, parentNoteId);
await branchService.moveToParentNote(clipboardBranchIds, parentBranchId);
clipboardBranchIds = [];
clipboardMode = null;
@@ -50,7 +50,7 @@ async function pasteInto(parentNoteId) {
for (const clipboardBranch of clipboardBranches) {
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
@@ -89,4 +89,4 @@ export default {
cut,
copy,
isClipboardEmpty
}
}
+37
View File
@@ -7,6 +7,7 @@ import Component from "../widgets/component.js";
import toastService from "./toast.js";
import noteCreateService from "./note_create.js";
import ws from "./ws.js";
import bundleService from "./bundle.js";
export default class Entrypoints extends Component {
constructor() {
@@ -182,4 +183,40 @@ export default class Entrypoints extends Component {
}
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");
}
}
+2 -2
View File
@@ -93,8 +93,8 @@ function updateDisplayedShortcuts($container) {
}
});
$container.find('button[data-command],a.icon-action[data-command],.kb-in-title').each(async (i, el) => {
const actionName = $(el).attr('data-command');
$container.find('[data-trigger-command]').each(async (i, el) => {
const actionName = $(el).attr('data-trigger-command');
const action = await getAction(actionName, true);
if (action) {
+21 -14
View File
@@ -81,24 +81,29 @@ function goToLink(e) {
}
else if (e.which === 1) {
const activeTabContext = appContext.tabManager.getActiveTabContext();
activeTabContext.setNote(notePath)
activeTabContext.setNote(notePath);
}
else {
return false;
}
}
else {
const address = $link.attr('href');
if (e.which === 1) {
const address = $link.attr('href');
if (address && address.startsWith('http')) {
window.open(address, '_blank');
if (address && address.startsWith('http')) {
window.open(address, '_blank');
}
}
else {
return false;
}
}
return true;
}
function newTabContextMenu(e) {
function linkContextMenu(e) {
const $link = $(e.target).closest("a");
const notePath = getNotePathFromLink($link);
@@ -113,8 +118,8 @@ function newTabContextMenu(e) {
x: e.pageX,
y: e.pageY,
items: [
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"},
{title: "Open note in new window", command: "openNoteInNewWindow", 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}) => {
if (command === 'openNoteInNewTab') {
@@ -155,21 +160,23 @@ $(document).on('mousedown', '.note-detail-text a', function (e) {
$(document).on('mousedown', '.note-detail-book a', goToLink);
$(document).on('mousedown', '.note-detail-render a', goToLink);
$(document).on('mousedown', '.note-detail-text.ck-read-only a,.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', goToLink);
$(document).on('mousedown', 'a.ck-link-actions__preview', goToLink);
$(document).on('click', 'a.ck-link-actions__preview', e => {
e.preventDefault();
e.stopPropagation();
});
$(document).on('contextmenu', 'a.ck-link-actions__preview', newTabContextMenu);
$(document).on('contextmenu', '.note-detail-text a', newTabContextMenu);
$(document).on('contextmenu', "a[data-action='note']", newTabContextMenu);
$(document).on('contextmenu', ".note-detail-render a", newTabContextMenu);
$(document).on('contextmenu', ".note-paths-widget a", newTabContextMenu);
$(document).on('contextmenu', 'a.ck-link-actions__preview', linkContextMenu);
$(document).on('contextmenu', '.note-detail-text a', linkContextMenu);
$(document).on('contextmenu', '.note-detail-readonly-text a', linkContextMenu);
$(document).on('contextmenu', "a[data-action='note']", linkContextMenu);
$(document).on('contextmenu', ".note-detail-render a", linkContextMenu);
$(document).on('contextmenu', ".note-paths-widget a", linkContextMenu);
export default {
getNotePathFromUrl,
createNoteLink,
goToLink
};
};
+4 -4
View File
@@ -4,7 +4,7 @@ export default class LoadResults {
this.noteIdToSourceId = {};
this.sourceIdToNoteIds = {};
this.branches = [];
this.attributes = [];
@@ -103,10 +103,10 @@ export default class LoadResults {
/**
* @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 Object.keys(this.noteIdToSourceId).length === 0
&& this.branches.length === 0
return this.branches.length === 0
&& this.attributes.length === 0;
}
@@ -119,4 +119,4 @@ export default class LoadResults {
&& this.contentNoteIdToSourceId.length === 0
&& this.options.length === 0;
}
}
}
+2 -14
View File
@@ -38,12 +38,6 @@ export default class MainTreeExecutors extends Component {
isProtected: activeNote.isProtected,
saveSelection: false
});
await ws.waitForMaxKnownSyncId();
appContext.tabManager.getActiveTabContext().setNote(note.noteId);
appContext.triggerCommand('focusAndSelectTitle');
}
async createNoteAfterCommand() {
@@ -55,17 +49,11 @@ export default class MainTreeExecutors extends Component {
return;
}
const {note} = await noteCreateService.createNote(parentNoteId, {
await noteCreateService.createNote(parentNoteId, {
target: 'after',
targetBranchId: node.data.branchId,
isProtected: isProtected,
saveSelection: true
saveSelection: false
});
await ws.waitForMaxKnownSyncId();
appContext.tabManager.getActiveTabContext().setNote(note.noteId);
appContext.triggerCommand('focusAndSelectTitle');
}
}
+5 -1
View File
@@ -48,8 +48,12 @@ async function createNote(parentNoteId, options = {}) {
}
if (options.activate) {
await ws.waitForMaxKnownSyncId();
const activeTabContext = appContext.tabManager.getActiveTabContext();
activeTabContext.setNote(note.noteId);
await activeTabContext.setNote(note.noteId);
appContext.triggerCommand('focusAndSelectTitle');
}
return {note, branch};
+10 -8
View File
@@ -82,7 +82,7 @@ export default class TabManager extends Component {
if (filteredTabs.length === 0) {
filteredTabs.push({
notePath: 'root',
notePath: this.isMainWindow ? 'root' : '',
active: true
});
}
@@ -196,7 +196,9 @@ export default class TabManager extends Component {
async openTabWithNote(notePath, activate, tabId = null) {
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) {
this.activateTab(tabContext.tabId, false);
@@ -236,7 +238,7 @@ export default class TabManager extends Component {
}
this.tabsUpdate.scheduleUpdate();
this.setCurrentNotePathToHash();
}
@@ -247,6 +249,9 @@ export default class TabManager extends Component {
return;
}
// close dangling autocompletes after closing the tab
$(".aa-input").autocomplete("close");
await this.triggerEvent('beforeTabRemove', {tabId});
if (this.tabContexts.length <= 1) {
@@ -265,9 +270,6 @@ export default class TabManager extends Component {
this.children = this.children.filter(tc => tc.tabId !== tabId);
// remove dangling autocompletes after closing the tab
$(".algolia-autocomplete").remove();
this.triggerEvent('tabRemoved', {tabId});
this.tabsUpdate.scheduleUpdate();
@@ -330,7 +332,7 @@ export default class TabManager extends Component {
this.removeTab(tabId);
appContext.openInNewWindow(notePath);
this.triggerCommand('openInWindow', {notePath});
}
async hoistedNoteChangedEvent({hoistedNoteId}) {
@@ -344,4 +346,4 @@ export default class TabManager extends Component {
}
}
}
}
}
+8 -1
View File
@@ -162,6 +162,13 @@ function getNoteIdFromNotePath(notePath) {
}
function getNoteIdAndParentIdFromNotePath(notePath) {
if (notePath === 'root') {
return {
noteId: 'root',
parentNoteId: 'none'
};
}
let parentNoteId = 'root';
let noteId = '';
@@ -286,4 +293,4 @@ export default {
getNotePathTitle,
getHashValueFromAddress,
parseNotePath
};
};
+2 -5
View File
@@ -57,7 +57,7 @@ class TreeContextMenu {
return [
{ 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: "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",
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes },
@@ -113,9 +113,6 @@ class TreeContextMenu {
if (command === 'openInTab') {
appContext.tabManager.openTabWithNote(notePath);
}
else if (command === 'openInWindow') {
appContext.openInNewWindow(notePath);
}
else if (command === "insertNoteAfter") {
const parentNoteId = this.node.data.parentNoteId;
const isProtected = await treeService.getParentProtectedStatus(this.node);
@@ -134,7 +131,7 @@ class TreeContextMenu {
});
}
else {
this.treeWidget.triggerCommand(command, {node: this.node});
this.treeWidget.triggerCommand(command, {node: this.node, notePath: notePath});
}
}
}
+58 -37
View File
@@ -37,6 +37,21 @@ function subscribeToMessages(messageHandler) {
// used to serialize sync operations
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) {
const message = JSON.parse(event.data);
@@ -51,13 +66,7 @@ async function handleMessage(event) {
$outstandingSyncsCount.html(message.outstandingSyncs);
if (syncRows.length > 0) {
const filteredRows = syncRows.filter(row =>
row.entityName !== 'recent_notes'
&& (row.entityName !== 'options' || row.entityId !== 'openTabs'));
if (filteredRows.length > 0) {
console.debug(utils.now(), "Sync data: ", filteredRows);
}
logRows(syncRows);
syncDataQueue.push(...syncRows);
@@ -133,13 +142,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() {
if (syncDataQueue.length > 0) {
const allSyncData = syncDataQueue;
const allSyncRows = syncDataQueue;
syncDataQueue = [];
const nonProcessedSyncRows = allSyncRows.filter(sync => !processedSyncIds.has(sync.id));
try {
await processSyncRows(allSyncData);
await processSyncRows(nonProcessedSyncRows);
}
catch (e) {
logError(`Encountered error ${e.message}: ${e.stack}, reloading frontend.`);
@@ -148,7 +165,11 @@ async function consumeSyncData() {
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();
@@ -170,7 +191,7 @@ function connectWebSocket() {
async function sendPing() {
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) {
@@ -212,18 +233,18 @@ subscribeToMessages(message => {
async function processSyncRows(syncRows) {
const missingNoteIds = [];
syncRows.forEach(({entityName, entity}) => {
if (entityName === 'branches' && !(entity.parentNoteId in treeCache.notes)) {
for (const {entityName, entity} of syncRows) {
if (entityName === 'branches' && !(entity.parentNoteId in treeCache.notes)) {
missingNoteIds.push(entity.parentNoteId);
}
else if (entityName === 'attributes'
&& entity.type === 'relation'
&& entity.name === 'template'
&& !(entity.noteId in treeCache.notes)) {
}
else if (entityName === 'attributes'
&& entity.type === 'relation'
&& entity.name === 'template'
&& !(entity.noteId in treeCache.notes)) {
missingNoteIds.push(entity.value);
}
});
missingNoteIds.push(entity.value);
}
}
if (missingNoteIds.length > 0) {
await treeCache.reloadNotes(missingNoteIds);
@@ -231,16 +252,16 @@ async function processSyncRows(syncRows) {
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];
if (note) {
note.update(sync.entity);
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];
const childNote = treeCache.notes[sync.entity.noteId];
const parentNote = treeCache.notes[sync.entity.parentNoteId];
@@ -286,9 +307,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) {
const branch = treeCache.branches[branchId];
@@ -298,10 +319,10 @@ async function processSyncRows(syncRows) {
}
loadResults.addNoteReordering(sync.entityId, sync.sourceId);
});
}
// 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];
const sourceNote = treeCache.notes[sync.entity.noteId];
const targetNote = sync.entity.type === 'relation' && treeCache.notes[sync.entity.value];
@@ -337,27 +358,27 @@ 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];
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);
});
}
syncRows.filter(sync => sync.entityName === 'options').forEach(sync => {
for (const sync of syncRows.filter(sync => sync.entityName === 'options')) {
if (sync.entity.name === 'openTabs') {
return; // only noise
continue; // only noise
}
options.set(sync.entity.name, sync.entity.value);
loadResults.addOption(sync.entity.name);
});
}
if (!loadResults.isEmpty()) {
if (loadResults.hasAttributeRelatedChanges()) {
@@ -374,4 +395,4 @@ export default {
subscribeToMessages,
waitForSyncId,
waitForMaxKnownSyncId
};
};
+15 -7
View File
@@ -5,31 +5,35 @@ import utils from "../services/utils.js";
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2.0;
export default class ZoomService extends Component {
class ZoomService extends Component {
constructor() {
super();
this.setZoomFactor(options.getFloat('zoomFactor'));
if (utils.isElectron()) {
options.initializedPromise.then(() => {
this.setZoomFactor(options.getFloat('zoomFactor'));
});
}
}
setZoomFactor(zoomFactor) {
zoomFactor = parseFloat(zoomFactor);
const webFrame = utils.dynamicRequire('electron').webFrame;
webFrame.setZoomFactor(zoomFactor);
}
async setZoomFactorAndSave(zoomFactor) {
if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
this.setZoomFactor(zoomFactor);
await options.save('zoomFactor', zoomFactor);
}
else {
console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
}
}
getCurrentZoom() {
return utils.dynamicRequire('electron').webFrame.getZoomFactor();
}
@@ -45,4 +49,8 @@ export default class ZoomService extends Component {
setZoomFactorAndSaveEvent({zoomFactor}) {
this.setZoomFactorAndSave(zoomFactor);
}
}
}
const zoomService = new ZoomService();
export default zoomService;
+6 -1
View File
@@ -30,6 +30,11 @@ class BasicWidget extends Component {
return this;
}
filling() {
this.css('flex-grow', '1');
return this;
}
hideInZenMode() {
this.class('hide-in-zen-mode');
return this;
@@ -109,4 +114,4 @@ class BasicWidget extends Component {
cleanup() {}
}
export default BasicWidget;
export default BasicWidget;
@@ -21,16 +21,16 @@ const TPL = `
</style>
<tr>
<th nowrap>Note ID:</th>
<td nowrap colspan="3" class="note-info-note-id"></td>
<th>Note ID:</th>
<td colspan="3" class="note-info-note-id"></td>
</tr>
<tr>
<th nowrap>Created:</th>
<td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-created"></td>
<th>Created:</th>
<td colspan="3" class="note-info-date-created"></td>
</tr>
<tr>
<th nowrap>Modified:</th>
<td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-modified"></td>
<th>Modified:</th>
<td colspan="3" class="note-info-date-modified"></td>
</tr>
<tr>
<th>Type:</th>
@@ -60,11 +60,11 @@ export default class NoteInfoWidget extends CollapsibleWidget {
this.$noteId.text(note.noteId);
this.$dateCreated
.text(noteComplement.dateCreated)
.text(noteComplement.dateCreated.substr(0, 16))
.attr("title", noteComplement.dateCreated);
this.$dateModified
.text(noteComplement.dateModified)
.text(noteComplement.dateModified.substr(0, 16))
.attr("title", noteComplement.dateCreated);
this.$type.text(note.type);
@@ -79,4 +79,4 @@ export default class NoteInfoWidget extends CollapsibleWidget {
this.refresh();
}
}
}
}
+5 -1
View File
@@ -82,6 +82,10 @@ export default class Component {
let release;
try {
if (this.mutex.isLocked()) {
console.debug("Mutex locked for", this.constructor.name);
}
release = await this.mutex.acquire();
await fun.call(this, data);
@@ -93,4 +97,4 @@ export default class Component {
}
}
}
}
}
+1 -4
View File
@@ -18,17 +18,14 @@ const WIDGET_TPL = `
<a data-trigger-command="collapseTree"
title="Collapse note tree"
data-command="collapseTree"
class="icon-action bx bx-layer-minus"></a>
<a data-trigger-command="scrollToActiveNote"
title="Scroll to active note"
data-command="scrollToActiveNote"
title="Scroll to active note"
class="icon-action bx bx-crosshair"></a>
<a data-trigger-command="searchNotes"
title="Search in notes"
data-command="searchNotes"
class="icon-action bx bx-search"></a>
</div>
`;
+6 -1
View File
@@ -1,5 +1,4 @@
import BasicWidget from "./basic_widget.js";
import keyboardActionService from "../services/keyboard_actions.js";
import utils from "../services/utils.js";
import syncService from "../services/sync.js";
@@ -39,6 +38,12 @@ const TPL = `
Sync (<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 class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools">
<span class="bx bx-terminal"></span>
Open Dev Tools
+9 -4
View File
@@ -186,7 +186,9 @@ export default class NoteDetailWidget extends TabAwareWidget {
const noteComplement = await this.tabContext.getNoteComplement();
if (note.hasLabel('readOnly') ||
(noteComplement.content && noteComplement.content.length > 10000)) {
(noteComplement.content
&& noteComplement.content.length > 10000)
&& !note.hasLabel('autoReadOnlyDisabled')) {
type = 'read-only-text';
}
}
@@ -195,7 +197,9 @@ export default class NoteDetailWidget extends TabAwareWidget {
const noteComplement = await this.tabContext.getNoteComplement();
if (note.hasLabel('readOnly') ||
(noteComplement.content && noteComplement.content.length > 30000)) {
(noteComplement.content
&& noteComplement.content.length > 30000)
&& !note.hasLabel('autoReadOnlyDisabled')) {
type = 'read-only-code';
}
}
@@ -304,9 +308,10 @@ export default class NoteDetailWidget extends TabAwareWidget {
return;
}
await noteCreateService.createNote(note.noteId, {
// without await as this otherwise causes deadlock through component mutex
noteCreateService.createNote(note.noteId, {
isProtected: note.isProtected,
saveSelection: true
});
}
}
}
+78 -45
View File
@@ -251,8 +251,8 @@ export default class NoteTreeWidget extends TabAwareWidget {
this.triggerCommand('setActiveScreen', {screen:'detail'});
}
},
expand: (event, data) => this.setExpandedToServer(data.node.data.branchId, true),
collapse: (event, data) => this.setExpandedToServer(data.node.data.branchId, false),
expand: (event, data) => this.setExpanded(data.node.data.branchId, true),
collapse: (event, data) => this.setExpanded(data.node.data.branchId, false),
hotkeys: utils.isMobile() ? undefined : { keydown: await this.getHotKeys() },
dnd5: {
autoExpandMS: 600,
@@ -265,6 +265,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
const notes = this.getSelectedOrActiveNodes(node).map(node => ({
noteId: node.data.noteId,
branchId: node.data.branchId,
title: node.title
}));
@@ -304,17 +305,28 @@ export default class NoteTreeWidget extends TabAwareWidget {
});
}
else {
const jsonStr = dataTransfer.getData("text");
let notes = null;
try {
notes = JSON.parse(jsonStr);
}
catch (e) {
console.error(`Cannot parse ${jsonStr} into notes for drop`);
return;
}
// This function MUST be defined to enable dropping of items on the tree.
// data.hitMode is 'before', 'after', or 'over'.
const selectedBranchIds = this.getSelectedOrActiveNodes().map(node => node.data.branchId);
const selectedBranchIds = notes.map(note => note.branchId);
if (data.hitMode === "before") {
branchService.moveBeforeBranch(selectedBranchIds, node.data.branchId);
} else if (data.hitMode === "after") {
branchService.moveAfterBranch(selectedBranchIds, node.data.branchId);
} else if (data.hitMode === "over") {
branchService.moveToParentNote(selectedBranchIds, node.data.noteId);
branchService.moveToParentNote(selectedBranchIds, node.data.branchId);
} else {
throw new Error("Unknown hitMode=" + data.hitMode);
}
@@ -571,13 +583,18 @@ export default class NoteTreeWidget extends TabAwareWidget {
/** @return {FancytreeNode[]} */
getSelectedOrActiveNodes(node = null) {
const notes = this.getSelectedNodes(true);
const nodes = this.getSelectedNodes(true);
if (notes.length === 0) {
notes.push(node ? node : this.getActiveNode());
// the node you start dragging should be included even if not selected
if (node && !nodes.find(n => n.key === node.key)) {
nodes.push(node);
}
return notes;
if (nodes.length === 0) {
nodes.push(this.getActiveNode());
}
return nodes;
}
async setExpandedStatusForSubtree(node, isExpanded) {
@@ -729,6 +746,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
node.icon = this.getIcon(note, isFolder);
node.extraClasses = this.getExtraClasses(note);
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.setExpanded(branch.isExpanded, {noEvents:true});
node.renderTitle();
}
@@ -804,7 +822,9 @@ export default class NoteTreeWidget extends TabAwareWidget {
async entitiesReloadedEvent({loadResults}) {
const activeNode = this.getActiveNode();
const nextNode = activeNode ? (activeNode.getNextSibling() || activeNode.getPrevSibling() || activeNode.getParent()) : null;
const activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null;
const nextNotePath = nextNode ? treeService.getNotePath(nextNode) : null;
const activeNoteId = activeNode ? activeNode.data.noteId : null;
const noteIdsToUpdate = new Set();
@@ -877,18 +897,12 @@ export default class NoteTreeWidget extends TabAwareWidget {
noteIdsToUpdate.add(noteId);
}
for (const noteId of noteIdsToReload) {
for (const node of this.getNodesByNoteId(noteId)) {
await node.load(true);
this.updateNode(node);
}
}
await this.batchUpdate(async () => {
for (const noteId of noteIdsToUpdate) {
for (const noteId of noteIdsToReload) {
for (const node of this.getNodesByNoteId(noteId)) {
this.updateNode(node);
await node.load(true);
noteIdsToUpdate.add(noteId);
}
}
@@ -910,6 +924,13 @@ export default class NoteTreeWidget extends TabAwareWidget {
}
});
// for some reason node update cannot be in the batchUpdate() block (node is not re-rendered)
for (const noteId of noteIdsToUpdate) {
for (const node of this.getNodesByNoteId(noteId)) {
this.updateNode(node);
}
}
if (activeNotePath) {
let node = await this.expandToNote(activeNotePath);
@@ -925,15 +946,27 @@ export default class NoteTreeWidget extends TabAwareWidget {
if (node) {
node.setActive(true, {noEvents: true});
}
else {
// this is used when original note has been deleted and we want to move the focus to the note above/below
node = await this.expandToNote(nextNotePath);
if (node) {
this.tree.setFocus();
node.setFocus(true);
await appContext.tabManager.getActiveTabContext().setNote(nextNotePath);
}
}
}
}
async setExpandedToServer(branchId, isExpanded) {
async setExpanded(branchId, isExpanded) {
utils.assertArguments(branchId);
const expandedNum = isExpanded ? 1 : 0;
const branch = treeCache.getBranch(branchId);
branch.isExpanded = isExpanded;
await server.put('branches/' + branchId + '/expanded/' + expandedNum);
await server.put(`branches/${branchId}/expanded/${isExpanded ? 1 : 0}`);
}
async reloadTreeFromCache() {
@@ -993,7 +1026,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
return false;
}
};
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
hotKeyMap[utils.normalizeShortcut(shortcut)] = node => {
@@ -1018,83 +1051,83 @@ export default class NoteTreeWidget extends TabAwareWidget {
async deleteNotesCommand({node}) {
const branchIds = this.getSelectedOrActiveBranchIds(node);
await branchService.deleteNotes(branchIds);
this.clearSelectedNodes();
}
moveNoteUpCommand({node}) {
const beforeNode = node.getPrevSibling();
if (beforeNode !== null) {
branchService.moveBeforeBranch([node.data.branchId], beforeNode.data.branchId);
}
}
moveNoteDownCommand({node}) {
const afterNode = node.getNextSibling();
if (afterNode !== null) {
branchService.moveAfterBranch([node.data.branchId], afterNode.data.branchId);
}
}
moveNoteUpInHierarchyCommand({node}) {
branchService.moveNodeUpInHierarchy(node);
}
moveNoteDownInHierarchyCommand({node}) {
const toNode = node.getPrevSibling();
if (toNode !== null) {
branchService.moveToParentNote([node.data.branchId], toNode.data.noteId);
branchService.moveToParentNote([node.data.branchId], toNode.data.branchId);
}
}
addNoteAboveToSelectionCommand() {
const node = this.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
const prevSibling = node.getPrevSibling();
if (prevSibling) {
prevSibling.setActive(true, {noEvents: true});
if (prevSibling.isSelected()) {
node.setSelected(false);
}
prevSibling.setSelected(true);
}
}
addNoteBelowToSelectionCommand() {
const node = this.getFocusedNode();
if (!node) {
return;
}
if (node.isActive()) {
node.setSelected(true);
}
const nextSibling = node.getNextSibling();
if (nextSibling) {
nextSibling.setActive(true, {noEvents: true});
if (nextSibling.isSelected()) {
node.setSelected(false);
}
nextSibling.setSelected(true);
}
}
@@ -1132,7 +1165,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
}
pasteNotesFromClipboardCommand({node}) {
clipboard.pasteInto(node.data.noteId);
clipboard.pasteInto(node.data.branchId);
}
pasteNotesAfterFromClipboard({node}) {
@@ -1178,4 +1211,4 @@ export default class NoteTreeWidget extends TabAwareWidget {
noteCreateService.duplicateNote(node.data.noteId, branch.parentNoteId);
}
}
}
@@ -19,6 +19,7 @@ const TPL = `
.promoted-attributes td, .promoted-attributes th {
padding: 5px;
min-width: 50px; /* otherwise checkboxes can collapse into 0 width (if there are only checkboxes) */
}
</style>
@@ -98,7 +99,7 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
const $labelCell = $("<th>").append(valueAttr.name);
const $input = $("<input>")
.prop("tabindex", definitionAttr.position)
.prop("attribute-id", valueAttr.isOwned ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one
.prop("attribute-id", valueAttr.noteId === this.noteId ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one
.prop("attribute-type", valueAttr.type)
.prop("attribute-name", valueAttr.name)
.prop("value", valueAttr.value)
@@ -266,4 +267,4 @@ export default class PromotedAttributesWidget extends TabAwareWidget {
$attr.prop("attribute-id", result.attributeId);
}
}
}
+10 -2
View File
@@ -3,10 +3,12 @@ import TabAwareWidget from "./tab_aware_widget.js";
const TPL = `
<div style="display: inline-flex;">
<button class="btn btn-sm icon-button bx bx-play-circle render-button"
data-trigger-command="renderActiveNote"
title="Render"></button>
<button class="btn btn-sm icon-button bx bx-play-circle execute-script-button"
title="Execute (Ctrl+Enter)"></button>
data-trigger-command="runActiveNote"
title="Execute"></button>
</div>`;
export default class RunScriptButtonsWidget extends TabAwareWidget {
@@ -21,6 +23,12 @@ export default class RunScriptButtonsWidget extends TabAwareWidget {
refreshWithNote(note) {
this.$renderButton.toggle(note.type === 'render');
this.$executeScriptButton.toggle(note.mime.startsWith('application/javascript'));
this.$executeScriptButton.toggle(note.type === 'code' && note.mime.startsWith('application/javascript'));
}
async entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId)) {
this.refresh();
}
}
}
+20 -14
View File
@@ -29,11 +29,11 @@ const TAB_TPL = `
<div class="note-tab-wrapper">
<div class="note-tab-title"></div>
<div class="note-tab-drag-handle"></div>
<div class="note-tab-close kb-in-title" title="Close tab" data-command="closeActiveTab"><span>×</span></div>
<div class="note-tab-close" title="Close tab" data-trigger-command="closeActiveTab"><span>×</span></div>
</div>
</div>`;
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab kb-in-title" data-command="openNewTab" title="Add new tab">+</div>`;
const NEW_TAB_BUTTON_TPL = `<div class="note-new-tab" data-trigger-command="openNewTab" title="Add new tab">+</div>`;
const FILLER_TPL = `<div class="tab-row-filler">
<div class="tab-row-border"></div>
</div>`;
@@ -258,9 +258,9 @@ export default class TabRowWidget extends BasicWidget {
x: e.pageX,
y: e.pageY,
items: [
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "empty"},
{title: "Close all tabs", command: "removeAllTabs", uiIcon: "empty"},
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"},
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "window-open"},
{title: "Close all tabs", command: "removeAllTabs", uiIcon: "x"},
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "x"},
],
selectMenuItemHandler: ({command}) => {
this.triggerCommand(command, {tabId});
@@ -394,10 +394,13 @@ export default class TabRowWidget extends BasicWidget {
this.setupDraggabilly();
}
setTabCloseEvent($tab) {
$tab.find('.note-tab-close')
.on('click', _ => appContext.tabManager.removeTab($tab.attr('data-tab-id')));
closeActiveTabCommand({$el}) {
const tabId = $el.closest(".note-tab").attr('data-tab-id');
appContext.tabManager.removeTab(tabId);
}
setTabCloseEvent($tab) {
$tab.on('mousedown', e => {
if (e.which === 2) {
appContext.tabManager.removeTab($tab.attr('data-tab-id'));
@@ -558,8 +561,6 @@ export default class TabRowWidget extends BasicWidget {
this.$newTab = $(NEW_TAB_BUTTON_TPL);
this.$tabContainer.append(this.$newTab);
this.$newTab.on('click', _ => this.triggerCommand('openNewTab'));
}
setupFiller() {
@@ -601,18 +602,23 @@ export default class TabRowWidget extends BasicWidget {
}
updateTab($tab, note) {
if (!note || !$tab.length) {
if (!$tab.length) {
return;
}
this.updateTitle($tab, note.title);
for (const clazz of Array.from($tab[0].classList)) { // create copy to safely iterate over while removing classes
if (clazz !== 'note-tab') {
$tab.removeClass(clazz);
}
}
if (!note) {
this.updateTitle($tab, 'New tab');
return;
}
this.updateTitle($tab, note.title);
$tab.addClass(note.getCssClass());
$tab.addClass(utils.getNoteTypeClass(note.type));
$tab.addClass(utils.getMimeTypeClass(note.mime));
@@ -635,4 +641,4 @@ export default class TabRowWidget extends BasicWidget {
this.updateTab($tab, tabContext.note);
}
}
}
}
@@ -1,9 +1,6 @@
import libraryLoader from "../../services/library_loader.js";
import bundleService from "../../services/bundle.js";
import toastService from "../../services/toast.js";
import server from "../../services/server.js";
import keyboardActionService from "../../services/keyboard_actions.js";
import TypeWidget from "./type_widget.js";
import keyboardActionService from "../../services/keyboard_actions.js";
const TPL = `
<div class="note-detail-code note-detail-printable">
@@ -27,11 +24,8 @@ export default class EditableCodeTypeWidget extends TypeWidget {
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find('.note-detail-code-editor');
this.$executeScriptButton = this.$widget.find(".execute-script-button");
keyboardActionService.setElementActionHandler(this.$widget, 'runActiveNote', () => this.executeCurrentNote());
this.$executeScriptButton.on('click', () => this.executeCurrentNote());
keyboardActionService.setupActionsForElement('code-detail', this.$widget, this);
this.initialized = this.initEditor();
@@ -106,26 +100,6 @@ export default class EditableCodeTypeWidget extends TypeWidget {
this.codeEditor.focus();
}
async executeCurrentNote() {
// ctrl+enter is also used elsewhere so make sure we're running only when appropriate
if (this.note.type !== 'code') {
return;
}
// make sure note is saved so we load latest changes
await this.spacedUpdate.updateNowIfNecessary();
if (this.note.mime.endsWith("env=frontend")) {
await bundleService.getAndExecuteBundle(this.noteId);
}
if (this.note.mime.endsWith("env=backend")) {
await server.post('script/run/' + this.noteId);
}
toastService.showMessage("Note executed");
}
cleanup() {
if (this.codeEditor) {
this.spacedUpdate.allowUpdateWithoutChange(() => {
+12 -6
View File
@@ -5,17 +5,23 @@ import TypeWidget from "./type_widget.js";
const TPL = `
<div class="note-detail-file note-detail-printable">
<style>
.file-table td {
overflow-wrap: anywhere;
}
</style>
<table class="file-table">
<tr>
<th nowrap>Note ID:</th>
<th>Note ID:</th>
<td class="file-note-id"></td>
<th nowrap>Original file name:</th>
<th>Original file name:</th>
<td class="file-filename"></td>
</tr>
<tr>
<th nowrap>File type:</th>
<th>File type:</th>
<td class="file-filetype"></td>
<th nowrap>File size:</th>
<th>File size:</th>
<td class="file-filesize"></td>
</tr>
</table>
@@ -94,7 +100,7 @@ export default class FileTypeWidget extends TypeWidget {
toastService.showError("Upload of a new file revision failed.");
}
});
return this.$widget;
}
@@ -130,4 +136,4 @@ export default class FileTypeWidget extends TypeWidget {
getFileUrl() {
return utils.getUrlForDownload("api/notes/" + this.noteId + "/download");
}
}
}
@@ -22,6 +22,10 @@ const TPL = `
.note-detail-readonly-text p:first-child, .note-detail-text::before {
margin-top: 0;
}
.note-detail-readonly-text img {
max-width: 100%;
}
</style>
<div class="alert alert-warning no-print">
@@ -77,4 +81,4 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
this.loadIncludedNote(noteId, $(el));
});
}
}
}
@@ -25,9 +25,6 @@ export default class RenderTypeWidget extends TypeWidget {
this.$widget = $(TPL);
this.$noteDetailRenderHelp = this.$widget.find('.note-detail-render-help');
this.$noteDetailRenderContent = this.$widget.find('.note-detail-render-content');
this.$renderButton = this.$widget.find('.render-button');
this.$renderButton.on('click', () => this.refresh());
return this.$widget;
}
@@ -46,4 +43,10 @@ export default class RenderTypeWidget extends TypeWidget {
cleanup() {
this.$noteDetailRenderContent.empty();
}
renderActiveNoteEvent() {
if (this.tabContext.isActive()) {
this.refresh();
}
}
}
+3 -2
View File
@@ -101,7 +101,7 @@ async function updateNoteAttributes(req) {
|| (attribute.type === 'relation' && attribute.value !== attributeEntity.value)) {
if (attribute.type !== 'relation' || !!attribute.value.trim()) {
const newAttribute = attribute.createClone(attribute.type, attribute.name, attribute.value);
const newAttribute = attributeEntity.createClone(attribute.type, attribute.name, attribute.value);
await newAttribute.save();
}
@@ -139,6 +139,7 @@ async function updateNoteAttributes(req) {
}
const note = await repository.getNote(noteId);
note.invalidateAttributeCache();
return await note.getAttributes();
}
@@ -198,4 +199,4 @@ module.exports = {
getEffectiveNoteAttributes,
createRelation,
deleteRelation
};
};
+15 -7
View File
@@ -14,25 +14,33 @@ const TaskContext = require('../../services/task_context');
*/
async function moveBranchToParent(req) {
const {branchId, parentNoteId} = req.params;
const {branchId, parentBranchId} = req.params;
const parentBranch = await repository.getBranch(parentBranchId);
const branchToMove = await repository.getBranch(branchId);
if (branchToMove.parentNoteId === parentNoteId) {
if (!parentBranch || !branchToMove) {
return [400, `One or both branches ${branchId}, ${parentBranchId} have not been found`];
}
if (branchToMove.parentNoteId === parentBranch.noteId) {
return { success: true }; // no-op
}
const validationResult = await treeService.validateParentChild(parentNoteId, branchToMove.noteId, branchId);
const validationResult = await treeService.validateParentChild(parentBranch.noteId, branchToMove.noteId, branchId);
if (!validationResult.success) {
return [200, validationResult];
}
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]);
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentBranch.noteId]);
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 10;
const newBranch = branchToMove.createClone(parentNoteId, newNotePos);
newBranch.isExpanded = true;
// expanding so that the new placement of the branch is immediately visible
parentBranch.isExpanded = true;
await parentBranch.save();
const newBranch = branchToMove.createClone(parentBranch.noteId, newNotePos);
await newBranch.save();
branchToMove.isDeleted = true;
@@ -178,4 +186,4 @@ module.exports = {
setExpandedForSubtree,
deleteBranch,
setPrefix
};
};
+3 -3
View File
@@ -3,10 +3,10 @@
const cloningService = require('../../services/cloning');
async function cloneNoteToParent(req) {
const {noteId, parentNoteId} = req.params;
const {noteId, parentBranchId} = req.params;
const {prefix} = req.body;
return await cloningService.cloneNoteToParent(noteId, parentNoteId, prefix);
return await cloningService.cloneNoteToParent(noteId, parentBranchId, prefix);
}
async function cloneNoteAfter(req) {
@@ -18,4 +18,4 @@ async function cloneNoteAfter(req) {
module.exports = {
cloneNoteToParent,
cloneNoteAfter
};
};
@@ -2,8 +2,15 @@
const sql = require('../../services/sql');
const log = require('../../services/log');
const backupService = require('../../services/backup');
const consistencyChecksService = require('../../services/consistency_checks');
async function backupDatabase() {
return {
backupFile: await backupService.backupNow("now")
};
}
async function vacuumDatabase() {
await sql.execute("VACUUM");
@@ -15,6 +22,7 @@ async function findAndFixConsistencyIssues() {
}
module.exports = {
backupDatabase,
vacuumDatabase,
findAndFixConsistencyIssues
};
};
+9 -1
View File
@@ -11,6 +11,14 @@ async function exportBranch(req, res) {
const {branchId, type, format, version, taskId} = req.params;
const branch = await repository.getBranch(branchId);
if (!branch) {
const message = `Cannot export branch ${branchId} since it does not exist.`;
log.error(message);
res.status(500).send(message);
return;
}
const taskContext = new TaskContext(taskId, 'export');
try {
@@ -39,4 +47,4 @@ async function exportBranch(req, res) {
module.exports = {
exportBranch
};
};
+4 -3
View File
@@ -1,6 +1,5 @@
"use strict";
const noteService = require('../../services/notes');
const protectedSessionService = require('../../services/protected_session');
const repository = require('../../services/repository');
const utils = require('../../services/utils');
@@ -45,7 +44,9 @@ async function downloadNoteFile(noteId, res, contentDisposition = true) {
if (contentDisposition) {
// (one) reason we're not using the originFileName (available as label) is that it's not
// available for older note revisions and thus would be inconsistent
res.setHeader('Content-Disposition', utils.getContentDisposition(note.title || "untitled"));
const filename = utils.formatDownloadTitle(note.title, note.type, note.mime);
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
}
res.setHeader('Content-Type', note.mime);
@@ -70,4 +71,4 @@ module.exports = {
openFile,
downloadFile,
downloadNoteFile
};
};
+3 -3
View File
@@ -16,7 +16,7 @@ const ApiToken = require('../../entities/api_token');
async function loginSync(req) {
if (!await sqlInit.schemaExists()) {
return [400, { message: "DB schema does not exist, can't sync." }];
return [500, { message: "DB schema does not exist, can't sync." }];
}
const timestampStr = req.body.timestamp;
@@ -27,7 +27,7 @@ async function loginSync(req) {
// login token is valid for 5 minutes
if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
return [400, { message: 'Auth request time is out of sync, please check that both client and server have correct time.' }];
return [401, { message: 'Auth request time is out of sync, please check that both client and server have correct time.' }];
}
const syncVersion = req.body.syncVersion;
@@ -102,4 +102,4 @@ module.exports = {
loginSync,
loginToProtectedSession,
token
};
};
+19 -9
View File
@@ -3,6 +3,7 @@
const repository = require('../../services/repository');
const noteCacheService = require('../../services/note_cache');
const protectedSessionService = require('../../services/protected_session');
const noteRevisionService = require('../../services/note_revisions');
const utils = require('../../services/utils');
const path = require('path');
@@ -37,13 +38,7 @@ async function getNoteRevision(req) {
* @return {string}
*/
function getRevisionFilename(noteRevision) {
let filename = noteRevision.title || "untitled";
if (noteRevision.type === 'text') {
filename += '.html';
} else if (['relation-map', 'search'].includes(noteRevision.type)) {
filename += '.json';
}
let filename = utils.formatDownloadTitle(noteRevision.title, noteRevision.type, noteRevision.mime);
const extension = path.extname(filename);
const date = noteRevision.dateCreated
@@ -109,6 +104,20 @@ async function eraseNoteRevision(req) {
}
}
async function restoreNoteRevision(req) {
const noteRevision = await repository.getNoteRevision(req.params.noteRevisionId);
if (noteRevision && !noteRevision.isErased) {
const note = await noteRevision.getNote();
await noteRevisionService.createNoteRevision(note);
note.title = noteRevision.title;
await note.setContent(await noteRevision.getContent());
await note.save();
}
}
async function getEditedNotesOnDate(req) {
const date = utils.sanitizeSql(req.params.date);
@@ -141,5 +150,6 @@ module.exports = {
downloadNoteRevision,
getEditedNotesOnDate,
eraseAllNoteRevisions,
eraseNoteRevision
};
eraseNoteRevision,
restoreNoteRevision
};
+11 -7
View File
@@ -25,7 +25,7 @@ const importRoute = require('./api/import');
const setupApiRoute = require('./api/setup');
const sqlRoute = require('./api/sql');
const anonymizationRoute = require('./api/anonymization');
const cleanupRoute = require('./api/cleanup');
const databaseRoute = require('./api/database');
const imageRoute = require('./api/image');
const attributesRoute = require('./api/attributes');
const scriptRoute = require('./api/script');
@@ -123,7 +123,7 @@ function register(app) {
apiRoute(POST, '/api/tree/load', treeApiRoute.load);
apiRoute(PUT, '/api/branches/:branchId/set-prefix', branchesApiRoute.setPrefix);
apiRoute(PUT, '/api/branches/:branchId/move-to/:parentNoteId', branchesApiRoute.moveBranchToParent);
apiRoute(PUT, '/api/branches/:branchId/move-to/:parentBranchId', branchesApiRoute.moveBranchToParent);
apiRoute(PUT, '/api/branches/:branchId/move-before/:beforeBranchId', branchesApiRoute.moveBranchBeforeNote);
apiRoute(PUT, '/api/branches/:branchId/move-after/:afterBranchId', branchesApiRoute.moveBranchAfterNote);
apiRoute(PUT, '/api/branches/:branchId/expanded/:expanded', branchesApiRoute.setExpanded);
@@ -145,13 +145,14 @@ function register(app) {
apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision);
apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision);
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle);
apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote);
apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate);
apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentNoteId', cloningApiRoute.cloneNoteToParent);
apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentBranchId', cloningApiRoute.cloneNoteToParent);
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
@@ -221,10 +222,13 @@ function register(app) {
apiRoute(POST, '/api/sql/execute', sqlRoute.execute);
apiRoute(POST, '/api/anonymization/anonymize', anonymizationRoute.anonymize);
// VACUUM requires execution outside of transaction
route(POST, '/api/cleanup/vacuum-database', [auth.checkApiAuthOrElectron, csrfMiddleware], cleanupRoute.vacuumDatabase, apiResultHandler, false);
// backup requires execution outside of transaction
route(POST, '/api/database/backup-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.backupDatabase, apiResultHandler, false);
route(POST, '/api/cleanup/find-and-fix-consistency-issues', [auth.checkApiAuthOrElectron, csrfMiddleware], cleanupRoute.findAndFixConsistencyIssues, apiResultHandler, false);
// VACUUM requires execution outside of transaction
route(POST, '/api/database/vacuum-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.vacuumDatabase, apiResultHandler, false);
route(POST, '/api/database/find-and-fix-consistency-issues', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.findAndFixConsistencyIssues, apiResultHandler, false);
apiRoute(POST, '/api/script/exec', scriptRoute.exec);
apiRoute(POST, '/api/script/run/:noteId', scriptRoute.run);
@@ -266,4 +270,4 @@ function register(app) {
module.exports = {
register
};
};
+1 -2
View File
@@ -6,9 +6,8 @@ const utils = require('../services/utils');
async function setupPage(req, res) {
if (await sqlInit.isDbInitialized()) {
const windowService = require('../services/window');
if (utils.isElectron()) {
const windowService = require('../services/window');
await windowService.createMainWindow();
windowService.closeSetupWindow();
}
+1
View File
@@ -19,6 +19,7 @@ const BUILTIN_ATTRIBUTES = [
{ type: 'label', name: 'appTheme' },
{ type: 'label', name: 'hidePromotedAttributes' },
{ type: 'label', name: 'readOnly' },
{ type: 'label', name: 'autoReadOnlyDisabled' },
{ type: 'label', name: 'cssClass' },
{ type: 'label', name: 'iconClass' },
{ type: 'label', name: 'keyboardShortcut' },
+36 -7
View File
@@ -28,14 +28,43 @@ async function periodBackup(optionName, fileName, periodInSeconds) {
}
}
const BACKUP_ATTEMPT_COUNT = 50;
async function backupNow(name) {
const sql = require('./sql');
// we don't want to backup DB in the middle of sync with potentially inconsistent DB state
await syncMutexService.doExclusively(async () => {
return await syncMutexService.doExclusively(async () => {
const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`;
fs.copySync(dataDir.DOCUMENT_PATH, backupFile);
try {
fs.unlinkSync(backupFile);
}
catch (e) {} // unlink throws exception if the file did not exist
log.info("Created backup at " + backupFile);
let success = false;
let attemptCount = 0
for (; attemptCount < BACKUP_ATTEMPT_COUNT && !success; attemptCount++) {
try {
await sql.executeNoWrap(`VACUUM INTO '${backupFile}'`);
success++;
}
catch (e) {
log.info(`Backup attempt ${attemptCount + 1} failed with "${e.message}", retrying...`);
}
// we re-try since VACUUM is very picky and it can't run if there's any other query currently running
// which is difficult to guarantee so we just re-try
}
if (attemptCount === BACKUP_ATTEMPT_COUNT) {
log.error(`Creating backup ${backupFile} failed`);
}
else {
log.info("Created backup at " + backupFile);
}
return backupFile;
});
}
@@ -44,12 +73,12 @@ if (!fs.existsSync(dataDir.BACKUP_DIR)) {
}
sqlInit.dbReady.then(() => {
setInterval(cls.wrap(regularBackup), 60 * 60 * 1000);
setInterval(cls.wrap(regularBackup), 4 * 60 * 60 * 1000);
// kickoff backup immediately
setTimeout(cls.wrap(regularBackup), 1000);
// kickoff first backup soon after start up
setTimeout(cls.wrap(regularBackup), 5 * 60 * 1000);
});
module.exports = {
backupNow
};
};
+1 -1
View File
@@ -1 +1 @@
module.exports = { buildDate:"2020-05-04T21:59:14+02:00", buildRevision: "cafcb67a8a3a1943acac829590b34ff729b57e09" };
module.exports = { buildDate:"2020-05-31T23:33:30+02:00", buildRevision: "8c88ce6f651c3df1cd97925e5b8e3281ca4b1665" };
+9 -6
View File
@@ -9,12 +9,14 @@ const Branch = require('../entities/branch');
const TaskContext = require("./task_context.js");
const utils = require('./utils');
async function cloneNoteToParent(noteId, parentNoteId, prefix) {
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) {
async function cloneNoteToParent(noteId, parentBranchId, prefix) {
const parentBranch = await repository.getBranch(parentBranchId);
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentBranch.noteId)) {
return { success: false, message: 'Note is deleted.' };
}
const validationResult = await treeService.validateParentChild(parentNoteId, noteId);
const validationResult = await treeService.validateParentChild(parentBranch.noteId, noteId);
if (!validationResult.success) {
return validationResult;
@@ -22,12 +24,13 @@ async function cloneNoteToParent(noteId, parentNoteId, prefix) {
const branch = await new Branch({
noteId: noteId,
parentNoteId: parentNoteId,
parentNoteId: parentBranch.noteId,
prefix: prefix,
isExpanded: 0
}).save();
await sql.execute("UPDATE branches SET isExpanded = 1 WHERE noteId = ?", [parentNoteId]);
parentBranch.isExpanded = true; // the new target should be expanded so it immediately shows up to the user
await parentBranch.save();
return { success: true, branchId: branch.branchId };
}
@@ -111,4 +114,4 @@ module.exports = {
ensureNoteIsAbsentFromParent,
toggleNoteInParent,
cloneNoteAfter
};
};
+7 -1
View File
@@ -193,7 +193,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
{
separator: "Tabs"
separator: "Tabs & Windows"
},
{
actionName: "openNewTab",
@@ -219,6 +219,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
description: "Activates tab on the left",
scope: "window"
},
{
actionName: "openNewWindow",
defaultShortcuts: [],
description: "Open new empty window",
scope: "window"
},
{
+29 -23
View File
@@ -13,6 +13,7 @@ const Attribute = require('../entities/attribute');
const hoistedNoteService = require('../services/hoisted_note');
const protectedSessionService = require('../services/protected_session');
const log = require('../services/log');
const utils = require('../services/utils');
const noteRevisionService = require('../services/note_revisions');
const attributeService = require('../services/attributes');
const request = require('./request');
@@ -276,9 +277,9 @@ async function downloadImage(noteId, imageUrl) {
const downloadImagePromises = {};
function replaceUrl(content, url, imageNote) {
const quoted = url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
const quotedUrl = utils.quoteRegex(url);
return content.replace(new RegExp(`\\s+src=[\"']${quoted}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
}
async function downloadImages(noteId, content) {
@@ -293,15 +294,11 @@ async function downloadImages(noteId, content) {
if (!url.includes('api/images/')
// this is and exception for the web clipper's "imageId"
&& (url.length !== 20 || url.toLowerCase().startsWith('http'))) {
if (url in downloadImagePromises) {
// download is already in progress
continue;
}
if (url in imageUrlToNoteIdMapping) {
const imageNote = await repository.getNote(imageUrlToNoteIdMapping[url]);
if (imageNote || imageNote.isDeleted) {
if (!imageNote || imageNote.isDeleted) {
delete imageUrlToNoteIdMapping[url];
}
else {
@@ -322,6 +319,11 @@ async function downloadImages(noteId, content) {
continue;
}
if (url in downloadImagePromises) {
// download is already in progress
continue;
}
// this is done asynchronously, it would be too slow to wait for the download
// given that save can be triggered very often
downloadImagePromises[url] = downloadImage(noteId, url);
@@ -338,28 +340,30 @@ async function downloadImages(noteId, content) {
// are downloaded and the IMG references are not updated. For this occassion we have this code
// which upon the download of all the images will update the note if the links have not been fixed before
const imageNotes = await repository.getNotes(Object.values(imageUrlToNoteIdMapping));
await sql.transactional(async () => {
const imageNotes = await repository.getNotes(Object.values(imageUrlToNoteIdMapping));
const origNote = await repository.getNote(noteId);
const origContent = await origNote.getContent();
let updatedContent = origContent;
const origNote = await repository.getNote(noteId);
const origContent = await origNote.getContent();
let updatedContent = origContent;
for (const url in imageUrlToNoteIdMapping) {
const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]);
for (const url in imageUrlToNoteIdMapping) {
const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]);
if (imageNote && !imageNote.isDeleted) {
updatedContent = replaceUrl(updatedContent, url, imageNote);
if (imageNote && !imageNote.isDeleted) {
updatedContent = replaceUrl(updatedContent, url, imageNote);
}
}
}
// update only if the links have not been already fixed.
if (updatedContent !== origContent) {
await origNote.setContent(updatedContent);
// update only if the links have not been already fixed.
if (updatedContent !== origContent) {
await origNote.setContent(updatedContent);
await scanForLinks(origNote);
await scanForLinks(origNote);
console.log(`Fixed the image links for note ${noteId} to the offline saved.`);
}
console.log(`Fixed the image links for note ${noteId} to the offline saved.`);
}
});
}, 5000);
});
@@ -658,7 +662,9 @@ async function scanForLinks(note) {
const content = await note.getContent();
const newContent = await saveLinks(note, content);
await note.setContent(newContent);
if (content !== newContent) {
await note.setContent(newContent);
}
}
catch (e) {
log.error(`Could not scan for links note ${note.noteId}: ${e.message} ${e.stack}`);
+1 -1
View File
@@ -1,6 +1,6 @@
const dayjs = require("dayjs");
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*([^\s=*]+|"[^"]+"))?/igu;
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*([^\s=*"]+|"[^"]+"))?/igu;
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
function calculateSmartValue(v) {
+7 -1
View File
@@ -153,6 +153,10 @@ async function execute(query, params = []) {
return await wrap(async db => db.run(query, ...params), query);
}
async function executeNoWrap(query, params = []) {
await dbConnection.run(query, ...params);
}
async function executeMany(query, params) {
// essentially just alias
await getManyRows(query, params);
@@ -221,6 +225,7 @@ async function transactional(func) {
await commit();
// note that sync rows sent from this action will be sent again by scheduled periodic ping
require('./ws.js').sendPingToAllClients();
transactionActive = false;
@@ -263,8 +268,9 @@ module.exports = {
getMap,
getColumn,
execute,
executeNoWrap,
executeMany,
executeScript,
transactional,
upsert
};
};
+44 -5
View File
@@ -5,6 +5,7 @@ const randtoken = require('rand-token').generator({source: 'crypto'});
const unescape = require('unescape');
const escape = require('escape-html');
const sanitize = require("sanitize-filename");
const mimeTypes = require('mime-types');
function newEntityId() {
return randomString(12);
@@ -166,10 +167,46 @@ function isStringNote(type, mime) {
|| STRING_MIME_TYPES.includes(mime);
}
function replaceAll(string, replaceWhat, replaceWith) {
const escapedWhat = replaceWhat.replace(/([\/,!\\^${}\[\]().*+?|<>\-&])/g, "\\$&");
function quoteRegex(url) {
return url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}
return string.replace(new RegExp(escapedWhat, "g"), replaceWith);
function replaceAll(string, replaceWhat, replaceWith) {
const quotedReplaceWhat = quoteRegex(replaceWhat);
return string.replace(new RegExp(quotedReplaceWhat, "g"), replaceWith);
}
function formatDownloadTitle(filename, type, mime) {
if (!filename) {
filename = "untitled";
}
if (type === 'text') {
return filename + '.html';
} else if (['relation-map', 'search'].includes(type)) {
return filename + '.json';
} else {
if (!mime) {
return filename;
}
mime = mime.toLowerCase();
const filenameLc = filename.toLowerCase();
const extensions = mimeTypes.extensions[mime];
if (!extensions || extensions.length === 0) {
return filename;
}
for (const ext of extensions) {
if (filenameLc.endsWith('.' + ext)) {
return filename;
}
}
return filename + '.' + extensions[0];
}
}
module.exports = {
@@ -198,5 +235,7 @@ module.exports = {
sanitizeFilenameForHeader,
getContentDisposition,
isStringNote,
replaceAll
};
quoteRegex,
replaceAll,
formatDownloadTitle
};
-1
View File
@@ -165,6 +165,5 @@ module.exports = {
createMainWindow,
createSetupWindow,
closeSetupWindow,
createExtraWindow,
registerGlobalShortcuts
};
+1 -5
View File
@@ -5,7 +5,7 @@
<link rel="shortcut icon" href="favicon.ico">
<title>Trilium Notes</title>
</head>
<body class="desktop theme-<%= theme %>" style="display: none; --main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
<body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
@@ -83,9 +83,5 @@
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
<script>
$("body").show();
</script>
</body>
</html>
+1 -1
View File
@@ -210,7 +210,7 @@
<script src="libraries/knockout.min.js"></script>
<script src="app-dist/setup.js" crossorigin type="module"></script>
<script src="app/setup.js" crossorigin type="module"></script>
<link href="stylesheets/themes.css" rel="stylesheet">
</body>
</html>