Compare commits

...

27 Commits

Author SHA1 Message Date
azivner
2b32addade release 0.27.4 2019-01-10 21:31:30 +01:00
azivner
0b251530fa open recent notes autocomplete by focus so it is then closed with blur, fixes #272 2019-01-10 21:04:06 +01:00
azivner
f5b933149a Merge remote-tracking branch 'origin/master' 2019-01-10 19:53:47 +01:00
azivner
48bbfb8bdb fix activating note by noteId when hoisting, fixes #320 2019-01-10 19:53:42 +01:00
zadam
889971c4d6 Merge pull request #312 from perissology/evernote-import
Fixes evernote import errors
2019-01-09 23:41:17 +01:00
azivner
0722494d41 fix saving JSON note with invalid JSON (previously in such a case content was not updated), fixes #307 2019-01-09 23:36:17 +01:00
azivner
4b977a3306 setup keyboard shortcuts on the setup page as well, closes #267 2019-01-09 22:08:24 +01:00
azivner
3ff3021acd shortcuts for mac should use cmd instead of ctrl, closes #290 2019-01-09 21:42:16 +01:00
azivner
99e56a9c42 make sure to save the search note before refreshing the tree 2019-01-09 19:54:32 +01:00
azivner
77279dfe16 fix anonymization 2019-01-09 19:49:02 +01:00
perissology
93f8050454 use resizedImage if image optimization fails 2019-01-09 06:29:49 -08:00
perissology
31cfede7a7 enex import: attempt to get correct mime from Buffer 2019-01-09 06:29:13 -08:00
azivner
c8ec86e537 allow refreshing saved note, closes #304 2019-01-08 23:32:03 +01:00
azivner
05aee884b6 fix saving search note content #304 2019-01-08 22:48:53 +01:00
azivner
012ba9e060 don't attempt to run protected notes outside of protected session, fixes #279 2019-01-08 21:21:49 +01:00
azivner
8e8fd88857 process only whitelisted mime types as an image, fixes #288 2019-01-08 20:45:34 +01:00
azivner
523ccdad6b reload note cache after import, closes #293 2019-01-08 20:19:41 +01:00
azivner
ded3f605be fix almost invisible buttons on options page, closes #297 2019-01-08 19:47:35 +01:00
azivner
030d12a465 stretch sync login token validity to 5 minutes #277 2019-01-07 23:29:56 +01:00
azivner
4d15628840 Merge remote-tracking branch 'origin/master' 2019-01-07 23:17:45 +01:00
azivner
81b849898c stretch body to full window width, fixes #276 2019-01-07 23:17:12 +01:00
zadam
3824486b85 Merge pull request #275 from Lee303/Dockerfile-dependency-fix
Update Dockerfile
2019-01-07 22:49:27 +01:00
Lee Spottiswood
081ab00a0a Update Dockerfile 2019-01-07 21:21:23 +00:00
zadam
04f6af5c9a Merge pull request #270 from svenefftinge/master
Make contributions easier
2019-01-07 21:46:02 +01:00
Sven Efftinge
4dc1f1f6eb Added contribute section and gitpod config 2019-01-07 12:52:02 +00:00
azivner
3930a02123 tree now uses standard font size which effectively makes it a bit larger 2019-01-06 20:59:19 +01:00
azivner
3112de105e fancytree selection/hover colors are shades of gray, border is rounded 2019-01-06 18:58:12 +01:00
27 changed files with 244 additions and 92 deletions

7
.gitpod.yml Normal file
View File

@@ -0,0 +1,7 @@
tasks:
- before: nvm install 10 && nvm use 10
init: npm install
command: npm run start
ports:
- port: 8080
onOpen: open-preview

View File

@@ -17,6 +17,7 @@ RUN set -x \
libtool \
make \
nasm \
libpng-dev \
&& npm install --production \
&& apk del .build-dependencies

View File

@@ -1,7 +1,7 @@
# Trilium Notes
[![Join the chat at https://gitter.im/trilium-notes/Lobby](https://badges.gitter.im/trilium-notes/Lobby.svg)](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases. See [screenshots](https://github.com/zadam/trilium/wiki/Screenshot-tour) for quick overview:
Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases. See [screenshots](https://github.com/zadam/trilium/wiki/Screenshot-tour) for quick overview:
![](https://raw.githubusercontent.com/wiki/zadam/trilium/images/screenshot.png)
@@ -34,4 +34,16 @@ Trilium is provided as either desktop application (Linux, Windows, Mac) or web a
[See wiki for complete list of documentation pages.](https://github.com/zadam/trilium/wiki/)
You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium.
You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium.
## Contribute
Use a browser based dev environment
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/zadam/trilium)
Or clone locally and run
```
npm install
npm run start
```

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.27.3",
"version": "0.27.4",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -33,6 +33,7 @@
"electron-in-page-search": "1.3.2",
"express": "4.16.4",
"express-session": "1.15.6",
"file-type": "10.7.0",
"fs-extra": "7.0.1",
"get-port": "4.1.0",
"helmet": "3.15.0",

View File

@@ -56,6 +56,9 @@ class Note extends Entity {
setContent(content) {
this.content = content;
// if parsing below is not successful then there's no jsonContent as opposed to still having the old unupdated ones
delete this.jsonContent;
try {
this.jsonContent = JSON.parse(this.content);
}

View File

@@ -36,6 +36,7 @@ import hoistedNoteService from './services/hoisted_note.js';
import noteTypeService from './services/note_type.js';
import linkService from './services/link.js';
import noteAutocompleteService from './services/note_autocomplete.js';
import macInit from './services/mac_init.js';
// required for CKEditor image upload plugin
window.glob.getCurrentNode = treeService.getCurrentNode;
@@ -110,28 +111,6 @@ if (utils.isElectron()) {
});
}
function exec(cmd) {
document.execCommand(cmd);
return false;
}
if (utils.isElectron() && utils.isMac()) {
utils.bindShortcut('ctrl+c', () => exec("copy"));
utils.bindShortcut('ctrl+v', () => exec('paste'));
utils.bindShortcut('ctrl+x', () => exec('cut'));
utils.bindShortcut('ctrl+a', () => exec('selectAll'));
utils.bindShortcut('ctrl+z', () => exec('undo'));
utils.bindShortcut('ctrl+y', () => exec('redo'));
utils.bindShortcut('meta+c', () => exec("copy"));
utils.bindShortcut('meta+v', () => exec('paste'));
utils.bindShortcut('meta+x', () => exec('cut'));
utils.bindShortcut('meta+a', () => exec('selectAll'));
utils.bindShortcut('meta+z', () => exec('undo'));
utils.bindShortcut('meta+y', () => exec('redo'));
}
$("#export-note-button").click(function () {
if ($(this).hasClass("disabled")) {
return;
@@ -140,6 +119,8 @@ $("#export-note-button").click(function () {
exportDialog.showDialog('single');
});
macInit.init();
treeService.showTree();
entrypoints.registerEntrypoints();

View File

@@ -6,8 +6,6 @@ const $dialog = $("#jump-to-note-dialog");
const $autoComplete = $("#jump-to-note-autocomplete");
const $showInFullTextButton = $("#show-in-full-text-button");
$dialog.on("shown.bs.modal", e => $autoComplete.focus());
async function showDialog() {
glob.activeDialog = $dialog;

View File

@@ -117,10 +117,6 @@ function registerEntrypoints() {
utils.bindShortcut('ctrl+f', openInPageSearch);
if (utils.isMac()) {
utils.bindShortcut('meta+f', openInPageSearch);
}
// FIXME: do we really need these at this point?
utils.bindShortcut("ctrl+shift+up", () => {
const node = treeService.getCurrentNode();

View File

@@ -0,0 +1,25 @@
/**
* Mac specific initialization
*/
import utils from "./utils.js";
function init() {
if (utils.isElectron() && utils.isMac()) {
utils.bindShortcut('meta+c', () => exec("copy"));
utils.bindShortcut('meta+v', () => exec('paste'));
utils.bindShortcut('meta+x', () => exec('cut'));
utils.bindShortcut('meta+a', () => exec('selectAll'));
utils.bindShortcut('meta+z', () => exec('undo'));
utils.bindShortcut('meta+y', () => exec('redo'));
}
}
function exec(cmd) {
document.execCommand(cmd);
return false;
}
export default {
init
}

View File

@@ -28,7 +28,7 @@ function clearText($el) {
function showRecentNotes($el) {
$el.setSelectedPath("");
$el.autocomplete("val", "");
$el.autocomplete("open");
$el.focus();
}
function initNoteAutocomplete($el, options) {
@@ -61,7 +61,13 @@ function initNoteAutocomplete($el, options) {
$clearTextButton.click(() => clearText($el));
$showRecentNotesButton.click(() => showRecentNotes($el));
$showRecentNotesButton.click(e => {
showRecentNotes($el);
// this will cause the click not give focus to the "show recent notes" button
// this is important because otherwise input will lose focus immediatelly and not show the results
return false;
});
$goToSelectedNoteButton.click(() => {
if ($el.hasClass("disabled")) {

View File

@@ -1,10 +1,13 @@
import noteDetailService from "./note_detail.js";
import treeService from "./tree.js";
import infoService from './info.js';
const $searchString = $("#search-string");
const $component = $('#note-detail-search');
const $refreshButton = $('#note-detail-search-refresh-results-button');
function getContent() {
JSON.stringify({
return JSON.stringify({
searchString: $searchString.val()
});
}
@@ -25,6 +28,14 @@ function show() {
$searchString.on('input', noteDetailService.noteChanged);
}
$refreshButton.click(async () => {
await noteDetailService.saveNoteIfChanged();
treeService.reload();
infoService.showMessage('Tree has been refreshed.');
});
export default {
getContent,
show,

View File

@@ -87,7 +87,7 @@ $searchInput.keyup(e => {
if (e && e.which === $.ui.keyCode.ENTER) {
doSearch();
}
}).focus();
});
$doSearchButton.click(() => doSearch()); // keep long form because of argument
$resetSearchButton.click(resetSearch);

View File

@@ -94,32 +94,41 @@ async function expandToNote(notePath, expandOpts) {
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
let hoistedNoteFound = false;
let parentNoteId = null;
for (const childNoteId of runPath) {
// for first node (!parentNoteId) it doesn't matter which node is found
let node = getNode(childNoteId, parentNoteId);
if (childNoteId === hoistedNoteId) {
hoistedNoteFound = true;
}
if (!node && parentNoteId) {
const parents = getNodesByNoteId(parentNoteId);
// we expand only after hoisted note since before then nodes are not actually present in the tree
if (hoistedNoteFound) {
// for first node (!parentNoteId) it doesn't matter which node is found
let node = getNode(childNoteId, parentNoteId);
for (const parent of parents) {
// force load parents. This is useful when fancytree doesn't contain recently created notes yet.
await parent.load(true);
if (!node && parentNoteId) {
const parents = getNodesByNoteId(parentNoteId);
for (const parent of parents) {
// force load parents. This is useful when fancytree doesn't contain recently created notes yet.
await parent.load(true);
}
node = getNode(childNoteId, parentNoteId);
}
node = getNode(childNoteId, parentNoteId);
}
if (!node) {
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
}
if (!node) {
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
}
if (childNoteId === noteId) {
return node;
}
else {
await node.setExpanded(true, expandOpts);
if (childNoteId === noteId) {
return node;
} else {
await node.setExpanded(true, expandOpts);
}
}
parentNoteId = childNoteId;
@@ -129,9 +138,12 @@ async function expandToNote(notePath, expandOpts) {
async function activateNote(notePath, noteLoadedListener) {
utils.assertArguments(notePath);
// notePath argument can contain only noteId which is not good when hoisted since
// then we need to check the whole note path
const runNotePath = await getRunPath(notePath);
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
if (hoistedNoteId !== 'root' && !notePath.includes(hoistedNoteId)) {
if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) {
if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree. Do you want to unhoist?")) {
return;
}
@@ -352,6 +364,7 @@ function clearSelectedNodes() {
}
async function treeInitialized() {
// - is used in mobile to indicate that we don't want to activate any note after load
if (startNotePath === '-') {
return;
}
@@ -363,7 +376,6 @@ async function treeInitialized() {
startNotePath = null;
}
// - is used in mobile to indicate that we don't want to activate any note after load
if (startNotePath) {
const node = await activateNote(startNotePath);
@@ -438,6 +450,16 @@ function initFancyTree(tree) {
$span.append(unhoistButton);
}
},
// this is done to automatically lazy load all expanded search notes after tree load
loadChildren: function(event, data) {
data.node.visit(function(subNode){
// Load all lazy/unloaded child nodes
// (which will trigger `loadChildren` recursively)
if( subNode.isUndefined() && subNode.isExpanded() ) {
subNode.load();
}
});
}
});

View File

@@ -86,7 +86,7 @@ async function prepareNode(branch) {
extraClasses: await getExtraClasses(note),
icon: await getIcon(note),
refKey: note.noteId,
expanded: (note.type !== 'search' && branch.isExpanded) || hoistedNoteId === note.noteId
expanded: branch.isExpanded || hoistedNoteId === note.noteId
};
if (note.hasChildren() || note.type === 'search') {

View File

@@ -137,6 +137,11 @@ function randomString(len) {
function bindShortcut(keyboardShortcut, handler) {
if (isDesktop()) {
if (isMac()) {
// use CMD (meta) instead of CTRL for all shortcuts
keyboardShortcut = keyboardShortcut.replace("ctrl", "meta");
}
$(document).bind('keydown', keyboardShortcut, e => {
handler();

View File

@@ -1,4 +1,7 @@
import utils from "./services/utils.js";
import macInit from './services/mac_init.js';
macInit.init();
function SetupModel() {
if (syncInProgress) {

View File

@@ -62,7 +62,7 @@
padding: 10px 0 10px 0;
margin: 0 20px 0 10px;
border: 1px solid #ddd;
border-radius: 5px;
border-radius: 7px;
}
#context-menu-container {

View File

@@ -2,6 +2,7 @@ body {
/* Fix for CKEditor block gutter icon "stretching" body and causing scrollbar to appear after pressing enter
on the last line of the editor. */
position: fixed;
width: 100%;
}
#title-container {
@@ -16,6 +17,16 @@ body {
flex-grow: 100;
}
ul.fancytree-container {
/* override specific size from fancytree.css */
font-family: inherit !important;
font-size: inherit !important;
}
.fancytree-title {
margin-left: 7px !important;
}
.fancytree-node:not(.fancytree-loading) .fancytree-expander {
background: none;
width: auto;
@@ -131,8 +142,21 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
/* By default not focused active tree item is not easily visible, this makes it more visible */
span.fancytree-active:not(.fancytree-focused) .fancytree-title {
background-color: #eee !important;
border-color: #ddd !important;
border-radius: 3px;
}
span.fancytree-active.fancytree-focused .fancytree-title {
background-color: #ddd !important;
border-color: #555 !important;
border-color: #bbb !important;
border-radius: 3px;
}
.fancytree-plain span.fancytree-node:hover span.fancytree-title {
background-color: #eee !important;
border-color: #bbb !important;
border-radius: 3px;
}
.ui-autocomplete {
@@ -366,7 +390,7 @@ div.ui-tooltip {
height: 150px;
}
.btn:not(.btn-primary):not(.btn-danger) {
.btn:not(.btn-primary):not(.btn-secondary):not(.btn-danger) {
border-color: #ddd;
background-color: #eee;
}

View File

@@ -7,6 +7,7 @@ const tarImportService = require('../../services/import/tar');
const singleImportService = require('../../services/import/single');
const cls = require('../../services/cls');
const path = require('path');
const noteCacheService = require('../../services/note_cache');
async function importToBranch(req) {
const parentNoteId = req.params.parentNoteId;
@@ -28,24 +29,32 @@ async function importToBranch(req) {
// and may produce unintended consequences
cls.disableEntityEvents();
let note; // typically root of the import - client can show it after finishing the import
if (extension === '.tar') {
return await tarImportService.importTar(file.buffer, parentNote);
note = await tarImportService.importTar(file.buffer, parentNote);
}
else if (extension === '.opml') {
return await opmlImportService.importOpml(file.buffer, parentNote);
note = await opmlImportService.importOpml(file.buffer, parentNote);
}
else if (extension === '.md') {
return await singleImportService.importMarkdown(file, parentNote);
note = await singleImportService.importMarkdown(file, parentNote);
}
else if (extension === '.html' || extension === '.htm') {
return await singleImportService.importHtml(file, parentNote);
note = await singleImportService.importHtml(file, parentNote);
}
else if (extension === '.enex') {
return await enexImportService.importEnex(file, parentNote);
note = await enexImportService.importEnex(file, parentNote);
}
else {
return [400, `Unrecognized extension ${extension}, must be .tar or .opml`];
}
// import has deactivated note events so note cache is not updated
// instead we force it to reload (can be async)
noteCacheService.load();
return note;
}
module.exports = {

View File

@@ -23,7 +23,8 @@ async function loginSync(req) {
const now = new Date();
if (Math.abs(timestamp.getTime() - now.getTime()) > 5000) {
// login token is valid for 5 minutes
if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) {
return [400, { message: 'Auth request time is out of sync' }];
}

View File

@@ -19,7 +19,6 @@ async function anonymize() {
await db.run("UPDATE notes SET title = 'title', content = 'text'");
await db.run("UPDATE note_revisions SET title = 'title', content = 'text'");
await db.run("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL");
await db.run("UPDATE images SET data = NULL");
await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN
('documentSecret', 'encryptedDataKey', 'passwordVerificationHash',
'passwordVerificationSalt', 'passwordDerivedKeySalt')`);

View File

@@ -1 +1 @@
module.exports = { buildDate:"2019-01-05T22:48:11+01:00", buildRevision: "9fca7f09a564c719c834d38d76c3b595c8579b2a" };
module.exports = { buildDate:"2019-01-10T21:31:30+01:00", buildRevision: "0b251530fa0ee61edc8dcc9235033abb73afc614" };

View File

@@ -1,6 +1,7 @@
"use strict";
const repository = require('./repository');
const log = require('./log');
const protectedSessionService = require('./protected_session');
const noteService = require('./notes');
const imagemin = require('imagemin');
@@ -13,7 +14,13 @@ const sanitizeFilename = require('sanitize-filename');
async function saveImage(buffer, originalName, parentNoteId) {
const resizedImage = await resize(buffer);
const optimizedImage = await optimize(resizedImage);
let optimizedImage;
try {
optimizedImage = await optimize(resizedImage);
} catch (e) {
log.error(e);
optimizedImage = resizedImage;
}
const imageFormat = imageType(optimizedImage);

View File

@@ -1,4 +1,5 @@
const sax = require("sax");
const fileType = require('file-type');
const stream = require('stream');
const xml2js = require('xml2js');
const log = require("../log");
@@ -144,7 +145,7 @@ async function importEnex(file, parentNote) {
});
}
else if (currentTag === 'mime') {
resource.mime = text;
resource.mime = text.toLowerCase();
if (text.startsWith("image/")) {
resource.title = "image";
@@ -222,7 +223,26 @@ async function importEnex(file, parentNote) {
const mediaRegex = new RegExp(`<en-media hash="${hash}"[^>]*>`, 'g');
if (resource.mime.startsWith("image/")) {
const fileTypeFromBuffer = fileType(resource.content);
if (fileTypeFromBuffer) {
// If fileType returns something for buffer, then set the mime given
resource.mime = fileTypeFromBuffer.mime;
}
const createResourceNote = async () => {
const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, {
attributes: resource.attributes,
type: 'file',
mime: resource.mime
})).note;
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
noteEntity.content = noteEntity.content.replace(mediaRegex, resourceLink);
}
if (["image/jpeg", "image/png", "image/gif"].includes(resource.mime)) {
try {
const originalName = "image." + resource.mime.substr(6);
const { url } = await imageService.saveImage(resource.content, originalName, noteEntity.noteId);
@@ -236,17 +256,13 @@ async function importEnex(file, parentNote) {
// otherwise image would be removed since no note would include it
note.content += imageLink;
}
} catch (e) {
log.error("error when saving image from ENEX file: " + e);
await createResourceNote();
}
}
else {
const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, {
attributes: resource.attributes,
type: 'file',
mime: resource.mime
})).note;
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
noteEntity.content = noteEntity.content.replace(mediaRegex, resourceLink);
await createResourceNote();
}
}

View File

@@ -33,9 +33,21 @@ async function load() {
archived = await sql.getMap(`SELECT noteId, isInheritable FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name = 'archived'`);
if (protectedSessionService.isProtectedSessionAvailable()) {
await loadProtectedNotes();
}
loaded = true;
}
async function loadProtectedNotes() {
protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`);
for (const noteId in protectedNoteTitles) {
protectedNoteTitles[noteId] = protectedSessionService.decryptNoteTitle(noteId, protectedNoteTitles[noteId]);
}
}
function highlightResults(results, allTokens) {
// we remove < signs because they can cause trouble in matching and overwriting existing highlighted chunks
// which would make the resulting HTML string invalid.
@@ -314,7 +326,16 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED
delete childToParent[note.noteId];
}
else {
noteTitles[note.noteId] = note.title;
if (note.isProtected) {
// we can assume we have protected session since we managed to update
// removing from the maps is important when switching between protected & unprotected
protectedNoteTitles[note.noteId] = note.title;
delete noteTitles[note.noteId];
}
else {
noteTitles[note.noteId] = note.title;
delete protectedNoteTitles[note.noteId];
}
}
}
else if (entityName === 'branches') {
@@ -355,15 +376,9 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED
}
});
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => {
if (!loaded) {
return;
}
protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`);
for (const noteId in protectedNoteTitles) {
protectedNoteTitles[noteId] = protectedSessionService.decryptNoteTitle(noteId, protectedNoteTitles[noteId]);
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => {
if (loaded) {
loadProtectedNotes();
}
});
@@ -372,5 +387,6 @@ sqlInit.dbReady.then(() => utils.stopWatch("Autocomplete load", load));
module.exports = {
findNotes,
getNotePath,
getNoteTitleForPath
getNoteTitleForPath,
load
};

View File

@@ -6,7 +6,7 @@ const sourceIdService = require('./source_id');
const log = require('./log');
async function executeNote(note, originEntity) {
if (!note.isJavaScript()) {
if (!note.isJavaScript() || !note.isContentAvailable) {
return;
}
@@ -80,6 +80,10 @@ function getParams(params) {
}
async function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = []) {
if (!note.isContentAvailable) {
return;
}
if (!note.isJavaScript() && !note.isHtml()) {
return;
}

View File

@@ -1,7 +1,12 @@
<div id="note-detail-search" class="note-detail-component">
<div style="display: flex; align-items: center;">
<strong>Search string: &nbsp; &nbsp;</strong>
<textarea rows="4" cols="50" id="search-string"></textarea>
<textarea rows="4" cols="40" id="search-string"></textarea>
<span>
&nbsp; &nbsp;
<button type="button" class="btn btn-primary" id="note-detail-search-refresh-results-button">Refresh tree</button>
</span>
</div>
<br />