From 3b268cc8eb66101266276907efba4a7c57fc4ec9 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 21:42:18 +0200 Subject: [PATCH 01/19] fix selecting note title after creation, closes #997 --- src/public/app/services/main_tree_executors.js | 16 ++-------------- src/public/app/services/note_create.js | 6 +++++- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/public/app/services/main_tree_executors.js b/src/public/app/services/main_tree_executors.js index 61717e64f..a365393f3 100644 --- a/src/public/app/services/main_tree_executors.js +++ b/src/public/app/services/main_tree_executors.js @@ -38,12 +38,6 @@ export default class MainTreeExecutors extends Component { isProtected: activeNote.isProtected, saveSelection: false }); - - await ws.waitForMaxKnownSyncId(); - - appContext.tabManager.getActiveTabContext().setNote(note.noteId); - - appContext.triggerCommand('focusAndSelectTitle'); } async createNoteAfterCommand() { @@ -55,17 +49,11 @@ export default class MainTreeExecutors extends Component { return; } - const {note} = await noteCreateService.createNote(parentNoteId, { + await noteCreateService.createNote(parentNoteId, { target: 'after', targetBranchId: node.data.branchId, isProtected: isProtected, - saveSelection: true + saveSelection: false }); - - await ws.waitForMaxKnownSyncId(); - - appContext.tabManager.getActiveTabContext().setNote(note.noteId); - - appContext.triggerCommand('focusAndSelectTitle'); } } \ No newline at end of file diff --git a/src/public/app/services/note_create.js b/src/public/app/services/note_create.js index 24501f10c..353bc7a36 100644 --- a/src/public/app/services/note_create.js +++ b/src/public/app/services/note_create.js @@ -48,8 +48,12 @@ async function createNote(parentNoteId, options = {}) { } if (options.activate) { + await ws.waitForMaxKnownSyncId(); + const activeTabContext = appContext.tabManager.getActiveTabContext(); - activeTabContext.setNote(note.noteId); + await activeTabContext.setNote(note.noteId); + + appContext.triggerCommand('focusAndSelectTitle'); } return {note, branch}; From 7f2755d4a05b0c110b49df4cae660f4855343fe5 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 22:18:09 +0200 Subject: [PATCH 02/19] refresh button state change on note update --- src/public/app/widgets/run_script_buttons.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/public/app/widgets/run_script_buttons.js b/src/public/app/widgets/run_script_buttons.js index a94a1e54f..f74b96be4 100644 --- a/src/public/app/widgets/run_script_buttons.js +++ b/src/public/app/widgets/run_script_buttons.js @@ -20,7 +20,13 @@ export default class RunScriptButtonsWidget extends TabAwareWidget { } refreshWithNote(note) { - this.$renderButton.toggle(note.type === 'render'); + this.$renderButton.toggle(note.type === 'render');console.log("note.mime", note.mime); this.$executeScriptButton.toggle(note.mime.startsWith('application/javascript')); } + + async entitiesReloadedEvent({loadResults}) { + if (loadResults.isNoteReloaded(this.noteId)) { + this.refresh(); + } + } } \ No newline at end of file From 9b9d6d86d0d70c93ebbec4a8f1ef8966052f6294 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 22:51:53 +0200 Subject: [PATCH 03/19] remove debugging console.log --- src/public/app/widgets/run_script_buttons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/public/app/widgets/run_script_buttons.js b/src/public/app/widgets/run_script_buttons.js index f74b96be4..a93bd7707 100644 --- a/src/public/app/widgets/run_script_buttons.js +++ b/src/public/app/widgets/run_script_buttons.js @@ -20,7 +20,7 @@ export default class RunScriptButtonsWidget extends TabAwareWidget { } refreshWithNote(note) { - this.$renderButton.toggle(note.type === 'render');console.log("note.mime", note.mime); + this.$renderButton.toggle(note.type === 'render'); this.$executeScriptButton.toggle(note.mime.startsWith('application/javascript')); } From edb9bc92294198b698608e608d11f64cf4e98514 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 23:58:52 +0200 Subject: [PATCH 04/19] fix "Render note" and "Execute script" buttons + refactoring around data-trigger-command handling --- src/public/app/services/app_context.js | 10 +++++-- src/public/app/services/entrypoints.js | 20 +++++++++++++ src/public/app/services/keyboard_actions.js | 4 +-- src/public/app/widgets/global_buttons.js | 5 +--- src/public/app/widgets/run_script_buttons.js | 6 ++-- src/public/app/widgets/tab_row.js | 15 +++++----- .../app/widgets/type_widgets/editable_code.js | 30 ++----------------- src/public/app/widgets/type_widgets/render.js | 9 ++++-- 8 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/public/app/services/app_context.js b/src/public/app/services/app_context.js index b71224839..f3258a9df 100644 --- a/src/public/app/services/app_context.js +++ b/src/public/app/services/app_context.js @@ -45,10 +45,12 @@ class AppContext extends Component { $("body").append($renderedWidget); - $renderedWidget.on('click', "[data-trigger-command]", e => { - const commandName = $(e.target).attr('data-trigger-command'); + $renderedWidget.on('click', "[data-trigger-command]", function() { + const commandName = $(this).attr('data-trigger-command'); + const $component = $(this).closest(".component"); + const component = $component.prop("component"); - this.triggerCommand(commandName); + component.triggerCommand(commandName); }); this.tabManager = new TabManager(); @@ -92,6 +94,8 @@ class AppContext extends Component { } } + // this might hint at error but sometimes this is used by components which are at different places + // in the component tree to communicate with each other console.debug(`Unhandled command ${name}, converting to event.`); return this.triggerEvent(name, data); diff --git a/src/public/app/services/entrypoints.js b/src/public/app/services/entrypoints.js index 262bf6e19..7c72337ad 100644 --- a/src/public/app/services/entrypoints.js +++ b/src/public/app/services/entrypoints.js @@ -7,6 +7,7 @@ import Component from "../widgets/component.js"; import toastService from "./toast.js"; import noteCreateService from "./note_create.js"; import ws from "./ws.js"; +import bundleService from "./bundle.js"; export default class Entrypoints extends Component { constructor() { @@ -199,4 +200,23 @@ export default class Entrypoints extends Component { async openNewWindowCommand() { this.openInWindowCommand({notePath: ''}); } + + async runActiveNoteCommand() { + const note = appContext.tabManager.getActiveTabNote(); + + // ctrl+enter is also used elsewhere so make sure we're running only when appropriate + if (!note || note.type !== 'code') { + return; + } + + if (note.mime.endsWith("env=frontend")) { + await bundleService.getAndExecuteBundle(note.noteId); + } + + if (note.mime.endsWith("env=backend")) { + await server.post('script/run/' + note.noteId); + } + + toastService.showMessage("Note executed"); + } } diff --git a/src/public/app/services/keyboard_actions.js b/src/public/app/services/keyboard_actions.js index 7ec249404..2eebf152c 100644 --- a/src/public/app/services/keyboard_actions.js +++ b/src/public/app/services/keyboard_actions.js @@ -93,8 +93,8 @@ function updateDisplayedShortcuts($container) { } }); - $container.find('button[data-command],a.icon-action[data-command],.kb-in-title').each(async (i, el) => { - const actionName = $(el).attr('data-command'); + $container.find('[data-trigger-command]').each(async (i, el) => { + const actionName = $(el).attr('data-trigger-command'); const action = await getAction(actionName, true); if (action) { diff --git a/src/public/app/widgets/global_buttons.js b/src/public/app/widgets/global_buttons.js index 9e9e199f1..67b74155c 100644 --- a/src/public/app/widgets/global_buttons.js +++ b/src/public/app/widgets/global_buttons.js @@ -18,17 +18,14 @@ const WIDGET_TPL = ` `; diff --git a/src/public/app/widgets/run_script_buttons.js b/src/public/app/widgets/run_script_buttons.js index a94a1e54f..0fbe765ca 100644 --- a/src/public/app/widgets/run_script_buttons.js +++ b/src/public/app/widgets/run_script_buttons.js @@ -3,10 +3,12 @@ import TabAwareWidget from "./tab_aware_widget.js"; const TPL = `
+ data-trigger-command="runActiveNote" + title="Execute">
`; export default class RunScriptButtonsWidget extends TabAwareWidget { @@ -21,6 +23,6 @@ export default class RunScriptButtonsWidget extends TabAwareWidget { refreshWithNote(note) { this.$renderButton.toggle(note.type === 'render'); - this.$executeScriptButton.toggle(note.mime.startsWith('application/javascript')); + this.$executeScriptButton.toggle(note.type === 'code' && note.mime.startsWith('application/javascript')); } } \ No newline at end of file diff --git a/src/public/app/widgets/tab_row.js b/src/public/app/widgets/tab_row.js index fd89cf47a..0a4b39791 100644 --- a/src/public/app/widgets/tab_row.js +++ b/src/public/app/widgets/tab_row.js @@ -29,11 +29,11 @@ const TAB_TPL = `
-
×
+
×
`; -const NEW_TAB_BUTTON_TPL = `
+
`; +const NEW_TAB_BUTTON_TPL = `
+
`; const FILLER_TPL = `
`; @@ -394,10 +394,13 @@ export default class TabRowWidget extends BasicWidget { this.setupDraggabilly(); } - setTabCloseEvent($tab) { - $tab.find('.note-tab-close') - .on('click', _ => appContext.tabManager.removeTab($tab.attr('data-tab-id'))); + closeActiveTabCommand({$el}) { + const tabId = $el.closest(".note-tab").attr('data-tab-id'); + appContext.tabManager.removeTab(tabId); + } + + setTabCloseEvent($tab) { $tab.on('mousedown', e => { if (e.which === 2) { appContext.tabManager.removeTab($tab.attr('data-tab-id')); @@ -558,8 +561,6 @@ export default class TabRowWidget extends BasicWidget { this.$newTab = $(NEW_TAB_BUTTON_TPL); this.$tabContainer.append(this.$newTab); - - this.$newTab.on('click', _ => this.triggerCommand('openNewTab')); } setupFiller() { diff --git a/src/public/app/widgets/type_widgets/editable_code.js b/src/public/app/widgets/type_widgets/editable_code.js index 20198ece9..1fd619f23 100644 --- a/src/public/app/widgets/type_widgets/editable_code.js +++ b/src/public/app/widgets/type_widgets/editable_code.js @@ -1,9 +1,6 @@ import libraryLoader from "../../services/library_loader.js"; -import bundleService from "../../services/bundle.js"; -import toastService from "../../services/toast.js"; -import server from "../../services/server.js"; -import keyboardActionService from "../../services/keyboard_actions.js"; import TypeWidget from "./type_widget.js"; +import keyboardActionService from "../../services/keyboard_actions.js"; const TPL = `
@@ -27,11 +24,8 @@ export default class EditableCodeTypeWidget extends TypeWidget { doRender() { this.$widget = $(TPL); this.$editor = this.$widget.find('.note-detail-code-editor'); - this.$executeScriptButton = this.$widget.find(".execute-script-button"); - keyboardActionService.setElementActionHandler(this.$widget, 'runActiveNote', () => this.executeCurrentNote()); - - this.$executeScriptButton.on('click', () => this.executeCurrentNote()); + keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); this.initialized = this.initEditor(); @@ -106,26 +100,6 @@ export default class EditableCodeTypeWidget extends TypeWidget { this.codeEditor.focus(); } - async executeCurrentNote() { - // ctrl+enter is also used elsewhere so make sure we're running only when appropriate - if (this.note.type !== 'code') { - return; - } - - // make sure note is saved so we load latest changes - await this.spacedUpdate.updateNowIfNecessary(); - - if (this.note.mime.endsWith("env=frontend")) { - await bundleService.getAndExecuteBundle(this.noteId); - } - - if (this.note.mime.endsWith("env=backend")) { - await server.post('script/run/' + this.noteId); - } - - toastService.showMessage("Note executed"); - } - cleanup() { if (this.codeEditor) { this.spacedUpdate.allowUpdateWithoutChange(() => { diff --git a/src/public/app/widgets/type_widgets/render.js b/src/public/app/widgets/type_widgets/render.js index 0317ac98e..111d2dba2 100644 --- a/src/public/app/widgets/type_widgets/render.js +++ b/src/public/app/widgets/type_widgets/render.js @@ -25,9 +25,6 @@ export default class RenderTypeWidget extends TypeWidget { this.$widget = $(TPL); this.$noteDetailRenderHelp = this.$widget.find('.note-detail-render-help'); this.$noteDetailRenderContent = this.$widget.find('.note-detail-render-content'); - this.$renderButton = this.$widget.find('.render-button'); - - this.$renderButton.on('click', () => this.refresh()); return this.$widget; } @@ -46,4 +43,10 @@ export default class RenderTypeWidget extends TypeWidget { cleanup() { this.$noteDetailRenderContent.empty(); } + + renderActiveNoteEvent() { + if (this.tabContext.isActive()) { + this.refresh(); + } + } } \ No newline at end of file From a0f8caeccd2172d70bdcc5b25cb326fc5b9188d0 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 23:59:26 +0200 Subject: [PATCH 05/19] added SQLite (Trilium) mime type --- src/public/app/services/mime_types.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/public/app/services/mime_types.js b/src/public/app/services/mime_types.js index a4919f03b..6427c3965 100644 --- a/src/public/app/services/mime_types.js +++ b/src/public/app/services/mime_types.js @@ -123,6 +123,7 @@ const MIME_TYPES_DICT = [ { title: "Spreadsheet", mime: "text/x-spreadsheet" }, { default: true, title: "SQL", mime: "text/x-sql" }, { title: "SQLite", mime: "text/x-sqlite" }, + { default: true, title: "SQLite (Trilium)", mime: "text/x-sqlite+trilium" }, { title: "Squirrel", mime: "text/x-squirrel" }, { title: "Stylus", mime: "text/x-styl" }, { default: true, title: "Swift", mime: "text/x-swift" }, From 8aa56080855d6ecace60f75fbb8510850b590b6b Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 5 May 2020 23:58:52 +0200 Subject: [PATCH 06/19] fix "Render note" and "Execute script" buttons + refactoring around data-trigger-command handling --- src/public/app/services/app_context.js | 10 +++++-- src/public/app/services/entrypoints.js | 20 +++++++++++++ src/public/app/services/keyboard_actions.js | 4 +-- src/public/app/widgets/global_buttons.js | 5 +--- src/public/app/widgets/run_script_buttons.js | 6 ++-- src/public/app/widgets/tab_row.js | 15 +++++----- .../app/widgets/type_widgets/editable_code.js | 30 ++----------------- src/public/app/widgets/type_widgets/render.js | 9 ++++-- 8 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/public/app/services/app_context.js b/src/public/app/services/app_context.js index b71224839..f3258a9df 100644 --- a/src/public/app/services/app_context.js +++ b/src/public/app/services/app_context.js @@ -45,10 +45,12 @@ class AppContext extends Component { $("body").append($renderedWidget); - $renderedWidget.on('click', "[data-trigger-command]", e => { - const commandName = $(e.target).attr('data-trigger-command'); + $renderedWidget.on('click', "[data-trigger-command]", function() { + const commandName = $(this).attr('data-trigger-command'); + const $component = $(this).closest(".component"); + const component = $component.prop("component"); - this.triggerCommand(commandName); + component.triggerCommand(commandName); }); this.tabManager = new TabManager(); @@ -92,6 +94,8 @@ class AppContext extends Component { } } + // this might hint at error but sometimes this is used by components which are at different places + // in the component tree to communicate with each other console.debug(`Unhandled command ${name}, converting to event.`); return this.triggerEvent(name, data); diff --git a/src/public/app/services/entrypoints.js b/src/public/app/services/entrypoints.js index 262bf6e19..7c72337ad 100644 --- a/src/public/app/services/entrypoints.js +++ b/src/public/app/services/entrypoints.js @@ -7,6 +7,7 @@ import Component from "../widgets/component.js"; import toastService from "./toast.js"; import noteCreateService from "./note_create.js"; import ws from "./ws.js"; +import bundleService from "./bundle.js"; export default class Entrypoints extends Component { constructor() { @@ -199,4 +200,23 @@ export default class Entrypoints extends Component { async openNewWindowCommand() { this.openInWindowCommand({notePath: ''}); } + + async runActiveNoteCommand() { + const note = appContext.tabManager.getActiveTabNote(); + + // ctrl+enter is also used elsewhere so make sure we're running only when appropriate + if (!note || note.type !== 'code') { + return; + } + + if (note.mime.endsWith("env=frontend")) { + await bundleService.getAndExecuteBundle(note.noteId); + } + + if (note.mime.endsWith("env=backend")) { + await server.post('script/run/' + note.noteId); + } + + toastService.showMessage("Note executed"); + } } diff --git a/src/public/app/services/keyboard_actions.js b/src/public/app/services/keyboard_actions.js index 7ec249404..2eebf152c 100644 --- a/src/public/app/services/keyboard_actions.js +++ b/src/public/app/services/keyboard_actions.js @@ -93,8 +93,8 @@ function updateDisplayedShortcuts($container) { } }); - $container.find('button[data-command],a.icon-action[data-command],.kb-in-title').each(async (i, el) => { - const actionName = $(el).attr('data-command'); + $container.find('[data-trigger-command]').each(async (i, el) => { + const actionName = $(el).attr('data-trigger-command'); const action = await getAction(actionName, true); if (action) { diff --git a/src/public/app/widgets/global_buttons.js b/src/public/app/widgets/global_buttons.js index 9e9e199f1..67b74155c 100644 --- a/src/public/app/widgets/global_buttons.js +++ b/src/public/app/widgets/global_buttons.js @@ -18,17 +18,14 @@ const WIDGET_TPL = `
`; diff --git a/src/public/app/widgets/run_script_buttons.js b/src/public/app/widgets/run_script_buttons.js index a93bd7707..f090e6fb1 100644 --- a/src/public/app/widgets/run_script_buttons.js +++ b/src/public/app/widgets/run_script_buttons.js @@ -3,10 +3,12 @@ import TabAwareWidget from "./tab_aware_widget.js"; const TPL = `
+ data-trigger-command="runActiveNote" + title="Execute">
`; export default class RunScriptButtonsWidget extends TabAwareWidget { @@ -21,7 +23,7 @@ export default class RunScriptButtonsWidget extends TabAwareWidget { refreshWithNote(note) { this.$renderButton.toggle(note.type === 'render'); - this.$executeScriptButton.toggle(note.mime.startsWith('application/javascript')); + this.$executeScriptButton.toggle(note.type === 'code' && note.mime.startsWith('application/javascript')); } async entitiesReloadedEvent({loadResults}) { diff --git a/src/public/app/widgets/tab_row.js b/src/public/app/widgets/tab_row.js index fd89cf47a..0a4b39791 100644 --- a/src/public/app/widgets/tab_row.js +++ b/src/public/app/widgets/tab_row.js @@ -29,11 +29,11 @@ const TAB_TPL = `
-
×
+
×
`; -const NEW_TAB_BUTTON_TPL = `
+
`; +const NEW_TAB_BUTTON_TPL = `
+
`; const FILLER_TPL = `
`; @@ -394,10 +394,13 @@ export default class TabRowWidget extends BasicWidget { this.setupDraggabilly(); } - setTabCloseEvent($tab) { - $tab.find('.note-tab-close') - .on('click', _ => appContext.tabManager.removeTab($tab.attr('data-tab-id'))); + closeActiveTabCommand({$el}) { + const tabId = $el.closest(".note-tab").attr('data-tab-id'); + appContext.tabManager.removeTab(tabId); + } + + setTabCloseEvent($tab) { $tab.on('mousedown', e => { if (e.which === 2) { appContext.tabManager.removeTab($tab.attr('data-tab-id')); @@ -558,8 +561,6 @@ export default class TabRowWidget extends BasicWidget { this.$newTab = $(NEW_TAB_BUTTON_TPL); this.$tabContainer.append(this.$newTab); - - this.$newTab.on('click', _ => this.triggerCommand('openNewTab')); } setupFiller() { diff --git a/src/public/app/widgets/type_widgets/editable_code.js b/src/public/app/widgets/type_widgets/editable_code.js index 20198ece9..1fd619f23 100644 --- a/src/public/app/widgets/type_widgets/editable_code.js +++ b/src/public/app/widgets/type_widgets/editable_code.js @@ -1,9 +1,6 @@ import libraryLoader from "../../services/library_loader.js"; -import bundleService from "../../services/bundle.js"; -import toastService from "../../services/toast.js"; -import server from "../../services/server.js"; -import keyboardActionService from "../../services/keyboard_actions.js"; import TypeWidget from "./type_widget.js"; +import keyboardActionService from "../../services/keyboard_actions.js"; const TPL = `
@@ -27,11 +24,8 @@ export default class EditableCodeTypeWidget extends TypeWidget { doRender() { this.$widget = $(TPL); this.$editor = this.$widget.find('.note-detail-code-editor'); - this.$executeScriptButton = this.$widget.find(".execute-script-button"); - keyboardActionService.setElementActionHandler(this.$widget, 'runActiveNote', () => this.executeCurrentNote()); - - this.$executeScriptButton.on('click', () => this.executeCurrentNote()); + keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); this.initialized = this.initEditor(); @@ -106,26 +100,6 @@ export default class EditableCodeTypeWidget extends TypeWidget { this.codeEditor.focus(); } - async executeCurrentNote() { - // ctrl+enter is also used elsewhere so make sure we're running only when appropriate - if (this.note.type !== 'code') { - return; - } - - // make sure note is saved so we load latest changes - await this.spacedUpdate.updateNowIfNecessary(); - - if (this.note.mime.endsWith("env=frontend")) { - await bundleService.getAndExecuteBundle(this.noteId); - } - - if (this.note.mime.endsWith("env=backend")) { - await server.post('script/run/' + this.noteId); - } - - toastService.showMessage("Note executed"); - } - cleanup() { if (this.codeEditor) { this.spacedUpdate.allowUpdateWithoutChange(() => { diff --git a/src/public/app/widgets/type_widgets/render.js b/src/public/app/widgets/type_widgets/render.js index 0317ac98e..111d2dba2 100644 --- a/src/public/app/widgets/type_widgets/render.js +++ b/src/public/app/widgets/type_widgets/render.js @@ -25,9 +25,6 @@ export default class RenderTypeWidget extends TypeWidget { this.$widget = $(TPL); this.$noteDetailRenderHelp = this.$widget.find('.note-detail-render-help'); this.$noteDetailRenderContent = this.$widget.find('.note-detail-render-content'); - this.$renderButton = this.$widget.find('.render-button'); - - this.$renderButton.on('click', () => this.refresh()); return this.$widget; } @@ -46,4 +43,10 @@ export default class RenderTypeWidget extends TypeWidget { cleanup() { this.$noteDetailRenderContent.empty(); } + + renderActiveNoteEvent() { + if (this.tabContext.isActive()) { + this.refresh(); + } + } } \ No newline at end of file From 62b993f06f680e05d119cc15dbb90356d1172fc6 Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 6 May 2020 21:24:51 +0200 Subject: [PATCH 07/19] fix closing tab by mouse --- package.json | 2 +- src/public/app/services/app_context.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 92c2961ae..c49b61e85 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "electron-rebuild": "1.10.1", "jsdoc": "3.6.4", "lorem-ipsum": "2.0.3", - "webpack": "5.0.0-beta.15", + "webpack": "5.0.0-beta.16", "webpack-cli": "4.0.0-beta.8" }, "optionalDependencies": { diff --git a/src/public/app/services/app_context.js b/src/public/app/services/app_context.js index f3258a9df..9cdbfb955 100644 --- a/src/public/app/services/app_context.js +++ b/src/public/app/services/app_context.js @@ -50,7 +50,7 @@ class AppContext extends Component { const $component = $(this).closest(".component"); const component = $component.prop("component"); - component.triggerCommand(commandName); + component.triggerCommand(commandName, {$el: $(this)}); }); this.tabManager = new TabManager(); From 5d8808a2ad5809ca3d929a1a4f4d72eb6f50aa8f Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 6 May 2020 21:41:14 +0200 Subject: [PATCH 08/19] fix renaming existing attributes + added new label autoReadOnlyDisabled to control automatic setting to readOnly mode --- package-lock.json | 50 +++++++++++++-------------- src/public/app/widgets/note_detail.js | 8 +++-- src/routes/api/attributes.js | 3 +- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56d4c5c00..ef2d72d2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6217,9 +6217,9 @@ } }, "jest-worker": { - "version": "25.4.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz", - "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", "dev": true, "requires": { "merge-stream": "^2.0.0", @@ -10117,9 +10117,9 @@ } }, "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", + "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", "dev": true }, "serve-favicon": { @@ -10272,9 +10272,9 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -10709,9 +10709,9 @@ "dev": true }, "terser": { - "version": "4.6.11", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz", - "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", + "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", "dev": true, "requires": { "commander": "^2.20.0", @@ -10728,19 +10728,19 @@ } }, "terser-webpack-plugin": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz", - "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.6.tgz", + "integrity": "sha512-I8IDsQwZrqjdmOicNeE8L/MhwatAap3mUrtcAKJuilsemUNcX+Hier/eAzwStVqhlCxq0aG3ni9bK/0BESXkTg==", "dev": true, "requires": { "cacache": "^13.0.1", - "find-cache-dir": "^3.2.0", - "jest-worker": "^25.1.0", - "p-limit": "^2.2.2", - "schema-utils": "^2.6.4", - "serialize-javascript": "^2.1.2", + "find-cache-dir": "^3.3.1", + "jest-worker": "^25.4.0", + "p-limit": "^2.3.0", + "schema-utils": "^2.6.6", + "serialize-javascript": "^3.0.0", "source-map": "^0.6.1", - "terser": "^4.4.3", + "terser": "^4.6.12", "webpack-sources": "^1.4.3" }, "dependencies": { @@ -11466,9 +11466,9 @@ "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" }, "webpack": { - "version": "5.0.0-beta.15", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.15.tgz", - "integrity": "sha512-nT+l7LteKTIzB3lmroEGL4qcCBqgHMpa3EJUvhQdfXRWjxCfWnnWdBARhp/To61omZhyNPz2Ye2J1ZEf070kWA==", + "version": "5.0.0-beta.16", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.0.0-beta.16.tgz", + "integrity": "sha512-O6YzI5H7XDPoXFrdC338P0GsSdhmYvz0//HL8LxVFHuRSbtHcV8mfx5U8ouWihFqwyvbfy27Bqoz2KY62kME9Q==", "dev": true, "requires": { "@webassemblyjs/ast": "1.9.0", @@ -11490,7 +11490,7 @@ "pkg-dir": "^4.2.0", "schema-utils": "^2.5.0", "tapable": "2.0.0-beta.9", - "terser-webpack-plugin": "^2.3.1", + "terser-webpack-plugin": "^2.3.6", "watchpack": "2.0.0-beta.13", "webpack-sources": "2.0.0-beta.8" } diff --git a/src/public/app/widgets/note_detail.js b/src/public/app/widgets/note_detail.js index 3cc970c48..6b690f13c 100644 --- a/src/public/app/widgets/note_detail.js +++ b/src/public/app/widgets/note_detail.js @@ -186,7 +186,9 @@ export default class NoteDetailWidget extends TabAwareWidget { const noteComplement = await this.tabContext.getNoteComplement(); if (note.hasLabel('readOnly') || - (noteComplement.content && noteComplement.content.length > 10000)) { + (noteComplement.content + && noteComplement.content.length > 10000) + && !note.hasLabel('autoReadOnlyDisabled')) { type = 'read-only-text'; } } @@ -195,7 +197,9 @@ export default class NoteDetailWidget extends TabAwareWidget { const noteComplement = await this.tabContext.getNoteComplement(); if (note.hasLabel('readOnly') || - (noteComplement.content && noteComplement.content.length > 30000)) { + (noteComplement.content + && noteComplement.content.length > 30000) + && !note.hasLabel('autoReadOnlyDisabled')) { type = 'read-only-code'; } } diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.js index b705e1181..25bbaaf7e 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/attributes.js @@ -101,7 +101,7 @@ async function updateNoteAttributes(req) { || (attribute.type === 'relation' && attribute.value !== attributeEntity.value)) { if (attribute.type !== 'relation' || !!attribute.value.trim()) { - const newAttribute = attribute.createClone(attribute.type, attribute.name, attribute.value); + const newAttribute = attributeEntity.createClone(attribute.type, attribute.name, attribute.value); await newAttribute.save(); } @@ -139,6 +139,7 @@ async function updateNoteAttributes(req) { } const note = await repository.getNote(noteId); + note.invalidateAttributeCache(); return await note.getAttributes(); } From 2369bcf9fcf53289fa5053171f1353c5ecb87064 Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 6 May 2020 23:11:34 +0200 Subject: [PATCH 09/19] fixes for image download --- .idea/dataSources.xml | 18 ++-------------- src/services/attributes.js | 1 + src/services/notes.js | 43 ++++++++++++++++++++------------------ 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index a9dfc5250..70caa7f2a 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,25 +1,11 @@ - + sqlite.xerial true org.sqlite.JDBC - jdbc:sqlite:$USER_HOME$/trilium-data/document.db - - - sqlite.xerial - true - org.sqlite.JDBC - jdbc:sqlite:$PROJECT_DIR$/dist/trilium linux x64/trilium-data/document.db - - - file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.16.1/xerial-sqlite-license.txt - - - file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.16.1/sqlite-jdbc-3.16.1.jar - - + jdbc:sqlite:$PROJECT_DIR$/../trilium-data/document.db \ No newline at end of file diff --git a/src/services/attributes.js b/src/services/attributes.js index d1abd7cec..bab322cbd 100644 --- a/src/services/attributes.js +++ b/src/services/attributes.js @@ -19,6 +19,7 @@ const BUILTIN_ATTRIBUTES = [ { type: 'label', name: 'appTheme' }, { type: 'label', name: 'hidePromotedAttributes' }, { type: 'label', name: 'readOnly' }, + { type: 'label', name: 'autoReadOnlyDisabled' }, { type: 'label', name: 'cssClass' }, { type: 'label', name: 'iconClass' }, { type: 'label', name: 'keyboardShortcut' }, diff --git a/src/services/notes.js b/src/services/notes.js index 4df9846e8..152c72ec4 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -293,15 +293,11 @@ async function downloadImages(noteId, content) { if (!url.includes('api/images/') // this is and exception for the web clipper's "imageId" && (url.length !== 20 || url.toLowerCase().startsWith('http'))) { - if (url in downloadImagePromises) { - // download is already in progress - continue; - } if (url in imageUrlToNoteIdMapping) { const imageNote = await repository.getNote(imageUrlToNoteIdMapping[url]); - if (imageNote || imageNote.isDeleted) { + if (!imageNote || imageNote.isDeleted) { delete imageUrlToNoteIdMapping[url]; } else { @@ -322,6 +318,11 @@ async function downloadImages(noteId, content) { continue; } + if (url in downloadImagePromises) { + // download is already in progress + continue; + } + // this is done asynchronously, it would be too slow to wait for the download // given that save can be triggered very often downloadImagePromises[url] = downloadImage(noteId, url); @@ -338,28 +339,30 @@ async function downloadImages(noteId, content) { // are downloaded and the IMG references are not updated. For this occassion we have this code // which upon the download of all the images will update the note if the links have not been fixed before - const imageNotes = await repository.getNotes(Object.values(imageUrlToNoteIdMapping)); + await sql.transactional(async () => { + const imageNotes = await repository.getNotes(Object.values(imageUrlToNoteIdMapping)); - const origNote = await repository.getNote(noteId); - const origContent = await origNote.getContent(); - let updatedContent = origContent; + const origNote = await repository.getNote(noteId); + const origContent = await origNote.getContent(); + let updatedContent = origContent; - for (const url in imageUrlToNoteIdMapping) { - const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]); + for (const url in imageUrlToNoteIdMapping) { + const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]); - if (imageNote && !imageNote.isDeleted) { - updatedContent = replaceUrl(updatedContent, url, imageNote); + if (imageNote && !imageNote.isDeleted) { + updatedContent = replaceUrl(updatedContent, url, imageNote); + } } - } - // update only if the links have not been already fixed. - if (updatedContent !== origContent) { - await origNote.setContent(updatedContent); + // update only if the links have not been already fixed. + if (updatedContent !== origContent) { + await origNote.setContent(updatedContent); - await scanForLinks(origNote); + await scanForLinks(origNote); - console.log(`Fixed the image links for note ${noteId} to the offline saved.`); - } + console.log(`Fixed the image links for note ${noteId} to the offline saved.`); + } + }); }, 5000); }); From 54ecd2ee75d1177cedadf9fee10319687feee5f0 Mon Sep 17 00:00:00 2001 From: MeIchthys <10717998+meichthys@users.noreply.github.com> Date: Wed, 6 May 2020 17:12:28 -0400 Subject: [PATCH 10/19] Prevent td text from overlapping th text (#1002) This makes all of the Note Info sections more consistent with each other. It prevents overlapping of text when the window is displayed in a small-width environment. --- src/public/app/widgets/collapsible_widgets/note_info.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/public/app/widgets/collapsible_widgets/note_info.js b/src/public/app/widgets/collapsible_widgets/note_info.js index f847d95fb..dad298d44 100644 --- a/src/public/app/widgets/collapsible_widgets/note_info.js +++ b/src/public/app/widgets/collapsible_widgets/note_info.js @@ -21,15 +21,15 @@ const TPL = ` - Note ID: + Note ID: - Created: + Created: - Modified: + Modified: @@ -79,4 +79,4 @@ export default class NoteInfoWidget extends CollapsibleWidget { this.refresh(); } } -} \ No newline at end of file +} From df11b076bc13e30bce449c63010e1a35a4f74802 Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 6 May 2020 23:24:13 +0200 Subject: [PATCH 11/19] release 0.42.1 --- package.json | 2 +- src/services/build.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c49b61e85..7e36d93f4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "trilium", "productName": "Trilium Notes", "description": "Trilium Notes", - "version": "0.42.0-beta", + "version": "0.42.1", "license": "AGPL-3.0-only", "main": "electron.js", "bin": { diff --git a/src/services/build.js b/src/services/build.js index e47c8290e..1071b5c7d 100644 --- a/src/services/build.js +++ b/src/services/build.js @@ -1 +1 @@ -module.exports = { buildDate:"2020-05-04T21:59:14+02:00", buildRevision: "cafcb67a8a3a1943acac829590b34ff729b57e09" }; +module.exports = { buildDate:"2020-05-06T23:24:13+02:00", buildRevision: "54ecd2ee75d1177cedadf9fee10319687feee5f0" }; From 115879ec4ac5fdcde7c9e636dccc5a8b5a7b3d61 Mon Sep 17 00:00:00 2001 From: zadam Date: Thu, 7 May 2020 23:02:46 +0200 Subject: [PATCH 12/19] fix note revisions displaying wrong tooltip --- package-lock.json | 82 +++++++++---------- src/public/app/dialogs/note_revisions.js | 10 ++- .../widgets/collapsible_widgets/note_info.js | 10 +-- 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef2d72d2d..ab50152f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "trilium", - "version": "0.42.0-beta", + "version": "0.42.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1263,7 +1263,7 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" } } @@ -1539,7 +1539,7 @@ }, "uuid": { "version": "2.0.3", - "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" } } @@ -1573,7 +1573,7 @@ "dependencies": { "semver": { "version": "4.3.6", - "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" } } @@ -1593,7 +1593,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "requires": { "readable-stream": "^2.3.5", @@ -1853,12 +1853,12 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "uuid": { "version": "2.0.3", - "resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" } } @@ -1973,7 +1973,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -2148,7 +2148,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2465,7 +2465,7 @@ }, "commander": { "version": "2.8.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "requires": { "graceful-readlink": ">= 1.0.0" @@ -3128,7 +3128,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -4957,7 +4957,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "getpass": { @@ -5221,7 +5221,7 @@ }, "got": { "version": "5.7.1", - "resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", + "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "requires": { "create-error-class": "^3.0.1", @@ -5869,7 +5869,7 @@ }, "into-stream": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", "requires": { "from2": "^2.1.1", @@ -6021,7 +6021,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-object": { @@ -6621,7 +6621,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { "graceful-fs": "^4.1.2", @@ -7130,7 +7130,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "minipass": { @@ -7230,7 +7230,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -7238,7 +7238,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -7432,7 +7432,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "got": { @@ -7468,7 +7468,7 @@ }, "p-cancelable": { "version": "0.4.1", - "resolved": "http://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" }, "p-event": { @@ -7592,7 +7592,7 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" } } @@ -7617,7 +7617,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "pify": { @@ -7674,7 +7674,7 @@ }, "get-stream": { "version": "2.3.1", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "requires": { "object-assign": "^4.0.1", @@ -7704,7 +7704,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } @@ -7744,7 +7744,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, "prepend-http": { @@ -7849,7 +7849,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -8227,7 +8227,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" }, "open": { @@ -8379,7 +8379,7 @@ }, "p-is-promise": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" }, "p-limit": { @@ -8860,7 +8860,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } @@ -9144,7 +9144,7 @@ "dependencies": { "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" } } @@ -9169,7 +9169,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "pify": { @@ -9207,7 +9207,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } @@ -9259,7 +9259,7 @@ }, "get-stream": { "version": "2.3.1", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "requires": { "object-assign": "^4.0.1", @@ -9289,7 +9289,7 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } @@ -9477,7 +9477,7 @@ }, "query-string": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "requires": { "decode-uri-component": "^0.2.0", @@ -9616,7 +9616,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -10484,7 +10484,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10509,7 +10509,7 @@ }, "strip-dirs": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", "integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=", "requires": { "chalk": "^1.0.0", @@ -10767,7 +10767,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -10786,7 +10786,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "requires": { "core-util-is": "~1.0.0", diff --git a/src/public/app/dialogs/note_revisions.js b/src/public/app/dialogs/note_revisions.js index 7f5f45903..36c346afe 100644 --- a/src/public/app/dialogs/note_revisions.js +++ b/src/public/app/dialogs/note_revisions.js @@ -42,10 +42,12 @@ async function loadNoteRevisions(noteId, noteRevId) { revisionItems = await server.get(`notes/${noteId}/revisions`); for (const item of revisionItems) { - $list.append($('') - .text(item.dateLastEdited.substr(0, 16) + ` (${item.contentLength} bytes)`) - .attr('data-note-revision-id', item.noteRevisionId)) - .attr('title', 'This revision was last edited on ' + item.dateLastEdited); + $list.append( + $('') + .text(item.dateLastEdited.substr(0, 16) + ` (${item.contentLength} bytes)`) + .attr('data-note-revision-id', item.noteRevisionId) + .attr('title', 'This revision was last edited on ' + item.dateLastEdited) + ); } $listDropdown.dropdown('show'); diff --git a/src/public/app/widgets/collapsible_widgets/note_info.js b/src/public/app/widgets/collapsible_widgets/note_info.js index dad298d44..e0cb8aaac 100644 --- a/src/public/app/widgets/collapsible_widgets/note_info.js +++ b/src/public/app/widgets/collapsible_widgets/note_info.js @@ -22,15 +22,15 @@ const TPL = ` Note ID: - + Created: - + Modified: - + Type: @@ -60,11 +60,11 @@ export default class NoteInfoWidget extends CollapsibleWidget { this.$noteId.text(note.noteId); this.$dateCreated - .text(noteComplement.dateCreated) + .text(noteComplement.dateCreated.substr(0, 16)) .attr("title", noteComplement.dateCreated); this.$dateModified - .text(noteComplement.dateModified) + .text(noteComplement.dateModified.substr(0, 16)) .attr("title", noteComplement.dateCreated); this.$type.text(note.type); From a3661cb7638d15e686ef6e53847fc61312dd5839 Mon Sep 17 00:00:00 2001 From: zadam Date: Thu, 7 May 2020 23:14:21 +0200 Subject: [PATCH 13/19] fix display of buttons for revisions when there is none --- src/public/app/dialogs/note_revisions.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/public/app/dialogs/note_revisions.js b/src/public/app/dialogs/note_revisions.js index 36c346afe..a448875fb 100644 --- a/src/public/app/dialogs/note_revisions.js +++ b/src/public/app/dialogs/note_revisions.js @@ -37,6 +37,7 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) { async function loadNoteRevisions(noteId, noteRevId) { $list.empty(); $content.empty(); + $titleButtons.empty(); note = appContext.tabManager.getActiveTabNote(); revisionItems = await server.get(`notes/${noteId}/revisions`); @@ -62,6 +63,8 @@ async function loadNoteRevisions(noteId, noteRevId) { $title.text("No revisions for this note yet..."); noteRevisionId = null; } + + $eraseAllRevisionsButton.toggle(revisionItems.length > 0); } $dialog.on('shown.bs.modal', () => { From 0e4eec10b94be88498a22405254709105a72751a Mon Sep 17 00:00:00 2001 From: zadam Date: Thu, 7 May 2020 23:34:13 +0200 Subject: [PATCH 14/19] added "restore this revision" button --- src/public/app/dialogs/note_revisions.js | 17 +++++++++++++++++ src/routes/api/note_revisions.js | 18 +++++++++++++++++- src/routes/routes.js | 1 + 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/public/app/dialogs/note_revisions.js b/src/public/app/dialogs/note_revisions.js index a448875fb..ad2e1c45f 100644 --- a/src/public/app/dialogs/note_revisions.js +++ b/src/public/app/dialogs/note_revisions.js @@ -82,6 +82,21 @@ async function setContentPane() { $title.html(revisionItem.title); + const $restoreRevisionButton = $(''); + + $restoreRevisionButton.on('click', async () => { + const confirmDialog = await import('../dialogs/confirm.js'); + const text = 'Do you want to restore this revision? This will overwrite current title/content of the note with this revision.'; + + if (await confirmDialog.confirm(text)) { + await server.put(`notes/${revisionItem.noteId}/restore-revision/${revisionItem.noteRevisionId}`); + + $dialog.modal('hide'); + + toastService.showMessage('Note revision has been restored.'); + } + }); + const $eraseRevisionButton = $(''); $eraseRevisionButton.on('click', async () => { @@ -98,6 +113,8 @@ async function setContentPane() { }); $titleButtons + .append($restoreRevisionButton) + .append('   ') .append($eraseRevisionButton) .append('   '); diff --git a/src/routes/api/note_revisions.js b/src/routes/api/note_revisions.js index 11131b83b..023ba30ee 100644 --- a/src/routes/api/note_revisions.js +++ b/src/routes/api/note_revisions.js @@ -3,6 +3,7 @@ const repository = require('../../services/repository'); const noteCacheService = require('../../services/note_cache'); const protectedSessionService = require('../../services/protected_session'); +const noteRevisionService = require('../../services/note_revisions'); const utils = require('../../services/utils'); const path = require('path'); @@ -109,6 +110,20 @@ async function eraseNoteRevision(req) { } } +async function restoreNoteRevision(req) { + const noteRevision = await repository.getNoteRevision(req.params.noteRevisionId); + + if (noteRevision && !noteRevision.isErased) { + const note = await noteRevision.getNote(); + + await noteRevisionService.createNoteRevision(note); + + note.title = noteRevision.title; + await note.setContent(await noteRevision.getContent()); + await note.save(); + } +} + async function getEditedNotesOnDate(req) { const date = utils.sanitizeSql(req.params.date); @@ -141,5 +156,6 @@ module.exports = { downloadNoteRevision, getEditedNotesOnDate, eraseAllNoteRevisions, - eraseNoteRevision + eraseNoteRevision, + restoreNoteRevision }; \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index 439ae1201..1de0a7e29 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -145,6 +145,7 @@ function register(app) { apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision); route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision); + apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision); apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); From 48c843c0875fdcc05a82219acef51082f8105ded Mon Sep 17 00:00:00 2001 From: zadam Date: Fri, 8 May 2020 10:24:57 +0200 Subject: [PATCH 15/19] fix setup on server edition --- src/routes/setup.js | 3 +-- src/services/window.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/routes/setup.js b/src/routes/setup.js index d57b619b9..4ba55699c 100644 --- a/src/routes/setup.js +++ b/src/routes/setup.js @@ -6,9 +6,8 @@ const utils = require('../services/utils'); async function setupPage(req, res) { if (await sqlInit.isDbInitialized()) { - const windowService = require('../services/window'); - if (utils.isElectron()) { + const windowService = require('../services/window'); await windowService.createMainWindow(); windowService.closeSetupWindow(); } diff --git a/src/services/window.js b/src/services/window.js index f9f2c8b78..c85a02ad1 100644 --- a/src/services/window.js +++ b/src/services/window.js @@ -165,6 +165,5 @@ module.exports = { createMainWindow, createSetupWindow, closeSetupWindow, - createExtraWindow, registerGlobalShortcuts }; \ No newline at end of file From 64c9734f05ce09e900183c1c299ba42a65524aa1 Mon Sep 17 00:00:00 2001 From: zadam Date: Fri, 8 May 2020 20:50:53 +0200 Subject: [PATCH 16/19] transform setup.js to the webpacked version in the build --- Dockerfile | 2 +- bin/build-server.sh | 2 +- bin/copy-trilium.sh | 3 ++- src/views/setup.ejs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 204ea0b31..99534a78a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.16.2-alpine +FROM node:12.16.3-alpine # Create app directory WORKDIR /usr/src/app diff --git a/bin/build-server.sh b/bin/build-server.sh index a6de256bb..2b5d512f2 100755 --- a/bin/build-server.sh +++ b/bin/build-server.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash PKG_DIR=dist/trilium-linux-x64-server -NODE_VERSION=12.16.2 +NODE_VERSION=12.16.3 if [ "$1" != "DONTCOPY" ] then diff --git a/bin/copy-trilium.sh b/bin/copy-trilium.sh index fe92ca912..dd7f128eb 100755 --- a/bin/copy-trilium.sh +++ b/bin/copy-trilium.sh @@ -34,4 +34,5 @@ find $DIR/libraries -name "*.map" -type f -delete rm -r $DIR/src/public/app sed -i -e 's/app\/desktop.js/app-dist\/desktop.js/g' $DIR/src/views/desktop.ejs -sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs \ No newline at end of file +sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs +sed -i -e 's/app\/setup.js/app-dist\/setup.js/g' $DIR/src/views/setup.ejs \ No newline at end of file diff --git a/src/views/setup.ejs b/src/views/setup.ejs index 2225d1c7f..ddbd94cfd 100644 --- a/src/views/setup.ejs +++ b/src/views/setup.ejs @@ -210,7 +210,7 @@ - + From c3438e0f3fb89be099c8b4e00819a9e149653adc Mon Sep 17 00:00:00 2001 From: zadam Date: Fri, 8 May 2020 22:22:43 +0200 Subject: [PATCH 17/19] implemented SQL console as a type of code note --- .../a2c75661-f9e2-478f-a69f-6a9409e69997.xml | 692 ------------------ libraries/codemirror/mode/meta.js | 2 +- src/public/app/dialogs/sql_console.js | 138 ---- src/public/app/services/mime_types.js | 2 +- .../app/widgets/type_widgets/editable_code.js | 150 +++- src/public/stylesheets/style.css | 7 +- src/services/options_init.js | 2 +- src/views/desktop.ejs | 1 - src/views/dialogs/sql_console.ejs | 27 - 9 files changed, 153 insertions(+), 868 deletions(-) delete mode 100644 .idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml delete mode 100644 src/public/app/dialogs/sql_console.js delete mode 100644 src/views/dialogs/sql_console.ejs diff --git a/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml b/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml deleted file mode 100644 index d76c47f86..000000000 --- a/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml +++ /dev/null @@ -1,692 +0,0 @@ - - - - - 3.16.1 - - - 1 - - - - - -
-
-
-
-
-
-
-
-
-
- 1 -
- - 1 -
- - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - - - 4 - INT|0s - 1 - 0 - - - 5 - TEXT|0s - 1 - "" - - - 1 - apiTokenId - - 1 - - - apiTokenId - 1 - sqlite_autoindex_api_tokens_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - - - 4 - TEXT|0s - 1 - - - 5 - TEXT|0s - 1 - '' - - - 6 - INT|0s - 1 - 0 - - - 7 - TEXT|0s - 1 - - - 8 - TEXT|0s - 1 - - - 9 - INT|0s - 1 - - - 10 - TEXT|0s - NULL - - - 11 - TEXT|0s - 1 - "" - - - 12 - int|0s - 0 - - - 1 - attributeId - - 1 - - - noteId - - - - name -value - - - - value - - - - attributeId - 1 - sqlite_autoindex_attributes_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - - - 4 - INTEGER|0s - 1 - - - 5 - TEXT|0s - - - 6 - INTEGER|0s - 1 - 0 - - - 7 - INTEGER|0s - 1 - 0 - - - 8 - TEXT|0s - NULL - - - 9 - TEXT|0s - 1 - - - 10 - TEXT|0s - 1 - - - 11 - TEXT|0s - 1 - "" - - - 1 - branchId - - 1 - - - noteId -parentNoteId - - - - parentNoteId - - - - branchId - 1 - sqlite_autoindex_branches_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - NULL - - - 3 - TEXT|0s - 1 - "" - - - 4 - TEXT|0s - 1 - - - 1 - noteId - - 1 - - - noteId - 1 - sqlite_autoindex_note_contents_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - - - 3 - TEXT|0s - 1 - '' - - - 4 - TEXT|0s - 1 - - - 1 - noteRevisionId - - 1 - - - noteRevisionId - 1 - sqlite_autoindex_note_revision_contents_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - - - 4 - INT|0s - 1 - - - 5 - INT|0s - 1 - 0 - - - 6 - INT|0s - 1 - 0 - - - 7 - TEXT|0s - 1 - - - 8 - TEXT|0s - 1 - - - 9 - TEXT|0s - 1 - - - 10 - TEXT|0s - 1 - - - 11 - TEXT|0s - 1 - - - 12 - TEXT|0s - 1 - '' - - - 13 - TEXT|0s - 1 - '' - - - 14 - TEXT|0s - 1 - '' - - - 1 - noteRevisionId - - 1 - - - noteId - - - - utcDateLastEdited - - - - utcDateCreated - - - - dateLastEdited - - - - dateCreated - - - - noteRevisionId - 1 - sqlite_autoindex_note_revisions_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - "note" - - - 3 - INT|0s - 1 - - - 4 - INT|0s - 1 - 0 - - - 5 - TEXT|0s - 1 - 'text' - - - 6 - TEXT|0s - 1 - 'text/html' - - - 7 - TEXT|0s - 1 - "" - - - 8 - INT|0s - 1 - 0 - - - 9 - TEXT|0s - NULL - - - 10 - INT|0s - 1 - 0 - - - 11 - TEXT|0s - 1 - - - 12 - TEXT|0s - 1 - - - 13 - TEXT|0s - 1 - - - 14 - TEXT|0s - 1 - - - 1 - noteId - - 1 - - - title - - - - type - - - - isDeleted - - - - dateCreated - - - - dateModified - - - - utcDateCreated - - - - utcDateModified - - - - noteId - 1 - sqlite_autoindex_notes_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - - - 3 - INTEGER|0s - 1 - 0 - - - 4 - TEXT|0s - 1 - "" - - - 5 - TEXT|0s - 1 - - - 6 - TEXT|0s - 1 - - - 1 - name - - 1 - - - name - 1 - sqlite_autoindex_options_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - "" - - - 4 - TEXT|0s - 1 - - - 5 - INT|0s - - - 1 - noteId - - 1 - - - noteId - 1 - sqlite_autoindex_recent_notes_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 1 - sourceId - - 1 - - - utcDateCreated - - - - sourceId - 1 - sqlite_autoindex_source_ids_1 - - - 1 - text|0s - - - 2 - text|0s - - - 3 - text|0s - - - 4 - integer|0s - - - 5 - text|0s - - - 1 - - - 2 - - - 1 - INTEGER|0s - 1 - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - - - 4 - TEXT|0s - 1 - - - 5 - INTEGER|0s - 1 - 0 - - - 6 - TEXT|0s - 1 - - - entityName -entityId - - 1 - - - utcSyncDate - - - - id - 1 - - - \ No newline at end of file diff --git a/libraries/codemirror/mode/meta.js b/libraries/codemirror/mode/meta.js index 7ec168333..506edeb03 100644 --- a/libraries/codemirror/mode/meta.js +++ b/libraries/codemirror/mode/meta.js @@ -134,7 +134,7 @@ {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]}, {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]}, {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]}, - {name: "SQLite", mime: "text/x-sqlite", mode: "sql"}, + {name: "SQLite", mimes: ["text/x-sqlite", "text/x-sqlite;schema=trilium"], mode: "sql"}, {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]}, {name: "Stylus", mime: "text/x-styl", mode: "stylus", ext: ["styl"]}, {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]}, diff --git a/src/public/app/dialogs/sql_console.js b/src/public/app/dialogs/sql_console.js deleted file mode 100644 index 39b2e6861..000000000 --- a/src/public/app/dialogs/sql_console.js +++ /dev/null @@ -1,138 +0,0 @@ -import libraryLoader from '../services/library_loader.js'; -import server from '../services/server.js'; -import toastService from "../services/toast.js"; -import utils from "../services/utils.js"; - -const $dialog = $("#sql-console-dialog"); -const $query = $('#sql-console-query'); -const $executeButton = $('#sql-console-execute'); -const $tableSchemas = $("#sql-console-table-schemas"); -const $resultContainer = $("#result-container"); - -let codeEditor; - -$dialog.on("shown.bs.modal", e => initEditor()); - -export async function showDialog() { - await showTableSchemas(); - - utils.openDialog($dialog); -} - -async function initEditor() { - if (!codeEditor) { - await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR); - - CodeMirror.keyMap.default["Shift-Tab"] = "indentLess"; - CodeMirror.keyMap.default["Tab"] = "indentMore"; - - // removing Escape binding so that Escape will propagate to the dialog (which will close on escape) - delete CodeMirror.keyMap.basic["Esc"]; - - CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js'; - - codeEditor = CodeMirror($query[0], { - value: "", - viewportMargin: Infinity, - indentUnit: 4, - highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false} - }); - - codeEditor.setOption("mode", "text/x-sqlite"); - CodeMirror.autoLoadMode(codeEditor, "sql"); - - codeEditor.setValue(`SELECT title, isProtected, type, mime FROM notes WHERE noteId = 'root'; ---- -SELECT noteId, parentNoteId, notePosition, prefix FROM branches WHERE branchId = 'root';`); - } - - codeEditor.focus(); -} - -async function execute() { - // execute the selected text or the whole content if there's no selection - let sqlQuery = codeEditor.getSelection(); - - if (!sqlQuery) { - sqlQuery = codeEditor.getValue(); - } - - const result = await server.post("sql/execute", { - query: sqlQuery - }); - - if (!result.success) { - toastService.showError(result.error); - return; - } - else { - toastService.showMessage("Query was executed successfully."); - } - - const results = result.results; - - $resultContainer.empty(); - - for (const rows of results) { - if (rows.length === 0) { - continue; - } - - const $table = $('
'); - $resultContainer.append($table); - - const result = rows[0]; - const $row = $(""); - - for (const key in result) { - $row.append($(""); - - for (const key in result) { - $row.append($("
").html(key)); - } - - $table.append($row); - - for (const result of rows) { - const $row = $("
").html(result[key])); - } - - $table.append($row); - } - } -} - -async function showTableSchemas() { - const tables = await server.get('sql/schema'); - - $tableSchemas.empty(); - - for (const table of tables) { - const $tableLink = $(' + + +
+ `; +let TABLE_SCHEMA; + export default class EditableCodeTypeWidget extends TypeWidget { static getType() { return "editable-code"; } doRender() { this.$widget = $(TPL); this.$editor = this.$widget.find('.note-detail-code-editor'); + this.$sqlConsoleArea = this.$widget.find('.sql-console-area'); + this.$sqlConsoleTableSchemas = this.$widget.find('.sql-console-table-schemas'); + this.$sqlConsoleExecuteButton = this.$widget.find('.sql-console-execute'); + this.$sqlConsoleResultContainer = this.$widget.find('.sql-console-result-container'); keyboardActionService.setupActionsForElement('code-detail', this.$widget, this); + utils.bindElShortcut(this.$editor, 'ctrl+return', () => this.execute()); + + this.$sqlConsoleExecuteButton.on('click', () => this.execute()); + this.initialized = this.initEditor(); return this.$widget; @@ -81,9 +135,103 @@ export default class EditableCodeTypeWidget extends TypeWidget { } }); + const isSqlConsole = note.mime === 'text/x-sqlite;schema=trilium'; + + this.$sqlConsoleArea.toggle(isSqlConsole); + + if (isSqlConsole) { + await this.showTableSchemas(); + } + this.show(); } + async showTableSchemas() { + if (!TABLE_SCHEMA) { + TABLE_SCHEMA = await server.get('sql/schema'); + } + + this.$sqlConsoleTableSchemas.empty(); + + for (const table of TABLE_SCHEMA) { + const $tableLink = $('