suneditor 3.1.3 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/dist/suneditor-contents.min.css +1 -1
  2. package/dist/suneditor.min.css +28 -1
  3. package/dist/suneditor.min.js +1 -1
  4. package/package.json +2 -1
  5. package/src/assets/design/color.css +12 -0
  6. package/src/assets/design/size.css +9 -2
  7. package/src/assets/icons/defaultIcons.js +96 -47
  8. package/src/assets/suneditor-contents.css +3 -2
  9. package/src/assets/suneditor.css +480 -31
  10. package/src/core/config/contextProvider.js +2 -1
  11. package/src/core/config/eventManager.js +41 -2
  12. package/src/core/config/optionProvider.js +23 -2
  13. package/src/core/editor.js +32 -14
  14. package/src/core/event/actions/index.js +76 -9
  15. package/src/core/event/effects/common.registry.js +2 -1
  16. package/src/core/event/effects/keydown.registry.js +169 -33
  17. package/src/core/event/effects/ruleHelpers.js +20 -6
  18. package/src/core/event/eventOrchestrator.js +136 -23
  19. package/src/core/event/handlers/handler_toolbar.js +4 -2
  20. package/src/core/event/handlers/handler_ww_dragDrop.js +10 -0
  21. package/src/core/event/handlers/handler_ww_input.js +9 -2
  22. package/src/core/event/handlers/handler_ww_key.js +86 -17
  23. package/src/core/event/handlers/handler_ww_mouse.js +45 -8
  24. package/src/core/event/ports.js +23 -5
  25. package/src/core/event/reducers/keydown.reducer.js +15 -2
  26. package/src/core/event/rules/keydown.rule.arrow.js +12 -2
  27. package/src/core/event/rules/keydown.rule.backspace.js +110 -15
  28. package/src/core/event/rules/keydown.rule.delete.js +62 -7
  29. package/src/core/event/rules/keydown.rule.enter.js +62 -31
  30. package/src/core/event/support/defaultLineManager.js +9 -2
  31. package/src/core/event/support/selectionState.js +13 -3
  32. package/src/core/kernel/coreKernel.js +1 -0
  33. package/src/core/logic/dom/char.js +13 -3
  34. package/src/core/logic/dom/format.js +171 -39
  35. package/src/core/logic/dom/html.js +357 -87
  36. package/src/core/logic/dom/inline.js +229 -34
  37. package/src/core/logic/dom/listFormat.js +111 -20
  38. package/src/core/logic/dom/nodeTransform.js +36 -11
  39. package/src/core/logic/dom/offset.js +116 -31
  40. package/src/core/logic/dom/selection.js +147 -29
  41. package/src/core/logic/panel/blockHandle.js +785 -0
  42. package/src/core/logic/panel/blockResolver.js +278 -0
  43. package/src/core/logic/panel/finder.js +16 -6
  44. package/src/core/logic/panel/menu.js +119 -18
  45. package/src/core/logic/panel/toolbar.js +75 -19
  46. package/src/core/logic/panel/viewer.js +76 -20
  47. package/src/core/logic/shell/_commandExecutor.js +44 -9
  48. package/src/core/logic/shell/commandDispatcher.js +46 -4
  49. package/src/core/logic/shell/component.js +102 -25
  50. package/src/core/logic/shell/focusManager.js +13 -3
  51. package/src/core/logic/shell/history.js +21 -6
  52. package/src/core/logic/shell/pluginManager.js +21 -13
  53. package/src/core/logic/shell/shortcuts.js +9 -1
  54. package/src/core/logic/shell/ui.js +139 -37
  55. package/src/core/schema/frameContext.js +34 -3
  56. package/src/core/schema/options.js +61 -19
  57. package/src/core/section/constructor.js +537 -103
  58. package/src/core/section/documentType.js +35 -6
  59. package/src/helper/converter.js +24 -7
  60. package/src/helper/dom/domCheck.js +25 -5
  61. package/src/helper/dom/domQuery.js +2 -1
  62. package/src/helper/dom/domUtils.js +17 -4
  63. package/src/helper/env.js +11 -2
  64. package/src/helper/keyCodeMap.js +6 -2
  65. package/src/helper/markdown.js +24 -5
  66. package/src/helper/msOffice.js +9 -2
  67. package/src/interfaces/plugins.js +1 -1
  68. package/src/langs/ckb.js +0 -1
  69. package/src/langs/cs.js +0 -1
  70. package/src/langs/da.js +0 -1
  71. package/src/langs/de.js +0 -1
  72. package/src/langs/en.js +0 -1
  73. package/src/langs/es.js +0 -1
  74. package/src/langs/fa.js +0 -1
  75. package/src/langs/fr.js +0 -1
  76. package/src/langs/he.js +0 -1
  77. package/src/langs/hu.js +0 -1
  78. package/src/langs/index.js +27 -1
  79. package/src/langs/it.js +0 -1
  80. package/src/langs/ja.js +0 -1
  81. package/src/langs/km.js +0 -1
  82. package/src/langs/ko.js +0 -1
  83. package/src/langs/lv.js +0 -1
  84. package/src/langs/nl.js +0 -1
  85. package/src/langs/pl.js +0 -1
  86. package/src/langs/pt_br.js +0 -1
  87. package/src/langs/ro.js +0 -1
  88. package/src/langs/ru.js +0 -1
  89. package/src/langs/se.js +0 -1
  90. package/src/langs/tr.js +0 -1
  91. package/src/langs/uk.js +0 -1
  92. package/src/langs/ur.js +0 -1
  93. package/src/langs/zh_cn.js +0 -1
  94. package/src/modules/contract/Browser.js +68 -23
  95. package/src/modules/contract/ColorPicker.js +12 -4
  96. package/src/modules/contract/Controller.js +37 -7
  97. package/src/modules/contract/Figure.js +159 -36
  98. package/src/modules/contract/HueSlider.js +27 -4
  99. package/src/modules/contract/Modal.js +62 -11
  100. package/src/modules/manager/FileManager.js +50 -8
  101. package/src/modules/ui/CommandMenu.js +597 -0
  102. package/src/modules/ui/ModalAnchorEditor.js +41 -9
  103. package/src/modules/ui/SelectMenu.js +481 -36
  104. package/src/modules/ui/index.js +1 -0
  105. package/src/plugins/browser/audioGallery.js +8 -1
  106. package/src/plugins/browser/fileBrowser.js +15 -2
  107. package/src/plugins/browser/fileGallery.js +8 -1
  108. package/src/plugins/browser/imageGallery.js +24 -3
  109. package/src/plugins/browser/videoGallery.js +19 -4
  110. package/src/plugins/command/blockquote.js +6 -1
  111. package/src/plugins/command/codeBlock.js +43 -5
  112. package/src/plugins/command/exportPDF.js +20 -4
  113. package/src/plugins/command/fileUpload.js +46 -11
  114. package/src/plugins/command/list_bulleted.js +6 -1
  115. package/src/plugins/command/list_numbered.js +6 -1
  116. package/src/plugins/dropdown/align.js +21 -33
  117. package/src/plugins/dropdown/backgroundColor.js +10 -2
  118. package/src/plugins/dropdown/blockStyle.js +17 -22
  119. package/src/plugins/dropdown/font.js +34 -34
  120. package/src/plugins/dropdown/hr.js +15 -43
  121. package/src/plugins/dropdown/layout.js +10 -27
  122. package/src/plugins/dropdown/lineHeight.js +10 -33
  123. package/src/plugins/dropdown/list.js +19 -25
  124. package/src/plugins/dropdown/paragraphStyle.js +16 -24
  125. package/src/plugins/dropdown/table/index.js +72 -17
  126. package/src/plugins/dropdown/table/render/table.html.js +3 -1
  127. package/src/plugins/dropdown/table/services/table.cell.js +33 -7
  128. package/src/plugins/dropdown/table/services/table.clipboard.js +5 -1
  129. package/src/plugins/dropdown/table/services/table.grid.js +40 -9
  130. package/src/plugins/dropdown/table/services/table.resize.js +55 -11
  131. package/src/plugins/dropdown/table/services/table.selection.js +27 -7
  132. package/src/plugins/dropdown/table/services/table.style.js +120 -20
  133. package/src/plugins/dropdown/template.js +13 -29
  134. package/src/plugins/dropdown/textStyle.js +22 -35
  135. package/src/plugins/field/autocomplete.js +8 -4
  136. package/src/plugins/field/slashCommand.js +297 -0
  137. package/src/plugins/index.js +3 -0
  138. package/src/plugins/input/fontSize.js +37 -8
  139. package/src/plugins/input/pageNavigator.js +7 -1
  140. package/src/plugins/modal/audio.js +71 -17
  141. package/src/plugins/modal/drawing.js +41 -11
  142. package/src/plugins/modal/embed.js +132 -22
  143. package/src/plugins/modal/image/index.js +115 -28
  144. package/src/plugins/modal/image/services/image.size.js +9 -2
  145. package/src/plugins/modal/image/services/image.upload.js +38 -4
  146. package/src/plugins/modal/link.js +9 -2
  147. package/src/plugins/modal/math.js +10 -2
  148. package/src/plugins/modal/video/index.js +112 -26
  149. package/src/plugins/modal/video/render/video.html.js +4 -1
  150. package/src/plugins/modal/video/services/video.size.js +15 -4
  151. package/src/plugins/modal/video/services/video.upload.js +10 -2
  152. package/src/plugins/popup/anchor.js +14 -2
  153. package/src/suneditor.js +8 -2
  154. package/src/themes/cobalt.css +12 -0
  155. package/src/themes/cream.css +12 -0
  156. package/src/themes/dark.css +12 -0
  157. package/src/themes/midnight.css +12 -0
  158. package/src/typedef.js +32 -21
  159. package/types/assets/icons/defaultIcons.d.ts +1 -0
  160. package/types/core/config/contextProvider.d.ts +7 -1
  161. package/types/core/config/eventManager.d.ts +37 -4
  162. package/types/core/config/optionProvider.d.ts +5 -1
  163. package/types/core/event/actions/index.d.ts +44 -6
  164. package/types/core/event/effects/keydown.registry.d.ts +37 -7
  165. package/types/core/event/effects/ruleHelpers.d.ts +6 -1
  166. package/types/core/event/eventOrchestrator.d.ts +6 -1
  167. package/types/core/event/handlers/handler_ww_clipboard.d.ts +15 -3
  168. package/types/core/event/handlers/handler_ww_dragDrop.d.ts +14 -2
  169. package/types/core/event/handlers/handler_ww_input.d.ts +10 -2
  170. package/types/core/event/handlers/handler_ww_key.d.ts +10 -2
  171. package/types/core/event/handlers/handler_ww_mouse.d.ts +25 -5
  172. package/types/core/event/ports.d.ts +7 -2
  173. package/types/core/event/rules/keydown.rule.backspace.d.ts +6 -1
  174. package/types/core/kernel/store.d.ts +4 -1
  175. package/types/core/logic/dom/format.d.ts +32 -14
  176. package/types/core/logic/dom/html.d.ts +11 -3
  177. package/types/core/logic/dom/nodeTransform.d.ts +6 -6
  178. package/types/core/logic/panel/blockHandle.d.ts +64 -0
  179. package/types/core/logic/panel/blockResolver.d.ts +50 -0
  180. package/types/core/logic/panel/menu.d.ts +72 -3
  181. package/types/core/logic/shell/commandDispatcher.d.ts +8 -0
  182. package/types/core/logic/shell/component.d.ts +5 -3
  183. package/types/core/logic/shell/shortcuts.d.ts +10 -1
  184. package/types/core/logic/shell/ui.d.ts +7 -1
  185. package/types/core/schema/context.d.ts +7 -1
  186. package/types/core/schema/frameContext.d.ts +36 -4
  187. package/types/core/schema/options.d.ts +110 -37
  188. package/types/core/section/constructor.d.ts +38 -1
  189. package/types/events.d.ts +208 -32
  190. package/types/helper/converter.d.ts +4 -2
  191. package/types/helper/dom/domCheck.d.ts +12 -2
  192. package/types/helper/dom/domQuery.d.ts +35 -7
  193. package/types/helper/dom/domUtils.d.ts +4 -1
  194. package/types/interfaces/plugins.d.ts +2 -2
  195. package/types/langs/_Lang.d.ts +0 -1
  196. package/types/modules/contract/Browser.d.ts +7 -7
  197. package/types/modules/contract/ColorPicker.d.ts +2 -2
  198. package/types/modules/contract/Controller.d.ts +7 -0
  199. package/types/modules/contract/Figure.d.ts +31 -4
  200. package/types/modules/ui/CommandMenu.d.ts +262 -0
  201. package/types/modules/ui/ModalAnchorEditor.d.ts +6 -1
  202. package/types/modules/ui/SelectMenu.d.ts +56 -3
  203. package/types/modules/ui/index.d.ts +1 -0
  204. package/types/plugins/browser/audioGallery.d.ts +17 -1
  205. package/types/plugins/browser/fileBrowser.d.ts +17 -1
  206. package/types/plugins/browser/fileGallery.d.ts +17 -1
  207. package/types/plugins/browser/imageGallery.d.ts +17 -1
  208. package/types/plugins/browser/videoGallery.d.ts +17 -1
  209. package/types/plugins/dropdown/align.d.ts +1 -1
  210. package/types/plugins/dropdown/blockStyle.d.ts +3 -1
  211. package/types/plugins/dropdown/table/index.d.ts +4 -1
  212. package/types/plugins/dropdown/table/services/table.clipboard.d.ts +5 -1
  213. package/types/plugins/dropdown/table/services/table.grid.d.ts +10 -2
  214. package/types/plugins/field/autocomplete.d.ts +0 -1
  215. package/types/plugins/field/slashCommand.d.ts +165 -0
  216. package/types/plugins/index.d.ts +3 -0
  217. package/types/plugins/modal/embed.d.ts +34 -0
  218. package/types/plugins/modal/link.d.ts +4 -1
  219. package/types/typedef.d.ts +48 -23
@@ -154,7 +154,8 @@ export default class ContextProvider {
154
154
  e.set('_wd', e.get('wysiwygFrame').contentDocument);
155
155
  e.set('wysiwyg', e.get('_wd').body);
156
156
  // e.get('wysiwyg').className += ' ' + options.get('_editableClass');
157
- if (frameOptions.get('_defaultStyles').editor) e.get('wysiwyg').style.cssText = frameOptions.get('_defaultStyles').editor;
157
+ if (frameOptions.get('_defaultStyles').editor)
158
+ e.get('wysiwyg').style.cssText = frameOptions.get('_defaultStyles').editor;
158
159
  if (frameOptions.get('height') === 'auto') e.set('_iframeAuto', e.get('_wd').body);
159
160
  } else {
160
161
  e.set('_ww', _w);
@@ -11,6 +11,9 @@ class EventManager {
11
11
  #frameContext;
12
12
  #frameOptions;
13
13
 
14
+ /** @type {?import('../event/eventOrchestrator').default} */
15
+ #orchestrator = null;
16
+
14
17
  /** @type {?SunEditor.Event.GlobalInfo} */
15
18
  #geckoActiveEvent = null;
16
19
 
@@ -56,6 +59,28 @@ class EventManager {
56
59
  };
57
60
  }
58
61
 
62
+ /**
63
+ * @internal
64
+ * @description Bind the EventOrchestrator once it is constructed. Invoked by `coreKernel`
65
+ * immediately after orchestrator creation so the public `applyTagEffect` method below can delegate.
66
+ * @param {import('../event/eventOrchestrator').default} orchestrator
67
+ */
68
+ _bindOrchestrator(orchestrator) {
69
+ this.#orchestrator = orchestrator;
70
+ }
71
+
72
+ /**
73
+ * @description Re-run the toolbar/menu active-state pass against the given selection node (or the
74
+ * current selection if `null`). Thin pass-through to {@link EventOrchestrator#applyTagEffect};
75
+ * exposed on `$.eventManager` so modules/plugins can request an immediate sync after they mutate
76
+ * `commandDispatcher.targets` (e.g. CommandMenu pushing newly-rendered rows).
77
+ * @param {?Node} [selectionNode]
78
+ * @returns {Node | undefined}
79
+ */
80
+ applyTagEffect(selectionNode) {
81
+ return this.#orchestrator?.applyTagEffect(selectionNode);
82
+ }
83
+
59
84
  /**
60
85
  * @description Register for an event.
61
86
  * - Only events registered with this method are unregistered or re-registered when methods such as 'setOptions', 'destroy' are called.
@@ -67,7 +92,14 @@ class EventManager {
67
92
  */
68
93
  addEvent(target, type, listener, useCapture) {
69
94
  if (!target) return null;
70
- if (target === _w || target === _d || typeof target.length !== 'number' || target.nodeType || (!Array.isArray(target) && target.length < 1)) target = [target];
95
+ if (
96
+ target === _w ||
97
+ target === _d ||
98
+ typeof target.length !== 'number' ||
99
+ target.nodeType ||
100
+ (!Array.isArray(target) && target.length < 1)
101
+ )
102
+ target = [target];
71
103
  if (target.length === 0) return null;
72
104
 
73
105
  const len = target.length;
@@ -103,7 +135,14 @@ class EventManager {
103
135
  const useCapture = params.useCapture;
104
136
 
105
137
  if (!target) return;
106
- if (target === _w || target === _d || typeof target.length !== 'number' || target.nodeType || (!Array.isArray(target) && target.length < 1)) target = [target];
138
+ if (
139
+ target === _w ||
140
+ target === _d ||
141
+ typeof target.length !== 'number' ||
142
+ target.nodeType ||
143
+ (!Array.isArray(target) && target.length < 1)
144
+ )
145
+ target = [target];
107
146
  if (target.length === 0) return;
108
147
 
109
148
  for (let i = 0, len = target.length; i < len; i++) {
@@ -215,7 +215,10 @@ export default class OptionProvider {
215
215
  const docHead = fc.get('_wd').head;
216
216
  const links = docHead.getElementsByTagName('link');
217
217
  while (links[0]) docHead.removeChild(links[0]);
218
- const parseDocument = new DOMParser().parseFromString(converter._setIframeStyleLinks(newRootOptions.get('iframe_cssFileName')), 'text/html');
218
+ const parseDocument = new DOMParser().parseFromString(
219
+ converter._setIframeStyleLinks(newRootOptions.get('iframe_cssFileName')),
220
+ 'text/html',
221
+ );
219
222
  const newLinks = parseDocument.head.children;
220
223
  const sTag = docHead.querySelector('style');
221
224
  while (newLinks[0]) docHead.insertBefore(newLinks[0], sTag);
@@ -225,6 +228,22 @@ export default class OptionProvider {
225
228
  fc.get('placeholder').textContent = newRootOptions.get('placeholder');
226
229
  }
227
230
 
231
+ if (diff.has('placeholder_line')) {
232
+ const phLineText = newRootOptions.get('placeholder_line');
233
+ fc.set('placeholder_line', phLineText);
234
+
235
+ const marked = fc.get('wysiwyg').querySelector('.se-placeholder-line');
236
+ if (marked) {
237
+ if (phLineText) {
238
+ marked.setAttribute('data-se-placeholder-line', phLineText);
239
+ } else {
240
+ marked.classList.remove('se-placeholder-line');
241
+ marked.removeAttribute('data-se-placeholder-line');
242
+ if (!marked.getAttribute('class')) marked.removeAttribute('class');
243
+ }
244
+ }
245
+ }
246
+
228
247
  // frame styles
229
248
  ui.setEditorStyle(newRootOptions.get('editorStyle'), fc);
230
249
 
@@ -339,7 +358,9 @@ export default class OptionProvider {
339
358
  for (let i = 0, len = keys.length, k; i < len; i++) {
340
359
  k = keys[i];
341
360
  if (OPTION_FIXED_FLAG[k] === 'fixed' || OPTION_FRAME_FIXED_FLAG[k] === 'fixed' || (plugins && plugins[k])) {
342
- console.warn(`[SUNEDITOR.warn.resetOptions] The "[${root + k}]" option cannot be changed after the editor is created.`);
361
+ console.warn(
362
+ `[SUNEDITOR.warn.resetOptions] The "[${root + k}]" option cannot be changed after the editor is created.`,
363
+ );
343
364
  keys.splice(i--, 1);
344
365
  len--;
345
366
  }
@@ -44,7 +44,11 @@ class Editor {
44
44
  */
45
45
  isEmpty(fc) {
46
46
  const wysiwyg = (fc || this.$.frameContext).get('wysiwyg');
47
- return dom.check.isZeroWidth(wysiwyg.textContent) && !wysiwyg.querySelector(this.$.options.get('allowedEmptyTags')) && (wysiwyg.innerText.match(/\n/g) || '').length <= 1;
47
+ return (
48
+ dom.check.isZeroWidth(wysiwyg.textContent) &&
49
+ !wysiwyg.querySelector(this.$.options.get('allowedEmptyTags')) &&
50
+ (wysiwyg.innerText.match(/\n/g) || '').length <= 1
51
+ );
48
52
  }
49
53
 
50
54
  /**
@@ -160,7 +164,9 @@ class Editor {
160
164
  env._w.setTimeout(() => {
161
165
  // Check if instance was destroyed (e.g., in SSR with dynamic imports mistake)
162
166
  if (!this.$.context?.size) {
163
- console.warn('[SUNEDITOR:E_INIT_FAIL] Editor instance was destroyed before initialization completed. Check if destroy() was called.');
167
+ console.warn(
168
+ '[SUNEDITOR:E_INIT_FAIL] Editor instance was destroyed before initialization completed. Check if destroy() was called.',
169
+ );
164
170
  return;
165
171
  }
166
172
 
@@ -169,8 +175,10 @@ class Editor {
169
175
  // roots
170
176
  this.$.contextProvider.applyToRoots((e) => {
171
177
  // observer
172
- if (this.#kernel._eventOrchestrator._wwFrameObserver) this.#kernel._eventOrchestrator._wwFrameObserver.observe(e.get('wysiwygFrame'));
173
- if (this.#kernel._eventOrchestrator._toolbarObserver) this.#kernel._eventOrchestrator._toolbarObserver.observe(e.get('_toolbarShadow'));
178
+ if (this.#kernel._eventOrchestrator._wwFrameObserver)
179
+ this.#kernel._eventOrchestrator._wwFrameObserver.observe(e.get('wysiwygFrame'));
180
+ if (this.#kernel._eventOrchestrator._toolbarObserver)
181
+ this.#kernel._eventOrchestrator._toolbarObserver.observe(e.get('_toolbarShadow'));
174
182
  // resource state
175
183
  this.$.ui._syncFrameState(e);
176
184
  });
@@ -195,12 +203,19 @@ class Editor {
195
203
  #initWysiwygArea(e, value) {
196
204
  // set content
197
205
  e.get('wysiwyg').innerHTML =
198
- this.$.html.clean(typeof value === 'string' ? value : (/^TEXTAREA$/i.test(e.get('originElement').nodeName) ? e.get('originElement').value : e.get('originElement').innerHTML) || '', {
199
- forceFormat: true,
200
- whitelist: null,
201
- blacklist: null,
202
- _freeCodeViewMode: this.$.options.get('freeCodeViewMode'),
203
- }) || '<' + this.$.options.get('defaultLine') + '><br></' + this.$.options.get('defaultLine') + '>';
206
+ this.$.html.clean(
207
+ typeof value === 'string'
208
+ ? value
209
+ : (/^TEXTAREA$/i.test(e.get('originElement').nodeName)
210
+ ? e.get('originElement').value
211
+ : e.get('originElement').innerHTML) || '',
212
+ {
213
+ forceFormat: true,
214
+ whitelist: null,
215
+ blacklist: null,
216
+ _freeCodeViewMode: this.$.options.get('freeCodeViewMode'),
217
+ },
218
+ ) || '<' + this.$.options.get('defaultLine') + '><br></' + this.$.options.get('defaultLine') + '>';
204
219
 
205
220
  // char counter
206
221
  if (e.has('charCounter')) e.get('charCounter').textContent = String(this.$.char.getLength());
@@ -299,15 +314,18 @@ class Editor {
299
314
 
300
315
  // document type
301
316
  if (e.get('documentTypeInner')) {
302
- if (this.$.options.get('_rtl')) e.get('wrapper').appendChild(e.get('documentTypeInner'));
303
- else e.get('wrapper').insertBefore(e.get('documentTypeInner'), e.get('wysiwygFrame'));
317
+ e.get('wrapper').insertBefore(e.get('documentTypeInner'), e.get('wysiwygFrame'));
304
318
  }
305
319
  if (e.get('documentTypePage')) {
306
- if (this.$.options.get('_rtl')) e.get('wrapper').insertBefore(e.get('documentTypePage'), e.get('wysiwygFrame'));
307
- else e.get('wrapper').appendChild(e.get('documentTypePage'));
320
+ e.get('wrapper').appendChild(e.get('documentTypePage'));
308
321
  // page mirror
309
322
  e.get('wrapper').appendChild(e.get('documentTypePageMirror'));
310
323
  }
324
+
325
+ // block handle
326
+ if (e.get('blockHandleArea')) {
327
+ e.get('wrapper').insertBefore(e.get('blockHandleArea'), e.get('wysiwygFrame'));
328
+ }
311
329
  });
312
330
 
313
331
  if (iframePromises.length > 0) {
@@ -102,7 +102,10 @@ export const A = {
102
102
  * @param {SunEditor.ComponentInfo} fileComponentInfo
103
103
  * @returns {Action}
104
104
  */
105
- backspaceComponentSelect: (selectionNode, range, fileComponentInfo) => ({ t: 'backspace.component.select', p: { selectionNode, range, fileComponentInfo } }),
105
+ backspaceComponentSelect: (selectionNode, range, fileComponentInfo) => ({
106
+ t: 'backspace.component.select',
107
+ p: { selectionNode, range, fileComponentInfo },
108
+ }),
106
109
  /**
107
110
  * @param {boolean} isList
108
111
  * @param {Node} sel
@@ -110,19 +113,41 @@ export const A = {
110
113
  * @param {SunEditor.ComponentInfo} fileComponentInfo
111
114
  * @returns {Action}
112
115
  */
113
- backspaceComponentRemove: (isList, sel, formatEl, fileComponentInfo) => ({ t: 'backspace.component.remove', p: { isList, sel, formatEl, fileComponentInfo } }),
116
+ backspaceComponentRemove: (isList, sel, formatEl, fileComponentInfo) => ({
117
+ t: 'backspace.component.remove',
118
+ p: { isList, sel, formatEl, fileComponentInfo },
119
+ }),
114
120
  /**
115
121
  * @param {Element} prev
116
122
  * @param {Element} formatEl
117
123
  * @param {Element} rangeEl
118
124
  * @returns {Action}
119
125
  */
120
- backspaceListMergePrev: (prev, formatEl, rangeEl) => ({ t: 'backspace.list.mergePrev', p: { prev, formatEl, rangeEl } }),
126
+ backspaceListMergePrev: (prev, formatEl, rangeEl) => ({
127
+ t: 'backspace.list.mergePrev',
128
+ p: { prev, formatEl, rangeEl },
129
+ }),
121
130
  /**
122
131
  * @param {Range} range
123
132
  * @returns {Action}
124
133
  */
125
134
  backspaceListRemoveNested: (range) => ({ t: 'backspace.list.removeNested', p: { range } }),
135
+ /**
136
+ * @param {Element} formatEl - empty line to remove
137
+ * @param {Element} prev - previous (normal) line to receive the caret
138
+ * @returns {Action}
139
+ */
140
+ backspaceEmptyLineMergePrev: (formatEl, prev) => ({ t: 'backspace.emptyLine.mergePrev', p: { formatEl, prev } }),
141
+
142
+ /**
143
+ * @param {Node} rowEndBr - the `<br>` the caret sits on (end of the empty row)
144
+ * @param {Node} rowStartBr - the `<br>` that starts the empty row (removed to merge it up)
145
+ * @returns {Action}
146
+ */
147
+ backspaceBrLineRowMerge: (rowEndBr, rowStartBr) => ({
148
+ t: 'backspace.brline.rowMerge',
149
+ p: { rowEndBr, rowStartBr },
150
+ }),
126
151
 
127
152
  // === delete ===
128
153
  /**
@@ -130,7 +155,10 @@ export const A = {
130
155
  * @param {SunEditor.ComponentInfo} fileComponentInfo
131
156
  * @returns {Action}
132
157
  */
133
- deleteComponentSelect: (formatEl, fileComponentInfo) => ({ t: 'delete.component.select', p: { formatEl, fileComponentInfo } }),
158
+ deleteComponentSelect: (formatEl, fileComponentInfo) => ({
159
+ t: 'delete.component.select',
160
+ p: { formatEl, fileComponentInfo },
161
+ }),
134
162
  /**
135
163
  * @param {Element} nextEl
136
164
  * @returns {Action}
@@ -142,7 +170,22 @@ export const A = {
142
170
  * @param {Element} rangeEl
143
171
  * @returns {Action}
144
172
  */
145
- deleteListRemoveNested: (range, formatEl, rangeEl) => ({ t: 'delete.list.removeNested', p: { range, formatEl, rangeEl } }),
173
+ deleteListRemoveNested: (range, formatEl, rangeEl) => ({
174
+ t: 'delete.list.removeNested',
175
+ p: { range, formatEl, rangeEl },
176
+ }),
177
+ /**
178
+ * @param {Element} formatEl - empty line to remove
179
+ * @param {Element} next - next (normal) line to receive the caret
180
+ * @returns {Action}
181
+ */
182
+ deleteEmptyLineMergeNext: (formatEl, next) => ({ t: 'delete.emptyLine.mergeNext', p: { formatEl, next } }),
183
+
184
+ /**
185
+ * @param {Node} rowEndBr - the `<br>` the caret sits on (end of the empty row); removed to pull the next row up
186
+ * @returns {Action}
187
+ */
188
+ deleteBrLineRowMerge: (rowEndBr) => ({ t: 'delete.brline.rowMerge', p: { rowEndBr } }),
146
189
 
147
190
  // === tab ===
148
191
  /**
@@ -184,7 +227,10 @@ export const A = {
184
227
  * @param {number} offset
185
228
  * @returns {Action}
186
229
  */
187
- enterFormatCleanBrAndZWS: (selectionNode, selectionFormat, brBlock, children, offset) => ({ t: 'enter.format.cleanBrAndZWS', p: { selectionNode, selectionFormat, brBlock, children, offset } }),
230
+ enterFormatCleanBrAndZWS: (selectionNode, selectionFormat, brBlock, children, offset) => ({
231
+ t: 'enter.format.cleanBrAndZWS',
232
+ p: { selectionNode, selectionFormat, brBlock, children, offset },
233
+ }),
188
234
  /**
189
235
  * @param {Element} brBlock
190
236
  * @param {Range} range
@@ -192,12 +238,27 @@ export const A = {
192
238
  * @param {number} offset
193
239
  * @returns {Action}
194
240
  */
195
- enterFormatInsertBrHtml: (brBlock, range, wSelection, offset) => ({ t: 'enter.format.insertBrHtml', p: { brBlock, range, wSelection, offset } }),
241
+ enterFormatInsertBrHtml: (brBlock, range, wSelection, offset) => ({
242
+ t: 'enter.format.insertBrHtml',
243
+ p: { brBlock, range, wSelection, offset },
244
+ }),
196
245
  /**
197
246
  * @param {Selection} wSelection
198
247
  * @returns {Action}
199
248
  */
200
249
  enterFormatInsertBrNode: (wSelection) => ({ t: 'enter.format.insertBrNode', p: { wSelection } }),
250
+ /**
251
+ * @description Insert one empty row at the caret inside a normal brLine (e.g. PRE).
252
+ * @param {Range} range Caret range captured at rule time.
253
+ * @returns {Action}
254
+ */
255
+ enterBrLineInsert: (range) => ({ t: 'enter.brline.insert', p: { range } }),
256
+ /**
257
+ * @description Exit a normal brLine — drop its trailing empty rows and add a default line after it.
258
+ * @param {Element} brBlock
259
+ * @returns {Action}
260
+ */
261
+ enterBrLineExit: (brBlock) => ({ t: 'enter.brline.exit', p: { brBlock } }),
201
262
  /**
202
263
  * @param {Element} formatEl
203
264
  * @param {Node} selectionNode
@@ -206,7 +267,10 @@ export const A = {
206
267
  * @param {boolean} [bidiSwapped]
207
268
  * @returns {Action}
208
269
  */
209
- enterFormatBreakAtEdge: (formatEl, selectionNode, formatStartEdge, formatEndEdge, bidiSwapped) => ({ t: 'enter.format.breakAtEdge', p: { formatEl, selectionNode, formatStartEdge, formatEndEdge, bidiSwapped } }),
270
+ enterFormatBreakAtEdge: (formatEl, selectionNode, formatStartEdge, formatEndEdge, bidiSwapped) => ({
271
+ t: 'enter.format.breakAtEdge',
272
+ p: { formatEl, selectionNode, formatStartEdge, formatEndEdge, bidiSwapped },
273
+ }),
210
274
  /**
211
275
  * @param {Element} formatEl
212
276
  * @param {Range} range
@@ -214,7 +278,10 @@ export const A = {
214
278
  * @param {boolean} formatEndEdge
215
279
  * @returns {Action}
216
280
  */
217
- enterFormatBreakWithSelection: (formatEl, range, formatStartEdge, formatEndEdge) => ({ t: 'enter.format.breakWithSelection', p: { formatEl, range, formatStartEdge, formatEndEdge } }),
281
+ enterFormatBreakWithSelection: (formatEl, range, formatStartEdge, formatEndEdge) => ({
282
+ t: 'enter.format.breakWithSelection',
283
+ p: { formatEl, range, formatStartEdge, formatEndEdge },
284
+ }),
218
285
  /**
219
286
  * @param {Element} formatEl
220
287
  * @param {Range} range
@@ -58,7 +58,8 @@ export default {
58
58
  'selection.setRange': ({ ports }, { sc, so, ec, eo }) => ports.selection.setRange(sc, so, ec, eo),
59
59
 
60
60
  /** @action formatRemoveBlock */
61
- 'format.removeBlock': ({ ports }, { rangeEl, selectedFormats, newBlockElement, shouldDelete, skipHistory }) => ports.format.removeBlock(rangeEl, { selectedFormats, newBlockElement, shouldDelete, skipHistory }),
61
+ 'format.removeBlock': ({ ports }, { rangeEl, selectedFormats, newBlockElement, shouldDelete, skipHistory }) =>
62
+ ports.format.removeBlock(rangeEl, { selectedFormats, newBlockElement, shouldDelete, skipHistory }),
62
63
 
63
64
  /** [helper] */
64
65