start . me .
Directory path . web , gh , editor .

HTML Document File : editor.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>basic HTML editor</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- TailwindCSS -->
    <!-- <script src="https://cdn.tailwindcss.com"></script> -->
    <script>

        // TailwindCSS

        // pairs of css selectors and classes to apply
        let cssSelectorsClassesPairs = [

            // ['button , input[type=button] , input[type=submit]', 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'],

            ['button , input[type=button] , input[type=submit] , a[href^="javascript:"] ', 'btn btn-blue'],

            ['.btn', 'font-bold py-2 px-4 rounded'],

            ['.btn-blue', 'bg-blue-500 text-white'],
            //['.btn-blue:hover', 'bg-blue-700'],
            ['.btn-blue', 'hover:bg-blue-700'],

            ['.btn-red', 'bg-red-500 text-white hover:bg-red-700'],

            ['textarea , input[type=text] , input:not([type])', 'border border-gray-300'],
        ]

        // cssSelectorsClassesPairs = [] // disable default pairs

        // apply classes to selectors
        function cssSelectorsClassesPairApply(pair) {

            // add classes to element
            function cssAddClasses(element, classes) {
                element.classList.add(...classes.split(' '));
            }

            // apply classes to all elements matching selector
            const [selector, classes] = pair;
            document.querySelectorAll(selector).forEach(element => cssAddClasses(element, classes));
        }

        function initCSSPairs(cssPairs) {
            // apply all pairs
            cssPairs.forEach(pair => cssSelectorsClassesPairApply(pair));
        }

        function initCSS() {
            initCSSPairs(cssSelectorsClassesPairs);
        }

        function init() {
            // initCSS();
        }

        init();

    </script>

    <script src="https://unpkg.com/ractive"></script>

    <!-- VueJS + PiniaJS -->

    <!-- VueJS 3 -->
    <script src="https://unpkg.com/vue@3.4.30/dist/vue.global.js"></script>

    <!-- PiniaJS 2 -->
    <!-- vue-demi required by pinia 2 -->
    <!-- <script src="https://unpkg.com/vue-demi@0.14.8/lib/index.iife.js"></script>
    <script src="https://unpkg.com/pinia@2.1.7/dist/pinia.iife.js"></script> -->

    <!-- BootstrapCSS + BootstrapJS + PopperJS -->

    <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
        crossorigin="anonymous"></script>

    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
        integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
        crossorigin="anonymous"></script> -->

</head>

<body>

    <script type="text/javascript">
        // show sources
        function showSources() {
            document.getElementById("sources").classList.toggle("hidden");
        }
    </script>
    <style>
        .hidden {
            display: none;
        }
    </style>
    Editor for DHTML <button type="button" onclick="showSources()">show sources (toggle)</button>
    <div id="sources" class="hidden">
        <script src="show-source.js" data-href="editor.js"></script>
        <button type="button" onclick="showSources()">show sources (toggle)</button>
        <script src="show-source.js" data-href="editor.html"></script>
        <button type="button" onclick="showSources()">show sources (toggle)</button>
        <script src="show-source.js" data-href="editor.css"></script>
        <button type="button" onclick="showSources()">show sources (toggle)</button>
    </div>

    <hr><br>

    <div id="page"></div>

    <pre id="html_console" contenteditable="" class="html_console border"></pre>
    <script>
        function html_write(html) {
            html_console.innerHTML += html
        }
        function html_write_line(html) {
            html_write(html + "\n")
        }
    </script>

    <hr>
    <button type="button" onclick="syncContent()">sync</button>
    <label><input type="checkbox" id="autoSync" checked="">auto-sync</label>
    <button type="button" onclick="location.reload()">reload</button>
    <br>
    <button type="button" onclick="codeDecorations()">code decorations</button>
    <button type="button" onclick="textarea.value=example1();textareaInput()">example1</button>
    <button type="button" onclick="textarea.value=example2();textareaInput()">example2</button>
    <br>

    <style>
        .relative {
            position: relative;
        }

        .editor,
        .textarea {
            box-sizing: border-box;
        }
    </style>
    <div class="relative">
        <div id="editor" class="editor border" spellcheck="false" translate="no"></div>
        <textarea id="textarea" class="textarea border" spellcheck="false" translate="no"
            title="textarea for html code editor" placeholder="write some HTML code here"></textarea>
    </div>
    <script>
        function bind() {

            // copy_size
            editor.style.width = textarea.getBoundingClientRect().width + "px"
            editor.style.height = textarea.getBoundingClientRect().height + "px"

            // sync scrolls
            editor.scrollTop = textarea.scrollTop
            editor.scrollLeft = textarea.scrollLeft
        }
        setInterval(bind, 0)
    </script>

    <script src="editor.js"></script>
    <link rel="stylesheet" href="editor.css">

    <script>

        // save & restore
        // [vue] save and restore vue.$data

        function save() {

            saved_data = {}

            if (typeof vue != "undefined") {

                for (const name of Object.keys(vue.$data)) {
                    saved_data[name] = vue.$data[name]
                }
            }

        } // end of: save()

        function restore() {

            if (typeof vue != "undefined") {

                for (const name of Object.keys(saved_data)) {
                    vue.$data[name] = saved_data[name]
                }
            }

        } // end of: restore()

        // end of: save & restore
    </script>

</body>

</html>