Я использую tinymce версии 6.8.4 с открытым исходным кодом и лицензией MIT в моем приложение - для него отведено отдельное окно <div id="editor-layer"></div>. 
Реализуй доработку tinymce по мои требованиям:

1. Отображение редактора похожее на Google Docs и Microsoft Word. В окне редактора должен отображаться лист выбранного формата ( например A4 ). Текст, таблица или изображение которое не помещается на текущую страницу автоматически должно помещаться на следующую страницу, которая создается автоматически. Если таблица не помещается на одну страницу, то её необходимо разбить на несколько частей. Изучи механизмы оптимизации отображения как у Monaco Editor, адаптируй и добавь в этот код.
2. Выбор размера A3, A4, A5, A6, портретный и ландшафтный режим. Режим можно менять в любой момент и содержимое должно подстроиться под новый размер. 
3. Возможность добавлять нумерацию страниц. Необходимо так же предусмотреть отключение нумерации на конкретной странице. Нумерация динамически подстроиться. Можно выбрать различные стили отображения номера страницы и настраивать их вручную.
4. Возможность добавлять верхний и нижний колонтитулы. Отключать и включать для любой отдельной страницы, редактировать ( добавлять любые данные доступные timymce). Можно выбрать отображать или нет нумерацию страниц, выбрать верхний или нижний колонтитул, а так же позицию и стили нумерации. 
5. Выбор размеров отступов (слева, справа, сверху, снизу) для всех страниц. 
6. Возможность использовать и создавать шаблон: колонтитулы, отступы, нумерацию, заглавную страницу. Шаблон создается тоже с помощью tinymce. При использовании шаблона все страницы автоматически будут применять шаблон. Так же остается возможность динамически изменять параметры, как для всех страниц, так и для отдельной конкретной страницы.
7. Добавь возможность экспорта в PDF - 2 варианта ( обычный PDF или PDF с Table of content).

Все страницы сохраняются в один файл HTML. При повторном открытии файла должно отображаться так же как и при создании, с теми же параметрами (формат листа, ориентация, отступы). Для этого добавляй meta теги в html со всеми необходимыми параметрами.
При печати или экспорте в PDF должна чётко соответствовать формату, который выбран.

У меня уже имеется разметка HTML. Элементы и кнопки настройка страниц и создания шаблонов встрой в панель tinymce.


<!DOCTYPE html>
<html lang="en">
  <head>
    <title>PDFGen</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" type="text/css" href="styles.css" />
  </head>
  <body>
    <div id="app">
      <div id="sidebar">
        <button id="set-dir">Настройка рабочей директории</button>
        <input type="text" id="search-input" placeholder="Поиск по файлам" />
        <button id="all-notes">Все заметки</button>
        <button id="favorites">Избранное</button>
        <button id="trash">Корзина</button>
        <button id="create-folder">Создать папку</button>
        <ul id="folders-list"></ul>
        <ul id="tags-list"></ul>
      </div>

      <div id="context-menu" class="context-menu" style="display: none">
        <button onclick="editItem()">Edit</button>
        <button onclick="deleteItem()">Delete</button>
      </div>

      <div id="subpanel"></div>
      <div id="editor">
      <div id="editor-layer"></div>
      </div>
      </div>
    </div>
    <script src="js/main.js"></script>
    <script src="editor/tinymce.min.js"></script>

    <script>
      const { settingsPrivate } = chrome;
      settingsPrivate.setPref('browser.enable_spellchecking', true);
      settingsPrivate.setPref('spellcheck.dictionaries', ['ru', 'en'], 'null', () => {});

      const imageUploadHandler = (blobInfo) => {
        const uploadPath = `${currentDir}\\uploads\\${blobInfo.filename()}`;
        return new Promise((resolve, reject) => {
          fs.writeFile(uploadPath, blobInfo.base64(), { encoding: 'base64' }, (err) => {
            if (err) reject(err);
            else resolve(uploadPath);
          });
        });
      };
      const resizeAndSaveImage = (imgSrc, width, height) => {
        const inputPath = path.resolve(imgSrc);
        const outputPath = path.resolve(imgSrc);
        if (fs.existsSync(inputPath)) {
          try {
            const data = fs.readFileSync(inputPath);
            const img = new Image();
            img.onload = function () {
              const canvas = document.createElement('canvas');
              canvas.width = width;
              canvas.height = height;
              const ctx = canvas.getContext('2d');
              ctx.drawImage(img, 0, 0, width, height);
              const resizedImage = canvas.toDataURL('image/png');
              const base64Data = resizedImage.replace(/^data:image\/png;base64,/, '');
              fs.writeFileSync(outputPath, base64Data, { encoding: 'base64' });
              console.log('Image successfully resized and saved.');
            };
            img.src = 'data:image/png;base64,' + data.toString('base64');
          } catch (err) {
            console.error('Error resizing and saving image:', err);
          }
        } else {
          console.error('Input image file does not exist:', inputPath);
        }
      };
      const deleteImageFile = (imgSrc) => {
        try {
          fs.unlinkSync(imgSrc);
          console.log('Delete File successfully.');
        } catch (error) {
          console.error('Error deleting file:', error);
        }
      };

      tinymce.init({
        selector: '#editor-layer',
        height: '100vh',
        resize: false,
        content_css: 'document',
        plugins:
          'preview importcss searchreplace autolink autosave save directionality code visualchars fullscreen image template codesample table charmap pagebreak nonbreaking advlist lists wordcount charmap quickbars',
        menubar: false,
        toolbar1:
          'restoredraft | undo redo | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | charmap | searchreplace',
        toolbar2:
          'blocks fontsize | bold italic strikeout | outdent indent |  numlist bullist | hr pagebreak | image table',
        toolbar_sticky: true,
        autosave_interval: '30s',
        autosave_retention: '2500m',
        autosave_prefix: '-{path}{query}-{id}-',
        images_reuse_filename: true,
        automatic_uploads: true,
        convert_urls: false,
        images_upload_handler: imageUploadHandler,
        file_picker_types: 'image',
        image_advtab: false,
        paste_data_images: false,
        resize_img_proportional: true,
        image_caption: true,
        image_description: false,
        promotion: false,
        branding: false,
        quickbars_insert_toolbar: '',
        quickbars_selection_toolbar: 'bold italic strikethrough h1 h2 h3 | removeformat',
        noneditable_class: 'mceNonEditable',
        toolbar_mode: 'sliding',
        contextmenu: 'image table',
        content_style: 'body { font-family:"Segoe UI"; font-size:10pt }',

        setup: (editor) => {
          editor.ui.registry.addToggleButton('strikethrough', {
            icon: 'sourcecode',
            tooltip: 'Format as code',
            onAction: () => editor.execCommand('mceToggleFormat', false, 'code'),
            onSetup: (api) => {
              api.setActive(editor.formatter.match('code'));
              return editor.formatter.formatChanged('code', api.setActive);
            },
          });
          editor.on('NodeChange', (e) => {
            const element = e.element;
            const nodeName = element.nodeName;
            if (nodeName === 'IMG' || (nodeName === 'FIGURE' && element.children[0].nodeName === 'IMG')) {
              const imgNode = nodeName === 'FIGURE' ? element.children[0] : element;
              const imgSrc = imgNode.getAttribute('data-mce-src') || imgNode.getAttribute('src');
              const width = parseInt(imgNode.getAttribute('width'), 10);
              const height = parseInt(imgNode.getAttribute('height'), 10);
              if (imgSrc && width && height) {
                resizeAndSaveImage(imgSrc, width, height);
              }
            }
          });
          editor.on('keydown', (e) => {
            if ((e.keyCode === 8 || e.keyCode === 46) && tinymce.activeEditor.selection) {
              const selectedNode = tinymce.activeEditor.selection.getNode();
              if (selectedNode && (selectedNode.nodeName === 'IMG' || selectedNode.nodeName === 'FIGURE')) {
                const imgNode = selectedNode.nodeName === 'FIGURE' ? selectedNode.children[0] : selectedNode;
                const imgSrc = imgNode.getAttribute('data-mce-src') || imgNode.getAttribute('src');
                if (imgSrc) {
                  deleteImageFile(imgSrc);
                }
              }
            }
          });
        },
      });
    </script>
  </body>
</html>