From 86861f6ec36b6015c20dd0b4529b627fc8f92667 Mon Sep 17 00:00:00 2001
From: SiriusXT <1160925501@qq.com>
Date: Wed, 31 May 2023 18:32:33 +0800
Subject: [PATCH] Show highlighted text in the left pane
---
src/public/app/layouts/desktop_layout.js | 2 +
src/public/app/widgets/highlighted_text.js | 267 ++++++++++++++++++
.../widgets/type_widgets/content_widget.js | 2 +
.../options/text_notes/highlighted_text.js | 90 ++++++
src/routes/api/options.js | 2 +
src/services/options_init.js | 2 +
6 files changed, 365 insertions(+)
create mode 100644 src/public/app/widgets/highlighted_text.js
create mode 100644 src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js
index 17dcc99a7..3a9ec2930 100644
--- a/src/public/app/layouts/desktop_layout.js
+++ b/src/public/app/layouts/desktop_layout.js
@@ -44,6 +44,7 @@ import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
+import HltWidget from "../widgets/highlighted_text.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
@@ -181,6 +182,7 @@ export default class DesktopLayout {
)
.child(new RightPaneContainer()
.child(new TocWidget())
+ .child(new HltWidget())
.child(...this.customWidgets.get('right-pane'))
)
)
diff --git a/src/public/app/widgets/highlighted_text.js b/src/public/app/widgets/highlighted_text.js
new file mode 100644
index 000000000..3dbfc9b5c
--- /dev/null
+++ b/src/public/app/widgets/highlighted_text.js
@@ -0,0 +1,267 @@
+/**
+ * Widget: Show highlighted text in the right pane
+ *
+ * By design there's no support for nonsensical or malformed constructs:
+ * - For example, if there is a formula in the middle of the highlighted text, the two ends of the formula will be regarded as two entries
+ */
+
+import attributeService from "../services/attributes.js";
+import RightPanelWidget from "./right_panel_widget.js";
+import options from "../services/options.js";
+import OnClickButtonWidget from "./buttons/onclick_button.js";
+
+const TPL = `
+
+
+
+
`;
+
+export default class HltWidget extends RightPanelWidget {
+ constructor() {
+ super();
+
+ this.closeHltButton = new CloseHltButton();
+ this.child(this.closeHltButton);
+ }
+
+ get widgetTitle() {
+ return "Highlighted Text";
+ }
+
+ isEnabled() {
+ return super.isEnabled()
+ && this.note.type === 'text'
+ && !this.noteContext.viewScope.hltTemporarilyHidden
+ && this.noteContext.viewScope.viewMode === 'default';
+ }
+
+ async doRenderBody() {
+ this.$body.empty().append($(TPL));
+ this.$hlt = this.$body.find('.hlt');
+ this.$body.find('.hlt-widget').append(this.closeHltButton.render());
+ }
+
+ async refreshWithNote(note) {
+ const hltLabel = note.getLabel('hlt');
+
+ if (hltLabel?.value === 'hide') {
+ this.toggleInt(false);
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ return;
+ }
+
+ let $hlt = "", hltColors = [], hltBgColors = [];
+
+ let optionsHltColors = JSON.parse(options.get('highlightedTextColors'));
+ let optionsHltBgColors = JSON.parse(options.get('highlightedTextBgColors'));
+ // Check for type text unconditionally in case alwaysShowWidget is set
+ if (this.note.type === 'text') {
+ const { content } = await note.getNoteComplement();
+ //hltColors/hltBgColors are the colors/background-color that appear in notes and in options
+ ({ $hlt, hltColors, hltBgColors } = await this.getHlt(content, optionsHltColors, optionsHltBgColors));
+ }
+ this.$hlt.html($hlt);
+ this.toggleInt(
+ ["", "show"].includes(hltLabel?.value)
+ || hltColors!=""
+ || hltBgColors!=""
+ );
+
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ }
+ //Converts color values in RGB, RGBA, or HSL format to hexadecimal format, removing transparency
+ colorToHex(color) {
+ function rgbToHex(rgb) {
+ // Converts color values in RGB or RGBA format to hexadecimal format
+ var rgba = rgb.match(/\d+/g);
+ var r = parseInt(rgba[0]);
+ var g = parseInt(rgba[1]);
+ var b = parseInt(rgba[2]);
+ var hex = "#";
+ hex += (r < 16 ? "0" : "") + r.toString(16);
+ hex += (g < 16 ? "0" : "") + g.toString(16);
+ hex += (b < 16 ? "0" : "") + b.toString(16);
+ return hex;
+ }
+
+ function hslToHex(hsl) {
+ // Convert color values in HSL format to RGB format and then to hexadecimal format
+ var hslValues = hsl.match(/\d+(\.\d+)?/g);
+ var h = parseFloat(hslValues[0]) / 360;
+ var s = parseFloat(hslValues[1]) / 100;
+ var l = parseFloat(hslValues[2]) / 100;
+ var r, g, b;
+
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ function hueToRgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ }
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1 / 3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1 / 3);
+ }
+
+ var hex = "#";
+ hex += (Math.round(r * 255) < 16 ? "0" : "") + Math.round(r * 255).toString(16);
+ hex += (Math.round(g * 255) < 16 ? "0" : "") + Math.round(g * 255).toString(16);
+ hex += (Math.round(b * 255) < 16 ? "0" : "") + Math.round(b * 255).toString(16);
+ return hex;
+ }
+ if (color.indexOf("rgb") !== -1) {
+ return rgbToHex(color);
+ } else if (color.indexOf("hsl") !== -1) {
+ return hslToHex(color);
+ } else {
+ return "";
+ }
+ }
+ // Determine whether the highlighted color is in the options, avoid errors caused by errors in color conversion,
+ // and the error of each value is acceptable within 2
+ hexIsInOptionHexs(targetColor, optionColors){
+ for (let i = 0; i < optionColors.length; i++) {
+ if (Math.abs(parseInt(optionColors[i].slice(1, 3), 16) - parseInt(targetColor.slice(1, 3), 16)) > 2) { continue; }
+ if (Math.abs(parseInt(optionColors[i].slice(3, 5), 16) - parseInt(targetColor.slice(3, 5), 16)) > 2) { continue; }
+ if (Math.abs(parseInt(optionColors[i].slice(5, 7), 16) - parseInt(targetColor.slice(5, 7), 16)) > 2) { continue; }
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Builds a jquery table of helight text.
+ */
+ getHlt(html, optionsHltColors, optionsHltBgColors) {
+ const hltBCs = $(html).find(`span[style*="background-color"],span[style*="color"]`)
+ const $hlt = $("");
+ let hltColors = [];
+ let hltBgColors = [];
+ for (let hltIndex = 0; hltIndex');
+
+ if (color != "") {
+ var hexColor = this.colorToHex(color);
+ if (this.hexIsInOptionHexs(hexColor,optionsHltColors)) {
+ $li.html(hltText)
+ hltColors.push(hexColor);
+ liDisplay=true;
+ }
+ }
+ if (bgColor != "") {
+ var hexBgColor = this.colorToHex(bgColor);
+ if (this.hexIsInOptionHexs(hexBgColor,optionsHltBgColors)) {
+ //When you need to add a background color, in order to make the display more comfortable, change the background color to transparent
+ $li.html(hltText.css("background-color", hexBgColor+"80"))
+ hltBgColors.push(hexBgColor);
+ liDisplay=true;
+ }
+ }
+ if(!liDisplay){
+ $li.css("display","none");
+ }
+ //The font color and background color may be nested or adjacent to each other. At this time, connect the front and back li to avoid interruption
+ if(hltIndex!=0 && hltBCs[hltIndex-1].nextSibling ===hltBCs[hltIndex] && $hlt.children().last().css("display")!="none"){
+ $hlt.children().last().append($li.html());
+ }else{
+ $li.on("click", () => this.jumpToHlt(hltIndex));
+ $hlt.append($li);
+ }
+
+ };
+ return {
+ $hlt,
+ hltColors,
+ hltBgColors
+ };
+ }
+
+ async jumpToHlt(hltIndex) {
+ const isReadOnly = await this.noteContext.isReadOnly();
+ if (isReadOnly) {
+ const $container = await this.noteContext.getContentElement();
+ const hltElement = $container.find(`span[style*="background-color"],span[style*="color"]`)[hltIndex];
+
+ if (hltElement != null) {
+ hltElement.scrollIntoView({ behavior: "smooth", block: "center" });
+ }
+ } else {
+ const textEditor = await this.noteContext.getTextEditor();
+ $(textEditor.editing.view.domRoots.values().next().value).find(`span[style*="background-color"],span[style*="color"]`)[hltIndex].scrollIntoView({
+ behavior: "smooth", block: "center"
+ });
+ }
+ }
+
+ async closeHltCommand() {
+ this.noteContext.viewScope.hltTemporarilyHidden = true;
+ await this.refresh();
+ this.triggerCommand('reEvaluateRightPaneVisibility');
+ }
+
+ async entitiesReloadedEvent({ loadResults }) {
+ if (loadResults.isNoteContentReloaded(this.noteId)) {
+ await this.refresh();
+ } else if (loadResults.getAttributes().find(attr => attr.type === 'label'
+ && (attr.name.toLowerCase().includes('readonly') || attr.name === 'hlt')
+ && attributeService.isAffecting(attr, this.note))) {
+ await this.refresh();
+ }
+ }
+}
+
+
+class CloseHltButton extends OnClickButtonWidget {
+ constructor() {
+ super();
+
+ this.icon("bx-x")
+ .title("Close HLT")
+ .titlePlacement("bottom")
+ .onClick((widget, e) => {
+ e.stopPropagation();
+
+ widget.triggerCommand("closeHlt");
+ })
+ .class("icon-action close-hlt");
+ }
+}
diff --git a/src/public/app/widgets/type_widgets/content_widget.js b/src/public/app/widgets/type_widgets/content_widget.js
index 967c996e5..f7a4846bd 100644
--- a/src/public/app/widgets/type_widgets/content_widget.js
+++ b/src/public/app/widgets/type_widgets/content_widget.js
@@ -7,6 +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 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";
@@ -61,6 +62,7 @@ const CONTENT_WIDGETS = {
_optionsTextNotes: [
HeadingStyleOptions,
TableOfContentsOptions,
+ HighlightedTextOptions,
TextAutoReadOnlySizeOptions
],
_optionsCodeNotes: [
diff --git a/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
new file mode 100644
index 000000000..7fc2ce825
--- /dev/null
+++ b/src/public/app/widgets/type_widgets/options/text_notes/highlighted_text.js
@@ -0,0 +1,90 @@
+import OptionsWidget from "../options_widget.js";
+
+const TPL = `
+
+
+
Highlighted Text
+
+ Displays highlighted text in the left pane. You can customize the highlighted text displayed in the left pane:
+ Text color:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Background color:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+