mirror of
https://github.com/zadam/trilium.git
synced 2025-11-06 13:26:01 +01:00
Merge remote-tracking branch 'origin/stable'
# Conflicts: # db/demo.zip # src/services/notes.js # src/services/request.js
This commit is contained in:
@@ -81,6 +81,8 @@ function setupGlobs() {
|
||||
|
||||
$("body").on("click", "a.external", function () {
|
||||
window.open($(this).attr("href"), '_blank');
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ async function getRenderedContent(note) {
|
||||
.attr("src", `api/images/${note.noteId}/${note.title}`)
|
||||
.css("max-width", "100%");
|
||||
}
|
||||
else if (type === 'file') {
|
||||
else if (type === 'file' || type === 'pdf') {
|
||||
function getFileUrl() {
|
||||
return utils.getUrlForDownload("api/notes/" + note.noteId + "/download");
|
||||
}
|
||||
@@ -47,19 +47,21 @@ async function getRenderedContent(note) {
|
||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||
$openButton.toggle(!note.isProtected);
|
||||
|
||||
$rendered = $('<div>');
|
||||
$rendered = $('<div style="display: flex; flex-direction: column; height: 100%;">');
|
||||
|
||||
if (note.mime === 'application/pdf' && utils.isElectron()) {
|
||||
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; height: 100%; flex-grow: 100;"></iframe>');
|
||||
if (type === 'pdf') {
|
||||
const $pdfPreview = $('<iframe class="pdf-preview" style="width: 100%; flex-grow: 100;"></iframe>');
|
||||
$pdfPreview.attr("src", utils.getUrlForDownload("api/notes/" + note.noteId + "/open"));
|
||||
|
||||
$rendered.append($pdfPreview);
|
||||
}
|
||||
|
||||
$rendered
|
||||
.append($downloadButton)
|
||||
.append(' ')
|
||||
.append($openButton);
|
||||
$rendered.append(
|
||||
$("<div>")
|
||||
.append($downloadButton)
|
||||
.append(' ')
|
||||
.append($openButton)
|
||||
);
|
||||
}
|
||||
else if (type === 'render') {
|
||||
$rendered = $('<div>');
|
||||
@@ -90,6 +92,10 @@ async function getRenderedContent(note) {
|
||||
function getRenderingType(note) {
|
||||
let type = note.type;
|
||||
|
||||
if (type === 'file' && note.mime === 'application/pdf' && utils.isElectron()) {
|
||||
type = 'pdf';
|
||||
}
|
||||
|
||||
if (note.isProtected) {
|
||||
if (protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
protectedSessionHolder.touchProtectedSession();
|
||||
@@ -104,4 +110,4 @@ function getRenderingType(note) {
|
||||
|
||||
export default {
|
||||
getRenderedContent
|
||||
};
|
||||
};
|
||||
|
||||
@@ -288,6 +288,13 @@ class TreeCache {
|
||||
async getNoteComplement(noteId) {
|
||||
if (!this.noteComplementPromises[noteId]) {
|
||||
this.noteComplementPromises[noteId] = server.get('notes/' + noteId).then(row => new NoteComplement(row));
|
||||
|
||||
// we don't want to keep large payloads forever in memory so we clean that up quite quickly
|
||||
// this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components)
|
||||
// this is also a work around for missing invalidation after change
|
||||
this.noteComplementPromises[noteId].then(
|
||||
() => setTimeout(() => this.noteComplementPromises[noteId] = null, 1000)
|
||||
);
|
||||
}
|
||||
|
||||
return await this.noteComplementPromises[noteId];
|
||||
|
||||
@@ -329,4 +329,9 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
saveSelection: true
|
||||
});
|
||||
}
|
||||
|
||||
// used by cutToNote in CKEditor build
|
||||
async saveNoteDetailNowCommand() {
|
||||
await this.spacedUpdate.updateNowIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,10 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
||||
extensions: utils.isMobile() ? ["dnd5", "clones"] : ["hotkeys", "dnd5", "clones"],
|
||||
source: treeData,
|
||||
scrollOfs: {
|
||||
top: 200,
|
||||
bottom: 200
|
||||
},
|
||||
scrollParent: this.$tree,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
click: (event, data) => {
|
||||
|
||||
@@ -36,10 +36,10 @@ export default class AbstractTextTypeWidget extends TypeWidget {
|
||||
.append($link)
|
||||
);
|
||||
|
||||
const {renderedContent} = await noteContentRenderer.getRenderedContent(note);
|
||||
const {renderedContent, type} = await noteContentRenderer.getRenderedContent(note);
|
||||
|
||||
$el.append(
|
||||
$('<div class="include-note-content">')
|
||||
$(`<div class="include-note-content type-${type}">`)
|
||||
.append(renderedContent)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export default class FileTypeWidget extends TypeWidget {
|
||||
this.$pdfPreview.attr('src', '').empty().hide();
|
||||
|
||||
if (noteComplement.content) {
|
||||
this.$previewContent.show();
|
||||
this.$previewContent.show().scrollTop(0);
|
||||
this.$previewContent.text(noteComplement.content);
|
||||
}
|
||||
else if (note.mime === 'application/pdf' && utils.isElectron()) {
|
||||
|
||||
@@ -687,11 +687,23 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.include-note.box-size-small .include-note-content.type-pdf {
|
||||
height: 10em; /* PDF is rendered in iframe and must be sized absolutely */
|
||||
}
|
||||
|
||||
.include-note.box-size-medium .include-note-content {
|
||||
max-height: 20em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.include-note.box-size-medium .include-note-content.type-pdf {
|
||||
height: 20em; /* PDF is rendered in iframe and must be sized absolutely */
|
||||
}
|
||||
|
||||
.include-note.box-size-full .include-note-content.type-pdf {
|
||||
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
color: var(--main-text-color) !important;
|
||||
background-color: var(--accented-background-color) !important;
|
||||
|
||||
@@ -17,8 +17,9 @@ function getNote(req) {
|
||||
if (note.isStringNote()) {
|
||||
note.content = note.getContent();
|
||||
|
||||
if (note.type === 'file') {
|
||||
note.content = note.content.substr(0, 10000);
|
||||
if (note.type === 'file' && note.content.length > 10000) {
|
||||
note.content = note.content.substr(0, 10000)
|
||||
+ `\r\n\r\n... and ${note.content.length - 10000} more characters.`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,18 +279,15 @@ const downloadImagePromises = {};
|
||||
function replaceUrl(content, url, imageNote) {
|
||||
const quotedUrl = utils.quoteRegex(url);
|
||||
|
||||
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
|
||||
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
|
||||
}
|
||||
|
||||
function downloadImages(noteId, content) {
|
||||
const re = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
|
||||
let match;
|
||||
|
||||
const origContent = content;
|
||||
|
||||
while (match = re.exec(origContent)) {
|
||||
const url = match[1];
|
||||
const imageRe = /<img[^>]*?\ssrc=['"]([^'">]+)['"]/ig;
|
||||
let imageMatch;
|
||||
|
||||
while (imageMatch = imageRe.exec(content)) {
|
||||
const url = imageMatch[1];
|
||||
const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url);
|
||||
|
||||
if (inlineImageMatch) {
|
||||
@@ -300,9 +297,9 @@ function downloadImages(noteId, content) {
|
||||
const imageService = require('../services/image');
|
||||
const {note} = imageService.saveImage(noteId, imageBuffer, "inline image", true);
|
||||
|
||||
content = content.substr(0, match.index)
|
||||
content = content.substr(0, imageMatch.index)
|
||||
+ `<img src="api/images/${note.noteId}/${note.title}"`
|
||||
+ content.substr(match.index + match[0].length);
|
||||
+ content.substr(imageMatch.index + imageMatch[0].length);
|
||||
}
|
||||
else if (!url.includes('api/images/')
|
||||
// this is an exception for the web clipper's "imageId"
|
||||
@@ -316,7 +313,6 @@ function downloadImages(noteId, content) {
|
||||
}
|
||||
else {
|
||||
content = replaceUrl(content, url, imageNote);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -328,7 +324,6 @@ function downloadImages(noteId, content) {
|
||||
imageUrlToNoteIdMapping[url] = existingImage.noteId;
|
||||
|
||||
content = replaceUrl(content, url, existingImage);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,11 @@ function exec(opts) {
|
||||
}
|
||||
|
||||
function getImage(imageUrl) {
|
||||
const proxyConf = syncOptions.getSyncProxy();
|
||||
const opts = {
|
||||
method: 'GET',
|
||||
url: imageUrl,
|
||||
proxy: syncOptions.getSyncProxy()
|
||||
proxy: proxyConf !== "noproxy" ? proxyConf : null
|
||||
};
|
||||
|
||||
const client = getClient(opts);
|
||||
|
||||
@@ -155,7 +155,11 @@ function getContentDisposition(filename) {
|
||||
return `file; filename="${sanitizedFilename}"; filename*=UTF-8''${sanitizedFilename}`;
|
||||
}
|
||||
|
||||
const STRING_MIME_TYPES = ["application/x-javascript", "image/svg+xml"];
|
||||
const STRING_MIME_TYPES = [
|
||||
"application/javascript",
|
||||
"application/x-javascript",
|
||||
"image/svg+xml"
|
||||
];
|
||||
|
||||
function isStringNote(type, mime) {
|
||||
// render and book are string note in the sense that they are expected to contain empty string
|
||||
|
||||
Reference in New Issue
Block a user