mirror of
https://github.com/zadam/trilium.git
synced 2025-10-27 16:26:31 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cf3fc13b9 | ||
|
|
2b69abf8ab | ||
|
|
3e49a7dbfa | ||
|
|
f782d2bef9 | ||
|
|
ccaa9eae3a | ||
|
|
24c5388e0c |
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.39.4",
|
"version": "0.39.5",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ async function initContextMenu(event, contextMenu) {
|
|||||||
// in such case we'll position it above click coordinates so it will fit into client
|
// in such case we'll position it above click coordinates so it will fit into client
|
||||||
const clickPosition = event.pageY;
|
const clickPosition = event.pageY;
|
||||||
const clientHeight = document.documentElement.clientHeight;
|
const clientHeight = document.documentElement.clientHeight;
|
||||||
const contextMenuHeight = $contextMenuContainer.height();
|
const contextMenuHeight = $contextMenuContainer.outerHeight() + 30;
|
||||||
|
|
||||||
let top;
|
let top;
|
||||||
|
|
||||||
if (clickPosition + contextMenuHeight > clientHeight) {
|
if (clickPosition + contextMenuHeight > clientHeight) {
|
||||||
|
|||||||
@@ -201,17 +201,13 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
|||||||
const newTab = !!options.newTab;
|
const newTab = !!options.newTab;
|
||||||
const activate = !!options.activate;
|
const activate = !!options.activate;
|
||||||
|
|
||||||
const notePath = await treeService.resolveNotePath(origNotePath);
|
let notePath = await treeService.resolveNotePath(origNotePath);
|
||||||
|
|
||||||
if (!notePath) {
|
if (!notePath) {
|
||||||
console.error(`Cannot resolve note path ${origNotePath}`);
|
console.error(`Cannot resolve note path ${origNotePath}`);
|
||||||
|
|
||||||
// fallback to display something
|
// fallback to display something
|
||||||
if (tabContexts.length === 0) {
|
notePath = 'root';
|
||||||
await openEmptyTab();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class NoteDetailText {
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.$component = ctx.$tabContent.find('.note-detail-text');
|
this.$component = ctx.$tabContent.find('.note-detail-text');
|
||||||
this.$editorEl = this.$component.find('.note-detail-text-editor');
|
this.$editorEl = this.$component.find('.note-detail-text-editor');
|
||||||
|
this.textEditorPromise = null;
|
||||||
this.textEditor = null;
|
this.textEditor = null;
|
||||||
|
|
||||||
this.$component.on("dblclick", "img", e => {
|
this.$component.on("dblclick", "img", e => {
|
||||||
@@ -67,7 +68,23 @@ class NoteDetailText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render() {
|
async render() {
|
||||||
if (!this.textEditor) {
|
if (!this.textEditorPromise) {
|
||||||
|
this.textEditorPromise = this.initEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.textEditorPromise;
|
||||||
|
|
||||||
|
// lazy loading above can take time and tab might have been already switched to another note
|
||||||
|
if (this.ctx.note && this.ctx.note.type === 'text') {
|
||||||
|
this.textEditor.isReadOnly = await this.isReadOnly();
|
||||||
|
|
||||||
|
this.$component.show();
|
||||||
|
|
||||||
|
this.textEditor.setData(this.ctx.note.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async initEditor() {
|
||||||
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
||||||
|
|
||||||
const codeBlockLanguages =
|
const codeBlockLanguages =
|
||||||
@@ -85,10 +102,7 @@ class NoteDetailText {
|
|||||||
// display of $component in both branches.
|
// display of $component in both branches.
|
||||||
this.$component.show();
|
this.$component.show();
|
||||||
|
|
||||||
// textEditor might have been initialized during previous await so checking again
|
const textEditorInstance = await BalloonEditor.create(this.$editorEl[0], {
|
||||||
// looks like double initialization can freeze CKEditor pretty badly
|
|
||||||
if (!this.textEditor) {
|
|
||||||
this.textEditor = await BalloonEditor.create(this.$editorEl[0], {
|
|
||||||
placeholder: "Type the content of your note here ...",
|
placeholder: "Type the content of your note here ...",
|
||||||
mention: mentionSetup,
|
mention: mentionSetup,
|
||||||
codeBlock: {
|
codeBlock: {
|
||||||
@@ -98,22 +112,13 @@ class NoteDetailText {
|
|||||||
|
|
||||||
if (glob.isDev && ENABLE_INSPECTOR) {
|
if (glob.isDev && ENABLE_INSPECTOR) {
|
||||||
await import('../../libraries/ckeditor/inspector.js');
|
await import('../../libraries/ckeditor/inspector.js');
|
||||||
CKEditorInspector.attach(this.textEditor);
|
CKEditorInspector.attach(textEditorInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.textEditor = textEditorInstance;
|
||||||
|
|
||||||
this.onNoteChange(() => this.ctx.noteChanged());
|
this.onNoteChange(() => this.ctx.noteChanged());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// lazy loading above can take time and tab might have been already switched to another note
|
|
||||||
if (this.ctx.note && this.ctx.note.type === 'text') {
|
|
||||||
this.textEditor.isReadOnly = await this.isReadOnly();
|
|
||||||
|
|
||||||
this.$component.show();
|
|
||||||
|
|
||||||
this.textEditor.setData(this.ctx.note.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getContent() {
|
getContent() {
|
||||||
const content = this.textEditor.getData();
|
const content = this.textEditor.getData();
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ async function setupProtectedSession(password) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$("#container").addClass('protected-session-active');
|
||||||
|
|
||||||
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
|
||||||
protectedSessionHolder.touchProtectedSession();
|
protectedSessionHolder.touchProtectedSession();
|
||||||
|
|
||||||
|
|||||||
@@ -350,8 +350,6 @@ class TabContext {
|
|||||||
|
|
||||||
this.$savedIndicator.fadeIn();
|
this.$savedIndicator.fadeIn();
|
||||||
|
|
||||||
this.$scriptArea.empty();
|
|
||||||
|
|
||||||
// run async
|
// run async
|
||||||
bundleService.executeRelationBundles(this.note, 'runOnNoteChange', this);
|
bundleService.executeRelationBundles(this.note, 'runOnNoteChange', this);
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ body {
|
|||||||
|
|
||||||
#context-menu-container {
|
#context-menu-container {
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
overflow: auto; /* make it scrollable when exceeding total height of the window */
|
/* !!! Cannot set overflow: auto, submenus will break !!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
#context-menu-container, #context-menu-container .dropdown-menu {
|
#context-menu-container, #context-menu-container .dropdown-menu {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2020-01-04T22:01:20+01:00", buildRevision: "3b8b4da149fbc1b17d09253693823f5135a55f2e" };
|
module.exports = { buildDate:"2020-01-08T21:01:24+01:00", buildRevision: "2b69abf8ab2241f01cd38b31308e54b9faaa74d5" };
|
||||||
|
|||||||
@@ -18,18 +18,6 @@ class ConsistencyChecks {
|
|||||||
this.fixedIssues = false;
|
this.fixedIssues = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findIssues(query, errorCb) {
|
|
||||||
const results = await sql.getRows(query);
|
|
||||||
|
|
||||||
for (const res of results) {
|
|
||||||
logError(errorCb(res));
|
|
||||||
|
|
||||||
this.unrecoveredConsistencyErrors = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
async findAndFixIssues(query, fixerCb) {
|
async findAndFixIssues(query, fixerCb) {
|
||||||
const results = await sql.getRows(query);
|
const results = await sql.getRows(query);
|
||||||
|
|
||||||
@@ -175,13 +163,6 @@ class ConsistencyChecks {
|
|||||||
logError(`Relation ${attributeId} references missing note ${noteId}`)
|
logError(`Relation ${attributeId} references missing note ${noteId}`)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.findIssues(`
|
|
||||||
SELECT noteRevisionId, note_revisions.noteId
|
|
||||||
FROM note_revisions
|
|
||||||
LEFT JOIN notes USING (noteId)
|
|
||||||
WHERE notes.noteId IS NULL`,
|
|
||||||
({noteRevisionId, noteId}) => `Note revision ${noteRevisionId} references missing note ${noteId}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findExistencyIssues() {
|
async findExistencyIssues() {
|
||||||
@@ -335,13 +316,22 @@ class ConsistencyChecks {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.findIssues(`
|
await this.findAndFixIssues(`
|
||||||
SELECT noteId
|
SELECT noteId
|
||||||
FROM notes
|
FROM notes
|
||||||
JOIN note_contents USING (noteId)
|
JOIN note_contents USING (noteId)
|
||||||
WHERE isErased = 1
|
WHERE isErased = 1
|
||||||
AND content IS NOT NULL`,
|
AND content IS NOT NULL`,
|
||||||
({noteId}) => `Note ${noteId} content is not null even though the note is erased`);
|
async ({noteId}) => {
|
||||||
|
if (this.autoFix) {
|
||||||
|
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
|
||||||
|
|
||||||
|
logFix(`Note ${noteId} content has been set to null since the note is erased`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError(`Note ${noteId} content is not null even though the note is erased`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await this.findAndFixIssues(`
|
await this.findAndFixIssues(`
|
||||||
SELECT noteId, noteRevisionId
|
SELECT noteId, noteRevisionId
|
||||||
@@ -398,20 +388,40 @@ class ConsistencyChecks {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.findIssues(`
|
await this.findAndFixIssues(`
|
||||||
SELECT noteRevisionId
|
SELECT noteRevisionId
|
||||||
FROM note_revisions
|
FROM note_revisions
|
||||||
JOIN note_revision_contents USING (noteRevisionId)
|
JOIN note_revision_contents USING (noteRevisionId)
|
||||||
WHERE isErased = 1
|
WHERE isErased = 1
|
||||||
AND content IS NOT NULL`,
|
AND content IS NOT NULL`,
|
||||||
({noteRevisionId}) => `Note revision ${noteRevisionId} content is not null even though the note revision is erased`);
|
async ({noteRevisionId}) => {
|
||||||
|
if (this.autoFix) {
|
||||||
|
await sql.execute(`UPDATE note_revision_contents SET content = NULL WHERE noteRevisionId = ?`, [noteRevisionId]);
|
||||||
|
|
||||||
await this.findIssues(`
|
logFix(`Note revision ${noteRevisionId} content was set to null since the note revision is erased`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError(`Note revision ${noteRevisionId} content is not null even though the note revision is erased`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.findAndFixIssues(`
|
||||||
SELECT noteId
|
SELECT noteId
|
||||||
FROM notes
|
FROM notes
|
||||||
WHERE isErased = 1
|
WHERE isErased = 1
|
||||||
AND isDeleted = 0`,
|
AND isDeleted = 0`,
|
||||||
({noteId}) => `Note ${noteId} is not deleted even though it is erased`);
|
async ({noteId}) => {
|
||||||
|
if (this.autoFix) {
|
||||||
|
const note = await repository.getNote(noteId);
|
||||||
|
note.isDeleted = true;
|
||||||
|
await note.save();
|
||||||
|
|
||||||
|
logFix(`Note ${noteId} was set to deleted since it is erased`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError(`Note ${noteId} is not deleted even though it is erased`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await this.findAndFixIssues(`
|
await this.findAndFixIssues(`
|
||||||
SELECT parentNoteId
|
SELECT parentNoteId
|
||||||
|
|||||||
@@ -490,6 +490,8 @@ async function eraseDeletedNotes() {
|
|||||||
SET isErased = 1,
|
SET isErased = 1,
|
||||||
title = NULL
|
title = NULL
|
||||||
WHERE isErased = 0 AND noteId IN (???)`, noteIdsToErase);
|
WHERE isErased = 0 AND noteId IN (???)`, noteIdsToErase);
|
||||||
|
|
||||||
|
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function duplicateNote(noteId, parentNoteId) {
|
async function duplicateNote(noteId, parentNoteId) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const dayjs = require("dayjs");
|
const dayjs = require("dayjs");
|
||||||
|
|
||||||
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*(\S+|"[^"]+"))?/igu;
|
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*([^\s=*]+|"[^"]+"))?/igu;
|
||||||
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
|
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
|
||||||
|
|
||||||
function calculateSmartValue(v) {
|
function calculateSmartValue(v) {
|
||||||
|
|||||||
Reference in New Issue
Block a user