mirror of
https://github.com/zadam/trilium.git
synced 2025-10-30 18:05:55 +01:00
Compare commits
29 Commits
v0.49.0-be
...
v0.49.2-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a85fe92aa | ||
|
|
feffd57f24 | ||
|
|
faf81ae056 | ||
|
|
003fec4b11 | ||
|
|
5ecb603e86 | ||
|
|
1fed71a92e | ||
|
|
dad82ea4e8 | ||
|
|
067251861d | ||
|
|
6bc8773d5f | ||
|
|
a910034c96 | ||
|
|
257cc66f62 | ||
|
|
00f24bdb63 | ||
|
|
fada3fe623 | ||
|
|
d97e454463 | ||
|
|
3128a7d62f | ||
|
|
b8fe9a41db | ||
|
|
8366a94bde | ||
|
|
f56123b864 | ||
|
|
ae951bfe23 | ||
|
|
ad8d35efe9 | ||
|
|
0217b1c85d | ||
|
|
c0aa14f586 | ||
|
|
b54cfab4ff | ||
|
|
a08985e7a6 | ||
|
|
a789025025 | ||
|
|
3f307b117e | ||
|
|
a232035d47 | ||
|
|
9d38e9342d | ||
|
|
265401775b |
5734
libraries/codemirror/keymap/vim.js
vendored
Normal file
5734
libraries/codemirror/keymap/vim.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.49.0-beta",
|
"version": "0.49.2-beta",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -1114,7 +1114,7 @@ class Note extends AbstractEntity {
|
|||||||
|
|
||||||
const branch = this.becca.getNote(parentNoteId).getParentBranches()[0];
|
const branch = this.becca.getNote(parentNoteId).getParentBranches()[0];
|
||||||
|
|
||||||
return cloningService.cloneNoteToParent(this.noteId, branch.branchId);
|
return cloningService.cloneNoteToBranch(this.noteId, branch.branchId);
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypt() {
|
decrypt() {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ async function cloneNotesTo(notePath) {
|
|||||||
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
|
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
|
||||||
|
|
||||||
for (const cloneNoteId of clonedNoteIds) {
|
for (const cloneNoteId of clonedNoteIds) {
|
||||||
await branchService.cloneNoteTo(cloneNoteId, targetBranchId, $clonePrefix.val());
|
await branchService.cloneNoteToBranch(cloneNoteId, targetBranchId, $clonePrefix.val());
|
||||||
|
|
||||||
const clonedNote = await froca.getNote(cloneNoteId);
|
const clonedNote = await froca.getNote(cloneNoteId);
|
||||||
const targetNote = await froca.getBranch(targetBranchId).getNote();
|
const targetNote = await froca.getBranch(targetBranchId).getNote();
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import mimeTypesService from "../../services/mime_types.js";
|
import mimeTypesService from "../../services/mime_types.js";
|
||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
|
import server from "../../services/server.js";
|
||||||
|
import toastService from "../../services/toast.js";
|
||||||
|
import utils from "../../services/utils.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
|
<h4>Use vim keybindings in CodeNotes (no ex mode)</h4>
|
||||||
|
<div class="custom-control custom-checkbox">
|
||||||
|
<input type="checkbox" class="custom-control-input" id="vim-keymap-enabled">
|
||||||
|
<label class="custom-control-label" for="vim-keymap-enabled">Enable Vim Keybindings</label>
|
||||||
|
</div>
|
||||||
<h4>Available MIME types in the dropdown</h4>
|
<h4>Available MIME types in the dropdown</h4>
|
||||||
|
|
||||||
<ul id="options-mime-types" style="max-height: 500px; overflow: auto; list-style-type: none;"></ul>`;
|
<ul id="options-mime-types" style="max-height: 500px; overflow: auto; list-style-type: none;"></ul>`;
|
||||||
@@ -10,12 +18,18 @@ export default class CodeNotesOptions {
|
|||||||
constructor() {
|
constructor() {
|
||||||
$("#options-code-notes").html(TPL);
|
$("#options-code-notes").html(TPL);
|
||||||
|
|
||||||
|
this.$vimKeymapEnabled = $("#vim-keymap-enabled");
|
||||||
|
this.$vimKeymapEnabled.on('change', () => {
|
||||||
|
const opts = { 'vimKeymapEnabled': this.$vimKeymapEnabled.is(":checked") ? "true" : "false" };
|
||||||
|
server.put('options', opts).then(() => toastService.showMessage("Options change have been saved."));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
this.$mimeTypes = $("#options-mime-types");
|
this.$mimeTypes = $("#options-mime-types");
|
||||||
}
|
}
|
||||||
|
|
||||||
async optionsLoaded() {
|
async optionsLoaded(options) {
|
||||||
this.$mimeTypes.empty();
|
this.$mimeTypes.empty();
|
||||||
|
this.$vimKeymapEnabled.prop("checked", options['vimKeymapEnabled'] === 'true');
|
||||||
let idCtr = 1;
|
let idCtr = 1;
|
||||||
|
|
||||||
for (const mimeType of await mimeTypesService.getMimeTypes()) {
|
for (const mimeType of await mimeTypesService.getMimeTypes()) {
|
||||||
@@ -45,4 +59,4 @@ export default class CodeNotesOptions {
|
|||||||
|
|
||||||
mimeTypesService.loadMimeTypes();
|
mimeTypesService.loadMimeTypes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,8 +196,18 @@ ws.subscribeToMessages(async message => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function cloneNoteTo(childNoteId, parentBranchId, prefix) {
|
async function cloneNoteToBranch(childNoteId, parentBranchId, prefix) {
|
||||||
const resp = await server.put(`notes/${childNoteId}/clone-to/${parentBranchId}`, {
|
const resp = await server.put(`notes/${childNoteId}/clone-to-branch/${parentBranchId}`, {
|
||||||
|
prefix: prefix
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!resp.success) {
|
||||||
|
alert(resp.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cloneNoteToNote(childNoteId, parentNoteId, prefix) {
|
||||||
|
const resp = await server.put(`notes/${childNoteId}/clone-to-note/${parentNoteId}`, {
|
||||||
prefix: prefix
|
prefix: prefix
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,5 +232,6 @@ export default {
|
|||||||
deleteNotes,
|
deleteNotes,
|
||||||
moveNodeUpInHierarchy,
|
moveNodeUpInHierarchy,
|
||||||
cloneNoteAfter,
|
cloneNoteAfter,
|
||||||
cloneNoteTo
|
cloneNoteToBranch,
|
||||||
|
cloneNoteToNote,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function pasteInto(parentBranchId) {
|
|||||||
for (const clipboardBranch of clipboardBranches) {
|
for (const clipboardBranch of clipboardBranches) {
|
||||||
const clipboardNote = await clipboardBranch.getNote();
|
const clipboardNote = await clipboardBranch.getNote();
|
||||||
|
|
||||||
await branchService.cloneNoteTo(clipboardNote.noteId, parentBranchId);
|
await branchService.cloneNoteToBranch(clipboardNote.noteId, parentBranchId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy will keep clipboardBranchIds and clipboardMode so it's possible to paste into multiple places
|
// copy will keep clipboardBranchIds and clipboardMode so it's possible to paste into multiple places
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const CODE_MIRROR = {
|
|||||||
"libraries/codemirror/addon/edit/matchtags.js",
|
"libraries/codemirror/addon/edit/matchtags.js",
|
||||||
"libraries/codemirror/addon/search/match-highlighter.js",
|
"libraries/codemirror/addon/search/match-highlighter.js",
|
||||||
"libraries/codemirror/mode/meta.js",
|
"libraries/codemirror/mode/meta.js",
|
||||||
|
"libraries/codemirror/keymap/vim.js",
|
||||||
"libraries/codemirror/addon/lint/lint.js",
|
"libraries/codemirror/addon/lint/lint.js",
|
||||||
"libraries/codemirror/addon/lint/eslint.js"
|
"libraries/codemirror/addon/lint/eslint.js"
|
||||||
],
|
],
|
||||||
|
|||||||
21
src/public/app/share.js
Normal file
21
src/public/app/share.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Fetch note with given ID from backend
|
||||||
|
*
|
||||||
|
* @param noteId of the given note to be fetched. If falsy, fetches current note.
|
||||||
|
*/
|
||||||
|
async function fetchNote(noteId = null) {
|
||||||
|
if (!noteId) {
|
||||||
|
noteId = document.body.getAttribute("data-note-id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await fetch(`api/notes/${noteId}`);
|
||||||
|
|
||||||
|
return await resp.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const toggleMenuButton = document.getElementById('toggleMenuButton');
|
||||||
|
const layout = document.getElementById('layout');
|
||||||
|
|
||||||
|
toggleMenuButton.addEventListener('click', () => layout.classList.toggle('showMenu'));
|
||||||
|
}, false);
|
||||||
@@ -226,6 +226,8 @@ const ATTR_HELP = {
|
|||||||
"renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered',
|
"renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered',
|
||||||
"widget": "target of this relation will be executed and rendered as a widget in the sidebar",
|
"widget": "target of this relation will be executed and rendered as a widget in the sidebar",
|
||||||
"shareCss": "CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree' and 'shareOmitDefaultCss' as well.",
|
"shareCss": "CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree' and 'shareOmitDefaultCss' as well.",
|
||||||
|
"shareJs": "JavaScript note which will be injected into the share page. JS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree'.",
|
||||||
|
"shareFavicon": "Favicon note to be set in the shared page. Typically you want to set it to share root and make it inheritable. Favicon note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree'.",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -103,17 +103,19 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
|
|||||||
async refreshWithNote(note) {
|
async refreshWithNote(note) {
|
||||||
this.clearItems();
|
this.clearItems();
|
||||||
|
|
||||||
const targetRelationCount = note.getTargetRelations().length;
|
// can't use froca since that would count only relations from loaded notes
|
||||||
if (targetRelationCount === 0) {
|
const resp = await server.get(`notes/${this.noteId}/backlink-count`);
|
||||||
|
|
||||||
|
if (!resp || !resp.count) {
|
||||||
this.$ticker.hide();
|
this.$ticker.hide();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.$ticker.show();
|
this.$ticker.show();
|
||||||
this.$count.text(
|
this.$count.text(
|
||||||
`${targetRelationCount} backlink`
|
`${resp.count} backlink`
|
||||||
+ (targetRelationCount === 1 ? '' : 's')
|
+ (resp.count === 1 ? '' : 's')
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearItems() {
|
clearItems() {
|
||||||
@@ -136,18 +138,22 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
|
|||||||
await froca.getNotes(backlinks.map(bl => bl.noteId)); // prefetch all
|
await froca.getNotes(backlinks.map(bl => bl.noteId)); // prefetch all
|
||||||
|
|
||||||
for (const backlink of backlinks) {
|
for (const backlink of backlinks) {
|
||||||
this.$items.append(await linkService.createNoteLink(backlink.noteId, {
|
const $item = $("<div>");
|
||||||
|
|
||||||
|
$item.append(await linkService.createNoteLink(backlink.noteId, {
|
||||||
showNoteIcon: true,
|
showNoteIcon: true,
|
||||||
showNotePath: true,
|
showNotePath: true,
|
||||||
showTooltip: false
|
showTooltip: false
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (backlink.relationName) {
|
if (backlink.relationName) {
|
||||||
this.$items.append($("<p>").text("relation: " + backlink.relationName));
|
$item.append($("<p>").text("relation: " + backlink.relationName));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.$items.append(...backlink.excerpts);
|
$item.append(...backlink.excerpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$items.append($item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const TPL = `
|
|||||||
|
|
||||||
export default class SharedInfoWidget extends NoteContextAwareWidget {
|
export default class SharedInfoWidget extends NoteContextAwareWidget {
|
||||||
isEnabled() {
|
isEnabled() {
|
||||||
return super.isEnabled() && this.note.hasAncestor('share');
|
return super.isEnabled() && this.noteId !== 'share' && this.note.hasAncestor('share');
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import server from "../services/server.js";
|
|||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
export default class SharedSwitchWidget extends SwitchWidget {
|
export default class SharedSwitchWidget extends SwitchWidget {
|
||||||
|
isEnabled() {
|
||||||
|
return super.isEnabled() && this.noteId !== 'root' && this.noteId !== 'share';
|
||||||
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
super.doRender();
|
super.doRender();
|
||||||
|
|
||||||
@@ -18,7 +22,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switchOn() {
|
switchOn() {
|
||||||
branchService.cloneNoteTo(this.noteId, 'share');
|
branchService.cloneNoteToNote(this.noteId, 'share');
|
||||||
}
|
}
|
||||||
|
|
||||||
async switchOff() {
|
async switchOff() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ws from "../../services/ws.js";
|
|||||||
import appContext from "../../services/app_context.js";
|
import appContext from "../../services/app_context.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import treeService from "../../services/tree.js";
|
import treeService from "../../services/tree.js";
|
||||||
|
import options from "../../services/options.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-detail-code note-detail-printable">
|
<div class="note-detail-code note-detail-printable">
|
||||||
@@ -14,20 +15,10 @@ const TPL = `
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trilium-api-docs-button {
|
|
||||||
/*display: none;*/
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-code-editor {
|
.note-detail-code-editor {
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<button class="btn bx bx-help-circle trilium-api-docs-button icon-button floating-button"
|
|
||||||
title="Open Trilium API docs"></button>
|
|
||||||
|
|
||||||
<div class="note-detail-code-editor"></div>
|
<div class="note-detail-code-editor"></div>
|
||||||
|
|
||||||
@@ -37,6 +28,13 @@ const TPL = `
|
|||||||
Execute <kbd data-command="runActiveNote"></kbd>
|
Execute <kbd data-command="runActiveNote"></kbd>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button class="no-print trilium-api-docs-button btn btn-sm"
|
||||||
|
title="Open Trilium API docs">
|
||||||
|
<span class="bx bx-help-circle"></span>
|
||||||
|
|
||||||
|
API docs
|
||||||
|
</button>
|
||||||
|
|
||||||
<button class="no-print save-to-note-button btn btn-sm">
|
<button class="no-print save-to-note-button btn btn-sm">
|
||||||
|
|
||||||
<span class="bx bx-save"></span>
|
<span class="bx bx-save"></span>
|
||||||
@@ -97,6 +95,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
|
|||||||
viewportMargin: Infinity,
|
viewportMargin: Infinity,
|
||||||
indentUnit: 4,
|
indentUnit: 4,
|
||||||
matchBrackets: true,
|
matchBrackets: true,
|
||||||
|
keyMap: options.is('vimKeymapEnabled') ? "vim": "default",
|
||||||
matchTags: {bothTags: true},
|
matchTags: {bothTags: true},
|
||||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
|
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
|
||||||
lint: true,
|
lint: true,
|
||||||
|
|||||||
@@ -32,15 +32,18 @@ body {
|
|||||||
#main {
|
#main {
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
flex-grow: 3;
|
flex-grow: 3;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 10px 20px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#parentLink {
|
||||||
|
float: right;
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title {
|
#title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px 20px 0 20px;
|
padding-top: 10px;
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@@ -52,7 +55,12 @@ pre {
|
|||||||
word-wrap: anywhere;
|
word-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menuButton {
|
iframe.pdf-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggleMenuButton {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
@@ -67,23 +75,74 @@ pre {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menuButton::after {
|
#childLinks.grid ul {
|
||||||
|
list-style-type: none;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#childLinks.grid ul li {
|
||||||
|
width: 180px;
|
||||||
|
height: 140px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#childLinks.grid ul li a {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
|
#childLinks.grid ul li a:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#childLinks.list ul {
|
||||||
|
list-style-type: none;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#childLinks.list ul li {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteClippedFrom {
|
||||||
|
padding: 10px 0 10px 0;
|
||||||
|
margin: 20px 0 20px 0;
|
||||||
|
color: #666;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggleMenuButton::after {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -2px;
|
top: -2px;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 48em) {
|
@media (max-width: 48em) {
|
||||||
#layout.navMenu #menu {
|
#layout.showMenu #menu {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menuButton {
|
#toggleMenuButton {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#layout.navMenu #main {
|
#layout.showMenu #main {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,11 +150,11 @@ pre {
|
|||||||
padding-left: 60px;
|
padding-left: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#layout.navMenu #menuButton::after {
|
#layout.showMenu #toggleMenuButton::after {
|
||||||
content: "«";
|
content: "«";
|
||||||
}
|
}
|
||||||
|
|
||||||
#menuButton::after {
|
#toggleMenuButton::after {
|
||||||
content: "»";
|
content: "»";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,18 @@
|
|||||||
|
|
||||||
const cloningService = require('../../services/cloning');
|
const cloningService = require('../../services/cloning');
|
||||||
|
|
||||||
function cloneNoteToParent(req) {
|
function cloneNoteToBranch(req) {
|
||||||
const {noteId, parentBranchId} = req.params;
|
const {noteId, parentBranchId} = req.params;
|
||||||
const {prefix} = req.body;
|
const {prefix} = req.body;
|
||||||
|
|
||||||
return cloningService.cloneNoteToParent(noteId, parentBranchId, prefix);
|
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneNoteToNote(req) {
|
||||||
|
const {noteId, parentNoteId} = req.params;
|
||||||
|
const {prefix} = req.body;
|
||||||
|
|
||||||
|
return cloningService.cloneNoteToNote(noteId, parentNoteId, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneNoteAfter(req) {
|
function cloneNoteAfter(req) {
|
||||||
@@ -16,6 +23,7 @@ function cloneNoteAfter(req) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cloneNoteToParent,
|
cloneNoteToBranch,
|
||||||
|
cloneNoteToNote,
|
||||||
cloneNoteAfter
|
cloneNoteAfter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -302,6 +302,21 @@ function uploadModifiedFile(req) {
|
|||||||
note.setContent(fileContent);
|
note.setContent(fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBacklinkCount(req) {
|
||||||
|
const {noteId} = req.params;
|
||||||
|
|
||||||
|
const note = becca.getNote(noteId);
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return [404, "Not found"];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
count: note.getTargetRelations().length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getNote,
|
getNote,
|
||||||
updateNote,
|
updateNote,
|
||||||
@@ -316,5 +331,6 @@ module.exports = {
|
|||||||
duplicateSubtree,
|
duplicateSubtree,
|
||||||
eraseDeletedNotesNow,
|
eraseDeletedNotesNow,
|
||||||
getDeleteNotesPreview,
|
getDeleteNotesPreview,
|
||||||
uploadModifiedFile
|
uploadModifiedFile,
|
||||||
|
getBacklinkCount
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const ALLOWED_OPTIONS = new Set([
|
|||||||
'similarNotesWidget',
|
'similarNotesWidget',
|
||||||
'editedNotesWidget',
|
'editedNotesWidget',
|
||||||
'calendarWidget',
|
'calendarWidget',
|
||||||
|
'vimKeymapEnabled',
|
||||||
'codeNotesMimeTypes',
|
'codeNotesMimeTypes',
|
||||||
'spellCheckEnabled',
|
'spellCheckEnabled',
|
||||||
'spellCheckLanguageCode',
|
'spellCheckLanguageCode',
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ function register(app) {
|
|||||||
apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
|
apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
|
||||||
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
|
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision);
|
apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision);
|
||||||
|
apiRoute(GET, '/api/notes/:noteId/backlink-count', notesApiRoute.getBacklinkCount);
|
||||||
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
|
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
|
||||||
apiRoute(POST, '/api/notes/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
|
apiRoute(POST, '/api/notes/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle);
|
apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle);
|
||||||
@@ -228,7 +229,8 @@ function register(app) {
|
|||||||
|
|
||||||
apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate);
|
apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate);
|
||||||
|
|
||||||
apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentBranchId', cloningApiRoute.cloneNoteToParent);
|
apiRoute(PUT, '/api/notes/:noteId/clone-to-branch/:parentBranchId', cloningApiRoute.cloneNoteToBranch);
|
||||||
|
apiRoute(PUT, '/api/notes/:noteId/clone-to-note/:parentNoteId', cloningApiRoute.cloneNoteToNote);
|
||||||
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
||||||
|
|
||||||
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'relation', name: 'widget', isDangerous: true },
|
{ type: 'relation', name: 'widget', isDangerous: true },
|
||||||
{ type: 'relation', name: 'renderNote', isDangerous: true },
|
{ type: 'relation', name: 'renderNote', isDangerous: true },
|
||||||
{ type: 'relation', name: 'shareCss', isDangerous: false },
|
{ type: 'relation', name: 'shareCss', isDangerous: false },
|
||||||
|
{ type: 'relation', name: 'shareJs', isDangerous: false },
|
||||||
|
{ type: 'relation', name: 'shareFavicon', isDangerous: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @returns {Note[]} */
|
/** @returns {Note[]} */
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2021-12-23T23:03:21+01:00", buildRevision: "f0217cae5eb4bdac12efe1d15bf26dc128e7f854" };
|
module.exports = { buildDate:"2022-01-02T22:43:30+01:00", buildRevision: "feffd57f240438d107c1ed1c1772545611a97dee" };
|
||||||
|
|||||||
@@ -10,20 +10,18 @@ const utils = require('./utils');
|
|||||||
const becca = require("../becca/becca");
|
const becca = require("../becca/becca");
|
||||||
const beccaService = require("../becca/becca_service");
|
const beccaService = require("../becca/becca_service");
|
||||||
|
|
||||||
function cloneNoteToParent(noteId, parentBranchId, prefix) {
|
function cloneNoteToNote(noteId, parentNoteId, prefix) {
|
||||||
if (parentBranchId === 'share') {
|
if (parentNoteId === 'share') {
|
||||||
const specialNotesService = require('./special_notes');
|
const specialNotesService = require('./special_notes');
|
||||||
// share root note is created lazily
|
// share root note is created lazily
|
||||||
specialNotesService.getShareRoot();
|
specialNotesService.getShareRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentBranch = becca.getBranch(parentBranchId);
|
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
||||||
|
|
||||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentBranch.noteId)) {
|
|
||||||
return { success: false, message: 'Note is deleted.' };
|
return { success: false, message: 'Note is deleted.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationResult = treeService.validateParentChild(parentBranch.noteId, noteId);
|
const validationResult = treeService.validateParentChild(parentNoteId, noteId);
|
||||||
|
|
||||||
if (!validationResult.success) {
|
if (!validationResult.success) {
|
||||||
return validationResult;
|
return validationResult;
|
||||||
@@ -31,21 +29,33 @@ function cloneNoteToParent(noteId, parentBranchId, prefix) {
|
|||||||
|
|
||||||
const branch = new Branch({
|
const branch = new Branch({
|
||||||
noteId: noteId,
|
noteId: noteId,
|
||||||
parentNoteId: parentBranch.noteId,
|
parentNoteId: parentNoteId,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
isExpanded: 0
|
isExpanded: 0
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
parentBranch.isExpanded = true; // the new target should be expanded so it immediately shows up to the user
|
|
||||||
parentBranch.save();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
branchId: branch.branchId,
|
branchId: branch.branchId,
|
||||||
notePath: beccaService.getNotePath(parentBranch.noteId).path + "/" + noteId
|
notePath: beccaService.getNotePath(parentNoteId).path + "/" + noteId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneNoteToBranch(noteId, parentBranchId, prefix) {
|
||||||
|
const parentBranch = becca.getBranch(parentBranchId);
|
||||||
|
|
||||||
|
if (!parentBranch) {
|
||||||
|
return { success: false, message: `Parent branch ${parentBranchId} does not exist.` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret = cloneNoteToNote(noteId, parentBranch.noteId, prefix);
|
||||||
|
|
||||||
|
parentBranch.isExpanded = true; // the new target should be expanded so it immediately shows up to the user
|
||||||
|
parentBranch.save();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
function ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) {
|
function ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) {
|
||||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
||||||
return { success: false, message: 'Note is deleted.' };
|
return { success: false, message: 'Note is deleted.' };
|
||||||
@@ -121,7 +131,8 @@ function isNoteDeleted(noteId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cloneNoteToParent,
|
cloneNoteToBranch,
|
||||||
|
cloneNoteToNote,
|
||||||
ensureNoteIsPresentInParent,
|
ensureNoteIsPresentInParent,
|
||||||
ensureNoteIsAbsentFromParent,
|
ensureNoteIsAbsentFromParent,
|
||||||
toggleNoteInParent,
|
toggleNoteInParent,
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ class ConsistencyChecks {
|
|||||||
WHERE noteId = ?
|
WHERE noteId = ?
|
||||||
and parentNoteId = ?
|
and parentNoteId = ?
|
||||||
and isDeleted = 0
|
and isDeleted = 0
|
||||||
ORDER BY utcDateCreated`, [noteId, parentNoteId]);
|
ORDER BY utcDateModified`, [noteId, parentNoteId]);
|
||||||
|
|
||||||
const branches = branchIds.map(branchId => becca.getBranch(branchId));
|
const branches = branchIds.map(branchId => becca.getBranch(branchId));
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ function getYearNote(dateStr, rootNote) {
|
|||||||
rootNote = getRootCalendarNote();
|
rootNote = getRootCalendarNote();
|
||||||
}
|
}
|
||||||
|
|
||||||
const yearStr = dateStr.substr(0, 4);
|
const yearStr = dateStr.trim().substr(0, 4);
|
||||||
|
|
||||||
let yearNote = attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
let yearNote = attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
||||||
|
|
||||||
@@ -138,6 +138,8 @@ function getDateNoteTitle(rootNote, dayNumber, dateObj) {
|
|||||||
|
|
||||||
/** @returns {Note} */
|
/** @returns {Note} */
|
||||||
function getDateNote(dateStr) {
|
function getDateNote(dateStr) {
|
||||||
|
dateStr = dateStr.trim().substr(0, 10);
|
||||||
|
|
||||||
let dateNote = attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
|
let dateNote = attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
|
||||||
|
|
||||||
if (dateNote) {
|
if (dateNote) {
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ const sanitizeHtml = require('sanitize-html');
|
|||||||
// intended mainly as protection against XSS via import
|
// intended mainly as protection against XSS via import
|
||||||
// secondarily it (partly) protects against "CSS takeover"
|
// secondarily it (partly) protects against "CSS takeover"
|
||||||
function sanitize(dirtyHtml) {
|
function sanitize(dirtyHtml) {
|
||||||
|
if (!dirtyHtml) {
|
||||||
|
return dirtyHtml;
|
||||||
|
}
|
||||||
|
|
||||||
// avoid H1 per https://github.com/zadam/trilium/issues/1552
|
// avoid H1 per https://github.com/zadam/trilium/issues/1552
|
||||||
// demote H1, and if that conflicts with existing H2, demote that, etc
|
// demote H1, and if that conflicts with existing H2, demote that, etc
|
||||||
const transformTags = {};
|
const transformTags = {};
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function importOpml(taskContext, fileBuffer, parentNote) {
|
|||||||
throw new Error("Unrecognized OPML version " + opmlVersion);
|
throw new Error("Unrecognized OPML version " + opmlVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
content = htmlSanitizer.sanitize(content);
|
content = htmlSanitizer.sanitize(content || "");
|
||||||
|
|
||||||
const {note} = noteService.createNewNote({
|
const {note} = noteService.createNewNote({
|
||||||
parentNoteId,
|
parentNoteId,
|
||||||
|
|||||||
@@ -240,13 +240,15 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (noteMeta && noteMeta.isClone) {
|
if (noteMeta && noteMeta.isClone) {
|
||||||
new Branch({
|
if (!becca.getBranchFromChildAndParent(noteId, parentNoteId)) {
|
||||||
noteId,
|
new Branch({
|
||||||
parentNoteId,
|
noteId,
|
||||||
isExpanded: noteMeta.isExpanded,
|
parentNoteId,
|
||||||
prefix: noteMeta.prefix,
|
isExpanded: noteMeta.isExpanded,
|
||||||
notePosition: noteMeta.notePosition
|
prefix: noteMeta.prefix,
|
||||||
}).save();
|
notePosition: noteMeta.notePosition
|
||||||
|
}).save();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -365,6 +367,16 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
note.setContent(content);
|
note.setContent(content);
|
||||||
|
|
||||||
|
if (!becca.getBranchFromChildAndParent(noteId, parentNoteId)) {
|
||||||
|
new Branch({
|
||||||
|
noteId,
|
||||||
|
parentNoteId,
|
||||||
|
isExpanded: noteMeta.isExpanded,
|
||||||
|
prefix: noteMeta.prefix,
|
||||||
|
notePosition: noteMeta.notePosition
|
||||||
|
}).save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
({note} = noteService.createNewNote({
|
({note} = noteService.createNewNote({
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ function downloadImages(noteId, content) {
|
|||||||
// which upon the download of all the images will update the note if the links have not been fixed before
|
// which upon the download of all the images will update the note if the links have not been fixed before
|
||||||
|
|
||||||
sql.transactional(() => {
|
sql.transactional(() => {
|
||||||
const imageNotes = becca.getNotes(Object.values(imageUrlToNoteIdMapping));
|
const imageNotes = becca.getNotes(Object.values(imageUrlToNoteIdMapping), true);
|
||||||
|
|
||||||
const origNote = becca.getNote(noteId);
|
const origNote = becca.getNote(noteId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
const becca = require('../becca/becca');
|
const becca = require('../becca/becca');
|
||||||
|
const sql = require("./sql.js");
|
||||||
|
|
||||||
function getOption(name) {
|
function getOption(name) {
|
||||||
const option = require('../becca/becca').getOption(name);
|
let option;
|
||||||
|
|
||||||
|
if (becca.loaded) {
|
||||||
|
option = becca.getOption(name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// e.g. in initial sync becca is not loaded because DB is not initialized
|
||||||
|
option = sql.getRow("SELECT * FROM options WHERE name = ?", name);
|
||||||
|
}
|
||||||
|
|
||||||
if (!option) {
|
if (!option) {
|
||||||
throw new Error(`Option "${name}" doesn't exist`);
|
throw new Error(`Option "${name}" doesn't exist`);
|
||||||
@@ -39,12 +48,12 @@ function getOptionBool(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setOption(name, value) {
|
function setOption(name, value) {
|
||||||
const option = becca.getOption(name);
|
|
||||||
|
|
||||||
if (value === true || value === false) {
|
if (value === true || value === false) {
|
||||||
value = value.toString();
|
value = value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const option = becca.getOption(name);
|
||||||
|
|
||||||
if (option) {
|
if (option) {
|
||||||
option.value = value;
|
option.value = value;
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ const defaultOptions = [
|
|||||||
{ name: 'imageMaxWidthHeight', value: '2000', isSynced: true },
|
{ name: 'imageMaxWidthHeight', value: '2000', isSynced: true },
|
||||||
{ name: 'imageJpegQuality', value: '75', isSynced: true },
|
{ name: 'imageJpegQuality', value: '75', isSynced: true },
|
||||||
{ name: 'autoFixConsistencyIssues', value: 'true', isSynced: false },
|
{ name: 'autoFixConsistencyIssues', value: 'true', isSynced: false },
|
||||||
|
{ name: 'vimKeymapEnabled', value: 'false', isSynced: false },
|
||||||
{ name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-sqlite;schema=trilium","text/x-swift","text/xml","text/x-yaml"]', isSynced: true },
|
{ name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-sqlite;schema=trilium","text/x-swift","text/xml","text/x-yaml"]', isSynced: true },
|
||||||
{ name: 'leftPaneWidth', value: '25', isSynced: false },
|
{ name: 'leftPaneWidth', value: '25', isSynced: false },
|
||||||
{ name: 'leftPaneVisible', value: 'true', isSynced: false },
|
{ name: 'leftPaneVisible', value: 'true', isSynced: false },
|
||||||
|
|||||||
@@ -371,7 +371,10 @@ function getLastSyncedPull() {
|
|||||||
|
|
||||||
function setLastSyncedPull(entityChangeId) {
|
function setLastSyncedPull(entityChangeId) {
|
||||||
const lastSyncedPullOption = becca.getOption('lastSyncedPull');
|
const lastSyncedPullOption = becca.getOption('lastSyncedPull');
|
||||||
lastSyncedPullOption.value = entityChangeId + '';
|
|
||||||
|
if (lastSyncedPullOption) { // might be null in initial sync when becca is not loaded
|
||||||
|
lastSyncedPullOption.value = entityChangeId + '';
|
||||||
|
}
|
||||||
|
|
||||||
// this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes
|
// this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes
|
||||||
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPull']);
|
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPull']);
|
||||||
@@ -389,7 +392,10 @@ function setLastSyncedPush(entityChangeId) {
|
|||||||
ws.setLastSyncedPush(entityChangeId);
|
ws.setLastSyncedPush(entityChangeId);
|
||||||
|
|
||||||
const lastSyncedPushOption = becca.getOption('lastSyncedPush');
|
const lastSyncedPushOption = becca.getOption('lastSyncedPush');
|
||||||
lastSyncedPushOption.value = entityChangeId + '';
|
|
||||||
|
if (lastSyncedPushOption) { // might be null in initial sync when becca is not loaded
|
||||||
|
lastSyncedPushOption.value = entityChangeId + '';
|
||||||
|
}
|
||||||
|
|
||||||
// this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes
|
// this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes
|
||||||
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPush']);
|
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPush']);
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ let mainWindow;
|
|||||||
let setupWindow;
|
let setupWindow;
|
||||||
|
|
||||||
async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
||||||
|
const spellcheckEnabled = optionService.getOptionBool('spellCheckEnabled');
|
||||||
|
|
||||||
const {BrowserWindow} = require('electron');
|
const {BrowserWindow} = require('electron');
|
||||||
|
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 1000,
|
width: 1000,
|
||||||
height: 800,
|
height: 800,
|
||||||
@@ -25,7 +28,7 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
|||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
spellcheck: optionService.getOptionBool('spellCheckEnabled')
|
spellcheck: spellcheckEnabled
|
||||||
},
|
},
|
||||||
frame: optionService.getOptionBool('nativeTitleBarVisible'),
|
frame: optionService.getOptionBool('nativeTitleBarVisible'),
|
||||||
icon: getIcon()
|
icon: getIcon()
|
||||||
@@ -33,6 +36,8 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
|||||||
|
|
||||||
win.setMenuBarVisibility(false);
|
win.setMenuBarVisibility(false);
|
||||||
win.loadURL('http://127.0.0.1:' + await port + '/?extra=1&extraHoistedNoteId=' + hoistedNoteId + '#' + notePath);
|
win.loadURL('http://127.0.0.1:' + await port + '/?extra=1&extraHoistedNoteId=' + hoistedNoteId + '#' + notePath);
|
||||||
|
|
||||||
|
configureWebContents(win.webContents, spellcheckEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('create-extra-window', (event, arg) => {
|
ipcMain.on('create-extra-window', (event, arg) => {
|
||||||
@@ -59,6 +64,7 @@ async function createMainWindow() {
|
|||||||
height: mainWindowState.height,
|
height: mainWindowState.height,
|
||||||
title: 'Trilium Notes',
|
title: 'Trilium Notes',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
enableRemoteModule: true,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
spellcheck: spellcheckEnabled
|
spellcheck: spellcheckEnabled
|
||||||
@@ -73,8 +79,10 @@ async function createMainWindow() {
|
|||||||
mainWindow.loadURL('http://127.0.0.1:' + await port);
|
mainWindow.loadURL('http://127.0.0.1:' + await port);
|
||||||
mainWindow.on('closed', () => mainWindow = null);
|
mainWindow.on('closed', () => mainWindow = null);
|
||||||
|
|
||||||
const {webContents} = mainWindow;
|
configureWebContents(mainWindow.webContents, spellcheckEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureWebContents(webContents, spellcheckEnabled) {
|
||||||
require("@electron/remote/main").enable(webContents);
|
require("@electron/remote/main").enable(webContents);
|
||||||
|
|
||||||
webContents.on('new-window', (e, url) => {
|
webContents.on('new-window', (e, url) => {
|
||||||
|
|||||||
@@ -1,43 +1,18 @@
|
|||||||
const {JSDOM} = require("jsdom");
|
const {JSDOM} = require("jsdom");
|
||||||
const NO_CONTENT = '<p>This note has no content.</p>';
|
|
||||||
const shaca = require("./shaca/shaca");
|
const shaca = require("./shaca/shaca");
|
||||||
|
|
||||||
function getChildrenList(note) {
|
|
||||||
if (note.hasChildren()) {
|
|
||||||
const document = new JSDOM().window.document;
|
|
||||||
|
|
||||||
const ulEl = document.createElement("ul");
|
|
||||||
|
|
||||||
for (const childNote of note.getChildNotes()) {
|
|
||||||
const li = document.createElement("li");
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.appendChild(document.createTextNode(childNote.title));
|
|
||||||
link.setAttribute("href", childNote.noteId);
|
|
||||||
|
|
||||||
li.appendChild(link);
|
|
||||||
ulEl.appendChild(li);
|
|
||||||
}
|
|
||||||
|
|
||||||
return '<p>Child notes:</p>' + ulEl.outerHTML;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContent(note) {
|
function getContent(note) {
|
||||||
let content = note.getContent();
|
let content = note.getContent();
|
||||||
|
let header = '';
|
||||||
|
let isEmpty = false;
|
||||||
|
|
||||||
if (note.type === 'text') {
|
if (note.type === 'text') {
|
||||||
const document = new JSDOM(content || "").window.document;
|
const document = new JSDOM(content || "").window.document;
|
||||||
|
|
||||||
const isEmpty = document.body.textContent.trim().length === 0
|
isEmpty = document.body.textContent.trim().length === 0
|
||||||
&& document.querySelectorAll("img").length === 0;
|
&& document.querySelectorAll("img").length === 0;
|
||||||
|
|
||||||
if (isEmpty) {
|
if (!isEmpty) {
|
||||||
content = NO_CONTENT + getChildrenList(note);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (const linkEl of document.querySelectorAll("a")) {
|
for (const linkEl of document.querySelectorAll("a")) {
|
||||||
const href = linkEl.getAttribute("href");
|
const href = linkEl.getAttribute("href");
|
||||||
|
|
||||||
@@ -49,6 +24,7 @@ function getContent(note) {
|
|||||||
|
|
||||||
if (linkedNote) {
|
if (linkedNote) {
|
||||||
linkEl.setAttribute("href", linkedNote.shareId);
|
linkEl.setAttribute("href", linkedNote.shareId);
|
||||||
|
linkEl.classList.add("type-" + linkedNote.type);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
linkEl.removeAttribute("href");
|
linkEl.removeAttribute("href");
|
||||||
@@ -57,11 +33,24 @@ function getContent(note) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content = document.body.innerHTML;
|
content = document.body.innerHTML;
|
||||||
|
|
||||||
|
if (content.includes(`<span class="math-tex">`)) {
|
||||||
|
header += `
|
||||||
|
<script src="../../libraries/katex/katex.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="../../libraries/katex/katex.min.css">
|
||||||
|
<script src="../../libraries/katex/auto-render.min.js"></script>
|
||||||
|
<script src="../../libraries/katex/mhchem.min.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
renderMathInElement(document.getElementById('content'));
|
||||||
|
});
|
||||||
|
</script>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (note.type === 'code' || note.type === 'mermaid') {
|
else if (note.type === 'code') {
|
||||||
if (!content?.trim()) {
|
if (!content?.trim()) {
|
||||||
content = NO_CONTENT + getChildrenList(note);
|
isEmpty = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const document = new JSDOM().window.document;
|
const document = new JSDOM().window.document;
|
||||||
@@ -72,22 +61,45 @@ function getContent(note) {
|
|||||||
content = preEl.outerHTML;
|
content = preEl.outerHTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (note.type === 'mermaid') {
|
||||||
|
content = `
|
||||||
|
<div class="mermaid">${content}</div>
|
||||||
|
<hr>
|
||||||
|
<details>
|
||||||
|
<summary>Chart source</summary>
|
||||||
|
<pre>${content}</pre>
|
||||||
|
</details>`
|
||||||
|
header += `<script src="../../libraries/mermaid.min.js"></script>`;
|
||||||
|
}
|
||||||
else if (note.type === 'image') {
|
else if (note.type === 'image') {
|
||||||
content = `<img src="api/images/${note.noteId}/${note.title}?${note.utcDateModified}">`;
|
content = `<img src="api/images/${note.noteId}/${note.title}?${note.utcDateModified}">`;
|
||||||
}
|
}
|
||||||
else if (note.type === 'file') {
|
else if (note.type === 'file') {
|
||||||
content = `<button type="button" onclick="location.href='api/notes/${note.noteId}/download'">Download file</button>`;
|
if (note.mime === 'application/pdf') {
|
||||||
|
content = `<iframe class="pdf-view" src="api/notes/${note.noteId}/view"></iframe>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
content = `<button type="button" onclick="location.href='api/notes/${note.noteId}/download'">Download file</button>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (note.type === 'book') {
|
else if (note.type === 'book') {
|
||||||
content = getChildrenList(note);
|
isEmpty = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
content = '<p>This note type cannot be displayed.</p>' + getChildrenList(note);
|
content = '<p>This note type cannot be displayed.</p>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return {
|
||||||
|
header,
|
||||||
|
content,
|
||||||
|
isEmpty
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getContent
|
getContent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,15 @@ function register(router) {
|
|||||||
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
|
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
const content = contentRenderer.getContent(note);
|
const {header, content, isEmpty} = contentRenderer.getContent(note);
|
||||||
|
|
||||||
const subRoot = getSharedSubTreeRoot(note);
|
const subRoot = getSharedSubTreeRoot(note);
|
||||||
|
|
||||||
res.render("share/page", {
|
res.render("share/page", {
|
||||||
note,
|
note,
|
||||||
|
header,
|
||||||
content,
|
content,
|
||||||
|
isEmpty,
|
||||||
subRoot
|
subRoot
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -44,19 +46,15 @@ function register(router) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/share/api/images/:noteId/:filename', (req, res, next) => {
|
router.get('/share/api/notes/:noteId', (req, res, next) => {
|
||||||
const image = shaca.getNote(req.params.noteId);
|
const {noteId} = req.params;
|
||||||
|
const note = shaca.getNote(noteId);
|
||||||
|
|
||||||
if (!image) {
|
if (!note) {
|
||||||
return res.status(404).send("Not found");
|
return res.status(404).send(`Note ${noteId} not found`);
|
||||||
}
|
|
||||||
else if (image.type !== 'image') {
|
|
||||||
return res.status(400).send("Requested note is not an image");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.set('Content-Type', image.mime);
|
res.json(note.getPojoWithAttributes());
|
||||||
|
|
||||||
res.send(image.getContent());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/share/api/notes/:noteId/download', (req, res, next) => {
|
router.get('/share/api/notes/:noteId/download', (req, res, next) => {
|
||||||
@@ -64,7 +62,7 @@ function register(router) {
|
|||||||
const note = shaca.getNote(noteId);
|
const note = shaca.getNote(noteId);
|
||||||
|
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return res.status(404).send(`Not found`);
|
return res.status(404).send(`Note ${noteId} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const utils = require("../services/utils");
|
const utils = require("../services/utils");
|
||||||
@@ -78,6 +76,36 @@ function register(router) {
|
|||||||
|
|
||||||
res.send(note.getContent());
|
res.send(note.getContent());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/share/api/images/:noteId/:filename', (req, res, next) => {
|
||||||
|
const image = shaca.getNote(req.params.noteId);
|
||||||
|
|
||||||
|
if (!image) {
|
||||||
|
return res.status(404).send(`Note ${noteId} not found`);
|
||||||
|
}
|
||||||
|
else if (image.type !== 'image') {
|
||||||
|
return res.status(400).send("Requested note is not an image");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.set('Content-Type', image.mime);
|
||||||
|
|
||||||
|
res.send(image.getContent());
|
||||||
|
});
|
||||||
|
|
||||||
|
// used for PDF viewing
|
||||||
|
router.get('/share/api/notes/:noteId/view', (req, res, next) => {
|
||||||
|
const {noteId} = req.params;
|
||||||
|
const note = shaca.getNote(noteId);
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
return res.status(404).send(`Note ${noteId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
|
res.setHeader('Content-Type', note.mime);
|
||||||
|
|
||||||
|
res.send(note.getContent());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ class Attribute extends AbstractEntity {
|
|||||||
|
|
||||||
return this.shaca.getNote(this.value);
|
return this.shaca.getNote(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPojo() {
|
||||||
|
return {
|
||||||
|
attributeId: this.attributeId,
|
||||||
|
noteId: this.noteId,
|
||||||
|
type: this.type,
|
||||||
|
name: this.name,
|
||||||
|
position: this.position,
|
||||||
|
value: this.value,
|
||||||
|
isInheritable: this.isInheritable
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Attribute;
|
module.exports = Attribute;
|
||||||
|
|||||||
@@ -410,6 +410,19 @@ class Note extends AbstractEntity {
|
|||||||
|
|
||||||
return sharedAlias || this.noteId;
|
return sharedAlias || this.noteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPojoWithAttributes() {
|
||||||
|
return {
|
||||||
|
noteId: this.noteId,
|
||||||
|
title: this.title,
|
||||||
|
type: this.type,
|
||||||
|
mime: this.mime,
|
||||||
|
utcDateModified: this.utcDateModified,
|
||||||
|
attributes: this.getAttributes().map(attr => attr.getPojo()),
|
||||||
|
parentNoteIds: this.parents.map(parentNote => parentNote.noteId),
|
||||||
|
childNoteIds: this.children.map(child => child.noteId)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Note;
|
module.exports = Note;
|
||||||
|
|||||||
@@ -59,11 +59,7 @@ function load() {
|
|||||||
SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified
|
SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified
|
||||||
FROM attributes
|
FROM attributes
|
||||||
WHERE isDeleted = 0
|
WHERE isDeleted = 0
|
||||||
AND noteId IN (${noteIdStr})
|
AND noteId IN (${noteIdStr})`);
|
||||||
AND (
|
|
||||||
(type = 'label' AND name IN ('archived', 'shareHiddenFromTree', 'shareAlias', 'shareOmitDefaultCss'))
|
|
||||||
OR (type = 'relation' AND name IN ('imageLink', 'template', 'shareCss'))
|
|
||||||
)`, []);
|
|
||||||
|
|
||||||
for (const row of rawAttributeRows) {
|
for (const row of rawAttributeRows) {
|
||||||
new Attribute(row);
|
new Attribute(row);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ async function start() {
|
|||||||
const parentNoteId = getRandomNoteId();
|
const parentNoteId = getRandomNoteId();
|
||||||
const prefix = Math.random() > 0.8 ? "prefix" : null;
|
const prefix = Math.random() > 0.8 ? "prefix" : null;
|
||||||
|
|
||||||
const result = await cloningService.cloneNoteToParent(noteIdToClone, parentNoteId, prefix);
|
const result = await cloningService.cloneNoteToBranch(noteIdToClone, parentNoteId, prefix);
|
||||||
|
|
||||||
console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED");
|
console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,8 @@
|
|||||||
<%- include('dialogs/delete_notes.ejs') %>
|
<%- include('dialogs/delete_notes.ejs') %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||||
|
|
||||||
window.baseApiUrl = 'api/';
|
window.baseApiUrl = 'api/';
|
||||||
window.device = "desktop";
|
window.device = "desktop";
|
||||||
window.glob = {
|
window.glob = {
|
||||||
|
|||||||
@@ -105,6 +105,8 @@
|
|||||||
<%- include('dialogs/confirm.ejs') %>
|
<%- include('dialogs/confirm.ejs') %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||||
|
|
||||||
window.baseApiUrl = 'api/';
|
window.baseApiUrl = 'api/';
|
||||||
window.device = "mobile";
|
window.device = "mobile";
|
||||||
window.glob = {
|
window.glob = {
|
||||||
|
|||||||
@@ -189,6 +189,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||||
|
|
||||||
window.glob = {
|
window.glob = {
|
||||||
sourceId: ''
|
sourceId: ''
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,45 +2,79 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<% if (note.hasRelation("shareFavicon")) { %>
|
||||||
|
<link rel="shortcut icon" href="api/notes/<%= note.getRelation("shareFavicon").value %>/download">
|
||||||
|
<% } else { %>
|
||||||
<link rel="shortcut icon" href="../favicon.ico">
|
<link rel="shortcut icon" href="../favicon.ico">
|
||||||
|
<% } %>
|
||||||
|
<script src="../app/share.js"></script>
|
||||||
<% if (!note.hasLabel("shareOmitDefaultCss")) { %>
|
<% if (!note.hasLabel("shareOmitDefaultCss")) { %>
|
||||||
<link href="../libraries/normalize.min.css" rel="stylesheet">
|
<link href="../libraries/normalize.min.css" rel="stylesheet">
|
||||||
<link href="../stylesheets/share.css" rel="stylesheet">
|
<link href="../stylesheets/share.css" rel="stylesheet">
|
||||||
<% } %>
|
<% } %>
|
||||||
<% if (note.type === 'text' || note.type === 'book') { %>
|
<% if (note.type === 'text' || note.type === 'book') { %>
|
||||||
<link href="../libraries/ckeditor/ckeditor-content.css" rel="stylesheet">
|
<link href="../libraries/ckeditor/ckeditor-content.css" rel="stylesheet">
|
||||||
<% } %>
|
<% } %>
|
||||||
<% for (const cssRelation of note.getRelations("shareCss")) { %>
|
<% for (const cssRelation of note.getRelations("shareCss")) { %>
|
||||||
<link href="api/notes/<%= cssRelation.value %>/download" rel="stylesheet">
|
<link href="api/notes/<%= cssRelation.value %>/download" rel="stylesheet">
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% for (const jsRelation of note.getRelations("shareJs")) { %>
|
||||||
|
<script type="module" src="api/notes/<%= jsRelation.value %>/download"></script>
|
||||||
|
<% } %>
|
||||||
|
<%- header %>
|
||||||
<title><%= note.title %></title>
|
<title><%= note.title %></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body data-note-id="<%= note.noteId %>">
|
||||||
<div id="layout">
|
<div id="layout">
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<h1 id="title"><%= note.title %></h1>
|
<% if (note.parents[0].noteId !== 'share' && note.parents.length !== 0) { %>
|
||||||
|
<nav id="parentLink">
|
||||||
|
parent: <a href="<%= note.parents[0].noteId %>"
|
||||||
|
class="type-<%= note.parents[0].type %>"><%= note.parents[0].title %></a>
|
||||||
|
</nav>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
<div id="content" class="note-<%= note.type %> <% if (note.type === 'text') { %>ck-content<% } %>">
|
<h1 id="title"><%= note.title %></h1>
|
||||||
|
|
||||||
|
<% if (note.hasLabel("pageUrl")) { %>
|
||||||
|
<div id="noteClippedFrom">This note was originally clipped from <a href="<%= note.getLabelValue("pageUrl") %>"><%= note.getLabelValue("pageUrl") %></a></div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (note.type === 'book') { %>
|
||||||
|
<% } else if (isEmpty) { %>
|
||||||
|
<p>This note has no content.</p>
|
||||||
|
<% } else { %>
|
||||||
|
<div id="content" class="type-<%= note.type %><% if (note.type === 'text') { %> ck-content<% } %>">
|
||||||
<%- content %>
|
<%- content %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% } %>
|
||||||
|
|
||||||
<% if (subRoot.hasChildren()) { %>
|
<% if (note.hasChildren()) { %>
|
||||||
<button id="menuButton"></button>
|
<nav id="childLinks" class="<% if (isEmpty) { %>grid<% } else { %>list<% } %>">
|
||||||
|
<% if (!isEmpty) { %>
|
||||||
|
<hr>
|
||||||
|
<span>Child notes: </span>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% for (const childNote of note.getChildNotes()) { %>
|
||||||
|
<li>
|
||||||
|
<a href="<%= childNote.shareId %>"
|
||||||
|
class="type-<%= childNote.type %>"><%= childNote.title %></a>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if (subRoot.hasChildren()) { %>
|
||||||
|
<button id="toggleMenuButton"></button>
|
||||||
|
|
||||||
<nav id="menu">
|
<nav id="menu">
|
||||||
<%- include('tree_item', {note: subRoot, activeNote: note}) %>
|
<%- include('tree_item', {note: subRoot, activeNote: note}) %>
|
||||||
</nav>
|
</nav>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
const menuButton = document.getElementById('menuButton');
|
|
||||||
const layout = document.getElementById('layout');
|
|
||||||
|
|
||||||
menuButton.addEventListener('click', () => layout.classList.toggle('navMenu'));
|
|
||||||
}());
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<% if (activeNote.noteId === note.noteId) { %>
|
<% if (activeNote.noteId === note.noteId) { %>
|
||||||
<strong><%= note.title %></strong>
|
<strong><%= note.title %></strong>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<a href="./<%= note.shareId %>"><%= note.title %></a>
|
<a class="type-<%= note.type %>" href="./<%= note.shareId %>"><%= note.title %></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ module.exports = {
|
|||||||
filename: 'desktop.js'
|
filename: 'desktop.js'
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
target: 'electron-main'
|
target: 'electron-renderer'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ module.exports = {
|
|||||||
filename: 'mobile.js'
|
filename: 'mobile.js'
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
target: 'electron-main'
|
target: 'electron-renderer'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ module.exports = {
|
|||||||
filename: 'setup.js'
|
filename: 'setup.js'
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
target: 'electron-main'
|
target: 'electron-renderer'
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user