@aquera/nile-elements 1.9.7 → 1.9.9

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 (244) hide show
  1. package/README.md +6 -0
  2. package/dist/{index-249de90a.esm.js → index-289706c7.esm.js} +1 -1
  3. package/dist/{index-df196058.cjs.js → index-d7e6f1c6.cjs.js} +2 -2
  4. package/dist/index-d7e6f1c6.cjs.js.map +1 -0
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.esm.js +1 -1
  7. package/dist/index.js +1587 -1276
  8. package/dist/katex-44476ab4.esm.js +1 -0
  9. package/dist/katex-f4186f87.cjs.js +2 -0
  10. package/dist/katex-f4186f87.cjs.js.map +1 -0
  11. package/dist/nile-color-picker/index.cjs.js +1 -1
  12. package/dist/nile-color-picker/index.esm.js +1 -1
  13. package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
  14. package/dist/nile-color-picker/nile-color-picker.cjs.js.map +1 -1
  15. package/dist/nile-color-picker/nile-color-picker.esm.js +1 -1
  16. package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
  17. package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
  18. package/dist/nile-select/virtual-scroll-helper.esm.js +52 -44
  19. package/dist/nile-virtual-select/renderer.cjs.js +1 -1
  20. package/dist/nile-virtual-select/renderer.cjs.js.map +1 -1
  21. package/dist/nile-virtual-select/renderer.esm.js +23 -22
  22. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
  23. package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
  24. package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
  25. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
  26. package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
  27. package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
  28. package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
  29. package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
  30. package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
  31. package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
  32. package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
  33. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
  34. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
  35. package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
  36. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
  37. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
  38. package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
  39. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
  40. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
  41. package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
  42. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js +1 -1
  43. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js.map +1 -1
  44. package/dist/nile-wysiwyg-editor/engine/plugins/math-view.esm.js +1 -1
  45. package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js +2 -0
  46. package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js.map +1 -0
  47. package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.esm.js +1 -0
  48. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
  49. package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
  50. package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
  51. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +1 -1
  52. package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
  53. package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
  54. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
  55. package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
  56. package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
  57. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
  58. package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
  59. package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
  60. package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
  61. package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
  62. package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js +2 -0
  63. package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js.map +1 -0
  64. package/dist/nile-wysiwyg-editor/math/katex-renderer.esm.js +1 -0
  65. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
  66. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
  67. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
  68. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
  69. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +22 -3
  70. package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +7 -6
  71. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
  72. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.esm.js +1 -1
  73. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js +2 -0
  74. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js.map +1 -0
  75. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.esm.js +1 -0
  76. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
  77. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js.map +1 -1
  78. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js +1 -1
  79. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js.map +1 -1
  80. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.esm.js +33 -54
  81. package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.esm.js +83 -95
  82. package/dist/nile-wysiwyg-editor/types.cjs.js +1 -1
  83. package/dist/nile-wysiwyg-editor/types.cjs.js.map +1 -1
  84. package/dist/nile-wysiwyg-editor/types.esm.js +1 -1
  85. package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js +2 -0
  86. package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js.map +1 -0
  87. package/dist/nile-wysiwyg-editor/ui/bubble-item.esm.js +1 -0
  88. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
  89. package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
  90. package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +23 -15
  91. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
  92. package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
  93. package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +3 -3
  94. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
  95. package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
  96. package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
  97. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
  98. package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
  99. package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +0 -3
  100. package/dist/nile-wysiwyg-editor/ui/preview.cjs.js +2 -0
  101. package/dist/nile-wysiwyg-editor/ui/preview.cjs.js.map +1 -0
  102. package/dist/nile-wysiwyg-editor/ui/preview.esm.js +8 -0
  103. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
  104. package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
  105. package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +225 -24
  106. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
  107. package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
  108. package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +9 -9
  109. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
  110. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
  111. package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +115 -13
  112. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
  113. package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
  114. package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +13 -12
  115. package/dist/src/index.d.ts +5 -0
  116. package/dist/src/index.js +3 -0
  117. package/dist/src/index.js.map +1 -1
  118. package/dist/src/nile-color-picker/nile-color-picker.d.ts +1 -0
  119. package/dist/src/nile-color-picker/nile-color-picker.js +1 -0
  120. package/dist/src/nile-color-picker/nile-color-picker.js.map +1 -1
  121. package/dist/src/nile-select/virtual-scroll-helper.js +9 -1
  122. package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
  123. package/dist/src/nile-virtual-select/renderer.js +1 -0
  124. package/dist/src/nile-virtual-select/renderer.js.map +1 -1
  125. package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +1 -0
  126. package/dist/src/nile-wysiwyg-editor/engine/index.js +1 -0
  127. package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
  128. package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js +3 -0
  129. package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js.map +1 -1
  130. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.d.ts +19 -4
  131. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js +23 -18
  132. package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js.map +1 -1
  133. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.d.ts +8 -3
  134. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js +41 -4
  135. package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js.map +1 -1
  136. package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.d.ts +17 -0
  137. package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js +174 -0
  138. package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js.map +1 -0
  139. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +33 -2
  140. package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
  141. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +49 -8
  142. package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
  143. package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +12 -0
  144. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +76 -8
  145. package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
  146. package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +2 -0
  147. package/dist/src/nile-wysiwyg-editor/engine/schema.js +154 -2
  148. package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
  149. package/dist/src/nile-wysiwyg-editor/index.d.ts +5 -0
  150. package/dist/src/nile-wysiwyg-editor/index.js +3 -0
  151. package/dist/src/nile-wysiwyg-editor/index.js.map +1 -1
  152. package/dist/src/nile-wysiwyg-editor/math/katex-renderer.d.ts +38 -0
  153. package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js +40 -0
  154. package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js.map +1 -0
  155. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +22 -3
  156. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
  157. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +36 -0
  158. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +125 -8
  159. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
  160. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +5 -2
  161. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
  162. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.d.ts +2 -0
  163. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js +1 -0
  164. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js.map +1 -1
  165. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.d.ts +55 -0
  166. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js +79 -0
  167. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js.map +1 -0
  168. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js +33 -54
  169. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js.map +1 -1
  170. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.d.ts +50 -2
  171. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js +158 -102
  172. package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js.map +1 -1
  173. package/dist/src/nile-wysiwyg-editor/types.d.ts +4 -2
  174. package/dist/src/nile-wysiwyg-editor/types.js +2 -2
  175. package/dist/src/nile-wysiwyg-editor/types.js.map +1 -1
  176. package/dist/src/nile-wysiwyg-editor/ui/bubble-item.d.ts +48 -0
  177. package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js +69 -0
  178. package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js.map +1 -0
  179. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.d.ts +17 -2
  180. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +128 -35
  181. package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
  182. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.d.ts +2 -1
  183. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +4 -3
  184. package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
  185. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +0 -3
  186. package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
  187. package/dist/src/nile-wysiwyg-editor/ui/preview.d.ts +20 -0
  188. package/dist/src/nile-wysiwyg-editor/ui/preview.js +44 -0
  189. package/dist/src/nile-wysiwyg-editor/ui/preview.js.map +1 -0
  190. package/dist/src/nile-wysiwyg-editor/ui/source-view.d.ts +35 -3
  191. package/dist/src/nile-wysiwyg-editor/ui/source-view.js +337 -24
  192. package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
  193. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.d.ts +1 -0
  194. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +9 -0
  195. package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
  196. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.d.ts +13 -2
  197. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +166 -10
  198. package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
  199. package/dist/src/nile-wysiwyg-editor/ui/table-menu.d.ts +1 -0
  200. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +10 -8
  201. package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
  202. package/dist/src/version.js +1 -1
  203. package/dist/src/version.js.map +1 -1
  204. package/dist/tsconfig.tsbuildinfo +1 -1
  205. package/dist/xml-25c4108a.esm.js +1 -0
  206. package/dist/xml-90fd974e.cjs.js +2 -0
  207. package/dist/xml-90fd974e.cjs.js.map +1 -0
  208. package/package.json +2 -1
  209. package/rollup.config.js +1 -0
  210. package/src/index.ts +8 -0
  211. package/src/nile-color-picker/nile-color-picker.ts +1 -0
  212. package/src/nile-select/virtual-scroll-helper.ts +9 -1
  213. package/src/nile-virtual-select/renderer.ts +1 -1
  214. package/src/nile-wysiwyg-editor/engine/index.ts +1 -0
  215. package/src/nile-wysiwyg-editor/engine/plugins/embed-view.ts +3 -0
  216. package/src/nile-wysiwyg-editor/engine/plugins/embeds.ts +26 -20
  217. package/src/nile-wysiwyg-editor/engine/plugins/math-view.ts +45 -4
  218. package/src/nile-wysiwyg-editor/engine/plugins/style-block-view.ts +178 -0
  219. package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +34 -2
  220. package/src/nile-wysiwyg-editor/engine/pm.test.ts +58 -8
  221. package/src/nile-wysiwyg-editor/engine/sanitize.ts +71 -8
  222. package/src/nile-wysiwyg-editor/engine/schema.ts +158 -7
  223. package/src/nile-wysiwyg-editor/index.ts +5 -0
  224. package/src/nile-wysiwyg-editor/math/katex-renderer.ts +59 -0
  225. package/src/nile-wysiwyg-editor/math/katex.d.ts +17 -0
  226. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +22 -3
  227. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +5 -2
  228. package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +142 -7
  229. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.ts +2 -0
  230. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.ts +68 -0
  231. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.ts +33 -54
  232. package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.ts +202 -108
  233. package/src/nile-wysiwyg-editor/types.ts +6 -4
  234. package/src/nile-wysiwyg-editor/ui/bubble-item.ts +60 -0
  235. package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +147 -40
  236. package/src/nile-wysiwyg-editor/ui/embed-panel.ts +4 -3
  237. package/src/nile-wysiwyg-editor/ui/math-panel.ts +0 -3
  238. package/src/nile-wysiwyg-editor/ui/preview.ts +42 -0
  239. package/src/nile-wysiwyg-editor/ui/source-view.ts +339 -31
  240. package/src/nile-wysiwyg-editor/ui/suggest-list.ts +10 -0
  241. package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +160 -10
  242. package/src/nile-wysiwyg-editor/ui/table-menu.ts +10 -8
  243. package/vscode-html-custom-data.json +75 -6
  244. package/dist/index-df196058.cjs.js.map +0 -1
@@ -1,31 +1,71 @@
1
1
  import { html, css, TemplateResult, CSSResultArray } from 'lit';
2
- import { customElement, property, query } from 'lit/decorators.js';
2
+ import { customElement, property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
5
+ import hljs from 'highlight.js/lib/core';
6
+ import xml from 'highlight.js/lib/languages/xml';
3
7
  import NileElement from '../../internal/nile-element';
8
+ import { sanitizeHtml } from '../engine/sanitize';
9
+ import './preview';
10
+
11
+ // HTML highlighting only — reuses the same lib the code-block feature already
12
+ // bundles, so this adds no new dependency. Registration is idempotent.
13
+ hljs.registerLanguage('xml', xml);
4
14
 
5
15
  /**
6
- * Raw HTML source editing mode. Replaces the rich surface while open; the
7
- * host re-parses (and re-sanitizes) the HTML on apply. Emits
16
+ * Raw HTML source editing mode. A split view: the raw HTML on the left, a
17
+ * live rendered preview on the right, with a draggable divider between them
18
+ * and a resizable body. The preview runs the same sanitizer the host applies
19
+ * on the way back, so what you see is what the document becomes. Emits
8
20
  * `wysiwyg-source-apply` detail: { html } and `wysiwyg-source-cancel`.
21
+ *
22
+ * The code pane is a lightweight editor: a real `<textarea>` for input, with a
23
+ * highlight.js-coloured layer painted behind it and a synced line-number
24
+ * gutter. No heavyweight code-editor dependency.
9
25
  */
10
26
  @customElement('nile-wysiwyg-source-view')
11
27
  export class NileWysiwygSourceView extends NileElement {
12
28
  @property({ type: String }) value = '';
13
29
 
14
- @query('textarea') textarea?: HTMLTextAreaElement;
30
+ @query('textarea') private textarea?: HTMLTextAreaElement;
31
+
32
+ @query('.hl > code') private highlightEl?: HTMLElement;
33
+
34
+ @query('.linenos-inner') private linenosEl?: HTMLElement;
35
+
36
+ @query('.body') private bodyEl?: HTMLElement;
37
+
38
+ /** Current source text, drives the live preview, highlight and gutter. */
39
+ @state() private draft = '';
40
+
41
+ /**
42
+ * Fraction of the body width given to the code pane (the rest is preview).
43
+ * Clamped while dragging the divider.
44
+ */
45
+ @state() private splitRatio = 0.5;
46
+
47
+ /** Whether the divider is currently being dragged. */
48
+ @state() private splitDragging = false;
15
49
 
16
50
  public static get styles(): CSSResultArray {
17
51
  return [
18
52
  css`
19
53
  :host {
20
- font-family: var(
21
- --nile-font-family-sans-serif,
22
- var(--ng-font-family-body, system-ui, -apple-system, sans-serif)
23
- );
24
54
  display: flex;
25
55
  flex-direction: column;
56
+ box-sizing: border-box;
57
+ overflow: hidden;
58
+ min-height: var(--nile-wysiwyg-min-height, 200px);
59
+ height: var(--nile-wysiwyg-source-height, 340px);
60
+ max-height: var(
61
+ --nile-wysiwyg-source-max-height,
62
+ var(--nile-wysiwyg-max-height, 70vh)
63
+ );
64
+ resize: vertical;
26
65
  }
27
66
 
28
67
  .bar {
68
+ flex: 0 0 auto;
29
69
  display: flex;
30
70
  align-items: center;
31
71
  justify-content: space-between;
@@ -57,32 +97,271 @@ export class NileWysiwygSourceView extends NileElement {
57
97
  color: #ffffff;
58
98
  }
59
99
 
60
- textarea {
61
- flex: 1;
62
- min-height: var(--nile-wysiwyg-min-height, 160px);
63
- padding: 12px 16px;
64
- border: none;
65
- outline: none;
66
- resize: vertical;
100
+ .body {
101
+ flex: 1 1 auto;
102
+ display: flex;
103
+ align-items: stretch;
104
+ overflow: hidden;
105
+ min-height: 0;
106
+ box-sizing: border-box;
107
+ }
108
+
109
+ .body--dragging {
110
+ cursor: col-resize;
111
+ user-select: none;
112
+ -webkit-user-select: none;
113
+ }
114
+
115
+ .body--dragging .pane {
116
+ pointer-events: none;
117
+ }
118
+
119
+ .pane {
120
+ min-width: 0;
121
+ min-height: 0;
122
+ box-sizing: border-box;
123
+ }
124
+
125
+ /* --- Lightweight code editor: gutter + highlighted textarea --- */
126
+ .pane--code {
127
+ display: flex;
128
+ background: var(--nile-colors-white-base, #ffffff);
129
+ /* Shared code metrics, inherited by the gutter, highlight and textarea
130
+ so every line lines up to the pixel. */
67
131
  font-family: var(--nile-wysiwyg-code-font, ui-monospace, SFMono-Regular, Menlo, monospace);
68
132
  font-size: 12px;
69
- line-height: 1.5;
133
+ line-height: 18px;
134
+ tab-size: 2;
135
+ -moz-tab-size: 2;
136
+ }
137
+
138
+ .linenos {
139
+ flex: 0 0 auto;
140
+ overflow: hidden;
141
+ box-sizing: border-box;
142
+ padding: 12px 8px 0;
143
+ text-align: right;
144
+ color: var(--nile-colors-neutral-400, #98a2b3);
145
+ background: var(--nile-colors-neutral-50, #f9fafb);
146
+ border-right: 1px solid var(--nile-colors-neutral-200, #eaecf0);
147
+ user-select: none;
148
+ }
149
+
150
+ .linenos-inner {
151
+ will-change: transform;
152
+ }
153
+
154
+ .linenos-inner > div {
155
+ height: 18px;
156
+ }
157
+
158
+ .input-wrap {
159
+ position: relative;
160
+ flex: 1 1 0;
161
+ min-width: 0;
162
+ overflow: hidden;
163
+ }
164
+
165
+ /* Highlight layer and textarea share the exact same box (offset for
166
+ padding via top/left, extending to the edges for horizontal scroll),
167
+ so the coloured text sits directly under the caret. */
168
+ .hl,
169
+ textarea {
170
+ position: absolute;
171
+ top: 12px;
172
+ left: 16px;
173
+ right: 0;
174
+ bottom: 0;
175
+ margin: 0;
176
+ border: 0;
177
+ padding: 0;
178
+ font: inherit;
179
+ line-height: inherit;
180
+ tab-size: inherit;
181
+ white-space: pre;
182
+ word-wrap: normal;
183
+ overflow-wrap: normal;
184
+ box-sizing: border-box;
185
+ }
186
+
187
+ .hl {
188
+ overflow: hidden;
189
+ pointer-events: none;
70
190
  color: var(--nile-colors-neutral-900, #101828);
191
+ }
192
+
193
+ .hl > code {
194
+ display: block;
195
+ will-change: transform;
196
+ }
197
+
198
+ textarea {
199
+ width: auto;
200
+ resize: none;
201
+ outline: none;
202
+ overflow: auto;
71
203
  background: transparent;
72
- box-sizing: border-box;
204
+ color: transparent;
205
+ caret-color: var(--nile-colors-neutral-900, #101828);
206
+ }
207
+
208
+ /* Must be translucent: the textarea's text is transparent and the
209
+ coloured code sits BEHIND it, so an opaque selection would hide the
210
+ text. A tint lets the highlighted code show through the selection. */
211
+ textarea::selection {
212
+ background: rgba(41, 112, 255, 0.3);
213
+ }
214
+ textarea::-moz-selection {
215
+ background: rgba(41, 112, 255, 0.3);
216
+ }
217
+
218
+ /* highlight.js token palette (Atom One Light), matching code blocks. */
219
+ .hl .hljs-comment,
220
+ .hl .hljs-quote { color: #a0a1a7; font-style: italic; }
221
+ .hl .hljs-keyword,
222
+ .hl .hljs-selector-tag,
223
+ .hl .hljs-doctag { color: #a626a4; }
224
+ .hl .hljs-name,
225
+ .hl .hljs-selector-id,
226
+ .hl .hljs-selector-class,
227
+ .hl .hljs-tag { color: #e45649; }
228
+ .hl .hljs-literal,
229
+ .hl .hljs-built_in { color: #0184bb; }
230
+ .hl .hljs-string,
231
+ .hl .hljs-attribute,
232
+ .hl .hljs-meta .hljs-string { color: #50a14f; }
233
+ .hl .hljs-attr,
234
+ .hl .hljs-variable,
235
+ .hl .hljs-type,
236
+ .hl .hljs-number { color: #986801; }
237
+ .hl .hljs-symbol,
238
+ .hl .hljs-meta,
239
+ .hl .hljs-title { color: #4078f2; }
240
+
241
+ /* Draggable divider between the code and preview panes. */
242
+ .gutter {
243
+ flex: 0 0 auto;
244
+ position: relative;
245
+ width: 1px;
246
+ align-self: stretch;
247
+ background: var(--nile-colors-neutral-300, #e4e7ec);
248
+ cursor: col-resize;
249
+ touch-action: none;
250
+ }
251
+
252
+ .gutter::before {
253
+ content: '';
254
+ position: absolute;
255
+ inset: 0 -4px;
256
+ }
257
+
258
+ .gutter:hover,
259
+ .body--dragging .gutter {
260
+ background: var(--ng-colors-bg-brand-primary, #2970ff);
261
+ }
262
+
263
+ .pane--preview {
264
+ flex: 1 1 0;
265
+ overflow: auto;
266
+ border-left: 1px solid var(--nile-colors-neutral-200, #eaecf0);
267
+ background: var(--nile-colors-white-base, #ffffff);
268
+ }
269
+
270
+ nile-wysiwyg-preview {
271
+ display: block;
272
+ }
273
+
274
+ .preview-empty {
275
+ display: block;
276
+ padding: 12px 16px;
277
+ color: var(--nile-colors-neutral-500, #98a2b3);
278
+ font-size: 12px;
279
+ opacity: 0.7;
73
280
  }
74
281
  `,
75
282
  ];
76
283
  }
77
284
 
285
+ protected willUpdate(changed: Map<string, unknown>): void {
286
+ // Sync the draft (and thus preview/highlight) whenever the host sets value.
287
+ if (changed.has('value')) this.draft = this.value;
288
+ }
289
+
290
+ /** The current (possibly edited) source text — what "Done" would apply. */
291
+ public get currentValue(): string {
292
+ return this.draft;
293
+ }
294
+
78
295
  public focusInput(): void {
79
296
  this.updateComplete.then(() => this.textarea?.focus());
80
297
  }
81
298
 
299
+ /** highlight.js-coloured HTML for the current draft (falls back to escaped). */
300
+ private get highlighted(): string {
301
+ if (!this.draft) return '';
302
+ try {
303
+ return hljs.highlight(this.draft, { language: 'xml' }).value;
304
+ } catch {
305
+ return this.draft.replace(/[&<>]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]!));
306
+ }
307
+ }
308
+
309
+ private get lineCount(): number {
310
+ return this.draft ? this.draft.split('\n').length : 1;
311
+ }
312
+
313
+ /** Begins a pointer-driven resize of the split divider. */
314
+ private startSplitDrag = (e: PointerEvent) => {
315
+ const body = this.bodyEl;
316
+ if (!body) return;
317
+ e.preventDefault();
318
+ this.splitDragging = true;
319
+
320
+ const rect = body.getBoundingClientRect();
321
+ const MIN = 0.15;
322
+ const MAX = 0.85;
323
+
324
+ const onMove = (ev: PointerEvent) => {
325
+ if (rect.width === 0) return;
326
+ const ratio = (ev.clientX - rect.left) / rect.width;
327
+ this.splitRatio = Math.min(MAX, Math.max(MIN, ratio));
328
+ };
329
+ const onUp = () => {
330
+ this.splitDragging = false;
331
+ window.removeEventListener('pointermove', onMove);
332
+ window.removeEventListener('pointerup', onUp);
333
+ };
334
+ window.addEventListener('pointermove', onMove);
335
+ window.addEventListener('pointerup', onUp);
336
+ };
337
+
338
+ /** Resets the divider to a 50/50 layout (double-click affordance). */
339
+ private resetSplit = () => {
340
+ this.splitRatio = 0.5;
341
+ };
342
+
343
+ private onInput = (e: Event) => {
344
+ this.draft = (e.target as HTMLTextAreaElement).value;
345
+ };
346
+
347
+ /** Keeps the highlight layer and line-number gutter aligned with scroll. */
348
+ private onScroll = (e: Event) => {
349
+ const ta = e.target as HTMLTextAreaElement;
350
+ if (this.highlightEl) {
351
+ this.highlightEl.style.transform = `translate(${-ta.scrollLeft}px, ${-ta.scrollTop}px)`;
352
+ }
353
+ if (this.linenosEl) {
354
+ this.linenosEl.style.transform = `translateY(${-ta.scrollTop}px)`;
355
+ }
356
+ };
357
+
82
358
  public render(): TemplateResult {
359
+ const preview = sanitizeHtml(this.draft);
360
+ const lines: TemplateResult[] = [];
361
+ for (let i = 1; i <= this.lineCount; i += 1) lines.push(html`<div>${i}</div>`);
83
362
  return html`
84
363
  <div class="bar" part="source-bar">
85
- <span>HTML source</span>
364
+ <span>HTML</span>
86
365
  <span class="actions">
87
366
  <button type="button" @click=${() => this.emit('wysiwyg-source-cancel')}>
88
367
  Cancel
@@ -90,24 +369,53 @@ export class NileWysiwygSourceView extends NileElement {
90
369
  <button
91
370
  type="button"
92
371
  class="primary"
93
- @click=${() =>
94
- this.emit('wysiwyg-source-apply', { html: this.textarea?.value ?? '' })}
372
+ @click=${() => this.emit('wysiwyg-source-apply', { html: this.draft })}
95
373
  >
96
374
  Done
97
375
  </button>
98
376
  </span>
99
377
  </div>
100
- <textarea
101
- part="source-textarea"
102
- spellcheck="false"
103
- .value=${this.value}
104
- @keydown=${(e: KeyboardEvent) => {
105
- if (e.key === 'Escape') {
106
- e.stopPropagation();
107
- this.emit('wysiwyg-source-cancel');
108
- }
109
- }}
110
- ></textarea>
378
+ <div class=${classMap({ body: true, 'body--dragging': this.splitDragging })}>
379
+ <div class="pane pane--code" style="flex: 0 0 ${this.splitRatio * 100}%">
380
+ <div class="linenos" part="line-numbers" aria-hidden="true">
381
+ <div class="linenos-inner">${lines}</div>
382
+ </div>
383
+ <div class="input-wrap">
384
+ <pre class="hl" aria-hidden="true"><code>${unsafeHTML(this.highlighted)}</code></pre>
385
+ <textarea
386
+ part="source-textarea"
387
+ spellcheck="false"
388
+ autocapitalize="off"
389
+ autocomplete="off"
390
+ wrap="off"
391
+ .value=${this.value}
392
+ @input=${this.onInput}
393
+ @scroll=${this.onScroll}
394
+ @keydown=${(e: KeyboardEvent) => {
395
+ if (e.key === 'Escape') {
396
+ e.stopPropagation();
397
+ this.emit('wysiwyg-source-cancel');
398
+ }
399
+ }}
400
+ ></textarea>
401
+ </div>
402
+ </div>
403
+ <div
404
+ class="gutter"
405
+ part="gutter"
406
+ role="separator"
407
+ aria-orientation="vertical"
408
+ aria-label="Resize source and preview"
409
+ title="Drag to resize · double-click to reset"
410
+ @pointerdown=${this.startSplitDrag}
411
+ @dblclick=${this.resetSplit}
412
+ ></div>
413
+ <div class="pane pane--preview" part="preview">
414
+ ${this.draft.trim()
415
+ ? html`<nile-wysiwyg-preview .content=${preview}></nile-wysiwyg-preview>`
416
+ : html`<span class="preview-empty">Nothing to preview</span>`}
417
+ </div>
418
+ </div>
111
419
  `;
112
420
  }
113
421
  }
@@ -86,6 +86,16 @@ export class NileWysiwygSuggestList extends NileElement {
86
86
  if (this.activeIndex >= this.items.length) this.activeIndex = 0;
87
87
  }
88
88
 
89
+ protected updated(changed: Map<string, unknown>): void {
90
+ // Keep the highlighted item visible as the user arrows through a list
91
+ // that overflows the popup's max-height. `nearest` is a no-op when the
92
+ // item is already visible (e.g. after a hover), so it never fights the mouse.
93
+ if (changed.has('activeIndex') || changed.has('items')) {
94
+ const active = this.renderRoot.querySelector('.item.active');
95
+ (active as HTMLElement | null)?.scrollIntoView({ block: 'nearest' });
96
+ }
97
+ }
98
+
89
99
  public next(): void {
90
100
  if (this.items.length) this.activeIndex = (this.activeIndex + 1) % this.items.length;
91
101
  }
@@ -2,23 +2,36 @@ import { html, css, TemplateResult, CSSResultArray } from 'lit';
2
2
  import { customElement, property, state } from 'lit/decorators.js';
3
3
  import NileElement from '../../internal/nile-element';
4
4
 
5
- const MAX_ROWS = 8;
6
- const MAX_COLS = 10;
5
+ /** Smallest grid shown before the pointer starts expanding it. */
6
+ const MIN_ROWS = 5;
7
+ const MIN_COLS = 5;
7
8
 
8
9
  /**
9
- * Toolbar item: a table button that opens an N×M hover grid. Picking a cell
10
- * emits `wysiwyg-command` with `insertTable { rows, cols }`.
10
+ * Toolbar item: a table button that opens a hover grid which auto-expands as
11
+ * the pointer reaches the last row/column (up to `maxRows`×`maxCols`), plus a
12
+ * "Custom" rows×cols input for larger tables. Picking a cell or confirming the
13
+ * custom size emits `wysiwyg-command` with `insertTable { rows, cols }`.
11
14
  */
12
15
  @customElement('nile-wysiwyg-table-picker')
13
16
  export class NileWysiwygTablePicker extends NileElement {
14
17
  @property({ type: Boolean, reflect: true }) disabled = false;
15
18
 
19
+ /** Hard cap on the number of rows selectable via grid or custom input. */
20
+ @property({ type: Number, attribute: 'max-rows', reflect: true }) maxRows = 20;
21
+
22
+ /** Hard cap on the number of columns selectable via grid or custom input. */
23
+ @property({ type: Number, attribute: 'max-cols', reflect: true }) maxCols = 20;
24
+
16
25
  @state() private open = false;
17
26
 
18
27
  @state() private hoverRows = 0;
19
28
 
20
29
  @state() private hoverCols = 0;
21
30
 
31
+ @state() private customRows = '';
32
+
33
+ @state() private customCols = '';
34
+
22
35
  public static get styles(): CSSResultArray {
23
36
  return [
24
37
  css`
@@ -69,7 +82,6 @@ export class NileWysiwygTablePicker extends NileElement {
69
82
 
70
83
  .grid {
71
84
  display: grid;
72
- grid-template-columns: repeat(${MAX_COLS}, 14px);
73
85
  gap: 2px;
74
86
  }
75
87
 
@@ -93,6 +105,71 @@ export class NileWysiwygTablePicker extends NileElement {
93
105
  font-size: 12px;
94
106
  color: var(--nile-colors-neutral-600, #475467);
95
107
  }
108
+
109
+ .custom {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 6px;
113
+ margin-top: 8px;
114
+ padding-top: 8px;
115
+ border-top: 1px solid var(--nile-colors-neutral-300, #e4e7ec);
116
+ }
117
+
118
+ .custom input {
119
+ width: 44px;
120
+ height: 26px;
121
+ padding: 0 6px;
122
+ border: 1px solid var(--nile-colors-neutral-400, #d0d5dd);
123
+ border-radius: 6px;
124
+ font-size: 12px;
125
+ box-sizing: border-box;
126
+ text-align: center;
127
+ color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900, #101828));
128
+ font-family: inherit;
129
+ /* Hide native number spinners: they overlap the centered text in this
130
+ narrow field and a stray click on the arrows mutates the value. */
131
+ -moz-appearance: textfield;
132
+ appearance: textfield;
133
+ }
134
+
135
+ .custom input::-webkit-outer-spin-button,
136
+ .custom input::-webkit-inner-spin-button {
137
+ -webkit-appearance: none;
138
+ margin: 0;
139
+ }
140
+
141
+ .custom input:focus {
142
+ outline: none;
143
+ border-color: var(--nile-colors-primary-600, var(--ng-componentcolors-utility-blue-600, #2563eb));
144
+ }
145
+
146
+ .custom .times {
147
+ font-size: 12px;
148
+ color: var(--nile-colors-neutral-600, #475467);
149
+ }
150
+
151
+ .custom button.insert {
152
+ margin-left: auto;
153
+ height: 26px;
154
+ padding: 0 12px;
155
+ border: none;
156
+ border-radius: 6px;
157
+ background: var(--nile-colors-primary-600, var(--ng-componentcolors-utility-blue-600, #2563eb));
158
+ color: #ffffff;
159
+ font-size: 12px;
160
+ font-weight: 600;
161
+ font-family: inherit;
162
+ cursor: pointer;
163
+ }
164
+
165
+ .custom button.insert:hover:not(:disabled) {
166
+ background: var(--nile-colors-primary-700, #1d4ed8);
167
+ }
168
+
169
+ .custom button.insert:disabled {
170
+ opacity: 0.4;
171
+ cursor: default;
172
+ }
96
173
  `,
97
174
  ];
98
175
  }
@@ -113,16 +190,35 @@ export class NileWysiwygTablePicker extends NileElement {
113
190
 
114
191
  private pick(rows: number, cols: number): void {
115
192
  this.open = false;
193
+ this.customRows = '';
194
+ this.customCols = '';
116
195
  this.emit('wysiwyg-command', {
117
196
  name: 'insertTable',
118
197
  attrs: { rows, cols, withHeaderRow: true },
119
198
  });
120
199
  }
121
200
 
201
+ /** Clamp raw input to 1..max; returns 0 when empty/invalid. */
202
+ private clamp(raw: string, max: number): number {
203
+ const n = parseInt(raw, 10);
204
+ if (!Number.isFinite(n) || n < 1) return 0;
205
+ return Math.min(n, max);
206
+ }
207
+
208
+ private insertCustom(): void {
209
+ const rows = this.clamp(this.customRows, this.maxRows);
210
+ const cols = this.clamp(this.customCols, this.maxCols);
211
+ if (rows >= 1 && cols >= 1) this.pick(rows, cols);
212
+ }
213
+
122
214
  public render(): TemplateResult {
215
+ // Grow the grid by one row/col once the pointer reaches the edge, capped.
216
+ const visibleRows = Math.min(Math.max(MIN_ROWS, this.hoverRows + 1), this.maxRows);
217
+ const visibleCols = Math.min(Math.max(MIN_COLS, this.hoverCols + 1), this.maxCols);
218
+
123
219
  const cells: TemplateResult[] = [];
124
- for (let r = 1; r <= MAX_ROWS; r += 1) {
125
- for (let c = 1; c <= MAX_COLS; c += 1) {
220
+ for (let r = 1; r <= visibleRows; r += 1) {
221
+ for (let c = 1; c <= visibleCols; c += 1) {
126
222
  cells.push(html`
127
223
  <span
128
224
  class="cell ${r <= this.hoverRows && c <= this.hoverCols ? 'hot' : ''}"
@@ -135,6 +231,11 @@ export class NileWysiwygTablePicker extends NileElement {
135
231
  `);
136
232
  }
137
233
  }
234
+
235
+ const customValid =
236
+ this.clamp(this.customRows, this.maxRows) >= 1 &&
237
+ this.clamp(this.customCols, this.maxCols) >= 1;
238
+
138
239
  return html`
139
240
  <button
140
241
  type="button"
@@ -154,11 +255,60 @@ export class NileWysiwygTablePicker extends NileElement {
154
255
  </button>
155
256
  ${this.open
156
257
  ? html`
157
- <div class="panel" @mousedown=${(e: Event) => e.preventDefault()}>
158
- <div class="grid">${cells}</div>
159
- <div class="label">
258
+ <div class="panel">
259
+ <div
260
+ class="grid"
261
+ style="grid-template-columns: repeat(${visibleCols}, 14px)"
262
+ @mousedown=${(e: Event) => e.preventDefault()}
263
+ @mouseleave=${() => {
264
+ this.hoverRows = 0;
265
+ this.hoverCols = 0;
266
+ }}
267
+ >
268
+ ${cells}
269
+ </div>
270
+ <div class="label" @mousedown=${(e: Event) => e.preventDefault()}>
160
271
  ${this.hoverRows > 0 ? `${this.hoverRows} × ${this.hoverCols}` : 'Pick a size'}
161
272
  </div>
273
+ <div class="custom">
274
+ <input
275
+ type="number"
276
+ min="1"
277
+ max=${this.maxRows}
278
+ aria-label="Rows"
279
+ placeholder="Rows"
280
+ .value=${this.customRows}
281
+ @input=${(e: Event) => {
282
+ this.customRows = (e.target as HTMLInputElement).value;
283
+ }}
284
+ @keydown=${(e: KeyboardEvent) => {
285
+ if (e.key === 'Enter') this.insertCustom();
286
+ }}
287
+ />
288
+ <span class="times">×</span>
289
+ <input
290
+ type="number"
291
+ min="1"
292
+ max=${this.maxCols}
293
+ aria-label="Columns"
294
+ placeholder="Cols"
295
+ .value=${this.customCols}
296
+ @input=${(e: Event) => {
297
+ this.customCols = (e.target as HTMLInputElement).value;
298
+ }}
299
+ @keydown=${(e: KeyboardEvent) => {
300
+ if (e.key === 'Enter') this.insertCustom();
301
+ }}
302
+ />
303
+ <button
304
+ type="button"
305
+ class="insert"
306
+ ?disabled=${!customValid}
307
+ @click=${() => this.insertCustom()}
308
+ >
309
+ Insert
310
+ </button>
311
+ </div>
162
312
  </div>
163
313
  `
164
314
  : ''}