Merge branch 'develop' into chore_eslint-fixes_src-routes

This commit is contained in:
Panagiotis Papadopoulos 2025-03-08 17:15:53 +01:00 committed by GitHub
commit c120132cbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
196 changed files with 2163 additions and 115284 deletions

View File

@ -1,10 +1,37 @@
.git
.idea
# ignored Files
.dockerignore
.editorconfig
.git*
.prettier*
electron*
entitlements.plist
forge.config.cjs
nodemon.json
renovate.json
trilium.iml
Dockerfile
Dockerfile.*
npm-debug.log
/src/**/*.spec.ts
# ignored folders
/.cache
/.git
/.github
/.idea
/.vscode
/bin
/build
/dist
/docs
/npm-debug.log
node_modules
/dump-db
/e2e
/integration-tests
/spec
/test
/test-etapi
/node_modules
src/**/*.ts
!src/services/asset_path.ts
# exceptions
!/bin/copy-dist.ts

View File

@ -44,16 +44,6 @@ jobs:
- test_dev
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
@ -82,20 +72,6 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- name: Build and export to Docker
uses: docker/build-push-action@v6
with:

View File

@ -57,9 +57,6 @@ jobs:
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- name: Build and export to Docker
uses: docker/build-push-action@v6
with:
@ -154,18 +151,6 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- run: npm ci
- name: Run the TypeScript build
run: npx tsc
- name: Create server-package.json
run: cat package.json | grep -v electron > server-package.json
- name: Login to GHCR
uses: docker/login-action@v3
with:

View File

@ -1,3 +1,7 @@
{
"recommendations": ["lokalise.i18n-ally", "editorconfig.editorconfig"]
"recommendations": [
"lokalise.i18n-ally",
"editorconfig.editorconfig",
"vitest.explorer"
]
}

4
.vscode/launch.json vendored
View File

@ -5,8 +5,8 @@
{
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"name": "nodemon server:start",
"program": "${workspaceFolder}/src/main",
"name": "nodemon start-server",
"program": "${workspaceFolder}/src/www",
"request": "launch",
"restart": true,
"runtimeExecutable": "nodemon",

View File

@ -19,5 +19,12 @@
"[css]": {
"editor.defaultFormatter": "vscode.css-language-features"
},
"npm.exclude": ["**/build", "**/dist", "**/out/**"]
"npm.exclude": [
"**/build",
"**/dist",
"**/out/**"
],
"[xml]": {
"editor.defaultFormatter": "redhat.vscode-xml"
}
}

View File

@ -1,62 +1,46 @@
# Build stage
FROM node:22.14.0-bullseye-slim AS builder
# Configure build dependencies in a single layer
RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \
automake \
g++ \
gcc \
libtool \
make \
nasm \
libpng-dev \
python3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /usr/src/app
WORKDIR /usr/src/app/build
# Copy only necessary files for build
COPY . .
COPY server-package.json package.json
# Build and cleanup in a single layer
RUN cp -R build/src/* src/. && \
cp build/docker_healthcheck.js . && \
rm docker_healthcheck.ts && \
npm install && \
npm run build:webpack && \
npm prune --omit=dev && \
RUN npm ci && \
npm run build:prepare-dist && \
npm cache clean --force && \
cp -r src/public/app/doc_notes src/public/app-dist/. && \
rm -rf src/public/app/* && \
mkdir -p src/public/app/services && \
cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \
rm src/services/asset_path.ts && \
rm -r build
rm -rf dist/node_modules && \
mv dist/* \
start-docker.sh \
/usr/src/app/ && \
rm -rf \
/usr/src/app/build \
/tmp/node-compile-cache
#TODO: improve node_modules handling in copy-dist/Dockerfile -> remove duplicated work
# currently copy-dist will copy certain node_module folders, but in the Dockerfile we delete them again (to keep image size down),
# as we install necessary dependencies in runtime buildstage anyways
# Runtime stage
FROM node:22.14.0-bullseye-slim
# Install only runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gosu \
&& rm -rf /var/lib/apt/lists/* && \
rm -rf /var/cache/apt/*
WORKDIR /usr/src/app
# Copy only necessary files from builder
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/src ./src
COPY --from=builder /usr/src/app/db ./db
COPY --from=builder /usr/src/app/docker_healthcheck.js .
COPY --from=builder /usr/src/app/start-docker.sh .
COPY --from=builder /usr/src/app/package.json .
COPY --from=builder /usr/src/app/config-sample.ini .
COPY --from=builder /usr/src/app/images ./images
COPY --from=builder /usr/src/app/translations ./translations
COPY --from=builder /usr/src/app/libraries ./libraries
# Install only runtime dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gosu && \
rm -rf \
/var/lib/apt/lists/* \
/var/cache/apt/*
COPY --from=builder /usr/src/app ./
RUN sed -i "/electron/d" package.json && \
npm ci --omit=dev && \
npm cache clean --force && \
rm -rf /tmp/node-compile-cache
# Configure container
EXPOSE 8080

View File

@ -1,38 +1,26 @@
# Build stage
FROM node:22.14.0-alpine AS builder
# Configure build dependencies
RUN apk add --no-cache --virtual .build-dependencies \
autoconf \
automake \
g++ \
gcc \
libtool \
make \
nasm \
libpng-dev \
python3
WORKDIR /usr/src/app
WORKDIR /usr/src/app/build
# Copy only necessary files for build
COPY . .
COPY server-package.json package.json
# Build and cleanup in a single layer
RUN cp -R build/src/* src/. && \
cp build/docker_healthcheck.js . && \
rm docker_healthcheck.ts && \
npm install && \
npm run build:webpack && \
npm prune --omit=dev && \
RUN npm ci && \
npm run build:prepare-dist && \
npm cache clean --force && \
cp -r src/public/app/doc_notes src/public/app-dist/. && \
rm -rf src/public/app && \
mkdir -p src/public/app/services && \
cp -r build/src/public/app/services/mime_type_definitions.js src/public/app/services/mime_type_definitions.js && \
rm src/services/asset_path.ts && \
rm -r build
rm -rf dist/node_modules && \
mv dist/* \
start-docker.sh \
/usr/src/app/ && \
rm -rf \
/usr/src/app/build \
/tmp/node-compile-cache
#TODO: improve node_modules handling in copy-dist/Dockerfile -> remove duplicated work
# currently copy-dist will copy certain node_module folders, but in the Dockerfile we delete them again (to keep image size down),
# as we install necessary dependencies in runtime buildstage anyways
# Runtime stage
FROM node:22.14.0-alpine
@ -42,17 +30,12 @@ RUN apk add --no-cache su-exec shadow
WORKDIR /usr/src/app
# Copy only necessary files from builder
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/src ./src
COPY --from=builder /usr/src/app/db ./db
COPY --from=builder /usr/src/app/docker_healthcheck.js .
COPY --from=builder /usr/src/app/start-docker.sh .
COPY --from=builder /usr/src/app/package.json .
COPY --from=builder /usr/src/app/config-sample.ini .
COPY --from=builder /usr/src/app/images ./images
COPY --from=builder /usr/src/app/translations ./translations
COPY --from=builder /usr/src/app/libraries ./libraries
COPY --from=builder /usr/src/app ./
RUN sed -i "/electron/d" package.json && \
npm ci --omit=dev && \
npm cache clean --force && \
rm -rf /tmp/node-compile-cache
# Add application user
RUN adduser -s /bin/false node; exit 0

View File

@ -5,11 +5,6 @@ set -e # Fail on any command error
VERSION=`jq -r ".version" package.json`
SERIES=${VERSION:0:4}-latest
cat package.json | grep -v electron > server-package.json
echo "Compiling typescript..."
npx tsc
sudo docker build -t triliumnext/notes:$VERSION --network host -t triliumnext/notes:$SERIES .
if [[ $VERSION != *"beta"* ]]; then

View File

@ -2,8 +2,6 @@ import fs from "fs-extra";
import path from "path";
const DEST_DIR = "./dist";
const DEST_DIR_SRC = path.join(DEST_DIR, "src");
const DEST_DIR_NODE_MODULES = path.join(DEST_DIR, "node_modules");
const VERBOSE = process.env.VERBOSE;
@ -13,43 +11,37 @@ function log(...args: any[]) {
}
}
async function copyNodeModuleFileOrFolder(source: string) {
const adjustedSource = source.substring(13);
const destination = path.join(DEST_DIR_NODE_MODULES, adjustedSource);
function copyNodeModuleFileOrFolder(source: string) {
const destination = path.join(DEST_DIR, source);
log(`Copying ${source} to ${destination}`);
await fs.ensureDir(path.dirname(destination));
await fs.copy(source, destination);
fs.ensureDirSync(path.dirname(destination));
fs.copySync(source, destination);
}
const copy = async () => {
for (const srcFile of fs.readdirSync("build")) {
const destFile = path.join(DEST_DIR, path.basename(srcFile));
log(`Copying source ${srcFile} -> ${destFile}.`);
fs.copySync(path.join("build", srcFile), destFile, { recursive: true });
}
try {
const filesToCopy = [
"config-sample.ini",
"tsconfig.webpack.json",
const assetsToCopy = new Set([
"./images",
"./libraries",
"./translations",
"./db",
"./config-sample.ini",
"./package-lock.json",
"./package.json",
"./src/views/",
"./src/etapi/etapi.openapi.yaml",
"./src/routes/api/openapi.json"
];
for (const file of filesToCopy) {
log(`Copying ${file}`);
await fs.copy(file, path.join(DEST_DIR, file));
}
"./src/routes/api/openapi.json",
"./src/public/icon.png",
"./src/public/manifest.webmanifest",
"./src/public/robots.txt",
"./src/public/fonts",
"./src/public/stylesheets",
"./src/public/translations"
]);
const dirsToCopy = ["images", "libraries", "translations", "db"];
for (const dir of dirsToCopy) {
log(`Copying ${dir}`);
await fs.copy(dir, path.join(DEST_DIR, dir));
}
const srcDirsToCopy = ["./src/public", "./src/views", "./build"];
for (const dir of srcDirsToCopy) {
log(`Copying ${dir}`);
await fs.copy(dir, path.join(DEST_DIR_SRC, path.basename(dir)));
for (const asset of assetsToCopy) {
log(`Copying ${asset}`);
fs.copySync(asset, path.join(DEST_DIR, asset));
}
/**
@ -58,10 +50,10 @@ const copy = async () => {
const publicDirsToCopy = ["./src/public/app/doc_notes"];
const PUBLIC_DIR = path.join(DEST_DIR, "src", "public", "app-dist");
for (const dir of publicDirsToCopy) {
await fs.copy(dir, path.join(PUBLIC_DIR, path.basename(dir)));
fs.copySync(dir, path.join(PUBLIC_DIR, path.basename(dir)));
}
const nodeModulesFile = [
const nodeModulesFile = new Set([
"node_modules/react/umd/react.production.min.js",
"node_modules/react/umd/react.development.js",
"node_modules/react-dom/umd/react-dom.production.min.js",
@ -71,13 +63,9 @@ const copy = async () => {
"node_modules/katex/dist/contrib/auto-render.min.js",
"node_modules/@highlightjs/cdn-assets/highlight.min.js",
"node_modules/@mind-elixir/node-menu/dist/node-menu.umd.cjs"
];
]);
for (const file of nodeModulesFile) {
await copyNodeModuleFileOrFolder(file);
}
const nodeModulesFolder = [
const nodeModulesFolder = new Set([
"node_modules/@excalidraw/excalidraw/dist/",
"node_modules/katex/dist/",
"node_modules/dayjs/",
@ -104,13 +92,15 @@ const copy = async () => {
"node_modules/@highlightjs/cdn-assets/languages",
"node_modules/@highlightjs/cdn-assets/styles",
"node_modules/leaflet/dist"
];
]);
for (const folder of nodeModulesFolder) {
await copyNodeModuleFileOrFolder(folder);
for (const nodeModuleItem of [...nodeModulesFile, ...nodeModulesFolder]) {
copyNodeModuleFileOrFolder(nodeModuleItem);
}
};
console.log("Copying complete!")
copy()
.then(() => console.log("Copying complete!"))
.catch((err) => console.error("Error during copy:", err));
} catch(err) {
console.error("Error during copy:", err)
}

View File

@ -14,7 +14,7 @@ fi
# Trigger the TypeScript build
echo TypeScript build start
npx tsc
npm run build:ts
echo TypeScript build finished
# Copy the TypeScript artifacts

View File

@ -1,5 +1,7 @@
#!/usr/bin/env bash
set -e
if [[ $# -eq 0 ]] ; then
echo "Missing argument of new version"
exit 1

View File

@ -1,10 +0,0 @@
CREATE TABLE IF NOT EXISTS "tasks"
(
"taskId" TEXT NOT NULL PRIMARY KEY,
"parentNoteId" TEXT NOT NULL,
"title" TEXT NOT NULL DEFAULT "",
"dueDate" INTEGER,
"isDone" INTEGER NOT NULL DEFAULT 0,
"isDeleted" INTEGER NOT NULL DEFAULT 0,
"utcDateModified" TEXT NOT NULL
);

View File

@ -132,14 +132,3 @@ CREATE INDEX IDX_attachments_ownerId_role
CREATE INDEX IDX_notes_blobId on notes (blobId);
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IDX_attachments_blobId on attachments (blobId);
CREATE TABLE IF NOT EXISTS "tasks"
(
"taskId" TEXT NOT NULL PRIMARY KEY,
"parentNoteId" TEXT NOT NULL,
"title" TEXT NOT NULL DEFAULT "",
"dueDate" INTEGER,
"isDone" INTEGER NOT NULL DEFAULT 0,
"isDeleted" INTEGER NOT NULL DEFAULT 0,
"utcDateModified" TEXT NOT NULL
);

View File

@ -14,7 +14,7 @@ npm install
## Running
See output of `npx esrun dump.ts --help`:
See output of `npx tsx dump.ts --help`:
```
dump-db.ts <path_to_document> <target_directory>

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,9 @@
"homepage": "https://github.com/TriliumNext/Notes/blob/master/dump-db/README.md",
"dependencies": {
"better-sqlite3": "^11.1.2",
"esrun": "^3.2.26",
"mime-types": "^2.1.34",
"sanitize-filename": "^1.6.3",
"tsx": "^4.19.3",
"yargs": "^17.3.1"
},
"devDependencies": {

View File

@ -42,14 +42,16 @@ test("User can change language from settings", async ({ page, context }) => {
// Check that the default value (English) is set.
await expect(app.currentNoteSplit).toContainText("Theme");
const languageCombobox = await app.currentNoteSplit.getByRole("combobox").first();
const languageCombobox = app.currentNoteSplit.getByRole("combobox").first();
await expect(languageCombobox).toHaveValue("en");
// Select Chinese and ensure the translation is set.
await languageCombobox.selectOption("cn");
await expect(app.currentNoteSplit).toContainText("主题", { timeout: 15000 });
await expect(languageCombobox).toHaveValue("cn");
// Select English again.
await languageCombobox.selectOption("en");
await expect(app.currentNoteSplit).toContainText("Language", { timeout: 15000 });
await expect(languageCombobox).toHaveValue("en");
});

View File

@ -42,7 +42,7 @@ export default class App {
url = "/";
}
await this.page.goto(url, { waitUntil: "networkidle" });
await this.page.goto(url, { waitUntil: "networkidle", timeout: 30_000 });
// Wait for the page to load.
if (url === "/") {

Binary file not shown.

View File

@ -35,39 +35,13 @@
return [];
}
await glob.requireLibrary(glob.ESLINT);
if (text.length > 20000) {
console.log("Skipping linting because of large size: ", text.length);
return [];
}
const errors = new eslint().verify(text, {
root: true,
parserOptions: {
ecmaVersion: "2019"
},
extends: ['eslint:recommended', 'airbnb-base'],
env: {
'browser': true,
'node': true
},
rules: {
'import/no-unresolved': 'off',
'func-names': 'off',
'comma-dangle': ['warn'],
'padded-blocks': 'off',
'linebreak-style': 'off',
'class-methods-use-this': 'off',
'no-unused-vars': ['warn', { vars: 'local', args: 'after-used' }],
'no-nested-ternary': 'off',
'no-underscore-dangle': ['error', {'allow': ['_super', '_lookupFactory']}]
},
globals: {
"api": "readonly"
}
});
const errors = await glob.linter(text);
console.log(errors);

112883
libraries/eslint/eslint.js vendored

File diff suppressed because one or more lines are too long

246
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "trilium",
"version": "0.92.2-beta",
"version": "0.92.3-beta",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "trilium",
"version": "0.92.2-beta",
"version": "0.92.3-beta",
"license": "AGPL-3.0-only",
"dependencies": {
"@braintree/sanitize-url": "7.1.1",
@ -23,7 +23,7 @@
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"autocomplete.js": "0.38.1",
"axios": "1.8.1",
"axios": "1.8.2",
"better-sqlite3": "11.8.1",
"boxicons": "2.1.4",
"chardet": "2.1.0",
@ -44,10 +44,11 @@
"electron-squirrel-startup": "1.0.1",
"electron-window-state": "5.0.3",
"escape-html": "1.0.3",
"eslint-linter-browserify": "9.22.0",
"express": "4.21.2",
"express-rate-limit": "7.5.0",
"express-session": "1.18.1",
"force-graph": "1.49.2",
"force-graph": "1.49.3",
"fs-extra": "11.3.0",
"helmet": "8.0.0",
"html": "1.0.0",
@ -114,8 +115,8 @@
"@electron-forge/maker-zip": "7.7.0",
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
"@electron/rebuild": "3.7.1",
"@eslint/js": "9.21.0",
"@playwright/test": "1.50.1",
"@eslint/js": "9.22.0",
"@playwright/test": "1.51.0",
"@popperjs/core": "2.11.8",
"@types/archiver": "6.0.3",
"@types/better-sqlite3": "7.6.12",
@ -140,7 +141,7 @@
"@types/leaflet-gpx": "1.3.7",
"@types/mime-types": "2.1.4",
"@types/multer": "1.4.12",
"@types/node": "22.13.8",
"@types/node": "22.13.9",
"@types/react": "18.3.18",
"@types/react-dom": "18.3.5",
"@types/safe-compare": "1.1.2",
@ -154,18 +155,19 @@
"@types/swagger-ui-express": "4.1.8",
"@types/tmp": "0.2.6",
"@types/turndown": "5.0.5",
"@types/ws": "8.5.14",
"@types/ws": "8.18.0",
"@types/xml2js": "0.4.14",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.0.7",
"@vitest/coverage-v8": "3.0.8",
"autoprefixer": "10.4.20",
"bootstrap": "5.3.3",
"cross-env": "7.0.3",
"css-loader": "7.1.2",
"electron": "34.3.0",
"eslint": "9.21.0",
"electron": "34.3.1",
"eslint": "9.22.0",
"esm": "3.2.25",
"happy-dom": "17.2.2",
"globals": "16.0.0",
"happy-dom": "17.4.0",
"i18next-http-backend": "3.0.2",
"jsdoc": "4.0.4",
"knockout": "3.5.1",
@ -187,7 +189,7 @@
"typedoc": "0.27.9",
"typescript": "5.8.2",
"typescript-eslint": "8.26.0",
"vitest": "3.0.7",
"vitest": "3.0.8",
"webpack": "5.98.0",
"webpack-cli": "6.0.1",
"webpack-dev-middleware": "7.4.2"
@ -2187,6 +2189,16 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz",
"integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
@ -2241,6 +2253,19 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@ -2249,9 +2274,9 @@
"license": "MIT"
},
"node_modules/@eslint/js": {
"version": "9.21.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
"integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz",
"integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==",
"dev": true,
"license": "MIT",
"engines": {
@ -3806,13 +3831,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.50.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz",
"integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==",
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.0.tgz",
"integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.50.1"
"playwright": "1.51.0"
},
"bin": {
"playwright": "cli.js"
@ -4861,9 +4886,9 @@
}
},
"node_modules/@types/node": {
"version": "22.13.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz",
"integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
"version": "22.13.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
@ -5099,9 +5124,9 @@
"license": "MIT"
},
"node_modules/@types/ws": {
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
"integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5352,9 +5377,9 @@
}
},
"node_modules/@vitest/coverage-v8": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.7.tgz",
"integrity": "sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.0.8.tgz",
"integrity": "sha512-y7SAKsQirsEJ2F8bulBck4DoluhI2EEgTimHd6EEUgJBGKy9tC25cpywh1MH4FvDGoG2Unt7+asVd1kj4qOSAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5375,8 +5400,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@vitest/browser": "3.0.7",
"vitest": "3.0.7"
"@vitest/browser": "3.0.8",
"vitest": "3.0.8"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@ -5385,14 +5410,14 @@
}
},
"node_modules/@vitest/expect": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz",
"integrity": "sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
"integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.7",
"@vitest/utils": "3.0.7",
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@ -5401,13 +5426,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.7.tgz",
"integrity": "sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
"integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.0.7",
"@vitest/spy": "3.0.8",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@ -5428,9 +5453,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.7.tgz",
"integrity": "sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
"integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5441,13 +5466,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.7.tgz",
"integrity": "sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
"integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.0.7",
"@vitest/utils": "3.0.8",
"pathe": "^2.0.3"
},
"funding": {
@ -5462,13 +5487,13 @@
"license": "MIT"
},
"node_modules/@vitest/snapshot": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.7.tgz",
"integrity": "sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
"integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.7",
"@vitest/pretty-format": "3.0.8",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@ -5484,9 +5509,9 @@
"license": "MIT"
},
"node_modules/@vitest/spy": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.7.tgz",
"integrity": "sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
"integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -5497,13 +5522,13 @@
}
},
"node_modules/@vitest/utils": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.7.tgz",
"integrity": "sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
"integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.0.7",
"@vitest/pretty-format": "3.0.8",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@ -6296,9 +6321,9 @@
}
},
"node_modules/axios": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
@ -9011,9 +9036,9 @@
}
},
"node_modules/electron": {
"version": "34.3.0",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.3.0.tgz",
"integrity": "sha512-I238qRnYTAsuwJ/rS7HGaFNY4NNKAcjX8nlj7mnNmj1TK3z4HvNoD1r7Zud81DYDFx8AITuLd76EPrEnnfF9Bg==",
"version": "34.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-34.3.1.tgz",
"integrity": "sha512-Vsgxc4FDGg7hjduKyvTP5qfNDxZHTliZIiWD1HlR5hHXx3BFjyVv3db/uEH1GaCU0KKyeNsBXRwS4WAOMaSH5g==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@ -9733,9 +9758,9 @@
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "20.17.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz",
"integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==",
"version": "20.17.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.23.tgz",
"integrity": "sha512-8PCGZ1ZJbEZuYNTMqywO+Sj4vSKjSjT6Ua+6RFOYlEvIvKQABPtrNkoVSLSKDb4obYcMhspVKmsw8Cm10NFRUg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@ -10058,18 +10083,19 @@
}
},
"node_modules/eslint": {
"version": "9.21.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
"integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz",
"integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.2",
"@eslint/config-helpers": "^0.1.0",
"@eslint/core": "^0.12.0",
"@eslint/eslintrc": "^3.3.0",
"@eslint/js": "9.21.0",
"@eslint/js": "9.22.0",
"@eslint/plugin-kit": "^0.2.7",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@ -10081,7 +10107,7 @@
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.2.0",
"eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"esquery": "^1.5.0",
@ -10117,6 +10143,12 @@
}
}
},
"node_modules/eslint-linter-browserify": {
"version": "9.22.0",
"resolved": "https://registry.npmjs.org/eslint-linter-browserify/-/eslint-linter-browserify-9.22.0.tgz",
"integrity": "sha512-b70x+ilh1XkugEZZvGJ6LNPE1+jxjsn4KIdj1OBMBGbzYj7l2lr3N/Y4NHKhFxq2a4v/J8WqojIOvy0AFDmrXw==",
"license": "MIT"
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@ -10172,9 +10204,9 @@
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
"integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@ -11091,9 +11123,9 @@
}
},
"node_modules/force-graph": {
"version": "1.49.2",
"resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.49.2.tgz",
"integrity": "sha512-dXArBN/5Aj9pYf2/vdab8C8v/1/HtZ7lrRSGoWgaofHwbopAgpGYTkveBOkeI9+u6xfgBA+02Wgau2QnZpREXQ==",
"version": "1.49.3",
"resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.49.3.tgz",
"integrity": "sha512-blBqeFq3vdIzqGgvWrML9xA2R0nS5nvjHsEt9lcWVZ29IcdWQ6wa4G0CG/Uv8bP9olwpsJPZSJe3W8vNhiMCnQ==",
"license": "MIT",
"dependencies": {
"@tweenjs/tween.js": "18 - 25",
@ -11598,9 +11630,9 @@
}
},
"node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz",
"integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==",
"dev": true,
"license": "MIT",
"engines": {
@ -11684,9 +11716,9 @@
"license": "MIT"
},
"node_modules/happy-dom": {
"version": "17.2.2",
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.2.2.tgz",
"integrity": "sha512-3I1/CrNi780sdOhuhUnFtgTWhloSc3quSZwsylI41jycx8o97M6Y4aQAu0phSexGusT7+59BxATh4L4xiY0HcA==",
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.0.tgz",
"integrity": "sha512-LN2BIuvdFZ8snmF6LtQB2vYBzRmgCx+uqlFX9JpKVRHQ44NODNnOchB4ZW8404djHhdbQgEHRAkXCZ0zGOyzew==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -15286,13 +15318,13 @@
}
},
"node_modules/playwright": {
"version": "1.50.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
"integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==",
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz",
"integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.50.1"
"playwright-core": "1.51.0"
},
"bin": {
"playwright": "cli.js"
@ -15305,9 +15337,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.50.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz",
"integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==",
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz",
"integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@ -19154,9 +19186,9 @@
}
},
"node_modules/vite-node": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.7.tgz",
"integrity": "sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
"integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -19199,19 +19231,19 @@
}
},
"node_modules/vitest": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.7.tgz",
"integrity": "sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==",
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
"integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/expect": "3.0.7",
"@vitest/mocker": "3.0.7",
"@vitest/pretty-format": "^3.0.7",
"@vitest/runner": "3.0.7",
"@vitest/snapshot": "3.0.7",
"@vitest/spy": "3.0.7",
"@vitest/utils": "3.0.7",
"@vitest/expect": "3.0.8",
"@vitest/mocker": "3.0.8",
"@vitest/pretty-format": "^3.0.8",
"@vitest/runner": "3.0.8",
"@vitest/snapshot": "3.0.8",
"@vitest/spy": "3.0.8",
"@vitest/utils": "3.0.8",
"chai": "^5.2.0",
"debug": "^4.4.0",
"expect-type": "^1.1.0",
@ -19223,7 +19255,7 @@
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vite-node": "3.0.7",
"vite-node": "3.0.8",
"why-is-node-running": "^2.3.0"
},
"bin": {
@ -19239,8 +19271,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.0.7",
"@vitest/ui": "3.0.7",
"@vitest/browser": "3.0.8",
"@vitest/ui": "3.0.8",
"happy-dom": "*",
"jsdom": "*"
},

View File

@ -2,7 +2,7 @@
"name": "trilium",
"productName": "TriliumNext Notes",
"description": "Build your personal knowledge base with TriliumNext Notes",
"version": "0.92.2-beta",
"version": "0.92.3-beta",
"license": "AGPL-3.0-only",
"main": "./dist/electron-main.js",
"author": {
@ -43,14 +43,16 @@
"docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts",
"docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js",
"docs:build": "npm run docs:build-backend && npm run docs:build-frontend",
"build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts",
"build:prepare-dist": "npm run build:webpack && rimraf ./dist && tsc && tsx ./bin/copy-dist.ts",
"build:webpack": "tsx node_modules/webpack/bin/webpack.js -c webpack.config.ts --progress",
"build:ts": "tsc -p tsconfig.build.json",
"build:clean": "rimraf ./dist ./build",
"build:prepare-dist": "npm run build:clean && npm run build:ts && npm run build:webpack && tsx ./bin/copy-dist.ts",
"test": "npm run client:test && npm run server:test",
"server:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest",
"server:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --coverage",
"client:test": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app",
"client:coverage": "cross-env TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db TRILIUM_INTEGRATION_TEST=memory vitest --root src/public/app --coverage",
"test:playwright": "playwright test",
"test:playwright": "playwright test --workers 1",
"test:integration-edit-db": "cross-env TRILIUM_INTEGRATION_TEST=edit TRILIUM_PORT=8081 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts",
"test:integration-mem-db": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts",
"test:integration-mem-db-dev": "cross-env TRILIUM_INTEGRATION_TEST=memory TRILIUM_PORT=8082 TRILIUM_ENV=dev TRILIUM_DATA_DIR=./integration-tests/db nodemon src/main.ts",
@ -79,7 +81,7 @@
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"autocomplete.js": "0.38.1",
"axios": "1.8.1",
"axios": "1.8.2",
"better-sqlite3": "11.8.1",
"boxicons": "2.1.4",
"chardet": "2.1.0",
@ -100,10 +102,11 @@
"electron-squirrel-startup": "1.0.1",
"electron-window-state": "5.0.3",
"escape-html": "1.0.3",
"eslint-linter-browserify": "9.22.0",
"express": "4.21.2",
"express-rate-limit": "7.5.0",
"express-session": "1.18.1",
"force-graph": "1.49.2",
"force-graph": "1.49.3",
"fs-extra": "11.3.0",
"helmet": "8.0.0",
"html": "1.0.0",
@ -167,8 +170,8 @@
"@electron-forge/maker-zip": "7.7.0",
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
"@electron/rebuild": "3.7.1",
"@eslint/js": "9.21.0",
"@playwright/test": "1.50.1",
"@eslint/js": "9.22.0",
"@playwright/test": "1.51.0",
"@popperjs/core": "2.11.8",
"@types/archiver": "6.0.3",
"@types/better-sqlite3": "7.6.12",
@ -193,7 +196,7 @@
"@types/leaflet-gpx": "1.3.7",
"@types/mime-types": "2.1.4",
"@types/multer": "1.4.12",
"@types/node": "22.13.8",
"@types/node": "22.13.9",
"@types/react": "18.3.18",
"@types/react-dom": "18.3.5",
"@types/safe-compare": "1.1.2",
@ -207,18 +210,19 @@
"@types/swagger-ui-express": "4.1.8",
"@types/tmp": "0.2.6",
"@types/turndown": "5.0.5",
"@types/ws": "8.5.14",
"@types/ws": "8.18.0",
"@types/xml2js": "0.4.14",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.0.7",
"@vitest/coverage-v8": "3.0.8",
"autoprefixer": "10.4.20",
"bootstrap": "5.3.3",
"cross-env": "7.0.3",
"css-loader": "7.1.2",
"electron": "34.3.0",
"eslint": "9.21.0",
"electron": "34.3.1",
"eslint": "9.22.0",
"esm": "3.2.25",
"happy-dom": "17.2.2",
"globals": "16.0.0",
"happy-dom": "17.4.0",
"i18next-http-backend": "3.0.2",
"jsdoc": "4.0.4",
"knockout": "3.5.1",
@ -240,7 +244,7 @@
"typedoc": "0.27.9",
"typescript": "5.8.2",
"typescript-eslint": "8.26.0",
"vitest": "3.0.7",
"vitest": "3.0.8",
"webpack": "5.98.0",
"webpack-cli": "6.0.1",
"webpack-dev-middleware": "7.4.2"

View File

@ -12,7 +12,6 @@ import type { AttachmentRow, BlobRow, RevisionRow } from "./entities/rows.js";
import BBlob from "./entities/bblob.js";
import BRecentNote from "./entities/brecent_note.js";
import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import type BTask from "./entities/btask.js";
interface AttachmentOpts {
includeContentLength?: boolean;
@ -33,7 +32,6 @@ export default class Becca {
attributeIndex!: Record<string, BAttribute[]>;
options!: Record<string, BOption>;
etapiTokens!: Record<string, BEtapiToken>;
tasks!: Record<string, BTask>;
allNoteSetCache: NoteSet | null;
@ -50,7 +48,6 @@ export default class Becca {
this.attributeIndex = {};
this.options = {};
this.etapiTokens = {};
this.tasks = {};
this.dirtyNoteSetCache();
@ -216,14 +213,6 @@ export default class Becca {
return this.etapiTokens[etapiTokenId];
}
getTasks(): BTask[] {
return Object.values(this.tasks);
}
getTask(taskId: string): BTask | null {
return this.tasks[taskId];
}
getEntity<T extends AbstractBeccaEntity<T>>(entityName: string, entityId: string): AbstractBeccaEntity<T> | null {
if (!entityName || !entityId) {
return null;

View File

@ -11,10 +11,9 @@ import BOption from "./entities/boption.js";
import BEtapiToken from "./entities/betapi_token.js";
import cls from "../services/cls.js";
import entityConstructor from "../becca/entity_constructor.js";
import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow, TaskRow } from "./entities/rows.js";
import type { AttributeRow, BranchRow, EtapiTokenRow, NoteRow, OptionRow } from "./entities/rows.js";
import type AbstractBeccaEntity from "./entities/abstract_becca_entity.js";
import ws from "../services/ws.js";
import BTask from "./entities/btask.js";
const beccaLoaded = new Promise<void>(async (res, rej) => {
const sqlInit = (await import("../services/sql_init.js")).default;
@ -64,17 +63,6 @@ function load() {
for (const row of sql.getRows<EtapiTokenRow>(`SELECT etapiTokenId, name, tokenHash, utcDateCreated, utcDateModified FROM etapi_tokens WHERE isDeleted = 0`)) {
new BEtapiToken(row);
}
try {
for (const row of sql.getRows<TaskRow>(`SELECT taskId, parentNoteId, title, dueDate, isDone, isDeleted FROM tasks WHERE isDeleted = 0`)) {
new BTask(row);
}
} catch (e: any) {
// Some older migrations trigger becca which would fail since the "tasks" table is not yet defined (didn't reach the right migration).
if (!(e.message.includes("no such table"))) {
throw e;
}
}
});
for (const noteId in becca.notes) {

View File

@ -1,84 +0,0 @@
import date_utils from "../../services/date_utils.js";
import AbstractBeccaEntity from "./abstract_becca_entity.js";
import type BOption from "./boption.js";
import type { TaskRow } from "./rows.js";
export default class BTask extends AbstractBeccaEntity<BOption> {
static get entityName() {
return "tasks";
}
static get primaryKeyName() {
return "taskId";
}
static get hashedProperties() {
return ["taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted"];
}
taskId?: string;
parentNoteId!: string;
title!: string;
dueDate?: string;
isDone!: boolean;
private _isDeleted?: boolean;
constructor(row?: TaskRow) {
super();
if (!row) {
return;
}
this.updateFromRow(row);
this.init();
}
get isDeleted() {
return !!this._isDeleted;
}
updateFromRow(row: TaskRow) {
this.taskId = row.taskId;
this.parentNoteId = row.parentNoteId;
this.title = row.title;
this.dueDate = row.dueDate;
this.isDone = !!row.isDone;
this._isDeleted = !!row.isDeleted;
this.utcDateModified = row.utcDateModified;
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
init() {
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
protected beforeSaving(opts?: {}): void {
super.beforeSaving();
this.utcDateModified = date_utils.utcNowDateTime();
if (this.taskId) {
this.becca.tasks[this.taskId] = this;
}
}
getPojo() {
return {
taskId: this.taskId,
parentNoteId: this.parentNoteId,
title: this.title,
dueDate: this.dueDate,
isDone: this.isDone,
isDeleted: this.isDeleted,
utcDateModified: this.utcDateModified
};
}
}

View File

@ -139,13 +139,3 @@ export interface NoteRow {
utcDateModified: string;
content?: string | Buffer;
}
export interface TaskRow {
taskId?: string;
parentNoteId: string;
title: string;
dueDate?: string;
isDone?: boolean;
isDeleted?: boolean;
utcDateModified?: string;
}

View File

@ -9,7 +9,6 @@ import BNote from "./entities/bnote.js";
import BOption from "./entities/boption.js";
import BRecentNote from "./entities/brecent_note.js";
import BRevision from "./entities/brevision.js";
import BTask from "./entities/btask.js";
type EntityClass = new (row?: any) => AbstractBeccaEntity<any>;
@ -22,8 +21,7 @@ const ENTITY_NAME_TO_ENTITY: Record<string, ConstructorData<any> & EntityClass>
notes: BNote,
options: BOption,
recent_notes: BRecentNote,
revisions: BRevision,
tasks: BTask
revisions: BRevision
};
function getEntityFromEntityName(entityName: keyof typeof ENTITY_NAME_TO_ENTITY) {

View File

@ -551,10 +551,12 @@ $(window).on("beforeunload", () => {
});
$(window).on("hashchange", function () {
const { notePath, ntxId, viewScope } = linkService.parseNavigationStateFromUrl(window.location.href);
const { notePath, ntxId, viewScope, searchString } = linkService.parseNavigationStateFromUrl(window.location.href);
if (notePath || ntxId) {
appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope);
} else if (searchString) {
appContext.triggerCommand("searchNotes", { searchString });
}
});

View File

@ -369,7 +369,8 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
const { note, viewScope } = this;
let title = viewScope?.viewMode === "default" ? note.title : `${note.title}: ${viewScope?.viewMode}`;
const isNormalView = (viewScope?.viewMode === "default" || viewScope?.viewMode === "contextual-help");
let title = (isNormalView ? note.title : `${note.title}: ${viewScope?.viewMode}`);
if (viewScope?.attachmentId) {
// assuming the attachment has been already loaded

View File

@ -1,6 +1,6 @@
{
"formatVersion": 2,
"appVersion": "0.92.0-beta",
"appVersion": "0.92.2-beta",
"files": [
{
"isClone": false,
@ -34,7 +34,7 @@
"OkOZllzB3fqN",
"yoAe4jV2yzbd"
],
"title": "Features",
"title": "New Features",
"notePosition": 40,
"prefix": null,
"isExpanded": false,
@ -47,53 +47,91 @@
"value": "bx bx-star",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "sorted",
"value": "dateCreated",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "sortDirection",
"value": "desc",
"isInheritable": false,
"position": 30
}
],
"format": "html",
"attachments": [],
"dirFileName": "Features",
"dirFileName": "New Features",
"children": [
{
"isClone": false,
"noteId": "13D1lOc9sqmZ",
"noteId": "3I277VKYxWDH",
"notePath": [
"OkOZllzB3fqN",
"yoAe4jV2yzbd",
"13D1lOc9sqmZ"
"3I277VKYxWDH"
],
"title": "Export as PDF",
"notePosition": 20,
"title": "Right-to-left text notes",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [],
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bx-align-right",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"dataFileName": "Export as PDF.html",
"dataFileName": "Right-to-left text notes.html",
"attachments": [
{
"attachmentId": "xsGM34t8ssKV",
"attachmentId": "PSBNAvDyj5Vy",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Export as PDF_image.png"
"dataFileName": "Right-to-left text notes_i.png"
},
{
"attachmentId": "cvyes4f1Vhmm",
"attachmentId": "YXYIJznak915",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_Export as PDF_image.png"
"dataFileName": "1_Right-to-left text notes_i.png"
},
{
"attachmentId": "b3v1pLE6TF1Y",
"attachmentId": "Do0S17lDl7uu",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "2_Export as PDF_image.png"
"dataFileName": "2_Right-to-left text notes_i.png"
},
{
"attachmentId": "D3lyhPvPvocb",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "3_Right-to-left text notes_i.png"
},
{
"attachmentId": "Tu7llk3GgRkA",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "4_Right-to-left text notes_i.png"
}
]
},
@ -106,12 +144,20 @@
"B3YLYM4erjnW"
],
"title": "Zen mode",
"notePosition": 30,
"notePosition": 20,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [],
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bxs-yin-yang",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"dataFileName": "Zen mode.html",
"attachments": [
@ -180,6 +226,50 @@
"dataFileName": "7_Zen mode_image.png"
}
]
},
{
"isClone": false,
"noteId": "13D1lOc9sqmZ",
"notePath": [
"OkOZllzB3fqN",
"yoAe4jV2yzbd",
"13D1lOc9sqmZ"
],
"title": "Export as PDF",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bxs-file-pdf",
"isInheritable": false,
"position": 30
}
],
"format": "html",
"dataFileName": "Export as PDF.html",
"attachments": [
{
"attachmentId": "xsGM34t8ssKV",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Export as PDF_image.png"
},
{
"attachmentId": "b3v1pLE6TF1Y",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_Export as PDF_image.png"
}
]
}
]
},
@ -233,8 +323,47 @@
}
],
"format": "html",
"dataFileName": "Text.html",
"attachments": []
"attachments": [],
"dirFileName": "Text",
"children": [
{
"isClone": false,
"noteId": "B0lcI9xz1r8K",
"notePath": [
"OkOZllzB3fqN",
"wmegHv51MJMd",
"crJtzsol4olb",
"B0lcI9xz1r8K"
],
"title": "Content language",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "3I277VKYxWDH",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"dataFileName": "Content language.html",
"attachments": [
{
"attachmentId": "OpIv6CnYCLVa",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Content language_image.png"
}
]
}
]
},
{
"isClone": false,
@ -382,7 +511,7 @@
"title": "Book",
"notePosition": 70,
"prefix": null,
"isExpanded": true,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
@ -576,6 +705,14 @@
"mime": "image/png",
"position": 10,
"dataFileName": "18_Calendar View_image.png"
},
{
"attachmentId": "JM6AU8N4MIgB",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "19_Calendar View_image.png"
}
]
}
@ -697,7 +834,7 @@
"wmegHv51MJMd",
"foPEtsL51pD2"
],
"title": "Geo Map",
"title": "Geo map",
"notePosition": 120,
"prefix": null,
"isExpanded": false,
@ -713,23 +850,15 @@
}
],
"format": "html",
"dataFileName": "Geo Map.html",
"dataFileName": "Geo map.html",
"attachments": [
{
"attachmentId": "J0baLTpafs7C",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Geo Map_image.png"
},
{
"attachmentId": "kcYjOvJDFkbS",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_Geo Map_image.png"
"dataFileName": "Geo map_image.png"
},
{
"attachmentId": "FDP3JzIVSnuJ",
@ -737,7 +866,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "2_Geo Map_image.png"
"dataFileName": "1_Geo map_image.png"
},
{
"attachmentId": "eUrcqc8RRuZG",
@ -745,7 +874,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "3_Geo Map_image.png"
"dataFileName": "2_Geo map_image.png"
},
{
"attachmentId": "1quk4yxJpeHZ",
@ -753,7 +882,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "4_Geo Map_image.png"
"dataFileName": "3_Geo map_image.png"
},
{
"attachmentId": "iSpyhQ5Ya6Nk",
@ -761,7 +890,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "5_Geo Map_image.png"
"dataFileName": "4_Geo map_image.png"
},
{
"attachmentId": "ut6vm2aXVfXI",
@ -769,7 +898,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "6_Geo Map_image.png"
"dataFileName": "5_Geo map_image.png"
},
{
"attachmentId": "uYdb9wWf5Nuv",
@ -777,15 +906,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "7_Geo Map_image.png"
},
{
"attachmentId": "GhHYO2LteDmZ",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "8_Geo Map_image.png"
"dataFileName": "6_Geo map_image.png"
},
{
"attachmentId": "viN50n5G4kB0",
@ -793,7 +914,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "9_Geo Map_image.png"
"dataFileName": "7_Geo map_image.png"
},
{
"attachmentId": "mgwGrtQZjxxb",
@ -801,7 +922,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "10_Geo Map_image.png"
"dataFileName": "8_Geo map_image.png"
},
{
"attachmentId": "PMqmCbNLlZOG",
@ -809,7 +930,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "11_Geo Map_image.png"
"dataFileName": "9_Geo map_image.png"
},
{
"attachmentId": "0AwaQMqt3FVA",
@ -817,7 +938,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "12_Geo Map_image.png"
"dataFileName": "10_Geo map_image.png"
},
{
"attachmentId": "gR2c2Thmfy3I",
@ -825,7 +946,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "13_Geo Map_image.png"
"dataFileName": "11_Geo map_image.png"
},
{
"attachmentId": "JULizn130rVI",
@ -833,7 +954,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "14_Geo Map_image.png"
"dataFileName": "12_Geo map_image.png"
},
{
"attachmentId": "MdC0DpifJwu4",
@ -841,7 +962,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "15_Geo Map_image.png"
"dataFileName": "13_Geo map_image.png"
},
{
"attachmentId": "gFR2Izzp18LQ",
@ -849,7 +970,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "16_Geo Map_image.png"
"dataFileName": "14_Geo map_image.png"
},
{
"attachmentId": "42AncDs7SSAf",
@ -857,15 +978,7 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "17_Geo Map_image.png"
},
{
"attachmentId": "pKdtiq4r0eFY",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "18_Geo Map_image.png"
"dataFileName": "15_Geo map_image.png"
},
{
"attachmentId": "FXRVvYpOxWyR",
@ -873,7 +986,23 @@
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "19_Geo Map_image.png"
"dataFileName": "16_Geo map_image.png"
},
{
"attachmentId": "qudP7UCtwIq3",
"title": "image.png",
"role": "image",
"mime": "image/jpg",
"position": 10,
"dataFileName": "17_Geo map_image.png"
},
{
"attachmentId": "utecGxWk08QY",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "18_Geo map_image.png"
}
]
}
@ -943,173 +1072,6 @@
}
]
},
{
"isClone": false,
"noteId": "DtJJ20yEozPA",
"notePath": [
"OkOZllzB3fqN",
"DtJJ20yEozPA"
],
"title": "Theme development",
"notePosition": 130,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bx-palette",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"attachments": [],
"dirFileName": "Theme development",
"children": [
{
"isClone": false,
"noteId": "5HH79ztN0fZA",
"notePath": [
"OkOZllzB3fqN",
"DtJJ20yEozPA",
"5HH79ztN0fZA"
],
"title": "Creating a custom theme",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "aH8Dk5aMiq7R",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"dataFileName": "Creating a custom theme.html",
"attachments": [
{
"attachmentId": "AJHVfQtIQgJ7",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Creating a custom theme_im.png"
},
{
"attachmentId": "gXLyv5KXjfxg",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_Creating a custom theme_im.png"
},
{
"attachmentId": "on1gD7BzCWdN",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "2_Creating a custom theme_im.png"
},
{
"attachmentId": "17p6z24yW5eP",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "3_Creating a custom theme_im.png"
},
{
"attachmentId": "K3cdwj8f90m0",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "4_Creating a custom theme_im.png"
},
{
"attachmentId": "bn93hwF7C8sR",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "5_Creating a custom theme_im.png"
}
]
},
{
"isClone": false,
"noteId": "aH8Dk5aMiq7R",
"notePath": [
"OkOZllzB3fqN",
"DtJJ20yEozPA",
"aH8Dk5aMiq7R"
],
"title": "Customize the Next theme",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [],
"format": "html",
"dataFileName": "Customize the Next theme.html",
"attachments": [
{
"attachmentId": "5z4bC0x0eH0P",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Customize the Next theme_i.png"
},
{
"attachmentId": "u0zkXkD7rGXA",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "1_Customize the Next theme_i.png"
}
]
},
{
"isClone": false,
"noteId": "pMq6N1oBV9oo",
"notePath": [
"OkOZllzB3fqN",
"DtJJ20yEozPA",
"pMq6N1oBV9oo"
],
"title": "Reference",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "po38jIc0LD2H",
"isInheritable": false,
"position": 10
}
],
"format": "html",
"dataFileName": "Reference.html",
"attachments": []
}
]
},
{
"isClone": false,
"noteId": "LTnkDnYmmZ7s",
@ -1283,7 +1245,7 @@
"title": "ETAPI",
"notePosition": 10,
"prefix": null,
"isExpanded": true,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [],
@ -1333,7 +1295,7 @@
"title": "Internal API",
"notePosition": 20,
"prefix": null,
"isExpanded": true,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -23,7 +23,7 @@
as PDF. On the server or PWA (mobile), the option is not available due
to technical constraints and it will be hidden.</p>
<p>To print a note, select the
<img src="2_Export as PDF_image.png" width="29"
<img src="1_Export as PDF_image.png" width="29"
height="31">button to the right of the note and select <i>Export as PDF</i>.</p>
<p>Afterwards you will be prompted to select where to save the PDF file.
Upon confirmation, the resulting PDF will be opened automatically using
@ -33,7 +33,7 @@
<a
href="#root/OeKBfN6JbMIq/jRV1MPt4mNSP/hrC6xn7hnDq5">report the issue</a>. In this case, it's best to offer a sample note (click
on the
<img src="2_Export as PDF_image.png" width="29" height="31">button, select Export note → This note and all of its descendants → HTML
<img src="1_Export as PDF_image.png" width="29" height="31">button, select Export note → This note and all of its descendants → HTML
in ZIP archive). Make sure not to accidentally leak any personal information.</p>
<h2>Landscape mode</h2>
<p>When exporting to PDF, there are no customizable settings such as page

View File

@ -0,0 +1,56 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Right-to-left text notes</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Right-to-left text notes</h1>
<div class="ck-content">
<p>Trilium now has basic support for right-to-left text, at note level.</p>
<figure
class="table">
<table>
<tbody>
<tr>
<td>
<figure class="image">
<img style="aspect-ratio:906/557;" src="3_Right-to-left text notes_i.png"
width="906" height="557">
</figure>
</td>
<td>
<figure class="image">
<img style="aspect-ratio:906/557;" src="2_Right-to-left text notes_i.png"
width="906" height="557">
</figure>
</td>
</tr>
</tbody>
</table>
</figure>
<p>Note that only the Text note type supports this.</p>
<p>The list of languages is configurable via the a new dedicated settings
page:</p>
<figure class="image">
<img style="aspect-ratio:1248/635;" src="4_Right-to-left text notes_i.png"
width="1248" height="635">
</figure>
<p>To select the corresponding language of the text, go to “Basic Properties”
and select your desired language.</p>
<p>
<img src="1_Right-to-left text notes_i.png" width="635" height="492">
</p>
<p>Feel free to report any issues regarding right to left support.</p>
<p>&nbsp;</p>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -118,6 +118,12 @@
<td>When present (regardless of value), it will show the number of the week
on the calendar.</td>
</tr>
<tr>
<td><code>~child:template</code>
</td>
<td>Defines the template for newly created notes in the calendar (via dragging
or clicking).</td>
</tr>
</tbody>
</table>
</figure>
@ -175,6 +181,36 @@
than the title, either a label (e.g. <code>#assignee</code>) or a relation
(e.g. <code>~for</code>). See <i>Advanced use-cases</i> for more information.</td>
</tr>
<tr>
<td><code>#calendar:promotedAttributes</code>
</td>
<td>
<p>Allows displaying the value of one or more promoted attributes in the
calendar like this:
<img src="19_Calendar View_image.png" width="131" height="113">
</p><pre><code class="language-text-x-trilium-auto">#label:weight="promoted,number,single,precision=1"
#label:mood="promoted,alias=Mood,single,text"
#calendar:promotedAttributes="label:weight,label:mood" </code></pre>
<p>It can also be used with relations, case in which it will display the
title of the target note:</p><pre><code class="language-text-x-trilium-auto">#relation:assignee="promoted,alias=Assignee,single,text"
#calendar:promotedAttributes="relation:assignee"
~assignee=@My assignee</code></pre>
</td>
</tr>
<tr>
<td><code>#calendar:startDate</code>
</td>
<td>Allows using a different label to represent the start date, other than <code>#startDate</code> (e.g. <code>#expiryDate</code>).
The label name must be prefixed with <code>#</code>. If the label is not
defined for a note, the default will be used instead.</td>
</tr>
<tr>
<td><code>#calendar:endDate</code>
</td>
<td>Allows using a different label to represent the start date, other than <code>#endDate</code>.
The label name must be prefixed with <code>#</code>. If the label is not
defined for a note, the default will be used instead.</td>
</tr>
</tbody>
</table>
</figure>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -5,12 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Geo Map</title>
<title data-trilium-title>Geo map</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Geo Map</h1>
<h1 data-trilium-h1>Geo map</h1>
<div class="ck-content">
<h2>Creating a new geo map</h2>
@ -26,7 +26,7 @@
<th>1</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:1256/1044;" src="9_Geo Map_image.png" width="1256"
<img style="aspect-ratio:1256/1044;" src="7_Geo map_image.png" width="1256"
height="1044">
</figure>
</td>
@ -36,7 +36,7 @@
<th>2</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:1720/1396;" src="3_Geo Map_image.png" width="1720"
<img style="aspect-ratio:1720/1396;" src="2_Geo map_image.png" width="1720"
height="1396">
</figure>
</td>
@ -69,18 +69,18 @@
<p>To create a marker, first navigate to the desired point on the map. Then
press the
<img class="image_resized" style="aspect-ratio:72/66;width:7.37%;"
src="4_Geo Map_image.png" width="72" height="66">button on the top-right of the map.</p>
src="3_Geo map_image.png" width="72" height="66">button on the top-right of the map.</p>
<p>If the button is not visible, make sure the button section is visible
by pressing the chevron button (
<img class="image_resized" style="aspect-ratio:72/66;width:7.51%;"
src="10_Geo Map_image.png" width="72" height="66">) in the top-right of the map.</p>
src="8_Geo map_image.png" width="72" height="66">) in the top-right of the map.</p>
</td>
</tr>
<tr>
<th>2</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:1730/416;" src="14_Geo Map_image.png" width="1730"
<img style="aspect-ratio:1730/416;" src="12_Geo map_image.png" width="1730"
height="416">
</figure>
<p>&nbsp;</p>
@ -96,7 +96,7 @@
<th>3</th>
<td>
<figure class="image">
<img style="aspect-ratio:1586/404;" src="1_Geo Map_image.png" width="1586"
<img style="aspect-ratio:1586/404;" src="Geo map_image.png" width="1586"
height="404">
</figure>
<p>&nbsp;</p>
@ -107,7 +107,7 @@
<th>4</th>
<td>
<figure class="image">
<img style="aspect-ratio:1696/608;" src="6_Geo Map_image.png" width="1696"
<img style="aspect-ratio:1696/608;" src="5_Geo map_image.png" width="1696"
height="608">
</figure>
<p>&nbsp;</p>
@ -122,7 +122,7 @@
<p>The location of a marker is stored in the <code>#geolocation</code> attribute
of the child notes:</p>
<figure class="image">
<img style="aspect-ratio:1288/278;" src="12_Geo Map_image.png" width="1288"
<img style="aspect-ratio:1288/278;" src="10_Geo map_image.png" width="1288"
height="278">
</figure>
<p>This value can be added manually if needed. The value of the attribute
@ -155,6 +155,13 @@
</ul>
</li>
</ul>
<h2>Icon and color of the markers</h2>
<p>
<img src="18_Geo map_image.png" alt="image" width="523" height="295">
</p>
<p>The markers will have the same icon as the note.</p>
<p>It's possible to add a custom color to a marker by assigning them a <code>#color</code> attribute
such as <code>#color=green</code>.</p>
<h2>Adding the coordinates manually</h2>
<p>In a nutshell, create a child note and set the <code>#geolocation</code> attribute
to the coordinates.</p>
@ -168,7 +175,7 @@
<th>1</th>
<td>
<figure class="image image-style-align-center image_resized" style="width:100%;">
<img style="aspect-ratio:732/918;" src="16_Geo Map_image.png" width="732"
<img style="aspect-ratio:732/918;" src="14_Geo map_image.png" width="732"
height="918">
</figure>
</td>
@ -185,7 +192,7 @@
<th>2</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:518/84;" src="19_Geo Map_image.png" width="518"
<img style="aspect-ratio:518/84;" src="16_Geo map_image.png" width="518"
height="84">
</figure>
</td>
@ -199,7 +206,7 @@
<th>3</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:1074/276;" src="11_Geo Map_image.png" width="1074"
<img style="aspect-ratio:1074/276;" src="9_Geo map_image.png" width="1074"
height="276">
</figure>
</td>
@ -225,7 +232,7 @@
<th>1</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:562/454;" src="17_Geo Map_image.png" width="562"
<img style="aspect-ratio:562/454;" src="15_Geo map_image.png" width="562"
height="454">
</figure>
</td>
@ -236,7 +243,7 @@
<th>2</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:696/480;" src="13_Geo Map_image.png" width="696"
<img style="aspect-ratio:696/480;" src="11_Geo map_image.png" width="696"
height="480">
</figure>
</td>
@ -250,7 +257,7 @@
<th>3</th>
<td>
<figure class="image">
<img style="aspect-ratio:640/276;" src="2_Geo Map_image.png" width="640"
<img style="aspect-ratio:640/276;" src="1_Geo map_image.png" width="640"
height="276">
</figure>
</td>
@ -275,7 +282,7 @@
<th>1</th>
<td>
<figure class="image">
<img style="aspect-ratio:226/74;" src="7_Geo Map_image.png" width="226"
<img style="aspect-ratio:226/74;" src="6_Geo map_image.png" width="226"
height="74">
</figure>
</td>
@ -286,7 +293,7 @@
<th>2</th>
<td>
<figure class="image">
<img style="aspect-ratio:322/222;" src="5_Geo Map_image.png" width="322"
<img style="aspect-ratio:322/222;" src="4_Geo map_image.png" width="322"
height="222">
</figure>
</td>
@ -297,7 +304,7 @@
<th>3</th>
<td>
<figure class="image image_resized" style="width:100%;">
<img style="aspect-ratio:620/530;" src="15_Geo Map_image.png" width="620"
<img style="aspect-ratio:620/530;" src="13_Geo map_image.png" width="620"
height="530">
</figure>
</td>
@ -310,9 +317,16 @@
</tbody>
</table>
</figure>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2>Troubleshooting</h2>
<h3>Grid-like artifacts on the map</h3>
<p>
<img class="image_resized" style="aspect-ratio:678/499;width:58%;" src="17_Geo map_image.png"
width="678" height="499">
</p>
<p>This occurs if the application is not at 100% zoom which causes the pixels
of the map to not render correctly due to fractional scaling. The only
possible solution i to set the UI zoom at 100% (default keyboard shortcut
is Ctrl+0).</p>
<p>&nbsp;</p>
</div>
</div>

View File

@ -1,19 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Text</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Text</h1>
<div class="ck-content"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,34 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../../style.css">
<base target="_parent">
<title data-trilium-title>Content language</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Content language</h1>
<div class="ck-content">
<p>A language hint can be provided for text notes. This option informs the
browser or the desktop application about the language the note is written
in (for example this might help with spellchecking), and it also determines
whether the text is displayed from right-to-left for languages such as
Arabic, Hebrew, etc.</p>
<p>For more information about right-to-left support, see&nbsp;<a class="reference-link"
href="../../New%20Features/Right-to-left%20text%20notes.html">Right-to-left text notes</a>.</p>
<p>To set the language of the content, go to “Basic Properties” and look
for the “Language” field. By default there will be no content languages
set, they can be configured by going to settings or by selecting the “Configure
languages” item in the list.</p>
<p>
<img src="Content language_image.png" width="635" height="492">
</p>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -1,94 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Creating a custom theme</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Creating a custom theme</h1>
<div class="ck-content">
<h2>Step 1. Find a place to place the themes</h2>
<p>Organization is an important aspect of managing a knowledge base. When
developing a new theme or importing an existing one it's a good idea to
keep them into one place.</p>
<p>As such, the first step is to create a new note to gather all the themes.</p>
<p>
<img src="5_Creating a custom theme_im.png" width="181" height="84">
</p>
<h2>Step 2. Create the theme</h2>
<figure class="table" style="width:100%;">
<table class="ck-table-resized">
<colgroup>
<col style="width:32.47%;">
<col style="width:67.53%;">
</colgroup>
<tbody>
<tr>
<td>
<figure class="image">
<img style="aspect-ratio:651/220;" src="3_Creating a custom theme_im.png"
width="651" height="220">
</figure>
</td>
<td style="vertical-align:top;">Themes are code notes with a special attribute. Start by creating a new
code note.</td>
</tr>
<tr>
<td>
<figure class="image">
<img style="aspect-ratio:302/349;" src="1_Creating a custom theme_im.png"
width="302" height="349">
</figure>
</td>
<td style="vertical-align:top;">Then change the note type to a CSS code.</td>
</tr>
<tr>
<td>
<figure class="image">
<img style="aspect-ratio:316/133;" src="Creating a custom theme_im.png"
width="316" height="133">
</figure>
</td>
<td style="vertical-align:top;">In the <i>Owned Attributes</i> section define the <code>#appTheme</code> attribute
to point to any desired name. This is the name that will show up in the
appearance section in settings.</td>
</tr>
</tbody>
</table>
</figure>
<h2>Step 3. Define the theme's CSS</h2>
<p>As a very simple example we will change the background color of the launcher
pane to a shade of blue.</p>
<p>To alter the different variables of the theme:</p><pre><code class="language-text-css">:root {
--launcher-pane-background-color: #0d6efd;
}</code></pre>
<h2>Step 4. Activating the theme</h2>
<p>Refresh the application (Ctrl+Shift+R is a good way to do so) and go to
settings. You should see the newly created theme:</p>
<p>
<img src="2_Creating a custom theme_im.png" width="631" height="481">
</p>
<p>Afterwards the application will refresh itself with the new theme:</p>
<p>
<img src="4_Creating a custom theme_im.png" width="653" height="554">
</p>
<p>Do note that the theme will be based off of the legacy theme. To override
that and base the theme on the new TriliumNext theme, see:&nbsp;<a class="reference-link"
href="Customize%20the%20Next%20theme.html">Theme base (legacy vs. next)</a>
</p>
<h2>Step 5. Making changes</h2>
<p>Simply go back to the note and change according to needs. To apply the
changes to the current window, press Ctrl+Shift+R to refresh.</p>
<p>It's a good idea to keep two windows, one for editing and the other one
for previewing the changes.</p>
</div>
</div>
</body>
</html>

View File

@ -1,36 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Customize the Next theme</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Customize the Next theme</h1>
<div class="ck-content">
<p>By default, any custom theme will be based on the legacy light theme.
To use the TriliumNext theme instead, add the <code>#appThemeBase=next</code> attribute
onto the existing theme. The <code>appTheme</code> attribute must also be
present.</p>
<p>
<img src="Customize the Next theme_i.png" width="424" height="140">
</p>
<p>When <code>appThemeBase</code> is set to <code>next</code> it will use the
“TriliumNext (auto)” theme. Any other value is ignored and will use the
legacy white theme instead.</p>
<h2>Overrides</h2>
<p>Do note that the TriliumNext theme has a few more overrides than the legacy
theme, so you might need to suffix <code>!important</code> if the style changes
are not applied.</p><pre><code class="language-text-css">:root {
--launcher-pane-background-color: #0d6efd !important;
}</code></pre>
</div>
</div>
</body>
</html>

View File

@ -1,180 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../style.css">
<base target="_parent">
<title data-trilium-title>Reference</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>Reference</h1>
<div class="ck-content">
<h2>Detecting mobile vs. desktop</h2>
<p>The mobile layout is different than the one on the desktop. Use <code>body.mobile</code> and <code>body.desktop</code> to
differentiate between them.</p><pre><code class="language-text-css">body.mobile #root-widget {
/* Do something on mobile */
}
body.desktop #root-widget {
/* Do something on desktop */
}</code></pre>
<p>Do note that there is also a “tablet mode” in the mobile layout. For that
particular case media queries are required:</p><pre><code class="language-text-css">@media (max-width: 991px) {
#launcher-pane {
/* Do something on mobile layout */
}
}
@media (min-width: 992px) {
#launcher-pane {
/* Do something on mobile tablet + desktop layout */
}
}</code></pre>
<h2>Detecting horizontal vs. vertical layout</h2>
<p>The user can select between vertical layout (the classical one, where
the launcher bar is on the left) and a horizontal layout (where the launcher
bar is on the top and tabs are full-width).</p>
<p>Different styles can be applied by using classes at <code>body</code> level:</p><pre><code class="language-text-x-trilium-auto">body.layout-vertical #left-pane {
/* Do something */
}
body.layout-horizontal #center-pane {
/* Do something else */
}</code></pre>
<p>The two different layouts use different containers (but they are present
in the DOM regardless of the user's choice), for example <code>#horizontal-main-container</code> and <code>#vertical-main-container</code> can
be used to customize the background of the content section.</p>
<h2>Detecting platform (Windows, macOS) or Electron</h2>
<p>It is possible to add particular styles that only apply to a given platform
by using the classes in <code>body</code>:</p>
<figure class="table">
<table>
<thead>
<tr>
<th>Windows</th>
<th>macOS</th>
</tr>
</thead>
<tbody>
<tr>
<td><pre><code class="language-text-x-trilium-auto">body.platform-win32 {
background: red;
}</code></pre>
</td>
<td><pre><code class="language-text-x-trilium-auto">body.platform-darwin {
background: red;
}</code></pre>
</td>
</tr>
</tbody>
</table>
</figure>
<p>It is also possible to only apply a style if running under Electron (desktop
application):</p><pre><code class="language-text-x-trilium-auto">body.electron {
background: blue;
}</code></pre>
<h3>Native title bar</h3>
<p>It's possible to detect if the user has selected the native title bar
or the custom title bar by querying against <code>body</code>:</p><pre><code class="language-text-x-trilium-auto">body.electron.native-titlebar {
/* Do something */
}
body.electron:not(.native-titlebar) {
/* Do something else */
}</code></pre>
<h3>Native window buttons</h3>
<p>When running under Electron with native title bar off, a feature was introduced
to use the platform-specific window buttons such as the semaphore on macOS.</p>
<p>See <a href="https://github.com/TriliumNext/Notes/pull/702">Native title bar buttons by eliandoran · Pull Request #702 · TriliumNext/Notes</a> for
the original implementation of this feature, including screenshots.</p>
<h4>On Windows</h4>
<p>The colors of the native window button area can be adjusted using a RGB
hex color:</p><pre><code class="language-text-x-trilium-auto">body {
--native-titlebar-foreground: #ffffff;
--native-titlebar-background: #ff0000;
}</code></pre>
<p>It is also possible to use transparency at the cost of reduced hover colors
using a RGBA hex color:</p><pre><code class="language-text-x-trilium-auto">body {
--native-titlebar-background: #ff0000aa;
}</code></pre>
<p>Note that the value is read when the window is initialized and then it
is refreshed only when the user changes their light/dark mode preference.</p>
<h4>On macOS</h4>
<p>On macOS the semaphore window buttons are enabled by default when the
native title bar is disabled. The offset of the buttons can be adjusted
using:</p><pre><code class="language-text-css">body {
--native-titlebar-darwin-x-offset: 12;
--native-titlebar-darwin-y-offset: 14 !important;
}</code></pre>
<h3>Background/transparency effects on Windows (Mica)</h3>
<p>Windows 11 offers a special background/transparency effect called Mica,
which can be enabled by themes by setting the <code>--background-material</code> variable
at <code>body</code> level:</p><pre><code class="language-text-css">body.electron.platform-win32 {
--background-material: tabbed;
}</code></pre>
<p>The value can be either <code>tabbed</code> (especially useful for the horizontal
layout) or <code>mica</code> (ideal for the vertical layout).</p>
<p>Do note that the Mica effect is applied at <code>body</code> level and the
theme needs to make the entire hierarchy (semi-)transparent in order for
it to be visible. Use the TrilumNext theme as an inspiration.</p>
<h2>Note icons, tab workspace accent color</h2>
<p>Theme capabilities are small adjustments done through CSS variables that
can affect the layout or the visual aspect of the application.</p>
<p>In the tab bar, to display the icons of notes instead of the icon of the
workspace:</p><pre><code class="language-text-css">:root {
--tab-note-icons: true;
}</code></pre>
<p>When a workspace is hoisted for a given tab, it is possible to get the
background color of that workspace, for example to apply a small strip
on the tab instead of the whole background color:</p><pre><code class="language-text-css">.note-tab .note-tab-wrapper {
--tab-background-color: initial !important;
}
.note-tab .note-tab-wrapper::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background-color: var(--workspace-tab-background-color);
}</code></pre>
<h2>Custom fonts</h2>
<p>Currently the only way to include a custom font is to use&nbsp;<a class="reference-link"
href="../Advanced%20topics/Custom%20resource%20providers.html">Custom resource providers</a>.
Basically import a font into Trilium and assign it <code>#customResourceProvider=fonts/myfont.ttf</code> and
then import the font in CSS via <code>/custom/fonts/myfont.ttf</code>.</p>
<h2>Dark and light themes</h2>
<p>A light theme needs to have the following CSS:</p><pre><code class="language-text-css">:root {
--theme-style: light;
}</code></pre>
<p>if the theme is dark, then <code>--theme-style</code> needs to be <code>dark</code>.</p>
<p>If the theme is auto (e.g. supports both light or dark based on <code>prefers-color-scheme</code>)
it must also declare (in addition to setting <code>--theme-style</code> to
either <code>light</code> or <code>dark</code>):</p><pre><code class="language-text-css">:root {
--theme-style-auto: true;
}</code></pre>
<p>This will affect the behavior of the Electron application by informing
the operating system of the color preference (e.g. background effects will
appear correct on Windows).</p>
</div>
</div>
</body>
</html>

View File

@ -6,6 +6,6 @@
</head>
<frameset cols="25%,75%">
<frame name="navigation" src="navigation.html">
<frame name="detail" src="User%20Guide/Features/Export%20as%20PDF.html">
<frame name="detail" src="User%20Guide/New%20Features/Right-to-left%20text%20notes.html">
</frameset>
</html>

View File

@ -9,17 +9,24 @@
<ul>
<li>User Guide
<ul>
<li>Features
<li>New Features
<ul>
<li><a href="User%20Guide/Features/Export%20as%20PDF.html" target="detail">Export as PDF</a>
<li><a href="User%20Guide/New%20Features/Right-to-left%20text%20notes.html"
target="detail">Right-to-left text notes</a>
</li>
<li><a href="User%20Guide/Features/Zen%20mode.html" target="detail">Zen mode</a>
<li><a href="User%20Guide/New%20Features/Zen%20mode.html" target="detail">Zen mode</a>
</li>
<li><a href="User%20Guide/New%20Features/Export%20as%20PDF.html" target="detail">Export as PDF</a>
</li>
</ul>
</li>
<li>Note Types
<ul>
<li><a href="User%20Guide/Note%20Types/Text.html" target="detail">Text</a>
<li>Text
<ul>
<li><a href="User%20Guide/Note%20Types/Text/Content%20language.html" target="detail">Content language</a>
</li>
</ul>
</li>
<li><a href="User%20Guide/Note%20Types/Code.html" target="detail">Code</a>
</li>
@ -45,7 +52,7 @@
</li>
<li><a href="User%20Guide/Note%20Types/Mind%20Map.html" target="detail">Mind Map</a>
</li>
<li><a href="User%20Guide/Note%20Types/Geo%20Map.html" target="detail">Geo Map</a>
<li><a href="User%20Guide/Note%20Types/Geo%20map.html" target="detail">Geo map</a>
</li>
</ul>
</li>
@ -56,18 +63,6 @@
</li>
</ul>
</li>
<li>Theme development
<ul>
<li><a href="User%20Guide/Theme%20development/Creating%20a%20custom%20theme.html"
target="detail">Creating a custom theme</a>
</li>
<li><a href="User%20Guide/Theme%20development/Customize%20the%20Next%20theme.html"
target="detail">Customize the Next theme</a>
</li>
<li><a href="User%20Guide/Theme%20development/Reference.html" target="detail">Reference</a>
</li>
</ul>
</li>
<li>Scripting
<ul>
<li>Examples

View File

@ -28,8 +28,7 @@ const NOTE_TYPE_ICONS = {
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap",
geoMap: "bx bx-map-alt",
taskList: "bx bx-list-check"
geoMap: "bx bx-map-alt"
};
/**
@ -37,25 +36,7 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType =
| "file"
| "image"
| "search"
| "noteMap"
| "launcher"
| "doc"
| "contentWidget"
| "text"
| "relationMap"
| "render"
| "canvas"
| "mermaid"
| "book"
| "webView"
| "code"
| "mindMap"
| "geoMap"
| "taskList";
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap";
export interface NotePathRecord {
isArchived: boolean;

View File

@ -1,34 +0,0 @@
import type { Froca } from "../services/froca-interface.js";
export interface FTaskRow {
taskId: string;
parentNoteId: string;
title: string;
dueDate?: string;
isDone?: boolean;
utcDateModified: string;
}
export default class FTask {
private froca: Froca;
taskId!: string;
parentNoteId!: string;
title!: string;
dueDate?: string;
isDone!: boolean;
utcDateModified!: string;
constructor(froca: Froca, row: FTaskRow) {
this.froca = froca;
this.update(row);
}
update(row: FTaskRow) {
this.taskId = row.taskId;
this.parentNoteId = row.parentNoteId;
this.title = row.title;
this.dueDate = row.dueDate;
this.isDone = !!row.isDone;
this.utcDateModified = row.utcDateModified;
}
}

View File

@ -23,6 +23,28 @@ async function removeAttributeById(noteId: string, attributeId: string) {
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
}
/**
* Sets the attribute of the given note to the provided value if its truthy, or removes the attribute if the value is falsy.
* For an attribute with an empty value, pass an empty string instead.
*
* @param note the note to set the attribute to.
* @param type the type of attribute (label or relation).
* @param name the name of the attribute to set.
* @param value the value of the attribute to set.
*/
async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
if (value) {
// Create or update the attribute.
await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value });
} else {
// Remove the attribute if it exists on the server but we don't define a value for it.
const attributeId = note.getAttribute(type, name)?.attributeId;
if (attributeId) {
await server.remove(`notes/${note.noteId}/attributes/${attributeId}`);
}
}
}
/**
* @returns - returns true if this attribute has the potential to influence the note in the argument.
* That can happen in multiple ways:
@ -66,6 +88,7 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
export default {
addLabel,
setLabel,
setAttribute,
removeAttributeById,
isAffecting
};

View File

@ -11,7 +11,7 @@ import FNote from "../entities/fnote.js";
import FAttachment from "../entities/fattachment.js";
import imageContextMenuService from "../menus/image_context_menu.js";
import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js";
import { loadElkIfNeeded } from "./mermaid.js";
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
import { normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js";
let idCounter = 1;
@ -226,7 +226,7 @@ async function renderMermaid(note: FNote | FAttachment, $renderedContent: JQuery
await loadElkIfNeeded(content);
const { svg } = await mermaid.mermaidAPI.render("in-mermaid-graph-" + idCounter++, content);
$renderedContent.append($(svg));
$renderedContent.append($(postprocessMermaidSvg(svg)));
} catch (e) {
const $error = $("<p>The diagram could not displayed.</p>");

View File

@ -0,0 +1,48 @@
import { lint } from "./eslint.js";
import { trimIndentation } from "../../../../spec/support/utils.js";
import { describe, expect, it } from "vitest";
describe("Linter", () => {
it("reports some basic errors", async () => {
const result = await lint(trimIndentation`
for (const i = 0; i<10; i++) {
}
`);
expect(result).toMatchObject([
{ message: "'i' is constant.", },
{ message: "Empty block statement." }
]);
});
it("reports no error for correct script", async () => {
const result = await lint(trimIndentation`
const foo = "bar";
console.log(foo.toString());
for (const x of [ 1, 2, 3]) {
console.log(x?.toString());
}
api.showMessage("Hi");
`);
expect(result.length).toBe(0);
});
it("reports unused functions as warnings", async () => {
const result = await lint(trimIndentation`
function hello() { }
function world() { }
console.log("Hello world");
`);
expect(result).toMatchObject([
{
message: "'hello' is defined but never used.",
severity: 1
},
{
message: "'world' is defined but never used.",
severity: 1
}
]);
});
});

View File

@ -0,0 +1,25 @@
export async function lint(code: string) {
const Linter = (await import("eslint-linter-browserify")).Linter;
const js = (await import("@eslint/js"));
const globals = (await import("globals"));
return new Linter().verify(code, [
js.configs.recommended,
{
languageOptions: {
parserOptions: {
ecmaVersion: 2024
},
globals: {
...globals.browser,
api: "readonly"
},
},
rules: {
"no-unused-vars": [ "warn", { vars: "local", args: "after-used" }]
}
}
]);
}

View File

@ -6,8 +6,6 @@ import appContext from "../components/app_context.js";
import FBlob, { type FBlobRow } from "../entities/fblob.js";
import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js";
import type { Froca } from "./froca-interface.js";
import FTask from "../entities/ftask.js";
import type { FTaskRow } from "../entities/ftask.js";
interface SubtreeResponse {
notes: FNoteRow[];
@ -39,7 +37,6 @@ class FrocaImpl implements Froca {
attributes!: Record<string, FAttribute>;
attachments!: Record<string, FAttachment>;
blobPromises!: Record<string, Promise<void | FBlob> | null>;
tasks!: Record<string, FTask>;
constructor() {
this.initializedPromise = this.loadInitialTree();
@ -55,7 +52,6 @@ class FrocaImpl implements Froca {
this.attributes = {};
this.attachments = {};
this.blobPromises = {};
this.tasks = {};
this.addResp(resp);
}
@ -372,24 +368,6 @@ class FrocaImpl implements Froca {
});
}
getTask(taskId: string) {
return this.tasks[taskId];
}
async getTasks(parentNoteId: string) {
const taskRows = await server.get<FTaskRow[]>(`tasks/${parentNoteId}`);
return this.processTaskRow(taskRows);
}
processTaskRow(taskRows: FTaskRow[]): FTask[] {
return taskRows.map((taskRow) => {
const task = new FTask(this, taskRow);
this.tasks[task.taskId] = task;
return task;
});
}
async getBlob(entityType: string, entityId: string) {
// I'm not sure why we're not using blobIds directly, it would save us this composite key ...
// perhaps one benefit is that we're always requesting the latest blob, not relying on perhaps faulty/slow

View File

@ -8,8 +8,6 @@ import FAttribute, { type FAttributeRow } from "../entities/fattribute.js";
import FAttachment, { type FAttachmentRow } from "../entities/fattachment.js";
import type { default as FNote, FNoteRow } from "../entities/fnote.js";
import type { EntityChange } from "../server_types.js";
import type { FTaskRow } from "../entities/ftask.js";
import FTask from "../entities/ftask.js";
async function processEntityChanges(entityChanges: EntityChange[]) {
const loadResults = new LoadResults(entityChanges);
@ -39,8 +37,6 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
processAttachment(loadResults, ec);
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens") {
// NOOP
} else if (ec.entityName === "tasks") {
processTaskChange(loadResults, ec);
} else {
throw new Error(`Unknown entityName '${ec.entityName}'`);
}
@ -310,35 +306,6 @@ function processAttachment(loadResults: LoadResults, ec: EntityChange) {
loadResults.addAttachmentRow(attachmentEntity);
}
function processTaskChange(loadResults: LoadResults, ec: EntityChange) {
if (ec.isErased && ec.entityId in froca.tasks) {
utils.reloadFrontendApp(`${ec.entityName} '${ec.entityId}' is erased, need to do complete reload.`);
return;
}
let task = froca.tasks[ec.entityId];
const taskEntity = ec.entity as FTaskRow;
if (ec.isErased || (ec.entity as any)?.isDeleted) {
if (task) {
delete froca.tasks[ec.entityId];
}
return;
}
if (ec.entity) {
if (task) {
task.update(ec.entity as FTaskRow);
} else {
task = new FTask(froca, ec.entity as FTaskRow);
froca.tasks[task.taskId] = task;
}
}
loadResults.addTaskRow(taskEntity);
}
export default {
processEntityChanges
};

Some files were not shown because too many files have changed in this diff Show More