Compare commits

...

32 Commits

Author SHA1 Message Date
azivner
69cbfaae17 release 0.1.2 2017-12-28 21:17:25 -05:00
azivner
aebce8f12b making not focused active tree item more visible 2017-12-28 20:53:31 -05:00
azivner
045ca1f0bf global CTRL-SHIFT-arrows to move in the note tree without losing focus in the note editor 2017-12-28 20:38:57 -05:00
azivner
bf2db6eac7 after loading new note make sure editor is scrolled to the top 2017-12-28 20:13:54 -05:00
azivner
cf84114f91 removed unused methods 2017-12-28 19:58:33 -05:00
azivner
6426157bb3 title in fancytree needs to be escaped for HTML special characters 2017-12-28 19:00:31 -05:00
azivner
332fc16852 fix pasting into non-expanded non-loaded node (UI only) 2017-12-27 22:44:27 -05:00
azivner
da2cd57428 CTRL+ENTER on note in tree pane switches to the editor 2017-12-27 21:16:47 -05:00
azivner
de9bab1181 fixed keyboard controlled clipboard 2017-12-27 21:12:54 -05:00
azivner
136375cf11 refactored detection of whether user is initialized 2017-12-27 18:23:24 -05:00
azivner
eabc7f80b7 release 0.1.1 2017-12-27 17:41:07 -05:00
azivner
6405d6e066 added linux ia32 build 2017-12-27 17:39:41 -05:00
azivner
f6d481a9e2 fix resource path bug 2017-12-27 16:44:15 -05:00
azivner
695f0e5879 release 0.1.0 2017-12-26 22:59:46 -05:00
azivner
ae337e4500 don't open dev tools for search page in electron 2017-12-26 22:24:47 -05:00
azivner
19ffa14f10 hide logout button in electron since it doesn't do anything 2017-12-26 22:19:42 -05:00
azivner
bf3f26fde8 using textarea instead of pre for note source since that's easier to copy to clipboard 2017-12-26 20:54:41 -05:00
azivner
dece400207 release 0.0.11 2017-12-26 19:57:44 -05:00
azivner
baab745462 some visual tweaks 2017-12-26 19:54:43 -05:00
azivner
0d3b3ec7c5 alt+s toggles search instead of just showing it 2017-12-26 19:16:04 -05:00
azivner
7aff20bb0d added show note source 2017-12-26 18:15:29 -05:00
azivner
5acf84aece Calling project "Trilium Notes" in some places as it is more self explanatory 2017-12-26 12:55:40 -05:00
azivner
c58a0df76d added list of wiki pages into README 2017-12-26 11:08:23 -05:00
azivner
20c14a1920 fix collapse tree keyboard shortcut 2017-12-26 11:00:04 -05:00
azivner
04063d8a9c sql console reports successful execution (useful when query doesn't produce any result set like in update/delete) 2017-12-26 10:21:33 -05:00
azivner
dd69e0135b added collapse sub-tree in context menu 2017-12-26 10:00:08 -05:00
azivner
ab6e78f726 keyboard shortcut titles on buttons 2017-12-26 09:44:16 -05:00
azivner
9029d18178 fix go to external link by double clicking 2017-12-26 09:10:54 -05:00
azivner
e9a1791e3d fix unwanted movement with navigation keys 2017-12-25 21:47:32 -05:00
azivner
70e13c8a20 release 0.0.10 2017-12-25 21:06:39 -05:00
azivner
7e1cc729f9 have to add changed files to index before committing 2017-12-25 21:06:35 -05:00
azivner
87e7828440 release scripts fixes 2017-12-25 21:05:08 -05:00
30 changed files with 348 additions and 170 deletions

View File

@@ -1,5 +1,5 @@
# Trilium
Hierarchical note taking application. Picture tells a thousand words:
# Trilium Notes
Trilium Notes is a hierarchical note taking application. Picture tells a thousand words:
![](https://raw.githubusercontent.com/wiki/zadam/trilium/images/screenshot.png)
@@ -8,7 +8,7 @@ Hierarchical note taking application. Picture tells a thousand words:
* Notes can be arranged into arbitrarily deep hierarchy
* Notes can have more than 1 parents - see [cloning](https://github.com/zadam/trilium/wiki/Cloning-notes)
* WYSIWYG (What You See Is What You Get) editing
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation) inspired by IDEs
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation)
* Seamless note versioning
* Can be deployed as web application and / or desktop application with offline access (electron based)
* [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server
@@ -21,7 +21,7 @@ Hierarchical note taking application. Picture tells a thousand words:
## Supported platforms
Desktop (electron) builds are available for Linux and Windows, both for 64 bits.
Desktop (electron-based) 64-bit builds are available for Linux and Windows.
Requirements for web based installation are [outlined here](https://github.com/zadam/trilium/wiki/Installation-as-webapp).
@@ -29,4 +29,15 @@ Currently only recent Chrome and Firefox are supported (tested) browsers. Other
## Documentation
See [wiki](https://github.com/zadam/trilium/wiki/Home) for complete list of available pages.
List of documentation pages:
* [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp)
* [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation)
* [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation)
* [Links](https://github.com/zadam/trilium/wiki/Links)
* [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes)
* [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes)
* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization)
* [Document](https://github.com/zadam/trilium/wiki/Document)
* [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts)
* [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting)

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env bash
echo 'module.exports = { build_date:"'`date --iso-8601=seconds`'", build_revision: "'`git log -1 --format="%H"`'" };' > services/build.js
echo "Deleting dist"
rm -r dist/*
@@ -10,9 +8,15 @@ cp -r ../trilium-node-binaries/sqlite/* node_modules/sqlite3/lib/binding/
cp -r ../trilium-node-binaries/scrypt/* node_modules/scrypt/bin/
./node_modules/.bin/electron-rebuild
./node_modules/.bin/electron-rebuild --arch=ia32
./node_modules/.bin/electron-packager . --out=dist --platform=linux,win32 --overwrite
./node_modules/.bin/electron-packager . --out=dist --platform=linux --arch=ia32 --overwrite
./node_modules/.bin/electron-rebuild --arch=x64
./node_modules/.bin/electron-packager . --out=dist --platform=linux --arch=x64 --overwrite
./node_modules/.bin/electron-packager . --out=dist --platform=win32 --arch=x64 --overwrite
# can't copy this before the packaging because the same file name is used for both linux and windows build
cp ../trilium-node-binaries/scrypt.node ./dist/trilium-win32-x64/resources/app/node_modules/scrypt/build/Release/

View File

@@ -4,8 +4,11 @@ VERSION=`jq -r ".version" package.json`
cd dist
echo "Packaging windows electron distribution..."
7z a trilium-windows-${VERSION}.7z trilium-win32-x64
echo "Packaging linux x64 electron distribution..."
7z a trilium-linux-x64-${VERSION}.7z trilium-linux-x64
echo "Packaging linux electron distribution..."
7z a trilium-linux-${VERSION}.7z trilium-linux-x64
echo "Packaging linux ia32 electron distribution..."
7z a trilium-linux-ia32-${VERSION}.7z trilium-linux-ia32
echo "Packaging windows x64 electron distribution..."
7z a trilium-windows-x64-${VERSION}.7z trilium-win32-x64

View File

@@ -18,35 +18,61 @@ if ! git diff-index --quiet HEAD --; then
exit 1
fi
echo "Releasing Trilium $VERSION"
jq '.version = "'$VERSION'"' package.json|sponge package.json
git add package.json
echo 'module.exports = { build_date:"'`date --iso-8601=seconds`'", build_revision: "'`git log -1 --format="%H"`'" };' > services/build.js
git add services/build.js
TAG=v$VERSION
git commit -m "$VERSION"
echo "Committing package.json version change"
git commit -m "release $VERSION"
git push
echo "Tagging commit with $TAG"
git tag $TAG
git push origin $TAG
echo "Releasing Trilium $VERSION"
bin/build.sh
build
bin/package.sh
package
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
LINUX_BUILD=trilium-linux-$VERSION.7z
WINDOWS_BUILD=trilium-windows-$VERSION.7z
echo "Creating release in GitHub"
github-release release \
--tag $TAG \
--name "$TAG release"
github-release upload \
--tag $TAG \
--name "$LINUX_BUILD" \
--file "dist/$LINUX_BUILD"
echo "Uploading linux x64 build"
github-release upload \
--tag $TAG \
--name "$WINDOWS_BUILD" \
--file "dist/$WINDOWS_BUILD"
--name "$LINUX_X64_BUILD" \
--file "dist/$LINUX_X64_BUILD"
echo "Uploading linux ia32 build"
github-release upload \
--tag $TAG \
--name "$LINUX_IA32_BUILD" \
--file "dist/$LINUX_IA32_BUILD"
echo "Uploading windows x64 build"
github-release upload \
--tag $TAG \
--name "$WINDOWS_X64_BUILD" \
--file "dist/$WINDOWS_X64_BUILD"
echo "Release finished!"

View File

@@ -1,5 +1,5 @@
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('root', 'root', 'root', 0, 0, '2017-12-22T11:41:07.000Z', '2017-12-22T11:41:07.000Z');
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('1Heh2acXfPNt', 'Trilium Demo', '<p>Welcome to Trilium!</p><p>&nbsp;</p><p>This is initial document provided by default Trilium to showcase some of its features and also give you some ideas how you might structure your notes. You can play with it, modify note content and tree structure as you wish.</p><p>&nbsp;</p><p>If you need any help, visit Trilium wesite: <a href="https://github.com/zadam/trilium">https://github.com/zadam/trilium</a></p><p>&nbsp;</p><p>Once you''re finished with experimenting and want to cleanup these pages, you can simply delete them all.</p>', 0, 0, '2017-12-23T00:46:39.304Z', '2017-12-23T04:08:45.445Z');
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('1Heh2acXfPNt', 'Trilium Demo', '<p>Welcome to Trilium Notes!</p><p>&nbsp;</p><p>This is initial document provided by default Trilium to showcase some of its features and also give you some ideas how you might structure your notes. You can play with it, modify note content and tree structure as you wish.</p><p>&nbsp;</p><p>If you need any help, visit Trilium wesite: <a href="https://github.com/zadam/trilium">https://github.com/zadam/trilium</a></p><p>&nbsp;</p><p>Once you''re finished with experimenting and want to cleanup these pages, you can simply delete them all.</p>', 0, 0, '2017-12-23T00:46:39.304Z', '2017-12-23T04:08:45.445Z');
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('3RkyK9LI18dO', 'Journal', '<p>Expand note on the left pane to see content.</p>', 0, 0, '2017-12-23T01:20:04.181Z', '2017-12-23T18:07:55.377Z');
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('L1Ox40M1aEyy', '2016', '<p>No content.</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p>', 0, 0, '2017-12-23T01:20:45.365Z', '2017-12-23T16:40:43.129Z');
INSERT INTO notes (note_id, note_title, note_text, is_protected, is_deleted, date_created, date_modified) VALUES ('HJusZTbBU494', '2017', '<p>No content.</p>', 0, 0, '2017-12-23T01:20:50.709Z', '2017-12-23T16:41:03.119Z');

View File

@@ -21,6 +21,7 @@ function createMainWindow() {
const win = new electron.BrowserWindow({
width: 1200,
height: 900,
title: 'Trilium Notes',
icon: path.join(__dirname, 'public/images/app-icons/png/256x256.png')
});

View File

@@ -1,7 +1,7 @@
{
"name": "trilium",
"description": "Trilium",
"version": "0.0.9",
"description": "Trilium Notes",
"version": "0.1.2",
"scripts": {
"start": "node ./bin/www",
"test-electron": "xo",

View File

@@ -69,7 +69,9 @@ const contextMenu = (function() {
{title: "Copy / clone <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "ui-icon-copy"},
{title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "ui-icon-scissors"},
{title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"},
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"}
{title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"},
{title: "----"},
{title: "Collapse sub-tree <kbd>Alt+-</kbd>", cmd: "collapse-sub-tree", uiIcon: "ui-icon-minus"}
],
beforeOpen: (event, ui) => {
const node = $.ui.fancytree.getNode(ui.target);
@@ -120,6 +122,9 @@ const contextMenu = (function() {
else if (ui.cmd === "delete") {
treeChanges.deleteNode(node);
}
else if (ui.cmd === "collapse-sub-tree") {
noteTree.collapseTree(node);
}
else {
messaging.logError("Unknown command: " + ui.cmd);
}

View File

@@ -13,7 +13,7 @@ const editTreePrefix = (function() {
await dialogEl.dialog({
modal: true,
width: 800
width: 500
});
const currentNode = noteTree.getCurrentNode();

View File

@@ -29,7 +29,7 @@ const noteHistory = (function() {
for (const item of historyItems) {
const dateModified = parseDate(item.date_modified_from);
$("#note-history-list").append($('<option>', {
listEl.append($('<option>', {
value: item.note_history_id,
text: formatDateTime(dateModified)
}));
@@ -42,6 +42,9 @@ const noteHistory = (function() {
listEl.val(noteHistoryId).trigger('change');
}
else {
titleEl.text("No history for this note yet...");
}
}
$(document).bind('keydown', 'alt+h', e => {

View File

@@ -0,0 +1,57 @@
"use strict";
const noteSource = (function() {
const dialogEl = $("#note-source-dialog");
const noteSourceEl = $("#note-source");
function showDialog() {
glob.activeDialog = dialogEl;
dialogEl.dialog({
modal: true,
width: 800,
height: 500
});
const noteText = noteEditor.getCurrentNote().detail.note_text;
noteSourceEl.text(formatHtml(noteText));
}
function formatHtml(str) {
const div = document.createElement('div');
div.innerHTML = str.trim();
return formatNode(div, 0).innerHTML.trim();
}
function formatNode(node, level) {
const indentBefore = new Array(level++ + 1).join(' ');
const indentAfter = new Array(level - 1).join(' ');
let textNode;
for (let i = 0; i < node.children.length; i++) {
textNode = document.createTextNode('\n' + indentBefore);
node.insertBefore(textNode, node.children[i]);
formatNode(node.children[i], level);
if (node.lastElementChild === node.children[i]) {
textNode = document.createTextNode('\n' + indentAfter);
node.appendChild(textNode);
}
}
return node;
}
$(document).bind('keydown', 'ctrl+u', e => {
showDialog();
e.preventDefault();
});
return {
showDialog
};
})();

View File

@@ -28,6 +28,9 @@ const sqlConsole = (function() {
showError(result.error);
return;
}
else {
showMessage("Query was executed successfully.");
}
const rows = result.rows;

View File

@@ -49,7 +49,7 @@ $(document).bind('keydown', 'ctrl+f', () => {
const searchInPage = require('electron-in-page-search').default;
const remote = require('electron').remote;
const inPageSearch = searchInPage(remote.getCurrentWebContents(), { openDevToolsOfSearchWindow: true });
const inPageSearch = searchInPage(remote.getCurrentWebContents());
inPageSearch.openSearchWindow();
@@ -57,6 +57,42 @@ $(document).bind('keydown', 'ctrl+f', () => {
}
});
$(document).bind('keydown', "ctrl+shift+left", () => {
const node = noteTree.getCurrentNode();
node.navigate($.ui.keyCode.LEFT, true);
$("#note-detail").focus();
return false;
});
$(document).bind('keydown', "ctrl+shift+right", () => {
const node = noteTree.getCurrentNode();
node.navigate($.ui.keyCode.RIGHT, true);
$("#note-detail").focus();
return false;
});
$(document).bind('keydown', "ctrl+shift+up", () => {
const node = noteTree.getCurrentNode();
node.navigate($.ui.keyCode.UP, true);
$("#note-detail").focus();
return false;
});
$(document).bind('keydown', "ctrl+shift+down", () => {
const node = noteTree.getCurrentNode();
node.navigate($.ui.keyCode.DOWN, true);
$("#note-detail").focus();
return false;
});
$(window).on('beforeunload', () => {
// this makes sure that when user e.g. reloads the page or navigates away from the page, the note's content is saved
// this sends the request asynchronously and doesn't wait for result
@@ -165,3 +201,5 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
return false;
};
$("#logout-button").toggle(!isElectron());

View File

@@ -42,18 +42,20 @@ const link = (function() {
e.preventDefault();
const linkEl = $(e.target);
const notePath = linkEl.attr("note-path") ? linkEl.attr("note-path") : getNotePathFromLink(linkEl.attr('href'));
const address = linkEl.attr("note-path") ? linkEl.attr("note-path") : linkEl.attr('href');
if (!notePath) {
if (!address) {
return;
}
if (notePath.startsWith('http')) {
window.open(notePath, '_blank');
if (address.startsWith('http')) {
window.open(address, '_blank');
return;
}
const notePath = getNotePathFromLink(address);
noteTree.activateNode(notePath);
// this is quite ugly hack, but it seems like we can't close the tooltip otherwise
@@ -84,8 +86,8 @@ const link = (function() {
// when click on link popup, in case of internal link, just go the the referenced note instead of default behavior
// of opening the link in new window/tab
$(document).on('click', "a[action='note']", goToLink);
$(document).on('click', 'div.popover-content a, div.ui-tooltip-content', goToLink);
$(document).on('dblclick', '#note-detail a, div.ui-tooltip-content', goToLink);
$(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink);
$(document).on('dblclick', '#note-detail a', goToLink);
return {
getNodePathFromLabel,

View File

@@ -128,6 +128,9 @@ const noteEditor = (function() {
setNoteBackgroundIfProtected(currentNote);
noteTree.setNoteTreeBackgroundBasedOnProtectedStatus(noteId);
// after loading new note make sure editor is scrolled to the top
noteDetailWrapperEl.scrollTop(0);
showAppIfHidden();
}

View File

@@ -4,6 +4,7 @@ const noteTree = (function() {
const treeEl = $("#tree");
const parentListEl = $("#parent-list");
const parentListListEl = $("#parent-list-list");
const noteDetailEl = $("#note-detail");
let startNotePath = null;
let notesTreeMap = {};
@@ -59,23 +60,6 @@ const noteTree = (function() {
return treeUtils.getNotePath(node);
}
function getCurrentNoteId() {
const node = getCurrentNode();
return node ? node.data.note_id : null;
}
function getCurrentClones() {
const noteId = getCurrentNoteId();
if (noteId) {
return getNodesByNoteId(noteId);
}
else {
return [];
}
}
function getNodesByNoteTreeId(noteTreeId) {
assertArguments(noteTreeId);
@@ -185,13 +169,15 @@ const noteTree = (function() {
const noteTreeId = getNoteTreeId(parentNoteId, noteId);
const noteTree = notesTreeMap[noteTreeId];
const title = (noteTree.prefix ? (noteTree.prefix + " - ") : "") + noteIdToTitle[noteTree.note_id];
const node = {
note_id: noteTree.note_id,
parent_note_id: noteTree.parent_note_id,
note_tree_id: noteTree.note_tree_id,
is_protected: noteTree.is_protected,
prefix: noteTree.prefix,
title: (noteTree.prefix ? (noteTree.prefix + " - ") : "") + noteIdToTitle[noteTree.note_id],
title: escapeHtml(title),
extraClasses: getExtraClasses(noteTree),
refKey: noteTree.note_id,
expanded: noteTree.is_expanded
@@ -429,20 +415,55 @@ const noteTree = (function() {
"f2": node => {
editTreePrefix.showDialog(node);
},
"alt+-": node => {
collapseTree(node);
},
"ctrl+c": node => {
contextMenu.copy(node);
showMessage("Note copied into clipboard.");
return false;
},
"ctrl+x": node => {
contextMenu.cut(node);
showMessage("Note cut into clipboard.");
return false;
},
"ctrl+v": node => {
contextMenu.pasteInto(node);
showMessage("Note pasted from clipboard into current note.");
return false;
},
"ctrl+return": node => {
noteDetailEl.focus();
},
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
// after opening context menu, standard shortcuts don't work, but they are detected here
// so we essentially takeover the standard handling with our implementation.
"left": node => {
node.navigate($.ui.keyCode.LEFT, true);
return false;
},
"right": node => {
node.navigate($.ui.keyCode.RIGHT, true);
return false;
},
"up": node => {
node.navigate($.ui.keyCode.UP, true);
return false;
},
"down": node => {
node.navigate($.ui.keyCode.DOWN, true);
return false;
}
};
@@ -502,57 +523,6 @@ const noteTree = (function() {
mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
},
dnd: dragAndDropSetup,
keydown: (event, data) => {
const node = data.node;
// Eat keyboard events, when a menu is open
if ($(".contextMenu:visible").length > 0)
return false;
switch (event.which) {
// Open context menu on [Space] key (simulate right click)
case 32: // [Space]
$(node.span).trigger("mousedown", {
preventDefault: true,
button: 2
})
.trigger("mouseup", {
preventDefault: true,
pageX: node.span.offsetLeft,
pageY: node.span.offsetTop,
button: 2
});
return false;
// Handle Ctrl-C, -X and -V
case 67:
if (event.ctrlKey) { // Ctrl-C
contextMenu.copy(node);
showMessage("Note copied into clipboard.");
return false;
}
break;
case 88:
if (event.ctrlKey) { // Ctrl-X
contextMenu.cut(node);
showMessage("Note cut into clipboard.");
return false;
}
break;
case 86:
if (event.ctrlKey) { // Ctrl-V
contextMenu.pasteInto(node);
showMessage("Note pasted from clipboard into current note.");
return false;
}
break;
}
},
lazyLoad: function(event, data){
const node = data.node.data;
@@ -595,13 +565,17 @@ const noteTree = (function() {
$(() => loadTree().then(noteTree => initFancyTree(noteTree)));
function collapseTree() {
treeEl.fancytree("getRootNode").visit(node => {
node.setExpanded(false);
});
function collapseTree(node = null) {
if (!node) {
node = treeEl.fancytree("getRootNode");
}
node.setExpanded(false);
node.visit(node => node.setExpanded(false));
}
$(document).bind('keydown', 'alt+c', collapseTree);
$(document).bind('keydown', 'alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
function scrollToCurrentNote() {
const node = getCurrentNode();

View File

@@ -8,14 +8,10 @@ const searchTree = (function() {
resetSearchButton.click(resetSearch);
function showSearch() {
searchBoxEl.show();
searchInputEl.focus();
}
function toggleSearch() {
if (searchBoxEl.is(":hidden")) {
showSearch();
searchBoxEl.show();
searchInputEl.focus();
}
else {
resetSearch();
@@ -52,7 +48,11 @@ const searchTree = (function() {
}
}).focus();
$(document).bind('keydown', 'alt+s', showSearch);
$(document).bind('keydown', 'alt+s', e => {
toggleSearch();
e.preventDefault();
});
return {
toggleSearch

View File

@@ -29,10 +29,12 @@ const treeChanges = (function() {
await server.put('notes/' + node.data.note_tree_id + '/move-to/' + toNode.data.note_id);
changeNode(node, node => {
node.moveTo(toNode);
// first expand which will force lazy load and only then move the node
// if this is not expanded before moving, then lazy load won't happen because it already contains node
toNode.setExpanded(true);
node.moveTo(toNode);
toNode.folder = true;
toNode.renderTitle();
});
@@ -52,7 +54,7 @@ const treeChanges = (function() {
}
async function deleteNode(node) {
if (!confirm('Are you sure you want to delete note "' + node.title + '"?')) {
if (!confirm('Are you sure you want to delete note "' + node.title + '" and all its sub-notes?')) {
return;
}
@@ -83,6 +85,8 @@ const treeChanges = (function() {
}
noteTree.reload();
showMessage("Note has been deleted.");
}
async function moveNodeUpInHierarchy(node) {

View File

@@ -37,7 +37,7 @@ const treeUtils = (function() {
const title = (prefix ? (prefix + " - ") : "") + noteTitle;
node.setTitle(title);
node.setTitle(escapeHtml(title));
}
return {

View File

@@ -94,3 +94,7 @@ function isTopLevelNode(node) {
function isRootNode(node) {
return node.key === "root_1";
}
function escapeHtml(str) {
return $('<div/>').text(str).html();
}

View File

@@ -74,6 +74,12 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
font-weight: bold;
}
/* 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: #ddd !important;
border-color: #555 !important;
}
.ui-autocomplete {
max-height: 300px;
overflow-y: auto;
@@ -168,8 +174,7 @@ div.ui-tooltip {
}
/* Allow to use <kbd> elements inside the title to define shortcut hints. */
.ui-menu kbd {
margin-left: 30px;
.ui-menu kbd, button kbd {
float: right;
color: black;
border: none;
@@ -177,8 +182,19 @@ div.ui-tooltip {
box-shadow: none;
}
.ui-menu kbd {
margin-left: 30px;
}
#note-id-display {
color: lightgrey;
margin-left: 10px;
}
#note-source {
height: 98%;
width: 100%;
overflow: scroll;
}
#loader-wrapper{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1000;background-color:#fff;opacity:1;transition:opacity 2s ease}

View File

@@ -67,7 +67,7 @@ router.delete('/:noteTreeId', auth.checkApiAuth, async (req, res, next) => {
router.get('/', auth.checkApiAuth, async (req, res, next) => {
const search = '%' + req.query.search + '%';
const result = await sql.getAll("SELECT note_id FROM notes WHERE note_title liKE ? OR note_text LIKE ?", [search, search]);
const result = await sql.getAll("SELECT note_id FROM notes WHERE note_title LIKE ? OR note_text LIKE ?", [search, search]);
const noteIdList = [];

View File

@@ -3,12 +3,9 @@
const migration = require('./migration');
const sql = require('./sql');
const utils = require('./utils');
const options = require('./options');
async function checkAuth(req, res, next) {
const username = await options.getOption('username');
if (!username) {
if (!await sql.isUserInitialized()) {
res.redirect("setup");
}
else if (!req.session.loggedIn && !utils.isElectron()) {
@@ -53,9 +50,7 @@ async function checkApiAuthForMigrationPage(req, res, next) {
}
async function checkAppNotInitialized(req, res, next) {
const username = await options.getOption('username');
if (username) {
if (await sql.isUserInitialized()) {
res.status(400).send("App already initialized.");
}
else {

View File

@@ -1 +1 @@
module.exports = { build_date:"2017-12-25T10:07:19-05:00", build_revision: "583123ab0a5df7dca5b19f2c67ab8ef853e72673" };
module.exports = { build_date:"2017-12-28T21:17:25-05:00", build_revision: "aebce8f12b87e7c3d5dbd23e75918f3d01a4cc64" };

View File

@@ -4,8 +4,9 @@ const ini = require('ini');
const fs = require('fs');
const dataDir = require('./data_dir');
const path = require('path');
const resource_dir = require('./resource_dir');
const configSampleFilePath = path.resolve(__dirname, "..", "config-sample.ini");
const configSampleFilePath = path.resolve(resource_dir.RESOURCE_DIR, "config-sample.ini");
const configFilePath = dataDir.TRILIUM_DATA_DIR + '/config.ini';

View File

@@ -3,14 +3,7 @@ const sql = require('./sql');
const options = require('./options');
const fs = require('fs-extra');
const log = require('./log');
const path = require('path');
const MIGRATIONS_DIR = path.resolve(__dirname, "..", "migrations");
if (!fs.existsSync(MIGRATIONS_DIR)) {
log.error("Could not find migration directory: " + MIGRATIONS_DIR);
process.exit(1);
}
const resource_dir = require('./resource_dir');
async function migrate() {
const migrations = [];
@@ -20,7 +13,7 @@ async function migrate() {
const currentDbVersion = parseInt(await options.getOption('db_version'));
fs.readdirSync(MIGRATIONS_DIR).forEach(file => {
fs.readdirSync(resource_dir.MIGRATIONS_DIR).forEach(file => {
const match = file.match(/([0-9]{4})__([a-zA-Z0-9_ ]+)\.(sql|js)/);
if (match) {
@@ -53,7 +46,7 @@ async function migrate() {
await sql.doInTransaction(async () => {
if (mig.type === 'sql') {
const migrationSql = fs.readFileSync(MIGRATIONS_DIR + "/" + mig.file).toString('utf8');
const migrationSql = fs.readFileSync(resource_dir.MIGRATIONS_DIR + "/" + mig.file).toString('utf8');
console.log("Migration with SQL script: " + migrationSql);
@@ -62,7 +55,7 @@ async function migrate() {
else if (mig.type === 'js') {
console.log("Migration with JS module");
const migrationModule = require("../" + MIGRATIONS_DIR + "/" + mig.file);
const migrationModule = require("../" + resource_dir.MIGRATIONS_DIR + "/" + mig.file);
await migrationModule(db);
}
else {

25
services/resource_dir.js Normal file
View File

@@ -0,0 +1,25 @@
const log = require('./log');
const path = require('path');
const fs = require('fs');
const RESOURCE_DIR = path.resolve(__dirname, "..");
const MIGRATIONS_DIR = path.resolve(RESOURCE_DIR, "migrations");
if (!fs.existsSync(MIGRATIONS_DIR)) {
log.error("Could not find migration directory: " + MIGRATIONS_DIR);
process.exit(1);
}
const DB_INIT_DIR = path.resolve(RESOURCE_DIR, "db");
if (!fs.existsSync(DB_INIT_DIR)) {
log.error("Could not find DB initialization directory: " + DB_INIT_DIR);
process.exit(1);
}
module.exports = {
RESOURCE_DIR,
MIGRATIONS_DIR,
DB_INIT_DIR
};

View File

@@ -4,8 +4,8 @@ const log = require('./log');
const dataDir = require('./data_dir');
const fs = require('fs');
const sqlite = require('sqlite');
const utils = require('./utils');
const app_info = require('./app_info');
const resource_dir = require('./resource_dir');
async function createConnection() {
return await sqlite.open(dataDir.DOCUMENT_PATH, {Promise});
@@ -28,9 +28,9 @@ const dbReady = new Promise((resolve, reject) => {
if (tableResults.length !== 1) {
log.info("Connected to db, but schema doesn't exist. Initializing schema ...");
const schema = fs.readFileSync('db/schema.sql', 'UTF-8');
const notesSql = fs.readFileSync('db/main_notes.sql', 'UTF-8');
const notesTreeSql = fs.readFileSync('db/main_notes_tree.sql', 'UTF-8');
const schema = fs.readFileSync(resource_dir.DB_INIT_DIR + '/schema.sql', 'UTF-8');
const notesSql = fs.readFileSync(resource_dir.DB_INIT_DIR + '/main_notes.sql', 'UTF-8');
const notesTreeSql = fs.readFileSync(resource_dir.DB_INIT_DIR + '/main_notes_tree.sql', 'UTF-8');
await doInTransaction(async () => {
await executeScript(schema);
@@ -49,9 +49,7 @@ const dbReady = new Promise((resolve, reject) => {
// the database
}
else {
const username = await getFirstValue("SELECT opt_value FROM options WHERE opt_name = 'username'");
if (!username) {
if (!await isUserInitialized()) {
log.info("Login/password not initialized. DB not ready.");
return;
@@ -235,8 +233,15 @@ async function isDbUpToDate() {
return upToDate;
}
async function isUserInitialized() {
const username = await getFirstValue("SELECT opt_value FROM options WHERE opt_name = 'username'");
return !!username;
}
module.exports = {
dbReady,
isUserInitialized,
insert,
replace,
getFirstValue,

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Trilium</title>
<title>Trilium Notes</title>
</head>
<body>
<div id="loader-wrapper"><div id="loader"></div></div>
@@ -12,12 +12,12 @@
<div id="header-title">
<img src="images/app-icons/png/24x24.png">
Trilium
Trilium Notes
</div>
<div style="flex-grow: 100;">
<button class="btn btn-xs" onclick="jumpToNote.showDialog();">Jump to note</button>
<button class="btn btn-xs" onclick="recentNotes.showDialog();">Recent notes</button>
<button class="btn btn-xs" onclick="jumpToNote.showDialog();" title="CTRL+J">Jump to note</button>
<button class="btn btn-xs" onclick="recentNotes.showDialog();" title="CTRL+E">Recent notes</button>
<button class="btn btn-xs" onclick="recentChanges.showDialog();">Recent changes</button>
<button class="btn btn-xs" onclick="eventLog.showDialog();">Event log</button>
</div>
@@ -29,7 +29,7 @@
<button class="btn btn-xs" onclick="settings.showDialog();">Settings</button>
<form action="logout" method="POST" style="display: inline;">
<form action="logout" id="logout-button" method="POST" style="display: inline;">
<input type="submit" class="btn btn-xs" value="Logout">
</form>
</div>
@@ -45,7 +45,7 @@
<img src="images/icons/list.png" alt="Collapse note tree"/>
</a>
<a onclick="noteTree.scrollToCurrentNote()" title="Scroll to current note" class="icon-action">
<a onclick="noteTree.scrollToCurrentNote()" title="Scroll to current note. Shortcut CTRL+." class="icon-action">
<img src="images/icons/crosshair.png" alt="Scroll to current note"/>
</a>
@@ -97,7 +97,7 @@
<span id="note-id-display" title="Note ID"></span>
<button class="btn btn-xs" style="margin: 10px;" onclick="noteHistory.showCurrentNoteHistory();">Note history</button>
<button class="btn btn-xs" title="ALT+H" style="margin: 10px;" onclick="noteHistory.showCurrentNoteHistory();">Note history</button>
</div>
</div>
@@ -113,13 +113,13 @@
<br/><br/>
<p>
<button class="btn btn-sm" id="recent-notes-jump-to">Jump to (enter)</button>
<button class="btn btn-sm" id="recent-notes-jump-to">Jump to <kbd>enter</kbd></button>
&nbsp;
<button class="btn btn-sm" id="recent-notes-add-link">Add link (l)</button>
<button class="btn btn-sm" id="recent-notes-add-link">Add link <kbd>l</kbd></button>
<button class="btn btn-sm" id="recent-notes-add-current-as-child">Add current as child (c)</button>
<button class="btn btn-sm" id="recent-notes-add-current-as-child">Add current as child <kbd>c</kbd></button>
<button class="btn btn-sm" id="recent-notes-add-recent-as-child">Add recent as child (r)</button>
<button class="btn btn-sm" id="recent-notes-add-recent-as-child">Add recent as child <kbd>r</kbd></button>
</p>
</div>
@@ -165,18 +165,18 @@
<input id="jump-to-note-autocomplete" style="width: 100%;">
</div>
<button name="action" value="jump" class="btn btn-sm">Jump</button>
<button name="action" value="jump" class="btn btn-sm">Jump <kbd>enter</kbd></button>
</form>
</div>
<div id="protected-session-password-dialog" title="Protected session" style="display: none;">
<form id="protected-session-password-form">
<div class="form-group">
<label for="protected-session-password">To proceed with requested action you need to enter protected session by entering password:</label>
<label for="protected-session-password">To proceed with requested action you need to start protected session by entering password:</label>
<input id="protected-session-password" class="form-control" type="password">
</div>
<button class="btn btn-sm">Enter protected session</button>
<button class="btn btn-sm">Start protected session <kbd>enter</kbd></button>
</form>
</div>
@@ -318,7 +318,7 @@
<div id="sql-console-dialog" title="SQL console" style="display: none; padding: 20px;">
<textarea style="width: 100%; height: 100px" id="sql-console-query"></textarea>
<button class="btn btn-danger" id="sql-console-execute">Execute</button>
<button class="btn btn-danger" id="sql-console-execute">Execute <kbd>CTRL+ENTER</kbd></button>
<table id="sql-console-results" class="table table-striped" style="overflow: scroll; width: 100%;">
<thead></thead>
@@ -326,6 +326,10 @@
</table>
</div>
<div id="note-source-dialog" title="Note source" style="display: none; padding: 20px;">
<textarea id="note-source" readonly="readonly"></textarea>
</div>
<div id="tooltip" style="display: none;"></div>
<script type="text/javascript">
@@ -390,6 +394,7 @@
<script src="javascripts/dialogs/event_log.js"></script>
<script src="javascripts/dialogs/edit_tree_prefix.js"></script>
<script src="javascripts/dialogs/sql_console.js"></script>
<script src="javascripts/dialogs/note_source.js"></script>
<script src="javascripts/link.js"></script>
<script src="javascripts/sync.js"></script>

View File

@@ -6,7 +6,7 @@
</head>
<body>
<div style="width: 500px; margin: auto;">
<h1>Trilium setup</h1>
<h1>Trilium Notes setup</h1>
<div class="alert alert-warning" id="alert" style="display: none;">
</div>