mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 19:36:12 +01:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e0a65b59c | ||
|
|
56e49cfc19 | ||
|
|
af40d73cee | ||
|
|
50b7063811 | ||
|
|
ee1b377bc2 | ||
|
|
51dfe8bb14 | ||
|
|
e426ee3e4f | ||
|
|
a434aa113d | ||
|
|
3b551e3e4d |
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.48.1-beta",
|
||||
"version": "0.48.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -2850,9 +2850,9 @@
|
||||
}
|
||||
},
|
||||
"electron": {
|
||||
"version": "13.5.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-13.5.2.tgz",
|
||||
"integrity": "sha512-CPakwDpy5m8dL0383F5uJboQcVtn9bT/+6/wdDKo8LuTUO9aER1TF41v7feZgZW2c+UwoGPWa814ElSQ3qta2A==",
|
||||
"version": "13.6.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-13.6.0.tgz",
|
||||
"integrity": "sha512-VDOUmRwa4eQ+5iXE+XHKXKX+2yk8Ey5uBe3nH9Hj6zNPUBY2S0EOClw7U90Ve2lLHXiQ6DnguNmDgOZKJLcHFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
@@ -2861,9 +2861,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.17.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz",
|
||||
"integrity": "sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==",
|
||||
"version": "14.17.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.27.tgz",
|
||||
"integrity": "sha512-94+Ahf9IcaDuJTle/2b+wzvjmutxXAEXU6O81JHblYXUg2BDG+dnBy7VxIPHKAyEEDHzCMQydTJuWvrE+Aanzw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.48.2",
|
||||
"version": "0.48.3",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -81,7 +81,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "13.5.2",
|
||||
"electron": "13.6.0",
|
||||
"electron-builder": "22.13.1",
|
||||
"electron-packager": "15.4.0",
|
||||
"electron-rebuild": "3.2.3",
|
||||
|
||||
@@ -764,8 +764,8 @@ class Note extends AbstractEntity {
|
||||
}
|
||||
|
||||
/** @return {String[]} */
|
||||
getSubtreeNoteIds() {
|
||||
return this.getSubtreeNotes().map(note => note.noteId);
|
||||
getSubtreeNoteIds(includeArchived = true) {
|
||||
return this.getSubtreeNotes(includeArchived).map(note => note.noteId);
|
||||
}
|
||||
|
||||
getDescendantNoteIds() {
|
||||
|
||||
@@ -23,6 +23,15 @@ async function getDateNote(date) {
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
async function getWeekNote(date) {
|
||||
const note = await server.get('special-notes/week/' + date, "date-note");
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
|
||||
return await froca.getNote(note.noteId);
|
||||
}
|
||||
|
||||
/** @return {NoteShort} */
|
||||
async function getMonthNote(month) {
|
||||
const note = await server.get('special-notes/month/' + month, "date-note");
|
||||
@@ -63,6 +72,7 @@ export default {
|
||||
getInboxNote,
|
||||
getTodayNote,
|
||||
getDateNote,
|
||||
getWeekNote,
|
||||
getMonthNote,
|
||||
getYearNote,
|
||||
createSqlConsole,
|
||||
|
||||
@@ -396,6 +396,15 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
*/
|
||||
this.getDateNote = dateNotesService.getDateNote;
|
||||
|
||||
/**
|
||||
* Returns date-note for the first date of the week of the given date. If it doesn't exist, it is automatically created.
|
||||
*
|
||||
* @method
|
||||
* @param {string} date - e.g. "2019-04-29"
|
||||
* @return {Promise<NoteShort>}
|
||||
*/
|
||||
this.getWeekNote = dateNotesService.getWeekNote;
|
||||
|
||||
/**
|
||||
* Returns month-note. If it doesn't exist, it is automatically created.
|
||||
*
|
||||
|
||||
@@ -25,12 +25,12 @@ const TPL = `
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a data-trigger-command="renderActiveNote" class="dropdown-item render-note-button"><kbd data-command="renderActiveNote"></kbd> Re-render note</a>
|
||||
<a data-trigger-command="findInText" class="dropdown-item">Search in note <kbd data-command="findInText"></a>
|
||||
<a data-trigger-command="findInText" class="dropdown-item find-in-text-button">Search in note <kbd data-command="findInText"></a>
|
||||
<a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</a>
|
||||
<a data-trigger-command="openNoteExternally" class="dropdown-item open-note-externally-button"><kbd data-command="openNoteExternally"></kbd> Open note externally</a>
|
||||
<a class="dropdown-item import-files-button">Import files</a>
|
||||
<a class="dropdown-item export-note-button">Export note</a>
|
||||
<a data-trigger-command="printActiveNote" class="dropdown-item print-note-button"><kbd data-command="printActiveNote"></kbd> Print note</a>
|
||||
<a data-trigger-command="printActiveNote" class="dropdown-item print-active-note-button"><kbd data-command="printActiveNote"></kbd> Print note</a>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -42,6 +42,8 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.$findInTextButton = this.$widget.find('.find-in-text-button');
|
||||
this.$printActiveNoteButton = this.$widget.find('.print-active-note-button');
|
||||
this.$showSourceButton = this.$widget.find('.show-source-button');
|
||||
this.$renderNoteButton = this.$widget.find('.render-note-button');
|
||||
|
||||
@@ -57,14 +59,18 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
||||
this.$importNoteButton = this.$widget.find('.import-files-button');
|
||||
this.$importNoteButton.on("click", () => import('../../dialogs/import.js').then(d => d.showDialog(this.noteId)));
|
||||
|
||||
this.$widget.on('click', '.dropdown-item', () => this.$widget.find('.dropdown-menu').dropdown('toggle'));
|
||||
this.$widget.on('click', '.dropdown-item', () => this.$widget.find("[data-toggle='dropdown']").dropdown('toggle'));
|
||||
|
||||
this.$openNoteExternallyButton = this.$widget.find(".open-note-externally-button");
|
||||
}
|
||||
|
||||
refreshWithNote(note) {
|
||||
this.toggleDisabled(this.$findInTextButton, ['text', 'code', 'book', 'search'].includes(note.type));
|
||||
|
||||
this.toggleDisabled(this.$showSourceButton, ['text', 'relation-map', 'search', 'code'].includes(note.type));
|
||||
|
||||
this.toggleDisabled(this.$printActiveNoteButton, ['text', 'code'].includes(note.type));
|
||||
|
||||
this.$renderNoteButton.toggle(note.type === 'render');
|
||||
|
||||
this.$openNoteExternallyButton.toggle(utils.isElectron());
|
||||
|
||||
@@ -17,7 +17,7 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
z-index: 1000;
|
||||
z-index: 10; /* should be below dropdown (note actions) */
|
||||
}
|
||||
|
||||
.map-type-switcher .bx {
|
||||
@@ -332,9 +332,9 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
||||
|
||||
if (this.widgetMode === 'ribbon') {
|
||||
setTimeout(() => {
|
||||
const node = this.nodes.find(node => node.id === this.noteId);
|
||||
const subGraphNoteIds = this.getSubGraphConnectedToCurrentNote(data);
|
||||
|
||||
this.graph.centerAt(node.x, node.y, 500);
|
||||
this.graph.zoomToFit(400, 50, node => subGraphNoteIds.has(node.id));
|
||||
}, 1000);
|
||||
}
|
||||
else if (this.widgetMode === 'type') {
|
||||
@@ -344,6 +344,39 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
||||
}
|
||||
}
|
||||
|
||||
getSubGraphConnectedToCurrentNote(data) {
|
||||
function getGroupedLinksBySource(links) {
|
||||
const map = {};
|
||||
|
||||
for (const link of links) {
|
||||
const key = link.source.id;
|
||||
map[key] = map[key] || [];
|
||||
map[key].push(link);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
const linksBySource = getGroupedLinksBySource(data.links);
|
||||
|
||||
const subGraphNoteIds = new Set();
|
||||
|
||||
function traverseGraph(noteId) {
|
||||
if (subGraphNoteIds.has(noteId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
subGraphNoteIds.add(noteId);
|
||||
|
||||
for (const link of linksBySource[noteId] || []) {
|
||||
traverseGraph(link.target.id);
|
||||
}
|
||||
}
|
||||
|
||||
traverseGraph(this.noteId);
|
||||
return subGraphNoteIds;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.$container.html('');
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||
const NOTE_TYPES = [
|
||||
{ type: "file", title: "File", selectable: false },
|
||||
{ type: "image", title: "Image", selectable: false },
|
||||
{ type: "search", title: "Saved search", selectable: false },
|
||||
{ type: "search", title: "Saved Search", selectable: false },
|
||||
{ type: "note-map", mime: '', title: "Note Map", selectable: false },
|
||||
|
||||
{ type: "text", mime: "text/html", title: "Text", selectable: true },
|
||||
{ type: "relation-map", mime: "application/json", title: "Relation Map", selectable: true },
|
||||
@@ -15,6 +16,8 @@ const NOTE_TYPES = [
|
||||
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
|
||||
];
|
||||
|
||||
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter(nt => !nt.selectable).map(nt => nt.type);
|
||||
|
||||
const TPL = `
|
||||
<div class="dropdown note-type-widget">
|
||||
<style>
|
||||
@@ -48,7 +51,7 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||
|
||||
async refreshWithNote(note) {
|
||||
this.$noteTypeButton.prop("disabled",
|
||||
() => ["file", "image", "search"].includes(note.type));
|
||||
() => NOT_SELECTABLE_NOTE_TYPES.includes(note.type));
|
||||
|
||||
this.$noteTypeDesc.text(await this.findTypeTitle(note.type, note.mime));
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
this.$notePathList.empty();
|
||||
|
||||
if (this.noteId === 'root') {
|
||||
await this.addPath('root');
|
||||
await this.getRenderedPath('root');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,22 +83,18 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
this.$notePathIntro.text("This note is not yet placed into the note tree.");
|
||||
}
|
||||
|
||||
const printedNotePaths = new Set();
|
||||
const renderedPaths = [];
|
||||
|
||||
for (const notePathRecord of sortedNotePaths) {
|
||||
const notePath = notePathRecord.notePath.join('/');
|
||||
|
||||
if (printedNotePaths.has(notePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printedNotePaths.add(notePath);
|
||||
|
||||
await this.addPath(notePath, notePathRecord);
|
||||
renderedPaths.push(await this.getRenderedPath(notePath, notePathRecord));
|
||||
}
|
||||
|
||||
this.$notePathList.empty().append(...renderedPaths);
|
||||
}
|
||||
|
||||
async addPath(notePath, notePathRecord) {
|
||||
async getRenderedPath(notePath, notePathRecord) {
|
||||
const title = await treeService.getNotePathTitle(notePath);
|
||||
|
||||
const $noteLink = await linkService.createNoteLink(notePath, {title});
|
||||
@@ -136,7 +132,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
||||
$noteLink.append(` ${icons.join(' ')}`);
|
||||
}
|
||||
|
||||
this.$notePathList.append($("<li>").append($noteLink));
|
||||
return $("<li>").append($noteLink);
|
||||
}
|
||||
|
||||
entitiesReloadedEvent({loadResults}) {
|
||||
|
||||
@@ -15,7 +15,7 @@ const htmlSanitizer = require('../../services/html_sanitizer');
|
||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
||||
|
||||
function findClippingNote(todayNote, pageUrl) {
|
||||
const notes = todayNote.searchNoteInSubtree(
|
||||
const notes = todayNote.searchNotesInSubtree(
|
||||
formatAttrForSearch({
|
||||
type: 'label',
|
||||
name: "pageUrl",
|
||||
@@ -93,6 +93,7 @@ function createNote(req) {
|
||||
|
||||
if (pageUrl) {
|
||||
note.setLabel('pageUrl', pageUrl);
|
||||
note.setLabel('iconClass', 'bx bx-globe');
|
||||
}
|
||||
|
||||
const rewrittenContent = processContent(images, note, content);
|
||||
|
||||
@@ -24,23 +24,54 @@ function buildDescendantCountMap() {
|
||||
return noteIdToCountMap;
|
||||
}
|
||||
|
||||
function getNeighbors(note, depth) {
|
||||
if (depth === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const retNoteIds = [];
|
||||
|
||||
for (const relation of note.getRelations()) {
|
||||
if (['relationMapLink', 'template', 'image'].includes(relation.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetNote = relation.getTargetNote();
|
||||
retNoteIds.push(targetNote.noteId);
|
||||
|
||||
for (const noteId of getNeighbors(targetNote, depth - 1)) {
|
||||
retNoteIds.push(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
return retNoteIds;
|
||||
}
|
||||
|
||||
function getLinkMap(req) {
|
||||
const mapRootNote = becca.getNote(req.params.noteId);
|
||||
// if the map root itself has ignore (journal typically) then there wouldn't be anything to display so
|
||||
// we'll just ignore it
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap');
|
||||
|
||||
const noteIds = new Set();
|
||||
const noteIds = new Set(
|
||||
mapRootNote.getSubtreeNotes(false)
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.map(note => note.noteId)
|
||||
);
|
||||
|
||||
const notes = mapRootNote.getSubtreeNotes(false)
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.map(note => [
|
||||
for (const noteId of getNeighbors(mapRootNote, 3)) {
|
||||
noteIds.add(noteId);
|
||||
}
|
||||
|
||||
const notes = Array.from(noteIds).map(noteId => {
|
||||
const note = becca.getNote(noteId);
|
||||
|
||||
return [
|
||||
note.noteId,
|
||||
note.isContentAvailable() ? note.title : '[protected]',
|
||||
note.type
|
||||
]);
|
||||
|
||||
notes.forEach(([noteId]) => noteIds.add(noteId));
|
||||
];
|
||||
});
|
||||
|
||||
const links = Object.values(becca.attributes).filter(rel => {
|
||||
if (rel.type !== 'relation' || rel.name === 'relationMapLink' || rel.name === 'template') {
|
||||
|
||||
@@ -14,6 +14,10 @@ function getDateNote(req) {
|
||||
return dateNoteService.getDateNote(req.params.date);
|
||||
}
|
||||
|
||||
function getWeekNote(req) {
|
||||
return dateNoteService.getWeekNote(req.params.date);
|
||||
}
|
||||
|
||||
function getMonthNote(req) {
|
||||
return dateNoteService.getMonthNote(req.params.month);
|
||||
}
|
||||
@@ -65,6 +69,7 @@ function getHoistedNote() {
|
||||
module.exports = {
|
||||
getInboxNote,
|
||||
getDateNote,
|
||||
getWeekNote,
|
||||
getMonthNote,
|
||||
getYearNote,
|
||||
getDateNotesForMonth,
|
||||
|
||||
@@ -262,6 +262,7 @@ function register(app) {
|
||||
|
||||
apiRoute(GET, '/api/special-notes/inbox/:date', specialNotesRoute.getInboxNote);
|
||||
apiRoute(GET, '/api/special-notes/date/:date', specialNotesRoute.getDateNote);
|
||||
apiRoute(GET, '/api/special-notes/week/:date', specialNotesRoute.getWeekNote);
|
||||
apiRoute(GET, '/api/special-notes/month/:month', specialNotesRoute.getMonthNote);
|
||||
apiRoute(GET, '/api/special-notes/year/:year', specialNotesRoute.getYearNote);
|
||||
apiRoute(GET, '/api/special-notes/notes-for-month/:month', specialNotesRoute.getDateNotesForMonth);
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2021-10-20T22:12:20+02:00", buildRevision: "a22e4d60b643542c72d0fd164b8096dfde026e18" };
|
||||
module.exports = { buildDate:"2021-10-22T21:31:44+02:00", buildRevision: "56e49cfc19d7c142e70a0454c3dabf47cf845e3c" };
|
||||
|
||||
Reference in New Issue
Block a user