@aquera/nile-elements 2.0.15 → 2.0.17

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 (376) hide show
  1. package/README.md +6 -0
  2. package/demo/nxtgen-classes.css +5 -5
  3. package/demo/nxtgen-dark.css +16 -16
  4. package/demo/nxtgen-properties.css +2 -2
  5. package/demo/nxtgen-utilities.css +5 -5
  6. package/demo/nxtgen.css +5 -5
  7. package/demo/utilities.css +2 -2
  8. package/demo/variables.css +2 -2
  9. package/dist/{index-f6944844.cjs.js → index-0c0c869d.cjs.js} +2 -2
  10. package/dist/{index-f6944844.cjs.js.map → index-0c0c869d.cjs.js.map} +1 -1
  11. package/dist/{index-403841c7.cjs.js → index-129ef6fb.cjs.js} +3 -3
  12. package/dist/{index-403841c7.cjs.js.map → index-129ef6fb.cjs.js.map} +1 -1
  13. package/dist/{index-1faa963c.esm.js → index-36f2a6b5.esm.js} +1 -1
  14. package/dist/index-4df50481.esm.js +1 -0
  15. package/dist/index-5d34cff1.esm.js +1 -0
  16. package/dist/index-7ae95edd.cjs.js +2 -0
  17. package/dist/{index-3ca67627.cjs.js.map → index-7ae95edd.cjs.js.map} +1 -1
  18. package/dist/index-7ca72b78.esm.js +1 -0
  19. package/dist/index-bbe23ad8.cjs.js +2 -0
  20. package/dist/index-bbe23ad8.cjs.js.map +1 -0
  21. package/dist/index-ccc6a799.cjs.js +5 -0
  22. package/dist/index-ccc6a799.cjs.js.map +1 -0
  23. package/dist/index.cjs.js +1 -1
  24. package/dist/index.esm.js +1 -1
  25. package/dist/index.js +124 -72
  26. package/dist/nile-auto-complete/index.cjs.js +1 -1
  27. package/dist/nile-auto-complete/nile-auto-complete.cjs.js +1 -1
  28. package/dist/nile-auto-complete/portal-manager.cjs.js +1 -1
  29. package/dist/nile-auto-complete/portal-utils.cjs.js +1 -1
  30. package/dist/nile-code-editor/extensionSetup.cjs.js +2 -2
  31. package/dist/nile-code-editor/index.cjs.js +1 -1
  32. package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
  33. package/dist/nile-code-editor/theme.cjs.js +1 -1
  34. package/dist/nile-color-picker/index.cjs.js +1 -1
  35. package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
  36. package/dist/nile-combobox/index.cjs.js +1 -1
  37. package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
  38. package/dist/nile-combobox/portal-manager.cjs.js +1 -1
  39. package/dist/nile-context-menu/index.cjs.js +1 -1
  40. package/dist/nile-context-menu/nile-context-menu.cjs.js +1 -1
  41. package/dist/nile-context-submenu/index.cjs.js +1 -1
  42. package/dist/nile-context-submenu/nile-context-submenu.cjs.js +1 -1
  43. package/dist/nile-detail/index.cjs.js +1 -1
  44. package/dist/nile-detail/nile-detail.cjs.js +1 -1
  45. package/dist/nile-dropdown/index.cjs.js +1 -1
  46. package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
  47. package/dist/nile-dropdown/portal-manager.cjs.js +1 -1
  48. package/dist/nile-file-upload/index.cjs.js +1 -1
  49. package/dist/nile-file-upload/nile-file-upload.cjs.js +1 -1
  50. package/dist/nile-file-upload/nile-file-upload.template.cjs.js +1 -1
  51. package/dist/nile-floating-panel/index.cjs.js +1 -1
  52. package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
  53. package/dist/nile-lite-tooltip/index.cjs.js +1 -1
  54. package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
  55. package/dist/nile-lite-tooltip/nile-lite-tooltip.css.cjs.js +1 -1
  56. package/dist/nile-lite-tooltip/nile-lite-tooltip.css.cjs.js.map +1 -1
  57. package/dist/nile-lite-tooltip/nile-lite-tooltip.css.esm.js +1 -1
  58. package/dist/nile-markdown-editor/index.cjs.js +1 -1
  59. package/dist/nile-markdown-editor/nile-markdown-editor.cjs.js +1 -1
  60. package/dist/nile-popover/index.cjs.js +1 -1
  61. package/dist/nile-popover/nile-popover.cjs.js +1 -1
  62. package/dist/nile-popover/portal-manager.cjs.js +1 -1
  63. package/dist/nile-popup/index.cjs.js +1 -1
  64. package/dist/nile-popup/nile-popup.cjs.js +1 -1
  65. package/dist/nile-segmented-progress-bar/index.cjs.js +1 -1
  66. package/dist/nile-segmented-progress-bar/nile-segmented-progress-bar.cjs.js +1 -1
  67. package/dist/nile-select/index.cjs.js +1 -1
  68. package/dist/nile-select/nile-select.cjs.js +1 -1
  69. package/dist/nile-select/portal-manager.cjs.js +1 -1
  70. package/dist/nile-side-bar-action/index.cjs.js +1 -1
  71. package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js +1 -1
  72. package/dist/nile-side-bar-action/portal-manager.cjs.js +1 -1
  73. package/dist/nile-tooltip/index.cjs.js +1 -1
  74. package/dist/nile-tooltip/nile-tooltip.cjs.js +1 -1
  75. package/dist/nile-tooltip/nile-tooltip.css.cjs.js +1 -1
  76. package/dist/nile-tooltip/nile-tooltip.css.cjs.js.map +1 -1
  77. package/dist/nile-tooltip/nile-tooltip.css.esm.js +1 -1
  78. package/dist/nile-virtual-select/index.cjs.js +1 -1
  79. package/dist/nile-virtual-select/nile-virtual-select.cjs.js +1 -1
  80. package/dist/nile-virtual-select/portal-manager.cjs.js +1 -1
  81. package/dist/nile-virtual-select/renderer.cjs.js +1 -1
  82. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
  83. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
  84. package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
  85. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
  86. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
  87. package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
  88. package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js +2 -0
  89. package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js.map +1 -0
  90. package/dist/nile-wysiwyg-editor/engine/email-css.esm.js +1 -0
  91. package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js +2 -0
  92. package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js.map +1 -0
  93. package/dist/nile-wysiwyg-editor/engine/email-render.esm.js +1 -0
  94. package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js +2 -0
  95. package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js.map +1 -0
  96. package/dist/nile-wysiwyg-editor/engine/html-detect.esm.js +1 -0
  97. package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
  98. package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
  99. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
  100. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js.map +1 -1
  101. package/dist/nile-wysiwyg-editor/engine/inputrules.esm.js +1 -1
  102. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
  103. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js.map +1 -1
  104. package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
  105. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
  106. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
  107. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
  108. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
  109. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
  110. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
  111. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
  112. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
  113. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
  114. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +1 -1
  115. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js.map +1 -1
  116. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -1
  117. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js +1 -1
  118. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js.map +1 -1
  119. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.esm.js +1 -1
  120. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js +1 -1
  121. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js.map +1 -1
  122. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.esm.js +1 -1
  123. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +1 -1
  124. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js.map +1 -1
  125. package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -1
  126. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js +1 -1
  127. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js.map +1 -1
  128. package/dist/nile-wysiwyg-editor/engine/plugins/link.esm.js +1 -1
  129. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js +1 -1
  130. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js.map +1 -1
  131. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.esm.js +1 -1
  132. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js +1 -1
  133. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js.map +1 -1
  134. package/dist/nile-wysiwyg-editor/engine/plugins/paste.esm.js +1 -1
  135. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +1 -1
  136. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js.map +1 -1
  137. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -1
  138. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js +1 -1
  139. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js.map +1 -1
  140. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.esm.js +1 -1
  141. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +1 -1
  142. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js.map +1 -1
  143. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -1
  144. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
  145. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
  146. package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
  147. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +2 -1
  148. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
  149. package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
  150. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
  151. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
  152. package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
  153. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
  154. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
  155. package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
  156. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js +1 -1
  157. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js.map +1 -1
  158. package/dist/nile-wysiwyg-editor/engine/serializer.esm.js +1 -1
  159. package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
  160. package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
  161. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
  162. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
  163. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
  164. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
  165. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +45 -4
  166. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +2 -2
  167. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
  168. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
  169. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js +1 -1
  170. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js.map +1 -1
  171. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.esm.js +2 -2
  172. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
  173. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
  174. package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +5 -5
  175. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
  176. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
  177. package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +1 -1
  178. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js +1 -1
  179. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js.map +1 -1
  180. package/dist/nile-wysiwyg-editor/ui/embed-menu.esm.js +1 -1
  181. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
  182. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
  183. package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
  184. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js +1 -1
  185. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js.map +1 -1
  186. package/dist/nile-wysiwyg-editor/ui/emoji-picker.esm.js +1 -1
  187. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js +1 -1
  188. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js.map +1 -1
  189. package/dist/nile-wysiwyg-editor/ui/find-replace.esm.js +3 -3
  190. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js +1 -1
  191. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js.map +1 -1
  192. package/dist/nile-wysiwyg-editor/ui/image-menu.esm.js +3 -3
  193. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js +1 -1
  194. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js.map +1 -1
  195. package/dist/nile-wysiwyg-editor/ui/image-panel.esm.js +1 -1
  196. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js +1 -1
  197. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js.map +1 -1
  198. package/dist/nile-wysiwyg-editor/ui/insert-menu.esm.js +1 -1
  199. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js +1 -1
  200. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js.map +1 -1
  201. package/dist/nile-wysiwyg-editor/ui/link-editor.esm.js +9 -9
  202. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js +1 -1
  203. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js.map +1 -1
  204. package/dist/nile-wysiwyg-editor/ui/markdown-panel.esm.js +1 -1
  205. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
  206. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
  207. package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +1 -1
  208. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js +1 -1
  209. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js.map +1 -1
  210. package/dist/nile-wysiwyg-editor/ui/shortcut-help.esm.js +1 -1
  211. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
  212. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
  213. package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +4 -4
  214. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js +1 -1
  215. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js.map +1 -1
  216. package/dist/nile-wysiwyg-editor/ui/special-chars.esm.js +1 -1
  217. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
  218. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
  219. package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +23 -12
  220. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
  221. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
  222. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +7 -7
  223. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
  224. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
  225. package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +1 -1
  226. package/dist/src/nile-lite-tooltip/nile-lite-tooltip.css.js +1 -1
  227. package/dist/src/nile-lite-tooltip/nile-lite-tooltip.css.js.map +1 -1
  228. package/dist/src/nile-tooltip/nile-tooltip.css.js +1 -1
  229. package/dist/src/nile-tooltip/nile-tooltip.css.js.map +1 -1
  230. package/dist/src/nile-wysiwyg-editor/engine/create-state.d.ts +5 -0
  231. package/dist/src/nile-wysiwyg-editor/engine/create-state.js +2 -0
  232. package/dist/src/nile-wysiwyg-editor/engine/create-state.js.map +1 -1
  233. package/dist/src/nile-wysiwyg-editor/engine/email-css.d.ts +14 -0
  234. package/dist/src/nile-wysiwyg-editor/engine/email-css.js +140 -0
  235. package/dist/src/nile-wysiwyg-editor/engine/email-css.js.map +1 -0
  236. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.d.ts +1 -0
  237. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js +1045 -0
  238. package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js.map +1 -0
  239. package/dist/src/nile-wysiwyg-editor/engine/email-render.d.ts +11 -0
  240. package/dist/src/nile-wysiwyg-editor/engine/email-render.js +93 -0
  241. package/dist/src/nile-wysiwyg-editor/engine/email-render.js.map +1 -0
  242. package/dist/src/nile-wysiwyg-editor/engine/html-detect.d.ts +2 -0
  243. package/dist/src/nile-wysiwyg-editor/engine/html-detect.js +24 -0
  244. package/dist/src/nile-wysiwyg-editor/engine/html-detect.js.map +1 -0
  245. package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +4 -1
  246. package/dist/src/nile-wysiwyg-editor/engine/index.js +3 -1
  247. package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
  248. package/dist/src/nile-wysiwyg-editor/engine/inputrules.d.ts +0 -5
  249. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js +19 -0
  250. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js.map +1 -1
  251. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js +3 -2
  252. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js.map +1 -1
  253. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.d.ts +4 -0
  254. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js +56 -1
  255. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js.map +1 -1
  256. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.d.ts +5 -0
  257. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js +27 -6
  258. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js.map +1 -1
  259. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +30 -7
  260. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
  261. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +46 -0
  262. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
  263. package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +9 -1
  264. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +730 -7
  265. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
  266. package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +3 -0
  267. package/dist/src/nile-wysiwyg-editor/engine/schema.js +215 -34
  268. package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
  269. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js +3 -1
  270. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js.map +1 -1
  271. package/dist/src/nile-wysiwyg-editor/engine/serializer.d.ts +12 -4
  272. package/dist/src/nile-wysiwyg-editor/engine/serializer.js +18 -8
  273. package/dist/src/nile-wysiwyg-editor/engine/serializer.js.map +1 -1
  274. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.d.ts +1 -0
  275. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js +287 -0
  276. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js.map +1 -0
  277. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +45 -4
  278. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
  279. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +23 -0
  280. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +113 -15
  281. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
  282. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +213 -0
  283. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
  284. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js +2 -2
  285. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js.map +1 -1
  286. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +1 -1
  287. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
  288. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js +1 -1
  289. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js.map +1 -1
  290. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +1 -1
  291. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
  292. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js +1 -1
  293. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js.map +1 -1
  294. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js +1 -1
  295. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js.map +1 -1
  296. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js +1 -1
  297. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js.map +1 -1
  298. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js +1 -1
  299. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js.map +1 -1
  300. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js +1 -1
  301. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js.map +1 -1
  302. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js +2 -2
  303. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js.map +1 -1
  304. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js +1 -1
  305. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js.map +1 -1
  306. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +1 -1
  307. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
  308. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js +1 -1
  309. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js.map +1 -1
  310. package/dist/src/nile-wysiwyg-editor/ui/source-view.js +2 -2
  311. package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
  312. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js +1 -1
  313. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js.map +1 -1
  314. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +14 -3
  315. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
  316. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +2 -2
  317. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
  318. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +1 -1
  319. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
  320. package/dist/src/version.js +1 -1
  321. package/dist/src/version.js.map +1 -1
  322. package/dist/tsconfig.tsbuildinfo +1 -1
  323. package/dist/version.cjs.js +1 -1
  324. package/dist/version.cjs.js.map +1 -1
  325. package/dist/version.esm.js +1 -1
  326. package/package.json +1 -1
  327. package/src/nile-lite-tooltip/nile-lite-tooltip.css.ts +1 -1
  328. package/src/nile-tooltip/nile-tooltip.css.ts +1 -1
  329. package/src/nile-wysiwyg-editor/engine/create-state.ts +7 -0
  330. package/src/nile-wysiwyg-editor/engine/email-css.ts +136 -0
  331. package/src/nile-wysiwyg-editor/engine/email-fidelity.test.ts +1347 -0
  332. package/src/nile-wysiwyg-editor/engine/email-render.ts +99 -0
  333. package/src/nile-wysiwyg-editor/engine/html-detect.ts +26 -0
  334. package/src/nile-wysiwyg-editor/engine/index.ts +10 -1
  335. package/src/nile-wysiwyg-editor/engine/inputrules.ts +18 -1
  336. package/src/nile-wysiwyg-editor/engine/plugins/image-view.ts +3 -2
  337. package/src/nile-wysiwyg-editor/engine/plugins/link.ts +58 -2
  338. package/src/nile-wysiwyg-editor/engine/plugins/paste.ts +31 -8
  339. package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +29 -7
  340. package/src/nile-wysiwyg-editor/engine/pm.test.ts +73 -0
  341. package/src/nile-wysiwyg-editor/engine/sanitize.ts +729 -7
  342. package/src/nile-wysiwyg-editor/engine/schema.ts +206 -33
  343. package/src/nile-wysiwyg-editor/engine/selection-state.ts +3 -1
  344. package/src/nile-wysiwyg-editor/engine/serializer.ts +30 -8
  345. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.ts +357 -0
  346. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +45 -4
  347. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +258 -0
  348. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +119 -12
  349. package/src/nile-wysiwyg-editor/ui/bookmark-panel.ts +2 -2
  350. package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +1 -1
  351. package/src/nile-wysiwyg-editor/ui/embed-menu.ts +1 -1
  352. package/src/nile-wysiwyg-editor/ui/embed-panel.ts +1 -1
  353. package/src/nile-wysiwyg-editor/ui/emoji-picker.ts +1 -1
  354. package/src/nile-wysiwyg-editor/ui/find-replace.ts +1 -1
  355. package/src/nile-wysiwyg-editor/ui/image-menu.ts +1 -1
  356. package/src/nile-wysiwyg-editor/ui/image-panel.ts +1 -1
  357. package/src/nile-wysiwyg-editor/ui/insert-menu.ts +1 -1
  358. package/src/nile-wysiwyg-editor/ui/link-editor.ts +2 -2
  359. package/src/nile-wysiwyg-editor/ui/markdown-panel.ts +1 -1
  360. package/src/nile-wysiwyg-editor/ui/math-panel.ts +1 -1
  361. package/src/nile-wysiwyg-editor/ui/shortcut-help.ts +1 -1
  362. package/src/nile-wysiwyg-editor/ui/source-view.ts +2 -2
  363. package/src/nile-wysiwyg-editor/ui/special-chars.ts +1 -1
  364. package/src/nile-wysiwyg-editor/ui/suggest-list.ts +14 -3
  365. package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +2 -2
  366. package/src/nile-wysiwyg-editor/ui/table-menu.ts +1 -1
  367. package/vscode-html-custom-data.json +15 -1
  368. package/web-test-runner.config.mjs +4 -0
  369. package/dist/index-3ca67627.cjs.js +0 -2
  370. package/dist/index-6f3fa4ab.esm.js +0 -1
  371. package/dist/index-7b1c9857.esm.js +0 -1
  372. package/dist/index-89c520e9.cjs.js +0 -5
  373. package/dist/index-89c520e9.cjs.js.map +0 -1
  374. package/dist/index-ba47645d.esm.js +0 -1
  375. package/dist/index-c3210a9c.cjs.js +0 -2
  376. package/dist/index-c3210a9c.cjs.js.map +0 -1
@@ -361,6 +361,111 @@ describe('nile-wysiwyg-editor', () => {
361
361
  expect(el.getHTML()).to.equal('<p><strong>bold</strong></p>');
362
362
  });
363
363
 
364
+ /** Types `text` one character at a time through handleTextInput, like a user. */
365
+ function typeChars(el: NileWysiwygEditor, text: string) {
366
+ const view = el.pmView!;
367
+ for (const ch of text) {
368
+ const { from, to } = view.state.selection;
369
+ const handled = view.someProp('handleTextInput', f =>
370
+ f(view, from, to, ch, () => view.state.tr.insertText(ch, from, to))
371
+ );
372
+ if (!handled) view.dispatch(view.state.tr.insertText(ch, from, to));
373
+ }
374
+ }
375
+
376
+ function selectText(el: NileWysiwygEditor, text: string) {
377
+ const view = el.pmView!;
378
+ let from = -1;
379
+ view.state.doc.descendants((node, pos) => {
380
+ if (from === -1 && node.isText && node.text!.includes(text)) {
381
+ from = pos + node.text!.indexOf(text);
382
+ }
383
+ });
384
+ view.dispatch(
385
+ view.state.tr.setSelection(TextSelection.create(view.state.doc, from, from + text.length))
386
+ );
387
+ }
388
+
389
+ it('typing over a fully linked selection keeps the link', async () => {
390
+ const el = await editorFixture();
391
+ el.value = '<p>Before <a href="https://x.test">Get discount</a> after</p>';
392
+ await el.updateComplete;
393
+ selectText(el, 'discount');
394
+ typeChars(el, 'deal');
395
+ expect(el.getHTML()).to.equal('<p>Before <a href="https://x.test">Get deal</a> after</p>');
396
+ });
397
+
398
+ it('keeps the link and inner marks when retyping a button label', async () => {
399
+ const el = await editorFixture();
400
+ el.value =
401
+ '<p><a href="#"><strong><span style="color: rgb(255, 255, 255);">Get discount</span></strong></a></p>';
402
+ await el.updateComplete;
403
+ selectText(el, 'Get discount');
404
+ typeChars(el, 'Shop');
405
+ expect(el.getHTML()).to.equal(
406
+ '<p><a href="#"><strong><span style="color: rgb(255, 255, 255);">Shop</span></strong></a></p>'
407
+ );
408
+ });
409
+
410
+ it('does not extend a link when typing after it without retyping', async () => {
411
+ const el = await editorFixture();
412
+ el.value = '<p><a href="https://x.test">link</a></p>';
413
+ await el.updateComplete;
414
+ const view = el.pmView!;
415
+ const end = view.state.doc.content.size - 1;
416
+ view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, end)));
417
+ typeChars(el, 'X');
418
+ expect(el.getHTML()).to.equal('<p><a href="https://x.test">link</a>X</p>');
419
+ });
420
+
421
+ it('ends the retype run when the caret moves', async () => {
422
+ const el = await editorFixture();
423
+ el.value = '<p><a href="https://x.test">old</a> tail</p>';
424
+ await el.updateComplete;
425
+ selectText(el, 'old');
426
+ typeChars(el, 'new');
427
+ selectText(el, 'tail');
428
+ typeChars(el, 'end');
429
+ expect(el.getHTML()).to.equal('<p><a href="https://x.test">new</a> end</p>');
430
+ });
431
+
432
+ it('does not keep a link when the selection is only partly linked', async () => {
433
+ const el = await editorFixture();
434
+ el.value = '<p>pre <a href="https://x.test">link</a></p>';
435
+ await el.updateComplete;
436
+ selectText(el, 'pre ');
437
+ // Extend the selection over the link too: start is outside the link.
438
+ const view = el.pmView!;
439
+ const { from } = view.state.selection;
440
+ view.dispatch(
441
+ view.state.tr.setSelection(TextSelection.create(view.state.doc, from, view.state.doc.content.size - 1))
442
+ );
443
+ typeChars(el, 'Z');
444
+ expect(el.getHTML()).to.equal('<p>Z</p>');
445
+ });
446
+
447
+ for (const seq of ['---', '***', '___']) {
448
+ it(`turns "${seq}" in an empty paragraph into a horizontal rule`, async () => {
449
+ const el = await editorFixture();
450
+ el.value = '<p></p>';
451
+ await el.updateComplete;
452
+ const view = el.pmView!;
453
+ view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, 1)));
454
+ typeChars(el, `${seq}next`);
455
+ expect(el.getHTML()).to.equal('<hr><p>next</p>');
456
+ });
457
+ }
458
+
459
+ it('leaves "---" alone when the paragraph has other text', async () => {
460
+ const el = await editorFixture();
461
+ el.value = '<p>a</p>';
462
+ await el.updateComplete;
463
+ const view = el.pmView!;
464
+ view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, 2)));
465
+ typeChars(el, '---');
466
+ expect(el.getHTML()).to.equal('<p>a---</p>');
467
+ });
468
+
364
469
  it('finds and replaces through the public API', async () => {
365
470
  const el = await editorFixture();
366
471
  el.value = '<p>red fox, red barn</p>';
@@ -750,4 +855,157 @@ describe('nile-wysiwyg-editor', () => {
750
855
  el.autosaveInterval = 0;
751
856
  });
752
857
  });
858
+
859
+ describe('allowedStyles (section F)', () => {
860
+ it('F1. parses the allowed-styles attribute into a Record', async () => {
861
+ const el = await editorFixture(
862
+ html`<nile-wysiwyg-editor
863
+ allowed-styles="td: letter-spacing; p: letter-spacing"
864
+ ></nile-wysiwyg-editor>`
865
+ );
866
+ expect(el.allowedStyles).to.deep.equal({
867
+ td: ['letter-spacing'],
868
+ p: ['letter-spacing'],
869
+ });
870
+ });
871
+
872
+ it('F2. re-parses the current value when allowedStyles is set', async () => {
873
+ const el = await editorFixture(
874
+ html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`
875
+ );
876
+ el.value = '<p style="letter-spacing:0.5px">hi</p>';
877
+ await el.updateComplete;
878
+ // Not allowed yet → extension prop not kept.
879
+ expect(el.getHTML()).to.not.include('letter-spacing');
880
+ el.allowedStyles = { p: ['letter-spacing'] };
881
+ await el.updateComplete;
882
+ expect(el.getHTML()).to.include('letter-spacing');
883
+ });
884
+
885
+ it('F3. warns once per bad entry and never throws', async () => {
886
+ const el = await editorFixture(
887
+ html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`
888
+ );
889
+ const warn = console.warn;
890
+ let count = 0;
891
+ console.warn = () => {
892
+ count += 1;
893
+ };
894
+ try {
895
+ el.allowedStyles = { td: ['position'], foo: ['x'] };
896
+ await el.updateComplete;
897
+ } finally {
898
+ console.warn = warn;
899
+ }
900
+ expect(count).to.be.greaterThan(0);
901
+ });
902
+
903
+ it('F4. the allowed-styles attribute is not reflected', async () => {
904
+ const el = await editorFixture(
905
+ html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`
906
+ );
907
+ el.allowedStyles = { td: ['letter-spacing'] };
908
+ await el.updateComplete;
909
+ expect(el.getAttribute('allowed-styles')).to.equal(null);
910
+ });
911
+ });
912
+
913
+ describe('email-fidelity live table view', () => {
914
+ it('applies every declaration of the table text style to the live table', async () => {
915
+ const el = await editorFixture(
916
+ html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`
917
+ );
918
+ el.value =
919
+ '<table role="presentation" style="font-family: Verdana, sans-serif; color: #112233; font-size: 18px"><tr><td><p>x</p></td></tr></table>';
920
+ await el.updateComplete;
921
+ const table = el.shadowRoot!.querySelector('.ProseMirror table') as HTMLTableElement;
922
+ expect(table.style.fontFamily).to.include('Verdana');
923
+ expect(table.style.color).to.equal('rgb(17, 34, 51)');
924
+ expect(table.style.fontSize).to.equal('18px');
925
+ });
926
+
927
+ it('lifts the column-resize min-width off nested layout tables', async () => {
928
+ const el = await editorFixture(
929
+ html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`
930
+ );
931
+ el.value =
932
+ '<table role="presentation" style="width: 100%"><tr><td><table role="presentation" style="width: 100%"><tr><td><p>a</p></td><td><p>b</p></td><td><p>c</p></td></tr></table></td></tr></table>';
933
+ await el.updateComplete;
934
+ const tables = el.shadowRoot!.querySelectorAll('.ProseMirror table');
935
+ const inner = tables[1] as HTMLTableElement;
936
+ expect(inner.hasAttribute('data-layout-table')).to.equal(true);
937
+ expect(getComputedStyle(inner).minWidth).to.equal('0px');
938
+ });
939
+ });
940
+
941
+ describe('long mention names', () => {
942
+ const LONG = [
943
+ 'source.JobAssignmentAdditionalJobDetailsJobFunction',
944
+ 'source.JobAssignmentAdditionalJobDetailsJobFunctionCode',
945
+ 'source.JobAssignmentAdditionalJobDetailsJobFunctionEffectiveStartDate',
946
+ ];
947
+
948
+ it('shows long suggestions in full: wrapped, no sideways scroll, full name as a tooltip', async () => {
949
+ const el = await editorFixture();
950
+ el.mentions = { $: LONG.map(key => ({ key, label: key })) };
951
+ const view = el.pmView!;
952
+ view.dispatch(view.state.tr.insertText('$source.Job', 1));
953
+ await el.updateComplete;
954
+ const list = el.shadowRoot!.querySelector('nile-wysiwyg-suggest-list')!;
955
+ await list.updateComplete;
956
+ expect(list.open).to.equal(true);
957
+ const box = list.shadowRoot!.querySelector('.list') as HTMLElement;
958
+ expect(box.scrollWidth).to.be.at.most(box.clientWidth);
959
+ const labels = [...list.shadowRoot!.querySelectorAll('.item .label')] as HTMLElement[];
960
+ expect(labels.map(l => l.textContent)).to.deep.equal(LONG);
961
+ expect(labels.map(l => l.title)).to.deep.equal(LONG);
962
+ for (const label of labels) expect(label.scrollWidth).to.be.at.most(label.clientWidth + 1);
963
+ });
964
+
965
+ it('wraps a long mention chip inside a narrow table cell', async () => {
966
+ const el = await editorFixture(
967
+ html`<nile-wysiwyg-editor email-fidelity style="width: 600px"></nile-wysiwyg-editor>`
968
+ );
969
+ const key =
970
+ 'source.ManagerOfManagerSecondLevelSupervisorOrganizationalAssignmentBusinessEmailElectronicAddressPrimary';
971
+ el.value = `<table role="presentation" style="width: 100%"><tr><td style="width: 50%"><p>Label</p></td><td style="width: 50%"><p><span class="nile-wysiwyg__mention" data-mention-trigger="$" data-mention-key="${key}" data-mention-label="${key}">$${key}</span></p></td></tr></table>`;
972
+ await el.updateComplete;
973
+ const chip = el.shadowRoot!.querySelector('.ProseMirror .nile-wysiwyg__mention') as HTMLElement;
974
+ const cell = chip.closest('td')!;
975
+ expect(chip.getBoundingClientRect().right).to.be.at.most(cell.getBoundingClientRect().right + 1);
976
+ expect(chip.getBoundingClientRect().height).to.be.greaterThan(30);
977
+ });
978
+
979
+ it('keeps a short mention chip on one line', async () => {
980
+ const el = await editorFixture();
981
+ el.value =
982
+ '<p><span class="nile-wysiwyg__mention" data-mention-trigger="$" data-mention-key="source.FirstName" data-mention-label="source.FirstName">$source.FirstName</span></p>';
983
+ await el.updateComplete;
984
+ const chip = el.shadowRoot!.querySelector('.ProseMirror .nile-wysiwyg__mention') as HTMLElement;
985
+ const lineHeight = parseFloat(getComputedStyle(chip).lineHeight) || 24;
986
+ expect(chip.getBoundingClientRect().height).to.be.below(lineHeight * 1.6);
987
+ });
988
+ });
989
+
990
+ describe('mention chip weight', () => {
991
+ const chip = (key: string) =>
992
+ `<span class="nile-wysiwyg__mention" data-mention-trigger="$" data-mention-key="${key}" data-mention-label="${key}">$${key}</span>`;
993
+ const weightOf = (el: NileWysiwygEditor, key: string) =>
994
+ getComputedStyle(
995
+ el.shadowRoot!.querySelector(`.ProseMirror [data-mention-key="${key}"]`)!
996
+ ).fontWeight;
997
+
998
+ it('keeps its default weight in plain text but follows bold, headings and explicit weights', async () => {
999
+ const el = await editorFixture(html`<nile-wysiwyg-editor email-fidelity></nile-wysiwyg-editor>`);
1000
+ el.value =
1001
+ `<p>Dear <strong>${chip('first')}</strong>, ${chip('plain')}</p>` +
1002
+ `<h2>${chip('heading')}</h2>` +
1003
+ `<table><tr><td style="font-weight: normal">${chip('normalCell')}</td></tr></table>`;
1004
+ await el.updateComplete;
1005
+ expect(weightOf(el, 'plain')).to.equal('500');
1006
+ expect(weightOf(el, 'first')).to.equal('700');
1007
+ expect(Number(weightOf(el, 'heading'))).to.be.at.least(600);
1008
+ expect(weightOf(el, 'normalCell')).to.equal('400');
1009
+ });
1010
+ });
753
1011
  });
@@ -20,6 +20,7 @@ import {
20
20
  jsonToDoc,
21
21
  isDocEmpty,
22
22
  sanitizeHtml,
23
+ relocateEmailContainers,
23
24
  getCommand,
24
25
  getMarkRange,
25
26
  flattenToSingleParagraph,
@@ -47,7 +48,11 @@ import {
47
48
  clearFindTr,
48
49
  replaceActiveTr,
49
50
  replaceAllTr,
51
+ normalizeEmailAllowedStyles,
52
+ parseAllowedStylesAttr,
50
53
  } from './engine';
54
+ import type { NormalizedAllowedStyles } from './engine';
55
+ import type { EmailRenderDefaults } from './engine/email-render';
51
56
  import type { ToolbarState } from './engine/selection-state';
52
57
  import type { CommandAttrs, CommandName } from './engine/commands';
53
58
  import { selectedImageInfo } from './engine/commands';
@@ -274,6 +279,9 @@ export class NileWysiwygEditor extends NileElement {
274
279
  /** Disables the ":" emoji picker. */
275
280
  @property({ type: Boolean, attribute: 'no-emoji' }) noEmoji = false;
276
281
 
282
+ /** Hides the floating table toolbar; with `email-fidelity` it also hides in layout tables. */
283
+ @property({ type: Boolean, attribute: 'no-table-menu' }) noTableMenu = false;
284
+
277
285
  /** Maximum character count; further input is rejected. 0 = unlimited. */
278
286
  @property({ type: Number, attribute: 'maxlength' }) maxLength = 0;
279
287
 
@@ -365,6 +373,20 @@ export class NileWysiwygEditor extends NileElement {
365
373
  @property({ type: Boolean, attribute: 'no-markdown-paste' })
366
374
  noMarkdownPaste = false;
367
375
 
376
+ /** Opt-in mode for table-based email HTML; off by default (output unchanged). */
377
+ @property({ type: Boolean, attribute: 'email-fidelity', reflect: true })
378
+ emailFidelity = false;
379
+
380
+ /** Extra inline CSS to keep per tag under `email-fidelity`: `{ td: ['letter-spacing'] }` or `"td: letter-spacing"`. */
381
+ @property({
382
+ attribute: 'allowed-styles',
383
+ converter: {
384
+ fromAttribute: (value: string | null): Record<string, string[]> =>
385
+ parseAllowedStylesAttr(value ?? ''),
386
+ },
387
+ })
388
+ allowedStyles: Record<string, string[]> = {};
389
+
368
390
  /** Disables typography autocorrect (smart quotes, dashes, (c) → ©). */
369
391
  @property({ type: Boolean, attribute: 'no-text-transform' })
370
392
  noTextTransform = false;
@@ -431,6 +453,10 @@ export class NileWysiwygEditor extends NileElement {
431
453
 
432
454
  private view?: EditorView;
433
455
 
456
+ /** Validated `allowedStyles`, recomputed once per config change (so the
457
+ * drop warnings fire once, not on every paste). */
458
+ private normalizedAllowedStyles: NormalizedAllowedStyles = new Map();
459
+
434
460
  private mentionMatch: SuggestionMatch | null = null;
435
461
 
436
462
  private slashMatch: SuggestionMatch | null = null;
@@ -453,6 +479,11 @@ export class NileWysiwygEditor extends NileElement {
453
479
 
454
480
  private syncingValue = false;
455
481
 
482
+ /** Last raw HTML given to setContent; a re-parse reads it while the document is unedited. */
483
+ private lastSourceHtml = '';
484
+
485
+ private lastSerializedValue = '';
486
+
456
487
  private changeTimer?: ReturnType<typeof setTimeout>;
457
488
 
458
489
  private pendingChange = false;
@@ -590,10 +621,16 @@ export class NileWysiwygEditor extends NileElement {
590
621
  }
591
622
 
592
623
  protected updated(changed: PropertyValues): void {
593
- if (changed.has('value') && this.view && !this.syncingValue) {
594
- const current = docToHtml(this.view.state.doc);
595
- if (current !== this.value) this.setContent(this.value);
624
+ // Normalize the allowlist first; a value change is external only if it differs from the serialized doc.
625
+ if (changed.has('allowedStyles')) {
626
+ this.normalizedAllowedStyles = normalizeEmailAllowedStyles(this.allowedStyles);
596
627
  }
628
+ const externalValueChange =
629
+ changed.has('value') &&
630
+ !!this.view &&
631
+ !this.syncingValue &&
632
+ this.serializeDoc(this.view.state.doc) !== this.value;
633
+ if (externalValueChange) this.setContent(this.value);
597
634
  this.syncingValue = false;
598
635
 
599
636
  if ((changed.has('disabled') || changed.has('readonly')) && this.view) {
@@ -618,6 +655,19 @@ export class NileWysiwygEditor extends NileElement {
618
655
  if (changed.has('noTextTransform') && this.view && !changed.has('value')) {
619
656
  this.view.updateState(this.buildState(this.view.state.doc));
620
657
  }
658
+ // Toggling fidelity re-parses the current value: the flag changes how the
659
+ // same HTML maps to the document model (container styles, cell layout…).
660
+ if (changed.has('emailFidelity') && this.view && !externalValueChange) {
661
+ this.setContent(this.reparseSource());
662
+ }
663
+ // A new allowlist re-parses so the newly-allowed properties take effect on
664
+ // already-loaded content (recovering them from the raw source when unedited).
665
+ else if (changed.has('allowedStyles') && this.view && !externalValueChange) {
666
+ this.setContent(this.reparseSource());
667
+ }
668
+ if (changed.has('noTableMenu') || changed.has('emailFidelity')) {
669
+ this.updateOverlays();
670
+ }
621
671
  // Lock page scrolling while fullscreen covers the viewport.
622
672
  if (changed.has('fullscreen')) {
623
673
  if (this.fullscreen) {
@@ -684,6 +734,8 @@ export class NileWysiwygEditor extends NileElement {
684
734
  getMaxLength: () => this.maxLength,
685
735
  getForcePlainText: () => this.pasteAsPlainText,
686
736
  getMarkdownPaste: () => !this.noMarkdownPaste,
737
+ getEmailFidelity: () => this.emailFidelity,
738
+ getAllowedStyles: () => this.normalizedAllowedStyles,
687
739
  getSanitize: () => this.sanitize,
688
740
  textTransform: !this.noTextTransform,
689
741
  });
@@ -703,10 +755,54 @@ export class NileWysiwygEditor extends NileElement {
703
755
  };
704
756
  }
705
757
 
758
+ /** HTML to re-parse when fidelity / the allowlist changes: the raw source when
759
+ * the document is still unedited (so a newly-allowed property can be recovered),
760
+ * otherwise the current serialized value (so user edits are kept). */
761
+ private reparseSource(): string {
762
+ return this.value === this.lastSerializedValue ? this.lastSourceHtml : this.value;
763
+ }
764
+
765
+ /** The saved HTML; with email-fidelity it carries the editor's table rendering. */
766
+ private serializeDoc(doc: PMNode): string {
767
+ return docToHtml(doc, this.emailFidelity ? this.emailRenderDefaults() : undefined);
768
+ }
769
+
770
+ private renderProbe: HTMLElement | null = null;
771
+
772
+ /** Theme-resolved table border and header colours; null where the theme draws none. */
773
+ private emailRenderDefaults(): EmailRenderDefaults {
774
+ const root = this.shadowRoot;
775
+ if (!root || !this.isConnected) return { borderColor: null, headerBackground: null };
776
+ if (!this.renderProbe || !this.renderProbe.isConnected) {
777
+ const probe = document.createElement('span');
778
+ probe.setAttribute('aria-hidden', 'true');
779
+ probe.style.cssText =
780
+ 'position:absolute;width:0;height:0;overflow:hidden;visibility:hidden;' +
781
+ 'border-top:1px solid var(--nile-wysiwyg-table-border, var(--nile-colors-neutral-300, var(--ng-colors-border-primary)));' +
782
+ 'background-color:var(--nile-wysiwyg-table-header-bg, var(--nile-colors-neutral-50, var(--ng-colors-bg-secondary)));';
783
+ root.appendChild(probe);
784
+ this.renderProbe = probe;
785
+ }
786
+ const computed = getComputedStyle(this.renderProbe);
787
+ const background = computed.backgroundColor;
788
+ return {
789
+ borderColor: computed.borderTopStyle === 'solid' ? computed.borderTopColor : null,
790
+ headerBackground:
791
+ background && background !== 'transparent' && background !== 'rgba(0, 0, 0, 0)'
792
+ ? background
793
+ : null,
794
+ };
795
+ }
796
+
797
+ /** Email-fidelity options threaded to every sanitize/parse call. */
798
+ private emailOptions(): { emailFidelity: boolean; allowedStyles: NormalizedAllowedStyles } {
799
+ return { emailFidelity: this.emailFidelity, allowedStyles: this.normalizedAllowedStyles };
800
+ }
801
+
706
802
  private mountView(): void {
707
803
  if (this.view || !this.surfaceEl) return;
708
804
  let doc = this.value
709
- ? htmlToDoc(defaultSchema, this.value, this.sanitize)
805
+ ? htmlToDoc(defaultSchema, this.value, this.sanitize, this.emailOptions())
710
806
  : undefined;
711
807
  if (doc && this.singleLine) doc = flattenToSingleParagraph(doc);
712
808
 
@@ -718,7 +814,11 @@ export class NileWysiwygEditor extends NileElement {
718
814
  // Word/Google Docs clipboard HTML gets normalized (real lists, no
719
815
  // mso noise) before the sanitizer clamps it to the schema.
720
816
  const cleaned = isOfficeHtml(pasted) ? cleanOfficeHtml(pasted) : pasted;
721
- return this.sanitize ? sanitizeHtml(cleaned) : cleaned;
817
+ let out = this.sanitize ? sanitizeHtml(cleaned, this.emailOptions()) : cleaned;
818
+ // Lift container background/font off the markup so pasted email HTML
819
+ // doesn't bloat into per-run marks (fidelity only).
820
+ if (this.emailFidelity) out = relocateEmailContainers(out);
821
+ return out;
722
822
  },
723
823
  handleDOMEvents: {
724
824
  mousedown: () => {
@@ -849,7 +949,7 @@ export class NileWysiwygEditor extends NileElement {
849
949
  }
850
950
 
851
951
  private buildChangeDetail(doc: PMNode): WysiwygChangeDetail {
852
- const value = isDocEmpty(doc) ? '' : docToHtml(doc);
952
+ const value = isDocEmpty(doc) ? '' : this.serializeDoc(doc);
853
953
  return { value, json: docToJson(doc), content: value };
854
954
  }
855
955
 
@@ -870,7 +970,7 @@ export class NileWysiwygEditor extends NileElement {
870
970
 
871
971
  private syncFormValue(doc: PMNode): void {
872
972
  if (!this.internals) return;
873
- const value = isDocEmpty(doc) ? '' : docToHtml(doc);
973
+ const value = isDocEmpty(doc) ? '' : this.serializeDoc(doc);
874
974
  this.internals.setFormValue(value === '' ? null : value);
875
975
  this.updateValidity();
876
976
  }
@@ -979,7 +1079,12 @@ export class NileWysiwygEditor extends NileElement {
979
1079
 
980
1080
  if (this.tableMenuEl) {
981
1081
  const showTable =
982
- this.hasFocus && !!this.toolbarState && this.toolbarState.inTable && !panelOpen;
1082
+ !this.noTableMenu &&
1083
+ this.hasFocus &&
1084
+ !!this.toolbarState &&
1085
+ this.toolbarState.inTable &&
1086
+ !(this.emailFidelity && this.toolbarState.tableLayout) &&
1087
+ !panelOpen;
983
1088
  this.tableMenuEl.open = showTable;
984
1089
  if (showTable) {
985
1090
  this.tableMenuEl.updateComplete.then(() =>
@@ -1790,7 +1895,7 @@ export class NileWysiwygEditor extends NileElement {
1790
1895
 
1791
1896
  public getHTML(): string {
1792
1897
  return this.view && !isDocEmpty(this.view.state.doc)
1793
- ? docToHtml(this.view.state.doc)
1898
+ ? this.serializeDoc(this.view.state.doc)
1794
1899
  : '';
1795
1900
  }
1796
1901
 
@@ -1833,15 +1938,17 @@ export class NileWysiwygEditor extends NileElement {
1833
1938
  if (typeof content === 'string') this.value = content;
1834
1939
  return;
1835
1940
  }
1941
+ if (typeof content === 'string') this.lastSourceHtml = content;
1836
1942
  let doc =
1837
1943
  typeof content === 'string'
1838
- ? htmlToDoc(defaultSchema, content, this.sanitize)
1944
+ ? htmlToDoc(defaultSchema, content, this.sanitize, this.emailOptions())
1839
1945
  : jsonToDoc(defaultSchema, content);
1840
1946
  if (this.singleLine) doc = flattenToSingleParagraph(doc);
1841
1947
  this.view.updateState(this.buildState(doc));
1842
1948
  this.toolbarState = this.snapshotToolbar(this.view.state);
1843
1949
  this.syncingValue = true;
1844
- this.value = isDocEmpty(doc) ? '' : docToHtml(doc);
1950
+ this.value = isDocEmpty(doc) ? '' : this.serializeDoc(doc);
1951
+ this.lastSerializedValue = this.value;
1845
1952
  this.syncFormValue(doc);
1846
1953
  if (emitChange) {
1847
1954
  this.emit('nile-input', this.buildChangeDetail(doc));
@@ -1856,7 +1963,7 @@ export class NileWysiwygEditor extends NileElement {
1856
1963
  /** Parses and inserts HTML at the current selection. */
1857
1964
  public insertHTML(htmlContent: string): void {
1858
1965
  if (!this.view) return;
1859
- const slice = htmlToSlice(defaultSchema, htmlContent, this.sanitize);
1966
+ const slice = htmlToSlice(defaultSchema, htmlContent, this.sanitize, this.emailOptions());
1860
1967
  this.view.dispatch(this.view.state.tr.replaceSelection(slice).scrollIntoView());
1861
1968
  this.view.focus();
1862
1969
  }
@@ -37,7 +37,7 @@ export class NileWysiwygBookmarkPanel extends NileElement {
37
37
  gap: 8px;
38
38
  min-width: 280px;
39
39
  padding: 12px;
40
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
40
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
41
41
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
42
42
  border-radius: var(--nile-wysiwyg-radius, 8px);
43
43
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -91,7 +91,7 @@ export class NileWysiwygBookmarkPanel extends NileElement {
91
91
 
92
92
  button.primary {
93
93
  background: var(--nile-colors-primary-600, var(--ng-componentcolors-utility-blue-600));
94
- color: var(--ng-colors-text-white);
94
+ color: var(--nile-colors-white-base, var(--ng-colors-text-white));
95
95
  }
96
96
  `,
97
97
  ];
@@ -134,7 +134,7 @@ export class NileWysiwygBubbleMenu extends NileElement {
134
134
  display: flex;
135
135
  gap: 2px;
136
136
  padding: 4px;
137
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
137
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
138
138
  border: 1px solid
139
139
  var(--nile-wysiwyg-border-color, var(--nile-colors-neutral-400, var(--ng-colors-border-primary)));
140
140
  border-radius: var(--nile-wysiwyg-radius, 8px);
@@ -70,7 +70,7 @@ export class NileWysiwygEmbedMenu extends NileElement {
70
70
  display: flex;
71
71
  gap: 2px;
72
72
  padding: 4px;
73
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
73
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
74
74
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
75
75
  border-radius: 8px;
76
76
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -42,7 +42,7 @@ export class NileWysiwygEmbedPanel extends NileElement {
42
42
  min-width: 340px;
43
43
  max-width: 420px;
44
44
  padding: 12px;
45
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
45
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
46
46
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
47
47
  border-radius: 8px;
48
48
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
@@ -36,7 +36,7 @@ export class NileWysiwygEmojiPicker extends NileElement {
36
36
  .panel {
37
37
  width: 316px;
38
38
  padding: 8px;
39
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
39
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
40
40
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
41
41
  border-radius: 8px;
42
42
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -47,7 +47,7 @@ export class NileWysiwygFindBar extends NileElement {
47
47
  align-items: center;
48
48
  gap: 6px;
49
49
  padding: 8px;
50
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
50
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
51
51
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
52
52
  border-radius: 8px;
53
53
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -77,7 +77,7 @@ export class NileWysiwygImageMenu extends NileElement {
77
77
  display: flex;
78
78
  gap: 2px;
79
79
  padding: 4px;
80
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
80
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
81
81
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
82
82
  border-radius: 8px;
83
83
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -64,7 +64,7 @@ export class NileWysiwygImagePanel extends NileElement {
64
64
  min-width: 340px;
65
65
  max-width: 420px;
66
66
  padding: 12px;
67
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
67
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
68
68
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
69
69
  border-radius: 8px;
70
70
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
@@ -45,7 +45,7 @@ export class NileWysiwygInsertMenu extends NileElement {
45
45
  overflow-y: auto;
46
46
  overscroll-behavior: contain;
47
47
  padding: 6px;
48
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
48
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
49
49
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
50
50
  border-radius: 8px;
51
51
  box-shadow: var(--nile-wysiwyg-popover-shadow, 0 4px 12px rgba(16, 24, 40, 0.12));
@@ -48,7 +48,7 @@ export class NileWysiwygLinkEditor extends NileElement {
48
48
  gap: 8px;
49
49
  min-width: 280px;
50
50
  padding: 12px;
51
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
51
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
52
52
  border: 1px solid
53
53
  var(--nile-wysiwyg-border-color, var(--nile-colors-neutral-400, var(--ng-colors-border-primary)));
54
54
  border-radius: var(--nile-wysiwyg-radius, 8px);
@@ -96,7 +96,7 @@ export class NileWysiwygLinkEditor extends NileElement {
96
96
 
97
97
  button.primary {
98
98
  background: var(--nile-colors-primary-600, var(--ng-componentcolors-utility-blue-600));
99
- color: var(--ng-colors-text-white);
99
+ color: var(--nile-colors-white-base, var(--ng-colors-text-white));
100
100
  }
101
101
 
102
102
  button.danger {
@@ -39,7 +39,7 @@ export class NileWysiwygMarkdownPanel extends NileElement {
39
39
  width: 460px;
40
40
  max-width: 90vw;
41
41
  padding: 14px;
42
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
42
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
43
43
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
44
44
  border-radius: 8px;
45
45
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
@@ -43,7 +43,7 @@ export class NileWysiwygMathPanel extends NileElement {
43
43
  min-width: 340px;
44
44
  max-width: 420px;
45
45
  padding: 12px;
46
- background: var(--nile-wysiwyg-popover-bg, var(--ng-colors-bg-primary));
46
+ background: var(--nile-wysiwyg-popover-bg, var(--nile-colors-white-base, var(--ng-colors-bg-primary)));
47
47
  border: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-primary));
48
48
  border-radius: 8px;
49
49
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);