Merge remote-tracking branch 'origin/develop' into feature/in_app_help

This commit is contained in:
Elian Doran 2025-02-07 22:28:01 +02:00
commit cecd7eccad
No known key found for this signature in database
43 changed files with 959 additions and 397 deletions

View File

@ -15,13 +15,22 @@ runs:
if: ${{ inputs.os == 'macos' }}
shell: bash
run: brew install python-setuptools
- name: Install rpm on Ubuntu for RPM package building
- name: Install dependencies for RPM and Flatpak package building
if: ${{ inputs.os == 'linux' }}
shell: bash
run: sudo apt install rpm
run: |
sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi)
FLATPAK_VERSION='24.08'
flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION
- name: Install dependencies
shell: bash
run: npm ci
- name: Temporary Flatpak arm64 workaround till https://github.com/electron/forge/pull/3839 is merged
if: ${{ inputs.os == 'linux' && inputs.arch == 'arm64' }}
shell: bash
run: sed -e "s/case 'armv7l'/case 'arm64'/g" -e "s/return 'arm'/return 'aarch64'/g" -i node_modules/@electron-forge/maker-flatpak/dist/MakerFlatpak.js
- name: Update build info
shell: bash
run: npm run update-build-info

View File

@ -25,4 +25,4 @@ runs:
run: |
mkdir -p upload
file=$(find dist -name '*.tar.xz' -print -quit)
cp "$file" "upload/TriliumNextNotes-linux-${{ inputs.arch }}-${{ github.ref_name }}.tar.xz"
cp "$file" "upload/TriliumNextNotes-Server-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.tar.xz"

View File

@ -26,7 +26,7 @@ jobs:
extension: [dmg, zip]
- name: linux
image: ubuntu-latest
extension: [deb, rpm, zip]
extension: [deb, rpm, zip, flatpak]
- name: windows
image: windows-latest
extension: exe
@ -53,6 +53,7 @@ jobs:
with:
name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}.${{matrix.os.extension}}
path: upload/*.${{ matrix.os.extension }}
build_linux_server:
name: Build Linux Server
strategy:

View File

@ -23,7 +23,7 @@ jobs:
extension: [dmg, zip]
- name: linux
image: ubuntu-latest
extension: [deb, rpm, zip]
extension: [deb, rpm, zip, flatpak]
- name: windows
image: windows-latest
extension: exe
@ -49,11 +49,14 @@ jobs:
- name: Publish release
uses: softprops/action-gh-release@v2
with:
draft: true
make_latest: false
prerelease: true
draft: false
fail_on_unmatched_files: true
files: upload/*.*
tag_name: nightly
name: Nightly Build
nightly-server:
name: Deploy server nightly
strategy:
@ -77,7 +80,9 @@ jobs:
- name: Publish release
uses: softprops/action-gh-release@v2
with:
draft: true
make_latest: false
prerelease: true
draft: false
fail_on_unmatched_files: true
files: upload/*.*
tag_name: nightly

View File

@ -23,7 +23,7 @@ jobs:
extension: [dmg, zip]
- name: linux
image: ubuntu-latest
extension: [deb, rpm, zip]
extension: [deb, rpm, zip, flatpak]
- name: windows
image: windows-latest
extension: exe
@ -46,6 +46,7 @@ jobs:
draft: true
fail_on_unmatched_files: true
files: upload/*.*
build_linux_server-x64:
name: Build Linux Server
strategy:

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ src/public/app-dist/
npm-debug.log
yarn-error.log
po-*/
.flatpak-builder/
*.db
!integration-tests/db/document.db

View File

@ -118,8 +118,10 @@ Head on over to our [Docs repo](https://github.com/TriliumNext/Docs)
## 🤝 Support
You can support the original Trilium developer using GitHub Sponsors, [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
Support for the TriliumNext organization will be possible in the near future.
Support for the TriliumNext organization will be possible in the near future. For now, you can:
- Support continued development on TriliumNext by supporting our developers: [eliandoran](https://github.com/sponsors/eliandoran) (See the [repository insights]([developers]([url](https://github.com/TriliumNext/Notes/graphs/contributors))) for a full list)
- Show a token of gratitude to the original Trilium developer ([zadam](https://github.com/sponsors/zadam)) via [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
## 🔑 License

Binary file not shown.

View File

@ -7,6 +7,7 @@ const extraResourcesForPlatform = getExtraResourcesForPlatform();
const baseLinuxMakerConfigOptions = {
icon: "./images/app-icons/png/128x128.png",
desktopTemplate: path.resolve("./bin/electron-forge/desktop.ejs"),
categories: ["Office", "Utility"]
};
module.exports = {
@ -59,6 +60,29 @@ module.exports = {
}
}
},
{
name: "@electron-forge/maker-flatpak",
config: {
options: {
...baseLinuxMakerConfigOptions,
id: "com.triliumnext.notes",
runtimeVersion: "24.08",
base: "org.electronjs.Electron2.BaseApp",
baseVersion: "24.08",
baseFlatpakref: "https://flathub.org/repo/flathub.flatpakrepo",
modules: [
{
name: "zypak",
sources: {
type: "git",
url: "https://github.com/refi64/zypak",
tag: "v2024.01.17"
}
}
]
},
}
},
{
name: "@electron-forge/maker-rpm",
config: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -1,124 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
<style type="text/css">
.st0{fill:#686768;}
.st1{fill:#808080;}
.st2{fill:url(#SVGID_1_);}
.st3{fill:url(#SVGID_2_);}
.st4{fill:url(#SVGID_3_);}
.st5{fill:#D9D9D9;}
.st6{fill:url(#SVGID_4_);}
.st7{opacity:0.47;}
.st8{fill:#5B5A5A;}
.st9{fill:#95C980;}
.st10{fill:#72B755;}
.st11{fill:#4FA52B;}
.st12{fill:#EE8C89;}
.st13{fill:#E96562;}
.st14{fill:#E33F3B;}
.st15{fill:#EFB075;}
.st16{fill:#E99547;}
.st17{fill:#E47B19;}
.st18{opacity:0.38;fill:url(#SVGID_5_);}
</style>
<g id="Layer_1">
<g id="Layer_1_1_">
</g>
</g>
<g id="Layer_2">
<polygon class="st0" points="86.3,63.2 86.2,99.4 33.1,101.1 32.5,99.9 53.9,67.1 "/>
<path class="st1" d="M86.3,61.9l-0.2,37.5c0,0-53.9,0.8-53.7,0.5l21.2-34L86.3,61.9z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="86.3479" y1="130.1949" x2="208.0555" y2="130.1949" gradientTransform="matrix(1 0 0 -1 0 258)">
<stop offset="0" style="stop-color:#E3E3E3"/>
<stop offset="1" style="stop-color:#F4F4F4"/>
</linearGradient>
<polygon class="st2" points="86.3,61.9 207.8,68.9 208.1,188.4 86.7,193.7 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="53.6067" y1="130.1949" x2="86.6402" y2="130.1949" gradientTransform="matrix(1 0 0 -1 0 258)">
<stop offset="0" style="stop-color:#D9D9D9"/>
<stop offset="1" style="stop-color:#D4D4D4"/>
</linearGradient>
<polygon class="st3" points="53.6,65.8 86.3,61.9 86.6,193.7 53.6,189.9 "/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="154.1068" y1="201.7921" x2="146.7211" y2="271.9565" gradientTransform="matrix(0.9941 1.431752e-03 -1.431754e-03 -1.1143 -2.6949 323.3557)">
<stop offset="0" style="stop-color:#B3B3B3"/>
<stop offset="0.4752" style="stop-color:#B5B5B5"/>
<stop offset="0.6464" style="stop-color:#BCBCBC"/>
<stop offset="0.7685" style="stop-color:#C7C7C7"/>
<stop offset="0.8671" style="stop-color:#D8D8D8"/>
<stop offset="0.9506" style="stop-color:#EEEEEE"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon class="st4" points="208.1,103.8 109,99.5 86.3,62.2 207.8,68.9 "/>
<polygon class="st1" points="112.8,93.1 234.1,99.3 235.4,97.8 88.5,63.5 86.3,61.9 "/>
<polygon class="st5" points="235.4,97.8 133.9,92.5 112.8,91.1 86.6,62.2 208,68.9 219.8,81.8 235,97.4 235,97.4 "/>
<radialGradient id="SVGID_4_" cx="480.8341" cy="689.2393" r="3.2653" gradientTransform="matrix(0.5088 -4.329579e-03 -0.1464 -14.7395 -56.4799 10295.6123)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="6.758273e-02" style="stop-color:#FFFFFF;stop-opacity:0.9324"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<path class="st6" d="M88.5,147.7c0.2,21.3,0.7,34.5,0.1,34.5c-0.7,0-1.2-5.1-2-26.3c-0.9-21.2-1.1-69.1-0.4-69.2
C86.8,86.5,88.3,126.5,88.5,147.7z"/>
<g class="st7">
<path class="st8" d="M53.5,189.5l16.6,1.7c2.7,0.3,5.6,0.5,8.3,0.9l8.3,1h-0.1l60.7-2.6l30.4-1.4l7.6-0.3c2.5-0.1,5.1-0.2,7.6-0.3
l15.2-0.4l-15.2,1c-2.5,0.2-5.1,0.3-7.6,0.4l-7.6,0.3l-30.4,1.4l-60.6,2.6l0,0l0,0l-8.3-1c-2.7-0.3-5.5-0.7-8.3-1.1L53.5,189.5z"
/>
<path class="st1" d="M53.1,189.5c2.7,0.2,5.5,0.3,8.4,0.5c2.9,0.2,5.8,0.4,8.6,0.7l8.3,0.9l8.3,1l-0.1,0.9l-0.1,0l0-0.9l60.8-2.5
l30.4-1.3l15.2-0.6l15.2-0.4l0,0.5l-15.2,0.9l-7.6,0.4l-7.6,0.3l-30.4,1.3l-60.7,2.5l0,0l0,0l-8.3-1l-8.3-1.1l-16.6-2.2
L53.1,189.5z M54.5,189.7l15.6,2l8.3,1.1l8.3,1l0,0l60.7-2.7l30.4-1.4l7.6-0.3l7.6-0.4l15.2-1l0,0.5l-15.2,0.4l-15.2,0.6
l-30.4,1.4l-60.7,2.7l0-0.9l0.1,0l-0.1,0.9l-8.3-1l-8.3-0.9c-2.7-0.3-5.3-0.5-7.9-0.9C59.6,190.4,57.1,190,54.5,189.7z"/>
</g>
<g>
<g>
<g>
<path class="st9" d="M177.3,134.6c-7.1,5.5-19.1,6.6-27.5,4.5c2.1-1.9,3.1-2.8,5.1-4.7c2-1.8,2.9-2.6,4.9-4.4
c6.8-6,10.2-8.8,16.8-14.4c-6.9,5.2-10.5,7.8-17.6,13.3c-2,1.6-3,2.3-5,3.9c-2.1,1.6-3.1,2.5-5.2,4.1c-0.4-7.7,0.9-18,8.4-23.5
c0.6-0.5,1.4-0.9,2.1-1.4c1-0.5,2-1.1,3.2-1.5c9.3-3.6,20.2-6.1,30.3-4.8c0.6,5.3-4,15.4-9.6,22.9c-0.8,1-1.5,2-2.3,2.9
C179.6,132.6,178.4,133.7,177.3,134.6z"/>
<path class="st10" d="M180.7,131.4c-7.5,4.9-18.4,4.7-25.8,3c2-1.8,2.9-2.6,4.9-4.4c6.8-6,10.2-8.8,16.8-14.4
c-6.9,5.2-10.5,7.8-17.6,13.3c-2,1.6-3,2.3-5,3.9c-0.4-6.4,0.3-15,5.3-20.7c1-0.5,2-1.1,3.2-1.5c9.3-3.7,20.2-6.1,30.3-4.8
c0.6,5.3-4,15.4-9.6,22.9C182.3,129.5,181.5,130.5,180.7,131.4z"/>
<path class="st11" d="M183.1,128.5c-7.2,3.4-16.7,3.1-23.2,1.5c6.8-6,10.2-8.8,16.8-14.4c-6.9,5.2-10.5,7.8-17.6,13.3
c-0.4-5.5,0-12.8,3.5-18.4c9.3-3.6,20.2-6.1,30.3-4.8C193.4,111,188.8,121.2,183.1,128.5z"/>
</g>
<g>
<path class="st12" d="M144,161.3c-2.9-7.2,0.4-15.9,3.8-21.1c0.9,2,1.3,3,2.2,4.9c0.9,1.9,1.3,2.8,2.1,4.6
c3,6.4,4.5,9.6,7.6,15.6c-2.5-6.2-3.8-9.4-6.4-16c-0.7-1.8-1.1-2.7-1.8-4.5c-0.8-1.9-1.2-2.9-1.9-4.9c6,1.8,13.7,5.4,16.5,12.2
c0.2,0.6,0.4,1.2,0.6,1.8c0.2,0.8,0.4,1.7,0.5,2.6c1.2,7.8-0.7,18.7-3.6,22.8c-4.5-0.7-11.8-6.3-16.3-12.3
c-0.6-0.8-1.2-1.6-1.7-2.4C145,163.5,144.5,162.4,144,161.3z"/>
<path class="st13" d="M145.7,164.6c-1.9-6.8,1.2-14.7,4.4-19.5c0.9,1.9,1.3,2.8,2.1,4.6c3,6.4,4.5,9.6,7.6,15.6
c-2.5-6.2-3.8-9.4-6.4-16c-0.7-1.8-1.1-2.7-1.8-4.5c5.1,1.4,11.7,4.2,15.2,9.2c0.2,0.8,0.4,1.7,0.5,2.6
c1.2,7.8-0.7,18.7-3.6,22.7c-4.5-0.7-11.8-6.3-16.3-12.3C146.8,166.2,146.3,165.4,145.7,164.6z"/>
<path class="st14" d="M147.4,167c-0.9-6.1,2-13,4.8-17.3c3,6.4,4.5,9.6,7.6,15.6c-2.5-6.2-3.8-9.4-6.4-16
c4.5,1.1,10.2,3.3,14,7.2c1.2,7.8-0.7,18.7-3.6,22.7C159.1,178.5,151.9,173,147.4,167z"/>
</g>
<g>
<path class="st15" d="M132.2,118.5c8.5,3.5,13.4,12.5,15.3,19c-2.4-0.8-3.5-1.1-5.9-1.9c-2.3-0.7-3.4-1.1-5.6-1.8
c-7.9-2.6-11.8-4-19.4-6.7c7.4,3.1,11.1,4.7,18.8,7.8c2.1,0.9,3.2,1.3,5.3,2.2c2.2,0.9,3.3,1.3,5.6,2.2c-6,3.4-15,6.8-23,3.7
c-0.6-0.2-1.4-0.6-2.1-1c-0.8-0.5-1.8-1-2.7-1.7c-6.9-4.9-14.3-13.6-17-21.8c4-2.8,14.3-3.4,23-1.9c1.2,0.2,2.3,0.4,3.5,0.7
C129.6,117.5,130.9,117.9,132.2,118.5z"/>
<path class="st16" d="M128,117.1c7.4,3.9,11.8,12.2,13.6,18.4c-2.3-0.7-3.4-1.1-5.6-1.8c-7.9-2.6-11.8-4-19.4-6.7
c7.4,3.1,11.1,4.7,18.7,7.8c2.1,0.9,3.2,1.3,5.3,2.2c-5,2.9-12.5,6-19.4,4.9c-0.8-0.5-1.8-1-2.7-1.7c-6.9-4.9-14.3-13.6-17-21.8
c4-2.8,14.3-3.4,23-1.9C125.8,116.6,126.9,116.7,128,117.1z"/>
<path class="st17" d="M124.6,116.3c6.1,4.3,9.8,11.7,11.4,17.3c-7.9-2.6-11.8-4-19.4-6.7c7.4,3.1,11.1,4.7,18.8,7.8
c-4.4,2.6-10.6,5.4-16.8,5.3c-6.9-4.9-14.3-13.6-17-21.8C105.6,115.4,115.8,114.8,124.6,116.3z"/>
</g>
</g>
</g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="226.3925" y1="147.1083" x2="159.9567" y2="186.9981">
<stop offset="0.1721" style="stop-color:#C7C7C7"/>
<stop offset="0.3798" style="stop-color:#D8D8D8"/>
<stop offset="0.6814" style="stop-color:#DADADA"/>
<stop offset="0.7898" style="stop-color:#E1E1E1"/>
<stop offset="0.867" style="stop-color:#ECECEC"/>
<stop offset="0.8745" style="stop-color:#EEEEEE"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<path class="st18" d="M208,128c-0.8,0.3-2.7,12.8-3,15.6c-0.6,4.7-3.2,23.3-9,33.5c-5.9,10.4-12.8,11.1-13.3,11.9l25.3-0.7
C208,169.3,208,146.9,208,128L208,128z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 256 256" style="enable-background:new 0 0 256 256;" xml:space="preserve">
<style type="text/css">
.st0{fill:#686768;}
.st1{fill:#808080;}
.st2{fill:url(#SVGID_1_);}
.st3{fill:url(#SVGID_2_);}
.st4{fill:url(#SVGID_3_);}
.st5{fill:#D9D9D9;}
.st6{fill:url(#SVGID_4_);}
.st7{opacity:0.47;}
.st8{fill:#5B5A5A;}
.st9{fill:#95C980;}
.st10{fill:#72B755;}
.st11{fill:#4FA52B;}
.st12{fill:#EE8C89;}
.st13{fill:#E96562;}
.st14{fill:#E33F3B;}
.st15{fill:#EFB075;}
.st16{fill:#E99547;}
.st17{fill:#E47B19;}
.st18{opacity:0.38;fill:url(#SVGID_5_);enable-background:new ;}
</style>
<g id="Layer_1_2_">
<g id="Layer_1_1_">
</g>
</g>
<g id="Layer_2_1_">
<polygon class="st0" points="69.5,48.6 69.3,93.1 4,95.2 3.3,93.7 29.6,53.4 "/>
<path class="st1" d="M69.5,47l-0.2,46.1c0,0-66.3,1-66,0.6l26.1-41.8L69.5,47z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="69.458" y1="120.0202" x2="219.2576" y2="120.0202" gradientTransform="matrix(1 0 0 1 0 8)">
<stop offset="0" style="stop-color:#E3E3E3"/>
<stop offset="1" style="stop-color:#F4F4F4"/>
</linearGradient>
<polygon class="st2" points="69.5,47 218.9,55.6 219.3,202.6 69.9,209.1 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="29.2408" y1="120.0202" x2="69.8681" y2="120.0202" gradientTransform="matrix(1 0 0 1 0 8)">
<stop offset="0" style="stop-color:#D9D9D9"/>
<stop offset="1" style="stop-color:#D4D4D4"/>
</linearGradient>
<polygon class="st3" points="29.2,51.8 69.5,47 69.8,209.1 29.2,204.4 "/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="151.9309" y1="42.7213" x2="142.8473" y2="-43.5726" gradientTransform="matrix(0.9941 1.431752e-03 1.431754e-03 1.1143 -3.0394 44.4335)">
<stop offset="0" style="stop-color:#B3B3B3"/>
<stop offset="0.4752" style="stop-color:#B5B5B5"/>
<stop offset="0.6464" style="stop-color:#BCBCBC"/>
<stop offset="0.7685" style="stop-color:#C7C7C7"/>
<stop offset="0.8671" style="stop-color:#D8D8D8"/>
<stop offset="0.9506" style="stop-color:#EEEEEE"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon class="st4" points="219.3,98.5 97.4,93.2 69.5,47.3 218.9,55.6 "/>
<polygon class="st1" points="102,85.3 251.2,93 252.8,91.1 72.2,48.9 69.5,47 "/>
<polygon class="st5" points="252.8,91.1 128,84.6 102,82.9 69.8,47.3 219.1,55.6 233.6,71.4 252.3,90.6 252.3,90.6 "/>
<radialGradient id="SVGID_4_" cx="445.2994" cy="-436.338" r="4.0179" gradientTransform="matrix(0.5088 -4.329579e-03 0.1464 14.7395 -92.0455 6569.5317)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="6.758273e-02" style="stop-color:#FFFFFF;stop-opacity:0.9324"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<path class="st6" d="M72.2,152.5c0.2,26.2,0.9,42.4,0.1,42.4c-0.9,0-1.5-6.3-2.5-32.3c-1.1-26.1-1.4-85-0.5-85.1
C70.1,77.2,71.9,126.4,72.2,152.5z"/>
<g class="st7">
<path class="st8" d="M29.1,203.9l20.4,2.1c3.3,0.4,6.9,0.6,10.2,1.1l10.2,1.2h-0.1l74.7-3.2l37.4-1.7l9.3-0.4
c3.1-0.1,6.3-0.2,9.3-0.4l18.7-0.5l-18.7,1.2c-3.1,0.2-6.3,0.4-9.3,0.5l-9.3,0.4l-37.4,1.7l-74.5,3.2l0,0l0,0L59.7,208
c-3.3-0.4-6.8-0.9-10.2-1.4L29.1,203.9z"/>
<path class="st1" d="M28.6,203.9c3.3,0.2,6.8,0.4,10.3,0.6s7.1,0.5,10.6,0.9l10.2,1.1l10.2,1.2l-0.1,1.1h-0.1v-1.1l74.8-3.1
l37.4-1.6l18.7-0.7l18.7-0.5v0.6l-18.7,1.1l-9.3,0.5l-9.3,0.4l-37.4,1.6l-74.7,3.1l0,0l0,0l-10.2-1.2l-10.2-1.4L29,203.8
L28.6,203.9z M30.3,204.1l19.2,2.5l10.2,1.4l10.2,1.2l0,0l74.7-3.3l37.4-1.7l9.3-0.4l9.3-0.5l18.7-1.2v0.6l-18.7,0.5l-18.7,0.7
l-37.4,1.7l-74.7,3.3v-1.1h0.1l-0.1,1.1l-10.2-1.2l-10.2-1.1c-3.3-0.4-6.5-0.6-9.7-1.1C36.6,205,33.5,204.5,30.3,204.1z"/>
</g>
<g>
<g>
<g>
<path class="st9" d="M181.4,136.4c-8.7,6.8-23.5,8.1-33.8,5.5c2.6-2.3,3.8-3.4,6.3-5.8c2.5-2.2,3.6-3.2,6-5.4
c8.4-7.4,12.5-10.8,20.7-17.7c-8.5,6.4-12.9,9.6-21.6,16.4c-2.5,2-3.7,2.8-6.1,4.8c-2.6,2-3.8,3.1-6.4,5
c-0.5-9.5,1.1-22.1,10.3-28.9c0.7-0.6,1.7-1.1,2.6-1.7c1.2-0.6,2.5-1.4,3.9-1.8c11.4-4.4,24.8-7.5,37.3-5.9
c0.7,6.5-4.9,18.9-11.8,28.2c-1,1.2-1.8,2.5-2.8,3.6C184.2,133.9,182.7,135.3,181.4,136.4z"/>
<path class="st10" d="M185.6,132.4c-9.2,6-22.6,5.8-31.7,3.7c2.5-2.2,3.6-3.2,6-5.4c8.4-7.4,12.5-10.8,20.7-17.7
c-8.5,6.4-12.9,9.6-21.6,16.4c-2.5,2-3.7,2.8-6.1,4.8c-0.5-7.9,0.4-18.4,6.5-25.5c1.2-0.6,2.5-1.4,3.9-1.8
c11.4-4.6,24.8-7.5,37.3-5.9c0.7,6.5-4.9,18.9-11.8,28.2C187.5,130.1,186.5,131.3,185.6,132.4z"/>
<path class="st11" d="M188.5,128.9c-8.9,4.2-20.5,3.8-28.5,1.8c8.4-7.4,12.5-10.8,20.7-17.7c-8.5,6.4-12.9,9.6-21.6,16.4
c-0.5-6.8,0-15.7,4.3-22.6c11.4-4.4,24.8-7.5,37.3-5.9C201.2,107.4,195.5,119.9,188.5,128.9z"/>
</g>
<g>
<path class="st12" d="M140.4,169.2c-3.6-8.9,0.5-19.6,4.7-26c1.1,2.5,1.6,3.7,2.7,6c1.1,2.3,1.6,3.4,2.6,5.7
c3.7,7.9,5.5,11.8,9.3,19.2c-3.1-7.6-4.7-11.6-7.9-19.7c-0.9-2.2-1.4-3.3-2.2-5.5c-1-2.3-1.5-3.6-2.3-6
c7.4,2.2,16.8,6.6,20.3,15c0.2,0.7,0.5,1.5,0.7,2.2c0.2,1,0.5,2.1,0.6,3.2c1.5,9.6-0.9,23-4.4,28c-5.5-0.9-14.5-7.7-20-15.1
c-0.7-1-1.5-2-2.1-3C141.7,171.9,141,170.6,140.4,169.2z"/>
<path class="st13" d="M142.5,173.3c-2.3-8.4,1.5-18.1,5.4-24c1.1,2.3,1.6,3.4,2.6,5.7c3.7,7.9,5.5,11.8,9.3,19.2
c-3.1-7.6-4.7-11.6-7.9-19.7c-0.9-2.2-1.4-3.3-2.2-5.5c6.3,1.7,14.4,5.2,18.7,11.3c0.2,1,0.5,2.1,0.6,3.2
c1.5,9.6-0.9,23-4.4,27.9c-5.5-0.9-14.5-7.7-20-15.1C143.9,175.2,143.3,174.3,142.5,173.3z"/>
<path class="st14" d="M144.6,176.2c-1.1-7.5,2.5-16,5.9-21.3c3.7,7.9,5.5,11.8,9.3,19.2c-3.1-7.6-4.7-11.6-7.9-19.7
c5.5,1.4,12.5,4.1,17.2,8.9c1.5,9.6-0.9,23-4.4,27.9C159,190.4,150.1,183.6,144.6,176.2z"/>
</g>
<g>
<path class="st15" d="M125.9,116.6c10.5,4.3,16.5,15.4,18.8,23.4c-3-1-4.3-1.4-7.3-2.3c-2.8-0.9-4.2-1.4-6.9-2.2
c-9.7-3.2-14.5-4.9-23.9-8.2c9.1,3.8,13.7,5.8,23.1,9.6c2.6,1.1,3.9,1.6,6.5,2.7c2.7,1.1,4.1,1.6,6.9,2.7
c-7.4,4.2-18.4,8.4-28.3,4.6c-0.7-0.2-1.7-0.7-2.6-1.2c-1-0.6-2.2-1.2-3.3-2.1c-8.5-6-17.6-16.7-20.9-26.8
c4.9-3.4,17.6-4.2,28.3-2.3c1.5,0.2,2.8,0.5,4.3,0.9C122.7,115.4,124.3,115.8,125.9,116.6z"/>
<path class="st16" d="M120.7,114.9c9.1,4.8,14.5,15,16.7,22.6c-2.8-0.9-4.2-1.4-6.9-2.2c-9.7-3.2-14.5-4.9-23.9-8.2
c9.1,3.8,13.7,5.8,23,9.6c2.6,1.1,3.9,1.6,6.5,2.7c-6.1,3.6-15.4,7.4-23.9,6c-1-0.6-2.2-1.2-3.3-2.1c-8.5-6-17.6-16.7-20.9-26.8
c4.9-3.4,17.6-4.2,28.3-2.3C118,114.2,119.4,114.4,120.7,114.9z"/>
<path class="st17" d="M116.6,113.9c7.5,5.3,12.1,14.4,14,21.3c-9.7-3.2-14.5-4.9-23.9-8.2c9.1,3.8,13.7,5.8,23.1,9.6
c-5.4,3.2-13,6.6-20.7,6.5c-8.5-6-17.6-16.7-20.9-26.8C93.2,112.8,105.7,112,116.6,113.9z"/>
</g>
</g>
</g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="241.7537" y1="104.2354" x2="160.0455" y2="55.1756" gradientTransform="matrix(1 0 0 -1 0 256)">
<stop offset="0.1721" style="stop-color:#C7C7C7"/>
<stop offset="0.3798" style="stop-color:#D8D8D8"/>
<stop offset="0.6814" style="stop-color:#DADADA"/>
<stop offset="0.7898" style="stop-color:#E1E1E1"/>
<stop offset="0.867" style="stop-color:#ECECEC"/>
<stop offset="0.8745" style="stop-color:#EEEEEE"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<path class="st18" d="M219.1,128.3c-1,0.4-3.3,15.7-3.7,19.2c-0.7,5.8-3.9,28.7-11.1,41.2c-7.3,12.8-15.7,13.7-16.4,14.6l31.1-0.9
C219.1,179.1,219.1,151.5,219.1,128.3L219.1,128.3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

258
package-lock.json generated
View File

@ -108,6 +108,7 @@
"@electron-forge/cli": "7.6.1",
"@electron-forge/maker-deb": "7.6.1",
"@electron-forge/maker-dmg": "7.6.1",
"@electron-forge/maker-flatpak": "7.6.1",
"@electron-forge/maker-rpm": "7.6.1",
"@electron-forge/maker-squirrel": "7.6.1",
"@electron-forge/maker-zip": "7.6.1",
@ -152,7 +153,7 @@
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.0.5",
"cross-env": "7.0.3",
"electron": "34.0.2",
"electron": "34.1.0",
"esm": "3.2.25",
"jasmine": "5.5.0",
"jsdoc": "4.0.4",
@ -781,6 +782,52 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/@electron-forge/maker-flatpak": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-flatpak/-/maker-flatpak-7.6.1.tgz",
"integrity": "sha512-a9EekF8cNzjizwMs8HObxRii2tkLrTcTNMvWNhQqcJohEkJV81zNOLu9l/OdIMlKQ9cF5SuRvA4/m2bQGfT80w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron-forge/maker-base": "7.6.1",
"@electron-forge/shared-types": "7.6.1",
"fs-extra": "^10.0.0"
},
"engines": {
"node": ">= 16.4.0"
},
"optionalDependencies": {
"@malept/electron-installer-flatpak": "^0.11.4"
}
},
"node_modules/@electron-forge/maker-flatpak/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@electron-forge/maker-flatpak/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@electron-forge/maker-rpm": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.6.1.tgz",
@ -2752,6 +2799,203 @@
"node": ">= 12.13.0"
}
},
"node_modules/@malept/electron-installer-flatpak": {
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/@malept/electron-installer-flatpak/-/electron-installer-flatpak-0.11.4.tgz",
"integrity": "sha512-ZdwhT4WeeJWdnsmALUtQ7bn4pzYVh0Vg+4NnF1S3n3OACc9IWg+B+LxI5gT3XSXIrxogouqkjM6gD8S592awyA==",
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin",
"linux"
],
"dependencies": {
"@malept/flatpak-bundler": "^0.4.0",
"debug": "^4.1.1",
"electron-installer-common": "^0.10.0",
"lodash": "^4.17.15",
"semver": "^7.1.1",
"yargs": "^16.0.0"
},
"bin": {
"electron-installer-flatpak": "bin/electron-installer-flatpak.js"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"license": "ISC",
"optional": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/@malept/electron-installer-flatpak/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@malept/electron-installer-flatpak/node_modules/yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
"license": "ISC",
"optional": true,
"engines": {
"node": ">=10"
}
},
"node_modules/@malept/flatpak-bundler": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
"integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"debug": "^4.1.1",
"fs-extra": "^9.0.0",
"lodash": "^4.17.15",
"tmp-promise": "^3.0.2"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@mermaid-js/layout-elk": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/@mermaid-js/layout-elk/-/layout-elk-0.1.7.tgz",
@ -7666,9 +7910,9 @@
}
},
"node_modules/electron": {
"version": "34.0.2",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.0.2.tgz",
"integrity": "sha512-u3F+DSUlg9NaGS+9qnYmSRN8VjAnc3LJDDk1ye1uISJnh4gjG76y3681qLowsPMx4obvCP2eBINnmbLo0yT5WA==",
"version": "34.1.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.1.0.tgz",
"integrity": "sha512-ZUid8XrGPA0dfes97PPADc8ecWOUX/qYRNp1glze9coZLEYc+PsMvgjVDCHSvjfHfiI+V3unwngSVpBouX71YQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@ -8387,9 +8631,9 @@
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "20.17.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz",
"integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==",
"version": "20.17.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz",
"integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"

View File

@ -150,6 +150,7 @@
"@electron-forge/cli": "7.6.1",
"@electron-forge/maker-deb": "7.6.1",
"@electron-forge/maker-dmg": "7.6.1",
"@electron-forge/maker-flatpak": "7.6.1",
"@electron-forge/maker-rpm": "7.6.1",
"@electron-forge/maker-squirrel": "7.6.1",
"@electron-forge/maker-zip": "7.6.1",
@ -194,7 +195,7 @@
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.0.5",
"cross-env": "7.0.3",
"electron": "34.0.2",
"electron": "34.1.0",
"esm": "3.2.25",
"jasmine": "5.5.0",
"jsdoc": "4.0.4",

View File

@ -4,14 +4,14 @@ import BAttribute from "../../src/becca/entities/battribute.js";
import becca from "../../src/becca/becca.js";
import randtoken from "rand-token";
import type SearchResult from "../../src/services/search/search_result.js";
import type { NoteType } from "../../src/becca/entities/rows.js";
import type { NoteRow, NoteType } from "../../src/becca/entities/rows.js";
randtoken.generator({ source: "crypto" });
function findNoteByTitle(searchResults: Array<SearchResult>, title: string): BNote | undefined {
export function findNoteByTitle(searchResults: Array<SearchResult>, title: string): BNote | undefined {
return searchResults.map((sr) => becca.notes[sr.noteId]).find((note) => note.title === title);
}
class NoteBuilder {
export class NoteBuilder {
note: BNote;
constructor(note: BNote) {
this.note = note;
@ -55,11 +55,11 @@ class NoteBuilder {
}
}
function id() {
export function id() {
return randtoken.generate(10);
}
function note(title: string, extraParams = {}) {
export function note(title: string, extraParams: Partial<NoteRow> = {}) {
const row = Object.assign(
{
noteId: id(),
@ -74,9 +74,3 @@ function note(title: string, extraParams = {}) {
return new NoteBuilder(note);
}
export default {
NoteBuilder,
findNoteByTitle,
note
};

View File

@ -79,7 +79,7 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b
return $container;
}
const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType"];
const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation"];
async function renderNormalAttributes(note: FNote) {
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();

View File

@ -238,12 +238,16 @@ async function renderMermaid(note: FNote, $renderedContent: JQuery<HTMLElement>)
* @param {FNote} note
* @returns {Promise<void>}
*/
async function renderChildrenList($renderedContent: JQuery<HTMLElement>, note: FNote) {
async function renderChildrenList($renderedContent: JQuery<HTMLElement>, note: FNote) {
let childNoteIds = note.getChildNoteIds();
if (!childNoteIds.length) {
return;
}
$renderedContent.css("padding", "10px");
$renderedContent.addClass("text-with-ellipsis");
let childNoteIds = note.getChildNoteIds();
if (childNoteIds.length > 10) {
childNoteIds = childNoteIds.slice(0, 10);
}

View File

@ -376,6 +376,10 @@ function linkContextMenu(e: PointerEvent) {
const $link = $(e.target as any).closest("a");
const url = $link.attr("href") || $link.attr("data-href");
if ($link.attr("data-no-context-menu")) {
return;
}
const { notePath, viewScope } = parseNavigationStateFromUrl(url);
if (!notePath) {

View File

@ -140,10 +140,6 @@ async function renderTooltip(note: FNote | null) {
}
const noteTitleWithPathAsSuffix = await treeService.getNoteTitleWithPathAsSuffix(bestNotePath);
let content = "";
if (noteTitleWithPathAsSuffix) {
content = `<h5 class="note-tooltip-title">${noteTitleWithPathAsSuffix.prop("outerHTML")}</h5>`;
}
const { $renderedAttributes } = await attributeRenderer.renderNormalAttributes(note);
@ -151,8 +147,21 @@ async function renderTooltip(note: FNote | null) {
tooltip: true,
trim: true
});
const isContentEmpty = ($renderedContent[0].innerHTML.length === 0);
content = `${content}<div class="note-tooltip-attributes">${$renderedAttributes[0].outerHTML}</div>${$renderedContent[0].outerHTML}`;
let content = "";
if (noteTitleWithPathAsSuffix) {
const classes = [ "note-tooltip-title" ];
if (isContentEmpty) {
classes.push("note-no-content");
}
content = `<h5 class="${classes.join(" ")}"><a href="#${note.noteId}" data-no-context-menu="true">${noteTitleWithPathAsSuffix.prop("outerHTML")}</a></h5>`;
}
content = `${content}<div class="note-tooltip-attributes">${$renderedAttributes[0].outerHTML}</div>`;
if (!isContentEmpty) {
content += $renderedContent[0].outerHTML;
}
return content;
}

View File

@ -13,7 +13,7 @@ import appContext from "../../components/app_context.js";
import type { Attribute } from "../../services/attribute_parser.js";
const TPL = `
<div class="attr-detail">
<div class="attr-detail tn-tool-dialog">
<style>
.attr-detail {
display: block;
@ -48,10 +48,14 @@ const TPL = `
text-align: left;
}
.attr-edit-table td input {
.attr-edit-table td input[not(type="checkbox")] {
width: 100%;
}
.attr-edit-table td input[type="checkbox"] {
display: inline-block;
}
.close-attr-detail-button {
font-size: x-large;
cursor: pointer;
@ -76,7 +80,7 @@ const TPL = `
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<h5 class="attr-detail-title">${t("attribute_detail.attr_detail_title")}</h5>
<span class="bx bx-x close-attr-detail-button" title="${t("attribute_detail.close_button_title")}"></span>
<span class="bx bx-x close-attr-detail-button tn-tool-button" title="${t("attribute_detail.close_button_title")}"></span>
</div>
<div class="attr-is-owned-by">${t("attribute_detail.attr_is_owned_by")}</div>
@ -104,7 +108,7 @@ const TPL = `
<th></th>
<td>
<label class="tn-checkbox">
<input type="checkbox" class="attr-input-promoted form-check" />
<input type="checkbox" class="attr-input-promoted" />
${t("attribute_detail.promoted")}
</label>
</td>
@ -161,7 +165,7 @@ const TPL = `
<th></th>
<td>
<label class="tn-checkbox">
<input type="checkbox" class="attr-input-inheritable form-check" />
<input type="checkbox" class="attr-input-inheritable" />
${t("attribute_detail.inheritable")}
</label>
</td>

View File

@ -33,9 +33,9 @@ const DROPDOWN_TPL = `
<div class="calendar-header">
<div class="calendar-month-selector">
<button class="calendar-btn bx bx-chevron-left" data-calendar-toggle="previous"></button>
<button class="calendar-btn tn-tool-button bx bx-chevron-left" data-calendar-toggle="previous"></button>
<button class="btn dropdown-toggle" type="button"
<button class="btn dropdown-toggle select-button" type="button"
data-bs-toggle="dropdown" data-bs-auto-close="true"
aria-expanded="false"
data-calendar-input="month"></button>
@ -45,15 +45,15 @@ const DROPDOWN_TPL = `
.join("")}
</ul>
<button class="calendar-btn bx bx-chevron-right" data-calendar-toggle="next"></button>
<button class="calendar-btn tn-tool-button bx bx-chevron-right" data-calendar-toggle="next"></button>
</div>
<div class="calendar-year-selector">
<button class="calendar-btn bx bx-chevron-left" data-calendar-toggle="previousYear"></button>
<button class="calendar-btn tn-tool-button bx bx-chevron-left" data-calendar-toggle="previousYear"></button>
<input type="number" min="1900" max="2999" step="1" data-calendar-input="year" />
<button class="calendar-btn bx bx-chevron-right" data-calendar-toggle="nextYear"></button>
<button class="calendar-btn tn-tool-button bx bx-chevron-right" data-calendar-toggle="nextYear"></button>
</div>
</div>

View File

@ -18,7 +18,9 @@ const TPL = `
<div class="form-group">
<label for="import-file-upload-input"><strong>${t("import.chooseImportFile")}</strong></label>
<input type="file" class="import-file-upload-input form-control-file" multiple />
<label class="tn-file-input tn-input-field">
<input type="file" class="import-file-upload-input form-control-file" multiple />
</label>
<p>${t("import.importDescription")} <strong class="import-note-title"></strong>.
</div>

View File

@ -17,14 +17,16 @@ const TPL = `
<div class="modal-body">
<div class="form-group">
<label for="upload-attachment-file-upload-input"><strong>${t("upload_attachments.choose_files")}</strong></label>
<input type="file" class="upload-attachment-file-upload-input form-control-file" multiple />
<label class="tn-file-input tn-input-field">
<input type="file" class="upload-attachment-file-upload-input form-control-file" multiple />
</label>
<p>${t("upload_attachments.files_will_be_uploaded")} <strong class="upload-attachment-note-title"></strong>.</p>
</div>
<div class="form-group">
<strong>${t("upload_attachments.options")}:</strong>
<div class="checkbox">
<label data-bs-toggle="tooltip" title="${escapeQuotes(t("upload_attachments.tooltip"))}">
<label class="tn-checkbox" data-bs-toggle="tooltip" title="${escapeQuotes(t("upload_attachments.tooltip"))}">
<input class="shrink-images-checkbox form-check-input" value="1" type="checkbox" checked> <span>${t("upload_attachments.shrink_images")}</span>
</label>
</div>

View File

@ -14,10 +14,14 @@ const TPL = `
}
.editability-dropdown .dropdown-item {
display: block !important;
display: flex !importamt;
}
.editability-dropdown .dropdown-item div {
.editability-dropdown .dropdown-item > div {
margin-left: 10px;
}
.editability-dropdown .description {
font-size: small;
color: var(--muted-text-color);
white-space: normal;
@ -30,18 +34,24 @@ const TPL = `
<div class="editability-dropdown dropdown-menu dropdown-menu-right tn-dropdown-list">
<a class="dropdown-item" href="#" data-editability="auto">
<span class="check">&check;</span>
${t("editability_select.auto")}
<div>${t("editability_select.note_is_editable")}</div>
<div>
${t("editability_select.auto")}
<div class="description">${t("editability_select.note_is_editable")}</div>
</div>
</a>
<a class="dropdown-item" href="#" data-editability="readOnly">
<span class="check">&check;</span>
${t("editability_select.read_only")}
<div>${t("editability_select.note_is_read_only")}</div>
<div>
${t("editability_select.read_only")}
<div class="description">${t("editability_select.note_is_read_only")}</div>
</div>
</a>
<a class="dropdown-item" href="#" data-editability="autoReadOnlyDisabled">
<span class="check">&check;</span>
${t("editability_select.always_editable")}
<div>${t("editability_select.note_is_always_editable")}</div>
<div>
${t("editability_select.always_editable")}
<div class="description">${t("editability_select.note_is_always_editable")}</div>
</div>
</a>
</div>
</div>

View File

@ -5,24 +5,39 @@ import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js";
const NOTE_TYPES = [
{ type: "file", title: t("note_types.file"), selectable: false },
{ type: "image", title: t("note_types.image"), selectable: false },
{ type: "search", title: t("note_types.saved-search"), selectable: false },
{ type: "noteMap", mime: "", title: t("note_types.note-map"), selectable: false },
{ type: "launcher", mime: "", title: t("note_types.launcher"), selectable: false },
{ type: "doc", mime: "", title: t("note_types.doc"), selectable: false },
{ type: "contentWidget", mime: "", title: t("note_types.widget"), selectable: false },
// The suggested note type ordering method: insert the item into the corresponding group,
// then ensure the items within the group are ordered alphabetically.
// The default note type (always the first item)
{ type: "text", mime: "text/html", title: t("note_types.text"), selectable: true },
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), selectable: true },
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), selectable: true },
{ type: "render", mime: "", title: t("note_types.render-note"), selectable: true },
// Text notes group
{ type: "book", mime: "", title: t("note_types.book"), selectable: true },
// Graphic notes
{ type: "canvas", mime: "application/json", title: t("note_types.canvas"), selectable: true },
{ type: "mermaid", mime: "text/mermaid", title: t("note_types.mermaid-diagram"), selectable: true },
{ type: "book", mime: "", title: t("note_types.book"), selectable: true },
// Map notes
{ type: "geoMap", mime: "application/json", title: t("note_types.geo-map"), isBeta: true, selectable: true },
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), selectable: true },
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), selectable: true },
// Misc note types
{ type: "render", mime: "", title: t("note_types.render-note"), selectable: true },
{ type: "webView", mime: "", title: t("note_types.web-view"), selectable: true },
{ type: "geoMap", mime: "application/json", title: t("note_types.geo-map"), selectable: true },
{ type: "code", mime: "text/plain", title: t("note_types.code"), selectable: true }
// Code notes
{ type: "code", mime: "text/plain", title: t("note_types.code"), selectable: true },
// Reserved types (cannot be created by the user)
{ type: "contentWidget", mime: "", title: t("note_types.widget"), selectable: false },
{ type: "doc", mime: "", title: t("note_types.doc"), selectable: false },
{ type: "file", title: t("note_types.file"), selectable: false },
{ type: "image", title: t("note_types.image"), selectable: false },
{ type: "launcher", mime: "", title: t("note_types.launcher"), selectable: false },
{ type: "noteMap", mime: "", title: t("note_types.note-map"), selectable: false },
{ type: "search", title: t("note_types.saved-search"), selectable: false }
];
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => !nt.selectable).map((nt) => nt.type);
@ -30,11 +45,18 @@ const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => !nt.selectable).map(
const TPL = `
<div class="dropdown note-type-widget">
<style>
.note-type-dropdown {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
.note-type-dropdown {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
.note-type-dropdown .badge {
margin-left: 8px;
background: var(--accented-background-color);
font-weight: normal;
color: var(--menu-text-color);
}
</style>
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle select-button note-type-button">
<span class="note-type-desc"></span>
@ -74,11 +96,16 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
for (const noteType of NOTE_TYPES.filter((nt) => nt.selectable)) {
let $typeLink;
const $title = $("<span>").text(noteType.title);
if (noteType.isBeta) {
$title.append($(`<span class="badge">`).text(t("note_types.beta-feature")));
}
if (noteType.type !== "code") {
$typeLink = $('<a class="dropdown-item">')
.attr("data-note-type", noteType.type)
.append('<span class="check">&check;</span> ')
.append($("<span>").text(noteType.title))
.append($title)
.on("click", (e) => {
const type = $typeLink.attr("data-note-type");
const noteType = NOTE_TYPES.find((nt) => nt.type === type);

View File

@ -42,6 +42,7 @@ const TAB_ROW_TPL = `
width: 100%;
background: var(--main-background-color);
overflow: hidden;
user-select: none;
}
.tab-row-widget.full-width {

View File

@ -48,11 +48,12 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
this.$wrapper.empty();
this.children = [];
const $helpButton = $(
'<button class="attachment-help-button" type="button" data-help-page="attachments.html" title="' +
t("attachment_detail.open_help_page") +
'"><span class="bx bx-help-circle"></span></button>'
);
const $helpButton = $(`
<button class="attachment-help-button icon-action bx bx-help-circle"
type="button" data-help-page="attachments.html"
title="${t("attachment_detail.open_help_page")}"
</button>
`);
utils.initHelpButtons($helpButton);
this.$linksWrapper.empty().append(

View File

@ -40,11 +40,12 @@ export default class AttachmentListTypeWidget extends TypeWidget {
}
async doRefresh(note) {
const $helpButton = $(
'<button class="attachment-help-button" type="button" data-help-page="attachments.html" title="' +
t("attachment_list.open_help_page") +
'"><span class="bx bx-help-circle"></span></button>'
);
const $helpButton = $(`
<button class="attachment-help-button icon-action bx bx-help-circle"
type="button" data-help-page="attachments.html"
title="${t("attachment_list.open_help_page")}">
</button>
`);
utils.initHelpButtons($helpButton);
const noteLink = await linkService.createLink(this.noteId); // do separately to avoid race condition between empty() and .append()
@ -52,7 +53,7 @@ export default class AttachmentListTypeWidget extends TypeWidget {
this.$linksWrapper.empty().append(
$("<div>").append(t("attachment_list.owning_note"), noteLink),
$("<div>").append(
$(`<div class="attachment-actions-toolbar">`).append(
$('<button class="btn btn-sm">')
.text(t("attachment_list.upload_attachments"))
.on("click", () => this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId })),

View File

@ -12,6 +12,7 @@ import asset_path from "../../../../services/asset_path.js";
import openContextMenu from "./geo_map_context_menu.js";
import link from "../../services/link.js";
import note_tooltip from "../../services/note_tooltip.js";
import appContext from "../../components/app_context.js";
const TPL = `\
<div class="note-detail-geo-map note-detail-printable">
@ -229,7 +230,15 @@ export default class GeoMapTypeWidget extends TypeWidget {
.on("moveend", e => {
this.moveMarker(note.noteId, (e.target as Marker).getLatLng());
});
marker.on("mousedown", ({ originalEvent }) => {
// Middle click to open in new tab
if (originalEvent.button === 1) {
const hoistedNoteId = this.hoistedNoteId;
//@ts-ignore, fix once tab manager is ported.
appContext.tabManager.openInNewTab(note.noteId, hoistedNoteId);
return true;
}
});
marker.on("contextmenu", (e) => {
openContextMenu(note.noteId, e.originalEvent);
});

View File

@ -41,25 +41,41 @@
flex-grow: 1;
}
.calendar-dropdown-widget .calendar-header input[type="number"] {
appearance: textfield !important;
}
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-outer-spin-button,
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.calendar-dropdown-widget .calendar-header select {
cursor: pointer;
}
.calendar-dropdown-widget .calendar-header input,
.calendar-dropdown-widget .calendar-header select {
.calendar-dropdown-widget .calendar-header .dropdown-toggle {
appearance: none;
text-align: center;
border: 0;
border-bottom: 1px solid var(--button-border-color);
background-color: var(--menu-background-color) !important;
border-left: unset;
background-color: var(--menu-background-color);
font-weight: bold;
outline: 0;
}
.calendar-dropdown-widget .calendar-header .dropdown-toggle::after {
border: unset; /* Disable the dropdown arrow */
}
.calendar-dropdown-widget .calendar-week {
display: flex;
flex-wrap: wrap;
}
.calendar-dropdown-widget .calendar-week span {
flex-direction: column;
flex: 0 0 14.28%;
@ -69,6 +85,7 @@
padding-top: 5px;
padding-bottom: 5px;
text-align: center;
text-transform: uppercase;
}
.calendar-dropdown-widget .calendar-body {
@ -79,7 +96,6 @@
.calendar-dropdown-widget .calendar-date {
align-items: center;
color: var(--main-text-color);
background-color: var(--main-background-color);
display: flex;
flex-direction: column;
flex: 0 0 14.28%;
@ -91,6 +107,7 @@
.calendar-dropdown-widget .calendar-date:hover {
color: var(--hover-item-text-color);
background-color: var(--hover-item-background-color);
text-decoration: underline;
}
.calendar-dropdown-widget .calendar-date-active {

View File

@ -1536,15 +1536,16 @@ textarea {
display: none !important;
}
.attachment-actions-toolbar {
display: flex;
align-items: center;
}
.attachment-help-button {
font-size: xx-large;
border: 0;
background: none;
cursor: pointer;
color: var(--main-text-color);
margin-left: 20px;
position: relative;
top: 8px;
display: inline-block;
margin-left: 10px;
vertical-align: middle;
font-size: 1em;
}
.jump-to-note-dialog .modal-header {

View File

@ -19,6 +19,9 @@
--accented-background-color: #555;
--tool-dialog-background-color: #262626;
--tool-dialog-shadow-color: black;
--button-text-color: currentColor;
--cmd-button-background-color: #ffffff28;

View File

@ -19,6 +19,9 @@
--accented-background-color: #f5f5f5;
--tool-dialog-background-color: white;
--tool-dialog-shadow-color: #00000070;
--button-text-color: currentColor;
--cmd-button-background-color: #0000000f;
@ -75,7 +78,7 @@
--menu-text-color: #272727;
--menu-background-color: #ffffffd9;
--menu-item-icon-color: #727272;
--menu-item-disabled-opacity: 0.5;
--menu-item-disabled-opacity: 0.6;
--menu-item-keyboard-shortcut-color: #666666a8;
--menu-item-arrow-color: #00000080;
--menu-item-delimiter-color: #00000030;

View File

@ -67,6 +67,14 @@
--tab-note-icons: true;
}
/* Tool dialogs - small dialogs without a backdrop */
div.tn-tool-dialog {
border-radius: 10px;
background: var(--tool-dialog-background-color) !important;
user-select: none;
box-shadow: 10px 10px 93px -25px var(--tool-dialog-shadow-color);
}
/*
* Note search suggestions
*/

View File

@ -141,10 +141,12 @@ input[type="text"],
input[type="number"],
input[type="password"],
textarea.form-control,
textarea {
textarea,
.tn-input-field {
outline: 3px solid transparent;
outline-offset: 6px;
border: unset;
border-radius: var(--bs-border-radius);
background: var(--input-background-color);
color: var(--input-text-color);
}
@ -154,7 +156,8 @@ input[type="text"]:hover,
input[type="number"]:hover,
input[type="password"]:hover,
textarea.form-control:hover,
textarea:hover {
textarea:hover,
.tn-input-field:hover {
background: var(--input-hover-background);
color: var(--input-hover-color);
}
@ -164,7 +167,9 @@ input[type="text"]:focus,
input[type="number"]:focus,
input[type="password"]:focus,
textarea.form-control:focus,
textarea:focus {
textarea:focus,
.tn-input-field:focus,
.tn-input-field:focus-within {
box-shadow: unset;
outline: 3px solid var(--input-focus-outline-color);
outline-offset: 0;
@ -337,6 +342,53 @@ optgroup {
line-height: 40px;
}
/*
* File input
*
* <label class="tn-file-input tn-input-field">
* <input type="file" />
* </label>
*/
.tn-file-input {
position: relative;
padding: .375rem 2.25rem .375rem .75rem;
}
.tn-file-input input[type="file"] {
background: transparent;
}
.tn-file-input input[type="file"]::file-selector-button {
/* Hide the "Browse..." button */
display: none;
}
.tn-file-input:hover input[type="file"] {
color: var(--input-hover-color);
}
.tn-file-input input[type="file"]:focus,
.tn-file-input input[type="file"]:active {
outline: none;
color: var(--input-focus-color);
}
/* The browse icon */
.tn-file-input::before {
display: flex;
position: absolute;
justify-content: center;
align-items: center;
content: "\eae1";
width: 2em;
height: 100%;
right: 0;
top: 0;
font-size: 1.2em;
font-family: boxicons;
}
/* Check boxes and radio buttons */
@supports selector(label:has(*)) {

View File

@ -1,3 +1,29 @@
/*
* Basic properties
*/
/* Note type dropdown */
div.note-type-dropdown .check {
margin-right: 6px;
}
/* Editability dropdown */
div.editability-dropdown a.dropdown-item {
padding: 4px 16px 4px 0;
align-items: start !important;
}
.editability-dropdown .dropdown-item .check {
margin-left: 4px;
}
.editability-dropdown .dropdown-item .description {
opacity: .75;
font-size: .85em;
}
/*
* Owned attributes
*/
@ -11,6 +37,11 @@
right: 30px;
}
/* Note path in attribute detail dialog */
.attr-detail .note-path {
margin-left: 8px;
}
/*
* Similar notes
*/

View File

@ -79,4 +79,10 @@
.detail-font-size-input-group,
.monospace-font-size-input-group {
width: fit-content;
}
/* Advanced */
.options-section ul.existing-anonymized-databases {
margin: 1em;
}

View File

@ -956,7 +956,7 @@ body.mobile .note-title {
* supported when this class is used.
*/
.dropdown-menu {
.dropdown-menu:not(.static) {
border-radius: var(--dropdown-border-radius);
padding: var(--menu-padding-size) !important;
font-size: 0.9rem !important;
@ -1034,7 +1034,8 @@ body.mobile .dropdown-submenu:hover {
background: transparent !important;
}
html body .dropdown-item.disabled {
html body .dropdown-item.disabled,
html body .dropdown-item[disabled] {
color: var(--menu-text-color) !important;
opacity: var(--menu-item-disabled-opacity);
}
@ -1087,6 +1088,20 @@ html body .dropdown-item.disabled {
color: var(--menu-item-arrow-color) !important;
}
/* Static menus (used as a list, such as on the note revisions dialog) */
body.desktop .dropdown-menu.static {
box-shadow: unset;
border-radius: 4px;
border: unset;
background-color: var(--card-background-color) !important;
padding: var(--menu-padding-size) !important;
user-select: none;
}
body.desktop .dropdown-menu.static .dropdown-item.active {
--active-item-text-color: var(--menu-text-color);
}
body.desktop .dropdown-menu .dropdown-toggle::after {
height: 100%;
}
@ -1104,34 +1119,38 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
*/
.calendar-dropdown-widget {
width: unset !important;
padding: 12px;
color: var(--calendar-color);
user-select: none;
}
.calendar-dropdown-widget .calendar-header {
padding: 8px 0 20px 0;
}
.calendar-dropdown-widget .calendar-header input[type="number"] {
appearance: textfield !important;
}
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-outer-spin-button,
.calendar-dropdown-widget .calendar-header input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
gap: 10px;
}
.calendar-dropdown-widget .calendar-header input,
.calendar-dropdown-widget .calendar-header [data-calendar-input="month"] {
/* TODO: Provide styling for background and states */
background: transparent !important;
border: unset;
--input-background-color: transparent;
--menu-background-color: transparent;
text-align: center;
font-size: 1.4em;
font-weight: 300;
}
.calendar-dropdown-widget .calendar-header input:not(:focus) {
outline: 3px solid transparent;
}
.calendar-dropdown-widget .calendar-header .calendar-month-selector .select-button {
--select-arrow-svg: ""; /* Disable the dropdown arrow */
min-width: 120px;
padding: 0 10px;
}
.calendar-dropdown-widget .calendar-header .dropdown-toggle::after {
content: unset !important;
}
@ -1174,6 +1193,7 @@ body .calendar-dropdown-widget .calendar-body a:hover {
border-radius: 6px;
background: var(--calendar-day-hover-background);
color: var(--calendar-day-hover-color) !important;
text-decoration: unset;
}
/*
@ -1186,13 +1206,25 @@ body .calendar-dropdown-widget .calendar-body a:hover {
.note-tooltip-content {
padding: 8px;
min-height: 56px;
}
.note-tooltip-content .note-title-with-path {
.note-tooltip-title .note-title-with-path {
display: flex;
flex-direction: column-reverse;
border-bottom: 2px solid currentColor;
}
.note-tooltip-title a {
color: inherit !important;
}
.note-tooltip-title.note-no-content {
margin: 0;
}
.note-tooltip-title:not(.note-no-content) .note-title-with-path {
padding-bottom: 6px;
border-bottom: 2px solid currentColor;
}
.note-tooltip-content .note-path {

View File

@ -1415,7 +1415,8 @@
"doc": "Doc",
"widget": "Widget",
"confirm-change": "It is not recommended to change note type when note content is not empty. Do you want to continue anyway?",
"geo-map": "Geo Map (beta)"
"geo-map": "Geo Map",
"beta-feature": "Beta"
},
"protect_note": {
"toggle-on": "Protect the note",

View File

@ -1381,7 +1381,8 @@
"launcher": "Scurtătură",
"widget": "Widget",
"confirm-change": "Nu se recomandă schimbarea tipului notiței atunci când ea are un conținut. Procedați oricum?",
"geo-map": "Hartă geografică (beta)"
"geo-map": "Hartă geografică",
"beta-feature": "Beta"
},
"protect_note": {
"toggle-off": "Deprotejează notița",

View File

@ -5,7 +5,7 @@ import BBranch from "../../../becca/entities/bbranch.js";
import SearchContext from "../search_context.js";
import dateUtils from "../../date_utils.js";
import becca from "../../../becca/becca.js";
import becca_mocking from "../../../../spec/support/becca_mocking.js";
import { findNoteByTitle, note, NoteBuilder } from "../../../../spec/support/becca_mocking.js";
describe("Search", () => {
let rootNote: any;
@ -13,7 +13,7 @@ describe("Search", () => {
beforeEach(() => {
becca.reset();
rootNote = new becca_mocking.NoteBuilder(new BNote({ noteId: "root", title: "root", type: "text" }));
rootNote = new NoteBuilder(new BNote({ noteId: "root", title: "root", type: "text" }));
new BBranch({
branchId: "none_root",
noteId: "root",
@ -23,18 +23,18 @@ describe("Search", () => {
});
it.skip("simple path match", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria")));
rootNote.child(note("Europe").child(note("Austria")));
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("europe austria", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it.skip("normal search looks also at attributes", () => {
const austria = becca_mocking.note("Austria");
const vienna = becca_mocking.note("Vienna");
const austria = note("Austria");
const vienna = note("Vienna");
rootNote.child(austria.relation("capital", vienna.note)).child(vienna.label("inhabitants", "1888776"));
@ -42,27 +42,27 @@ describe("Search", () => {
let searchResults = searchService.findResultsWithQuery("capital", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("inhabitants", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Vienna")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Vienna")).toBeTruthy();
});
it.skip("normal search looks also at type and mime", () => {
rootNote.child(becca_mocking.note("Effective Java", { type: "book", mime: "" })).child(becca_mocking.note("Hello World.java", { type: "code", mime: "text/x-java" }));
rootNote.child(note("Effective Java", { type: "book", mime: "" })).child(note("Hello World.java", { type: "code", mime: "text/x-java" }));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("book", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Effective Java")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Effective Java")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("text", searchContext); // should match mime
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Hello World.java")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Hello World.java")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("java", searchContext);
@ -70,110 +70,104 @@ describe("Search", () => {
});
it.skip("only end leafs are results", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria")));
rootNote.child(note("Europe").child(note("Austria")));
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("europe", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
});
it.skip("only end leafs are results", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria").label("capital", "Vienna")));
rootNote.child(note("Europe").child(note("Austria").label("capital", "Vienna")));
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("Vienna", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("label comparison with short syntax", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria").label("capital", "Vienna")).child(becca_mocking.note("Czech Republic").label("capital", "Prague")));
rootNote.child(note("Europe").child(note("Austria").label("capital", "Vienna")).child(note("Czech Republic").label("capital", "Prague")));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("#capital=Vienna", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
// case sensitivity:
searchResults = searchService.findResultsWithQuery("#CAPITAL=VIENNA", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("#caPItal=vienNa", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("label comparison with full syntax", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria").label("capital", "Vienna")).child(becca_mocking.note("Czech Republic").label("capital", "Prague")));
rootNote.child(note("Europe").child(note("Austria").label("capital", "Vienna")).child(note("Czech Republic").label("capital", "Prague")));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("# note.labels.capital=Prague", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
it("numeric label comparison", () => {
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.label("country", "", true)
.child(becca_mocking.note("Austria").label("population", "8859000"))
.child(becca_mocking.note("Czech Republic").label("population", "10650000"))
.child(note("Austria").label("population", "8859000"))
.child(note("Czech Republic").label("population", "10650000"))
);
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("#country #population >= 10000000", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
it.skip("inherited label comparison", () => {
rootNote.child(becca_mocking.note("Europe").label("country", "", true).child(becca_mocking.note("Austria")).child(becca_mocking.note("Czech Republic")));
rootNote.child(note("Europe").label("country", "", true).child(note("Austria")).child(note("Czech Republic")));
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("austria #country", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("numeric label comparison fallback to string comparison", () => {
// dates should not be coerced into numbers which would then give wrong numbers
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.label("country", "", true)
.child(becca_mocking.note("Austria").label("established", "1955-07-27"))
.child(becca_mocking.note("Czech Republic").label("established", "1993-01-01"))
.child(becca_mocking.note("Hungary").label("established", "1920-06-04"))
.child(note("Austria").label("established", "1955-07-27"))
.child(note("Czech Republic").label("established", "1993-01-01"))
.child(note("Hungary").label("established", "1920-06-04"))
);
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery('#established <= "1955-01-01"', searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Hungary")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Hungary")).toBeTruthy();
searchResults = searchService.findResultsWithQuery('#established > "1955-01-01"', searchContext);
expect(searchResults.length).toEqual(2);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
it("smart date comparisons", () => {
// dates should not be coerced into numbers which would then give wrong numbers
rootNote.child(
becca_mocking
.note("My note", { dateCreated: dateUtils.localNowDateTime() })
rootNote.child(note("My note", { dateCreated: dateUtils.localNowDateTime() })
.label("year", new Date().getFullYear().toString())
.label("month", dateUtils.localNowDate().substr(0, 7))
.label("date", dateUtils.localNowDate())
@ -188,7 +182,7 @@ describe("Search", () => {
.toEqual(expectedResultCount);
if (expectedResultCount === 1) {
expect(becca_mocking.findNoteByTitle(searchResults, "My note")).toBeTruthy();
expect(findNoteByTitle(searchResults, "My note")).toBeTruthy();
}
}
@ -225,30 +219,26 @@ describe("Search", () => {
});
it("logical or", () => {
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.label("country", "", true)
.child(becca_mocking.note("Austria").label("languageFamily", "germanic"))
.child(becca_mocking.note("Czech Republic").label("languageFamily", "slavic"))
.child(becca_mocking.note("Hungary").label("languageFamily", "finnougric"))
.child(note("Austria").label("languageFamily", "germanic"))
.child(note("Czech Republic").label("languageFamily", "slavic"))
.child(note("Hungary").label("languageFamily", "finnougric"))
);
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("#languageFamily = slavic OR #languageFamily = germanic", searchContext);
expect(searchResults.length).toEqual(2);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("fuzzy attribute search", () => {
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.label("country", "", true)
.child(becca_mocking.note("Austria").label("languageFamily", "germanic"))
.child(becca_mocking.note("Czech Republic").label("languageFamily", "slavic"))
.child(note("Austria").label("languageFamily", "germanic"))
.child(note("Czech Republic").label("languageFamily", "slavic"))
);
let searchContext = new SearchContext({ fuzzyAttributeSearch: false });
@ -266,147 +256,135 @@ describe("Search", () => {
searchResults = searchService.findResultsWithQuery("#languageFamily=ger", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("filter by note property", () => {
rootNote.child(becca_mocking.note("Europe").child(becca_mocking.note("Austria")).child(becca_mocking.note("Czech Republic")));
rootNote.child(note("Europe").child(note("Austria")).child(note("Czech Republic")));
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("# note.title =* czech", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
it("filter by note's parent", () => {
rootNote
.child(
becca_mocking
.note("Europe")
.child(becca_mocking.note("Austria"))
.child(becca_mocking.note("Czech Republic").child(becca_mocking.note("Prague")))
.child(note("Europe")
.child(note("Austria"))
.child(note("Czech Republic").child(note("Prague")))
)
.child(becca_mocking.note("Asia").child(becca_mocking.note("Taiwan")));
.child(note("Asia").child(note("Taiwan")));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("# note.parents.title = Europe", searchContext);
expect(searchResults.length).toEqual(2);
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# note.parents.title = Asia", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Taiwan")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Taiwan")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# note.parents.parents.title = Europe", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Prague")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
});
it("filter by note's ancestor", () => {
rootNote
.child(
becca_mocking
.note("Europe")
.child(becca_mocking.note("Austria"))
.child(becca_mocking.note("Czech Republic").child(becca_mocking.note("Prague").label("city")))
.child(note("Europe")
.child(note("Austria"))
.child(note("Czech Republic").child(note("Prague").label("city")))
)
.child(becca_mocking.note("Asia").child(becca_mocking.note("Taiwan").child(becca_mocking.note("Taipei").label("city"))));
.child(note("Asia").child(note("Taiwan").child(note("Taipei").label("city"))));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("#city AND note.ancestors.title = Europe", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Prague")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("#city AND note.ancestors.title = Asia", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
});
it("filter by note's child", () => {
rootNote
.child(
becca_mocking
.note("Europe")
.child(becca_mocking.note("Austria").child(becca_mocking.note("Vienna")))
.child(becca_mocking.note("Czech Republic").child(becca_mocking.note("Prague")))
.child(note("Europe")
.child(note("Austria").child(note("Vienna")))
.child(note("Czech Republic").child(note("Prague")))
)
.child(becca_mocking.note("Oceania").child(becca_mocking.note("Australia")));
.child(note("Oceania").child(note("Australia")));
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("# note.children.title =* Aust", searchContext);
expect(searchResults.length).toEqual(2);
expect(becca_mocking.findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(becca_mocking.findNoteByTitle(searchResults, "Oceania")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Oceania")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# note.children.title =* Aust AND note.children.title *= republic", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# note.children.children.title = Prague", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Europe")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
});
it("filter by relation's note properties using short syntax", () => {
const austria = becca_mocking.note("Austria");
const portugal = becca_mocking.note("Portugal");
const austria = note("Austria");
const portugal = note("Portugal");
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.child(austria)
.child(becca_mocking.note("Czech Republic").relation("neighbor", austria.note))
.child(note("Czech Republic").relation("neighbor", austria.note))
.child(portugal)
.child(becca_mocking.note("Spain").relation("neighbor", portugal.note))
.child(note("Spain").relation("neighbor", portugal.note))
);
const searchContext = new SearchContext();
let searchResults = searchService.findResultsWithQuery("# ~neighbor.title = Austria", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# ~neighbor.title = Portugal", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Spain")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Spain")).toBeTruthy();
});
it("filter by relation's note properties using long syntax", () => {
const austria = becca_mocking.note("Austria");
const portugal = becca_mocking.note("Portugal");
const austria = note("Austria");
const portugal = note("Portugal");
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.child(austria)
.child(becca_mocking.note("Czech Republic").relation("neighbor", austria.note))
.child(note("Czech Republic").relation("neighbor", austria.note))
.child(portugal)
.child(becca_mocking.note("Spain").relation("neighbor", portugal.note))
.child(note("Spain").relation("neighbor", portugal.note))
);
const searchContext = new SearchContext();
const searchResults = searchService.findResultsWithQuery("# note.relations.neighbor.title = Austria", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
});
it("filter by multiple level relation", () => {
const austria = becca_mocking.note("Austria");
const slovakia = becca_mocking.note("Slovakia");
const italy = becca_mocking.note("Italy");
const ukraine = becca_mocking.note("Ukraine");
const austria = note("Austria");
const slovakia = note("Slovakia");
const italy = note("Italy");
const ukraine = note("Ukraine");
rootNote.child(
becca_mocking
.note("Europe")
rootNote.child(note("Europe")
.child(austria.relation("neighbor", italy.note).relation("neighbor", slovakia.note))
.child(becca_mocking.note("Czech Republic").relation("neighbor", austria.note).relation("neighbor", slovakia.note))
.child(note("Czech Republic").relation("neighbor", austria.note).relation("neighbor", slovakia.note))
.child(slovakia.relation("neighbor", ukraine.note))
.child(ukraine)
);
@ -415,25 +393,25 @@ describe("Search", () => {
let searchResults = searchService.findResultsWithQuery("# note.relations.neighbor.relations.neighbor.title = Italy", searchContext);
expect(searchResults.length).toEqual(1);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
searchResults = searchService.findResultsWithQuery("# note.relations.neighbor.relations.neighbor.title = Ukraine", searchContext);
expect(searchResults.length).toEqual(2);
expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
});
it("test note properties", () => {
const austria = becca_mocking.note("Austria");
const austria = note("Austria");
austria.relation("myself", austria.note);
austria.label("capital", "Vienna");
austria.label("population", "8859000");
rootNote
.child(becca_mocking.note("Asia"))
.child(becca_mocking.note("Europe").child(austria.child(becca_mocking.note("Vienna")).child(becca_mocking.note("Sebastian Kurz"))))
.child(becca_mocking.note("Mozart").child(austria));
.child(note("Asia"))
.child(note("Europe").child(austria.child(note("Vienna")).child(note("Sebastian Kurz"))))
.child(note("Mozart").child(austria));
austria.note.isProtected = false;
austria.note.dateCreated = "2020-05-14 12:11:42.001+0200";
@ -490,12 +468,12 @@ describe("Search", () => {
});
it("test order by", () => {
const italy = becca_mocking.note("Italy").label("capital", "Rome");
const slovakia = becca_mocking.note("Slovakia").label("capital", "Bratislava");
const austria = becca_mocking.note("Austria").label("capital", "Vienna");
const ukraine = becca_mocking.note("Ukraine").label("capital", "Kiev");
const italy = note("Italy").label("capital", "Rome");
const slovakia = note("Slovakia").label("capital", "Bratislava");
const austria = note("Austria").label("capital", "Vienna");
const ukraine = note("Ukraine").label("capital", "Kiev");
rootNote.child(becca_mocking.note("Europe").child(ukraine).child(slovakia).child(austria).child(italy));
rootNote.child(note("Europe").child(ukraine).child(slovakia).child(austria).child(italy));
const searchContext = new SearchContext();
@ -533,10 +511,10 @@ describe("Search", () => {
});
it("test not(...)", () => {
const italy = becca_mocking.note("Italy").label("capital", "Rome");
const slovakia = becca_mocking.note("Slovakia").label("capital", "Bratislava");
const italy = note("Italy").label("capital", "Rome");
const slovakia = note("Slovakia").label("capital", "Bratislava");
rootNote.child(becca_mocking.note("Europe").child(slovakia).child(italy));
rootNote.child(note("Europe").child(slovakia).child(italy));
const searchContext = new SearchContext();
@ -550,10 +528,10 @@ describe("Search", () => {
});
it.skip("test note.text *=* something", () => {
const italy = becca_mocking.note("Italy").label("capital", "Rome");
const slovakia = becca_mocking.note("Slovakia").label("capital", "Bratislava");
const italy = note("Italy").label("capital", "Rome");
const slovakia = note("Slovakia").label("capital", "Bratislava");
rootNote.child(becca_mocking.note("Europe").child(slovakia).child(italy));
rootNote.child(note("Europe").child(slovakia).child(italy));
const searchContext = new SearchContext();
@ -563,10 +541,10 @@ describe("Search", () => {
});
it.skip("test that fulltext does not match archived notes", () => {
const italy = becca_mocking.note("Italy").label("capital", "Rome");
const slovakia = becca_mocking.note("Slovakia").label("capital", "Bratislava");
const italy = note("Italy").label("capital", "Rome");
const slovakia = note("Slovakia").label("capital", "Bratislava");
rootNote.child(becca_mocking.note("Reddit").label("archived", "", true).child(becca_mocking.note("Post X")).child(becca_mocking.note("Post Y"))).child(becca_mocking.note("Reddit is bad"));
rootNote.child(note("Reddit").label("archived", "", true).child(note("Post X")).child(note("Post Y"))).child(note("Reddit is bad"));
const searchContext = new SearchContext({ includeArchivedNotes: false });
@ -579,14 +557,14 @@ describe("Search", () => {
// it("comparison between labels", () => {
// rootNote
// .child(becca_mocking.note("Europe")
// .child(becca_mocking.note("Austria")
// .child(note("Europe")
// .child(note("Austria")
// .label('capital', 'Vienna')
// .label('largestCity', 'Vienna'))
// .child(becca_mocking.note("Canada")
// .child(note("Canada")
// .label('capital', 'Ottawa')
// .label('largestCity', 'Toronto'))
// .child(becca_mocking.note("Czech Republic")
// .child(note("Czech Republic")
// .label('capital', 'Prague')
// .label('largestCity', 'Prague'))
// );
@ -595,7 +573,7 @@ describe("Search", () => {
//
// const searchResults = searchService.findResultsWithQuery('#capital = #largestCity', searchContext);
// expect(searchResults.length).toEqual(2);
// expect(becca_mocking.findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
// expect(becca_mocking.findNoteByTitle(searchResults, "Austria")).toBeTruthy();
// expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
// expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
// })
});

View File

@ -1,8 +1,8 @@
import { describe, it, expect, beforeEach } from "vitest";
import becca_mocking from "../../../spec/support/becca_mocking.js";
import ValueExtractor from "./value_extractor.js";
import becca from "../../becca/becca.js";
import SearchContext from "./search_context.js";
import { note } from "../../../spec/support/becca_mocking.js";
const dsc = new SearchContext();
@ -12,7 +12,7 @@ describe("Value extractor", () => {
});
it("simple title extraction", async () => {
const europe = becca_mocking.note("Europe").note;
const europe = note("Europe").note;
const valueExtractor = new ValueExtractor(dsc, ["note", "title"]);
@ -21,7 +21,7 @@ describe("Value extractor", () => {
});
it("label extraction", async () => {
const austria = becca_mocking.note("Austria").label("Capital", "Vienna").note;
const austria = note("Austria").label("Capital", "Vienna").note;
let valueExtractor = new ValueExtractor(dsc, ["note", "labels", "capital"]);
@ -35,8 +35,8 @@ describe("Value extractor", () => {
});
it("parent/child property extraction", async () => {
const vienna = becca_mocking.note("Vienna");
const europe = becca_mocking.note("Europe").child(becca_mocking.note("Austria").child(vienna));
const vienna = note("Vienna");
const europe = note("Europe").child(note("Austria").child(vienna));
let valueExtractor = new ValueExtractor(dsc, ["note", "children", "children", "title"]);
@ -50,9 +50,9 @@ describe("Value extractor", () => {
});
it("extract through relation", async () => {
const czechRepublic = becca_mocking.note("Czech Republic").label("capital", "Prague");
const slovakia = becca_mocking.note("Slovakia").label("capital", "Bratislava");
const austria = becca_mocking.note("Austria").relation("neighbor", czechRepublic.note).relation("neighbor", slovakia.note);
const czechRepublic = note("Czech Republic").label("capital", "Prague");
const slovakia = note("Slovakia").label("capital", "Bratislava");
const austria = note("Austria").relation("neighbor", czechRepublic.note).relation("neighbor", slovakia.note);
let valueExtractor = new ValueExtractor(dsc, ["note", "relations", "neighbor", "labels", "capital"]);

72
src/services/tree.spec.ts Normal file
View File

@ -0,0 +1,72 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { note, NoteBuilder } from "../../spec/support/becca_mocking.js";
import becca from "../becca/becca.js";
import BBranch from "../becca/entities/bbranch.js";
import BNote from "../becca/entities/bnote.js";
import tree from "./tree.js";
import cls from "./cls.js";
describe("Tree", () => {
let rootNote!: NoteBuilder;
beforeEach(() => {
becca.reset();
rootNote = new NoteBuilder(new BNote({
noteId: "root",
title: "root",
type: "text"
}))
new BBranch({
branchId: "none_root",
noteId: "root",
parentNoteId: "none",
notePosition: 10
});
vi.mock("./sql.js", () => {
return {
default: {
transactional: (cb: Function) => {
cb();
},
execute: () => { },
replace: () => { },
getMap: () => { }
}
}
});
vi.mock("./sql_init.js", () => {
return {
dbReady: () => { console.log("Hello world") }
}
});
});
it("custom sort order is idempotent", () => {
rootNote.label("sorted", "order");
// Add values which have a defined order.
for (let i=0; i<=5; i++) {
rootNote.child(note(String(i)).label("order", String(i)));
}
// Add a few values which have no defined order.
for (let i=6; i<10; i++) {
rootNote.child(note(String(i)));
}
const expectedOrder = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
// Sort a few times to ensure that the resulting order is the same.
for (let i=0; i<5; i++) {
cls.init(() => {
tree.sortNotesIfNeeded(rootNote.note.noteId);
});
const order = rootNote.note.children.map((child) => child.title);
expect(order).toStrictEqual(expectedOrder);
}
});
});

View File

@ -145,8 +145,8 @@ function sortNotes(parentNoteId: string, customSortBy: string = "title", reverse
return compare(bottomBEl, bottomAEl) * (reverse ? -1 : 1);
}
const customAEl = fetchValue(a, customSortBy);
const customBEl = fetchValue(b, customSortBy);
const customAEl = fetchValue(a, customSortBy) ?? fetchValue(a, "title");
const customBEl = fetchValue(b, customSortBy) ?? fetchValue(b, "title");
if (customAEl !== customBEl) {
return compare(customAEl, customBEl);