mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
moved similar notes from sidebar to the center pane
This commit is contained in:
parent
75c9db6432
commit
80f269d844
@ -20,7 +20,7 @@ import NoteInfoWidget from "../widgets/collapsible_widgets/note_info.js";
|
|||||||
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
|
import CalendarWidget from "../widgets/collapsible_widgets/calendar.js";
|
||||||
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
|
import LinkMapWidget from "../widgets/collapsible_widgets/link_map.js";
|
||||||
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
|
import NoteRevisionsWidget from "../widgets/collapsible_widgets/note_revisions.js";
|
||||||
import SimilarNotesWidget from "../widgets/collapsible_widgets/similar_notes.js";
|
import SimilarNotesWidget from "../widgets/similar_notes.js";
|
||||||
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
|
import WhatLinksHereWidget from "../widgets/collapsible_widgets/what_links_here.js";
|
||||||
import SidePaneToggles from "../widgets/side_pane_toggles.js";
|
import SidePaneToggles from "../widgets/side_pane_toggles.js";
|
||||||
import EditedNotesWidget from "../widgets/collapsible_widgets/edited_notes.js";
|
import EditedNotesWidget from "../widgets/collapsible_widgets/edited_notes.js";
|
||||||
@ -138,6 +138,7 @@ export default class DesktopMainWindowLayout {
|
|||||||
)
|
)
|
||||||
.child(new TabCachingWidget(() => new AttributeListWidget()))
|
.child(new TabCachingWidget(() => new AttributeListWidget()))
|
||||||
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
.child(new TabCachingWidget(() => new NoteDetailWidget()))
|
||||||
|
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
|
||||||
.child(...this.customWidgets.get('center-pane'))
|
.child(...this.customWidgets.get('center-pane'))
|
||||||
)
|
)
|
||||||
.child(new SidePaneContainer('right')
|
.child(new SidePaneContainer('right')
|
||||||
@ -148,7 +149,6 @@ export default class DesktopMainWindowLayout {
|
|||||||
.child(new TabCachingWidget(() => new EditedNotesWidget()))
|
.child(new TabCachingWidget(() => new EditedNotesWidget()))
|
||||||
.child(new TabCachingWidget(() => new LinkMapWidget()))
|
.child(new TabCachingWidget(() => new LinkMapWidget()))
|
||||||
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
.child(new TabCachingWidget(() => new NoteRevisionsWidget()))
|
||||||
.child(new TabCachingWidget(() => new SimilarNotesWidget()))
|
|
||||||
.child(new TabCachingWidget(() => new WhatLinksHereWidget()))
|
.child(new TabCachingWidget(() => new WhatLinksHereWidget()))
|
||||||
.child(...this.customWidgets.get('right-pane'))
|
.child(...this.customWidgets.get('right-pane'))
|
||||||
)
|
)
|
||||||
|
@ -56,7 +56,7 @@ const TPL = `
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attr-expander:not(.error):hover hr {
|
.attr-expander:hover hr {
|
||||||
border-color: var(--main-text-color);
|
border-color: var(--main-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
import CollapsibleWidget from "../collapsible_widget.js";
|
|
||||||
import linkService from "../../services/link.js";
|
|
||||||
import server from "../../services/server.js";
|
|
||||||
import treeCache from "../../services/tree_cache.js";
|
|
||||||
|
|
||||||
const TPL = `
|
|
||||||
<div>
|
|
||||||
<style>
|
|
||||||
.similar-notes-content a {
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px dotted var(--main-border-color);
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: var(--accented-background-color);
|
|
||||||
padding: 0 10px 0 10px;
|
|
||||||
margin: 3px;
|
|
||||||
max-width: 10em;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="similar-notes-content"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default class SimilarNotesWidget extends CollapsibleWidget {
|
|
||||||
get widgetTitle() { return "Similar notes"; }
|
|
||||||
|
|
||||||
async doRenderBody() {
|
|
||||||
this.$body.html(TPL);
|
|
||||||
|
|
||||||
this.$similarNotesContent = this.$body.find(".similar-notes-content");
|
|
||||||
}
|
|
||||||
|
|
||||||
get help() {
|
|
||||||
return {
|
|
||||||
title: "This list contains notes which might be similar to the current note based on textual similarity of note title."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
noteSwitched() {
|
|
||||||
const noteId = this.noteId;
|
|
||||||
|
|
||||||
this.$similarNotesContent.empty();
|
|
||||||
|
|
||||||
// avoid executing this expensive operation multiple times when just going through notes (with keyboard especially)
|
|
||||||
// until the users settles on a note
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.noteId === noteId) {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshWithNote(note) {
|
|
||||||
// remember which title was when we found the similar notes
|
|
||||||
this.title = note.title;
|
|
||||||
|
|
||||||
const similarNotes = await server.get('similar-notes/' + this.noteId);
|
|
||||||
|
|
||||||
if (similarNotes.length === 0) {
|
|
||||||
this.$similarNotesContent.text("No similar notes found ...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteIds = similarNotes.flatMap(note => note.notePath);
|
|
||||||
|
|
||||||
await treeCache.getNotes(noteIds, true); // preload all at once
|
|
||||||
|
|
||||||
const $list = $('<div>');
|
|
||||||
|
|
||||||
for (const similarNote of similarNotes) {
|
|
||||||
const note = await treeCache.getNote(similarNote.noteId, true);
|
|
||||||
|
|
||||||
if (!note) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const $item = (await linkService.createNoteLink(similarNote.notePath.join("/")))
|
|
||||||
.css("font-size", 24 * similarNote.coeff);
|
|
||||||
|
|
||||||
$list.append($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$similarNotesContent.empty().append($list);
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({loadResults}) {
|
|
||||||
if (this.note && this.title !== this.note.title) {
|
|
||||||
this.rendered = false;
|
|
||||||
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,7 +51,7 @@ const TPL = `
|
|||||||
.tree-settings-button {
|
.tree-settings-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 20px;
|
right: 10px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
163
src/public/app/widgets/similar_notes.js
Normal file
163
src/public/app/widgets/similar_notes.js
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import linkService from "../services/link.js";
|
||||||
|
import server from "../services/server.js";
|
||||||
|
import treeCache from "../services/tree_cache.js";
|
||||||
|
import TabAwareWidget from "./tab_aware_widget.js";
|
||||||
|
import options from "../services/options.js";
|
||||||
|
|
||||||
|
const TPL = `
|
||||||
|
<div class="similar-notes-widget hide-in-zen-mode">
|
||||||
|
<style>
|
||||||
|
.similar-notes-wrapper {
|
||||||
|
max-height: 100px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-wrapper a {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px dotted var(--main-border-color);
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
margin: 0 3px 0 3px;
|
||||||
|
max-width: 10em;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-expander {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
color: var(--muted-text-color);
|
||||||
|
font-size: 90%;
|
||||||
|
margin: 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-expander-text {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-expander:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-expander:hover hr {
|
||||||
|
border-color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.similar-notes-expander:hover .similar-notes-expander-text {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="similar-notes-expander">
|
||||||
|
<hr class="w-100">
|
||||||
|
|
||||||
|
<div class="similar-notes-expander-text"
|
||||||
|
title="This list contains notes which might be similar to the current note based on textual similarity of note title, its labels and relations."></div>
|
||||||
|
|
||||||
|
<hr class="w-100">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="similar-notes-wrapper"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default class SimilarNotesWidget extends TabAwareWidget {
|
||||||
|
doRender() {
|
||||||
|
this.$widget = $(TPL);
|
||||||
|
this.overflowing();
|
||||||
|
|
||||||
|
this.$similarNotesWrapper = this.$widget.find(".similar-notes-wrapper");
|
||||||
|
this.$similarNotesExpanderText = this.$widget.find(".similar-notes-expander-text");
|
||||||
|
|
||||||
|
this.$expander = this.$widget.find('.similar-notes-expander');
|
||||||
|
this.$expander.on('click', async () => {
|
||||||
|
const collapse = this.$similarNotesWrapper.is(":visible");
|
||||||
|
|
||||||
|
await options.save('similarNotesExpanded', !collapse);
|
||||||
|
|
||||||
|
this.triggerEvent(`similarNotesCollapsedStateChanged`, {collapse});
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.$widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
noteSwitched() {
|
||||||
|
const noteId = this.noteId;
|
||||||
|
|
||||||
|
this.toggleInt(false);
|
||||||
|
this.$similarNotesWrapper.hide(); // we'll open it in refresh() if needed
|
||||||
|
|
||||||
|
// avoid executing this expensive operation multiple times when just going through notes (with keyboard especially)
|
||||||
|
// until the users settles on a note
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.noteId === noteId) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshWithNote(note) {
|
||||||
|
// remember which title was when we found the similar notes
|
||||||
|
this.title = note.title;
|
||||||
|
|
||||||
|
const similarNotes = await server.get('similar-notes/' + this.noteId);
|
||||||
|
|
||||||
|
this.toggleInt(similarNotes.length > 0);
|
||||||
|
|
||||||
|
if (similarNotes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.is('similarNotesExpanded')) {
|
||||||
|
this.$similarNotesWrapper.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$similarNotesExpanderText.text(`${similarNotes.length} similar note${similarNotes.length === 1 ? '': "s"}`);
|
||||||
|
|
||||||
|
const noteIds = similarNotes.flatMap(note => note.notePath);
|
||||||
|
|
||||||
|
await treeCache.getNotes(noteIds, true); // preload all at once
|
||||||
|
|
||||||
|
const $list = $('<div>');
|
||||||
|
|
||||||
|
for (const similarNote of similarNotes) {
|
||||||
|
const note = await treeCache.getNote(similarNote.noteId, true);
|
||||||
|
|
||||||
|
if (!note) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $item = (await linkService.createNoteLink(similarNote.notePath.join("/")))
|
||||||
|
.css("font-size", 24 * similarNote.coeff);
|
||||||
|
|
||||||
|
$list.append($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$similarNotesWrapper.empty().append($list);
|
||||||
|
}
|
||||||
|
|
||||||
|
entitiesReloadedEvent({loadResults}) {
|
||||||
|
if (this.note && this.title !== this.note.title) {
|
||||||
|
this.rendered = false;
|
||||||
|
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
|
||||||
|
* separately but should behave uniformly for the user.
|
||||||
|
*/
|
||||||
|
similarNotesCollapsedStateChangedEvent({collapse}) {
|
||||||
|
if (collapse) {
|
||||||
|
this.$similarNotesWrapper.slideUp(200);
|
||||||
|
} else {
|
||||||
|
this.$similarNotesWrapper.slideDown(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -82,7 +82,7 @@ export default class StandardTopWidget extends BasicWidget {
|
|||||||
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
|
this.$leaveProtectedSessionButton = this.$widget.find(".leave-protected-session-button");
|
||||||
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
|
this.$leaveProtectedSessionButton.on('click', protectedSessionService.leaveProtectedSession);
|
||||||
|
|
||||||
return this.$widget
|
return this.$widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
protectedSessionStartedEvent() {
|
protectedSessionStartedEvent() {
|
||||||
|
@ -39,7 +39,8 @@ const ALLOWED_OPTIONS = new Set([
|
|||||||
'rightPaneVisible',
|
'rightPaneVisible',
|
||||||
'nativeTitleBarVisible',
|
'nativeTitleBarVisible',
|
||||||
'attributeListExpanded',
|
'attributeListExpanded',
|
||||||
'promotedAttributesExpanded'
|
'promotedAttributesExpanded',
|
||||||
|
'similarNotesExpanded'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function getOptions() {
|
function getOptions() {
|
||||||
|
@ -229,7 +229,7 @@ function findSimilarNotes(noteId) {
|
|||||||
|
|
||||||
results.sort((a, b) => a.coeff > b.coeff ? -1 : 1);
|
results.sort((a, b) => a.coeff > b.coeff ? -1 : 1);
|
||||||
|
|
||||||
return results.length > 50 ? results.slice(0, 50) : results;
|
return results.length > 50 ? results.slice(0, 200) : results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +86,8 @@ const defaultOptions = [
|
|||||||
{ name: 'hideArchivedNotes_main', value: 'false', isSynced: false },
|
{ name: 'hideArchivedNotes_main', value: 'false', isSynced: false },
|
||||||
{ name: 'hideIncludedImages_main', value: 'true', isSynced: false },
|
{ name: 'hideIncludedImages_main', value: 'true', isSynced: false },
|
||||||
{ name: 'attributeListExpanded', value: 'false', isSynced: false },
|
{ name: 'attributeListExpanded', value: 'false', isSynced: false },
|
||||||
{ name: 'promotedAttributesExpanded', value: 'false', isSynced: true }
|
{ name: 'promotedAttributesExpanded', value: 'true', isSynced: true },
|
||||||
|
{ name: 'similarNotesExpanded', value: 'true', isSynced: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
function initStartupOptions() {
|
function initStartupOptions() {
|
||||||
|
@ -102,7 +102,7 @@ function searchNotesForAutocomplete(query) {
|
|||||||
fuzzyAttributeSearch: true
|
fuzzyAttributeSearch: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = searchTrimmedNotes(query, parsingContext);
|
const {results} = searchTrimmedNotes(query, parsingContext);
|
||||||
|
|
||||||
highlightSearchResults(results, parsingContext.highlightedTokens);
|
highlightSearchResults(results, parsingContext.highlightedTokens);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user