Merge branch 'master' into next60

# Conflicts:
#	package-lock.json
#	package.json
This commit is contained in:
zadam 2023-04-10 18:52:30 +02:00
commit 15a11daf38
62 changed files with 2281 additions and 369 deletions

16
.eslintrc.js Normal file
View file

@ -0,0 +1,16 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest"
},
"rules": {
}
}

View file

@ -1,7 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JSUnfilteredForInLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
</component>
</project>

View file

@ -1,6 +1,6 @@
# Trilium Notes
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md)
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
[![Join the chat at https://gitter.im/trilium-notes/Lobby](https://badges.gitter.im/trilium-notes/Lobby.svg)](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Trilium Notes 是一个层次化的笔记应用程序,专注于建立大型个人知识库。请参阅[屏幕截图](https://github.com/zadam/trilium/wiki/Screenshot-tour)以快速了解:

83
README.ja.md Normal file
View file

@ -0,0 +1,83 @@
# Trilium Notes
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
Trilium Notes は、大規模な個人知識ベースの構築に焦点を当てた、階層型ノートアプリケーションです。概要は[スクリーンショット](https://github.com/zadam/trilium/wiki/Screenshot-tour)をご覧ください:
<a href="https://github.com/zadam/trilium/wiki/Screenshot-tour"><img src="https://raw.githubusercontent.com/wiki/zadam/trilium/images/screenshot.png" alt="Trilium Screenshot" width="1000"></a>
ウクライナは現在、ロシアの侵略から自国を守っています。[ウクライナ軍や人道的な慈善団体への寄付](https://standforukraine.com/)をご検討ください。
<p float="left">
<img src="https://upload.wikimedia.org/wikipedia/commons/4/49/Flag_of_Ukraine.svg" alt="drawing" width="400"/>
<img src="https://signmyrocket.com//uploads/2b2a523cd0c0e76cdbba95a89a9636b2_1676971281.jpg" alt="Trilium Notes supports Ukraine!" width="570"/>
</p>
## 🎁 特徴
* ノートは、任意の深さのツリーに配置できます。単一のノートをツリー内の複数の場所に配置できます ([cloning](https://github.com/zadam/trilium/wiki/Cloning-notes) を参照)
* マークダウン[オートフォーマット](https://github.com/zadam/trilium/wiki/Text-notes#autoformat)による、表、画像、[数学](https://github.com/zadam/trilium/wiki/Text-notes#math-support)などの豊富な WYSIWYG ノート編集機能
* シンタックスハイライトを含む[ソースコード付きノート](https://github.com/zadam/trilium/wiki/Code-notes)の編集をサポート
* [ノート間のナビゲーション](https://github.com/zadam/trilium/wiki/Note-navigation)、全文検索、[ノートホイスト](https://github.com/zadam/trilium/wiki/Note-hoisting)が高速かつ簡単に行えます
* シームレスな[ノートのバージョン管理](https://github.com/zadam/trilium/wiki/Note-revisions)
* ノート[属性](https://github.com/zadam/trilium/wiki/Attributes)は、ノート整理、クエリ、高度な[スクリプト](https://github.com/zadam/trilium/wiki/Scripts)に使用できます
* 自己ホスト型同期サーバーとの[同期](https://github.com/zadam/trilium/wiki/Synchronization)
* [同期サーバーをホストするサードパーティ・サービス](https://trilium.cc/paid-hosting)があります
* 公開インターネットへのノートの[共有](https://github.com/zadam/trilium/wiki/Sharing)(公開)
* ノートごとの粒度を持つ強力な[ノート暗号化](https://github.com/zadam/trilium/wiki/Protected-notes)
* 組み込みの Excalidraw を使用した図のスケッチ (ノート タイプ"キャンバス")
* ノートとその関係を可視化するための[関係図](https://github.com/zadam/trilium/wiki/Relation-map)と[リンクマップ](https://github.com/zadam/trilium/wiki/Link-map)
* [スクリプティング](https://github.com/zadam/trilium/wiki/Scripts) - [高度なショーケース](https://github.com/zadam/trilium/wiki/Advanced-showcases)を参照
* 自動化のための [REST API](https://github.com/zadam/trilium/wiki/ETAPI)
* ユーザビリティとパフォーマンスの両方で 100 000 ノート以上に拡張可能
* スマートフォンとタブレット向けのタッチ最適化[モバイルフロントエンド](https://github.com/zadam/trilium/wiki/Mobile-frontend)
* [ナイトテーマ](https://github.com/zadam/trilium/wiki/Themes)
* [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) と [Markdown のインポートとエクスポート](https://github.com/zadam/trilium/wiki/Markdown)
* Web コンテンツを簡単に保存するための [Web クリッパー](https://github.com/zadam/trilium/wiki/Web-clipper)
サードパーティのテーマ、スクリプト、プラグインなどは、 [awesome-trilium](https://github.com/Nriver/awesome-trilium) をチェックしてください。
## 🏗 ビルド
Trilium は、デスクトップアプリケーションLinux、Windowsまたはサーバー上でホストされるウェブアプリケーションLinuxとして提供されます。 Mac OS のデスクトップビルドも利用可能ですが、 [unsupported](https://github.com/zadam/trilium/wiki/FAQ#mac-os-support) となっています。
* デスクトップで Trilium を使用したい場合は、 [latest release](https://github.com/zadam/trilium/releases/latest) からお使いのプラットフォームのバイナリリリースをダウンロードし、パッケージを解凍して ``trilium`` の実行ファイルを実行してください。
* サーバーに Trilium をインストールする場合は、[このページ](https://github.com/zadam/trilium/wiki/Server-installation)に従ってください。
* 現在、対応(動作確認)しているブラウザは、最近の Chrome と Firefox のみです。
Trilium は Flatpak としても提供されます:
[<img width="240" src="https://flathub.org/assets/badges/flathub-badge-en.png">](https://flathub.org/apps/details/com.github.zadam.trilium)
## 📝 ドキュメント
[ドキュメントページの全リストはwikiをご覧ください。](https://github.com/zadam/trilium/wiki/)
また、[個人的な知識基盤のパターン](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base)を読むと、 Trilium の使い方のヒントを得ることができます。
## 💻 コントリビュート
ブラウザベースの開発環境を使用
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/zadam/trilium)
または、ローカルにクローンして実行
```
npm install
npm run start-server
```
## 📢 シャウトアウト
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - 市場で最高の WYSIWYG エディター、非常にインタラクティブで聞き上手なチーム
* [FancyTree](https://github.com/mar10/fancytree) - 真の競争相手がいない、非常に機能豊富なツリーライブラリです。 Trilium Notes は、これなしでは成り立たないでしょう。
* [CodeMirror](https://github.com/codemirror/CodeMirror) - 膨大な数の言語をサポートするコードエディタ
* [jsPlumb](https://github.com/jsplumb/jsplumb) - 競合のないビジュアルコネクティビティライブラリです。[関係図](https://github.com/zadam/trilium/wiki/Relation-map)、[リンク図](https://github.com/zadam/trilium/wiki/Link-map)で使用。
## 🤝 サポート
GitHub スポンサー、[PayPal](https://paypal.me/za4am)もしくは Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2) にて Trilium をサポートすることができます。
## 🔑 ライセンス
このプログラムはフリーソフトウェアです:フリーソフトウェア財団が発行した GNU Affero General Public License のバージョン3、またはそれ以降のバージョンのいずれかに従って、再配布および/または改変することができます。

View file

@ -1,6 +1,6 @@
# Trilium Notes
[![Join the chat at https://gitter.im/trilium-notes/Lobby](https://badges.gitter.im/trilium-notes/Lobby.svg)](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md)
[![Join the chat at https://gitter.im/trilium-notes/Lobby](https://badges.gitter.im/trilium-notes/Lobby.svg)](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases. See [screenshots](https://github.com/zadam/trilium/wiki/Screenshot-tour) for quick overview:
@ -76,7 +76,7 @@ npm run start-server
## 🤝 Support
You can support Trilum using GitHub Sponsors, [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
You can support Trilium using GitHub Sponsors, [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
## 🔑 License

View file

@ -1,6 +1,6 @@
# Trilium Notes
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md)
[English](https://github.com/zadam/trilium/blob/master/README.md) | [Chinese](https://github.com/zadam/trilium/blob/master/README-ZH_CN.md) | [Russian](https://github.com/zadam/trilium/blob/master/README.ru.md) | [Japanese](https://github.com/zadam/trilium/blob/master/README.ja.md)
[![Join the chat at https://gitter.im/trilium-notes/Lobby](https://badges.gitter.im/trilium-notes/Lobby.svg)](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Trilium Notes это приложение для заметок с иерархической структурой, ориентированное на создание больших персональных баз знаний. Для быстрого ознакомления посмотрите [скриншот-тур](https://github.com/zadam/trilium/wiki/Screenshot-tour):

View file

@ -2,12 +2,18 @@ module.exports = () => {
const cls = require("../../src/services/cls");
const beccaLoader = require("../../src/becca/becca_loader");
const becca = require("../../src/becca/becca");
const log = require("../../src/services/log");
cls.init(() => {
beccaLoader.load();
const hidden = becca.getNote("_hidden");
if (!hidden) {
log.info("MIGRATION 212: no _hidden note, skipping.");
return;
}
for (const noteId of hidden.getSubtreeNoteIds({includeHidden: true})) {
if (noteId.startsWith("_")) { // is "named" note
const note = becca.getNote(noteId);

12
images/icon-black.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="53" height="40">
<g transform="matrix(0.51887405 0 0 0.51887405 -30.622847 -23.349333)">
<g>
<path d="M139.19968 46.483948C 133.79512 46.316727 130.99477 48.84615 130.3185 49.08075C 130.93373 47.91552 131.34544 47.18139 132.91882 45.54309C 127.567184 45.48699 122.60501 46.645172 118.819 50.18706C 113.74112 54.93721 114.13588 58.872803 114.12565 58.8534C 114.12665 58.8554 113.324486 56.28694 113.083885 53.53986C 107.557976 61.18914 105.376045 71.32445 109.565636 78.88518C 112.16631 72.611374 116.29593 67.204346 121.617935 62.940678C 121.79327 58.466747 123.59313 54.686737 126.437935 51.89591C 123.87077 54.96861 122.73801 58.46922 122.8523 61.99735C 129.0425 57.37914 136.62968 54.301147 144.96371 53.29115C 138.92981 54.39993 133.4628 56.804817 128.68442 60.08495C 131.67526 61.4509 135.09087 61.66504 138.68073 60.41479C 135.23903 62.02769 131.41844 62.291573 127.55341 60.88681C 124.123 63.39894 121.072174 66.36721 118.44998 69.62935C 123.10523 71.15123 128.19724 70.83655 133.26453 68.25387C 128.6128 71.25848 123.14792 72.38525 117.30839 71.10572C 114.98041 74.22992 113.02919 77.58555 111.50413 81.04383C 113.382675 82.42883 121.53502 86.77435 135.51878 76.4623C 133.54428 75.878815 131.94408 75.053665 131.94337 75.05225C 131.98787 75.07205 133.38448 75.200424 137.46893 72.753555C 141.07997 70.64182 144.18617 66.81666 146.47923 63.839214C 144.52872 63.120605 143.00966 62.366364 143.00896 62.364605C 143.00795 62.363605 145.52956 62.534286 149.44116 59.539906C 152.93013 56.869026 156.82515 52.997295 156.83716 53.106655C 156.86186 53.129955 148.2449 46.763355 139.19968 46.483955" stroke="none" fill="#000000" fill-rule="nonzero" />
<path d="M76.90892 60.39415C 81.01041 59.20187 83.62202 60.57837 84.18329 60.62363C 83.49581 59.85657 83.04422 59.37815 81.5362 58.43957C 85.61753 57.342182 89.62709 57.24689 93.18141 59.20068C 97.94861 61.82108 98.37924 64.89886 98.38368 64.88219C 98.38287 64.884186 98.51835 62.767998 98.19094 60.626408C 103.84037 65.368546 107.39591 72.664375 105.59999 79.25351C 102.44364 74.98344 98.27924 71.67578 93.41609 69.47424C 92.44918 66.098694 90.369 63.571594 87.674324 62.0049C 90.20933 63.84117 91.72702 66.28648 92.29676 68.9982C 86.70379 66.69781 80.32978 65.84657 73.76949 66.719246C 78.589516 66.37542 88.79929 67.0738 97.083725 73.94838C 93.808014 76.02605 89.85612 76.78993 85.50101 75.81959C 89.617004 77.19292 94.00519 76.97535 98.23167 74.84885C 100.59329 76.77167 102.71017 78.94501 104.520195 81.28075C 103.3416 82.70688 97.917564 87.62569 85.30545 82.520676C 86.70662 81.686905 87.77634 80.74236 87.77674 80.74117C 87.746544 80.76498 86.70259 81.13821 83.12366 80.07813C 79.96972 79.18005 76.88272 76.87608 74.574715 75.058075C 75.932335 74.12624 76.953674 73.25198 76.953674 73.2504C 76.95406 73.24959 75.05817 73.87652 71.50989 72.36501C 68.344666 71.016304 64.64558 68.832634 64.65687 68.918396C 64.642365 68.940994 70.04542 62.389626 76.90893 60.394146" stroke="none" fill="#000000" fill-rule="nonzero" />
<path d="M106.35028 116.78676C 103.35939 114.0036 103.23137 111.1697 102.99563 110.68548C 102.692345 111.63276 102.514435 112.24205 102.46853 113.950066C 99.56593 111.13644 97.53256 107.88377 97.42573 103.98627C 97.2827 98.76002 99.63347 96.87469 99.61758 96.879555C 99.619576 96.879555 97.79303 97.82024 96.170654 99.15473C 97.362595 92.146484 101.699936 85.59194 108.057846 83.79309C 106.043465 88.51463 105.32079 93.58698 105.859375 98.68461C 103.522285 101.15936 102.43365 104.12773 102.44335 107.12493C 102.7356 104.12552 104.0304 101.66008 106.00902 99.8426C 106.82087 105.59001 109.21976 111.25976 113.143005 116.22528C 110.507935 112.4288 106.112755 103.67846 107.79472 93.44062C 111.1198 95.10421 113.68158 97.97678 114.99713 102.04415C 114.133644 97.97323 111.813774 94.46955 107.98454 92.04802C 108.433075 89.14839 109.210045 86.32549 110.27087 83.67378C 112.03185 83.93501 118.768524 85.95295 120.66769 98.87222C 119.291214 98.13378 117.984055 97.72311 117.98273 97.72354C 118.017624 97.73644 118.836945 98.41008 119.69913 101.88272C 120.4889 104.925644 120.07702 108.61209 119.68893 111.41583C 118.25153 110.761665 117.02649 110.35595 117.025604 110.356834C 117.024605 110.356834 118.470055 111.60574 118.94197 115.277824C 119.36268 118.55367 119.348564 122.68398 119.41433 122.632C 119.44033 122.63284 111.35593 121.44354 106.35022 116.78675" stroke="none" fill="#000000" fill-rule="nonzero" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

12
images/icon-color.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="53" height="40">
<g transform="matrix(0.51887405 0 0 0.51887405 -30.622847 -23.349333)">
<g>
<path d="M139.19968 46.483948C 133.79512 46.316727 130.99477 48.84615 130.3185 49.08075C 130.93373 47.91552 131.34544 47.18139 132.91882 45.54309C 127.567184 45.48699 122.60501 46.645172 118.819 50.18706C 113.74112 54.93721 114.13588 58.872803 114.12565 58.8534C 114.12665 58.8554 113.324486 56.28694 113.083885 53.53986C 107.557976 61.18914 105.376045 71.32445 109.565636 78.88518C 112.16631 72.611374 116.29593 67.204346 121.617935 62.940678C 121.79327 58.466747 123.59313 54.686737 126.437935 51.89591C 123.87077 54.96861 122.73801 58.46922 122.8523 61.99735C 129.0425 57.37914 136.62968 54.301147 144.96371 53.29115C 138.92981 54.39993 133.4628 56.804817 128.68442 60.08495C 131.67526 61.4509 135.09087 61.66504 138.68073 60.41479C 135.23903 62.02769 131.41844 62.291573 127.55341 60.88681C 124.123 63.39894 121.072174 66.36721 118.44998 69.62935C 123.10523 71.15123 128.19724 70.83655 133.26453 68.25387C 128.6128 71.25848 123.14792 72.38525 117.30839 71.10572C 114.98041 74.22992 113.02919 77.58555 111.50413 81.04383C 113.382675 82.42883 121.53502 86.77435 135.51878 76.4623C 133.54428 75.878815 131.94408 75.053665 131.94337 75.05225C 131.98787 75.07205 133.38448 75.200424 137.46893 72.753555C 141.07997 70.64182 144.18617 66.81666 146.47923 63.839214C 144.52872 63.120605 143.00966 62.366364 143.00896 62.364605C 143.00795 62.363605 145.52956 62.534286 149.44116 59.539906C 152.93013 56.869026 156.82515 52.997295 156.83716 53.106655C 156.86186 53.129955 148.2449 46.763355 139.19968 46.483955" stroke="none" fill="#4FA52B" fill-rule="nonzero" />
<path d="M76.90892 60.39415C 81.01041 59.20187 83.62202 60.57837 84.18329 60.62363C 83.49581 59.85657 83.04422 59.37815 81.5362 58.43957C 85.61753 57.342182 89.62709 57.24689 93.18141 59.20068C 97.94861 61.82108 98.37924 64.89886 98.38368 64.88219C 98.38287 64.884186 98.51835 62.767998 98.19094 60.626408C 103.84037 65.368546 107.39591 72.664375 105.59999 79.25351C 102.44364 74.98344 98.27924 71.67578 93.41609 69.47424C 92.44918 66.098694 90.369 63.571594 87.674324 62.0049C 90.20933 63.84117 91.72702 66.28648 92.29676 68.9982C 86.70379 66.69781 80.32978 65.84657 73.76949 66.719246C 78.589516 66.37542 88.79929 67.0738 97.083725 73.94838C 93.808014 76.02605 89.85612 76.78993 85.50101 75.81959C 89.617004 77.19292 94.00519 76.97535 98.23167 74.84885C 100.59329 76.77167 102.71017 78.94501 104.520195 81.28075C 103.3416 82.70688 97.917564 87.62569 85.30545 82.520676C 86.70662 81.686905 87.77634 80.74236 87.77674 80.74117C 87.746544 80.76498 86.70259 81.13821 83.12366 80.07813C 79.96972 79.18005 76.88272 76.87608 74.574715 75.058075C 75.932335 74.12624 76.953674 73.25198 76.953674 73.2504C 76.95406 73.24959 75.05817 73.87652 71.50989 72.36501C 68.344666 71.016304 64.64558 68.832634 64.65687 68.918396C 64.642365 68.940994 70.04542 62.389626 76.90893 60.394146" stroke="none" fill="#E47B19" fill-rule="nonzero" />
<path d="M106.35028 116.78676C 103.35939 114.0036 103.23137 111.1697 102.99563 110.68548C 102.692345 111.63276 102.514435 112.24205 102.46853 113.950066C 99.56593 111.13644 97.53256 107.88377 97.42573 103.98627C 97.2827 98.76002 99.63347 96.87469 99.61758 96.879555C 99.619576 96.879555 97.79303 97.82024 96.170654 99.15473C 97.362595 92.146484 101.699936 85.59194 108.057846 83.79309C 106.043465 88.51463 105.32079 93.58698 105.859375 98.68461C 103.522285 101.15936 102.43365 104.12773 102.44335 107.12493C 102.7356 104.12552 104.0304 101.66008 106.00902 99.8426C 106.82087 105.59001 109.21976 111.25976 113.143005 116.22528C 110.507935 112.4288 106.112755 103.67846 107.79472 93.44062C 111.1198 95.10421 113.68158 97.97678 114.99713 102.04415C 114.133644 97.97323 111.813774 94.46955 107.98454 92.04802C 108.433075 89.14839 109.210045 86.32549 110.27087 83.67378C 112.03185 83.93501 118.768524 85.95295 120.66769 98.87222C 119.291214 98.13378 117.984055 97.72311 117.98273 97.72354C 118.017624 97.73644 118.836945 98.41008 119.69913 101.88272C 120.4889 104.925644 120.07702 108.61209 119.68893 111.41583C 118.25153 110.761665 117.02649 110.35595 117.025604 110.356834C 117.024605 110.356834 118.470055 111.60574 118.94197 115.277824C 119.36268 118.55367 119.348564 122.68398 119.41433 122.632C 119.44033 122.63284 111.35593 121.44354 106.35022 116.78675" stroke="none" fill="#E33F3B" fill-rule="nonzero" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

12
images/icon-grey.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body_1" width="53" height="40">
<g transform="matrix(0.51887405 0 0 0.51887405 -30.622847 -23.349333)">
<g>
<path d="M139.19968 46.483948C 133.79512 46.316727 130.99477 48.84615 130.3185 49.08075C 130.93373 47.91552 131.34544 47.18139 132.91882 45.54309C 127.567184 45.48699 122.60501 46.645172 118.819 50.18706C 113.74112 54.93721 114.13588 58.872803 114.12565 58.8534C 114.12665 58.8554 113.324486 56.28694 113.083885 53.53986C 107.557976 61.18914 105.376045 71.32445 109.565636 78.88518C 112.16631 72.611374 116.29593 67.204346 121.617935 62.940678C 121.79327 58.466747 123.59313 54.686737 126.437935 51.89591C 123.87077 54.96861 122.73801 58.46922 122.8523 61.99735C 129.0425 57.37914 136.62968 54.301147 144.96371 53.29115C 138.92981 54.39993 133.4628 56.804817 128.68442 60.08495C 131.67526 61.4509 135.09087 61.66504 138.68073 60.41479C 135.23903 62.02769 131.41844 62.291573 127.55341 60.88681C 124.123 63.39894 121.072174 66.36721 118.44998 69.62935C 123.10523 71.15123 128.19724 70.83655 133.26453 68.25387C 128.6128 71.25848 123.14792 72.38525 117.30839 71.10572C 114.98041 74.22992 113.02919 77.58555 111.50413 81.04383C 113.382675 82.42883 121.53502 86.77435 135.51878 76.4623C 133.54428 75.878815 131.94408 75.053665 131.94337 75.05225C 131.98787 75.07205 133.38448 75.200424 137.46893 72.753555C 141.07997 70.64182 144.18617 66.81666 146.47923 63.839214C 144.52872 63.120605 143.00966 62.366364 143.00896 62.364605C 143.00795 62.363605 145.52956 62.534286 149.44116 59.539906C 152.93013 56.869026 156.82515 52.997295 156.83716 53.106655C 156.86186 53.129955 148.2449 46.763355 139.19968 46.483955" stroke="none" fill="#ABABAB" fill-rule="nonzero" />
<path d="M76.90892 60.39415C 81.01041 59.20187 83.62202 60.57837 84.18329 60.62363C 83.49581 59.85657 83.04422 59.37815 81.5362 58.43957C 85.61753 57.342182 89.62709 57.24689 93.18141 59.20068C 97.94861 61.82108 98.37924 64.89886 98.38368 64.88219C 98.38287 64.884186 98.51835 62.767998 98.19094 60.626408C 103.84037 65.368546 107.39591 72.664375 105.59999 79.25351C 102.44364 74.98344 98.27924 71.67578 93.41609 69.47424C 92.44918 66.098694 90.369 63.571594 87.674324 62.0049C 90.20933 63.84117 91.72702 66.28648 92.29676 68.9982C 86.70379 66.69781 80.32978 65.84657 73.76949 66.719246C 78.589516 66.37542 88.79929 67.0738 97.083725 73.94838C 93.808014 76.02605 89.85612 76.78993 85.50101 75.81959C 89.617004 77.19292 94.00519 76.97535 98.23167 74.84885C 100.59329 76.77167 102.71017 78.94501 104.520195 81.28075C 103.3416 82.70688 97.917564 87.62569 85.30545 82.520676C 86.70662 81.686905 87.77634 80.74236 87.77674 80.74117C 87.746544 80.76498 86.70259 81.13821 83.12366 80.07813C 79.96972 79.18005 76.88272 76.87608 74.574715 75.058075C 75.932335 74.12624 76.953674 73.25198 76.953674 73.2504C 76.95406 73.24959 75.05817 73.87652 71.50989 72.36501C 68.344666 71.016304 64.64558 68.832634 64.65687 68.918396C 64.642365 68.940994 70.04542 62.389626 76.90893 60.394146" stroke="none" fill="#ABABAB" fill-rule="nonzero" />
<path d="M106.35028 116.78676C 103.35939 114.0036 103.23137 111.1697 102.99563 110.68548C 102.692345 111.63276 102.514435 112.24205 102.46853 113.950066C 99.56593 111.13644 97.53256 107.88377 97.42573 103.98627C 97.2827 98.76002 99.63347 96.87469 99.61758 96.879555C 99.619576 96.879555 97.79303 97.82024 96.170654 99.15473C 97.362595 92.146484 101.699936 85.59194 108.057846 83.79309C 106.043465 88.51463 105.32079 93.58698 105.859375 98.68461C 103.522285 101.15936 102.43365 104.12773 102.44335 107.12493C 102.7356 104.12552 104.0304 101.66008 106.00902 99.8426C 106.82087 105.59001 109.21976 111.25976 113.143005 116.22528C 110.507935 112.4288 106.112755 103.67846 107.79472 93.44062C 111.1198 95.10421 113.68158 97.97678 114.99713 102.04415C 114.133644 97.97323 111.813774 94.46955 107.98454 92.04802C 108.433075 89.14839 109.210045 86.32549 110.27087 83.67378C 112.03185 83.93501 118.768524 85.95295 120.66769 98.87222C 119.291214 98.13378 117.984055 97.72311 117.98273 97.72354C 118.017624 97.73644 118.836945 98.41008 119.69913 101.88272C 120.4889 104.925644 120.07702 108.61209 119.68893 111.41583C 118.25153 110.761665 117.02649 110.35595 117.025604 110.356834C 117.024605 110.356834 118.470055 111.60574 118.94197 115.277824C 119.36268 118.55367 119.348564 122.68398 119.41433 122.632C 119.44033 122.63284 111.35593 121.44354 106.35022 116.78675" stroke="none" fill="#ABABAB" fill-rule="nonzero" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

1944
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.59.2",
"version": "0.59.3",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@ -34,7 +34,7 @@
"@excalidraw/excalidraw": "0.14.2",
"archiver": "5.3.1",
"async-mutex": "0.4.0",
"axios": "1.3.4",
"axios": "1.3.5",
"better-sqlite3": "7.4.5",
"chokidar": "3.5.3",
"cls-hooked": "4.2.2",
@ -55,28 +55,28 @@
"express-rate-limit": "6.7.0",
"express-session": "1.17.3",
"fs-extra": "11.1.1",
"helmet": "6.0.1",
"helmet": "6.1.2",
"html": "1.0.0",
"html2plaintext": "2.1.4",
"http-proxy-agent": "5.0.0",
"https-proxy-agent": "5.0.1",
"image-type": "4.1.0",
"ini": "4.0.0",
"ini": "3.0.1",
"is-animated": "2.0.2",
"is-svg": "4.3.2",
"jimp": "0.22.7",
"joplin-turndown-plugin-gfm": "1.0.12",
"jsdom": "21.1.0",
"jsdom": "21.1.1",
"mime-types": "2.1.35",
"multer": "1.4.5-lts.1",
"node-abi": "3.33.0",
"node-abi": "3.35.0",
"normalize-strings": "1.1.1",
"open": "8.4.1",
"rand-token": "1.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"request": "2.88.2",
"rimraf": "3.0.2",
"rimraf": "5.0.0",
"safe-compare": "1.1.4",
"sanitize-filename": "1.6.3",
"sanitize-html": "2.10.0",
@ -90,7 +90,7 @@
"turndown": "7.1.2",
"unescape": "1.0.1",
"ws": "8.13.0",
"xml2js": "0.4.23",
"xml2js": "0.5.0",
"yauzl": "2.10.0"
},
"devDependencies": {
@ -99,12 +99,13 @@
"electron-builder": "23.6.0",
"electron-packager": "17.1.1",
"electron-rebuild": "3.2.9",
"eslint": "^8.38.0",
"esm": "3.2.25",
"jasmine": "4.6.0",
"jsdoc": "4.0.2",
"lorem-ipsum": "2.0.8",
"rcedit": "3.0.1",
"webpack": "5.76.3",
"webpack": "5.78.0",
"webpack-cli": "5.0.1"
},
"optionalDependencies": {

View file

@ -5,12 +5,13 @@ const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser');
const helmet = require('helmet');
const session = require('express-session');
const compression = require('compression')
const compression = require('compression');
const FileStore = require('session-file-store')(session);
const sessionSecret = require('./services/session_secret');
const dataDir = require('./services/data_dir');
const utils = require('./services/utils');
const assetPath = require('./services/asset_path');
const env = require('./services/env');
require('./services/handlers');
require('./becca/becca_loader');
@ -30,27 +31,37 @@ app.use(helmet({
crossOriginEmbedderPolicy: false
}));
const persistentCacheStatic = (root, options) => {
if (!env.isDev()) {
options = {
maxAge: '1y',
...options
};
}
return express.static(root, options);
};
app.use(express.text({limit: '500mb'}));
app.use(express.json({limit: '500mb'}));
app.use(express.raw({limit: '500mb'}));
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public/root')));
app.use(`/${assetPath}/app`, express.static(path.join(__dirname, 'public/app')));
app.use(`/${assetPath}/app-dist`, express.static(path.join(__dirname, 'public/app-dist')));
app.use(`/${assetPath}/fonts`, express.static(path.join(__dirname, 'public/fonts')));
app.use(`/${assetPath}/app`, persistentCacheStatic(path.join(__dirname, 'public/app')));
app.use(`/${assetPath}/app-dist`, persistentCacheStatic(path.join(__dirname, 'public/app-dist')));
app.use(`/${assetPath}/fonts`, persistentCacheStatic(path.join(__dirname, 'public/fonts')));
app.use(`/assets/vX/fonts`, express.static(path.join(__dirname, 'public/fonts')));
app.use(`/${assetPath}/stylesheets`, express.static(path.join(__dirname, 'public/stylesheets')));
app.use(`/${assetPath}/stylesheets`, persistentCacheStatic(path.join(__dirname, 'public/stylesheets')));
app.use(`/assets/vX/stylesheets`, express.static(path.join(__dirname, 'public/stylesheets')));
app.use(`/${assetPath}/libraries`, express.static(path.join(__dirname, '..', 'libraries')));
app.use(`/${assetPath}/libraries`, persistentCacheStatic(path.join(__dirname, '..', 'libraries')));
app.use(`/assets/vX/libraries`, express.static(path.join(__dirname, '..', 'libraries')));
// excalidraw-view mode in shared notes
app.use(`/${assetPath}/node_modules/react/umd/react.production.min.js`, express.static(path.join(__dirname, '..', 'node_modules/react/umd/react.production.min.js')));
app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.production.min.js`, express.static(path.join(__dirname, '..', 'node_modules/react-dom/umd/react-dom.production.min.js')));
app.use(`/${assetPath}/node_modules/react/umd/react.production.min.js`, persistentCacheStatic(path.join(__dirname, '..', 'node_modules/react/umd/react.production.min.js')));
app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.production.min.js`, persistentCacheStatic(path.join(__dirname, '..', 'node_modules/react-dom/umd/react-dom.production.min.js')));
// expose whole dist folder since complete assets are needed in edit and share
app.use(`/node_modules/@excalidraw/excalidraw/dist/`, express.static(path.join(__dirname, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
app.use(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, express.static(path.join(__dirname, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
app.use(`/${assetPath}/images`, express.static(path.join(__dirname, '..', 'images')));
app.use(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, persistentCacheStatic(path.join(__dirname, '..', 'node_modules/@excalidraw/excalidraw/dist/')));
app.use(`/${assetPath}/images`, persistentCacheStatic(path.join(__dirname, '..', 'images')));
app.use(`/assets/vX/images`, express.static(path.join(__dirname, '..', 'images')));
app.use(`/manifest.webmanifest`, express.static(path.join(__dirname, 'public/manifest.webmanifest')));
app.use(`/robots.txt`, express.static(path.join(__dirname, 'public/robots.txt')));
@ -61,7 +72,7 @@ const sessionParser = session({
cookie: {
// path: "/",
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // in milliseconds
maxAge: 24 * 60 * 60 * 1000 // in milliseconds
},
name: 'trilium.sid',
store: new FileStore({

View file

@ -70,7 +70,9 @@ function reload() {
}
function postProcessEntityUpdate(entityName, entity) {
if (entityName === 'branches') {
if (entityName === 'notes') {
noteUpdated(entity);
} else if (entityName === 'branches') {
branchUpdated(entity);
} else if (entityName === 'attributes') {
attributeUpdated(entity);
@ -161,6 +163,15 @@ function branchDeleted(branchId) {
delete becca.branches[branch.branchId];
}
function noteUpdated(entity) {
const note = becca.notes[entity.noteId];
if (note) {
// type / mime could have been changed, and they are present in flatTextCache
note.flatTextCache = null;
}
}
function branchUpdated(branch) {
const childNote = becca.notes[branch.noteId];

View file

@ -96,7 +96,7 @@ class BAttribute extends AbstractBeccaEntity {
}
if (this.type === 'relation' && !(this.value in this.becca.notes)) {
throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it target not existing note '${this.value}'.`);
throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it targets not existing note '${this.value}'.`);
}
}

View file

@ -100,7 +100,7 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.parents = [];
/** @type {BNote[]}
* @private*/
* @private */
this.children = [];
/** @type {BAttribute[]}
* @private */
@ -110,11 +110,11 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.__attributeCache = null;
/** @type {BAttribute[]|null}
* @private*/
* @private */
this.inheritableAttributeCache = null;
/** @type {BAttribute[]}
* @private*/
* @private */
this.targetRelations = [];
this.becca.addNote(this.noteId, this);
@ -472,6 +472,20 @@ class BNote extends AbstractBeccaEntity {
*/
hasLabel(name, value) { return this.hasAttribute(LABEL, name, value); }
/**
* @param {string} name - label name
* @returns {boolean} true if label exists (including inherited) and does not have "false" value.
*/
isLabelTruthy(name) {
const label = this.getLabel(name);
if (!label) {
return false;
}
return label && label.value !== 'false';
}
/**
* @param {string} name - label name
* @param {string} [value] - label value

View file

@ -2,7 +2,7 @@ const becca = require('./becca');
const log = require('../services/log');
const beccaService = require('./becca_service');
const dateUtils = require('../services/date_utils');
const { JSDOM } = require("jsdom");
const {JSDOM} = require("jsdom");
const DEBUG = false;
@ -168,7 +168,6 @@ function trimMime(mime) {
}
mimeCache[mime] = str;
mimeCache[mime] = str;
}
return mimeCache[mime];
@ -224,8 +223,8 @@ function splitToWords(text) {
*/
function hasConnectingRelation(sourceNote, targetNote) {
return sourceNote.getAttributes().find(attr => attr.type === 'relation'
&& ['includenotelink', 'imagelink'].includes(attr.name)
&& attr.value === targetNote.noteId);
&& ['includenotelink', 'imagelink'].includes(attr.name)
&& attr.value === targetNote.noteId);
}
async function findSimilarNotes(noteId) {
@ -301,7 +300,7 @@ async function findSimilarNotes(noteId) {
for (const branch of parentNote.getParentBranches()) {
score += gatherRewards(branch.prefix, 0.3)
+ gatherAncestorRewards(branch.parentNote);
+ gatherAncestorRewards(branch.parentNote);
}
}
}
@ -314,7 +313,7 @@ async function findSimilarNotes(noteId) {
function computeScore(candidateNote) {
let score = gatherRewards(trimMime(candidateNote.mime))
+ gatherAncestorRewards(candidateNote);
+ gatherAncestorRewards(candidateNote);
if (candidateNote.isDecrypted) {
score += gatherRewards(candidateNote.title);
@ -382,7 +381,7 @@ async function findSimilarNotes(noteId) {
score += 1;
}
else if (utcDateCreated.substr(0, 10) === dateLimits.minDate.substr(0, 10)
|| utcDateCreated.substr(0, 10) === dateLimits.maxDate.substr(0, 10)) {
|| utcDateCreated.substr(0, 10) === dateLimits.maxDate.substr(0, 10)) {
if (displayRewards) {
console.log("Adding reward for same day of creation");
}

View file

@ -20,7 +20,7 @@ function register(router) {
const {date} = req.params;
if (!isValidDate(date)) {
throw getDateInvalidError(res, date);
throw getDateInvalidError(date);
}
const note = specialNotesService.getInboxNote(date);
@ -31,7 +31,7 @@ function register(router) {
const {date} = req.params;
if (!isValidDate(date)) {
throw getDateInvalidError(res, date);
throw getDateInvalidError(date);
}
const note = dateNotesService.getDayNote(date);
@ -42,7 +42,7 @@ function register(router) {
const {date} = req.params;
if (!isValidDate(date)) {
throw getDateInvalidError(res, date);
throw getDateInvalidError(date);
}
const note = dateNotesService.getWeekNote(date);
@ -53,7 +53,7 @@ function register(router) {
const {month} = req.params;
if (!/[0-9]{4}-[0-9]{2}/.test(month)) {
throw getMonthInvalidError(res, month);
throw getMonthInvalidError(month);
}
const note = dateNotesService.getMonthNote(month);
@ -64,7 +64,7 @@ function register(router) {
const {year} = req.params;
if (!/[0-9]{4}/.test(year)) {
throw getYearInvalidError(res, year);
throw getYearInvalidError(year);
}
const note = dateNotesService.getYearNote(year);

View file

@ -608,6 +608,20 @@ class FNote {
*/
hasLabel(name) { return this.hasAttribute(LABEL, name); }
/**
* @param {string} name - label name
* @returns {boolean} true if label exists (including inherited) and does not have "false" value.
*/
isLabelTruthy(name) {
const label = this.getLabel(name);
if (!label) {
return false;
}
return label && label.value !== 'false';
}
/**
* @param {string} name - relation name
* @returns {boolean} true if relation exists (excluding inherited)
@ -719,7 +733,14 @@ class FNote {
});
// attrs are not resorted if position changes after initial load
promotedAttrs.sort((a, b) => a.position < b.position ? -1 : 1);
promotedAttrs.sort((a, b) => {
if (a.noteId === b.noteId) {
return a.position < b.position ? -1 : 1;
} else {
// inherited promoted attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
return a.noteId < b.noteId ? -1 : 1;
}
});
return promotedAttrs;
}

View file

@ -128,6 +128,10 @@ export default class DesktopLayout {
)
.child(
new RibbonContainer()
// order of the widgets matter. Some of these want to "activate" themselves
// when visible, when this happens to multiple of them, the first one "wins".
// promoted attributes should always win.
.ribbon(new PromotedAttributesWidget())
.ribbon(new ScriptExecutorWidget())
.ribbon(new SearchDefinitionWidget())
.ribbon(new EditedNotesWidget())
@ -135,7 +139,6 @@ export default class DesktopLayout {
.ribbon(new NotePropertiesWidget())
.ribbon(new FilePropertiesWidget())
.ribbon(new ImagePropertiesWidget())
.ribbon(new PromotedAttributesWidget())
.ribbon(new BasicPropertiesWidget())
.ribbon(new OwnedAttributeListWidget())
.ribbon(new InheritedAttributesWidget())

View file

@ -99,11 +99,16 @@ class ContextMenu {
const $item = $("<li>")
.addClass("dropdown-item")
.append($link)
.on('contextmenu', e => false)
// important to use mousedown instead of click since the former does not change focus
// (especially important for focused text for spell check)
.on('mousedown', e => {
e.stopPropagation();
if (e.which !== 1) { // only left click triggers menu items
return false;
}
this.hide();
if (item.handler) {
@ -142,7 +147,9 @@ class ContextMenu {
// "contextmenu" event also triggers "click" event which depending on the timing can close just opened context menu
// we might filter out right clicks, but then it's better if even right clicks close the context menu
if (Date.now() - this.dateContextMenuOpenedMs > 300) {
this.$widget.hide();
// seems like if we hide the menu immediately, some clicks can get propagated to the underlying component
// see https://github.com/zadam/trilium/pull/3805 for details
setTimeout(() => this.$widget.hide(), 100);
}
}
}

View file

@ -10,7 +10,7 @@ async function moveBeforeBranch(branchIdsToMove, beforeBranchId) {
branchIdsToMove = filterRootNote(branchIdsToMove);
branchIdsToMove = filterSearchBranches(branchIdsToMove);
const beforeBranch = await froca.getBranch(beforeBranchId);
const beforeBranch = froca.getBranch(beforeBranchId);
if (['root', '_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(beforeBranch.noteId)) {
toastService.showError('Cannot move notes here.');
@ -31,7 +31,7 @@ async function moveAfterBranch(branchIdsToMove, afterBranchId) {
branchIdsToMove = filterRootNote(branchIdsToMove);
branchIdsToMove = filterSearchBranches(branchIdsToMove);
const afterNote = await froca.getBranch(afterBranchId).getNote();
const afterNote = froca.getBranch(afterBranchId).getNote();
const forbiddenNoteIds = [
'root',
@ -59,7 +59,7 @@ async function moveAfterBranch(branchIdsToMove, afterBranchId) {
}
async function moveToParentNote(branchIdsToMove, newParentBranchId) {
const newParentBranch = await froca.getBranch(newParentBranchId);
const newParentBranch = froca.getBranch(newParentBranchId);
if (newParentBranch.noteId === '_lbRoot') {
toastService.showError('Cannot move notes here.');
@ -165,7 +165,7 @@ function filterRootNote(branchIds) {
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
return branchIds.filter(branchId => {
const branch = froca.getBranch(branchId);
const branch = froca.getBranch(branchId);
return branch.noteId !== 'root'
&& branch.noteId !== hoistedNoteId;

View file

@ -14,7 +14,7 @@ async function processEntityChanges(entityChanges) {
if (ec.entityName === 'notes') {
processNoteChange(loadResults, ec);
} else if (ec.entityName === 'branches') {
processBranchChange(loadResults, ec);
await processBranchChange(loadResults, ec);
} else if (ec.entityName === 'attributes') {
processAttributeChange(loadResults, ec);
} else if (ec.entityName === 'note_reordering') {
@ -104,7 +104,7 @@ function processNoteChange(loadResults, ec) {
}
}
function processBranchChange(loadResults, ec) {
async function processBranchChange(loadResults, ec) {
if (ec.isErased && ec.entityId in froca.branches) {
utils.reloadFrontendApp(`${ec.entityName} ${ec.entityId} is erased, need to do complete reload.`);
return;
@ -138,7 +138,15 @@ function processBranchChange(loadResults, ec) {
loadResults.addBranch(ec.entityId, ec.componentId);
const childNote = froca.notes[ec.entity.noteId];
const parentNote = froca.notes[ec.entity.parentNoteId];
let parentNote = froca.notes[ec.entity.parentNoteId];
if (childNote && !parentNote) {
// a branch cannot exist without the parent
// a note loaded into froca has to also contain all its ancestors
// this problem happened e.g. in sharing where _share was hidden and thus not loaded
// sharing meant cloning into _share, which crashed because _share was not loaded
parentNote = await froca.getNote(ec.entity.parentNoteId);
}
if (branch) {
branch.update(ec.entity);

View file

@ -65,7 +65,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(notePath);
appContext.triggerEvent('focusAndSelectTitle');
await appContext.triggerEvent('focusAndSelectTitle');
};
/**
@ -82,7 +82,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await appContext.tabManager.openContextWithNote(notePath, { activate });
if (activate) {
appContext.triggerEvent('focusAndSelectTitle');
await appContext.triggerEvent('focusAndSelectTitle');
}
};
@ -100,10 +100,10 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
const {ntxId} = subContexts[subContexts.length - 1];
appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
await appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
if (activate) {
appContext.triggerEvent('focusAndSelectTitle');
await appContext.triggerEvent('focusAndSelectTitle');
}
};

View file

@ -10,7 +10,6 @@ export async function uploadFiles(parentNoteId, files, options) {
}
const taskId = utils.randomString(10);
let noteId;
let counter = 0;
for (const file of files) {
@ -25,19 +24,19 @@ export async function uploadFiles(parentNoteId, files, options) {
formData.append(key, options[key]);
}
({noteId} = await $.ajax({
await $.ajax({
url: `${baseApiUrl}notes/${parentNoteId}/import`,
headers: await server.getHeaders(),
data: formData,
dataType: 'json',
type: 'POST',
timeout: 60 * 60 * 1000,
error: function(xhr) {
error: function (xhr) {
toastService.showError(`Import failed: ${xhr.responseText}`);
},
contentType: false, // NEEDED, DON'T REMOVE THIS
processData: false, // NEEDED, DON'T REMOVE THIS
}));
});
}
}
@ -74,4 +73,4 @@ ws.subscribeToMessages(async message => {
export default {
uploadFiles
}
};

View file

@ -12,86 +12,88 @@ const TPL = `
position: relative;
height: 100%;
}
.note-list.grid-view .note-list-container {
display: flex;
flex-wrap: wrap;
}
.note-list.grid-view .note-book-card {
flex-basis: 300px;
border: 1px solid transparent;
}
.note-list.grid-view .note-expander {
display: none;
}
.note-list.grid-view .note-book-card {
max-height: 300px;
}
.note-list.grid-view .note-book-card:hover {
cursor: pointer;
border: 1px solid var(--main-border-color);
background: var(--more-accented-background-color);
}
.note-book-card {
border-radius: 10px;
background-color: var(--accented-background-color);
padding: 10px 15px 15px 8px;
margin: 5px 5px 5px 0;
margin: 5px 5px 5px 5px;
overflow: hidden;
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 1;
}
.note-book-card:not(.expanded) .note-book-content {
display: none !important;
padding: 10px
}
.note-book-card.expanded .note-book-content {
display: block;
min-height: 0;
height: 100%;
padding-top: 10px;
}
.note-book-header {
border-bottom: 1px solid var(--main-border-color);
margin-bottom: 0;
padding-bottom: .5rem;
word-break: break-all;
}
/* not-expanded title is limited to one line only */
.note-book-card:not(.expanded) .note-book-header {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.note-book-header .rendered-note-attributes {
font-size: medium;
}
.note-book-header .rendered-note-attributes:before {
content: "\\00a0\\00a0";
}
.note-book-header .note-icon {
font-size: 100%;
display: inline-block;
padding-right: 7px;
position: relative;
}
.note-book-card .note-book-card {
border: 1px solid var(--main-border-color);
}
.note-book-content.type-image, .note-book-content.type-file, .note-book-content.type-protectedSession {
display: flex;
align-items: center;
@ -99,46 +101,46 @@ const TPL = `
text-align: center;
padding: 10px;
}
.note-book-content.type-image img, .note-book-content.type-canvas svg {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.note-book-card.type-image .note-book-content img,
.note-book-card.type-text .note-book-content img,
.note-book-card.type-canvas .note-book-content img {
max-width: 100%;
max-height: 100%;
}
.note-book-header {
flex-grow: 0;
}
.note-list-wrapper {
height: 100%;
overflow: auto;
}
.note-expander {
font-size: x-large;
position: relative;
top: 3px;
cursor: pointer;
}
.note-list-pager {
text-align: center;
}
</style>
<div class="note-list-wrapper">
<div class="note-list-pager"></div>
<div class="note-list-container"></div>
<div class="note-list-pager"></div>
</div>
</div>`;

View file

@ -29,7 +29,7 @@ function formatTimeWithSeconds(date) {
// this is producing local time!
function formatDate(date) {
// return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
// return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear();
// instead of european format we'll just use ISO as that's pretty unambiguous
return formatDateISO(date);
@ -45,7 +45,7 @@ function formatDateTime(date) {
}
function localNowDateTime() {
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ')
return dayjs().format('YYYY-MM-DD HH:mm:ss.SSSZZ');
}
function now() {
@ -101,7 +101,7 @@ async function stopWatch(what, func) {
}
function formatValueWithWhitespace(val) {
return /[^\w_-]/.test(val) ? `"${val}"` : val;
return /[^\w-]/.test(val) ? `"${val}"` : val;
}
function formatLabel(label) {
@ -329,7 +329,7 @@ function initHelpDropdown($el) {
initHelpButtons($dropdownMenu);
}
const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/"
const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/";
function openHelp(e) {
window.open(wikiBaseUrl + $(e.target).attr("data-help-page"), '_blank');
@ -340,7 +340,7 @@ function initHelpButtons($el) {
// so we do it manually
$el.on("click", e => {
if ($(e.target).attr("data-help-page")) {
openHelp(e)
openHelp(e);
}
});
}

View file

@ -285,7 +285,11 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$title = this.$widget.find('.attr-detail-title');
this.$inputName = this.$widget.find('.attr-input-name');
this.$inputName.on('keyup', () => this.userEditedAttribute());
this.$inputName.on('input', ev => {
if (!ev.originalEvent?.isComposing) { // https://github.com/zadam/trilium/pull/3812
this.userEditedAttribute();
}
});
this.$inputName.on('change', () => this.userEditedAttribute());
this.$inputName.on('autocomplete:closed', () => this.userEditedAttribute());
@ -299,7 +303,11 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$rowValue = this.$widget.find('.attr-row-value');
this.$inputValue = this.$widget.find('.attr-input-value');
this.$inputValue.on('keyup', () => this.userEditedAttribute());
this.$inputValue.on('input', ev => {
if (!ev.originalEvent?.isComposing) { // https://github.com/zadam/trilium/pull/3812
this.userEditedAttribute();
}
});
this.$inputValue.on('change', () => this.userEditedAttribute());
this.$inputValue.on('autocomplete:closed', () => this.userEditedAttribute());
this.$inputValue.on('focus', () => {
@ -328,7 +336,11 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$rowInverseRelation = this.$widget.find('.attr-row-inverse-relation');
this.$inputInverseRelation = this.$widget.find('.attr-input-inverse-relation');
this.$inputInverseRelation.on('keyup', () => this.userEditedAttribute());
this.$inputInverseRelation.on('input', ev => {
if (!ev.originalEvent?.isComposing) { // https://github.com/zadam/trilium/pull/3812
this.userEditedAttribute();
}
});
this.$rowTargetNote = this.$widget.find('.attr-row-target-note');
this.$inputTargetNote = this.$widget.find('.attr-input-target-note');

View file

@ -16,16 +16,17 @@ const TPL = `
}
.global-menu-button {
background-image: url("${window.glob.assetPath}/images/icon-black.png");
background-image: url("${window.glob.assetPath}/images/icon-black.svg");
background-repeat: no-repeat;
background-position: 50% 45%;
background-position: 50% 80%;
background-size: 45px;
width: 100%;
height: 100%;
position: relative;
}
.global-menu-button:hover {
background-image: url("${window.glob.assetPath}/images/icon-color.png");
background-image: url("${window.glob.assetPath}/images/icon-color.svg");
border: 0;
}

View file

@ -65,6 +65,26 @@ const TPL = `<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" ro
sort folders at the top
</label>
</div>
<br />
<h5>Natural Sort</h5>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="sort-natural" value="1">
sort with respect to different character sorting and collation rules in different languages or regions.
</label>
</div>
<br />
<div class="form-check">
<label>
Natural sort language
<input class="form-control" name="sort-locale">
The language code for natural sort, e.g. "zh-CN" for Chinese.
</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Sort <kbd>enter</kbd></button>
@ -83,8 +103,10 @@ export default class SortChildNotesDialog extends BasicWidget {
const sortBy = this.$form.find("input[name='sort-by']:checked").val();
const sortDirection = this.$form.find("input[name='sort-direction']:checked").val();
const foldersFirst = this.$form.find("input[name='sort-folders-first']").is(":checked");
const sortNatural = this.$form.find("input[name='sort-natural']").is(":checked");
const sortLocale = this.$form.find("input[name='sort-locale']").val();
await server.put(`notes/${this.parentNoteId}/sort-children`, {sortBy, sortDirection, foldersFirst});
await server.put(`notes/${this.parentNoteId}/sort-children`, {sortBy, sortDirection, foldersFirst, sortNatural, sortLocale});
utils.closeActiveDialog();
});

View file

@ -11175,6 +11175,22 @@ const icons = [
}
];
function getIconClass(icon) {
if (icon.type_of_icon === 'LOGO') {
return `bxl-${icon.name}`;
}
else if (icon.type_of_icon === 'SOLID') {
return `bxs-${icon.name}`;
}
else {
return `bx-${icon.name}`;
}
}
for (const icon of icons) {
icon.className = getIconClass(icon);
}
export default {
categories,
icons

View file

@ -1,5 +1,6 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import attributeService from "../services/attributes.js";
import server from "../services/server.js";
const TPL = `
<div class="note-icon-widget dropdown">
@ -147,6 +148,8 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
}
async renderDropdown(categoryId, search) {
const iconToCountPromise = this.getIconToCountMap();
this.$iconList.empty();
if (this.getIconLabels().length > 0) {
@ -165,41 +168,50 @@ export default class NoteIconWidget extends NoteContextAwareWidget {
search = search?.trim()?.toLowerCase();
for (const icon of icons) {
const filteredIcons = icons.filter(icon => {
if (categoryId && icon.category_id !== categoryId) {
continue;
return false;
}
if (search) {
if (!icon.name.includes(search) && !icon.term?.find(t => t.includes(search))) {
continue;
return false;
}
}
this.$iconList.append(
$('<span>')
.addClass(this.getIconClass(icon))
.attr("title", icon.name)
);
return true;
});
const iconToCount = await iconToCountPromise;
filteredIcons.sort((a, b) => {
const countA = iconToCount[a.className] || 0;
const countB = iconToCount[b.className] || 0;
return countB - countA;
});
for (const icon of filteredIcons) {
this.$iconList.append(this.renderIcon(icon));
}
this.$iconSearch.focus();
}
async getIconToCountMap() {
const {iconClassToCountMap} = await server.get('other/icon-usage');
return iconClassToCountMap;
}
renderIcon(icon) {
return $('<span>')
.addClass("bx " + icon.className)
.attr("title", icon.name);
}
getIconLabels() {
return this.note.getOwnedLabels()
.filter(label => ['workspaceIconClass', 'iconClass'].includes(label.name));
}
getIconClass(icon) {
if (icon.type_of_icon === 'LOGO') {
return `bx bxl-${icon.name}`;
}
else if (icon.type_of_icon === 'SOLID') {
return `bx bxs-${icon.name}`;
}
else {
return `bx bx-${icon.name}`;
}
}
}

View file

@ -55,7 +55,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
this.$container = this.$widget.find(".note-map-container");
this.$styleResolver = this.$widget.find('.style-resolver');
window.addEventListener('resize', () => this.setDimensions(), false);
new ResizeObserver(() => this.setDimensions()).observe(this.$container[0])
this.$widget.find(".map-type-switcher button").on("click", async e => {
const type = $(e.target).closest("button").attr("data-type");

View file

@ -41,7 +41,6 @@ const TPL = `
}
.tree-actions {
padding: 4px 0;
background-color: var(--launcher-pane-background-color);
z-index: 100;
position: absolute;
@ -49,13 +48,15 @@ const TPL = `
display: flex;
align-items: flex-end;
justify-content: flex-end;
right: 11.77px;
right: 17px;
border-radius: 7px;
border: 1px solid var(--main-border-color);
}
button.tree-floating-button {
margin: 1px;
font-size: 1.5em;
padding: 5px;
margin-right: 5px;
max-height: 34px;
color: var(--launcher-pane-text-color);
background-color: var(--button-background-color);

View file

@ -92,7 +92,18 @@ export default class InheritedAttributesWidget extends NoteContextAwareWidget {
}
getInheritedAttributes(note) {
return note.getAttributes().filter(attr => attr.noteId !== this.noteId);
const attrs = note.getAttributes().filter(attr => attr.noteId !== this.noteId);
attrs.sort((a, b) => {
if (a.noteId === b.noteId) {
return a.position < b.position ? -1 : 1;
} else {
// inherited attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
return a.noteId < b.noteId ? -1 : 1;
}
});
return attrs;
}
entitiesReloadedEvent({loadResults}) {

View file

@ -87,8 +87,8 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget {
this.noteMapWidget.setDimensions();
});
window.addEventListener('resize', () => {
if (!this.graph) { // no graph has been even rendered
const handleResize = () => {
if (!this.noteMapWidget.graph) { // no graph has been even rendered
return;
}
@ -98,7 +98,9 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget {
else if (this.openState === 'small') {
this.setSmallSize();
}
}, false);
}
new ResizeObserver(handleResize).observe(this.$widget[0])
}
setSmallSize() {

View file

@ -29,6 +29,9 @@ const TPL = `
.promoted-attribute-cell div.input-group {
margin-left: 10px;
}
.promoted-attribute-cell strong {
word-break:keep-all;
}
</style>
<div class="promoted-attributes-container"></div>
@ -54,13 +57,13 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
const promotedDefAttrs = note.getPromotedDefinitionAttributes();
if (promotedDefAttrs.length === 0) {
return { show: false };
return {show: false};
}
return {
show: true,
activate: true,
title: "Promoted attributes",
title: "Promoted Attributes",
icon: "bx bx-table"
};
}
@ -144,7 +147,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
return;
}
attributeValues = attributeValues.map(attribute => ({ value: attribute }));
attributeValues = attributeValues.map(attribute => ({value: attribute}));
$input.autocomplete({
appendTo: document.querySelector('body'),
@ -164,7 +167,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
}
}]);
$input.on('autocomplete:selected', e => this.promotedAttributeChanged(e))
$input.on('autocomplete:selected', e => this.promotedAttributeChanged(e));
});
}
else if (definition.labelType === 'number') {

View file

@ -180,7 +180,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
return {
show: this.isEnabled(),
activate: true,
title: 'Search parameters',
title: 'Search Parameters',
icon: 'bx bx-search'
};
}

View file

@ -39,7 +39,14 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
this.$sharedText.text("This note is shared publicly on");
}
else {
link = `${location.protocol}//${location.host}${location.pathname}share/${shareId}`;
let host = location.host;
if (host.endsWith('/')) {
// seems like IE has trailing slash
// https://github.com/zadam/trilium/issues/3782
host = host.substr(0, host.length - 1);
}
link = `${location.protocol}//${host}${location.pathname}share/${shareId}`;
this.$sharedText.text("This note is shared locally on");
}

View file

@ -60,7 +60,7 @@ export default class SqlResultWidget extends NoteContextAwareWidget {
const $row = $("<tr>");
for (const key in result) {
$row.append($("<th>").html(key));
$row.append($("<th>").text(key));
}
$table.append($row);
@ -69,7 +69,7 @@ export default class SqlResultWidget extends NoteContextAwareWidget {
const $row = $("<tr>");
for (const key in result) {
$row.append($("<td>").html(result[key]));
$row.append($("<td>").text(result[key]));
}
$table.append($row);

View file

@ -280,14 +280,7 @@ export default class TabRowWidget extends BasicWidget {
this.layoutTabs();
};
// ResizeObserver exists only in FF69
if (typeof ResizeObserver !== "undefined") {
new ResizeObserver(resizeListener).observe(this.$widget[0]);
}
else {
// for older firefox
window.addEventListener('resize', resizeListener);
}
new ResizeObserver(resizeListener).observe(this.$widget[0]);
this.tabEls.forEach((tabEl) => this.setTabCloseEvent(tabEl));
}

View file

@ -24,6 +24,7 @@ const FONT_FAMILIES = [
{ value: "Bradley Hand", label: "Bradley Hand" },
{ value: "Luminari", label: "Luminari" },
{ value: "Comic Sans MS", label: "Comic Sans MS" },
{ value: "Microsoft YaHei", label: "Microsoft YaHei" },
];
const TPL = `

View file

@ -470,11 +470,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
async createNoteBox(noteId, title, x, y) {
const $link = await linkService.createNoteLink(noteId, {title});
$link.mousedown(e => {
console.log(e);
linkService.goToLink(e);
});
$link.mousedown(e => linkService.goToLink(e));
const note = await froca.getNote(noteId);

View file

@ -73,7 +73,7 @@
}
body .global-menu-button {
background-image: url("../images/icon-grey.png");
background-image: url("../images/icon-grey.svg");
}
body ::-webkit-calendar-picker-indicator {

View file

@ -94,13 +94,13 @@ function undeleteNote(req) {
function sortChildNotes(req) {
const noteId = req.params.noteId;
const {sortBy, sortDirection, foldersFirst} = req.body;
const {sortBy, sortDirection, foldersFirst, sortNatural, sortLocale} = req.body;
log.info(`Sorting '${noteId}' children with ${sortBy} ${sortDirection}, foldersFirst=${foldersFirst}`);
log.info(`Sorting '${noteId}' children with ${sortBy} ${sortDirection}, foldersFirst=${foldersFirst}, sortNatural=${sortNatural}, sortLocale=${sortLocale}`);
const reverse = sortDirection === 'desc';
treeService.sortNotes(noteId, sortBy, reverse, foldersFirst);
treeService.sortNotes(noteId, sortBy, reverse, foldersFirst, sortNatural, sortLocale);
}
function protectNote(req) {

View file

@ -74,7 +74,7 @@ function getOptions() {
}
}
resultMap['isPasswordSet'] = !!optionMap['passwordVerificationHash'] ? 'true' : 'false';
resultMap['isPasswordSet'] = optionMap['passwordVerificationHash'] ? 'true' : 'false';
return resultMap;
}

29
src/routes/api/other.js Normal file
View file

@ -0,0 +1,29 @@
const becca = require("../../becca/becca");
function getIconUsage() {
const iconClassToCountMap = {};
for (const {value: iconClass, noteId} of becca.findAttributes('label', 'iconClass')) {
if (noteId.startsWith("_")) {
continue; // ignore icons of "system" notes since they were not set by the user
}
if (!iconClass?.trim()) {
continue;
}
for (const clazz of iconClass.trim().split(/\s+/)) {
if (clazz === 'bx') {
continue;
}
iconClassToCountMap[clazz] = (iconClassToCountMap[clazz] || 0) + 1;
}
}
return { iconClassToCountMap };
}
module.exports = {
getIconUsage
};

View file

@ -57,6 +57,7 @@ const backendLogRoute = require('./api/backend_log');
const statsRoute = require('./api/stats');
const fontsRoute = require('./api/fonts');
const etapiTokensApiRoutes = require('./api/etapi_tokens');
const otherRoute = require('./api/other');
const shareRoutes = require('../share/routes');
const etapiAuthRoutes = require('../etapi/auth');
const etapiAppInfoRoutes = require('../etapi/app_info');
@ -305,6 +306,7 @@ function register(app) {
apiRoute(POST, '/api/delete-notes-preview', notesApiRoute.getDeleteNotesPreview);
route(GET, '/api/fonts', [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
apiRoute(GET, '/api/other/icon-usage', otherRoute.getIconUsage);
apiRoute(GET, '/api/etapi-tokens', etapiTokensApiRoutes.getTokens);
apiRoute(POST, '/api/etapi-tokens', etapiTokensApiRoutes.createToken);

View file

@ -1,4 +1,4 @@
"use strict"
"use strict";
function formatAttrForSearch(attr, searchWithValue) {
let searchStr = '';
@ -28,7 +28,7 @@ function formatAttrForSearch(attr, searchWithValue) {
}
function formatValue(val) {
if (!/[^\w_]/.test(val)) {
if (!/[^\w]/.test(val)) {
return val;
}
else if (!val.includes('"')) {
@ -47,4 +47,4 @@ function formatValue(val) {
module.exports = {
formatAttrForSearch
}
};

View file

@ -7,7 +7,7 @@ const BAttribute = require('../becca/entities/battribute');
const {formatAttrForSearch} = require("./attribute_formatter");
const BUILTIN_ATTRIBUTES = require("./builtin_attributes");
const ATTRIBUTE_TYPES = [ 'label', 'relation' ];
const ATTRIBUTE_TYPES = ['label', 'relation'];
/** @returns {BNote[]} */
function getNotesWithLabel(name, value = undefined) {
@ -122,7 +122,7 @@ function isAttributeType(type) {
function isAttributeDangerous(type, name) {
return BUILTIN_ATTRIBUTES.some(attr =>
attr.type === attr.type &&
attr.type === type &&
attr.name.toLowerCase() === name.trim().toLowerCase() &&
attr.isDangerous
);

View file

@ -471,7 +471,7 @@ function BackendScriptApi(currentNote, apiParams) {
if (opts.type === 'script' && !opts.scriptNoteId) { throw new Error("scriptNoteId is mandatory for launchers of type 'script'"); }
if (opts.type === 'customWidget' && !opts.widgetNoteId) { throw new Error("widgetNoteId is mandatory for launchers of type 'customWidget'"); }
const parentNoteId = !!opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
const parentNoteId = opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
const noteId = 'al_' + opts.id;
const launcherNote =

View file

@ -1 +1 @@
module.exports = { buildDate:"2023-03-14T21:15:08+01:00", buildRevision: "d8e9086bdeb721db795783b5d92395a9bd6882c9" };
module.exports = { buildDate:"", buildRevision: "9881e6de3e4966af39ec6245562dca6ac7b25eaa" };

View file

@ -42,6 +42,8 @@ module.exports = [
{ type: 'label', name: 'sorted' },
{ type: 'label', name: 'sortDirection' },
{ type: 'label', name: 'sortFoldersFirst' },
{ type: 'label', name: 'sortNatural' },
{ type: 'label', name: 'sortLocale' },
{ type: 'label', name: 'top' },
{ type: 'label', name: 'fullContentWidth' },
{ type: 'label', name: 'shareHiddenFromTree' },

View file

@ -46,7 +46,7 @@ eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETE
if (entityName === 'attributes') {
runAttachedRelations(entity.getNote(), 'runOnAttributeChange', entity);
if (entity.type === 'label' && ['sorted', 'sortDirection', 'sortFoldersFirst'].includes(entity.name)) {
if (entity.type === 'label' && ['sorted', 'sortDirection', 'sortFoldersFirst', 'sortNatural', 'sortLocale'].includes(entity.name)) {
handleSortedAttribute(entity);
} else if (entity.type === 'label') {
handleMaybeSortingLabel(entity);
@ -101,7 +101,7 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) =>
noteService.duplicateSubtreeWithoutRoot(templateNote.noteId, note.noteId);
}
}
else if (entity.type === 'label' && ['sorted', 'sortDirection', 'sortFoldersFirst'].includes(entity.name)) {
else if (entity.type === 'label' && ['sorted', 'sortDirection', 'sortFoldersFirst', 'sortNatural', 'sortLocale'].includes(entity.name)) {
handleSortedAttribute(entity);
}
else if (entity.type === 'label') {

View file

@ -24,7 +24,7 @@ const noteTypesService = require("./note_types");
const {attach} = require("jsdom/lib/jsdom/living/helpers/svg/basic-types.js");
function getNewNotePosition(parentNote) {
if (parentNote.hasLabel('newNotesOnTop')) {
if (parentNote.isLabelTruthy('newNotesOnTop')) {
const minNotePos = parentNote.getChildBranches()
.reduce((min, note) => Math.min(min, note.notePosition), 0);
@ -827,7 +827,7 @@ function duplicateSubtree(origNoteId, newParentNoteId) {
throw new Error('Duplicating root is not possible');
}
log.info(`Duplicating ${origNoteId} subtree into ${newParentNoteId}`);
log.info(`Duplicating '${origNoteId}' subtree into '${newParentNoteId}'`);
const origNote = becca.notes[origNoteId];
// might be null if orig note is not in the target newParentNoteId
@ -905,7 +905,8 @@ function duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapp
attr.value = noteIdMapping[attr.value];
}
attr.save();
// the relation targets may not be created yet, the mapping is pre-generated
attr.save({skipValidation: true});
}
for (const childBranch of origNote.getChildBranches()) {

View file

@ -125,7 +125,7 @@ function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds =
}
if (root) {
scriptEnv = !!backendOverrideContent
scriptEnv = backendOverrideContent
? 'backend'
: note.getScriptEnv();
}

View file

@ -5,6 +5,7 @@ function lex(str) {
const fulltextTokens = [];
const expressionTokens = [];
/** @type {boolean|string} */
let quotes = false; // otherwise contains used quote - ', " or `
let fulltextEnded = false;
let currentWord = '';

View file

@ -155,7 +155,7 @@ function getExpression(tokens, searchContext, level = 0) {
i++;
return new NoteContentFulltextExp(operator.token, {tokens: [tokens[i].token], raw });
return new NoteContentFulltextExp(operator.token, {tokens: [tokens[i].token], raw});
}
if (tokens[i].token === 'parents') {
@ -389,7 +389,7 @@ function getExpression(tokens, searchContext, level = 0) {
else if (token === 'note') {
i++;
expressions.push(parseNoteProperty(tokens));
expressions.push(parseNoteProperty());
continue;
}

View file

@ -73,7 +73,7 @@ function searchFromRelation(note, relationName) {
return [];
}
const result = scriptService.executeNote(scriptNote, { originEntity: note });
const result = scriptService.executeNote(scriptNote, {originEntity: note});
if (!Array.isArray(result)) {
log.info(`Result from ${scriptNote.noteId} is not an array.`);
@ -288,7 +288,7 @@ function searchNotesForAutocomplete(query) {
noteTitle: beccaService.getNoteTitle(result.noteId),
notePathTitle: result.notePathTitle,
highlightedNotePathTitle: result.highlightedNotePathTitle
}
};
});
}
@ -299,7 +299,9 @@ function highlightSearchResults(searchResults, highlightedTokens) {
// which would make the resulting HTML string invalid.
// { and } are used for marking <b> and </b> tag (to avoid matches on single 'b' character)
// < and > are used for marking <small> and </small>
highlightedTokens = highlightedTokens.map(token => token.replace('/[<\{\}]/g', ''));
highlightedTokens = highlightedTokens
.map(token => token.replace('/[<\{\}]/g', ''))
.filter(token => !!token?.trim());
// sort by the longest, so we first highlight the longest matches
highlightedTokens.sort((a, b) => a.length > b.length ? -1 : 1);
@ -307,7 +309,7 @@ function highlightSearchResults(searchResults, highlightedTokens) {
for (const result of searchResults) {
const note = becca.notes[result.noteId];
result.highlightedNotePathTitle = result.notePathTitle.replace('/[<\{\}]/g', '');
result.highlightedNotePathTitle = result.notePathTitle.replace(/[<{}]/g, '');
if (highlightedTokens.find(token => note.type.includes(token))) {
result.highlightedNotePathTitle += ` "type: ${note.type}'`;
@ -368,7 +370,7 @@ function formatAttribute(attr) {
let label = `#${utils.escapeHtml(attr.name)}`;
if (attr.value) {
const val = /[^\w_-]/.test(attr.value) ? `"${attr.value}"` : attr.value;
const val = /[^\w-]/.test(attr.value) ? `"${attr.value}"` : attr.value;
label += `=${utils.escapeHtml(val)}`;
}

View file

@ -217,7 +217,7 @@ function wrap(query, func) {
// in these cases error should be simply ignored.
console.log(e.message);
return null
return null;
}
throw e;
@ -281,7 +281,7 @@ function fillParamList(paramIds, truncate = true) {
}
// doing it manually to avoid this showing up on the sloq query list
const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`, paramIds);
const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`);
s.run(paramIds);
}

View file

@ -123,11 +123,16 @@ function loadSubtreeNoteIds(parentNoteId, subtreeNoteIds) {
}
}
function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, foldersFirst = false) {
function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, foldersFirst = false, sortNatural = false, sortLocale) {
if (!customSortBy) {
customSortBy = 'title';
}
if (!sortLocale) {
// sortLocale can not be empty string or null value, default value must be set to undefined.
sortLocale = undefined;
}
sql.transactional(() => {
const notes = becca.getNote(parentNoteId).getChildNotes();
@ -153,7 +158,14 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
}
function compare(a, b) {
return b === null || b === undefined || a < b ? -1 : 1;
if (!sortNatural){
// alphabetical sort
return b === null || b === undefined || a < b ? -1 : 1;
} else {
// natural sort
return a.localeCompare(b, sortLocale, {numeric: true, sensitivity: 'base'});
}
}
const topAEl = fetchValue(a, 'top');
@ -224,8 +236,11 @@ function sortNotesIfNeeded(parentNoteId) {
const sortReversed = parentNote.getLabelValue('sortDirection')?.toLowerCase() === "desc";
const sortFoldersFirstLabel = parentNote.getLabel('sortFoldersFirst');
const sortFoldersFirst = sortFoldersFirstLabel && sortFoldersFirstLabel.value.toLowerCase() !== "false";
const sortNaturalLabel = parentNote.getLabel('sortNatural');
const sortNatural = sortNaturalLabel && sortNaturalLabel.value.toLowerCase() !== "false";
const sortLocale = parentNote.getLabelValue('sortLocale');
sortNotes(parentNoteId, sortedLabel.value, sortReversed, sortFoldersFirst);
sortNotes(parentNoteId, sortedLabel.value, sortReversed, sortFoldersFirst, sortNatural, sortLocale);
}
/**