mirror of
https://github.com/zadam/trilium.git
synced 2025-10-29 17:26:38 +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",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.49.0-beta",
|
||||
"version": "0.49.2-beta",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
|
||||
@@ -1114,7 +1114,7 @@ class Note extends AbstractEntity {
|
||||
|
||||
const branch = this.becca.getNote(parentNoteId).getParentBranches()[0];
|
||||
|
||||
return cloningService.cloneNoteToParent(this.noteId, branch.branchId);
|
||||
return cloningService.cloneNoteToBranch(this.noteId, branch.branchId);
|
||||
}
|
||||
|
||||
decrypt() {
|
||||
|
||||
@@ -48,7 +48,7 @@ async function cloneNotesTo(notePath) {
|
||||
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
|
||||
|
||||
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 targetNote = await froca.getBranch(targetBranchId).getNote();
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import mimeTypesService from "../../services/mime_types.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 = `
|
||||
<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>
|
||||
|
||||
<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() {
|
||||
$("#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");
|
||||
}
|
||||
|
||||
async optionsLoaded() {
|
||||
async optionsLoaded(options) {
|
||||
this.$mimeTypes.empty();
|
||||
|
||||
this.$vimKeymapEnabled.prop("checked", options['vimKeymapEnabled'] === 'true');
|
||||
let idCtr = 1;
|
||||
|
||||
for (const mimeType of await mimeTypesService.getMimeTypes()) {
|
||||
@@ -45,4 +59,4 @@ export default class CodeNotesOptions {
|
||||
|
||||
mimeTypesService.loadMimeTypes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +196,18 @@ ws.subscribeToMessages(async message => {
|
||||
}
|
||||
});
|
||||
|
||||
async function cloneNoteTo(childNoteId, parentBranchId, prefix) {
|
||||
const resp = await server.put(`notes/${childNoteId}/clone-to/${parentBranchId}`, {
|
||||
async function cloneNoteToBranch(childNoteId, parentBranchId, prefix) {
|
||||
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
|
||||
});
|
||||
|
||||
@@ -222,5 +232,6 @@ export default {
|
||||
deleteNotes,
|
||||
moveNodeUpInHierarchy,
|
||||
cloneNoteAfter,
|
||||
cloneNoteTo
|
||||
cloneNoteToBranch,
|
||||
cloneNoteToNote,
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ async function pasteInto(parentBranchId) {
|
||||
for (const clipboardBranch of clipboardBranches) {
|
||||
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
|
||||
|
||||
@@ -10,6 +10,7 @@ const CODE_MIRROR = {
|
||||
"libraries/codemirror/addon/edit/matchtags.js",
|
||||
"libraries/codemirror/addon/search/match-highlighter.js",
|
||||
"libraries/codemirror/mode/meta.js",
|
||||
"libraries/codemirror/keymap/vim.js",
|
||||
"libraries/codemirror/addon/lint/lint.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',
|
||||
"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.",
|
||||
"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) {
|
||||
this.clearItems();
|
||||
|
||||
const targetRelationCount = note.getTargetRelations().length;
|
||||
if (targetRelationCount === 0) {
|
||||
// can't use froca since that would count only relations from loaded notes
|
||||
const resp = await server.get(`notes/${this.noteId}/backlink-count`);
|
||||
|
||||
if (!resp || !resp.count) {
|
||||
this.$ticker.hide();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.$ticker.show();
|
||||
this.$count.text(
|
||||
`${targetRelationCount} backlink`
|
||||
+ (targetRelationCount === 1 ? '' : 's')
|
||||
);
|
||||
}
|
||||
|
||||
this.$ticker.show();
|
||||
this.$count.text(
|
||||
`${resp.count} backlink`
|
||||
+ (resp.count === 1 ? '' : 's')
|
||||
);
|
||||
}
|
||||
|
||||
clearItems() {
|
||||
@@ -136,18 +138,22 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
|
||||
await froca.getNotes(backlinks.map(bl => bl.noteId)); // prefetch all
|
||||
|
||||
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,
|
||||
showNotePath: true,
|
||||
showTooltip: false
|
||||
}));
|
||||
|
||||
if (backlink.relationName) {
|
||||
this.$items.append($("<p>").text("relation: " + backlink.relationName));
|
||||
$item.append($("<p>").text("relation: " + backlink.relationName));
|
||||
}
|
||||
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 {
|
||||
isEnabled() {
|
||||
return super.isEnabled() && this.note.hasAncestor('share');
|
||||
return super.isEnabled() && this.noteId !== 'share' && this.note.hasAncestor('share');
|
||||
}
|
||||
|
||||
doRender() {
|
||||
|
||||
@@ -4,6 +4,10 @@ import server from "../services/server.js";
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
export default class SharedSwitchWidget extends SwitchWidget {
|
||||
isEnabled() {
|
||||
return super.isEnabled() && this.noteId !== 'root' && this.noteId !== 'share';
|
||||
}
|
||||
|
||||
doRender() {
|
||||
super.doRender();
|
||||
|
||||
@@ -18,7 +22,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
|
||||
}
|
||||
|
||||
switchOn() {
|
||||
branchService.cloneNoteTo(this.noteId, 'share');
|
||||
branchService.cloneNoteToNote(this.noteId, 'share');
|
||||
}
|
||||
|
||||
async switchOff() {
|
||||
|
||||
@@ -6,6 +6,7 @@ import ws from "../../services/ws.js";
|
||||
import appContext from "../../services/app_context.js";
|
||||
import toastService from "../../services/toast.js";
|
||||
import treeService from "../../services/tree.js";
|
||||
import options from "../../services/options.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-code note-detail-printable">
|
||||
@@ -14,20 +15,10 @@ const TPL = `
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trilium-api-docs-button {
|
||||
/*display: none;*/
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.note-detail-code-editor {
|
||||
min-height: 50px;
|
||||
}
|
||||
</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>
|
||||
|
||||
@@ -37,6 +28,13 @@ const TPL = `
|
||||
Execute <kbd data-command="runActiveNote"></kbd>
|
||||
</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">
|
||||
|
||||
<span class="bx bx-save"></span>
|
||||
@@ -97,6 +95,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
|
||||
viewportMargin: Infinity,
|
||||
indentUnit: 4,
|
||||
matchBrackets: true,
|
||||
keyMap: options.is('vimKeymapEnabled') ? "vim": "default",
|
||||
matchTags: {bothTags: true},
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
|
||||
lint: true,
|
||||
|
||||
@@ -32,15 +32,18 @@ body {
|
||||
#main {
|
||||
flex-basis: 0;
|
||||
flex-grow: 3;
|
||||
overflow: auto;
|
||||
padding: 10px 20px 20px 20px;
|
||||
}
|
||||
|
||||
#parentLink {
|
||||
float: right;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#title {
|
||||
margin: 0;
|
||||
padding: 20px 20px 0 20px;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 20px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
img {
|
||||
@@ -52,7 +55,12 @@ pre {
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
||||
#menuButton {
|
||||
iframe.pdf-view {
|
||||
width: 100%;
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
#toggleMenuButton {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 8px;
|
||||
@@ -67,23 +75,74 @@ pre {
|
||||
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;
|
||||
top: -2px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 48em) {
|
||||
#layout.navMenu #menu {
|
||||
#layout.showMenu #menu {
|
||||
display: block;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
#menuButton {
|
||||
#toggleMenuButton {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#layout.navMenu #main {
|
||||
#layout.showMenu #main {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -91,11 +150,11 @@ pre {
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
#layout.navMenu #menuButton::after {
|
||||
#layout.showMenu #toggleMenuButton::after {
|
||||
content: "«";
|
||||
}
|
||||
|
||||
#menuButton::after {
|
||||
#toggleMenuButton::after {
|
||||
content: "»";
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,18 @@
|
||||
|
||||
const cloningService = require('../../services/cloning');
|
||||
|
||||
function cloneNoteToParent(req) {
|
||||
function cloneNoteToBranch(req) {
|
||||
const {noteId, parentBranchId} = req.params;
|
||||
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) {
|
||||
@@ -16,6 +23,7 @@ function cloneNoteAfter(req) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cloneNoteToParent,
|
||||
cloneNoteToBranch,
|
||||
cloneNoteToNote,
|
||||
cloneNoteAfter
|
||||
};
|
||||
|
||||
@@ -302,6 +302,21 @@ function uploadModifiedFile(req) {
|
||||
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 = {
|
||||
getNote,
|
||||
updateNote,
|
||||
@@ -316,5 +331,6 @@ module.exports = {
|
||||
duplicateSubtree,
|
||||
eraseDeletedNotesNow,
|
||||
getDeleteNotesPreview,
|
||||
uploadModifiedFile
|
||||
uploadModifiedFile,
|
||||
getBacklinkCount
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'similarNotesWidget',
|
||||
'editedNotesWidget',
|
||||
'calendarWidget',
|
||||
'vimKeymapEnabled',
|
||||
'codeNotesMimeTypes',
|
||||
'spellCheckEnabled',
|
||||
'spellCheckLanguageCode',
|
||||
|
||||
@@ -220,6 +220,7 @@ function register(app) {
|
||||
apiRoute(DELETE, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
|
||||
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
|
||||
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/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
|
||||
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(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);
|
||||
|
||||
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: 'renderNote', isDangerous: true },
|
||||
{ type: 'relation', name: 'shareCss', isDangerous: false },
|
||||
{ type: 'relation', name: 'shareJs', isDangerous: false },
|
||||
{ type: 'relation', name: 'shareFavicon', isDangerous: false },
|
||||
];
|
||||
|
||||
/** @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 beccaService = require("../becca/becca_service");
|
||||
|
||||
function cloneNoteToParent(noteId, parentBranchId, prefix) {
|
||||
if (parentBranchId === 'share') {
|
||||
function cloneNoteToNote(noteId, parentNoteId, prefix) {
|
||||
if (parentNoteId === 'share') {
|
||||
const specialNotesService = require('./special_notes');
|
||||
// share root note is created lazily
|
||||
specialNotesService.getShareRoot();
|
||||
}
|
||||
|
||||
const parentBranch = becca.getBranch(parentBranchId);
|
||||
|
||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentBranch.noteId)) {
|
||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
||||
return { success: false, message: 'Note is deleted.' };
|
||||
}
|
||||
|
||||
const validationResult = treeService.validateParentChild(parentBranch.noteId, noteId);
|
||||
const validationResult = treeService.validateParentChild(parentNoteId, noteId);
|
||||
|
||||
if (!validationResult.success) {
|
||||
return validationResult;
|
||||
@@ -31,21 +29,33 @@ function cloneNoteToParent(noteId, parentBranchId, prefix) {
|
||||
|
||||
const branch = new Branch({
|
||||
noteId: noteId,
|
||||
parentNoteId: parentBranch.noteId,
|
||||
parentNoteId: parentNoteId,
|
||||
prefix: prefix,
|
||||
isExpanded: 0
|
||||
}).save();
|
||||
|
||||
parentBranch.isExpanded = true; // the new target should be expanded so it immediately shows up to the user
|
||||
parentBranch.save();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
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) {
|
||||
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
|
||||
return { success: false, message: 'Note is deleted.' };
|
||||
@@ -121,7 +131,8 @@ function isNoteDeleted(noteId) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cloneNoteToParent,
|
||||
cloneNoteToBranch,
|
||||
cloneNoteToNote,
|
||||
ensureNoteIsPresentInParent,
|
||||
ensureNoteIsAbsentFromParent,
|
||||
toggleNoteInParent,
|
||||
|
||||
@@ -259,7 +259,7 @@ class ConsistencyChecks {
|
||||
WHERE noteId = ?
|
||||
and parentNoteId = ?
|
||||
and isDeleted = 0
|
||||
ORDER BY utcDateCreated`, [noteId, parentNoteId]);
|
||||
ORDER BY utcDateModified`, [noteId, parentNoteId]);
|
||||
|
||||
const branches = branchIds.map(branchId => becca.getBranch(branchId));
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ function getYearNote(dateStr, rootNote) {
|
||||
rootNote = getRootCalendarNote();
|
||||
}
|
||||
|
||||
const yearStr = dateStr.substr(0, 4);
|
||||
const yearStr = dateStr.trim().substr(0, 4);
|
||||
|
||||
let yearNote = attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
||||
|
||||
@@ -138,6 +138,8 @@ function getDateNoteTitle(rootNote, dayNumber, dateObj) {
|
||||
|
||||
/** @returns {Note} */
|
||||
function getDateNote(dateStr) {
|
||||
dateStr = dateStr.trim().substr(0, 10);
|
||||
|
||||
let dateNote = attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
|
||||
|
||||
if (dateNote) {
|
||||
|
||||
@@ -3,6 +3,10 @@ const sanitizeHtml = require('sanitize-html');
|
||||
// intended mainly as protection against XSS via import
|
||||
// secondarily it (partly) protects against "CSS takeover"
|
||||
function sanitize(dirtyHtml) {
|
||||
if (!dirtyHtml) {
|
||||
return dirtyHtml;
|
||||
}
|
||||
|
||||
// avoid H1 per https://github.com/zadam/trilium/issues/1552
|
||||
// demote H1, and if that conflicts with existing H2, demote that, etc
|
||||
const transformTags = {};
|
||||
|
||||
@@ -51,7 +51,7 @@ async function importOpml(taskContext, fileBuffer, parentNote) {
|
||||
throw new Error("Unrecognized OPML version " + opmlVersion);
|
||||
}
|
||||
|
||||
content = htmlSanitizer.sanitize(content);
|
||||
content = htmlSanitizer.sanitize(content || "");
|
||||
|
||||
const {note} = noteService.createNewNote({
|
||||
parentNoteId,
|
||||
|
||||
@@ -240,13 +240,15 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
|
||||
if (noteMeta && noteMeta.isClone) {
|
||||
new Branch({
|
||||
noteId,
|
||||
parentNoteId,
|
||||
isExpanded: noteMeta.isExpanded,
|
||||
prefix: noteMeta.prefix,
|
||||
notePosition: noteMeta.notePosition
|
||||
}).save();
|
||||
if (!becca.getBranchFromChildAndParent(noteId, parentNoteId)) {
|
||||
new Branch({
|
||||
noteId,
|
||||
parentNoteId,
|
||||
isExpanded: noteMeta.isExpanded,
|
||||
prefix: noteMeta.prefix,
|
||||
notePosition: noteMeta.notePosition
|
||||
}).save();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -365,6 +367,16 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
|
||||
note.setContent(content);
|
||||
|
||||
if (!becca.getBranchFromChildAndParent(noteId, parentNoteId)) {
|
||||
new Branch({
|
||||
noteId,
|
||||
parentNoteId,
|
||||
isExpanded: noteMeta.isExpanded,
|
||||
prefix: noteMeta.prefix,
|
||||
notePosition: noteMeta.notePosition
|
||||
}).save();
|
||||
}
|
||||
}
|
||||
else {
|
||||
({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
|
||||
|
||||
sql.transactional(() => {
|
||||
const imageNotes = becca.getNotes(Object.values(imageUrlToNoteIdMapping));
|
||||
const imageNotes = becca.getNotes(Object.values(imageUrlToNoteIdMapping), true);
|
||||
|
||||
const origNote = becca.getNote(noteId);
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
const becca = require('../becca/becca');
|
||||
const sql = require("./sql.js");
|
||||
|
||||
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) {
|
||||
throw new Error(`Option "${name}" doesn't exist`);
|
||||
@@ -39,12 +48,12 @@ function getOptionBool(name) {
|
||||
}
|
||||
|
||||
function setOption(name, value) {
|
||||
const option = becca.getOption(name);
|
||||
|
||||
if (value === true || value === false) {
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
const option = becca.getOption(name);
|
||||
|
||||
if (option) {
|
||||
option.value = value;
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ const defaultOptions = [
|
||||
{ name: 'imageMaxWidthHeight', value: '2000', isSynced: true },
|
||||
{ name: 'imageJpegQuality', value: '75', isSynced: true },
|
||||
{ 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: 'leftPaneWidth', value: '25', isSynced: false },
|
||||
{ name: 'leftPaneVisible', value: 'true', isSynced: false },
|
||||
|
||||
@@ -371,7 +371,10 @@ function getLastSyncedPull() {
|
||||
|
||||
function setLastSyncedPull(entityChangeId) {
|
||||
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
|
||||
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPull']);
|
||||
@@ -389,7 +392,10 @@ function setLastSyncedPush(entityChangeId) {
|
||||
ws.setLastSyncedPush(entityChangeId);
|
||||
|
||||
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
|
||||
sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPush']);
|
||||
|
||||
@@ -16,7 +16,10 @@ let mainWindow;
|
||||
let setupWindow;
|
||||
|
||||
async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
||||
const spellcheckEnabled = optionService.getOptionBool('spellCheckEnabled');
|
||||
|
||||
const {BrowserWindow} = require('electron');
|
||||
|
||||
const win = new BrowserWindow({
|
||||
width: 1000,
|
||||
height: 800,
|
||||
@@ -25,7 +28,7 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
||||
enableRemoteModule: true,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
spellcheck: optionService.getOptionBool('spellCheckEnabled')
|
||||
spellcheck: spellcheckEnabled
|
||||
},
|
||||
frame: optionService.getOptionBool('nativeTitleBarVisible'),
|
||||
icon: getIcon()
|
||||
@@ -33,6 +36,8 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') {
|
||||
|
||||
win.setMenuBarVisibility(false);
|
||||
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) => {
|
||||
@@ -59,6 +64,7 @@ async function createMainWindow() {
|
||||
height: mainWindowState.height,
|
||||
title: 'Trilium Notes',
|
||||
webPreferences: {
|
||||
enableRemoteModule: true,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
spellcheck: spellcheckEnabled
|
||||
@@ -73,8 +79,10 @@ async function createMainWindow() {
|
||||
mainWindow.loadURL('http://127.0.0.1:' + await port);
|
||||
mainWindow.on('closed', () => mainWindow = null);
|
||||
|
||||
const {webContents} = mainWindow;
|
||||
configureWebContents(mainWindow.webContents, spellcheckEnabled);
|
||||
}
|
||||
|
||||
function configureWebContents(webContents, spellcheckEnabled) {
|
||||
require("@electron/remote/main").enable(webContents);
|
||||
|
||||
webContents.on('new-window', (e, url) => {
|
||||
|
||||
@@ -1,43 +1,18 @@
|
||||
const {JSDOM} = require("jsdom");
|
||||
const NO_CONTENT = '<p>This note has no content.</p>';
|
||||
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) {
|
||||
let content = note.getContent();
|
||||
let header = '';
|
||||
let isEmpty = false;
|
||||
|
||||
if (note.type === 'text') {
|
||||
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;
|
||||
|
||||
if (isEmpty) {
|
||||
content = NO_CONTENT + getChildrenList(note);
|
||||
}
|
||||
else {
|
||||
if (!isEmpty) {
|
||||
for (const linkEl of document.querySelectorAll("a")) {
|
||||
const href = linkEl.getAttribute("href");
|
||||
|
||||
@@ -49,6 +24,7 @@ function getContent(note) {
|
||||
|
||||
if (linkedNote) {
|
||||
linkEl.setAttribute("href", linkedNote.shareId);
|
||||
linkEl.classList.add("type-" + linkedNote.type);
|
||||
}
|
||||
else {
|
||||
linkEl.removeAttribute("href");
|
||||
@@ -57,11 +33,24 @@ function getContent(note) {
|
||||
}
|
||||
|
||||
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()) {
|
||||
content = NO_CONTENT + getChildrenList(note);
|
||||
isEmpty = true;
|
||||
}
|
||||
else {
|
||||
const document = new JSDOM().window.document;
|
||||
@@ -72,22 +61,45 @@ function getContent(note) {
|
||||
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') {
|
||||
content = `<img src="api/images/${note.noteId}/${note.title}?${note.utcDateModified}">`;
|
||||
}
|
||||
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') {
|
||||
content = getChildrenList(note);
|
||||
isEmpty = true;
|
||||
}
|
||||
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 = {
|
||||
getContent
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@ function register(router) {
|
||||
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
|
||||
|
||||
if (note) {
|
||||
const content = contentRenderer.getContent(note);
|
||||
const {header, content, isEmpty} = contentRenderer.getContent(note);
|
||||
|
||||
const subRoot = getSharedSubTreeRoot(note);
|
||||
|
||||
res.render("share/page", {
|
||||
note,
|
||||
header,
|
||||
content,
|
||||
isEmpty,
|
||||
subRoot
|
||||
});
|
||||
}
|
||||
@@ -44,19 +46,15 @@ function register(router) {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/share/api/images/:noteId/:filename', (req, res, next) => {
|
||||
const image = shaca.getNote(req.params.noteId);
|
||||
router.get('/share/api/notes/:noteId', (req, res, next) => {
|
||||
const {noteId} = req.params;
|
||||
const note = shaca.getNote(noteId);
|
||||
|
||||
if (!image) {
|
||||
return res.status(404).send("Not found");
|
||||
}
|
||||
else if (image.type !== 'image') {
|
||||
return res.status(400).send("Requested note is not an image");
|
||||
if (!note) {
|
||||
return res.status(404).send(`Note ${noteId} not found`);
|
||||
}
|
||||
|
||||
res.set('Content-Type', image.mime);
|
||||
|
||||
res.send(image.getContent());
|
||||
res.json(note.getPojoWithAttributes());
|
||||
});
|
||||
|
||||
router.get('/share/api/notes/:noteId/download', (req, res, next) => {
|
||||
@@ -64,7 +62,7 @@ function register(router) {
|
||||
const note = shaca.getNote(noteId);
|
||||
|
||||
if (!note) {
|
||||
return res.status(404).send(`Not found`);
|
||||
return res.status(404).send(`Note ${noteId} not found`);
|
||||
}
|
||||
|
||||
const utils = require("../services/utils");
|
||||
@@ -78,6 +76,36 @@ function register(router) {
|
||||
|
||||
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 = {
|
||||
|
||||
@@ -89,6 +89,18 @@ class Attribute extends AbstractEntity {
|
||||
|
||||
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;
|
||||
|
||||
@@ -410,6 +410,19 @@ class Note extends AbstractEntity {
|
||||
|
||||
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;
|
||||
|
||||
@@ -59,11 +59,7 @@ function load() {
|
||||
SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified
|
||||
FROM attributes
|
||||
WHERE isDeleted = 0
|
||||
AND noteId IN (${noteIdStr})
|
||||
AND (
|
||||
(type = 'label' AND name IN ('archived', 'shareHiddenFromTree', 'shareAlias', 'shareOmitDefaultCss'))
|
||||
OR (type = 'relation' AND name IN ('imageLink', 'template', 'shareCss'))
|
||||
)`, []);
|
||||
AND noteId IN (${noteIdStr})`);
|
||||
|
||||
for (const row of rawAttributeRows) {
|
||||
new Attribute(row);
|
||||
|
||||
@@ -61,7 +61,7 @@ async function start() {
|
||||
const parentNoteId = getRandomNoteId();
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
<%- include('dialogs/delete_notes.ejs') %>
|
||||
|
||||
<script type="text/javascript">
|
||||
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||
|
||||
window.baseApiUrl = 'api/';
|
||||
window.device = "desktop";
|
||||
window.glob = {
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
<%- include('dialogs/confirm.ejs') %>
|
||||
|
||||
<script type="text/javascript">
|
||||
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||
|
||||
window.baseApiUrl = 'api/';
|
||||
window.device = "mobile";
|
||||
window.glob = {
|
||||
|
||||
@@ -189,6 +189,8 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||
|
||||
window.glob = {
|
||||
sourceId: ''
|
||||
};
|
||||
|
||||
@@ -2,45 +2,79 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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">
|
||||
<% } %>
|
||||
<script src="../app/share.js"></script>
|
||||
<% if (!note.hasLabel("shareOmitDefaultCss")) { %>
|
||||
<link href="../libraries/normalize.min.css" rel="stylesheet">
|
||||
<link href="../stylesheets/share.css" rel="stylesheet">
|
||||
<link href="../libraries/normalize.min.css" rel="stylesheet">
|
||||
<link href="../stylesheets/share.css" rel="stylesheet">
|
||||
<% } %>
|
||||
<% 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")) { %>
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<div id="layout">
|
||||
<div id="main">
|
||||
<h1 id="title"><%= note.title %></h1>
|
||||
<body data-note-id="<%= note.noteId %>">
|
||||
<div id="layout">
|
||||
<div id="main">
|
||||
<% 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 %>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (subRoot.hasChildren()) { %>
|
||||
<button id="menuButton"></button>
|
||||
<% if (note.hasChildren()) { %>
|
||||
<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">
|
||||
<%- include('tree_item', {note: subRoot, activeNote: note}) %>
|
||||
</nav>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const menuButton = document.getElementById('menuButton');
|
||||
const layout = document.getElementById('layout');
|
||||
|
||||
menuButton.addEventListener('click', () => layout.classList.toggle('navMenu'));
|
||||
}());
|
||||
</script>
|
||||
<% } %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<% if (activeNote.noteId === note.noteId) { %>
|
||||
<strong><%= note.title %></strong>
|
||||
<% } else { %>
|
||||
<a href="./<%= note.shareId %>"><%= note.title %></a>
|
||||
<a class="type-<%= note.type %>" href="./<%= note.shareId %>"><%= note.title %></a>
|
||||
<% } %>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -11,5 +11,5 @@ module.exports = {
|
||||
filename: 'desktop.js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
target: 'electron-main'
|
||||
target: 'electron-renderer'
|
||||
};
|
||||
|
||||
@@ -11,5 +11,5 @@ module.exports = {
|
||||
filename: 'mobile.js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
target: 'electron-main'
|
||||
target: 'electron-renderer'
|
||||
};
|
||||
|
||||
@@ -11,5 +11,5 @@ module.exports = {
|
||||
filename: 'setup.js'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
target: 'electron-main'
|
||||
target: 'electron-renderer'
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user