Compare commits

..

40 Commits

Author SHA1 Message Date
zadam 2502646a64 release 0.39.1-beta 2019-12-27 21:21:57 +01:00
zadam 3d95d69f80 styling hack for the design to look correct in both FF and chrome 2019-12-27 21:18:14 +01:00
zadam df751f5d67 fix icon 2019-12-27 21:11:56 +01:00
zadam 4f06b6de78 fix registration of global shortcuts, fixes #786 2019-12-27 20:28:27 +01:00
zadam d2177cd517 fix detection of desktop build in setup page, closes #787 2019-12-27 20:22:46 +01:00
zadam 0affcf5ad2 move electron-installer-debian to optional dependencies since it can't be installed on windows, #783 2019-12-26 10:02:55 +01:00
zadam 7a416b107b fix electron dep issue in the server version, closes #784 2019-12-25 19:38:28 +01:00
zadam 1ff124dab7 release 0.39.0-beta 2019-12-25 12:18:52 +01:00
zadam 4cb511bad0 fix import with style.css 2019-12-25 12:09:49 +01:00
zadam 73c8d145fa tar export fixes + added code block example to the demo document 2019-12-25 11:56:55 +01:00
zadam ab79f24729 added content style to tar html export as well 2019-12-25 11:34:45 +01:00
zadam cec71f65b3 load ckeditor content style for printing to have more similar look to the in-editor, #782 2019-12-25 10:59:45 +01:00
zadam f75c008154 more sync recovery fixes 2019-12-24 17:49:44 +01:00
zadam 474baa7d95 sync recovery fixes 2019-12-24 16:00:31 +01:00
zadam a155b6e8d5 create separate window for setup and then main window 2019-12-24 14:42:03 +01:00
zadam 229974e543 added option to enable native title bar (disabled by default) 2019-12-24 12:10:32 +01:00
zadam 6fc19bfb93 Merge branch 'stable' 2019-12-24 11:55:27 +01:00
zadam d389100611 properly cleanup sidebar content after closing tab 2019-12-23 21:46:37 +01:00
zadam ea7257a5b2 cleaned up experimental attribute pane 2019-12-23 21:13:56 +01:00
zadam 0ebc947fbd visual tweaks 2019-12-23 21:05:47 +01:00
zadam c89514f9bb saving size and visibility state of the panes 2019-12-23 20:34:29 +01:00
zadam e0368e395c removed left and right sidebar sizing options 2019-12-23 19:45:59 +01:00
zadam 6986c201dd removed hideTabRowForOneTab option 2019-12-23 19:39:48 +01:00
zadam bcf163f8a1 css alignment 2019-12-23 17:12:17 +01:00
zadam 15aaead7b9 show/hide switcher for the sidebar 2019-12-23 16:48:34 +01:00
zadam d29c5c4758 sidebar pulled outside of the tab content and added splitter 2019-12-23 15:50:24 +01:00
zadam 81e2baeee5 blur title buttons after clicking 2019-12-23 13:46:26 +01:00
zadam 4cececafc9 added title bar buttons 2019-12-23 13:34:54 +01:00
zadam 7c8e7a3f4b frameless design with tabs on top, split for left panel 2019-12-23 11:52:45 +01:00
zadam 613d5f93e8 convert css grid design to flex based one 2019-12-23 08:52:57 +01:00
zadam 4f5b23fbf8 tar file export now sets mtime to tar records based on utcDateCreated of a note, closes #487 2019-12-22 10:57:55 +01:00
zadam a37b9cfc7b steel blue tweaks in demo document 2019-12-21 15:48:36 +01:00
zadam 2bc18bc214 use main border color for tab border 2019-12-21 15:37:51 +01:00
zadam f31a998c5d path list contains a button to add a new path, closes #611 2019-12-21 13:55:13 +01:00
zadam 5552917533 Merge branch 'stable' 2019-12-21 13:39:12 +01:00
zadam d1941cc650 Merge branch 'stable' 2019-12-20 21:00:30 +01:00
zadam 0db3722ec2 package updates 2019-12-20 18:04:05 +01:00
zadam d47403c0e7 implemented sync hash check recovery process 2019-12-18 22:58:30 +01:00
zadam 77311954a1 added sectors for contect check computation 2019-12-18 22:24:13 +01:00
zadam b7cf4fe96b more general filter parsing 2019-12-18 20:51:48 +01:00
42 changed files with 1332 additions and 793 deletions
+32 -2
View File
@@ -2,7 +2,7 @@
<dataSource name="document.db">
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.17">
<root id="1">
<ServerVersion>3.25.1</ServerVersion>
<ServerVersion>3.16.1</ServerVersion>
</root>
<schema id="2" parent="1" name="main">
<Current>1</Current>
@@ -57,6 +57,7 @@
<index id="24" parent="6" name="sqlite_autoindex_api_tokens_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>apiTokenId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="25" parent="6">
@@ -125,17 +126,21 @@
<index id="37" parent="7" name="sqlite_autoindex_attributes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>attributeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="38" parent="7" name="IDX_attributes_noteId_index">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="39" parent="7" name="IDX_attributes_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="40" parent="7" name="IDX_attributes_value_index">
<ColNames>value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="41" parent="7">
<ColNames>attributeId</ColNames>
@@ -197,14 +202,17 @@ value</ColNames>
<index id="52" parent="8" name="sqlite_autoindex_branches_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="53" parent="8" name="IDX_branches_noteId_parentNoteId">
<ColNames>noteId
parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="54" parent="8" name="IDX_branches_parentNoteId">
<ColNames>parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="55" parent="8">
<ColNames>branchId</ColNames>
@@ -235,6 +243,7 @@ parentNoteId</ColNames>
<index id="60" parent="9" name="sqlite_autoindex_note_contents_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="61" parent="9">
@@ -265,6 +274,7 @@ parentNoteId</ColNames>
<index id="66" parent="10" name="sqlite_autoindex_note_revision_contents_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="67" parent="10">
@@ -349,22 +359,28 @@ parentNoteId</ColNames>
<index id="82" parent="11" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="83" parent="11" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="84" parent="11" name="IDX_note_revisions_utcDateLastEdited">
<ColNames>utcDateLastEdited</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="85" parent="11" name="IDX_note_revisions_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="86" parent="11" name="IDX_note_revisions_dateLastEdited">
<ColNames>dateLastEdited</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="87" parent="11" name="IDX_note_revisions_dateCreated">
<ColNames>dateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="88" parent="11">
<ColNames>noteRevisionId</ColNames>
@@ -446,28 +462,36 @@ parentNoteId</ColNames>
<index id="102" parent="12" name="sqlite_autoindex_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="103" parent="12" name="IDX_notes_title">
<ColNames>title</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="104" parent="12" name="IDX_notes_type">
<ColNames>type</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="105" parent="12" name="IDX_notes_isDeleted">
<ColNames>isDeleted</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="106" parent="12" name="IDX_notes_dateCreated">
<ColNames>dateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="107" parent="12" name="IDX_notes_dateModified">
<ColNames>dateModified</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="108" parent="12" name="IDX_notes_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="109" parent="12" name="IDX_notes_utcDateModified">
<ColNames>utcDateModified</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="110" parent="12">
<ColNames>noteId</ColNames>
@@ -508,6 +532,7 @@ parentNoteId</ColNames>
<index id="117" parent="13" name="sqlite_autoindex_options_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="118" parent="13">
@@ -543,6 +568,7 @@ parentNoteId</ColNames>
<index id="124" parent="14" name="sqlite_autoindex_recent_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="125" parent="14">
@@ -563,10 +589,12 @@ parentNoteId</ColNames>
<index id="128" parent="15" name="sqlite_autoindex_source_ids_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>sourceId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="129" parent="15" name="IDX_source_ids_utcDateCreated">
<ColNames>utcDateCreated</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="130" parent="15">
<ColNames>sourceId</ColNames>
@@ -587,7 +615,7 @@ parentNoteId</ColNames>
</column>
<column id="134" parent="16" name="rootpage">
<Position>4</Position>
<DataType>int|0s</DataType>
<DataType>integer|0s</DataType>
</column>
<column id="135" parent="16" name="sql">
<Position>5</Position>
@@ -628,10 +656,12 @@ parentNoteId</ColNames>
<index id="143" parent="18" name="IDX_sync_entityName_entityId">
<ColNames>entityName
entityId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="144" parent="18" name="IDX_sync_utcSyncDate">
<ColNames>utcSyncDate</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="145" parent="18">
<ColNames>id</ColNames>
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:12.13.1-alpine
FROM node:12.14.0-alpine
# Create app directory
WORKDIR /usr/src/app
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
PKG_DIR=dist/trilium-linux-x64-server
NODE_VERSION=12.13.1
NODE_VERSION=12.14.0
if [ "$1" != "DONTCOPY" ]
then
BIN
View File
Binary file not shown.
+15 -111
View File
@@ -1,86 +1,17 @@
'use strict';
const {app, globalShortcut, BrowserWindow} = require('electron');
const path = require('path');
const log = require('./src/services/log');
const {app, globalShortcut} = require('electron');
const sqlInit = require('./src/services/sql_init');
const cls = require('./src/services/cls');
const url = require("url");
const port = require('./src/services/port');
const env = require('./src/services/env');
const keyboardActionsService = require('./src/services/keyboard_actions');
const appIconService = require('./src/services/app_icon');
const windowStateKeeper = require('electron-window-state');
const windowService = require('./src/services/window');
// Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
appIconService.installLocalAppIcon();
// Prevent window being garbage collected
let mainWindow;
require('electron-dl')({ saveAs: true });
function onClosed() {
// Dereference the window
// For multiple windows store them in an array
mainWindow = null;
}
async function createMainWindow() {
await sqlInit.dbConnection;
// if schema doesn't exist -> setup process
// if schema exists, then we need to wait until the migration process is finished
if (await sqlInit.schemaExists()) {
await sqlInit.dbReady;
}
const mainWindowState = windowStateKeeper({
// default window width & height so it's usable on 1600 * 900 display (including some extra panels etc.)
defaultWidth: 1200,
defaultHeight: 800
});
const win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
title: 'Trilium Notes',
webPreferences: {
nodeIntegration: true
},
icon: path.join(__dirname, 'images/app-icons/png/256x256' + (env.isDev() ? '-dev' : '') + '.png')
});
mainWindowState.manage(win);
win.setMenuBarVisibility(false);
win.loadURL('http://127.0.0.1:' + await port);
win.on('closed', onClosed);
win.webContents.on('new-window', (e, url) => {
if (url !== win.webContents.getURL()) {
e.preventDefault();
require('electron').shell.openExternal(url);
}
});
// prevent drag & drop to navigate away from trilium
win.webContents.on('will-navigate', (ev, targetUrl) => {
const parsedUrl = url.parse(targetUrl);
// we still need to allow internal redirects from setup and migration pages
if (!['localhost', '127.0.0.1'].includes(parsedUrl.hostname) || (parsedUrl.path && parsedUrl.path !== '/')) {
ev.preventDefault();
}
});
return win;
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
@@ -90,50 +21,23 @@ app.on('window-all-closed', () => {
}
});
app.on('activate', () => {
if (!mainWindow) {
mainWindow = createMainWindow();
}
});
async function registerGlobalShortcuts() {
await sqlInit.dbReady;
const allActions = await keyboardActionsService.getKeyboardActions();
for (const action of allActions) {
if (!action.effectiveShortcuts) {
continue;
}
for (const shortcut of action.effectiveShortcuts) {
if (shortcut.startsWith('global:')) {
const translatedShortcut = shortcut.substr(7);
const result = globalShortcut.register(translatedShortcut, cls.wrap(async () => {
// window may be hidden / not in focus
mainWindow.focus();
mainWindow.webContents.send('globalShortcut', action.actionName);
}));
if (result) {
log.info(`Registered global shortcut ${translatedShortcut} for action ${action.actionName}`);
}
else {
log.info(`Could not register global shortcut ${translatedShortcut}`);
}
}
}
}
}
app.on('ready', async () => {
app.setAppUserModelId('com.github.zadam.trilium');
mainWindow = await createMainWindow();
await sqlInit.dbConnection;
registerGlobalShortcuts();
// if schema doesn't exist -> setup process
// if schema exists, then we need to wait until the migration process is finished
if (await sqlInit.schemaExists()) {
await sqlInit.dbReady;
await windowService.createMainWindow();
}
else {
await windowService.createSetupWindow();
}
await windowService.registerGlobalShortcuts();
});
app.on('will-quit', () => {
+310
View File
@@ -0,0 +1,310 @@
/*
* !!!!!!! This stylesheet is heavily modified compared to the original for similarity with in-editor look !!!!!!!
* This is used for printing and tar HTML export
*
* CKEditor 5 (v15.0.0) content styles.
* Generated on Wed, 27 Nov 2019 13:26:13 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/content-styles.html
*/
:root {
--ck-highlight-marker-blue: #72cdfd;
--ck-highlight-marker-green: #63f963;
--ck-highlight-marker-pink: #fc7999;
--ck-highlight-marker-yellow: #fdfd77;
--ck-highlight-pen-green: #118800;
--ck-highlight-pen-red: #e91313;
--ck-image-style-spacing: 1.5em;
--ck-todo-list-checkmark-size: 16px;
font-family: Arial, Sans-Serif;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list {
list-style: none;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list li {
margin-bottom: 5px;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list li .todo-list {
margin-top: 5px;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label > input {
-webkit-appearance: none;
display: inline-block;
position: relative;
width: var(--ck-todo-list-checkmark-size);
height: var(--ck-todo-list-checkmark-size);
vertical-align: middle;
border: 0;
left: -25px;
margin-right: -15px;
right: 0;
margin-left: 0;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label > input::before {
display: block;
position: absolute;
box-sizing: border-box;
content: '';
width: 100%;
height: 100%;
border: 1px solid hsl(0, 0%, 20%);
border-radius: 2px;
transition: 250ms ease-in-out box-shadow, 250ms ease-in-out background, 250ms ease-in-out border;
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label > input::after {
display: block;
position: absolute;
box-sizing: content-box;
pointer-events: none;
content: '';
left: calc( var(--ck-todo-list-checkmark-size) / 3 );
top: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
width: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
height: calc( var(--ck-todo-list-checkmark-size) / 2.6 );
border-style: solid;
border-color: transparent;
border-width: 0 calc( var(--ck-todo-list-checkmark-size) / 8 ) calc( var(--ck-todo-list-checkmark-size) / 8 ) 0;
transform: rotate(45deg);
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label > input[checked]::before {
border-color: hsl(126, 64%, 41%);
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label > input[checked]::after {
border-color: hsl(126, 64%, 41%);
}
/* ckeditor5-list/theme/todolist.css */
.todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* ckeditor5-image/theme/image.css */
.image {
display: table;
clear: both;
text-align: center;
margin: 1em auto;
}
/* ckeditor5-image/theme/image.css */
.image > img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 50px;
}
/* ckeditor5-block-quote/theme/blockquote.css */
blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* ckeditor5-image/theme/imageresize.css */
.image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* ckeditor5-image/theme/imageresize.css */
.image.image_resized img {
width: 100%;
}
/* ckeditor5-image/theme/imageresize.css */
.image.image_resized > figcaption {
display: block;
}
/* ckeditor5-image/theme/imagestyle.css */
.image-style-side,
.image-style-align-left,
.image-style-align-center,
.image-style-align-right {
max-width: 50%;
}
/* ckeditor5-image/theme/imagestyle.css */
.image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* ckeditor5-image/theme/imagestyle.css */
.image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* ckeditor5-image/theme/imagestyle.css */
.image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* ckeditor5-media-embed/theme/mediaembed.css */
.media {
clear: both;
margin: 1em 0;
display: block;
min-width: 15em;
}
/* ckeditor5-table/theme/table.css */
.table {
display: table;
margin: 0;
}
/* ckeditor5-table/theme/table.css */
.table table {
border-collapse: collapse;
border-spacing: 0;
border: 1px double hsl(0, 0%, 70%);
}
/* ckeditor5-table/theme/table.css */
.table table td,
.table table th {
min-width: 2em;
padding: .4em;
border: 1px solid #d9d9d9;
}
/* ckeditor5-table/theme/table.css */
.table table th {
font-weight: bold;
background-color: #f5f5f5;
text-align: left;
}
/* ckeditor5-highlight/theme/highlight.css */
.marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* ckeditor5-highlight/theme/highlight.css */
.marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* ckeditor5-highlight/theme/highlight.css */
.marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* ckeditor5-highlight/theme/highlight.css */
.marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* ckeditor5-highlight/theme/highlight.css */
.pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* ckeditor5-highlight/theme/highlight.css */
.pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: #fff;
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* ckeditor5-basic-styles/theme/code.css */
code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* ckeditor5-image/theme/imagecaption.css */
.image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: hsl(0, 0%, 20%);
background-color: hsl(0, 0%, 97%);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* ckeditor5-horizontal-line/theme/horizontalline.css */
hr {
border-width: 1px 0 0;
border-style: solid;
border-color: hsl(0, 0%, 37%);
margin: 0;
}
/* ckeditor5-code-block/theme/codeblock.css */
pre {
padding: 1em;
color: #353535;
background: hsla(0, 0%, 78%, 0.3);
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
text-align: left;
direction: ltr;
tab-size: 4;
white-space: pre-wrap;
font-style: normal;
min-width: 200px;
}
/* ckeditor5-code-block/theme/codeblock.css */
pre code {
background: unset;
padding: 0;
border-radius: 0;
}
.ck-widget__selection-handle {
display: none;
}
@media print {
/* ckeditor5-page-break/theme/pagebreak.css */
.page-break {
padding: 0;
}
/* ckeditor5-page-break/theme/pagebreak.css */
.page-break::after {
display: none;
}
}
+3
View File
File diff suppressed because one or more lines are too long
+149 -153
View File
@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.38.2",
"version": "0.39.0-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -629,12 +629,9 @@
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
},
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"requires": {
"es6-promisify": "^5.0.0"
}
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
},
"ajv": {
"version": "6.10.2",
@@ -760,6 +757,12 @@
"temp-file": "^3.3.4"
},
"dependencies": {
"ejs": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz",
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
"dev": true
},
"hosted-git-info": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.2.tgz",
@@ -768,6 +771,12 @@
"requires": {
"lru-cache": "^5.1.1"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
@@ -871,7 +880,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/asar/-/asar-2.0.1.tgz",
"integrity": "sha512-Vo9yTuUtyFahkVMFaI6uMuX6N7k5DWa6a/8+7ov0/f8Lq9TVR0tUjzSzxQSxT1Y+RJIZgnP7BVb6Uhi+9cjxqA==",
"dev": true,
"requires": {
"chromium-pickle-js": "^0.2.0",
"commander": "^2.20.0",
@@ -885,14 +893,12 @@
"commander": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
"integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
"dev": true
"integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg=="
},
"glob": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -906,7 +912,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz",
"integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==",
"dev": true,
"requires": {
"bluebird": "^3.5.0",
"tmp": "0.1.0"
@@ -972,8 +977,7 @@
"author-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz",
"integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=",
"dev": true
"integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA="
},
"aws-sign2": {
"version": "0.7.0",
@@ -1137,8 +1141,7 @@
"bluebird": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz",
"integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==",
"dev": true
"integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg=="
},
"bluebird-lst": {
"version": "1.0.9",
@@ -1592,8 +1595,7 @@
"chromium-pickle-js": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
"integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=",
"dev": true
"integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU="
},
"ci-info": {
"version": "2.0.0",
@@ -1662,7 +1664,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
@@ -1672,20 +1673,17 @@
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
@@ -1696,7 +1694,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
@@ -1752,7 +1749,6 @@
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
@@ -1760,8 +1756,7 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-support": {
"version": "1.1.3",
@@ -1977,7 +1972,7 @@
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/cross-spawn-promise/-/cross-spawn-promise-0.10.1.tgz",
"integrity": "sha1-25y0xQxgtyoVvgSbeBIs44LYexA=",
"dev": true,
"optional": true,
"requires": {
"cross-spawn": "^5.1.0"
}
@@ -2050,8 +2045,7 @@
"cuint": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
"integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=",
"dev": true
"integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs="
},
"currently-unhandled": {
"version": "0.4.1",
@@ -2102,9 +2096,9 @@
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
},
"dayjs": {
"version": "1.8.17",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.17.tgz",
"integrity": "sha512-47VY/htqYqr9GHd7HW/h56PpQzRBSJcxIQFwqL3P20bMF/3az5c3PWdVY3LmPXFl6cQCYHL7c79b9ov+2bOBbw=="
"version": "1.8.18",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.18.tgz",
"integrity": "sha512-JBMJZghNK8TtuoPnKNIzW9xavVVigld/zmZNpZSyQbkb2Opp55YIfZUpE4OEqPF/iyUVQTKcn1bC2HtC8B7s3g=="
},
"debug": {
"version": "4.1.1",
@@ -2473,6 +2467,20 @@
"sanitize-filename": "^1.6.2",
"semver": "^6.3.0",
"temp-file": "^3.3.4"
},
"dependencies": {
"ejs": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz",
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"builder-util": {
@@ -2732,9 +2740,9 @@
},
"dependencies": {
"@types/node": {
"version": "10.17.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.0.tgz",
"integrity": "sha512-wuJwN2KV4tIRz1bu9vq5kSPasJ8IsEjZaP1ZR7KlmdUZvGF/rXy8DmXOVwUD0kAtvtJ7aqMKPqUXC0NUTDbrDg==",
"version": "10.17.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.11.tgz",
"integrity": "sha512-dNd2pp8qTzzNLAs3O8nH3iU9DG9866KHq9L3ISPB7DOGERZN81nW/5/g/KzMJpCU8jrbCiMRBzV9/sCEdRosig==",
"dev": true
}
}
@@ -2877,13 +2885,13 @@
}
},
"electron-dl": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-1.14.0.tgz",
"integrity": "sha512-4okyei42a1mLsvLK7hLrIfd20EQzB18nIlLTwBV992aMSmTGLUEFRTmO1MfSslGNrzD8nuPuy1l/VxO8so4lig==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-2.0.0.tgz",
"integrity": "sha512-crv64+Z2ZCsHCoJHBBOypDKx8cy+4jTGMc5/rDDzIajihDN/wITkJOdb6XG8b43YlWr3NS1/r6j+IdwaFNmMiQ==",
"requires": {
"ext-name": "^5.0.0",
"pupa": "^1.0.0",
"unused-filename": "^1.0.0"
"pupa": "^2.0.1",
"unused-filename": "^2.1.0"
}
},
"electron-download": {
@@ -2946,7 +2954,7 @@
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.7.3.tgz",
"integrity": "sha512-l4chYFTWr6uWODKYUXeC+Z4tqGa3b8e+Y2WUBf3F7Ruv6yYzZ+Ccic65oXreeotx09B7sUx1KTuwXRsRJHKlMw==",
"dev": true,
"optional": true,
"requires": {
"asar": "^2.0.1",
"cross-spawn-promise": "^0.10.1",
@@ -2963,7 +2971,7 @@
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"dev": true,
"optional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -2972,6 +2980,12 @@
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"optional": true
}
}
},
@@ -2979,7 +2993,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-2.0.1.tgz",
"integrity": "sha512-6/rgdWoezE5/JKSu6Y1mK3TT2lUeRKpviVoh063DKSLwcDPHQFgJ0/uDxqGppi3AF4du8n1vVio0VEkCEwlM1A==",
"dev": true,
"optional": true,
"requires": {
"debug": "^4.1.1",
"electron-installer-common": "^0.7.1",
@@ -2994,19 +3008,19 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
"optional": true
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
"optional": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"optional": true,
"requires": {
"locate-path": "^3.0.0"
}
@@ -3015,13 +3029,13 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
"optional": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"optional": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
@@ -3032,7 +3046,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^4.1.0"
}
@@ -3041,7 +3055,7 @@
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz",
"integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==",
"dev": true,
"optional": true,
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
@@ -3060,7 +3074,7 @@
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz",
"integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==",
"dev": true,
"optional": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@@ -3166,6 +3180,14 @@
"sanitize-filename": "^1.6.0",
"semver": "^6.0.0",
"yargs-parser": "^16.0.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"electron-publish": {
@@ -3320,8 +3342,7 @@
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"encodeurl": {
"version": "1.0.2",
@@ -3389,18 +3410,10 @@
"dev": true,
"optional": true
},
"es6-promise": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"requires": {
"es6-promise": "^4.0.3"
}
"escape-goat": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz",
"integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q=="
},
"escape-html": {
"version": "1.0.3",
@@ -3834,9 +3847,9 @@
}
},
"file-type": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.0.tgz",
"integrity": "sha512-WTvyKq8yjtNmUtVAD8LGcTkvtCdJglM6ks2HTqEClm6+65XTqM6MoZYA1Vtra50DLRWLiM38fEs1y56f5VhnUA=="
"version": "12.4.2",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz",
"integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg=="
},
"filename-regex": {
"version": "2.0.1",
@@ -4094,7 +4107,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz",
"integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==",
"dev": true
"optional": true
},
"gauge": {
"version": "2.7.4",
@@ -4121,14 +4134,13 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-folder-size": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz",
"integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==",
"dev": true,
"optional": true,
"requires": {
"gar": "^1.0.4",
"tiny-each-async": "2.0.3"
@@ -4452,6 +4464,13 @@
"integrity": "sha512-vKea1DrcLA80Hrfc7AQgfoGvEaByfR5mH08t+zuWOWY94TFBmabdEL56mUbcijvadG9RxsXR2gUUFrfj4/iTcA==",
"dev": true,
"optional": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"optional": true
}
}
},
@@ -4896,22 +4915,12 @@
}
},
"http-proxy-agent": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
"integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-3.0.0.tgz",
"integrity": "sha512-uGuJaBWQWDQCJI5ip0d/VTYZW0nRrlLWXA4A7P1jrsa+f77rW2yXz315oBt6zGCF6l8C2tlMxY7ffULCj+5FhA==",
"requires": {
"agent-base": "4",
"debug": "3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
"agent-base": "5",
"debug": "4"
}
},
"http-signature": {
@@ -4925,27 +4934,12 @@
}
},
"https-proxy-agent": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz",
"integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
"requires": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
"agent-base": "5",
"debug": "4"
}
},
"iconv-lite": {
@@ -5819,7 +5813,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
@@ -6068,6 +6061,13 @@
"integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==",
"requires": {
"semver": "^6.0.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"map-obj": {
@@ -7399,7 +7399,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
"integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
@@ -7408,7 +7407,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
@@ -7442,8 +7440,7 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"package-json": {
"version": "6.5.0",
@@ -7547,6 +7544,12 @@
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"url-parse-lax": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
@@ -7567,7 +7570,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz",
"integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=",
"dev": true,
"requires": {
"author-regex": "^1.0.0"
}
@@ -8496,9 +8498,12 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"pupa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pupa/-/pupa-1.0.0.tgz",
"integrity": "sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y="
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz",
"integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==",
"requires": {
"escape-goat": "^2.0.0"
}
},
"qs": {
"version": "6.7.0",
@@ -8796,14 +8801,12 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"requizzle": {
"version": "0.2.3",
@@ -8966,9 +8969,9 @@
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.1.1.tgz",
"integrity": "sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A=="
},
"semver-compare": {
"version": "1.0.0",
@@ -9706,7 +9709,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz",
"integrity": "sha1-jru/1tYpXxNwAD+7NxYq/loKUdE=",
"dev": true
"optional": true
},
"tinycolor2": {
"version": "1.4.1",
@@ -9717,7 +9720,6 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
"integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
"dev": true,
"requires": {
"rimraf": "^2.6.3"
},
@@ -9726,7 +9728,6 @@
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -9740,7 +9741,6 @@
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
@@ -9751,7 +9751,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.0.2.tgz",
"integrity": "sha512-zl71nFWjPKW2KXs+73gEk8RmqvtAeXPxhWDkTUoa3MSMkjq3I+9OeknjF178MQoMYsdqL730hfzvNfEkePxq9Q==",
"dev": true,
"optional": true,
"requires": {
"tmp": "0.1.0"
}
@@ -9986,12 +9986,19 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"unused-filename": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-1.0.0.tgz",
"integrity": "sha1-00CID3GuIRXrqhMlvvBcxmhEacY=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz",
"integrity": "sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg==",
"requires": {
"modify-filename": "^1.1.0",
"path-exists": "^3.0.0"
"path-exists": "^4.0.0"
},
"dependencies": {
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
}
}
},
"unzip-response": {
@@ -10288,8 +10295,7 @@
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wide-align": {
"version": "1.1.3",
@@ -10345,7 +10351,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
"optional": true
},
"wordwrap": {
"version": "1.0.0",
@@ -10356,7 +10362,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
@@ -10366,14 +10371,12 @@
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
@@ -10381,14 +10384,12 @@
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
@@ -10399,7 +10400,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
@@ -10430,12 +10430,9 @@
}
},
"ws": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz",
"integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==",
"requires": {
"async-limiter": "^1.0.0"
}
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz",
"integrity": "sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A=="
},
"x-xss-protection": {
"version": "1.3.0",
@@ -10509,8 +10506,7 @@
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yallist": {
"version": "3.1.1",
+11 -9
View File
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.38.3",
"version": "0.39.1-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -27,23 +27,23 @@
"commonmark": "0.29.0",
"cookie-parser": "1.4.4",
"csurf": "1.10.0",
"dayjs": "1.8.17",
"dayjs": "1.8.18",
"debug": "4.1.1",
"ejs": "2.7.4",
"electron-debug": "3.0.1",
"electron-dl": "1.14.0",
"electron-dl": "2.0.0",
"electron-find": "1.0.6",
"electron-spellchecker": "2.2.1",
"electron-window-state": "5.0.3",
"express": "4.17.1",
"express-session": "1.17.0",
"file-type": "12.4.0",
"file-type": "12.4.2",
"fs-extra": "8.1.0",
"helmet": "3.21.2",
"html": "1.0.0",
"html2plaintext": "2.1.2",
"http-proxy-agent": "2.1.0",
"https-proxy-agent": "3.0.1",
"http-proxy-agent": "3.0.0",
"https-proxy-agent": "4.0.0",
"image-type": "4.1.0",
"imagemin": "7.0.1",
"imagemin-giflossy": "5.1.10",
@@ -63,7 +63,7 @@
"rimraf": "3.0.0",
"sanitize-filename": "1.6.3",
"sax": "1.2.4",
"semver": "6.3.0",
"semver": "7.1.1",
"serve-favicon": "2.5.0",
"session-file-store": "1.3.1",
"simple-node-logger": "18.12.23",
@@ -74,15 +74,17 @@
"turndown": "5.0.3",
"turndown-plugin-gfm": "1.0.2",
"unescape": "1.0.1",
"ws": "7.2.0"
"ws": "7.2.1"
},
"devDependencies": {
"electron": "6.0.12",
"electron-builder": "22.1.0",
"electron-installer-debian": "2.0.1",
"electron-packager": "14.1.1",
"electron-rebuild": "1.8.8",
"jsdoc": "3.6.3",
"lorem-ipsum": "2.0.3"
},
"optionalDependencies": {
"electron-installer-debian": "2.0.1"
}
}
+37 -2
View File
@@ -31,6 +31,8 @@ import dateNoteService from './services/date_notes.js';
import sidebarService from './services/sidebar.js';
import importService from './services/import.js';
import keyboardActionService from "./services/keyboard_actions.js";
import splitService from "./services/split.js";
import optionService from "./services/options.js";
window.glob.isDesktop = utils.isDesktop;
window.glob.isMobile = utils.isMobile;
@@ -142,7 +144,10 @@ async function printActiveNote() {
$tabContext.$tabContent.find('.note-detail-component:visible').printThis({
header: $("<h2>").text($tabContext.note && $tabContext.note.title).prop('outerHTML') ,
importCSS: false,
loadCSS: "libraries/codemirror/codemirror.css",
loadCSS: [
"libraries/codemirror/codemirror.css",
"libraries/ckeditor/ckeditor-content.css"
],
debug: true
});
}
@@ -176,4 +181,34 @@ noteAutocompleteService.init();
if (utils.isElectron()) {
import("./services/spell_check.js").then(spellCheckService => spellCheckService.initSpellCheck());
}
}
optionService.waitForOptions().then(options => {
if (utils.isElectron() && !options.is('nativeTitleBarVisible')) {
$("#title-bar-buttons").show();
$("#minimize-btn").on('click', () => {
$("#minimize-btn").trigger('blur');
const {remote} = require('electron');
remote.BrowserWindow.getFocusedWindow().minimize();
});
$("#maximize-btn").on('click', () => {
$("#maximize-btn").trigger('blur');
const {remote} = require('electron');
const focusedWindow = remote.BrowserWindow.getFocusedWindow();
if (focusedWindow.isMaximized()) {
focusedWindow.unmaximize();
} else {
focusedWindow.maximize();
}
});
$("#close-btn").on('click', () => {
$("#close-btn").trigger('blur');
const {remote} = require('electron');
remote.BrowserWindow.getFocusedWindow().close();
});
}
});
@@ -19,12 +19,13 @@ const TPL = `
<input type="number" class="form-control" id="zoom-factor-select" min="0.3" max="2.0" step="0.1"/>
</div>
<div class="col-4">
<label for="one-tab-display-select">If there's only one tab, then...</label>
<select class="form-control" id="one-tab-display-select">
<option value="show">show the tab bar</option>
<option value="hide">hide the tab bar</option>
<label for="native-title-bar-select">Native title bar (requires app restart)</label>
<select class="form-control" id="native-title-bar-select">
<option value="show">enabled</option>
<option value="hide">disabled</option>
</select>
</div>
</div>
@@ -69,37 +70,6 @@ const TPL = `
</div>
<p>Note that tree and detail font sizing is relative to the main font size setting.</p>
<h4>Left pane sizing</h4>
<div class="form-group row">
<div class="col-6">
<label for="left-pane-min-width">Left pane minimum width (in pixels)</label>
<div class="input-group">
<input type="number" class="form-control" id="left-pane-min-width" min="100" max="2000" step="1"/>
<div class="input-group-append">
<span class="input-group-text">px</span>
</div>
</div>
</div>
<div class="col-6">
<label for="left-pane-min-width">Left pane width percent of window size</label>
<div class="input-group">
<input type="number" class="form-control" id="left-pane-width-percent" min="0" max="99" step="1"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<p>Left pane width is calculated from the percent of window size, if this is smaller than minimum width, then minimum width is used. If you want to have fixed width left pane, set minimum width to the desired width and set percent to 0.</p>
</form>`;
export default class ApperanceOptions {
@@ -108,9 +78,7 @@ export default class ApperanceOptions {
this.$themeSelect = $("#theme-select");
this.$zoomFactorSelect = $("#zoom-factor-select");
this.$oneTabDisplaySelect = $("#one-tab-display-select");
this.$leftPaneMinWidth = $("#left-pane-min-width");
this.$leftPaneWidthPercent = $("#left-pane-width-percent");
this.$nativeTitleBarSelect = $("#native-title-bar-select");
this.$mainFontSize = $("#main-font-size");
this.$treeFontSize = $("#tree-font-size");
this.$detailFontSize = $("#detail-font-size");
@@ -141,23 +109,10 @@ export default class ApperanceOptions {
this.$zoomFactorSelect.on('change', () => { zoomService.setZoomFactorAndSave(this.$zoomFactorSelect.val()); });
this.$oneTabDisplaySelect.on('change', () => {
const hideTabRowForOneTab = this.$oneTabDisplaySelect.val() === 'hide' ? 'true' : 'false';
this.$nativeTitleBarSelect.on('change', () => {
const nativeTitleBarVisible = this.$nativeTitleBarSelect.val() === 'show' ? 'true' : 'false';
server.put('options/hideTabRowForOneTab/' + hideTabRowForOneTab)
.then(optionsService.reloadOptions);
});
this.$leftPaneMinWidth.on('change', async () => {
await server.put('options/leftPaneMinWidth/' + this.$leftPaneMinWidth.val());
this.resizeLeftPanel();
});
this.$leftPaneWidthPercent.on('change', async () => {
await server.put('options/leftPaneWidthPercent/' + this.$leftPaneWidthPercent.val());
this.resizeLeftPanel();
server.put('options/nativeTitleBarVisible/' + nativeTitleBarVisible);
});
this.$mainFontSize.on('change', async () => {
@@ -204,24 +159,13 @@ export default class ApperanceOptions {
this.$zoomFactorSelect.prop('disabled', true);
}
this.$oneTabDisplaySelect.val(options.hideTabRowForOneTab === 'true' ? 'hide' : 'show');
this.$leftPaneMinWidth.val(options.leftPaneMinWidth);
this.$leftPaneWidthPercent.val(options.leftPaneWidthPercent);
this.$nativeTitleBarSelect.val(options.nativeTitleBarVisible === 'true' ? 'show' : 'hide');
this.$mainFontSize.val(options.mainFontSize);
this.$treeFontSize.val(options.treeFontSize);
this.$detailFontSize.val(options.detailFontSize);
}
resizeLeftPanel() {
const leftPanePercent = parseInt(this.$leftPaneWidthPercent.val());
const rightPanePercent = 100 - leftPanePercent;
const leftPaneMinWidth = this.$leftPaneMinWidth.val();
this.$container.css("grid-template-columns", `minmax(${leftPaneMinWidth}px, ${leftPanePercent}fr) ${rightPanePercent}fr`);
}
applyFontSizes() {
this.$body.get(0).style.setProperty("--main-font-size", this.$mainFontSize.val() + "%");
this.$body.get(0).style.setProperty("--tree-font-size", this.$treeFontSize.val() + "%");
@@ -3,45 +3,6 @@ import server from "../../services/server.js";
import optionsService from "../../services/options.js";
const TPL = `
<h4>Show sidebar in new tab</h4>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="show-sidebar-in-new-tab">
<label class="form-check-label" for="show-sidebar-in-new-tab">Show sidebar in new tab</label>
</div>
<br>
<h4>Sidebar sizing</h4>
<div class="form-group row">
<div class="col-6">
<label for="sidebar-min-width">Sidebar minimum width (in pixels)</label>
<div class="input-group">
<input type="number" class="form-control" id="sidebar-min-width" min="100" max="2000" step="1"/>
<div class="input-group-append">
<span class="input-group-text">px</span>
</div>
</div>
</div>
<div class="col-6">
<label for="left-pane-min-width">Sidebar width percent of the detail pane</label>
<div class="input-group">
<input type="number" class="form-control" id="sidebar-width-percent" min="0" max="70" step="1"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<p>Sidebar width is calculated from the percent of the detail pane, if this is smaller than minimum width, then minimum width is used. If you want to have fixed width sidebar, set minimum width to the desired width and set percent to 0.</p>
<h4>Widgets</h4>
<div id="widgets-configuration" class="row">
@@ -58,48 +19,15 @@ export default class SidebarOptions {
constructor() {
$("#options-sidebar").html(TPL);
this.$sidebarMinWidth = $("#sidebar-min-width");
this.$sidebarWidthPercent = $("#sidebar-width-percent");
this.$showSidebarInNewTab = $("#show-sidebar-in-new-tab");
this.$widgetsConfiguration = $("#widgets-configuration");
this.$widgetsEnabled = $("#widgets-enabled");
this.$widgetsDisabled = $("#widgets-disabled");
this.$sidebarMinWidth.on('change', async () => {
await server.put('options/sidebarMinWidth/' + this.$sidebarMinWidth.val());
this.resizeSidebar();
});
this.$sidebarWidthPercent.on('change', async () => {
await server.put('options/sidebarWidthPercent/' + this.$sidebarWidthPercent.val());
this.resizeSidebar();
});
this.$showSidebarInNewTab.on('change', async () => {
const flag = this.$showSidebarInNewTab.is(":checked") ? 'true' : 'false';
await server.put('options/showSidebarInNewTab/' + flag);
optionsService.reloadOptions();
});
}
async optionsLoaded(options) {
this.$widgetsEnabled.empty();
this.$widgetsDisabled.empty();
this.$sidebarMinWidth.val(options.sidebarMinWidth);
this.$sidebarWidthPercent.val(options.sidebarWidthPercent);
if (options.showSidebarInNewTab === 'true') {
this.$showSidebarInNewTab.attr("checked", "checked");
}
else {
this.$showSidebarInNewTab.removeAttr("checked");
}
const widgets = [
{name: 'attributes', title: 'Attributes'},
{name: 'linkMap', title: 'Link map'},
@@ -188,19 +116,4 @@ export default class SidebarOptions {
return null;
}
}
resizeSidebar() {
const sidebarWidthPercent = parseInt(this.$sidebarWidthPercent.val());
const sidebarMinWidth = this.$sidebarMinWidth.val();
// need to find them dynamically since they change
const $sidebar = $(".note-detail-sidebar");
const $content = $(".note-detail-content");
$sidebar.css("width", sidebarWidthPercent + '%');
$sidebar.css("min-width", sidebarMinWidth + 'px');
$content.css("width", (100 - sidebarWidthPercent) + '%');
}
}
+13 -2
View File
@@ -112,10 +112,21 @@ function registerEntrypoints() {
keyboardActionService.setGlobalActionHandler("ForwardInNoteHistory", window.history.forward);
}
let zenModeActive = false;
// hide (toggle) everything except for the note content for zen mode
const toggleZenMode = () => {
$(".hide-in-zen-mode").toggle();
$("#container").toggleClass("zen-mode");
if (!zenModeActive) {
$(".hide-in-zen-mode").addClass("hidden-by-zen-mode");
$("#container").addClass("zen-mode");
zenModeActive = true;
}
else {
// not hiding / showing explicitly since element might be hidden also for other reasons
$(".hide-in-zen-mode").removeClass("hidden-by-zen-mode");
$("#container").removeClass("zen-mode");
zenModeActive = false;
}
};
$("#toggle-zen-mode-button").on('click', toggleZenMode);
+59 -18
View File
@@ -1,6 +1,47 @@
import bundleService from "./bundle.js";
import ws from "./ws.js";
import optionsService from "./options.js";
import splitService from "./split.js";
import optionService from "./options.js";
import server from "./server.js";
import noteDetailService from "./note_detail.js";
const $sidebar = $("#right-pane");
const $sidebarContainer = $('#sidebar-container');
const $showSidebarButton = $("#show-sidebar-button");
const $hideSidebarButton = $("#hide-sidebar-button");
optionService.waitForOptions().then(options => toggleSidebar(options.is('rightPaneVisible')));
function toggleSidebar(show) {
$sidebar.toggle(show);
$showSidebarButton.toggle(!show);
$hideSidebarButton.toggle(show);
if (show) {
splitService.setupSplitWithSidebar();
}
else {
splitService.setupSplitWithoutSidebar();
}
}
$hideSidebarButton.on('click', () => {
toggleSidebar(false);
server.put('options/rightPaneVisible/false');
});
$showSidebarButton.on('click', async () => {
toggleSidebar(true);
await server.put('options/rightPaneVisible/true');
const {sidebar} = noteDetailService.getActiveTabContext();
await sidebar.noteLoaded();
sidebar.show();
});
class Sidebar {
/**
@@ -14,30 +55,18 @@ class Sidebar {
widgets: []
}, state);
this.widgets = [];
this.$sidebar = ctx.$tabContent.find(".note-detail-sidebar");
this.$widgetContainer = this.$sidebar.find(".note-detail-widget-container");
this.$showSideBarButton = this.ctx.$tabContent.find(".show-sidebar-button");
this.$hideSidebarButton = this.$sidebar.find(".hide-sidebar-button");
this.$hideSidebarButton.on('click', () => {
this.$sidebar.hide();
this.$showSideBarButton.show();
this.ctx.stateChanged();
});
this.$widgetContainer = $sidebar.find(`.sidebar-widget-container[data-tab-id=${this.ctx.tabId}]`);
this.$showSideBarButton.on('click', () => {
this.$sidebar.show();
this.$showSideBarButton.hide();
this.ctx.stateChanged();
this.noteLoaded();
});
if (this.$widgetContainer.length === 0) {
this.$widgetContainer = $(`<div class="sidebar-widget-container">`).attr('data-tab-id', this.ctx.tabId);
this.$showSideBarButton.toggle(!state.visible);
this.$sidebar.toggle(state.visible);
$sidebarContainer.append(this.$widgetContainer);
}
}
isVisible() {
return this.$sidebar.css("display") !== "none";
return $sidebar.css("display") !== "none";
}
getSidebarState() {
@@ -91,6 +120,12 @@ class Sidebar {
this.renderWidgets(widgets);
}
show() {
$sidebarContainer.find('.sidebar-widget-container').each((i, el) => {
$(el).toggle($(el).attr('data-tab-id') === this.ctx.tabId);
});
}
// it's important that this method is sync so that the whole render-update is atomic
// otherwise we could get race issues (doubled widgets etc.)
renderWidgets(widgets) {
@@ -119,6 +154,12 @@ class Sidebar {
this.$widgetContainer.empty().append(...widgetsToAppend);
}
remove() {
if (this.$widgetContainer) {
this.$widgetContainer.remove();
}
}
eventReceived(name, data) {
for (const widget of this.widgets) {
if (widget.eventReceived) {
+51
View File
@@ -0,0 +1,51 @@
import server from "./server.js";
import optionService from "./options.js";
let instance;
async function getPaneWidths() {
const options = await optionService.waitForOptions();
return {
leftPaneWidth: options.getInt('leftPaneWidth'),
rightPaneWidth: options.getInt('rightPaneWidth')
};
}
async function setupSplitWithSidebar() {
if (instance) {
instance.destroy();
}
const {leftPaneWidth, rightPaneWidth} = await getPaneWidths();
instance = Split(['#left-pane', '#center-pane', '#right-pane'], {
sizes: [leftPaneWidth, 100 - leftPaneWidth - rightPaneWidth, rightPaneWidth],
gutterSize: 5,
onDragEnd: sizes => {
server.put('options/leftPaneWidth/' + Math.round(sizes[0]));
server.put('options/rightPaneWidth/' + Math.round(sizes[2]));
}
});
}
async function setupSplitWithoutSidebar() {
if (instance) {
instance.destroy();
}
const {leftPaneWidth} = await getPaneWidths();
instance = Split(['#left-pane', '#center-pane'], {
sizes: [leftPaneWidth, 100 - leftPaneWidth],
gutterSize: 5,
onDragEnd: sizes => {
server.put('options/leftPaneWidth/' + Math.round(sizes[0]));
}
});
}
export default {
setupSplitWithSidebar,
setupSplitWithoutSidebar
};
+24 -4
View File
@@ -11,6 +11,8 @@ import protectedSessionService from "./protected_session.js";
import optionsService from "./options.js";
import linkService from "./link.js";
import Sidebar from "./sidebar.js";
import libraryLoader from "./library_loader.js";
import noteAutocompleteService from "./note_autocomplete.js";
const $tabContentsContainer = $("#note-tab-container");
@@ -148,6 +150,10 @@ class TabContext {
this.setCurrentNotePathToHash();
if (this.sidebar) {
this.sidebar.noteLoaded(); // load async
}
this.noteChangeDisabled = true;
try {
@@ -181,10 +187,6 @@ class TabContext {
this.noteType.update();
}
if (this.sidebar) {
this.sidebar.noteLoaded(); // load async
}
bundleService.executeRelationBundles(this.note, 'runOnNoteView', this);
}
@@ -204,6 +206,10 @@ class TabContext {
this.$tabContent.show();
if (this.sidebar) {
this.sidebar.show();
}
this.setCurrentNotePathToHash();
this.setTitleBar();
}
@@ -411,6 +417,16 @@ class TabContext {
await this.addPath(notePath, isCurrent);
}
const cloneLink = $("<div>")
.addClass("dropdown-item")
.append(
$('<button class="btn btn-sm">')
.text('Clone note to new location...')
.on('click', () => import("../dialogs/clone_to.js").then(d => d.showDialog([this.note.noteId])))
);
this.$notePathList.append(cloneLink);
}
}
@@ -422,6 +438,10 @@ class TabContext {
await this.saveNoteIfChanged();
this.$tabContent.remove();
}
if (this.sidebar) {
this.sidebar.remove();
}
}
cleanup() {
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -81,7 +81,7 @@ function SetupModel() {
password: password1,
theme: theme
}).then(() => {
window.location.replace("./");
window.location.replace("./setup");
});
}
else if (this.setupType() === 'sync-from-server') {
+70 -50
View File
@@ -6,31 +6,39 @@ body {
margin: 0 auto; /* center */
height: 100vh;
display: grid;
grid-template-areas: "header header"
"left-pane tabs"
"left-pane tab-container";
grid-template-rows: auto
auto
1fr;
justify-content: center;
grid-gap: 0;
display: flex;
flex-direction: column;
}
#container.zen-mode {
grid-template-areas:
"tab-container" !important;
grid-template-rows: auto
auto
!important;
grid-template-columns: 1fr !important;
.hidden-by-zen-mode {
display: none !important;
}
.gutter {
background: linear-gradient(to bottom, transparent, var(--accented-background-color), transparent);
}
.gutter:hover {
background: linear-gradient(to bottom, transparent, var(--main-border-color), transparent);
}
.gutter.gutter-horizontal {
cursor: col-resize;
}
.gutter.gutter-vertical {
cursor: row-resize;
}
#center-pane {
display: flex;
flex-direction: column;
}
#note-tab-container {
grid-area: tab-container;
min-height: 0;
padding-left: 10px;
width: 100%;
}
#search-box {
@@ -49,13 +57,11 @@ body {
}
#left-pane {
grid-area: left-pane;
display: flex;
flex-direction: column;
}
#header {
grid-area: header;
background-color: var(--header-background-color);
display: flex;
align-items: center;
@@ -90,7 +96,7 @@ body {
padding: 3px 0 3px 0;
border: 1px solid var(--main-border-color);
border-radius: 7px;
margin: 5px 15px 5px 5px;
margin: 3px 5px 5px 5px;
}
.dropdown-menu {
@@ -191,7 +197,7 @@ body {
.note-title-row {
flex-grow: 0;
flex-shrink: 0;
margin-top: 10px;
padding-top: 2px;
}
.note-title {
@@ -206,12 +212,13 @@ body {
.note-tab-row {
box-sizing: border-box;
position: relative;
height: 33px;
height: 34px;
min-height: 34px;
width: 100%;
background: var(--main-background-color);
border-radius: 5px 5px 0 0;
overflow: hidden;
grid-area: tabs;
margin-top: 5px;
margin-top: 2px;
}
.note-tab-row * {
box-sizing: inherit;
@@ -244,7 +251,7 @@ body {
text-align: center;
font-size: 24px;
cursor: pointer;
border-bottom: 1px solid var(--button-border-color);
border-bottom: 1px solid var(--main-border-color);
}
.note-new-tab:hover {
@@ -253,11 +260,24 @@ body {
}
.tab-row-filler {
-webkit-app-region: drag;
position: absolute;
left: 0;
background: linear-gradient(to right, var(--button-border-color), transparent);
height: 33px;
}
.tab-row-filler .tab-row-border {
position: relative;
background: linear-gradient(to right, var(--main-border-color), transparent);
height: 1px;
margin-top: 32px;
margin-top: 31px;
}
/* hack for 1 pixel difference in handling between FF and chrome */
@-moz-document url-prefix() {
.tab-row-filler .tab-row-border {
margin-top: 32px;
}
}
.note-tab-row .note-tab[active] {
@@ -288,12 +308,12 @@ body {
overflow: hidden;
pointer-events: all;
background-color: var(--accented-background-color);
border-bottom: 1px solid var(--button-border-color);
border-bottom: 1px solid var(--main-border-color);
}
.note-tab-row .note-tab[active] .note-tab-wrapper {
background-color: var(--main-background-color);
border: 1px solid var(--button-border-color);
border: 1px solid var(--main-border-color);
border-bottom: 0;
font-weight: bold;
}
@@ -385,30 +405,26 @@ body {
transition: transform 120ms ease-in-out;
}
.hide-sidebar-button {
color: var(--main-text-color);
background: none;
border: 1px solid transparent;
padding: 2px 8px 2px 8px;
border-radius: 2px;
#hide-sidebar-button, #show-sidebar-button {
position: fixed;
bottom: 10px;
right: 10px;
z-index: 100000;
}
.hide-sidebar-button:hover {
border-color: var(--button-border-color);
}
.note-detail-sidebar {
#right-pane {
overflow: auto;
padding-top: 12px;
padding-top: 4px;
padding-left: 7px;
font-size: 90%;
height: 100%;
}
.note-detail-sidebar .card {
#right-pane .card {
border: 0;
}
.note-detail-sidebar .card-header {
#right-pane .card-header {
background: inherit;
padding: 5px 10px 5px 10px;
width: 99%; /* to give minimal right margin */
@@ -421,7 +437,7 @@ body {
justify-content: space-between;
}
.note-detail-sidebar .widget-title {
#right-pane .widget-title {
border-radius: 0;
padding: 0;
border: 0;
@@ -431,29 +447,29 @@ body {
color: var(--muted-text-color) !important;
}
.note-detail-sidebar .widget-header-action {
#right-pane .widget-header-action {
color: var(--link-color) !important;
cursor: pointer;
}
.note-detail-sidebar .widget-help {
#right-pane .widget-help {
color: var(--muted-text-color);
position: relative;
top: 2px;
}
.note-detail-sidebar .widget-help.no-link:hover {
#right-pane .widget-help.no-link:hover {
cursor: default;
text-decoration: none;
}
.note-detail-sidebar .card-body {
#right-pane .card-body {
width: 100%;
padding: 8px;
border: 0;
}
.note-detail-sidebar .card-body ul {
#right-pane .card-body ul {
padding-left: 25px;
margin-bottom: 5px;
}
@@ -485,4 +501,8 @@ body {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#title-bar-buttons {
min-width: 100px;
}
+12 -1
View File
@@ -333,7 +333,7 @@ div.ui-tooltip {
color: #888 !important;
}
.dropdown-menu a:hover:not(.disabled), li.dropdown-item:hover:not(.disabled) {
.dropdown-menu a:hover:not(.disabled), li.dropdown-item:hover:not(.disabled), div.dropdown-item:hover:not(.disabled) {
color: var(--hover-item-text-color) !important;
background-color: var(--hover-item-background-color) !important;
cursor: pointer;
@@ -474,6 +474,17 @@ button.icon-button {
width: 15em;
}
#global-menu-wrapper {
height: 35px;
border-bottom: 1px solid var(--main-border-color);
}
#global-menu button {
margin-right: 10px;
height: 33px;
border-bottom: none;
}
#global-menu .dropdown-menu {
width: 20em;
}
+5 -7
View File
@@ -13,17 +13,11 @@ const ALLOWED_OPTIONS = new Set([
'syncServerHost',
'syncServerTimeout',
'syncProxy',
'leftPaneMinWidth',
'leftPaneWidthPercent',
'sidebarMinWidth',
'sidebarWidthPercent',
'showSidebarInNewTab',
'hoistedNoteId',
'mainFontSize',
'treeFontSize',
'detailFontSize',
'openTabs',
'hideTabRowForOneTab',
'noteInfoWidget',
'attributesWidget',
'linkMapWidget',
@@ -36,7 +30,11 @@ const ALLOWED_OPTIONS = new Set([
'spellCheckEnabled',
'spellCheckLanguageCode',
'imageMaxWidthHeight',
'imageJpegQuality'
'imageJpegQuality',
'leftPaneWidth',
'rightPaneWidth',
'rightPaneVisible',
'nativeTitleBarVisible'
]);
async function getOptions() {
+14 -2
View File
@@ -10,6 +10,8 @@ const contentHashService = require('../../services/content_hash');
const log = require('../../services/log');
const syncOptions = require('../../services/sync_options');
const dateUtils = require('../../services/date_utils');
const entityConstructor = require('../../entities/entity_constructor');
const utils = require('../../services/utils');
async function testSync() {
try {
@@ -47,7 +49,7 @@ async function getStats() {
async function checkSync() {
return {
hashes: await contentHashService.getHashes(),
entityHashes: await contentHashService.getEntityHashes(),
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
};
}
@@ -137,6 +139,15 @@ async function syncFinished() {
await sqlInit.dbInitialized();
}
async function queueSector(req) {
const entityName = utils.sanitizeSqlIdentifier(req.params.entityName);
const sector = utils.sanitizeSqlIdentifier(req.params.sector);
const entityPrimaryKey = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
await syncTableService.addEntitySyncsForSector(entityName, entityPrimaryKey, sector);
}
module.exports = {
testSync,
checkSync,
@@ -147,5 +158,6 @@ module.exports = {
getChanged,
update,
getStats,
syncFinished
syncFinished,
queueSector
};
-6
View File
@@ -18,12 +18,6 @@ async function index(req, res) {
res.render(view, {
csrfToken: csrfToken,
theme: options.theme,
leftPaneMinWidth: parseInt(options.leftPaneMinWidth),
leftPaneWidthPercent: parseInt(options.leftPaneWidthPercent),
rightPaneWidthPercent: 100 - parseInt(options.leftPaneWidthPercent),
sidebarMinWidth: parseInt(options.sidebarMinWidth),
sidebarWidthPercent: parseInt(options.sidebarWidthPercent),
contentWidthPercent: 100 - parseInt(options.sidebarWidthPercent),
mainFontSize: parseInt(options.mainFontSize),
treeFontSize: parseInt(options.treeFontSize),
detailFontSize: parseInt(options.detailFontSize),
+2 -1
View File
@@ -114,7 +114,7 @@ function register(app) {
route(GET, '/login', [auth.checkAppInitialized], loginRoute.loginPage);
route(POST, '/login', [], loginRoute.login);
route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout);
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
route(GET, '/setup', [], setupRoute.setupPage);
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
apiRoute(POST, '/api/tree/load', treeApiRoute.load);
@@ -199,6 +199,7 @@ function register(app) {
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);
route(POST, '/api/sync/finished', [auth.checkApiAuth], syncApiRoute.syncFinished, apiResultHandler);
route(POST, '/api/sync/queue-sector/:entityName/:sector', [auth.checkApiAuth], syncApiRoute.queueSector, apiResultHandler);
route(GET, '/api/sync/stats', [], syncApiRoute.getStats, apiResultHandler);
apiRoute(POST, '/api/recent-notes', recentNotesRoute.addRecentNote);
+9 -1
View File
@@ -2,10 +2,18 @@
const sqlInit = require('../services/sql_init');
const setupService = require('../services/setup');
const utils = require('../services/utils');
const windowService = require('../services/window');
async function setupPage(req, res) {
if (await sqlInit.isDbInitialized()) {
res.redirect('/');
if (utils.isElectron()) {
await windowService.createMainWindow();
windowService.closeSetupWindow();
}
else {
res.redirect('/');
}
}
// we got here because DB is not completely initialized so if schema exists
+1 -1
View File
@@ -5,7 +5,7 @@ const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 155;
const SYNC_VERSION = 12;
const SYNC_VERSION = 13;
const CLIPPER_PROTOCOL_VERSION = "1.0";
module.exports = {
+1 -1
View File
@@ -1 +1 @@
module.exports = { buildDate:"2019-12-24T10:51:51+01:00", buildRevision: "0a723834953f35e9be1d6f0480981cb3fc497801" };
module.exports = { buildDate:"2019-12-27T21:21:57+01:00", buildRevision: "3d95d69f80805311816acd5adf677350c3ff22b8" };
+39 -32
View File
@@ -3,7 +3,6 @@
const sql = require('./sql');
const utils = require('./utils');
const log = require('./log');
const ws = require('./ws.js');
const ApiToken = require('../entities/api_token');
const Branch = require('../entities/branch');
const Note = require('../entities/note');
@@ -12,65 +11,73 @@ const NoteRevision = require('../entities/note_revision');
const RecentNote = require('../entities/recent_note');
const Option = require('../entities/option');
async function getHash(tableName, primaryKeyName, whereBranch) {
// subselect is necessary to have correct ordering in GROUP_CONCAT
const query = `SELECT GROUP_CONCAT(hash) FROM (SELECT hash FROM ${tableName} `
+ (whereBranch ? `WHERE ${whereBranch} ` : '') + `ORDER BY ${primaryKeyName})`;
async function getSectorHashes(tableName, primaryKeyName, whereBranch) {
const hashes = await sql.getRows(`SELECT ${primaryKeyName} AS id, hash FROM ${tableName} `
+ (whereBranch ? `WHERE ${whereBranch} ` : '')
+ ` ORDER BY ${primaryKeyName}`);
let contentToHash = await sql.getValue(query);
const map = {};
if (!contentToHash) { // might be null in case of no rows
contentToHash = "";
for (const {id, hash} of hashes) {
map[id[0]] = (map[id[0]] || "") + hash;
}
return utils.hash(contentToHash);
for (const key in map) {
map[key] = utils.hash(map[key]);
}
return map;
}
async function getHashes() {
async function getEntityHashes() {
const startTime = new Date();
const hashes = {
notes: await getHash(Note.entityName, Note.primaryKeyName),
note_contents: await getHash("note_contents", "noteId"),
branches: await getHash(Branch.entityName, Branch.primaryKeyName),
note_revisions: await getHash(NoteRevision.entityName, NoteRevision.primaryKeyName),
note_revision_contents: await getHash("note_revision_contents", "noteRevisionId"),
recent_notes: await getHash(RecentNote.entityName, RecentNote.primaryKeyName),
options: await getHash(Option.entityName, Option.primaryKeyName, "isSynced = 1"),
attributes: await getHash(Attribute.entityName, Attribute.primaryKeyName),
api_tokens: await getHash(ApiToken.entityName, ApiToken.primaryKeyName),
notes: await getSectorHashes(Note.entityName, Note.primaryKeyName),
note_contents: await getSectorHashes("note_contents", "noteId"),
branches: await getSectorHashes(Branch.entityName, Branch.primaryKeyName),
note_revisions: await getSectorHashes(NoteRevision.entityName, NoteRevision.primaryKeyName),
note_revision_contents: await getSectorHashes("note_revision_contents", "noteRevisionId"),
recent_notes: await getSectorHashes(RecentNote.entityName, RecentNote.primaryKeyName),
options: await getSectorHashes(Option.entityName, Option.primaryKeyName, "isSynced = 1"),
attributes: await getSectorHashes(Attribute.entityName, Attribute.primaryKeyName),
api_tokens: await getSectorHashes(ApiToken.entityName, ApiToken.primaryKeyName),
};
const elapseTimeMs = Date.now() - startTime.getTime();
const elapsedTimeMs = Date.now() - startTime.getTime();
log.info(`Content hash computation took ${elapseTimeMs}ms`);
log.info(`Content hash computation took ${elapsedTimeMs}ms`);
return hashes;
}
async function checkContentHashes(otherHashes) {
const hashes = await getHashes();
let allChecksPassed = true;
const entityHashes = await getEntityHashes();
const failedChecks = [];
for (const key in hashes) {
if (hashes[key] !== otherHashes[key]) {
allChecksPassed = false;
for (const entityName in entityHashes) {
const thisSectorHashes = entityHashes[entityName];
const otherSectorHashes = otherHashes[entityName];
log.info(`Content hash check for ${key} FAILED. Local is ${hashes[key]}, remote is ${otherHashes[key]}`);
const sectors = new Set(Object.keys(thisSectorHashes).concat(Object.keys(otherSectorHashes)));
if (key !== 'recent_notes') {
// let's not get alarmed about recent notes which get updated often and can cause failures in race conditions
ws.sendMessageToAllClients({type: 'sync-hash-check-failed'});
for (const sector of sectors) {
if (thisSectorHashes[sector] !== otherSectorHashes[sector]) {
log.info(`Content hash check for ${entityName} sector ${sector} FAILED. Local is ${thisSectorHashes[sector]}, remote is ${otherSectorHashes[sector]}`);
failedChecks.push({ entityName, sector });
}
}
}
if (allChecksPassed) {
if (failedChecks.length === 0) {
log.info("Content hash checks PASSED");
}
return failedChecks;
}
module.exports = {
getHashes,
getEntityHashes,
checkContentHashes
};
+48 -10
View File
@@ -2,6 +2,7 @@
const html = require('html');
const repository = require('../repository');
const dateUtils = require('../date_utils');
const tar = require('tar-stream');
const path = require('path');
const mimeTypes = require('mime-types');
@@ -10,6 +11,8 @@ const packageInfo = require('../../../package.json');
const utils = require('../utils');
const protectedSessionService = require('../protected_session');
const sanitize = require("sanitize-filename");
const fs = require("fs");
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
/**
* @param {TaskContext} taskContext
@@ -187,13 +190,13 @@ async function exportToTar(taskContext, branch, format, res) {
for (let i = 0; i < targetPath.length - 1; i++) {
const meta = noteIdToMeta[targetPath[i]];
url += meta.dirFileName + '/';
url += encodeURIComponent(meta.dirFileName) + '/';
}
const meta = noteIdToMeta[targetPath[targetPath.length - 1]];
// link can target note which is only "folder-note" and as such will not have a file in an export
url += meta.dataFileName || meta.dirFileName;
url += encodeURIComponent(meta.dataFileName || meta.dirFileName);
return url;
}
@@ -202,13 +205,13 @@ async function exportToTar(taskContext, branch, format, res) {
content = content.replace(/src="[^"]*api\/images\/([a-zA-Z0-9]+)\/[^"]*"/g, (match, targetNoteId) => {
const url = getTargetUrl(targetNoteId, noteMeta);
return url ? `src="${encodeURIComponent(url)}"` : match;
return url ? `src="${url}"` : match;
});
content = content.replace(/href="[^"]*#root[a-zA-Z0-9\/]*\/([a-zA-Z0-9]+)\/?"/g, (match, targetNoteId) => {
const url = getTargetUrl(targetNoteId, noteMeta);
return url ? `href="${encodeURIComponent(url)}"` : match;
return url ? `href="${url}"` : match;
});
return content;
@@ -223,8 +226,13 @@ async function exportToTar(taskContext, branch, format, res) {
if (noteMeta.format === 'html') {
if (!content.substr(0, 100).toLowerCase().includes("<html")) {
const cssUrl = "../".repeat(noteMeta.notePath.length - 1) + 'style.css';
content = `<html>
<head><meta charset="utf-8"></head>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="${cssUrl}">
</head>
<body>
<h1>${utils.escapeHtml(title)}</h1>
${content}
@@ -255,7 +263,7 @@ ${content}
if (noteMeta.isClone) {
const targetUrl = getTargetUrl(noteMeta.noteId, noteMeta);
let content = `<p>This is a clone of a note. Go to its <a href="${encodeURIComponent(targetUrl)}">primary location</a>.</p>`;
let content = `<p>This is a clone of a note. Go to its <a href="${targetUrl}">primary location</a>.</p>`;
content = prepareContent(noteMeta.title, content, noteMeta);
@@ -270,7 +278,11 @@ ${content}
if (noteMeta.dataFileName) {
const content = prepareContent(noteMeta.title, await note.getContent(), noteMeta);
pack.entry({name: filePathPrefix + noteMeta.dataFileName, size: content.length}, content);
pack.entry({
name: filePathPrefix + noteMeta.dataFileName,
size: content.length,
mtime: dateUtils.parseDateTime(note.utcDateModified)
}, content);
}
taskContext.increaseProgressCount();
@@ -278,7 +290,11 @@ ${content}
if (noteMeta.children && noteMeta.children.length > 0) {
const directoryPath = filePathPrefix + noteMeta.dirFileName;
pack.entry({name: directoryPath, type: 'directory'});
pack.entry({
name: directoryPath,
type: 'directory',
mtime: dateUtils.parseDateTime(note.utcDateModified)
});
for (const childMeta of noteMeta.children) {
await saveNote(childMeta, directoryPath + '/');
@@ -314,7 +330,15 @@ ${content}
return html + '</li>';
}
const fullHtml = '<html><head><meta charset="utf-8"></head><body><ul>' + saveNavigationInner(rootMeta) + '</ul></body></html>'
const fullHtml = `<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul>${saveNavigationInner(rootMeta)}</ul>
</body>
</html>`;
const prettyHtml = html.prettyPrint(fullHtml, {indent_size: 2});
pack.entry({name: navigationMeta.dataFileName, size: prettyHtml.length}, prettyHtml);
@@ -351,6 +375,12 @@ ${content}
pack.entry({name: indexMeta.dataFileName, size: fullHtml.length}, fullHtml);
}
function saveCss(rootMeta, cssMeta) {
const cssContent = fs.readFileSync(RESOURCE_DIR + '/libraries/ckeditor/ckeditor-content.css');
pack.entry({name: cssMeta.dataFileName, size: cssContent.length}, cssContent);
}
const existingFileNames = format === 'html' ? ['navigation', 'index'] : [];
const rootMeta = await getNoteMeta(branch, { notePath: [] }, existingFileNames);
@@ -360,7 +390,7 @@ ${content}
files: [ rootMeta ]
};
let navigationMeta, indexMeta;
let navigationMeta, indexMeta, cssMeta;
if (format === 'html') {
navigationMeta = {
@@ -376,6 +406,13 @@ ${content}
};
metaFile.files.push(indexMeta);
cssMeta = {
noImport: true,
dataFileName: "style.css"
};
metaFile.files.push(cssMeta);
}
for (const noteMeta of Object.values(noteIdToMeta)) {
@@ -397,6 +434,7 @@ ${content}
if (format === 'html') {
saveNavigation(rootMeta, navigationMeta);
saveIndex(rootMeta, indexMeta);
saveCss(rootMeta, cssMeta);
}
pack.finalize();
+3 -3
View File
@@ -274,6 +274,9 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
return /^(?:[a-z]+:)?\/\//i.test(url);
}
content = content.replace(/<html.*<body[^>]*>/gis, "");
content = content.replace(/<\/body>.*<\/html>/gis, "");
content = content.replace(/src="([^"]*)"/g, (match, url) => {
url = decodeURIComponent(url);
@@ -298,9 +301,6 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
return `href="#root/${targetNoteId}"`;
});
content = content.replace(/<html.*<body[^>]*>/gis, "");
content = content.replace(/<\/body>.*<\/html>/gis, "");
content = content.replace(/<h1>([^<]*)<\/h1>/gi, (match, text) => {
if (noteTitle.trim() === text.trim()) {
return ""; // remove whole H1 tag
+5 -8
View File
@@ -33,7 +33,6 @@ async function initNotSyncedOptions(initialized, startNotePath = 'root', opts =
notePath: startNotePath,
active: true,
sidebar: {
visible: true,
widgets: []
}
}
@@ -61,14 +60,9 @@ const defaultOptions = [
{ name: 'protectedSessionTimeout', value: '600', isSynced: true },
{ name: 'hoistedNoteId', value: 'root', isSynced: false },
{ name: 'zoomFactor', value: '1.0', isSynced: false },
{ name: 'leftPaneMinWidth', value: '350', isSynced: false },
{ name: 'leftPaneWidthPercent', value: '20', isSynced: false },
{ name: 'mainFontSize', value: '100', isSynced: false },
{ name: 'treeFontSize', value: '100', isSynced: false },
{ name: 'detailFontSize', value: '110', isSynced: false },
{ name: 'sidebarMinWidth', value: '350', isSynced: false },
{ name: 'sidebarWidthPercent', value: '25', isSynced: false },
{ name: 'showSidebarInNewTab', value: 'true', isSynced: false },
{ name: 'calendarWidget', value: '{"enabled":true,"expanded":true,"position":20}', isSynced: false },
{ name: 'editedNotesWidget', value: '{"enabled":true,"expanded":true,"position":50}', isSynced: false },
{ name: 'noteInfoWidget', value: '{"enabled":true,"expanded":true,"position":100}', isSynced: false },
@@ -79,11 +73,14 @@ const defaultOptions = [
{ name: 'similarNotesWidget', value: '{"enabled":true,"expanded":true,"position":600}', isSynced: false },
{ name: 'spellCheckEnabled', value: 'true', isSynced: false },
{ name: 'spellCheckLanguageCode', value: 'en-US', isSynced: false },
{ name: 'hideTabRowForOneTab', value: 'false', isSynced: false },
{ name: 'imageMaxWidthHeight', value: '1200', isSynced: true },
{ name: 'imageJpegQuality', value: '75', isSynced: true },
{ name: 'autoFixConsistencyIssues', value: 'true', isSynced: false },
{ name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-swift","text/xml","text/x-yaml"]', isSynced: true }
{ name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-swift","text/xml","text/x-yaml"]', isSynced: true },
{ name: 'leftPaneWidth', value: '25', isSynced: false },
{ name: 'rightPaneWidth', value: '25', isSynced: false },
{ name: 'rightPaneVisible', value: 'true', isSynced: false },
{ name: 'nativeTitleBarVisible', value: 'false', isSynced: false }
];
async function initStartupOptions() {
+1 -1
View File
@@ -1,6 +1,6 @@
const dayjs = require("dayjs");
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*([\p{L}\p{Number}_/-]+|"[^"]+"))?/igu;
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*(\S+|"[^"]+"))?/igu;
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
function calculateSmartValue(v) {
+28 -9
View File
@@ -15,6 +15,8 @@ const syncMutexService = require('./sync_mutex');
const cls = require('./cls');
const request = require('./request');
const ws = require('./ws');
const syncTableService = require('./sync_table');
const entityConstructor = require('../entities/entity_constructor');
let proxyToggle = true;
@@ -30,17 +32,22 @@ async function sync() {
return { success: false, message: 'Sync not configured' };
}
const syncContext = await login();
let continueSync = false;
await pushSync(syncContext);
do {
const syncContext = await login();
await pullSync(syncContext);
await pushSync(syncContext);
await pushSync(syncContext);
await pullSync(syncContext);
await syncFinished(syncContext);
await pushSync(syncContext);
await checkContentHash(syncContext);
await syncFinished(syncContext);
continueSync = await checkContentHash(syncContext);
}
while (continueSync);
return {
success: true
@@ -225,7 +232,7 @@ async function checkContentHash(syncContext) {
if (await getLastSyncedPull() < resp.maxSyncId) {
log.info("There are some outstanding pulls, skipping content check.");
return;
return true;
}
const notPushedSyncs = await sql.getValue("SELECT EXISTS(SELECT 1 FROM sync WHERE id > ?)", [await getLastSyncedPush()]);
@@ -233,10 +240,22 @@ async function checkContentHash(syncContext) {
if (notPushedSyncs) {
log.info(`There's ${notPushedSyncs} outstanding pushes, skipping content check.`);
return;
return true;
}
await contentHashService.checkContentHashes(resp.hashes);
const failedChecks = await contentHashService.checkContentHashes(resp.entityHashes);
for (const {entityName, sector} of failedChecks) {
const entityPrimaryKey = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
await syncTableService.addEntitySyncsForSector(entityName, entityPrimaryKey, sector);
await syncRequest(syncContext, 'POST', `/api/sync/queue-sector/${entityName}/${sector}`);
log.info(`Added sector ${sector} of ${entityName} to sync queue.`);
}
return failedChecks.length > 0;
}
async function syncRequest(syncContext, method, requestPath, body) {
+23 -8
View File
@@ -6,7 +6,7 @@ const cls = require('./cls');
let syncs = [];
async function addEntitySync(entityName, entityId, sourceId) {
async function insertEntitySync(entityName, entityId, sourceId) {
const sync = {
entityName: entityName,
entityId: entityId,
@@ -16,11 +16,25 @@ async function addEntitySync(entityName, entityId, sourceId) {
sync.id = await sql.replace("sync", sync);
return sync;
}
async function addEntitySync(entityName, entityId, sourceId) {
const sync = await insertEntitySync(entityName, entityId, sourceId);
syncs.push(sync);
setTimeout(() => require('./ws').sendPingToAllClients(), 50);
}
async function addEntitySyncsForSector(entityName, entityPrimaryKey, sector) {
const entityIds = await sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName} WHERE SUBSTR(${entityPrimaryKey}, 1, 1) = ?`, [sector]);
for (const entityId of entityIds) {
await insertEntitySync(entityName, entityId, 'content-check');
}
}
function getMaxSyncId() {
return syncs.length === 0 ? 0 : syncs[syncs.length - 1].id;
}
@@ -29,19 +43,19 @@ function getEntitySyncsNewerThan(syncId) {
return syncs.filter(s => s.id > syncId);
}
async function cleanupSyncRowsForMissingEntities(entityName, entityKey) {
async function cleanupSyncRowsForMissingEntities(entityName, entityPrimaryKey) {
await sql.execute(`
DELETE
FROM sync
WHERE sync.entityName = '${entityName}'
AND sync.entityId NOT IN (SELECT ${entityKey} FROM ${entityName})`);
AND sync.entityId NOT IN (SELECT ${entityPrimaryKey} FROM ${entityName})`);
}
async function fillSyncRows(entityName, entityKey, condition = '') {
async function fillSyncRows(entityName, entityPrimaryKey, condition = '') {
try {
await cleanupSyncRowsForMissingEntities(entityName, entityKey);
await cleanupSyncRowsForMissingEntities(entityName, entityPrimaryKey);
const entityIds = await sql.getColumn(`SELECT ${entityKey} FROM ${entityName}`
const entityIds = await sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName}`
+ (condition ? ` WHERE ${condition}` : ''));
let createdCount = 0;
@@ -69,7 +83,7 @@ async function fillSyncRows(entityName, entityKey, condition = '') {
catch (e) {
// this is to fix migration from 0.30 to 0.32, can be removed later
// see https://github.com/zadam/trilium/issues/557
log.error(`Filling sync rows failed for ${entityName} ${entityKey} with error "${e.message}", continuing`);
log.error(`Filling sync rows failed for ${entityName} ${entityPrimaryKey} with error "${e.message}", continuing`);
}
}
@@ -101,5 +115,6 @@ module.exports = {
addEntitySync,
fillAllSyncRows,
getEntitySyncsNewerThan,
getMaxSyncId
getMaxSyncId,
addEntitySyncsForSector
};
+74 -54
View File
@@ -50,135 +50,155 @@ async function updateEntity(sync, entity, sourceId) {
}
}
async function updateNote(entity, sourceId) {
const origNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [entity.noteId]);
function shouldWeUpdateEntity(localEntity, remoteEntity) {
if (!localEntity) {
return true;
}
if (!origNote || origNote.utcDateModified < entity.utcDateModified) {
const localDate = localEntity.utcDateModified || localEntity.utcDateCreated;
const remoteDate = remoteEntity.utcDateModified || remoteEntity.utcDateCreated;
if (localDate < remoteDate) {
return true;
}
// this can happen in case of sync error when hashes are different but dates are the same - we should still update
if (localEntity.hash !== remoteEntity.hash && localDate === remoteDate) {
return true;
}
return false;
}
async function updateNote(remoteEntity, sourceId) {
const localEntity = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [remoteEntity.noteId]);
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
await sql.transactional(async () => {
await sql.replace("notes", entity);
await sql.replace("notes", remoteEntity);
await syncTableService.addNoteSync(entity.noteId, sourceId);
await syncTableService.addNoteSync(remoteEntity.noteId, sourceId);
});
log.info("Update/sync note " + entity.noteId);
log.info("Update/sync note " + remoteEntity.noteId);
}
}
async function updateNoteContent(entity, sourceId) {
const origNoteContent = await sql.getRow("SELECT * FROM note_contents WHERE noteId = ?", [entity.noteId]);
async function updateNoteContent(remoteEntity, sourceId) {
const localEntity = await sql.getRow("SELECT * FROM note_contents WHERE noteId = ?", [remoteEntity.noteId]);
if (!origNoteContent || origNoteContent.utcDateModified < entity.utcDateModified) {
entity.content = entity.content === null ? null : Buffer.from(entity.content, 'base64');
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
remoteEntity.content = remoteEntity.content === null ? null : Buffer.from(remoteEntity.content, 'base64');
await sql.transactional(async () => {
await sql.replace("note_contents", entity);
await sql.replace("note_contents", remoteEntity);
await syncTableService.addNoteContentSync(entity.noteId, sourceId);
await syncTableService.addNoteContentSync(remoteEntity.noteId, sourceId);
});
log.info("Update/sync note content for noteId=" + entity.noteId);
log.info("Update/sync note content for noteId=" + remoteEntity.noteId);
}
}
async function updateBranch(entity, sourceId) {
const orig = await sql.getRowOrNull("SELECT * FROM branches WHERE branchId = ?", [entity.branchId]);
async function updateBranch(remoteEntity, sourceId) {
const localEntity = await sql.getRowOrNull("SELECT * FROM branches WHERE branchId = ?", [remoteEntity.branchId]);
await sql.transactional(async () => {
if (orig === null || orig.utcDateModified < entity.utcDateModified) {
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
// isExpanded is not synced unless it's a new branch instance
// otherwise in case of full new sync we'll get all branches (even root) collapsed.
if (orig) {
delete entity.isExpanded;
if (localEntity) {
delete remoteEntity.isExpanded;
}
await sql.replace('branches', entity);
await sql.replace('branches', remoteEntity);
await syncTableService.addBranchSync(entity.branchId, sourceId);
await syncTableService.addBranchSync(remoteEntity.branchId, sourceId);
log.info("Update/sync branch " + entity.branchId);
log.info("Update/sync branch " + remoteEntity.branchId);
}
});
}
async function updateNoteRevision(entity, sourceId) {
const orig = await sql.getRowOrNull("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [entity.noteRevisionId]);
async function updateNoteRevision(remoteEntity, sourceId) {
const localEntity = await sql.getRowOrNull("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [remoteEntity.noteRevisionId]);
await sql.transactional(async () => {
if (orig === null || orig.utcDateModified < entity.utcDateModified) {
await sql.replace('note_revisions', entity);
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
await sql.replace('note_revisions', remoteEntity);
await syncTableService.addNoteRevisionSync(entity.noteRevisionId, sourceId);
await syncTableService.addNoteRevisionSync(remoteEntity.noteRevisionId, sourceId);
log.info("Update/sync note revision " + entity.noteRevisionId);
log.info("Update/sync note revision " + remoteEntity.noteRevisionId);
}
});
}
async function updateNoteRevisionContent(entity, sourceId) {
const orig = await sql.getRowOrNull("SELECT * FROM note_revision_contents WHERE noteRevisionId = ?", [entity.noteRevisionId]);
async function updateNoteRevisionContent(remoteEntity, sourceId) {
const localEntity = await sql.getRowOrNull("SELECT * FROM note_revision_contents WHERE noteRevisionId = ?", [remoteEntity.noteRevisionId]);
await sql.transactional(async () => {
if (orig === null || orig.utcDateModified < entity.utcDateModified) {
entity.content = entity.content === null ? null : Buffer.from(entity.content, 'base64');
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
remoteEntity.content = remoteEntity.content === null ? null : Buffer.from(remoteEntity.content, 'base64');
await sql.replace('note_revision_contents', entity);
await sql.replace('note_revision_contents', remoteEntity);
await syncTableService.addNoteRevisionContentSync(entity.noteRevisionId, sourceId);
await syncTableService.addNoteRevisionContentSync(remoteEntity.noteRevisionId, sourceId);
log.info("Update/sync note revision content " + entity.noteRevisionId);
log.info("Update/sync note revision content " + remoteEntity.noteRevisionId);
}
});
}
async function updateNoteReordering(entityId, entity, sourceId) {
async function updateNoteReordering(entityId, remote, sourceId) {
await sql.transactional(async () => {
for (const key in entity) {
await sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
for (const key in remote) {
await sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [remote[key], key]);
}
await syncTableService.addNoteReorderingSync(entityId, sourceId);
});
}
async function updateOptions(entity, sourceId) {
const orig = await sql.getRowOrNull("SELECT * FROM options WHERE name = ?", [entity.name]);
async function updateOptions(remoteEntity, sourceId) {
const localEntity = await sql.getRowOrNull("SELECT * FROM options WHERE name = ?", [remoteEntity.name]);
if (orig && !orig.isSynced) {
if (localEntity && !localEntity.isSynced) {
return;
}
await sql.transactional(async () => {
if (orig === null || orig.utcDateModified < entity.utcDateModified) {
await sql.replace('options', entity);
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
await sql.replace('options', remoteEntity);
await syncTableService.addOptionsSync(entity.name, sourceId);
await syncTableService.addOptionsSync(remoteEntity.name, sourceId);
}
});
}
async function updateRecentNotes(entity, sourceId) {
const orig = await sql.getRowOrNull("SELECT * FROM recent_notes WHERE noteId = ?", [entity.noteId]);
async function updateRecentNotes(remoteEntity, sourceId) {
const localEntity = await sql.getRowOrNull("SELECT * FROM recent_notes WHERE noteId = ?", [remoteEntity.noteId]);
if (orig === null || orig.utcDateCreated < entity.utcDateCreated) {
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
await sql.transactional(async () => {
await sql.replace('recent_notes', entity);
await sql.replace('recent_notes', remoteEntity);
await syncTableService.addRecentNoteSync(entity.noteId, sourceId);
await syncTableService.addRecentNoteSync(remoteEntity.noteId, sourceId);
});
}
}
async function updateAttribute(entity, sourceId) {
const origAttribute = await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [entity.attributeId]);
async function updateAttribute(remoteEntity, sourceId) {
const localEntity = await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [remoteEntity.attributeId]);
if (!origAttribute || origAttribute.utcDateModified <= entity.utcDateModified) {
if (shouldWeUpdateEntity(localEntity, remoteEntity)) {
await sql.transactional(async () => {
await sql.replace("attributes", entity);
await sql.replace("attributes", remoteEntity);
await syncTableService.addAttributeSync(entity.attributeId, sourceId);
await syncTableService.addAttributeSync(remoteEntity.attributeId, sourceId);
});
log.info("Update/sync attribute " + entity.attributeId);
log.info("Update/sync attribute " + remoteEntity.attributeId);
}
}
+5
View File
@@ -53,6 +53,10 @@ function sanitizeSql(str) {
return str.replace(/'/g, "''");
}
function sanitizeSqlIdentifier(str) {
return str.replace(/[^A-Za-z0-9_]/g, "");
}
function prepareSqlForLike(prefix, str, suffix) {
const value = str
.replace(/\\/g, "\\\\")
@@ -174,6 +178,7 @@ module.exports = {
hash,
isEmptyOrWhitespace,
sanitizeSql,
sanitizeSqlIdentifier,
prepareSqlForLike,
stopWatch,
escapeHtml,
+131
View File
@@ -0,0 +1,131 @@
const path = require('path');
const url = require("url");
const port = require('./port');
const optionService = require('./options');
const env = require('./env');
const log = require('./log');
const sqlInit = require('./sql_init');
const cls = require('./cls');
const keyboardActionsService = require('./keyboard_actions');
// Prevent window being garbage collected
/** @type {Electron.BrowserWindow} */
let mainWindow;
/** @type {Electron.BrowserWindow} */
let setupWindow;
async function createMainWindow() {
const windowStateKeeper = require('electron-window-state'); // should not be statically imported
const mainWindowState = windowStateKeeper({
// default window width & height so it's usable on 1600 * 900 display (including some extra panels etc.)
defaultWidth: 1200,
defaultHeight: 800
});
const {BrowserWindow} = require('electron'); // should not be statically imported
mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
title: 'Trilium Notes',
webPreferences: {
nodeIntegration: true
},
frame: await optionService.getOptionBool('nativeTitleBarVisible'),
icon: getIcon()
});
mainWindowState.manage(mainWindow);
mainWindow.setMenuBarVisibility(false);
mainWindow.loadURL('http://127.0.0.1:' + await port);
mainWindow.on('closed', () => mainWindow = null);
mainWindow.webContents.on('new-window', (e, url) => {
if (url !== mainWindow.webContents.getURL()) {
e.preventDefault();
require('electron').shell.openExternal(url);
}
});
// prevent drag & drop to navigate away from trilium
mainWindow.webContents.on('will-navigate', (ev, targetUrl) => {
const parsedUrl = url.parse(targetUrl);
// we still need to allow internal redirects from setup and migration pages
if (!['localhost', '127.0.0.1'].includes(parsedUrl.hostname) || (parsedUrl.path && parsedUrl.path !== '/')) {
ev.preventDefault();
}
});
}
function getIcon() {
return path.join(__dirname, '../../images/app-icons/png/256x256' + (env.isDev() ? '-dev' : '') + '.png');
}
async function createSetupWindow() {
const {BrowserWindow} = require('electron'); // should not be statically imported
setupWindow = new BrowserWindow({
width: 800,
height: 800,
title: 'Trilium Notes Setup',
icon: getIcon(),
webPreferences: {
// necessary for e.g. utils.isElectron()
nodeIntegration: true
}
});
setupWindow.setMenuBarVisibility(false);
setupWindow.loadURL('http://127.0.0.1:' + await port);
setupWindow.on('closed', () => setupWindow = null);
}
function closeSetupWindow() {
if (setupWindow) {
setupWindow.close();
}
}
async function registerGlobalShortcuts() {
const {globalShortcut} = require('electron');
await sqlInit.dbReady;
const allActions = await keyboardActionsService.getKeyboardActions();
for (const action of allActions) {
if (!action.effectiveShortcuts) {
continue;
}
for (const shortcut of action.effectiveShortcuts) {
if (shortcut.startsWith('global:')) {
const translatedShortcut = shortcut.substr(7);
const result = globalShortcut.register(translatedShortcut, cls.wrap(async () => {
// window may be hidden / not in focus
mainWindow.focus();
mainWindow.webContents.send('globalShortcut', action.actionName);
}));
if (result) {
log.info(`Registered global shortcut ${translatedShortcut} for action ${action.actionName}`);
}
else {
log.info(`Could not register global shortcut ${translatedShortcut}`);
}
}
}
}
}
module.exports = {
createMainWindow,
createSetupWindow,
closeSetupWindow,
registerGlobalShortcuts
};
+39
View File
@@ -0,0 +1,39 @@
<div id="center-pane">
<div id="note-tab-container">
<div class="note-tab-content note-tab-content-template">
<div class="note-detail-content">
<% include title.ejs %>
<div class="note-detail-script-area"></div>
<table class="note-detail-promoted-attributes"></table>
<div class="note-detail-component-wrapper">
<div class="note-detail-text note-detail-component">
<div class="note-detail-text-editor" tabindex="10000"></div>
</div>
<div class="note-detail-code note-detail-component">
<div class="note-detail-code-editor"></div>
</div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>
<% include details/file.ejs %>
<% include details/image.ejs %>
<% include details/relation_map.ejs %>
<% include details/protected_session_password.ejs %>
<% include details/book.ejs %>
</div>
</div>
</div>
</div>
</div>
+93 -75
View File
@@ -10,48 +10,9 @@
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) minmax(0, <%= rightPaneWidthPercent %>fr)">
<div id="header" class="hide-in-zen-mode">
<div id="history-navigation" style="display: none;">
<a id="history-back-button" title="Go to previous note." class="icon-action bx bx-left-arrow-circle"></a>
<a id="history-forward-button" title="Go to next note." class="icon-action bx bx-right-arrow-circle"></a>
</div>
<div style="flex-grow: 100; display: flex;">
<button class="btn btn-sm" id="jump-to-note-dialog-button" data-kb-action="JumpToNote">
<span class="bx bx-crosshair"></span>
Jump to note
</button>
<button class="btn btn-sm" id="recent-changes-button" data-kb-action="ShowRecentChanges">
<span class="bx bx-history"></span>
Recent changes
</button>
<button class="btn btn-sm"
id="enter-protected-session-button"
title="Enter protected session to be able to find and view protected notes">
<span class="bx bx-log-in"></span>
Enter protected session
</button>
<button class="btn btn-sm"
id="leave-protected-session-button"
title="Leave protected session so that protected notes are not accessible any more."
style="display: none;">
<span class="bx bx-log-out"></span>
Leave protected session
</button>
</div>
<div id="plugin-buttons">
</div>
<div>
<div id="container" style="display: none;">
<div style="display: flex;">
<div id="global-menu-wrapper">
<div class="dropdown" id="global-menu">
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle">
<span class="bx bx-menu"></span>
@@ -123,54 +84,109 @@
</div>
</div>
</div>
</div>
<div id="left-pane" class="hide-in-zen-mode">
<div id="global-buttons">
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action bx bx-folder-plus"></a>
<a id="collapse-tree-button" title="Collapse note tree" data-kb-action="CollapseTree" class="icon-action bx bx-layer-minus"></a>
<a id="scroll-to-active-note-button" title="Scroll to active note" data-kb-action="ScrollToActiveNote" class="icon-action bx bx-crosshair"></a>
<a id="toggle-search-button" title="Search in notes" data-kb-action="SearchNotes" class="icon-action bx bx-search"></a>
<div class="note-tab-row hide-in-zen-mode">
<div class="note-tab-row-content"></div>
</div>
<div id="search-box">
<div class="form-group">
<div class="input-group">
<input name="search-text" id="search-text" class="form-control"
placeholder="Search text, labels" autocomplete="off">
<div id="title-bar-buttons" style="display: none;">
<button class="btn icon-action bx bx-minus" id="minimize-btn"></button>
<button class="btn icon-action bx bx-checkbox" id="maximize-btn"></button>
<button class="btn icon-action bx bx-x" id="close-btn"></button>
</div>
</div>
<div class="input-group-append">
<button id="do-search-button" class="btn btn-sm icon-button bx bx-search" title="Search (enter)"></button>
<div id="header" class="hide-in-zen-mode">
<div id="history-navigation" style="display: none;">
<a id="history-back-button" title="Go to previous note." class="icon-action bx bx-left-arrow-circle"></a>
<a id="history-forward-button" title="Go to next note." class="icon-action bx bx-right-arrow-circle"></a>
</div>
<div style="flex-grow: 100; display: flex;">
<button class="btn btn-sm" id="jump-to-note-dialog-button" data-kb-action="JumpToNote">
<span class="bx bx-crosshair"></span>
Jump to note
</button>
<button class="btn btn-sm" id="recent-changes-button" data-kb-action="ShowRecentChanges">
<span class="bx bx-history"></span>
Recent changes
</button>
<button class="btn btn-sm"
id="enter-protected-session-button"
title="Enter protected session to be able to find and view protected notes">
<span class="bx bx-log-in"></span>
Enter protected session
</button>
<button class="btn btn-sm"
id="leave-protected-session-button"
title="Leave protected session so that protected notes are not accessible any more."
style="display: none;">
<span class="bx bx-log-out"></span>
Leave protected session
</button>
</div>
<div id="plugin-buttons">
</div>
</div>
<div style="display: flex; height: 100%;">
<div id="left-pane" class="hide-in-zen-mode">
<div id="global-buttons">
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action bx bx-folder-plus"></a>
<a id="collapse-tree-button" title="Collapse note tree" data-kb-action="CollapseTree" class="icon-action bx bx-layer-minus"></a>
<a id="scroll-to-active-note-button" title="Scroll to active note" data-kb-action="ScrollToActiveNote" class="icon-action bx bx-crosshair"></a>
<a id="toggle-search-button" title="Search in notes" data-kb-action="SearchNotes" class="icon-action bx bx-search"></a>
</div>
<div id="search-box">
<div class="form-group">
<div class="input-group">
<input name="search-text" id="search-text" class="form-control"
placeholder="Search text, labels" autocomplete="off">
<div class="input-group-append">
<button id="do-search-button" class="btn btn-sm icon-button bx bx-search" title="Search (enter)"></button>
</div>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: space-evenly; flex-wrap: wrap;">
<button id="save-search-button" class="btn btn-sm"
title="This will create new saved search note under active note.">
<span class="bx bx-save"></span> Save search</button>
<button id="close-search-button" class="btn btn-sm"><span class="bx bx-x"></span> Close search</button>
</div>
</div>
<div id="search-results">
<strong>Search results:</strong>
<div style="display: flex; align-items: center; justify-content: space-evenly; flex-wrap: wrap;">
<button id="save-search-button" class="btn btn-sm"
title="This will create new saved search note under active note.">
<span class="bx bx-save"></span> Save search</button>
<button id="close-search-button" class="btn btn-sm"><span class="bx bx-x"></span> Close search</button>
<ul id="search-results-inner"></ul>
</div>
<div id="tree"></div>
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
</div>
<div id="search-results">
<strong>Search results:</strong>
<% include center.ejs %>
<ul id="search-results-inner"></ul>
</div>
<div id="tree"></div>
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
<% include sidebar.ejs %>
</div>
<% include tabs.ejs %>
<% include dialogs/about.ejs %>
<% include dialogs/add_link.ejs %>
<% include dialogs/attributes.ejs %>
@@ -228,6 +244,8 @@
<script src="libraries/dayjs.min.js"></script>
<script src="libraries/split.min.js"></script>
<link href="stylesheets/themes.css" rel="stylesheet">
<link href="stylesheets/style.css" rel="stylesheet">
<link href="stylesheets/desktop.css" rel="stylesheet">
+4 -5
View File
@@ -1,7 +1,6 @@
<div class="note-detail-sidebar hide-in-zen-mode" style="width: <%= sidebarWidthPercent %>%; min-width: <%= sidebarMinWidth %>px">
<div style="text-align: center; margin-bottom: 5px;">
<button class="hide-sidebar-button">hide sidebar <span class="bx bx-chevrons-right"></span></button>
</div>
<button id="hide-sidebar-button" class="btn btn-sm icon-button bx bx-chevrons-right" title="Hide sidebar"></button>
<button id="show-sidebar-button" class="btn btn-sm icon-button bx bx-chevrons-left" title="Show sidebar"></button>
<div class="note-detail-widget-container"></div>
<div id="right-pane" class="hide-in-zen-mode">
<div id="sidebar-container"></div>
</div>
-43
View File
@@ -1,43 +0,0 @@
<div class="note-tab-row hide-in-hide-in-zen-mode">
<div class="note-tab-row-content"></div>
</div>
<div id="note-tab-container">
<div class="note-tab-content note-tab-content-template">
<div class="note-detail-content" style="width: <%= contentWidthPercent %>%">
<% include title.ejs %>
<div class="note-detail-script-area"></div>
<table class="note-detail-promoted-attributes"></table>
<div class="note-detail-component-wrapper">
<div class="note-detail-text note-detail-component">
<div class="note-detail-text-editor" tabindex="10000"></div>
</div>
<div class="note-detail-code note-detail-component">
<div class="note-detail-code-editor"></div>
</div>
<% include details/empty.ejs %>
<% include details/search.ejs %>
<% include details/render.ejs %>
<% include details/file.ejs %>
<% include details/image.ejs %>
<% include details/relation_map.ejs %>
<% include details/protected_session_password.ejs %>
<% include details/book.ejs %>
</div>
</div>
<% include sidebar.ejs %>
</div>
</div>
-2
View File
@@ -65,8 +65,6 @@
<a class="dropdown-item show-note-info-button"><kbd data-kb-action="ShowNoteInfo"></kbd> Note info</a>
</div>
</div>
<button class="btn btn-sm icon-button bx bx-chevrons-left show-sidebar-button" title="Show sidebar"></button>
</div>
</div>
</div>