Я использую tinymce 6.8.4 в моем приложение - для него отведено отдельное окно. Реализуй доработку по мои требованиям:

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

Все страницы сохраняются в один файл HTML, используя стандартный метод tinymce. При повторном открытии файла должно отображаться так же как и при создании, с теми же параметрами (формат листа, ориентация, отступы). Можешь при сохранении так же сохранять в json файл данные со всеми параметрами файла, чтобы при повторном открытии файла отображался так же, как и при сохранении.

При печати или экспорте в PDF должна чётко соответствовать формату, который выбран. 
Все параметры можно изменять динамически и содержимое должно подстраиваться под размеры. 

У меня уже имеется разметка HTML. Для редактора <div id="editor-layer"></div>. 
<!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>
Реализуй для начала полностью пункт 1 и 2