mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Compare commits
	
		
			34 Commits
		
	
	
		
			v0.42.0-be
			...
			v0.42.3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2526715aa4 | ||
|  | 04c573e212 | ||
|  | 58f4f5d1e6 | ||
|  | fa5d982a55 | ||
|  | 108afe8896 | ||
|  | 37da0adb8a | ||
|  | 4f50864ec8 | ||
|  | 30b9ef8604 | ||
|  | b063b4c528 | ||
|  | e08b0141a4 | ||
|  | 9d8b8e26a1 | ||
|  | cb70109ee7 | ||
|  | e541abbd60 | ||
|  | 940a70adc5 | ||
|  | 88e8eb7e9c | ||
|  | b365c186a1 | ||
|  | 64c9734f05 | ||
|  | 48c843c087 | ||
|  | 0e4eec10b9 | ||
|  | a3661cb763 | ||
|  | 115879ec4a | ||
|  | df11b076bc | ||
|  | 54ecd2ee75 | ||
|  | 2369bcf9fc | ||
|  | 5d8808a2ad | ||
|  | 62b993f06f | ||
|  | 8aa5608085 | ||
|  | b452d7e5c5 | ||
|  | 9b9d6d86d0 | ||
|  | 7f2755d4a0 | ||
|  | 3b268cc8eb | ||
|  | 6dfe335707 | ||
|  | c7125d2b50 | ||
|  | e8a33a5ee7 | 
							
								
								
									
										16
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
								
							| @@ -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> | ||||
| @@ -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>""</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>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="31" parent="7" name="position"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="32" parent="7" name="utcDateCreated"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="33" parent="7" name="utcDateModified"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="34" parent="7" name="isDeleted"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="35" parent="7" name="deleteId"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <DefaultExpression>NULL</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="36" parent="7" name="hash"> | ||||
|       <Position>11</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="37" parent="7" name="isInheritable"> | ||||
|       <Position>12</Position> | ||||
|       <DataType>int|0s</DataType> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="38" parent="7" name="sqlite_autoindex_attributes_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>attributeId</ColNames> | ||||
|       <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>""</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>""</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>''</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>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="82" parent="11" name="mime"> | ||||
|       <Position>13</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="83" parent="11" name="hash"> | ||||
|       <Position>14</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="84" parent="11" name="sqlite_autoindex_note_revisions_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <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>"note"</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="93" parent="12" name="contentLength"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="94" parent="12" name="isProtected"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="95" parent="12" name="type"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="96" parent="12" name="mime"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text/html'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="97" parent="12" name="hash"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="98" parent="12" name="isDeleted"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="99" parent="12" name="deleteId"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <DefaultExpression>NULL</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="100" parent="12" name="isErased"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="101" parent="12" name="dateCreated"> | ||||
|       <Position>11</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="102" parent="12" name="dateModified"> | ||||
|       <Position>12</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="103" parent="12" name="utcDateCreated"> | ||||
|       <Position>13</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="104" parent="12" name="utcDateModified"> | ||||
|       <Position>14</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="105" parent="12" name="sqlite_autoindex_notes_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <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>""</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>""</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,4 +1,4 @@ | ||||
| FROM node:12.16.2-alpine | ||||
| FROM node:12.16.3-alpine | ||||
|  | ||||
| # Create app directory | ||||
| WORKDIR /usr/src/app | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
							
								
								
									
										254
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										254
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -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" | ||||
|       } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.42.0-beta", | ||||
|   "version": "0.42.3", | ||||
|   "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": { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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()) { | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|     }) | ||||
| }); | ||||
| }); | ||||
|   | ||||
| @@ -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('   ') | ||||
|         .append($eraseRevisionButton) | ||||
|         .append('   '); | ||||
|  | ||||
|   | ||||
| @@ -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()) | ||||
|             ); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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"); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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,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; | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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'); | ||||
|     } | ||||
| } | ||||
| @@ -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}; | ||||
|   | ||||
| @@ -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); | ||||
| @@ -330,7 +332,7 @@ export default class TabManager extends Component { | ||||
|  | ||||
|         this.removeTab(tabId); | ||||
|  | ||||
|         appContext.openInNewWindow(notePath); | ||||
|         this.triggerCommand('openInWindow', {notePath}); | ||||
|     } | ||||
|  | ||||
|     async hoistedNoteChangedEvent({hoistedNoteId}) { | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -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}); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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,7 @@ async function consumeSyncData() { | ||||
|             utils.reloadApp(); | ||||
|         } | ||||
|  | ||||
|         lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncData[allSyncData.length - 1].id); | ||||
|         lastProcessedSyncId = Math.max(lastProcessedSyncId, allSyncRows[allSyncRows.length - 1].id); | ||||
|     } | ||||
|  | ||||
|     checkSyncIdListeners(); | ||||
| @@ -170,7 +187,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 +229,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 +248,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 +303,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 +315,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 +354,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 +391,4 @@ export default { | ||||
|     subscribeToMessages, | ||||
|     waitForSyncId, | ||||
|     waitForMaxKnownSyncId | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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> | ||||
| `; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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, | ||||
| @@ -804,7 +804,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 +879,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 +906,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 +928,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 +1008,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 +1033,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); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     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); | ||||
|         } | ||||
|     } | ||||
| @@ -1178,4 +1193,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); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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(() => { | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -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,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 | ||||
| }; | ||||
|   | ||||
| @@ -145,6 +145,7 @@ 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); | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|   | ||||
| @@ -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' }, | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2020-05-04T21:59:14+02:00", buildRevision: "cafcb67a8a3a1943acac829590b34ff729b57e09" }; | ||||
| module.exports = { buildDate:"2020-05-20T08:54:55+02:00", buildRevision: "04c573e212db06e1dd60c74707e40f6912c85aab" }; | ||||
|   | ||||
| @@ -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" | ||||
|     }, | ||||
|  | ||||
|  | ||||
|     { | ||||
|   | ||||
| @@ -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); | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -221,6 +221,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; | ||||
| @@ -267,4 +268,4 @@ module.exports = { | ||||
|     executeScript, | ||||
|     transactional, | ||||
|     upsert | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -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 | ||||
| }; | ||||
|   | ||||
| @@ -165,6 +165,5 @@ module.exports = { | ||||
|     createMainWindow, | ||||
|     createSetupWindow, | ||||
|     closeSetupWindow, | ||||
|     createExtraWindow, | ||||
|     registerGlobalShortcuts | ||||
| }; | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user