diff --git a/package-lock.json b/package-lock.json index c5624b0f7..08b25858a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "trilium", - "version": "0.45.1", + "version": "0.45.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -586,11 +586,6 @@ "defer-to-connect": "^1.0.1" } }, - "@tokenizer/token": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz", - "integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w==" - }, "@tootallnate/once": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz", @@ -611,7 +606,8 @@ "@types/debug": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true }, "@types/eslint": { "version": "7.2.4", @@ -3680,17 +3676,6 @@ "pend": "~1.2.0" } }, - "file-type": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.0.0.tgz", - "integrity": "sha512-0u4D11yDu0NXF+8qp1eiQ/6cOwPI7sbu9/bGzUOhlwzKbB9FYFivgTtsVPtlkXAakfByWjOsLp2/Jx927OVetg==", - "requires": { - "readable-web-to-node-stream": "^2.0.0", - "strtok3": "^6.0.3", - "token-types": "^2.0.0", - "typedarray-to-buffer": "^3.1.5" - } - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -5882,11 +5867,6 @@ "pify": "^2.0.0" } }, - "peek-readable": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz", - "integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA==" - }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -6229,11 +6209,6 @@ "util-deprecate": "~1.0.1" } }, - "readable-web-to-node-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-2.0.0.tgz", - "integrity": "sha512-+oZJurc4hXpaaqsN68GoZGQAQIA3qr09Or4fqEsargABnbe5Aau8hFn6ISVleT3cpY/0n/8drn7huyyEvTbghA==" - }, "rechoir": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", @@ -7005,16 +6980,6 @@ "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" }, - "strtok3": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.4.tgz", - "integrity": "sha512-rqWMKwsbN9APU47bQTMEYTPcwdpKDtmf1jVhHzNW2cL1WqAxaM9iBb9t5P2fj+RV2YsErUWgQzHD5JwV0uCTEQ==", - "requires": { - "@tokenizer/token": "^0.1.1", - "@types/debug": "^4.1.5", - "peek-readable": "^3.1.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7293,15 +7258,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "token-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-2.0.0.tgz", - "integrity": "sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw==", - "requires": { - "@tokenizer/token": "^0.1.0", - "ieee754": "^1.1.13" - } - }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", diff --git a/src/public/app/dialogs/export.js b/src/public/app/dialogs/export.js index c3b8c8b18..b600ade80 100644 --- a/src/public/app/dialogs/export.js +++ b/src/public/app/dialogs/export.js @@ -3,6 +3,7 @@ import utils from "../services/utils.js"; import ws from "../services/ws.js"; import toastService from "../services/toast.js"; import treeCache from "../services/tree_cache.js"; +import openService from "../services/open.js"; const $dialog = $("#export-dialog"); const $form = $("#export-form"); @@ -73,9 +74,9 @@ $form.on('submit', () => { function exportBranch(branchId, type, format, version) { taskId = utils.randomString(10); - const url = utils.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`); + const url = openService.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`); - utils.download(url); + openService.download(url); } $('input[name=export-type]').on('change', function () { @@ -133,4 +134,4 @@ ws.subscribeToMessages(async message => { toastService.showPersistent(toast); } -}); \ No newline at end of file +}); diff --git a/src/public/app/dialogs/note_revisions.js b/src/public/app/dialogs/note_revisions.js index 39bdd2ea2..f5f3baaf9 100644 --- a/src/public/app/dialogs/note_revisions.js +++ b/src/public/app/dialogs/note_revisions.js @@ -3,6 +3,7 @@ import server from '../services/server.js'; import toastService from "../services/toast.js"; import appContext from "../services/app_context.js"; import libraryLoader from "../services/library_loader.js"; +import openService from "../services/open.js"; const $dialog = $("#note-revisions-dialog"); const $list = $("#note-revision-list"); @@ -121,11 +122,7 @@ async function setContentPane() { const $downloadButton = $(''); - $downloadButton.on('click', () => { - const url = utils.getUrlForDownload(`api/notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}/download`); - - utils.download(url); - }); + $downloadButton.on('click', () => openService.downloadNoteRevision(revisionItem.noteId, revisionItem.noteRevisionId)); $titleButtons.append($downloadButton); diff --git a/src/public/app/dialogs/options/keyboard_shortcuts.js b/src/public/app/dialogs/options/keyboard_shortcuts.js index 0d9133eec..2eaef5d98 100644 --- a/src/public/app/dialogs/options/keyboard_shortcuts.js +++ b/src/public/app/dialogs/options/keyboard_shortcuts.js @@ -81,7 +81,7 @@ export default class KeyboardShortcutsOptions { .filter(shortcut => !!shortcut); const opts = {}; - opts['keyboardShortcuts' + actionName] = JSON.stringify(shortcuts); + opts['keyboardShortcuts' + actionName.substr(0, 1).toUpperCase() + actionName.substr(1)] = JSON.stringify(shortcuts); server.put('options', opts); }); @@ -138,4 +138,4 @@ export default class KeyboardShortcutsOptions { }); }); } -} \ No newline at end of file +} diff --git a/src/public/app/services/note_content_renderer.js b/src/public/app/services/note_content_renderer.js index a4115d8d5..51638c735 100644 --- a/src/public/app/services/note_content_renderer.js +++ b/src/public/app/services/note_content_renderer.js @@ -1,9 +1,9 @@ import server from "./server.js"; -import utils from "./utils.js"; import renderService from "./render.js"; import protectedSessionService from "./protected_session.js"; import protectedSessionHolder from "./protected_session_holder.js"; import libraryLoader from "./library_loader.js"; +import openService from "./open.js"; async function getRenderedContent(note) { const type = getRenderingType(note); @@ -32,24 +32,11 @@ async function getRenderedContent(note) { .css("max-width", "100%"); } else if (type === 'file' || type === 'pdf') { - function getFileUrl() { - return utils.getUrlForDownload("api/notes/" + note.noteId + "/download"); - } - const $downloadButton = $(''); const $openButton = $(''); - $downloadButton.on('click', () => utils.download(getFileUrl())); - $openButton.on('click', () => { - if (utils.isElectron()) { - const open = utils.dynamicRequire("open"); - - open(getFileUrl(), {url: true}); - } - else { - window.location.href = getFileUrl(); - } - }); + $downloadButton.on('click', () => openService.downloadFileNote(note.noteId)); + $openButton.on('click', () => openService.openFileNote(note.noteId)); // open doesn't work for protected notes since it works through browser which isn't in protected session $openButton.toggle(!note.isProtected); @@ -58,7 +45,7 @@ async function getRenderedContent(note) { if (type === 'pdf') { const $pdfPreview = $(''); - $pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + note.noteId + "/open")); + $pdfPreview.attr("src", openService.getUrlForDownload("api/notes/" + note.noteId + "/open")); $rendered.append($pdfPreview); } diff --git a/src/public/app/services/open.js b/src/public/app/services/open.js new file mode 100644 index 000000000..6054e223c --- /dev/null +++ b/src/public/app/services/open.js @@ -0,0 +1,71 @@ +import utils from "./utils.js"; +import server from "./server.js"; + +function getFileUrl(noteId) { + return getUrlForDownload("api/notes/" + noteId + "/download"); +} + +function download(url) { + if (utils.isElectron()) { + const remote = utils.dynamicRequire('electron').remote; + + remote.getCurrentWebContents().downloadURL(url); + } else { + window.location.href = url; + } +} + +function downloadFileNote(noteId) { + const url = getFileUrl(noteId) + '?' + Date.now(); // don't use cache + + download(url); +} + +async function openFileNote(noteId) { + if (utils.isElectron()) { + const resp = await server.post("notes/" + noteId + "/saveToTmpDir"); + + const electron = utils.dynamicRequire('electron'); + const res = await electron.shell.openPath(resp.tmpFilePath); + + if (res) { + // fallback in case there's no default application for this file + open(getFileUrl(noteId), {url: true}); + } + } + else { + window.location.href = getFileUrl(noteId); + } +} + +function downloadNoteRevision(noteId, noteRevisionId) { + const url = getUrlForDownload(`api/notes/${noteId}/revisions/${noteRevisionId}/download`); + + download(url); +} + +/** + * @param url - should be without initial slash!!! + */ +function getUrlForDownload(url) { + if (utils.isElectron()) { + // electron needs absolute URL so we extract current host, port, protocol + return getHost() + '/' + url; + } + else { + // web server can be deployed on subdomain so we need to use relative path + return url; + } +} + +function getHost() { + const url = new URL(window.location.href); + return url.protocol + "//" + url.hostname + ":" + url.port; +} + +export default { + downloadFileNote, + openFileNote, + downloadNoteRevision, + getUrlForDownload +} diff --git a/src/public/app/services/utils.js b/src/public/app/services/utils.js index 8e4e50a9f..aad45b500 100644 --- a/src/public/app/services/utils.js +++ b/src/public/app/services/utils.js @@ -105,24 +105,6 @@ function formatLabel(label) { return str; } -function getHost() { - const url = new URL(window.location.href); - return url.protocol + "//" + url.hostname + ":" + url.port; -} - -function download(url) { - url += '?' + Date.now(); // don't use cache - - if (isElectron()) { - const remote = dynamicRequire('electron').remote; - - remote.getCurrentWebContents().downloadURL(url); - } - else { - window.location.href = url; - } -} - function toObject(array, fn) { const obj = {}; @@ -294,20 +276,6 @@ async function clearBrowserCache() { } } -/** - * @param url - should be without initial slash!!! - */ -function getUrlForDownload(url) { - if (isElectron()) { - // electron needs absolute URL so we extract current host, port, protocol - return getHost() + '/' + url; - } - else { - // web server can be deployed on subdomain so we need to use relative path - return url; - } -} - function copySelectionToClipboard() { const text = window.getSelection().toString(); if (navigator.clipboard) { @@ -366,7 +334,6 @@ export default { escapeHtml, stopWatch, formatLabel, - download, toObject, randomString, bindGlobalShortcut, @@ -384,7 +351,6 @@ export default { focusSavedElement, isHtmlEmpty, clearBrowserCache, - getUrlForDownload, normalizeShortcut, copySelectionToClipboard, isCKEditorInitialized, diff --git a/src/public/app/widgets/type_widgets/file.js b/src/public/app/widgets/type_widgets/file.js index 554d13564..fbf17e1f3 100644 --- a/src/public/app/widgets/type_widgets/file.js +++ b/src/public/app/widgets/type_widgets/file.js @@ -1,4 +1,5 @@ import utils from "../../services/utils.js"; +import openService from "../../services/open.js"; import server from "../../services/server.js"; import toastService from "../../services/toast.js"; import TypeWidget from "./type_widget.js"; @@ -73,24 +74,8 @@ export default class FileTypeWidget extends TypeWidget { this.$uploadNewRevisionButton = this.$widget.find(".file-upload-new-revision"); this.$uploadNewRevisionInput = this.$widget.find(".file-upload-new-revision-input"); - this.$downloadButton.on('click', () => utils.download(this.getFileUrl())); - - this.$openButton.on('click', async () => { - if (utils.isElectron()) { - const resp = await server.post("notes/" + this.noteId + "/saveToTmpDir"); - - const electron = utils.dynamicRequire('electron'); - const res = await electron.shell.openPath(resp.tmpFilePath); - - if (res) { - // fallback in case there's no default application for this file - open(this.getFileUrl(), {url: true}); - } - } - else { - window.location.href = this.getFileUrl(); - } - }); + this.$downloadButton.on('click', () => openService.downloadFileNote(this.noteId)); + this.$openButton.on('click', () => openService.openFileNote(this.noteId)); this.$uploadNewRevisionButton.on("click", () => { this.$uploadNewRevisionInput.trigger("click"); @@ -146,14 +131,10 @@ export default class FileTypeWidget extends TypeWidget { } else if (note.mime === 'application/pdf') { this.$pdfPreview.show(); - this.$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + this.noteId + "/open")); + this.$pdfPreview.attr("src", openService.getUrlForDownload("api/notes/" + this.noteId + "/open")); } // open doesn't work for protected notes since it works through browser which isn't in protected session this.$openButton.toggle(!note.isProtected); } - - getFileUrl() { - return utils.getUrlForDownload("api/notes/" + this.noteId + "/download"); - } } diff --git a/src/public/app/widgets/type_widgets/image.js b/src/public/app/widgets/type_widgets/image.js index 5e5ffc567..c369536eb 100644 --- a/src/public/app/widgets/type_widgets/image.js +++ b/src/public/app/widgets/type_widgets/image.js @@ -1,6 +1,7 @@ import utils from "../../services/utils.js"; import toastService from "../../services/toast.js"; import server from "../../services/server.js"; +import openService from "../../services/open.js"; import TypeWidget from "./type_widget.js"; const TPL = ` @@ -64,7 +65,7 @@ class ImageTypeWidget extends TypeWidget { this.$fileSize = this.$widget.find(".image-filesize"); this.$imageDownloadButton = this.$widget.find(".image-download"); - this.$imageDownloadButton.on('click', () => utils.download(this.getFileUrl())); + this.$imageDownloadButton.on('click', () => openService.downloadFileNote(this.noteId)); this.$copyToClipboardButton.on('click',() => { this.$imageWrapper.attr('contenteditable','true'); @@ -145,10 +146,6 @@ class ImageTypeWidget extends TypeWidget { selection.removeAllRanges(); selection.addRange(range); } - - getFileUrl() { - return utils.getUrlForDownload(`api/notes/${this.noteId}/download`); - } } export default ImageTypeWidget diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index ccf2c2541..31ab8d0d7 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -59,7 +59,7 @@ ul.fancytree-container { font-size: x-large; text-transform: none; line-height: 1; - content: "\ea1d"; + content: "\e9b2"; position: relative; top: -2px; margin-right: 5px; @@ -72,7 +72,7 @@ ul.fancytree-container { .fancytree-node.fancytree-expanded .fancytree-expander:before { font-family: 'boxicons' !important; - content: "\ea17"; + content: "\e9ac"; } /** some common text styling for cssClass label */ diff --git a/src/services/keyboard_actions.js b/src/services/keyboard_actions.js index 1db1ce37d..bb19dbeed 100644 --- a/src/services/keyboard_actions.js +++ b/src/services/keyboard_actions.js @@ -365,7 +365,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [ }, { actionName: "openDevTools", - defaultShortcuts: ["CommandOrControl+Shift+I"], + defaultShortcuts: isElectron ? ["CommandOrControl+Shift+I"] : [], scope: "window" }, { @@ -408,13 +408,7 @@ for (const action of DEFAULT_KEYBOARD_ACTIONS) { } } -let cachedActions = null; - function getKeyboardActions() { - if (cachedActions) { - return cachedActions; - } - const actions = JSON.parse(JSON.stringify(DEFAULT_KEYBOARD_ACTIONS)); for (const action of actions) { @@ -442,8 +436,6 @@ function getKeyboardActions() { } } - cachedActions = actions; - return actions; }