mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 08:46:20 +02:00
Compare commits
32 Commits
feat/add-c
...
feat/llm-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb2ace41b0 | ||
|
|
778f13e2e6 | ||
|
|
bb3d0f0319 | ||
|
|
cec627a744 | ||
|
|
2958ae4587 | ||
|
|
8da904cf55 | ||
|
|
b37d9b4b3d | ||
|
|
ac415c1007 | ||
|
|
d38ca72e08 | ||
|
|
16622f43e3 | ||
|
|
f89c202fcc | ||
|
|
97ec882528 | ||
|
|
a1e596b81b | ||
|
|
3db145b6e6 | ||
|
|
0d898385f6 | ||
|
|
89fcfabd3c | ||
|
|
4c01d7d8f1 | ||
|
|
42ee351487 | ||
|
|
e0383c49cb | ||
|
|
6fbc5b2b14 | ||
|
|
5562559b0b | ||
|
|
c119ffe478 | ||
|
|
27847ab720 | ||
|
|
755b1ed42f | ||
|
|
4e36dc8e5e | ||
|
|
8bc70a4190 | ||
|
|
d798d29e92 | ||
|
|
6e0fee6cb3 | ||
|
|
e0e1f0796b | ||
|
|
e98954c555 | ||
|
|
87fd6afec6 | ||
|
|
dccd6477d2 |
22
.github/actions/build-electron/action.yml
vendored
22
.github/actions/build-electron/action.yml
vendored
@@ -162,25 +162,3 @@ runs:
|
||||
echo "Found ZIP: $zip_file"
|
||||
echo "Note: ZIP files are not code signed, but their contents should be"
|
||||
fi
|
||||
|
||||
- name: Sign the RPM
|
||||
if: inputs.os == 'linux'
|
||||
shell: ${{ inputs.shell }}
|
||||
run: |
|
||||
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import
|
||||
|
||||
# Import the key into RPM for verification
|
||||
gpg --export -a > pubkey
|
||||
rpm --import pubkey
|
||||
rm pubkey
|
||||
|
||||
# Sign the RPM
|
||||
rpm_file=$(find ./apps/desktop/upload -name "*.rpm" -print -quit)
|
||||
rpmsign --define "_gpg_name Trilium Notes Signing Key <triliumnotes@outlook.com>" --addsign "$rpm_file"
|
||||
rpm -Kv "$rpm_file"
|
||||
|
||||
# Validate code signing
|
||||
if ! rpm -K "$rpm_file" | grep -q "digests signatures OK"; then
|
||||
echo .rpm file not signed
|
||||
exit 1
|
||||
fi
|
||||
|
||||
2
.github/actions/report-size/action.yml
vendored
2
.github/actions/report-size/action.yml
vendored
@@ -44,7 +44,7 @@ runs:
|
||||
steps:
|
||||
# Checkout branch to compare to [required]
|
||||
- name: Checkout base branch
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
path: br-base
|
||||
|
||||
1
.github/workflows/checks.yml
vendored
1
.github/workflows/checks.yml
vendored
@@ -12,7 +12,6 @@ jobs:
|
||||
steps:
|
||||
- name: Check if PRs have conflicts
|
||||
uses: eps1lon/actions-label-merge-conflict@v3
|
||||
if: github.repository == ${{ vars.REPO_MAIN }}
|
||||
with:
|
||||
dirtyLabel: "merge-conflicts"
|
||||
repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}"
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
|
||||
8
.github/workflows/dev.yml
vendored
8
.github/workflows/dev.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # needed for https://github.com/marketplace/actions/nx-set-shas
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- check-affected
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- test_dev
|
||||
- check-affected
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
- dockerfile: Dockerfile
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Install dependencies
|
||||
|
||||
4
.github/workflows/main-docker.yml
vendored
4
.github/workflows/main-docker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- dockerfile: Dockerfile
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set IMAGE_NAME to lowercase
|
||||
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
|
||||
@@ -141,7 +141,7 @@ jobs:
|
||||
run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
7
.github/workflows/nightly.yml
vendored
7
.github/workflows/nightly.yml
vendored
@@ -27,7 +27,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
nightly-electron:
|
||||
if: github.repository == ${{ vars.REPO_MAIN }}
|
||||
name: Deploy nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -48,7 +47,7 @@ jobs:
|
||||
forge_platform: win32
|
||||
runs-on: ${{ matrix.os.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
@@ -76,7 +75,6 @@ jobs:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
|
||||
|
||||
- name: Publish release
|
||||
uses: softprops/action-gh-release@v2.3.2
|
||||
@@ -98,7 +96,6 @@ jobs:
|
||||
path: apps/desktop/upload
|
||||
|
||||
nightly-server:
|
||||
if: github.repository == ${{ vars.REPO_MAIN }}
|
||||
name: Deploy server nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -111,7 +108,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run the build
|
||||
uses: ./.github/actions/build-server
|
||||
|
||||
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
filter: tree:0
|
||||
fetch-depth: 0
|
||||
|
||||
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
forge_platform: win32
|
||||
runs-on: ${{ matrix.os.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Set up node & dependencies
|
||||
uses: actions/setup-node@v4
|
||||
@@ -58,7 +58,6 @@ jobs:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
|
||||
|
||||
- name: Upload the artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -79,7 +78,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run the build
|
||||
uses: ./.github/actions/build-server
|
||||
@@ -102,7 +101,7 @@ jobs:
|
||||
steps:
|
||||
- run: mkdir upload
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
docs/Release Notes
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,7 +10,6 @@ node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
|
||||
6
.idea/.gitignore
generated
vendored
Normal file
6
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Default ignored files
|
||||
/workspace.xml
|
||||
|
||||
# Datasource local storage ignored files
|
||||
/dataSources.local.xml
|
||||
/dataSources/
|
||||
15
.idea/codeStyles/Project.xml
generated
Normal file
15
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="OTHER_INDENT_OPTIONS">
|
||||
<value>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</value>
|
||||
</option>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="document.db" uuid="2a4ac1e6-b828-4a2a-8e4a-3f59f10aff26">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/data/document.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
4
.idea/encodings.xml
generated
Normal file
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||
</project>
|
||||
15
.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxProjectSettings">
|
||||
<option name="commitMessageIssueKeyValidationOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
<option name="commitMessageValidationEnabledOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
11
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<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" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/jsLinters/jslint.xml
generated
Normal file
9
.idea/jsLinters/jslint.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSLintConfiguration">
|
||||
<option devel="true" />
|
||||
<option es6="true" />
|
||||
<option maxerr="50" />
|
||||
<option node="true" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/misc.xml
generated
Normal file
8
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="openjdk-16" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/sqldialects.xml
generated
Normal file
7
.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$" dialect="SQLite" />
|
||||
<file url="PROJECT" dialect="SQLite" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
4
.vscode/i18n-ally-custom-framework.yml
vendored
4
.vscode/i18n-ally-custom-framework.yml
vendored
@@ -3,7 +3,6 @@
|
||||
languageIds:
|
||||
- javascript
|
||||
- typescript
|
||||
- typescriptreact
|
||||
- html
|
||||
|
||||
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
|
||||
@@ -26,10 +25,9 @@ scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
|
||||
# The "$1" will be replaced by the keypath specified.
|
||||
refactorTemplates:
|
||||
- t("$1")
|
||||
- {t("$1")}
|
||||
- ${t("$1")}
|
||||
- <%= t("$1") %>
|
||||
|
||||
|
||||
# If set to true, only enables this custom framework (will disable all built-in frameworks)
|
||||
monopoly: true
|
||||
monopoly: true
|
||||
38
README.md
38
README.md
@@ -1,11 +1,11 @@
|
||||
# Trilium Notes
|
||||
|
||||
 
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp) [](https://hosted.weblate.org/engage/trilium/)
|
||||
|
||||
[English](./README.md) | [Chinese (Simplified)](./docs/README-ZH_CN.md) | [Chinese (Traditional)](./docs/README-ZH_TW.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
|
||||
[English](./README.md) | [Chinese](./docs/README-ZH_CN.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
|
||||
|
||||
Trilium Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
|
||||
|
||||
@@ -46,15 +46,15 @@ See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for q
|
||||
- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
|
||||
- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
|
||||
|
||||
## ❓Why TriliumNext?
|
||||
## ⚠️ Why TriliumNext?
|
||||
|
||||
The original Trilium developer ([Zadam](https://github.com/zadam)) has graciously given the Trilium repository to the community project which resides at https://github.com/TriliumNext
|
||||
[The original Trilium project is in maintenance mode](https://github.com/zadam/trilium/issues/4620).
|
||||
|
||||
### ⬆️Migrating from Zadam/Trilium?
|
||||
### Migrating from Trilium?
|
||||
|
||||
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Trilium instance. Simply [install TriliumNext/Trilium](#-installation) as usual and it will use your existing database.
|
||||
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Notes instance. Simply [install TriliumNext/Notes](#-installation) as usual and it will use your existing database.
|
||||
|
||||
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext/Trilium have their sync versions incremented which prevents direct migration.
|
||||
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Notes/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext have their sync versions incremented.
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
@@ -75,14 +75,14 @@ Feel free to join our official conversations. We would love to hear what feature
|
||||
|
||||
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions.)
|
||||
- The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
|
||||
- [Github Discussions](https://github.com/TriliumNext/Trilium/discussions) (For asynchronous discussions.)
|
||||
- [Github Issues](https://github.com/TriliumNext/Trilium/issues) (For bug reports and feature requests.)
|
||||
- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For asynchronous discussions.)
|
||||
- [Github Issues](https://github.com/TriliumNext/Notes/issues) (For bug reports and feature requests.)
|
||||
|
||||
## 🏗 Installation
|
||||
|
||||
### Windows / MacOS
|
||||
|
||||
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
|
||||
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
|
||||
|
||||
### Linux
|
||||
|
||||
@@ -90,7 +90,7 @@ If your distribution is listed in the table below, use your distribution's packa
|
||||
|
||||
[](https://repology.org/project/triliumnext/versions)
|
||||
|
||||
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
|
||||
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
|
||||
|
||||
TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
|
||||
|
||||
@@ -104,15 +104,13 @@ Currently only the latest versions of Chrome & Firefox are supported (and tested
|
||||
|
||||
To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below).
|
||||
|
||||
See issue https://github.com/TriliumNext/Trilium/issues/4962 for more information on mobile app support.
|
||||
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid). Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
|
||||
|
||||
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid).
|
||||
Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
|
||||
Note: It is best to disable automatic updates on your server installation (see below) when using TriliumDroid since the sync version must match between Trilium and TriliumDroid.
|
||||
See issue https://github.com/TriliumNext/Notes/issues/72 for more information on mobile app support.
|
||||
|
||||
### Server
|
||||
|
||||
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/trilium)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
|
||||
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/notes)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
|
||||
|
||||
|
||||
## 💻 Contribute
|
||||
@@ -154,11 +152,11 @@ pnpm install
|
||||
pnpm nx --project=desktop electron-forge:make -- --arch=x64 --platform=win32
|
||||
```
|
||||
|
||||
For more details, see the [development docs](https://github.com/TriliumNext/Trilium/tree/main/docs/Developer%20Guide/Developer%20Guide).
|
||||
For more details, see the [development docs](https://github.com/TriliumNext/Notes/blob/develop/docs/Developer%20Guide/Developer%20Guide/Building%20and%20deployment/Running%20a%20development%20build.md).
|
||||
|
||||
### Developer Documentation
|
||||
|
||||
Please view the [documentation guide](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
|
||||
Please view the [documentation guide](./docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
|
||||
|
||||
## 👏 Shoutouts
|
||||
|
||||
@@ -170,7 +168,7 @@ Please view the [documentation guide](https://github.com/TriliumNext/Trilium/blo
|
||||
## 🤝 Support
|
||||
|
||||
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/trilium/graphs/contributors))) for a full list)
|
||||
- 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).
|
||||
|
||||
|
||||
|
||||
@@ -35,13 +35,13 @@
|
||||
"chore:generate-openapi": "tsx bin/generate-openapi.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.55.0",
|
||||
"@stylistic/eslint-plugin": "5.2.3",
|
||||
"@playwright/test": "1.54.2",
|
||||
"@stylistic/eslint-plugin": "5.2.2",
|
||||
"@types/express": "5.0.3",
|
||||
"@types/node": "22.17.2",
|
||||
"@types/node": "22.17.0",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"eslint": "9.34.0",
|
||||
"eslint": "9.32.0",
|
||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||
"esm": "3.2.25",
|
||||
"jsdoc": "4.0.4",
|
||||
@@ -49,8 +49,8 @@
|
||||
"rcedit": "4.0.1",
|
||||
"rimraf": "6.0.1",
|
||||
"tslib": "2.8.1",
|
||||
"typedoc": "0.28.10",
|
||||
"typedoc-plugin-missing-exports": "4.1.0"
|
||||
"typedoc": "0.28.9",
|
||||
"typedoc-plugin-missing-exports": "4.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"appdmg": "0.6.6"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@triliumnext/client",
|
||||
"version": "0.98.0",
|
||||
"version": "0.97.2",
|
||||
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-only",
|
||||
@@ -10,16 +10,16 @@
|
||||
"url": "https://github.com/TriliumNext/Notes"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "9.34.0",
|
||||
"@eslint/js": "9.32.0",
|
||||
"@excalidraw/excalidraw": "0.18.0",
|
||||
"@fullcalendar/core": "6.1.19",
|
||||
"@fullcalendar/daygrid": "6.1.19",
|
||||
"@fullcalendar/interaction": "6.1.19",
|
||||
"@fullcalendar/list": "6.1.19",
|
||||
"@fullcalendar/multimonth": "6.1.19",
|
||||
"@fullcalendar/timegrid": "6.1.19",
|
||||
"@fullcalendar/core": "6.1.18",
|
||||
"@fullcalendar/daygrid": "6.1.18",
|
||||
"@fullcalendar/interaction": "6.1.18",
|
||||
"@fullcalendar/list": "6.1.18",
|
||||
"@fullcalendar/multimonth": "6.1.18",
|
||||
"@fullcalendar/timegrid": "6.1.18",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||
"@mermaid-js/layout-elk": "0.1.9",
|
||||
"@mermaid-js/layout-elk": "0.1.8",
|
||||
"@mind-elixir/node-menu": "5.0.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@triliumnext/ckeditor5": "workspace:*",
|
||||
@@ -36,7 +36,7 @@
|
||||
"draggabilly": "3.0.0",
|
||||
"force-graph": "1.50.1",
|
||||
"globals": "16.3.0",
|
||||
"i18next": "25.4.1",
|
||||
"i18next": "25.3.2",
|
||||
"i18next-http-backend": "3.0.2",
|
||||
"jquery": "3.7.1",
|
||||
"jquery.fancytree": "2.38.5",
|
||||
@@ -46,13 +46,12 @@
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.2.0",
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "16.2.0",
|
||||
"mermaid": "11.10.1",
|
||||
"mind-elixir": "5.0.6",
|
||||
"marked": "16.1.2",
|
||||
"mermaid": "11.9.0",
|
||||
"mind-elixir": "5.0.4",
|
||||
"normalize.css": "8.0.1",
|
||||
"panzoom": "9.4.3",
|
||||
"preact": "10.27.1",
|
||||
"react-i18next": "15.7.2",
|
||||
"preact": "10.27.0",
|
||||
"split.js": "1.6.5",
|
||||
"svg-pan-zoom": "3.6.2",
|
||||
"tabulator-tables": "6.3.1",
|
||||
@@ -66,11 +65,11 @@
|
||||
"@types/leaflet": "1.9.20",
|
||||
"@types/leaflet-gpx": "1.3.7",
|
||||
"@types/mark.js": "8.11.12",
|
||||
"@types/tabulator-tables": "6.2.10",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"@types/tabulator-tables": "6.2.9",
|
||||
"copy-webpack-plugin": "13.0.0",
|
||||
"happy-dom": "18.0.1",
|
||||
"script-loader": "0.7.2",
|
||||
"vite-plugin-static-copy": "3.1.2"
|
||||
"vite-plugin-static-copy": "3.1.1"
|
||||
},
|
||||
"nx": {
|
||||
"name": "client",
|
||||
|
||||
@@ -30,7 +30,6 @@ import type CodeMirror from "@triliumnext/codemirror";
|
||||
import { StartupChecks } from "./startup_checks.js";
|
||||
import type { CreateNoteOpts } from "../services/note_create.js";
|
||||
import { ColumnComponent } from "tabulator-tables";
|
||||
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
|
||||
|
||||
interface Layout {
|
||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||
@@ -93,9 +92,7 @@ export type CommandMappings = {
|
||||
closeTocCommand: CommandData;
|
||||
closeHlt: CommandData;
|
||||
showLaunchBarSubtree: CommandData;
|
||||
showRevisions: CommandData & {
|
||||
noteId?: string | null;
|
||||
};
|
||||
showRevisions: CommandData;
|
||||
showLlmChat: CommandData;
|
||||
createAiChat: CommandData;
|
||||
showOptions: CommandData & {
|
||||
@@ -371,9 +368,6 @@ export type CommandMappings = {
|
||||
};
|
||||
refreshTouchBar: CommandData;
|
||||
reloadTextEditor: CommandData;
|
||||
chooseNoteType: CommandData & {
|
||||
callback: ChooseNoteTypeCallback
|
||||
}
|
||||
};
|
||||
|
||||
type EventMappings = {
|
||||
|
||||
@@ -30,7 +30,6 @@ import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolb
|
||||
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
|
||||
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||
import NoteListWidget from "../widgets/note_list.js";
|
||||
import { CallToActionDialog } from "../widgets/dialogs/call_to_action.jsx";
|
||||
|
||||
export function applyModals(rootContainer: RootContainer) {
|
||||
rootContainer
|
||||
@@ -67,5 +66,4 @@ export function applyModals(rootContainer: RootContainer) {
|
||||
.child(new PromotedAttributesWidget())
|
||||
.child(new NoteDetailWidget())
|
||||
.child(new NoteListWidget(true)))
|
||||
.child(new CallToActionDialog());
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import TabRowWidget from "../widgets/tab_row.js";
|
||||
import RefreshButton from "../widgets/floating_buttons/refresh_button.js";
|
||||
import MobileEditorToolbar from "../widgets/ribbon_widgets/mobile_editor_toolbar.js";
|
||||
import { applyModals } from "./layout_commons.js";
|
||||
import CloseZenButton from "../widgets/close_zen_button.js";
|
||||
|
||||
const MOBILE_CSS = `
|
||||
<style>
|
||||
@@ -175,8 +174,7 @@ export default class MobileLayout {
|
||||
.id("mobile-bottom-bar")
|
||||
.child(new TabRowWidget().css("height", "40px"))
|
||||
.child(new FlexContainer("row").class("horizontal").css("height", "53px").child(new LauncherContainer(true)).child(new GlobalMenuWidget(true)).id("launcher-pane"))
|
||||
)
|
||||
.child(new CloseZenButton());
|
||||
);
|
||||
applyModals(rootContainer);
|
||||
return rootContainer;
|
||||
}
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
/**
|
||||
* @module CKEditor Plugin Configuration Service
|
||||
*
|
||||
* This service manages the dynamic configuration of CKEditor plugins based on user preferences.
|
||||
* It handles plugin enablement, dependency resolution, and toolbar configuration.
|
||||
*/
|
||||
|
||||
import server from "./server.js";
|
||||
import type {
|
||||
PluginConfiguration,
|
||||
PluginMetadata,
|
||||
PluginRegistry,
|
||||
PluginValidationResult
|
||||
} from "@triliumnext/commons";
|
||||
|
||||
/**
|
||||
* Cache for plugin registry and user configuration
|
||||
*/
|
||||
let pluginRegistryCache: PluginRegistry | null = null;
|
||||
let userConfigCache: PluginConfiguration[] | null = null;
|
||||
let cacheTimestamp = 0;
|
||||
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
/**
|
||||
* Get the plugin registry from server
|
||||
*/
|
||||
export async function getPluginRegistry(): Promise<PluginRegistry> {
|
||||
const now = Date.now();
|
||||
if (pluginRegistryCache && (now - cacheTimestamp) < CACHE_DURATION) {
|
||||
return pluginRegistryCache;
|
||||
}
|
||||
|
||||
try {
|
||||
pluginRegistryCache = await server.get<PluginRegistry>('ckeditor-plugins/registry');
|
||||
cacheTimestamp = now;
|
||||
return pluginRegistryCache;
|
||||
} catch (error) {
|
||||
console.error('Failed to load CKEditor plugin registry:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's plugin configuration from server
|
||||
*/
|
||||
export async function getUserPluginConfig(): Promise<PluginConfiguration[]> {
|
||||
const now = Date.now();
|
||||
if (userConfigCache && (now - cacheTimestamp) < CACHE_DURATION) {
|
||||
return userConfigCache;
|
||||
}
|
||||
|
||||
try {
|
||||
userConfigCache = await server.get<PluginConfiguration[]>('ckeditor-plugins/config');
|
||||
cacheTimestamp = now;
|
||||
return userConfigCache;
|
||||
} catch (error) {
|
||||
console.error('Failed to load user plugin configuration:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache (call when configuration is updated)
|
||||
*/
|
||||
export function clearCache(): void {
|
||||
pluginRegistryCache = null;
|
||||
userConfigCache = null;
|
||||
cacheTimestamp = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the enabled plugins for the current user
|
||||
*/
|
||||
export async function getEnabledPlugins(): Promise<Set<string>> {
|
||||
const userConfig = await getUserPluginConfig();
|
||||
const enabledPlugins = new Set<string>();
|
||||
|
||||
// Add all enabled user plugins
|
||||
userConfig.forEach(config => {
|
||||
if (config.enabled) {
|
||||
enabledPlugins.add(config.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Always include core plugins
|
||||
const registry = await getPluginRegistry();
|
||||
Object.values(registry.plugins).forEach(plugin => {
|
||||
if (plugin.isCore) {
|
||||
enabledPlugins.add(plugin.id);
|
||||
}
|
||||
});
|
||||
|
||||
return enabledPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get disabled plugin names for CKEditor config
|
||||
*/
|
||||
export async function getDisabledPlugins(): Promise<string[]> {
|
||||
try {
|
||||
const registry = await getPluginRegistry();
|
||||
const enabledPlugins = await getEnabledPlugins();
|
||||
const disabledPlugins: string[] = [];
|
||||
|
||||
// Find plugins that are disabled
|
||||
Object.values(registry.plugins).forEach(plugin => {
|
||||
if (!plugin.isCore && !enabledPlugins.has(plugin.id)) {
|
||||
// Map plugin ID to actual CKEditor plugin names if needed
|
||||
const pluginNames = getPluginNames(plugin.id);
|
||||
disabledPlugins.push(...pluginNames);
|
||||
}
|
||||
});
|
||||
|
||||
return disabledPlugins;
|
||||
} catch (error) {
|
||||
console.warn("Failed to get disabled plugins, returning empty list:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map plugin ID to actual CKEditor plugin names
|
||||
* Some plugins might have multiple names or different names than their ID
|
||||
*/
|
||||
function getPluginNames(pluginId: string): string[] {
|
||||
const nameMap: Record<string, string[]> = {
|
||||
"emoji": ["EmojiMention", "EmojiPicker"],
|
||||
"math": ["Math", "AutoformatMath"],
|
||||
"image": ["Image", "ImageCaption", "ImageInline", "ImageResize", "ImageStyle", "ImageToolbar", "ImageUpload"],
|
||||
"table": ["Table", "TableToolbar", "TableProperties", "TableCellProperties", "TableSelection", "TableCaption", "TableColumnResize"],
|
||||
"font": ["Font", "FontColor", "FontBackgroundColor"],
|
||||
"list": ["List", "ListProperties"],
|
||||
"specialcharacters": ["SpecialCharacters", "SpecialCharactersEssentials"],
|
||||
"findandreplace": ["FindAndReplace"],
|
||||
"horizontalline": ["HorizontalLine"],
|
||||
"pagebreak": ["PageBreak"],
|
||||
"removeformat": ["RemoveFormat"],
|
||||
"alignment": ["Alignment"],
|
||||
"indent": ["Indent", "IndentBlock"],
|
||||
"codeblock": ["CodeBlock"],
|
||||
"blockquote": ["BlockQuote"],
|
||||
"todolist": ["TodoList"],
|
||||
"heading": ["Heading", "HeadingButtonsUI"],
|
||||
"paragraph": ["ParagraphButtonUI"],
|
||||
// Add more mappings as needed
|
||||
};
|
||||
|
||||
return nameMap[pluginId] || [pluginId.charAt(0).toUpperCase() + pluginId.slice(1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the current plugin configuration
|
||||
*/
|
||||
export async function validatePluginConfiguration(): Promise<PluginValidationResult> {
|
||||
try {
|
||||
const userConfig = await getUserPluginConfig();
|
||||
return await server.post<PluginValidationResult>('ckeditor-plugins/validate', {
|
||||
plugins: userConfig
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to validate plugin configuration:', error);
|
||||
return {
|
||||
valid: false,
|
||||
errors: [{
|
||||
type: "missing_dependency",
|
||||
pluginId: "unknown",
|
||||
message: `Validation failed: ${error}`
|
||||
}],
|
||||
warnings: [],
|
||||
resolvedPlugins: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get toolbar items that should be hidden based on disabled plugins
|
||||
*/
|
||||
export async function getHiddenToolbarItems(): Promise<string[]> {
|
||||
const registry = await getPluginRegistry();
|
||||
const enabledPlugins = await getEnabledPlugins();
|
||||
const hiddenItems: string[] = [];
|
||||
|
||||
Object.values(registry.plugins).forEach(plugin => {
|
||||
if (!enabledPlugins.has(plugin.id) && plugin.toolbarItems) {
|
||||
hiddenItems.push(...plugin.toolbarItems);
|
||||
}
|
||||
});
|
||||
|
||||
return hiddenItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user plugin configuration
|
||||
*/
|
||||
export async function updatePluginConfiguration(plugins: PluginConfiguration[]): Promise<void> {
|
||||
try {
|
||||
const response = await server.put('ckeditor-plugins/config', {
|
||||
plugins,
|
||||
validate: true
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
throw new Error(response.errors?.join(", ") || "Update failed");
|
||||
}
|
||||
|
||||
// Clear cache so next requests fetch fresh data
|
||||
clearCache();
|
||||
} catch (error) {
|
||||
console.error('Failed to update plugin configuration:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getPluginRegistry,
|
||||
getUserPluginConfig,
|
||||
getEnabledPlugins,
|
||||
getDisabledPlugins,
|
||||
getHiddenToolbarItems,
|
||||
validatePluginConfiguration,
|
||||
updatePluginConfiguration,
|
||||
clearCache
|
||||
};
|
||||
@@ -35,10 +35,8 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
|
||||
loadResults.addOption(attributeEntity.name);
|
||||
} else if (ec.entityName === "attachments") {
|
||||
processAttachment(loadResults, ec);
|
||||
} else if (ec.entityName === "blobs") {
|
||||
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens") {
|
||||
// NOOP - these entities are handled at the backend level and don't require frontend processing
|
||||
} else if (ec.entityName === "etapi_tokens") {
|
||||
loadResults.hasEtapiTokenChanges = true;
|
||||
} else {
|
||||
throw new Error(`Unknown entityName '${ec.entityName}'`);
|
||||
}
|
||||
@@ -79,7 +77,9 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
|
||||
noteAttributeCache.invalidate();
|
||||
}
|
||||
|
||||
const appContext = (await import("../components/app_context.js")).default;
|
||||
// TODO: Remove after porting the file
|
||||
// @ts-ignore
|
||||
const appContext = (await import("../components/app_context.js")).default as any;
|
||||
await appContext.triggerEvent("entitiesReloaded", { loadResults });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import i18next from "i18next";
|
||||
import i18nextHttpBackend from "i18next-http-backend";
|
||||
import server from "./server.js";
|
||||
import type { Locale } from "@triliumnext/commons";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
let locales: Locale[] | null;
|
||||
|
||||
@@ -17,7 +16,6 @@ export async function initLocale() {
|
||||
|
||||
locales = await server.get<Locale[]>("options/locales");
|
||||
|
||||
i18next.use(initReactI18next);
|
||||
await i18next.use(i18nextHttpBackend).init({
|
||||
lng: locale,
|
||||
fallbackLng: "en",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AttachmentRow, EtapiTokenRow } from "@triliumnext/commons";
|
||||
import type { AttachmentRow } from "@triliumnext/commons";
|
||||
import type { AttributeType } from "../entities/fattribute.js";
|
||||
import type { EntityChange } from "../server_types.js";
|
||||
|
||||
@@ -53,7 +53,6 @@ type EntityRowMappings = {
|
||||
options: OptionRow;
|
||||
revisions: RevisionRow;
|
||||
note_reordering: NoteReorderingRow;
|
||||
etapi_tokens: EtapiTokenRow;
|
||||
};
|
||||
|
||||
export type EntityRowNames = keyof EntityRowMappings;
|
||||
@@ -69,7 +68,6 @@ export default class LoadResults {
|
||||
private contentNoteIdToComponentId: ContentNoteIdToComponentIdRow[];
|
||||
private optionNames: string[];
|
||||
private attachmentRows: AttachmentRow[];
|
||||
public hasEtapiTokenChanges: boolean = false;
|
||||
|
||||
constructor(entityChanges: EntityChange[]) {
|
||||
const entities: Record<string, Record<string, any>> = {};
|
||||
@@ -217,8 +215,7 @@ export default class LoadResults {
|
||||
this.revisionRows.length === 0 &&
|
||||
this.contentNoteIdToComponentId.length === 0 &&
|
||||
this.optionNames.length === 0 &&
|
||||
this.attachmentRows.length === 0 &&
|
||||
!this.hasEtapiTokenChanges
|
||||
this.attachmentRows.length === 0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ export interface Suggestion {
|
||||
commandShortcut?: string;
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
container?: HTMLElement | null;
|
||||
interface Options {
|
||||
container?: HTMLElement;
|
||||
fastSearch?: boolean;
|
||||
allowCreatingNotes?: boolean;
|
||||
allowJumpToSearchNotes?: boolean;
|
||||
@@ -82,12 +82,12 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
|
||||
// Check if we're in command mode
|
||||
if (options.isCommandPalette && term.startsWith(">")) {
|
||||
const commandQuery = term.substring(1).trim();
|
||||
|
||||
|
||||
// Get commands (all if no query, filtered if query provided)
|
||||
const commands = commandQuery.length === 0
|
||||
const commands = commandQuery.length === 0
|
||||
? commandRegistry.getAllCommands()
|
||||
: commandRegistry.searchCommands(commandQuery);
|
||||
|
||||
|
||||
// Convert commands to suggestions
|
||||
const commandSuggestions: Suggestion[] = commands.map(cmd => ({
|
||||
action: "command",
|
||||
@@ -99,7 +99,7 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
|
||||
commandShortcut: cmd.shortcut,
|
||||
icon: cmd.icon
|
||||
}));
|
||||
|
||||
|
||||
cb(commandSuggestions);
|
||||
return;
|
||||
}
|
||||
@@ -452,21 +452,6 @@ function init() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function which triggers the display of recent notes in the autocomplete input and focuses it.
|
||||
*
|
||||
* @param inputElement - The input element to trigger recent notes on.
|
||||
*/
|
||||
export function triggerRecentNotes(inputElement: HTMLInputElement | null | undefined) {
|
||||
if (!inputElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $el = $(inputElement);
|
||||
showRecentNotes($el);
|
||||
$el.trigger("focus").trigger("select");
|
||||
}
|
||||
|
||||
export default {
|
||||
autocompleteSourceForCKEditor,
|
||||
initNoteAutocomplete,
|
||||
|
||||
@@ -109,6 +109,8 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
||||
|
||||
async function chooseNoteType() {
|
||||
return new Promise<ChooseNoteTypeResponse>((res) => {
|
||||
// TODO: Remove ignore after callback for chooseNoteType is defined in app_context.ts
|
||||
//@ts-ignore
|
||||
appContext.triggerCommand("chooseNoteType", { callback: res });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { OptionNames } from "@triliumnext/commons";
|
||||
import server from "./server.js";
|
||||
import { isShare } from "./utils.js";
|
||||
|
||||
export type OptionValue = number | string;
|
||||
type OptionValue = number | string;
|
||||
|
||||
class Options {
|
||||
initializedPromise: Promise<void>;
|
||||
@@ -77,14 +76,6 @@ class Options {
|
||||
await server.put(`options`, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves multiple options at once, by supplying a record where the keys are the option names and the values represent the stringified value to set.
|
||||
* @param newValues the record of keys and values.
|
||||
*/
|
||||
async saveMany<T extends OptionNames>(newValues: Record<T, OptionValue>) {
|
||||
await server.put<void>("options", newValues);
|
||||
}
|
||||
|
||||
async toggle(key: string) {
|
||||
await this.save(key, (!this.is(key)).toString());
|
||||
}
|
||||
|
||||
@@ -14,32 +14,6 @@ interface ShortcutBinding {
|
||||
// Store all active shortcut bindings for management
|
||||
const activeBindings: Map<string, ShortcutBinding[]> = new Map();
|
||||
|
||||
// Handle special key mappings and aliases
|
||||
const keyMap: { [key: string]: string[] } = {
|
||||
'return': ['Enter'],
|
||||
'enter': ['Enter'], // alias for return
|
||||
'del': ['Delete'],
|
||||
'delete': ['Delete'], // alias for del
|
||||
'esc': ['Escape'],
|
||||
'escape': ['Escape'], // alias for esc
|
||||
'space': [' ', 'Space'],
|
||||
'tab': ['Tab'],
|
||||
'backspace': ['Backspace'],
|
||||
'home': ['Home'],
|
||||
'end': ['End'],
|
||||
'pageup': ['PageUp'],
|
||||
'pagedown': ['PageDown'],
|
||||
'up': ['ArrowUp'],
|
||||
'down': ['ArrowDown'],
|
||||
'left': ['ArrowLeft'],
|
||||
'right': ['ArrowRight']
|
||||
};
|
||||
|
||||
// Function keys
|
||||
for (let i = 1; i <= 19; i++) {
|
||||
keyMap[`f${i}`] = [`F${i}`];
|
||||
}
|
||||
|
||||
function removeGlobalShortcut(namespace: string) {
|
||||
bindGlobalShortcut("", null, namespace);
|
||||
}
|
||||
@@ -150,6 +124,32 @@ export function keyMatches(e: KeyboardEvent, key: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle special key mappings and aliases
|
||||
const keyMap: { [key: string]: string[] } = {
|
||||
'return': ['Enter'],
|
||||
'enter': ['Enter'], // alias for return
|
||||
'del': ['Delete'],
|
||||
'delete': ['Delete'], // alias for del
|
||||
'esc': ['Escape'],
|
||||
'escape': ['Escape'], // alias for esc
|
||||
'space': [' ', 'Space'],
|
||||
'tab': ['Tab'],
|
||||
'backspace': ['Backspace'],
|
||||
'home': ['Home'],
|
||||
'end': ['End'],
|
||||
'pageup': ['PageUp'],
|
||||
'pagedown': ['PageDown'],
|
||||
'up': ['ArrowUp'],
|
||||
'down': ['ArrowDown'],
|
||||
'left': ['ArrowLeft'],
|
||||
'right': ['ArrowRight']
|
||||
};
|
||||
|
||||
// Function keys
|
||||
for (let i = 1; i <= 19; i++) {
|
||||
keyMap[`f${i}`] = [`F${i}`];
|
||||
}
|
||||
|
||||
const mappedKeys = keyMap[key.toLowerCase()];
|
||||
if (mappedKeys) {
|
||||
return mappedKeys.includes(e.key) || mappedKeys.includes(e.code);
|
||||
@@ -163,7 +163,7 @@ export function keyMatches(e: KeyboardEvent, key: string): boolean {
|
||||
|
||||
// For letter keys, use the physical key code for consistency
|
||||
if (key.length === 1 && key >= 'a' && key <= 'z') {
|
||||
return e.key.toLowerCase() === key.toLowerCase();
|
||||
return e.code === `Key${key.toUpperCase()}`;
|
||||
}
|
||||
|
||||
// For regular keys, check both key and code as fallback
|
||||
|
||||
@@ -5,7 +5,7 @@ const SVG_MIME = "image/svg+xml";
|
||||
|
||||
export const isShare = !window.glob;
|
||||
|
||||
export function reloadFrontendApp(reason?: string) {
|
||||
function reloadFrontendApp(reason?: string) {
|
||||
if (reason) {
|
||||
logInfo(`Frontend app reload: ${reason}`);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export function reloadFrontendApp(reason?: string) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
export function restartDesktopApp() {
|
||||
function restartDesktopApp() {
|
||||
if (!isElectron()) {
|
||||
reloadFrontendApp();
|
||||
return;
|
||||
@@ -125,7 +125,7 @@ function formatDateISO(date: Date) {
|
||||
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
|
||||
}
|
||||
|
||||
export function formatDateTime(date: Date, userSuppliedFormat?: string): string {
|
||||
function formatDateTime(date: Date, userSuppliedFormat?: string): string {
|
||||
if (userSuppliedFormat?.trim()) {
|
||||
return dayjs(date).format(userSuppliedFormat);
|
||||
} else {
|
||||
@@ -144,7 +144,7 @@ function now() {
|
||||
/**
|
||||
* Returns `true` if the client is currently running under Electron, or `false` if running in a web browser.
|
||||
*/
|
||||
export function isElectron() {
|
||||
function isElectron() {
|
||||
return !!(window && window.process && window.process.type);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ function randomString(len: number) {
|
||||
return text;
|
||||
}
|
||||
|
||||
export function isMobile() {
|
||||
function isMobile() {
|
||||
return (
|
||||
window.glob?.device === "mobile" ||
|
||||
// window.glob.device is not available in setup
|
||||
@@ -306,7 +306,7 @@ function copySelectionToClipboard() {
|
||||
}
|
||||
}
|
||||
|
||||
export function dynamicRequire(moduleName: string) {
|
||||
function dynamicRequire(moduleName: string) {
|
||||
if (typeof __non_webpack_require__ !== "undefined") {
|
||||
return __non_webpack_require__(moduleName);
|
||||
} else {
|
||||
@@ -374,42 +374,33 @@ async function openInAppHelp($button: JQuery<HTMLElement>) {
|
||||
|
||||
const inAppHelpPage = $button.attr("data-in-app-help");
|
||||
if (inAppHelpPage) {
|
||||
openInAppHelpFromUrl(inAppHelpPage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the in-app help at the given page in a split note. If there already is a split note open with a help page, it will be replaced by this one.
|
||||
*
|
||||
* @param inAppHelpPage the ID of the help note (excluding the `_help_` prefix).
|
||||
* @returns a promise that resolves once the help has been opened.
|
||||
*/
|
||||
export async function openInAppHelpFromUrl(inAppHelpPage: string) {
|
||||
// Dynamic import to avoid import issues in tests.
|
||||
const appContext = (await import("../components/app_context.js")).default;
|
||||
const activeContext = appContext.tabManager.getActiveContext();
|
||||
if (!activeContext) {
|
||||
// Dynamic import to avoid import issues in tests.
|
||||
const appContext = (await import("../components/app_context.js")).default;
|
||||
const activeContext = appContext.tabManager.getActiveContext();
|
||||
if (!activeContext) {
|
||||
return;
|
||||
}
|
||||
const subContexts = activeContext.getSubContexts();
|
||||
const targetNote = `_help_${inAppHelpPage}`;
|
||||
const helpSubcontext = subContexts.find((s) => s.viewScope?.viewMode === "contextual-help");
|
||||
const viewScope: ViewScope = {
|
||||
viewMode: "contextual-help",
|
||||
};
|
||||
if (!helpSubcontext) {
|
||||
// The help is not already open, open a new split with it.
|
||||
const { ntxId } = subContexts[subContexts.length - 1];
|
||||
appContext.triggerCommand("openNewNoteSplit", {
|
||||
ntxId,
|
||||
notePath: targetNote,
|
||||
hoistedNoteId: "_help",
|
||||
viewScope
|
||||
})
|
||||
} else {
|
||||
// There is already a help window open, make sure it opens on the right note.
|
||||
helpSubcontext.setNote(targetNote, { viewScope });
|
||||
}
|
||||
return;
|
||||
}
|
||||
const subContexts = activeContext.getSubContexts();
|
||||
const targetNote = `_help_${inAppHelpPage}`;
|
||||
const helpSubcontext = subContexts.find((s) => s.viewScope?.viewMode === "contextual-help");
|
||||
const viewScope: ViewScope = {
|
||||
viewMode: "contextual-help",
|
||||
};
|
||||
if (!helpSubcontext) {
|
||||
// The help is not already open, open a new split with it.
|
||||
const { ntxId } = subContexts[subContexts.length - 1];
|
||||
appContext.triggerCommand("openNewNoteSplit", {
|
||||
ntxId,
|
||||
notePath: targetNote,
|
||||
hoistedNoteId: "_help",
|
||||
viewScope
|
||||
})
|
||||
} else {
|
||||
// There is already a help window open, make sure it opens on the right note.
|
||||
helpSubcontext.setNote(targetNote, { viewScope });
|
||||
}
|
||||
}
|
||||
|
||||
function initHelpButtons($el: JQuery<HTMLElement> | JQuery<Window>) {
|
||||
@@ -744,50 +735,6 @@ function isLaunchBarConfig(noteId: string) {
|
||||
return ["_lbRoot", "_lbAvailableLaunchers", "_lbVisibleLaunchers", "_lbMobileRoot", "_lbMobileAvailableLaunchers", "_lbMobileVisibleLaunchers"].includes(noteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class to the <body> of the page, where the class name is formed via a prefix and a value.
|
||||
* Useful for configurable options such as `heading-style-markdown`, where `heading-style` is the prefix and `markdown` is the dynamic value.
|
||||
* There is no separator between the prefix and the value, if needed it has to be supplied manually to the prefix.
|
||||
*
|
||||
* @param prefix the prefix.
|
||||
* @param value the value to be appended to the prefix.
|
||||
*/
|
||||
export function toggleBodyClass(prefix: string, value: string) {
|
||||
const $body = $("body");
|
||||
for (const clazz of Array.from($body[0].classList)) {
|
||||
// create copy to safely iterate over while removing classes
|
||||
if (clazz.startsWith(prefix)) {
|
||||
$body.removeClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
$body.addClass(prefix + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic comparison for equality between the two arrays. The values are strictly checked via `===`.
|
||||
*
|
||||
* @param a the first array to compare.
|
||||
* @param b the second array to compare.
|
||||
* @returns `true` if both arrays are equals, `false` otherwise.
|
||||
*/
|
||||
export function arrayEqual<T>(a: T[], b: T[]) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i=0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default {
|
||||
reloadFrontendApp,
|
||||
restartDesktopApp,
|
||||
|
||||
@@ -320,3 +320,8 @@ h6 {
|
||||
page-break-after: avoid;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
figure.table {
|
||||
/* Workaround for https://github.com/ckeditor/ckeditor5/issues/18903. Remove once official fix is released */
|
||||
display: table !important;
|
||||
}
|
||||
@@ -28,28 +28,6 @@
|
||||
--ck-mention-list-max-height: 500px;
|
||||
}
|
||||
|
||||
body#trilium-app.motion-disabled *,
|
||||
body#trilium-app.motion-disabled *::before,
|
||||
body#trilium-app.motion-disabled *::after {
|
||||
/* Disable transitions and animations */
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
body#trilium-app.shadows-disabled *,
|
||||
body#trilium-app.shadows-disabled *::before,
|
||||
body#trilium-app.shadows-disabled *::after {
|
||||
/* Disable shadows */
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body#trilium-app.backdrop-effects-disabled *,
|
||||
body#trilium-app.backdrop-effects-disabled *::before,
|
||||
body#trilium-app.backdrop-effects-disabled *::after {
|
||||
/* Disable backdrop effects */
|
||||
backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
.table {
|
||||
--bs-table-bg: transparent !important;
|
||||
}
|
||||
@@ -161,15 +139,6 @@ textarea,
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
.form-group.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* Add a gap between consecutive radios / check boxes */
|
||||
label.tn-radio + label.tn-radio,
|
||||
label.tn-checkbox + label.tn-checkbox {
|
||||
@@ -377,7 +346,7 @@ body.desktop .tabulator-popup-container {
|
||||
|
||||
@supports (animation-fill-mode: forwards) {
|
||||
/* Delay the opening of submenus */
|
||||
body.desktop:not(.motion-disabled) .dropdown-submenu .dropdown-menu {
|
||||
body.desktop .dropdown-submenu .dropdown-menu {
|
||||
opacity: 0;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: var(--submenu-opening-delay);
|
||||
@@ -673,10 +642,6 @@ table.promoted-attributes-in-tooltip th {
|
||||
z-index: calc(var(--ck-z-panel) - 1) !important;
|
||||
}
|
||||
|
||||
.tooltip.tooltip-top {
|
||||
z-index: 32767 !important;
|
||||
}
|
||||
|
||||
.tooltip-trigger {
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
@@ -1769,12 +1734,16 @@ button.close:hover {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.options-section input[type="number"] {
|
||||
.options-number-input {
|
||||
/* overriding settings from .form-control */
|
||||
width: 10em !important;
|
||||
flex-grow: 0 !important;
|
||||
}
|
||||
|
||||
.options-mime-types {
|
||||
column-width: 250px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
cursor: auto;
|
||||
}
|
||||
@@ -1917,7 +1886,6 @@ body.zen #launcher-container,
|
||||
body.zen #launcher-pane,
|
||||
body.zen #left-pane,
|
||||
body.zen #right-pane,
|
||||
body.zen #mobile-sidebar-wrapper,
|
||||
body.zen .tab-row-container,
|
||||
body.zen .tab-row-widget,
|
||||
body.zen .ribbon-container:not(:has(.classic-toolbar-widget.visible)),
|
||||
@@ -1925,8 +1893,7 @@ body.zen .ribbon-container:has(.classic-toolbar-widget.visible) .ribbon-top-row,
|
||||
body.zen .ribbon-container .ribbon-body:not(:has(.classic-toolbar-widget.visible)),
|
||||
body.zen .note-icon-widget,
|
||||
body.zen .title-row .button-widget,
|
||||
body.zen .floating-buttons-children > *:not(.bx-edit-alt),
|
||||
body.zen .action-button {
|
||||
body.zen .floating-buttons-children > *:not(.bx-edit-alt) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -1968,10 +1935,6 @@ body.zen .note-title-widget input {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
body.zen #detail-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Content renderer */
|
||||
|
||||
footer.file-footer,
|
||||
@@ -2282,13 +2245,6 @@ footer.webview-footer button {
|
||||
padding: 1px 10px 1px 10px;
|
||||
}
|
||||
|
||||
/* Search result highlighting */
|
||||
.search-result-title b,
|
||||
.search-result-content b {
|
||||
font-weight: 900;
|
||||
color: var(--admonition-warning-accent-color);
|
||||
}
|
||||
|
||||
/* Customized icons */
|
||||
|
||||
.bx-tn-toc::before {
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
|
||||
--menu-text-color: #e3e3e3;
|
||||
--menu-background-color: #222222d9;
|
||||
--menu-background-color-no-backdrop: #1b1b1b;
|
||||
--menu-item-icon-color: #8c8c8c;
|
||||
--menu-item-disabled-opacity: 0.5;
|
||||
--menu-item-keyboard-shortcut-color: #ffffff8f;
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
|
||||
--menu-text-color: #272727;
|
||||
--menu-background-color: #ffffffd9;
|
||||
--menu-background-color-no-backdrop: #fdfdfd;
|
||||
--menu-item-icon-color: #727272;
|
||||
--menu-item-disabled-opacity: 0.6;
|
||||
--menu-item-keyboard-shortcut-color: #666666a8;
|
||||
|
||||
@@ -83,12 +83,6 @@
|
||||
--tab-note-icons: true;
|
||||
}
|
||||
|
||||
body.backdrop-effects-disabled {
|
||||
/* Backdrop effects are disabled, replace the menu background color with the
|
||||
* no-backdrop fallback color */
|
||||
--menu-background-color: var(--menu-background-color-no-backdrop);
|
||||
}
|
||||
|
||||
/*
|
||||
* MENUS
|
||||
*
|
||||
|
||||
@@ -233,16 +233,16 @@ div.tn-tool-dialog {
|
||||
|
||||
/* Item title link */
|
||||
|
||||
.recent-changes-content ul li a {
|
||||
.recent-changes-content ul li .note-title a {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.recent-changes-content ul li a:hover {
|
||||
.recent-changes-content ul li .note-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Item title for deleted notes */
|
||||
.recent-changes-content ul li.deleted-note .note-title {
|
||||
.recent-changes-content ul li.deleted-note .note-title > .note-title {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,9 @@ div.note-detail-empty {
|
||||
}
|
||||
|
||||
.options-section:not(.tn-no-card) {
|
||||
margin: auto;
|
||||
margin: auto;
|
||||
min-width: var(--options-card-min-width);
|
||||
max-width: var(--options-card-max-width);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--card-border-color) !important;
|
||||
box-shadow: var(--card-box-shadow);
|
||||
@@ -190,11 +192,6 @@ div.note-detail-empty {
|
||||
margin-bottom: calc(var(--options-title-offset) + 26px) !important;
|
||||
}
|
||||
|
||||
body.desktop .option-section:not(.tn-no-card) {
|
||||
min-width: var(--options-card-min-width);
|
||||
max-width: var(--options-card-max-width);
|
||||
}
|
||||
|
||||
.note-detail-content-widget-content.options {
|
||||
--default-padding: 15px;
|
||||
padding-top: calc(var(--default-padding) + var(--options-title-offset) + var(--options-title-font-size));
|
||||
@@ -236,6 +233,11 @@ body.desktop .option-section:not(.tn-no-card) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.options-section .options-mime-types {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.options-section .form-group {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Sobre Trilium Notes",
|
||||
"close": "Tanca",
|
||||
"homepage": "Pàgina principal:"
|
||||
},
|
||||
"add_link": {
|
||||
"note": "Nota"
|
||||
"note": "Nota",
|
||||
"close": "Tanca"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"close": "Tanca",
|
||||
"prefix": "Prefix: ",
|
||||
"save": "Desa"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"close": "Tanca",
|
||||
"labels": "Etiquetes",
|
||||
"relations": "Relacions",
|
||||
"notes": "Notes",
|
||||
"other": "Altres"
|
||||
},
|
||||
"clone_to": {
|
||||
"close": "Tanca"
|
||||
},
|
||||
"confirm": {
|
||||
"confirmation": "Confirmació",
|
||||
"close": "Tanca",
|
||||
"cancel": "Cancel·la",
|
||||
"ok": "OK"
|
||||
},
|
||||
@@ -31,34 +39,53 @@
|
||||
"export": "Exporta"
|
||||
},
|
||||
"help": {
|
||||
"close": "Tanca",
|
||||
"troubleshooting": "Solució de problemes",
|
||||
"other": "Altres"
|
||||
},
|
||||
"import": {
|
||||
"close": "Tanca",
|
||||
"options": "Opcions",
|
||||
"import": "Importa"
|
||||
},
|
||||
"include_note": {
|
||||
"close": "Tanca",
|
||||
"label_note": "Nota"
|
||||
},
|
||||
"info": {
|
||||
"closeButton": "Tanca",
|
||||
"okButton": "OK"
|
||||
},
|
||||
"jump_to_note": {
|
||||
"close": "Tanca"
|
||||
},
|
||||
"markdown_import": {
|
||||
"close": "Tanca"
|
||||
},
|
||||
"move_to": {
|
||||
"close": "Tanca"
|
||||
},
|
||||
"note_type_chooser": {
|
||||
"close": "Tanca",
|
||||
"templates": "Plantilles:"
|
||||
},
|
||||
"password_not_set": {
|
||||
"close": "Tanca"
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Sol·licitud",
|
||||
"close": "Tanca",
|
||||
"defaultTitle": "Sol·licitud"
|
||||
},
|
||||
"protected_session_password": {
|
||||
"close_label": "Tanca"
|
||||
},
|
||||
"recent_changes": {
|
||||
"close": "Tanca",
|
||||
"undelete_link": "recuperar"
|
||||
},
|
||||
"revisions": {
|
||||
"close": "Tanca",
|
||||
"restore_button": "Restaura",
|
||||
"delete_button": "Suprimeix",
|
||||
"download_button": "Descarrega",
|
||||
@@ -66,12 +93,14 @@
|
||||
"preview": "Vista prèvia:"
|
||||
},
|
||||
"sort_child_notes": {
|
||||
"close": "Tanca",
|
||||
"title": "títol",
|
||||
"ascending": "ascendent",
|
||||
"descending": "descendent",
|
||||
"folders": "Carpetes"
|
||||
},
|
||||
"upload_attachments": {
|
||||
"close": "Tanca",
|
||||
"options": "Opcions",
|
||||
"upload": "Puja"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Πληροφορίες για το Trilium Notes",
|
||||
"close": "Κλείσιμο",
|
||||
"homepage": "Αρχική Σελίδα:",
|
||||
"app_version": "Έκδοση εφαρμογής:",
|
||||
"db_version": "Έκδοση βάσης δεδομένων:",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "About Trilium Notes",
|
||||
"close": "Close",
|
||||
"homepage": "Homepage:",
|
||||
"app_version": "App version:",
|
||||
"db_version": "DB version:",
|
||||
@@ -27,22 +28,25 @@
|
||||
"add_link": {
|
||||
"add_link": "Add link",
|
||||
"help_on_links": "Help on links",
|
||||
"close": "Close",
|
||||
"note": "Note",
|
||||
"search_note": "search for note by its name",
|
||||
"link_title_mirrors": "link title mirrors the note's current title",
|
||||
"link_title_arbitrary": "link title can be changed arbitrarily",
|
||||
"link_title": "Link title",
|
||||
"button_add_link": "Add link"
|
||||
"button_add_link": "Add link <kbd>enter</kbd>"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"edit_branch_prefix": "Edit branch prefix",
|
||||
"help_on_tree_prefix": "Help on Tree prefix",
|
||||
"close": "Close",
|
||||
"prefix": "Prefix: ",
|
||||
"save": "Save",
|
||||
"branch_prefix_saved": "Branch prefix has been saved."
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Bulk actions",
|
||||
"close": "Close",
|
||||
"affected_notes": "Affected notes",
|
||||
"include_descendants": "Include descendants of the selected notes",
|
||||
"available_actions": "Available actions",
|
||||
@@ -57,18 +61,20 @@
|
||||
},
|
||||
"clone_to": {
|
||||
"clone_notes_to": "Clone notes to...",
|
||||
"close": "Close",
|
||||
"help_on_links": "Help on links",
|
||||
"notes_to_clone": "Notes to clone",
|
||||
"target_parent_note": "Target parent note",
|
||||
"search_for_note_by_its_name": "search for note by its name",
|
||||
"cloned_note_prefix_title": "Cloned note will be shown in note tree with given prefix",
|
||||
"prefix_optional": "Prefix (optional)",
|
||||
"clone_to_selected_note": "Clone to selected note",
|
||||
"clone_to_selected_note": "Clone to selected note <kbd>enter</kbd>",
|
||||
"no_path_to_clone_to": "No path to clone to.",
|
||||
"note_cloned": "Note \"{{clonedTitle}}\" has been cloned into \"{{targetTitle}}\""
|
||||
},
|
||||
"confirm": {
|
||||
"confirmation": "Confirmation",
|
||||
"close": "Close",
|
||||
"cancel": "Cancel",
|
||||
"ok": "OK",
|
||||
"are_you_sure_remove_note": "Are you sure you want to remove the note \"{{title}}\" from relation map? ",
|
||||
@@ -81,9 +87,9 @@
|
||||
"delete_all_clones_description": "Delete also all clones (can be undone in recent changes)",
|
||||
"erase_notes_description": "Normal (soft) deletion only marks the notes as deleted and they can be undeleted (in recent changes dialog) within a period of time. Checking this option will erase the notes immediately and it won't be possible to undelete the notes.",
|
||||
"erase_notes_warning": "Erase notes permanently (can't be undone), including all clones. This will force application reload.",
|
||||
"notes_to_be_deleted": "Following notes will be deleted ({{notesCount}})",
|
||||
"notes_to_be_deleted": "Following notes will be deleted ({{- noteCount}})",
|
||||
"no_note_to_delete": "No note will be deleted (only clones).",
|
||||
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{ relationCount}})",
|
||||
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{- relationCount}})",
|
||||
"cancel": "Cancel",
|
||||
"ok": "OK",
|
||||
"deleted_relation_text": "Note {{- note}} (to be deleted) is referenced by relation {{- relation}} originating from {{- source}}."
|
||||
@@ -107,20 +113,21 @@
|
||||
"format_pdf": "PDF - for printing or sharing purposes."
|
||||
},
|
||||
"help": {
|
||||
"title": "Cheatsheet",
|
||||
"fullDocumentation": "Help (full documentation is available <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
|
||||
"close": "Close",
|
||||
"noteNavigation": "Note navigation",
|
||||
"goUpDown": "go up/down in the list of notes",
|
||||
"collapseExpand": "collapse/expand node",
|
||||
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - go up/down in the list of notes",
|
||||
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - collapse/expand node",
|
||||
"notSet": "not set",
|
||||
"goBackForwards": "go back / forwards in the history",
|
||||
"showJumpToNoteDialog": "show <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Jump to\" dialog</a>",
|
||||
"scrollToActiveNote": "scroll to active note",
|
||||
"jumpToParentNote": "jump to parent note",
|
||||
"jumpToParentNote": "<kbd>Backspace</kbd> - jump to parent note",
|
||||
"collapseWholeTree": "collapse whole note tree",
|
||||
"collapseSubTree": "collapse sub-tree",
|
||||
"tabShortcuts": "Tab shortcuts",
|
||||
"newTabNoteLink": "on note link opens note in a new tab",
|
||||
"newTabWithActivationNoteLink": "on note link opens and activates the note in a new tab",
|
||||
"newTabNoteLink": "<kbd>Ctrl+click</kbd> - (or <kbd>middle mouse click</kbd>) on note link opens note in a new tab",
|
||||
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (or <kbd>Shift+middle mouse click</kbd>) on note link opens and activates the note in a new tab",
|
||||
"onlyInDesktop": "Only in desktop (Electron build)",
|
||||
"openEmptyTab": "open empty tab",
|
||||
"closeActiveTab": "close active tab",
|
||||
@@ -135,14 +142,14 @@
|
||||
"moveNoteUpHierarchy": "move note up in the hierarchy",
|
||||
"multiSelectNote": "multi-select note above/below",
|
||||
"selectAllNotes": "select all notes in the current level",
|
||||
"selectNote": "select note",
|
||||
"selectNote": "<kbd>Shift+click</kbd> - select note",
|
||||
"copyNotes": "copy active note (or current selection) into clipboard (used for <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">cloning</a>)",
|
||||
"cutNotes": "cut current note (or current selection) into clipboard (used for moving notes)",
|
||||
"pasteNotes": "paste note(s) as sub-note into active note (which is either move or clone depending on whether it was copied or cut into clipboard)",
|
||||
"deleteNotes": "delete note / sub-tree",
|
||||
"editingNotes": "Editing notes",
|
||||
"editNoteTitle": "in tree pane will switch from tree pane into note title. Enter from note title will switch focus to text editor. <kbd>Ctrl+.</kbd> will switch back from editor to tree pane.",
|
||||
"createEditLink": "create / edit external link",
|
||||
"createEditLink": "<kbd>Ctrl+K</kbd> - create / edit external link",
|
||||
"createInternalLink": "create internal link",
|
||||
"followLink": "follow link under cursor",
|
||||
"insertDateTime": "insert current date and time at caret position",
|
||||
@@ -162,6 +169,7 @@
|
||||
},
|
||||
"import": {
|
||||
"importIntoNote": "Import into note",
|
||||
"close": "Close",
|
||||
"chooseImportFile": "Choose import file",
|
||||
"importDescription": "Content of the selected file(s) will be imported as child note(s) into",
|
||||
"options": "Options",
|
||||
@@ -188,13 +196,14 @@
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Include note",
|
||||
"close": "Close",
|
||||
"label_note": "Note",
|
||||
"placeholder_search": "search for note by its name",
|
||||
"box_size_prompt": "Box size of the included note:",
|
||||
"box_size_small": "small (~ 10 lines)",
|
||||
"box_size_medium": "medium (~ 30 lines)",
|
||||
"box_size_full": "full (box shows complete text)",
|
||||
"button_include": "Include note"
|
||||
"button_include": "Include note <kbd>enter</kbd>"
|
||||
},
|
||||
"info": {
|
||||
"modalTitle": "Info message",
|
||||
@@ -203,20 +212,23 @@
|
||||
},
|
||||
"jump_to_note": {
|
||||
"search_placeholder": "Search for note by its name or type > for commands...",
|
||||
"search_button": "Search in full text"
|
||||
"close": "Close",
|
||||
"search_button": "Search in full text <kbd>Ctrl+Enter</kbd>"
|
||||
},
|
||||
"markdown_import": {
|
||||
"dialog_title": "Markdown import",
|
||||
"close": "Close",
|
||||
"modal_body_text": "Because of browser sandbox it's not possible to directly read clipboard from JavaScript. Please paste the Markdown to import to textarea below and click on Import button",
|
||||
"import_button": "Import",
|
||||
"import_button": "Import Ctrl+Enter",
|
||||
"import_success": "Markdown content has been imported into the document."
|
||||
},
|
||||
"move_to": {
|
||||
"dialog_title": "Move notes to ...",
|
||||
"close": "Close",
|
||||
"notes_to_move": "Notes to move",
|
||||
"target_parent_note": "Target parent note",
|
||||
"search_placeholder": "search for note by its name",
|
||||
"move_button": "Move to selected note",
|
||||
"move_button": "Move to selected note <kbd>enter</kbd>",
|
||||
"error_no_path": "No path to move to.",
|
||||
"move_success_message": "Selected notes have been moved into "
|
||||
},
|
||||
@@ -224,19 +236,20 @@
|
||||
"change_path_prompt": "Change where to create the new note:",
|
||||
"search_placeholder": "search path by name (default if empty)",
|
||||
"modal_title": "Choose note type",
|
||||
"close": "Close",
|
||||
"modal_body": "Choose note type / template of the new note:",
|
||||
"templates": "Templates",
|
||||
"builtin_templates": "Built-in Templates"
|
||||
"templates": "Templates:"
|
||||
},
|
||||
"password_not_set": {
|
||||
"title": "Password is not set",
|
||||
"close": "Close",
|
||||
"body1": "Protected notes are encrypted using a user password, but password has not been set yet.",
|
||||
"body2": "To be able to protect notes, click the button below to open the Options dialog and set your password.",
|
||||
"go_to_password_options": "Go to Password options"
|
||||
"body2": "To be able to protect notes, click <a class=\"open-password-options-button\" href=\"javascript:\">here</a> to open the Options dialog and set your password."
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Prompt",
|
||||
"ok": "OK",
|
||||
"close": "Close",
|
||||
"ok": "OK <kbd>enter</kbd>",
|
||||
"defaultTitle": "Prompt"
|
||||
},
|
||||
"protected_session_password": {
|
||||
@@ -244,11 +257,12 @@
|
||||
"help_title": "Help on Protected notes",
|
||||
"close_label": "Close",
|
||||
"form_label": "To proceed with requested action you need to start protected session by entering password:",
|
||||
"start_button": "Start protected session"
|
||||
"start_button": "Start protected session <kbd>enter</kbd>"
|
||||
},
|
||||
"recent_changes": {
|
||||
"title": "Recent changes",
|
||||
"erase_notes_button": "Erase deleted notes now",
|
||||
"close": "Close",
|
||||
"deleted_notes_message": "Deleted notes have been erased.",
|
||||
"no_changes_message": "No changes yet...",
|
||||
"undelete_link": "undelete",
|
||||
@@ -259,6 +273,7 @@
|
||||
"delete_all_revisions": "Delete all revisions of this note",
|
||||
"delete_all_button": "Delete all revisions",
|
||||
"help_title": "Help on Note Revisions",
|
||||
"close": "Close",
|
||||
"revision_last_edited": "This revision was last edited on {{date}}",
|
||||
"confirm_delete_all": "Do you want to delete all revisions of this note?",
|
||||
"no_revisions": "No revisions for this note yet...",
|
||||
@@ -280,6 +295,7 @@
|
||||
},
|
||||
"sort_child_notes": {
|
||||
"sort_children_by": "Sort children by...",
|
||||
"close": "Close",
|
||||
"sorting_criteria": "Sorting criteria",
|
||||
"title": "title",
|
||||
"date_created": "date created",
|
||||
@@ -293,12 +309,13 @@
|
||||
"sort_with_respect_to_different_character_sorting": "sort with respect to different character sorting and collation rules in different languages or regions.",
|
||||
"natural_sort_language": "Natural sort language",
|
||||
"the_language_code_for_natural_sort": "The language code for natural sort, e.g. \"zh-CN\" for Chinese.",
|
||||
"sort": "Sort"
|
||||
"sort": "Sort <kbd>enter</kbd>"
|
||||
},
|
||||
"upload_attachments": {
|
||||
"upload_attachments_to_note": "Upload attachments to note",
|
||||
"close": "Close",
|
||||
"choose_files": "Choose files",
|
||||
"files_will_be_uploaded": "Files will be uploaded as attachments into {{noteTitle}}",
|
||||
"files_will_be_uploaded": "Files will be uploaded as attachments into",
|
||||
"options": "Options",
|
||||
"shrink_images": "Shrink images",
|
||||
"upload": "Upload",
|
||||
@@ -1101,24 +1118,18 @@
|
||||
"title": "Application Theme",
|
||||
"theme_label": "Theme",
|
||||
"override_theme_fonts_label": "Override theme fonts",
|
||||
"auto_theme": "Legacy (Follow system color scheme)",
|
||||
"light_theme": "Legacy (Light)",
|
||||
"dark_theme": "Legacy (Dark)",
|
||||
"triliumnext": "Trilium (Follow system color scheme)",
|
||||
"triliumnext-light": "Trilium (Light)",
|
||||
"triliumnext-dark": "Trilium (Dark)",
|
||||
"auto_theme": "Auto",
|
||||
"light_theme": "Light",
|
||||
"dark_theme": "Dark",
|
||||
"triliumnext": "TriliumNext Beta (Follow system color scheme)",
|
||||
"triliumnext-light": "TriliumNext Beta (Light)",
|
||||
"triliumnext-dark": "TriliumNext Beta (Dark)",
|
||||
"layout": "Layout",
|
||||
"layout-vertical-title": "Vertical",
|
||||
"layout-horizontal-title": "Horizontal",
|
||||
"layout-vertical-description": "launcher bar is on the left (default)",
|
||||
"layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width."
|
||||
},
|
||||
"ui-performance": {
|
||||
"title": "Performance",
|
||||
"enable-motion": "Enable transitions and animations",
|
||||
"enable-shadows": "Enable shadows",
|
||||
"enable-backdrop-effects": "Enable background effects for menus, popups and panels"
|
||||
},
|
||||
"ai_llm": {
|
||||
"not_started": "Not started",
|
||||
"title": "AI Settings",
|
||||
@@ -1259,12 +1270,7 @@
|
||||
"selected_provider": "Selected Provider",
|
||||
"selected_provider_description": "Choose the AI provider for chat and completion features",
|
||||
"select_model": "Select model...",
|
||||
"select_provider": "Select provider...",
|
||||
"ai_enabled": "AI features enabled",
|
||||
"ai_disabled": "AI features disabled",
|
||||
"no_models_found_online": "No models found. Please check your API key and settings.",
|
||||
"no_models_found_ollama": "No Ollama models found. Please check if Ollama is running.",
|
||||
"error_fetching": "Error fetching models: {{error}}"
|
||||
"select_provider": "Select provider..."
|
||||
},
|
||||
"zoom_factor": {
|
||||
"title": "Zoom Factor (desktop build only)",
|
||||
@@ -1321,7 +1327,7 @@
|
||||
},
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Note Revision Snapshot Interval",
|
||||
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See <doc>wiki</doc> for more info.",
|
||||
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> for more info.",
|
||||
"snapshot_time_interval_label": "Note revision snapshot time interval:"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
@@ -1383,7 +1389,7 @@
|
||||
},
|
||||
"custom_date_time_format": {
|
||||
"title": "Custom Date/Time Format",
|
||||
"description": "Customize the format of the date and time inserted via <shortcut /> or the toolbar. See <doc>Day.js docs</doc> for available format tokens.",
|
||||
"description": "Customize the format of the date and time inserted via <kbd></kbd> or the toolbar. See <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">Day.js docs</a> for available format tokens.",
|
||||
"format_string": "Format string:",
|
||||
"formatted_time": "Formatted date/time:"
|
||||
},
|
||||
@@ -1814,43 +1820,6 @@
|
||||
"multiline-toolbar": "Display the toolbar on multiple lines if it doesn't fit."
|
||||
}
|
||||
},
|
||||
"ckeditor_plugins": {
|
||||
"title": "Editor Plugins",
|
||||
"description": "Configure which CKEditor plugins are enabled. Changes take effect when the editor is reloaded.",
|
||||
"loading": "Loading plugin configuration...",
|
||||
"load_failed": "Failed to load plugin configuration.",
|
||||
"load_error": "Error loading plugins",
|
||||
"retry": "Retry",
|
||||
"category_formatting": "Text Formatting",
|
||||
"category_structure": "Document Structure",
|
||||
"category_media": "Media & Files",
|
||||
"category_tables": "Tables",
|
||||
"category_advanced": "Advanced Features",
|
||||
"category_trilium": "Trilium Features",
|
||||
"category_external": "External Plugins",
|
||||
"stats_enabled": "Enabled",
|
||||
"stats_total": "Total",
|
||||
"stats_core": "Core",
|
||||
"stats_premium": "Premium",
|
||||
"no_license": "no license",
|
||||
"premium": "Premium",
|
||||
"premium_required": "Requires premium CKEditor license",
|
||||
"has_dependencies": "Dependencies",
|
||||
"depends_on": "Depends on",
|
||||
"toolbar_items": "Toolbar items",
|
||||
"validate": "Validate",
|
||||
"validation_error": "Validation failed",
|
||||
"validation_errors": "Configuration Errors:",
|
||||
"validation_warnings": "Configuration Warnings:",
|
||||
"save": "Save Changes",
|
||||
"save_success": "Plugin configuration saved successfully",
|
||||
"save_error": "Failed to save configuration",
|
||||
"reload_editor_notice": "Please reload any open text notes to apply changes",
|
||||
"reset_defaults": "Reset to Defaults",
|
||||
"reset_confirm": "Are you sure you want to reset all plugin settings to their default values?",
|
||||
"reset_success": "Plugin configuration reset to defaults",
|
||||
"reset_error": "Failed to reset configuration"
|
||||
},
|
||||
"electron_context_menu": {
|
||||
"add-term-to-dictionary": "Add \"{{term}}\" to dictionary",
|
||||
"cut": "Cut",
|
||||
@@ -2038,26 +2007,6 @@
|
||||
"open_externally": "Open externally"
|
||||
},
|
||||
"modal": {
|
||||
"close": "Close",
|
||||
"help_title": "Display more information about this screen"
|
||||
},
|
||||
"call_to_action": {
|
||||
"next_theme_title": "Try the new Trilium theme",
|
||||
"next_theme_message": "You are currently using the legacy theme, would you like to try the new theme?",
|
||||
"next_theme_button": "Try the new theme",
|
||||
"background_effects_title": "Background effects are now stable",
|
||||
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of color to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer.",
|
||||
"background_effects_button": "Enable background effects",
|
||||
"dismiss": "Dismiss"
|
||||
},
|
||||
"settings": {
|
||||
"related_settings": "Related settings"
|
||||
},
|
||||
"settings_appearance": {
|
||||
"related_code_blocks": "Color scheme for code blocks in text notes",
|
||||
"related_code_notes": "Color scheme for code notes"
|
||||
},
|
||||
"units": {
|
||||
"percentage": "%"
|
||||
"close": "Close"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "درباره Trilium Notes",
|
||||
"homepage": "صفحه اصلی:",
|
||||
"app_version": "نسخه برنامه:",
|
||||
"db_version": "نسخه پایگاه داده:",
|
||||
"sync_version": "نسخه منطبق:",
|
||||
"build_date": "تاریخ ساخت:",
|
||||
"build_revision": "نسخه بازنگری شده:",
|
||||
"data_directory": "دایرکتوری داده:"
|
||||
},
|
||||
"toast": {
|
||||
"critical-error": {
|
||||
"title": "خطای بحرانی",
|
||||
"message": "خطای بحرانی رخ داده که مانع از اجرای برنامه می شود\n\n {{message}}\n\nبه احتمال زیاد ناشی از خطای غیرمنتظره در اجرای ناموفق یک اسکریپت است. برنامه را در مد ایمن اجرا کنید و خطا را بررسی نمایید."
|
||||
}
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "افزودن لینک",
|
||||
"note": "یادداشت"
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Lisätietoja Trilium Notes:ista",
|
||||
"homepage": "Kotisivu:",
|
||||
"app_version": "Sovelluksen versio:",
|
||||
"db_version": "Tietokannan versio:",
|
||||
"build_date": "Koontipäivämäärä:",
|
||||
"data_directory": "Datakansio:",
|
||||
"sync_version": "Synkronoinnin versio:",
|
||||
"build_revision": "Sovelluksen versio:"
|
||||
},
|
||||
"toast": {
|
||||
"critical-error": {
|
||||
"title": "Kriittinen virhe"
|
||||
},
|
||||
"widget-error": {
|
||||
"title": "Widgetin luonti epäonnistui"
|
||||
}
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Lisää linkki",
|
||||
"link_title": "Linkin otsikko",
|
||||
"button_add_link": "Lisää linkki",
|
||||
"note": "Muistio",
|
||||
"search_note": "etsi muistiota sen nimellä"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"prefix": "Etuliite: ",
|
||||
"save": "Tallenna"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Massatoiminnot",
|
||||
"available_actions": "Saatavilla olevat toiminnot",
|
||||
"chosen_actions": "Valitut toiminnot",
|
||||
"execute_bulk_actions": "Toteuta massatoiminnot",
|
||||
"bulk_actions_executed": "Massatoiminnot on toteutettu onnistuneesti.",
|
||||
"none_yet": "Ei vielä... lisää toiminto klikkaamalla jotiain yllä saatavilla olevaa yltä.",
|
||||
"labels": "Merkit",
|
||||
"relations": "Suhteet",
|
||||
"notes": "Muistiot",
|
||||
"other": "Muut",
|
||||
"affected_notes": "Vaikuttaa muistioihin"
|
||||
},
|
||||
"clone_to": {
|
||||
"clone_notes_to": "Kopioi muistiot...",
|
||||
"help_on_links": "Apua linkkeihin",
|
||||
"notes_to_clone": "Kopioitavat muistiot",
|
||||
"target_parent_note": "Kohteen päämuistio",
|
||||
"search_for_note_by_its_name": "ensi muistiota sen nimellä",
|
||||
"cloned_note_prefix_title": "Kopioitu muistia näytetään puussa annetulla etuliitteellä",
|
||||
"prefix_optional": "Etuliite (valinnainen)",
|
||||
"clone_to_selected_note": "Kopioi valittuun muistioon",
|
||||
"note_cloned": "Muistio \"{{clonedTitle}}\" on kopioitu \"{{targetTitle}}\""
|
||||
},
|
||||
"confirm": {
|
||||
"confirmation": "Vahvistus",
|
||||
"cancel": "Peruuta",
|
||||
"ok": "OK",
|
||||
"also_delete_note": "Poista myös muistio"
|
||||
},
|
||||
"delete_notes": {
|
||||
"delete_notes_preview": "Poista muistion esikatselu",
|
||||
"close": "Sulje",
|
||||
"notes_to_be_deleted": "Seuraavat muistiot tullaan poistamaan ({{notesCount}})",
|
||||
"no_note_to_delete": "Muistioita ei poisteta (vain kopiot).",
|
||||
"cancel": "Peruuta",
|
||||
"ok": "OK"
|
||||
},
|
||||
"export": {
|
||||
"export_note_title": "Vie muistio",
|
||||
"close": "Sulje",
|
||||
"format_html": "HTML - suositeltu, sillä se säilyttää kaikki formatoinnit",
|
||||
"format_markdown": "Markdown - tämä säilyttää suurimman osan formatoinneista.",
|
||||
"opml_version_1": "OPML v1.0 - pelkkä teksti",
|
||||
"opml_version_2": "OPML v2.0 - sallii myös HTML:n",
|
||||
"export": "Vie",
|
||||
"choose_export_type": "Valitse ensin viennin tyyppi",
|
||||
"export_status": "Viennin tila",
|
||||
"export_in_progress": "Vienti käynnissä: {{progressCount}}",
|
||||
"export_finished_successfully": "Vienti valmistui onnistuneesti.",
|
||||
"format_pdf": "PDF - tulostukseen ja jakamiseen."
|
||||
},
|
||||
"help": {
|
||||
"title": "Lunttilappu",
|
||||
"noteNavigation": "Muistion navigointi",
|
||||
"goUpDown": "mene ylös/alas muistioiden listassa",
|
||||
"collapseExpand": "pienennä/suurenna solmu",
|
||||
"notSet": "ei asetettu",
|
||||
"goBackForwards": "mene taaksepäin/eteenpäin historiassa",
|
||||
"jumpToParentNote": "Hyppää ylempään muistioon",
|
||||
"collapseWholeTree": "pienennä koko muistio puu",
|
||||
"onlyInDesktop": "Vain työpöytänäkymässä (Electron build)",
|
||||
"openEmptyTab": "Avaa tyhjä välilehti",
|
||||
"closeActiveTab": "sulje aktiivinen välilehti",
|
||||
"activateNextTab": "aktivoi seuraava välilehti",
|
||||
"activatePreviousTab": "aktivoi edellinen välilehti",
|
||||
"creatingNotes": "Luo muistiota",
|
||||
"movingCloningNotes": "Siirrä / kopioi muistioita",
|
||||
"moveNoteUpHierarchy": "siirrä muistio ylöspäin listassa",
|
||||
"selectNote": "valitse muistio",
|
||||
"editingNotes": "Muokkaa solmua",
|
||||
"createEditLink": "luo / muokkaa ulkoista linkkiä",
|
||||
"createInternalLink": "luo sisäinen linkki",
|
||||
"insertDateTime": "lisää nykyinen päivämäärä ja aika hiiren kohdalle",
|
||||
"troubleshooting": "Vianmääritys",
|
||||
"reloadFrontend": "lataa Trilium:in käyttöliittymä",
|
||||
"showDevTools": "näytä kehittäjätyökalut",
|
||||
"showSQLConsole": "näytä SQL konsoli",
|
||||
"other": "Muut"
|
||||
},
|
||||
"import": {
|
||||
"importIntoNote": "Tuo muistioon",
|
||||
"chooseImportFile": "Valitse tuonnin tiedosto",
|
||||
"options": "Valinnat",
|
||||
"safeImport": "Turvallinen tuonti",
|
||||
"shrinkImages": "Kutista kuvat",
|
||||
"replaceUnderscoresWithSpaces": "Korvaa alaviivat väleillä tuotujen muistioiden tiedostonimissä",
|
||||
"import": "Tuo",
|
||||
"failed": "Tuonti epäonnistui: {{message}}.",
|
||||
"html_import_tags": {
|
||||
"title": "HTML Tuonnin Tunnisteet",
|
||||
"placeholder": "Lisää HTML tunnisteet, yksi per rivi"
|
||||
},
|
||||
"import-status": "Tuonnin tila",
|
||||
"in-progress": "Tuonti vaiheessa: {{progress}}",
|
||||
"successful": "Tuonti valmistui onnistuneesti."
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Sisällytä muistio",
|
||||
"label_note": "Muistio",
|
||||
"placeholder_search": "etsi muistiota sen nimellä",
|
||||
"box_size_small": "pieni (~ 10 riviä)",
|
||||
"box_size_medium": "keskisuuri (~ 30 riviä)",
|
||||
"button_include": "Sisällytä muistio"
|
||||
},
|
||||
"info": {
|
||||
"modalTitle": "Info viesti",
|
||||
"closeButton": "Sulje",
|
||||
"okButton": "OK"
|
||||
},
|
||||
"jump_to_note": {
|
||||
"search_button": "Etsi koko tekstistä"
|
||||
},
|
||||
"call_to_action": {
|
||||
"dismiss": "Hylkää"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"about": {
|
||||
"close": "Chiudi",
|
||||
"app_version": "Versione dell'app:",
|
||||
"db_version": "Versione DB:",
|
||||
"sync_version": "Versione Sync:",
|
||||
@@ -26,6 +27,7 @@
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Aggiungi un collegamento",
|
||||
"close": "Chiudi",
|
||||
"note": "Nota",
|
||||
"search_note": "cerca una nota per nome",
|
||||
"link_title_mirrors": "il titolo del collegamento rispecchia il titolo della nota corrente",
|
||||
@@ -37,12 +39,14 @@
|
||||
"branch_prefix": {
|
||||
"edit_branch_prefix": "Modifica il prefisso del ramo",
|
||||
"help_on_tree_prefix": "Aiuto sui prefissi dell'Albero",
|
||||
"close": "Chiudi",
|
||||
"prefix": "Prefisso: ",
|
||||
"save": "Salva",
|
||||
"branch_prefix_saved": "Il prefisso del ramo è stato salvato."
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Azioni massive",
|
||||
"close": "Chiudi",
|
||||
"affected_notes": "Note influenzate",
|
||||
"include_descendants": "Includi i discendenti della nota selezionata",
|
||||
"available_actions": "Azioni disponibili",
|
||||
@@ -57,6 +61,7 @@
|
||||
},
|
||||
"clone_to": {
|
||||
"clone_notes_to": "Clona note in...",
|
||||
"close": "Chiudi",
|
||||
"help_on_links": "Aiuto sui collegamenti",
|
||||
"notes_to_clone": "Note da clonare",
|
||||
"target_parent_note": "Nodo padre obiettivo",
|
||||
@@ -68,6 +73,7 @@
|
||||
"note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\""
|
||||
},
|
||||
"confirm": {
|
||||
"close": "Chiudi",
|
||||
"cancel": "Annulla",
|
||||
"ok": "OK",
|
||||
"confirmation": "Conferma",
|
||||
@@ -100,19 +106,47 @@
|
||||
"choose_export_type": "Scegli prima il tipo di esportazione, per favore",
|
||||
"export_in_progress": "Esportazione in corso: {{progressCount}}",
|
||||
"export_finished_successfully": "Esportazione terminata con successo.",
|
||||
"format_pdf": "PDF- allo scopo di stampa o esportazione.",
|
||||
"export_type_subtree": "Questa nota e tutti i suoi discendenti",
|
||||
"format_html": "HTML - raccomandato in quanto mantiene tutti i formati",
|
||||
"format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.",
|
||||
"format_markdown": "MArkdown - questo conserva la maggior parte della formattazione."
|
||||
"format_pdf": "PDF- allo scopo di stampa o esportazione."
|
||||
},
|
||||
"help": {
|
||||
"close": "Chiudi",
|
||||
"fullDocumentation": "Aiuto (la documentazione completa è disponibile <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)"
|
||||
},
|
||||
"import": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"include_note": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"jump_to_note": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"markdown_import": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"move_to": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"note_type_chooser": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"password_not_set": {
|
||||
"close": "Chiudi",
|
||||
"body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.",
|
||||
"body2": "Per proteggere le note, fare clic su <a class=\"open-password-options-button\" href=\"javascript:\">qui</a> per aprire la finestra di dialogo Opzioni e impostare la password."
|
||||
},
|
||||
"protected_session_password": {
|
||||
"close_label": "Chiudi"
|
||||
},
|
||||
"prompt": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"recent_changes": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"revisions": {
|
||||
"close": "Chiudi"
|
||||
},
|
||||
"abstract_bulk_action": {
|
||||
"remove_this_search_action": "Rimuovi questa azione di ricerca"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Trilium Notes에 대해서",
|
||||
"homepage": "홈페이지:",
|
||||
"app_version": "앱 버전:",
|
||||
"db_version": "DB 버전:",
|
||||
"sync_version": "동기화 버전:",
|
||||
"build_date": "빌드 날짜:",
|
||||
"build_revision": "빌드 리비전:",
|
||||
"data_directory": "데이터 경로:"
|
||||
},
|
||||
"toast": {
|
||||
"critical-error": {
|
||||
"title": "심각한 오류",
|
||||
"message": "클라이언트 애플리케이션 시작 도중 심각한 오류가 발생했습니다:\n\n{{message}}\n\n이는 스크립트가 예기치 않게 실패하면서 발생한 것일 수 있습니다. 애플리케이션을 안전 모드로 시작한 뒤 문제를 해결해 보세요."
|
||||
},
|
||||
"widget-error": {
|
||||
"title": "위젯 초기화 실패"
|
||||
}
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "링크 추가",
|
||||
"note": "노트",
|
||||
"search_note": "이름으로 노트 검색하기"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"save": "저장"
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "O notatkach Trilium",
|
||||
"homepage": "Strona główna:",
|
||||
"app_version": "Wersja aplikacji:",
|
||||
"db_version": "Wersja bazy danych:",
|
||||
"sync_version": "Wersja synchronizacji:",
|
||||
"build_date": "Zbudowano:",
|
||||
"build_revision": "Rewizja zbudowania:",
|
||||
"data_directory": "Katalog z danymi:"
|
||||
},
|
||||
"toast": {
|
||||
"critical-error": {
|
||||
"title": "Błąd krytyczny",
|
||||
"message": "Wystąpił krytyczny błąd uniemożliwiający uruchomienie aplikacji:\n\n{{message}}\n\nJest to spowodowane najprawdopodobniej niespodziewanym błędem skryptu. Spróbuj uruchomić aplikację ponownie w trybie bezpiecznym i zaadresuj problem."
|
||||
}
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Dodaj link"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"save": "Zapisz"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"labels": "Etykiety",
|
||||
"notes": "Notatki",
|
||||
"other": "Inne",
|
||||
"relations": "Powiązania"
|
||||
},
|
||||
"confirm": {
|
||||
"ok": "OK",
|
||||
"cancel": "Anuluj"
|
||||
},
|
||||
"delete_notes": {
|
||||
"cancel": "Anuluj",
|
||||
"close": "Zamknij"
|
||||
},
|
||||
"export": {
|
||||
"close": "Zamknij"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "Podrobnosti Trilium Notes",
|
||||
"homepage": "Domača stran:",
|
||||
"app_version": "Verzija aplikacije:",
|
||||
"db_version": "Verzija DB:",
|
||||
"sync_version": "Verzija Sync:"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"about": {
|
||||
"title": "O Trilium Belеškama",
|
||||
"close": "Zatvori",
|
||||
"homepage": "Početna stranica:",
|
||||
"app_version": "Verzija aplikacije:",
|
||||
"db_version": "Verzija baze podataka:",
|
||||
@@ -27,6 +28,7 @@
|
||||
"add_link": {
|
||||
"add_link": "Dodaj link",
|
||||
"help_on_links": "Pomoć na linkovima",
|
||||
"close": "Zatvori",
|
||||
"note": "Beleška",
|
||||
"search_note": "potražite belešku po njenom imenu",
|
||||
"link_title_mirrors": "naziv linka preslikava trenutan naziv beleške",
|
||||
@@ -37,12 +39,14 @@
|
||||
"branch_prefix": {
|
||||
"edit_branch_prefix": "Izmeni prefiks grane",
|
||||
"help_on_tree_prefix": "Pomoć na prefiksu Drveta",
|
||||
"close": "Zatvori",
|
||||
"prefix": "Prefiks: ",
|
||||
"save": "Sačuvaj",
|
||||
"branch_prefix_saved": "Prefiks grane je sačuvan."
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Grupne akcije",
|
||||
"close": "Zatvori",
|
||||
"affected_notes": "Pogođene beleške",
|
||||
"include_descendants": "Obuhvati potomke izabranih beleški",
|
||||
"available_actions": "Dostupne akcije",
|
||||
@@ -57,6 +61,7 @@
|
||||
},
|
||||
"clone_to": {
|
||||
"clone_notes_to": "Klonirajte beleške u...",
|
||||
"close": "Zatvori",
|
||||
"help_on_links": "Pomoć na linkovima",
|
||||
"notes_to_clone": "Beleške za kloniranje",
|
||||
"target_parent_note": "Ciljna nadređena beleška",
|
||||
@@ -69,6 +74,7 @@
|
||||
},
|
||||
"confirm": {
|
||||
"confirmation": "Potvrda",
|
||||
"close": "Zatvori",
|
||||
"cancel": "Otkaži",
|
||||
"ok": "U redu",
|
||||
"are_you_sure_remove_note": "Da li ste sigurni da želite da uklonite belešku \"{{title}}\" iz mape odnosa? ",
|
||||
@@ -107,6 +113,8 @@
|
||||
"format_pdf": "PDF - za namene štampanja ili deljenja."
|
||||
},
|
||||
"help": {
|
||||
"fullDocumentation": "Pomoć (puna dokumentacija je dostupna <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
|
||||
"close": "Zatvori",
|
||||
"noteNavigation": "Navigacija beleški",
|
||||
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - kretanje gore/dole u listi sa beleškama",
|
||||
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - sakupi/proširi čvor",
|
||||
@@ -114,12 +122,12 @@
|
||||
"goBackForwards": "idi u nazad/napred kroz istoriju",
|
||||
"showJumpToNoteDialog": "prikaži <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Idi na\" dijalog</a>",
|
||||
"scrollToActiveNote": "skroluj do aktivne beleške",
|
||||
"jumpToParentNote": "idi do nadređene beleške",
|
||||
"jumpToParentNote": "<kbd>Backspace</kbd> - idi do nadređene beleške",
|
||||
"collapseWholeTree": "sakupi celo drvo beleški",
|
||||
"collapseSubTree": "sakupi pod-drvo",
|
||||
"tabShortcuts": "Prečice na karticama",
|
||||
"newTabNoteLink": "na link beleške otvara belešku u novoj kartici",
|
||||
"newTabWithActivationNoteLink": "na link beleške otvara i aktivira belešku u novoj kartici",
|
||||
"newTabNoteLink": "<kbd>Ctrl+click</kbd> - (ili <kbd>middle mouse click</kbd>) na link beleške otvara belešku u novoj kartici",
|
||||
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (ili <kbd>Shift+middle mouse click</kbd>) na link beleške otvara i aktivira belešku u novoj kartici",
|
||||
"onlyInDesktop": "Samo na dektop-u (Electron verzija)",
|
||||
"openEmptyTab": "otvori praznu karticu",
|
||||
"closeActiveTab": "zatvori aktivnu karticu",
|
||||
@@ -134,14 +142,14 @@
|
||||
"moveNoteUpHierarchy": "pomeri belešku na gore u hijerarhiji",
|
||||
"multiSelectNote": "višestruki izbor beleški iznad/ispod",
|
||||
"selectAllNotes": "izaberi sve beleške u trenutnom nivou",
|
||||
"selectNote": "izaberi belešku",
|
||||
"selectNote": "<kbd>Shift+click</kbd> - izaberi belešku",
|
||||
"copyNotes": "kopiraj aktivnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">kloniranje</a>)",
|
||||
"cutNotes": "iseci trenutnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za premeštanje beleški)",
|
||||
"pasteNotes": "nalepi belešku/e kao podbelešku u aktivnoj belešci (koja se ili premešta ili klonira u zavisnosti od toga da li je beleška kopirana ili isečena u privremenu memoriju)",
|
||||
"deleteNotes": "obriši belešku / podstablo",
|
||||
"editingNotes": "Izmena beleški",
|
||||
"editNoteTitle": "u ravni drveta će se prebaciti sa ravni drveta na naslov beleške. Ulaz sa naslova beleške će prebaciti fokus na uređivač teksta. <kbd>Ctrl+.</kbd> će se vratiti sa uređivača na ravan drveta.",
|
||||
"createEditLink": "napravi / izmeni spoljašnji link",
|
||||
"createEditLink": "<kbd>Ctrl+K</kbd> - napravi / izmeni spoljašnji link",
|
||||
"createInternalLink": "napravi unutrašnji link",
|
||||
"followLink": "prati link ispod kursora",
|
||||
"insertDateTime": "ubaci trenutan datum i vreme na poziciju kursora",
|
||||
@@ -161,6 +169,7 @@
|
||||
},
|
||||
"import": {
|
||||
"importIntoNote": "Uvezi u belešku",
|
||||
"close": "Zatvori",
|
||||
"chooseImportFile": "Izaberi datoteku za uvoz",
|
||||
"importDescription": "Sadržaj izabranih datoteka će biti uvezen kao podbeleške u",
|
||||
"options": "Opcije",
|
||||
@@ -187,13 +196,14 @@
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Uključi belešku",
|
||||
"close": "Zatvori",
|
||||
"label_note": "Beleška",
|
||||
"placeholder_search": "pretraži belešku po njenom imenu",
|
||||
"box_size_prompt": "Veličina kutije priložene beleške:",
|
||||
"box_size_small": "mala (~ 10 redova)",
|
||||
"box_size_medium": "srednja (~ 30 redova)",
|
||||
"box_size_full": "puna (kutija prikazuje ceo tekst)",
|
||||
"button_include": "Uključi belešku"
|
||||
"button_include": "Uključi belešku <kbd>enter</kbd>"
|
||||
},
|
||||
"info": {
|
||||
"modalTitle": "Informativna poruka",
|
||||
@@ -202,20 +212,23 @@
|
||||
},
|
||||
"jump_to_note": {
|
||||
"search_placeholder": "Pretraži belešku po njenom imenu ili unesi > za komande...",
|
||||
"close": "Zatvori",
|
||||
"search_button": "Pretraga u punom tekstu <kbd>Ctrl+Enter</kbd>"
|
||||
},
|
||||
"markdown_import": {
|
||||
"dialog_title": "Uvoz za Markdown",
|
||||
"close": "Zatvori",
|
||||
"modal_body_text": "Zbog Sandbox-a pretraživača nije moguće direktno učitati privremenu memoriju iz JavaScript-a. Molimo vas da nalepite Markdown za uvoz u tekstualno polje ispod i kliknete na dugme za uvoz",
|
||||
"import_button": "Uvoz",
|
||||
"import_button": "Uvoz Ctrl+Enter",
|
||||
"import_success": "Markdown sadržaj je učitan u dokument."
|
||||
},
|
||||
"move_to": {
|
||||
"dialog_title": "Premesti beleške u ...",
|
||||
"close": "Zatvori",
|
||||
"notes_to_move": "Beleške za premeštanje",
|
||||
"target_parent_note": "Ciljana nadbeleška",
|
||||
"search_placeholder": "potraži belešku po njenom imenu",
|
||||
"move_button": "Pređi na izabranu belešku",
|
||||
"move_button": "Pređi na izabranu belešku <kbd>enter</kbd>",
|
||||
"error_no_path": "Nema putanje za premeštanje.",
|
||||
"move_success_message": "Izabrane beleške su premeštene u "
|
||||
},
|
||||
@@ -223,16 +236,19 @@
|
||||
"change_path_prompt": "Promenite gde će se napraviti nova beleška:",
|
||||
"search_placeholder": "pretraži putanju po njenom imenu (podrazumevano ako je prazno)",
|
||||
"modal_title": "Izaberite tip beleške",
|
||||
"close": "Zatvori",
|
||||
"modal_body": "Izaberite tip beleške / šablon za novu belešku:",
|
||||
"templates": "Šabloni"
|
||||
"templates": "Šabloni:"
|
||||
},
|
||||
"password_not_set": {
|
||||
"title": "Lozinka nije podešena",
|
||||
"close": "Zatvori",
|
||||
"body1": "Zaštićene beleške su enkriptovane sa korisničkom lozinkom, ali lozinka još uvek nije podešena.",
|
||||
"body2": "Za biste mogli da sačuvate beleške, kliknite <a class=\"open-password-options-button\" href=\"javascript:\">ovde</a> da otvorite dijalog sa Opcijama i podesite svoju lozinku."
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Upit",
|
||||
"close": "Zatvori",
|
||||
"ok": "U redu <kbd>enter</kbd>",
|
||||
"defaultTitle": "Upit"
|
||||
},
|
||||
@@ -241,11 +257,12 @@
|
||||
"help_title": "Pomoć za Zaštićene beleške",
|
||||
"close_label": "Zatvori",
|
||||
"form_label": "Da biste nastavili sa traženom akcijom moraćete započeti zaštićenu sesiju tako što ćete uneti lozinku:",
|
||||
"start_button": "Započni zaštićenu sesiju"
|
||||
"start_button": "Započni zaštićenu sesiju <kbd>enter</kbd>"
|
||||
},
|
||||
"recent_changes": {
|
||||
"title": "Nedavne promene",
|
||||
"erase_notes_button": "Obriši izabrane beleške odmah",
|
||||
"close": "Zatvori",
|
||||
"deleted_notes_message": "Obrisane beleške su uklonjene.",
|
||||
"no_changes_message": "Još uvek nema izmena...",
|
||||
"undelete_link": "poništi brisanje",
|
||||
@@ -256,6 +273,7 @@
|
||||
"delete_all_revisions": "Obriši sve revizije ove beleške",
|
||||
"delete_all_button": "Obriši sve revizije",
|
||||
"help_title": "Pomoć za Revizije beleški",
|
||||
"close": "Zatvori",
|
||||
"revision_last_edited": "Ova revizija je poslednji put izmenjena {{date}}",
|
||||
"confirm_delete_all": "Da li želite da obrišete sve revizije ove beleške?",
|
||||
"no_revisions": "Još uvek nema revizija za ovu belešku...",
|
||||
@@ -277,6 +295,7 @@
|
||||
},
|
||||
"sort_child_notes": {
|
||||
"sort_children_by": "Sortiranje podbeleški po...",
|
||||
"close": "Zatvori",
|
||||
"sorting_criteria": "Kriterijum za sortiranje",
|
||||
"title": "naslov",
|
||||
"date_created": "datum kreiranja",
|
||||
@@ -290,12 +309,13 @@
|
||||
"sort_with_respect_to_different_character_sorting": "sortiranje sa poštovanjem različitih pravila sortiranja karaktera i kolacija u različitim jezicima ili regionima.",
|
||||
"natural_sort_language": "Jezik za prirodno sortiranje",
|
||||
"the_language_code_for_natural_sort": "Kod jezika za prirodno sortiranje, npr. \"zh-CN\" za Kineski.",
|
||||
"sort": "Sortiraj"
|
||||
"sort": "Sortiraj <kbd>enter</kbd>"
|
||||
},
|
||||
"upload_attachments": {
|
||||
"upload_attachments_to_note": "Otpremite priloge uz belešku",
|
||||
"close": "Zatvori",
|
||||
"choose_files": "Izaberite datoteke",
|
||||
"files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u {{noteTitle}}",
|
||||
"files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u",
|
||||
"options": "Opcije",
|
||||
"shrink_images": "Smanji slike",
|
||||
"upload": "Otpremi",
|
||||
|
||||
@@ -1,26 +1,71 @@
|
||||
{
|
||||
"about": {
|
||||
"close": "Kapat",
|
||||
"homepage": "Giriş sayfası:",
|
||||
"app_version": "Uygulama versiyonu:",
|
||||
"db_version": "Veritabanı versiyonu:"
|
||||
},
|
||||
"add_link": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"close": "Kapat",
|
||||
"save": "Kaydet"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"clone_to": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"confirm": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"recent_changes": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"delete_notes": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"export": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"help": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"include_note": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"import": {
|
||||
"close": "Kapat",
|
||||
"chooseImportFile": "İçe aktarım dosyası",
|
||||
"importDescription": "Seçilen dosya(lar) alt not olarak içe aktarılacaktır"
|
||||
},
|
||||
"info": {
|
||||
"closeButton": "Kapat"
|
||||
},
|
||||
"jump_to_note": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"markdown_import": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"move_to": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"note_type_chooser": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"password_not_set": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"prompt": {
|
||||
"close": "Kapat"
|
||||
},
|
||||
"protected_session_password": {
|
||||
"close_label": "Kapat"
|
||||
},
|
||||
"revisions": {
|
||||
"close": "Kapat"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"homepage": "Trang chủ:",
|
||||
"title": "Về Trilium Notes"
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Thêm liên kết",
|
||||
"button_add_link": "Thêm liên kết"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"other": "Khác"
|
||||
},
|
||||
"branch_prefix": {
|
||||
"save": "Lưu"
|
||||
},
|
||||
"confirm": {
|
||||
"ok": "OK",
|
||||
"cancel": "Huỷ"
|
||||
},
|
||||
"delete_notes": {
|
||||
"close": "Đóng",
|
||||
"ok": "OK",
|
||||
"cancel": "Huỷ"
|
||||
},
|
||||
"export": {
|
||||
"close": "Đóng"
|
||||
},
|
||||
"help": {
|
||||
"other": "Khác"
|
||||
},
|
||||
"toast": {
|
||||
"critical-error": {
|
||||
"title": "Lỗi nghiêm trọng"
|
||||
}
|
||||
},
|
||||
"import": {
|
||||
"options": "Tuỳ chọn"
|
||||
},
|
||||
"info": {
|
||||
"okButton": "OK",
|
||||
"closeButton": "Đóng"
|
||||
},
|
||||
"move_to": {
|
||||
"dialog_title": "Chuyển ghi chép tới..."
|
||||
},
|
||||
"prompt": {
|
||||
"ok": "OK"
|
||||
},
|
||||
"protected_session_password": {
|
||||
"close_label": "Đóng"
|
||||
},
|
||||
"revisions": {
|
||||
"restore_button": "Khôi phục",
|
||||
"delete_button": "Xoá"
|
||||
},
|
||||
"upload_attachments": {
|
||||
"options": "Tuỳ chọn"
|
||||
},
|
||||
"attribute_detail": {
|
||||
"name": "Tên",
|
||||
"value": "Giá trị",
|
||||
"text": "Văn bản",
|
||||
"number": "Số",
|
||||
"delete": "Xoá"
|
||||
},
|
||||
"rename_note": {
|
||||
"rename_note": "Đổi tên ghi chép"
|
||||
},
|
||||
"add_label": {
|
||||
"add_label": "Thêm nhãn",
|
||||
"label_name_placeholder": "tên nhãn",
|
||||
"help_text_item2": "hoặc thay đổi giá trị của nhãn có sẵn"
|
||||
},
|
||||
"rename_label": {
|
||||
"rename_label": "Đặt lại tên nhãn"
|
||||
},
|
||||
"call_to_action": {
|
||||
"dismiss": "Bỏ qua"
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { ComponentChildren } from "preact";
|
||||
import { memo } from "preact/compat";
|
||||
import AbstractBulkAction from "./abstract_bulk_action";
|
||||
|
||||
interface BulkActionProps {
|
||||
label: string | ComponentChildren;
|
||||
children?: ComponentChildren;
|
||||
helpText?: ComponentChildren;
|
||||
bulkAction: AbstractBulkAction;
|
||||
}
|
||||
|
||||
// Define styles as constants to prevent recreation
|
||||
const flexContainerStyle = { display: "flex", alignItems: "center" } as const;
|
||||
const labelStyle = { marginRight: "10px" } as const;
|
||||
const textStyle = { marginRight: "10px", marginLeft: "10px" } as const;
|
||||
|
||||
const BulkAction = memo(({ label, children, helpText, bulkAction }: BulkActionProps) => {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<div style={flexContainerStyle}>
|
||||
<div style={labelStyle} className="text-nowrap">{label}</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
<td className="button-column">
|
||||
{helpText && <div className="dropdown help-dropdown">
|
||||
<span className="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div className="dropdown-menu dropdown-menu-right p-4">
|
||||
{helpText}
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
<span
|
||||
className="bx bx-x icon-action action-conf-del"
|
||||
onClick={() => bulkAction?.deleteAction()}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
||||
export default BulkAction;
|
||||
|
||||
export const BulkActionText = memo(({ text }: { text: string }) => {
|
||||
return (
|
||||
<div
|
||||
style={textStyle}
|
||||
className="text-nowrap">
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import { t } from "../../services/i18n.js";
|
||||
import server from "../../services/server.js";
|
||||
import ws from "../../services/ws.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import type FAttribute from "../../entities/fattribute.js";
|
||||
import { VNode } from "preact";
|
||||
|
||||
export interface ActionDefinition {
|
||||
interface ActionDefinition {
|
||||
script: string;
|
||||
relationName: string;
|
||||
targetNoteId: string;
|
||||
@@ -26,9 +27,26 @@ export default abstract class AbstractBulkAction {
|
||||
this.actionDef = actionDef;
|
||||
}
|
||||
|
||||
// to be overridden
|
||||
abstract doRender(): VNode;
|
||||
render() {
|
||||
try {
|
||||
const $rendered = this.doRender();
|
||||
|
||||
$rendered
|
||||
.find(".action-conf-del")
|
||||
.on("click", () => this.deleteAction())
|
||||
.attr("title", t("abstract_bulk_action.remove_this_search_action"));
|
||||
|
||||
utils.initHelpDropdown($rendered);
|
||||
|
||||
return $rendered;
|
||||
} catch (e: any) {
|
||||
logError(`Failed rendering search action: ${JSON.stringify(this.attribute.dto)} with error: ${e.message} ${e.stack}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// to be overridden
|
||||
abstract doRender(): JQuery<HTMLElement>;
|
||||
static get actionName() {
|
||||
return "";
|
||||
}
|
||||
@@ -48,6 +66,9 @@ export default abstract class AbstractBulkAction {
|
||||
|
||||
async deleteAction() {
|
||||
await server.remove(`notes/${this.attribute.noteId}/attributes/${this.attribute.attributeId}`);
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
|
||||
//await this.triggerCommand('refreshSearchDefinition');
|
||||
}
|
||||
}
|
||||
|
||||
58
apps/client/src/widgets/bulk_actions/execute_script.ts
Normal file
58
apps/client/src/widgets/bulk_actions/execute_script.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { t } from "../../services/i18n.js";
|
||||
import SpacedUpdate from "../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "./abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td>
|
||||
${t("execute_script.execute_script")}
|
||||
</td>
|
||||
<td>
|
||||
<input type="text"
|
||||
class="form-control script"
|
||||
placeholder="note.title = note.title + '- suffix';"/>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
${t("execute_script.help_text")}
|
||||
|
||||
${t("execute_script.example_1")}
|
||||
|
||||
<pre>note.title = note.title + ' - suffix';</pre>
|
||||
|
||||
${t("execute_script.example_2")}
|
||||
|
||||
<pre>for (const attr of note.getOwnedAttributes) { attr.markAsDeleted(); }</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class ExecuteScriptBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "executeScript";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("execute_script.execute_script");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
const $script = $action.find(".script");
|
||||
$script.val(this.actionDef.script || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({ script: $script.val() });
|
||||
}, 1000);
|
||||
|
||||
$script.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import FormTextBox from "../react/FormTextBox.jsx";
|
||||
import AbstractBulkAction, { ActionDefinition } from "./abstract_bulk_action.js";
|
||||
import BulkAction from "./BulkAction.jsx";
|
||||
import { useSpacedUpdate } from "../react/hooks.jsx";
|
||||
|
||||
function ExecuteScriptBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ script, setScript ] = useState(actionDef.script);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ script }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ script ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("execute_script.execute_script")}
|
||||
helpText={<>
|
||||
{t("execute_script.help_text")}
|
||||
|
||||
{t("execute_script.example_1")}
|
||||
|
||||
<pre>note.title = note.title + ' - suffix';</pre>
|
||||
|
||||
{t("execute_script.example_2")}
|
||||
|
||||
<pre>{"for (const attr of note.getOwnedAttributes) { attr.markAsDeleted(); }"}</pre>
|
||||
</>}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder="note.title = note.title + '- suffix';"
|
||||
currentValue={script} onChange={setScript}
|
||||
/>
|
||||
</BulkAction>
|
||||
);
|
||||
}
|
||||
|
||||
export default class ExecuteScriptBulkAction extends AbstractBulkAction {
|
||||
|
||||
static get actionName() {
|
||||
return "executeScript";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("execute_script.execute_script");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <ExecuteScriptBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
|
||||
}
|
||||
70
apps/client/src/widgets/bulk_actions/label/add_label.ts
Normal file
70
apps/client/src/widgets/bulk_actions/label/add_label.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("add_label.add_label")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control label-name"
|
||||
placeholder="${t("add_label.label_name_placeholder")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("add_label.label_name_title")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("add_label.to_value")}</div>
|
||||
|
||||
<input type="text" class="form-control label-value" placeholder="${t("add_label.new_value_placeholder")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("add_label.help_text")}</p>
|
||||
|
||||
<ul>
|
||||
<li>${t("add_label.help_text_item1")}</li>
|
||||
<li>${t("add_label.help_text_item2")}</li>
|
||||
</ul>
|
||||
|
||||
${t("add_label.help_text_note")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class AddLabelBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "addLabel";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("add_label.add_label");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $labelName = $action.find(".label-name");
|
||||
$labelName.val(this.actionDef.labelName || "");
|
||||
|
||||
const $labelValue = $action.find(".label-value");
|
||||
$labelValue.val(this.actionDef.labelValue || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
labelName: $labelName.val(),
|
||||
labelValue: $labelValue.val()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$labelValue.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../../../services/i18n";
|
||||
import FormTextBox from "../../react/FormTextBox";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction";
|
||||
import { useSpacedUpdate } from "../../react/hooks";
|
||||
|
||||
function AddLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
|
||||
const [ labelValue, setLabelValue ] = useState<string>(actionDef.labelValue ?? "");
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName, labelValue }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName, labelValue]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("add_label.add_label")}
|
||||
helpText={<>
|
||||
<p>{t("add_label.help_text")}</p>
|
||||
|
||||
<ul>
|
||||
<li>{t("add_label.help_text_item1")}</li>
|
||||
<li>{t("add_label.help_text_item2")}</li>
|
||||
</ul>
|
||||
|
||||
{t("add_label.help_text_note")}
|
||||
</>}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("add_label.label_name_placeholder")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("add_label.label_name_title")}
|
||||
currentValue={labelName} onChange={setLabelName}
|
||||
/>
|
||||
<BulkActionText text={t("add_label.to_value")} />
|
||||
<FormTextBox
|
||||
placeholder={t("add_label.new_value_placeholder")}
|
||||
currentValue={labelValue} onChange={setLabelValue}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class AddLabelBulkAction extends AbstractBulkAction {
|
||||
|
||||
doRender() {
|
||||
return <AddLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />;
|
||||
}
|
||||
|
||||
static get actionName() {
|
||||
return "addLabel";
|
||||
}
|
||||
|
||||
static get actionTitle() {
|
||||
return t("add_label.add_label");
|
||||
}
|
||||
}
|
||||
43
apps/client/src/widgets/bulk_actions/label/delete_label.ts
Normal file
43
apps/client/src/widgets/bulk_actions/label/delete_label.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td>
|
||||
${t("delete_label.delete_label")}
|
||||
</td>
|
||||
<td>
|
||||
<input type="text"
|
||||
class="form-control label-name"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("delete_label.label_name_title")}"
|
||||
placeholder="${t("delete_label.label_name_placeholder")}"/>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class DeleteLabelBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteLabel";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_label.delete_label");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
const $labelName = $action.find(".label-name");
|
||||
$labelName.val(this.actionDef.labelName || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({ labelName: $labelName.val() });
|
||||
}, 1000);
|
||||
|
||||
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
import BulkAction from "../BulkAction.jsx";
|
||||
|
||||
function DeleteLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
|
||||
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("delete_label.delete_label")}
|
||||
>
|
||||
<FormTextBox
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("delete_label.label_name_title")}
|
||||
placeholder={t("delete_label.label_name_placeholder")}
|
||||
currentValue={labelName} onChange={setLabelName}
|
||||
/>
|
||||
</BulkAction>
|
||||
);
|
||||
}
|
||||
|
||||
export default class DeleteLabelBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteLabel";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_label.delete_label");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <DeleteLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
60
apps/client/src/widgets/bulk_actions/label/rename_label.ts
Normal file
60
apps/client/src/widgets/bulk_actions/label/rename_label.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_label.rename_label_from")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control old-label-name"
|
||||
placeholder="${t("rename_label.old_name_placeholder")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("rename_label.name_title")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("rename_label.to")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control new-label-name"
|
||||
placeholder="${t("rename_label.new_name_placeholder")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("rename_label.name_title")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class RenameLabelBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameLabel";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("rename_label.rename_label");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $oldLabelName = $action.find(".old-label-name");
|
||||
$oldLabelName.val(this.actionDef.oldLabelName || "");
|
||||
|
||||
const $newLabelName = $action.find(".new-label-name");
|
||||
$newLabelName.val(this.actionDef.newLabelName || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
oldLabelName: $oldLabelName.val(),
|
||||
newLabelName: $newLabelName.val()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$oldLabelName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$newLabelName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function RenameLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
|
||||
const [ oldLabelName, setOldLabelName ] = useState(actionDef.oldLabelName);
|
||||
const [ newLabelName, setNewLabelName ] = useState(actionDef.newLabelName);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ oldLabelName, newLabelName }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ oldLabelName, newLabelName ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("rename_label.rename_label_from")}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("rename_label.old_name_placeholder")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("rename_label.name_title")}
|
||||
currentValue={oldLabelName} onChange={setOldLabelName}
|
||||
/>
|
||||
|
||||
<BulkActionText text={t("rename_label.to")} />
|
||||
|
||||
<FormTextBox
|
||||
placeholder={t("rename_label.new_name_placeholder")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("rename_label.name_title")}
|
||||
currentValue={newLabelName} onChange={setNewLabelName}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class RenameLabelBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameLabel";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("rename_label.rename_label");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <RenameLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("update_label_value.update_label_value")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control label-name"
|
||||
placeholder="${t("update_label_value.label_name_placeholder")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("update_label_value.label_name_title")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("update_label_value.to_value")}</div>
|
||||
|
||||
<input type="text" class="form-control label-value" placeholder="${t("update_label_value.new_value_placeholder")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("update_label_value.help_text")}</p>
|
||||
|
||||
${t("update_label_value.help_text_note")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class UpdateLabelValueBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "updateLabelValue";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("update_label_value.update_label_value");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $labelName = $action.find(".label-name");
|
||||
$labelName.val(this.actionDef.labelName || "");
|
||||
|
||||
const $labelValue = $action.find(".label-value");
|
||||
$labelValue.val(this.actionDef.labelValue || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
labelName: $labelName.val(),
|
||||
labelValue: $labelValue.val()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$labelValue.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
function UpdateLabelValueComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
|
||||
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
|
||||
const [ labelValue, setLabelValue ] = useState<string>(actionDef.labelValue ?? "");
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName, labelValue }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName, labelValue]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("update_label_value.update_label_value")}
|
||||
helpText={<>
|
||||
<p>{t("update_label_value.help_text")}</p>
|
||||
|
||||
{t("update_label_value.help_text_note")}
|
||||
</>}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("update_label_value.label_name_placeholder")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("update_label_value.label_name_title")}
|
||||
currentValue={labelName} onChange={setLabelName}
|
||||
/>
|
||||
<BulkActionText text={t("update_label_value.to_value")} />
|
||||
<FormTextBox
|
||||
placeholder={t("update_label_value.new_value_placeholder")}
|
||||
currentValue={labelValue} onChange={setLabelValue}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class UpdateLabelValueBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "updateLabelValue";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("update_label_value.update_label_value");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <UpdateLabelValueComponent bulkAction={this} actionDef={this.actionDef} />;
|
||||
}
|
||||
}
|
||||
38
apps/client/src/widgets/bulk_actions/note/delete_note.ts
Normal file
38
apps/client/src/widgets/bulk_actions/note/delete_note.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span class="bx bx-trash"></span>
|
||||
|
||||
${t("delete_note.delete_matched_notes")}
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("delete_note.delete_matched_notes_description")}</p>
|
||||
|
||||
<p>${t("delete_note.undelete_notes_instruction")}</p>
|
||||
|
||||
${t("delete_note.erase_notes_instruction")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class DeleteNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteNote";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_note.delete_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return $(TPL);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import BulkAction from "../BulkAction.jsx";
|
||||
import Icon from "../../react/Icon.jsx";
|
||||
|
||||
function DeleteNoteBulkActionComponent({ bulkAction }: { bulkAction: AbstractBulkAction }) {
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={<><Icon icon="bx bx-trash" /> {t("delete_note.delete_matched_notes")}</>}
|
||||
helpText={<>
|
||||
<p>{t("delete_note.delete_matched_notes_description")}</p>
|
||||
|
||||
<p>{t("delete_note.undelete_notes_instruction")}</p>
|
||||
|
||||
{t("delete_note.erase_notes_instruction")}
|
||||
</>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default class DeleteNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteNote";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_note.delete_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <DeleteNoteBulkActionComponent bulkAction={this} />
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span class="bx bx-trash"></span>
|
||||
${t("delete_revisions.delete_note_revisions")}
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
${t("delete_revisions.all_past_note_revisions")}
|
||||
</div>
|
||||
</div>
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class DeleteRevisionsBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteRevisions";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_revisions.delete_note_revisions");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return $(TPL);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import Icon from "../../react/Icon.jsx";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import BulkAction from "../BulkAction.jsx";
|
||||
|
||||
function DeleteRevisionsBulkActionComponent({ bulkAction }: { bulkAction: AbstractBulkAction }) {
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={<><Icon icon="bx bx-trash" /> {t("delete_revisions.delete_note_revisions")}</>}
|
||||
helpText={t("delete_revisions.all_past_note_revisions")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default class DeleteRevisionsBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteRevisions";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_revisions.delete_note_revisions");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <DeleteRevisionsBulkActionComponent bulkAction={this} />
|
||||
}
|
||||
}
|
||||
64
apps/client/src/widgets/bulk_actions/note/move_note.ts
Normal file
64
apps/client/src/widgets/bulk_actions/note/move_note.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import noteAutocompleteService from "../../../services/note_autocomplete.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("move_note.move_note")}</div>
|
||||
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("move_note.to")}</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control target-parent-note" placeholder="${t("move_note.target_parent_note")}"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("move_note.on_all_matched_notes")}:</p>
|
||||
|
||||
<ul style="margin-bottom: 0;">
|
||||
<li>${t("move_note.move_note_new_parent")}</li>
|
||||
<li>${t("move_note.clone_note_new_parent")}</li>
|
||||
<li>${t("move_note.nothing_will_happen")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class MoveNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "moveNote";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("move_note.move_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $targetParentNote = $action.find(".target-parent-note");
|
||||
noteAutocompleteService.initNoteAutocomplete($targetParentNote);
|
||||
$targetParentNote.setNote(this.actionDef.targetParentNoteId);
|
||||
|
||||
$targetParentNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
targetParentNoteId: $targetParentNote.getSelectedNoteId()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$targetParentNote.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function MoveNoteBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ targetParentNoteId, setTargetParentNoteId ] = useState<string>();
|
||||
const spacedUpdate = useSpacedUpdate(() => {
|
||||
return bulkAction.saveAction({ targetParentNoteId: targetParentNoteId })
|
||||
});
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ targetParentNoteId ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("move_note.move_note")}
|
||||
helpText={<>
|
||||
<p>{t("move_note.on_all_matched_notes")}:</p>
|
||||
|
||||
<ul style="margin-bottom: 0;">
|
||||
<li>{t("move_note.move_note_new_parent")}</li>
|
||||
<li>{t("move_note.clone_note_new_parent")}</li>
|
||||
<li>{t("move_note.nothing_will_happen")}</li>
|
||||
</ul>
|
||||
</>}
|
||||
>
|
||||
<BulkActionText text={t("move_note.to")} />
|
||||
|
||||
<NoteAutocomplete
|
||||
placeholder={t("move_note.target_parent_note")}
|
||||
noteId={targetParentNoteId} noteIdChanged={setTargetParentNoteId}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class MoveNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "moveNote";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("move_note.move_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <MoveNoteBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
61
apps/client/src/widgets/bulk_actions/note/rename_note.ts
Normal file
61
apps/client/src/widgets/bulk_actions/note/rename_note.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_note.rename_note_title_to")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control new-title"
|
||||
placeholder="${t("rename_note.new_note_title")}"
|
||||
title="${t("rename_note.click_help_icon")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("rename_note.evaluated_as_js_string")}</p>
|
||||
|
||||
<ul>
|
||||
<li>${t("rename_note.example_note")}</li>
|
||||
<li>${t("rename_note.example_new_title")}</li>
|
||||
<li>${t("rename_note.example_date_prefix")}</li>
|
||||
</ul>
|
||||
|
||||
${t("rename_note.api_docs")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class RenameNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameNote";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("rename_note.rename_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $newTitle = $action.find(".new-title");
|
||||
$newTitle.val(this.actionDef.newTitle || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
newTitle: $newTitle.val()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$newTitle.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BulkAction from "../BulkAction.jsx";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
import RawHtml from "../../react/RawHtml.jsx";
|
||||
|
||||
function RenameNoteBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
|
||||
const [ newTitle, setNewTitle ] = useState<string>(actionDef.newTitle ?? "");
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ newTitle }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ newTitle ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("rename_note.rename_note_title_to")}
|
||||
helpText={<>
|
||||
<p>{t("rename_note.evaluated_as_js_string")}</p>
|
||||
|
||||
<ul>
|
||||
<li><RawHtml html={t("rename_note.example_note")} /></li>
|
||||
<li><RawHtml html={t("rename_note.example_new_title")} /></li>
|
||||
<li><RawHtml html={t("rename_note.example_date_prefix")} /></li>
|
||||
</ul>
|
||||
|
||||
<RawHtml html={t("rename_note.api_docs")} />
|
||||
</>}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("rename_note.new_note_title")}
|
||||
title={("rename_note.click_help_icon")}
|
||||
currentValue={newTitle} onChange={setNewTitle}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class RenameNoteBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameNote";
|
||||
}
|
||||
|
||||
static get actionTitle() {
|
||||
return t("rename_note.rename_note");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <RenameNoteBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import noteAutocompleteService from "../../../services/note_autocomplete.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("add_relation.add_relation")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control relation-name"
|
||||
placeholder="${t("add_relation.relation_name")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
style="flex-shrink: 3"
|
||||
title="${t("add_relation.allowed_characters")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("add_relation.to")}</div>
|
||||
|
||||
<div class="input-group" style="flex-shrink: 2">
|
||||
<input type="text" class="form-control target-note" placeholder="${t("add_relation.target_note")}"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
${t("add_relation.create_relation_on_all_matched_notes")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class AddRelationBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "addRelation";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("add_relation.add_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $relationName = $action.find(".relation-name");
|
||||
$relationName.val(this.actionDef.relationName || "");
|
||||
|
||||
const $targetNote = $action.find(".target-note");
|
||||
noteAutocompleteService.initNoteAutocomplete($targetNote);
|
||||
$targetNote.setNote(this.actionDef.targetNoteId);
|
||||
|
||||
$targetNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
relationName: $relationName.val(),
|
||||
targetNoteId: $targetNote.getSelectedNoteId()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$targetNote.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import noteAutocompleteService from "../../../services/note_autocomplete.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function AddRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ relationName, setRelationName ] = useState<string>(actionDef.relationName);
|
||||
const [ targetNoteId, setTargetNoteId ] = useState<string>(actionDef.targetNoteId);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName, targetNoteId }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName, targetNoteId ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("add_relation.add_relation")}
|
||||
helpText={t("add_relation.create_relation_on_all_matched_notes")}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("add_relation.relation_name")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
style={{ flexShrink: 3 }}
|
||||
title={t("add_relation.allowed_characters")}
|
||||
currentValue={relationName} onChange={setRelationName}
|
||||
/>
|
||||
|
||||
<BulkActionText text={t("add_relation.to")} />
|
||||
|
||||
<NoteAutocomplete
|
||||
placeholder={t("add_relation.target_note")}
|
||||
noteId={targetNoteId} noteIdChanged={setTargetNoteId}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class AddRelationBulkAction extends AbstractBulkAction {
|
||||
|
||||
static get actionName() {
|
||||
return "addRelation";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("add_relation.add_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <AddRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td>
|
||||
${t("delete_relation.delete_relation")}
|
||||
</td>
|
||||
<td>
|
||||
<div style="display: flex; align-items: center">
|
||||
<input type="text"
|
||||
class="form-control relation-name"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
placeholder="${t("delete_relation.relation_name")}"
|
||||
title="${t("delete_relation.allowed_characters")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class DeleteRelationBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "deleteRelation";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("delete_relation.delete_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
const $relationName = $action.find(".relation-name");
|
||||
$relationName.val(this.actionDef.relationName || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({ relationName: $relationName.val() });
|
||||
}, 1000);
|
||||
|
||||
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BulkAction from "../BulkAction.jsx";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function DeleteRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ relationName, setRelationName ] = useState(actionDef.relationName);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("delete_relation.delete_relation")}
|
||||
>
|
||||
<FormTextBox
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
placeholder={t("delete_relation.relation_name")}
|
||||
title={t("delete_relation.allowed_characters")}
|
||||
currentValue={relationName} onChange={setRelationName}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class DeleteRelationBulkAction extends AbstractBulkAction {
|
||||
|
||||
static get actionName() {
|
||||
return "deleteRelation";
|
||||
}
|
||||
|
||||
static get actionTitle() {
|
||||
return t("delete_relation.delete_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <DeleteRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_relation.rename_relation_from")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control old-relation-name"
|
||||
placeholder="${t("rename_relation.old_name")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("rename_relation.allowed_characters")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("rename_relation.to")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control new-relation-name"
|
||||
placeholder="${t("rename_relation.new_name")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title="${t("rename_relation.allowed_characters")}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class RenameRelationBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameRelation";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("rename_relation.rename_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $oldRelationName = $action.find(".old-relation-name");
|
||||
$oldRelationName.val(this.actionDef.oldRelationName || "");
|
||||
|
||||
const $newRelationName = $action.find(".new-relation-name");
|
||||
$newRelationName.val(this.actionDef.newRelationName || "");
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
oldRelationName: $oldRelationName.val(),
|
||||
newRelationName: $newRelationName.val()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$oldRelationName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$newRelationName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function RenameRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ oldRelationName, setOldRelationName ] = useState(actionDef.oldRelationName);
|
||||
const [ newRelationName, setNewRelationName ] = useState(actionDef.newRelationName);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ oldRelationName, newRelationName }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ oldRelationName, newRelationName ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("rename_relation.rename_relation_from")}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("rename_relation.old_name")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("rename_relation.allowed_characters")}
|
||||
currentValue={oldRelationName} onChange={setOldRelationName}
|
||||
/>
|
||||
|
||||
<BulkActionText text={t("rename_relation.to")} />
|
||||
|
||||
<FormTextBox
|
||||
placeholder={t("rename_relation.new_name")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
title={t("rename_relation.allowed_characters")}
|
||||
currentValue={newRelationName} onChange={setNewRelationName}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class RenameRelationBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "renameRelation";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("rename_relation.rename_relation");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <RenameRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import AbstractBulkAction from "../abstract_bulk_action.js";
|
||||
import noteAutocompleteService from "../../../services/note_autocomplete.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="margin-right: 10px;" class="text-nowrap">${t("update_relation_target.update_relation")}</div>
|
||||
|
||||
<input type="text"
|
||||
class="form-control relation-name"
|
||||
placeholder="${t("update_relation_target.relation_name")}"
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
style="flex-shrink: 3"
|
||||
title="${t("update_relation_target.allowed_characters")}"/>
|
||||
|
||||
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("update_relation_target.to")}</div>
|
||||
|
||||
<div class="input-group" style="flex-shrink: 2">
|
||||
<input type="text" class="form-control target-note" placeholder="${t("update_relation_target.target_note")}"/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
<div class="dropdown help-dropdown">
|
||||
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu dropdown-menu-right p-4">
|
||||
<p>${t("update_relation_target.on_all_matched_notes")}:</p>
|
||||
|
||||
<ul style="margin-bottom: 0;">
|
||||
<li>${t("update_relation_target.change_target_note")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="bx bx-x icon-action action-conf-del"></span>
|
||||
</td>
|
||||
</tr>`;
|
||||
|
||||
export default class UpdateRelationTargetBulkAction extends AbstractBulkAction {
|
||||
static get actionName() {
|
||||
return "updateRelationTarget";
|
||||
}
|
||||
static get actionTitle() {
|
||||
return t("update_relation_target.update_relation_target");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
const $action = $(TPL);
|
||||
|
||||
const $relationName = $action.find(".relation-name");
|
||||
$relationName.val(this.actionDef.relationName || "");
|
||||
|
||||
const $targetNote = $action.find(".target-note");
|
||||
noteAutocompleteService.initNoteAutocomplete($targetNote);
|
||||
$targetNote.setNote(this.actionDef.targetNoteId);
|
||||
|
||||
$targetNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
const spacedUpdate = new SpacedUpdate(async () => {
|
||||
await this.saveAction({
|
||||
relationName: $relationName.val(),
|
||||
targetNoteId: $targetNote.getSelectedNoteId()
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
$targetNote.on("input", () => spacedUpdate.scheduleUpdate());
|
||||
|
||||
return $action;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
|
||||
import { t } from "../../../services/i18n.js";
|
||||
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
|
||||
import FormTextBox from "../../react/FormTextBox.jsx";
|
||||
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { useSpacedUpdate } from "../../react/hooks.jsx";
|
||||
|
||||
function UpdateRelationTargetComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
|
||||
const [ relationName, setRelationName ] = useState(actionDef.relationName);
|
||||
const [ targetNoteId, setTargetNoteId ] = useState(actionDef.targetNoteId);
|
||||
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName, targetNoteId }));
|
||||
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName, targetNoteId ]);
|
||||
|
||||
return (
|
||||
<BulkAction
|
||||
bulkAction={bulkAction}
|
||||
label={t("update_relation_target.update_relation")}
|
||||
helpText={<>
|
||||
<p>{t("update_relation_target.on_all_matched_notes")}:</p>
|
||||
|
||||
<ul style="margin-bottom: 0;">
|
||||
<li>{t("update_relation_target.change_target_note")}</li>
|
||||
</ul>
|
||||
</>}
|
||||
>
|
||||
<FormTextBox
|
||||
placeholder={t("update_relation_target.relation_name")}
|
||||
pattern="[\\p{L}\\p{N}_:]+"
|
||||
style={{ flexShrink: 3 }}
|
||||
title={t("update_relation_target.allowed_characters")}
|
||||
currentValue={relationName} onChange={setRelationName}
|
||||
/>
|
||||
|
||||
<BulkActionText text={t("update_relation_target.to")} />
|
||||
|
||||
<NoteAutocomplete
|
||||
placeholder={t("update_relation_target.target_note")}
|
||||
containerStyle={{ flexShrink: 2 }}
|
||||
noteId={targetNoteId} noteIdChanged={setTargetNoteId}
|
||||
/>
|
||||
</BulkAction>
|
||||
)
|
||||
}
|
||||
|
||||
export default class UpdateRelationTargetBulkAction extends AbstractBulkAction {
|
||||
|
||||
static get actionName() {
|
||||
return "updateRelationTarget";
|
||||
}
|
||||
|
||||
static get actionTitle() {
|
||||
return t("update_relation_target.update_relation_target");
|
||||
}
|
||||
|
||||
doRender() {
|
||||
return <UpdateRelationTargetComponent bulkAction={this} actionDef={this.actionDef} />
|
||||
}
|
||||
|
||||
}
|
||||
@@ -363,6 +363,7 @@ export default class GlobalMenuWidget extends BasicWidget {
|
||||
|
||||
this.$zoomState = this.$widget.find(".zoom-state");
|
||||
this.$toggleZenMode = this.$widget.find('[data-trigger-command="toggleZenMode"');
|
||||
this.$toggleZenMode.toggle(!utils.isMobile());
|
||||
this.$widget.on("show.bs.dropdown", () => this.#onShown());
|
||||
if (this.tooltip) {
|
||||
this.$widget.on("hide.bs.dropdown", () => this.tooltip.enable());
|
||||
@@ -414,7 +415,7 @@ export default class GlobalMenuWidget extends BasicWidget {
|
||||
}
|
||||
|
||||
async fetchLatestVersion() {
|
||||
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Trilium/releases/latest";
|
||||
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Notes/releases/latest";
|
||||
|
||||
const resp = await fetch(RELEASES_API_URL);
|
||||
const data = await resp.json();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
const TPL = /*html*/`\
|
||||
<div class="close-zen-container">
|
||||
@@ -29,10 +28,6 @@ const TPL = /*html*/`\
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
body.zen.mobile .close-zen-container {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
body.zen.electron:not(.platform-darwin):not(.native-titlebar) .close-zen-container {
|
||||
left: calc(env(titlebar-area-width) - var(--zen-button-size) - 2px);
|
||||
right: unset;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { EventData } from "../../components/app_context.js";
|
||||
import FlexContainer from "./flex_container.js";
|
||||
import options from "../../services/options.js";
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
import FlexContainer from "./flex_container.js";
|
||||
|
||||
/**
|
||||
* The root container is the top-most widget/container, from which the entire layout derives.
|
||||
@@ -29,45 +27,15 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
|
||||
window.visualViewport?.addEventListener("resize", () => this.#onMobileResize());
|
||||
}
|
||||
|
||||
this.#setMotion(options.is("motionEnabled"));
|
||||
this.#setShadows(options.is("shadowsEnabled"));
|
||||
this.#setBackdropEffects(options.is("backdropEffectsEnabled"));
|
||||
|
||||
return super.render();
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
if (loadResults.isOptionReloaded("motionEnabled")) {
|
||||
this.#setMotion(options.is("motionEnabled"));
|
||||
}
|
||||
|
||||
if (loadResults.isOptionReloaded("shadowsEnabled")) {
|
||||
this.#setShadows(options.is("shadowsEnabled"));
|
||||
}
|
||||
|
||||
if (loadResults.isOptionReloaded("backdropEffectsEnabled")) {
|
||||
this.#setBackdropEffects(options.is("backdropEffectsEnabled"));
|
||||
}
|
||||
}
|
||||
|
||||
#onMobileResize() {
|
||||
const currentViewportHeight = getViewportHeight();
|
||||
const isKeyboardOpened = (currentViewportHeight < this.originalViewportHeight);
|
||||
this.$widget.toggleClass("virtual-keyboard-opened", isKeyboardOpened);
|
||||
}
|
||||
|
||||
#setMotion(enabled: boolean) {
|
||||
document.body.classList.toggle("motion-disabled", !enabled);
|
||||
jQuery.fx.off = !enabled;
|
||||
}
|
||||
|
||||
#setShadows(enabled: boolean) {
|
||||
document.body.classList.toggle("shadows-disabled", !enabled);
|
||||
}
|
||||
|
||||
#setBackdropEffects(enabled: boolean) {
|
||||
document.body.classList.toggle("backdrop-effects-disabled", !enabled);
|
||||
}
|
||||
}
|
||||
|
||||
function getViewportHeight() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user