mirror of
https://github.com/zadam/trilium.git
synced 2025-11-11 15:55:52 +01:00
Merge remote-tracking branch 'origin/main' into feature/export_with_share_theme
This commit is contained in:
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -13,9 +13,9 @@ name: "CodeQL Advanced"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "develop" ]
|
branches: [ "main" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "develop" ]
|
branches: [ "main" ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '20 7 * * 0'
|
- cron: '20 7 * * 0'
|
||||||
|
|
||||||
|
|||||||
11
.github/workflows/dev.yml
vendored
11
.github/workflows/dev.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: Dev
|
name: Dev
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ develop ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ develop ]
|
branches: [ main ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@@ -12,8 +12,8 @@ concurrency:
|
|||||||
env:
|
env:
|
||||||
GHCR_REGISTRY: ghcr.io
|
GHCR_REGISTRY: ghcr.io
|
||||||
DOCKERHUB_REGISTRY: docker.io
|
DOCKERHUB_REGISTRY: docker.io
|
||||||
IMAGE_NAME: ${{ github.repository_owner }}/notes
|
IMAGE_NAME: ${{ github.repository}}
|
||||||
TEST_TAG: ${{ github.repository_owner }}/notes:test
|
TEST_TAG: ${{ github.repository}}:test
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write # for PR comments
|
pull-requests: write # for PR comments
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: nrwl/nx-set-shas@v4
|
- uses: nrwl/nx-set-shas@v4
|
||||||
- name: Check affected
|
- name: Check affected
|
||||||
run: pnpm nx affected --verbose -t typecheck build rebuild-deps
|
run: pnpm nx affected --verbose -t typecheck build rebuild-deps test-build
|
||||||
|
|
||||||
test_dev:
|
test_dev:
|
||||||
name: Test development
|
name: Test development
|
||||||
@@ -77,6 +77,7 @@ jobs:
|
|||||||
- name: Trigger client build
|
- name: Trigger client build
|
||||||
run: pnpm nx run client:build
|
run: pnpm nx run client:build
|
||||||
- name: Send client bundle stats to RelativeCI
|
- name: Send client bundle stats to RelativeCI
|
||||||
|
if: false
|
||||||
uses: relative-ci/agent-action@v3
|
uses: relative-ci/agent-action@v3
|
||||||
with:
|
with:
|
||||||
webpackStatsFile: ./apps/client/dist/webpack-stats.json
|
webpackStatsFile: ./apps/client/dist/webpack-stats.json
|
||||||
|
|||||||
14
.github/workflows/main-docker.yml
vendored
14
.github/workflows/main-docker.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "develop"
|
- "main"
|
||||||
- "feature/update**"
|
- "feature/update**"
|
||||||
- "feature/server_esm**"
|
- "feature/server_esm**"
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
@@ -14,8 +14,8 @@ on:
|
|||||||
env:
|
env:
|
||||||
GHCR_REGISTRY: ghcr.io
|
GHCR_REGISTRY: ghcr.io
|
||||||
DOCKERHUB_REGISTRY: docker.io
|
DOCKERHUB_REGISTRY: docker.io
|
||||||
IMAGE_NAME: ${{ github.repository_owner }}/notes
|
IMAGE_NAME: ${{ github.repository}}
|
||||||
TEST_TAG: ${{ github.repository_owner }}/notes:test
|
TEST_TAG: ${{ github.repository}}:test
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -83,6 +83,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm exec nx run server-e2e:e2e
|
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm exec nx run server-e2e:e2e
|
||||||
|
|
||||||
|
- name: Upload Playwright trace
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Playwright trace (${{ matrix.dockerfile }})
|
||||||
|
path: test-output/playwright/output
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- .github/actions/build-electron/*
|
- .github/actions/build-electron/*
|
||||||
- .github/workflows/nightly.yml
|
- .github/workflows/nightly.yml
|
||||||
- forge.config.cjs
|
- forge.config.ts
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
|||||||
4
.github/workflows/playwright.yml
vendored
4
.github/workflows/playwright.yml
vendored
@@ -3,7 +3,7 @@ name: playwright
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -40,4 +40,4 @@ jobs:
|
|||||||
# - run: npx nx-cloud record -- echo Hello World
|
# - run: npx nx-cloud record -- echo Hello World
|
||||||
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
|
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
|
||||||
# When you enable task distribution, run the e2e-ci task instead of e2e
|
# When you enable task distribution, run the e2e-ci task instead of e2e
|
||||||
- run: pnpm exec nx affected -t e2e
|
- run: pnpm exec nx affected -t e2e --exclude desktop-e2e
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
forge_platform: darwin
|
forge_platform: darwin
|
||||||
- name: linux
|
- name: linux
|
||||||
image: ubuntu-latest
|
image: ubuntu-22.04
|
||||||
shell: bash
|
shell: bash
|
||||||
forge_platform: linux
|
forge_platform: linux
|
||||||
- name: windows
|
- name: windows
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,11 +1,5 @@
|
|||||||
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||||
|
|
||||||
# Workaround for Nx bug: parent .gitignore files with '*' can cause
|
|
||||||
# `nx show projects` to return nothing by ignoring subprojects.
|
|
||||||
# See: https://github.com/nrwl/nx/issues/27368
|
|
||||||
# Unignore everything to ensure Nx detects all projects
|
|
||||||
!*
|
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
dist
|
dist
|
||||||
tmp
|
tmp
|
||||||
@@ -52,3 +46,4 @@ upload
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
/result
|
/result
|
||||||
|
.svelte-kit
|
||||||
@@ -1,7 +1,2 @@
|
|||||||
_regroup
|
_regroup
|
||||||
_regroup_monorepo
|
_regroup_monorepo
|
||||||
|
|
||||||
# Asset copying respects .gitignore / .nxignore for some reason.
|
|
||||||
# See https://github.com/nrwl/nx/issues/20309
|
|
||||||
!dist
|
|
||||||
!node_modules
|
|
||||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -9,6 +9,8 @@
|
|||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"tobermory.es6-string-html",
|
"tobermory.es6-string-html",
|
||||||
"vitest.explorer",
|
"vitest.explorer",
|
||||||
"yzhang.markdown-all-in-one"
|
"yzhang.markdown-all-in-one",
|
||||||
|
"svelte.svelte-vscode",
|
||||||
|
"bradlc.vscode-tailwindcss"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# TriliumNext Notes
|
# Trilium Notes
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
[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)
|
[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)
|
||||||
|
|
||||||
TriliumNext Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
|
Trilium Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
|
||||||
|
|
||||||
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
|
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for q
|
|||||||
* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
|
* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
|
||||||
* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
|
* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
|
||||||
* UI available in English, German, Spanish, French, Romanian, and Chinese (simplified and traditional)
|
* UI available in English, German, Spanish, French, Romanian, and Chinese (simplified and traditional)
|
||||||
* Direct [OpenID and TOTP integration](.docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md") for more secure login
|
* Direct [OpenID and TOTP integration](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md) for more secure login
|
||||||
* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
|
* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
|
||||||
* there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
|
* there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
|
||||||
* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
|
* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
|
||||||
@@ -153,7 +153,7 @@ Please view the [documentation guide](./docs/Developer%20Guide/Developer%20Guide
|
|||||||
## 👏 Shoutouts
|
## 👏 Shoutouts
|
||||||
|
|
||||||
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
|
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
|
||||||
* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. TriliumNext Notes would not be the same without it.
|
* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. Trilium Notes would not be the same without it.
|
||||||
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
|
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
|
||||||
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map.html) and [link maps](https://triliumnext.github.io/Docs/Wiki/note-map.html#link-map)
|
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map.html) and [link maps](https://triliumnext.github.io/Docs/Wiki/note-map.html#link-map)
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ if ! git diff-index --quiet HEAD --; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BASE_BRANCH=master
|
BASE_BRANCH=main
|
||||||
|
|
||||||
if [[ "$VERSION" == *"beta"* ]]; then
|
if [[ "$VERSION" == *"beta"* ]]; then
|
||||||
BASE_BRANCH=beta
|
BASE_BRANCH=beta
|
||||||
|
|||||||
@@ -47,11 +47,3 @@ echo "Tagging commit with $TAG"
|
|||||||
|
|
||||||
git tag $TAG
|
git tag $TAG
|
||||||
git push origin $TAG
|
git push origin $TAG
|
||||||
|
|
||||||
echo "Updating master"
|
|
||||||
|
|
||||||
git fetch
|
|
||||||
git checkout master
|
|
||||||
git reset --hard origin/master
|
|
||||||
git merge origin/develop
|
|
||||||
git push
|
|
||||||
@@ -25,9 +25,10 @@ stats() {
|
|||||||
# Print the number of existing strings on the JSON files for each locale
|
# Print the number of existing strings on the JSON files for each locale
|
||||||
s=$(number_of_keys "${paths[0]}/en/server.json")
|
s=$(number_of_keys "${paths[0]}/en/server.json")
|
||||||
c=$(number_of_keys "${paths[1]}/en/translation.json")
|
c=$(number_of_keys "${paths[1]}/en/translation.json")
|
||||||
echo "| locale |server strings |client strings |"
|
echo "| locale | server strings | client strings |"
|
||||||
echo "|--------|---------------|---------------|"
|
echo "|--------|----------------|----------------|"
|
||||||
echo "| en | ${s} | ${c} |"
|
echo "| en | ${s} | ${c} |"
|
||||||
|
echo "|--------|----------------|----------------|"
|
||||||
for locale in "${locales[@]}"; do
|
for locale in "${locales[@]}"; do
|
||||||
s=$(number_of_keys "${paths[0]}/${locale}/server.json")
|
s=$(number_of_keys "${paths[0]}/${locale}/server.json")
|
||||||
c=$(number_of_keys "${paths[1]}/${locale}/translation.json")
|
c=$(number_of_keys "${paths[1]}/${locale}/translation.json")
|
||||||
@@ -78,7 +79,10 @@ file_path="$(
|
|||||||
cd -- "$(dirname "${0}")" >/dev/null 2>&1 || exit
|
cd -- "$(dirname "${0}")" >/dev/null 2>&1 || exit
|
||||||
pwd -P
|
pwd -P
|
||||||
)"
|
)"
|
||||||
paths=("${file_path}/../translations/" "${file_path}/../src/public/translations/")
|
paths=(
|
||||||
|
"${file_path}/../../apps/server/src/assets/translations/"
|
||||||
|
"${file_path}/../../apps/client/src/translations/"
|
||||||
|
)
|
||||||
locales=(cn de es fr pt_br ro tw)
|
locales=(cn de es fr pt_br ro tw)
|
||||||
|
|
||||||
if [ $# -eq 1 ]; then
|
if [ $# -eq 1 ]; then
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export default tseslint.config(
|
|||||||
"dist/*",
|
"dist/*",
|
||||||
"docs/*",
|
"docs/*",
|
||||||
"demo/*",
|
"demo/*",
|
||||||
"libraries/*",
|
|
||||||
"src/public/app-dist/*",
|
"src/public/app-dist/*",
|
||||||
"src/public/app/doc_notes/*"
|
"src/public/app/doc_notes/*"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export default [
|
|||||||
"dist/*",
|
"dist/*",
|
||||||
"docs/*",
|
"docs/*",
|
||||||
"demo/*",
|
"demo/*",
|
||||||
"libraries/*",
|
|
||||||
// TriliumNextTODO: check if we want to format packages here as well - for now skipping it
|
// TriliumNextTODO: check if we want to format packages here as well - for now skipping it
|
||||||
"packages/*",
|
"packages/*",
|
||||||
"src/public/app-dist/*",
|
"src/public/app-dist/*",
|
||||||
|
|||||||
@@ -35,13 +35,13 @@
|
|||||||
"chore:generate-openapi": "tsx bin/generate-openapi.js"
|
"chore:generate-openapi": "tsx bin/generate-openapi.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.53.0",
|
"@playwright/test": "1.53.1",
|
||||||
"@stylistic/eslint-plugin": "4.4.1",
|
"@stylistic/eslint-plugin": "5.0.0",
|
||||||
"@types/express": "5.0.3",
|
"@types/express": "5.0.3",
|
||||||
"@types/node": "22.15.31",
|
"@types/node": "22.15.32",
|
||||||
"@types/yargs": "17.0.33",
|
"@types/yargs": "17.0.33",
|
||||||
"@vitest/coverage-v8": "3.2.3",
|
"@vitest/coverage-v8": "3.2.4",
|
||||||
"eslint": "9.28.0",
|
"eslint": "9.29.0",
|
||||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
"jsdoc": "4.0.4",
|
"jsdoc": "4.0.4",
|
||||||
|
|||||||
4
apps/client/.env
Normal file
4
apps/client/.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# The development license key for premium CKEditor features.
|
||||||
|
# Note: This key must only be used for the Trilium Notes project.
|
||||||
|
# Expires on: 2025-09-13
|
||||||
|
VITE_CKEDITOR_KEY=eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3NTc3MjE1OTksImp0aSI6ImFiN2E0NjZmLWJlZGMtNDNiYy1iMzU4LTk0NGQ0YWJhY2I3ZiIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsic2giLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkNNVCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJTRUUiLCJFQ0giLCJFSVMiXSwidmMiOiI1MzlkOWY5YyJ9.2rvKPql4hmukyXhEtWPZ8MLxKvzPIwzCdykO653g7IxRRZy2QJpeRszElZx9DakKYZKXekVRAwQKgHxwkgbE_w
|
||||||
1
apps/client/.env.production
Normal file
1
apps/client/.env.production
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VITE_CKEDITOR_ENABLE_INSPECTOR=false
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@triliumnext/client",
|
"name": "@triliumnext/client",
|
||||||
"version": "0.94.1",
|
"version": "0.95.0",
|
||||||
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "TriliumNext Notes Team",
|
"name": "Trilium Notes Team",
|
||||||
"email": "contact@eliandoran.me",
|
"email": "contact@eliandoran.me",
|
||||||
"url": "https://github.com/TriliumNext/Notes"
|
"url": "https://github.com/TriliumNext/Notes"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/js": "9.28.0",
|
"@eslint/js": "9.29.0",
|
||||||
"@excalidraw/excalidraw": "0.18.0",
|
"@excalidraw/excalidraw": "0.18.0",
|
||||||
"@fullcalendar/core": "6.1.17",
|
"@fullcalendar/core": "6.1.17",
|
||||||
"@fullcalendar/daygrid": "6.1.17",
|
"@fullcalendar/daygrid": "6.1.17",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"@fullcalendar/list": "6.1.17",
|
"@fullcalendar/list": "6.1.17",
|
||||||
"@fullcalendar/multimonth": "6.1.17",
|
"@fullcalendar/multimonth": "6.1.17",
|
||||||
"@fullcalendar/timegrid": "6.1.17",
|
"@fullcalendar/timegrid": "6.1.17",
|
||||||
"@mermaid-js/layout-elk": "0.1.7",
|
"@mermaid-js/layout-elk": "0.1.8",
|
||||||
"@mind-elixir/node-menu": "1.0.5",
|
"@mind-elixir/node-menu": "1.0.5",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
"@triliumnext/ckeditor5": "workspace:*",
|
"@triliumnext/ckeditor5": "workspace:*",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"@triliumnext/highlightjs": "workspace:*",
|
"@triliumnext/highlightjs": "workspace:*",
|
||||||
"@triliumnext/share-theme": "workspace:*",
|
"@triliumnext/share-theme": "workspace:*",
|
||||||
"autocomplete.js": "0.38.1",
|
"autocomplete.js": "0.38.1",
|
||||||
"bootstrap": "5.3.6",
|
"bootstrap": "5.3.7",
|
||||||
"boxicons": "2.1.4",
|
"boxicons": "2.1.4",
|
||||||
"dayjs": "1.11.13",
|
"dayjs": "1.11.13",
|
||||||
"dayjs-plugin-utc": "0.1.2",
|
"dayjs-plugin-utc": "0.1.2",
|
||||||
@@ -47,8 +47,8 @@
|
|||||||
"leaflet-gpx": "2.2.0",
|
"leaflet-gpx": "2.2.0",
|
||||||
"mark.js": "8.11.1",
|
"mark.js": "8.11.1",
|
||||||
"marked": "15.0.12",
|
"marked": "15.0.12",
|
||||||
"mermaid": "11.6.0",
|
"mermaid": "11.7.0",
|
||||||
"mind-elixir": "4.6.0",
|
"mind-elixir": "4.6.1",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"panzoom": "9.4.3",
|
"panzoom": "9.4.3",
|
||||||
"preact": "10.26.9",
|
"preact": "10.26.9",
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"copy-webpack-plugin": "13.0.0",
|
"copy-webpack-plugin": "13.0.0",
|
||||||
"happy-dom": "18.0.1",
|
"happy-dom": "18.0.1",
|
||||||
"script-loader": "0.7.2",
|
"script-loader": "0.7.2",
|
||||||
"vite-plugin-static-copy": "3.0.0"
|
"vite-plugin-static-copy": "3.0.2"
|
||||||
},
|
},
|
||||||
"nx": {
|
"nx": {
|
||||||
"name": "client",
|
"name": "client",
|
||||||
@@ -75,6 +75,9 @@
|
|||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"^build"
|
"^build"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"circular-deps": {
|
||||||
|
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,424 +0,0 @@
|
|||||||
/*
|
|
||||||
* Remove template code below
|
|
||||||
*/
|
|
||||||
html {
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
||||||
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
|
||||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
|
||||||
'Noto Color Emoji';
|
|
||||||
line-height: 1.5;
|
|
||||||
tab-size: 4;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
p,
|
|
||||||
pre {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
*,
|
|
||||||
::before,
|
|
||||||
::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-width: 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: currentColor;
|
|
||||||
}
|
|
||||||
h1,
|
|
||||||
h2 {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
||||||
'Liberation Mono', 'Courier New', monospace;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
shape-rendering: auto;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
background-color: rgba(55, 65, 81, 1);
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
color: rgba(229, 231, 235, 1);
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
||||||
'Liberation Mono', 'Courier New', monospace;
|
|
||||||
overflow: scroll;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow {
|
|
||||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
||||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
.rounded {
|
|
||||||
border-radius: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
max-width: 768px;
|
|
||||||
padding-bottom: 3rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
color: rgba(55, 65, 81, 1);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#welcome {
|
|
||||||
margin-top: 2.5rem;
|
|
||||||
}
|
|
||||||
#welcome h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
#welcome span {
|
|
||||||
display: block;
|
|
||||||
font-size: 1.875rem;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 2.25rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
#hero {
|
|
||||||
align-items: center;
|
|
||||||
background-color: hsla(214, 62%, 21%, 1);
|
|
||||||
border: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: rgba(55, 65, 81, 1);
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
margin-top: 3.5rem;
|
|
||||||
}
|
|
||||||
#hero .text-container {
|
|
||||||
color: rgba(255, 255, 255, 1);
|
|
||||||
padding: 3rem 2rem;
|
|
||||||
}
|
|
||||||
#hero .text-container h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
#hero .text-container h2 svg {
|
|
||||||
color: hsla(162, 47%, 50%, 1);
|
|
||||||
height: 2rem;
|
|
||||||
left: -0.25rem;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 2rem;
|
|
||||||
}
|
|
||||||
#hero .text-container h2 span {
|
|
||||||
margin-left: 2.5rem;
|
|
||||||
}
|
|
||||||
#hero .text-container a {
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
color: rgba(55, 65, 81, 1);
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
#hero .logo-container {
|
|
||||||
display: none;
|
|
||||||
justify-content: center;
|
|
||||||
padding-left: 2rem;
|
|
||||||
padding-right: 2rem;
|
|
||||||
}
|
|
||||||
#hero .logo-container svg {
|
|
||||||
color: rgba(255, 255, 255, 1);
|
|
||||||
width: 66.666667%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#middle-content {
|
|
||||||
align-items: flex-start;
|
|
||||||
display: grid;
|
|
||||||
gap: 4rem;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
margin-top: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#learning-materials {
|
|
||||||
padding: 2.5rem 2rem;
|
|
||||||
}
|
|
||||||
#learning-materials h2 {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
line-height: 1.75rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
.list-item-link {
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
display: flex;
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.list-item-link svg:first-child {
|
|
||||||
margin-right: 1rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
width: 1.5rem;
|
|
||||||
}
|
|
||||||
.list-item-link > span {
|
|
||||||
flex-grow: 1;
|
|
||||||
font-weight: 400;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
}
|
|
||||||
.list-item-link > span > span {
|
|
||||||
color: rgba(107, 114, 128, 1);
|
|
||||||
display: block;
|
|
||||||
flex-grow: 1;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 1rem;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
}
|
|
||||||
.list-item-link svg:last-child {
|
|
||||||
height: 1rem;
|
|
||||||
transition-property: all;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
width: 1rem;
|
|
||||||
}
|
|
||||||
.list-item-link:hover {
|
|
||||||
color: rgba(255, 255, 255, 1);
|
|
||||||
background-color: hsla(162, 47%, 50%, 1);
|
|
||||||
}
|
|
||||||
.list-item-link:hover > span {
|
|
||||||
}
|
|
||||||
.list-item-link:hover > span > span {
|
|
||||||
color: rgba(243, 244, 246, 1);
|
|
||||||
}
|
|
||||||
.list-item-link:hover svg:last-child {
|
|
||||||
transform: translateX(0.25rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
#other-links {
|
|
||||||
}
|
|
||||||
.button-pill {
|
|
||||||
padding: 1.5rem 2rem;
|
|
||||||
transition-duration: 300ms;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.button-pill svg {
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 3rem;
|
|
||||||
}
|
|
||||||
.button-pill > span {
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 1.125rem;
|
|
||||||
line-height: 1.75rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
.button-pill span span {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
|
||||||
.button-pill:hover svg,
|
|
||||||
.button-pill:hover {
|
|
||||||
color: rgba(255, 255, 255, 1) !important;
|
|
||||||
}
|
|
||||||
#nx-console:hover {
|
|
||||||
background-color: rgba(0, 122, 204, 1);
|
|
||||||
}
|
|
||||||
#nx-console svg {
|
|
||||||
color: rgba(0, 122, 204, 1);
|
|
||||||
}
|
|
||||||
#nx-console-jetbrains {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
#nx-console-jetbrains:hover {
|
|
||||||
background-color: rgba(255, 49, 140, 1);
|
|
||||||
}
|
|
||||||
#nx-console-jetbrains svg {
|
|
||||||
color: rgba(255, 49, 140, 1);
|
|
||||||
}
|
|
||||||
#nx-repo:hover {
|
|
||||||
background-color: rgba(24, 23, 23, 1);
|
|
||||||
}
|
|
||||||
#nx-repo svg {
|
|
||||||
color: rgba(24, 23, 23, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#nx-cloud {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
margin-top: 2rem;
|
|
||||||
padding: 2.5rem 2rem;
|
|
||||||
}
|
|
||||||
#nx-cloud > div {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
#nx-cloud > div svg {
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 3rem;
|
|
||||||
}
|
|
||||||
#nx-cloud > div h2 {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
line-height: 1.75rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
#nx-cloud > div h2 span {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
}
|
|
||||||
#nx-cloud p {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
#nx-cloud pre {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
#nx-cloud a {
|
|
||||||
color: rgba(107, 114, 128, 1);
|
|
||||||
display: block;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
#nx-cloud a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
#commands {
|
|
||||||
padding: 2.5rem 2rem;
|
|
||||||
|
|
||||||
margin-top: 3.5rem;
|
|
||||||
}
|
|
||||||
#commands h2 {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: -0.025em;
|
|
||||||
line-height: 1.75rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
#commands p {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 1.5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
details {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding-left: 1rem;
|
|
||||||
padding-right: 1rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
details pre > span {
|
|
||||||
color: rgba(181, 181, 181, 1);
|
|
||||||
}
|
|
||||||
summary {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
display: flex;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 0.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition-property: background-color, border-color, color, fill, stroke,
|
|
||||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
|
||||||
-webkit-backdrop-filter;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-duration: 150ms;
|
|
||||||
}
|
|
||||||
summary:hover {
|
|
||||||
background-color: rgba(243, 244, 246, 1);
|
|
||||||
}
|
|
||||||
summary svg {
|
|
||||||
height: 1.5rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
width: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#love {
|
|
||||||
color: rgba(107, 114, 128, 1);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
margin-top: 3.5rem;
|
|
||||||
opacity: 0.6;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#love svg {
|
|
||||||
color: rgba(252, 165, 165, 1);
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
display: inline;
|
|
||||||
margin-top: -0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
|
||||||
#hero {
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
#hero .logo-container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
#middle-content {
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { AppElement } from './app.element';
|
|
||||||
|
|
||||||
describe('AppElement', () => {
|
|
||||||
let app: AppElement;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
app = new AppElement();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create successfully', () => {
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have a greeting', () => {
|
|
||||||
app.connectedCallback();
|
|
||||||
|
|
||||||
expect(app.querySelector('h1').innerHTML).toContain(
|
|
||||||
'Welcome @triliumnext/client'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
import './app.element.css';
|
|
||||||
|
|
||||||
export class AppElement extends HTMLElement {
|
|
||||||
public static observedAttributes = [
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
const title = '@triliumnext/client';
|
|
||||||
this.innerHTML = `
|
|
||||||
<div class="wrapper">
|
|
||||||
<div class="container">
|
|
||||||
<!-- WELCOME -->
|
|
||||||
<div id="welcome">
|
|
||||||
<h1>
|
|
||||||
<span> Hello there, </span>
|
|
||||||
Welcome ${title} 👋
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
|
||||||
<div id="hero" class="rounded">
|
|
||||||
<div class="text-container">
|
|
||||||
<h2>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>You're up and running</span>
|
|
||||||
</h2>
|
|
||||||
<a href="#commands"> What's next? </a>
|
|
||||||
</div>
|
|
||||||
<div class="logo-container">
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
role="img"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- MIDDLE CONTENT -->
|
|
||||||
<div id="middle-content">
|
|
||||||
<div id="learning-materials" class="rounded shadow">
|
|
||||||
<h2>Learning materials</h2>
|
|
||||||
<a href="https://nx.dev/getting-started/intro?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Documentation
|
|
||||||
<span> Everything is in there </span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="https://nx.dev/blog/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Blog
|
|
||||||
<span> Changelog, features & events </span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="https://www.youtube.com/@NxDevtools/videos?utm_source=nx-project&sub_confirmation=1" target="_blank" rel="noreferrer" class="list-item-link">
|
|
||||||
<svg
|
|
||||||
role="img"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>YouTube</title>
|
|
||||||
<path
|
|
||||||
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
YouTube channel
|
|
||||||
<span> Nx Show, talks & tutorials </span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="https://nx.dev/react-tutorial/1-code-generation?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Interactive tutorials
|
|
||||||
<span> Create an app, step-by-step </span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href="https://nxplaybook.com/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path d="M12 14l9-5-9-5-9 5 9 5z" />
|
|
||||||
<path
|
|
||||||
d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Video courses
|
|
||||||
<span> Nx custom courses </span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div id="other-links">
|
|
||||||
<a id="nx-console" class="button-pill rounded shadow" href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&utm_source=nx-project" target="_blank" rel="noreferrer">
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
role="img"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>Visual Studio Code</title>
|
|
||||||
<path
|
|
||||||
d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Install Nx Console for VSCode
|
|
||||||
<span>The official VSCode extension for Nx.</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
id="nx-console-jetbrains"
|
|
||||||
class="button-pill rounded shadow"
|
|
||||||
href="https://plugins.jetbrains.com/plugin/21060-nx-console"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
height="48"
|
|
||||||
width="48"
|
|
||||||
viewBox="20 20 60 60"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path d="m22.5 22.5h60v60h-60z" />
|
|
||||||
<g fill="#fff">
|
|
||||||
<path d="m29.03 71.25h22.5v3.75h-22.5z" />
|
|
||||||
<path d="m28.09 38 1.67-1.58a1.88 1.88 0 0 0 1.47.87c.64 0 1.06-.44 1.06-1.31v-5.98h2.58v6a3.48 3.48 0 0 1 -.87 2.6 3.56 3.56 0 0 1 -2.57.95 3.84 3.84 0 0 1 -3.34-1.55z" />
|
|
||||||
<path d="m36 30h7.53v2.19h-5v1.44h4.49v2h-4.42v1.49h5v2.21h-7.6z" />
|
|
||||||
<path d="m47.23 32.29h-2.8v-2.29h8.21v2.27h-2.81v7.1h-2.6z" />
|
|
||||||
<path d="m29.13 43.08h4.42a3.53 3.53 0 0 1 2.55.83 2.09 2.09 0 0 1 .6 1.53 2.16 2.16 0 0 1 -1.44 2.09 2.27 2.27 0 0 1 1.86 2.29c0 1.61-1.31 2.59-3.55 2.59h-4.44zm5 2.89c0-.52-.42-.8-1.18-.8h-1.29v1.64h1.24c.79 0 1.25-.26 1.25-.81zm-.9 2.66h-1.57v1.73h1.62c.8 0 1.24-.31 1.24-.86 0-.5-.4-.87-1.27-.87z" />
|
|
||||||
<path d="m38 43.08h4.1a4.19 4.19 0 0 1 3 1 2.93 2.93 0 0 1 .9 2.19 3 3 0 0 1 -1.93 2.89l2.24 3.27h-3l-1.88-2.84h-.87v2.84h-2.56zm4 4.5c.87 0 1.39-.43 1.39-1.11 0-.75-.54-1.12-1.4-1.12h-1.44v2.26z" />
|
|
||||||
<path d="m49.59 43h2.5l4 9.44h-2.79l-.67-1.69h-3.63l-.67 1.69h-2.71zm2.27 5.73-1-2.65-1.06 2.65z" />
|
|
||||||
<path d="m56.46 43.05h2.6v9.37h-2.6z" />
|
|
||||||
<path d="m60.06 43.05h2.42l3.37 5v-5h2.57v9.37h-2.26l-3.53-5.14v5.14h-2.57z" />
|
|
||||||
<path d="m68.86 51 1.45-1.73a4.84 4.84 0 0 0 3 1.13c.71 0 1.08-.24 1.08-.65 0-.4-.31-.6-1.59-.91-2-.46-3.53-1-3.53-2.93 0-1.74 1.37-3 3.62-3a5.89 5.89 0 0 1 3.86 1.25l-1.26 1.84a4.63 4.63 0 0 0 -2.62-.92c-.63 0-.94.25-.94.6 0 .42.32.61 1.63.91 2.14.46 3.44 1.16 3.44 2.91 0 1.91-1.51 3-3.79 3a6.58 6.58 0 0 1 -4.35-1.5z" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Install Nx Console for JetBrains
|
|
||||||
<span>
|
|
||||||
Available for WebStorm, Intellij IDEA Ultimate and more!
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<div id="nx-cloud" class="rounded shadow">
|
|
||||||
<div>
|
|
||||||
<svg id="nx-cloud-logo" role="img" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" fill="transparent" viewBox="0 0 24 24">
|
|
||||||
<path stroke-width="2" d="M23 3.75V6.5c-3.036 0-5.5 2.464-5.5 5.5s-2.464 5.5-5.5 5.5-5.5 2.464-5.5 5.5H3.75C2.232 23 1 21.768 1 20.25V3.75C1 2.232 2.232 1 3.75 1h16.5C21.768 1 23 2.232 23 3.75Z" />
|
|
||||||
<path stroke-width="2" d="M23 6v14.1667C23 21.7307 21.7307 23 20.1667 23H6c0-3.128 2.53867-5.6667 5.6667-5.6667 3.128 0 5.6666-2.5386 5.6666-5.6666C17.3333 8.53867 19.872 6 23 6Z" />
|
|
||||||
</svg>
|
|
||||||
<h2>
|
|
||||||
Nx Cloud
|
|
||||||
<span>
|
|
||||||
Enable faster CI & better DX
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
You can activate distributed tasks executions and caching by
|
|
||||||
running:
|
|
||||||
</p>
|
|
||||||
<pre>nx connect</pre>
|
|
||||||
<a href="https://nx.app/?utm_source=nx-project" target="_blank" rel="noreferrer"> What is Nx Cloud? </a>
|
|
||||||
</div>
|
|
||||||
<a id="nx-repo" class="button-pill rounded shadow" href="https://github.com/nrwl/nx?utm_source=nx-project" target="_blank" rel="noreferrer">
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
role="img"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
Nx is open source
|
|
||||||
<span> Love Nx? Give us a star! </span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- COMMANDS -->
|
|
||||||
<div id="commands" class="rounded shadow">
|
|
||||||
<h2>Next steps</h2>
|
|
||||||
<p>Here are some things you can do with Nx:</p>
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Add UI library
|
|
||||||
</summary>
|
|
||||||
<pre><span># Generate UI lib</span>
|
|
||||||
nx g @nx/angular:lib ui
|
|
||||||
|
|
||||||
<span># Add a component</span>
|
|
||||||
nx g @nx/angular:component ui/src/lib/button</pre>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
View interactive project graph
|
|
||||||
</summary>
|
|
||||||
<pre>nx graph</pre>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
<svg
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Run affected commands
|
|
||||||
</summary>
|
|
||||||
<pre><span># see what's been affected by changes</span>
|
|
||||||
nx affected:graph
|
|
||||||
|
|
||||||
<span># run tests for current changes</span>
|
|
||||||
nx affected:test
|
|
||||||
|
|
||||||
<span># run e2e tests for current changes</span>
|
|
||||||
nx affected:e2e</pre>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p id="love">
|
|
||||||
Carefully crafted with
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
stroke="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define('triliumnext-root', AppElement);
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,14 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Client</title>
|
|
||||||
<base href="/" />
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<triliumnext-root></triliumnext-root>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
import './app/app.element';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
import bundleService from "../services/bundle.js";
|
|
||||||
import RootCommandExecutor from "./root_command_executor.js";
|
import RootCommandExecutor from "./root_command_executor.js";
|
||||||
import Entrypoints, { type SqlExecuteResults } from "./entrypoints.js";
|
import Entrypoints, { type SqlExecuteResults } from "./entrypoints.js";
|
||||||
import options from "../services/options.js";
|
import options from "../services/options.js";
|
||||||
@@ -281,6 +280,7 @@ export type CommandMappings = {
|
|||||||
buildIcon(name: string): NativeImage;
|
buildIcon(name: string): NativeImage;
|
||||||
};
|
};
|
||||||
refreshTouchBar: CommandData;
|
refreshTouchBar: CommandData;
|
||||||
|
reloadTextEditor: CommandData;
|
||||||
};
|
};
|
||||||
|
|
||||||
type EventMappings = {
|
type EventMappings = {
|
||||||
@@ -469,6 +469,7 @@ export class AppContext extends Component {
|
|||||||
|
|
||||||
this.tabManager.loadTabs();
|
this.tabManager.loadTabs();
|
||||||
|
|
||||||
|
const bundleService = (await import("../services/bundle.js")).default;
|
||||||
setTimeout(() => bundleService.executeStartupBundles(), 2000);
|
setTimeout(() => bundleService.executeStartupBundles(), 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type FNote from "../entities/fnote.js";
|
|||||||
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
||||||
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
||||||
import type CodeMirror from "@triliumnext/codemirror";
|
import type CodeMirror from "@triliumnext/codemirror";
|
||||||
|
import { closeActiveDialog } from "../services/dialog.js";
|
||||||
|
|
||||||
export interface SetNoteOpts {
|
export interface SetNoteOpts {
|
||||||
triggerSwitchEvent?: unknown;
|
triggerSwitchEvent?: unknown;
|
||||||
@@ -83,7 +84,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
|
|||||||
|
|
||||||
await this.triggerEvent("beforeNoteSwitch", { noteContext: this });
|
await this.triggerEvent("beforeNoteSwitch", { noteContext: this });
|
||||||
|
|
||||||
utils.closeActiveDialog();
|
closeActiveDialog();
|
||||||
|
|
||||||
this.notePath = resolvedNotePath;
|
this.notePath = resolvedNotePath;
|
||||||
this.viewScope = opts.viewScope;
|
this.viewScope = opts.viewScope;
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ export default class TabManager extends Component {
|
|||||||
const titleFragments = [
|
const titleFragments = [
|
||||||
// it helps to navigate in history if note title is included in the title
|
// it helps to navigate in history if note title is included in the title
|
||||||
await activeNoteContext.getNavigationTitle(),
|
await activeNoteContext.getNavigationTitle(),
|
||||||
"TriliumNext Notes"
|
"Trilium Notes"
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
document.title = titleFragments.join(" - ");
|
document.title = titleFragments.join(" - ");
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import server from "../services/server.js";
|
import server from "../services/server.js";
|
||||||
import noteAttributeCache from "../services/note_attribute_cache.js";
|
import noteAttributeCache from "../services/note_attribute_cache.js";
|
||||||
import ws from "../services/ws.js";
|
import ws from "../services/ws.js";
|
||||||
import froca from "../services/froca.js";
|
|
||||||
import protectedSessionHolder from "../services/protected_session_holder.js";
|
import protectedSessionHolder from "../services/protected_session_holder.js";
|
||||||
import cssClassManager from "../services/css_class_manager.js";
|
import cssClassManager from "../services/css_class_manager.js";
|
||||||
import type { Froca } from "../services/froca-interface.js";
|
import type { Froca } from "../services/froca-interface.js";
|
||||||
@@ -410,8 +409,8 @@ class FNote {
|
|||||||
const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({
|
const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({
|
||||||
notePath: path,
|
notePath: path,
|
||||||
isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
|
isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
|
||||||
isArchived: path.some((noteId) => froca.notes[noteId].isArchived),
|
isArchived: path.some((noteId) => this.froca.notes[noteId].isArchived),
|
||||||
isSearch: path.some((noteId) => froca.notes[noteId].type === "search"),
|
isSearch: path.some((noteId) => this.froca.notes[noteId].type === "search"),
|
||||||
isHidden: path.includes("_hidden")
|
isHidden: path.includes("_hidden")
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -982,7 +981,7 @@ class FNote {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentNote = froca.notes[parentNoteId];
|
const parentNote = this.froca.notes[parentNoteId];
|
||||||
|
|
||||||
if (!parentNote || parentNote.type === "search") {
|
if (!parentNote || parentNote.type === "search") {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
// Source: https://github.com/codemirror/codemirror5/pull/7080/files
|
|
||||||
|
|
||||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
||||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
||||||
|
|
||||||
(function (mod) {
|
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
||||||
mod(require("../../lib/codemirror"));
|
|
||||||
else if (typeof define == "function" && define.amd) // AMD
|
|
||||||
define(["../../lib/codemirror"], mod);
|
|
||||||
else // Plain browser env
|
|
||||||
mod(CodeMirror);
|
|
||||||
})(function (CodeMirror) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
CodeMirror.defineMode("hcl", function (config) {
|
|
||||||
var indentUnit = config.indentUnit;
|
|
||||||
|
|
||||||
var keywords = {
|
|
||||||
"resource": true,
|
|
||||||
"variable": true,
|
|
||||||
"output": true,
|
|
||||||
"module": true,
|
|
||||||
"provider": true,
|
|
||||||
"data": true,
|
|
||||||
"locals": true,
|
|
||||||
"terraform": true,
|
|
||||||
"if": true,
|
|
||||||
"else": true,
|
|
||||||
"for": true,
|
|
||||||
"foreach": true,
|
|
||||||
"in": true,
|
|
||||||
"true": true,
|
|
||||||
"false": true,
|
|
||||||
"null": true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var atoms = {
|
|
||||||
"true": true,
|
|
||||||
"false": true,
|
|
||||||
"null": true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
|
|
||||||
|
|
||||||
var curPunc;
|
|
||||||
|
|
||||||
function tokenBase(stream, state) {
|
|
||||||
var ch = stream.next();
|
|
||||||
if (ch == '"' || ch == "'" || ch == "`") {
|
|
||||||
state.tokenize = tokenString(ch);
|
|
||||||
return state.tokenize(stream, state);
|
|
||||||
}
|
|
||||||
if (/[\d\.]/.test(ch)) {
|
|
||||||
if (ch == ".") {
|
|
||||||
stream.match(/^[0-9_]+([eE][\-+]?[0-9_]+)?/);
|
|
||||||
} else {
|
|
||||||
stream.match(/^[0-9_]*\.?[0-9_]*([eE][\-+]?[0-9_]+)?/);
|
|
||||||
}
|
|
||||||
return "number";
|
|
||||||
}
|
|
||||||
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
|
||||||
curPunc = ch;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (ch == "/") {
|
|
||||||
if (stream.eat("*")) {
|
|
||||||
state.tokenize = tokenComment;
|
|
||||||
return tokenComment(stream, state);
|
|
||||||
}
|
|
||||||
if (stream.eat("/")) {
|
|
||||||
stream.skipToEnd();
|
|
||||||
return "comment";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isOperatorChar.test(ch)) {
|
|
||||||
stream.eatWhile(isOperatorChar);
|
|
||||||
return "operator";
|
|
||||||
}
|
|
||||||
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
|
|
||||||
var cur = stream.current();
|
|
||||||
if (keywords.propertyIsEnumerable(cur)) {
|
|
||||||
return "keyword";
|
|
||||||
}
|
|
||||||
if (atoms.propertyIsEnumerable(cur)) return "atom";
|
|
||||||
return "variable";
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenString(quote) {
|
|
||||||
return function (stream, state) {
|
|
||||||
var escaped = false,
|
|
||||||
next,
|
|
||||||
end = false;
|
|
||||||
while ((next = stream.next()) != null) {
|
|
||||||
if (next == quote && !escaped) {
|
|
||||||
end = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
escaped = !escaped && quote != "`" && next == "\\";
|
|
||||||
}
|
|
||||||
if (end || !(escaped || quote == "`"))
|
|
||||||
state.tokenize = tokenBase;
|
|
||||||
return "string";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenComment(stream, state) {
|
|
||||||
var maybeEnd = false,
|
|
||||||
ch;
|
|
||||||
while (ch = stream.next()) {
|
|
||||||
if (ch == "/" && maybeEnd) {
|
|
||||||
state.tokenize = tokenBase;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
maybeEnd = (ch == "*");
|
|
||||||
}
|
|
||||||
return "comment";
|
|
||||||
}
|
|
||||||
|
|
||||||
function Context(indented, column, type, align, prev) {
|
|
||||||
this.indented = indented;
|
|
||||||
this.column = column;
|
|
||||||
this.type = type;
|
|
||||||
this.align = align;
|
|
||||||
this.prev = prev;
|
|
||||||
}
|
|
||||||
function pushContext(state, col, type) {
|
|
||||||
return state.context = new Context(state.indented, col, type, null, state.context);
|
|
||||||
}
|
|
||||||
function popContext(state) {
|
|
||||||
if (!state.context.prev) return;
|
|
||||||
var t = state.context.type;
|
|
||||||
if (t == ")" || t == "]" || t == "}")
|
|
||||||
state.indented = state.context.indented;
|
|
||||||
return state.context = state.context.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface
|
|
||||||
|
|
||||||
return {
|
|
||||||
startState: function (basecolumn) {
|
|
||||||
return {
|
|
||||||
tokenize: null,
|
|
||||||
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
|
|
||||||
indented: 0,
|
|
||||||
startOfLine: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
token: function (stream, state) {
|
|
||||||
var ctx = state.context;
|
|
||||||
if (stream.sol()) {
|
|
||||||
if (ctx.align == null) ctx.align = false;
|
|
||||||
state.indented = stream.indentation();
|
|
||||||
state.startOfLine = true;
|
|
||||||
}
|
|
||||||
if (stream.eatSpace()) return null;
|
|
||||||
curPunc = null;
|
|
||||||
var style = (state.tokenize || tokenBase)(stream, state);
|
|
||||||
if (style == "comment") return style;
|
|
||||||
if (ctx.align == null) ctx.align = true;
|
|
||||||
|
|
||||||
if (curPunc == "{") pushContext(state, stream.column(), "}");
|
|
||||||
else if (curPunc == "[") pushContext(state, stream.column(), "]");
|
|
||||||
else if (curPunc == "(") pushContext(state, stream.column(), ")");
|
|
||||||
else if (curPunc == "}" && ctx.type == "}") popContext(state);
|
|
||||||
else if (curPunc == ctx.type) popContext(state);
|
|
||||||
state.startOfLine = false;
|
|
||||||
return style;
|
|
||||||
},
|
|
||||||
|
|
||||||
indent: function (state, textAfter) {
|
|
||||||
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
|
|
||||||
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
|
|
||||||
if (firstChar == "#" || firstChar == ";") return 0;
|
|
||||||
if (stream.sol()) {
|
|
||||||
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
|
|
||||||
state.context.type = "}";
|
|
||||||
return ctx.indented;
|
|
||||||
}
|
|
||||||
var closing = firstChar == ctx.type;
|
|
||||||
if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
|
||||||
else return ctx.indented + (closing ? 0 : indentUnit);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
electricChars: "{}):",
|
|
||||||
closeBrackets: "()[]{}''\"\"``",
|
|
||||||
fold: "brace",
|
|
||||||
blockCommentStart: "/*",
|
|
||||||
blockCommentEnd: "*/",
|
|
||||||
lineComment: "//"
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
CodeMirror.defineMIME("text/x-hcl", "hcl");
|
|
||||||
CodeMirror.modeInfo.push({
|
|
||||||
ext: [ "hcl " ],
|
|
||||||
mime: "text/x-hcl",
|
|
||||||
mode: "hcl",
|
|
||||||
name: "Terraform (HCL)"
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -194,14 +194,15 @@ class ContextMenu {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.isMobile) {
|
$item.on("mouseup", (e) => {
|
||||||
$item.on("mouseup", (e) =>{
|
// Prevent submenu from failing to expand on mobile
|
||||||
|
if (!this.isMobile || !("items" in item && item.items)) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below.
|
// Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below.
|
||||||
this.hide();
|
this.hide();
|
||||||
return false;
|
return false;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ("enabled" in item && item.enabled !== undefined && !item.enabled) {
|
if ("enabled" in item && item.enabled !== undefined && !item.enabled) {
|
||||||
$item.addClass("disabled");
|
$item.addClass("disabled");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ScriptContext from "./script_context.js";
|
import ScriptContext from "./script_context.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import toastService from "./toast.js";
|
import toastService, { showError } from "./toast.js";
|
||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
@@ -37,7 +37,9 @@ async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $cont
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
const note = await froca.getNote(bundle.noteId);
|
const note = await froca.getNote(bundle.noteId);
|
||||||
|
|
||||||
toastService.showAndLogError(`Execution of JS note "${note?.title}" with ID ${bundle.noteId} failed with error: ${e?.message}`);
|
const message = `Execution of JS note "${note?.title}" with ID ${bundle.noteId} failed with error: ${e?.message}`;
|
||||||
|
showError(message);
|
||||||
|
logError(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import froca from "./froca.js";
|
|||||||
import linkService from "./link.js";
|
import linkService from "./link.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import toast from "./toast.js";
|
import { throwError } from "./ws.js";
|
||||||
|
|
||||||
let clipboardBranchIds: string[] = [];
|
let clipboardBranchIds: string[] = [];
|
||||||
let clipboardMode: string | null = null;
|
let clipboardMode: string | null = null;
|
||||||
@@ -37,7 +37,7 @@ async function pasteAfter(afterBranchId: string) {
|
|||||||
|
|
||||||
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
|
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
|
||||||
} else {
|
} else {
|
||||||
toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`);
|
throwError(`Unrecognized clipboard mode=${clipboardMode}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ async function pasteInto(parentBranchId: string) {
|
|||||||
|
|
||||||
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
|
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
|
||||||
} else {
|
} else {
|
||||||
toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`);
|
throwError(`Unrecognized clipboard mode=${clipboardMode}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,41 @@
|
|||||||
|
import { Modal } from "bootstrap";
|
||||||
import appContext from "../components/app_context.js";
|
import appContext from "../components/app_context.js";
|
||||||
import type { ConfirmDialogOptions, ConfirmDialogResult, ConfirmWithMessageOptions } from "../widgets/dialogs/confirm.js";
|
import type { ConfirmDialogOptions, ConfirmDialogResult, ConfirmWithMessageOptions } from "../widgets/dialogs/confirm.js";
|
||||||
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
|
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
|
||||||
|
import { focusSavedElement, saveFocusedElement } from "./focus.js";
|
||||||
|
|
||||||
|
export async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
|
||||||
|
if (closeActDialog) {
|
||||||
|
closeActiveDialog();
|
||||||
|
glob.activeDialog = $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFocusedElement();
|
||||||
|
Modal.getOrCreateInstance($dialog[0]).show();
|
||||||
|
|
||||||
|
$dialog.on("hidden.bs.modal", () => {
|
||||||
|
const $autocompleteEl = $(".aa-input");
|
||||||
|
if ("autocomplete" in $autocompleteEl) {
|
||||||
|
$autocompleteEl.autocomplete("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!glob.activeDialog || glob.activeDialog === $dialog) {
|
||||||
|
focusSavedElement();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
|
||||||
|
keyboardActionsService.updateDisplayedShortcuts($dialog);
|
||||||
|
|
||||||
|
return $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeActiveDialog() {
|
||||||
|
if (glob.activeDialog) {
|
||||||
|
Modal.getOrCreateInstance(glob.activeDialog[0]).hide();
|
||||||
|
glob.activeDialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function info(message: string) {
|
async function info(message: string) {
|
||||||
return new Promise((res) => appContext.triggerCommand("showInfoDialog", { message, callback: res }));
|
return new Promise((res) => appContext.triggerCommand("showInfoDialog", { message, callback: res }));
|
||||||
|
|||||||
29
apps/client/src/services/focus.ts
Normal file
29
apps/client/src/services/focus.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
let $lastFocusedElement: JQuery<HTMLElement> | null;
|
||||||
|
|
||||||
|
// perhaps there should be saved focused element per tab?
|
||||||
|
export function saveFocusedElement() {
|
||||||
|
$lastFocusedElement = $(":focus");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function focusSavedElement() {
|
||||||
|
if (!$lastFocusedElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lastFocusedElement.hasClass("ck")) {
|
||||||
|
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
|
||||||
|
// the bug manifests itself in resetting the cursor position to the first character - jumping above
|
||||||
|
|
||||||
|
const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance");
|
||||||
|
|
||||||
|
if (editor) {
|
||||||
|
editor.editing.view.focus();
|
||||||
|
} else {
|
||||||
|
console.log("Could not find CKEditor instance to focus last element");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$lastFocusedElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastFocusedElement = null;
|
||||||
|
}
|
||||||
@@ -245,6 +245,10 @@ class FrocaImpl implements Froca {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
|
async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
|
||||||
|
if (noteIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
noteIds = Array.from(new Set(noteIds)); // make unique
|
noteIds = Array.from(new Set(noteIds)); // make unique
|
||||||
const missingNoteIds = noteIds.filter((noteId) => !this.notes[noteId]);
|
const missingNoteIds = noteIds.filter((noteId) => !this.notes[noteId]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { LOCALES } from "@triliumnext/commons";
|
import { LOCALES } from "@triliumnext/commons";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("i18n", () => {
|
describe("i18n", () => {
|
||||||
it("translations are valid JSON", () => {
|
it("translations are valid JSON", () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import toastService from "./toast.js";
|
import toastService, { showError } from "./toast.js";
|
||||||
|
|
||||||
function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
|
function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
|
||||||
try {
|
try {
|
||||||
@@ -11,7 +11,9 @@ function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
|
|||||||
if (success) {
|
if (success) {
|
||||||
toastService.showMessage(t("image.copied-to-clipboard"));
|
toastService.showMessage(t("image.copied-to-clipboard"));
|
||||||
} else {
|
} else {
|
||||||
toastService.showAndLogError(t("image.cannot-copy"));
|
const message = t("image.cannot-copy");
|
||||||
|
showError(message);
|
||||||
|
logError(message);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
window.getSelection()?.removeAllRanges();
|
window.getSelection()?.removeAllRanges();
|
||||||
|
|||||||
@@ -289,13 +289,11 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (suggestion.action === "create-note") {
|
if (suggestion.action === "create-note") {
|
||||||
const { success, noteType, templateNoteId } = await noteCreateService.chooseNoteType();
|
const { success, noteType, templateNoteId, notePath } = await noteCreateService.chooseNoteType();
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { note } = await noteCreateService.createNote( notePath || suggestion.parentNoteId, {
|
||||||
const { note } = await noteCreateService.createNote(suggestion.parentNoteId, {
|
|
||||||
title: suggestion.noteTitle,
|
title: suggestion.noteTitle,
|
||||||
activate: false,
|
activate: false,
|
||||||
type: noteType,
|
type: noteType,
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ async function chooseNoteType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) {
|
async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) {
|
||||||
const { success, noteType, templateNoteId } = await chooseNoteType();
|
const { success, noteType, templateNoteId, notePath } = await chooseNoteType();
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
@@ -125,7 +125,7 @@ async function createNoteWithTypePrompt(parentNotePath: string, options: CreateN
|
|||||||
options.type = noteType;
|
options.type = noteType;
|
||||||
options.templateNoteId = templateNoteId;
|
options.templateNoteId = templateNoteId;
|
||||||
|
|
||||||
return await createNote(parentNotePath, options);
|
return await createNote(notePath || parentNotePath, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the first element is heading, parse it out and use it as a new heading. */
|
/* If the first element is heading, parse it out and use it as a new heading. */
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { t } from "./i18n.js";
|
|||||||
import type { MenuItem } from "../menus/context_menu.js";
|
import type { MenuItem } from "../menus/context_menu.js";
|
||||||
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
||||||
|
|
||||||
|
const SEPARATOR = { title: "----" };
|
||||||
|
|
||||||
async function getNoteTypeItems(command?: TreeCommandNames) {
|
async function getNoteTypeItems(command?: TreeCommandNames) {
|
||||||
const items: MenuItem<TreeCommandNames>[] = [
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
||||||
@@ -18,14 +20,23 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
|
|||||||
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
|
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
|
||||||
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
|
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
|
||||||
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
|
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
|
||||||
|
...await getBuiltInTemplates(command),
|
||||||
|
...await getUserTemplates(command)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserTemplates(command?: TreeCommandNames) {
|
||||||
const templateNoteIds = await server.get<string[]>("search-templates");
|
const templateNoteIds = await server.get<string[]>("search-templates");
|
||||||
const templateNotes = await froca.getNotes(templateNoteIds);
|
const templateNotes = await froca.getNotes(templateNoteIds);
|
||||||
|
if (templateNotes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
if (templateNotes.length > 0) {
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
items.push({ title: "----" });
|
SEPARATOR
|
||||||
|
];
|
||||||
for (const templateNote of templateNotes) {
|
for (const templateNote of templateNotes) {
|
||||||
items.push({
|
items.push({
|
||||||
title: templateNote.title,
|
title: templateNote.title,
|
||||||
@@ -35,8 +46,33 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
|
|||||||
templateNoteId: templateNote.noteId
|
templateNoteId: templateNote.noteId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBuiltInTemplates(command?: TreeCommandNames) {
|
||||||
|
const templatesRoot = await froca.getNote("_templates");
|
||||||
|
if (!templatesRoot) {
|
||||||
|
console.warn("Unable to find template root.");
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const childNotes = await templatesRoot.getChildNotes();
|
||||||
|
if (childNotes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
|
SEPARATOR
|
||||||
|
];
|
||||||
|
for (const templateNote of childNotes) {
|
||||||
|
items.push({
|
||||||
|
title: templateNote.title,
|
||||||
|
uiIcon: templateNote.getIcon(),
|
||||||
|
command: command,
|
||||||
|
type: templateNote.type,
|
||||||
|
templateNoteId: templateNote.noteId
|
||||||
|
});
|
||||||
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import FrontendScriptApi, { type Entity } from "./frontend_script_api.js";
|
import type { Entity } from "./frontend_script_api.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ async function ScriptContext(startNoteId: string, allNoteIds: string[], originEn
|
|||||||
throw new Error(`Could not find start note ${startNoteId}.`);
|
throw new Error(`Could not find start note ${startNoteId}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FrontendScriptApi = (await import("./frontend_script_api.js")).default;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modules: modules,
|
modules: modules,
|
||||||
notes: utils.toObject(allNotes, (note) => [note.noteId, note]),
|
notes: utils.toObject(allNotes, (note) => [note.noteId, note]),
|
||||||
|
|||||||
@@ -276,7 +276,8 @@ async function reportError(method: string, url: string, statusCode: number, resp
|
|||||||
} else {
|
} else {
|
||||||
const title = `${statusCode} ${method} ${url}`;
|
const title = `${statusCode} ${method} ${url}`;
|
||||||
toastService.showErrorTitleAndMessage(title, messageStr);
|
toastService.showErrorTitleAndMessage(title, messageStr);
|
||||||
toastService.throwError(`${title} - ${message}`);
|
const { throwError } = await import("./ws.js");
|
||||||
|
throwError(`${title} - ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,13 +78,7 @@ function showMessage(message: string, delay = 2000) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAndLogError(message: string, delay = 10000) {
|
export function showError(message: string, delay = 10000) {
|
||||||
showError(message, delay);
|
|
||||||
|
|
||||||
ws.logError(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(message: string, delay = 10000) {
|
|
||||||
console.log(utils.now(), "error: ", message);
|
console.log(utils.now(), "error: ", message);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
@@ -108,18 +102,10 @@ function showErrorTitleAndMessage(title: string, message: string, delay = 10000)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function throwError(message: string) {
|
|
||||||
ws.logError(message);
|
|
||||||
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
showMessage,
|
showMessage,
|
||||||
showError,
|
showError,
|
||||||
showErrorTitleAndMessage,
|
showErrorTitleAndMessage,
|
||||||
showAndLogError,
|
|
||||||
throwError,
|
|
||||||
showPersistent,
|
showPersistent,
|
||||||
closePersistent
|
closePersistent
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Modal } from "bootstrap";
|
|
||||||
import type { ViewScope } from "./link.js";
|
import type { ViewScope } from "./link.js";
|
||||||
|
|
||||||
const SVG_MIME = "image/svg+xml";
|
const SVG_MIME = "image/svg+xml";
|
||||||
@@ -275,71 +274,6 @@ function getMimeTypeClass(mime: string) {
|
|||||||
return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`;
|
return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeActiveDialog() {
|
|
||||||
if (glob.activeDialog) {
|
|
||||||
Modal.getOrCreateInstance(glob.activeDialog[0]).hide();
|
|
||||||
glob.activeDialog = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let $lastFocusedElement: JQuery<HTMLElement> | null;
|
|
||||||
|
|
||||||
// perhaps there should be saved focused element per tab?
|
|
||||||
function saveFocusedElement() {
|
|
||||||
$lastFocusedElement = $(":focus");
|
|
||||||
}
|
|
||||||
|
|
||||||
function focusSavedElement() {
|
|
||||||
if (!$lastFocusedElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($lastFocusedElement.hasClass("ck")) {
|
|
||||||
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
|
|
||||||
// the bug manifests itself in resetting the cursor position to the first character - jumping above
|
|
||||||
|
|
||||||
const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance");
|
|
||||||
|
|
||||||
if (editor) {
|
|
||||||
editor.editing.view.focus();
|
|
||||||
} else {
|
|
||||||
console.log("Could not find CKEditor instance to focus last element");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$lastFocusedElement.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
$lastFocusedElement = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
|
|
||||||
if (closeActDialog) {
|
|
||||||
closeActiveDialog();
|
|
||||||
glob.activeDialog = $dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveFocusedElement();
|
|
||||||
Modal.getOrCreateInstance($dialog[0]).show();
|
|
||||||
|
|
||||||
$dialog.on("hidden.bs.modal", () => {
|
|
||||||
const $autocompleteEl = $(".aa-input");
|
|
||||||
if ("autocomplete" in $autocompleteEl) {
|
|
||||||
$autocompleteEl.autocomplete("close");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!glob.activeDialog || glob.activeDialog === $dialog) {
|
|
||||||
focusSavedElement();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Fix once keyboard_actions is ported.
|
|
||||||
// @ts-ignore
|
|
||||||
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
|
|
||||||
keyboardActionsService.updateDisplayedShortcuts($dialog);
|
|
||||||
|
|
||||||
return $dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHtmlEmpty(html: string) {
|
function isHtmlEmpty(html: string) {
|
||||||
if (!html) {
|
if (!html) {
|
||||||
return true;
|
return true;
|
||||||
@@ -825,10 +759,6 @@ export default {
|
|||||||
setCookie,
|
setCookie,
|
||||||
getNoteTypeClass,
|
getNoteTypeClass,
|
||||||
getMimeTypeClass,
|
getMimeTypeClass,
|
||||||
closeActiveDialog,
|
|
||||||
openDialog,
|
|
||||||
saveFocusedElement,
|
|
||||||
focusSavedElement,
|
|
||||||
isHtmlEmpty,
|
isHtmlEmpty,
|
||||||
clearBrowserCache,
|
clearBrowserCache,
|
||||||
copySelectionToClipboard,
|
copySelectionToClipboard,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad;
|
|||||||
let lastPingTs: number;
|
let lastPingTs: number;
|
||||||
let frontendUpdateDataQueue: EntityChange[] = [];
|
let frontendUpdateDataQueue: EntityChange[] = [];
|
||||||
|
|
||||||
function logError(message: string) {
|
export function logError(message: string) {
|
||||||
console.error(utils.now(), message); // needs to be separate from .trace()
|
console.error(utils.now(), message); // needs to be separate from .trace()
|
||||||
|
|
||||||
if (ws && ws.readyState === 1) {
|
if (ws && ws.readyState === 1) {
|
||||||
@@ -301,6 +301,12 @@ setTimeout(() => {
|
|||||||
setInterval(sendPing, 1000);
|
setInterval(sendPing, 1000);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
export function throwError(message: string) {
|
||||||
|
logError(message);
|
||||||
|
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
logError,
|
logError,
|
||||||
subscribeToMessages,
|
subscribeToMessages,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
--bs-body-font-weight: var(--main-font-weight) !important;
|
--bs-body-font-weight: var(--main-font-weight) !important;
|
||||||
--bs-body-color: var(--main-text-color) !important;
|
--bs-body-color: var(--main-text-color) !important;
|
||||||
--bs-body-bg: var(--main-background-color) !important;
|
--bs-body-bg: var(--main-background-color) !important;
|
||||||
|
--ck-mention-list-max-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
@@ -391,7 +392,7 @@ body.desktop .dropdown-menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
|
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
|
||||||
body.desktop #context-menu-container .dropdown-item > span {
|
body #context-menu-container .dropdown-item > span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -439,10 +440,11 @@ body.desktop #context-menu-container .dropdown-item > span {
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
|
font-size: var(--monospace-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
body .cm-editor {
|
.cm-scroller {
|
||||||
font-size: var(--monospace-font-size);
|
font-family: var(--monospace-font-family) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body .cm-editor .cm-gutters {
|
body .cm-editor .cm-gutters {
|
||||||
@@ -1273,6 +1275,29 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
white-space: normal !important;
|
white-space: normal !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Slash commands */
|
||||||
|
|
||||||
|
.ck.ck-slash-command-button {
|
||||||
|
padding: 0.5em 1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-slash-command-button__text-part,
|
||||||
|
.ck.ck-template-form__text-part {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
line-height: 1.2em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-slash-command-button__text-part > span,
|
||||||
|
.ck.ck-template-form__text-part > span {
|
||||||
|
line-height: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-slash-command-button__text-part .ck.ck-slash-command-button__description,
|
||||||
|
.ck.ck-template-form__text-part .ck-template-form__description {
|
||||||
|
display: block;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
.area-expander {
|
.area-expander {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -396,3 +396,19 @@ div.tn-tool-dialog {
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE TYPE CHOOSER DIALOG
|
||||||
|
*/
|
||||||
|
|
||||||
|
.note-type-chooser-dialog div.note-type-dropdown {
|
||||||
|
/* Disable the active item highlighting since there is no use for it here */
|
||||||
|
--active-item-text-color: initial;
|
||||||
|
--active-item-background-color: initial;
|
||||||
|
|
||||||
|
font-size: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-type-chooser-dialog div.note-type-dropdown .dropdown-item span.bx {
|
||||||
|
margin-right: .25em;
|
||||||
|
}
|
||||||
@@ -267,7 +267,7 @@ input::selection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-group button:focus-visible,
|
.input-group button:focus-visible,
|
||||||
.input-group a:focus-visible {
|
.input-group a:focus-visible:not(.dropdown-item) {
|
||||||
box-shadow: unset;
|
box-shadow: unset;
|
||||||
outline: transparent;
|
outline: transparent;
|
||||||
border: transparent;
|
border: transparent;
|
||||||
@@ -349,7 +349,7 @@ select:hover,
|
|||||||
select.form-select:hover,
|
select.form-select:hover,
|
||||||
select.form-control:hover,
|
select.form-control:hover,
|
||||||
.select-button.dropdown-toggle.btn:hover {
|
.select-button.dropdown-toggle.btn:hover {
|
||||||
background: var(--input-hover-background) var(--dropdown-arrow);
|
background: var(--input-hover-background) var(--dropdown-arrow,);
|
||||||
color: var(--input-hover-color);
|
color: var(--input-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,6 +201,11 @@
|
|||||||
color: var(--menu-item-icon-color);
|
color: var(--menu-item-icon-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Slash commands */
|
||||||
|
.ck.ck-slash-command-button__text-part .ck.ck-button__label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* Separator */
|
/* Separator */
|
||||||
:root .ck .ck-list__separator {
|
:root .ck .ck-list__separator {
|
||||||
margin: .5em 0;
|
margin: .5em 0;
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ div.note-detail-empty {
|
|||||||
border: unset;
|
border: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NOTE ATTACHMENTS */
|
||||||
|
|
||||||
|
.attachment-list div.links-wrapper {
|
||||||
|
font-size: unset;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OPTIONS PAGES
|
* OPTIONS PAGES
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ body.layout-horizontal > .horizontal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.calendar-dropdown-widget .calendar-header .calendar-month-selector .select-button {
|
.calendar-dropdown-widget .calendar-header .calendar-month-selector .select-button {
|
||||||
--select-arrow-svg: ""; /* Disable the dropdown arrow */
|
--select-arrow-svg: initial; /* Disable the dropdown arrow */
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
@@ -1145,12 +1145,18 @@ body.mobile .note-title {
|
|||||||
|
|
||||||
/* The "Change note icon" button */
|
/* The "Change note icon" button */
|
||||||
|
|
||||||
.note-icon-widget .note-icon {
|
:root .note-icon-widget button.note-icon,
|
||||||
|
:root .note-icon-widget button.note-icon:hover {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-icon-widget .note-icon:hover {
|
/* Dropdown open */
|
||||||
|
:root .note-icon-widget button.note-icon.show {
|
||||||
|
background: var(--ck-editor-toolbar-dropdown-button-open-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root .note-icon-widget button.note-icon:not(:disabled):hover {
|
||||||
background: var(--icon-button-hover-background);
|
background: var(--icon-button-hover-background);
|
||||||
color: var(--icon-button-hover-color);
|
color: var(--icon-button-hover-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "关于 TriliumNext Notes",
|
"title": "关于 Trilium Notes",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"homepage": "项目主页:",
|
"homepage": "项目主页:",
|
||||||
"app_version": "应用版本:",
|
"app_version": "应用版本:",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "Über TriliumNext Notes",
|
"title": "Über Trilium Notes",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"homepage": "Startseite:",
|
"homepage": "Startseite:",
|
||||||
"app_version": "App-Version:",
|
"app_version": "App-Version:",
|
||||||
@@ -639,7 +639,7 @@
|
|||||||
"reload_frontend": "Frontend neu laden",
|
"reload_frontend": "Frontend neu laden",
|
||||||
"show_hidden_subtree": "Versteckten Teilbaum anzeigen",
|
"show_hidden_subtree": "Versteckten Teilbaum anzeigen",
|
||||||
"show_help": "Hilfe anzeigen",
|
"show_help": "Hilfe anzeigen",
|
||||||
"about": "Über TriliumNext Notes",
|
"about": "Über Trilium Notes",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"show-cheatsheet": "Cheatsheet anzeigen",
|
"show-cheatsheet": "Cheatsheet anzeigen",
|
||||||
"toggle-zen-mode": "Zen Modus"
|
"toggle-zen-mode": "Zen Modus"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "About TriliumNext Notes",
|
"title": "About Trilium Notes",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"homepage": "Homepage:",
|
"homepage": "Homepage:",
|
||||||
"app_version": "App version:",
|
"app_version": "App version:",
|
||||||
@@ -233,6 +233,8 @@
|
|||||||
"move_success_message": "Selected notes have been moved into "
|
"move_success_message": "Selected notes have been moved into "
|
||||||
},
|
},
|
||||||
"note_type_chooser": {
|
"note_type_chooser": {
|
||||||
|
"change_path_prompt": "Change where to create the new note:",
|
||||||
|
"search_placeholder": "search path by name (default if empty)",
|
||||||
"modal_title": "Choose note type",
|
"modal_title": "Choose note type",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"modal_body": "Choose note type / template of the new note:",
|
"modal_body": "Choose note type / template of the new note:",
|
||||||
@@ -641,7 +643,7 @@
|
|||||||
"reload_frontend": "Reload Frontend",
|
"reload_frontend": "Reload Frontend",
|
||||||
"show_hidden_subtree": "Show Hidden Subtree",
|
"show_hidden_subtree": "Show Hidden Subtree",
|
||||||
"show_help": "Show Help",
|
"show_help": "Show Help",
|
||||||
"about": "About TriliumNext Notes",
|
"about": "About Trilium Notes",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"show-cheatsheet": "Show Cheatsheet",
|
"show-cheatsheet": "Show Cheatsheet",
|
||||||
"toggle-zen-mode": "Zen Mode"
|
"toggle-zen-mode": "Zen Mode"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "Acerca de TriliumNext Notes",
|
"title": "Acerca de Trilium Notes",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"homepage": "Página principal:",
|
"homepage": "Página principal:",
|
||||||
"app_version": "Versión de la aplicación:",
|
"app_version": "Versión de la aplicación:",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"message": "Ha ocurrido un error crítico que previene que el cliente de la aplicación inicie:\n\n{{message}}\n\nMuy probablemente es causado por un script que falla de forma inesperada. Intente iniciar la aplicación en modo seguro y atienda el error."
|
"message": "Ha ocurrido un error crítico que previene que el cliente de la aplicación inicie:\n\n{{message}}\n\nMuy probablemente es causado por un script que falla de forma inesperada. Intente iniciar la aplicación en modo seguro y atienda el error."
|
||||||
},
|
},
|
||||||
"widget-error": {
|
"widget-error": {
|
||||||
"title": "No se pudo inicializar un widget",
|
"title": "Hubo un fallo al inicializar un widget",
|
||||||
"message-custom": "El widget personalizado de la nota con ID \"{{id}}\", titulada \"{{title}}\" no pudo ser inicializado debido a:\n\n{{message}}",
|
"message-custom": "El widget personalizado de la nota con ID \"{{id}}\", titulada \"{{title}}\" no pudo ser inicializado debido a:\n\n{{message}}",
|
||||||
"message-unknown": "Un widget no pudo ser inicializado debido a:\n\n{{message}}"
|
"message-unknown": "Un widget no pudo ser inicializado debido a:\n\n{{message}}"
|
||||||
},
|
},
|
||||||
@@ -127,6 +127,7 @@
|
|||||||
"collapseSubTree": "colapsar subárbol",
|
"collapseSubTree": "colapsar subárbol",
|
||||||
"tabShortcuts": "Atajos de pestañas",
|
"tabShortcuts": "Atajos de pestañas",
|
||||||
"newTabNoteLink": "<kbd>CTRL+clic</kbd> - (o clic central del mouse) en el enlace de la nota abre la nota en una nueva pestaña",
|
"newTabNoteLink": "<kbd>CTRL+clic</kbd> - (o clic central del mouse) en el enlace de la nota abre la nota en una nueva pestaña",
|
||||||
|
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+clic</kbd> - (o <kbd>Shift+clic de rueda de ratón</kbd>) en el enlace de la nota abre y activa la nota en una nueva pestaña",
|
||||||
"onlyInDesktop": "Solo en escritorio (compilación con Electron)",
|
"onlyInDesktop": "Solo en escritorio (compilación con Electron)",
|
||||||
"openEmptyTab": "abrir pestaña vacía",
|
"openEmptyTab": "abrir pestaña vacía",
|
||||||
"closeActiveTab": "cerrar pestaña activa",
|
"closeActiveTab": "cerrar pestaña activa",
|
||||||
@@ -232,6 +233,8 @@
|
|||||||
"move_success_message": "Las notas seleccionadas se han movido a "
|
"move_success_message": "Las notas seleccionadas se han movido a "
|
||||||
},
|
},
|
||||||
"note_type_chooser": {
|
"note_type_chooser": {
|
||||||
|
"change_path_prompt": "Cambiar donde se creará la nueva nota:",
|
||||||
|
"search_placeholder": "ruta de búsqueda por nombre (por defecto si está vacío)",
|
||||||
"modal_title": "Elija el tipo de nota",
|
"modal_title": "Elija el tipo de nota",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"modal_body": "Elija el tipo de nota/plantilla de la nueva nota:",
|
"modal_body": "Elija el tipo de nota/plantilla de la nueva nota:",
|
||||||
@@ -274,9 +277,9 @@
|
|||||||
"revision_last_edited": "Esta revisión se editó por última vez en {{date}}",
|
"revision_last_edited": "Esta revisión se editó por última vez en {{date}}",
|
||||||
"confirm_delete_all": "¿Quiere eliminar todas las revisiones de esta nota?",
|
"confirm_delete_all": "¿Quiere eliminar todas las revisiones de esta nota?",
|
||||||
"no_revisions": "Aún no hay revisiones para esta nota...",
|
"no_revisions": "Aún no hay revisiones para esta nota...",
|
||||||
"restore_button": "",
|
"restore_button": "Restaurar",
|
||||||
"confirm_restore": "¿Quiere restaurar esta revisión? Esto sobrescribirá el título actual y el contenido de la nota con esta revisión.",
|
"confirm_restore": "¿Quiere restaurar esta revisión? Esto sobrescribirá el título actual y el contenido de la nota con esta revisión.",
|
||||||
"delete_button": "",
|
"delete_button": "Eliminar",
|
||||||
"confirm_delete": "¿Quieres eliminar esta revisión?",
|
"confirm_delete": "¿Quieres eliminar esta revisión?",
|
||||||
"revisions_deleted": "Se han eliminado las revisiones de nota.",
|
"revisions_deleted": "Se han eliminado las revisiones de nota.",
|
||||||
"revision_restored": "Se ha restaurado la revisión de nota.",
|
"revision_restored": "Se ha restaurado la revisión de nota.",
|
||||||
@@ -588,6 +591,7 @@
|
|||||||
"sat": "Sáb",
|
"sat": "Sáb",
|
||||||
"sun": "Dom",
|
"sun": "Dom",
|
||||||
"cannot_find_day_note": "No se puede encontrar la nota del día",
|
"cannot_find_day_note": "No se puede encontrar la nota del día",
|
||||||
|
"cannot_find_week_note": "No se puede encontrar la nota de la semana",
|
||||||
"january": "Enero",
|
"january": "Enero",
|
||||||
"febuary": "Febrero",
|
"febuary": "Febrero",
|
||||||
"march": "Marzo",
|
"march": "Marzo",
|
||||||
@@ -639,7 +643,7 @@
|
|||||||
"reload_frontend": "Recargar interfaz",
|
"reload_frontend": "Recargar interfaz",
|
||||||
"show_hidden_subtree": "Mostrar subárbol oculto",
|
"show_hidden_subtree": "Mostrar subárbol oculto",
|
||||||
"show_help": "Mostrar ayuda",
|
"show_help": "Mostrar ayuda",
|
||||||
"about": "Acerca de TriliumNext Notes",
|
"about": "Acerca de Trilium Notes",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"show-cheatsheet": "Mostrar hoja de trucos",
|
"show-cheatsheet": "Mostrar hoja de trucos",
|
||||||
"toggle-zen-mode": "Modo Zen"
|
"toggle-zen-mode": "Modo Zen"
|
||||||
@@ -1121,6 +1125,148 @@
|
|||||||
"layout-vertical-description": "la barra del lanzador está en la izquierda (por defecto)",
|
"layout-vertical-description": "la barra del lanzador está en la izquierda (por defecto)",
|
||||||
"layout-horizontal-description": "la barra de lanzamiento está debajo de la barra de pestañas, la barra de pestañas ahora tiene ancho completo."
|
"layout-horizontal-description": "la barra de lanzamiento está debajo de la barra de pestañas, la barra de pestañas ahora tiene ancho completo."
|
||||||
},
|
},
|
||||||
|
"ai_llm": {
|
||||||
|
"not_started": "No iniciado",
|
||||||
|
"title": "IA y ajustes de embeddings",
|
||||||
|
"processed_notes": "Notas procesadas",
|
||||||
|
"total_notes": "Notas totales",
|
||||||
|
"progress": "Progreso",
|
||||||
|
"queued_notes": "Notas en fila",
|
||||||
|
"failed_notes": "Notas fallidas",
|
||||||
|
"last_processed": "Última procesada",
|
||||||
|
"refresh_stats": "Recargar estadísticas",
|
||||||
|
"enable_ai_features": "Habilitar características IA/LLM",
|
||||||
|
"enable_ai_description": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM",
|
||||||
|
"openai_tab": "OpenAI",
|
||||||
|
"anthropic_tab": "Anthropic",
|
||||||
|
"voyage_tab": "Voyage AI",
|
||||||
|
"ollama_tab": "Ollama",
|
||||||
|
"enable_ai": "Habilitar características IA/LLM",
|
||||||
|
"enable_ai_desc": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM",
|
||||||
|
"provider_configuration": "Configuración de proveedor de IA",
|
||||||
|
"provider_precedence": "Precedencia de proveedor",
|
||||||
|
"provider_precedence_description": "Lista de proveedores en orden de precedencia separada por comas (p.e., 'openai,anthropic,ollama')",
|
||||||
|
"temperature": "Temperatura",
|
||||||
|
"temperature_description": "Controla la aleatoriedad de las respuestas (0 = determinista, 2 = aleatoriedad máxima)",
|
||||||
|
"system_prompt": "Mensaje de sistema",
|
||||||
|
"system_prompt_description": "Mensaje de sistema predeterminado utilizado para todas las interacciones de IA",
|
||||||
|
"openai_configuration": "Configuración de OpenAI",
|
||||||
|
"openai_settings": "Ajustes de OpenAI",
|
||||||
|
"api_key": "Clave API",
|
||||||
|
"url": "URL base",
|
||||||
|
"model": "Modelo",
|
||||||
|
"openai_api_key_description": "Tu clave API de OpenAI para acceder a sus servicios de IA",
|
||||||
|
"anthropic_api_key_description": "Tu clave API de Anthropic para acceder a los modelos Claude",
|
||||||
|
"default_model": "Modelo por defecto",
|
||||||
|
"openai_model_description": "Ejemplos: gpt-4o, gpt-4-turbo, gpt-3.5-turbo",
|
||||||
|
"base_url": "URL base",
|
||||||
|
"openai_url_description": "Por defecto: https://api.openai.com/v1",
|
||||||
|
"anthropic_settings": "Ajustes de Anthropic",
|
||||||
|
"anthropic_url_description": "URL base para la API de Anthropic (por defecto: https://api.anthropic.com)",
|
||||||
|
"anthropic_model_description": "Modelos Claude de Anthropic para el completado de chat",
|
||||||
|
"voyage_settings": "Ajustes de Voyage AI",
|
||||||
|
"ollama_settings": "Ajustes de Ollama",
|
||||||
|
"ollama_url_description": "URL para la API de Ollama (por defecto: http://localhost:11434)",
|
||||||
|
"ollama_model_description": "Modelo de Ollama a usar para el completado de chat",
|
||||||
|
"anthropic_configuration": "Configuración de Anthropic",
|
||||||
|
"voyage_configuration": "Configuración de Voyage AI",
|
||||||
|
"voyage_url_description": "Por defecto: https://api.voyageai.com/v1",
|
||||||
|
"ollama_configuration": "Configuración de Ollama",
|
||||||
|
"enable_ollama": "Habilitar Ollama",
|
||||||
|
"enable_ollama_description": "Habilitar Ollama para uso de modelo de IA local",
|
||||||
|
"ollama_url": "URL de Ollama",
|
||||||
|
"ollama_model": "Modelo de Ollama",
|
||||||
|
"refresh_models": "Refrescar modelos",
|
||||||
|
"refreshing_models": "Refrescando...",
|
||||||
|
"enable_automatic_indexing": "Habilitar indexado automático",
|
||||||
|
"rebuild_index": "Recrear índice",
|
||||||
|
"rebuild_index_error": "Error al comenzar la reconstrucción del índice. Consulte los registros para más detalles.",
|
||||||
|
"note_title": "Título de nota",
|
||||||
|
"error": "Error",
|
||||||
|
"last_attempt": "Último intento",
|
||||||
|
"actions": "Acciones",
|
||||||
|
"retry": "Reintentar",
|
||||||
|
"partial": "{{ percentage }}% completado",
|
||||||
|
"retry_queued": "Nota en la cola para reintento",
|
||||||
|
"retry_failed": "Hubo un fallo al poner en la cola a la nota para reintento",
|
||||||
|
"max_notes_per_llm_query": "Máximo de notas por consulta",
|
||||||
|
"max_notes_per_llm_query_description": "Número máximo de notas similares a incluir en el contexto IA",
|
||||||
|
"active_providers": "Proveedores activos",
|
||||||
|
"disabled_providers": "Proveedores deshabilitados",
|
||||||
|
"remove_provider": "Eliminar proveedor de la búsqueda",
|
||||||
|
"restore_provider": "Restaurar proveedor a la búsqueda",
|
||||||
|
"similarity_threshold": "Bias de similaridad",
|
||||||
|
"similarity_threshold_description": "Puntuación de similaridad mínima (0-1) para incluir notas en el contexto para consultas LLM",
|
||||||
|
"reprocess_index": "Reconstruir el índice de búsqueda",
|
||||||
|
"reprocessing_index": "Reconstruyendo...",
|
||||||
|
"reprocess_index_started": "La optimización de índice de búsqueda comenzó en segundo plano",
|
||||||
|
"reprocess_index_error": "Error al reconstruir el índice de búsqueda",
|
||||||
|
"index_rebuild_progress": "Progreso de reconstrucción de índice",
|
||||||
|
"index_rebuilding": "Optimizando índice ({{percentage}}%)",
|
||||||
|
"index_rebuild_complete": "Optimización de índice completa",
|
||||||
|
"index_rebuild_status_error": "Error al comprobar el estado de reconstrucción del índice",
|
||||||
|
"never": "Nunca",
|
||||||
|
"processing": "Procesando ({{percentage}}%)",
|
||||||
|
"incomplete": "Incompleto ({{percentage}}%)",
|
||||||
|
"complete": "Completo (100%)",
|
||||||
|
"refreshing": "Refrescando...",
|
||||||
|
"auto_refresh_notice": "Refrescar automáticamente cada {{seconds}} segundos",
|
||||||
|
"note_queued_for_retry": "Nota en la cola para reintento",
|
||||||
|
"failed_to_retry_note": "Hubo un fallo al reintentar nota",
|
||||||
|
"all_notes_queued_for_retry": "Todas las notas con fallo agregadas a la cola para reintento",
|
||||||
|
"failed_to_retry_all": "Hubo un fallo al reintentar notas",
|
||||||
|
"ai_settings": "Ajustes de IA",
|
||||||
|
"api_key_tooltip": "Clave API para acceder al servicio",
|
||||||
|
"empty_key_warning": {
|
||||||
|
"anthropic": "La clave API de Anthropic está vacía. Por favor, ingrese una clave API válida.",
|
||||||
|
"openai": "La clave API de OpenAI está vacía. Por favor, ingrese una clave API válida.",
|
||||||
|
"voyage": "La clave API de Voyage está vacía. Por favor, ingrese una clave API válida.",
|
||||||
|
"ollama": "La clave API de Ollama está vacía. Por favor, ingrese una clave API válida."
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"processing": "Procesando...",
|
||||||
|
"thinking": "Pensando...",
|
||||||
|
"loading": "Cargando...",
|
||||||
|
"generating": "Generando..."
|
||||||
|
},
|
||||||
|
"name": "IA",
|
||||||
|
"openai": "OpenAI",
|
||||||
|
"use_enhanced_context": "Utilizar contexto mejorado",
|
||||||
|
"enhanced_context_description": "Provee a la IA con más contexto de la nota y sus notas relacionadas para obtener mejores respuestas",
|
||||||
|
"show_thinking": "Mostrar pensamiento",
|
||||||
|
"show_thinking_description": "Mostrar la cadena del proceso de pensamiento de la IA",
|
||||||
|
"enter_message": "Ingrese su mensaje...",
|
||||||
|
"error_contacting_provider": "Error al contactar con su proveedor de IA. Por favor compruebe sus ajustes y conexión a internet.",
|
||||||
|
"error_generating_response": "Error al generar respuesta de IA",
|
||||||
|
"index_all_notes": "Indexar todas las notas",
|
||||||
|
"index_status": "Estado de índice",
|
||||||
|
"indexed_notes": "Notas indexadas",
|
||||||
|
"indexing_stopped": "Indexado detenido",
|
||||||
|
"indexing_in_progress": "Indexado en progreso...",
|
||||||
|
"last_indexed": "Último indexado",
|
||||||
|
"n_notes_queued": "{{ count }} nota agregada a la cola para indexado",
|
||||||
|
"n_notes_queued_plural": "{{ count }} notas agregadas a la cola para indexado",
|
||||||
|
"note_chat": "Chat de nota",
|
||||||
|
"notes_indexed": "{{ count }} nota indexada",
|
||||||
|
"notes_indexed_plural": "{{ count }} notas indexadas",
|
||||||
|
"sources": "Fuentes",
|
||||||
|
"start_indexing": "Comenzar indexado",
|
||||||
|
"use_advanced_context": "Usar contexto avanzado",
|
||||||
|
"ollama_no_url": "Ollama no está configurado. Por favor ingrese una URL válida.",
|
||||||
|
"chat": {
|
||||||
|
"root_note_title": "Chats de IA",
|
||||||
|
"root_note_content": "Esta nota contiene tus conversaciones de chat de IA guardadas.",
|
||||||
|
"new_chat_title": "Nuevo chat",
|
||||||
|
"create_new_ai_chat": "Crear nuevo chat de IA"
|
||||||
|
},
|
||||||
|
"create_new_ai_chat": "Crear nuevo chat de IA",
|
||||||
|
"configuration_warnings": "Hay algunos problemas con su configuración de IA. Por favor compruebe sus ajustes.",
|
||||||
|
"experimental_warning": "La característica de LLM aún es experimental - ha sido advertido.",
|
||||||
|
"selected_provider": "Proveedor seleccionado",
|
||||||
|
"selected_provider_description": "Elija el proveedor de IA para el chat y características de completado",
|
||||||
|
"select_model": "Seleccionar modelo...",
|
||||||
|
"select_provider": "Seleccionar proveedor..."
|
||||||
|
},
|
||||||
"zoom_factor": {
|
"zoom_factor": {
|
||||||
"title": "Factor de zoom (solo versión de escritorio)",
|
"title": "Factor de zoom (solo versión de escritorio)",
|
||||||
"description": "El zoom también se puede controlar con los atajos CTRL+- y CTRL+=."
|
"description": "El zoom también se puede controlar con los atajos CTRL+- y CTRL+=."
|
||||||
@@ -1236,12 +1382,26 @@
|
|||||||
"label": "Tamaño para modo de solo lectura automático (notas de texto)",
|
"label": "Tamaño para modo de solo lectura automático (notas de texto)",
|
||||||
"unit": "caracteres"
|
"unit": "caracteres"
|
||||||
},
|
},
|
||||||
|
"custom_date_time_format": {
|
||||||
|
"title": "Formato de fecha/hora personalizada",
|
||||||
|
"description": "Personalizar el formado de fecha y la hora insertada vía <kbd></kbd> o la barra de herramientas. Véa la <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">documentación de Day.js</a> para más tokens de formato disponibles.",
|
||||||
|
"format_string": "Cadena de formato:",
|
||||||
|
"formatted_time": "Fecha/hora personalizada:"
|
||||||
|
},
|
||||||
"i18n": {
|
"i18n": {
|
||||||
"title": "Localización",
|
"title": "Localización",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"first-day-of-the-week": "Primer día de la semana",
|
"first-day-of-the-week": "Primer día de la semana",
|
||||||
"sunday": "Domingo",
|
"sunday": "Domingo",
|
||||||
"monday": "Lunes"
|
"monday": "Lunes",
|
||||||
|
"first-week-of-the-year": "Primer semana del año",
|
||||||
|
"first-week-contains-first-day": "Primer semana que contiene al primer día del año",
|
||||||
|
"first-week-contains-first-thursday": "Primer semana que contiene al primer jueves del año",
|
||||||
|
"first-week-has-minimum-days": "Primer semana que contiene un mínimo de días",
|
||||||
|
"min-days-in-first-week": "Días mínimos en la primer semana",
|
||||||
|
"first-week-info": "Primer semana que contiene al primer jueves del año está basado en el estándar<a href=\"https://en.wikipedia.org/wiki/ISO_week_date#First_week\">ISO 8601</a>.",
|
||||||
|
"first-week-warning": "Cambiar las opciones de primer semana puede causar duplicados con las Notas Semanales existentes y las Notas Semanales existentes no serán actualizadas respectivamente.",
|
||||||
|
"formatting-locale": "Fecha y formato de número"
|
||||||
},
|
},
|
||||||
"backup": {
|
"backup": {
|
||||||
"automatic_backup": "Copia de seguridad automática",
|
"automatic_backup": "Copia de seguridad automática",
|
||||||
@@ -1308,6 +1468,39 @@
|
|||||||
"password_mismatch": "Las nuevas contraseñas no son las mismas.",
|
"password_mismatch": "Las nuevas contraseñas no son las mismas.",
|
||||||
"password_changed_success": "La contraseña ha sido cambiada. Trilium se recargará después de presionar Aceptar."
|
"password_changed_success": "La contraseña ha sido cambiada. Trilium se recargará después de presionar Aceptar."
|
||||||
},
|
},
|
||||||
|
"multi_factor_authentication": {
|
||||||
|
"title": "Autenticación Multi-Factor",
|
||||||
|
"description": "La autenticación multifactor (MFA) agrega una capa adicional de seguridad a su cuenta. En lugar de solo ingresar una contraseña para iniciar sesión, MFA requiere que proporcione una o más pruebas adicionales para verificar su identidad. De esta manera, incluso si alguien se apodera de su contraseña, aún no puede acceder a su cuenta sin la segunda pieza de información. Es como agregar una cerradura adicional a su puerta, lo que hace que sea mucho más difícil para cualquier otra persona entrar.<br><br>Por favor siga las instrucciones a continuación para habilitar MFA. Si no lo configura correctamente, el inicio de sesión volverá a solo contraseña.",
|
||||||
|
"mfa_enabled": "Habilitar la autenticación multifactor",
|
||||||
|
"mfa_method": "Método MFA",
|
||||||
|
"electron_disabled": "Actualmente la autenticación multifactor no está soportada en la compilación de escritorio.",
|
||||||
|
"totp_title": "Contraseña de un solo uso basada en el tiempo (TOTP)",
|
||||||
|
"totp_description": "TOTP (contraseña de un solo uso basada en el tiempo) es una característica de seguridad que genera un código temporal único que cambia cada 30 segundos. Utiliza este código, junto con su contraseña para iniciar sesión en su cuenta, lo que hace que sea mucho más difícil para cualquier otra persona acceder a ella.",
|
||||||
|
"totp_secret_title": "Generar secreto TOTP",
|
||||||
|
"totp_secret_generate": "Generar secreto TOTP",
|
||||||
|
"totp_secret_regenerate": "Regenerar secreto TOTP",
|
||||||
|
"no_totp_secret_warning": "Para habilitar TOTP, primero debe de generar un secreto TOTP.",
|
||||||
|
"totp_secret_description_warning": "Después de generar un nuevo secreto TOTP, le será requerido que inicie sesión otra vez con el nuevo secreto TOTP.",
|
||||||
|
"totp_secret_generated": "Secreto TOTP generado",
|
||||||
|
"totp_secret_warning": "Por favor guarde el secreto generado en una ubicación segura. No será mostrado de nuevo.",
|
||||||
|
"totp_secret_regenerate_confirm": "¿Está seguro que desea regenerar el secreto TOTP? Esto va a invalidar el secreto TOTP previo y todos los códigos de recuperación existentes.",
|
||||||
|
"recovery_keys_title": "Claves de recuperación para un solo inicio de sesión",
|
||||||
|
"recovery_keys_description": "Las claves de recuperación para un solo inicio de sesión son usadas para iniciar sesión incluso cuando no puede acceder a los códigos de su autentificador.",
|
||||||
|
"recovery_keys_description_warning": "Las claves de recuperación no son mostrada de nuevo después de dejar esta página, manténgalas en un lugar seguro.<br>Después de que una clave de recuperación es utilizada ya no puede utilizarse de nuevo.",
|
||||||
|
"recovery_keys_error": "Error al generar códigos de recuperación",
|
||||||
|
"recovery_keys_no_key_set": "No hay códigos de recuperación establecidos",
|
||||||
|
"recovery_keys_generate": "Generar códigos de recuperación",
|
||||||
|
"recovery_keys_regenerate": "Regenerar códigos de recuperación",
|
||||||
|
"recovery_keys_used": "Usado: {{date}}",
|
||||||
|
"recovery_keys_unused": "El código de recuperación {{index}} está sin usar",
|
||||||
|
"oauth_title": "OAuth/OpenID",
|
||||||
|
"oauth_description": "OpenID es una forma estandarizada de permitirle iniciar sesión en sitios web utilizando una cuenta de otro servicio, como Google, para verificar su identidad. Siga estas <a href = \"https://developers.google.com/identity/openid-connect/openid-connect\">instrucciones</a> para configurar un servicio OpenID a través de Google.",
|
||||||
|
"oauth_description_warning": "Para habilitar OAuth/OpenID, necesita establecer la URL base de OAuth/OpenID, ID de cliente y secreto de cliente en el archivo config.ini y reiniciar la aplicación. Si desea establecerlas desde variables de ambiente, por favor establezca TRILIUM_OAUTH_BASE_URL, TRILIUM_OAUTH_CLIENT_ID y TRILIUM_OAUTH_CLIENT_SECRET.",
|
||||||
|
"oauth_missing_vars": "Ajustes faltantes: {{variables}}",
|
||||||
|
"oauth_user_account": "Cuenta de usuario: ",
|
||||||
|
"oauth_user_email": "Correo electrónico de usuario: ",
|
||||||
|
"oauth_user_not_logged_in": "¡No ha iniciado sesión!"
|
||||||
|
},
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"keyboard_shortcuts": "Atajos de teclado",
|
"keyboard_shortcuts": "Atajos de teclado",
|
||||||
"multiple_shortcuts": "Varios atajos para la misma acción se pueden separar mediante comas.",
|
"multiple_shortcuts": "Varios atajos para la misma acción se pueden separar mediante comas.",
|
||||||
@@ -1431,7 +1624,9 @@
|
|||||||
"widget": "Widget",
|
"widget": "Widget",
|
||||||
"confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?",
|
"confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?",
|
||||||
"geo-map": "Mapa Geo",
|
"geo-map": "Mapa Geo",
|
||||||
"beta-feature": "Beta"
|
"beta-feature": "Beta",
|
||||||
|
"ai-chat": "Chat de IA",
|
||||||
|
"task-list": "Lista de tareas"
|
||||||
},
|
},
|
||||||
"protect_note": {
|
"protect_note": {
|
||||||
"toggle-on": "Proteger la nota",
|
"toggle-on": "Proteger la nota",
|
||||||
@@ -1541,7 +1736,9 @@
|
|||||||
},
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"cut": "La(s) notas(s) han sido cortadas al portapapeles.",
|
"cut": "La(s) notas(s) han sido cortadas al portapapeles.",
|
||||||
"copied": "La(s) notas(s) han sido copiadas al portapapeles."
|
"copied": "La(s) notas(s) han sido copiadas al portapapeles.",
|
||||||
|
"copy_failed": "No se puede copiar al portapapeles debido a problemas de permisos.",
|
||||||
|
"copy_success": "Copiado al portapapeles."
|
||||||
},
|
},
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"note-revision-created": "Una revisión de nota ha sido creada.",
|
"note-revision-created": "Una revisión de nota ha sido creada.",
|
||||||
@@ -1584,7 +1781,7 @@
|
|||||||
"auto-detect-language": "Detectado automáticamente"
|
"auto-detect-language": "Detectado automáticamente"
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "",
|
"title": "Bloques de código",
|
||||||
"description": "Controla el resaltado de sintaxis para bloques de código dentro de las notas de texto, las notas de código no serán afectadas.",
|
"description": "Controla el resaltado de sintaxis para bloques de código dentro de las notas de texto, las notas de código no serán afectadas.",
|
||||||
"color-scheme": "Esquema de color"
|
"color-scheme": "Esquema de color"
|
||||||
},
|
},
|
||||||
@@ -1592,7 +1789,8 @@
|
|||||||
"word_wrapping": "Ajuste de palabras",
|
"word_wrapping": "Ajuste de palabras",
|
||||||
"theme_none": "Sin resaltado de sintaxis",
|
"theme_none": "Sin resaltado de sintaxis",
|
||||||
"theme_group_light": "Temas claros",
|
"theme_group_light": "Temas claros",
|
||||||
"theme_group_dark": "Temas oscuros"
|
"theme_group_dark": "Temas oscuros",
|
||||||
|
"copy_title": "Copiar al portapapeles"
|
||||||
},
|
},
|
||||||
"classic_editor_toolbar": {
|
"classic_editor_toolbar": {
|
||||||
"title": "Formato"
|
"title": "Formato"
|
||||||
@@ -1713,5 +1911,22 @@
|
|||||||
},
|
},
|
||||||
"png_export_button": {
|
"png_export_button": {
|
||||||
"button_title": "Exportar diagrama como PNG"
|
"button_title": "Exportar diagrama como PNG"
|
||||||
|
},
|
||||||
|
"svg": {
|
||||||
|
"export_to_png": "El diagrama no pudo ser exportado a PNG."
|
||||||
|
},
|
||||||
|
"code_theme": {
|
||||||
|
"title": "Apariencia",
|
||||||
|
"word_wrapping": "Ajuste de palabras",
|
||||||
|
"color-scheme": "Esquema de color"
|
||||||
|
},
|
||||||
|
"cpu_arch_warning": {
|
||||||
|
"title": "Por favor descargue la versión ARM64",
|
||||||
|
"message_macos": "TriliumNext está siendo ejecutado bajo traducción Rosetta 2, lo que significa que está usando la versión Intel (x64) en Apple Silicon Mac. Esto impactará significativamente en el rendimiento y la vida de la batería.",
|
||||||
|
"message_windows": "TriliumNext está siendo ejecutado bajo emulación, lo que significa que está usando la version Intel (x64) en Windows en un dispositivo ARM. Esto impactará significativamente en el rendimiento y la vida de la batería.",
|
||||||
|
"recommendation": "Para la mejor experiencia, por favor descargue la versión nativa ARM64 de TriliumNext desde nuestra página de lanzamientos.",
|
||||||
|
"download_link": "Descargar versión nativa",
|
||||||
|
"continue_anyway": "Continuar de todas maneras",
|
||||||
|
"dont_show_again": "No mostrar esta advertencia otra vez"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "À propos de TriliumNext Notes",
|
"title": "À propos de Trilium Notes",
|
||||||
"close": "Fermer",
|
"close": "Fermer",
|
||||||
"homepage": "Page d'accueil :",
|
"homepage": "Page d'accueil :",
|
||||||
"app_version": "Version de l'application :",
|
"app_version": "Version de l'application :",
|
||||||
@@ -639,7 +639,7 @@
|
|||||||
"reload_frontend": "Recharger l'interface",
|
"reload_frontend": "Recharger l'interface",
|
||||||
"show_hidden_subtree": "Afficher le Sous-arbre caché",
|
"show_hidden_subtree": "Afficher le Sous-arbre caché",
|
||||||
"show_help": "Afficher l'aide",
|
"show_help": "Afficher l'aide",
|
||||||
"about": "À propos de TriliumNext Notes",
|
"about": "À propos de Trilium Notes",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
"show-cheatsheet": "Afficher l'aide rapide",
|
"show-cheatsheet": "Afficher l'aide rapide",
|
||||||
"toggle-zen-mode": "Zen Mode"
|
"toggle-zen-mode": "Zen Mode"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "Despre TriliumNext Notes",
|
"title": "Despre Trilium Notes",
|
||||||
"homepage": "Site web:",
|
"homepage": "Site web:",
|
||||||
"app_version": "Versiune aplicație:",
|
"app_version": "Versiune aplicație:",
|
||||||
"db_version": "Versiune bază de date:",
|
"db_version": "Versiune bază de date:",
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"system-default": "Fontul predefinit al sistemului"
|
"system-default": "Fontul predefinit al sistemului"
|
||||||
},
|
},
|
||||||
"global_menu": {
|
"global_menu": {
|
||||||
"about": "Despre TriliumNext Notes",
|
"about": "Despre Trilium Notes",
|
||||||
"advanced": "Opțiuni avansate",
|
"advanced": "Opțiuni avansate",
|
||||||
"configure_launchbar": "Configurează bara de lansare",
|
"configure_launchbar": "Configurează bara de lansare",
|
||||||
"logout": "Deautentificare",
|
"logout": "Deautentificare",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"title": "關於 TriliumNext Notes",
|
"title": "關於 Trilium Notes",
|
||||||
"homepage": "項目主頁:",
|
"homepage": "項目主頁:",
|
||||||
"app_version": "軟件版本:",
|
"app_version": "軟件版本:",
|
||||||
"db_version": "資料庫版本:",
|
"db_version": "資料庫版本:",
|
||||||
|
|||||||
5
apps/client/src/types-assets.d.ts
vendored
5
apps/client/src/types-assets.d.ts
vendored
@@ -8,4 +8,9 @@ declare module "*?url" {
|
|||||||
export default path;
|
export default path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "*?raw" {
|
||||||
|
var content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
declare module "boxicons/css/boxicons.min.css" { }
|
declare module "boxicons/css/boxicons.min.css" { }
|
||||||
|
|||||||
16
apps/client/src/vite-env.d.ts
vendored
Normal file
16
apps/client/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ViteTypeOptions {
|
||||||
|
strictImportMetaEnv: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
/** The license key for CKEditor premium features. */
|
||||||
|
readonly VITE_CKEDITOR_KEY?: string;
|
||||||
|
/** Whether to enable the CKEditor inspector (see https://ckeditor.com/docs/ckeditor5/latest/framework/develpment-tools/inspector.html). */
|
||||||
|
readonly VITE_CKEDITOR_ENABLE_INSPECTOR?: "true" | "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import utils from "../../services/utils.js";
|
|||||||
import shortcutService from "../../services/shortcuts.js";
|
import shortcutService from "../../services/shortcuts.js";
|
||||||
import appContext from "../../components/app_context.js";
|
import appContext from "../../components/app_context.js";
|
||||||
import type { Attribute } from "../../services/attribute_parser.js";
|
import type { Attribute } from "../../services/attribute_parser.js";
|
||||||
|
import { focusSavedElement, saveFocusedElement } from "../../services/focus.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="attr-detail tn-tool-dialog">
|
<div class="attr-detail tn-tool-dialog">
|
||||||
@@ -483,7 +484,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.saveFocusedElement();
|
saveFocusedElement();
|
||||||
|
|
||||||
this.attrType = this.getAttrType(attribute);
|
this.attrType = this.getAttrType(attribute);
|
||||||
|
|
||||||
@@ -605,7 +606,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
utils.focusSavedElement();
|
focusSavedElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelAndClose() {
|
async cancelAndClose() {
|
||||||
@@ -613,7 +614,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
utils.focusSavedElement();
|
focusSavedElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
userEditedAttribute() {
|
userEditedAttribute() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import BasicWidget from "../basic_widget.js";
|
|||||||
import openService from "../../services/open.js";
|
import openService from "../../services/open.js";
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
interface AppInfo {
|
interface AppInfo {
|
||||||
appVersion: string;
|
appVersion: string;
|
||||||
@@ -111,6 +112,6 @@ export default class AboutDialog extends BasicWidget {
|
|||||||
|
|
||||||
async openAboutDialogEvent() {
|
async openAboutDialogEvent() {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import type { Suggestion } from "../../services/note_autocomplete.js";
|
import type { Suggestion } from "../../services/note_autocomplete.js";
|
||||||
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
|
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
|
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -111,7 +111,7 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.updateTitleSettingsVisibility();
|
this.updateTitleSettingsVisibility();
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
await openDialog(this.$widget);
|
||||||
|
|
||||||
this.$autoComplete.val("");
|
this.$autoComplete.val("");
|
||||||
this.$linkTitle.val("");
|
this.$linkTitle.val("");
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import treeService from "../../services/tree.js";
|
|||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import appContext from "../../components/app_context.js";
|
import appContext from "../../components/app_context.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`<div class="branch-prefix-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
const TPL = /*html*/`<div class="branch-prefix-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
@@ -93,7 +93,7 @@ export default class BranchPrefixDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.refresh(notePath);
|
await this.refresh(notePath);
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
async savePrefix() {
|
async savePrefix() {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import bulkActionService from "../../services/bulk_action.js";
|
import bulkActionService from "../../services/bulk_action.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
@@ -104,7 +104,7 @@ export default class BulkActionsDialog extends BasicWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
toastService.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
|
toastService.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
|
||||||
utils.closeActiveDialog();
|
closeActiveDialog();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +170,6 @@ export default class BulkActionsDialog extends BasicWidget {
|
|||||||
this.$includeDescendants.prop("checked", false);
|
this.$includeDescendants.prop("checked", false);
|
||||||
|
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
@@ -8,6 +7,7 @@ import appContext from "../../components/app_context.js";
|
|||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
@@ -94,7 +94,7 @@ export default class CloneToDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
this.$noteAutoComplete.val("").trigger("focus");
|
this.$noteAutoComplete.val("").trigger("focus");
|
||||||
this.$noteList.empty();
|
this.$noteList.empty();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import linkService from "../../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import type { FAttributeRow } from "../../entities/fattribute.js";
|
import type { FAttributeRow } from "../../entities/fattribute.js";
|
||||||
|
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
// TODO: Use common with server.
|
// TODO: Use common with server.
|
||||||
interface Response {
|
interface Response {
|
||||||
@@ -119,13 +119,13 @@ export default class DeleteNotesDialog extends BasicWidget {
|
|||||||
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
|
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
|
||||||
|
|
||||||
this.$cancelButton.on("click", () => {
|
this.$cancelButton.on("click", () => {
|
||||||
utils.closeActiveDialog();
|
closeActiveDialog();
|
||||||
|
|
||||||
this.resolve({ proceed: false });
|
this.resolve({ proceed: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$okButton.on("click", () => {
|
this.$okButton.on("click", () => {
|
||||||
utils.closeActiveDialog();
|
closeActiveDialog();
|
||||||
|
|
||||||
this.resolve({
|
this.resolve({
|
||||||
proceed: true,
|
proceed: true,
|
||||||
@@ -179,7 +179,7 @@ export default class DeleteNotesDialog extends BasicWidget {
|
|||||||
|
|
||||||
await this.renderDeletePreview();
|
await this.renderDeletePreview();
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.$deleteAllClones.prop("checked", !!forceDeleteAllClones).prop("disabled", !!forceDeleteAllClones);
|
this.$deleteAllClones.prop("checked", !!forceDeleteAllClones).prop("disabled", !!forceDeleteAllClones);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import BasicWidget from "../basic_widget.js";
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="export-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
<div class="export-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -214,7 +215,7 @@ export default class ExportDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.$widget.find(".opml-v2").prop("checked", true); // setting default
|
this.$widget.find(".opml-v2").prop("checked", true); // setting default
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="help-dialog modal use-tn-links" tabindex="-1" role="dialog">
|
<div class="help-dialog modal use-tn-links" tabindex="-1" role="dialog">
|
||||||
@@ -155,6 +155,6 @@ export default class HelpDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showCheatsheetEvent() {
|
showCheatsheetEvent() {
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import utils, { escapeQuotes } from "../../services/utils.js";
|
import { escapeQuotes } from "../../services/utils.js";
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
import importService, { type UploadFilesOptions } from "../../services/import.js";
|
import importService, { type UploadFilesOptions } from "../../services/import.js";
|
||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
@@ -6,6 +6,7 @@ import BasicWidget from "../basic_widget.js";
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import { Modal, Tooltip } from "bootstrap";
|
import { Modal, Tooltip } from "bootstrap";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
<div class="import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -155,7 +156,7 @@ export default class ImportDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
|
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
async importIntoNote(parentNoteId: string) {
|
async importIntoNote(parentNoteId: string) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
import type EditableTextTypeWidget from "../type_widgets/editable_text.js";
|
import type EditableTextTypeWidget from "../type_widgets/editable_text.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="include-note-dialog modal mx-auto" tabindex="-1" role="dialog">
|
<div class="include-note-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -83,7 +83,7 @@ export default class IncludeNoteDialog extends BasicWidget {
|
|||||||
async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
|
async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
|
||||||
this.textTypeWidget = textTypeWidget;
|
this.textTypeWidget = textTypeWidget;
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
|
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import type { ConfirmDialogCallback } from "./confirm.js";
|
import type { ConfirmDialogCallback } from "./confirm.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="info-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
|
<div class="info-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
|
||||||
@@ -72,7 +72,7 @@ export default class InfoDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.resolve = callback;
|
this.resolve = callback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import appContext from "../../components/app_context.js";
|
|||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import shortcutService from "../../services/shortcuts.js";
|
import shortcutService from "../../services/shortcuts.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
|
const TPL = /*html*/`<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
@@ -54,7 +55,7 @@ export default class JumpToNoteDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async jumpToNoteEvent() {
|
async jumpToNoteEvent() {
|
||||||
const dialogPromise = utils.openDialog(this.$widget);
|
const dialogPromise = openDialog(this.$widget);
|
||||||
if (utils.isMobile()) {
|
if (utils.isMobile()) {
|
||||||
dialogPromise.then(($dialog) => {
|
dialogPromise.then(($dialog) => {
|
||||||
const el = $dialog.find(">.modal-dialog")[0];
|
const el = $dialog.find(">.modal-dialog")[0];
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import BasicWidget from "../basic_widget.js";
|
|||||||
import shortcutService from "../../services/shortcuts.js";
|
import shortcutService from "../../services/shortcuts.js";
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -89,7 +90,7 @@ export default class MarkdownImportDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.convertMarkdownToHtml(text);
|
this.convertMarkdownToHtml(text);
|
||||||
} else {
|
} else {
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import branchService from "../../services/branches.js";
|
import branchService from "../../services/branches.js";
|
||||||
@@ -7,6 +6,7 @@ import treeService from "../../services/tree.js";
|
|||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="move-to-dialog modal mx-auto" tabindex="-1" role="dialog">
|
<div class="move-to-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -83,7 +83,7 @@ export default class MoveToDialog extends BasicWidget {
|
|||||||
async moveBranchIdsToEvent({ branchIds }: EventData<"moveBranchIdsTo">) {
|
async moveBranchIdsToEvent({ branchIds }: EventData<"moveBranchIdsTo">) {
|
||||||
this.movedBranchIds = branchIds;
|
this.movedBranchIds = branchIds;
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.$noteAutoComplete.val("").trigger("focus");
|
this.$noteAutoComplete.val("").trigger("focus");
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { CommandNames } from "../../components/app_context.js";
|
|||||||
import type { MenuCommandItem } from "../../menus/context_menu.js";
|
import type { MenuCommandItem } from "../../menus/context_menu.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import noteTypesService from "../../services/note_types.js";
|
import noteTypesService from "../../services/note_types.js";
|
||||||
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Dropdown, Modal } from "bootstrap";
|
import { Dropdown, Modal } from "bootstrap";
|
||||||
|
|
||||||
@@ -13,6 +14,11 @@ const TPL = /*html*/`
|
|||||||
z-index: 1100 !important;
|
z-index: 1100 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-type-chooser-dialog .input-group {
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.note-type-chooser-dialog .note-type-dropdown {
|
.note-type-chooser-dialog .note-type-dropdown {
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: large;
|
font-size: large;
|
||||||
@@ -30,6 +36,12 @@ const TPL = /*html*/`
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("note_type_chooser.close")}"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("note_type_chooser.close")}"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
${t("note_type_chooser.change_path_prompt")}
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="choose-note-path form-control" placeholder="${t("note_type_chooser.search_placeholder")}">
|
||||||
|
</div>
|
||||||
|
|
||||||
${t("note_type_chooser.modal_body")}
|
${t("note_type_chooser.modal_body")}
|
||||||
|
|
||||||
<div class="dropdown" style="display: flex;">
|
<div class="dropdown" style="display: flex;">
|
||||||
@@ -37,7 +49,7 @@ const TPL = /*html*/`
|
|||||||
data-bs-toggle="dropdown" data-bs-display="static">
|
data-bs-toggle="dropdown" data-bs-display="static">
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="note-type-dropdown dropdown-menu"></div>
|
<div class="note-type-dropdown dropdown-menu static"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,6 +60,7 @@ export interface ChooseNoteTypeResponse {
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
noteType?: string;
|
noteType?: string;
|
||||||
templateNoteId?: string;
|
templateNoteId?: string;
|
||||||
|
notePath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Callback = (data: ChooseNoteTypeResponse) => void;
|
type Callback = (data: ChooseNoteTypeResponse) => void;
|
||||||
@@ -57,6 +70,7 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
|||||||
private dropdown!: Dropdown;
|
private dropdown!: Dropdown;
|
||||||
private modal!: Modal;
|
private modal!: Modal;
|
||||||
private $noteTypeDropdown!: JQuery<HTMLElement>;
|
private $noteTypeDropdown!: JQuery<HTMLElement>;
|
||||||
|
private $autoComplete!: JQuery<HTMLElement>;
|
||||||
private $originalFocused: JQuery<HTMLElement> | null;
|
private $originalFocused: JQuery<HTMLElement> | null;
|
||||||
private $originalDialog: JQuery<HTMLElement> | null;
|
private $originalDialog: JQuery<HTMLElement> | null;
|
||||||
|
|
||||||
@@ -72,6 +86,7 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
|||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
|
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
|
||||||
|
|
||||||
|
this.$autoComplete = this.$widget.find(".choose-note-path");
|
||||||
this.$noteTypeDropdown = this.$widget.find(".note-type-dropdown");
|
this.$noteTypeDropdown = this.$widget.find(".note-type-dropdown");
|
||||||
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find(".note-type-dropdown-trigger")[0]);
|
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find(".note-type-dropdown-trigger")[0]);
|
||||||
|
|
||||||
@@ -116,9 +131,20 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
noteAutocompleteService
|
||||||
|
.initNoteAutocomplete(this.$autoComplete, {
|
||||||
|
allowCreatingNotes: false,
|
||||||
|
hideGoToSelectedNoteButton: true,
|
||||||
|
allowJumpToSearchNotes: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async chooseNoteTypeEvent({ callback }: { callback: Callback }) {
|
async chooseNoteTypeEvent({ callback }: { callback: Callback }) {
|
||||||
this.$originalFocused = $(":focus");
|
this.$originalFocused = $(":focus");
|
||||||
|
|
||||||
|
await this.refresh();
|
||||||
|
|
||||||
const noteTypes = await noteTypesService.getNoteTypeItems();
|
const noteTypes = await noteTypesService.getNoteTypeItems();
|
||||||
|
|
||||||
this.$noteTypeDropdown.empty();
|
this.$noteTypeDropdown.empty();
|
||||||
@@ -153,12 +179,14 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
|||||||
const $item = $(e.target).closest(".dropdown-item");
|
const $item = $(e.target).closest(".dropdown-item");
|
||||||
const noteType = $item.attr("data-note-type");
|
const noteType = $item.attr("data-note-type");
|
||||||
const templateNoteId = $item.attr("data-template-note-id");
|
const templateNoteId = $item.attr("data-template-note-id");
|
||||||
|
const notePath = this.$autoComplete.getSelectedNotePath() || undefined;
|
||||||
|
|
||||||
if (this.resolve) {
|
if (this.resolve) {
|
||||||
this.resolve({
|
this.resolve({
|
||||||
success: true,
|
success: true,
|
||||||
noteType,
|
noteType,
|
||||||
templateNoteId
|
templateNoteId,
|
||||||
|
notePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.resolve = null;
|
this.resolve = null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
|
||||||
@@ -37,6 +37,6 @@ export default class PasswordNoteSetDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showPasswordNotSetEvent() {
|
showPasswordNotSetEvent() {
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
|
||||||
@@ -110,6 +110,6 @@ export default class PromptDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.$dialogBody.empty().append($("<div>").addClass("form-group").append(this.$question).append(this.$answer));
|
this.$dialogBody.empty().append($("<div>").addClass("form-group").append(this.$question).append(this.$answer));
|
||||||
|
|
||||||
utils.openDialog(this.$widget, false);
|
openDialog(this.$widget, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import protectedSessionService from "../../services/protected_session.js";
|
import protectedSessionService from "../../services/protected_session.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export default class ProtectedSessionPasswordDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showProtectedSessionPasswordDialogEvent() {
|
showProtectedSessionPasswordDialogEvent() {
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.$passwordInput.trigger("focus");
|
this.$passwordInput.trigger("focus");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import { formatDateTime } from "../../utils/formatters.js";
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import appContext, { type EventData } from "../../components/app_context.js";
|
import appContext, { type EventData } from "../../components/app_context.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import dialogService from "../../services/dialog.js";
|
import dialogService, { openDialog } from "../../services/dialog.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
import hoistedNoteService from "../../services/hoisted_note.js";
|
import hoistedNoteService from "../../services/hoisted_note.js";
|
||||||
import linkService from "../../services/link.js";
|
import linkService from "../../services/link.js";
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import ws from "../../services/ws.js";
|
import ws from "../../services/ws.js";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ export default class RecentChangesDialog extends BasicWidget {
|
|||||||
|
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import appContext from "../../components/app_context.js";
|
|||||||
import openService from "../../services/open.js";
|
import openService from "../../services/open.js";
|
||||||
import protectedSessionHolder from "../../services/protected_session_holder.js";
|
import protectedSessionHolder from "../../services/protected_session_holder.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import dialogService from "../../services/dialog.js";
|
import dialogService, { openDialog } from "../../services/dialog.js";
|
||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import type { NoteType } from "../../entities/fnote.js";
|
import type { NoteType } from "../../entities/fnote.js";
|
||||||
@@ -182,7 +182,7 @@ export default class RevisionsDialog extends BasicWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
await this.loadRevisions(noteId);
|
await this.loadRevisions(noteId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import utils from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
const TPL = /*html*/`<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
|
const TPL = /*html*/`<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -97,14 +97,14 @@ export default class SortChildNotesDialog extends BasicWidget {
|
|||||||
|
|
||||||
await server.put(`notes/${this.parentNoteId}/sort-children`, { sortBy, sortDirection, foldersFirst, sortNatural, sortLocale });
|
await server.put(`notes/${this.parentNoteId}/sort-children`, { sortBy, sortDirection, foldersFirst, sortNatural, sortLocale });
|
||||||
|
|
||||||
utils.closeActiveDialog();
|
closeActiveDialog();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
|
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
|
||||||
this.parentNoteId = node.data.noteId;
|
this.parentNoteId = node.data.noteId;
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
|
|
||||||
this.$form.find("input:first").focus();
|
this.$form.find("input:first").focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import utils, { escapeQuotes } from "../../services/utils.js";
|
import { escapeQuotes } from "../../services/utils.js";
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
import importService from "../../services/import.js";
|
import importService from "../../services/import.js";
|
||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
import { Modal, Tooltip } from "bootstrap";
|
import { Modal, Tooltip } from "bootstrap";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
import { openDialog } from "../../services/dialog.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
<div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -98,7 +99,7 @@ export default class UploadAttachmentsDialog extends BasicWidget {
|
|||||||
|
|
||||||
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
|
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadAttachments(parentNoteId: string) {
|
async uploadAttachments(parentNoteId: string) {
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ export default class CodeButtonsWidget extends NoteContextAwareWidget {
|
|||||||
toastService.showMessage(t("code_buttons.opening_api_docs_message"));
|
toastService.showMessage(t("code_buttons.opening_api_docs_message"));
|
||||||
|
|
||||||
if (this.note?.mime.endsWith("frontend")) {
|
if (this.note?.mime.endsWith("frontend")) {
|
||||||
window.open("https://zadam.github.io/trilium/frontend_api/FrontendScriptApi.html", "_blank");
|
window.open("https://triliumnext.github.io/Notes/Script%20API/interfaces/Frontend_Script_API.Api.html", "_blank");
|
||||||
} else {
|
} else {
|
||||||
window.open("https://zadam.github.io/trilium/backend_api/BackendScriptApi.html", "_blank");
|
window.open("https://triliumnext.github.io/Notes/Script%20API/interfaces/Backend_Script_API.Api.html", "_blank");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export default class ContextualHelpButton extends NoteContextAwareWidget {
|
|||||||
return byNoteType[note.type];
|
return byNoteType[note.type];
|
||||||
} else if (note?.hasLabel("calendarRoot")) {
|
} else if (note?.hasLabel("calendarRoot")) {
|
||||||
return "l0tKav7yLHGF";
|
return "l0tKav7yLHGF";
|
||||||
|
} else if (note?.hasLabel("textSnippet")) {
|
||||||
|
return "pwc194wlRzcH";
|
||||||
} else if (note && note.type === "book") {
|
} else if (note && note.type === "book") {
|
||||||
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
|
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,66 @@
|
|||||||
import { ALLOWED_PROTOCOLS } from "../../../services/link.js";
|
import { ALLOWED_PROTOCOLS } from "../../../services/link.js";
|
||||||
import { MIME_TYPE_AUTO } from "@triliumnext/commons";
|
import { MIME_TYPE_AUTO } from "@triliumnext/commons";
|
||||||
import type { EditorConfig } from "@triliumnext/ckeditor5";
|
import { buildExtraCommands, type EditorConfig, PREMIUM_PLUGINS } from "@triliumnext/ckeditor5";
|
||||||
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
|
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
|
||||||
import options from "../../../services/options.js";
|
import options from "../../../services/options.js";
|
||||||
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
|
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
|
||||||
import utils from "../../../services/utils.js";
|
|
||||||
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
|
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
|
||||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
||||||
|
import getTemplates from "./snippets.js";
|
||||||
|
import { t } from "../../../services/i18n.js";
|
||||||
|
import { getMermaidConfig } from "../../../services/mermaid.js";
|
||||||
|
import noteAutocompleteService, { type Suggestion } from "../../../services/note_autocomplete.js";
|
||||||
|
import mimeTypesService from "../../../services/mime_types.js";
|
||||||
|
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
||||||
|
import { buildToolbarConfig } from "./toolbar.js";
|
||||||
|
|
||||||
const TEXT_FORMATTING_GROUP = {
|
export const OPEN_SOURCE_LICENSE_KEY = "GPL";
|
||||||
label: "Text formatting",
|
|
||||||
icon: "text"
|
|
||||||
};
|
|
||||||
|
|
||||||
export function buildConfig(): EditorConfig {
|
export interface BuildEditorOptions {
|
||||||
return {
|
forceGplLicense: boolean;
|
||||||
|
isClassicEditor: boolean;
|
||||||
|
contentLanguage: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildConfig(opts: BuildEditorOptions): Promise<EditorConfig> {
|
||||||
|
const licenseKey = (opts.forceGplLicense ? OPEN_SOURCE_LICENSE_KEY : getLicenseKey());
|
||||||
|
const hasPremiumLicense = (licenseKey !== OPEN_SOURCE_LICENSE_KEY);
|
||||||
|
|
||||||
|
const config: EditorConfig = {
|
||||||
|
licenseKey,
|
||||||
|
placeholder: t("editable_text.placeholder"),
|
||||||
|
mention: {
|
||||||
|
feeds: [
|
||||||
|
{
|
||||||
|
marker: "@",
|
||||||
|
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
||||||
|
itemRenderer: (item) => {
|
||||||
|
const itemElement = document.createElement("button");
|
||||||
|
|
||||||
|
itemElement.innerHTML = `${(item as Suggestion).highlightedNotePathTitle} `;
|
||||||
|
|
||||||
|
return itemElement;
|
||||||
|
},
|
||||||
|
minimumCharacters: 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
codeBlock: {
|
||||||
|
languages: buildListOfLanguages()
|
||||||
|
},
|
||||||
|
math: {
|
||||||
|
engine: "katex",
|
||||||
|
outputType: "span", // or script
|
||||||
|
lazyLoad: async () => {
|
||||||
|
(window as any).katex = (await import("../../../services/math.js")).default
|
||||||
|
},
|
||||||
|
forceOutputType: false, // forces output to use outputType
|
||||||
|
enablePreview: true // Enable preview view
|
||||||
|
},
|
||||||
|
mermaid: {
|
||||||
|
lazyLoad: async () => (await import("mermaid")).default, // FIXME
|
||||||
|
config: getMermaidConfig()
|
||||||
|
},
|
||||||
image: {
|
image: {
|
||||||
styles: {
|
styles: {
|
||||||
options: [
|
options: [
|
||||||
@@ -121,147 +167,67 @@ export function buildConfig(): EditorConfig {
|
|||||||
clipboard: {
|
clipboard: {
|
||||||
copy: copyTextWithToast
|
copy: copyTextWithToast
|
||||||
},
|
},
|
||||||
|
slashCommand: {
|
||||||
|
removeCommands: [],
|
||||||
|
dropdownLimit: Number.MAX_SAFE_INTEGER,
|
||||||
|
extraCommands: buildExtraCommands()
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
definitions: await getTemplates()
|
||||||
|
},
|
||||||
|
htmlSupport: {
|
||||||
|
allow: JSON.parse(options.get("allowedHtmlTags"))
|
||||||
|
},
|
||||||
// This value must be kept in sync with the language defined in webpack.config.js.
|
// This value must be kept in sync with the language defined in webpack.config.js.
|
||||||
language: "en"
|
language: "en"
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function buildToolbarConfig(isClassicToolbar: boolean) {
|
// Set up content language.
|
||||||
if (utils.isMobile()) {
|
const { contentLanguage } = opts;
|
||||||
return buildMobileToolbar();
|
if (contentLanguage) {
|
||||||
} else if (isClassicToolbar) {
|
config.language = {
|
||||||
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
|
ui: (typeof config.language === "string" ? config.language : "en"),
|
||||||
return buildClassicToolbar(multilineToolbar);
|
content: contentLanguage
|
||||||
} else {
|
}
|
||||||
return buildFloatingToolbar();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function buildMobileToolbar() {
|
// Enable premium plugins.
|
||||||
const classicConfig = buildClassicToolbar(false);
|
if (hasPremiumLicense) {
|
||||||
const items: string[] = [];
|
config.extraPlugins = [
|
||||||
|
...PREMIUM_PLUGINS
|
||||||
for (const item of classicConfig.toolbar.items) {
|
];
|
||||||
if (typeof item === "object" && "items" in item) {
|
|
||||||
for (const subitem of item.items) {
|
|
||||||
items.push(subitem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...classicConfig,
|
...config,
|
||||||
toolbar: {
|
...buildToolbarConfig(opts.isClassicEditor)
|
||||||
...classicConfig.toolbar,
|
};
|
||||||
items
|
}
|
||||||
|
|
||||||
|
function buildListOfLanguages() {
|
||||||
|
const userLanguages = mimeTypesService
|
||||||
|
.getMimeTypes()
|
||||||
|
.filter((mt) => mt.enabled)
|
||||||
|
.map((mt) => ({
|
||||||
|
language: normalizeMimeTypeForCKEditor(mt.mime),
|
||||||
|
label: mt.title
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
language: mimeTypesService.MIME_TYPE_AUTO,
|
||||||
|
label: t("editable-text.auto-detect-language")
|
||||||
|
},
|
||||||
|
...userLanguages
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLicenseKey() {
|
||||||
|
const premiumLicenseKey = import.meta.env.VITE_CKEDITOR_KEY;
|
||||||
|
if (!premiumLicenseKey) {
|
||||||
|
logError("CKEditor license key is not set, premium features will not be available.");
|
||||||
|
return OPEN_SOURCE_LICENSE_KEY;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildClassicToolbar(multilineToolbar: boolean) {
|
return premiumLicenseKey;
|
||||||
// For nested toolbars, refer to https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/toolbar.html#grouping-toolbar-items-in-dropdowns-nested-toolbars.
|
|
||||||
return {
|
|
||||||
toolbar: {
|
|
||||||
items: [
|
|
||||||
"heading",
|
|
||||||
"fontSize",
|
|
||||||
"|",
|
|
||||||
"bold",
|
|
||||||
"italic",
|
|
||||||
{
|
|
||||||
...TEXT_FORMATTING_GROUP,
|
|
||||||
items: ["underline", "strikethrough", "|", "superscript", "subscript", "|", "kbd"]
|
|
||||||
},
|
|
||||||
"|",
|
|
||||||
"fontColor",
|
|
||||||
"fontBackgroundColor",
|
|
||||||
"removeFormat",
|
|
||||||
"|",
|
|
||||||
"bulletedList",
|
|
||||||
"numberedList",
|
|
||||||
"todoList",
|
|
||||||
"|",
|
|
||||||
"blockQuote",
|
|
||||||
"admonition",
|
|
||||||
"insertTable",
|
|
||||||
"|",
|
|
||||||
"code",
|
|
||||||
"codeBlock",
|
|
||||||
"|",
|
|
||||||
"footnote",
|
|
||||||
{
|
|
||||||
label: "Insert",
|
|
||||||
icon: "plus",
|
|
||||||
items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
|
||||||
},
|
|
||||||
"|",
|
|
||||||
"alignment",
|
|
||||||
"outdent",
|
|
||||||
"indent",
|
|
||||||
"|",
|
|
||||||
"markdownImport",
|
|
||||||
"cuttonote",
|
|
||||||
"findAndReplace"
|
|
||||||
],
|
|
||||||
shouldNotGroupWhenFull: multilineToolbar
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildFloatingToolbar() {
|
|
||||||
return {
|
|
||||||
toolbar: {
|
|
||||||
items: [
|
|
||||||
"fontSize",
|
|
||||||
"bold",
|
|
||||||
"italic",
|
|
||||||
"underline",
|
|
||||||
{
|
|
||||||
...TEXT_FORMATTING_GROUP,
|
|
||||||
items: [ "strikethrough", "|", "superscript", "subscript", "|", "kbd" ]
|
|
||||||
},
|
|
||||||
"|",
|
|
||||||
"fontColor",
|
|
||||||
"fontBackgroundColor",
|
|
||||||
"|",
|
|
||||||
"code",
|
|
||||||
"link",
|
|
||||||
"bookmark",
|
|
||||||
"removeFormat",
|
|
||||||
"internallink",
|
|
||||||
"cuttonote"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
blockToolbar: [
|
|
||||||
"heading",
|
|
||||||
"|",
|
|
||||||
"bulletedList",
|
|
||||||
"numberedList",
|
|
||||||
"todoList",
|
|
||||||
"|",
|
|
||||||
"blockQuote",
|
|
||||||
"admonition",
|
|
||||||
"codeBlock",
|
|
||||||
"insertTable",
|
|
||||||
"footnote",
|
|
||||||
{
|
|
||||||
label: "Insert",
|
|
||||||
icon: "plus",
|
|
||||||
items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
|
||||||
},
|
|
||||||
"|",
|
|
||||||
"alignment",
|
|
||||||
"outdent",
|
|
||||||
"indent",
|
|
||||||
"|",
|
|
||||||
"imageUpload",
|
|
||||||
"markdownImport",
|
|
||||||
"specialCharacters",
|
|
||||||
"emoji",
|
|
||||||
"findAndReplace"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
105
apps/client/src/widgets/type_widgets/ckeditor/snippets.ts
Normal file
105
apps/client/src/widgets/type_widgets/ckeditor/snippets.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import debounce from "debounce";
|
||||||
|
import froca from "../../../services/froca.js";
|
||||||
|
import type LoadResults from "../../../services/load_results.js";
|
||||||
|
import search from "../../../services/search.js";
|
||||||
|
import type { TemplateDefinition } from "@triliumnext/ckeditor5";
|
||||||
|
import appContext from "../../../components/app_context.js";
|
||||||
|
import TemplateIcon from "@ckeditor/ckeditor5-icons/theme/icons/template.svg?raw";
|
||||||
|
import type FNote from "../../../entities/fnote.js";
|
||||||
|
|
||||||
|
interface TemplateData {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let templateCache: Map<string, TemplateData> = new Map();
|
||||||
|
const debouncedHandleContentUpdate = debounce(handleContentUpdate, 1000);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the list of snippets based on the user's notes to be passed down to the CKEditor configuration.
|
||||||
|
*
|
||||||
|
* @returns the list of templates.
|
||||||
|
*/
|
||||||
|
export default async function getTemplates() {
|
||||||
|
// Build the definitions and populate the cache.
|
||||||
|
const snippets = await search.searchForNotes("#textSnippet");
|
||||||
|
const definitions: TemplateDefinition[] = [];
|
||||||
|
for (const snippet of snippets) {
|
||||||
|
const { description } = await invalidateCacheFor(snippet);
|
||||||
|
|
||||||
|
definitions.push({
|
||||||
|
title: snippet.title,
|
||||||
|
data: () => templateCache.get(snippet.noteId)?.content ?? "",
|
||||||
|
icon: TemplateIcon,
|
||||||
|
description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function invalidateCacheFor(snippet: FNote) {
|
||||||
|
const description = snippet.getLabelValue("textSnippetDescription");
|
||||||
|
const data: TemplateData = {
|
||||||
|
title: snippet.title,
|
||||||
|
description: description ?? undefined,
|
||||||
|
content: await snippet.getContent()
|
||||||
|
};
|
||||||
|
templateCache.set(snippet.noteId, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFullReload() {
|
||||||
|
console.warn("Full text editor reload needed");
|
||||||
|
appContext.triggerCommand("reloadTextEditor");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleContentUpdate(affectedNoteIds: string[]) {
|
||||||
|
const updatedNoteIds = new Set(affectedNoteIds);
|
||||||
|
const templateNoteIds = new Set(templateCache.keys());
|
||||||
|
const affectedTemplateNoteIds = templateNoteIds.intersection(updatedNoteIds);
|
||||||
|
|
||||||
|
await froca.getNotes(affectedNoteIds);
|
||||||
|
|
||||||
|
let fullReloadNeeded = false;
|
||||||
|
for (const affectedTemplateNoteId of affectedTemplateNoteIds) {
|
||||||
|
try {
|
||||||
|
const template = await froca.getNote(affectedTemplateNoteId);
|
||||||
|
if (!template) {
|
||||||
|
console.warn("Unable to obtain template with ID ", affectedTemplateNoteId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTitle = template.title;
|
||||||
|
if (templateCache.get(affectedTemplateNoteId)?.title !== newTitle) {
|
||||||
|
fullReloadNeeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await invalidateCacheFor(template);
|
||||||
|
} catch (e) {
|
||||||
|
// If a note was not found while updating the cache, it means we need to do a full reload.
|
||||||
|
fullReloadNeeded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullReloadNeeded) {
|
||||||
|
handleFullReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateTemplateCache(loadResults: LoadResults): boolean {
|
||||||
|
const affectedNoteIds = loadResults.getNoteIds();
|
||||||
|
|
||||||
|
// React to creation or deletion of text snippets.
|
||||||
|
if (loadResults.getAttributeRows().find((attr) =>
|
||||||
|
attr.type === "label" &&
|
||||||
|
(attr.name === "textSnippet" || attr.name === "textSnippetDescription"))) {
|
||||||
|
handleFullReload();
|
||||||
|
} else if (affectedNoteIds.length > 0) {
|
||||||
|
// Update content and titles if one of the template notes were updated.
|
||||||
|
debouncedHandleContentUpdate(affectedNoteIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { buildClassicToolbar, buildFloatingToolbar } from "./config.js";
|
import { buildClassicToolbar, buildFloatingToolbar } from "./toolbar.js";
|
||||||
|
|
||||||
type ToolbarConfig = string | "|" | { items: ToolbarConfig[] };
|
type ToolbarConfig = string | "|" | { items: ToolbarConfig[] };
|
||||||
|
|
||||||
149
apps/client/src/widgets/type_widgets/ckeditor/toolbar.ts
Normal file
149
apps/client/src/widgets/type_widgets/ckeditor/toolbar.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import utils from "../../../services/utils.js";
|
||||||
|
import options from "../../../services/options.js";
|
||||||
|
|
||||||
|
const TEXT_FORMATTING_GROUP = {
|
||||||
|
label: "Text formatting",
|
||||||
|
icon: "text"
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildToolbarConfig(isClassicToolbar: boolean) {
|
||||||
|
if (utils.isMobile()) {
|
||||||
|
return buildMobileToolbar();
|
||||||
|
} else if (isClassicToolbar) {
|
||||||
|
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
|
||||||
|
return buildClassicToolbar(multilineToolbar);
|
||||||
|
} else {
|
||||||
|
return buildFloatingToolbar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildMobileToolbar() {
|
||||||
|
const classicConfig = buildClassicToolbar(false);
|
||||||
|
const items: string[] = [];
|
||||||
|
|
||||||
|
for (const item of classicConfig.toolbar.items) {
|
||||||
|
if (typeof item === "object" && "items" in item) {
|
||||||
|
for (const subitem of item.items) {
|
||||||
|
items.push(subitem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...classicConfig,
|
||||||
|
toolbar: {
|
||||||
|
...classicConfig.toolbar,
|
||||||
|
items
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildClassicToolbar(multilineToolbar: boolean) {
|
||||||
|
// For nested toolbars, refer to https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/toolbar.html#grouping-toolbar-items-in-dropdowns-nested-toolbars.
|
||||||
|
return {
|
||||||
|
toolbar: {
|
||||||
|
items: [
|
||||||
|
"heading",
|
||||||
|
"fontSize",
|
||||||
|
"|",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
{
|
||||||
|
...TEXT_FORMATTING_GROUP,
|
||||||
|
items: ["underline", "strikethrough", "|", "superscript", "subscript", "|", "kbd"]
|
||||||
|
},
|
||||||
|
"|",
|
||||||
|
"fontColor",
|
||||||
|
"fontBackgroundColor",
|
||||||
|
"removeFormat",
|
||||||
|
"|",
|
||||||
|
"bulletedList",
|
||||||
|
"numberedList",
|
||||||
|
"todoList",
|
||||||
|
"|",
|
||||||
|
"blockQuote",
|
||||||
|
"admonition",
|
||||||
|
"insertTable",
|
||||||
|
"|",
|
||||||
|
"code",
|
||||||
|
"codeBlock",
|
||||||
|
"|",
|
||||||
|
"footnote",
|
||||||
|
{
|
||||||
|
label: "Insert",
|
||||||
|
icon: "plus",
|
||||||
|
items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
||||||
|
},
|
||||||
|
"|",
|
||||||
|
"alignment",
|
||||||
|
"outdent",
|
||||||
|
"indent",
|
||||||
|
"|",
|
||||||
|
"insertTemplate",
|
||||||
|
"markdownImport",
|
||||||
|
"cuttonote",
|
||||||
|
"findAndReplace"
|
||||||
|
],
|
||||||
|
shouldNotGroupWhenFull: multilineToolbar
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildFloatingToolbar() {
|
||||||
|
return {
|
||||||
|
toolbar: {
|
||||||
|
items: [
|
||||||
|
"fontSize",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"underline",
|
||||||
|
{
|
||||||
|
...TEXT_FORMATTING_GROUP,
|
||||||
|
items: [ "strikethrough", "|", "superscript", "subscript", "|", "kbd" ]
|
||||||
|
},
|
||||||
|
"|",
|
||||||
|
"fontColor",
|
||||||
|
"fontBackgroundColor",
|
||||||
|
"|",
|
||||||
|
"code",
|
||||||
|
"link",
|
||||||
|
"bookmark",
|
||||||
|
"removeFormat",
|
||||||
|
"internallink",
|
||||||
|
"cuttonote"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
blockToolbar: [
|
||||||
|
"heading",
|
||||||
|
"|",
|
||||||
|
"bulletedList",
|
||||||
|
"numberedList",
|
||||||
|
"todoList",
|
||||||
|
"|",
|
||||||
|
"blockQuote",
|
||||||
|
"admonition",
|
||||||
|
"codeBlock",
|
||||||
|
"insertTable",
|
||||||
|
"footnote",
|
||||||
|
{
|
||||||
|
label: "Insert",
|
||||||
|
icon: "plus",
|
||||||
|
items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
|
||||||
|
},
|
||||||
|
"|",
|
||||||
|
"alignment",
|
||||||
|
"outdent",
|
||||||
|
"indent",
|
||||||
|
"|",
|
||||||
|
"insertTemplate",
|
||||||
|
"imageUpload",
|
||||||
|
"markdownImport",
|
||||||
|
"specialCharacters",
|
||||||
|
"emoji",
|
||||||
|
"findAndReplace"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import noteAutocompleteService, { type Suggestion } from "../../services/note_autocomplete.js";
|
|
||||||
import mimeTypesService from "../../services/mime_types.js";
|
|
||||||
import utils, { hasTouchBar } from "../../services/utils.js";
|
import utils, { hasTouchBar } from "../../services/utils.js";
|
||||||
import keyboardActionService from "../../services/keyboard_actions.js";
|
import keyboardActionService from "../../services/keyboard_actions.js";
|
||||||
import froca from "../../services/froca.js";
|
import froca from "../../services/froca.js";
|
||||||
@@ -12,29 +9,11 @@ import dialogService from "../../services/dialog.js";
|
|||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import toast from "../../services/toast.js";
|
import toast from "../../services/toast.js";
|
||||||
import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
|
import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
|
||||||
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
|
import { buildConfig, BuildEditorOptions, OPEN_SOURCE_LICENSE_KEY } from "./ckeditor/config.js";
|
||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import { getMermaidConfig } from "../../services/mermaid.js";
|
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
|
||||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig } from "@triliumnext/ckeditor5";
|
|
||||||
import "@triliumnext/ckeditor5/index.css";
|
import "@triliumnext/ckeditor5/index.css";
|
||||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
import { updateTemplateCache } from "./ckeditor/snippets.js";
|
||||||
|
|
||||||
const ENABLE_INSPECTOR = false;
|
|
||||||
|
|
||||||
const mentionSetup: MentionFeed[] = [
|
|
||||||
{
|
|
||||||
marker: "@",
|
|
||||||
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
|
||||||
itemRenderer: (item) => {
|
|
||||||
const itemElement = document.createElement("button");
|
|
||||||
|
|
||||||
itemElement.innerHTML = `${(item as Suggestion).highlightedNotePathTitle} `;
|
|
||||||
|
|
||||||
return itemElement;
|
|
||||||
},
|
|
||||||
minimumCharacters: 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="note-detail-editable-text note-detail-printable">
|
<div class="note-detail-editable-text note-detail-printable">
|
||||||
@@ -98,24 +77,6 @@ const TPL = /*html*/`
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function buildListOfLanguages() {
|
|
||||||
const userLanguages = mimeTypesService
|
|
||||||
.getMimeTypes()
|
|
||||||
.filter((mt) => mt.enabled)
|
|
||||||
.map((mt) => ({
|
|
||||||
language: normalizeMimeTypeForCKEditor(mt.mime),
|
|
||||||
label: mt.title
|
|
||||||
}));
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
language: mimeTypesService.MIME_TYPE_AUTO,
|
|
||||||
label: t("editable-text.auto-detect-language")
|
|
||||||
},
|
|
||||||
...userLanguages
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor can operate into two distinct modes:
|
* The editor can operate into two distinct modes:
|
||||||
*
|
*
|
||||||
@@ -148,7 +109,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
async initEditor() {
|
async initEditor() {
|
||||||
const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
|
const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
|
||||||
const editorClass = isClassicEditor ? ClassicEditor : PopupEditor;
|
|
||||||
|
|
||||||
// CKEditor since version 12 needs the element to be visible before initialization. At the same time,
|
// CKEditor since version 12 needs the element to be visible before initialization. At the same time,
|
||||||
// we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
|
// we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
|
||||||
@@ -193,34 +153,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.watchdog.setCreator(async (_, editorConfig) => {
|
this.watchdog.setCreator(async (_, editorConfig) => {
|
||||||
logInfo("Creating new CKEditor");
|
logInfo("Creating new CKEditor");
|
||||||
|
|
||||||
const finalConfig = {
|
|
||||||
...editorConfig,
|
|
||||||
...buildConfig(),
|
|
||||||
...buildToolbarConfig(isClassicEditor),
|
|
||||||
htmlSupport: {
|
|
||||||
allow: JSON.parse(options.get("allowedHtmlTags")),
|
|
||||||
styles: true,
|
|
||||||
classes: true,
|
|
||||||
attributes: true
|
|
||||||
},
|
|
||||||
licenseKey: "GPL"
|
|
||||||
};
|
|
||||||
|
|
||||||
const contentLanguage = this.note?.getLabelValue("language");
|
const contentLanguage = this.note?.getLabelValue("language");
|
||||||
if (contentLanguage) {
|
this.contentLanguage = contentLanguage ?? null;
|
||||||
// TODO: Wrong type?
|
|
||||||
//@ts-ignore
|
|
||||||
finalConfig.language = {
|
|
||||||
ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"),
|
|
||||||
content: contentLanguage
|
|
||||||
}
|
|
||||||
this.contentLanguage = contentLanguage;
|
|
||||||
} else {
|
|
||||||
this.contentLanguage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//@ts-ignore
|
const opts: BuildEditorOptions = {
|
||||||
const editor = await editorClass.create(this.$editor[0], finalConfig);
|
contentLanguage: this.contentLanguage,
|
||||||
|
forceGplLicense: false,
|
||||||
|
isClassicEditor
|
||||||
|
};
|
||||||
|
const editor = await buildEditor(this.$editor[0], isClassicEditor, opts);
|
||||||
|
|
||||||
const notificationsPlugin = editor.plugins.get("Notification");
|
const notificationsPlugin = editor.plugins.get("Notification");
|
||||||
notificationsPlugin.on("show:warning", (evt, data) => {
|
notificationsPlugin.on("show:warning", (evt, data) => {
|
||||||
@@ -278,7 +219,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
editor.model.document.on("change:data", () => this.spacedUpdate.scheduleUpdate());
|
editor.model.document.on("change:data", () => this.spacedUpdate.scheduleUpdate());
|
||||||
|
|
||||||
if (glob.isDev && ENABLE_INSPECTOR) {
|
if (import.meta.env.VITE_CKEDITOR_ENABLE_INSPECTOR === "true") {
|
||||||
const CKEditorInspector = (await import("@ckeditor/ckeditor5-inspector")).default;
|
const CKEditorInspector = (await import("@ckeditor/ckeditor5-inspector")).default;
|
||||||
CKEditorInspector.attach(editor);
|
CKEditorInspector.attach(editor);
|
||||||
}
|
}
|
||||||
@@ -297,28 +238,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createEditor() {
|
async createEditor() {
|
||||||
await this.watchdog.create(this.$editor[0], {
|
await this.watchdog.create(this.$editor[0]);
|
||||||
placeholder: t("editable_text.placeholder"),
|
|
||||||
mention: {
|
|
||||||
feeds: mentionSetup,
|
|
||||||
},
|
|
||||||
codeBlock: {
|
|
||||||
languages: buildListOfLanguages()
|
|
||||||
},
|
|
||||||
math: {
|
|
||||||
engine: "katex",
|
|
||||||
outputType: "span", // or script
|
|
||||||
lazyLoad: async () => {
|
|
||||||
(window as any).katex = (await import("../../services/math.js")).default
|
|
||||||
},
|
|
||||||
forceOutputType: false, // forces output to use outputType
|
|
||||||
enablePreview: true // Enable preview view
|
|
||||||
},
|
|
||||||
mermaid: {
|
|
||||||
lazyLoad: async () => (await import("mermaid")).default, // FIXME
|
|
||||||
config: getMermaidConfig()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async doRefresh(note: FNote) {
|
async doRefresh(note: FNote) {
|
||||||
@@ -328,7 +248,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
const data = blob?.content || "";
|
const data = blob?.content || "";
|
||||||
const newContentLanguage = this.note?.getLabelValue("language");
|
const newContentLanguage = this.note?.getLabelValue("language");
|
||||||
if (this.contentLanguage !== newContentLanguage) {
|
if (this.contentLanguage !== newContentLanguage) {
|
||||||
await this.reinitialize(data);
|
await this.reinitializeWithData(data);
|
||||||
} else {
|
} else {
|
||||||
this.watchdog.editor?.setData(data);
|
this.watchdog.editor?.setData(data);
|
||||||
}
|
}
|
||||||
@@ -564,7 +484,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.refreshIncludedNote(this.$editor, noteId);
|
this.refreshIncludedNote(this.$editor, noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async reinitialize(data: string) {
|
async reinitializeWithData(data: string) {
|
||||||
if (!this.watchdog) {
|
if (!this.watchdog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -574,9 +494,25 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.watchdog.editor?.setData(data);
|
this.watchdog.editor?.setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onLanguageChanged() {
|
async reinitialize() {
|
||||||
const data = this.watchdog.editor?.getData();
|
const data = this.watchdog.editor?.getData();
|
||||||
await this.reinitialize(data ?? "");
|
await this.reinitializeWithData(data ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
async reloadTextEditorEvent() {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLanguageChanged() {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
async entitiesReloadedEvent(e: EventData<"entitiesReloaded">) {
|
||||||
|
await super.entitiesReloadedEvent(e);
|
||||||
|
|
||||||
|
if (updateTemplateCache(e.loadResults)) {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
|
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
|
||||||
@@ -640,3 +576,19 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function buildEditor(element: HTMLElement, isClassicEditor: boolean, opts: BuildEditorOptions) {
|
||||||
|
const editorClass = isClassicEditor ? ClassicEditor : PopupEditor;
|
||||||
|
let config = await buildConfig(opts);
|
||||||
|
let editor = await editorClass.create(element, config);
|
||||||
|
|
||||||
|
if (editor.isReadOnly) {
|
||||||
|
editor.destroy();
|
||||||
|
|
||||||
|
opts.forceGplLicense = true;
|
||||||
|
config = await buildConfig(opts);
|
||||||
|
editor = await editorClass.create(element, config);
|
||||||
|
}
|
||||||
|
return editor;
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const TPL = `\
|
|||||||
<div class="options-section">
|
<div class="options-section">
|
||||||
<h4>Related settings</h4>
|
<h4>Related settings</h4>
|
||||||
|
|
||||||
<nav class="related-settings">
|
<nav class="related-settings use-tn-links">
|
||||||
<li>Color scheme for code blocks in text notes</li>
|
<li>Color scheme for code blocks in text notes</li>
|
||||||
<li>Color scheme for code notes</li>
|
<li>Color scheme for code notes</li>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const TPL = /*html*/`
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="form-text">${t("i18n.first-week-info")}</p>
|
<p class="form-text use-tn-links">${t("i18n.first-week-info")}</p>
|
||||||
|
|
||||||
<div class="admonition warning" role="alert">
|
<div class="admonition warning" role="alert">
|
||||||
${t("i18n.first-week-warning")}
|
${t("i18n.first-week-warning")}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const TPL = /*html*/`
|
|||||||
<div class="options-section">
|
<div class="options-section">
|
||||||
<h4>${t("custom_date_time_format.title")}</h4>
|
<h4>${t("custom_date_time_format.title")}</h4>
|
||||||
|
|
||||||
<p class="description">
|
<p class="description use-tn-links">
|
||||||
${t("custom_date_time_format.description")}
|
${t("custom_date_time_format.description")}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/vitest",
|
"outDir": "./out-tsc/vitest",
|
||||||
"types": [
|
"types": [
|
||||||
"vitest/globals",
|
|
||||||
"vitest/importMeta",
|
"vitest/importMeta",
|
||||||
"vite/client",
|
"vite/client",
|
||||||
"node",
|
"node",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { viteStaticCopy } from 'vite-plugin-static-copy'
|
|||||||
import asset_path from './src/asset_path';
|
import asset_path from './src/asset_path';
|
||||||
import webpackStatsPlugin from 'rollup-plugin-webpack-stats';
|
import webpackStatsPlugin from 'rollup-plugin-webpack-stats';
|
||||||
|
|
||||||
const assets = [ "assets", "stylesheets", "libraries", "fonts", "translations" ];
|
const assets = [ "assets", "stylesheets", "fonts", "translations" ];
|
||||||
|
|
||||||
export default defineConfig(() => ({
|
export default defineConfig(() => ({
|
||||||
root: __dirname,
|
root: __dirname,
|
||||||
@@ -84,7 +84,10 @@ export default defineConfig(() => ({
|
|||||||
output: {
|
output: {
|
||||||
entryFileNames: "src/[name].js",
|
entryFileNames: "src/[name].js",
|
||||||
chunkFileNames: "src/[name].js",
|
chunkFileNames: "src/[name].js",
|
||||||
assetFileNames: "src/[name].[ext]"
|
assetFileNames: "src/[name].[ext]",
|
||||||
|
manualChunks: {
|
||||||
|
"ckeditor5": [ "@triliumnext/ckeditor5" ]
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onwarn(warning, rollupWarn) {
|
onwarn(warning, rollupWarn) {
|
||||||
if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
|
if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
|
||||||
@@ -94,6 +97,12 @@ export default defineConfig(() => ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
test: {
|
||||||
|
environment: "happy-dom",
|
||||||
|
setupFiles: [
|
||||||
|
"./src/test/setup.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: [
|
exclude: [
|
||||||
"@triliumnext/highlightjs"
|
"@triliumnext/highlightjs"
|
||||||
|
|||||||
3
apps/desktop-e2e/.env
Normal file
3
apps/desktop-e2e/.env
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
TRILIUM_INTEGRATION_TEST=memory-no-store
|
||||||
|
TRILIUM_PORT=8082
|
||||||
|
TRILIUM_DATA_DIR=data
|
||||||
15
apps/desktop-e2e/eslint.config.mjs
Normal file
15
apps/desktop-e2e/eslint.config.mjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import playwright from "eslint-plugin-playwright";
|
||||||
|
import baseConfig from "../../eslint.config.mjs";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
playwright.configs["flat/recommended"],
|
||||||
|
...baseConfig,
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.js"
|
||||||
|
],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user