mirror of
https://github.com/zadam/trilium.git
synced 2025-10-27 00:06:30 +01:00
Compare commits
25 Commits
v0.41.4-be
...
v0.41.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5769587305 | ||
|
|
ffbfccb701 | ||
|
|
907cdd8fcb | ||
|
|
7ea53d468e | ||
|
|
586d6b4557 | ||
|
|
8f68ff1932 | ||
|
|
a1ea2c9115 | ||
|
|
e8ce81a133 | ||
|
|
aff12950f0 | ||
|
|
75c58cbf79 | ||
|
|
87a1e98fa2 | ||
|
|
d1eacbb574 | ||
|
|
71d248cd87 | ||
|
|
ac608b9334 | ||
|
|
32020d78b5 | ||
|
|
ff853c7d0a | ||
|
|
8526cb2315 | ||
|
|
dc89f72e75 | ||
|
|
657ff16267 | ||
|
|
ed759f5585 | ||
|
|
a86177bb59 | ||
|
|
9f1b3cc892 | ||
|
|
8473f72ec8 | ||
|
|
666d202a3a | ||
|
|
988fae50cb |
2
.idea/dataSources.xml
generated
2
.idea/dataSources.xml
generated
@@ -5,7 +5,7 @@
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../trilium-data/document.db</jdbc-url>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<ServerVersion>3.25.1</ServerVersion>
|
||||
<ServerVersion>3.16.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
@@ -57,6 +57,7 @@
|
||||
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>apiTokenId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="25" parent="6">
|
||||
@@ -130,17 +131,21 @@
|
||||
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>attributeId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="40" parent="7" name="IDX_attributes_name_value">
|
||||
<ColNames>name
|
||||
value</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="41" parent="7" name="IDX_attributes_value_index">
|
||||
<ColNames>value</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="42" parent="7">
|
||||
<ColNames>attributeId</ColNames>
|
||||
@@ -207,14 +212,17 @@ value</ColNames>
|
||||
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>branchId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
||||
<ColNames>noteId
|
||||
parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
||||
<ColNames>parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="57" parent="8">
|
||||
<ColNames>branchId</ColNames>
|
||||
@@ -245,6 +253,7 @@ parentNoteId</ColNames>
|
||||
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="63" parent="9">
|
||||
@@ -275,6 +284,7 @@ parentNoteId</ColNames>
|
||||
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="69" parent="10">
|
||||
@@ -359,22 +369,28 @@ parentNoteId</ColNames>
|
||||
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
||||
<ColNames>utcDateLastEdited</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
||||
<ColNames>dateLastEdited</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="90" parent="11">
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
@@ -461,28 +477,36 @@ parentNoteId</ColNames>
|
||||
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="106" parent="12" name="IDX_notes_title">
|
||||
<ColNames>title</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="107" parent="12" name="IDX_notes_type">
|
||||
<ColNames>type</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
||||
<ColNames>isDeleted</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="110" parent="12" name="IDX_notes_dateModified">
|
||||
<ColNames>dateModified</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
||||
<ColNames>utcDateModified</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="113" parent="12">
|
||||
<ColNames>noteId</ColNames>
|
||||
@@ -523,6 +547,7 @@ parentNoteId</ColNames>
|
||||
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="121" parent="13">
|
||||
@@ -558,6 +583,7 @@ parentNoteId</ColNames>
|
||||
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="128" parent="14">
|
||||
@@ -578,10 +604,12 @@ parentNoteId</ColNames>
|
||||
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>sourceId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="133" parent="15">
|
||||
<ColNames>sourceId</ColNames>
|
||||
@@ -602,7 +630,7 @@ parentNoteId</ColNames>
|
||||
</column>
|
||||
<column id="137" parent="16" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>int|0s</DataType>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="138" parent="16" name="sql">
|
||||
<Position>5</Position>
|
||||
@@ -649,10 +677,12 @@ parentNoteId</ColNames>
|
||||
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
||||
<ColNames>entityName
|
||||
entityId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
||||
<ColNames>utcSyncDate</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="149" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
|
||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -8,7 +8,7 @@ Trilium Notes is a hierarchical note taking application with focus on building l
|
||||
## Features
|
||||
|
||||
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://github.com/zadam/trilium/wiki/Cloning-notes))
|
||||
* Rich WYSIWYG note editing including e.g. tables and images with markdown [autoformat](https://github.com/zadam/trilium/wiki/Text-editor#autoformat)
|
||||
* Rich WYSIWYG note editing including e.g. tables and images with markdown [autoformat](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)
|
||||
* Support for editing [notes with source code](https://github.com/zadam/trilium/wiki/Code-notes), including syntax highlighting
|
||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation), full text search and [note hoisting](https://github.com/zadam/trilium/wiki/Note-hoisting)
|
||||
* Seamless [note versioning](https://github.com/zadam/trilium/wiki/Note-revisions)
|
||||
|
||||
@@ -5,6 +5,8 @@ if [[ $# -eq 0 ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
npm run webpack
|
||||
|
||||
DIR=$1
|
||||
|
||||
rm -rf $DIR
|
||||
@@ -25,7 +27,7 @@ cp -r electron.js $DIR/
|
||||
cp webpack-* $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
|
||||
|
||||
|
||||
@@ -879,7 +879,7 @@
|
||||
</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><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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -507,7 +507,7 @@
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -6813,7 +6813,7 @@ Cache is note instance scoped.
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -79,7 +79,7 @@ export default Attribute;</code></pre>
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -81,7 +81,7 @@ export default Branch;</code></pre>
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -61,7 +61,7 @@ export default NoteComplement;</code></pre>
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -493,7 +493,7 @@ export default NoteShort;</code></pre>
|
||||
</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><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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</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><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>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
@@ -443,7 +443,7 @@ export default FrontendScriptApi;</code></pre>
|
||||
</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><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>
|
||||
|
||||
<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>
|
||||
709
package-lock.json
generated
709
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.41.4-beta",
|
||||
"version": "0.41.5",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -16,7 +16,7 @@
|
||||
"start-server": "TRILIUM_ENV=dev node ./src/www",
|
||||
"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-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",
|
||||
"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",
|
||||
"cookie-parser": "1.4.5",
|
||||
"csurf": "1.11.0",
|
||||
"dayjs": "1.8.24",
|
||||
"dayjs": "1.8.25",
|
||||
"debug": "4.1.1",
|
||||
"ejs": "3.0.2",
|
||||
"ejs": "3.1.2",
|
||||
"electron-debug": "3.0.1",
|
||||
"electron-dl": "3.0.0",
|
||||
"electron-find": "1.0.6",
|
||||
"electron-window-state": "5.0.3",
|
||||
"express": "4.17.1",
|
||||
"express-session": "1.17.0",
|
||||
"file-type": "14.1.4",
|
||||
"express-session": "1.17.1",
|
||||
"file-type": "14.2.0",
|
||||
"fs-extra": "9.0.0",
|
||||
"helmet": "3.22.0",
|
||||
"html": "1.0.0",
|
||||
@@ -51,10 +51,10 @@
|
||||
"imagemin-pngquant": "8.0.0",
|
||||
"ini": "1.3.5",
|
||||
"is-svg": "4.2.1",
|
||||
"jimp": "0.10.2",
|
||||
"mime-types": "2.1.26",
|
||||
"jimp": "0.10.3",
|
||||
"mime-types": "2.1.27",
|
||||
"multer": "1.4.2",
|
||||
"node-abi": "2.15.0",
|
||||
"node-abi": "2.16.0",
|
||||
"open": "7.0.3",
|
||||
"portscanner": "2.2.0",
|
||||
"rand-token": "1.0.1",
|
||||
@@ -73,18 +73,18 @@
|
||||
"turndown": "6.0.0",
|
||||
"turndown-plugin-gfm": "1.0.2",
|
||||
"unescape": "1.0.1",
|
||||
"ws": "7.2.3",
|
||||
"ws": "7.2.5",
|
||||
"yauzl": "^2.10.0",
|
||||
"yazl": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "9.0.0-beta.16",
|
||||
"electron-builder": "22.4.1",
|
||||
"electron": "9.0.0-beta.20",
|
||||
"electron-builder": "22.5.1",
|
||||
"electron-packager": "14.2.1",
|
||||
"electron-rebuild": "1.10.1",
|
||||
"jsdoc": "3.6.4",
|
||||
"lorem-ipsum": "2.0.3",
|
||||
"webpack": "5.0.0-beta.14",
|
||||
"webpack": "5.0.0-beta.15",
|
||||
"webpack-cli": "4.0.0-beta.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
||||
@@ -5,8 +5,9 @@ import bundleService from "./services/bundle.js";
|
||||
import noteAutocompleteService from './services/note_autocomplete.js';
|
||||
import macInit from './services/mac_init.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 DesktopExtraWindowLayout from "./layouts/desktop_extra_window_layout.js";
|
||||
|
||||
glob.setupGlobs();
|
||||
|
||||
@@ -23,9 +24,11 @@ $('[data-toggle="tooltip"]').tooltip({
|
||||
macInit.init();
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import optionsService from "../../services/options.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import server from "../../services/server.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
|
||||
@@ -20,7 +20,9 @@ const TPL = `
|
||||
<input type="text" class="form-control" id="spell-check-language-code" placeholder="for example "en-US", "de-AT"">
|
||||
</div>
|
||||
|
||||
<p>Multiple languages can be separated by comman. Changes to the spell check options will take effect after application restart.</p>
|
||||
<p>Multiple languages can be separated by comma, e.g. <code>en-US, de-DE, cs</code>. Changes to the spell check options will take effect after application restart.</p>
|
||||
|
||||
<p><strong>Available language codes: </strong> <span id="available-language-codes"></span></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -95,6 +97,14 @@ export default class ProtectedSessionOptions {
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$availableLanguageCodes = $("#available-language-codes");
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const {webContents} = utils.dynamicRequire('electron').remote.getCurrentWindow();
|
||||
|
||||
this.$availableLanguageCodes.text(webContents.session.availableSpellCheckerLanguages.join(', '));
|
||||
}
|
||||
|
||||
this.$eraseNotesAfterTimeInSeconds = $("#erase-notes-after-time-in-seconds");
|
||||
|
||||
this.$eraseNotesAfterTimeInSeconds.on('change', () => {
|
||||
|
||||
@@ -32,10 +32,10 @@ export async function showDialog(ancestorNoteId) {
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
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) {
|
||||
const formattedTime = utils.formatTime(utils.parseDate(change.date));
|
||||
const formattedTime = change.date.substr(11, 5);
|
||||
|
||||
let $noteLink;
|
||||
|
||||
@@ -82,7 +82,12 @@ export async function showDialog(ancestorNoteId) {
|
||||
}
|
||||
|
||||
$changesList.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(
|
||||
$("<span>")
|
||||
.text(formattedTime)
|
||||
.attr("title", change.date)
|
||||
)
|
||||
.append(' - ')
|
||||
.append($noteLink));
|
||||
}
|
||||
|
||||
@@ -92,23 +97,9 @@ export async function showDialog(ancestorNoteId) {
|
||||
|
||||
function groupByDate(result) {
|
||||
const groupedByDate = new Map();
|
||||
const dayCache = {};
|
||||
|
||||
for (const row of result) {
|
||||
let dateDay = utils.parseDate(row.date);
|
||||
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;
|
||||
}
|
||||
const dateDay = row.date.substr(0, 10);
|
||||
|
||||
if (!groupedByDate.has(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 GlobalMenuWidget from "./global_menu.js";
|
||||
import TabRowWidget from "./tab_row.js";
|
||||
import TitleBarButtonsWidget from "./title_bar_buttons.js";
|
||||
import StandardTopWidget from "./standard_top_widget.js";
|
||||
import SidePaneContainer from "./side_pane_container.js";
|
||||
import GlobalButtonsWidget from "./global_buttons.js";
|
||||
import SearchBoxWidget from "./search_box.js";
|
||||
import SearchResultsWidget from "./search_results.js";
|
||||
import NoteTreeWidget from "./note_tree.js";
|
||||
import TabCachingWidget from "./tab_caching_widget.js";
|
||||
import NotePathsWidget from "./note_paths.js";
|
||||
import NoteTitleWidget from "./note_title.js";
|
||||
import RunScriptButtonsWidget from "./run_script_buttons.js";
|
||||
import ProtectedNoteSwitchWidget from "./protected_note_switch.js";
|
||||
import NoteTypeWidget from "./note_type.js";
|
||||
import NoteActionsWidget from "./note_actions.js";
|
||||
import PromotedAttributesWidget from "./promoted_attributes.js";
|
||||
import NoteDetailWidget from "./note_detail.js";
|
||||
import NoteInfoWidget from "./note_info.js";
|
||||
import CalendarWidget from "./calendar.js";
|
||||
import AttributesWidget from "./attributes.js";
|
||||
import LinkMapWidget from "./link_map.js";
|
||||
import NoteRevisionsWidget from "./note_revisions.js";
|
||||
import SimilarNotesWidget from "./similar_notes.js";
|
||||
import WhatLinksHereWidget from "./what_links_here.js";
|
||||
import SidePaneToggles from "./side_pane_toggles.js";
|
||||
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 StandardTopWidget from "../widgets/standard_top_widget.js";
|
||||
import SidePaneContainer from "../widgets/side_pane_container.js";
|
||||
import GlobalButtonsWidget from "../widgets/global_buttons.js";
|
||||
import SearchBoxWidget from "../widgets/search_box.js";
|
||||
import SearchResultsWidget from "../widgets/search_results.js";
|
||||
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||
import TabCachingWidget from "../widgets/tab_caching_widget.js";
|
||||
import NotePathsWidget from "../widgets/note_paths.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";
|
||||
import NoteInfoWidget from "../widgets/collapsible_widgets/note_info.js";
|
||||
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
|
||||
import AttributesWidget from "../widgets/collapsible_widgets/attributes.js";
|
||||
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
|
||||
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
|
||||
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
|
||||
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
|
||||
import SidePaneToggles from "../widgets/side_pane_toggles.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
|
||||
const RIGHT_PANE_CSS = `
|
||||
@@ -98,7 +98,7 @@ const RIGHT_PANE_CSS = `
|
||||
}
|
||||
</style>`;
|
||||
|
||||
export default class DesktopLayout {
|
||||
export default class DesktopMainWindowLayout {
|
||||
constructor(customWidgets) {
|
||||
this.customWidgets = customWidgets;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import FlexContainer from "./flex_container.js";
|
||||
import NoteTitleWidget from "./note_title.js";
|
||||
import NoteDetailWidget from "./note_detail.js";
|
||||
import NoteTreeWidget from "./note_tree.js";
|
||||
import MobileGlobalButtonsWidget from "./mobile_global_buttons.js";
|
||||
import CloseDetailButtonWidget from "./close_detail_button.js";
|
||||
import MobileDetailMenuWidget from "./mobile_detail_menu.js";
|
||||
import ScreenContainer from "./screen_container.js";
|
||||
import FlexContainer from "../widgets/flex_container.js";
|
||||
import NoteTitleWidget from "../widgets/note_title.js";
|
||||
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||
import MobileGlobalButtonsWidget from "../widgets/mobile_widgets/mobile_global_buttons.js";
|
||||
import CloseDetailButtonWidget from "../widgets/mobile_widgets/close_detail_button.js";
|
||||
import MobileDetailMenuWidget from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
||||
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
|
||||
|
||||
const MOBILE_CSS = `
|
||||
<style>
|
||||
@@ -1,5 +1,5 @@
|
||||
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";
|
||||
|
||||
glob.setupGlobs();
|
||||
|
||||
@@ -9,10 +9,16 @@ import TabManager from "./tab_manager.js";
|
||||
import treeService from "./tree.js";
|
||||
import Component from "../widgets/component.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";
|
||||
|
||||
class AppContext extends Component {
|
||||
constructor(isMainWindow) {
|
||||
super();
|
||||
|
||||
this.isMainWindow = isMainWindow;
|
||||
}
|
||||
|
||||
setLayout(layout) {
|
||||
this.layout = layout;
|
||||
}
|
||||
@@ -95,14 +101,21 @@ class AppContext extends Component {
|
||||
return $(el).closest(".component").prop('component');
|
||||
}
|
||||
|
||||
async protectedSessionStartedEvent() {
|
||||
await treeCache.loadInitialTree();
|
||||
async openInNewWindow(notePath) {
|
||||
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
|
||||
$(window).on('beforeunload', () => {
|
||||
|
||||
@@ -113,12 +113,16 @@ function newTabContextMenu(e) {
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"}
|
||||
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"},
|
||||
{title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "arrow-up-right"}
|
||||
],
|
||||
selectMenuItemHandler: ({command}) => {
|
||||
if (command === 'openNoteInNewTab') {
|
||||
appContext.tabManager.openTabWithNote(notePath);
|
||||
}
|
||||
else if (command === 'openNoteInNewWindow') {
|
||||
appContext.openInNewWindow(notePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -162,6 +166,7 @@ $(document).on('contextmenu', 'a.ck-link-actions__preview', newTabContextMenu);
|
||||
$(document).on('contextmenu', '.note-detail-text a', newTabContextMenu);
|
||||
$(document).on('contextmenu', "a[data-action='note']", newTabContextMenu);
|
||||
$(document).on('contextmenu', ".note-detail-render a", newTabContextMenu);
|
||||
$(document).on('contextmenu', ".note-paths-widget a", newTabContextMenu);
|
||||
|
||||
export default {
|
||||
getNotePathFromUrl,
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import treeService from './tree.js';
|
||||
import utils from './utils.js';
|
||||
import server from './server.js';
|
||||
import protectedSessionHolder from './protected_session_holder.js';
|
||||
import toastService from "./toast.js";
|
||||
import ws from "./ws.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
const $enterProtectedSessionButton = $("#enter-protected-session-button");
|
||||
const $leaveProtectedSessionButton = $("#leave-protected-session-button");
|
||||
import treeCache from "./tree_cache.js";
|
||||
|
||||
let protectedSessionDeferred = null;
|
||||
|
||||
@@ -45,6 +42,10 @@ async function setupProtectedSession(password) {
|
||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
||||
protectedSessionHolder.touchProtectedSession();
|
||||
|
||||
await treeCache.loadInitialTree();
|
||||
|
||||
await appContext.triggerEvent('treeCacheReloaded');
|
||||
|
||||
appContext.triggerEvent('protectedSessionStarted');
|
||||
|
||||
if (protectedSessionDeferred !== null) {
|
||||
@@ -54,9 +55,6 @@ async function setupProtectedSession(password) {
|
||||
protectedSessionDeferred = null;
|
||||
}
|
||||
|
||||
$enterProtectedSessionButton.hide();
|
||||
$leaveProtectedSessionButton.show();
|
||||
|
||||
toastService.showMessage("Protected session has been started.");
|
||||
}
|
||||
|
||||
|
||||
@@ -39,9 +39,16 @@ function touchProtectedSession() {
|
||||
}
|
||||
}
|
||||
|
||||
function touchProtectedSessionIfNecessary(note) {
|
||||
if (note && note.isProtected && isProtectedSessionAvailable()) {
|
||||
touchProtectedSession();
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
setProtectedSessionId,
|
||||
resetProtectedSession,
|
||||
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
|
||||
const allHeaders = {
|
||||
'trilium-source-id': glob.sourceId,
|
||||
'trilium-local-now-datetime': utils.localNowDateTime(),
|
||||
'x-csrf-token': glob.csrfToken
|
||||
};
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ class TabContext extends Component {
|
||||
|
||||
this.autoBookDisabled = false;
|
||||
this.textPreviewDisabled = false;
|
||||
this.codePreviewDisabled = false;
|
||||
|
||||
setTimeout(async () => {
|
||||
// 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);
|
||||
|
||||
if (this.note.isProtected && protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
// FIXME: there are probably more places where this should be done
|
||||
protectedSessionHolder.touchProtectedSession();
|
||||
}
|
||||
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||
|
||||
if (triggerSwitchEvent) {
|
||||
this.triggerEvent('tabNoteSwitched', {
|
||||
|
||||
@@ -6,6 +6,7 @@ import treeCache from "./tree_cache.js";
|
||||
import treeService from "./tree.js";
|
||||
import utils from "./utils.js";
|
||||
import TabContext from "./tab_context.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
export default class TabManager extends Component {
|
||||
constructor() {
|
||||
@@ -14,6 +15,10 @@ export default class TabManager extends Component {
|
||||
this.activeTabId = null;
|
||||
|
||||
this.tabsUpdate = new SpacedUpdate(async () => {
|
||||
if (!appContext.isMainWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const openTabs = this.tabContexts
|
||||
.map(tc => tc.getTabState())
|
||||
.filter(t => !!t);
|
||||
@@ -30,7 +35,9 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
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
|
||||
// (useful, among others, for opening clipped notes from clipper)
|
||||
@@ -39,17 +46,17 @@ export default class TabManager extends Component {
|
||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (noteId && await treeCache.noteExists(noteId)) {
|
||||
for (const tab of openTabs) {
|
||||
for (const tab of tabsToOpen) {
|
||||
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) {
|
||||
foundTab.active = true;
|
||||
}
|
||||
else {
|
||||
openTabs.push({
|
||||
tabsToOpen.push({
|
||||
notePath: notePath,
|
||||
active: true
|
||||
});
|
||||
@@ -59,7 +66,7 @@ export default class TabManager extends Component {
|
||||
|
||||
let filteredTabs = [];
|
||||
|
||||
for (const openTab of openTabs) {
|
||||
for (const openTab of tabsToOpen) {
|
||||
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
||||
|
||||
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}) {
|
||||
if (hoistedNoteId === 'root') {
|
||||
return;
|
||||
|
||||
@@ -98,9 +98,8 @@ async function prepareNode(branch) {
|
||||
key: utils.randomString(12) // this should prevent some "duplicate key" errors
|
||||
};
|
||||
|
||||
if (note.hasChildren() || note.type === 'search') {
|
||||
node.folder = true;
|
||||
}
|
||||
node.folder = getChildBranchesWithoutImages(note).length > 0
|
||||
|| note.type === 'search';
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -108,16 +107,9 @@ async function prepareNode(branch) {
|
||||
async function prepareRealBranch(parentNote) {
|
||||
utils.assertArguments(parentNote);
|
||||
|
||||
const childBranches = await parentNote.getChildBranches();
|
||||
|
||||
if (!childBranches) {
|
||||
ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const noteList = [];
|
||||
|
||||
for (const branch of childBranches) {
|
||||
for (const branch of getChildBranchesWithoutImages(parentNote)) {
|
||||
const node = await prepareNode(branch);
|
||||
|
||||
noteList.push(node);
|
||||
@@ -126,6 +118,20 @@ async function prepareRealBranch(parentNote) {
|
||||
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) {
|
||||
await treeCache.reloadNotes([note.noteId]);
|
||||
|
||||
@@ -170,5 +176,6 @@ export default {
|
||||
prepareRootNode,
|
||||
prepareBranch,
|
||||
getExtraClasses,
|
||||
getIcon
|
||||
getIcon,
|
||||
getChildBranchesWithoutImages
|
||||
}
|
||||
@@ -56,7 +56,8 @@ class TreeContextMenu {
|
||||
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
|
||||
|
||||
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",
|
||||
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
|
||||
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
||||
@@ -111,6 +112,9 @@ class TreeContextMenu {
|
||||
if (command === 'openInTab') {
|
||||
appContext.tabManager.openTabWithNote(notePath);
|
||||
}
|
||||
else if (command === 'openInWindow') {
|
||||
appContext.openInNewWindow(notePath);
|
||||
}
|
||||
else if (command === "insertNoteAfter") {
|
||||
const parentNoteId = this.node.data.parentNoteId;
|
||||
const isProtected = await treeService.getParentProtectedStatus(this.node);
|
||||
|
||||
@@ -40,6 +40,10 @@ function formatDateTime(date) {
|
||||
return formatDate(date) + " " + formatTime(date);
|
||||
}
|
||||
|
||||
function localNowDateTime() {
|
||||
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||
}
|
||||
|
||||
function now() {
|
||||
return formatTimeWithSeconds(new Date());
|
||||
}
|
||||
@@ -239,7 +243,7 @@ function focusSavedElement() {
|
||||
$lastFocusedElement = null;
|
||||
}
|
||||
|
||||
function openDialog($dialog) {
|
||||
async function openDialog($dialog) {
|
||||
closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
@@ -253,6 +257,9 @@ function openDialog($dialog) {
|
||||
focusSavedElement();
|
||||
}
|
||||
});
|
||||
|
||||
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
|
||||
keyboardActionsService.updateDisplayedShortcuts($dialog);
|
||||
}
|
||||
|
||||
function isHtmlEmpty(html) {
|
||||
@@ -318,6 +325,7 @@ export default {
|
||||
formatDate,
|
||||
formatDateISO,
|
||||
formatDateTime,
|
||||
localNowDateTime,
|
||||
now,
|
||||
isElectron,
|
||||
isMac,
|
||||
|
||||
@@ -57,7 +57,10 @@ class BasicWidget extends Component {
|
||||
for (const key in this.attrs) {
|
||||
if (key === 'style') {
|
||||
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 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import utils from "../services/utils.js";
|
||||
import linkService from "../services/link.js";
|
||||
import ws from "../services/ws.js";
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import ws from "../../services/ws.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
|
||||
export default class AttributesWidget extends CollapsibleWidget {
|
||||
get widgetTitle() { return "Attributes"; }
|
||||
@@ -16,7 +16,7 @@ export default class AttributesWidget extends CollapsibleWidget {
|
||||
get headerActions() {
|
||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||
$showFullButton.on('click', async () => {
|
||||
const attributesDialog = await import("../dialogs/attributes.js");
|
||||
const attributesDialog = await import("../../dialogs/attributes.js");
|
||||
attributesDialog.showDialog();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import libraryLoader from "../services/library_loader.js";
|
||||
import utils from "../services/utils.js";
|
||||
import dateNoteService from "../services/date_notes.js";
|
||||
import server from "../services/server.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
import libraryLoader from "../../services/library_loader.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import dateNoteService from "../../services/date_notes.js";
|
||||
import server from "../../services/server.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="calendar-widget">
|
||||
@@ -1,7 +1,7 @@
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import linkService from "../services/link.js";
|
||||
import server from "../services/server.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import server from "../../services/server.js";
|
||||
import treeCache from "../../services/tree_cache.js";
|
||||
|
||||
export default class EditedNotesWidget extends CollapsibleWidget {
|
||||
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;
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
||||
get headerActions() {
|
||||
const $showFullButton = $("<a>").append("show full").addClass('widget-header-action');
|
||||
$showFullButton.on('click', async () => {
|
||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
||||
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||
linkMapDialog.showDialog();
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export default class LinkMapWidget extends CollapsibleWidget {
|
||||
const $linkMapContainer = this.$body.find('.link-map-container');
|
||||
$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, {
|
||||
maxDepth: 1,
|
||||
@@ -1,4 +1,4 @@
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<table class="note-info-widget-table">
|
||||
@@ -1,5 +1,5 @@
|
||||
import server from "../services/server.js";
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import server from "../../services/server.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<ul class="note-revision-list" style="max-height: 150px; overflow: auto;">
|
||||
@@ -19,7 +19,7 @@ class NoteRevisionsWidget extends CollapsibleWidget {
|
||||
get headerActions() {
|
||||
const $showFullButton = $("<a>").append("show dialog").addClass('widget-header-action');
|
||||
$showFullButton.on('click', async () => {
|
||||
const attributesDialog = await import("../dialogs/note_revisions.js");
|
||||
const attributesDialog = await import("../../dialogs/note_revisions.js");
|
||||
attributesDialog.showCurrentNoteRevisions(this.noteId);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import linkService from "../services/link.js";
|
||||
import server from "../services/server.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
import linkService from "../../services/link.js";
|
||||
import server from "../../services/server.js";
|
||||
import treeCache from "../../services/tree_cache.js";
|
||||
|
||||
export default class SimilarNotesWidget extends CollapsibleWidget {
|
||||
get widgetTitle() { return "Similar notes"; }
|
||||
@@ -1,5 +1,5 @@
|
||||
import CollapsibleWidget from "./collapsible_widget.js";
|
||||
import linkService from "../services/link.js";
|
||||
import CollapsibleWidget from "../collapsible_widget.js";
|
||||
import linkService from "../../services/link.js";
|
||||
|
||||
export default class WhatLinksHereWidget extends CollapsibleWidget {
|
||||
get widgetTitle() { return "What links here"; }
|
||||
@@ -13,7 +13,7 @@ export default class WhatLinksHereWidget extends CollapsibleWidget {
|
||||
get headerActions() {
|
||||
const $showFullButton = $("<a>").append("show link map").addClass('widget-header-action');
|
||||
$showFullButton.on('click', async () => {
|
||||
const linkMapDialog = await import("../dialogs/link_map.js");
|
||||
const linkMapDialog = await import("../../dialogs/link_map.js");
|
||||
linkMapDialog.showDialog();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<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 appContext from "../services/app_context.js";
|
||||
import contextMenu from "../services/context_menu.js";
|
||||
import noteCreateService from "../services/note_create.js";
|
||||
import branchService from "../services/branches.js";
|
||||
import BasicWidget from "../basic_widget.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
import contextMenu from "../../services/context_menu.js";
|
||||
import noteCreateService from "../../services/note_create.js";
|
||||
import branchService from "../../services/branches.js";
|
||||
|
||||
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 = `
|
||||
<div id="global-buttons">
|
||||
@@ -1,4 +1,4 @@
|
||||
import Component from "./component.js";
|
||||
import Component from "../component.js";
|
||||
|
||||
export default class MobileScreenSwitcherExecutor extends Component {
|
||||
setActiveScreenCommand({screen}) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import FlexContainer from "./flex_container.js";
|
||||
import FlexContainer from "../flex_container.js";
|
||||
|
||||
export default class ScreenContainer extends FlexContainer {
|
||||
constructor(screenName, direction) {
|
||||
@@ -6,7 +6,7 @@ import server from "../services/server.js";
|
||||
import libraryLoader from "../services/library_loader.js";
|
||||
import EmptyTypeWidget from "./type_widgets/empty.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 ImageTypeWidget from "./type_widgets/image.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 DeletedTypeWidget from "./type_widgets/deleted.js";
|
||||
import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js";
|
||||
import ReadOnlyCodeTypeWidget from "./type_widgets/read_only_code.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail">
|
||||
@@ -38,7 +39,8 @@ const typeWidgetClasses = {
|
||||
'deleted': DeletedTypeWidget,
|
||||
'editable-text': EditableTextTypeWidget,
|
||||
'read-only-text': ReadOnlyTextTypeWidget,
|
||||
'code': CodeTypeWidget,
|
||||
'editable-code': EditableCodeTypeWidget,
|
||||
'read-only-code': ReadOnlyCodeTypeWidget,
|
||||
'file': FileTypeWidget,
|
||||
'image': ImageTypeWidget,
|
||||
'search': SearchTypeWidget,
|
||||
@@ -61,6 +63,8 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
const dto = note.dto;
|
||||
dto.content = this.getTypeWidget().getContent();
|
||||
|
||||
protectedSessionHolder.touchProtectedSessionIfNecessary(note);
|
||||
|
||||
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') {
|
||||
type = 'editable-text';
|
||||
}
|
||||
|
||||
if (type === 'code') {
|
||||
type = 'editable-code';
|
||||
}
|
||||
|
||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
type = 'protected-session';
|
||||
}
|
||||
@@ -274,6 +291,12 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
codePreviewDisabledEvent({tabContext}) {
|
||||
if (this.isTab(tabContext.tabId)) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async cutIntoNoteCommand() {
|
||||
const note = appContext.tabManager.getActiveTabNote();
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export default class NotePathsWidget extends TabAwareWidget {
|
||||
this.$currentPath.append(
|
||||
$("<a>")
|
||||
.attr('href', '#' + curPath)
|
||||
.attr('data-note-path', curPath)
|
||||
.addClass('no-tooltip-preview')
|
||||
.text(await treeService.getNoteTitle(noteId, parentNoteId))
|
||||
);
|
||||
|
||||
@@ -29,6 +29,8 @@ export default class NoteTitleWidget extends TabAwareWidget {
|
||||
this.spacedUpdate = new SpacedUpdate(async () => {
|
||||
const title = this.$noteTitle.val();
|
||||
|
||||
protectedSessionHolder.touchProtectedSessionIfNecessary(this.note);
|
||||
|
||||
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.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.extraClasses = treeBuilder.getExtraClasses(note);
|
||||
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
|
||||
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()) {
|
||||
|
||||
@@ -5,12 +5,12 @@ const TPL = `
|
||||
<div class="btn-group btn-group-xs">
|
||||
<button type="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 type="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>
|
||||
</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) {
|
||||
// stop propagation of the event to the children, individual tab widget should not know about tab switching
|
||||
// since they are per-tab
|
||||
if (name === 'tabNoteSwitchedAndActivated') {
|
||||
name = 'tabNoteSwitched';
|
||||
}
|
||||
|
||||
if (name === 'tabNoteSwitched') {
|
||||
if (['tabNoteSwitched', 'tabNoteSwitchedAndActivated'].includes(name)) {
|
||||
// this event is propagated only to the widgets of a particular tab
|
||||
const widget = this.widgets[data.tabContext.tabId];
|
||||
|
||||
if (widget) {
|
||||
return widget.handleEvent(name, data);
|
||||
if (widget && (widget.hasBeenAlreadyShown || name === 'tabNoteSwitchedAndActivated')) {
|
||||
widget.hasBeenAlreadyShown = true;
|
||||
|
||||
return widget.handleEvent('tabNoteSwitched', data);
|
||||
}
|
||||
else {
|
||||
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 Promise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -258,8 +258,9 @@ export default class TabRowWidget extends BasicWidget {
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
items: [
|
||||
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "empty"},
|
||||
{title: "Close all tabs", command: "removeAllTabs", uiIcon: "empty"},
|
||||
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"}
|
||||
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"},
|
||||
],
|
||||
selectMenuItemHandler: ({command}) => {
|
||||
this.triggerCommand(command, {tabId});
|
||||
|
||||
@@ -21,8 +21,8 @@ const TPL = `
|
||||
<div class="note-detail-code-editor"></div>
|
||||
</div>`;
|
||||
|
||||
export default class CodeTypeWidget extends TypeWidget {
|
||||
static getType() { return "code"; }
|
||||
export default class EditableCodeTypeWidget extends TypeWidget {
|
||||
static getType() { return "editable-code"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
@@ -1,13 +1,9 @@
|
||||
import libraryLoader from "../../services/library_loader.js";
|
||||
import noteAutocompleteService from '../../services/note_autocomplete.js';
|
||||
import mimeTypesService from '../../services/mime_types.js';
|
||||
import TypeWidget from "./type_widget.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
import keyboardActionService from "../../services/keyboard_actions.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";
|
||||
|
||||
const ENABLE_INSPECTOR = false;
|
||||
@@ -60,6 +56,7 @@ const TPL = `
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
font-family: var(--detail-text-font-family);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
||||
codePreviewDisabledEvent({tabContext}) {
|
||||
if (this.isTab(tabContext.tabId)) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ async function loginSync(req) {
|
||||
|
||||
// login token is valid for 5 minutes
|
||||
if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
|
||||
return [400, { message: 'Auth request time is out of sync' }];
|
||||
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;
|
||||
|
||||
@@ -18,13 +18,11 @@ async function getNoteRevision(req) {
|
||||
|
||||
if (noteRevision.type === 'file') {
|
||||
if (noteRevision.isStringNote()) {
|
||||
await noteRevision.getContent();
|
||||
|
||||
noteRevision.content = noteRevision.content.substr(0, 10000);
|
||||
noteRevision.content = (await noteRevision.getContent()).substr(0, 10000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await noteRevision.getContent();
|
||||
noteRevision.content = await noteRevision.getContent();
|
||||
|
||||
if (noteRevision.content && noteRevision.type === 'image') {
|
||||
noteRevision.content = noteRevision.content.toString('base64');
|
||||
|
||||
@@ -13,17 +13,17 @@ async function getRecentChanges(req) {
|
||||
SELECT * FROM (
|
||||
SELECT note_revisions.noteId,
|
||||
note_revisions.noteRevisionId,
|
||||
note_revisions.utcDateCreated AS date
|
||||
note_revisions.dateLastEdited AS date
|
||||
FROM note_revisions
|
||||
ORDER BY note_revisions.utcDateCreated DESC
|
||||
ORDER BY note_revisions.dateLastEdited DESC
|
||||
)
|
||||
UNION ALL SELECT * FROM (
|
||||
SELECT
|
||||
notes.noteId,
|
||||
NULL AS noteRevisionId,
|
||||
utcDateModified AS date
|
||||
dateModified AS date
|
||||
FROM notes
|
||||
ORDER BY utcDateModified DESC
|
||||
ORDER BY dateModified DESC
|
||||
)
|
||||
ORDER BY date DESC`);
|
||||
|
||||
@@ -44,7 +44,7 @@ async function getRecentChanges(req) {
|
||||
notes.title AS current_title,
|
||||
notes.isProtected AS current_isProtected,
|
||||
note_revisions.title,
|
||||
note_revisions.utcDateCreated AS date
|
||||
note_revisions.dateCreated AS date
|
||||
FROM
|
||||
note_revisions
|
||||
JOIN notes USING(noteId)
|
||||
@@ -60,7 +60,7 @@ async function getRecentChanges(req) {
|
||||
notes.title AS current_title,
|
||||
notes.isProtected AS current_isProtected,
|
||||
notes.title,
|
||||
notes.utcDateModified AS date
|
||||
notes.dateModified AS date
|
||||
FROM
|
||||
notes
|
||||
WHERE noteId = ?`, [noteRow.noteId]));
|
||||
|
||||
@@ -11,7 +11,7 @@ const env = require('../services/env');
|
||||
async function index(req, res) {
|
||||
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();
|
||||
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"),
|
||||
instanceName: config.General ? config.General.instanceName : null,
|
||||
appCssNoteIds: await getAppCssNoteIds(),
|
||||
isDev: env.isDev()
|
||||
isDev: env.isDev(),
|
||||
isMainWindow: !req.query.extra
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
const setupRoute = require('./setup');
|
||||
const loginRoute = require('./login');
|
||||
const indexRoute = require('./index');
|
||||
@@ -82,6 +84,7 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
|
||||
try {
|
||||
const result = await cls.init(async () => {
|
||||
cls.namespace.set('sourceId', req.headers['trilium-source-id']);
|
||||
cls.namespace.set('localNowDateTime', req.headers['`trilium-local-now-datetime`']);
|
||||
protectedSessionService.setProtectedSessionId(req);
|
||||
|
||||
if (transactional) {
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
const sqlInit = require('../services/sql_init');
|
||||
const setupService = require('../services/setup');
|
||||
const utils = require('../services/utils');
|
||||
const windowService = require('../services/window');
|
||||
|
||||
async function setupPage(req, res) {
|
||||
if (await sqlInit.isDbInitialized()) {
|
||||
const windowService = require('../services/window');
|
||||
|
||||
if (utils.isElectron()) {
|
||||
await windowService.createMainWindow();
|
||||
windowService.closeSetupWindow();
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2020-04-15T23:00:13+02:00", buildRevision: "dadcc93ae37fc03918837630c0a5a27a69af3495" };
|
||||
module.exports = { buildDate:"2020-04-20T22:40:02+02:00", buildRevision: "a86177bb597c752fbc96a24d4be7ab5ae6c0344d" };
|
||||
|
||||
@@ -12,7 +12,9 @@ const VIRTUAL_ATTRIBUTES = [
|
||||
"type",
|
||||
"mime",
|
||||
"text",
|
||||
"parentCount"
|
||||
"parentCount",
|
||||
"attributeName",
|
||||
"attributeValue"
|
||||
];
|
||||
|
||||
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
|
||||
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
|
||||
+ `ON ${alias}.noteId = notes.noteId `
|
||||
+ `AND ${alias}.name = '${property}' AND ${alias}.isDeleted = 0`;
|
||||
+ `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0 `
|
||||
+ `AND ${alias}.name = '${property}' `;
|
||||
|
||||
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') {
|
||||
const alias = "note_contents";
|
||||
|
||||
@@ -73,79 +93,85 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
|
||||
});
|
||||
}
|
||||
|
||||
let where = '1';
|
||||
const params = [];
|
||||
|
||||
for (const filter of filters) {
|
||||
if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
|
||||
continue; // these are not real filters
|
||||
}
|
||||
function parseWhereFilters(filters) {
|
||||
let whereStmt = '';
|
||||
|
||||
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') {
|
||||
where += `${accessor} IS NOT NULL`;
|
||||
}
|
||||
else if (filter.operator === 'not-exists') {
|
||||
where += `${accessor} IS NULL`;
|
||||
}
|
||||
else if (filter.operator === '=' || filter.operator === '!=') {
|
||||
where += `${accessor} ${filter.operator} ?`;
|
||||
params.push(filter.value);
|
||||
}
|
||||
else if (filter.operator === '*=' || filter.operator === '!*=') {
|
||||
where += `${accessor}`
|
||||
if (filter.children) {
|
||||
whereStmt += "(" + parseWhereFilters(filter.children) + ")";
|
||||
continue;
|
||||
}
|
||||
|
||||
const accessor = getAccessor(filter.name);
|
||||
|
||||
if (filter.operator === 'exists') {
|
||||
whereStmt += `${accessor} IS NOT NULL`;
|
||||
} else if (filter.operator === 'not-exists') {
|
||||
whereStmt += `${accessor} IS NULL`;
|
||||
} 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' : '')
|
||||
+ ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
|
||||
}
|
||||
else if (filter.operator === '=*' || filter.operator === '!=*') {
|
||||
where += `${accessor}`
|
||||
} else if (filter.operator === '=*' || filter.operator === '!=*') {
|
||||
whereStmt += `${accessor}`
|
||||
+ (filter.operator.includes('!') ? ' NOT' : '')
|
||||
+ ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
|
||||
}
|
||||
else if (filter.operator === '*=*' || filter.operator === '!*=*') {
|
||||
const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
|
||||
} else if (filter.operator === '*=*' || filter.operator === '!*=*') {
|
||||
const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
|
||||
|
||||
let condition = "(" + columns.map(column =>
|
||||
`${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
|
||||
.join(" OR ") + ")";
|
||||
let condition = "(" + columns.map(column =>
|
||||
`${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
|
||||
.join(" OR ") + ")";
|
||||
|
||||
if (filter.operator.includes('!')) {
|
||||
condition = "NOT(" + condition + ")";
|
||||
}
|
||||
if (filter.operator.includes('!')) {
|
||||
condition = "NOT(" + condition + ")";
|
||||
}
|
||||
|
||||
if (['text', 'title', 'content'].includes(filter.name)) {
|
||||
// for title/content search does not make sense to search for protected notes
|
||||
condition = `(${condition} AND notes.isProtected = 0)`;
|
||||
}
|
||||
if (['text', 'title', 'content'].includes(filter.name)) {
|
||||
// for title/content search does not make sense to search for protected notes
|
||||
condition = `(${condition} AND notes.isProtected = 0)`;
|
||||
}
|
||||
|
||||
where += condition;
|
||||
}
|
||||
else if ([">", ">=", "<", "<="].includes(filter.operator)) {
|
||||
let floatParam;
|
||||
whereStmt += condition;
|
||||
} else if ([">", ">=", "<", "<="].includes(filter.operator)) {
|
||||
let floatParam;
|
||||
|
||||
// from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
|
||||
if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
|
||||
floatParam = parseFloat(filter.value);
|
||||
}
|
||||
// from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
|
||||
if (/^[+-]?([0-9]*[.])?[0-9]+$/.test(filter.value)) {
|
||||
floatParam = parseFloat(filter.value);
|
||||
}
|
||||
|
||||
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
|
||||
where += `${accessor} ${filter.operator} ?`;
|
||||
params.push(filter.value);
|
||||
}
|
||||
else {
|
||||
where += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
|
||||
params.push(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
|
||||
whereStmt += `${accessor} ${filter.operator} ?`;
|
||||
params.push(filter.value);
|
||||
} else {
|
||||
whereStmt += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
|
||||
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 no ordering is given then order at least by note title
|
||||
orderBy.push("notes.title");
|
||||
|
||||
@@ -13,6 +13,10 @@ function getSourceId() {
|
||||
return namespace.get('sourceId');
|
||||
}
|
||||
|
||||
function getLocalNowDateTime() {
|
||||
return namespace.get('localNowDateTime');
|
||||
}
|
||||
|
||||
function disableEntityEvents() {
|
||||
namespace.set('disableEntityEvents', true);
|
||||
}
|
||||
@@ -50,6 +54,7 @@ module.exports = {
|
||||
wrap,
|
||||
namespace,
|
||||
getSourceId,
|
||||
getLocalNowDateTime,
|
||||
disableEntityEvents,
|
||||
isEntityEventsDisabled,
|
||||
reset,
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
const dayjs = require('dayjs');
|
||||
const cls = require('./cls');
|
||||
|
||||
function utcNowDateTime() {
|
||||
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() {
|
||||
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||
return cls.getLocalNowDateTime()
|
||||
|| dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
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: '*=*',
|
||||
value: searchText
|
||||
});
|
||||
|
||||
filters.push({
|
||||
relation: 'or',
|
||||
name: 'attributeName',
|
||||
operator: '*=*',
|
||||
value: searchText
|
||||
});
|
||||
|
||||
filters.push({
|
||||
relation: 'or',
|
||||
name: 'attributeValue',
|
||||
operator: '*=*',
|
||||
value: searchText
|
||||
});
|
||||
}
|
||||
else {
|
||||
const tokens = searchText.split(/\s+/);
|
||||
@@ -67,9 +81,27 @@ module.exports = function (searchText) {
|
||||
for (const token of tokens) {
|
||||
filters.push({
|
||||
relation: 'and',
|
||||
name: 'text',
|
||||
operator: '*=*',
|
||||
value: token
|
||||
name: 'sub',
|
||||
children: [
|
||||
{
|
||||
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 cls = require('./cls');
|
||||
const keyboardActionsService = require('./keyboard_actions');
|
||||
const {ipcMain} = require('electron');
|
||||
|
||||
// Prevent window being garbage collected
|
||||
/** @type {Electron.BrowserWindow} */
|
||||
@@ -14,6 +15,29 @@ let mainWindow;
|
||||
/** @type {Electron.BrowserWindow} */
|
||||
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() {
|
||||
const windowStateKeeper = require('electron-window-state'); // should not be statically imported
|
||||
|
||||
@@ -68,7 +92,7 @@ async function createMainWindow() {
|
||||
|
||||
if (spellcheckEnabled) {
|
||||
const languageCodes = (await optionService.getOption('spellCheckLanguageCode'))
|
||||
.split('/')
|
||||
.split(',')
|
||||
.map(code => code.trim());
|
||||
|
||||
webContents.session.setSpellCheckerLanguages(languageCodes);
|
||||
@@ -141,5 +165,6 @@ module.exports = {
|
||||
createMainWindow,
|
||||
createSetupWindow,
|
||||
closeSetupWindow,
|
||||
createExtraWindow,
|
||||
registerGlobalShortcuts
|
||||
};
|
||||
@@ -46,8 +46,9 @@
|
||||
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
||||
instanceName: '<%= instanceName %>',
|
||||
csrfToken: '<%= csrfToken %>',
|
||||
isDev: '<%= isDev %>',
|
||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>
|
||||
isDev: <%= isDev %>,
|
||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
||||
isMainWindow: <%= isMainWindow %>
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ul>
|
||||
<li><kbd>UP</kbd>, <kbd>DOWN</kbd> - go up/down in the list of notes</li>
|
||||
<li><kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - collapse/expand node</li>
|
||||
<li><kbd data-command="backInNoteHistory"></kbd>, <kbd data-command="BackInNoteHistory"></kbd> - go back / forwards in the history</li>
|
||||
<li><kbd data-command="backInNoteHistory"></kbd>, <kbd data-command="forwardInNoteHistory"></kbd> - go back / forwards in the history</li>
|
||||
<li><kbd data-command="jumpToNote"></kbd> - show <a class="external" href="https://github.com/zadam/trilium/wiki/Note-navigation#jump-to-note">"Jump to" dialog</a></li>
|
||||
<li><kbd data-command="scrollToActiveNote"></kbd> - scroll to active note</li>
|
||||
<li><kbd data-command="activateParentNote"></kbd> - jumps to parent note</li>
|
||||
@@ -69,9 +69,9 @@
|
||||
|
||||
<p class="card-text">
|
||||
<ul>
|
||||
<li><kbd data-command="moveNoteUp"></kbd>, <kbd data-command="MoveNoteDown"></kbd> - move note up/down in the note list</li>
|
||||
<li><kbd data-command="moveNoteUpInHierarchy"></kbd>, <kbd data-command="MoveNoteDownInHierarchy"></kbd> - move note up in the hierarchy</li>
|
||||
<li><kbd data-command="addNoteAboveToSelection"></kbd>, <kbd data-command="AddNoteBelowToSelection"></kbd> - multi-select note above/below</li>
|
||||
<li><kbd data-command="moveNoteUp"></kbd>, <kbd data-command="moveNoteDown"></kbd> - move note up/down in the note list</li>
|
||||
<li><kbd data-command="moveNoteUpInHierarchy"></kbd>, <kbd data-command="moveNoteDownInHierarchy"></kbd> - move note up in the hierarchy</li>
|
||||
<li><kbd data-command="addNoteAboveToSelection"></kbd>, <kbd data-command="addNoteBelowToSelection"></kbd> - multi-select note above/below</li>
|
||||
<li><kbd data-command="selectAllNotesInParent"></kbd> - select all notes in the current level</li>
|
||||
<li><kbd>Shift+click</kbd> - select note</li>
|
||||
<li><kbd data-command="copyNotesToClipboard"></kbd> - copies active note (or current selection) into clipboard (used for <a class="external" href="https://github.com/zadam/trilium/wiki/Cloning notes">cloning</a>)</li>
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><a class="external" href="https://github.com/zadam/trilium/wiki/Text-editor#autoformat">Markdown-like autoformatting</a></h5>
|
||||
<h5 class="card-title"><a class="external" href="https://github.com/zadam/trilium/wiki/Text-notes#autoformat">Markdown-like autoformatting</a></h5>
|
||||
|
||||
<p class="card-text">
|
||||
<ul>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div id="note-revision-title-buttons"></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>
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
maxSyncIdAtLoad: <%= maxSyncIdAtLoad %>,
|
||||
instanceName: '<%= instanceName %>',
|
||||
csrfToken: '<%= csrfToken %>',
|
||||
isDev: '<%= isDev %>',
|
||||
isDev: <%= isDev %>,
|
||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user