mirror of
https://github.com/zadam/trilium.git
synced 2025-10-28 08:46:43 +01:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5769587305 | ||
|
|
ffbfccb701 | ||
|
|
907cdd8fcb | ||
|
|
7ea53d468e | ||
|
|
586d6b4557 | ||
|
|
8f68ff1932 | ||
|
|
a1ea2c9115 | ||
|
|
e8ce81a133 | ||
|
|
aff12950f0 | ||
|
|
75c58cbf79 | ||
|
|
87a1e98fa2 | ||
|
|
d1eacbb574 | ||
|
|
71d248cd87 | ||
|
|
ac608b9334 | ||
|
|
32020d78b5 | ||
|
|
ff853c7d0a | ||
|
|
8526cb2315 | ||
|
|
dc89f72e75 | ||
|
|
657ff16267 | ||
|
|
ed759f5585 |
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<dataSource name="document.db">
|
<dataSource name="document.db">
|
||||||
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.17">
|
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.18">
|
||||||
<root id="1">
|
<root id="1">
|
||||||
<ServerVersion>3.25.1</ServerVersion>
|
<ServerVersion>3.16.1</ServerVersion>
|
||||||
</root>
|
</root>
|
||||||
<schema id="2" parent="1" name="main">
|
<schema id="2" parent="1" name="main">
|
||||||
<Current>1</Current>
|
<Current>1</Current>
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>apiTokenId</ColNames>
|
<ColNames>apiTokenId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="25" parent="6">
|
<key id="25" parent="6">
|
||||||
@@ -130,17 +131,21 @@
|
|||||||
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>attributeId</ColNames>
|
<ColNames>attributeId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="40" parent="7" name="IDX_attributes_name_value">
|
<index id="40" parent="7" name="IDX_attributes_name_value">
|
||||||
<ColNames>name
|
<ColNames>name
|
||||||
value</ColNames>
|
value</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="41" parent="7" name="IDX_attributes_value_index">
|
<index id="41" parent="7" name="IDX_attributes_value_index">
|
||||||
<ColNames>value</ColNames>
|
<ColNames>value</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="42" parent="7">
|
<key id="42" parent="7">
|
||||||
<ColNames>attributeId</ColNames>
|
<ColNames>attributeId</ColNames>
|
||||||
@@ -207,14 +212,17 @@ value</ColNames>
|
|||||||
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
||||||
<ColNames>noteId
|
<ColNames>noteId
|
||||||
parentNoteId</ColNames>
|
parentNoteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
||||||
<ColNames>parentNoteId</ColNames>
|
<ColNames>parentNoteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="57" parent="8">
|
<key id="57" parent="8">
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
@@ -245,6 +253,7 @@ parentNoteId</ColNames>
|
|||||||
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="63" parent="9">
|
<key id="63" parent="9">
|
||||||
@@ -275,6 +284,7 @@ parentNoteId</ColNames>
|
|||||||
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="69" parent="10">
|
<key id="69" parent="10">
|
||||||
@@ -359,22 +369,28 @@ parentNoteId</ColNames>
|
|||||||
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
||||||
<ColNames>utcDateLastEdited</ColNames>
|
<ColNames>utcDateLastEdited</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
||||||
<ColNames>utcDateCreated</ColNames>
|
<ColNames>utcDateCreated</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
||||||
<ColNames>dateLastEdited</ColNames>
|
<ColNames>dateLastEdited</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
||||||
<ColNames>dateCreated</ColNames>
|
<ColNames>dateCreated</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="90" parent="11">
|
<key id="90" parent="11">
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
@@ -461,28 +477,36 @@ parentNoteId</ColNames>
|
|||||||
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="106" parent="12" name="IDX_notes_title">
|
<index id="106" parent="12" name="IDX_notes_title">
|
||||||
<ColNames>title</ColNames>
|
<ColNames>title</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="107" parent="12" name="IDX_notes_type">
|
<index id="107" parent="12" name="IDX_notes_type">
|
||||||
<ColNames>type</ColNames>
|
<ColNames>type</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
||||||
<ColNames>isDeleted</ColNames>
|
<ColNames>isDeleted</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
||||||
<ColNames>dateCreated</ColNames>
|
<ColNames>dateCreated</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="110" parent="12" name="IDX_notes_dateModified">
|
<index id="110" parent="12" name="IDX_notes_dateModified">
|
||||||
<ColNames>dateModified</ColNames>
|
<ColNames>dateModified</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
||||||
<ColNames>utcDateCreated</ColNames>
|
<ColNames>utcDateCreated</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
||||||
<ColNames>utcDateModified</ColNames>
|
<ColNames>utcDateModified</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="113" parent="12">
|
<key id="113" parent="12">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
@@ -523,6 +547,7 @@ parentNoteId</ColNames>
|
|||||||
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>name</ColNames>
|
<ColNames>name</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="121" parent="13">
|
<key id="121" parent="13">
|
||||||
@@ -558,6 +583,7 @@ parentNoteId</ColNames>
|
|||||||
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="128" parent="14">
|
<key id="128" parent="14">
|
||||||
@@ -578,10 +604,12 @@ parentNoteId</ColNames>
|
|||||||
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
||||||
<ColNames>utcDateCreated</ColNames>
|
<ColNames>utcDateCreated</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="133" parent="15">
|
<key id="133" parent="15">
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
@@ -602,7 +630,7 @@ parentNoteId</ColNames>
|
|||||||
</column>
|
</column>
|
||||||
<column id="137" parent="16" name="rootpage">
|
<column id="137" parent="16" name="rootpage">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>int|0s</DataType>
|
<DataType>integer|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="138" parent="16" name="sql">
|
<column id="138" parent="16" name="sql">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
@@ -649,10 +677,12 @@ parentNoteId</ColNames>
|
|||||||
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
||||||
<ColNames>entityName
|
<ColNames>entityName
|
||||||
entityId</ColNames>
|
entityId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
||||||
<ColNames>utcSyncDate</ColNames>
|
<ColNames>utcSyncDate</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="149" parent="18">
|
<key id="149" parent="18">
|
||||||
<ColNames>id</ColNames>
|
<ColNames>id</ColNames>
|
||||||
|
|||||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -5,6 +5,8 @@ if [[ $# -eq 0 ]] ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
npm run webpack
|
||||||
|
|
||||||
DIR=$1
|
DIR=$1
|
||||||
|
|
||||||
rm -rf $DIR
|
rm -rf $DIR
|
||||||
@@ -25,7 +27,7 @@ cp -r electron.js $DIR/
|
|||||||
cp webpack-* $DIR/
|
cp webpack-* $DIR/
|
||||||
|
|
||||||
# run in subshell (so we return to original dir)
|
# run in subshell (so we return to original dir)
|
||||||
(cd $DIR && npm install --only=prod && npm run webpack)
|
(cd $DIR && npm install --only=prod)
|
||||||
|
|
||||||
find $DIR/libraries -name "*.map" -type f -delete
|
find $DIR/libraries -name "*.map" -type f -delete
|
||||||
|
|
||||||
|
|||||||
@@ -879,7 +879,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -5483,7 +5483,7 @@ Typical use case is when new note has been created, we should wait until it is s
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -507,7 +507,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -6813,7 +6813,7 @@ Cache is note instance scoped.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default Attribute;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default Branch;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default NoteComplement;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ export default NoteShort;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -95,6 +95,275 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="subsection-title">Methods</h3>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="decorateWidget"><span class="type-signature"></span>decorateWidget<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
for overriding
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line93">line 93</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="doRenderBody"><span class="type-signature">(async) </span>doRenderBody<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
for overriding
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line96">line 96</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h4 class="name" id="widgetCollapsedStateChangedEvent"><span class="type-signature"></span>widgetCollapsedStateChangedEvent<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
|
||||||
|
separately but should behave uniformly for the user.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dl class="details">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<dt class="tag-source">Source:</dt>
|
||||||
|
<dd class="tag-source"><ul class="dummy"><li>
|
||||||
|
<a href="widgets_collapsible_widget.js.html">widgets/collapsible_widget.js</a>, <a href="widgets_collapsible_widget.js.html#line86">line 86</a>
|
||||||
|
</li></ul></dd>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -333,7 +602,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ export default FrontendScriptApi;</code></pre>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3><a href="global.html">Global</a></h3>
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<br class="clear">
|
<br class="clear">
|
||||||
|
|||||||
151
docs/frontend_api/widgets_collapsible_widget.js.html
Normal file
151
docs/frontend_api/widgets_collapsible_widget.js.html
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>JSDoc: Source: widgets/collapsible_widget.js</title>
|
||||||
|
|
||||||
|
<script src="scripts/prettify/prettify.js"> </script>
|
||||||
|
<script src="scripts/prettify/lang-css.js"> </script>
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
|
||||||
|
<h1 class="page-title">Source: widgets/collapsible_widget.js</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<article>
|
||||||
|
<pre class="prettyprint source linenums"><code>import TabAwareWidget from "./tab_aware_widget.js";
|
||||||
|
import options from "../services/options.js";
|
||||||
|
|
||||||
|
const WIDGET_TPL = `
|
||||||
|
<div class="card widget">
|
||||||
|
<div class="card-header">
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-sm widget-title" data-toggle="collapse" data-target="#[to be set]">
|
||||||
|
Collapsible Group Item
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a class="widget-help external no-arrow bx bx-info-circle"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="widget-header-actions"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="[to be set]" class="collapse body-wrapper" style="transition: none; ">
|
||||||
|
<div class="card-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class CollapsibleWidget extends TabAwareWidget {
|
||||||
|
get widgetTitle() { return "Untitled widget"; }
|
||||||
|
|
||||||
|
get headerActions() { return []; }
|
||||||
|
|
||||||
|
get help() { return {}; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(WIDGET_TPL);
|
||||||
|
this.$widget.find('[data-target]').attr('data-target', "#" + this.componentId);
|
||||||
|
|
||||||
|
this.$bodyWrapper = this.$widget.find('.body-wrapper');
|
||||||
|
this.$bodyWrapper.attr('id', this.componentId); // for toggle to work we need id
|
||||||
|
|
||||||
|
this.widgetName = this.constructor.name;
|
||||||
|
|
||||||
|
if (!options.is(this.widgetName + 'Collapsed')) {
|
||||||
|
this.$bodyWrapper.collapse("show");
|
||||||
|
}
|
||||||
|
|
||||||
|
// using immediate variants of the event so that the previous collapse is not caught
|
||||||
|
this.$bodyWrapper.on('hide.bs.collapse', () => this.saveCollapsed(true));
|
||||||
|
this.$bodyWrapper.on('show.bs.collapse', () => this.saveCollapsed(false));
|
||||||
|
|
||||||
|
this.$body = this.$bodyWrapper.find('.card-body');
|
||||||
|
|
||||||
|
this.$title = this.$widget.find('.widget-title');
|
||||||
|
this.$title.text(this.widgetTitle);
|
||||||
|
|
||||||
|
this.$help = this.$widget.find('.widget-help');
|
||||||
|
|
||||||
|
if (this.help.title) {
|
||||||
|
this.$help.attr("title", this.help.title);
|
||||||
|
this.$help.attr("href", this.help.url || "javascript:");
|
||||||
|
|
||||||
|
if (!this.help.url) {
|
||||||
|
this.$help.addClass('no-link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$help.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$headerActions = this.$widget.find('.widget-header-actions');
|
||||||
|
this.$headerActions.append(...this.headerActions);
|
||||||
|
|
||||||
|
this.initialized = this.doRenderBody();
|
||||||
|
|
||||||
|
this.decorateWidget();
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveCollapsed(collapse) {
|
||||||
|
options.save(this.widgetName + 'Collapsed', collapse.toString());
|
||||||
|
|
||||||
|
this.triggerEvent(`widgetCollapsedStateChanged`, {widgetName: this.widgetName, collapse});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
|
||||||
|
* separately but should behave uniformly for the user.
|
||||||
|
*/
|
||||||
|
widgetCollapsedStateChangedEvent({widgetName, collapse}) {
|
||||||
|
if (widgetName === this.widgetName) {
|
||||||
|
this.$bodyWrapper.toggleClass('show', !collapse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** for overriding */
|
||||||
|
decorateWidget() {}
|
||||||
|
|
||||||
|
/** for overriding */
|
||||||
|
async doRenderBody() {}
|
||||||
|
|
||||||
|
isExpanded() {
|
||||||
|
return this.$bodyWrapper.hasClass("show");
|
||||||
|
}
|
||||||
|
}</code></pre>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Branch.html">Branch</a></li><li><a href="FrontendScriptApi.html">FrontendScriptApi</a></li><li><a href="NoteComplement.html">NoteComplement</a></li><li><a href="NoteShort.html">NoteShort</a></li></ul><h3>Global</h3><ul><li><a href="global.html#decorateWidget">decorateWidget</a></li><li><a href="global.html#doRenderBody">doRenderBody</a></li><li><a href="global.html#widgetCollapsedStateChangedEvent">widgetCollapsedStateChangedEvent</a></li></ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<br class="clear">
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.4</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script> prettyPrint(); </script>
|
||||||
|
<script src="scripts/linenumber.js"> </script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
532
package-lock.json
generated
532
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -16,7 +16,7 @@
|
|||||||
"start-server": "TRILIUM_ENV=dev node ./src/www",
|
"start-server": "TRILIUM_ENV=dev node ./src/www",
|
||||||
"start-electron": "TRILIUM_ENV=dev electron .",
|
"start-electron": "TRILIUM_ENV=dev electron .",
|
||||||
"build-backend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
|
"build-backend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
|
||||||
"build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js",
|
"build-frontend-docs": "./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/collapsible_widget.js",
|
||||||
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
|
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
|
||||||
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js"
|
"webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js"
|
||||||
},
|
},
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"commonmark": "0.29.1",
|
"commonmark": "0.29.1",
|
||||||
"cookie-parser": "1.4.5",
|
"cookie-parser": "1.4.5",
|
||||||
"csurf": "1.11.0",
|
"csurf": "1.11.0",
|
||||||
"dayjs": "1.8.24",
|
"dayjs": "1.8.25",
|
||||||
"debug": "4.1.1",
|
"debug": "4.1.1",
|
||||||
"ejs": "3.0.2",
|
"ejs": "3.1.2",
|
||||||
"electron-debug": "3.0.1",
|
"electron-debug": "3.0.1",
|
||||||
"electron-dl": "3.0.0",
|
"electron-dl": "3.0.0",
|
||||||
"electron-find": "1.0.6",
|
"electron-find": "1.0.6",
|
||||||
"electron-window-state": "5.0.3",
|
"electron-window-state": "5.0.3",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.1",
|
||||||
"file-type": "14.1.4",
|
"file-type": "14.2.0",
|
||||||
"fs-extra": "9.0.0",
|
"fs-extra": "9.0.0",
|
||||||
"helmet": "3.22.0",
|
"helmet": "3.22.0",
|
||||||
"html": "1.0.0",
|
"html": "1.0.0",
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"imagemin-pngquant": "8.0.0",
|
"imagemin-pngquant": "8.0.0",
|
||||||
"ini": "1.3.5",
|
"ini": "1.3.5",
|
||||||
"is-svg": "4.2.1",
|
"is-svg": "4.2.1",
|
||||||
"jimp": "0.10.2",
|
"jimp": "0.10.3",
|
||||||
"mime-types": "2.1.26",
|
"mime-types": "2.1.27",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"node-abi": "2.15.0",
|
"node-abi": "2.16.0",
|
||||||
"open": "7.0.3",
|
"open": "7.0.3",
|
||||||
"portscanner": "2.2.0",
|
"portscanner": "2.2.0",
|
||||||
"rand-token": "1.0.1",
|
"rand-token": "1.0.1",
|
||||||
@@ -73,18 +73,18 @@
|
|||||||
"turndown": "6.0.0",
|
"turndown": "6.0.0",
|
||||||
"turndown-plugin-gfm": "1.0.2",
|
"turndown-plugin-gfm": "1.0.2",
|
||||||
"unescape": "1.0.1",
|
"unescape": "1.0.1",
|
||||||
"ws": "7.2.3",
|
"ws": "7.2.5",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0",
|
||||||
"yazl": "^2.5.1"
|
"yazl": "^2.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "9.0.0-beta.18",
|
"electron": "9.0.0-beta.20",
|
||||||
"electron-builder": "22.5.1",
|
"electron-builder": "22.5.1",
|
||||||
"electron-packager": "14.2.1",
|
"electron-packager": "14.2.1",
|
||||||
"electron-rebuild": "1.10.1",
|
"electron-rebuild": "1.10.1",
|
||||||
"jsdoc": "3.6.4",
|
"jsdoc": "3.6.4",
|
||||||
"lorem-ipsum": "2.0.3",
|
"lorem-ipsum": "2.0.3",
|
||||||
"webpack": "5.0.0-beta.14",
|
"webpack": "5.0.0-beta.15",
|
||||||
"webpack-cli": "4.0.0-beta.8"
|
"webpack-cli": "4.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import bundleService from "./services/bundle.js";
|
|||||||
import noteAutocompleteService from './services/note_autocomplete.js';
|
import noteAutocompleteService from './services/note_autocomplete.js';
|
||||||
import macInit from './services/mac_init.js';
|
import macInit from './services/mac_init.js';
|
||||||
import contextMenu from "./services/context_menu.js";
|
import contextMenu from "./services/context_menu.js";
|
||||||
import DesktopLayout from "./widgets/desktop_layout.js";
|
import DesktopMainWindowLayout from "./layouts/desktop_main_window_layout.js";
|
||||||
import glob from "./services/glob.js";
|
import glob from "./services/glob.js";
|
||||||
|
import DesktopExtraWindowLayout from "./layouts/desktop_extra_window_layout.js";
|
||||||
|
|
||||||
glob.setupGlobs();
|
glob.setupGlobs();
|
||||||
|
|
||||||
@@ -23,9 +24,11 @@ $('[data-toggle="tooltip"]').tooltip({
|
|||||||
macInit.init();
|
macInit.init();
|
||||||
|
|
||||||
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
||||||
const desktopLayout = new DesktopLayout(widgetBundles);
|
const layout = window.glob.isMainWindow
|
||||||
|
? new DesktopMainWindowLayout(widgetBundles)
|
||||||
|
: new DesktopExtraWindowLayout(widgetBundles);
|
||||||
|
|
||||||
appContext.setLayout(desktopLayout);
|
appContext.setLayout(layout);
|
||||||
appContext.start();
|
appContext.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||||
const $changesList = $('<ul>');
|
const $changesList = $('<ul>');
|
||||||
|
|
||||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append($changesList);
|
const dayEl = $('<div>').append($('<b>').text(dateDay)).append($changesList);
|
||||||
|
|
||||||
for (const change of dayChanges) {
|
for (const change of dayChanges) {
|
||||||
const formattedTime = utils.formatTime(utils.parseDate(change.date));
|
const formattedTime = change.date.substr(11, 5);
|
||||||
|
|
||||||
let $noteLink;
|
let $noteLink;
|
||||||
|
|
||||||
@@ -82,7 +82,12 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$changesList.append($('<li>')
|
$changesList.append($('<li>')
|
||||||
.append(formattedTime + ' - ')
|
.append(
|
||||||
|
$("<span>")
|
||||||
|
.text(formattedTime)
|
||||||
|
.attr("title", change.date)
|
||||||
|
)
|
||||||
|
.append(' - ')
|
||||||
.append($noteLink));
|
.append($noteLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,23 +97,9 @@ export async function showDialog(ancestorNoteId) {
|
|||||||
|
|
||||||
function groupByDate(result) {
|
function groupByDate(result) {
|
||||||
const groupedByDate = new Map();
|
const groupedByDate = new Map();
|
||||||
const dayCache = {};
|
|
||||||
|
|
||||||
for (const row of result) {
|
for (const row of result) {
|
||||||
let dateDay = utils.parseDate(row.date);
|
const dateDay = row.date.substr(0, 10);
|
||||||
dateDay.setHours(0);
|
|
||||||
dateDay.setMinutes(0);
|
|
||||||
dateDay.setSeconds(0);
|
|
||||||
dateDay.setMilliseconds(0);
|
|
||||||
|
|
||||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
|
||||||
// reference equality
|
|
||||||
if (dayCache[dateDay]) {
|
|
||||||
dateDay = dayCache[dateDay];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dayCache[dateDay] = dateDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!groupedByDate.has(dateDay)) {
|
if (!groupedByDate.has(dateDay)) {
|
||||||
groupedByDate.set(dateDay, []);
|
groupedByDate.set(dateDay, []);
|
||||||
|
|||||||
48
src/public/app/layouts/desktop_extra_window_layout.js
Normal file
48
src/public/app/layouts/desktop_extra_window_layout.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
|
import GlobalMenuWidget from "../widgets/global_menu.js";
|
||||||
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
|
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
|
||||||
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
|
import TabCachingWidget from "../widgets/tab_caching_widget.js";
|
||||||
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
|
import RunScriptButtonsWidget from "../widgets/run_script_buttons.js";
|
||||||
|
import ProtectedNoteSwitchWidget from "../widgets/protected_note_switch.js";
|
||||||
|
import NoteTypeWidget from "../widgets/note_type.js";
|
||||||
|
import NoteActionsWidget from "../widgets/note_actions.js";
|
||||||
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
|
|
||||||
|
export default class DesktopExtraWindowLayout {
|
||||||
|
constructor(customWidgets) {
|
||||||
|
this.customWidgets = customWidgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRootWidget(appContext) {
|
||||||
|
appContext.mainTreeWidget = new NoteTreeWidget();
|
||||||
|
|
||||||
|
return new FlexContainer('column')
|
||||||
|
.setParent(appContext)
|
||||||
|
.id('root-widget')
|
||||||
|
.css('height', '100vh')
|
||||||
|
.child(new FlexContainer('row')
|
||||||
|
.child(new GlobalMenuWidget())
|
||||||
|
.child(new TabRowWidget())
|
||||||
|
.child(new TitleBarButtonsWidget()))
|
||||||
|
.child(new FlexContainer('row')
|
||||||
|
.collapsible()
|
||||||
|
.child(new FlexContainer('column').id('center-pane').css('flex-grow', '1')
|
||||||
|
.child(new FlexContainer('row').class('title-row')
|
||||||
|
.cssBlock('.title-row > * { margin: 5px; }')
|
||||||
|
.child(new NoteTitleWidget())
|
||||||
|
.child(new RunScriptButtonsWidget().hideInZenMode())
|
||||||
|
.child(new ProtectedNoteSwitchWidget().hideInZenMode())
|
||||||
|
.child(new NoteTypeWidget().hideInZenMode())
|
||||||
|
.child(new NoteActionsWidget().hideInZenMode())
|
||||||
|
)
|
||||||
|
.child(new TabCachingWidget(() => new PromotedAttributesWidget()))
|
||||||
|
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
||||||
|
.child(...this.customWidgets.get('center-pane'))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
import GlobalMenuWidget from "./global_menu.js";
|
import GlobalMenuWidget from "../widgets/global_menu.js";
|
||||||
import TabRowWidget from "./tab_row.js";
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
import TitleBarButtonsWidget from "./title_bar_buttons.js";
|
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
|
||||||
import StandardTopWidget from "./standard_top_widget.js";
|
import StandardTopWidget from "../widgets/standard_top_widget.js";
|
||||||
import SidePaneContainer from "./side_pane_container.js";
|
import SidePaneContainer from "../widgets/side_pane_container.js";
|
||||||
import GlobalButtonsWidget from "./global_buttons.js";
|
import GlobalButtonsWidget from "../widgets/global_buttons.js";
|
||||||
import SearchBoxWidget from "./search_box.js";
|
import SearchBoxWidget from "../widgets/search_box.js";
|
||||||
import SearchResultsWidget from "./search_results.js";
|
import SearchResultsWidget from "../widgets/search_results.js";
|
||||||
import NoteTreeWidget from "./note_tree.js";
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import TabCachingWidget from "./tab_caching_widget.js";
|
import TabCachingWidget from "../widgets/tab_caching_widget.js";
|
||||||
import NotePathsWidget from "./note_paths.js";
|
import NotePathsWidget from "../widgets/note_paths.js";
|
||||||
import NoteTitleWidget from "./note_title.js";
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
import RunScriptButtonsWidget from "./run_script_buttons.js";
|
import RunScriptButtonsWidget from "../widgets/run_script_buttons.js";
|
||||||
import ProtectedNoteSwitchWidget from "./protected_note_switch.js";
|
import ProtectedNoteSwitchWidget from "../widgets/protected_note_switch.js";
|
||||||
import NoteTypeWidget from "./note_type.js";
|
import NoteTypeWidget from "../widgets/note_type.js";
|
||||||
import NoteActionsWidget from "./note_actions.js";
|
import NoteActionsWidget from "../widgets/note_actions.js";
|
||||||
import PromotedAttributesWidget from "./promoted_attributes.js";
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
import NoteDetailWidget from "./note_detail.js";
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteInfoWidget from "./note_info.js";
|
import NoteInfoWidget from "../widgets/collapsible_widgets/note_info.js";
|
||||||
import CalendarWidget from "./calendar.js";
|
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
|
||||||
import AttributesWidget from "./attributes.js";
|
import AttributesWidget from "../widgets/collapsible_widgets/attributes.js";
|
||||||
import LinkMapWidget from "./link_map.js";
|
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
|
||||||
import NoteRevisionsWidget from "./note_revisions.js";
|
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
|
||||||
import SimilarNotesWidget from "./similar_notes.js";
|
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
|
||||||
import WhatLinksHereWidget from "./what_links_here.js";
|
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
|
||||||
import SidePaneToggles from "./side_pane_toggles.js";
|
import SidePaneToggles from "../widgets/side_pane_toggles.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../services/app_context.js";
|
||||||
|
|
||||||
const RIGHT_PANE_CSS = `
|
const RIGHT_PANE_CSS = `
|
||||||
@@ -98,7 +98,7 @@ const RIGHT_PANE_CSS = `
|
|||||||
}
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopMainWindowLayout {
|
||||||
constructor(customWidgets) {
|
constructor(customWidgets) {
|
||||||
this.customWidgets = customWidgets;
|
this.customWidgets = customWidgets;
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../widgets/flex_container.js";
|
||||||
import NoteTitleWidget from "./note_title.js";
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
import NoteDetailWidget from "./note_detail.js";
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteTreeWidget from "./note_tree.js";
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import MobileGlobalButtonsWidget from "./mobile_global_buttons.js";
|
import MobileGlobalButtonsWidget from "../widgets/mobile_widgets/mobile_global_buttons.js";
|
||||||
import CloseDetailButtonWidget from "./close_detail_button.js";
|
import CloseDetailButtonWidget from "../widgets/mobile_widgets/close_detail_button.js";
|
||||||
import MobileDetailMenuWidget from "./mobile_detail_menu.js";
|
import MobileDetailMenuWidget from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
||||||
import ScreenContainer from "./screen_container.js";
|
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
|
||||||
|
|
||||||
const MOBILE_CSS = `
|
const MOBILE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import appContext from "./services/app_context.js";
|
import appContext from "./services/app_context.js";
|
||||||
import MobileLayout from "./widgets/mobile_layout.js";
|
import MobileLayout from "./layouts/mobile_layout.js";
|
||||||
import glob from "./services/glob.js";
|
import glob from "./services/glob.js";
|
||||||
|
|
||||||
glob.setupGlobs();
|
glob.setupGlobs();
|
||||||
|
|||||||
@@ -9,10 +9,16 @@ import TabManager from "./tab_manager.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import Component from "../widgets/component.js";
|
import Component from "../widgets/component.js";
|
||||||
import keyboardActionsService from "./keyboard_actions.js";
|
import keyboardActionsService from "./keyboard_actions.js";
|
||||||
import MobileScreenSwitcherExecutor from "../widgets/mobile_screen_switcher.js";
|
import MobileScreenSwitcherExecutor from "../widgets/mobile_widgets/mobile_screen_switcher.js";
|
||||||
import MainTreeExecutors from "./main_tree_executors.js";
|
import MainTreeExecutors from "./main_tree_executors.js";
|
||||||
|
|
||||||
class AppContext extends Component {
|
class AppContext extends Component {
|
||||||
|
constructor(isMainWindow) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.isMainWindow = isMainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
setLayout(layout) {
|
setLayout(layout) {
|
||||||
this.layout = layout;
|
this.layout = layout;
|
||||||
}
|
}
|
||||||
@@ -95,14 +101,21 @@ class AppContext extends Component {
|
|||||||
return $(el).closest(".component").prop('component');
|
return $(el).closest(".component").prop('component');
|
||||||
}
|
}
|
||||||
|
|
||||||
async protectedSessionStartedEvent() {
|
async openInNewWindow(notePath) {
|
||||||
await treeCache.loadInitialTree();
|
if (utils.isElectron()) {
|
||||||
|
const {ipcRenderer} = utils.dynamicRequire('electron');
|
||||||
|
|
||||||
this.triggerEvent('treeCacheReloaded');
|
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();
|
const appContext = new AppContext(window.glob.isMainWindow);
|
||||||
|
|
||||||
// we should save all outstanding changes before the page/app is closed
|
// we should save all outstanding changes before the page/app is closed
|
||||||
$(window).on('beforeunload', () => {
|
$(window).on('beforeunload', () => {
|
||||||
|
|||||||
@@ -113,12 +113,16 @@ function newTabContextMenu(e) {
|
|||||||
x: e.pageX,
|
x: e.pageX,
|
||||||
y: e.pageY,
|
y: e.pageY,
|
||||||
items: [
|
items: [
|
||||||
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"}
|
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"},
|
||||||
|
{title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "arrow-up-right"}
|
||||||
],
|
],
|
||||||
selectMenuItemHandler: ({command}) => {
|
selectMenuItemHandler: ({command}) => {
|
||||||
if (command === 'openNoteInNewTab') {
|
if (command === 'openNoteInNewTab') {
|
||||||
appContext.tabManager.openTabWithNote(notePath);
|
appContext.tabManager.openTabWithNote(notePath);
|
||||||
}
|
}
|
||||||
|
else if (command === 'openNoteInNewWindow') {
|
||||||
|
appContext.openInNewWindow(notePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import treeService from './tree.js';
|
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
import protectedSessionHolder from './protected_session_holder.js';
|
import protectedSessionHolder from './protected_session_holder.js';
|
||||||
import toastService from "./toast.js";
|
import toastService from "./toast.js";
|
||||||
import ws from "./ws.js";
|
import ws from "./ws.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
|
import treeCache from "./tree_cache.js";
|
||||||
const $enterProtectedSessionButton = $("#enter-protected-session-button");
|
|
||||||
const $leaveProtectedSessionButton = $("#leave-protected-session-button");
|
|
||||||
|
|
||||||
let protectedSessionDeferred = null;
|
let protectedSessionDeferred = null;
|
||||||
|
|
||||||
@@ -45,6 +42,10 @@ async function setupProtectedSession(password) {
|
|||||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
||||||
protectedSessionHolder.touchProtectedSession();
|
protectedSessionHolder.touchProtectedSession();
|
||||||
|
|
||||||
|
await treeCache.loadInitialTree();
|
||||||
|
|
||||||
|
await appContext.triggerEvent('treeCacheReloaded');
|
||||||
|
|
||||||
appContext.triggerEvent('protectedSessionStarted');
|
appContext.triggerEvent('protectedSessionStarted');
|
||||||
|
|
||||||
if (protectedSessionDeferred !== null) {
|
if (protectedSessionDeferred !== null) {
|
||||||
@@ -54,9 +55,6 @@ async function setupProtectedSession(password) {
|
|||||||
protectedSessionDeferred = null;
|
protectedSessionDeferred = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$enterProtectedSessionButton.hide();
|
|
||||||
$leaveProtectedSessionButton.show();
|
|
||||||
|
|
||||||
toastService.showMessage("Protected session has been started.");
|
toastService.showMessage("Protected session has been started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,16 @@ function touchProtectedSession() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function touchProtectedSessionIfNecessary(note) {
|
||||||
|
if (note && note.isProtected && isProtectedSessionAvailable()) {
|
||||||
|
touchProtectedSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setProtectedSessionId,
|
setProtectedSessionId,
|
||||||
resetProtectedSession,
|
resetProtectedSession,
|
||||||
isProtectedSessionAvailable,
|
isProtectedSessionAvailable,
|
||||||
touchProtectedSession
|
touchProtectedSession,
|
||||||
|
touchProtectedSessionIfNecessary
|
||||||
};
|
};
|
||||||
@@ -8,6 +8,7 @@ function getHeaders(headers) {
|
|||||||
// also avoiding using underscores instead of dashes since nginx filters them out by default
|
// also avoiding using underscores instead of dashes since nginx filters them out by default
|
||||||
const allHeaders = {
|
const allHeaders = {
|
||||||
'trilium-source-id': glob.sourceId,
|
'trilium-source-id': glob.sourceId,
|
||||||
|
'trilium-local-now-datetime': utils.localNowDateTime(),
|
||||||
'x-csrf-token': glob.csrfToken
|
'x-csrf-token': glob.csrfToken
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class TabContext extends Component {
|
|||||||
|
|
||||||
this.autoBookDisabled = false;
|
this.autoBookDisabled = false;
|
||||||
this.textPreviewDisabled = false;
|
this.textPreviewDisabled = false;
|
||||||
|
this.codePreviewDisabled = false;
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
@@ -69,10 +70,7 @@ class TabContext extends Component {
|
|||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) {
|
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||||
// FIXME: there are probably more places where this should be done
|
|
||||||
protectedSessionHolder.touchProtectedSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (triggerSwitchEvent) {
|
if (triggerSwitchEvent) {
|
||||||
this.triggerEvent('tabNoteSwitched', {
|
this.triggerEvent('tabNoteSwitched', {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import treeCache from "./tree_cache.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import TabContext from "./tab_context.js";
|
import TabContext from "./tab_context.js";
|
||||||
|
import appContext from "./app_context.js";
|
||||||
|
|
||||||
export default class TabManager extends Component {
|
export default class TabManager extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -14,6 +15,10 @@ export default class TabManager extends Component {
|
|||||||
this.activeTabId = null;
|
this.activeTabId = null;
|
||||||
|
|
||||||
this.tabsUpdate = new SpacedUpdate(async () => {
|
this.tabsUpdate = new SpacedUpdate(async () => {
|
||||||
|
if (!appContext.isMainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const openTabs = this.tabContexts
|
const openTabs = this.tabContexts
|
||||||
.map(tc => tc.getTabState())
|
.map(tc => tc.getTabState())
|
||||||
.filter(t => !!t);
|
.filter(t => !!t);
|
||||||
@@ -30,7 +35,9 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadTabs() {
|
async loadTabs() {
|
||||||
const openTabs = options.getJson('openTabs') || [];
|
const tabsToOpen = appContext.isMainWindow
|
||||||
|
? (options.getJson('openTabs') || [])
|
||||||
|
: [];
|
||||||
|
|
||||||
// if there's notePath in the URL, make sure it's open and active
|
// if there's notePath in the URL, make sure it's open and active
|
||||||
// (useful, among others, for opening clipped notes from clipper)
|
// (useful, among others, for opening clipped notes from clipper)
|
||||||
@@ -39,17 +46,17 @@ export default class TabManager extends Component {
|
|||||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
||||||
|
|
||||||
if (noteId && await treeCache.noteExists(noteId)) {
|
if (noteId && await treeCache.noteExists(noteId)) {
|
||||||
for (const tab of openTabs) {
|
for (const tab of tabsToOpen) {
|
||||||
tab.active = false;
|
tab.active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundTab = openTabs.find(tab => noteId === treeService.getNoteIdFromNotePath(tab.notePath));
|
const foundTab = tabsToOpen.find(tab => noteId === treeService.getNoteIdFromNotePath(tab.notePath));
|
||||||
|
|
||||||
if (foundTab) {
|
if (foundTab) {
|
||||||
foundTab.active = true;
|
foundTab.active = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
openTabs.push({
|
tabsToOpen.push({
|
||||||
notePath: notePath,
|
notePath: notePath,
|
||||||
active: true
|
active: true
|
||||||
});
|
});
|
||||||
@@ -59,7 +66,7 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
let filteredTabs = [];
|
let filteredTabs = [];
|
||||||
|
|
||||||
for (const openTab of openTabs) {
|
for (const openTab of tabsToOpen) {
|
||||||
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
||||||
|
|
||||||
if (await treeCache.noteExists(noteId)) {
|
if (await treeCache.noteExists(noteId)) {
|
||||||
@@ -315,6 +322,14 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveTabToNewWindowCommand({tabId}) {
|
||||||
|
const notePath = this.getTabContextById(tabId).notePath;
|
||||||
|
|
||||||
|
this.removeTab(tabId);
|
||||||
|
|
||||||
|
appContext.openInNewWindow(notePath);
|
||||||
|
}
|
||||||
|
|
||||||
async hoistedNoteChangedEvent({hoistedNoteId}) {
|
async hoistedNoteChangedEvent({hoistedNoteId}) {
|
||||||
if (hoistedNoteId === 'root') {
|
if (hoistedNoteId === 'root') {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -98,9 +98,8 @@ async function prepareNode(branch) {
|
|||||||
key: utils.randomString(12) // this should prevent some "duplicate key" errors
|
key: utils.randomString(12) // this should prevent some "duplicate key" errors
|
||||||
};
|
};
|
||||||
|
|
||||||
if (note.hasChildren() || note.type === 'search') {
|
node.folder = getChildBranchesWithoutImages(note).length > 0
|
||||||
node.folder = true;
|
|| note.type === 'search';
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -108,16 +107,9 @@ async function prepareNode(branch) {
|
|||||||
async function prepareRealBranch(parentNote) {
|
async function prepareRealBranch(parentNote) {
|
||||||
utils.assertArguments(parentNote);
|
utils.assertArguments(parentNote);
|
||||||
|
|
||||||
const childBranches = await parentNote.getChildBranches();
|
|
||||||
|
|
||||||
if (!childBranches) {
|
|
||||||
ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteList = [];
|
const noteList = [];
|
||||||
|
|
||||||
for (const branch of childBranches) {
|
for (const branch of getChildBranchesWithoutImages(parentNote)) {
|
||||||
const node = await prepareNode(branch);
|
const node = await prepareNode(branch);
|
||||||
|
|
||||||
noteList.push(node);
|
noteList.push(node);
|
||||||
@@ -126,6 +118,20 @@ async function prepareRealBranch(parentNote) {
|
|||||||
return noteList;
|
return noteList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getChildBranchesWithoutImages(parentNote) {
|
||||||
|
const childBranches = parentNote.getChildBranches();
|
||||||
|
|
||||||
|
if (!childBranches) {
|
||||||
|
ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageLinks = parentNote.getRelations('imageLink');
|
||||||
|
|
||||||
|
// image is already visible in the parent note so no need to display it separately in the book
|
||||||
|
return childBranches.filter(branch => !imageLinks.find(rel => rel.value === branch.noteId));
|
||||||
|
}
|
||||||
|
|
||||||
async function prepareSearchBranch(note) {
|
async function prepareSearchBranch(note) {
|
||||||
await treeCache.reloadNotes([note.noteId]);
|
await treeCache.reloadNotes([note.noteId]);
|
||||||
|
|
||||||
@@ -170,5 +176,6 @@ export default {
|
|||||||
prepareRootNode,
|
prepareRootNode,
|
||||||
prepareBranch,
|
prepareBranch,
|
||||||
getExtraClasses,
|
getExtraClasses,
|
||||||
getIcon
|
getIcon,
|
||||||
|
getChildBranchesWithoutImages
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,8 @@ class TreeContextMenu {
|
|||||||
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ title: 'Open in new tab', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
|
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
|
||||||
|
{ title: 'Open in a new window', command: "openInWindow", uiIcon: "empty", enabled: noSelectedNotes },
|
||||||
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus",
|
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus",
|
||||||
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
|
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
|
||||||
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
||||||
@@ -111,6 +112,9 @@ class TreeContextMenu {
|
|||||||
if (command === 'openInTab') {
|
if (command === 'openInTab') {
|
||||||
appContext.tabManager.openTabWithNote(notePath);
|
appContext.tabManager.openTabWithNote(notePath);
|
||||||
}
|
}
|
||||||
|
else if (command === 'openInWindow') {
|
||||||
|
appContext.openInNewWindow(notePath);
|
||||||
|
}
|
||||||
else if (command === "insertNoteAfter") {
|
else if (command === "insertNoteAfter") {
|
||||||
const parentNoteId = this.node.data.parentNoteId;
|
const parentNoteId = this.node.data.parentNoteId;
|
||||||
const isProtected = await treeService.getParentProtectedStatus(this.node);
|
const isProtected = await treeService.getParentProtectedStatus(this.node);
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ function formatDateTime(date) {
|
|||||||
return formatDate(date) + " " + formatTime(date);
|
return formatDate(date) + " " + formatTime(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function localNowDateTime() {
|
||||||
|
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||||
|
}
|
||||||
|
|
||||||
function now() {
|
function now() {
|
||||||
return formatTimeWithSeconds(new Date());
|
return formatTimeWithSeconds(new Date());
|
||||||
}
|
}
|
||||||
@@ -321,6 +325,7 @@ export default {
|
|||||||
formatDate,
|
formatDate,
|
||||||
formatDateISO,
|
formatDateISO,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
|
localNowDateTime,
|
||||||
now,
|
now,
|
||||||
isElectron,
|
isElectron,
|
||||||
isMac,
|
isMac,
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ class BasicWidget extends Component {
|
|||||||
for (const key in this.attrs) {
|
for (const key in this.attrs) {
|
||||||
if (key === 'style') {
|
if (key === 'style') {
|
||||||
if (this.attrs[key]) {
|
if (this.attrs[key]) {
|
||||||
$widget.attr(key, $widget.attr('style') + ';' + this.attrs[key]);
|
let style = $widget.attr('style');
|
||||||
|
style = style ? `${style}; ${this.attrs[key]}` : this.attrs[key];
|
||||||
|
|
||||||
|
$widget.attr(key, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import ws from "../services/ws.js";
|
import ws from "../../services/ws.js";
|
||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
export default class AttributesWidget extends CollapsibleWidget {
|
export default class AttributesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Attributes"; }
|
get widgetTitle() { return "Attributes"; }
|
||||||
@@ -16,7 +16,7 @@ export default class AttributesWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const attributesDialog = await import("../dialogs/attributes.js");
|
const attributesDialog = await import("../../dialogs/attributes.js");
|
||||||
attributesDialog.showDialog();
|
attributesDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import libraryLoader from "../services/library_loader.js";
|
import libraryLoader from "../../services/library_loader.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import dateNoteService from "../services/date_notes.js";
|
import dateNoteService from "../../services/date_notes.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../../services/app_context.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="calendar-widget">
|
<div class="calendar-widget">
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import treeCache from "../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
|
|
||||||
export default class EditedNotesWidget extends CollapsibleWidget {
|
export default class EditedNotesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Edited notes on this day"; }
|
get widgetTitle() { return "Edited notes on this day"; }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
let linkMapContainerIdCtr = 1;
|
let linkMapContainerIdCtr = 1;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||||
linkMapDialog.showDialog();
|
linkMapDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
|||||||
const $linkMapContainer = this.$body.find('.link-map-container');
|
const $linkMapContainer = this.$body.find('.link-map-container');
|
||||||
$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
|
$linkMapContainer.attr("id", "link-map-container-" + linkMapContainerIdCtr++);
|
||||||
|
|
||||||
const LinkMapServiceClass = (await import('../services/link_map.js')).default;
|
const LinkMapServiceClass = (await import('../../services/link_map.js')).default;
|
||||||
|
|
||||||
this.linkMapService = new LinkMapServiceClass(note, $linkMapContainer, {
|
this.linkMapService = new LinkMapServiceClass(note, $linkMapContainer, {
|
||||||
maxDepth: 1,
|
maxDepth: 1,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<table class="note-info-widget-table">
|
<table class="note-info-widget-table">
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
|
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
|
||||||
@@ -19,7 +19,7 @@ class NoteRevisionsWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const attributesDialog = await import("../dialogs/note_revisions.js");
|
const attributesDialog = await import("../../dialogs/note_revisions.js");
|
||||||
attributesDialog.showCurrentNoteRevisions(this.noteId);
|
attributesDialog.showCurrentNoteRevisions(this.noteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import server from "../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import treeCache from "../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
|
|
||||||
export default class SimilarNotesWidget extends CollapsibleWidget {
|
export default class SimilarNotesWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "Similar notes"; }
|
get widgetTitle() { return "Similar notes"; }
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import CollapsibleWidget from "./collapsible_widget.js";
|
import CollapsibleWidget from "../collapsible_widget.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
|
|
||||||
export default class WhatLinksHereWidget extends CollapsibleWidget {
|
export default class WhatLinksHereWidget extends CollapsibleWidget {
|
||||||
get widgetTitle() { return "What links here"; }
|
get widgetTitle() { return "What links here"; }
|
||||||
@@ -13,7 +13,7 @@ export default class WhatLinksHereWidget extends CollapsibleWidget {
|
|||||||
get headerActions() {
|
get headerActions() {
|
||||||
const $showFullButton = $("<a>").append("show link map").addClass('widget-header-action');
|
const $showFullButton = $("<a>").append("show link map").addClass('widget-header-action');
|
||||||
$showFullButton.on('click', async () => {
|
$showFullButton.on('click', async () => {
|
||||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||||
linkMapDialog.showDialog();
|
linkMapDialog.showDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
<button type="button" class="action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import appContext from "../services/app_context.js";
|
import appContext from "../../services/app_context.js";
|
||||||
import contextMenu from "../services/context_menu.js";
|
import contextMenu from "../../services/context_menu.js";
|
||||||
import noteCreateService from "../services/note_create.js";
|
import noteCreateService from "../../services/note_create.js";
|
||||||
import branchService from "../services/branches.js";
|
import branchService from "../../services/branches.js";
|
||||||
|
|
||||||
const TPL = `<button type="button" class="action-button bx bx-menu"></button>`;
|
const TPL = `<button type="button" class="action-button bx bx-menu"></button>`;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
const WIDGET_TPL = `
|
const WIDGET_TPL = `
|
||||||
<div id="global-buttons">
|
<div id="global-buttons">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Component from "./component.js";
|
import Component from "../component.js";
|
||||||
|
|
||||||
export default class MobileScreenSwitcherExecutor extends Component {
|
export default class MobileScreenSwitcherExecutor extends Component {
|
||||||
setActiveScreenCommand({screen}) {
|
setActiveScreenCommand({screen}) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "../flex_container.js";
|
||||||
|
|
||||||
export default class ScreenContainer extends FlexContainer {
|
export default class ScreenContainer extends FlexContainer {
|
||||||
constructor(screenName, direction) {
|
constructor(screenName, direction) {
|
||||||
@@ -6,7 +6,7 @@ import server from "../services/server.js";
|
|||||||
import libraryLoader from "../services/library_loader.js";
|
import libraryLoader from "../services/library_loader.js";
|
||||||
import EmptyTypeWidget from "./type_widgets/empty.js";
|
import EmptyTypeWidget from "./type_widgets/empty.js";
|
||||||
import EditableTextTypeWidget from "./type_widgets/editable_text.js";
|
import EditableTextTypeWidget from "./type_widgets/editable_text.js";
|
||||||
import CodeTypeWidget from "./type_widgets/code.js";
|
import EditableCodeTypeWidget from "./type_widgets/editable_code.js";
|
||||||
import FileTypeWidget from "./type_widgets/file.js";
|
import FileTypeWidget from "./type_widgets/file.js";
|
||||||
import ImageTypeWidget from "./type_widgets/image.js";
|
import ImageTypeWidget from "./type_widgets/image.js";
|
||||||
import SearchTypeWidget from "./type_widgets/search.js";
|
import SearchTypeWidget from "./type_widgets/search.js";
|
||||||
@@ -19,6 +19,7 @@ import keyboardActionsService from "../services/keyboard_actions.js";
|
|||||||
import noteCreateService from "../services/note_create.js";
|
import noteCreateService from "../services/note_create.js";
|
||||||
import DeletedTypeWidget from "./type_widgets/deleted.js";
|
import DeletedTypeWidget from "./type_widgets/deleted.js";
|
||||||
import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js";
|
import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js";
|
||||||
|
import ReadOnlyCodeTypeWidget from "./type_widgets/read_only_code.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-detail">
|
<div class="note-detail">
|
||||||
@@ -38,7 +39,8 @@ const typeWidgetClasses = {
|
|||||||
'deleted': DeletedTypeWidget,
|
'deleted': DeletedTypeWidget,
|
||||||
'editable-text': EditableTextTypeWidget,
|
'editable-text': EditableTextTypeWidget,
|
||||||
'read-only-text': ReadOnlyTextTypeWidget,
|
'read-only-text': ReadOnlyTextTypeWidget,
|
||||||
'code': CodeTypeWidget,
|
'editable-code': EditableCodeTypeWidget,
|
||||||
|
'read-only-code': ReadOnlyCodeTypeWidget,
|
||||||
'file': FileTypeWidget,
|
'file': FileTypeWidget,
|
||||||
'image': ImageTypeWidget,
|
'image': ImageTypeWidget,
|
||||||
'search': SearchTypeWidget,
|
'search': SearchTypeWidget,
|
||||||
@@ -61,6 +63,8 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
const dto = note.dto;
|
const dto = note.dto;
|
||||||
dto.content = this.getTypeWidget().getContent();
|
dto.content = this.getTypeWidget().getContent();
|
||||||
|
|
||||||
|
protectedSessionHolder.touchProtectedSessionIfNecessary(note);
|
||||||
|
|
||||||
await server.put('notes/' + noteId, dto, this.componentId);
|
await server.put('notes/' + noteId, dto, this.componentId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -187,10 +191,23 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'code' && !this.tabContext.codePreviewDisabled) {
|
||||||
|
const noteComplement = await this.tabContext.getNoteComplement();
|
||||||
|
|
||||||
|
if (note.hasLabel('readOnly') ||
|
||||||
|
(noteComplement.content && noteComplement.content.length > 30000)) {
|
||||||
|
type = 'read-only-code';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'text') {
|
if (type === 'text') {
|
||||||
type = 'editable-text';
|
type = 'editable-text';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'code') {
|
||||||
|
type = 'editable-code';
|
||||||
|
}
|
||||||
|
|
||||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||||
type = 'protected-session';
|
type = 'protected-session';
|
||||||
}
|
}
|
||||||
@@ -274,6 +291,12 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codePreviewDisabledEvent({tabContext}) {
|
||||||
|
if (this.isTab(tabContext.tabId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async cutIntoNoteCommand() {
|
async cutIntoNoteCommand() {
|
||||||
const note = appContext.tabManager.getActiveTabNote();
|
const note = appContext.tabManager.getActiveTabNote();
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
|||||||
this.spacedUpdate = new SpacedUpdate(async () => {
|
this.spacedUpdate = new SpacedUpdate(async () => {
|
||||||
const title = this.$noteTitle.val();
|
const title = this.$noteTitle.val();
|
||||||
|
|
||||||
|
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||||
|
|
||||||
await server.put(`notes/${this.noteId}/change-title`, {title});
|
await server.put(`notes/${this.noteId}/change-title`, {title});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,7 +389,8 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
|||||||
|
|
||||||
node.data.isProtected = note.isProtected;
|
node.data.isProtected = note.isProtected;
|
||||||
node.data.noteType = note.type;
|
node.data.noteType = note.type;
|
||||||
node.folder = note.type === 'search' || note.getChildNoteIds().length > 0;
|
node.folder = treeBuilder.getChildBranchesWithoutImages(note).length > 0
|
||||||
|
|| note.type === 'search';
|
||||||
node.icon = treeBuilder.getIcon(note);
|
node.icon = treeBuilder.getIcon(note);
|
||||||
node.extraClasses = treeBuilder.getExtraClasses(note);
|
node.extraClasses = treeBuilder.getExtraClasses(note);
|
||||||
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
||||||
@@ -481,6 +482,15 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
|||||||
// missing handling of things inherited from template
|
// missing handling of things inherited from template
|
||||||
noteIdsToReload.add(attr.noteId);
|
noteIdsToReload.add(attr.noteId);
|
||||||
}
|
}
|
||||||
|
else if (attr.type === 'relation' && attr.name === 'imageLink') {
|
||||||
|
const note = treeCache.getNoteFromCache(attr.noteId);
|
||||||
|
|
||||||
|
if (note && note.getChildNoteIds().includes(attr.value)) {
|
||||||
|
// there's new/deleted imageLink betwen note and its image child - which can show/hide
|
||||||
|
// the image (if there is a imageLink relation between parent and child then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree)
|
||||||
|
noteIdsToReload.add(attr.noteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const branch of loadResults.getBranches()) {
|
for (const branch of loadResults.getBranches()) {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ const TPL = `
|
|||||||
<div class="btn-group btn-group-xs">
|
<div class="btn-group btn-group-xs">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm icon-button bx bx-check-shield protect-button"
|
class="btn btn-sm icon-button bx bx-check-shield protect-button"
|
||||||
title="Protected note can be viewed and edited only after entering password">
|
title="Set this note as protected which means it will possible to view and edit this note only after entering password">
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm icon-button bx bx-shield-x unprotect-button"
|
class="btn btn-sm icon-button bx bx-shield-x unprotect-button"
|
||||||
title="Not protected note can be viewed without entering password">
|
title="Set this note as unprotected which will make it viewable and editable without entering password">
|
||||||
</button>
|
</button>
|
||||||
</div>`;``;
|
</div>`;``;
|
||||||
|
|
||||||
|
|||||||
@@ -57,29 +57,39 @@ export default class TabCachingWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* widget.hasBeenAlreadyShown is intended for lazy loading of cached tabs - initial note switches of new tabs
|
||||||
|
* are not executed, we're waiting for the first tab activation and then we update the tab. After this initial
|
||||||
|
* activation further note switches are always propagated to the tabs.
|
||||||
|
*/
|
||||||
handleEventInChildren(name, data) {
|
handleEventInChildren(name, data) {
|
||||||
// stop propagation of the event to the children, individual tab widget should not know about tab switching
|
if (['tabNoteSwitched', 'tabNoteSwitchedAndActivated'].includes(name)) {
|
||||||
// since they are per-tab
|
|
||||||
if (name === 'tabNoteSwitchedAndActivated') {
|
|
||||||
name = 'tabNoteSwitched';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'tabNoteSwitched') {
|
|
||||||
// this event is propagated only to the widgets of a particular tab
|
// this event is propagated only to the widgets of a particular tab
|
||||||
const widget = this.widgets[data.tabContext.tabId];
|
const widget = this.widgets[data.tabContext.tabId];
|
||||||
|
|
||||||
if (widget) {
|
if (widget && (widget.hasBeenAlreadyShown || name === 'tabNoteSwitchedAndActivated')) {
|
||||||
return widget.handleEvent(name, data);
|
widget.hasBeenAlreadyShown = true;
|
||||||
|
|
||||||
|
return widget.handleEvent('tabNoteSwitched', data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name !== 'activeTabChanged') {
|
if (name === 'activeTabChanged') {
|
||||||
|
const widget = this.widgets[data.tabContext.tabId];
|
||||||
|
|
||||||
|
if (widget.hasBeenAlreadyShown) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widget.hasBeenAlreadyShown = true;
|
||||||
|
|
||||||
|
return widget.handleEvent(name, data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return super.handleEventInChildren(name, data);
|
return super.handleEventInChildren(name, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,8 +258,9 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
x: e.pageX,
|
x: e.pageX,
|
||||||
y: e.pageY,
|
y: e.pageY,
|
||||||
items: [
|
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", command: "removeAllTabs", uiIcon: "empty"},
|
||||||
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"}
|
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"},
|
||||||
],
|
],
|
||||||
selectMenuItemHandler: ({command}) => {
|
selectMenuItemHandler: ({command}) => {
|
||||||
this.triggerCommand(command, {tabId});
|
this.triggerCommand(command, {tabId});
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ const TPL = `
|
|||||||
<div class="note-detail-code-editor"></div>
|
<div class="note-detail-code-editor"></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
export default class CodeTypeWidget extends TypeWidget {
|
export default class EditableCodeTypeWidget extends TypeWidget {
|
||||||
static getType() { return "code"; }
|
static getType() { return "editable-code"; }
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
@@ -1,13 +1,9 @@
|
|||||||
import libraryLoader from "../../services/library_loader.js";
|
import libraryLoader from "../../services/library_loader.js";
|
||||||
import noteAutocompleteService from '../../services/note_autocomplete.js';
|
import noteAutocompleteService from '../../services/note_autocomplete.js';
|
||||||
import mimeTypesService from '../../services/mime_types.js';
|
import mimeTypesService from '../../services/mime_types.js';
|
||||||
import TypeWidget from "./type_widget.js";
|
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import appContext from "../../services/app_context.js";
|
|
||||||
import keyboardActionService from "../../services/keyboard_actions.js";
|
import keyboardActionService from "../../services/keyboard_actions.js";
|
||||||
import treeCache from "../../services/tree_cache.js";
|
import treeCache from "../../services/tree_cache.js";
|
||||||
import linkService from "../../services/link.js";
|
|
||||||
import noteContentRenderer from "../../services/note_content_renderer.js";
|
|
||||||
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
|
||||||
|
|
||||||
const ENABLE_INSPECTOR = false;
|
const ENABLE_INSPECTOR = false;
|
||||||
@@ -60,6 +56,7 @@ const TPL = `
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: var(--detail-text-font-family);
|
font-family: var(--detail-text-font-family);
|
||||||
|
padding-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-text-editor {
|
.note-detail-text-editor {
|
||||||
|
|||||||
44
src/public/app/widgets/type_widgets/read_only_code.js
Normal file
44
src/public/app/widgets/type_widgets/read_only_code.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import TypeWidget from "./type_widget.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="note-detail-read-only-code note-detail-printable">
|
||||||
|
<style>
|
||||||
|
.note-detail-read-only-code {
|
||||||
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-detail-read-only-code-content {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="alert alert-warning no-print" style="margin-bottom: 0;">
|
||||||
|
Read only code view is shown. <a href="#" class="edit-note">Click here</a> to edit the note.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre class="note-detail-read-only-code-content"></pre>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
export default class ReadOnlyCodeTypeWidget extends TypeWidget {
|
||||||
|
static getType() { return "read-only-code"; }
|
||||||
|
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.$content = this.$widget.find('.note-detail-read-only-code-content');
|
||||||
|
|
||||||
|
this.$widget.find('a.edit-note').on('click', () => {
|
||||||
|
this.tabContext.codePreviewDisabled = true;
|
||||||
|
|
||||||
|
this.triggerEvent('codePreviewDisabled', {tabContext: this.tabContext});
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
async doRefresh(note) {
|
||||||
|
const noteComplement = await this.tabContext.getNoteComplement();
|
||||||
|
|
||||||
|
this.$content.text(noteComplement.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,4 +48,10 @@ export default class TypeWidget extends TabAwareWidget {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codePreviewDisabledEvent({tabContext}) {
|
||||||
|
if (this.isTab(tabContext.tabId)) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ async function loginSync(req) {
|
|||||||
|
|
||||||
// login token is valid for 5 minutes
|
// login token is valid for 5 minutes
|
||||||
if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
|
if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
|
||||||
return [400, { message: 'Auth request time is out of sync' }];
|
return [400, { message: 'Auth request time is out of sync, please check that both client and server have correct time.' }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncVersion = req.body.syncVersion;
|
const syncVersion = req.body.syncVersion;
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ async function getNoteRevision(req) {
|
|||||||
|
|
||||||
if (noteRevision.type === 'file') {
|
if (noteRevision.type === 'file') {
|
||||||
if (noteRevision.isStringNote()) {
|
if (noteRevision.isStringNote()) {
|
||||||
await noteRevision.getContent();
|
noteRevision.content = (await noteRevision.getContent()).substr(0, 10000);
|
||||||
|
|
||||||
noteRevision.content = noteRevision.content.substr(0, 10000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await noteRevision.getContent();
|
noteRevision.content = await noteRevision.getContent();
|
||||||
|
|
||||||
if (noteRevision.content && noteRevision.type === 'image') {
|
if (noteRevision.content && noteRevision.type === 'image') {
|
||||||
noteRevision.content = noteRevision.content.toString('base64');
|
noteRevision.content = noteRevision.content.toString('base64');
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ async function getRecentChanges(req) {
|
|||||||
SELECT * FROM (
|
SELECT * FROM (
|
||||||
SELECT note_revisions.noteId,
|
SELECT note_revisions.noteId,
|
||||||
note_revisions.noteRevisionId,
|
note_revisions.noteRevisionId,
|
||||||
note_revisions.utcDateCreated AS date
|
note_revisions.dateLastEdited AS date
|
||||||
FROM note_revisions
|
FROM note_revisions
|
||||||
ORDER BY note_revisions.utcDateCreated DESC
|
ORDER BY note_revisions.dateLastEdited DESC
|
||||||
)
|
)
|
||||||
UNION ALL SELECT * FROM (
|
UNION ALL SELECT * FROM (
|
||||||
SELECT
|
SELECT
|
||||||
notes.noteId,
|
notes.noteId,
|
||||||
NULL AS noteRevisionId,
|
NULL AS noteRevisionId,
|
||||||
utcDateModified AS date
|
dateModified AS date
|
||||||
FROM notes
|
FROM notes
|
||||||
ORDER BY utcDateModified DESC
|
ORDER BY dateModified DESC
|
||||||
)
|
)
|
||||||
ORDER BY date DESC`);
|
ORDER BY date DESC`);
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ async function getRecentChanges(req) {
|
|||||||
notes.title AS current_title,
|
notes.title AS current_title,
|
||||||
notes.isProtected AS current_isProtected,
|
notes.isProtected AS current_isProtected,
|
||||||
note_revisions.title,
|
note_revisions.title,
|
||||||
note_revisions.utcDateCreated AS date
|
note_revisions.dateCreated AS date
|
||||||
FROM
|
FROM
|
||||||
note_revisions
|
note_revisions
|
||||||
JOIN notes USING(noteId)
|
JOIN notes USING(noteId)
|
||||||
@@ -60,7 +60,7 @@ async function getRecentChanges(req) {
|
|||||||
notes.title AS current_title,
|
notes.title AS current_title,
|
||||||
notes.isProtected AS current_isProtected,
|
notes.isProtected AS current_isProtected,
|
||||||
notes.title,
|
notes.title,
|
||||||
notes.utcDateModified AS date
|
notes.dateModified AS date
|
||||||
FROM
|
FROM
|
||||||
notes
|
notes
|
||||||
WHERE noteId = ?`, [noteRow.noteId]));
|
WHERE noteId = ?`, [noteRow.noteId]));
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const env = require('../services/env');
|
|||||||
async function index(req, res) {
|
async function index(req, res) {
|
||||||
const options = await optionService.getOptionsMap();
|
const options = await optionService.getOptionsMap();
|
||||||
|
|
||||||
const view = req.cookies['trilium-device'] === 'mobile' ? 'mobile' : 'desktop';
|
let view = req.cookies['trilium-device'] === 'mobile' ? 'mobile' : 'desktop';
|
||||||
|
|
||||||
const csrfToken = req.csrfToken();
|
const csrfToken = req.csrfToken();
|
||||||
log.info(`Generated CSRF token ${csrfToken} with secret ${res.getHeader('set-cookie')}`);
|
log.info(`Generated CSRF token ${csrfToken} with secret ${res.getHeader('set-cookie')}`);
|
||||||
@@ -26,7 +26,8 @@ async function index(req, res) {
|
|||||||
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
||||||
instanceName: config.General ? config.General.instanceName : null,
|
instanceName: config.General ? config.General.instanceName : null,
|
||||||
appCssNoteIds: await getAppCssNoteIds(),
|
appCssNoteIds: await getAppCssNoteIds(),
|
||||||
isDev: env.isDev()
|
isDev: env.isDev(),
|
||||||
|
isMainWindow: !req.query.extra
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
const setupRoute = require('./setup');
|
const setupRoute = require('./setup');
|
||||||
const loginRoute = require('./login');
|
const loginRoute = require('./login');
|
||||||
const indexRoute = require('./index');
|
const indexRoute = require('./index');
|
||||||
@@ -82,6 +84,7 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
|
|||||||
try {
|
try {
|
||||||
const result = await cls.init(async () => {
|
const result = await cls.init(async () => {
|
||||||
cls.namespace.set('sourceId', req.headers['trilium-source-id']);
|
cls.namespace.set('sourceId', req.headers['trilium-source-id']);
|
||||||
|
cls.namespace.set('localNowDateTime', req.headers['`trilium-local-now-datetime`']);
|
||||||
protectedSessionService.setProtectedSessionId(req);
|
protectedSessionService.setProtectedSessionId(req);
|
||||||
|
|
||||||
if (transactional) {
|
if (transactional) {
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
const sqlInit = require('../services/sql_init');
|
const sqlInit = require('../services/sql_init');
|
||||||
const setupService = require('../services/setup');
|
const setupService = require('../services/setup');
|
||||||
const utils = require('../services/utils');
|
const utils = require('../services/utils');
|
||||||
const windowService = require('../services/window');
|
|
||||||
|
|
||||||
async function setupPage(req, res) {
|
async function setupPage(req, res) {
|
||||||
if (await sqlInit.isDbInitialized()) {
|
if (await sqlInit.isDbInitialized()) {
|
||||||
|
const windowService = require('../services/window');
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
await windowService.createMainWindow();
|
await windowService.createMainWindow();
|
||||||
windowService.closeSetupWindow();
|
windowService.closeSetupWindow();
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2020-04-20T22:39:23+02:00", buildRevision: "9f1b3cc892bdbbb150d54bac1bbe6356168ed5d3" };
|
module.exports = { buildDate:"2020-04-20T22:40:02+02:00", buildRevision: "a86177bb597c752fbc96a24d4be7ab5ae6c0344d" };
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ const VIRTUAL_ATTRIBUTES = [
|
|||||||
"type",
|
"type",
|
||||||
"mime",
|
"mime",
|
||||||
"text",
|
"text",
|
||||||
"parentCount"
|
"parentCount",
|
||||||
|
"attributeName",
|
||||||
|
"attributeValue"
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = function(filters, selectedColumns = 'notes.*') {
|
module.exports = function(filters, selectedColumns = 'notes.*') {
|
||||||
@@ -33,11 +35,29 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
|
|||||||
|
|
||||||
// forcing to use particular index since SQLite query planner would often choose something pretty bad
|
// forcing to use particular index since SQLite query planner would often choose something pretty bad
|
||||||
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
|
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
|
||||||
+ `ON ${alias}.noteId = notes.noteId `
|
+ `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0 `
|
||||||
+ `AND ${alias}.name = '${property}' AND ${alias}.isDeleted = 0`;
|
+ `AND ${alias}.name = '${property}' `;
|
||||||
|
|
||||||
accessor = `${alias}.value`;
|
accessor = `${alias}.value`;
|
||||||
}
|
}
|
||||||
|
else if (['attributeType', 'attributeName', 'attributeValue'].includes(property)) {
|
||||||
|
const alias = "attr_filter";
|
||||||
|
|
||||||
|
if (!(alias in joins)) {
|
||||||
|
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
|
||||||
|
+ `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property === 'attributeType') {
|
||||||
|
accessor = `${alias}.type`
|
||||||
|
} else if (property === 'attributeName') {
|
||||||
|
accessor = `${alias}.name`
|
||||||
|
} else if (property === 'attributeValue') {
|
||||||
|
accessor = `${alias}.value`
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unrecognized property ${property}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (property === 'content') {
|
else if (property === 'content') {
|
||||||
const alias = "note_contents";
|
const alias = "note_contents";
|
||||||
|
|
||||||
@@ -73,79 +93,85 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let where = '1';
|
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
for (const filter of filters) {
|
function parseWhereFilters(filters) {
|
||||||
if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
|
let whereStmt = '';
|
||||||
continue; // these are not real filters
|
|
||||||
}
|
|
||||||
|
|
||||||
where += " " + filter.relation + " ";
|
for (const filter of filters) {
|
||||||
|
if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
|
||||||
|
continue; // these are not real filters
|
||||||
|
}
|
||||||
|
|
||||||
const accessor = getAccessor(filter.name);
|
if (whereStmt) {
|
||||||
|
whereStmt += " " + filter.relation + " ";
|
||||||
|
}
|
||||||
|
|
||||||
if (filter.operator === 'exists') {
|
if (filter.children) {
|
||||||
where += `${accessor} IS NOT NULL`;
|
whereStmt += "(" + parseWhereFilters(filter.children) + ")";
|
||||||
}
|
continue;
|
||||||
else if (filter.operator === 'not-exists') {
|
}
|
||||||
where += `${accessor} IS NULL`;
|
|
||||||
}
|
const accessor = getAccessor(filter.name);
|
||||||
else if (filter.operator === '=' || filter.operator === '!=') {
|
|
||||||
where += `${accessor} ${filter.operator} ?`;
|
if (filter.operator === 'exists') {
|
||||||
params.push(filter.value);
|
whereStmt += `${accessor} IS NOT NULL`;
|
||||||
}
|
} else if (filter.operator === 'not-exists') {
|
||||||
else if (filter.operator === '*=' || filter.operator === '!*=') {
|
whereStmt += `${accessor} IS NULL`;
|
||||||
where += `${accessor}`
|
} else if (filter.operator === '=' || filter.operator === '!=') {
|
||||||
|
whereStmt += `${accessor} ${filter.operator} ?`;
|
||||||
|
params.push(filter.value);
|
||||||
|
} else if (filter.operator === '*=' || filter.operator === '!*=') {
|
||||||
|
whereStmt += `${accessor}`
|
||||||
+ (filter.operator.includes('!') ? ' NOT' : '')
|
+ (filter.operator.includes('!') ? ' NOT' : '')
|
||||||
+ ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
|
+ ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
|
||||||
}
|
} else if (filter.operator === '=*' || filter.operator === '!=*') {
|
||||||
else if (filter.operator === '=*' || filter.operator === '!=*') {
|
whereStmt += `${accessor}`
|
||||||
where += `${accessor}`
|
|
||||||
+ (filter.operator.includes('!') ? ' NOT' : '')
|
+ (filter.operator.includes('!') ? ' NOT' : '')
|
||||||
+ ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
|
+ ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
|
||||||
}
|
} else if (filter.operator === '*=*' || filter.operator === '!*=*') {
|
||||||
else if (filter.operator === '*=*' || filter.operator === '!*=*') {
|
const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
|
||||||
const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
|
|
||||||
|
|
||||||
let condition = "(" + columns.map(column =>
|
let condition = "(" + columns.map(column =>
|
||||||
`${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
|
`${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
|
||||||
.join(" OR ") + ")";
|
.join(" OR ") + ")";
|
||||||
|
|
||||||
if (filter.operator.includes('!')) {
|
if (filter.operator.includes('!')) {
|
||||||
condition = "NOT(" + condition + ")";
|
condition = "NOT(" + condition + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (['text', 'title', 'content'].includes(filter.name)) {
|
if (['text', 'title', 'content'].includes(filter.name)) {
|
||||||
// for title/content search does not make sense to search for protected notes
|
// for title/content search does not make sense to search for protected notes
|
||||||
condition = `(${condition} AND notes.isProtected = 0)`;
|
condition = `(${condition} AND notes.isProtected = 0)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
where += condition;
|
whereStmt += condition;
|
||||||
}
|
} else if ([">", ">=", "<", "<="].includes(filter.operator)) {
|
||||||
else if ([">", ">=", "<", "<="].includes(filter.operator)) {
|
let floatParam;
|
||||||
let floatParam;
|
|
||||||
|
|
||||||
// from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
|
// from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
|
||||||
if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
|
if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
|
||||||
floatParam = parseFloat(filter.value);
|
floatParam = parseFloat(filter.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (floatParam === undefined || isNaN(floatParam)) {
|
if (floatParam === undefined || isNaN(floatParam)) {
|
||||||
// if the value can't be parsed as float then we assume that string comparison should be used instead of numeric
|
// if the value can't be parsed as float then we assume that string comparison should be used instead of numeric
|
||||||
where += `${accessor} ${filter.operator} ?`;
|
whereStmt += `${accessor} ${filter.operator} ?`;
|
||||||
params.push(filter.value);
|
params.push(filter.value);
|
||||||
}
|
} else {
|
||||||
else {
|
whereStmt += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
|
||||||
where += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
|
params.push(floatParam);
|
||||||
params.push(floatParam);
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown operator " + filter.operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
throw new Error("Unknown operator " + filter.operator);
|
return whereStmt;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const where = parseWhereFilters(filters);
|
||||||
|
|
||||||
if (orderBy.length === 0) {
|
if (orderBy.length === 0) {
|
||||||
// if no ordering is given then order at least by note title
|
// if no ordering is given then order at least by note title
|
||||||
orderBy.push("notes.title");
|
orderBy.push("notes.title");
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ function getSourceId() {
|
|||||||
return namespace.get('sourceId');
|
return namespace.get('sourceId');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocalNowDateTime() {
|
||||||
|
return namespace.get('localNowDateTime');
|
||||||
|
}
|
||||||
|
|
||||||
function disableEntityEvents() {
|
function disableEntityEvents() {
|
||||||
namespace.set('disableEntityEvents', true);
|
namespace.set('disableEntityEvents', true);
|
||||||
}
|
}
|
||||||
@@ -50,6 +54,7 @@ module.exports = {
|
|||||||
wrap,
|
wrap,
|
||||||
namespace,
|
namespace,
|
||||||
getSourceId,
|
getSourceId,
|
||||||
|
getLocalNowDateTime,
|
||||||
disableEntityEvents,
|
disableEntityEvents,
|
||||||
isEntityEventsDisabled,
|
isEntityEventsDisabled,
|
||||||
reset,
|
reset,
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
const dayjs = require('dayjs');
|
const dayjs = require('dayjs');
|
||||||
|
const cls = require('./cls');
|
||||||
|
|
||||||
function utcNowDateTime() {
|
function utcNowDateTime() {
|
||||||
return utcDateStr(new Date());
|
return utcDateStr(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CLS date time is important in web deployments - server often runs in different time zone than user is located in
|
||||||
|
// so we'd prefer client timezone to be used to record local dates. For this reason requests from client contain
|
||||||
|
// "trilium-local-now-datetime" header which is then stored in CLS
|
||||||
function localNowDateTime() {
|
function localNowDateTime() {
|
||||||
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
return cls.getLocalNowDateTime()
|
||||||
|
|| dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||||
}
|
}
|
||||||
|
|
||||||
function localNowDate() {
|
function localNowDate() {
|
||||||
const date = new Date();
|
const clsDateTime = cls.getLocalNowDateTime();
|
||||||
|
|
||||||
return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate());
|
if (clsDateTime) {
|
||||||
|
return clsDateTime.substr(0, 10);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pad(num) {
|
function pad(num) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
isDev: function () {
|
isDev: function () {
|
||||||
return process.env.TRILIUM_ENV && process.env.TRILIUM_ENV === 'dev';
|
return !!(process.env.TRILIUM_ENV && process.env.TRILIUM_ENV === 'dev');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -60,6 +60,20 @@ module.exports = function (searchText) {
|
|||||||
operator: '*=*',
|
operator: '*=*',
|
||||||
value: searchText
|
value: searchText
|
||||||
});
|
});
|
||||||
|
|
||||||
|
filters.push({
|
||||||
|
relation: 'or',
|
||||||
|
name: 'attributeName',
|
||||||
|
operator: '*=*',
|
||||||
|
value: searchText
|
||||||
|
});
|
||||||
|
|
||||||
|
filters.push({
|
||||||
|
relation: 'or',
|
||||||
|
name: 'attributeValue',
|
||||||
|
operator: '*=*',
|
||||||
|
value: searchText
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const tokens = searchText.split(/\s+/);
|
const tokens = searchText.split(/\s+/);
|
||||||
@@ -67,9 +81,27 @@ module.exports = function (searchText) {
|
|||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
filters.push({
|
filters.push({
|
||||||
relation: 'and',
|
relation: 'and',
|
||||||
name: 'text',
|
name: 'sub',
|
||||||
operator: '*=*',
|
children: [
|
||||||
value: token
|
{
|
||||||
|
relation: 'or',
|
||||||
|
name: 'text',
|
||||||
|
operator: '*=*',
|
||||||
|
value: token
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'or',
|
||||||
|
name: 'attributeName',
|
||||||
|
operator: '*=*',
|
||||||
|
value: token
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'or',
|
||||||
|
name: 'attributeValue',
|
||||||
|
operator: '*=*',
|
||||||
|
value: token
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const log = require('./log');
|
|||||||
const sqlInit = require('./sql_init');
|
const sqlInit = require('./sql_init');
|
||||||
const cls = require('./cls');
|
const cls = require('./cls');
|
||||||
const keyboardActionsService = require('./keyboard_actions');
|
const keyboardActionsService = require('./keyboard_actions');
|
||||||
|
const {ipcMain} = require('electron');
|
||||||
|
|
||||||
// Prevent window being garbage collected
|
// Prevent window being garbage collected
|
||||||
/** @type {Electron.BrowserWindow} */
|
/** @type {Electron.BrowserWindow} */
|
||||||
@@ -14,6 +15,29 @@ let mainWindow;
|
|||||||
/** @type {Electron.BrowserWindow} */
|
/** @type {Electron.BrowserWindow} */
|
||||||
let setupWindow;
|
let setupWindow;
|
||||||
|
|
||||||
|
async function createExtraWindow(notePath) {
|
||||||
|
const {BrowserWindow} = require('electron');
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
width: 1000,
|
||||||
|
height: 800,
|
||||||
|
title: 'Trilium Notes',
|
||||||
|
webPreferences: {
|
||||||
|
enableRemoteModule: true,
|
||||||
|
nodeIntegration: true,
|
||||||
|
spellcheck: await optionService.getOptionBool('spellCheckEnabled')
|
||||||
|
},
|
||||||
|
frame: await optionService.getOptionBool('nativeTitleBarVisible'),
|
||||||
|
icon: getIcon()
|
||||||
|
});
|
||||||
|
|
||||||
|
win.setMenuBarVisibility(false);
|
||||||
|
win.loadURL('http://127.0.0.1:' + await port + '/?extra=1#' + notePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on('create-extra-window', (event, arg) => {
|
||||||
|
createExtraWindow(arg.notePath);
|
||||||
|
});
|
||||||
|
|
||||||
async function createMainWindow() {
|
async function createMainWindow() {
|
||||||
const windowStateKeeper = require('electron-window-state'); // should not be statically imported
|
const windowStateKeeper = require('electron-window-state'); // should not be statically imported
|
||||||
|
|
||||||
@@ -141,5 +165,6 @@ module.exports = {
|
|||||||
createMainWindow,
|
createMainWindow,
|
||||||
createSetupWindow,
|
createSetupWindow,
|
||||||
closeSetupWindow,
|
closeSetupWindow,
|
||||||
|
createExtraWindow,
|
||||||
registerGlobalShortcuts
|
registerGlobalShortcuts
|
||||||
};
|
};
|
||||||
@@ -46,8 +46,9 @@
|
|||||||
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
||||||
instanceName: '<%= instanceName %>',
|
instanceName: '<%= instanceName %>',
|
||||||
csrfToken: '<%= csrfToken %>',
|
csrfToken: '<%= csrfToken %>',
|
||||||
isDev: '<%= isDev %>',
|
isDev: <%= isDev %>,
|
||||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>
|
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
||||||
|
isMainWindow: <%= isMainWindow %>
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<div id="note-revision-title-buttons"></div>
|
<div id="note-revision-title-buttons"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="note-revision-content" style="overflow: auto;"></div>
|
<div id="note-revision-content" style="overflow: auto; word-break: break-word;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
||||||
instanceName: '<%= instanceName %>',
|
instanceName: '<%= instanceName %>',
|
||||||
csrfToken: '<%= csrfToken %>',
|
csrfToken: '<%= csrfToken %>',
|
||||||
isDev: '<%= isDev %>',
|
isDev: <%= isDev %>,
|
||||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>
|
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user