mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01: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 OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
			
		||||
import appContext from "../components/app_context.js";
 | 
			
		||||
import libraryLoader from "../services/library_loader.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `<div class="highlights-list-widget">
 | 
			
		||||
    <style>
 | 
			
		||||
@ -52,7 +53,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
                .icon("bx-slider")
 | 
			
		||||
                .title("Options")
 | 
			
		||||
                .titlePlacement("left")
 | 
			
		||||
                .onClick(() => appContext.tabManager.openContextWithNote('_optionsTextNotes', {activate: true}))
 | 
			
		||||
                .onClick(() => appContext.tabManager.openContextWithNote('_optionsTextNotes', { activate: true }))
 | 
			
		||||
                .class("icon-action"),
 | 
			
		||||
            new OnClickButtonWidget()
 | 
			
		||||
                .icon("bx-x")
 | 
			
		||||
@ -97,8 +98,8 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
        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, hlLiCount} = this.getHighlightList(content, optionsHighlightsList));
 | 
			
		||||
            const { content } = await note.getNoteComplement();
 | 
			
		||||
            ({ $highlightsList, hlLiCount } = await this.getHighlightList(content, optionsHighlightsList));
 | 
			
		||||
        }
 | 
			
		||||
        this.$highlightsList.empty().append($highlightsList);
 | 
			
		||||
        if (hlLiCount > 0) {
 | 
			
		||||
@ -112,7 +113,79 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
        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
 | 
			
		||||
        const regex1 = /<span[^>]*style\s*=\s*[^>]*background-color:[^>]*?>[\s\S]*?<\/span>/gi;
 | 
			
		||||
        // matches a span containing color
 | 
			
		||||
@ -152,6 +225,10 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
        const combinedRegex = new RegExp(combinedRegexStr, 'gi');
 | 
			
		||||
        const $highlightsList = $("<ol>");
 | 
			
		||||
        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++) {
 | 
			
		||||
            const subHtml = match[0];
 | 
			
		||||
            const startIndex = match.index;
 | 
			
		||||
@ -166,11 +243,19 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
                const hasText = $(subHtml).text().trim();
 | 
			
		||||
 | 
			
		||||
                if (hasText) {
 | 
			
		||||
                    $highlightsList.append(
 | 
			
		||||
                        $('<li>')
 | 
			
		||||
                            .html(subHtml)
 | 
			
		||||
                            .on("click", () => this.jumpToHighlightsList(findSubStr, hltIndex))
 | 
			
		||||
                    );
 | 
			
		||||
                    const substring = content.substring(prevEndIndex, startIndex);
 | 
			
		||||
                    //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.
 | 
			
		||||
                    if (this.areOuterTagsConsistent(prevSubHtml, subHtml) && onlyMathRegex.test(substring)) {
 | 
			
		||||
                        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++;
 | 
			
		||||
                } else {
 | 
			
		||||
@ -179,6 +264,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            prevEndIndex = endIndex;
 | 
			
		||||
            prevSubHtml = subHtml;
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
            $highlightsList,
 | 
			
		||||
@ -234,7 +320,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
 | 
			
		||||
        this.triggerCommand('reEvaluateRightPaneVisibility');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async entitiesReloadedEvent({loadResults}) {
 | 
			
		||||
    async entitiesReloadedEvent({ loadResults }) {
 | 
			
		||||
        if (loadResults.isNoteContentReloaded(this.noteId)) {
 | 
			
		||||
            await this.refresh();
 | 
			
		||||
        } 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 OnClickButtonWidget from "./buttons/onclick_button.js";
 | 
			
		||||
import appContext from "../components/app_context.js";
 | 
			
		||||
import libraryLoader from "../services/library_loader.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `<div class="toc-widget">
 | 
			
		||||
    <style>
 | 
			
		||||
@ -120,6 +121,52 @@ export default class TocWidget extends RightPanelWidget {
 | 
			
		||||
        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.
 | 
			
		||||
     *
 | 
			
		||||
@ -128,7 +175,7 @@ export default class TocWidget extends RightPanelWidget {
 | 
			
		||||
     *         with an onclick event that will cause the document to scroll to
 | 
			
		||||
     *         the desired position.
 | 
			
		||||
     */
 | 
			
		||||
    getToc(html) {
 | 
			
		||||
    async getToc(html) {
 | 
			
		||||
        // Regular expression for headings <h1>...</h1> using non-greedy
 | 
			
		||||
        // matching and backreferences
 | 
			
		||||
        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
 | 
			
		||||
            //
 | 
			
		||||
 | 
			
		||||
            const headingText = $("<div>").html(m[2]).text();
 | 
			
		||||
            const $li = $('<li>').text(headingText);
 | 
			
		||||
            const headingText = await this.replaceMathTextWithKatax(m[2])
 | 
			
		||||
            const $li = $('<li>').html(headingText);
 | 
			
		||||
            $li.on("click", () => this.jumpToHeading(headingIndex));
 | 
			
		||||
            $ols[$ols.length - 1].append($li);
 | 
			
		||||
            headingCount = headingIndex;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user