Merge branch 'master' into next61

# Conflicts:
#	package-lock.json
#	src/public/app/services/note_content_renderer.js
#	src/public/app/widgets/note_tree.js
#	src/routes/routes.js
#	src/services/consistency_checks.js
#	src/services/notes.js
#	src/services/task_context.js
This commit is contained in:
zadam
2023-07-10 18:20:36 +02:00
41 changed files with 253 additions and 192 deletions

View File

@@ -26,7 +26,7 @@ export default class AbstractBulkAction {
}
}
// to be overriden
// to be overridden
doRender() {}
async saveAction(data) {

View File

@@ -50,7 +50,7 @@ export default class RightDropdownButtonWidget extends BasicWidget {
this.$widget.find(".dropdown-menu").append(this.$dropdownContent);
}
// to be overriden
// to be overridden
async dropdownShow() {}
hideDropdown() {

View File

@@ -25,7 +25,7 @@ const TPL = `
</div>
<div class="checkbox">
<label title="Normal (soft) deletion only marks the notes as deleted and they can be undeleted (in recent changes dialog) within a period of time. Checking this option will erase the notes immediatelly and it won't be possible to undelete the notes.">
<label title="Normal (soft) deletion only marks the notes as deleted and they can be undeleted (in recent changes dialog) within a period of time. Checking this option will erase the notes immediately and it won't be possible to undelete the notes.">
<input class="erase-notes" value="1" type="checkbox">
erase notes permanently (can't be undone), including all clones. This will force application reload.

View File

@@ -10,20 +10,20 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js";
const TPL = `<div class="highlists-list-widget">
const TPL = `<div class="highlights-list-widget">
<style>
.highlists-list-widget {
.highlights-list-widget {
padding: 10px;
contain: none;
overflow: auto;
position: relative;
}
.highlists-list > ol {
.highlights-list > ol {
padding-left: 20px;
}
.highlists-list li {
.highlights-list li {
cursor: pointer;
margin-bottom: 3px;
text-align: justify;
@@ -32,18 +32,18 @@ const TPL = `<div class="highlists-list-widget">
hyphens: auto;
}
.highlists-list li:hover {
.highlights-list li:hover {
font-weight: bold;
}
.close-highlists-list {
.close-highlights-list {
position: absolute;
top: 2px;
right: 0px;
}
</style>
<span class="highlists-list"></span>
<span class="highlights-list"></span>
</div>`;
export default class HighlightsListWidget extends RightPanelWidget {
@@ -55,61 +55,61 @@ export default class HighlightsListWidget extends RightPanelWidget {
}
get widgetTitle() {
return "Highlighted Text";
return "Highlights List";
}
isEnabled() {
return super.isEnabled()
&& this.note.type === 'text'
&& !this.noteContext.viewScope.highlightedTextTemporarilyHidden
&& !this.noteContext.viewScope.highlightsListTemporarilyHidden
&& this.noteContext.viewScope.viewMode === 'default';
}
async doRenderBody() {
this.$body.empty().append($(TPL));
this.$highlightsList = this.$body.find('.highlists-list');
this.$body.find('.highlists-list-widget').append(this.closeHltButton.render());
this.$highlightsList = this.$body.find('.highlights-list');
this.$body.find('.highlights-list-widget').append(this.closeHltButton.render());
}
async refreshWithNote(note) {
/* The reason for adding highlightedTextPreviousVisible is to record whether the previous state
of the highlightedText is hidden or displayed, and then let it be displayed/hidden at the initial time.
/* The reason for adding highlightsListPreviousVisible is to record whether the previous state
of the highlightsList is hidden or displayed, and then let it be displayed/hidden at the initial time.
If there is no such value, when the right panel needs to display toc but not highlighttext,
every time the note content is changed, highlighttext Widget will appear and then close immediately,
because getHlt function will consume time */
if (this.noteContext.viewScope.highlightedTextPreviousVisible) {
if (this.noteContext.viewScope.highlightsListPreviousVisible) {
this.toggleInt(true);
} else {
this.toggleInt(false);
}
const optionsHlt = JSON.parse(options.get('highlightedText'));
const optionsHighlightsList = JSON.parse(options.get('highlightsList'));
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHlt) {
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHighlightsList) {
this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility");
return;
}
let $highlightsList = "", hltLiCount = -1;
let $highlightsList = "", hlLiCount = -1;
// Check for type text unconditionally in case alwaysShowWidget is set
if (this.note.type === 'text') {
const {content} = await note.getNoteComplement();
({$highlightsList, hltLiCount} = this.getHighlightList(content, optionsHlt));
({$highlightsList, hlLiCount} = this.getHighlightList(content, optionsHighlightsList));
}
this.$highlightsList.empty().append($highlightsList);
if (hltLiCount > 0) {
if (hlLiCount > 0) {
this.toggleInt(true);
this.noteContext.viewScope.highlightedTextPreviousVisible = true;
this.noteContext.viewScope.highlightsListPreviousVisible = true;
} else {
this.toggleInt(false);
this.noteContext.viewScope.highlightedTextPreviousVisible = false;
this.noteContext.viewScope.highlightsListPreviousVisible = false;
}
this.triggerCommand("reEvaluateRightPaneVisibility");
}
getHighlightList(content, optionsHlt) {
getHighlightList(content, optionsHighlightsList) {
// matches a span containing background-color
const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
// matches a span containing color
@@ -120,27 +120,27 @@ export default class HighlightsListWidget extends RightPanelWidget {
const regex4 = /<strong>[\s\S]*?<\/strong>/gi;
// match underline
const regex5 = /<u>[\s\S]*?<\/u>/g;
// Possible values in optionsHlt '["bold","italic","underline","color","bgColor"]'
// Possible values in optionsHighlightsList '["bold","italic","underline","color","bgColor"]'
// element priority span>i>strong>u
let findSubStr = "", combinedRegexStr = "";
if (optionsHlt.includes("bgColor")) {
findSubStr += `,span[style*="background-color"]`;
if (optionsHighlightsList.includes("bgColor")) {
findSubStr += `,span[style*="background-color"]:not(section.include-note span[style*="background-color"])`;
combinedRegexStr += `|${regex1.source}`;
}
if (optionsHlt.includes("color")) {
findSubStr += `,span[style*="color"]`;
if (optionsHighlightsList.includes("color")) {
findSubStr += `,span[style*="color"]:not(section.include-note span[style*="color"])`;
combinedRegexStr += `|${regex2.source}`;
}
if (optionsHlt.includes("italic")) {
findSubStr += `,i`;
if (optionsHighlightsList.includes("italic")) {
findSubStr += `,i:not(section.include-note i)`;
combinedRegexStr += `|${regex3.source}`;
}
if (optionsHlt.indexOf("bold")) {
findSubStr += `,strong`;
if (optionsHighlightsList.includes("bold")) {
findSubStr += `,strong:not(section.include-note strong)`;
combinedRegexStr += `|${regex4.source}`;
}
if (optionsHlt.includes("underline")) {
findSubStr += `,u`;
if (optionsHighlightsList.includes("underline")) {
findSubStr += `,u:not(section.include-note u)`;
combinedRegexStr += `|${regex5.source}`;
}
@@ -148,7 +148,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
combinedRegexStr = `(` + combinedRegexStr.substring(1) + `)`;
const combinedRegex = new RegExp(combinedRegexStr, 'gi');
const $highlightsList = $("<ol>");
let prevEndIndex = -1, hltLiCount = 0;
let prevEndIndex = -1, hlLiCount = 0;
for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
const subHtml = match[0];
const startIndex = match.index;
@@ -158,16 +158,18 @@ export default class HighlightsListWidget extends RightPanelWidget {
$highlightsList.children().last().append(subHtml);
} else {
// TODO: can't be done with $(subHtml).text()?
const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim();
//Cant remember why regular expressions are used here, but modified to $(subHtml).text() works as expected
//const hasText = [...subHtml.matchAll(/(?<=^|>)[^><]+?(?=<|$)/g)].map(matchTmp => matchTmp[0]).join('').trim();
const hasText = $(subHtml).text().trim();
if (hasText) {
$highlightsList.append(
$('<li>')
.html(subHtml)
.on("click", () => this.jumpToHighlightedText(findSubStr, hltIndex))
.on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex))
);
hltLiCount++;
hlLiCount++;
} else {
// hide li if its text content is empty
continue;
@@ -177,11 +179,11 @@ export default class HighlightsListWidget extends RightPanelWidget {
}
return {
$highlightsList,
hltLiCount
hlLiCount
};
}
async jumpToHighlightedText(findSubStr, itemIndex) {
async jumpToHighlightsList(findSubStr, itemIndex) {
const isReadOnly = await this.noteContext.isReadOnly();
let targetElement;
if (isReadOnly) {
@@ -224,7 +226,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
}
async closeHltCommand() {
this.noteContext.viewScope.highlightedTextTemporarilyHidden = true;
this.noteContext.viewScope.highlightsListTemporarilyHidden = true;
await this.refresh();
this.triggerCommand('reEvaluateRightPaneVisibility');
}
@@ -245,13 +247,13 @@ class CloseHltButton extends OnClickButtonWidget {
super();
this.icon("bx-x")
.title("Close HighlightedTextWidget")
.title("Close HighlightsListWidget")
.titlePlacement("bottom")
.onClick((widget, e) => {
e.stopPropagation();
widget.triggerCommand("closeHlt");
})
.class("icon-action close-highlists-list");
.class("icon-action close-highlights-list");
}
}

View File

@@ -9584,7 +9584,7 @@ const icons = [
"term": [
"honor",
"honour",
"acheivement"
"achievement"
]
},
{
@@ -9595,7 +9595,7 @@ const icons = [
"term": [
"honor",
"honour",
"acheivement"
"achievement"
]
},
{

View File

@@ -166,7 +166,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
generateColorFromString(str) {
if (this.themeStyle === "dark") {
str = `0${str}`; // magic lightening modifier
str = `0${str}`; // magic lightning modifier
}
let hash = 0;

View File

@@ -1116,7 +1116,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const note = froca.getNoteFromCache(ecAttr.noteId);
if (note && note.getChildNoteIds().includes(ecAttr.value)) {
// there's a new /deleted imageLink betwen note and its image child - which can show/hide
// there's a new /deleted imageLink between note and its image child - which can show/hide
// the image (if there is an imageLink relation between parent and child,
// then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree)
noteIdsToReload.add(ecAttr.noteId);

View File

@@ -39,7 +39,7 @@ export default class AbstractSearchOption extends Component {
}
}
// to be overriden
// to be overridden
doRender() {}
async deleteOption() {

View File

@@ -2,6 +2,7 @@ import AbstractSearchOption from "./abstract_search_option.js";
import SpacedUpdate from "../../services/spaced_update.js";
import server from "../../services/server.js";
import shortcutService from "../../services/shortcuts.js";
import appContext from "../../components/app_context.js";
const TPL = `
<tr>
@@ -56,6 +57,7 @@ export default class SearchString extends AbstractSearchOption {
this.spacedUpdate = new SpacedUpdate(async () => {
const searchString = this.$searchString.val();
appContext.lastSearchString = searchString;
await this.setAttribute('label', 'searchString', searchString);
@@ -84,6 +86,7 @@ export default class SearchString extends AbstractSearchOption {
}
focusOnSearchDefinitionEvent() {
this.$searchString.focus();
this.$searchString.val(appContext.lastSearchString).focus().select();
this.spacedUpdate.scheduleUpdate();
}
}

View File

@@ -187,7 +187,7 @@ export default class TocWidget extends RightPanelWidget {
if (isReadOnly) {
const $container = await this.noteContext.getContentElement();
const headingElement = $container.find(":header")[headingIndex];
const headingElement = $container.find(":header:not(section.include-note :header)")[headingIndex];
if (headingElement != null) {
headingElement.scrollIntoView({ behavior: "smooth" });
@@ -206,7 +206,7 @@ export default class TocWidget extends RightPanelWidget {
// navigate (note that the TOC rendering and other TOC
// entries' navigation could be wrong too)
if (headingNode != null) {
$(textEditor.editing.view.domRoots.values().next().value).find(':header')[headingIndex].scrollIntoView({
$(textEditor.editing.view.domRoots.values().next().value).find(':header:not(section.include-note :header)')[headingIndex].scrollIntoView({
behavior: 'smooth'
});
}

View File

@@ -77,7 +77,7 @@ const TPL = `
*
* Discussion of storing svg in the note:
* - Pro: we will combat bit-rot. Showing the SVG will be very fast and easy, since it is already there.
* - Con: The note will get bigger (~40-50%?), we will generate more bandwith. However, using trilium
* - Con: The note will get bigger (~40-50%?), we will generate more bandwidth. However, using trilium
* desktop instance mitigates that issue.
*
* Roadmap:

View File

@@ -7,7 +7,7 @@ import MaxContentWidthOptions from "./options/appearance/max_content_width.js";
import KeyboardShortcutsOptions from "./options/shortcuts.js";
import HeadingStyleOptions from "./options/text_notes/heading_style.js";
import TableOfContentsOptions from "./options/text_notes/table_of_contents.js";
import HighlightedTextOptions from "./options/text_notes/highlighted_text.js";
import HighlightsListOptions from "./options/text_notes/highlights_list.js";
import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js";
import VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js";
import WrapLinesOptions from "./options/code_notes/wrap_lines.js";
@@ -63,7 +63,7 @@ const CONTENT_WIDGETS = {
_optionsTextNotes: [
HeadingStyleOptions,
TableOfContentsOptions,
HighlightedTextOptions,
HighlightsListOptions,
TextAutoReadOnlySizeOptions
],
_optionsCodeNotes: [

View File

@@ -96,7 +96,7 @@ export default class EtapiOptions extends OptionsWidget {
.append($("<td>").append(
$('<span class="bx bx-pen token-table-button" title="Rename this token"></span>')
.on("click", () => this.renameToken(token.etapiTokenId, token.name)),
$('<span class="bx bx-trash token-table-button" title="Delete / deactive this token"></span>')
$('<span class="bx bx-trash token-table-button" title="Delete / deactivate this token"></span>')
.on("click", () => this.deleteToken(token.etapiTokenId, token.name))
))
);

View File

@@ -1,40 +0,0 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Highlighted Text</h4>
<p>You can customize the highlighted text displayed in the right panel:</p>
</div>
<label><input type="checkbox" class="highlighted-text-check" value="bold"> Bold font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="italic"> Italic font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="underline"> Underlined font &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="color"> Font with color &nbsp;</label>
<label><input type="checkbox" class="highlighted-text-check" value="bgColor"> Font with background color &nbsp;</label>
</div>
</div>`;
export default class HighlightedTextOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$hlt = this.$widget.find("input.highlighted-text-check");
this.$hlt.on('change', () => {
const hltVals = this.$widget.find('input.highlighted-text-check[type="checkbox"]:checked').map(function () {
return this.value;
}).get();
this.updateOption('highlightedText', JSON.stringify(hltVals));
});
}
async optionsLoaded(options) {
const hltVals = JSON.parse(options.highlightedText);
this.$widget.find('input.highlighted-text-check[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltVals) !== -1) {
$(this).prop("checked", true);
} else {
$(this).prop("checked", false);
}
});
}
}

View File

@@ -0,0 +1,40 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Highlights List</h4>
<p>You can customize the highlights list displayed in the right panel:</p>
</div>
<label><input type="checkbox" class="highlights-list-check" value="bold"> Bold font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="italic"> Italic font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="underline"> Underlined font &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="color"> Font with color &nbsp;</label>
<label><input type="checkbox" class="highlights-list-check" value="bgColor"> Font with background color &nbsp;</label>
</div>
</div>`;
export default class HighlightsListOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$hlt = this.$widget.find("input.highlights-list-check");
this.$hlt.on('change', () => {
const hltVals = this.$widget.find('input.highlights-list-check[type="checkbox"]:checked').map(function () {
return this.value;
}).get();
this.updateOption('highlightsList', JSON.stringify(hltVals));
});
}
async optionsLoaded(options) {
const hltVals = JSON.parse(options.highlightsList);
this.$widget.find('input.highlights-list-check[type="checkbox"]').each(function () {
if ($.inArray($(this).val(), hltVals) !== -1) {
$(this).prop("checked", true);
} else {
$(this).prop("checked", false);
}
});
}
}

View File

@@ -412,7 +412,7 @@ export default class RelationMapTypeWidget extends TypeWidget {
}
});
// if there's no event, then this has been triggered programatically
// if there's no event, then this has been triggered programmatically
if (!originalEvent) {
return;
}