mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 15:11:31 +08:00 
			
		
		
		
	Let toc and highlights_list display formulas
This commit is contained in:
		
							parent
							
								
									7a11f9aaff
								
							
						
					
					
						commit
						bb97e1a661
					
				@ -10,6 +10,7 @@ import RightPanelWidget from "./right_panel_widget.js";
 | 
				
			|||||||
import options from "../services/options.js";
 | 
					import options from "../services/options.js";
 | 
				
			||||||
import OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
					import OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
				
			||||||
import appContext from "../components/app_context.js";
 | 
					import appContext from "../components/app_context.js";
 | 
				
			||||||
 | 
					import libraryLoader from "../services/library_loader.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TPL = `<div class="highlights-list-widget">
 | 
					const TPL = `<div class="highlights-list-widget">
 | 
				
			||||||
    <style>
 | 
					    <style>
 | 
				
			||||||
@ -52,7 +53,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
                .icon("bx-slider")
 | 
					                .icon("bx-slider")
 | 
				
			||||||
                .title("Options")
 | 
					                .title("Options")
 | 
				
			||||||
                .titlePlacement("left")
 | 
					                .titlePlacement("left")
 | 
				
			||||||
                .onClick(() => appContext.tabManager.openContextWithNote('_optionsTextNotes', {activate: true}))
 | 
					                .onClick(() => appContext.tabManager.openContextWithNote('_optionsTextNotes', { activate: true }))
 | 
				
			||||||
                .class("icon-action"),
 | 
					                .class("icon-action"),
 | 
				
			||||||
            new OnClickButtonWidget()
 | 
					            new OnClickButtonWidget()
 | 
				
			||||||
                .icon("bx-x")
 | 
					                .icon("bx-x")
 | 
				
			||||||
@ -97,8 +98,8 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
        let $highlightsList = "", hlLiCount = -1;
 | 
					        let $highlightsList = "", hlLiCount = -1;
 | 
				
			||||||
        // Check for type text unconditionally in case alwaysShowWidget is set
 | 
					        // Check for type text unconditionally in case alwaysShowWidget is set
 | 
				
			||||||
        if (this.note.type === 'text') {
 | 
					        if (this.note.type === 'text') {
 | 
				
			||||||
            const {content} = await note.getNoteComplement();
 | 
					            const { content } = await note.getNoteComplement();
 | 
				
			||||||
            ({$highlightsList, hlLiCount} = this.getHighlightList(content, optionsHighlightsList));
 | 
					            ({ $highlightsList, hlLiCount } = await this.getHighlightList(content, optionsHighlightsList));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.$highlightsList.empty().append($highlightsList);
 | 
					        this.$highlightsList.empty().append($highlightsList);
 | 
				
			||||||
        if (hlLiCount > 0) {
 | 
					        if (hlLiCount > 0) {
 | 
				
			||||||
@ -112,7 +113,79 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
        this.triggerCommand("reEvaluateRightPaneVisibility");
 | 
					        this.triggerCommand("reEvaluateRightPaneVisibility");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getHighlightList(content, optionsHighlightsList) {
 | 
					    extractOuterTag(htmlStr) {
 | 
				
			||||||
 | 
					        if (htmlStr === null) {
 | 
				
			||||||
 | 
					            return null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Regular expressions that match only the outermost tag
 | 
				
			||||||
 | 
					        const regex = /^<([a-zA-Z]+)([^>]*)>/;
 | 
				
			||||||
 | 
					        const match = htmlStr.match(regex);
 | 
				
			||||||
 | 
					        if (match) {
 | 
				
			||||||
 | 
					            const tagName = match[1].toLowerCase(); // Extract tag name
 | 
				
			||||||
 | 
					            const attributes = match[2].trim(); // Extract label attributes
 | 
				
			||||||
 | 
					            return { tagName, attributes };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    areOuterTagsConsistent(str1, str2) {
 | 
				
			||||||
 | 
					        const tag1 = this.extractOuterTag(str1);
 | 
				
			||||||
 | 
					        const tag2 = this.extractOuterTag(str2);
 | 
				
			||||||
 | 
					        // If one of them has no label, returns false
 | 
				
			||||||
 | 
					        if (!tag1 || !tag2) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Compare tag names and attributes to see if they are the same
 | 
				
			||||||
 | 
					        return tag1.tagName === tag2.tagName && tag1.attributes === tag2.attributes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Rendering formulas in strings using katex
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param {string} html Note's html content
 | 
				
			||||||
 | 
					     * @returns {string} The HTML content with mathematical formulas rendered by KaTeX.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async replaceMathTextWithKatax(html) {
 | 
				
			||||||
 | 
					        const mathTextRegex = /<span class="math-tex">\\\(([\s\S]*?)\\\)<\/span>/g;
 | 
				
			||||||
 | 
					        var matches = [...html.matchAll(mathTextRegex)];
 | 
				
			||||||
 | 
					        let modifiedText = html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (matches.length > 0) {
 | 
				
			||||||
 | 
					            // Process all matches asynchronously
 | 
				
			||||||
 | 
					            for (const match of matches) {
 | 
				
			||||||
 | 
					                let latexCode = match[1];
 | 
				
			||||||
 | 
					                let rendered;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    rendered = katex.renderToString(latexCode, {
 | 
				
			||||||
 | 
					                        throwOnError: false
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } catch (e) {
 | 
				
			||||||
 | 
					                    if (e instanceof ReferenceError && e.message.includes('katex is not defined')) {
 | 
				
			||||||
 | 
					                        // Load KaTeX if it is not already loaded
 | 
				
			||||||
 | 
					                        await libraryLoader.requireLibrary(libraryLoader.KATEX);
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            rendered = katex.renderToString(latexCode, {
 | 
				
			||||||
 | 
					                                throwOnError: false
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        } catch (renderError) {
 | 
				
			||||||
 | 
					                            console.error("KaTeX rendering error after loading library:", renderError);
 | 
				
			||||||
 | 
					                            rendered = match[0]; // Fall back to original if error persists
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        console.error("KaTeX rendering error:", e);
 | 
				
			||||||
 | 
					                        rendered = match[0]; // Fall back to original on error
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Replace the matched formula in the modified text
 | 
				
			||||||
 | 
					                modifiedText = modifiedText.replace(match[0], rendered);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return modifiedText;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async getHighlightList(content, optionsHighlightsList) {
 | 
				
			||||||
        // matches a span containing background-color
 | 
					        // matches a span containing background-color
 | 
				
			||||||
        const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
 | 
					        const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
 | 
				
			||||||
        // matches a span containing color
 | 
					        // matches a span containing color
 | 
				
			||||||
@ -152,6 +225,10 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
        const combinedRegex = new RegExp(combinedRegexStr, 'gi');
 | 
					        const combinedRegex = new RegExp(combinedRegexStr, 'gi');
 | 
				
			||||||
        const $highlightsList = $("<ol>");
 | 
					        const $highlightsList = $("<ol>");
 | 
				
			||||||
        let prevEndIndex = -1, hlLiCount = 0;
 | 
					        let prevEndIndex = -1, hlLiCount = 0;
 | 
				
			||||||
 | 
					        let prevSubHtml = null;
 | 
				
			||||||
 | 
					        // Used to determine if a string is only a formula
 | 
				
			||||||
 | 
					        const onlyMathRegex = /^<span class="math-tex">\\\([^\)]*?\)<\/span>(?:<span class="math-tex">\\\([^\)]*?\)<\/span>)*$/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
 | 
					        for (let match = null, hltIndex = 0; ((match = combinedRegex.exec(content)) !== null); hltIndex++) {
 | 
				
			||||||
            const subHtml = match[0];
 | 
					            const subHtml = match[0];
 | 
				
			||||||
            const startIndex = match.index;
 | 
					            const startIndex = match.index;
 | 
				
			||||||
@ -166,11 +243,19 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
                const hasText = $(subHtml).text().trim();
 | 
					                const hasText = $(subHtml).text().trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (hasText) {
 | 
					                if (hasText) {
 | 
				
			||||||
                    $highlightsList.append(
 | 
					                    const substring = content.substring(prevEndIndex, startIndex);
 | 
				
			||||||
                        $('<li>')
 | 
					                    //If the two elements have the same style and there are only formulas in between, append the formulas and the current element to the end of the previous element.
 | 
				
			||||||
                            .html(subHtml)
 | 
					                    if (this.areOuterTagsConsistent(prevSubHtml, subHtml) && onlyMathRegex.test(substring)) {
 | 
				
			||||||
                            .on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex))
 | 
					                        const $lastLi = $highlightsList.children('li').last();
 | 
				
			||||||
                    );
 | 
					                        $lastLi.append(await this.replaceMathTextWithKatax(substring));
 | 
				
			||||||
 | 
					                        $lastLi.append(subHtml);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $highlightsList.append(
 | 
				
			||||||
 | 
					                            $('<li>')
 | 
				
			||||||
 | 
					                                .html(subHtml)
 | 
				
			||||||
 | 
					                                .on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex))
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    hlLiCount++;
 | 
					                    hlLiCount++;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
@ -179,6 +264,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            prevEndIndex = endIndex;
 | 
					            prevEndIndex = endIndex;
 | 
				
			||||||
 | 
					            prevSubHtml = subHtml;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            $highlightsList,
 | 
					            $highlightsList,
 | 
				
			||||||
@ -234,7 +320,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
				
			|||||||
        this.triggerCommand('reEvaluateRightPaneVisibility');
 | 
					        this.triggerCommand('reEvaluateRightPaneVisibility');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async entitiesReloadedEvent({loadResults}) {
 | 
					    async entitiesReloadedEvent({ loadResults }) {
 | 
				
			||||||
        if (loadResults.isNoteContentReloaded(this.noteId)) {
 | 
					        if (loadResults.isNoteContentReloaded(this.noteId)) {
 | 
				
			||||||
            await this.refresh();
 | 
					            await this.refresh();
 | 
				
			||||||
        } else if (loadResults.getAttributeRows().find(attr => attr.type === 'label'
 | 
					        } else if (loadResults.getAttributeRows().find(attr => attr.type === 'label'
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ import RightPanelWidget from "./right_panel_widget.js";
 | 
				
			|||||||
import options from "../services/options.js";
 | 
					import options from "../services/options.js";
 | 
				
			||||||
import OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
					import OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
				
			||||||
import appContext from "../components/app_context.js";
 | 
					import appContext from "../components/app_context.js";
 | 
				
			||||||
 | 
					import libraryLoader from "../services/library_loader.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TPL = `<div class="toc-widget">
 | 
					const TPL = `<div class="toc-widget">
 | 
				
			||||||
    <style>
 | 
					    <style>
 | 
				
			||||||
@ -120,6 +121,52 @@ export default class TocWidget extends RightPanelWidget {
 | 
				
			|||||||
        this.triggerCommand("reEvaluateRightPaneVisibility");
 | 
					        this.triggerCommand("reEvaluateRightPaneVisibility");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Rendering formulas in strings using katex
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param {string} html Note's html content
 | 
				
			||||||
 | 
					     * @returns {string} The HTML content with mathematical formulas rendered by KaTeX.
 | 
				
			||||||
 | 
					     */  
 | 
				
			||||||
 | 
					    async replaceMathTextWithKatax(html) {
 | 
				
			||||||
 | 
					        const mathTextRegex = /<span class="math-tex">\\\(([\s\S]*?)\\\)<\/span>/g;
 | 
				
			||||||
 | 
					        var matches = [...html.matchAll(mathTextRegex)];
 | 
				
			||||||
 | 
					        let modifiedText = html;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        if (matches.length > 0) {
 | 
				
			||||||
 | 
					            // Process all matches asynchronously
 | 
				
			||||||
 | 
					            for (const match of matches) {
 | 
				
			||||||
 | 
					                let latexCode = match[1];
 | 
				
			||||||
 | 
					                let rendered;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    rendered = katex.renderToString(latexCode, {
 | 
				
			||||||
 | 
					                        throwOnError: false
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } catch (e) {
 | 
				
			||||||
 | 
					                    if (e instanceof ReferenceError && e.message.includes('katex is not defined')) {
 | 
				
			||||||
 | 
					                        // Load KaTeX if it is not already loaded
 | 
				
			||||||
 | 
					                        await libraryLoader.requireLibrary(libraryLoader.KATEX);
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            rendered = katex.renderToString(latexCode, {
 | 
				
			||||||
 | 
					                                throwOnError: false
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        } catch (renderError) {
 | 
				
			||||||
 | 
					                            console.error("KaTeX rendering error after loading library:", renderError);
 | 
				
			||||||
 | 
					                            rendered = match[0]; // Fall back to original if error persists
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        console.error("KaTeX rendering error:", e);
 | 
				
			||||||
 | 
					                        rendered = match[0]; // Fall back to original on error
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					                // Replace the matched formula in the modified text
 | 
				
			||||||
 | 
					                modifiedText = modifiedText.replace(match[0], rendered);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return modifiedText;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Builds a jquery table of contents.
 | 
					     * Builds a jquery table of contents.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -128,7 +175,7 @@ export default class TocWidget extends RightPanelWidget {
 | 
				
			|||||||
     *         with an onclick event that will cause the document to scroll to
 | 
					     *         with an onclick event that will cause the document to scroll to
 | 
				
			||||||
     *         the desired position.
 | 
					     *         the desired position.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    getToc(html) {
 | 
					    async getToc(html) {
 | 
				
			||||||
        // Regular expression for headings <h1>...</h1> using non-greedy
 | 
					        // Regular expression for headings <h1>...</h1> using non-greedy
 | 
				
			||||||
        // matching and backreferences
 | 
					        // matching and backreferences
 | 
				
			||||||
        const headingTagsRegex = /<h(\d+)[^>]*>(.*?)<\/h\1>/gi;
 | 
					        const headingTagsRegex = /<h(\d+)[^>]*>(.*?)<\/h\1>/gi;
 | 
				
			||||||
@ -167,8 +214,8 @@ export default class TocWidget extends RightPanelWidget {
 | 
				
			|||||||
            // Create the list item and set up the click callback
 | 
					            // Create the list item and set up the click callback
 | 
				
			||||||
            //
 | 
					            //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const headingText = $("<div>").html(m[2]).text();
 | 
					            const headingText = await this.replaceMathTextWithKatax(m[2])
 | 
				
			||||||
            const $li = $('<li>').text(headingText);
 | 
					            const $li = $('<li>').html(headingText);
 | 
				
			||||||
            $li.on("click", () => this.jumpToHeading(headingIndex));
 | 
					            $li.on("click", () => this.jumpToHeading(headingIndex));
 | 
				
			||||||
            $ols[$ols.length - 1].append($li);
 | 
					            $ols[$ols.length - 1].append($li);
 | 
				
			||||||
            headingCount = headingIndex;
 | 
					            headingCount = headingIndex;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user