TextEditor Widget

First of all, thank you very much for your attention. This is my first time so my apologies in advance.
I have been working on a speech recognition problem transcribing voice to text. I need to move from audio to text or text to audio ( audio recording and the corresponding transcribed text) depending on different circumstances. I have been working with Panel to avoid any web coding in JS. As expected, after the initial phases, I need some guidance to move forward.

I’m working with the TextEditor widget and I’m lost on how to capture the events associated with a selected text via double-click or a direct selection of a text via mouse. Is any way to capture the selected text in a TextEditor widget?

I have reviewed the documentation and the different available mechanisms, but I have only managed to capture via callback when the “value” attribute changes, but I cannot capture any selection event in the GUI. I need to know what the selected text has been, and its relative positioning with respect to the beginning of the text. I’m not familiar with JS, so reviewing Quill.js hasn’t been much help.

Please, do you have any suggestions?
Thanks, in advance,

1 Like

Hi,

you can create a custom-selected text component with the ReactiveHTML component. An event listener set the property in the selected text attribute.

import panel as pn
import param 

wysiwyg = pn.widgets.TextEditor(placeholder='Enter some text')

class SelectedText(pn.reactive.ReactiveHTML):

    selected_text = param.String(default='', doc='The selected text')

    _template = """
<div id="area"> ${selected_text} </div>
"""

    _scripts = {
        'render': """
    document.addEventListener('mouseup', event => {  
        if(window.getSelection().toString().length){
            let exactText = window.getSelection().toString();        
            console.log(exactText);
            data.selected_text = exactText;
        }
    }
    )   
    """
    }

    @param.depends('selected_text', watch=True)
    def check_selected_text(v):
        print (wysiwyg.value.index(v.selected_text))

st = SelectedText()

pn.Column(wysiwyg, st).servable()
2 Likes

Thanks a lot.
I analyzed the possibility of using the templates, but I didn’t know or understand how to make the connection between the TextEditor and reactive.ReactiveHTML, I thought they were completely disconnected. It’s so clean the suggestion that I’m embarrassed to have asked.
Clearly, I need to understand better the relationship and how to combine these different possibilities and components.
Again, thanks for your prompt answer.

2 Likes

It is ok to ask, some concepts need time till one fully captures them. Panel has a lot of power, but a lot of concepts and API’s to assimilate.

On the other hand, take into account that the text editor gives the HTML response with the HTML tags, and the selected component gives you plain text, then some more logic in the python function check_selected_text is needed to compare them and get the index of the selected text in the TextEditor widget.

I was checking this

but some corner cases still appear. Maybe someone can give you some hints on how to retrieve the selected text with the HTML tags too. It is worthwhile to check the window.getselection object

it seems there is no easy way to parse HTML to plain text without complex functions or some library. The best library seems to be “pip install html2text”

4 Likes

There’s nothing documented as it’s simply not supported out of the box by Panel :slight_smile: This is a pretty specific use case!

@nghenzi provided a solution (I haven’t tried it) that relies on creating a ReactiveHTML component. This is I would say a solution for advanced users, you’ll have to have some knowledge in HTML/CSS/JavaScript to get this to work exactly as you expect, or the right amount of perseverance and Google search skills :wink:

Thanks all for the help. You are right perseverance, patience, and intensity. Following the suggestion, I have included some extra code to get also the relative position of the selected text:

class SelectedText(pn.reactive.ReactiveHTML):

    selected_text = param.String(default='', doc='The selected text')

    _template = """
        <div id="area"> ${selected_text} </div>
        """

    _scripts = {
        'render': """
        document.addEventListener('mouseup', event => {
            var selection = window.getSelection();
            var start = selection.anchorOffset;
            var end = selection.focusOffset;
            if(end > start) {

                let exactText = selection.toString();        
                data.selected_text = exactText + " "+ `${start.toString()} ${end.toString()}`;
            }
        })"""
    }

Thanks again for all the help.

1 Like

Great it worked for you !