@aquera/nile-elements 1.9.6 → 1.9.7

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 (510) hide show
  1. package/README.md +3 -0
  2. package/dist/index-178e1e57.cjs.js +2 -0
  3. package/dist/index-178e1e57.cjs.js.map +1 -0
  4. package/dist/index-1faa963c.esm.js +1 -0
  5. package/dist/index-249de90a.esm.js +1 -0
  6. package/dist/index-353172ed.esm.js +1 -0
  7. package/dist/index-6f3fa4ab.esm.js +1 -0
  8. package/dist/index-89c520e9.cjs.js +5 -0
  9. package/dist/index-89c520e9.cjs.js.map +1 -0
  10. package/dist/index-920675c4.esm.js +1 -0
  11. package/dist/index-b387a9f4.cjs.js +2 -0
  12. package/dist/index-b387a9f4.cjs.js.map +1 -0
  13. package/dist/index-cc1385e0.cjs.js +2 -0
  14. package/dist/index-cc1385e0.cjs.js.map +1 -0
  15. package/dist/index-dedc38a6.esm.js +1 -0
  16. package/dist/index-df196058.cjs.js +2 -0
  17. package/dist/index-df196058.cjs.js.map +1 -0
  18. package/dist/index-f956f56d.cjs.js +7 -0
  19. package/dist/index-f956f56d.cjs.js.map +1 -0
  20. package/dist/index.cjs.js +1 -1
  21. package/dist/index.esm.js +1 -1
  22. package/dist/index.js +3595 -629
  23. package/dist/internal/glyph.cjs.js +2 -0
  24. package/dist/internal/glyph.cjs.js.map +1 -0
  25. package/dist/internal/glyph.esm.js +5 -0
  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 +7 -7
  31. package/dist/nile-code-editor/extensionSetup.cjs.js.map +1 -1
  32. package/dist/nile-code-editor/extensionSetup.esm.js +1 -1
  33. package/dist/nile-code-editor/index.cjs.js +1 -1
  34. package/dist/nile-code-editor/index.esm.js +1 -1
  35. package/dist/nile-code-editor/nile-code-editor.cjs.js +2 -2
  36. package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
  37. package/dist/nile-code-editor/nile-code-editor.esm.js +1 -1
  38. package/dist/nile-color-picker/index.cjs.js +1 -1
  39. package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
  40. package/dist/nile-combobox/index.cjs.js +1 -1
  41. package/dist/nile-combobox/index.esm.js +1 -1
  42. package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
  43. package/dist/nile-combobox/nile-combobox.esm.js +1 -1
  44. package/dist/nile-combobox/portal-manager.cjs.js +1 -1
  45. package/dist/nile-context-menu/index.cjs.js +1 -1
  46. package/dist/nile-context-menu/nile-context-menu.cjs.js +1 -1
  47. package/dist/nile-context-submenu/index.cjs.js +1 -1
  48. package/dist/nile-context-submenu/nile-context-submenu.cjs.js +1 -1
  49. package/dist/nile-detail/index.cjs.js +1 -1
  50. package/dist/nile-detail/index.esm.js +1 -1
  51. package/dist/nile-detail/nile-detail.cjs.js +1 -1
  52. package/dist/nile-detail/nile-detail.esm.js +1 -1
  53. package/dist/nile-dropdown/index.cjs.js +1 -1
  54. package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
  55. package/dist/nile-dropdown/portal-manager.cjs.js +1 -1
  56. package/dist/nile-file-upload/index.cjs.js +1 -1
  57. package/dist/nile-file-upload/nile-file-upload.cjs.js +1 -1
  58. package/dist/nile-file-upload/nile-file-upload.template.cjs.js +1 -1
  59. package/dist/nile-floating-panel/index.cjs.js +1 -1
  60. package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
  61. package/dist/nile-lite-tooltip/index.cjs.js +1 -1
  62. package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
  63. package/dist/nile-markdown-editor/index.cjs.js +1 -1
  64. package/dist/nile-markdown-editor/nile-markdown-editor.cjs.js +1 -1
  65. package/dist/nile-popover/index.cjs.js +1 -1
  66. package/dist/nile-popover/nile-popover.cjs.js +1 -1
  67. package/dist/nile-popover/portal-manager.cjs.js +1 -1
  68. package/dist/nile-popup/index.cjs.js +1 -1
  69. package/dist/nile-popup/nile-popup.cjs.js +1 -1
  70. package/dist/nile-select/index.cjs.js +1 -1
  71. package/dist/nile-select/nile-select.cjs.js +1 -1
  72. package/dist/nile-select/portal-manager.cjs.js +1 -1
  73. package/dist/nile-side-bar-action/index.cjs.js +1 -1
  74. package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js +1 -1
  75. package/dist/nile-side-bar-action/portal-manager.cjs.js +1 -1
  76. package/dist/nile-tooltip/index.cjs.js +1 -1
  77. package/dist/nile-tooltip/nile-tooltip.cjs.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/emoji.cjs.js +2 -0
  83. package/dist/nile-wysiwyg-editor/emoji.cjs.js.map +1 -0
  84. package/dist/nile-wysiwyg-editor/emoji.esm.js +1 -0
  85. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +2 -0
  86. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -0
  87. package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -0
  88. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +2 -0
  89. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -0
  90. package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -0
  91. package/dist/nile-wysiwyg-editor/engine/export.cjs.js +2 -0
  92. package/dist/nile-wysiwyg-editor/engine/export.cjs.js.map +1 -0
  93. package/dist/nile-wysiwyg-editor/engine/export.esm.js +1 -0
  94. package/dist/nile-wysiwyg-editor/engine/index.cjs.js +2 -0
  95. package/dist/nile-wysiwyg-editor/engine/index.cjs.js.map +1 -0
  96. package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -0
  97. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +2 -0
  98. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js.map +1 -0
  99. package/dist/nile-wysiwyg-editor/engine/inputrules.esm.js +1 -0
  100. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +2 -0
  101. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js.map +1 -0
  102. package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -0
  103. package/dist/nile-wysiwyg-editor/engine/markdown-parse.cjs.js +2 -0
  104. package/dist/nile-wysiwyg-editor/engine/markdown-parse.cjs.js.map +1 -0
  105. package/dist/nile-wysiwyg-editor/engine/markdown-parse.esm.js +1 -0
  106. package/dist/nile-wysiwyg-editor/engine/markdown.cjs.js +2 -0
  107. package/dist/nile-wysiwyg-editor/engine/markdown.cjs.js.map +1 -0
  108. package/dist/nile-wysiwyg-editor/engine/markdown.esm.js +1 -0
  109. package/dist/nile-wysiwyg-editor/engine/office-paste.cjs.js +2 -0
  110. package/dist/nile-wysiwyg-editor/engine/office-paste.cjs.js.map +1 -0
  111. package/dist/nile-wysiwyg-editor/engine/office-paste.esm.js +1 -0
  112. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +2 -0
  113. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -0
  114. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -0
  115. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +2 -0
  116. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -0
  117. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -0
  118. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +2 -0
  119. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -0
  120. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -0
  121. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +2 -0
  122. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js.map +1 -0
  123. package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -0
  124. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js +2 -0
  125. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js.map +1 -0
  126. package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.esm.js +1 -0
  127. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js +2 -0
  128. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js.map +1 -0
  129. package/dist/nile-wysiwyg-editor/engine/plugins/image-view.esm.js +1 -0
  130. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +2 -0
  131. package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js.map +1 -0
  132. package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -0
  133. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js +2 -0
  134. package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js.map +1 -0
  135. package/dist/nile-wysiwyg-editor/engine/plugins/link.esm.js +1 -0
  136. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js +2 -0
  137. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js.map +1 -0
  138. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.esm.js +1 -0
  139. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js +2 -0
  140. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js.map +1 -0
  141. package/dist/nile-wysiwyg-editor/engine/plugins/max-length.esm.js +1 -0
  142. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js +2 -0
  143. package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js.map +1 -0
  144. package/dist/nile-wysiwyg-editor/engine/plugins/paste.esm.js +1 -0
  145. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +2 -0
  146. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js.map +1 -0
  147. package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -0
  148. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js +2 -0
  149. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js.map +1 -0
  150. package/dist/nile-wysiwyg-editor/engine/plugins/single-line.esm.js +1 -0
  151. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +2 -0
  152. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js.map +1 -0
  153. package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -0
  154. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +2 -0
  155. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -0
  156. package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -0
  157. package/dist/nile-wysiwyg-editor/engine/plugins/task-list.cjs.js +2 -0
  158. package/dist/nile-wysiwyg-editor/engine/plugins/task-list.cjs.js.map +1 -0
  159. package/dist/nile-wysiwyg-editor/engine/plugins/task-list.esm.js +1 -0
  160. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +2 -0
  161. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -0
  162. package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -0
  163. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +2 -0
  164. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -0
  165. package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -0
  166. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +2 -0
  167. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -0
  168. package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -0
  169. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js +2 -0
  170. package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js.map +1 -0
  171. package/dist/nile-wysiwyg-editor/engine/serializer.esm.js +1 -0
  172. package/dist/nile-wysiwyg-editor/index.cjs.js +2 -0
  173. package/dist/nile-wysiwyg-editor/index.cjs.js.map +1 -0
  174. package/dist/nile-wysiwyg-editor/index.esm.js +1 -0
  175. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +2 -0
  176. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -0
  177. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +2 -0
  178. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -0
  179. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +780 -0
  180. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +97 -0
  181. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +2 -0
  182. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js.map +1 -0
  183. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.esm.js +1 -0
  184. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +2 -0
  185. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js.map +1 -0
  186. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js +2 -0
  187. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js.map +1 -0
  188. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.esm.js +244 -0
  189. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.esm.js +137 -0
  190. package/dist/nile-wysiwyg-editor/types.cjs.js +2 -0
  191. package/dist/nile-wysiwyg-editor/types.cjs.js.map +1 -0
  192. package/dist/nile-wysiwyg-editor/types.esm.js +1 -0
  193. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js +2 -0
  194. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js.map +1 -0
  195. package/dist/nile-wysiwyg-editor/ui/bookmark-panel.esm.js +97 -0
  196. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +2 -0
  197. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -0
  198. package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +67 -0
  199. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +2 -0
  200. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -0
  201. package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +65 -0
  202. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js +2 -0
  203. package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js.map +1 -0
  204. package/dist/nile-wysiwyg-editor/ui/embed-menu.esm.js +64 -0
  205. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +2 -0
  206. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -0
  207. package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +68 -0
  208. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js +2 -0
  209. package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js.map +1 -0
  210. package/dist/nile-wysiwyg-editor/ui/emoji-picker.esm.js +96 -0
  211. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js +2 -0
  212. package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js.map +1 -0
  213. package/dist/nile-wysiwyg-editor/ui/find-replace.esm.js +112 -0
  214. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js +2 -0
  215. package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js.map +1 -0
  216. package/dist/nile-wysiwyg-editor/ui/image-menu.esm.js +64 -0
  217. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js +2 -0
  218. package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js.map +1 -0
  219. package/dist/nile-wysiwyg-editor/ui/image-panel.esm.js +171 -0
  220. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js +2 -0
  221. package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js.map +1 -0
  222. package/dist/nile-wysiwyg-editor/ui/insert-menu.esm.js +90 -0
  223. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js +2 -0
  224. package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js.map +1 -0
  225. package/dist/nile-wysiwyg-editor/ui/link-editor.esm.js +114 -0
  226. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js +2 -0
  227. package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js.map +1 -0
  228. package/dist/nile-wysiwyg-editor/ui/markdown-panel.esm.js +80 -0
  229. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +2 -0
  230. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -0
  231. package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +92 -0
  232. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js +2 -0
  233. package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js.map +1 -0
  234. package/dist/nile-wysiwyg-editor/ui/shortcut-help.esm.js +147 -0
  235. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +2 -0
  236. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -0
  237. package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +79 -0
  238. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js +2 -0
  239. package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js.map +1 -0
  240. package/dist/nile-wysiwyg-editor/ui/special-chars.esm.js +96 -0
  241. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +2 -0
  242. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -0
  243. package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +67 -0
  244. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +2 -0
  245. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -0
  246. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +100 -0
  247. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +2 -0
  248. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -0
  249. package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +58 -0
  250. package/dist/src/index.d.ts +24 -0
  251. package/dist/src/index.js +19 -0
  252. package/dist/src/index.js.map +1 -1
  253. package/dist/src/internal/glyph.d.ts +6 -0
  254. package/dist/src/internal/glyph.js +13 -0
  255. package/dist/src/internal/glyph.js.map +1 -0
  256. package/dist/src/nile-wysiwyg-editor/emoji.d.ts +5 -0
  257. package/dist/src/nile-wysiwyg-editor/emoji.js +138 -0
  258. package/dist/src/nile-wysiwyg-editor/emoji.js.map +1 -0
  259. package/dist/src/nile-wysiwyg-editor/engine/commands.d.ts +69 -0
  260. package/dist/src/nile-wysiwyg-editor/engine/commands.js +863 -0
  261. package/dist/src/nile-wysiwyg-editor/engine/commands.js.map +1 -0
  262. package/dist/src/nile-wysiwyg-editor/engine/create-state.d.ts +35 -0
  263. package/dist/src/nile-wysiwyg-editor/engine/create-state.js +67 -0
  264. package/dist/src/nile-wysiwyg-editor/engine/create-state.js.map +1 -0
  265. package/dist/src/nile-wysiwyg-editor/engine/export.d.ts +38 -0
  266. package/dist/src/nile-wysiwyg-editor/engine/export.js +208 -0
  267. package/dist/src/nile-wysiwyg-editor/engine/export.js.map +1 -0
  268. package/dist/src/nile-wysiwyg-editor/engine/export.test.d.ts +1 -0
  269. package/dist/src/nile-wysiwyg-editor/engine/export.test.js +96 -0
  270. package/dist/src/nile-wysiwyg-editor/engine/export.test.js.map +1 -0
  271. package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +37 -0
  272. package/dist/src/nile-wysiwyg-editor/engine/index.js +29 -0
  273. package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -0
  274. package/dist/src/nile-wysiwyg-editor/engine/inputrules.d.ts +12 -0
  275. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js +144 -0
  276. package/dist/src/nile-wysiwyg-editor/engine/inputrules.js.map +1 -0
  277. package/dist/src/nile-wysiwyg-editor/engine/keymap.d.ts +15 -0
  278. package/dist/src/nile-wysiwyg-editor/engine/keymap.js +94 -0
  279. package/dist/src/nile-wysiwyg-editor/engine/keymap.js.map +1 -0
  280. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.d.ts +22 -0
  281. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.js +287 -0
  282. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.js.map +1 -0
  283. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.d.ts +1 -0
  284. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.js +106 -0
  285. package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.js.map +1 -0
  286. package/dist/src/nile-wysiwyg-editor/engine/markdown.d.ts +10 -0
  287. package/dist/src/nile-wysiwyg-editor/engine/markdown.js +162 -0
  288. package/dist/src/nile-wysiwyg-editor/engine/markdown.js.map +1 -0
  289. package/dist/src/nile-wysiwyg-editor/engine/office-paste.d.ts +13 -0
  290. package/dist/src/nile-wysiwyg-editor/engine/office-paste.js +182 -0
  291. package/dist/src/nile-wysiwyg-editor/engine/office-paste.js.map +1 -0
  292. package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.d.ts +10 -0
  293. package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.js +165 -0
  294. package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.js.map +1 -0
  295. package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.d.ts +17 -0
  296. package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js +69 -0
  297. package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js.map +1 -0
  298. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.d.ts +17 -0
  299. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js +96 -0
  300. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js.map +1 -0
  301. package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.d.ts +26 -0
  302. package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.js +123 -0
  303. package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.js.map +1 -0
  304. package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.d.ts +28 -0
  305. package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.js +72 -0
  306. package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.js.map +1 -0
  307. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.d.ts +41 -0
  308. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js +176 -0
  309. package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js.map +1 -0
  310. package/dist/src/nile-wysiwyg-editor/engine/plugins/image.d.ts +15 -0
  311. package/dist/src/nile-wysiwyg-editor/engine/plugins/image.js +64 -0
  312. package/dist/src/nile-wysiwyg-editor/engine/plugins/image.js.map +1 -0
  313. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.d.ts +11 -0
  314. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js +42 -0
  315. package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js.map +1 -0
  316. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.d.ts +18 -0
  317. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js +46 -0
  318. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js.map +1 -0
  319. package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.d.ts +7 -0
  320. package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.js +26 -0
  321. package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.js.map +1 -0
  322. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.d.ts +31 -0
  323. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js +55 -0
  324. package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js.map +1 -0
  325. package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.d.ts +7 -0
  326. package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.js +25 -0
  327. package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.js.map +1 -0
  328. package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.d.ts +10 -0
  329. package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.js +48 -0
  330. package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.js.map +1 -0
  331. package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.d.ts +29 -0
  332. package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.js +87 -0
  333. package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.js.map +1 -0
  334. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.d.ts +8 -0
  335. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +11 -0
  336. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -0
  337. package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.d.ts +16 -0
  338. package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.js +49 -0
  339. package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.js.map +1 -0
  340. package/dist/src/nile-wysiwyg-editor/engine/pm.test.d.ts +1 -0
  341. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +883 -0
  342. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -0
  343. package/dist/src/nile-wysiwyg-editor/engine/productivity.test.d.ts +1 -0
  344. package/dist/src/nile-wysiwyg-editor/engine/productivity.test.js +129 -0
  345. package/dist/src/nile-wysiwyg-editor/engine/productivity.test.js.map +1 -0
  346. package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +13 -0
  347. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +155 -0
  348. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -0
  349. package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +20 -0
  350. package/dist/src/nile-wysiwyg-editor/engine/schema.js +823 -0
  351. package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -0
  352. package/dist/src/nile-wysiwyg-editor/engine/selection-state.d.ts +70 -0
  353. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js +138 -0
  354. package/dist/src/nile-wysiwyg-editor/engine/selection-state.js.map +1 -0
  355. package/dist/src/nile-wysiwyg-editor/engine/serializer.d.ts +18 -0
  356. package/dist/src/nile-wysiwyg-editor/engine/serializer.js +63 -0
  357. package/dist/src/nile-wysiwyg-editor/engine/serializer.js.map +1 -0
  358. package/dist/src/nile-wysiwyg-editor/index.d.ts +3 -0
  359. package/dist/src/nile-wysiwyg-editor/index.js +3 -0
  360. package/dist/src/nile-wysiwyg-editor/index.js.map +1 -0
  361. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.d.ts +1 -0
  362. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +782 -0
  363. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -0
  364. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +364 -0
  365. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +2038 -0
  366. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -0
  367. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.d.ts +1 -0
  368. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +636 -0
  369. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -0
  370. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.d.ts +1 -0
  371. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js +2 -0
  372. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js.map +1 -0
  373. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.d.ts +6 -0
  374. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js +251 -0
  375. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js.map +1 -0
  376. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.d.ts +41 -0
  377. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js +700 -0
  378. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js.map +1 -0
  379. package/dist/src/nile-wysiwyg-editor/types.d.ts +49 -0
  380. package/dist/src/nile-wysiwyg-editor/types.js +8 -0
  381. package/dist/src/nile-wysiwyg-editor/types.js.map +1 -0
  382. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.d.ts +20 -0
  383. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js +159 -0
  384. package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js.map +1 -0
  385. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.d.ts +21 -0
  386. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +125 -0
  387. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -0
  388. package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.d.ts +18 -0
  389. package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.js +107 -0
  390. package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.js.map +1 -0
  391. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.d.ts +19 -0
  392. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js +123 -0
  393. package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js.map +1 -0
  394. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.d.ts +27 -0
  395. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +146 -0
  396. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -0
  397. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.d.ts +21 -0
  398. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js +154 -0
  399. package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js.map +1 -0
  400. package/dist/src/nile-wysiwyg-editor/ui/find-replace.d.ts +26 -0
  401. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js +178 -0
  402. package/dist/src/nile-wysiwyg-editor/ui/find-replace.js.map +1 -0
  403. package/dist/src/nile-wysiwyg-editor/ui/image-menu.d.ts +18 -0
  404. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js +129 -0
  405. package/dist/src/nile-wysiwyg-editor/ui/image-menu.js.map +1 -0
  406. package/dist/src/nile-wysiwyg-editor/ui/image-panel.d.ts +56 -0
  407. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js +349 -0
  408. package/dist/src/nile-wysiwyg-editor/ui/image-panel.js.map +1 -0
  409. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.d.ts +25 -0
  410. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js +142 -0
  411. package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js.map +1 -0
  412. package/dist/src/nile-wysiwyg-editor/ui/link-editor.d.ts +30 -0
  413. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js +189 -0
  414. package/dist/src/nile-wysiwyg-editor/ui/link-editor.js.map +1 -0
  415. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.d.ts +24 -0
  416. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js +144 -0
  417. package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js.map +1 -0
  418. package/dist/src/nile-wysiwyg-editor/ui/math-panel.d.ts +31 -0
  419. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +172 -0
  420. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -0
  421. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.d.ts +30 -0
  422. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js +296 -0
  423. package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js.map +1 -0
  424. package/dist/src/nile-wysiwyg-editor/ui/source-view.d.ts +19 -0
  425. package/dist/src/nile-wysiwyg-editor/ui/source-view.js +120 -0
  426. package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -0
  427. package/dist/src/nile-wysiwyg-editor/ui/special-chars.d.ts +20 -0
  428. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js +199 -0
  429. package/dist/src/nile-wysiwyg-editor/ui/special-chars.js.map +1 -0
  430. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.d.ts +31 -0
  431. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +133 -0
  432. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -0
  433. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.d.ts +23 -0
  434. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +177 -0
  435. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -0
  436. package/dist/src/nile-wysiwyg-editor/ui/table-menu.d.ts +16 -0
  437. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +101 -0
  438. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -0
  439. package/dist/src/version.js +1 -1
  440. package/dist/src/version.js.map +1 -1
  441. package/dist/tsconfig.tsbuildinfo +1 -1
  442. package/package.json +18 -2
  443. package/src/index.ts +57 -1
  444. package/src/internal/glyph.ts +13 -0
  445. package/src/nile-wysiwyg-editor/emoji.ts +137 -0
  446. package/src/nile-wysiwyg-editor/engine/commands.ts +1035 -0
  447. package/src/nile-wysiwyg-editor/engine/create-state.ts +117 -0
  448. package/src/nile-wysiwyg-editor/engine/export.test.ts +113 -0
  449. package/src/nile-wysiwyg-editor/engine/export.ts +242 -0
  450. package/src/nile-wysiwyg-editor/engine/index.ts +69 -0
  451. package/src/nile-wysiwyg-editor/engine/inputrules.ts +169 -0
  452. package/src/nile-wysiwyg-editor/engine/keymap.ts +146 -0
  453. package/src/nile-wysiwyg-editor/engine/markdown-parse.test.ts +148 -0
  454. package/src/nile-wysiwyg-editor/engine/markdown-parse.ts +337 -0
  455. package/src/nile-wysiwyg-editor/engine/markdown.ts +165 -0
  456. package/src/nile-wysiwyg-editor/engine/office-paste.ts +199 -0
  457. package/src/nile-wysiwyg-editor/engine/plugins/code-highlight.ts +173 -0
  458. package/src/nile-wysiwyg-editor/engine/plugins/embed-view.ts +85 -0
  459. package/src/nile-wysiwyg-editor/engine/plugins/embeds.ts +99 -0
  460. package/src/nile-wysiwyg-editor/engine/plugins/find-replace.ts +155 -0
  461. package/src/nile-wysiwyg-editor/engine/plugins/format-painter.ts +80 -0
  462. package/src/nile-wysiwyg-editor/engine/plugins/image-view.ts +220 -0
  463. package/src/nile-wysiwyg-editor/engine/plugins/image.ts +80 -0
  464. package/src/nile-wysiwyg-editor/engine/plugins/link.ts +42 -0
  465. package/src/nile-wysiwyg-editor/engine/plugins/math-view.ts +56 -0
  466. package/src/nile-wysiwyg-editor/engine/plugins/max-length.ts +24 -0
  467. package/src/nile-wysiwyg-editor/engine/plugins/paste.ts +74 -0
  468. package/src/nile-wysiwyg-editor/engine/plugins/placeholder.ts +24 -0
  469. package/src/nile-wysiwyg-editor/engine/plugins/single-line.ts +47 -0
  470. package/src/nile-wysiwyg-editor/engine/plugins/suggestions.ts +110 -0
  471. package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +12 -0
  472. package/src/nile-wysiwyg-editor/engine/plugins/task-list.ts +65 -0
  473. package/src/nile-wysiwyg-editor/engine/pm.test.ts +1088 -0
  474. package/src/nile-wysiwyg-editor/engine/productivity.test.ts +163 -0
  475. package/src/nile-wysiwyg-editor/engine/sanitize.ts +156 -0
  476. package/src/nile-wysiwyg-editor/engine/schema.ts +859 -0
  477. package/src/nile-wysiwyg-editor/engine/selection-state.ts +210 -0
  478. package/src/nile-wysiwyg-editor/engine/serializer.ts +78 -0
  479. package/src/nile-wysiwyg-editor/index.ts +3 -0
  480. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +782 -0
  481. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +750 -0
  482. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +2155 -0
  483. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.ts +1 -0
  484. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.ts +251 -0
  485. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.ts +772 -0
  486. package/src/nile-wysiwyg-editor/types.ts +83 -0
  487. package/src/nile-wysiwyg-editor/ui/bookmark-panel.ts +155 -0
  488. package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +135 -0
  489. package/src/nile-wysiwyg-editor/ui/code-block-menu.ts +105 -0
  490. package/src/nile-wysiwyg-editor/ui/embed-menu.ts +133 -0
  491. package/src/nile-wysiwyg-editor/ui/embed-panel.ts +143 -0
  492. package/src/nile-wysiwyg-editor/ui/emoji-picker.ts +152 -0
  493. package/src/nile-wysiwyg-editor/ui/find-replace.ts +174 -0
  494. package/src/nile-wysiwyg-editor/ui/image-menu.ts +140 -0
  495. package/src/nile-wysiwyg-editor/ui/image-panel.ts +337 -0
  496. package/src/nile-wysiwyg-editor/ui/insert-menu.ts +142 -0
  497. package/src/nile-wysiwyg-editor/ui/link-editor.ts +180 -0
  498. package/src/nile-wysiwyg-editor/ui/markdown-panel.ts +143 -0
  499. package/src/nile-wysiwyg-editor/ui/math-panel.ts +169 -0
  500. package/src/nile-wysiwyg-editor/ui/shortcut-help.ts +311 -0
  501. package/src/nile-wysiwyg-editor/ui/source-view.ts +119 -0
  502. package/src/nile-wysiwyg-editor/ui/special-chars.ts +209 -0
  503. package/src/nile-wysiwyg-editor/ui/suggest-list.ts +138 -0
  504. package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +173 -0
  505. package/src/nile-wysiwyg-editor/ui/table-menu.ts +109 -0
  506. package/vscode-html-custom-data.json +450 -0
  507. package/web-dev-server.config.mjs +6 -2
  508. package/dist/index-5fef1a0d.esm.js +0 -1
  509. package/dist/index-8f38fae3.cjs.js +0 -2
  510. package/dist/index-8f38fae3.cjs.js.map +0 -1
@@ -0,0 +1,2038 @@
1
+ import { __decorate } from "tslib";
2
+ import { html, nothing } from 'lit';
3
+ import { customElement, property, query, state } from 'lit/decorators.js';
4
+ import { EditorView } from 'prosemirror-view';
5
+ import { TextSelection } from 'prosemirror-state';
6
+ import NileElement from '../internal/nile-element';
7
+ import { styles } from './nile-wysiwyg-editor.css';
8
+ import { defaultSchema, createEditorState, computeToolbarState, htmlToDoc, htmlToSlice, docToHtml, docToText, docToMarkdown, markdownToHtml, docToJson, jsonToDoc, isDocEmpty, sanitizeHtml, getCommand, getMarkRange, flattenToSingleParagraph, imageUploadKey, findUploadPos, countWords, TaskItemView, ImageView, FigureView, EmbedView, MathView, isOfficeHtml, cleanOfficeHtml, toInlineStyledHtml, buildWordDocument, buildPrintDocument, isFormatPainterArmed, armFormatPainter, disarmFormatPainter, applyFormatPainter, getFindState, searchTr, findStepTr, clearFindTr, replaceActiveTr, replaceAllTr, } from './engine';
9
+ import { selectedImageInfo } from './engine/commands';
10
+ import { DEFAULT_TOOLBAR } from './types';
11
+ import { EMOJI } from './emoji';
12
+ import './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar';
13
+ import './ui/link-editor';
14
+ import './ui/bubble-menu';
15
+ import './ui/image-panel';
16
+ import './ui/table-menu';
17
+ import './ui/suggest-list';
18
+ import './ui/find-replace';
19
+ import './ui/source-view';
20
+ import './ui/embed-panel';
21
+ import './ui/bookmark-panel';
22
+ import './ui/code-block-menu';
23
+ import './ui/shortcut-help';
24
+ import './ui/image-menu';
25
+ import './ui/embed-menu';
26
+ import './ui/special-chars';
27
+ import './ui/emoji-picker';
28
+ import './ui/insert-menu';
29
+ import './ui/math-panel';
30
+ import './ui/markdown-panel';
31
+ const SLASH_ITEMS = [
32
+ { key: 'heading1', label: 'Heading 1', hint: '#', command: 'heading', attrs: { level: 1 } },
33
+ { key: 'heading2', label: 'Heading 2', hint: '##', command: 'heading', attrs: { level: 2 } },
34
+ { key: 'heading3', label: 'Heading 3', hint: '###', command: 'heading', attrs: { level: 3 } },
35
+ { key: 'bulletList', label: 'Bulleted list', hint: '-', command: 'bulletList' },
36
+ { key: 'orderedList', label: 'Numbered list', hint: '1.', command: 'orderedList' },
37
+ { key: 'taskList', label: 'Checklist', hint: '[ ]', command: 'taskList' },
38
+ { key: 'blockquote', label: 'Blockquote', hint: '>', command: 'blockquote' },
39
+ { key: 'codeBlock', label: 'Code block', hint: '```', command: 'codeBlock' },
40
+ { key: 'table', label: 'Table', hint: '3×3', command: 'insertTable', attrs: { rows: 3, cols: 3 } },
41
+ { key: 'image', label: 'Image', command: 'openImage' },
42
+ { key: 'embed', label: 'Video embed', hint: 'YouTube…', command: 'openEmbed' },
43
+ { key: 'bookmark', label: 'Bookmark (anchor)', command: 'openBookmark' },
44
+ { key: 'horizontalRule', label: 'Divider', hint: '---', command: 'horizontalRule' },
45
+ ];
46
+ let uploadCounter = 0;
47
+ function readFileAsDataURL(file) {
48
+ return new Promise((resolve, reject) => {
49
+ const reader = new FileReader();
50
+ reader.onload = () => resolve(reader.result);
51
+ reader.onerror = () => reject(reader.error);
52
+ reader.readAsDataURL(file);
53
+ });
54
+ }
55
+ const CHANGE_DEBOUNCE_MS = 250;
56
+ /**
57
+ * WYSIWYG rich text editor powered by ProseMirror.
58
+ *
59
+ * Requires evergreen browsers: selection inside shadow DOM relies on
60
+ * `Selection.getComposedRanges()` (Safari 17+, recent Firefox) or Chrome's
61
+ * `shadowRoot.getSelection()`.
62
+ *
63
+ * @tag nile-wysiwyg-editor
64
+ *
65
+ * @event nile-change - Debounced content change. detail: { value, json, content }
66
+ * @event nile-input - Every document change. detail: { value, json, content }
67
+ * @event nile-focus - Editor gained focus.
68
+ * @event nile-blur - Editor lost focus.
69
+ * @event nile-selection-change - detail: ToolbarState snapshot.
70
+ * @event nile-image-upload-request - Cancelable. detail: { file, insert(src, alt) }.
71
+ * Call preventDefault() and use insert() to fully own the upload.
72
+ * @event nile-image-upload-error - detail: { file, error } when uploadHandler rejects.
73
+ * @event nile-mention-query - detail: { trigger, query, provide(items) } for async sources.
74
+ * @event nile-mention-select - detail: { trigger, key, label } when a mention is inserted.
75
+ * @event nile-word-count - detail: { words, characters } on every document change.
76
+ * @event nile-autosave - detail: { value, json, content } on the autosave interval.
77
+ *
78
+ * @csspart base - Outer container.
79
+ * @csspart toolbar - The toolbar element.
80
+ * @csspart surface - The scrollable editing surface.
81
+ */
82
+ let NileWysiwygEditor = class NileWysiwygEditor extends NileElement {
83
+ static get styles() {
84
+ return [styles];
85
+ }
86
+ constructor() {
87
+ super();
88
+ /** Document content as HTML. Not reflected to the attribute. */
89
+ this.value = '';
90
+ this.placeholder = '';
91
+ this.disabled = false;
92
+ this.readonly = false;
93
+ /** Toolbar config string; `"none"` hides the toolbar. */
94
+ this.toolbar = DEFAULT_TOOLBAR;
95
+ /** Shows a balloon toolbar over text selections. */
96
+ this.bubbleMenu = false;
97
+ /** Sanitizes incoming HTML (value, paste, insertHTML). Leave on. */
98
+ this.sanitize = true;
99
+ /** Constrains content to a single paragraph. */
100
+ this.singleLine = false;
101
+ /** Form field name. */
102
+ this.name = '';
103
+ this.required = false;
104
+ /**
105
+ * Fallback when no uploadHandler is set and no nile-image-upload-request
106
+ * listener takes over: embed image files as base64 data URLs.
107
+ */
108
+ this.allowBase64Images = true;
109
+ /**
110
+ * Shows an "Upload from computer" button in the Insert Image dialog. Off by
111
+ * default (URL-only, like the RTE's default); files can always be added via
112
+ * drag & drop or paste regardless.
113
+ */
114
+ this.uploadFromComputer = false;
115
+ /** Max upload size (bytes) shown/enforced by the image dialog. Default 2 MB. */
116
+ this.imageMaxFileSize = 2 * 1024 * 1024;
117
+ /**
118
+ * Mention config: trigger character → items, same shape and JSON attribute
119
+ * format as nile-rich-text-editor. For async sources, configure the
120
+ * trigger with an empty array and listen to nile-mention-query.
121
+ */
122
+ this.mentions = {};
123
+ /** Disables the "/" command menu. */
124
+ this.noSlashCommands = false;
125
+ /** Disables the ":" emoji picker. */
126
+ this.noEmoji = false;
127
+ /** Maximum character count; further input is rejected. 0 = unlimited. */
128
+ this.maxLength = 0;
129
+ /** Always show the word/character counter footer (implied by maxlength). */
130
+ this.showCounter = false;
131
+ /** Keeps the toolbar pinned to the viewport while scrolling long content. */
132
+ this.stickyToolbar = false;
133
+ /** Reusable snippets surfaced in the "/" menu (JSON attribute). */
134
+ this.snippets = [];
135
+ /** Merge fields offered in the merge-field picker (JSON attribute). */
136
+ this.mergeFields = [];
137
+ /** Content templates offered in the template picker (JSON attribute). */
138
+ this.templates = [];
139
+ /** Emit nile-autosave every N ms while the document has changed. 0 = off. */
140
+ this.autosaveInterval = 0;
141
+ /** When on, every paste drops formatting and inserts plain text. */
142
+ this.pasteAsPlainText = false;
143
+ /** Disables Markdown detection on plain-text pastes. */
144
+ this.noMarkdownPaste = false;
145
+ /** Disables typography autocorrect (smart quotes, dashes, (c) → ©). */
146
+ this.noTextTransform = false;
147
+ /** Expands the editor to fill the viewport. Esc exits. */
148
+ this.fullscreen = false;
149
+ /** Outlines block boundaries (paragraphs, headings, lists…) while editing. */
150
+ this.showBlocks = false;
151
+ this.hasFocus = false;
152
+ this.sourceMode = false;
153
+ this.counter = { words: 0, characters: 0 };
154
+ /** Which list the shared insert-menu is showing, if open. */
155
+ this.insertMenuMode = null;
156
+ this.mentionMatch = null;
157
+ this.slashMatch = null;
158
+ this.emojiMatch = null;
159
+ this.activeSuggestSource = null;
160
+ this.suggestDismissed = null;
161
+ this.mentionQueryToken = '';
162
+ this.lastAutosaveValue = '';
163
+ this.defaultValue = '';
164
+ this.syncingValue = false;
165
+ this.pendingChange = false;
166
+ /** Body overflow value before fullscreen locked it; null = not locked. */
167
+ this.previousBodyOverflow = null;
168
+ /**
169
+ * Closes any open insert panel when the pointer goes down outside of it —
170
+ * whether elsewhere in the editor (e.g. the content area) or off the widget.
171
+ */
172
+ this.onOutsidePointerDown = (event) => {
173
+ const openPanels = this.floatingPanels.filter((p) => !!p && p.open);
174
+ if (!openPanels.length)
175
+ return;
176
+ const path = event.composedPath();
177
+ // Click landed inside an open panel — leave it be.
178
+ if (openPanels.some(panel => path.includes(panel)))
179
+ return;
180
+ // Refocus the editor only when the click stayed within the widget.
181
+ const insideWidget = path.includes(this) || (!!this.shadowRoot && path.includes(this.shadowRoot));
182
+ this.closeAllPanels(insideWidget);
183
+ };
184
+ if (typeof this.attachInternals === 'function') {
185
+ this.internals = this.attachInternals();
186
+ }
187
+ }
188
+ connectedCallback() {
189
+ super.connectedCallback();
190
+ // Remount after a disconnect/reconnect (e.g. host moved in the DOM).
191
+ if (this.hasUpdated && !this.view) {
192
+ this.updateComplete.then(() => this.mountView());
193
+ }
194
+ // Close floating panels when the user clicks anywhere outside them.
195
+ document.addEventListener('pointerdown', this.onOutsidePointerDown, true);
196
+ }
197
+ disconnectedCallback() {
198
+ super.disconnectedCallback();
199
+ document.removeEventListener('pointerdown', this.onOutsidePointerDown, true);
200
+ if (this.changeTimer)
201
+ clearTimeout(this.changeTimer);
202
+ if (this.autosaveTimer)
203
+ clearInterval(this.autosaveTimer);
204
+ if (this.previousBodyOverflow !== null) {
205
+ document.body.style.overflow = this.previousBodyOverflow;
206
+ this.previousBodyOverflow = null;
207
+ }
208
+ this.view?.destroy();
209
+ this.view = undefined;
210
+ }
211
+ firstUpdated() {
212
+ this.defaultValue = this.value;
213
+ this.mountView();
214
+ }
215
+ updated(changed) {
216
+ if (changed.has('value') && this.view && !this.syncingValue) {
217
+ const current = docToHtml(this.view.state.doc);
218
+ if (current !== this.value)
219
+ this.setContent(this.value);
220
+ }
221
+ this.syncingValue = false;
222
+ if ((changed.has('disabled') || changed.has('readonly')) && this.view) {
223
+ this.view.setProps({});
224
+ }
225
+ if (changed.has('placeholder') && this.view) {
226
+ // Decorations are recomputed on any transaction.
227
+ this.view.dispatch(this.view.state.tr);
228
+ }
229
+ if (changed.has('required'))
230
+ this.updateValidity();
231
+ if (changed.has('autosaveInterval'))
232
+ this.restartAutosave();
233
+ if ((changed.has('pasteAsPlainText') ||
234
+ changed.has('fullscreen') ||
235
+ changed.has('showBlocks')) &&
236
+ this.view) {
237
+ this.toolbarState = this.snapshotToolbar(this.view.state);
238
+ }
239
+ // Input rules are baked into the state at build time; rebuild in place
240
+ // (same doc, fresh plugins) when the autocorrect toggle changes.
241
+ if (changed.has('noTextTransform') && this.view && !changed.has('value')) {
242
+ this.view.updateState(this.buildState(this.view.state.doc));
243
+ }
244
+ // Lock page scrolling while fullscreen covers the viewport.
245
+ if (changed.has('fullscreen')) {
246
+ if (this.fullscreen) {
247
+ if (this.previousBodyOverflow === null) {
248
+ this.previousBodyOverflow = document.body.style.overflow;
249
+ document.body.style.overflow = 'hidden';
250
+ }
251
+ }
252
+ else if (this.previousBodyOverflow !== null) {
253
+ document.body.style.overflow = this.previousBodyOverflow;
254
+ this.previousBodyOverflow = null;
255
+ }
256
+ }
257
+ }
258
+ restartAutosave() {
259
+ if (this.autosaveTimer)
260
+ clearInterval(this.autosaveTimer);
261
+ this.autosaveTimer = undefined;
262
+ if (this.autosaveInterval > 0) {
263
+ this.lastAutosaveValue = this.value;
264
+ this.autosaveTimer = setInterval(() => {
265
+ if (!this.view || this.value === this.lastAutosaveValue)
266
+ return;
267
+ this.lastAutosaveValue = this.value;
268
+ this.emit('nile-autosave', this.buildChangeDetail(this.view.state.doc));
269
+ }, this.autosaveInterval);
270
+ }
271
+ }
272
+ // ------------------------------------------------------------------ view
273
+ buildState(doc) {
274
+ return createEditorState({
275
+ schema: defaultSchema,
276
+ doc,
277
+ getPlaceholder: () => this.placeholder,
278
+ singleLine: this.singleLine,
279
+ onLinkShortcut: () => this.openLinkEditor(),
280
+ onFindShortcut: () => this.openFindBar(),
281
+ onShortcutHelp: () => this.openShortcutHelp(),
282
+ onImageFiles: (files, pos) => {
283
+ for (const file of files)
284
+ this.insertImageFile(file, pos);
285
+ },
286
+ mentions: {
287
+ getTriggers: () => Object.keys(this.mentions),
288
+ onChange: match => {
289
+ this.mentionMatch = match;
290
+ this.updateSuggestList();
291
+ },
292
+ onKeyDown: event => this.handleSuggestKey(event, 'mention'),
293
+ },
294
+ slashCommands: {
295
+ onChange: match => {
296
+ this.slashMatch = match;
297
+ this.updateSuggestList();
298
+ },
299
+ onKeyDown: event => this.handleSuggestKey(event, 'slash'),
300
+ },
301
+ emoji: {
302
+ onChange: match => {
303
+ this.emojiMatch = match;
304
+ this.updateSuggestList();
305
+ },
306
+ onKeyDown: event => this.handleSuggestKey(event, 'emoji'),
307
+ },
308
+ getMaxLength: () => this.maxLength,
309
+ getForcePlainText: () => this.pasteAsPlainText,
310
+ getMarkdownPaste: () => !this.noMarkdownPaste,
311
+ getSanitize: () => this.sanitize,
312
+ textTransform: !this.noTextTransform,
313
+ });
314
+ }
315
+ /**
316
+ * ToolbarState snapshot augmented with host-only flags the pure
317
+ * computeToolbarState can't know about (e.g. the paste-as-plain-text toggle).
318
+ */
319
+ snapshotToolbar(state) {
320
+ return {
321
+ ...computeToolbarState(state),
322
+ pasteAsPlainText: this.pasteAsPlainText,
323
+ fullscreen: this.fullscreen,
324
+ showBlocks: this.showBlocks,
325
+ formatPainter: isFormatPainterArmed(state),
326
+ };
327
+ }
328
+ mountView() {
329
+ if (this.view || !this.surfaceEl)
330
+ return;
331
+ let doc = this.value
332
+ ? htmlToDoc(defaultSchema, this.value, this.sanitize)
333
+ : undefined;
334
+ if (doc && this.singleLine)
335
+ doc = flattenToSingleParagraph(doc);
336
+ this.view = new EditorView(this.surfaceEl, {
337
+ state: this.buildState(doc),
338
+ dispatchTransaction: tr => this.handleTransaction(tr),
339
+ editable: () => !this.disabled && !this.readonly,
340
+ transformPastedHTML: pasted => {
341
+ // Word/Google Docs clipboard HTML gets normalized (real lists, no
342
+ // mso noise) before the sanitizer clamps it to the schema.
343
+ const cleaned = isOfficeHtml(pasted) ? cleanOfficeHtml(pasted) : pasted;
344
+ return this.sanitize ? sanitizeHtml(cleaned) : cleaned;
345
+ },
346
+ handleDOMEvents: {
347
+ mousedown: () => {
348
+ // Clicking into the document dismisses the never-focused popovers
349
+ // (they take no focus, so blur can't close them).
350
+ this.closeSpecialChars(false);
351
+ this.closeInsertMenu(false);
352
+ return false;
353
+ },
354
+ focus: () => {
355
+ this.hasFocus = true;
356
+ this.updateOverlays();
357
+ this.emit('nile-focus');
358
+ return false;
359
+ },
360
+ blur: (_view, event) => {
361
+ // Focus moving into the editor's own chrome (language picker,
362
+ // panels) is not a blur — closing overlays here would dismiss
363
+ // the control the user just clicked.
364
+ const related = event.relatedTarget;
365
+ if (related && (this.shadowRoot?.contains(related) || this.contains(related))) {
366
+ return false;
367
+ }
368
+ this.hasFocus = false;
369
+ this.flushChange();
370
+ this.updateOverlays();
371
+ this.closeSuggestList();
372
+ this.closeSpecialChars(false);
373
+ this.emit('nile-blur');
374
+ return false;
375
+ },
376
+ },
377
+ attributes: {
378
+ role: 'textbox',
379
+ 'aria-multiline': this.singleLine ? 'false' : 'true',
380
+ },
381
+ nodeViews: {
382
+ task_item: (node, view, getPos) => new TaskItemView(node, view, getPos),
383
+ image: (node, view, getPos) => new ImageView(node, view, getPos),
384
+ figure: (node, view, getPos) => new FigureView(node, view, getPos),
385
+ embed: (node, view, getPos) => new EmbedView(node, view, getPos),
386
+ math: node => new MathView(node, this.mathRenderer),
387
+ },
388
+ });
389
+ this.toolbarState = this.snapshotToolbar(this.view.state);
390
+ this.counter = countWords(this.view.state.doc);
391
+ this.syncFormValue(this.view.state.doc);
392
+ }
393
+ handleTransaction(tr) {
394
+ if (!this.view)
395
+ return;
396
+ const newState = this.view.state.apply(tr);
397
+ this.view.updateState(newState);
398
+ // Format painter: once armed, the next non-empty selection the user makes
399
+ // receives the copied formatting, then the painter disarms.
400
+ if (tr.selectionSet && !newState.selection.empty && isFormatPainterArmed(newState)) {
401
+ const applyTr = applyFormatPainter(newState);
402
+ if (applyTr) {
403
+ this.view.dispatch(applyTr);
404
+ return;
405
+ }
406
+ }
407
+ this.toolbarState = this.snapshotToolbar(newState);
408
+ this.emit('nile-selection-change', this.toolbarState);
409
+ this.updateOverlays();
410
+ if (this.findBarEl?.open) {
411
+ const find = getFindState(newState);
412
+ this.findBarEl.matchCount = find.matches.length;
413
+ this.findBarEl.activeIndex = find.activeIndex;
414
+ }
415
+ if (tr.docChanged) {
416
+ const detail = this.buildChangeDetail(newState.doc);
417
+ this.syncingValue = true;
418
+ this.value = detail.value;
419
+ this.syncFormValue(newState.doc);
420
+ this.emit('nile-input', detail);
421
+ this.counter = countWords(newState.doc);
422
+ this.emit('nile-word-count', this.counter);
423
+ this.scheduleChange();
424
+ }
425
+ }
426
+ buildChangeDetail(doc) {
427
+ const value = isDocEmpty(doc) ? '' : docToHtml(doc);
428
+ return { value, json: docToJson(doc), content: value };
429
+ }
430
+ scheduleChange() {
431
+ this.pendingChange = true;
432
+ if (this.changeTimer)
433
+ clearTimeout(this.changeTimer);
434
+ this.changeTimer = setTimeout(() => this.flushChange(), CHANGE_DEBOUNCE_MS);
435
+ }
436
+ flushChange() {
437
+ if (!this.pendingChange || !this.view)
438
+ return;
439
+ this.pendingChange = false;
440
+ if (this.changeTimer)
441
+ clearTimeout(this.changeTimer);
442
+ this.emit('nile-change', this.buildChangeDetail(this.view.state.doc));
443
+ }
444
+ // ------------------------------------------------------------------ form
445
+ syncFormValue(doc) {
446
+ if (!this.internals)
447
+ return;
448
+ const value = isDocEmpty(doc) ? '' : docToHtml(doc);
449
+ this.internals.setFormValue(value === '' ? null : value);
450
+ this.updateValidity();
451
+ }
452
+ updateValidity() {
453
+ if (!this.internals)
454
+ return;
455
+ const empty = this.view ? isDocEmpty(this.view.state.doc) : this.value === '';
456
+ if (this.required && empty) {
457
+ this.internals.setValidity({ valueMissing: true }, 'Please fill out this field.', this.surfaceEl ?? undefined);
458
+ }
459
+ else {
460
+ this.internals.setValidity({});
461
+ }
462
+ }
463
+ formResetCallback() {
464
+ this.setContent(this.defaultValue);
465
+ }
466
+ formDisabledCallback(disabled) {
467
+ this.disabled = disabled;
468
+ }
469
+ checkValidity() {
470
+ return this.internals ? this.internals.checkValidity() : true;
471
+ }
472
+ reportValidity() {
473
+ return this.internals ? this.internals.reportValidity() : true;
474
+ }
475
+ // -------------------------------------------------------------- overlays
476
+ /** Positions an overlay element near a doc position (default: selection). */
477
+ positionOverlay(el, placement, pos) {
478
+ if (!this.view || !this.baseEl)
479
+ return;
480
+ const from = pos ?? this.view.state.selection.from;
481
+ const coords = this.view.coordsAtPos(from);
482
+ const baseRect = this.baseEl.getBoundingClientRect();
483
+ const left = Math.max(4, Math.min(coords.left - baseRect.left, baseRect.width - el.offsetWidth - 4));
484
+ el.style.left = `${left}px`;
485
+ if (placement === 'above') {
486
+ el.style.top = `${Math.max(4, coords.top - baseRect.top - el.offsetHeight - 8)}px`;
487
+ }
488
+ else {
489
+ el.style.top = `${coords.bottom - baseRect.top + 6}px`;
490
+ }
491
+ }
492
+ /** Positions a floating panel directly below a toolbar/anchor element. */
493
+ positionOverlayBelowAnchor(el, anchor) {
494
+ if (!this.baseEl)
495
+ return;
496
+ const baseRect = this.baseEl.getBoundingClientRect();
497
+ const anchorRect = anchor.getBoundingClientRect();
498
+ const left = Math.max(4, Math.min(anchorRect.left - baseRect.left, baseRect.width - el.offsetWidth - 4));
499
+ // Clear any CSS-pinned `right` (e.g. the find bar) so `left` wins.
500
+ el.style.right = 'auto';
501
+ el.style.left = `${left}px`;
502
+ el.style.top = `${anchorRect.bottom - baseRect.top + 6}px`;
503
+ }
504
+ /** Finds a toolbar button by its aria-label, to anchor a panel below it. */
505
+ toolbarButton(label) {
506
+ return (this.toolbarEl?.shadowRoot?.querySelector(`button[aria-label="${label}"]`) ?? null);
507
+ }
508
+ updateOverlays() {
509
+ if (!this.bubbleMenuEl)
510
+ return;
511
+ const panelOpen = this.linkEditorEl?.open ||
512
+ this.imagePanelEl?.open ||
513
+ this.embedPanelEl?.open ||
514
+ this.bookmarkPanelEl?.open ||
515
+ this.emojiPickerEl?.open;
516
+ const show = this.bubbleMenu &&
517
+ this.hasFocus &&
518
+ !!this.toolbarState &&
519
+ !this.toolbarState.selectionEmpty &&
520
+ !this.toolbarState.imageSelected &&
521
+ !this.toolbarState.embedSelected &&
522
+ !panelOpen;
523
+ this.bubbleMenuEl.open = show;
524
+ if (show) {
525
+ this.bubbleMenuEl.toolbarState = this.toolbarState;
526
+ this.bubbleMenuEl.updateComplete.then(() => this.positionOverlay(this.bubbleMenuEl, 'above'));
527
+ }
528
+ if (this.tableMenuEl) {
529
+ const showTable = this.hasFocus && !!this.toolbarState && this.toolbarState.inTable && !panelOpen;
530
+ this.tableMenuEl.open = showTable;
531
+ if (showTable) {
532
+ this.tableMenuEl.updateComplete.then(() => this.positionOverlay(this.tableMenuEl, 'below'));
533
+ }
534
+ }
535
+ if (this.codeMenuEl) {
536
+ const showCode = this.hasFocus && !!this.toolbarState && this.toolbarState.inCodeBlock && !panelOpen;
537
+ this.codeMenuEl.open = showCode;
538
+ if (showCode) {
539
+ this.codeMenuEl.language = this.toolbarState.codeBlockLanguage ?? '';
540
+ this.codeMenuEl.updateComplete.then(() => this.positionOverlay(this.codeMenuEl, 'above'));
541
+ }
542
+ }
543
+ if (this.imageMenuEl) {
544
+ const showImage = this.hasFocus && !!this.toolbarState && this.toolbarState.imageSelected && !panelOpen;
545
+ this.imageMenuEl.open = showImage;
546
+ if (showImage) {
547
+ this.imageMenuEl.toolbarState = this.toolbarState;
548
+ this.imageMenuEl.updateComplete.then(() => this.positionOverlay(this.imageMenuEl, 'above'));
549
+ }
550
+ }
551
+ if (this.embedMenuEl) {
552
+ const showEmbed = this.hasFocus && !!this.toolbarState && this.toolbarState.embedSelected && !panelOpen;
553
+ this.embedMenuEl.open = showEmbed;
554
+ if (showEmbed) {
555
+ this.embedMenuEl.toolbarState = this.toolbarState;
556
+ this.embedMenuEl.updateComplete.then(() => this.positionOverlay(this.embedMenuEl, 'above'));
557
+ }
558
+ }
559
+ }
560
+ // -------------------------------------------------------- special chars
561
+ openSpecialChars() {
562
+ if (!this.view || !this.specialCharsEl || this.disabled || this.readonly)
563
+ return;
564
+ this.specialCharsEl.open = true;
565
+ this.specialCharsEl.updateComplete.then(() => this.positionOverlay(this.specialCharsEl, 'below'));
566
+ }
567
+ closeSpecialChars(refocus = true) {
568
+ if (this.specialCharsEl)
569
+ this.specialCharsEl.open = false;
570
+ if (refocus)
571
+ this.view?.focus();
572
+ }
573
+ insertSpecialChar(char) {
574
+ if (!this.view)
575
+ return;
576
+ this.view.dispatch(this.view.state.tr.insertText(char).scrollIntoView());
577
+ this.view.focus();
578
+ }
579
+ // ------------------------------------------------ templates & merge fields
580
+ openInsertMenu(mode) {
581
+ if (!this.view || !this.insertMenuEl || this.disabled || this.readonly)
582
+ return;
583
+ this.insertMenuMode = mode;
584
+ const menu = this.insertMenuEl;
585
+ if (mode === 'merge') {
586
+ menu.heading = 'Merge field';
587
+ menu.emptyText = 'No merge fields configured.';
588
+ menu.items = this.mergeFields.map(f => ({ label: f.label, description: `{{${f.id}}}` }));
589
+ }
590
+ else {
591
+ menu.heading = 'Template';
592
+ menu.emptyText = 'No templates configured.';
593
+ menu.items = this.templates.map(t => ({ label: t.label, description: t.description }));
594
+ }
595
+ menu.open = true;
596
+ if (this.bubbleMenuEl)
597
+ this.bubbleMenuEl.open = false;
598
+ menu.updateComplete.then(() => this.positionOverlay(menu, 'below'));
599
+ }
600
+ closeInsertMenu(refocus = true) {
601
+ if (this.insertMenuEl)
602
+ this.insertMenuEl.open = false;
603
+ this.insertMenuMode = null;
604
+ if (refocus)
605
+ this.view?.focus();
606
+ }
607
+ handleInsertSelect(index) {
608
+ const mode = this.insertMenuMode;
609
+ this.closeInsertMenu();
610
+ if (!this.view)
611
+ return;
612
+ if (mode === 'merge') {
613
+ const field = this.mergeFields[index];
614
+ if (field)
615
+ this.execCommand('insertMergeField', { id: field.id, label: field.label });
616
+ }
617
+ else if (mode === 'template') {
618
+ const template = this.templates[index];
619
+ if (template)
620
+ this.insertHTML(template.html);
621
+ }
622
+ }
623
+ /** Inserts a merge-field placeholder at the selection. */
624
+ insertMergeField(id, label) {
625
+ this.execCommand('insertMergeField', { id, label: label ?? null });
626
+ }
627
+ /** Changes the case of the selected text. */
628
+ changeCase(mode) {
629
+ this.execCommand('changeCase', { textCase: mode });
630
+ }
631
+ // ---------------------------------------------------------------- math
632
+ openMathPanel() {
633
+ if (!this.view || !this.mathPanelEl || this.disabled || this.readonly)
634
+ return;
635
+ this.mathPanelEl.reset();
636
+ this.mathPanelEl.open = true;
637
+ if (this.bubbleMenuEl)
638
+ this.bubbleMenuEl.open = false;
639
+ if (this.tableMenuEl)
640
+ this.tableMenuEl.open = false;
641
+ this.mathPanelEl.updateComplete.then(() => {
642
+ const anchor = this.toolbarButton('Insert math (LaTeX)');
643
+ if (anchor)
644
+ this.positionOverlayBelowAnchor(this.mathPanelEl, anchor);
645
+ else
646
+ this.positionOverlay(this.mathPanelEl, 'below');
647
+ this.mathPanelEl.focusInput();
648
+ });
649
+ }
650
+ closeMathPanel(refocus = true) {
651
+ if (this.mathPanelEl)
652
+ this.mathPanelEl.open = false;
653
+ if (refocus)
654
+ this.view?.focus();
655
+ }
656
+ openMarkdownPanel() {
657
+ if (!this.view || !this.markdownPanelEl || this.disabled || this.readonly)
658
+ return;
659
+ this.markdownPanelEl.reset();
660
+ this.markdownPanelEl.open = true;
661
+ if (this.bubbleMenuEl)
662
+ this.bubbleMenuEl.open = false;
663
+ if (this.tableMenuEl)
664
+ this.tableMenuEl.open = false;
665
+ this.markdownPanelEl.updateComplete.then(() => {
666
+ const anchor = this.toolbarButton('Insert from Markdown');
667
+ if (anchor)
668
+ this.positionOverlayBelowAnchor(this.markdownPanelEl, anchor);
669
+ else
670
+ this.positionOverlay(this.markdownPanelEl, 'below');
671
+ this.markdownPanelEl.focusInput();
672
+ });
673
+ }
674
+ closeMarkdownPanel(refocus = true) {
675
+ if (this.markdownPanelEl)
676
+ this.markdownPanelEl.open = false;
677
+ if (refocus)
678
+ this.view?.focus();
679
+ }
680
+ applyMath(latex, display) {
681
+ this.closeMathPanel();
682
+ this.execCommand('insertMath', { latex, mathDisplay: display });
683
+ }
684
+ /** Inserts a math formula (LaTeX) at the selection. */
685
+ insertMath(latex, display = 'inline') {
686
+ this.execCommand('insertMath', { latex, mathDisplay: display });
687
+ }
688
+ // ----------------------------------------------------------------- emoji
689
+ openEmojiPicker() {
690
+ if (!this.view || !this.emojiPickerEl || this.disabled || this.readonly)
691
+ return;
692
+ this.emojiPickerEl.open = true;
693
+ if (this.bubbleMenuEl)
694
+ this.bubbleMenuEl.open = false;
695
+ if (this.tableMenuEl)
696
+ this.tableMenuEl.open = false;
697
+ this.emojiPickerEl.updateComplete.then(() => {
698
+ this.positionOverlay(this.emojiPickerEl, 'below');
699
+ this.emojiPickerEl.focusInput();
700
+ });
701
+ }
702
+ closeEmojiPicker(refocus = true) {
703
+ if (this.emojiPickerEl)
704
+ this.emojiPickerEl.open = false;
705
+ if (refocus)
706
+ this.view?.focus();
707
+ }
708
+ // ---------------------------------------------------------------- embeds
709
+ openEmbedPanel() {
710
+ if (!this.view || !this.embedPanelEl || this.disabled || this.readonly)
711
+ return;
712
+ this.embedPanelEl.reset();
713
+ this.embedPanelEl.open = true;
714
+ if (this.bubbleMenuEl)
715
+ this.bubbleMenuEl.open = false;
716
+ if (this.tableMenuEl)
717
+ this.tableMenuEl.open = false;
718
+ this.embedPanelEl.updateComplete.then(() => {
719
+ // Anchor below the embed toolbar button when available.
720
+ const anchor = this.toolbarButton('Embed video (YouTube, Vimeo, Loom)');
721
+ if (anchor)
722
+ this.positionOverlayBelowAnchor(this.embedPanelEl, anchor);
723
+ else
724
+ this.positionOverlay(this.embedPanelEl, 'below');
725
+ this.embedPanelEl.focusInput();
726
+ });
727
+ }
728
+ closeEmbedPanel(refocus = true) {
729
+ if (this.embedPanelEl)
730
+ this.embedPanelEl.open = false;
731
+ if (refocus)
732
+ this.view?.focus();
733
+ }
734
+ // -------------------------------------------------------------- bookmarks
735
+ openBookmarkPanel() {
736
+ if (!this.view || !this.bookmarkPanelEl || this.disabled || this.readonly)
737
+ return;
738
+ this.bookmarkPanelEl.open = true;
739
+ if (this.bubbleMenuEl)
740
+ this.bubbleMenuEl.open = false;
741
+ if (this.tableMenuEl)
742
+ this.tableMenuEl.open = false;
743
+ this.bookmarkPanelEl.updateComplete.then(() => {
744
+ this.positionOverlay(this.bookmarkPanelEl, 'below');
745
+ this.bookmarkPanelEl.focusInput();
746
+ });
747
+ }
748
+ closeBookmarkPanel(refocus = true) {
749
+ if (this.bookmarkPanelEl)
750
+ this.bookmarkPanelEl.open = false;
751
+ if (refocus)
752
+ this.view?.focus();
753
+ }
754
+ // ---------------------------------------------------------------- images
755
+ openImagePanel() {
756
+ if (!this.view || !this.imagePanelEl || this.disabled || this.readonly)
757
+ return;
758
+ // Editing an already-selected image/figure pre-fills the fields (and shows
759
+ // Remove); otherwise open a blank insert panel.
760
+ const info = selectedImageInfo(this.view.state);
761
+ if (info) {
762
+ this.imagePanelEl.prefill({
763
+ src: info.node.attrs.src,
764
+ alt: info.node.attrs.alt ?? '',
765
+ caption: info.isFigure ? info.node.textContent : '',
766
+ });
767
+ }
768
+ else {
769
+ this.imagePanelEl.reset();
770
+ }
771
+ this.imagePanelEl.open = true;
772
+ if (this.bubbleMenuEl)
773
+ this.bubbleMenuEl.open = false;
774
+ if (this.tableMenuEl)
775
+ this.tableMenuEl.open = false;
776
+ this.imagePanelEl.updateComplete.then(() => {
777
+ // Anchor below the image toolbar button when it's available; otherwise
778
+ // fall back to positioning at the cursor.
779
+ const anchor = this.toolbarButton('Insert image');
780
+ if (anchor)
781
+ this.positionOverlayBelowAnchor(this.imagePanelEl, anchor);
782
+ else
783
+ this.positionOverlay(this.imagePanelEl, 'below');
784
+ this.imagePanelEl.focusInput();
785
+ });
786
+ }
787
+ closeImagePanel(refocus = true) {
788
+ if (this.imagePanelEl)
789
+ this.imagePanelEl.open = false;
790
+ if (refocus)
791
+ this.view?.focus();
792
+ }
793
+ insertImageAt(src, alt, pos, caption = null) {
794
+ if (!this.view)
795
+ return;
796
+ if (pos !== null) {
797
+ const $pos = this.view.state.doc.resolve(Math.min(pos, this.view.state.doc.content.size));
798
+ this.view.dispatch(this.view.state.tr.setSelection(TextSelection.near($pos)));
799
+ }
800
+ this.execCommand('insertImage', { src, alt, caption });
801
+ }
802
+ /**
803
+ * Routes an image file through the upload pipeline: a cancelable
804
+ * `nile-image-upload-request` first, then `uploadHandler` (with an
805
+ * uploading placeholder), then the base64 fallback.
806
+ */
807
+ async insertImageFile(file, pos = null, meta) {
808
+ if (!this.view)
809
+ return;
810
+ const metaAlt = meta?.alt ?? null;
811
+ const metaCaption = meta?.caption ?? null;
812
+ const request = this.emit('nile-image-upload-request', {
813
+ file,
814
+ insert: (src, alt) => this.insertImageAt(src, alt ?? metaAlt, pos, metaCaption),
815
+ }, true, true, true);
816
+ if (request.defaultPrevented)
817
+ return;
818
+ if (this.uploadHandler) {
819
+ uploadCounter += 1;
820
+ const id = `nile-upload-${uploadCounter}`;
821
+ const at = pos ?? this.view.state.selection.from;
822
+ this.view.dispatch(this.view.state.tr.setMeta(imageUploadKey, { add: { id, pos: at } }));
823
+ try {
824
+ const { src, alt } = await this.uploadHandler(file);
825
+ const mappedPos = this.view ? findUploadPos(this.view.state, id) : null;
826
+ this.view?.dispatch(this.view.state.tr.setMeta(imageUploadKey, { remove: { id } }));
827
+ this.insertImageAt(src, metaAlt ?? alt ?? null, mappedPos, metaCaption);
828
+ }
829
+ catch (error) {
830
+ this.view?.dispatch(this.view.state.tr.setMeta(imageUploadKey, { remove: { id } }));
831
+ this.emit('nile-image-upload-error', { file, error });
832
+ }
833
+ return;
834
+ }
835
+ if (this.allowBase64Images) {
836
+ const src = await readFileAsDataURL(file);
837
+ this.insertImageAt(src, metaAlt, pos, metaCaption);
838
+ }
839
+ }
840
+ // ---------------------------------------------------- mentions & slash
841
+ dismissKeyFor(source, match) {
842
+ return `${source}:${match.trigger}:${match.from}`;
843
+ }
844
+ handleSuggestKey(event, source) {
845
+ if (this.activeSuggestSource !== source || !this.suggestListEl?.open)
846
+ return false;
847
+ switch (event.key) {
848
+ case 'ArrowDown':
849
+ this.suggestListEl.next();
850
+ return true;
851
+ case 'ArrowUp':
852
+ this.suggestListEl.prev();
853
+ return true;
854
+ case 'Enter':
855
+ case 'Tab':
856
+ return this.suggestListEl.selectCurrent();
857
+ case 'Escape': {
858
+ const match = source === 'mention'
859
+ ? this.mentionMatch
860
+ : source === 'slash'
861
+ ? this.slashMatch
862
+ : this.emojiMatch;
863
+ if (match)
864
+ this.suggestDismissed = this.dismissKeyFor(source, match);
865
+ this.closeSuggestList();
866
+ return true;
867
+ }
868
+ default:
869
+ return false;
870
+ }
871
+ }
872
+ closeSuggestList() {
873
+ if (this.suggestListEl)
874
+ this.suggestListEl.open = false;
875
+ this.activeSuggestSource = null;
876
+ }
877
+ updateSuggestList() {
878
+ if (!this.suggestListEl || !this.view)
879
+ return;
880
+ const mention = this.mentionMatch?.active ? this.mentionMatch : null;
881
+ const slash = !this.noSlashCommands && this.slashMatch?.active ? this.slashMatch : null;
882
+ const emoji = !this.noEmoji && this.emojiMatch?.active ? this.emojiMatch : null;
883
+ const source = mention
884
+ ? 'mention'
885
+ : slash
886
+ ? 'slash'
887
+ : emoji
888
+ ? 'emoji'
889
+ : null;
890
+ const match = mention ?? slash ?? emoji;
891
+ if (!source || !match || this.disabled || this.readonly) {
892
+ this.closeSuggestList();
893
+ return;
894
+ }
895
+ if (this.suggestDismissed === this.dismissKeyFor(source, match))
896
+ return;
897
+ this.suggestDismissed = null;
898
+ if (source === 'mention') {
899
+ const all = this.mentions[match.trigger] ?? [];
900
+ const query = match.query.toLowerCase();
901
+ this.suggestListEl.items = all.filter(item => item.label.toLowerCase().includes(query));
902
+ // Async override: listeners may provide items for this exact query.
903
+ const token = `${match.trigger} ${match.query}`;
904
+ this.mentionQueryToken = token;
905
+ this.emit('nile-mention-query', {
906
+ trigger: match.trigger,
907
+ query: match.query,
908
+ provide: (items) => {
909
+ if (this.mentionQueryToken !== token || !Array.isArray(items))
910
+ return;
911
+ this.suggestListEl.items = items
912
+ .filter(i => i && typeof i.key === 'string' && typeof i.label === 'string')
913
+ .map(i => ({ key: i.key, label: i.label }));
914
+ },
915
+ });
916
+ }
917
+ else if (source === 'slash') {
918
+ const query = match.query.toLowerCase();
919
+ const snippetItems = this.snippets.map(s => ({
920
+ key: `snippet:${s.key}`,
921
+ label: s.label,
922
+ hint: 'snippet',
923
+ }));
924
+ this.suggestListEl.items = [...SLASH_ITEMS, ...snippetItems].filter(item => item.label.toLowerCase().includes(query));
925
+ }
926
+ else {
927
+ const query = match.query.toLowerCase();
928
+ this.suggestListEl.items = EMOJI.filter(e => e.name.includes(query))
929
+ .slice(0, 12)
930
+ .map(e => ({ key: e.name, label: `${e.char} ${e.name}` }));
931
+ if (this.suggestListEl.items.length === 0) {
932
+ this.closeSuggestList();
933
+ return;
934
+ }
935
+ }
936
+ this.activeSuggestSource = source;
937
+ this.suggestListEl.resetActive();
938
+ this.suggestListEl.open = true;
939
+ this.suggestListEl.updateComplete.then(() => this.positionOverlay(this.suggestListEl, 'below', match.from));
940
+ }
941
+ handleSuggestSelect(item) {
942
+ if (!this.view)
943
+ return;
944
+ if (this.activeSuggestSource === 'mention' && this.mentionMatch?.active) {
945
+ const match = this.mentionMatch;
946
+ const { state } = this.view;
947
+ const mention = state.schema.nodes.mention.create({
948
+ trigger: match.trigger,
949
+ key: item.key,
950
+ label: item.label,
951
+ });
952
+ this.view.dispatch(state.tr.replaceWith(match.from, match.to, [mention, state.schema.text(' ')]));
953
+ this.view.focus();
954
+ this.emit('nile-mention-select', {
955
+ trigger: match.trigger,
956
+ key: item.key,
957
+ label: item.label,
958
+ });
959
+ }
960
+ else if (this.activeSuggestSource === 'slash' && this.slashMatch?.active) {
961
+ const match = this.slashMatch;
962
+ this.view.dispatch(this.view.state.tr.delete(match.from, match.to));
963
+ this.closeSuggestList();
964
+ if (item.key.startsWith('snippet:')) {
965
+ const snippet = this.snippets.find(s => `snippet:${s.key}` === item.key);
966
+ if (snippet)
967
+ this.insertHTML(snippet.html);
968
+ return;
969
+ }
970
+ const slashItem = SLASH_ITEMS.find(s => s.key === item.key);
971
+ if (!slashItem)
972
+ return;
973
+ if (slashItem.command === 'openImage')
974
+ this.openImagePanel();
975
+ else if (slashItem.command === 'openEmbed')
976
+ this.openEmbedPanel();
977
+ else if (slashItem.command === 'openBookmark')
978
+ this.openBookmarkPanel();
979
+ else
980
+ this.execCommand(slashItem.command, slashItem.attrs);
981
+ }
982
+ else if (this.activeSuggestSource === 'emoji' && this.emojiMatch?.active) {
983
+ const match = this.emojiMatch;
984
+ const emoji = EMOJI.find(e => e.name === item.key);
985
+ if (emoji) {
986
+ this.view.dispatch(this.view.state.tr.insertText(emoji.char, match.from, match.to));
987
+ this.view.focus();
988
+ }
989
+ }
990
+ this.closeSuggestList();
991
+ }
992
+ // ---------------------------------------------------------- find & replace
993
+ /** Opens the find & replace bar (also bound to Mod-f inside the editor). */
994
+ openFindBar() {
995
+ if (!this.findBarEl || this.sourceMode)
996
+ return;
997
+ this.findBarEl.open = true;
998
+ const find = this.view ? getFindState(this.view.state) : null;
999
+ this.findBarEl.matchCount = find?.matches.length ?? 0;
1000
+ this.findBarEl.activeIndex = find?.activeIndex ?? 0;
1001
+ this.findBarEl.updateComplete.then(() => {
1002
+ // Anchor below the Find & replace toolbar button when present; otherwise
1003
+ // fall back to the CSS-pinned top-right position.
1004
+ const anchor = this.toolbarButton('Find & replace (Ctrl+F)');
1005
+ if (anchor)
1006
+ this.positionOverlayBelowAnchor(this.findBarEl, anchor);
1007
+ this.findBarEl.focusInput();
1008
+ });
1009
+ }
1010
+ closeFindBar() {
1011
+ if (this.view)
1012
+ this.view.dispatch(clearFindTr(this.view.state));
1013
+ if (this.findBarEl)
1014
+ this.findBarEl.open = false;
1015
+ this.view?.focus();
1016
+ }
1017
+ find(query) {
1018
+ if (!this.view)
1019
+ return 0;
1020
+ this.view.dispatch(searchTr(this.view.state, query));
1021
+ this.scrollToActiveMatch();
1022
+ return getFindState(this.view.state).matches.length;
1023
+ }
1024
+ findNext() {
1025
+ if (!this.view)
1026
+ return;
1027
+ this.view.dispatch(findStepTr(this.view.state, 'next'));
1028
+ this.scrollToActiveMatch();
1029
+ }
1030
+ findPrev() {
1031
+ if (!this.view)
1032
+ return;
1033
+ this.view.dispatch(findStepTr(this.view.state, 'prev'));
1034
+ this.scrollToActiveMatch();
1035
+ }
1036
+ replace(replacement) {
1037
+ if (!this.view)
1038
+ return;
1039
+ const tr = replaceActiveTr(this.view.state, replacement);
1040
+ if (tr)
1041
+ this.view.dispatch(tr);
1042
+ }
1043
+ replaceAll(replacement) {
1044
+ if (!this.view)
1045
+ return;
1046
+ const tr = replaceAllTr(this.view.state, replacement);
1047
+ if (tr)
1048
+ this.view.dispatch(tr);
1049
+ }
1050
+ scrollToActiveMatch() {
1051
+ if (!this.view)
1052
+ return;
1053
+ const find = getFindState(this.view.state);
1054
+ const match = find.matches[find.activeIndex];
1055
+ if (!match)
1056
+ return;
1057
+ this.view.dispatch(this.view.state.tr
1058
+ .setSelection(TextSelection.create(this.view.state.doc, match.from, match.to))
1059
+ .scrollIntoView());
1060
+ }
1061
+ // -------------------------------------------------------------- source view
1062
+ /** Toggles raw-HTML source editing. Content re-sanitizes on the way back. */
1063
+ toggleSourceView() {
1064
+ if (this.sourceMode) {
1065
+ this.applySourceView(this.sourceViewEl?.textarea?.value ?? null);
1066
+ }
1067
+ else {
1068
+ this.sourceMode = true;
1069
+ this.updateComplete.then(() => {
1070
+ if (this.sourceViewEl) {
1071
+ this.sourceViewEl.value = this.getHTML();
1072
+ this.sourceViewEl.focusInput();
1073
+ }
1074
+ });
1075
+ }
1076
+ }
1077
+ applySourceView(htmlContent) {
1078
+ this.sourceMode = false;
1079
+ if (htmlContent !== null)
1080
+ this.setContent(htmlContent, true);
1081
+ this.updateComplete.then(() => this.view?.focus());
1082
+ }
1083
+ /** Word and character counts for the current document. */
1084
+ getWordCount() {
1085
+ return this.view ? countWords(this.view.state.doc) : { words: 0, characters: 0 };
1086
+ }
1087
+ /**
1088
+ * Arms the format painter (copies the formatting at the selection) or, if
1089
+ * already armed, disarms it. Once armed, the next non-empty selection the
1090
+ * user makes receives the copied formatting.
1091
+ */
1092
+ toggleFormatPainter() {
1093
+ if (!this.view)
1094
+ return;
1095
+ const tr = isFormatPainterArmed(this.view.state)
1096
+ ? disarmFormatPainter(this.view.state)
1097
+ : armFormatPainter(this.view.state);
1098
+ if (tr)
1099
+ this.view.dispatch(tr);
1100
+ this.view.focus();
1101
+ }
1102
+ // ---------------------------------------------------------- export & import
1103
+ /** The live, styled content element used as the source for exports. */
1104
+ get contentRoot() {
1105
+ return this.view?.dom ?? null;
1106
+ }
1107
+ /** Sanitizes `this.name` (or a fallback) into a safe download basename. */
1108
+ exportFilename(extension) {
1109
+ const base = (this.name || 'document').replace(/[^\w.-]+/g, '-').replace(/^-+|-+$/g, '') || 'document';
1110
+ return `${base}.${extension}`;
1111
+ }
1112
+ downloadBlob(content, mime, filename) {
1113
+ const blob = new Blob([content], { type: mime });
1114
+ const url = URL.createObjectURL(blob);
1115
+ const anchor = document.createElement('a');
1116
+ anchor.href = url;
1117
+ anchor.download = filename;
1118
+ anchor.style.display = 'none';
1119
+ document.body.appendChild(anchor);
1120
+ anchor.click();
1121
+ anchor.remove();
1122
+ // Revoke after the click so the download has a chance to start.
1123
+ setTimeout(() => URL.revokeObjectURL(url), 0);
1124
+ }
1125
+ /**
1126
+ * The document as HTML with every style folded into inline `style`
1127
+ * attributes — the form email clients (which strip `<style>` blocks and
1128
+ * classes) can render. Returns the string; does not download.
1129
+ */
1130
+ getInlineStyledHTML() {
1131
+ const root = this.contentRoot;
1132
+ if (!this.view || !root || isDocEmpty(this.view.state.doc))
1133
+ return '';
1134
+ return toInlineStyledHtml(root);
1135
+ }
1136
+ /** Downloads the document as an inline-styled, email-ready `.html` file. */
1137
+ exportInlineHtml(filename) {
1138
+ const body = this.getInlineStyledHTML();
1139
+ if (!body)
1140
+ return;
1141
+ const doc = `<!DOCTYPE html>\n<html><head><meta charset="utf-8"></head><body>${body}</body></html>`;
1142
+ this.downloadBlob(doc, 'text/html;charset=utf-8', filename ?? this.exportFilename('html'));
1143
+ }
1144
+ /** Downloads the document as a Word-openable `.doc` file. */
1145
+ exportToWord(filename) {
1146
+ const body = this.getInlineStyledHTML();
1147
+ if (!body)
1148
+ return;
1149
+ const doc = buildWordDocument(body, this.name || 'Document');
1150
+ this.downloadBlob(doc, 'application/msword', filename ?? this.exportFilename('doc'));
1151
+ }
1152
+ /**
1153
+ * Opens the browser print dialog with a self-contained, print-tuned copy of
1154
+ * the document ("Save as PDF"). Honours page breaks, embedded images and the
1155
+ * base font; optional running header/footer text.
1156
+ */
1157
+ exportToPdf(options = {}) {
1158
+ const body = this.getInlineStyledHTML();
1159
+ if (!body)
1160
+ return;
1161
+ const source = buildPrintDocument(body, { title: this.name || 'Document', ...options });
1162
+ const frame = document.createElement('iframe');
1163
+ frame.setAttribute('aria-hidden', 'true');
1164
+ Object.assign(frame.style, {
1165
+ position: 'fixed',
1166
+ right: '0',
1167
+ bottom: '0',
1168
+ width: '0',
1169
+ height: '0',
1170
+ border: '0',
1171
+ });
1172
+ document.body.appendChild(frame);
1173
+ const cleanup = () => {
1174
+ // Delay removal so the browser's print job keeps its document alive.
1175
+ setTimeout(() => frame.remove(), 1000);
1176
+ };
1177
+ frame.onload = () => {
1178
+ const win = frame.contentWindow;
1179
+ if (!win) {
1180
+ cleanup();
1181
+ return;
1182
+ }
1183
+ win.focus();
1184
+ win.addEventListener('afterprint', cleanup, { once: true });
1185
+ win.print();
1186
+ };
1187
+ const fdoc = frame.contentDocument;
1188
+ if (!fdoc) {
1189
+ frame.remove();
1190
+ return;
1191
+ }
1192
+ fdoc.open();
1193
+ fdoc.write(source);
1194
+ fdoc.close();
1195
+ }
1196
+ /**
1197
+ * Imports Word / Google-Docs HTML: normalizes the proprietary markup to
1198
+ * clean semantic HTML, then replaces the document. Fidelity note: comments
1199
+ * and track-changes are not preserved — the editor has no model for them,
1200
+ * and binary `.docx`/`.doc` files are not parsed (export as "Web Page" /
1201
+ * HTML first, or paste directly, which is handled automatically).
1202
+ */
1203
+ importFromWord(html, emitChange = true) {
1204
+ if (!html)
1205
+ return;
1206
+ const cleaned = isOfficeHtml(html) ? cleanOfficeHtml(html) : html;
1207
+ this.setContent(cleaned, emitChange);
1208
+ }
1209
+ /** Opens a file picker for an HTML export and imports it (toolbar action). */
1210
+ importFromWordFile() {
1211
+ const input = document.createElement('input');
1212
+ input.type = 'file';
1213
+ input.accept = '.htm,.html,text/html';
1214
+ input.style.display = 'none';
1215
+ input.addEventListener('change', () => {
1216
+ const file = input.files?.[0];
1217
+ input.remove();
1218
+ if (!file)
1219
+ return;
1220
+ file.text().then(text => this.importFromWord(text));
1221
+ });
1222
+ document.body.appendChild(input);
1223
+ input.click();
1224
+ }
1225
+ // -------------------------------------------------------- shortcut help
1226
+ /** Opens the keyboard shortcut reference (also bound to Mod-/). */
1227
+ openShortcutHelp() {
1228
+ if (!this.shortcutHelpEl)
1229
+ return;
1230
+ this.shortcutHelpEl.open = true;
1231
+ this.shortcutHelpEl.focusPanel();
1232
+ }
1233
+ closeShortcutHelp() {
1234
+ if (this.shortcutHelpEl)
1235
+ this.shortcutHelpEl.open = false;
1236
+ this.view?.focus();
1237
+ }
1238
+ // ------------------------------------------------------------ fullscreen
1239
+ /** Toggles distraction-free fullscreen mode. Esc exits. */
1240
+ toggleFullscreen() {
1241
+ this.fullscreen = !this.fullscreen;
1242
+ this.view?.focus();
1243
+ }
1244
+ openLinkEditor() {
1245
+ if (!this.view || !this.linkEditorEl || this.disabled || this.readonly)
1246
+ return;
1247
+ const ts = this.toolbarState ?? computeToolbarState(this.view.state);
1248
+ this.linkEditorEl.href = ts.linkHref ?? '';
1249
+ this.linkEditorEl.text = '';
1250
+ this.linkEditorEl.allowText = ts.selectionEmpty && !ts.linkActive;
1251
+ this.linkEditorEl.canRemove = ts.linkActive;
1252
+ this.linkEditorEl.open = true;
1253
+ if (this.bubbleMenuEl)
1254
+ this.bubbleMenuEl.open = false;
1255
+ this.linkEditorEl.updateComplete.then(() => {
1256
+ this.positionOverlay(this.linkEditorEl, 'below');
1257
+ this.linkEditorEl.focusInput();
1258
+ });
1259
+ }
1260
+ closeLinkEditor(refocus = true) {
1261
+ if (this.linkEditorEl)
1262
+ this.linkEditorEl.open = false;
1263
+ if (refocus)
1264
+ this.view?.focus();
1265
+ }
1266
+ applyLink(href, text) {
1267
+ if (!this.view)
1268
+ return;
1269
+ const { state } = this.view;
1270
+ const linkType = state.schema.marks.link;
1271
+ if (state.selection.empty && !getMarkRange(state.selection.$from, linkType)) {
1272
+ const label = text || href;
1273
+ const node = state.schema.text(label, [linkType.create({ href })]);
1274
+ this.view.dispatch(state.tr.replaceSelectionWith(node, false).scrollIntoView());
1275
+ }
1276
+ else {
1277
+ this.execCommand('link', { href });
1278
+ }
1279
+ this.closeLinkEditor();
1280
+ }
1281
+ // ---------------------------------------------------------------- public
1282
+ /** The underlying ProseMirror EditorView. Advanced escape hatch. */
1283
+ get pmView() {
1284
+ return this.view;
1285
+ }
1286
+ /** Document as ProseMirror JSON. */
1287
+ get json() {
1288
+ return this.view ? docToJson(this.view.state.doc) : null;
1289
+ }
1290
+ set json(value) {
1291
+ if (value)
1292
+ this.setContent(value);
1293
+ }
1294
+ focus() {
1295
+ this.view?.focus();
1296
+ }
1297
+ blur() {
1298
+ this.view?.dom?.blur();
1299
+ }
1300
+ getHTML() {
1301
+ return this.view && !isDocEmpty(this.view.state.doc)
1302
+ ? docToHtml(this.view.state.doc)
1303
+ : '';
1304
+ }
1305
+ getJSON() {
1306
+ return this.json;
1307
+ }
1308
+ getText() {
1309
+ return this.view ? docToText(this.view.state.doc) : '';
1310
+ }
1311
+ /**
1312
+ * Document as Markdown. Best-effort: structural nodes and Markdown-native
1313
+ * marks are converted; color/font marks are dropped to plain text. Use
1314
+ * getHTML() when a loss-free representation is required.
1315
+ */
1316
+ getMarkdown() {
1317
+ return this.view && !isDocEmpty(this.view.state.doc)
1318
+ ? docToMarkdown(this.view.state.doc)
1319
+ : '';
1320
+ }
1321
+ /**
1322
+ * Replaces the document with parsed Markdown. Covers the same subset as
1323
+ * getMarkdown(): headings, lists, code fences, tables, quotes and the
1324
+ * inline marks. The parsed HTML flows through the schema and sanitizer.
1325
+ */
1326
+ setMarkdown(markdown, emitChange = false) {
1327
+ this.setContent(markdownToHtml(markdown), emitChange);
1328
+ }
1329
+ /** Parses and inserts Markdown at the current selection. */
1330
+ insertMarkdown(markdown) {
1331
+ this.insertHTML(markdownToHtml(markdown));
1332
+ }
1333
+ /** Replaces the document. Accepts an HTML string or ProseMirror JSON. */
1334
+ setContent(content, emitChange = false) {
1335
+ if (!this.view) {
1336
+ if (typeof content === 'string')
1337
+ this.value = content;
1338
+ return;
1339
+ }
1340
+ let doc = typeof content === 'string'
1341
+ ? htmlToDoc(defaultSchema, content, this.sanitize)
1342
+ : jsonToDoc(defaultSchema, content);
1343
+ if (this.singleLine)
1344
+ doc = flattenToSingleParagraph(doc);
1345
+ this.view.updateState(this.buildState(doc));
1346
+ this.toolbarState = this.snapshotToolbar(this.view.state);
1347
+ this.syncingValue = true;
1348
+ this.value = isDocEmpty(doc) ? '' : docToHtml(doc);
1349
+ this.syncFormValue(doc);
1350
+ if (emitChange) {
1351
+ this.emit('nile-input', this.buildChangeDetail(doc));
1352
+ this.emit('nile-change', this.buildChangeDetail(doc));
1353
+ }
1354
+ }
1355
+ clear() {
1356
+ this.setContent('');
1357
+ }
1358
+ /** Parses and inserts HTML at the current selection. */
1359
+ insertHTML(htmlContent) {
1360
+ if (!this.view)
1361
+ return;
1362
+ const slice = htmlToSlice(defaultSchema, htmlContent, this.sanitize);
1363
+ this.view.dispatch(this.view.state.tr.replaceSelection(slice).scrollIntoView());
1364
+ this.view.focus();
1365
+ }
1366
+ /** Runs a named editor command. Returns whether the command applied. */
1367
+ execCommand(name, attrs) {
1368
+ if (!this.view)
1369
+ return false;
1370
+ const command = getCommand(this.view.state, name, attrs);
1371
+ if (!command)
1372
+ return false;
1373
+ const applied = command(this.view.state, this.view.dispatch, this.view);
1374
+ this.view.focus();
1375
+ return applied;
1376
+ }
1377
+ /** True when the given mark/block command is active at the selection. */
1378
+ isCommandActive(name) {
1379
+ const ts = this.toolbarState;
1380
+ if (!ts)
1381
+ return false;
1382
+ switch (name) {
1383
+ case 'bold':
1384
+ return ts.bold;
1385
+ case 'italic':
1386
+ return ts.italic;
1387
+ case 'underline':
1388
+ return ts.underline;
1389
+ case 'strike':
1390
+ return ts.strike;
1391
+ case 'code':
1392
+ return ts.code;
1393
+ case 'link':
1394
+ return ts.linkActive;
1395
+ case 'bulletList':
1396
+ return ts.inBulletList;
1397
+ case 'orderedList':
1398
+ return ts.inOrderedList;
1399
+ case 'blockquote':
1400
+ return ts.inBlockquote;
1401
+ default:
1402
+ return false;
1403
+ }
1404
+ }
1405
+ // ---------------------------------------------------------------- render
1406
+ handleCommand(event) {
1407
+ event.stopPropagation();
1408
+ const { name, attrs } = event.detail;
1409
+ if (name === 'openLink') {
1410
+ this.openLinkEditor();
1411
+ return;
1412
+ }
1413
+ if (name === 'openImage') {
1414
+ this.openImagePanel();
1415
+ return;
1416
+ }
1417
+ if (name === 'openEmbed') {
1418
+ this.openEmbedPanel();
1419
+ return;
1420
+ }
1421
+ if (name === 'openBookmark') {
1422
+ this.openBookmarkPanel();
1423
+ return;
1424
+ }
1425
+ if (name === 'toggleShowBlocks') {
1426
+ this.showBlocks = !this.showBlocks;
1427
+ this.view?.focus();
1428
+ return;
1429
+ }
1430
+ if (name === 'toggleSource') {
1431
+ this.toggleSourceView();
1432
+ return;
1433
+ }
1434
+ if (name === 'togglePastePlainText') {
1435
+ this.pasteAsPlainText = !this.pasteAsPlainText;
1436
+ this.view?.focus();
1437
+ return;
1438
+ }
1439
+ if (name === 'toggleFullscreen') {
1440
+ this.toggleFullscreen();
1441
+ return;
1442
+ }
1443
+ if (name === 'openShortcutHelp') {
1444
+ this.openShortcutHelp();
1445
+ return;
1446
+ }
1447
+ if (name === 'openSpecialChars') {
1448
+ this.openSpecialChars();
1449
+ return;
1450
+ }
1451
+ if (name === 'openEmoji') {
1452
+ this.openEmojiPicker();
1453
+ return;
1454
+ }
1455
+ if (name === 'openFind') {
1456
+ this.openFindBar();
1457
+ return;
1458
+ }
1459
+ if (name === 'exportPdf') {
1460
+ this.exportToPdf();
1461
+ return;
1462
+ }
1463
+ if (name === 'exportWord') {
1464
+ this.exportToWord();
1465
+ return;
1466
+ }
1467
+ if (name === 'exportInlineHtml') {
1468
+ this.exportInlineHtml();
1469
+ return;
1470
+ }
1471
+ if (name === 'importWord') {
1472
+ this.importFromWordFile();
1473
+ return;
1474
+ }
1475
+ if (name === 'toggleFormatPainter') {
1476
+ this.toggleFormatPainter();
1477
+ return;
1478
+ }
1479
+ if (name === 'openMergeField') {
1480
+ this.openInsertMenu('merge');
1481
+ return;
1482
+ }
1483
+ if (name === 'openTemplate') {
1484
+ this.openInsertMenu('template');
1485
+ return;
1486
+ }
1487
+ if (name === 'openMarkdown') {
1488
+ this.openMarkdownPanel();
1489
+ return;
1490
+ }
1491
+ if (name === 'openMath') {
1492
+ this.openMathPanel();
1493
+ return;
1494
+ }
1495
+ this.execCommand(name, attrs);
1496
+ }
1497
+ handleBaseKeydown(event) {
1498
+ if (event.key !== 'Escape' || event.defaultPrevented)
1499
+ return;
1500
+ // The special-chars panel never takes focus, so its Esc lands here.
1501
+ if (this.specialCharsEl?.open) {
1502
+ this.closeSpecialChars();
1503
+ return;
1504
+ }
1505
+ // Esc exits fullscreen — but only when nothing else claimed it (panels
1506
+ // stopPropagation, ProseMirror handlers preventDefault).
1507
+ if (!this.fullscreen)
1508
+ return;
1509
+ const panelOpen = this.linkEditorEl?.open ||
1510
+ this.imagePanelEl?.open ||
1511
+ this.embedPanelEl?.open ||
1512
+ this.bookmarkPanelEl?.open ||
1513
+ this.emojiPickerEl?.open ||
1514
+ this.findBarEl?.open ||
1515
+ this.shortcutHelpEl?.open ||
1516
+ this.suggestListEl?.open;
1517
+ if (!panelOpen)
1518
+ this.fullscreen = false;
1519
+ }
1520
+ handleChromeFocusOut(event) {
1521
+ // Focus left the widget entirely (e.g. clicked the page) while it was
1522
+ // parked on chrome like the language picker: close the overlays.
1523
+ const related = event.relatedTarget;
1524
+ if (related && (this.shadowRoot?.contains(related) || this.contains(related)))
1525
+ return;
1526
+ if (this.view?.hasFocus())
1527
+ return;
1528
+ this.hasFocus = false;
1529
+ this.updateOverlays();
1530
+ }
1531
+ /** The floating insert panels that should dismiss on outside interaction. */
1532
+ get floatingPanels() {
1533
+ return [
1534
+ this.linkEditorEl,
1535
+ this.imagePanelEl,
1536
+ this.embedPanelEl,
1537
+ this.bookmarkPanelEl,
1538
+ this.specialCharsEl,
1539
+ this.emojiPickerEl,
1540
+ this.insertMenuEl,
1541
+ this.mathPanelEl,
1542
+ this.markdownPanelEl,
1543
+ ];
1544
+ }
1545
+ /** Closes every open insert panel. `refocus` returns focus to the editor. */
1546
+ closeAllPanels(refocus = true) {
1547
+ if (this.linkEditorEl?.open)
1548
+ this.closeLinkEditor(refocus);
1549
+ if (this.imagePanelEl?.open)
1550
+ this.closeImagePanel(refocus);
1551
+ if (this.embedPanelEl?.open)
1552
+ this.closeEmbedPanel(refocus);
1553
+ if (this.bookmarkPanelEl?.open)
1554
+ this.closeBookmarkPanel(refocus);
1555
+ if (this.specialCharsEl?.open)
1556
+ this.closeSpecialChars(refocus);
1557
+ if (this.emojiPickerEl?.open)
1558
+ this.closeEmojiPicker(refocus);
1559
+ if (this.insertMenuEl?.open)
1560
+ this.closeInsertMenu(refocus);
1561
+ if (this.mathPanelEl?.open)
1562
+ this.closeMathPanel(refocus);
1563
+ if (this.markdownPanelEl?.open)
1564
+ this.closeMarkdownPanel(refocus);
1565
+ }
1566
+ render() {
1567
+ const showToolbar = this.toolbar !== 'none' && !this.readonly && !this.sourceMode;
1568
+ return html `
1569
+ <div
1570
+ class="nile-wysiwyg ${this.showBlocks ? 'nile-wysiwyg--show-blocks' : ''}"
1571
+ part="base"
1572
+ @wysiwyg-command=${this.handleCommand}
1573
+ @focusout=${this.handleChromeFocusOut}
1574
+ @keydown=${this.handleBaseKeydown}
1575
+ >
1576
+ ${showToolbar
1577
+ ? html `
1578
+ <nile-wysiwyg-toolbar
1579
+ part="toolbar"
1580
+ exportparts="button, select, color"
1581
+ config=${this.toolbar}
1582
+ ?disabled=${this.disabled}
1583
+ .toolbarState=${this.toolbarState}
1584
+ ></nile-wysiwyg-toolbar>
1585
+ `
1586
+ : nothing}
1587
+ ${this.sourceMode
1588
+ ? html `
1589
+ <nile-wysiwyg-source-view
1590
+ @wysiwyg-source-apply=${(e) => {
1591
+ e.stopPropagation();
1592
+ this.applySourceView(e.detail.html);
1593
+ }}
1594
+ @wysiwyg-source-cancel=${(e) => {
1595
+ e.stopPropagation();
1596
+ this.applySourceView(null);
1597
+ }}
1598
+ ></nile-wysiwyg-source-view>
1599
+ `
1600
+ : nothing}
1601
+ <div
1602
+ class="nile-wysiwyg__surface"
1603
+ part="surface"
1604
+ style=${this.sourceMode ? 'display: none' : ''}
1605
+ ></div>
1606
+ ${(this.maxLength > 0 || this.showCounter) && !this.sourceMode
1607
+ ? html `
1608
+ <div
1609
+ class="nile-wysiwyg__counter ${this.maxLength > 0 &&
1610
+ this.counter.characters >= this.maxLength
1611
+ ? 'over'
1612
+ : ''}"
1613
+ part="counter"
1614
+ >
1615
+ ${this.maxLength > 0
1616
+ ? `${this.counter.characters} / ${this.maxLength}`
1617
+ : `${this.counter.words} words · ${this.counter.characters} characters`}
1618
+ </div>
1619
+ `
1620
+ : nothing}
1621
+ <nile-wysiwyg-shortcut-help
1622
+ @wysiwyg-shortcut-help-close=${(e) => {
1623
+ e.stopPropagation();
1624
+ this.closeShortcutHelp();
1625
+ }}
1626
+ ></nile-wysiwyg-shortcut-help>
1627
+ <nile-wysiwyg-bubble-menu></nile-wysiwyg-bubble-menu>
1628
+ <nile-wysiwyg-table-menu></nile-wysiwyg-table-menu>
1629
+ <nile-wysiwyg-code-menu></nile-wysiwyg-code-menu>
1630
+ <nile-wysiwyg-image-menu></nile-wysiwyg-image-menu>
1631
+ <nile-wysiwyg-embed-menu></nile-wysiwyg-embed-menu>
1632
+ <nile-wysiwyg-special-chars
1633
+ @wysiwyg-char-select=${(e) => {
1634
+ e.stopPropagation();
1635
+ this.insertSpecialChar(e.detail.char);
1636
+ }}
1637
+ @wysiwyg-char-cancel=${(e) => {
1638
+ e.stopPropagation();
1639
+ this.closeSpecialChars();
1640
+ }}
1641
+ ></nile-wysiwyg-special-chars>
1642
+ <nile-wysiwyg-emoji-picker
1643
+ @wysiwyg-emoji-select=${(e) => {
1644
+ e.stopPropagation();
1645
+ this.closeEmojiPicker();
1646
+ this.insertSpecialChar(e.detail.char);
1647
+ }}
1648
+ @wysiwyg-emoji-cancel=${(e) => {
1649
+ e.stopPropagation();
1650
+ this.closeEmojiPicker();
1651
+ }}
1652
+ ></nile-wysiwyg-emoji-picker>
1653
+ <nile-wysiwyg-insert-menu
1654
+ @wysiwyg-insert-select=${(e) => {
1655
+ e.stopPropagation();
1656
+ this.handleInsertSelect(e.detail.index);
1657
+ }}
1658
+ @wysiwyg-insert-cancel=${(e) => {
1659
+ e.stopPropagation();
1660
+ this.closeInsertMenu();
1661
+ }}
1662
+ ></nile-wysiwyg-insert-menu>
1663
+ <nile-wysiwyg-math-panel
1664
+ @wysiwyg-math-apply=${(e) => {
1665
+ e.stopPropagation();
1666
+ this.applyMath(e.detail.latex, e.detail.display);
1667
+ }}
1668
+ @wysiwyg-math-cancel=${(e) => {
1669
+ e.stopPropagation();
1670
+ this.closeMathPanel();
1671
+ }}
1672
+ ></nile-wysiwyg-math-panel>
1673
+ <nile-wysiwyg-markdown-panel
1674
+ @wysiwyg-markdown-apply=${(e) => {
1675
+ e.stopPropagation();
1676
+ this.closeMarkdownPanel();
1677
+ this.insertMarkdown(e.detail.markdown);
1678
+ }}
1679
+ @wysiwyg-markdown-cancel=${(e) => {
1680
+ e.stopPropagation();
1681
+ this.closeMarkdownPanel();
1682
+ }}
1683
+ ></nile-wysiwyg-markdown-panel>
1684
+ <nile-wysiwyg-embed-panel
1685
+ @wysiwyg-embed-apply=${(e) => {
1686
+ e.stopPropagation();
1687
+ this.closeEmbedPanel();
1688
+ this.execCommand('insertEmbed', {
1689
+ src: e.detail.src,
1690
+ provider: e.detail.provider,
1691
+ });
1692
+ }}
1693
+ @wysiwyg-embed-cancel=${(e) => {
1694
+ e.stopPropagation();
1695
+ this.closeEmbedPanel();
1696
+ }}
1697
+ ></nile-wysiwyg-embed-panel>
1698
+ <nile-wysiwyg-bookmark-panel
1699
+ @wysiwyg-bookmark-apply=${(e) => {
1700
+ e.stopPropagation();
1701
+ this.closeBookmarkPanel();
1702
+ this.execCommand('insertBookmark', { id: e.detail.id });
1703
+ }}
1704
+ @wysiwyg-bookmark-cancel=${(e) => {
1705
+ e.stopPropagation();
1706
+ this.closeBookmarkPanel();
1707
+ }}
1708
+ ></nile-wysiwyg-bookmark-panel>
1709
+ <nile-wysiwyg-suggest-list
1710
+ @wysiwyg-suggest-select=${(e) => {
1711
+ e.stopPropagation();
1712
+ this.handleSuggestSelect(e.detail.item);
1713
+ }}
1714
+ ></nile-wysiwyg-suggest-list>
1715
+ <nile-wysiwyg-find-bar
1716
+ @wysiwyg-find=${(e) => {
1717
+ e.stopPropagation();
1718
+ this.find(e.detail.query);
1719
+ }}
1720
+ @wysiwyg-find-step=${(e) => {
1721
+ e.stopPropagation();
1722
+ if (e.detail.direction === 'prev')
1723
+ this.findPrev();
1724
+ else
1725
+ this.findNext();
1726
+ }}
1727
+ @wysiwyg-replace=${(e) => {
1728
+ e.stopPropagation();
1729
+ this.replace(e.detail.value);
1730
+ }}
1731
+ @wysiwyg-replace-all=${(e) => {
1732
+ e.stopPropagation();
1733
+ this.replaceAll(e.detail.value);
1734
+ }}
1735
+ @wysiwyg-find-close=${(e) => {
1736
+ e.stopPropagation();
1737
+ this.closeFindBar();
1738
+ }}
1739
+ ></nile-wysiwyg-find-bar>
1740
+ <nile-wysiwyg-image-panel
1741
+ ?upload-from-computer=${this.uploadFromComputer}
1742
+ .maxFileSize=${this.imageMaxFileSize}
1743
+ @wysiwyg-image-apply=${(e) => {
1744
+ e.stopPropagation();
1745
+ const editing = this.imagePanelEl?.editing;
1746
+ this.closeImagePanel();
1747
+ this.execCommand(editing ? 'updateImage' : 'insertImage', {
1748
+ src: e.detail.src,
1749
+ alt: e.detail.alt || null,
1750
+ caption: e.detail.caption || null,
1751
+ });
1752
+ }}
1753
+ @wysiwyg-image-file=${(e) => {
1754
+ e.stopPropagation();
1755
+ this.closeImagePanel();
1756
+ this.insertImageFile(e.detail.file, null, {
1757
+ alt: e.detail.alt || null,
1758
+ caption: e.detail.caption || null,
1759
+ });
1760
+ }}
1761
+ @wysiwyg-image-remove=${(e) => {
1762
+ e.stopPropagation();
1763
+ this.closeImagePanel();
1764
+ this.execCommand('deleteImage');
1765
+ }}
1766
+ @wysiwyg-image-cancel=${(e) => {
1767
+ e.stopPropagation();
1768
+ this.closeImagePanel();
1769
+ }}
1770
+ ></nile-wysiwyg-image-panel>
1771
+ <nile-wysiwyg-link-editor
1772
+ @wysiwyg-link-apply=${(e) => {
1773
+ e.stopPropagation();
1774
+ this.applyLink(e.detail.href, e.detail.text);
1775
+ }}
1776
+ @wysiwyg-link-remove=${(e) => {
1777
+ e.stopPropagation();
1778
+ this.execCommand('unlink');
1779
+ this.closeLinkEditor();
1780
+ }}
1781
+ @wysiwyg-link-cancel=${(e) => {
1782
+ e.stopPropagation();
1783
+ this.closeLinkEditor();
1784
+ }}
1785
+ ></nile-wysiwyg-link-editor>
1786
+ </div>
1787
+ `;
1788
+ }
1789
+ };
1790
+ NileWysiwygEditor.formAssociated = true;
1791
+ __decorate([
1792
+ property({ type: String })
1793
+ ], NileWysiwygEditor.prototype, "value", void 0);
1794
+ __decorate([
1795
+ property({ type: String })
1796
+ ], NileWysiwygEditor.prototype, "placeholder", void 0);
1797
+ __decorate([
1798
+ property({ type: Boolean, reflect: true })
1799
+ ], NileWysiwygEditor.prototype, "disabled", void 0);
1800
+ __decorate([
1801
+ property({ type: Boolean, reflect: true })
1802
+ ], NileWysiwygEditor.prototype, "readonly", void 0);
1803
+ __decorate([
1804
+ property({ type: String })
1805
+ ], NileWysiwygEditor.prototype, "toolbar", void 0);
1806
+ __decorate([
1807
+ property({ type: Boolean, attribute: 'bubble-menu' })
1808
+ ], NileWysiwygEditor.prototype, "bubbleMenu", void 0);
1809
+ __decorate([
1810
+ property({ type: Boolean })
1811
+ ], NileWysiwygEditor.prototype, "sanitize", void 0);
1812
+ __decorate([
1813
+ property({ type: Boolean, attribute: 'singleline' })
1814
+ ], NileWysiwygEditor.prototype, "singleLine", void 0);
1815
+ __decorate([
1816
+ property({ type: String })
1817
+ ], NileWysiwygEditor.prototype, "name", void 0);
1818
+ __decorate([
1819
+ property({ type: Boolean, reflect: true })
1820
+ ], NileWysiwygEditor.prototype, "required", void 0);
1821
+ __decorate([
1822
+ property({ attribute: false })
1823
+ ], NileWysiwygEditor.prototype, "uploadHandler", void 0);
1824
+ __decorate([
1825
+ property({ attribute: false })
1826
+ ], NileWysiwygEditor.prototype, "allowBase64Images", void 0);
1827
+ __decorate([
1828
+ property({ type: Boolean, attribute: 'upload-from-computer' })
1829
+ ], NileWysiwygEditor.prototype, "uploadFromComputer", void 0);
1830
+ __decorate([
1831
+ property({ type: Number, attribute: 'image-max-file-size' })
1832
+ ], NileWysiwygEditor.prototype, "imageMaxFileSize", void 0);
1833
+ __decorate([
1834
+ property({
1835
+ attribute: 'mentions',
1836
+ converter: {
1837
+ fromAttribute: (value) => {
1838
+ try {
1839
+ const parsed = JSON.parse(value);
1840
+ const out = {};
1841
+ for (const trigger of Object.keys(parsed)) {
1842
+ const items = parsed[trigger];
1843
+ if (Array.isArray(items)) {
1844
+ out[trigger] = items
1845
+ .filter(i => i && typeof i.key === 'string' && typeof i.label === 'string')
1846
+ .map(i => ({ key: i.key, label: i.label }));
1847
+ }
1848
+ }
1849
+ return out;
1850
+ }
1851
+ catch {
1852
+ return {};
1853
+ }
1854
+ },
1855
+ toAttribute: (value) => JSON.stringify(value),
1856
+ },
1857
+ })
1858
+ ], NileWysiwygEditor.prototype, "mentions", void 0);
1859
+ __decorate([
1860
+ property({ type: Boolean, attribute: 'no-slash-commands' })
1861
+ ], NileWysiwygEditor.prototype, "noSlashCommands", void 0);
1862
+ __decorate([
1863
+ property({ type: Boolean, attribute: 'no-emoji' })
1864
+ ], NileWysiwygEditor.prototype, "noEmoji", void 0);
1865
+ __decorate([
1866
+ property({ type: Number, attribute: 'maxlength' })
1867
+ ], NileWysiwygEditor.prototype, "maxLength", void 0);
1868
+ __decorate([
1869
+ property({ type: Boolean, attribute: 'show-counter' })
1870
+ ], NileWysiwygEditor.prototype, "showCounter", void 0);
1871
+ __decorate([
1872
+ property({ type: Boolean, attribute: 'sticky-toolbar', reflect: true })
1873
+ ], NileWysiwygEditor.prototype, "stickyToolbar", void 0);
1874
+ __decorate([
1875
+ property({
1876
+ attribute: 'snippets',
1877
+ converter: {
1878
+ fromAttribute: (value) => {
1879
+ try {
1880
+ const parsed = JSON.parse(value);
1881
+ return Array.isArray(parsed)
1882
+ ? parsed.filter(s => s &&
1883
+ typeof s.key === 'string' &&
1884
+ typeof s.label === 'string' &&
1885
+ typeof s.html === 'string')
1886
+ : [];
1887
+ }
1888
+ catch {
1889
+ return [];
1890
+ }
1891
+ },
1892
+ toAttribute: (value) => JSON.stringify(value),
1893
+ },
1894
+ })
1895
+ ], NileWysiwygEditor.prototype, "snippets", void 0);
1896
+ __decorate([
1897
+ property({
1898
+ attribute: 'merge-fields',
1899
+ converter: {
1900
+ fromAttribute: (value) => {
1901
+ try {
1902
+ const parsed = JSON.parse(value);
1903
+ return Array.isArray(parsed)
1904
+ ? parsed.filter(f => f && typeof f.id === 'string' && typeof f.label === 'string')
1905
+ : [];
1906
+ }
1907
+ catch {
1908
+ return [];
1909
+ }
1910
+ },
1911
+ toAttribute: (value) => JSON.stringify(value),
1912
+ },
1913
+ })
1914
+ ], NileWysiwygEditor.prototype, "mergeFields", void 0);
1915
+ __decorate([
1916
+ property({
1917
+ attribute: 'templates',
1918
+ converter: {
1919
+ fromAttribute: (value) => {
1920
+ try {
1921
+ const parsed = JSON.parse(value);
1922
+ return Array.isArray(parsed)
1923
+ ? parsed.filter(t => t && typeof t.label === 'string' && typeof t.html === 'string')
1924
+ : [];
1925
+ }
1926
+ catch {
1927
+ return [];
1928
+ }
1929
+ },
1930
+ toAttribute: (value) => JSON.stringify(value),
1931
+ },
1932
+ })
1933
+ ], NileWysiwygEditor.prototype, "templates", void 0);
1934
+ __decorate([
1935
+ property({ attribute: false })
1936
+ ], NileWysiwygEditor.prototype, "mathRenderer", void 0);
1937
+ __decorate([
1938
+ property({ type: Number, attribute: 'autosave-interval' })
1939
+ ], NileWysiwygEditor.prototype, "autosaveInterval", void 0);
1940
+ __decorate([
1941
+ property({ type: Boolean, attribute: 'paste-as-plain-text', reflect: true })
1942
+ ], NileWysiwygEditor.prototype, "pasteAsPlainText", void 0);
1943
+ __decorate([
1944
+ property({ type: Boolean, attribute: 'no-markdown-paste' })
1945
+ ], NileWysiwygEditor.prototype, "noMarkdownPaste", void 0);
1946
+ __decorate([
1947
+ property({ type: Boolean, attribute: 'no-text-transform' })
1948
+ ], NileWysiwygEditor.prototype, "noTextTransform", void 0);
1949
+ __decorate([
1950
+ property({ type: Boolean, reflect: true })
1951
+ ], NileWysiwygEditor.prototype, "fullscreen", void 0);
1952
+ __decorate([
1953
+ property({ type: Boolean, attribute: 'show-blocks', reflect: true })
1954
+ ], NileWysiwygEditor.prototype, "showBlocks", void 0);
1955
+ __decorate([
1956
+ state()
1957
+ ], NileWysiwygEditor.prototype, "toolbarState", void 0);
1958
+ __decorate([
1959
+ state()
1960
+ ], NileWysiwygEditor.prototype, "hasFocus", void 0);
1961
+ __decorate([
1962
+ state()
1963
+ ], NileWysiwygEditor.prototype, "sourceMode", void 0);
1964
+ __decorate([
1965
+ state()
1966
+ ], NileWysiwygEditor.prototype, "counter", void 0);
1967
+ __decorate([
1968
+ state()
1969
+ ], NileWysiwygEditor.prototype, "insertMenuMode", void 0);
1970
+ __decorate([
1971
+ query('.nile-wysiwyg')
1972
+ ], NileWysiwygEditor.prototype, "baseEl", void 0);
1973
+ __decorate([
1974
+ query('nile-wysiwyg-toolbar')
1975
+ ], NileWysiwygEditor.prototype, "toolbarEl", void 0);
1976
+ __decorate([
1977
+ query('.nile-wysiwyg__surface')
1978
+ ], NileWysiwygEditor.prototype, "surfaceEl", void 0);
1979
+ __decorate([
1980
+ query('nile-wysiwyg-link-editor')
1981
+ ], NileWysiwygEditor.prototype, "linkEditorEl", void 0);
1982
+ __decorate([
1983
+ query('nile-wysiwyg-bubble-menu')
1984
+ ], NileWysiwygEditor.prototype, "bubbleMenuEl", void 0);
1985
+ __decorate([
1986
+ query('nile-wysiwyg-image-panel')
1987
+ ], NileWysiwygEditor.prototype, "imagePanelEl", void 0);
1988
+ __decorate([
1989
+ query('nile-wysiwyg-table-menu')
1990
+ ], NileWysiwygEditor.prototype, "tableMenuEl", void 0);
1991
+ __decorate([
1992
+ query('nile-wysiwyg-suggest-list')
1993
+ ], NileWysiwygEditor.prototype, "suggestListEl", void 0);
1994
+ __decorate([
1995
+ query('nile-wysiwyg-find-bar')
1996
+ ], NileWysiwygEditor.prototype, "findBarEl", void 0);
1997
+ __decorate([
1998
+ query('nile-wysiwyg-source-view')
1999
+ ], NileWysiwygEditor.prototype, "sourceViewEl", void 0);
2000
+ __decorate([
2001
+ query('nile-wysiwyg-embed-panel')
2002
+ ], NileWysiwygEditor.prototype, "embedPanelEl", void 0);
2003
+ __decorate([
2004
+ query('nile-wysiwyg-bookmark-panel')
2005
+ ], NileWysiwygEditor.prototype, "bookmarkPanelEl", void 0);
2006
+ __decorate([
2007
+ query('nile-wysiwyg-code-menu')
2008
+ ], NileWysiwygEditor.prototype, "codeMenuEl", void 0);
2009
+ __decorate([
2010
+ query('nile-wysiwyg-shortcut-help')
2011
+ ], NileWysiwygEditor.prototype, "shortcutHelpEl", void 0);
2012
+ __decorate([
2013
+ query('nile-wysiwyg-image-menu')
2014
+ ], NileWysiwygEditor.prototype, "imageMenuEl", void 0);
2015
+ __decorate([
2016
+ query('nile-wysiwyg-embed-menu')
2017
+ ], NileWysiwygEditor.prototype, "embedMenuEl", void 0);
2018
+ __decorate([
2019
+ query('nile-wysiwyg-special-chars')
2020
+ ], NileWysiwygEditor.prototype, "specialCharsEl", void 0);
2021
+ __decorate([
2022
+ query('nile-wysiwyg-emoji-picker')
2023
+ ], NileWysiwygEditor.prototype, "emojiPickerEl", void 0);
2024
+ __decorate([
2025
+ query('nile-wysiwyg-insert-menu')
2026
+ ], NileWysiwygEditor.prototype, "insertMenuEl", void 0);
2027
+ __decorate([
2028
+ query('nile-wysiwyg-math-panel')
2029
+ ], NileWysiwygEditor.prototype, "mathPanelEl", void 0);
2030
+ __decorate([
2031
+ query('nile-wysiwyg-markdown-panel')
2032
+ ], NileWysiwygEditor.prototype, "markdownPanelEl", void 0);
2033
+ NileWysiwygEditor = __decorate([
2034
+ customElement('nile-wysiwyg-editor')
2035
+ ], NileWysiwygEditor);
2036
+ export { NileWysiwygEditor };
2037
+ export default NileWysiwygEditor;
2038
+ //# sourceMappingURL=nile-wysiwyg-editor.js.map