mirror of
https://github.com/zadam/trilium.git
synced 2025-11-03 03:46:37 +01:00
Compare commits
24 Commits
v0.27.4
...
v0.28.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd4db406de | ||
|
|
e50f9cd0a3 | ||
|
|
2b64cbce2c | ||
|
|
2797c942ab | ||
|
|
f1c3278874 | ||
|
|
424c22dcde | ||
|
|
5c223dfd12 | ||
|
|
6a3e7a5a8e | ||
|
|
f88cdac000 | ||
|
|
eeead90f32 | ||
|
|
b607857409 | ||
|
|
9268f88bc3 | ||
|
|
f7f0560a9f | ||
|
|
3d8905207e | ||
|
|
dbc312010b | ||
|
|
b115a7cf19 | ||
|
|
348562352c | ||
|
|
70fd917e7c | ||
|
|
d2b60764cd | ||
|
|
581b1fdaa5 | ||
|
|
3c19a712c0 | ||
|
|
cc27f16088 | ||
|
|
dffdb82288 | ||
|
|
62b44e3549 |
@@ -3,22 +3,18 @@
|
|||||||
BUILD_DIR=./dist/trilium-linux-x64
|
BUILD_DIR=./dist/trilium-linux-x64
|
||||||
rm -rf $BUILD_DIR
|
rm -rf $BUILD_DIR
|
||||||
|
|
||||||
# we build x64 as second so that we keep X64 binaries in node_modules for local development and server build
|
rm -r node_modules/sqlite3/lib/binding/*
|
||||||
echo "Rebuilding binaries for linux-x64"
|
|
||||||
./node_modules/.bin/electron-rebuild --arch=x64
|
|
||||||
|
|
||||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
|
cp -r bin/deps/linux-x64/sqlite/* node_modules/sqlite3/lib/binding/
|
||||||
|
|
||||||
|
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite
|
||||||
|
|
||||||
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
|
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
|
||||||
|
|
||||||
rm -r "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/*"
|
|
||||||
|
|
||||||
cp -r bin/deps/linux-x64/sqlite/electron* "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/"
|
|
||||||
|
|
||||||
rm -r $BUILD_DIR/resources/app/bin/deps
|
|
||||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||||
rm -r $BUILD_DIR/swiftshader
|
rm -r $BUILD_DIR/swiftshader
|
||||||
|
|
||||||
echo "Packaging linux x64 electron distribution..."
|
echo "Packaging linux x64 electron distribution..."
|
||||||
VERSION=`jq -r ".version" package.json`
|
VERSION=`jq -r ".version" package.json`
|
||||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
|
|
||||||
|
tar cJf $BUILD_DIR-${VERSION}.tar.xz $BUILD_DIR
|
||||||
|
|||||||
@@ -3,25 +3,30 @@
|
|||||||
BUILD_DIR=./dist/trilium-mac-x64
|
BUILD_DIR=./dist/trilium-mac-x64
|
||||||
rm -rf $BUILD_DIR
|
rm -rf $BUILD_DIR
|
||||||
|
|
||||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns
|
echo "Copying required mac binaries"
|
||||||
|
|
||||||
|
rm -r node_modules/sqlite3/lib/binding/*
|
||||||
|
rm -r node_modules/mozjpeg/vendor/*
|
||||||
|
rm -r node_modules/pngquant-bin/vendor/*
|
||||||
|
rm -r node_modules/giflossy/vendor/*
|
||||||
|
|
||||||
|
cp -r bin/deps/mac-x64/sqlite/* node_modules/sqlite3/lib/binding/
|
||||||
|
cp bin/deps/mac-x64/image/cjpeg node_modules/mozjpeg/vendor/
|
||||||
|
cp bin/deps/mac-x64/image/pngquant node_modules/pngquant-bin/vendor/
|
||||||
|
cp bin/deps/mac-x64/image/gifsicle node_modules/giflossy/vendor/
|
||||||
|
|
||||||
|
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns
|
||||||
|
|
||||||
# Mac build has by default useless directory level
|
# Mac build has by default useless directory level
|
||||||
mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR
|
mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR
|
||||||
|
|
||||||
echo "Copying required mac binaries"
|
./reset-local.sh
|
||||||
|
|
||||||
MAC_RES_DIR=$BUILD_DIR/Trilium\ Notes.app/Contents/Resources/app
|
echo "Zipping mac x64 electron distribution..."
|
||||||
|
|
||||||
rm -r "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/*"
|
|
||||||
|
|
||||||
cp -r bin/deps/mac-x64/sqlite/* "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/"
|
|
||||||
cp bin/deps/mac-x64/image/cjpeg "$MAC_RES_DIR/node_modules/mozjpeg/vendor/"
|
|
||||||
cp bin/deps/mac-x64/image/pngquant "$MAC_RES_DIR/node_modules/pngquant-bin/vendor/"
|
|
||||||
cp bin/deps/mac-x64/image/gifsicle "$MAC_RES_DIR/node_modules/giflossy/vendor/"
|
|
||||||
|
|
||||||
rm -r "$MAC_RES_DIR/bin/deps"
|
|
||||||
|
|
||||||
echo "Packaging mac x64 electron distribution..."
|
|
||||||
|
|
||||||
VERSION=`jq -r ".version" package.json`
|
VERSION=`jq -r ".version" package.json`
|
||||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
|
|
||||||
|
cd dist
|
||||||
|
|
||||||
|
rm trilium-mac-x64-${VERSION}.zip
|
||||||
|
zip -r9 --symlinks trilium-mac-x64-${VERSION}.zip trilium-mac-x64
|
||||||
|
|||||||
@@ -34,4 +34,5 @@ chmod 755 trilium.sh
|
|||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
VERSION=`jq -r ".version" ../package.json`
|
VERSION=`jq -r ".version" ../package.json`
|
||||||
7z a trilium-linux-x64-server-${VERSION}.7z trilium-linux-x64-server
|
|
||||||
|
tar cJf trilium-linux-x64-server-${VERSION}.tar.gz trilium-linux-x64-server
|
||||||
@@ -3,23 +3,30 @@
|
|||||||
BUILD_DIR=./dist/trilium-windows-x64
|
BUILD_DIR=./dist/trilium-windows-x64
|
||||||
rm -rf $BUILD_DIR
|
rm -rf $BUILD_DIR
|
||||||
|
|
||||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=win32 --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico
|
echo "Copying required windows binaries"
|
||||||
|
|
||||||
|
rm -r node_modules/sqlite3/lib/binding/*
|
||||||
|
rm -r node_modules/mozjpeg/vendor/*
|
||||||
|
rm -r node_modules/pngquant-bin/vendor/*
|
||||||
|
rm -r node_modules/giflossy/vendor/*
|
||||||
|
|
||||||
|
cp -r bin/deps/win-x64/sqlite/* node_modules/sqlite3/lib/binding/
|
||||||
|
cp bin/deps/win-x64/image/cjpeg.exe node_modules/mozjpeg/vendor/
|
||||||
|
cp bin/deps/win-x64/image/pngquant.exe node_modules/pngquant-bin/vendor/
|
||||||
|
cp bin/deps/win-x64/image/gifsicle.exe node_modules/giflossy/vendor/
|
||||||
|
|
||||||
|
./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=win32 --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico
|
||||||
|
|
||||||
mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
|
mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
|
||||||
|
|
||||||
echo "Copying required windows binaries"
|
|
||||||
|
|
||||||
WIN_RES_DIR=$BUILD_DIR/resources/app
|
|
||||||
|
|
||||||
cp -r bin/deps/win-x64/sqlite/* $WIN_RES_DIR/node_modules/sqlite3/lib/binding/
|
|
||||||
cp bin/deps/win-x64/image/cjpeg.exe $WIN_RES_DIR/node_modules/mozjpeg/vendor/
|
|
||||||
cp bin/deps/win-x64/image/pngquant.exe $WIN_RES_DIR/node_modules/pngquant-bin/vendor/
|
|
||||||
cp bin/deps/win-x64/image/gifsicle.exe $WIN_RES_DIR/node_modules/giflossy/vendor/
|
|
||||||
|
|
||||||
rm -r $WIN_RES_DIR/bin/deps
|
|
||||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||||
rm -r $BUILD_DIR/swiftshader
|
rm -r $BUILD_DIR/swiftshader
|
||||||
|
|
||||||
echo "Packaging windows x64 electron distribution..."
|
./reset-local.sh
|
||||||
|
|
||||||
|
echo "Zipping windows x64 electron distribution..."
|
||||||
VERSION=`jq -r ".version" package.json`
|
VERSION=`jq -r ".version" package.json`
|
||||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
|
|
||||||
|
cd dist
|
||||||
|
|
||||||
|
zip -r9 trilium-windows-x64-${VERSION}.zip trilium-windows-x64
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ git push origin $TAG
|
|||||||
|
|
||||||
bin/build.sh
|
bin/build.sh
|
||||||
|
|
||||||
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
|
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.tar.gz
|
||||||
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
|
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.zip
|
||||||
MAC_X64_BUILD=trilium-mac-x64-$VERSION.7z
|
MAC_X64_BUILD=trilium-mac-x64-$VERSION.zip
|
||||||
SERVER_BUILD=trilium-linux-x64-server-$VERSION.7z
|
SERVER_BUILD=trilium-linux-x64-server-$VERSION.tar.gz
|
||||||
|
|
||||||
echo "Creating release in GitHub"
|
echo "Creating release in GitHub"
|
||||||
|
|
||||||
|
|||||||
3
bin/reset-local.sh
Executable file
3
bin/reset-local.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
./node_modules/.bin/electron-rebuild --arch=x64
|
||||||
BIN
db/demo.tar
BIN
db/demo.tar
Binary file not shown.
62
db/migrations/0122__add_iv_to_columns.js
Normal file
62
db/migrations/0122__add_iv_to_columns.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
const sql = require('../../src/services/sql');
|
||||||
|
|
||||||
|
function prependIv(cipherText, ivText) {
|
||||||
|
const arr = ivText.split("").map(c => parseInt(c) || 0);
|
||||||
|
const iv = Buffer.from(arr);
|
||||||
|
const payload = Buffer.from(cipherText, 'base64');
|
||||||
|
const complete = Buffer.concat([iv, payload]);
|
||||||
|
|
||||||
|
return complete.toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateEncryptedDataKey() {
|
||||||
|
const encryptedDataKey = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKey'");
|
||||||
|
const encryptedDataKeyIv = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKeyIv'");
|
||||||
|
|
||||||
|
const newEncryptedDataKey = prependIv(encryptedDataKey, encryptedDataKeyIv);
|
||||||
|
|
||||||
|
await sql.execute("UPDATE options SET value = ? WHERE name = 'encryptedDataKey'", [newEncryptedDataKey]);
|
||||||
|
|
||||||
|
await sql.execute("DELETE FROM options WHERE name = 'encryptedDataKeyIv'");
|
||||||
|
await sql.execute("DELETE FROM sync WHERE entityName = 'options' AND entityId = 'encryptedDataKeyIv'");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateNotes() {
|
||||||
|
const protectedNotes = await sql.getRows("SELECT noteId, title, content FROM notes WHERE isProtected = 1");
|
||||||
|
|
||||||
|
for (const note of protectedNotes) {
|
||||||
|
if (note.title !== null) {
|
||||||
|
note.title = prependIv(note.title, "0" + note.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.content !== null) {
|
||||||
|
note.content = prependIv(note.content, "1" + note.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sql.execute("UPDATE notes SET title = ?, content = ? WHERE noteId = ?", [note.title, note.content, note.noteId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateNoteRevisions() {
|
||||||
|
const protectedNoteRevisions = await sql.getRows("SELECT noteRevisionId, title, content FROM note_revisions WHERE isProtected = 1");
|
||||||
|
|
||||||
|
for (const noteRevision of protectedNoteRevisions) {
|
||||||
|
if (noteRevision.title !== null) {
|
||||||
|
noteRevision.title = prependIv(noteRevision.title, "0" + noteRevision.noteRevisionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noteRevision.content !== null) {
|
||||||
|
noteRevision.content = prependIv(noteRevision.content, "1" + noteRevision.noteRevisionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await sql.execute("UPDATE note_revisions SET title = ?, content = ? WHERE noteRevisionId = ?", [noteRevision.title, noteRevision.content, noteRevision.noteRevisionId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async () => {
|
||||||
|
await updateEncryptedDataKey();
|
||||||
|
|
||||||
|
await updateNotes();
|
||||||
|
|
||||||
|
await updateNoteRevisions();
|
||||||
|
};
|
||||||
8
db/migrations/0123__add_options_for_font_sizes.sql
Normal file
8
db/migrations/0123__add_options_for_font_sizes.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('mainFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
|
||||||
|
|
||||||
|
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('treeFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
|
||||||
|
|
||||||
|
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('detailFontSize', '110', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0);
|
||||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"version": "0.27.2-beta",
|
"version": "0.27.4",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3870,9 +3870,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"file-type": {
|
"file-type": {
|
||||||
"version": "4.4.0",
|
"version": "10.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.7.0.tgz",
|
||||||
"integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU="
|
"integrity": "sha512-AbaGtdWYYRaVrv2MwL/65myuRJ9j3e79e7etJ79US18QHuVlzJBcQHUH+HxDUoLtbyWRTUfLzLkGXX3pP9kfZg=="
|
||||||
},
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -5000,6 +5000,13 @@
|
|||||||
"integrity": "sha1-FQKvMTX5BuEiyHfDHpSve3qRRsU=",
|
"integrity": "sha1-FQKvMTX5BuEiyHfDHpSve3qRRsU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"file-type": "^4.1.0"
|
"file-type": "^4.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"file-type": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz",
|
||||||
|
"integrity": "sha1-G2AOX8ofvcboDApwxxyNul95BsU="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"imagemin": {
|
"imagemin": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.27.4",
|
"version": "0.28.0-beta",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -17,7 +17,8 @@
|
|||||||
"start-electron": "electron . --disable-gpu",
|
"start-electron": "electron . --disable-gpu",
|
||||||
"build-backend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
|
"build-backend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js",
|
||||||
"build-frontend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js",
|
"build-frontend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js",
|
||||||
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs"
|
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
|
||||||
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async-mutex": "0.1.3",
|
"async-mutex": "0.1.3",
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"devtron": "1.4.0",
|
"devtron": "1.4.0",
|
||||||
"electron": "4.0.1",
|
"electron": "4.0.1",
|
||||||
|
"electron-builder": "20.38.4",
|
||||||
"electron-compile": "6.4.3",
|
"electron-compile": "6.4.3",
|
||||||
"electron-packager": "13.0.1",
|
"electron-packager": "13.0.1",
|
||||||
"electron-rebuild": "1.8.2",
|
"electron-rebuild": "1.8.2",
|
||||||
|
|||||||
@@ -47,7 +47,18 @@ class Note extends Entity {
|
|||||||
if (this.isProtected && this.noteId) {
|
if (this.isProtected && this.noteId) {
|
||||||
this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
|
this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable();
|
||||||
|
|
||||||
protectedSessionService.decryptNote(this);
|
if (this.isContentAvailable) {
|
||||||
|
protectedSessionService.decryptNote(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// saving ciphertexts in case we do want to update protected note outside of protected session
|
||||||
|
// (which is allowed)
|
||||||
|
this.titleCipherText = this.title;
|
||||||
|
this.contentCipherText = this.content;
|
||||||
|
|
||||||
|
this.title = "[protected]";
|
||||||
|
this.content = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setContent(this.content);
|
this.setContent(this.content);
|
||||||
@@ -629,12 +640,21 @@ class Note extends Entity {
|
|||||||
// cannot be static!
|
// cannot be static!
|
||||||
updatePojo(pojo) {
|
updatePojo(pojo) {
|
||||||
if (pojo.isProtected) {
|
if (pojo.isProtected) {
|
||||||
protectedSessionService.encryptNote(pojo);
|
if (this.isContentAvailable) {
|
||||||
|
protectedSessionService.encryptNote(pojo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// updating protected note outside of protected session means we will keep original ciphertexts
|
||||||
|
pojo.title = pojo.titleCipherText;
|
||||||
|
pojo.content = pojo.contentCipherText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete pojo.jsonContent;
|
delete pojo.jsonContent;
|
||||||
delete pojo.isContentAvailable;
|
delete pojo.isContentAvailable;
|
||||||
delete pojo.__attributeCache;
|
delete pojo.__attributeCache;
|
||||||
|
delete pojo.titleCipherText;
|
||||||
|
delete pojo.contentCipherText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ addTabHandler((function() {
|
|||||||
const $zoomFactorSelect = $("#zoom-factor-select");
|
const $zoomFactorSelect = $("#zoom-factor-select");
|
||||||
const $leftPaneMinWidth = $("#left-pane-min-width");
|
const $leftPaneMinWidth = $("#left-pane-min-width");
|
||||||
const $leftPaneWidthPercent = $("#left-pane-width-percent");
|
const $leftPaneWidthPercent = $("#left-pane-width-percent");
|
||||||
const $html = $("html");
|
const $mainFontSize = $("#main-font-size");
|
||||||
|
const $treeFontSize = $("#tree-font-size");
|
||||||
|
const $detailFontSize = $("#detail-font-size");
|
||||||
|
const $body = $("body");
|
||||||
const $container = $("#container");
|
const $container = $("#container");
|
||||||
|
|
||||||
function optionsLoaded(options) {
|
function optionsLoaded(options) {
|
||||||
@@ -59,21 +62,27 @@ addTabHandler((function() {
|
|||||||
|
|
||||||
$leftPaneMinWidth.val(options.leftPaneMinWidth);
|
$leftPaneMinWidth.val(options.leftPaneMinWidth);
|
||||||
$leftPaneWidthPercent.val(options.leftPaneWidthPercent);
|
$leftPaneWidthPercent.val(options.leftPaneWidthPercent);
|
||||||
|
|
||||||
|
$mainFontSize.val(options.mainFontSize);
|
||||||
|
$treeFontSize.val(options.treeFontSize);
|
||||||
|
$detailFontSize.val(options.detailFontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
$themeSelect.change(function() {
|
$themeSelect.change(function() {
|
||||||
const newTheme = $(this).val();
|
const newTheme = $(this).val();
|
||||||
|
|
||||||
$html.attr("class", "theme-" + newTheme);
|
for (const clazz of $body[0].classList) {
|
||||||
|
if (clazz.startsWith("theme-")) {
|
||||||
|
$body.removeClass(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$body.addClass("theme-" + newTheme);
|
||||||
|
|
||||||
server.put('options/theme/' + newTheme);
|
server.put('options/theme/' + newTheme);
|
||||||
});
|
});
|
||||||
|
|
||||||
$zoomFactorSelect.change(function() {
|
$zoomFactorSelect.change(function() { zoomService.setZoomFactorAndSave($(this).val()); });
|
||||||
const newZoomFactor = $(this).val();
|
|
||||||
|
|
||||||
zoomService.setZoomFactorAndSave(newZoomFactor);
|
|
||||||
});
|
|
||||||
|
|
||||||
function resizeLeftPanel() {
|
function resizeLeftPanel() {
|
||||||
const leftPanePercent = parseInt($leftPaneWidthPercent.val());
|
const leftPanePercent = parseInt($leftPaneWidthPercent.val());
|
||||||
@@ -83,20 +92,42 @@ addTabHandler((function() {
|
|||||||
$container.css("grid-template-columns", `minmax(${leftPaneMinWidth}px, ${leftPanePercent}fr) ${rightPanePercent}fr`);
|
$container.css("grid-template-columns", `minmax(${leftPaneMinWidth}px, ${leftPanePercent}fr) ${rightPanePercent}fr`);
|
||||||
}
|
}
|
||||||
|
|
||||||
$leftPaneMinWidth.change(function() {
|
$leftPaneMinWidth.change(async function() {
|
||||||
const newMinWidth = $(this).val();
|
await server.put('options/leftPaneMinWidth/' + $(this).val());
|
||||||
|
|
||||||
resizeLeftPanel();
|
resizeLeftPanel();
|
||||||
|
|
||||||
server.put('options/leftPaneMinWidth/' + newMinWidth);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$leftPaneWidthPercent.change(function() {
|
$leftPaneWidthPercent.change(async function() {
|
||||||
const newWidthPercent = $(this).val();
|
await server.put('options/leftPaneWidthPercent/' + $(this).val());
|
||||||
|
|
||||||
resizeLeftPanel();
|
resizeLeftPanel();
|
||||||
|
});
|
||||||
|
|
||||||
server.put('options/leftPaneWidthPercent/' + newWidthPercent);
|
function applyFontSizes() {
|
||||||
|
console.log($mainFontSize.val() + "% !important");
|
||||||
|
|
||||||
|
$body.get(0).style.setProperty("--main-font-size", $mainFontSize.val() + "%");
|
||||||
|
$body.get(0).style.setProperty("--tree-font-size", $treeFontSize.val() + "%");
|
||||||
|
$body.get(0).style.setProperty("--detail-font-size", $detailFontSize.val() + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
$mainFontSize.change(async function() {
|
||||||
|
await server.put('options/mainFontSize/' + $(this).val());
|
||||||
|
|
||||||
|
applyFontSizes();
|
||||||
|
});
|
||||||
|
|
||||||
|
$treeFontSize.change(async function() {
|
||||||
|
await server.put('options/treeFontSize/' + $(this).val());
|
||||||
|
|
||||||
|
applyFontSizes();
|
||||||
|
});
|
||||||
|
|
||||||
|
$detailFontSize.change(async function() {
|
||||||
|
await server.put('options/detailFontSize/' + $(this).val());
|
||||||
|
|
||||||
|
applyFontSizes();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ async function getAndExecuteBundle(noteId, originEntity = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function executeBundle(bundle, originEntity) {
|
async function executeBundle(bundle, originEntity) {
|
||||||
const apiContext = ScriptContext(bundle.note, bundle.allNotes, originEntity);
|
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await (function () {
|
return await (function () {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ function registerEntrypoints() {
|
|||||||
|
|
||||||
utils.bindShortcut('ctrl+r', utils.reloadApp);
|
utils.bindShortcut('ctrl+r', utils.reloadApp);
|
||||||
|
|
||||||
$(document).bind('keydown', 'ctrl+shift+i', () => {
|
utils.bindShortcut('ctrl+shift+i', () => {
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
require('electron').remote.getCurrentWindow().toggleDevTools();
|
require('electron').remote.getCurrentWindow().toggleDevTools();
|
||||||
|
|
||||||
@@ -135,8 +135,8 @@ function registerEntrypoints() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
$(document).bind('keydown', 'ctrl+-', zoomService.decreaseZoomFactor);
|
utils.bindShortcut('ctrl+-', zoomService.decreaseZoomFactor);
|
||||||
$(document).bind('keydown', 'ctrl+=', zoomService.increaseZoomFactor);
|
utils.bindShortcut('ctrl+=', zoomService.increaseZoomFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
|
|||||||
this.activateNewNote = async notePath => {
|
this.activateNewNote = async notePath => {
|
||||||
await treeService.reload();
|
await treeService.reload();
|
||||||
|
|
||||||
await treeService.activateNote(notePath, noteDetailService.focusOnTitle);
|
await treeService.activateNote(notePath, noteDetailService.focusAndSelectTitle);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -145,8 +145,9 @@ async function saveNoteIfChanged() {
|
|||||||
function setNoteBackgroundIfProtected(note) {
|
function setNoteBackgroundIfProtected(note) {
|
||||||
$noteDetailWrapper.toggleClass("protected", note.isProtected);
|
$noteDetailWrapper.toggleClass("protected", note.isProtected);
|
||||||
$protectButton.toggleClass("active", note.isProtected);
|
$protectButton.toggleClass("active", note.isProtected);
|
||||||
|
$protectButton.prop("disabled", note.isProtected);
|
||||||
$unprotectButton.toggleClass("active", !note.isProtected);
|
$unprotectButton.toggleClass("active", !note.isProtected);
|
||||||
$unprotectButton.prop("disabled", !protectedSessionHolder.isProtectedSessionAvailable());
|
$unprotectButton.prop("disabled", !note.isProtected || !protectedSessionHolder.isProtectedSessionAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleProtectedSession() {
|
async function handleProtectedSession() {
|
||||||
@@ -283,6 +284,10 @@ function focusOnTitle() {
|
|||||||
$noteTitle.focus();
|
$noteTitle.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function focusAndSelectTitle() {
|
||||||
|
$noteTitle.focus().select();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys,
|
* Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys,
|
||||||
* we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on
|
* we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on
|
||||||
@@ -342,6 +347,7 @@ export default {
|
|||||||
getCurrentNoteType,
|
getCurrentNoteType,
|
||||||
getCurrentNoteId,
|
getCurrentNoteId,
|
||||||
focusOnTitle,
|
focusOnTitle,
|
||||||
|
focusAndSelectTitle,
|
||||||
saveNote,
|
saveNote,
|
||||||
saveNoteIfChanged,
|
saveNoteIfChanged,
|
||||||
noteChanged,
|
noteChanged,
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import FrontendScriptApi from './frontend_script_api.js';
|
import FrontendScriptApi from './frontend_script_api.js';
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
|
import treeCache from './tree_cache.js';
|
||||||
|
|
||||||
function ScriptContext(startNote, allNotes, originEntity = null) {
|
async function ScriptContext(startNoteId, allNoteIds, originEntity = null) {
|
||||||
const modules = {};
|
const modules = {};
|
||||||
|
|
||||||
|
const startNote = await treeCache.getNote(startNoteId);
|
||||||
|
const allNotes = await treeCache.getNotes(allNoteIds);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modules: modules,
|
modules: modules,
|
||||||
notes: utils.toObject(allNotes, note => [note.noteId, note]),
|
notes: utils.toObject(allNotes, note => [note.noteId, note]),
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection
|
|||||||
|
|
||||||
await noteDetailService.saveNoteIfChanged();
|
await noteDetailService.saveNoteIfChanged();
|
||||||
|
|
||||||
noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusOnTitle);
|
noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusAndSelectTitle);
|
||||||
|
|
||||||
const noteEntity = new NoteShort(treeCache, note);
|
const noteEntity = new NoteShort(treeCache, note);
|
||||||
const branchEntity = new Branch(treeCache, branch);
|
const branchEntity = new Branch(treeCache, branch);
|
||||||
|
|||||||
@@ -168,9 +168,24 @@ async function getExtraClasses(note) {
|
|||||||
|
|
||||||
extraClasses.push(note.type);
|
extraClasses.push(note.type);
|
||||||
|
|
||||||
|
if (note.mime) { // some notes should not have mime type (e.g. render)
|
||||||
|
extraClasses.push(getMimeTypeClass(note.mime));
|
||||||
|
}
|
||||||
|
|
||||||
return extraClasses.join(" ");
|
return extraClasses.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMimeTypeClass(mime) {
|
||||||
|
const semicolonIdx = mime.indexOf(';');
|
||||||
|
|
||||||
|
if (semicolonIdx !== -1) {
|
||||||
|
// stripping everything following the semicolon
|
||||||
|
mime = mime.substr(0, semicolonIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'mime-' + mime.toLowerCase().replace(/[\W_]+/g,"-");
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
prepareTree,
|
prepareTree,
|
||||||
prepareBranch,
|
prepareBranch,
|
||||||
|
|||||||
@@ -151,11 +151,15 @@ function bindShortcut(keyboardShortcut, handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isMobile() {
|
function isMobile() {
|
||||||
return window.device === "mobile";
|
return window.device === "mobile"
|
||||||
|
// window.device is not available in setup
|
||||||
|
|| (!window.device && /Mobi/.test(navigator.userAgent));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDesktop() {
|
function isDesktop() {
|
||||||
return window.device === "desktop";
|
return window.device === "desktop"
|
||||||
|
// window.device is not available in setup
|
||||||
|
|| (!window.device && !/Mobi/.test(navigator.userAgent));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCookie(name, value) {
|
function setCookie(name, value) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -9,12 +9,12 @@
|
|||||||
*
|
*
|
||||||
* This section is automatically generated from the `skin-common.less` template.
|
* This section is automatically generated from the `skin-common.less` template.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2008-2018, Martin Wendt (http://wwWendt.de)
|
* Copyright (c) 2008-2019, Martin Wendt (http://wwWendt.de)
|
||||||
* Released under the MIT license
|
* Released under the MIT license
|
||||||
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||||||
*
|
*
|
||||||
* @version 2.30.0
|
* @version 2.30.2
|
||||||
* @date 2018-09-02T15:42:49Z
|
* @date 2019-01-13T08:17:01Z
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
/*------------------------------------------------------------------------------
|
/*------------------------------------------------------------------------------
|
||||||
* Helpers
|
* Helpers
|
||||||
@@ -336,7 +336,8 @@ span.fancytree-icon {
|
|||||||
.fancytree-loading span.fancytree-expander,
|
.fancytree-loading span.fancytree-expander,
|
||||||
.fancytree-loading span.fancytree-expander:hover,
|
.fancytree-loading span.fancytree-expander:hover,
|
||||||
.fancytree-statusnode-loading span.fancytree-icon,
|
.fancytree-statusnode-loading span.fancytree-icon,
|
||||||
.fancytree-statusnode-loading span.fancytree-icon:hover {
|
.fancytree-statusnode-loading span.fancytree-icon:hover,
|
||||||
|
span.fancytree-icon.fancytree-icon-loading {
|
||||||
background-image: url("../skin-win8/loading.gif");
|
background-image: url("../skin-win8/loading.gif");
|
||||||
background-position: 0px 0px;
|
background-position: 0px 0px;
|
||||||
}
|
}
|
||||||
@@ -479,6 +480,8 @@ ul.fancytree-container.fancytree-rtl.fancytree-no-connector > li {
|
|||||||
* 'table' extension
|
* 'table' extension
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
table.fancytree-ext-table {
|
table.fancytree-ext-table {
|
||||||
|
font-family: tahoma, arial, helvetica;
|
||||||
|
font-size: 10pt;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
/* ext-ariagrid */
|
/* ext-ariagrid */
|
||||||
}
|
}
|
||||||
@@ -536,6 +539,9 @@ table.fancytree-ext-columnview span.fancytree-node {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
table.fancytree-ext-columnview span.fancytree-node.fancytree-expanded {
|
table.fancytree-ext-columnview span.fancytree-node.fancytree-expanded {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
table.fancytree-ext-columnview span.fancytree-node.fancytree-active {
|
||||||
background-color: #CBE8F6;
|
background-color: #CBE8F6;
|
||||||
}
|
}
|
||||||
table.fancytree-ext-columnview .fancytree-has-children span.fancytree-cv-right {
|
table.fancytree-ext-columnview .fancytree-has-children span.fancytree-cv-right {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,7 @@
|
|||||||
|
body {
|
||||||
|
font-size: var(--main-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
#container {
|
#container {
|
||||||
margin: 0 auto; /* center */
|
margin: 0 auto; /* center */
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@@ -26,6 +30,7 @@
|
|||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
flex-basis: 60%;
|
flex-basis: 60%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
font-size: var(--tree-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#left-pane {
|
#left-pane {
|
||||||
@@ -36,7 +41,7 @@
|
|||||||
|
|
||||||
#header {
|
#header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
background-color: #f8f8f8;
|
background-color: var(--header-background-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
@@ -44,7 +49,7 @@
|
|||||||
|
|
||||||
#header button {
|
#header button {
|
||||||
padding: 1px 5px 1px 5px;
|
padding: 1px 5px 1px 5px;
|
||||||
font-size: small;
|
font-size: smaller;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
@@ -86,3 +91,7 @@
|
|||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#note-detail-wrapper {
|
||||||
|
font-size: var(--detail-font-size);
|
||||||
|
}
|
||||||
@@ -12,12 +12,13 @@
|
|||||||
.note-box {
|
.note-box {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
border: 1px solid #666;
|
border: 1px solid #666;
|
||||||
box-shadow: 2px 2px 19px #999;
|
box-shadow: 2px 2px 19px #999;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
background-color: white;
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-box:hover {
|
.note-box:hover {
|
||||||
background-color: #ddd;
|
background-color: var(--more-accented-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-box .title {
|
.note-box .title {
|
||||||
@@ -37,11 +38,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.connection-label.jtk-hover, .jtk-source-hover, .jtk-target-hover {
|
.connection-label.jtk-hover, .jtk-source-hover, .jtk-target-hover {
|
||||||
background-color: #ddd;
|
background-color: var(--more-accented-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.connection-label {
|
.connection-label {
|
||||||
background-color: white;
|
background-color: var(--accented-background-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
padding: 0.3em;
|
padding: 0.3em;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
@@ -64,10 +66,6 @@
|
|||||||
box-shadow: 0 0 6px black;
|
box-shadow: 0 0 6px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statemachine-demo .jtk-endpoint {
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragHover {
|
.dragHover {
|
||||||
border: 2px solid orange;
|
border: 2px solid orange;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,116 @@
|
|||||||
|
:root {
|
||||||
|
--main-font-size: normal;
|
||||||
|
--tree-font-size: normal;
|
||||||
|
--detail-font-size: normal;
|
||||||
|
|
||||||
|
--main-background-color: white;
|
||||||
|
--main-text-color: black;
|
||||||
|
--accented-background-color: #eee;
|
||||||
|
--more-accented-background-color: #ccc;
|
||||||
|
--header-background-color: #f8f8f8;
|
||||||
|
--button-background-color: #eee;
|
||||||
|
--button-border-color: #ddd;
|
||||||
|
--button-text-color: black;
|
||||||
|
--button-border-radius: 5px;
|
||||||
|
--muted-text-color: #444;
|
||||||
|
--input-text-color: black;
|
||||||
|
--input-background-color: white;
|
||||||
|
--modal-background-color: white;
|
||||||
|
--hover-item-text-color: black;
|
||||||
|
--hover-item-background-color: #eee;
|
||||||
|
--active-item-text-color: black;
|
||||||
|
--active-item-background-color: #ccc;
|
||||||
|
--menu-text-color: black;
|
||||||
|
--menu-background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-black {
|
||||||
|
--main-background-color: black;
|
||||||
|
--main-text-color: white;
|
||||||
|
--accented-background-color: #222;
|
||||||
|
--more-accented-background-color: #444;
|
||||||
|
--header-background-color: black;
|
||||||
|
--button-background-color: #333;
|
||||||
|
--button-border-color: #444;
|
||||||
|
--button-text-color: white;
|
||||||
|
--button-border-radius: 5px;
|
||||||
|
--muted-text-color: #ccc;
|
||||||
|
--input-text-color: white;
|
||||||
|
--input-background-color: black;
|
||||||
|
--modal-background-color: #222;
|
||||||
|
--hover-item-text-color: black;
|
||||||
|
--hover-item-background-color: #aaa;
|
||||||
|
--active-item-text-color: black;
|
||||||
|
--active-item-background-color: #ccc;
|
||||||
|
--menu-text-color: white;
|
||||||
|
--menu-background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-black .CodeMirror {
|
||||||
|
filter: invert(100%) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-dark {
|
||||||
|
--main-background-color: #333;
|
||||||
|
--main-text-color: white;
|
||||||
|
--accented-background-color: #555;
|
||||||
|
--more-accented-background-color: #777;
|
||||||
|
--header-background-color: #333;
|
||||||
|
--button-background-color: #555;
|
||||||
|
--button-border-color: #444;
|
||||||
|
--button-text-color: white;
|
||||||
|
--button-border-radius: 5px;
|
||||||
|
--muted-text-color: #ccc;
|
||||||
|
--input-text-color: white;
|
||||||
|
--input-background-color: #333;
|
||||||
|
--modal-background-color: #555;
|
||||||
|
--hover-item-text-color: black;
|
||||||
|
--hover-item-background-color: #aaa;
|
||||||
|
--active-item-text-color: black;
|
||||||
|
--active-item-background-color: #ccc;
|
||||||
|
--menu-text-color: white;
|
||||||
|
--menu-background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-dark .CodeMirror {
|
||||||
|
filter: invert(90%) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* this fixes FF filter vs. position fixed bug: https://github.com/zadam/trilium/issues/233 */
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
/* Fix for CKEditor block gutter icon "stretching" body and causing scrollbar to appear after pressing enter
|
/* Fix for CKEditor block gutter icon "stretching" body and causing scrollbar to appear after pressing enter
|
||||||
on the last line of the editor. */
|
on the last line of the editor. */
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
background-color: var(--main-background-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select {
|
||||||
|
color: var(--input-text-color) !important;
|
||||||
|
background: var(--input-background-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-text {
|
||||||
|
background-color: var(--accented-background-color) !important;
|
||||||
|
color: var(--muted-text-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--modal-background-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active {
|
||||||
|
background-color: var(--more-accented-background-color) !important;
|
||||||
|
color: var(--main-text-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title-container {
|
#title-container {
|
||||||
@@ -11,7 +119,8 @@ body {
|
|||||||
|
|
||||||
#note-title {
|
#note-title {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
font-size: x-large;
|
margin-right: 10px;
|
||||||
|
font-size: 150%;
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 5em;
|
width: 5em;
|
||||||
flex-grow: 100;
|
flex-grow: 100;
|
||||||
@@ -89,8 +198,6 @@ ul.fancytree-container {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-detail-text { font-size: 1.1em; }
|
|
||||||
|
|
||||||
#note-detail-text h1 { font-size: 2.0em; }
|
#note-detail-text h1 { font-size: 2.0em; }
|
||||||
#note-detail-text h2 { font-size: 1.8em; }
|
#note-detail-text h2 { font-size: 1.8em; }
|
||||||
#note-detail-text h3 { font-size: 1.6em; }
|
#note-detail-text h3 { font-size: 1.6em; }
|
||||||
@@ -117,12 +224,18 @@ ul.fancytree-container {
|
|||||||
|
|
||||||
ul.fancytree-container {
|
ul.fancytree-container {
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
|
background-color: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fancytree-custom-icon {
|
.fancytree-custom-icon {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.fancytree-title {
|
||||||
|
color: inherit !important;
|
||||||
|
background: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
span.fancytree-node.protected > span.fancytree-custom-icon {
|
span.fancytree-node.protected > span.fancytree-custom-icon {
|
||||||
filter: drop-shadow(2px 2px 2px black);
|
filter: drop-shadow(2px 2px 2px black);
|
||||||
}
|
}
|
||||||
@@ -140,22 +253,24 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
|
|||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* By default not focused active tree item is not easily visible, this makes it more visible */
|
span.fancytree-active.fancytree-focused .fancytree-title {
|
||||||
span.fancytree-active:not(.fancytree-focused) .fancytree-title {
|
color: var(--active-item-text-color) !important;
|
||||||
background-color: #eee !important;
|
background-color: var(--active-item-background-color) !important;
|
||||||
border-color: #ddd !important;
|
border-color: #ddd !important;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-active.fancytree-focused .fancytree-title {
|
span.fancytree-active:not(.fancytree-focused) .fancytree-title {
|
||||||
background-color: #ddd !important;
|
color: var(--hover-item-text-color) !important;
|
||||||
border-color: #bbb !important;
|
background-color: var(--hover-item-background-color) !important;
|
||||||
|
border-color: #ddd !important;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fancytree-plain span.fancytree-node:hover span.fancytree-title {
|
span.fancytree-node:not(.fancytree-active):hover span.fancytree-title {
|
||||||
background-color: #eee !important;
|
color: var(--hover-item-text-color) !important;
|
||||||
border-color: #bbb !important;
|
background-color: var(--hover-item-background-color) !important;
|
||||||
|
border-color: #ddd !important;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +359,7 @@ div.ui-tooltip {
|
|||||||
|
|
||||||
/* Allow to use <kbd> elements inside the title to define shortcut hints. */
|
/* Allow to use <kbd> elements inside the title to define shortcut hints. */
|
||||||
.ui-menu kbd, button kbd {
|
.ui-menu kbd, button kbd {
|
||||||
color: black;
|
color: var(--muted-text-color);
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -265,8 +380,14 @@ div.ui-tooltip {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
color: var(--menu-text-color) !important;
|
||||||
|
background-color: var(--menu-background-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-menu a:hover:not(.disabled) {
|
.dropdown-menu a:hover:not(.disabled) {
|
||||||
background-color: #eee !important;
|
color: var(--hover-item-text-color) !important;
|
||||||
|
background-color: var(--hover-item-background-color) !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +397,7 @@ div.ui-tooltip {
|
|||||||
|
|
||||||
.dropdown-menu kbd
|
.dropdown-menu kbd
|
||||||
{
|
{
|
||||||
color: black;
|
color: var(--muted-text-color);
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -344,7 +465,7 @@ div.ui-tooltip {
|
|||||||
|
|
||||||
#file-table th, #file-table td {
|
#file-table th, #file-table td {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: large;
|
font-size: larger;
|
||||||
}
|
}
|
||||||
|
|
||||||
#children-overview {
|
#children-overview {
|
||||||
@@ -360,9 +481,9 @@ div.ui-tooltip {
|
|||||||
|
|
||||||
.child-overview {
|
.child-overview {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: large;
|
font-size: larger;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: #f4f4f4;
|
background: var(--accented-background-color);
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 90px;
|
height: 90px;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
@@ -376,7 +497,7 @@ div.ui-tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.child-overview a {
|
.child-overview a {
|
||||||
color: #444;
|
color: var(--muted-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#sql-console-query {
|
#sql-console-query {
|
||||||
@@ -390,13 +511,18 @@ div.ui-tooltip {
|
|||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
border-radius: var(--button-border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
.btn:not(.btn-primary):not(.btn-secondary):not(.btn-danger) {
|
.btn:not(.btn-primary):not(.btn-secondary):not(.btn-danger) {
|
||||||
border-color: #ddd;
|
border-color: var(--button-border-color);
|
||||||
background-color: #eee;
|
background-color: var(--button-background-color);
|
||||||
|
color: var(--button-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.active:not(.btn-primary) {
|
.btn.active:not(.btn-primary) {
|
||||||
background-color: #ccc;
|
background-color: #ccc !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-path-list a.current {
|
#note-path-list a.current {
|
||||||
@@ -417,33 +543,6 @@ button.icon-button {
|
|||||||
width: 15em;
|
width: 15em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Themes */
|
|
||||||
|
|
||||||
html {
|
|
||||||
/* this fixes FF filter vs. position fixed bug: https://github.com/zadam/trilium/issues/233 */
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.theme-black, html.theme-black img, html.theme-black video {
|
|
||||||
filter: invert(100%) hue-rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.theme-black body {
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.theme-dark {
|
|
||||||
filter: invert(90%) hue-rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.theme-dark img, html.theme-dark video {
|
|
||||||
filter: invert(100%) hue-rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.theme-dark body {
|
|
||||||
background: #191819;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ck.ck-block-toolbar-button {
|
.ck.ck-block-toolbar-button {
|
||||||
transform: translateX(10px);
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
@@ -546,12 +645,12 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-inner {
|
.tooltip-inner {
|
||||||
background-color: #fbfbfb !important;
|
background-color: var(--accented-background-color) !important;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
/* height needs to stay small because tooltip has problem when it can't fit to either top or bottom of the cursor */
|
/* height needs to stay small because tooltip has problem when it can't fit to either top or bottom of the cursor */
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: black;
|
color: var(--main-text-color);
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -581,7 +680,7 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
|||||||
|
|
||||||
.algolia-autocomplete .aa-dropdown-menu {
|
.algolia-autocomplete .aa-dropdown-menu {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #fff;
|
background-color: var(--main-background-color);
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
z-index: 2000 !important;
|
z-index: 2000 !important;
|
||||||
@@ -603,7 +702,8 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
|||||||
}
|
}
|
||||||
|
|
||||||
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor {
|
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor {
|
||||||
background-color: #B2D7FF;
|
color: var(--hover-item-text-color);
|
||||||
|
background-color: var(--hover-item-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.help-button {
|
.help-button {
|
||||||
@@ -669,7 +769,7 @@ div[data-notify="container"] {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 11px;
|
top: 11px;
|
||||||
font-size: x-large;
|
font-size: 150%;
|
||||||
color: #777;
|
color: #777;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
@@ -731,3 +831,12 @@ div[data-notify="container"] {
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ck-content .image > figcaption {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#options-dialog input[type=number] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
const protectedSessionService = require('../../services/protected_session');
|
const protectedSessionService = require('../../services/protected_session');
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
|
const utils = require('../../services/utils');
|
||||||
|
|
||||||
async function uploadFile(req) {
|
async function uploadFile(req) {
|
||||||
const parentNoteId = req.params.parentNoteId;
|
const parentNoteId = req.params.parentNoteId;
|
||||||
@@ -49,7 +50,7 @@ async function downloadFile(req, res) {
|
|||||||
const originalFileName = await note.getLabel('originalFileName');
|
const originalFileName = await note.getLabel('originalFileName');
|
||||||
const fileName = originalFileName ? originalFileName.value : note.title;
|
const fileName = originalFileName ? originalFileName.value : note.title;
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"');
|
res.setHeader('Content-Disposition', utils.getContentDisposition(fileName));
|
||||||
res.setHeader('Content-Type', note.mime);
|
res.setHeader('Content-Type', note.mime);
|
||||||
|
|
||||||
res.send(note.content);
|
res.send(note.content);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const log = require('../../services/log');
|
|||||||
|
|
||||||
// options allowed to be updated directly in options dialog
|
// options allowed to be updated directly in options dialog
|
||||||
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
|
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
|
||||||
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent', 'hoistedNoteId'];
|
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent', 'hoistedNoteId', 'mainFontSize', 'treeFontSize', 'detailFontSize'];
|
||||||
|
|
||||||
async function getOptions() {
|
async function getOptions() {
|
||||||
return await optionService.getOptionsMap(ALLOWED_OPTIONS);
|
return await optionService.getOptionsMap(ALLOWED_OPTIONS);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ async function getStartupBundles() {
|
|||||||
const bundles = [];
|
const bundles = [];
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
const bundle = await scriptService.getScriptBundle(note);
|
const bundle = await scriptService.getScriptBundleForFrontend(note);
|
||||||
|
|
||||||
if (bundle) {
|
if (bundle) {
|
||||||
bundles.push(bundle);
|
bundles.push(bundle);
|
||||||
@@ -53,14 +53,21 @@ async function getRelationBundles(req) {
|
|||||||
const bundles = [];
|
const bundles = [];
|
||||||
|
|
||||||
for (const noteId of uniqueNoteIds) {
|
for (const noteId of uniqueNoteIds) {
|
||||||
bundles.push(await scriptService.getScriptBundleForNoteId(noteId));
|
const note = await repository.getNote(noteId);
|
||||||
|
const bundle = await scriptService.getScriptBundleForFrontend(note);
|
||||||
|
|
||||||
|
if (bundle) {
|
||||||
|
bundles.push(bundle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bundles;
|
return bundles;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getBundle(req) {
|
async function getBundle(req) {
|
||||||
return await scriptService.getScriptBundleForNoteId(req.params.noteId);
|
const note = await repository.getNote(req.params.noteId);
|
||||||
|
|
||||||
|
return await scriptService.getScriptBundleForFrontend(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ async function index(req, res) {
|
|||||||
leftPaneMinWidth: parseInt(options.leftPaneMinWidth),
|
leftPaneMinWidth: parseInt(options.leftPaneMinWidth),
|
||||||
leftPaneWidthPercent: parseInt(options.leftPaneWidthPercent),
|
leftPaneWidthPercent: parseInt(options.leftPaneWidthPercent),
|
||||||
rightPaneWidthPercent: 100 - parseInt(options.leftPaneWidthPercent),
|
rightPaneWidthPercent: 100 - parseInt(options.leftPaneWidthPercent),
|
||||||
|
mainFontSize: parseInt(options.mainFontSize),
|
||||||
|
treeFontSize: parseInt(options.treeFontSize),
|
||||||
|
detailFontSize: parseInt(options.detailFontSize),
|
||||||
sourceId: await sourceIdService.generateSourceId(),
|
sourceId: await sourceIdService.generateSourceId(),
|
||||||
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
||||||
instanceName: config.General ? config.General.instanceName : null,
|
instanceName: config.General ? config.General.instanceName : null,
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ const build = require('./build');
|
|||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||||
|
|
||||||
const APP_DB_VERSION = 121;
|
const APP_DB_VERSION = 123;
|
||||||
const SYNC_VERSION = 3;
|
const SYNC_VERSION = 4;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
appVersion: packageJson.version,
|
appVersion: packageJson.version,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2019-01-10T21:31:30+01:00", buildRevision: "0b251530fa0ee61edc8dcc9235033abb73afc614" };
|
module.exports = { buildDate:"2019-01-14T23:51:55+01:00", buildRevision: "e50f9cd0a354e29bb40c161cf7288e13b732e0d3" };
|
||||||
|
|||||||
@@ -18,25 +18,26 @@ function shaArray(content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pad(data) {
|
function pad(data) {
|
||||||
let padded = Array.from(data);
|
if (data.length > 16) {
|
||||||
|
data = data.slice(0, 16);
|
||||||
if (data.length >= 16) {
|
|
||||||
padded = padded.slice(0, 16);
|
|
||||||
}
|
}
|
||||||
else {
|
else if (data.length < 16) {
|
||||||
padded = padded.concat(Array(16 - padded.length).fill(0));
|
const zeros = Array(16 - data.length).fill(0);
|
||||||
|
|
||||||
|
data = Buffer.concat([data, Buffer.from(zeros)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Buffer.from(padded);
|
return Buffer.from(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function encrypt(key, iv, plainText) {
|
function encrypt(key, plainText, ivLength = 13) {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
throw new Error("No data key!");
|
throw new Error("No data key!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const plainTextBuffer = Buffer.from(plainText);
|
const plainTextBuffer = Buffer.from(plainText);
|
||||||
|
|
||||||
|
const iv = crypto.randomBytes(ivLength);
|
||||||
const cipher = crypto.createCipheriv('aes-128-cbc', pad(key), pad(iv));
|
const cipher = crypto.createCipheriv('aes-128-cbc', pad(key), pad(iv));
|
||||||
|
|
||||||
const digest = shaArray(plainTextBuffer).slice(0, 4);
|
const digest = shaArray(plainTextBuffer).slice(0, 4);
|
||||||
@@ -45,17 +46,23 @@ function encrypt(key, iv, plainText) {
|
|||||||
|
|
||||||
const encryptedData = Buffer.concat([cipher.update(digestWithPayload), cipher.final()]);
|
const encryptedData = Buffer.concat([cipher.update(digestWithPayload), cipher.final()]);
|
||||||
|
|
||||||
return encryptedData.toString('base64');
|
const encryptedDataWithIv = Buffer.concat([iv, encryptedData]);
|
||||||
|
|
||||||
|
return encryptedDataWithIv.toString('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
function decrypt(key, iv, cipherText) {
|
function decrypt(key, cipherText, ivLength = 13) {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return "[protected]";
|
return "[protected]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cipherTextBufferWithIv = Buffer.from(cipherText, 'base64');
|
||||||
|
const iv = cipherTextBufferWithIv.slice(0, ivLength);
|
||||||
|
|
||||||
|
const cipherTextBuffer = cipherTextBufferWithIv.slice(ivLength);
|
||||||
|
|
||||||
const decipher = crypto.createDecipheriv('aes-128-cbc', pad(key), pad(iv));
|
const decipher = crypto.createDecipheriv('aes-128-cbc', pad(key), pad(iv));
|
||||||
|
|
||||||
const cipherTextBuffer = Buffer.from(cipherText, 'base64');
|
|
||||||
const decryptedBytes = Buffer.concat([decipher.update(cipherTextBuffer), decipher.final()]);
|
const decryptedBytes = Buffer.concat([decipher.update(cipherTextBuffer), decipher.final()]);
|
||||||
|
|
||||||
const digest = decryptedBytes.slice(0, 4);
|
const digest = decryptedBytes.slice(0, 4);
|
||||||
@@ -70,8 +77,8 @@ function decrypt(key, iv, cipherText) {
|
|||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
function decryptString(dataKey, iv, cipherText) {
|
function decryptString(dataKey, cipherText) {
|
||||||
const buffer = decrypt(dataKey, iv, cipherText);
|
const buffer = decrypt(dataKey, cipherText);
|
||||||
|
|
||||||
const str = buffer.toString('utf-8');
|
const str = buffer.toString('utf-8');
|
||||||
|
|
||||||
@@ -84,26 +91,8 @@ function decryptString(dataKey, iv, cipherText) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function noteTitleIv(iv) {
|
|
||||||
if (!iv) {
|
|
||||||
throw new Error("Empty iv!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return "0" + iv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function noteContentIv(iv) {
|
|
||||||
if (!iv) {
|
|
||||||
throw new Error("Empty iv!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return "1" + iv;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
encrypt,
|
encrypt,
|
||||||
decrypt,
|
decrypt,
|
||||||
decryptString,
|
decryptString
|
||||||
noteTitleIv,
|
|
||||||
noteContentIv
|
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const sanitize = require("sanitize-filename");
|
const repository = require("../repository");
|
||||||
const repository = require("../../services/repository");
|
const utils = require('../utils');
|
||||||
const utils = require('../../services/utils');
|
|
||||||
|
|
||||||
async function exportToOpml(branch, res) {
|
async function exportToOpml(branch, res) {
|
||||||
const note = await branch.getNote();
|
const note = await branch.getNote();
|
||||||
const title = (branch.prefix ? (branch.prefix + ' - ') : '') + note.title;
|
|
||||||
const sanitizedTitle = sanitize(title);
|
|
||||||
|
|
||||||
async function exportNoteInner(branchId) {
|
async function exportNoteInner(branchId) {
|
||||||
const branch = await repository.getBranch(branchId);
|
const branch = await repository.getBranch(branchId);
|
||||||
@@ -31,7 +28,9 @@ async function exportToOpml(branch, res) {
|
|||||||
res.write('</outline>');
|
res.write('</outline>');
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', 'file; filename="' + sanitizedTitle + '.opml"');
|
const filename = (branch.prefix ? (branch.prefix + ' - ') : '') + note.title + ".opml";
|
||||||
|
|
||||||
|
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
|
||||||
res.setHeader('Content-Type', 'text/x-opml');
|
res.setHeader('Content-Type', 'text/x-opml');
|
||||||
|
|
||||||
res.write(`<?xml version="1.0" encoding="UTF-8"?>
|
res.write(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const sanitize = require("sanitize-filename");
|
|
||||||
const TurndownService = require('turndown');
|
const TurndownService = require('turndown');
|
||||||
const mimeTypes = require('mime-types');
|
const mimeTypes = require('mime-types');
|
||||||
const html = require('html');
|
const html = require('html');
|
||||||
|
const utils = require('../utils');
|
||||||
|
|
||||||
async function exportSingleNote(branch, format, res) {
|
async function exportSingleNote(branch, format, res) {
|
||||||
const note = await branch.getNote();
|
const note = await branch.getNote();
|
||||||
@@ -42,11 +42,9 @@ async function exportSingleNote(branch, format, res) {
|
|||||||
mime = 'application/json';
|
mime = 'application/json';
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = sanitize(note.title);
|
const filename = note.title + "." + extension;
|
||||||
|
|
||||||
console.log(name, extension, mime);
|
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', `file; filename="${name}.${extension}"`);
|
|
||||||
res.setHeader('Content-Type', mime + '; charset=UTF-8');
|
res.setHeader('Content-Type', mime + '; charset=UTF-8');
|
||||||
|
|
||||||
res.send(payload);
|
res.send(payload);
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ const html = require('html');
|
|||||||
const repository = require('../repository');
|
const repository = require('../repository');
|
||||||
const tar = require('tar-stream');
|
const tar = require('tar-stream');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const sanitize = require("sanitize-filename");
|
|
||||||
const mimeTypes = require('mime-types');
|
const mimeTypes = require('mime-types');
|
||||||
const TurndownService = require('turndown');
|
const TurndownService = require('turndown');
|
||||||
const packageInfo = require('../../../package.json');
|
const packageInfo = require('../../../package.json');
|
||||||
|
const utils = require('../utils');
|
||||||
|
const sanitize = require("sanitize-filename");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param format - 'html' or 'markdown'
|
* @param format - 'html' or 'markdown'
|
||||||
@@ -219,9 +220,9 @@ async function exportToTar(branch, format, res) {
|
|||||||
pack.finalize();
|
pack.finalize();
|
||||||
|
|
||||||
const note = await branch.getNote();
|
const note = await branch.getNote();
|
||||||
const tarFileName = sanitize((branch.prefix ? (branch.prefix + " - ") : "") + note.title);
|
const tarFileName = (branch.prefix ? (branch.prefix + " - ") : "") + note.title + ".tar";
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', `file; filename="${tarFileName}.tar"`);
|
res.setHeader('Content-Disposition', utils.getContentDisposition(tarFileName));
|
||||||
res.setHeader('Content-Type', 'application/tar');
|
res.setHeader('Content-Type', 'application/tar');
|
||||||
|
|
||||||
pack.pipe(res);
|
pack.pipe(res);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityName, entity }) => {
|
eventService.subscribe([ eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED ], async ({ entityName, entity }) => {
|
||||||
if (entityName === 'attributes') {
|
if (entityName === 'attributes') {
|
||||||
await runAttachedRelations(await entity.getNote(), 'runOnAttributeChange', entity);
|
await runAttachedRelations(await entity.getNote(), 'runOnAttributeChange', entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,18 +359,8 @@ async function deleteNote(branch) {
|
|||||||
const notDeletedBranches = await note.getBranches();
|
const notDeletedBranches = await note.getBranches();
|
||||||
|
|
||||||
if (notDeletedBranches.length === 0) {
|
if (notDeletedBranches.length === 0) {
|
||||||
// maybe a bit counter-intuitively, protected notes can be deleted also outside of protected session
|
note.isDeleted = true;
|
||||||
// this is because protected notes offer only confidentiality which makes some things simpler - e.g. deletion UI
|
await note.save();
|
||||||
// to allow this, we just set the isDeleted flag, otherwise saving would fail because of attempt to encrypt
|
|
||||||
// content with non-existent protected session key
|
|
||||||
// we don't reset content here, that's postponed and done later to give the user a chance to correct a mistake
|
|
||||||
await sql.execute("UPDATE notes SET isDeleted = 1 WHERE noteId = ?", [note.noteId]);
|
|
||||||
// need to manually trigger sync since it's not taken care of by note save
|
|
||||||
await syncTableService.addNoteSync(note.noteId);
|
|
||||||
|
|
||||||
for (const noteRevision of await note.getRevisions()) {
|
|
||||||
await noteRevision.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const childBranch of await note.getChildBranches()) {
|
for (const childBranch of await note.getChildBranches()) {
|
||||||
await deleteNote(childBranch);
|
await deleteNote(childBranch);
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ async function initSyncedOptions(username, password) {
|
|||||||
|
|
||||||
// passwordEncryptionService expects these options to already exist
|
// passwordEncryptionService expects these options to already exist
|
||||||
await optionService.createOption('encryptedDataKey', '', true);
|
await optionService.createOption('encryptedDataKey', '', true);
|
||||||
await optionService.createOption('encryptedDataKeyIv', '', true);
|
|
||||||
|
|
||||||
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16), true);
|
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,7 @@ async function verifyPassword(password) {
|
|||||||
async function setDataKey(password, plainTextDataKey) {
|
async function setDataKey(password, plainTextDataKey) {
|
||||||
const passwordDerivedKey = await myScryptService.getPasswordDerivedKey(password);
|
const passwordDerivedKey = await myScryptService.getPasswordDerivedKey(password);
|
||||||
|
|
||||||
const encryptedDataKeyIv = utils.randomString(16);
|
const newEncryptedDataKey = dataEncryptionService.encrypt(passwordDerivedKey, plainTextDataKey, 16);
|
||||||
|
|
||||||
await optionService.setOption('encryptedDataKeyIv', encryptedDataKeyIv);
|
|
||||||
|
|
||||||
const buffer = Buffer.from(plainTextDataKey);
|
|
||||||
|
|
||||||
const newEncryptedDataKey = dataEncryptionService.encrypt(passwordDerivedKey, encryptedDataKeyIv, buffer);
|
|
||||||
|
|
||||||
await optionService.setOption('encryptedDataKey', newEncryptedDataKey);
|
await optionService.setOption('encryptedDataKey', newEncryptedDataKey);
|
||||||
}
|
}
|
||||||
@@ -28,10 +22,9 @@ async function setDataKey(password, plainTextDataKey) {
|
|||||||
async function getDataKey(password) {
|
async function getDataKey(password) {
|
||||||
const passwordDerivedKey = await myScryptService.getPasswordDerivedKey(password);
|
const passwordDerivedKey = await myScryptService.getPasswordDerivedKey(password);
|
||||||
|
|
||||||
const encryptedDataKeyIv = await optionService.getOption('encryptedDataKeyIv');
|
|
||||||
const encryptedDataKey = await optionService.getOption('encryptedDataKey');
|
const encryptedDataKey = await optionService.getOption('encryptedDataKey');
|
||||||
|
|
||||||
const decryptedDataKey = dataEncryptionService.decrypt(passwordDerivedKey, encryptedDataKeyIv, encryptedDataKey);
|
const decryptedDataKey = dataEncryptionService.decrypt(passwordDerivedKey, encryptedDataKey, 16);
|
||||||
|
|
||||||
return decryptedDataKey;
|
return decryptedDataKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ function decryptNoteTitle(noteId, encryptedTitle) {
|
|||||||
const dataKey = getDataKey();
|
const dataKey = getDataKey();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const iv = dataEncryptionService.noteTitleIv(noteId);
|
return dataEncryptionService.decryptString(dataKey, encryptedTitle);
|
||||||
|
|
||||||
return dataEncryptionService.decryptString(dataKey, iv, encryptedTitle);
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
e.message = `Cannot decrypt note title for noteId=${noteId}: ` + e.message;
|
e.message = `Cannot decrypt note title for noteId=${noteId}: ` + e.message;
|
||||||
@@ -57,17 +55,15 @@ function decryptNote(note) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (note.title) {
|
if (note.title) {
|
||||||
note.title = dataEncryptionService.decryptString(dataKey, dataEncryptionService.noteTitleIv(note.noteId), note.title);
|
note.title = dataEncryptionService.decryptString(dataKey, note.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.content) {
|
if (note.content) {
|
||||||
const contentIv = dataEncryptionService.noteContentIv(note.noteId);
|
if (note.type === 'file' || note.type === 'image') {
|
||||||
|
note.content = dataEncryptionService.decrypt(dataKey, note.content);
|
||||||
if (note.type === 'file') {
|
|
||||||
note.content = dataEncryptionService.decrypt(dataKey, contentIv, note.content);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
note.content = dataEncryptionService.decryptString(dataKey, contentIv, note.content);
|
note.content = dataEncryptionService.decryptString(dataKey, note.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,26 +87,26 @@ function decryptNoteRevision(hist) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hist.title) {
|
if (hist.title) {
|
||||||
hist.title = dataEncryptionService.decryptString(dataKey, dataEncryptionService.noteTitleIv(hist.noteRevisionId), hist.title);
|
hist.title = dataEncryptionService.decryptString(dataKey, hist.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hist.content) {
|
if (hist.content) {
|
||||||
hist.content = dataEncryptionService.decryptString(dataKey, dataEncryptionService.noteContentIv(hist.noteRevisionId), hist.content);
|
hist.content = dataEncryptionService.decryptString(dataKey, hist.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function encryptNote(note) {
|
function encryptNote(note) {
|
||||||
const dataKey = getDataKey();
|
const dataKey = getDataKey();
|
||||||
|
|
||||||
note.title = dataEncryptionService.encrypt(dataKey, dataEncryptionService.noteTitleIv(note.noteId), note.title);
|
note.title = dataEncryptionService.encrypt(dataKey, note.title);
|
||||||
note.content = dataEncryptionService.encrypt(dataKey, dataEncryptionService.noteContentIv(note.noteId), note.content);
|
note.content = dataEncryptionService.encrypt(dataKey, note.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function encryptNoteRevision(revision) {
|
function encryptNoteRevision(revision) {
|
||||||
const dataKey = getDataKey();
|
const dataKey = getDataKey();
|
||||||
|
|
||||||
revision.title = dataEncryptionService.encrypt(dataKey, dataEncryptionService.noteTitleIv(revision.noteRevisionId), revision.title);
|
revision.title = dataEncryptionService.encrypt(dataKey, revision.title);
|
||||||
revision.content = dataEncryptionService.encrypt(dataKey, dataEncryptionService.noteContentIv(revision.noteRevisionId), revision.content);
|
revision.content = dataEncryptionService.encrypt(dataKey, revision.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -47,11 +47,6 @@ async function getBranch(branchId) {
|
|||||||
return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]);
|
return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {Image|null} */
|
|
||||||
async function getImage(imageId) {
|
|
||||||
return await getEntity("SELECT * FROM images WHERE imageId = ?", [imageId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {Attribute|null} */
|
/** @returns {Attribute|null} */
|
||||||
async function getAttribute(attributeId) {
|
async function getAttribute(attributeId) {
|
||||||
return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]);
|
return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]);
|
||||||
@@ -122,7 +117,6 @@ module.exports = {
|
|||||||
getEntity,
|
getEntity,
|
||||||
getNote,
|
getNote,
|
||||||
getBranch,
|
getBranch,
|
||||||
getImage,
|
|
||||||
getAttribute,
|
getAttribute,
|
||||||
getOption,
|
getOption,
|
||||||
updateEntity,
|
updateEntity,
|
||||||
|
|||||||
@@ -79,6 +79,19 @@ function getParams(params) {
|
|||||||
}).join(",");
|
}).join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getScriptBundleForFrontend(note) {
|
||||||
|
const bundle = await getScriptBundle(note);
|
||||||
|
|
||||||
|
// for frontend we return just noteIds because frontend needs to use its own entity instances
|
||||||
|
bundle.noteId = bundle.note.noteId;
|
||||||
|
delete bundle.note;
|
||||||
|
|
||||||
|
bundle.allNoteIds = bundle.allNotes.map(note => note.noteId);
|
||||||
|
delete bundle.allNotes;
|
||||||
|
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
async function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = []) {
|
async function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = []) {
|
||||||
if (!note.isContentAvailable) {
|
if (!note.isContentAvailable) {
|
||||||
return;
|
return;
|
||||||
@@ -153,14 +166,8 @@ function sanitizeVariableName(str) {
|
|||||||
return str.replace(/[^a-z0-9_]/gim, "");
|
return str.replace(/[^a-z0-9_]/gim, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getScriptBundleForNoteId(noteId) {
|
|
||||||
const note = await repository.getNote(noteId);
|
|
||||||
return await getScriptBundle(note);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
executeNote,
|
executeNote,
|
||||||
executeScript,
|
executeScript,
|
||||||
getScriptBundle,
|
getScriptBundleForFrontend
|
||||||
getScriptBundleForNoteId
|
|
||||||
};
|
};
|
||||||
@@ -4,6 +4,7 @@ const crypto = require('crypto');
|
|||||||
const randtoken = require('rand-token').generator({source: 'crypto'});
|
const randtoken = require('rand-token').generator({source: 'crypto'});
|
||||||
const unescape = require('unescape');
|
const unescape = require('unescape');
|
||||||
const escape = require('escape-html');
|
const escape = require('escape-html');
|
||||||
|
const sanitize = require("sanitize-filename");
|
||||||
|
|
||||||
function newEntityId() {
|
function newEntityId() {
|
||||||
return randomString(12);
|
return randomString(12);
|
||||||
@@ -127,6 +128,22 @@ function crash() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeFilenameForHeader(filename) {
|
||||||
|
let sanitizedFilename = sanitize(filename);
|
||||||
|
|
||||||
|
if (sanitizedFilename.trim().length === 0) {
|
||||||
|
sanitizedFilename = "file";
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeURIComponent(sanitizedFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContentDisposition(filename) {
|
||||||
|
const sanitizedFilename = sanitizeFilenameForHeader(filename);
|
||||||
|
|
||||||
|
return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
randomSecureToken,
|
randomSecureToken,
|
||||||
randomString,
|
randomString,
|
||||||
@@ -147,5 +164,7 @@ module.exports = {
|
|||||||
intersection,
|
intersection,
|
||||||
union,
|
union,
|
||||||
escapeRegExp,
|
escapeRegExp,
|
||||||
crash
|
crash,
|
||||||
|
sanitizeFilenameForHeader,
|
||||||
|
getContentDisposition
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" class="theme-<%= theme %>">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Trilium Notes</title>
|
<title>Trilium Notes</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="desktop">
|
<body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
|
||||||
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) <%= rightPaneWidthPercent %>fr">
|
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) <%= rightPaneWidthPercent %>fr">
|
||||||
<div id="header" class="hide-toggle">
|
<div id="header" class="hide-toggle">
|
||||||
<div id="history-navigation" style="display: none;">
|
<div id="history-navigation" style="display: none;">
|
||||||
|
|||||||
@@ -55,6 +55,46 @@
|
|||||||
|
|
||||||
<p>Zooming can be controlled with CTRL-+ and CTRL-= shortcuts as well.</p>
|
<p>Zooming can be controlled with CTRL-+ and CTRL-= shortcuts as well.</p>
|
||||||
|
|
||||||
|
<h3>Font sizes</h3>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-4">
|
||||||
|
<label for="main-font-size">Main font size</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="main-font-size" min="50" max="200" step="10"/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-4">
|
||||||
|
<label for="tree-font-size">Note tree font size</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="tree-font-size" min="50" max="200" step="10"/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-4">
|
||||||
|
<label for="detail-font-size">Note detail font size</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" id="detail-font-size" min="50" max="200" step="10"/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Note that tree and detail font sizing is relative to the main font size setting.</p>
|
||||||
|
|
||||||
<h3>Left pane sizing</h3>
|
<h3>Left pane sizing</h3>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px;">
|
<div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px; font-size: larger; display: none;">
|
||||||
<h1>Trilium Notes setup</h1>
|
<h1>Trilium Notes setup</h1>
|
||||||
|
|
||||||
<div class="alert alert-warning" id="alert" style="display: none;">
|
<div class="alert alert-warning" id="alert" style="display: none;">
|
||||||
|
|||||||
Reference in New Issue
Block a user