@aquera/nile-elements 2.0.16 → 2.0.18

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 (383) hide show
  1. package/README.md +6 -0
  2. package/dist/{index-f6944844.cjs.js → index-0c0c869d.cjs.js} +2 -2
  3. package/dist/{index-f6944844.cjs.js.map → index-0c0c869d.cjs.js.map} +1 -1
  4. package/dist/{index-403841c7.cjs.js → index-129ef6fb.cjs.js} +3 -3
  5. package/dist/{index-403841c7.cjs.js.map → index-129ef6fb.cjs.js.map} +1 -1
  6. package/dist/{index-1faa963c.esm.js → index-36f2a6b5.esm.js} +1 -1
  7. package/dist/index-4df50481.esm.js +1 -0
  8. package/dist/index-5d34cff1.esm.js +1 -0
  9. package/dist/index-7ae95edd.cjs.js +2 -0
  10. package/dist/{index-3ca67627.cjs.js.map → index-7ae95edd.cjs.js.map} +1 -1
  11. package/dist/index-7ca72b78.esm.js +1 -0
  12. package/dist/index-bbe23ad8.cjs.js +2 -0
  13. package/dist/index-bbe23ad8.cjs.js.map +1 -0
  14. package/dist/index-ccc6a799.cjs.js +5 -0
  15. package/dist/index-ccc6a799.cjs.js.map +1 -0
  16. package/dist/index.cjs.js +1 -1
  17. package/dist/index.esm.js +1 -1
  18. package/dist/index.js +686 -464
  19. package/dist/nile-auto-complete/index.cjs.js +1 -1
  20. package/dist/nile-auto-complete/nile-auto-complete.cjs.js +1 -1
  21. package/dist/nile-auto-complete/portal-manager.cjs.js +1 -1
  22. package/dist/nile-auto-complete/portal-utils.cjs.js +1 -1
  23. package/dist/nile-code-editor/extensionSetup.cjs.js +2 -2
  24. package/dist/nile-code-editor/index.cjs.js +1 -1
  25. package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
  26. package/dist/nile-code-editor/theme.cjs.js +1 -1
  27. package/dist/nile-color-picker/index.cjs.js +1 -1
  28. package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
  29. package/dist/nile-combobox/index.cjs.js +1 -1
  30. package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
  31. package/dist/nile-combobox/portal-manager.cjs.js +1 -1
  32. package/dist/nile-context-menu/index.cjs.js +1 -1
  33. package/dist/nile-context-menu/nile-context-menu.cjs.js +1 -1
  34. package/dist/nile-context-submenu/index.cjs.js +1 -1
  35. package/dist/nile-context-submenu/nile-context-submenu.cjs.js +1 -1
  36. package/dist/nile-detail/index.cjs.js +1 -1
  37. package/dist/nile-detail/nile-detail.cjs.js +1 -1
  38. package/dist/nile-dropdown/index.cjs.js +1 -1
  39. package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
  40. package/dist/nile-dropdown/portal-manager.cjs.js +1 -1
  41. package/dist/nile-file-upload/index.cjs.js +1 -1
  42. package/dist/nile-file-upload/nile-file-upload.cjs.js +1 -1
  43. package/dist/nile-file-upload/nile-file-upload.template.cjs.js +1 -1
  44. package/dist/nile-floating-panel/index.cjs.js +1 -1
  45. package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
  46. package/dist/nile-lite-tooltip/index.cjs.js +1 -1
  47. package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
  48. package/dist/nile-markdown-editor/index.cjs.js +1 -1
  49. package/dist/nile-markdown-editor/nile-markdown-editor.cjs.js +1 -1
  50. package/dist/nile-minimap/index.cjs.js +2 -0
  51. package/dist/nile-minimap/index.cjs.js.map +1 -0
  52. package/dist/nile-minimap/index.esm.js +1 -0
  53. package/dist/nile-minimap/nile-minimap.cjs.js +2 -0
  54. package/dist/nile-minimap/nile-minimap.cjs.js.map +1 -0
  55. package/dist/nile-minimap/nile-minimap.css.cjs.js +2 -0
  56. package/dist/nile-minimap/nile-minimap.css.cjs.js.map +1 -0
  57. package/dist/nile-minimap/nile-minimap.css.esm.js +150 -0
  58. package/dist/nile-minimap/nile-minimap.esm.js +22 -0
  59. package/dist/nile-popover/index.cjs.js +1 -1
  60. package/dist/nile-popover/nile-popover.cjs.js +1 -1
  61. package/dist/nile-popover/portal-manager.cjs.js +1 -1
  62. package/dist/nile-popup/index.cjs.js +1 -1
  63. package/dist/nile-popup/nile-popup.cjs.js +1 -1
  64. package/dist/nile-segmented-progress-bar/index.cjs.js +1 -1
  65. package/dist/nile-segmented-progress-bar/nile-segmented-progress-bar.cjs.js +1 -1
  66. package/dist/nile-select/index.cjs.js +1 -1
  67. package/dist/nile-select/nile-select.cjs.js +1 -1
  68. package/dist/nile-select/portal-manager.cjs.js +1 -1
  69. package/dist/nile-side-bar-action/index.cjs.js +1 -1
  70. package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js +1 -1
  71. package/dist/nile-side-bar-action/portal-manager.cjs.js +1 -1
  72. package/dist/nile-tooltip/index.cjs.js +1 -1
  73. package/dist/nile-tooltip/nile-tooltip.cjs.js +1 -1
  74. package/dist/nile-virtual-select/index.cjs.js +1 -1
  75. package/dist/nile-virtual-select/nile-virtual-select.cjs.js +1 -1
  76. package/dist/nile-virtual-select/portal-manager.cjs.js +1 -1
  77. package/dist/nile-virtual-select/renderer.cjs.js +1 -1
  78. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
  79. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
  80. package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
  81. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
  82. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
  83. package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
  84. package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js +2 -0
  85. package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js.map +1 -0
  86. package/dist/nile-wysiwyg-editor/engine/email-css.esm.js +1 -0
  87. package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js +2 -0
  88. package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js.map +1 -0
  89. package/dist/nile-wysiwyg-editor/engine/email-render.esm.js +1 -0
  90. package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js +2 -0
  91. package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js.map +1 -0
  92. package/dist/nile-wysiwyg-editor/engine/html-detect.esm.js +1 -0
  93. package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
  94. package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
  95. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
  96. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js.map +1 -1
  97. package/dist/nile-wysiwyg-editor/engine/inputrules.esm.js +1 -1
  98. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
  99. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js.map +1 -1
  100. package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
  101. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
  102. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
  103. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
  104. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
  105. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
  106. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
  107. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
  108. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
  109. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
  110. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +1 -1
  111. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js.map +1 -1
  112. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -1
  113. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js +1 -1
  114. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js.map +1 -1
  115. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.esm.js +1 -1
  116. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js +1 -1
  117. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js.map +1 -1
  118. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.esm.js +1 -1
  119. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +1 -1
  120. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js.map +1 -1
  121. package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -1
  122. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js +1 -1
  123. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js.map +1 -1
  124. package/dist/nile-wysiwyg-editor/engine/plugins/link.esm.js +1 -1
  125. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js +1 -1
  126. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js.map +1 -1
  127. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.esm.js +1 -1
  128. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js +1 -1
  129. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js.map +1 -1
  130. package/dist/nile-wysiwyg-editor/engine/plugins/paste.esm.js +1 -1
  131. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +1 -1
  132. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js.map +1 -1
  133. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -1
  134. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js +1 -1
  135. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js.map +1 -1
  136. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.esm.js +1 -1
  137. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +1 -1
  138. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js.map +1 -1
  139. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -1
  140. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
  141. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
  142. package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
  143. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +2 -1
  144. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
  145. package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
  146. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
  147. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
  148. package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
  149. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
  150. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
  151. package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
  152. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js +1 -1
  153. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js.map +1 -1
  154. package/dist/nile-wysiwyg-editor/engine/serializer.esm.js +1 -1
  155. package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
  156. package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
  157. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
  158. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
  159. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
  160. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
  161. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +45 -4
  162. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +2 -2
  163. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
  164. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
  165. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js +1 -1
  166. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js.map +1 -1
  167. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.esm.js +2 -2
  168. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
  169. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
  170. package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +5 -5
  171. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
  172. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
  173. package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +1 -1
  174. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js +1 -1
  175. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js.map +1 -1
  176. package/dist/nile-wysiwyg-editor/ui/embed-menu.esm.js +1 -1
  177. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
  178. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
  179. package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
  180. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js +1 -1
  181. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js.map +1 -1
  182. package/dist/nile-wysiwyg-editor/ui/emoji-picker.esm.js +1 -1
  183. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js +1 -1
  184. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js.map +1 -1
  185. package/dist/nile-wysiwyg-editor/ui/find-replace.esm.js +3 -3
  186. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js +1 -1
  187. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js.map +1 -1
  188. package/dist/nile-wysiwyg-editor/ui/image-menu.esm.js +3 -3
  189. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js +1 -1
  190. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js.map +1 -1
  191. package/dist/nile-wysiwyg-editor/ui/image-panel.esm.js +1 -1
  192. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js +1 -1
  193. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js.map +1 -1
  194. package/dist/nile-wysiwyg-editor/ui/insert-menu.esm.js +1 -1
  195. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js +1 -1
  196. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js.map +1 -1
  197. package/dist/nile-wysiwyg-editor/ui/link-editor.esm.js +9 -9
  198. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js +1 -1
  199. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js.map +1 -1
  200. package/dist/nile-wysiwyg-editor/ui/markdown-panel.esm.js +1 -1
  201. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
  202. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
  203. package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +1 -1
  204. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js +1 -1
  205. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js.map +1 -1
  206. package/dist/nile-wysiwyg-editor/ui/shortcut-help.esm.js +1 -1
  207. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
  208. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
  209. package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +4 -4
  210. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js +1 -1
  211. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js.map +1 -1
  212. package/dist/nile-wysiwyg-editor/ui/special-chars.esm.js +1 -1
  213. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
  214. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
  215. package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +23 -12
  216. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
  217. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
  218. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +7 -7
  219. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
  220. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
  221. package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +1 -1
  222. package/dist/src/index.d.ts +1 -0
  223. package/dist/src/index.js +1 -0
  224. package/dist/src/index.js.map +1 -1
  225. package/dist/src/nile-minimap/index.d.ts +1 -0
  226. package/dist/src/nile-minimap/index.js +2 -0
  227. package/dist/src/nile-minimap/index.js.map +1 -0
  228. package/dist/src/nile-minimap/nile-minimap.css.d.ts +12 -0
  229. package/dist/src/nile-minimap/nile-minimap.css.js +162 -0
  230. package/dist/src/nile-minimap/nile-minimap.css.js.map +1 -0
  231. package/dist/src/nile-minimap/nile-minimap.d.ts +131 -0
  232. package/dist/src/nile-minimap/nile-minimap.js +704 -0
  233. package/dist/src/nile-minimap/nile-minimap.js.map +1 -0
  234. package/dist/src/nile-minimap/nile-minimap.test.d.ts +1 -0
  235. package/dist/src/nile-minimap/nile-minimap.test.js +785 -0
  236. package/dist/src/nile-minimap/nile-minimap.test.js.map +1 -0
  237. package/dist/src/nile-wysiwyg-editor/engine/create-state.d.ts +5 -0
  238. package/dist/src/nile-wysiwyg-editor/engine/create-state.js +2 -0
  239. package/dist/src/nile-wysiwyg-editor/engine/create-state.js.map +1 -1
  240. package/dist/src/nile-wysiwyg-editor/engine/email-css.d.ts +14 -0
  241. package/dist/src/nile-wysiwyg-editor/engine/email-css.js +140 -0
  242. package/dist/src/nile-wysiwyg-editor/engine/email-css.js.map +1 -0
  243. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.d.ts +1 -0
  244. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js +1045 -0
  245. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js.map +1 -0
  246. package/dist/src/nile-wysiwyg-editor/engine/email-render.d.ts +11 -0
  247. package/dist/src/nile-wysiwyg-editor/engine/email-render.js +93 -0
  248. package/dist/src/nile-wysiwyg-editor/engine/email-render.js.map +1 -0
  249. package/dist/src/nile-wysiwyg-editor/engine/html-detect.d.ts +2 -0
  250. package/dist/src/nile-wysiwyg-editor/engine/html-detect.js +24 -0
  251. package/dist/src/nile-wysiwyg-editor/engine/html-detect.js.map +1 -0
  252. package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +4 -1
  253. package/dist/src/nile-wysiwyg-editor/engine/index.js +3 -1
  254. package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
  255. package/dist/src/nile-wysiwyg-editor/engine/inputrules.d.ts +0 -5
  256. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js +19 -0
  257. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js.map +1 -1
  258. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js +3 -2
  259. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js.map +1 -1
  260. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.d.ts +4 -0
  261. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js +56 -1
  262. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js.map +1 -1
  263. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.d.ts +5 -0
  264. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js +27 -6
  265. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js.map +1 -1
  266. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +30 -7
  267. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
  268. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +46 -0
  269. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
  270. package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +9 -1
  271. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +730 -7
  272. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
  273. package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +3 -0
  274. package/dist/src/nile-wysiwyg-editor/engine/schema.js +215 -34
  275. package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
  276. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js +3 -1
  277. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js.map +1 -1
  278. package/dist/src/nile-wysiwyg-editor/engine/serializer.d.ts +12 -4
  279. package/dist/src/nile-wysiwyg-editor/engine/serializer.js +18 -8
  280. package/dist/src/nile-wysiwyg-editor/engine/serializer.js.map +1 -1
  281. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.d.ts +1 -0
  282. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js +287 -0
  283. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js.map +1 -0
  284. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +45 -4
  285. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
  286. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +23 -0
  287. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +113 -15
  288. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
  289. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +213 -0
  290. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
  291. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js +2 -2
  292. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js.map +1 -1
  293. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +1 -1
  294. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
  295. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js +1 -1
  296. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js.map +1 -1
  297. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +1 -1
  298. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
  299. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js +1 -1
  300. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js.map +1 -1
  301. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js +1 -1
  302. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js.map +1 -1
  303. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js +1 -1
  304. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js.map +1 -1
  305. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js +1 -1
  306. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js.map +1 -1
  307. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js +1 -1
  308. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js.map +1 -1
  309. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js +2 -2
  310. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js.map +1 -1
  311. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js +1 -1
  312. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js.map +1 -1
  313. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +1 -1
  314. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
  315. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js +1 -1
  316. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js.map +1 -1
  317. package/dist/src/nile-wysiwyg-editor/ui/source-view.js +2 -2
  318. package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
  319. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js +1 -1
  320. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js.map +1 -1
  321. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +14 -3
  322. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
  323. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +2 -2
  324. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
  325. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +1 -1
  326. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
  327. package/dist/src/version.js +1 -1
  328. package/dist/src/version.js.map +1 -1
  329. package/dist/tsconfig.tsbuildinfo +1 -1
  330. package/package.json +2 -1
  331. package/src/index.ts +1 -0
  332. package/src/nile-minimap/index.ts +1 -0
  333. package/src/nile-minimap/nile-minimap.css.ts +164 -0
  334. package/src/nile-minimap/nile-minimap.test.ts +924 -0
  335. package/src/nile-minimap/nile-minimap.ts +796 -0
  336. package/src/nile-wysiwyg-editor/engine/create-state.ts +7 -0
  337. package/src/nile-wysiwyg-editor/engine/email-css.ts +136 -0
  338. package/src/nile-wysiwyg-editor/engine/email-fidelity.test.ts +1347 -0
  339. package/src/nile-wysiwyg-editor/engine/email-render.ts +99 -0
  340. package/src/nile-wysiwyg-editor/engine/html-detect.ts +26 -0
  341. package/src/nile-wysiwyg-editor/engine/index.ts +10 -1
  342. package/src/nile-wysiwyg-editor/engine/inputrules.ts +18 -1
  343. package/src/nile-wysiwyg-editor/engine/plugins/image-view.ts +3 -2
  344. package/src/nile-wysiwyg-editor/engine/plugins/link.ts +58 -2
  345. package/src/nile-wysiwyg-editor/engine/plugins/paste.ts +31 -8
  346. package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +29 -7
  347. package/src/nile-wysiwyg-editor/engine/pm.test.ts +73 -0
  348. package/src/nile-wysiwyg-editor/engine/sanitize.ts +729 -7
  349. package/src/nile-wysiwyg-editor/engine/schema.ts +206 -33
  350. package/src/nile-wysiwyg-editor/engine/selection-state.ts +3 -1
  351. package/src/nile-wysiwyg-editor/engine/serializer.ts +30 -8
  352. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.ts +357 -0
  353. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +45 -4
  354. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +258 -0
  355. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +119 -12
  356. package/src/nile-wysiwyg-editor/ui/bookmark-panel.ts +2 -2
  357. package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +1 -1
  358. package/src/nile-wysiwyg-editor/ui/embed-menu.ts +1 -1
  359. package/src/nile-wysiwyg-editor/ui/embed-panel.ts +1 -1
  360. package/src/nile-wysiwyg-editor/ui/emoji-picker.ts +1 -1
  361. package/src/nile-wysiwyg-editor/ui/find-replace.ts +1 -1
  362. package/src/nile-wysiwyg-editor/ui/image-menu.ts +1 -1
  363. package/src/nile-wysiwyg-editor/ui/image-panel.ts +1 -1
  364. package/src/nile-wysiwyg-editor/ui/insert-menu.ts +1 -1
  365. package/src/nile-wysiwyg-editor/ui/link-editor.ts +2 -2
  366. package/src/nile-wysiwyg-editor/ui/markdown-panel.ts +1 -1
  367. package/src/nile-wysiwyg-editor/ui/math-panel.ts +1 -1
  368. package/src/nile-wysiwyg-editor/ui/shortcut-help.ts +1 -1
  369. package/src/nile-wysiwyg-editor/ui/source-view.ts +2 -2
  370. package/src/nile-wysiwyg-editor/ui/special-chars.ts +1 -1
  371. package/src/nile-wysiwyg-editor/ui/suggest-list.ts +14 -3
  372. package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +2 -2
  373. package/src/nile-wysiwyg-editor/ui/table-menu.ts +1 -1
  374. package/vscode-html-custom-data.json +116 -25
  375. package/web-test-runner.config.mjs +4 -0
  376. package/dist/index-3ca67627.cjs.js +0 -2
  377. package/dist/index-6f3fa4ab.esm.js +0 -1
  378. package/dist/index-7b1c9857.esm.js +0 -1
  379. package/dist/index-89c520e9.cjs.js +0 -5
  380. package/dist/index-89c520e9.cjs.js.map +0 -1
  381. package/dist/index-ba47645d.esm.js +0 -1
  382. package/dist/index-c3210a9c.cjs.js +0 -2
  383. package/dist/index-c3210a9c.cjs.js.map +0 -1
@@ -0,0 +1,99 @@
1
+ import { Fragment, Node as PMNode } from 'prosemirror-model';
2
+
3
+ /** email-fidelity: writes the editor's data-table rendering into the saved HTML; layout tables keep their authored output. */
4
+
5
+ /** Theme-resolved colours the editor draws data tables with; null = not drawn. */
6
+ export interface EmailRenderDefaults {
7
+ borderColor: string | null;
8
+ headerBackground: string | null;
9
+ }
10
+
11
+ /** Kept in sync by hand with `.ProseMirror td, .ProseMirror th { padding: 6px 8px }`. */
12
+ const CELL_PADDING: Record<string, string> = {
13
+ paddingTop: '6px',
14
+ paddingRight: '8px',
15
+ paddingBottom: '6px',
16
+ paddingLeft: '8px',
17
+ };
18
+
19
+ const CELL_BORDERS = ['borderTop', 'borderRight', 'borderBottom', 'borderLeft'];
20
+
21
+ /** `.ProseMirror td > *` zeroes block margins inside cells. */
22
+ const CELL_CHILD_MARGIN = 'margin-top: 0px; margin-bottom: 0px';
23
+
24
+ type Context = 'data-table' | 'data-cell' | null;
25
+
26
+ /** Returns a copy of `doc` with the editor's data-table rendering filled into node attrs. */
27
+ export function applyEmailRenderDefaults(doc: PMNode, defaults: EmailRenderDefaults): PMNode {
28
+ return fill(doc, null, defaults);
29
+ }
30
+
31
+ function fill(node: PMNode, context: Context, defaults: EmailRenderDefaults): PMNode {
32
+ if (node.isLeaf) return node;
33
+ const name = node.type.name;
34
+ let attrs = node.attrs;
35
+ let childContext: Context = null;
36
+
37
+ if (name === 'table') {
38
+ if (!attrs.layout) {
39
+ childContext = 'data-table';
40
+ if (!attrs.width) attrs = { ...attrs, width: '100%' };
41
+ }
42
+ } else if (name === 'table_row') {
43
+ childContext = context;
44
+ } else if ((name === 'table_cell' || name === 'table_header') && context === 'data-table') {
45
+ attrs = cellDefaults(attrs, name === 'table_header', defaults);
46
+ childContext = 'data-cell';
47
+ } else if (context === 'data-cell' && 'margin' in attrs && !attrs.margin) {
48
+ attrs = { ...attrs, margin: CELL_CHILD_MARGIN };
49
+ }
50
+
51
+ const children: PMNode[] = [];
52
+ node.forEach(child => children.push(fill(child, childContext, defaults)));
53
+ return node.type.create(attrs, Fragment.fromArray(children), node.marks);
54
+ }
55
+
56
+ function cellDefaults(
57
+ attrs: Record<string, any>,
58
+ header: boolean,
59
+ defaults: EmailRenderDefaults
60
+ ): Record<string, any> {
61
+ const next = { ...attrs };
62
+ for (const [key, value] of Object.entries(CELL_PADDING)) if (!next[key]) next[key] = value;
63
+ if (!next.valign) next.valign = 'top';
64
+ if (defaults.borderColor) {
65
+ const border = `1px solid ${defaults.borderColor}`;
66
+ for (const key of CELL_BORDERS) if (!next[key]) next[key] = border;
67
+ }
68
+ // A resized column keeps its width in data-colwidth, which mail clients ignore.
69
+ const colwidth = next.colwidth as number[] | null;
70
+ if (!next.width && Array.isArray(colwidth) && colwidth.length && colwidth.every(w => w > 0)) {
71
+ next.width = `${colwidth.reduce((sum, w) => sum + w, 0)}px`;
72
+ }
73
+ if (header) {
74
+ if (!next.background && defaults.headerBackground) next.background = defaults.headerBackground;
75
+ if (!next.textAlign) next.textAlign = 'left';
76
+ const textStyle = (next.textStyle as string | null) ?? '';
77
+ if (!/font-weight/i.test(textStyle)) {
78
+ next.textStyle = textStyle ? `${textStyle}; font-weight: 600` : 'font-weight: 600';
79
+ }
80
+ }
81
+ return next;
82
+ }
83
+
84
+ /** Collapsed borders and no cell spacing on every table, fixed columns on data tables. */
85
+ export function finishEmailTables(container: HTMLElement): void {
86
+ for (const table of Array.from(container.querySelectorAll('table'))) {
87
+ if (!table.hasAttribute('cellpadding')) table.setAttribute('cellpadding', '0');
88
+ if (!table.hasAttribute('cellspacing')) table.setAttribute('cellspacing', '0');
89
+ const style = table.getAttribute('style') ?? '';
90
+ const add: string[] = [];
91
+ if (!/(^|;)\s*border-collapse\s*:/i.test(style)) add.push('border-collapse: collapse');
92
+ const layout = table.getAttribute('role') === 'presentation';
93
+ if (!layout && !/(^|;)\s*table-layout\s*:/i.test(style)) add.push('table-layout: fixed');
94
+ if (add.length) {
95
+ const base = style.trim().replace(/;\s*$/, '');
96
+ table.setAttribute('style', base ? `${base}; ${add.join('; ')}` : add.join('; '));
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,26 @@
1
+ /** Conservative check: does a text/plain paste look like HTML rather than prose or Markdown? */
2
+
3
+ /** Block-level tags whose balanced open+close pair marks real HTML structure. */
4
+ const BLOCK_TAGS = 'table|div|p|h1|h2|h3|h4|h5|h6|ul|ol|blockquote|section|body';
5
+
6
+ const BALANCED_BLOCK = new RegExp(
7
+ `<(${BLOCK_TAGS})(?:\\s[^>]*)?>[\\s\\S]*?</\\1\\s*>`,
8
+ 'i'
9
+ );
10
+
11
+ export function looksLikeHtml(text: string): boolean {
12
+ if (!text) return false;
13
+ // Skip leading whitespace and HTML comments so a doctype/`<html>` preamble
14
+ // behind a comment still counts.
15
+ let head = text.replace(/^\s+/, '');
16
+ while (head.startsWith('<!--')) {
17
+ const end = head.indexOf('-->');
18
+ if (end === -1) break;
19
+ head = head.slice(end + 3).replace(/^\s+/, '');
20
+ }
21
+ const lower = head.toLowerCase();
22
+ if (lower.startsWith('<!doctype html') || lower.startsWith('<html')) return true;
23
+ // A balanced block-level element anywhere is enough; the backreference keeps
24
+ // the open and close tag names matched.
25
+ return BALANCED_BLOCK.test(text);
26
+ }
@@ -1,6 +1,15 @@
1
1
  export { createSchema, defaultSchema } from './schema';
2
2
  export type { Alignment } from './schema';
3
- export { sanitizeHtml, isSafeUrl } from './sanitize';
3
+ export { sanitizeHtml, isSafeUrl, relocateEmailContainers } from './sanitize';
4
+ export {
5
+ normalizeEmailAllowedStyles,
6
+ parseAllowedStylesAttr,
7
+ isExtensionPropValid,
8
+ isExtensionValueValid,
9
+ sanitizeExtraStyleForWrite,
10
+ } from './email-css';
11
+ export type { NormalizedAllowedStyles } from './email-css';
12
+ export { looksLikeHtml } from './html-detect';
4
13
  export {
5
14
  htmlToDoc,
6
15
  htmlToSlice,
@@ -5,7 +5,7 @@ import {
5
5
  InputRule,
6
6
  } from 'prosemirror-inputrules';
7
7
  import { MarkType, Schema } from 'prosemirror-model';
8
- import { Plugin } from 'prosemirror-state';
8
+ import { Plugin, TextSelection } from 'prosemirror-state';
9
9
 
10
10
  function escapeRegex(text: string): string {
11
11
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -128,6 +128,21 @@ function linkifyRule(schema: Schema): InputRule {
128
128
  * (**bold**, *italic*, `code`, ~~strike~~), and (unless disabled) the
129
129
  * typography autocorrect rules.
130
130
  */
131
+ /** "---", "***" or "___" alone in a top-level paragraph → horizontal rule. */
132
+ function horizontalRuleRule(schema: Schema): InputRule {
133
+ return new InputRule(/^(?:---|\*\*\*|___)$/, (state, _match, start) => {
134
+ const $start = state.doc.resolve(start);
135
+ const { paragraph, horizontal_rule: hr } = schema.nodes;
136
+ // The rule's two earlier characters must be the whole paragraph.
137
+ if ($start.parent.type !== paragraph || $start.parent.content.size !== 2) return null;
138
+ const index = $start.index(-1);
139
+ if (!$start.node(-1).canReplaceWith(index, index + 1, hr)) return null;
140
+ const before = $start.before();
141
+ const tr = state.tr.replaceWith(before, $start.after(), [hr.create(), paragraph.create()]);
142
+ return tr.setSelection(TextSelection.create(tr.doc, before + 2)).scrollIntoView();
143
+ });
144
+ }
145
+
131
146
  export function buildInputRules(schema: Schema, options: InputRulesOptions = {}): Plugin {
132
147
  const transforms = options.textTransform === false ? [] : textTransformRules();
133
148
  return inputRules({
@@ -152,6 +167,8 @@ export function buildInputRules(schema: Schema, options: InputRulesOptions = {})
152
167
  ),
153
168
  // "```" → code block
154
169
  textblockTypeInputRule(/^```$/, schema.nodes.code_block),
170
+ // "---" / "***" / "___" → horizontal rule
171
+ horizontalRuleRule(schema),
155
172
  // "[] " or "[ ] " → checklist
156
173
  wrappingInputRule(/^\s*\[[ ]?\]\s$/, schema.nodes.task_list),
157
174
  // Inline marks. Bold before italic so '**' is never read as italic;
@@ -1,6 +1,7 @@
1
1
  import { Node as PMNode } from 'prosemirror-model';
2
2
  import { EditorView, NodeView } from 'prosemirror-view';
3
3
  import { NodeSelection } from 'prosemirror-state';
4
+ import { imageWidthCss } from '../schema';
4
5
 
5
6
  const MIN_WIDTH = 40;
6
7
 
@@ -92,7 +93,7 @@ export class ImageView implements NodeView {
92
93
  else this.img.removeAttribute('alt');
93
94
  if (title) this.img.title = title;
94
95
  else this.img.removeAttribute('title');
95
- this.img.style.width = width ? `${width}px` : '';
96
+ this.img.style.width = imageWidthCss(width) ?? '';
96
97
  }
97
98
 
98
99
  update(node: PMNode): boolean {
@@ -183,7 +184,7 @@ export class FigureView implements NodeView {
183
184
  else this.img.removeAttribute('alt');
184
185
  if (title) this.img.title = title;
185
186
  else this.img.removeAttribute('title');
186
- this.dom.style.width = width ? `${width}px` : '';
187
+ this.dom.style.width = imageWidthCss(width) ?? '';
187
188
  if (align) this.dom.setAttribute('data-align', align);
188
189
  else this.dom.removeAttribute('data-align');
189
190
  this.dom.classList.toggle(
@@ -1,4 +1,5 @@
1
- import { Plugin } from 'prosemirror-state';
1
+ import { Plugin, PluginKey, EditorState } from 'prosemirror-state';
2
+ import { Mark } from 'prosemirror-model';
2
3
  import { isSafeUrl } from '../sanitize';
3
4
 
4
5
  const URL_PATTERN = /^https?:\/\/\S+$/i;
@@ -8,14 +9,69 @@ export interface LinkPluginOptions {
8
9
  onOpenLink?: (href: string) => void;
9
10
  }
10
11
 
12
+ /** Where retyped link text continues: the caret position and the link it keeps. */
13
+ interface RetypeState {
14
+ pos: number;
15
+ mark: Mark;
16
+ }
17
+
18
+ const retypeKey = new PluginKey<RetypeState | null>('nile-wysiwyg-link-retype');
19
+
20
+ /** The link mark covering every text node in [from, to), or null. */
21
+ function linkCovering(state: EditorState, from: number, to: number): Mark | null {
22
+ const type = state.schema.marks.link;
23
+ let mark: Mark | null = null;
24
+ let covered = true;
25
+ state.doc.nodesBetween(from, to, node => {
26
+ if (!covered || !node.isText) return;
27
+ const found = type.isInSet(node.marks);
28
+ if (!found || (mark && !found.eq(mark))) covered = false;
29
+ else mark = found;
30
+ });
31
+ return covered ? mark : null;
32
+ }
33
+
11
34
  /**
12
35
  * Link behaviors that belong to the document, not the chrome:
13
36
  * - Mod/Ctrl-click opens the link.
14
37
  * - Pasting a bare URL over a text selection turns the selection into a link.
38
+ * - Typing over a selection that is entirely one link keeps the link, for the
39
+ * replaced text and for the characters typed straight after it (the link mark
40
+ * is non-inclusive, so without this the new label drops out of the link — an
41
+ * email button renamed by retyping lost its href).
15
42
  */
16
43
  export function linkPlugin(options: LinkPluginOptions = {}): Plugin {
17
- return new Plugin({
44
+ return new Plugin<RetypeState | null>({
45
+ key: retypeKey,
46
+ state: {
47
+ init: () => null,
48
+ apply(tr, prev) {
49
+ const meta = tr.getMeta(retypeKey) as RetypeState | null | undefined;
50
+ if (meta !== undefined) return meta;
51
+ if (!prev) return null;
52
+ // Any caret move, or an edit elsewhere, ends the retype run.
53
+ if (!tr.docChanged) return tr.selectionSet ? null : prev;
54
+ const pos = tr.mapping.map(prev.pos);
55
+ return tr.selection.empty && tr.selection.from === pos ? { ...prev, pos } : null;
56
+ },
57
+ },
18
58
  props: {
59
+ handleTextInput(view, from, to, text) {
60
+ const { state } = view;
61
+ let mark: Mark | null = null;
62
+ if (from < to) mark = linkCovering(state, from, to);
63
+ else {
64
+ const run = retypeKey.getState(state);
65
+ if (run && run.pos === from) mark = run.mark;
66
+ }
67
+ if (!mark) return false;
68
+ const end = from + text.length;
69
+ const tr = state.tr.insertText(text, from, to).addMark(from, end, mark);
70
+ tr.setMeta(retypeKey, { pos: end, mark });
71
+ view.dispatch(tr);
72
+ return true;
73
+ },
74
+
19
75
  handleClick(view, _pos, event) {
20
76
  if (!(event.metaKey || event.ctrlKey)) return false;
21
77
  const target = (event.target as HTMLElement | null)?.closest?.('a[href]');
@@ -1,7 +1,9 @@
1
1
  import { Plugin } from 'prosemirror-state';
2
2
  import { Slice, Fragment, Node as PMNode, Schema } from 'prosemirror-model';
3
3
  import { markdownToHtml, looksLikeMarkdown } from '../markdown-parse';
4
- import { htmlToSlice } from '../serializer';
4
+ import { looksLikeHtml } from '../html-detect';
5
+ import { htmlToDoc, htmlToSlice } from '../serializer';
6
+ import { NormalizedAllowedStyles } from '../email-css';
5
7
 
6
8
  /**
7
9
  * Builds a slice of plain paragraphs from text, splitting on newlines. Open
@@ -36,6 +38,10 @@ export interface PasteOptions {
36
38
  getMarkdownPaste?: () => boolean;
37
39
  /** Read live: whether pasted HTML is sanitized. Defaults to on. */
38
40
  getSanitize?: () => boolean;
41
+ /** Read live: when true, HTML-looking plain text is parsed as HTML (email fidelity). */
42
+ getEmailFidelity?: () => boolean;
43
+ /** Read live: the validated per-app email-fidelity style allowlist. */
44
+ getAllowedStyles?: () => NormalizedAllowedStyles;
39
45
  }
40
46
 
41
47
  /**
@@ -47,20 +53,37 @@ export function pastePlugin(schema: Schema, options: PasteOptions = {}): Plugin
47
53
  return new Plugin({
48
54
  props: {
49
55
  handlePaste(view, event) {
50
- if (options.getForcePlainText?.()) return false;
51
- if (options.getMarkdownPaste && !options.getMarkdownPaste()) return false;
52
56
  // Only plain-text pastes qualify: an HTML flavor means the source
53
57
  // was already rich, and code blocks take text verbatim.
54
58
  const html = event.clipboardData?.getData('text/html') ?? '';
55
59
  const text = event.clipboardData?.getData('text/plain') ?? '';
56
60
  if (html.trim() || !text.trim()) return false;
57
61
  if (view.state.selection.$from.parent.type.spec.code) return false;
62
+ const forcePlain = options.getForcePlainText?.() ?? false;
63
+ const sanitize = options.getSanitize?.() ?? true;
64
+
65
+ // Precedence: paste-as-plain-text → HTML detection (fidelity) → Markdown.
66
+ if (looksLikeHtml(text)) {
67
+ if (options.getEmailFidelity?.() && !forcePlain) {
68
+ // Closed slice so the whole document (tables, blocks) lands intact
69
+ // rather than being opened and merged into the target paragraph.
70
+ const doc = htmlToDoc(schema, text, sanitize, {
71
+ emailFidelity: true,
72
+ allowedStyles: options.getAllowedStyles?.(),
73
+ });
74
+ const slice = new Slice(doc.content, 0, 0);
75
+ view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
76
+ } else {
77
+ const slice = plainTextSlice(schema, text);
78
+ view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
79
+ }
80
+ return true;
81
+ }
82
+
83
+ if (forcePlain) return false;
84
+ if (options.getMarkdownPaste && !options.getMarkdownPaste()) return false;
58
85
  if (!looksLikeMarkdown(text)) return false;
59
- const slice = htmlToSlice(
60
- schema,
61
- markdownToHtml(text),
62
- options.getSanitize?.() ?? true
63
- );
86
+ const slice = htmlToSlice(schema, markdownToHtml(text), sanitize);
64
87
  view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
65
88
  return true;
66
89
  },
@@ -1,14 +1,9 @@
1
1
  import { Plugin } from 'prosemirror-state';
2
2
  import { Node as PMNode } from 'prosemirror-model';
3
3
  import { tableEditing, columnResizing, TableView } from 'prosemirror-tables';
4
+ import { tableStyles } from '../schema';
4
5
 
5
- /**
6
- * TableView that also reflects the author's `class`/`id` onto the inner
7
- * `<table>` element (the base TableView leaves it bare and puts its own class on
8
- * the wrapper div). The attributes round-trip via the schema's toDOM on export;
9
- * this makes `table.grid …` style rules match in the live editor too. Passed to
10
- * `columnResizing` as its `View`, so column resizing keeps working.
11
- */
6
+ /** TableView that also puts the author's class/id and fidelity table styles on the inner `<table>`. */
12
7
  class HtmlAttrTableView extends TableView {
13
8
  constructor(node: PMNode, defaultCellMinWidth: number) {
14
9
  super(node, defaultCellMinWidth);
@@ -21,6 +16,33 @@ class HtmlAttrTableView extends TableView {
21
16
  this.table.className = htmlClass ?? '';
22
17
  if (htmlId) this.table.id = htmlId;
23
18
  else this.table.removeAttribute('id');
19
+ // Email-fidelity attrs (width, max-width, background, border, centring).
20
+ // Width is only overridden when set, so column-resize sizing still applies.
21
+ const style = this.table.style;
22
+ // tableStyles() entries can hold several declarations ('color: x; font-family: y'
23
+ // for the table text style or an allowedStyles extension), so flatten them first.
24
+ const decls = tableStyles(node.attrs)
25
+ .flatMap(entry => entry.split(';'))
26
+ .map(decl => decl.trim())
27
+ .filter(decl => decl.includes(':'));
28
+ for (const prop of [
29
+ 'max-width', 'background-color', 'border', 'margin-left', 'margin-right',
30
+ 'color', 'font-family', 'font-size', 'font-weight', 'line-height',
31
+ ]) {
32
+ style.removeProperty(prop);
33
+ }
34
+ // Clear any per-app extension properties first so a value that changed (or an
35
+ // entry that was removed) doesn't linger from the previous application.
36
+ for (const decl of decls) style.removeProperty(decl.slice(0, decl.indexOf(':')).trim());
37
+ for (const decl of decls) {
38
+ const idx = decl.indexOf(':');
39
+ style.setProperty(decl.slice(0, idx).trim(), decl.slice(idx + 1).trim());
40
+ }
41
+ // A layout table with no width of its own (an email button) shrinks to fit
42
+ // under email-fidelity; the host CSS keys off this hook.
43
+ this.table.toggleAttribute('data-autowidth', Boolean(node.attrs.layout && !node.attrs.width));
44
+ // Lets the host CSS drop column resizing's min-width on layout tables.
45
+ this.table.toggleAttribute('data-layout-table', Boolean(node.attrs.layout));
24
46
  }
25
47
 
26
48
  update(node: PMNode): boolean {
@@ -948,6 +948,79 @@ describe('figure & image commands', () => {
948
948
  });
949
949
  });
950
950
 
951
+ describe('image percentage widths (fix 1, global)', () => {
952
+ const ON = { emailFidelity: true } as const;
953
+
954
+ /** First <img> style.width after a full htmlToDoc→docToHtml round-trip. */
955
+ function imgWidth(html: string, on = false): string {
956
+ const doc = on
957
+ ? htmlToDoc(defaultSchema, html, true, ON)
958
+ : htmlToDoc(defaultSchema, html);
959
+ const out = docToHtml(doc);
960
+ const m = new DOMParser().parseFromString(out, 'text/html');
961
+ return m.querySelector('img')?.style.width ?? '';
962
+ }
963
+
964
+ it('round-trips width:100% as a percentage with the flag off', () => {
965
+ expect(imgWidth('<p><img src="https://x.co/i.png" style="width:100%"></p>')).to.equal('100%');
966
+ });
967
+
968
+ it('round-trips width:100% as a percentage with the flag on', () => {
969
+ expect(
970
+ imgWidth('<p><img src="https://x.co/i.png" style="width:100%"></p>', true)
971
+ ).to.equal('100%');
972
+ });
973
+
974
+ it('keeps a fractional percentage (50.5%)', () => {
975
+ expect(imgWidth('<p><img src="https://x.co/i.png" style="width:50.5%"></p>')).to.equal('50.5%');
976
+ });
977
+
978
+ it('still emits a pixel width for width:320px', () => {
979
+ expect(imgWidth('<p><img src="https://x.co/i.png" style="width:320px"></p>')).to.equal('320px');
980
+ });
981
+
982
+ it('falls back to the width attribute (px) when there is no style width', () => {
983
+ expect(imgWidth('<p><img src="https://x.co/i.png" width="1232"></p>')).to.equal('1232px');
984
+ });
985
+
986
+ it('drops an out-of-range percentage (150%)', () => {
987
+ const out = docToHtml(
988
+ htmlToDoc(defaultSchema, '<p><img src="https://x.co/i.png" style="width:150%"></p>')
989
+ );
990
+ expect(out).to.not.include('150%');
991
+ expect(out).to.not.include('width: 150');
992
+ });
993
+
994
+ it('drops a calc() width', () => {
995
+ const out = docToHtml(
996
+ htmlToDoc(
997
+ defaultSchema,
998
+ '<p><img src="https://x.co/i.png" style="width:calc(100% - 2px)"></p>',
999
+ true,
1000
+ ON
1001
+ )
1002
+ );
1003
+ expect(out).to.not.include('calc(');
1004
+ });
1005
+
1006
+ it('round-trips a percentage width on a figure', () => {
1007
+ const out = docToHtml(
1008
+ htmlToDoc(
1009
+ defaultSchema,
1010
+ '<figure><img src="https://x.co/i.png" style="width:50%"><figcaption>c</figcaption></figure>'
1011
+ )
1012
+ );
1013
+ expect(out).to.include('width: 50%');
1014
+ });
1015
+
1016
+ it('serialize → parse → serialize is a fixpoint for a percentage width', () => {
1017
+ const src = '<p><img src="https://x.co/i.png" style="width:100%"></p>';
1018
+ const once = docToHtml(htmlToDoc(defaultSchema, src, true, ON));
1019
+ const twice = docToHtml(htmlToDoc(defaultSchema, once, true, ON));
1020
+ expect(twice).to.equal(once);
1021
+ });
1022
+ });
1023
+
951
1024
  describe('paste from Office', () => {
952
1025
  it('detects Word and Google Docs clipboard HTML', () => {
953
1026
  expect(isOfficeHtml('<p class="MsoNormal">x</p>')).to.equal(true);