mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					37da0adb8a | ||
| 
						 | 
					4f50864ec8 | ||
| 
						 | 
					30b9ef8604 | ||
| 
						 | 
					b063b4c528 | ||
| 
						 | 
					e08b0141a4 | ||
| 
						 | 
					9d8b8e26a1 | ||
| 
						 | 
					cb70109ee7 | ||
| 
						 | 
					e541abbd60 | ||
| 
						 | 
					940a70adc5 | ||
| 
						 | 
					88e8eb7e9c | ||
| 
						 | 
					b365c186a1 | ||
| 
						 | 
					64c9734f05 | ||
| 
						 | 
					48c843c087 | ||
| 
						 | 
					0e4eec10b9 | ||
| 
						 | 
					a3661cb763 | ||
| 
						 | 
					115879ec4a | 
@@ -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
 | 
			
		||||
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "trilium",
 | 
			
		||||
  "version": "0.42.0-beta",
 | 
			
		||||
  "version": "0.42.1",
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
@@ -3345,9 +3345,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "electron": {
 | 
			
		||||
      "version": "9.0.0-beta.22",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.22.tgz",
 | 
			
		||||
      "integrity": "sha512-dfqAf+CXXTKcNDj7DU7mYsmx+oZQcXOvJnZ8ZsgAHjrE9Tv8zsYUgCP3JlO4Z8CIazgleKXYmgh6H2stdK7fEA==",
 | 
			
		||||
      "version": "9.0.0-beta.24",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.24.tgz",
 | 
			
		||||
      "integrity": "sha512-25L3XMqm/1CCaV5CgU5ZkhKXw9830WeipJrTW0+VC5XTKp/3xHwhxyQ5G1kQnOTJd7IGwOamvw237D6e1YKnng==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@electron/get": "^1.0.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  "name": "trilium",
 | 
			
		||||
  "productName": "Trilium Notes",
 | 
			
		||||
  "description": "Trilium Notes",
 | 
			
		||||
  "version": "0.42.1",
 | 
			
		||||
  "version": "0.42.2",
 | 
			
		||||
  "license": "AGPL-3.0-only",
 | 
			
		||||
  "main": "electron.js",
 | 
			
		||||
  "bin": {
 | 
			
		||||
@@ -78,7 +78,7 @@
 | 
			
		||||
    "yazl": "^2.5.1"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "electron": "9.0.0-beta.22",
 | 
			
		||||
    "electron": "9.0.0-beta.24",
 | 
			
		||||
    "electron-builder": "22.6.0",
 | 
			
		||||
    "electron-packager": "14.2.1",
 | 
			
		||||
    "electron-rebuild": "1.10.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -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";
 | 
			
		||||
@@ -73,7 +73,7 @@ class AppContext extends Component {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (utils.isElectron()) {
 | 
			
		||||
            this.child(new ZoomService());
 | 
			
		||||
            this.child(zoomService);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.triggerEvent('initialRenderComplete');
 | 
			
		||||
@@ -134,4 +134,4 @@ $(window).on('hashchange', function() {
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default appContext;
 | 
			
		||||
export default appContext;
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +118,7 @@ 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 tab", command: "openNoteInNewTab", uiIcon: "empty"},
 | 
			
		||||
            {title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "window-open"}
 | 
			
		||||
        ],
 | 
			
		||||
        selectMenuItemHandler: ({command}) => {
 | 
			
		||||
@@ -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
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,10 @@ 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 receivedSyncIds = new Set();
 | 
			
		||||
 | 
			
		||||
async function handleMessage(event) {
 | 
			
		||||
    const message = JSON.parse(event.data);
 | 
			
		||||
 | 
			
		||||
@@ -52,14 +56,19 @@ async function handleMessage(event) {
 | 
			
		||||
 | 
			
		||||
        if (syncRows.length > 0) {
 | 
			
		||||
            const filteredRows = syncRows.filter(row =>
 | 
			
		||||
                row.entityName !== 'recent_notes'
 | 
			
		||||
                !receivedSyncIds.has(row.id)
 | 
			
		||||
                && row.entityName !== 'recent_notes'
 | 
			
		||||
                && (row.entityName !== 'options' || row.entityId !== 'openTabs'));
 | 
			
		||||
 | 
			
		||||
            if (filteredRows.length > 0) {
 | 
			
		||||
                console.debug(utils.now(), "Sync data: ", filteredRows);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            syncDataQueue.push(...syncRows);
 | 
			
		||||
            for (const row of filteredRows) {
 | 
			
		||||
                receivedSyncIds.add(row.id);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            syncDataQueue.push(...filteredRows);
 | 
			
		||||
 | 
			
		||||
            // we set lastAcceptedSyncId even before sync processing and send ping so that backend can start sending more updates
 | 
			
		||||
            lastAcceptedSyncId = Math.max(lastAcceptedSyncId, syncRows[syncRows.length - 1].id);
 | 
			
		||||
@@ -170,7 +179,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) {
 | 
			
		||||
@@ -374,4 +383,4 @@ export default {
 | 
			
		||||
    subscribeToMessages,
 | 
			
		||||
    waitForSyncId,
 | 
			
		||||
    waitForMaxKnownSyncId
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,31 +5,33 @@ 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'));
 | 
			
		||||
        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 +47,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,15 +22,15 @@ const TPL = `
 | 
			
		||||
 | 
			
		||||
    <tr>
 | 
			
		||||
        <th>Note ID:</th>
 | 
			
		||||
        <td nowrap colspan="3" class="note-info-note-id"></td>
 | 
			
		||||
        <td colspan="3" class="note-info-note-id"></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <th>Created:</th>
 | 
			
		||||
        <td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-created"></td>
 | 
			
		||||
        <td colspan="3" class="note-info-date-created"></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <th>Modified:</th>
 | 
			
		||||
        <td nowrap colspan="3" style="overflow: hidden; text-overflow: ellipsis;" class="note-info-date-modified"></td>
 | 
			
		||||
        <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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
@@ -926,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() {
 | 
			
		||||
@@ -994,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 => {
 | 
			
		||||
@@ -1019,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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1179,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -602,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));
 | 
			
		||||
@@ -636,4 +641,4 @@ export default class TabRowWidget extends BasicWidget {
 | 
			
		||||
            this.updateTab($tab, tabContext.note);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -199,4 +199,4 @@ module.exports = {
 | 
			
		||||
    getEffectiveNoteAttributes,
 | 
			
		||||
    createRelation,
 | 
			
		||||
    deleteRelation
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
module.exports = { buildDate:"2020-05-06T23:24:13+02:00", buildRevision: "54ecd2ee75d1177cedadf9fee10319687feee5f0" };
 | 
			
		||||
module.exports = { buildDate:"2020-05-12T16:46:45+02:00", buildRevision: "4f50864ec8346a12d7845cb4c91a3de3b1043d34" };
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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