Handling Text Selection and Dynamic Span Wrapping with Class Manipulation in plain JavaScript

i want it to so that when a user highlights a text and clicks on a button that for example makes that text bold, we convert the selected text node into a span and add a class to it.
but let say they only high light an invidiual letter, we conver that individual letter into a span and let say user then decides to highlight the rest of the text for that character, we remove the old span and retreive the class from it and group it into a new span with the old class adding into that.

this also the same vice verse. if a text ndoe is already a span and a single letter is highlighted we create its own span for it. but right now i am trying to collect the text node and i seem to be capturing text nodes with empty space and the parent container is all over the place based on different circumstances. such as double clicking highlight and where i start the highlight.
i am using the following text as a test case/

this is text is here replaceable

TLDR trying to replicate https://quilljs.com/playground/snow but without using quill or other thrid party as they dont seem to do what i want.

      let selection = this.window.getSelection();
      let node = null;

      for (let rangeNumber = selection.rangeCount-1; rangeNumber >= 0; rangeNumber--) {
          let range = selection.getRangeAt(rangeNumber);
          // range.collapse(true)
          console.log(range)
  
          if (range.startContainer === range.endContainer && range.endContainer.nodeType === Node.TEXT_NODE) {
              range.startContainer.splitText(range.endOffset);
              let textNode = range.startContainer.splitText(range.startOffset);
              rangeNodes.push(textNode);
              console.log("same container")
          } else {
        /* edge-case for rare circumstances where, the end-container may contain a text node, but not be the text node itself, end-container is redefined for iterator */
              let startContainer=range.startContainer;
              let endContainer=range.endContainer;
              if (range.endContainer.nodeType != Node.TEXT_NODE && range.endContainer.childNodes.length>range.endOffset) endContainer=range.endContainer.childNodes[range.endOffset];
              if (range.startContainer.nodeType != Node.TEXT_NODE && range.startOffset>0) startContainer=range.startContainer.childNodes[range.startOffset-1];
              /* collect all text nodes inside range, ignore them if they are marked as non-selectable through css */
              let textIterator = document.createNodeIterator(range.commonAncestorContainer, NodeFilter.SHOW_TEXT, (node) => (node.compareDocumentPosition(startContainer)==Node.DOCUMENT_POSITION_PRECEDING && node.compareDocumentPosition(endContainer)==Node.DOCUMENT_POSITION_FOLLOWING) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT );
              while (node = textIterator.nextNode()) { console.log("textirator"); if (!isTextNodeUnparsedWhitespace(node) && window.getComputedStyle(node.parentElement).userSelect!="none") rangeNodes.push(node);}
              /* separate first and final text node */
              if (range.endContainer.nodeType === Node.TEXT_NODE && window.getComputedStyle(range.endContainer.parentElement).userSelect!="none") {
                        const endNode = range.endContainer.splitText(range.endOffset);

                        const text  = document.createTextNode(range.endContainer.textContent);
                        console.log(text.textContent , "endcontainer")
                        rangeNodes.push(text);

                        // range.endContainer.parentNode.replaceChild(text, range.endContainer);  
                        
                       
                        // console.log(range.endContainer, range.endContainer,range.endContainer.parentElement, range.startOffset, range.endOffset,"end node")

                    }
                    if (range.startContainer.nodeType === Node.TEXT_NODE && window.getComputedStyle(range.startContainer.parentElement).userSelect!="none") {
                        const startNode = range.startContainer.splitText(range.startOffset);
                        // console.log(startNode, range.startContainer)
                        console.log(startNode.textContent , "startcontainer")

                        rangeNodes.unshift(startNode);
                    }
          }

      }