Can a replacing decoration generate a string instead of an HTMLElement


I am creating a ViewPlugin which contains some Decoration.replace and it’s working great. I am replacing some text through a custom WidgetType. However, I see that the WidgetType's toDOM() method has to return an HTMLElement. In my use case, I’d like to be able to ouput a simple string instead of an actual HTML element.

If I inspect the HTML before the replacement, I have:
<span class="cm-header cm-header-1">Header $var</span>

After the replacement (where $var = “replacement”), I get:

<img class="cm-widgetBuffer" aria-hidden="true">
<span class="cm-header cm-header-1">Header </span>
<span contenteditable="false">replacement</span>
<img class="cm-widgetBuffer" aria-hidden="true">

When I would like to get:
<span class="cm-header cm-header-1">Header replacement</span>

This is the code I have for toDOM():

toDOM() {
     let wrap = document.createElement("span");
     wrap.innerHTML = this.value;
     return wrap

I have tried doing wrap.outerHTML = this.value instead of setting the innerHTML but that resulted in many errors and didn’t work.

I have also tried a view.dispatch but that actually changes the source file (the plugin I’m writing is for which uses CodeMirror under the hood) and in my case I just want to change what is displayed. I’ve also tried a view.state.doc.replace but nothing was happening and I’m not sure why.

Is there a way to simply replace the text with some other text? Maybe without passing a widget in the ReplaceDecorationSpec and using the other properties that are allowed?

You can create a widget type that creates a <span> element with some text in it, which should be functionally equivalent to replacing the text.

Let me give a bit more background info:
I’m extending CodeMirror that is being used by the Obsidian markdown app so it makes things a bit more complex. Obsidian has a feature called LivePreview where in a single CodeMirror editor, it will show the source markdown on the lines where something is selected and everywhere else, it will actually generate the HTML corresponding to the source markdown. So basically, as you type, you modify the source markdown but everything around it is the generated HTML preview of the markdown.

My Obsidian plugin detects which lines are in LivePreview mode and then I find matches for the user’s configured variables and swap the variable name with the variable value via a Decoration.replace and a custom WidgetType. Now, that is working except that a WidgetType's toDOM() method has to return an HTMLElement so I’m generating the most non-intrusive element that I know of: HTMLSpanElement. The problem is that it messes up the formatting.

For example, it “breaks” the style of the header for the replaced content:
Peek 2022-03-19 15-24

So maybe there is another way to achieve what I want or maybe this is more of a feature request for toDOM() to return HTMLElement | string or something like that.

I’m not sure what the screencast is trying to show, but generally, unless the span is styled or there’s some odd selectors on the parent element, adding a span around text should not affect styling.

I also have a use case where I’m replacing a variable inside an attribute of an HTML element so that’s why I’m asking if there’s a way to replace with just text and not an HTML element (along with the widget buffers).

For example:

<img width="300" alt="" src="app://$MEDIA/2022/example.jpg" >

becomes the following:

<img width="300" alt="" src="app://local/home/jffaust/drive/Media/2022/example.jpg" >