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

This commit is contained in:
Elian Doran 2024-03-17 21:43:59 +02:00
commit 926b3e9650
No known key found for this signature in database
18 changed files with 2163 additions and 5675 deletions

View File

@ -1,7 +0,0 @@
node_modules
dist
bin
docs
libraries
coverage
play

View File

@ -1,212 +0,0 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true,
node: true,
},
// plugins: ['prettier'], // to be activated
extends: ['eslint:recommended', 'airbnb-base', 'plugin:jsonc/recommended-with-jsonc', 'prettier'],
overrides: [
{
files: ['*.json', '*.json5', '*.jsonc'],
parser: 'jsonc-eslint-parser',
},
{
files: ['package.json'],
parser: 'jsonc-eslint-parser',
rules: {
'jsonc/sort-keys': [
'off',
{
pathPattern: '^$',
order: [
'name',
'version',
'private',
'packageManager',
'description',
'type',
'keywords',
'homepage',
'bugs',
'license',
'author',
'contributors',
'funding',
'files',
'main',
'module',
'exports',
'unpkg',
'jsdelivr',
'browser',
'bin',
'man',
'directories',
'repository',
'publishConfig',
'scripts',
'peerDependencies',
'peerDependenciesMeta',
'optionalDependencies',
'dependencies',
'devDependencies',
'engines',
'config',
'overrides',
'pnpm',
'husky',
'lint-staged',
'eslintConfig',
],
},
{
pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies$',
order: { type: 'asc' },
},
],
},
},
],
globals: {
$: true,
jQuery: true,
glob: true,
log: true,
EditorWatchdog: true,
React: true,
appState: true,
ExcalidrawLib: true,
elements: true,
files: true,
ReactDOM: true,
// src\public\app\widgets\type_widgets\relation_map.js
jsPlumb: true,
panzoom: true,
logError: true,
// src\public\app\widgets\type_widgets\image.js
WZoom: true,
// \src\public\app\widgets\type_widgets\read_only_text.js
renderMathInElement: true,
// \src\public\app\widgets\type_widgets\editable_text.js
BalloonEditor: true,
FancytreeNode: true,
CKEditorInspector: true,
// \src\public\app\widgets\type_widgets\editable_code.js
CodeMirror: true,
// \src\public\app\services\resizer.js
Split: true,
// \src\public\app\services\content_renderer.js
mermaid: true,
// src\public\app\services\frontend_script_api.js
dayjs: true,
// \src\public\app\widgets\note_map.js
ForceGraph: true,
// \src\public\app\setup.js
ko: true,
syncInProgress: true,
// src\public\app\services\utils.js
logInfo: true,
__non_webpack_require__: true,
describe: true,
it: true,
expect: true
},
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
// eslint:recommended
'no-unused-vars': 'off',
'linebreak-style': 'off',
'no-useless-escape': 'off',
'no-empty': 'off',
'no-constant-condition': 'off',
'getter-return': 'off',
'no-cond-assign': 'off',
'no-async-promise-executor': 'off',
'no-extra-semi': 'off',
'no-inner-declarations': 'off',
// prettier
'prettier/prettier': ['off', { endOfLine: 'auto' }],
// airbnb-base
'no-console': 'off',
'no-plusplus': 'off',
'no-param-reassign': 'off',
'global-require': 'off',
'no-use-before-define': 'off',
'no-await-in-loop': 'off',
radix: 'off',
'import/order': 'off',
'import/no-extraneous-dependencies': 'off',
'prefer-destructuring': 'off',
'no-shadow': 'off',
'no-new': 'off',
'no-restricted-syntax': 'off',
strict: 'off',
'class-methods-use-this': 'off',
'no-else-return': 'off',
'import/no-dynamic-require': 'off',
'no-underscore-dangle': 'off',
'prefer-template': 'off',
'consistent-return': 'off',
'no-continue': 'off',
'object-shorthand': 'off',
'one-var': 'off',
'prefer-const': 'off',
'spaced-comment': 'off',
'no-loop-func': 'off',
'arrow-body-style': 'off',
'guard-for-in': 'off',
'no-return-assign': 'off',
'dot-notation': 'off',
'func-names': 'off',
'import/no-useless-path-segments': 'off',
'default-param-last': 'off',
'prefer-arrow-callback': 'off',
'no-unneeded-ternary': 'off',
'no-return-await': 'off',
'import/extensions': 'off',
'no-var': 'off',
'import/newline-after-import': 'off',
'no-restricted-globals': 'off',
'operator-assignment': 'off',
'no-eval': 'off',
'max-classes-per-file': 'off',
'vars-on-top': 'off',
'no-bitwise': 'off',
'no-lonely-if': 'off',
'no-multi-assign': 'off',
'no-promise-executor-return': 'off',
'no-empty-function': 'off',
'import/no-unresolved': 'off',
camelcase: 'off',
eqeqeq: 'off',
'lines-between-class-members': 'off',
'import/no-cycle': 'off',
'new-cap': 'off',
'prefer-object-spread': 'off',
'no-new-func': 'off',
'no-unused-expressions': 'off',
'lines-around-directive': 'off',
'prefer-exponentiation-operator': 'off',
'no-restricted-properties': 'off',
'prefer-rest-params': 'off',
'no-unreachable-loop': 'off',
'no-alert': 'off',
'no-useless-return': 'off',
'no-nested-ternary': 'off',
'prefer-regex-literals': 'off',
'import/no-named-as-default-member': 'off',
yoda: 'off',
'no-script-url': 'off',
'no-prototype-builtins':'off'
},
};

1
.husky/.gitignore vendored
View File

@ -1 +0,0 @@
_

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#npx lint-staged

View File

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

View File

@ -1,13 +0,0 @@
//https://prettier.io/docs/en/options.html
module.exports = {
semi: true,
trailingComma: 'none',
singleQuote: true,
printWidth: 100,
tabWidth: 4,
useTabs: false,
quoteProps: "as-needed",
bracketSpacing: true,
arrowParens: "avoid"
// htmlWhitespaceSensitivity: 'ignore',
};

View File

@ -1,6 +0,0 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
]
}

33
.vscode/settings.json vendored
View File

@ -1,34 +1,5 @@
{ {
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[json]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"editor.formatOnSave": true, "editor.formatOnSave": true,
"eslint.format.enable": true,
"eslint.probe": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"vue",
"markdown",
"json",
"jsonc"
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"vue",
"markdown",
"json",
"jsonc"
],
"files.eol": "\n", "files.eol": "\n",
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib"
} }

View File

@ -2,49 +2,37 @@
SRC_DIR=./dist/trilium-linux-x64-src SRC_DIR=./dist/trilium-linux-x64-src
if [ "$1" != "DONTCOPY" ] [ "$1" != "DONTCOPY" ] && ./bin/copy-trilium.sh "$SRC_DIR"
then
./bin/copy-trilium.sh $SRC_DIR
fi
rm -r $SRC_DIR/src/public/app-dist/*.mobile.* rm -r "$SRC_DIR"/src/public/app-dist/*.mobile.*
echo "Copying required linux-x64 binaries" echo "Copying required linux-x64 binaries"
cp -r bin/better-sqlite3/linux-desktop-better_sqlite3.node "$SRC_DIR"/node_modules/better-sqlite3/build/Release/better_sqlite3.node
cp -r bin/better-sqlite3/linux-desktop-better_sqlite3.node $SRC_DIR/node_modules/better-sqlite3/build/Release/better_sqlite3.node
echo "Packaging linux x64 electron build" echo "Packaging linux x64 electron build"
./node_modules/.bin/electron-packager "$SRC_DIR" --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
./node_modules/.bin/electron-packager $SRC_DIR --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
BUILD_DIR=./dist/trilium-linux-x64 BUILD_DIR=./dist/trilium-linux-x64
rm -rf $BUILD_DIR rm -rf "$BUILD_DIR"
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR mv "./dist/Trilium Notes-linux-x64" "$BUILD_DIR"
cp images/app-icons/png/128x128.png $BUILD_DIR/icon.png cp images/app-icons/png/128x128.png "$BUILD_DIR"/icon.png
cp bin/tpl/anonymize-database.sql "$BUILD_DIR"/
cp bin/tpl/anonymize-database.sql $BUILD_DIR/ cp -r dump-db "$BUILD_DIR"/
rm -rf "$BUILD_DIR"/dump-db/node_modules
cp -r dump-db $BUILD_DIR/ for f in 'trilium-portable' 'trilium-safe-mode' 'trilium-no-cert-check'; do
rm -rf $BUILD_DIR/dump-db/node_modules cp bin/tpl/"$f".sh "$BUILD_DIR"/
chmod 755 "$BUILD_DIR"/"$f".sh
cp bin/tpl/trilium-portable.sh $BUILD_DIR/ done
chmod 755 $BUILD_DIR/trilium-portable.sh
cp bin/tpl/trilium-safe-mode.sh $BUILD_DIR/
chmod 755 $BUILD_DIR/trilium-safe-mode.sh
cp bin/tpl/trilium-no-cert-check.sh $BUILD_DIR/
chmod 755 $BUILD_DIR/trilium-no-cert-check.sh
echo "Packaging linux x64 electron distribution..." echo "Packaging linux x64 electron distribution..."
VERSION=`jq -r ".version" package.json` VERSION=`jq -r ".version" package.json`
cd dist pushd dist
tar cJf "trilium-linux-x64-${VERSION}.tar.xz" trilium-linux-x64
tar cJf trilium-linux-x64-${VERSION}.tar.xz trilium-linux-x64 popd
cd ..
bin/build-debian.sh bin/build-debian.sh

View File

@ -4,47 +4,49 @@ if [[ $# -eq 0 ]] ; then
echo "Missing argument of target directory" echo "Missing argument of target directory"
exit 1 exit 1
fi fi
if ! [[ $(which npm) ]]; then
echo "Missing npm"
exit 1
fi
n exec 18.18.2 npm run webpack n exec 18.18.2 npm run webpack || npm run webpack
DIR=$1 DIR="$1"
rm -rf $DIR rm -rf "$DIR"
mkdir $DIR mkdir -pv "$DIR"
echo "Copying Trilium to build directory $DIR" echo "Copying Trilium to build directory $DIR"
cp -r images $DIR/ for d in 'images' 'libraries' 'src' 'db'; do
cp -r libraries $DIR/ cp -r "$d" "$DIR"/
cp -r src $DIR/ done
cp -r db $DIR/ for f in 'package.json' 'package-lock.json' 'README.md' 'LICENSE' 'config-sample.ini' 'electron.js'; do
cp -r package.json $DIR/ cp "$f" "$DIR"/
cp -r package-lock.json $DIR/ done
cp -r README.md $DIR/ cp webpack-* "$DIR"/ # here warning because there is no 'webpack-*', but webpack.config.js only
cp -r LICENSE $DIR/
cp -r config-sample.ini $DIR/
cp -r electron.js $DIR/
cp webpack-* $DIR/
# run in subshell (so we return to original dir) # run in subshell (so we return to original dir)
(cd $DIR && n exec 18.18.2 npm install --only=prod) (cd $DIR && n exec 18.18.2 npm install --only=prod)
if [[ -d "$DIR"/node_modules ]]; then
# cleanup of useless files in dependencies # cleanup of useless files in dependencies
rm -r $DIR/node_modules/image-q/demo for d in 'image-q/demo' 'better-sqlite3/Release' 'better-sqlite3/deps/sqlite3.tar.gz' '@jimp/plugin-print/fonts' 'jimp/browser' 'jimp/fonts'; do
rm -r $DIR/node_modules/better-sqlite3/Release [[ -e "$DIR"/node_modules/"$d" ]] && rm -rv "$DIR"/node_modules/"$d"
rm -r $DIR/node_modules/better-sqlite3/deps/sqlite3.tar.gz done
rm -r $DIR/node_modules/@jimp/plugin-print/fonts
rm -r $DIR/node_modules/jimp/browser
rm -r $DIR/node_modules/jimp/fonts
# delete all tests (there are often large images as test file for jimp etc.) # delete all tests (there are often large images as test file for jimp etc.)
find $DIR/node_modules -name test -exec rm -rf {} \; for d in 'test' 'docs' 'demo'; do
find $DIR/node_modules -name docs -exec rm -rf {} \; find "$DIR"/node_modules -name "$d" -exec rm -rf {} \;
find $DIR/node_modules -name demo -exec rm -rf {} \; done
fi
find $DIR/libraries -name "*.map" -type f -delete find $DIR/libraries -name "*.map" -type f -delete
cp $DIR/src/public/app/share.js $DIR/src/public/app-dist/ d="$DIR"/src/public
cp -r $DIR/src/public/app/doc_notes $DIR/src/public/app-dist/ [[ -d "$d"/app-dist ]] || mkdir -pv "$d"/app-dist
cp "$d"/app/share.js "$d"/app-dist/
cp -r "$d"/app/doc_notes "$d"/app-dist/
rm -rf $DIR/src/public/app rm -rf "$d"/app
unset f d DIR

7289
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.63.1-beta", "version": "0.63.3",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {
@ -31,21 +31,19 @@
"test-jasmine": "TRILIUM_DATA_DIR=~/trilium/data-test jasmine", "test-jasmine": "TRILIUM_DATA_DIR=~/trilium/data-test jasmine",
"test-es6": "node -r esm spec-es6/attribute_parser.spec.js ", "test-es6": "node -r esm spec-es6/attribute_parser.spec.js ",
"test": "npm run test-jasmine && npm run test-es6", "test": "npm run test-jasmine && npm run test-es6",
"postinstall": "rimraf ./node_modules/canvas", "postinstall": "rimraf ./node_modules/canvas"
"lint": "eslint . --cache",
"prepare": "husky install || echo 'Husky install failed, expected on flatpak build'"
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "6.0.4", "@braintree/sanitize-url": "6.0.4",
"@electron/remote": "2.1.0", "@electron/remote": "2.1.2",
"@excalidraw/excalidraw": "0.16.1", "@excalidraw/excalidraw": "0.17.3",
"@types/cls-hooked": "^4.3.8", "@types/cls-hooked": "^4.3.8",
"archiver": "6.0.1", "archiver": "7.0.0",
"async-mutex": "0.4.0", "async-mutex": "0.4.1",
"axios": "1.6.2", "axios": "1.6.7",
"better-sqlite3": "8.4.0", "better-sqlite3": "8.4.0",
"boxicons": "2.1.4", "boxicons": "2.1.4",
"chokidar": "3.5.3", "chokidar": "3.6.0",
"cls-hooked": "4.2.2", "cls-hooked": "4.2.2",
"compression": "1.7.4", "compression": "1.7.4",
"cookie-parser": "1.4.6", "cookie-parser": "1.4.6",
@ -55,35 +53,35 @@
"debounce": "1.2.1", "debounce": "1.2.1",
"ejs": "3.1.9", "ejs": "3.1.9",
"electron-debug": "3.2.0", "electron-debug": "3.2.0",
"electron-dl": "3.5.1", "electron-dl": "3.5.2",
"electron-window-state": "5.0.3", "electron-window-state": "5.0.3",
"escape-html": "1.0.3", "escape-html": "1.0.3",
"express": "4.18.2", "express": "4.18.3",
"express-partial-content": "1.0.2", "express-partial-content": "1.0.2",
"express-rate-limit": "7.1.4", "express-rate-limit": "7.2.0",
"express-session": "1.17.3", "express-session": "1.18.0",
"force-graph": "1.43.4", "force-graph": "1.43.5",
"fs-extra": "11.1.1", "fs-extra": "11.2.0",
"helmet": "7.1.0", "helmet": "7.1.0",
"html": "1.0.0", "html": "1.0.0",
"html2plaintext": "2.1.4", "html2plaintext": "2.1.4",
"http-proxy-agent": "7.0.0", "http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.4",
"image-type": "4.1.0", "image-type": "4.1.0",
"ini": "3.0.1", "ini": "3.0.1",
"is-animated": "2.0.2", "is-animated": "2.0.2",
"is-svg": "4.3.2", "is-svg": "4.3.2",
"jimp": "0.22.10", "jimp": "0.22.12",
"joplin-turndown-plugin-gfm": "1.0.12", "joplin-turndown-plugin-gfm": "1.0.12",
"jquery": "3.7.1", "jquery": "3.7.1",
"jquery-hotkeys": "0.2.2", "jquery-hotkeys": "0.2.2",
"jsdom": "22.1.0", "jsdom": "24.0.0",
"katex": "0.16.9", "katex": "0.16.9",
"marked": "9.1.6", "marked": "12.0.0",
"mermaid": "10.6.1", "mermaid": "10.9.0",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"node-abi": "3.51.0", "node-abi": "3.56.0",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"open": "8.4.1", "open": "8.4.1",
"panzoom": "9.4.3", "panzoom": "9.4.3",
@ -95,21 +93,21 @@
"rimraf": "5.0.5", "rimraf": "5.0.5",
"safe-compare": "1.1.4", "safe-compare": "1.1.4",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"sanitize-html": "2.11.0", "sanitize-html": "2.12.1",
"sax": "1.3.0", "sax": "1.3.0",
"semver": "7.5.4", "semver": "7.6.0",
"serve-favicon": "2.5.0", "serve-favicon": "2.5.0",
"session-file-store": "1.5.0", "session-file-store": "1.5.0",
"split.js": "1.6.5", "split.js": "1.6.5",
"stream-throttle": "0.1.3", "stream-throttle": "0.1.3",
"striptags": "3.2.0", "striptags": "3.2.0",
"tmp": "0.2.1", "tmp": "0.2.3",
"tree-kill": "1.2.2", "tree-kill": "1.2.2",
"turndown": "7.1.2", "turndown": "7.1.2",
"unescape": "1.0.1", "unescape": "1.0.1",
"ws": "8.14.2", "ws": "8.16.0",
"xml2js": "0.6.2", "xml2js": "0.6.2",
"yauzl": "2.10.0" "yauzl": "3.1.2"
}, },
"devDependencies": { "devDependencies": {
"@types/better-sqlite3": "^7.6.9", "@types/better-sqlite3": "^7.6.9",
@ -121,35 +119,22 @@
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"electron": "25.9.8", "electron": "25.9.8",
"electron-builder": "24.6.4", "electron-builder": "24.13.3",
"electron-packager": "17.1.2", "electron-packager": "17.1.2",
"electron-rebuild": "3.2.9", "electron-rebuild": "3.2.9",
"eslint": "8.54.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-jsonc": "2.10.0",
"eslint-plugin-prettier": "5.0.1",
"esm": "3.2.25", "esm": "3.2.25",
"husky": "8.0.3",
"jasmine": "5.1.0", "jasmine": "5.1.0",
"jsdoc": "4.0.2", "jsdoc": "4.0.2",
"jsonc-eslint-parser": "2.4.0",
"lint-staged": "15.1.0",
"lorem-ipsum": "2.0.8", "lorem-ipsum": "2.0.8",
"nodemon": "3.0.1", "nodemon": "3.1.0",
"prettier": "3.1.0",
"rcedit": "4.0.1", "rcedit": "4.0.1",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"webpack": "5.89.0", "webpack": "5.90.3",
"webpack-cli": "5.1.4" "webpack-cli": "5.1.4"
}, },
"optionalDependencies": { "optionalDependencies": {
"electron-installer-debian": "3.2.0" "electron-installer-debian": "3.2.0"
},
"lint-staged": {
"*.js": "eslint --cache --fix"
} }
} }

View File

@ -158,6 +158,13 @@ class BRevision extends AbstractBeccaEntity<BRevision> {
return this.getAttachments().filter(attachment => attachment.title === title)[0]; return this.getAttachments().filter(attachment => attachment.title === title)[0];
} }
/**
* Revisions are not soft-deletable, they are immediately hard-deleted (erased).
*/
eraseRevision() {
require("../../services/erase.js").eraseRevisions([this.revisionId]);
}
beforeSaving() { beforeSaving() {
super.beforeSaving(); super.beforeSaving();

View File

@ -4,6 +4,7 @@ import froca from "./froca.js";
import attributeRenderer from "./attribute_renderer.js"; import attributeRenderer from "./attribute_renderer.js";
import libraryLoader from "./library_loader.js"; import libraryLoader from "./library_loader.js";
import treeService from "./tree.js"; import treeService from "./tree.js";
import utils from "./utils.js";
const TPL = ` const TPL = `
<div class="note-list"> <div class="note-list">
@ -215,7 +216,11 @@ class NoteListRenderer {
if (highlightedTokens.length > 0) { if (highlightedTokens.length > 0) {
await libraryLoader.requireLibrary(libraryLoader.MARKJS); await libraryLoader.requireLibrary(libraryLoader.MARKJS);
this.highlightRegex = new RegExp(highlightedTokens.join("|"), 'gi'); const regex = highlightedTokens
.map(token => utils.escapeRegExp(token))
.join("|");
this.highlightRegex = new RegExp(regex, 'gi');
} else { } else {
this.highlightRegex = null; this.highlightRegex = null;
} }

View File

@ -1,10 +1,8 @@
import libraryLoader from "../../services/library_loader.js"; import libraryLoader from '../../services/library_loader.js';
import TypeWidget from "./type_widget.js"; import TypeWidget from './type_widget.js';
import utils from '../../services/utils.js'; import utils from '../../services/utils.js';
import linkService from '../../services/link.js'; import linkService from '../../services/link.js';
import debounce from "../../services/debounce.js"; import debounce from '../../services/debounce.js';
const {sleep} = utils;
const TPL = ` const TPL = `
<div class="canvas-widget note-detail-canvas note-detail-printable note-detail"> <div class="canvas-widget note-detail-canvas note-detail-printable note-detail">
@ -115,7 +113,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
this.currentSceneVersion = this.SCENE_VERSION_INITIAL; this.currentSceneVersion = this.SCENE_VERSION_INITIAL;
// will be overwritten // will be overwritten
this.excalidrawRef;
this.$render; this.$render;
this.$widget; this.$widget;
this.reactHandlers; // used to control react state this.reactHandlers; // used to control react state
@ -155,7 +152,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
const renderElement = this.$render.get(0); const renderElement = this.$render.get(0);
ReactDOM.unmountComponentAtNode(renderElement); ReactDOM.unmountComponentAtNode(renderElement);
ReactDOM.render(React.createElement(this.createExcalidrawReactApp), renderElement); const root = ReactDOM.createRoot(renderElement);
root.render(React.createElement(this.createExcalidrawReactApp));
}); });
return this.$widget; return this.$widget;
@ -179,9 +177,9 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
const blob = await note.getBlob(); const blob = await note.getBlob();
// before we load content into excalidraw, make sure excalidraw has loaded // before we load content into excalidraw, make sure excalidraw has loaded
while (!this.excalidrawRef?.current) { while (!this.excalidrawApi) {
console.log("excalidrawRef not yet loaded, sleep 200ms..."); console.log("excalidrawApi not yet loaded, sleep 200ms...");
await sleep(200); await utils.sleep(200);
} }
/** /**
@ -199,7 +197,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
collaborators: [] collaborators: []
}; };
this.excalidrawRef.current.updateScene(sceneData); this.excalidrawApi.updateScene(sceneData);
} }
else if (blob.content) { else if (blob.content) {
// load saved content into excalidraw canvas // load saved content into excalidraw canvas
@ -246,9 +244,9 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
fileArray.push(file); fileArray.push(file);
} }
this.excalidrawRef.current.updateScene(sceneData); this.excalidrawApi.updateScene(sceneData);
this.excalidrawRef.current.addFiles(fileArray); this.excalidrawApi.addFiles(fileArray);
this.excalidrawRef.current.history.clear(); this.excalidrawApi.history.clear();
} }
Promise.all( Promise.all(
@ -261,7 +259,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
} }
const libraryItems = blobs.map(blob => blob.getJsonContentSafely()).filter(item => !!item); const libraryItems = blobs.map(blob => blob.getJsonContentSafely()).filter(item => !!item);
this.excalidrawRef.current.updateLibrary({libraryItems, merge: false}); this.excalidrawApi.updateLibrary({libraryItems, merge: false});
}); });
// set initial scene version // set initial scene version
@ -275,17 +273,17 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
* this is automatically called after this.saveData(); * this is automatically called after this.saveData();
*/ */
async getData() { async getData() {
const elements = this.excalidrawRef.current.getSceneElements(); const elements = this.excalidrawApi.getSceneElements();
const appState = this.excalidrawRef.current.getAppState(); const appState = this.excalidrawApi.getAppState();
/** /**
* A file is not deleted, even though removed from canvas. Therefore, we only keep * A file is not deleted, even though removed from canvas. Therefore, we only keep
* files that are referenced by an element. Maybe this will change with a new excalidraw version? * files that are referenced by an element. Maybe this will change with a new excalidraw version?
*/ */
const files = this.excalidrawRef.current.getFiles(); const files = this.excalidrawApi.getFiles();
// parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share // parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share
const svg = await window.ExcalidrawLib.exportToSvg({ const svg = await ExcalidrawLib.exportToSvg({
elements, elements,
appState, appState,
exportPadding: 5, // 5 px padding exportPadding: 5, // 5 px padding
@ -321,7 +319,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
// this.libraryChanged is unset in dataSaved() // this.libraryChanged is unset in dataSaved()
// there's no separate method to get library items, so have to abuse this one // there's no separate method to get library items, so have to abuse this one
const libraryItems = await this.excalidrawRef.current.updateLibrary({merge: true}); const libraryItems = await this.excalidrawApi.updateLibrary({merge: true});
let position = 10; let position = 10;
@ -379,9 +377,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
createExcalidrawReactApp() { createExcalidrawReactApp() {
const React = window.React; const React = window.React;
const { Excalidraw } = window.ExcalidrawLib; const { Excalidraw } = window.ExcalidrawLib;
const excalidrawRef = React.useRef(null);
this.excalidrawRef = excalidrawRef;
const excalidrawWrapperRef = React.useRef(null); const excalidrawWrapperRef = React.useRef(null);
this.excalidrawWrapperRef = excalidrawWrapperRef; this.excalidrawWrapperRef = excalidrawWrapperRef;
const [dimensions, setDimensions] = React.useState({ const [dimensions, setDimensions] = React.useState({
@ -439,7 +434,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
React.createElement(Excalidraw, { React.createElement(Excalidraw, {
// this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu // this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu
theme: this.themeStyle, theme: this.themeStyle,
ref: excalidrawRef, excalidrawAPI: api => { this.excalidrawApi = api; },
width: dimensions.width, width: dimensions.width,
height: dimensions.height, height: dimensions.height,
onPaste: (data, event) => { onPaste: (data, event) => {
@ -483,8 +478,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
} }
getSceneVersion() { getSceneVersion() {
if (this.excalidrawRef) { if (this.excalidrawApi) {
const elements = this.excalidrawRef.current.getSceneElements(); const elements = this.excalidrawApi.getSceneElements();
return window.ExcalidrawLib.getSceneVersion(elements); return window.ExcalidrawLib.getSceneVersion(elements);
} else { } else {
return this.SCENE_VERSION_ERROR; return this.SCENE_VERSION_ERROR;

View File

@ -88,3 +88,7 @@ body .CodeMirror {
.excalidraw.theme--dark { .excalidraw.theme--dark {
--theme-filter: invert(80%) hue-rotate(180deg) !important; --theme-filter: invert(80%) hue-rotate(180deg) !important;
} }
body .todo-list input[type="checkbox"]:not(:checked):before {
border-color: var(--muted-text-color) !important;
}

View File

@ -1 +1 @@
module.exports = { buildDate:"2024-01-21T23:49:23+01:00", buildRevision: "4f8073daa7cff1b8b6737ae45792b2e87c2adf33" }; module.exports = { buildDate:"2024-03-03T06:58:18+01:00", buildRevision: "0ad337c8e806ba84d48d7b97aa46df52d9f236a8" };

View File

@ -111,11 +111,7 @@ class NoteContentFulltextExp extends Expression {
if (type === 'text' && mime === 'text/html') { if (type === 'text' && mime === 'text/html') {
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412 content = this.stripTags(content);
content = striptags(content, ['a'], ' ');
// at least the closing tag can be easily stripped
content = content.replace(/<\/a>/ig, "");
} }
content = content.replace(/&nbsp;/g, ' '); content = content.replace(/&nbsp;/g, ' ');
@ -123,6 +119,23 @@ class NoteContentFulltextExp extends Expression {
return content.trim(); return content.trim();
} }
stripTags(content) {
// we want to allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
// we want to insert space in place of block tags (because they imply text separation)
// but we don't want to insert text for typical formatting inline tags which can occur within one word
const linkTag = 'a';
const inlineFormattingTags = ['b', 'strong', 'em', 'i', 'span', 'big', 'small', 'font', 'sub', 'sup'];
// replace tags which imply text separation with a space
content = striptags(content, [linkTag, ...inlineFormattingTags], ' ');
// replace the inline formatting tags (but not links) without a space
content = striptags(content, [linkTag], '');
// at least the closing link tag can be easily stripped
return content.replace(/<\/a>/ig, "");
}
} }
module.exports = NoteContentFulltextExp; module.exports = NoteContentFulltextExp;