@aquera/nile-elements 2.0.16 → 2.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/{index-f6944844.cjs.js → index-0c0c869d.cjs.js} +2 -2
- package/dist/{index-f6944844.cjs.js.map → index-0c0c869d.cjs.js.map} +1 -1
- package/dist/{index-403841c7.cjs.js → index-129ef6fb.cjs.js} +3 -3
- package/dist/{index-403841c7.cjs.js.map → index-129ef6fb.cjs.js.map} +1 -1
- package/dist/{index-1faa963c.esm.js → index-36f2a6b5.esm.js} +1 -1
- package/dist/index-4df50481.esm.js +1 -0
- package/dist/index-5d34cff1.esm.js +1 -0
- package/dist/index-7ae95edd.cjs.js +2 -0
- package/dist/{index-3ca67627.cjs.js.map → index-7ae95edd.cjs.js.map} +1 -1
- package/dist/index-7ca72b78.esm.js +1 -0
- package/dist/index-bbe23ad8.cjs.js +2 -0
- package/dist/index-bbe23ad8.cjs.js.map +1 -0
- package/dist/index-ccc6a799.cjs.js +5 -0
- package/dist/index-ccc6a799.cjs.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +686 -464
- package/dist/nile-auto-complete/index.cjs.js +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.cjs.js +1 -1
- package/dist/nile-auto-complete/portal-manager.cjs.js +1 -1
- package/dist/nile-auto-complete/portal-utils.cjs.js +1 -1
- package/dist/nile-code-editor/extensionSetup.cjs.js +2 -2
- package/dist/nile-code-editor/index.cjs.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.cjs.js +1 -1
- package/dist/nile-code-editor/theme.cjs.js +1 -1
- package/dist/nile-color-picker/index.cjs.js +1 -1
- package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
- package/dist/nile-combobox/index.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
- package/dist/nile-combobox/portal-manager.cjs.js +1 -1
- package/dist/nile-context-menu/index.cjs.js +1 -1
- package/dist/nile-context-menu/nile-context-menu.cjs.js +1 -1
- package/dist/nile-context-submenu/index.cjs.js +1 -1
- package/dist/nile-context-submenu/nile-context-submenu.cjs.js +1 -1
- package/dist/nile-detail/index.cjs.js +1 -1
- package/dist/nile-detail/nile-detail.cjs.js +1 -1
- package/dist/nile-dropdown/index.cjs.js +1 -1
- package/dist/nile-dropdown/nile-dropdown.cjs.js +1 -1
- package/dist/nile-dropdown/portal-manager.cjs.js +1 -1
- package/dist/nile-file-upload/index.cjs.js +1 -1
- package/dist/nile-file-upload/nile-file-upload.cjs.js +1 -1
- package/dist/nile-file-upload/nile-file-upload.template.cjs.js +1 -1
- package/dist/nile-floating-panel/index.cjs.js +1 -1
- package/dist/nile-floating-panel/nile-floating-panel.cjs.js +1 -1
- package/dist/nile-lite-tooltip/index.cjs.js +1 -1
- package/dist/nile-lite-tooltip/nile-lite-tooltip.cjs.js +1 -1
- package/dist/nile-markdown-editor/index.cjs.js +1 -1
- package/dist/nile-markdown-editor/nile-markdown-editor.cjs.js +1 -1
- package/dist/nile-minimap/index.cjs.js +2 -0
- package/dist/nile-minimap/index.cjs.js.map +1 -0
- package/dist/nile-minimap/index.esm.js +1 -0
- package/dist/nile-minimap/nile-minimap.cjs.js +2 -0
- package/dist/nile-minimap/nile-minimap.cjs.js.map +1 -0
- package/dist/nile-minimap/nile-minimap.css.cjs.js +2 -0
- package/dist/nile-minimap/nile-minimap.css.cjs.js.map +1 -0
- package/dist/nile-minimap/nile-minimap.css.esm.js +150 -0
- package/dist/nile-minimap/nile-minimap.esm.js +22 -0
- package/dist/nile-popover/index.cjs.js +1 -1
- package/dist/nile-popover/nile-popover.cjs.js +1 -1
- package/dist/nile-popover/portal-manager.cjs.js +1 -1
- package/dist/nile-popup/index.cjs.js +1 -1
- package/dist/nile-popup/nile-popup.cjs.js +1 -1
- package/dist/nile-segmented-progress-bar/index.cjs.js +1 -1
- package/dist/nile-segmented-progress-bar/nile-segmented-progress-bar.cjs.js +1 -1
- package/dist/nile-select/index.cjs.js +1 -1
- package/dist/nile-select/nile-select.cjs.js +1 -1
- package/dist/nile-select/portal-manager.cjs.js +1 -1
- package/dist/nile-side-bar-action/index.cjs.js +1 -1
- package/dist/nile-side-bar-action/nile-side-bar-action.cjs.js +1 -1
- package/dist/nile-side-bar-action/portal-manager.cjs.js +1 -1
- package/dist/nile-tooltip/index.cjs.js +1 -1
- package/dist/nile-tooltip/nile-tooltip.cjs.js +1 -1
- package/dist/nile-virtual-select/index.cjs.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js +1 -1
- package/dist/nile-virtual-select/portal-manager.cjs.js +1 -1
- package/dist/nile-virtual-select/renderer.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/email-css.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/email-css.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/email-render.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/email-render.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/html-detect.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/html-detect.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/inputrules.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/link.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +2 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/serializer.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +45 -4
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +2 -2
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.esm.js +2 -2
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +5 -5
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-menu.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/find-replace.esm.js +3 -3
- package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/image-menu.esm.js +3 -3
- package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/image-panel.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/insert-menu.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/link-editor.esm.js +9 -9
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +4 -4
- package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/special-chars.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +23 -12
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +7 -7
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-minimap/index.d.ts +1 -0
- package/dist/src/nile-minimap/index.js +2 -0
- package/dist/src/nile-minimap/index.js.map +1 -0
- package/dist/src/nile-minimap/nile-minimap.css.d.ts +12 -0
- package/dist/src/nile-minimap/nile-minimap.css.js +162 -0
- package/dist/src/nile-minimap/nile-minimap.css.js.map +1 -0
- package/dist/src/nile-minimap/nile-minimap.d.ts +131 -0
- package/dist/src/nile-minimap/nile-minimap.js +704 -0
- package/dist/src/nile-minimap/nile-minimap.js.map +1 -0
- package/dist/src/nile-minimap/nile-minimap.test.d.ts +1 -0
- package/dist/src/nile-minimap/nile-minimap.test.js +785 -0
- package/dist/src/nile-minimap/nile-minimap.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.d.ts +5 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.js +2 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/email-css.d.ts +14 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-css.js +140 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-css.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js +1045 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-fidelity.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-render.d.ts +11 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-render.js +93 -0
- package/dist/src/nile-wysiwyg-editor/engine/email-render.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/html-detect.d.ts +2 -0
- package/dist/src/nile-wysiwyg-editor/engine/html-detect.js +24 -0
- package/dist/src/nile-wysiwyg-editor/engine/html-detect.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +4 -1
- package/dist/src/nile-wysiwyg-editor/engine/index.js +3 -1
- package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.d.ts +0 -5
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.js +19 -0
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js +3 -2
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.d.ts +4 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js +56 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.d.ts +5 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js +27 -6
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +30 -7
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +46 -0
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +9 -1
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +730 -7
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +3 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.js +215 -34
- package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/selection-state.js +3 -1
- package/dist/src/nile-wysiwyg-editor/engine/selection-state.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/serializer.d.ts +12 -4
- package/dist/src/nile-wysiwyg-editor/engine/serializer.js +18 -8
- package/dist/src/nile-wysiwyg-editor/engine/serializer.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js +287 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +45 -4
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +23 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +113 -15
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +213 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js +2 -2
- package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/find-replace.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/find-replace.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/image-menu.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/image-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/image-panel.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/image-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/link-editor.js +2 -2
- package/dist/src/nile-wysiwyg-editor/ui/link-editor.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js +2 -2
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/special-chars.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/special-chars.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +14 -3
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +2 -2
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/index.ts +1 -0
- package/src/nile-minimap/index.ts +1 -0
- package/src/nile-minimap/nile-minimap.css.ts +164 -0
- package/src/nile-minimap/nile-minimap.test.ts +924 -0
- package/src/nile-minimap/nile-minimap.ts +796 -0
- package/src/nile-wysiwyg-editor/engine/create-state.ts +7 -0
- package/src/nile-wysiwyg-editor/engine/email-css.ts +136 -0
- package/src/nile-wysiwyg-editor/engine/email-fidelity.test.ts +1347 -0
- package/src/nile-wysiwyg-editor/engine/email-render.ts +99 -0
- package/src/nile-wysiwyg-editor/engine/html-detect.ts +26 -0
- package/src/nile-wysiwyg-editor/engine/index.ts +10 -1
- package/src/nile-wysiwyg-editor/engine/inputrules.ts +18 -1
- package/src/nile-wysiwyg-editor/engine/plugins/image-view.ts +3 -2
- package/src/nile-wysiwyg-editor/engine/plugins/link.ts +58 -2
- package/src/nile-wysiwyg-editor/engine/plugins/paste.ts +31 -8
- package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +29 -7
- package/src/nile-wysiwyg-editor/engine/pm.test.ts +73 -0
- package/src/nile-wysiwyg-editor/engine/sanitize.ts +729 -7
- package/src/nile-wysiwyg-editor/engine/schema.ts +206 -33
- package/src/nile-wysiwyg-editor/engine/selection-state.ts +3 -1
- package/src/nile-wysiwyg-editor/engine/serializer.ts +30 -8
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor-email-fidelity.test.ts +357 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +45 -4
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +258 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +119 -12
- package/src/nile-wysiwyg-editor/ui/bookmark-panel.ts +2 -2
- package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/embed-menu.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/embed-panel.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/emoji-picker.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/find-replace.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/image-menu.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/image-panel.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/insert-menu.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/link-editor.ts +2 -2
- package/src/nile-wysiwyg-editor/ui/markdown-panel.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/math-panel.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/shortcut-help.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/source-view.ts +2 -2
- package/src/nile-wysiwyg-editor/ui/special-chars.ts +1 -1
- package/src/nile-wysiwyg-editor/ui/suggest-list.ts +14 -3
- package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +2 -2
- package/src/nile-wysiwyg-editor/ui/table-menu.ts +1 -1
- package/vscode-html-custom-data.json +116 -25
- package/web-test-runner.config.mjs +4 -0
- package/dist/index-3ca67627.cjs.js +0 -2
- package/dist/index-6f3fa4ab.esm.js +0 -1
- package/dist/index-7b1c9857.esm.js +0 -1
- package/dist/index-89c520e9.cjs.js +0 -5
- package/dist/index-89c520e9.cjs.js.map +0 -1
- package/dist/index-ba47645d.esm.js +0 -1
- package/dist/index-c3210a9c.cjs.js +0 -2
- package/dist/index-c3210a9c.cjs.js.map +0 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Fragment, Node as PMNode } from 'prosemirror-model';
|
|
2
|
+
|
|
3
|
+
/** email-fidelity: writes the editor's data-table rendering into the saved HTML; layout tables keep their authored output. */
|
|
4
|
+
|
|
5
|
+
/** Theme-resolved colours the editor draws data tables with; null = not drawn. */
|
|
6
|
+
export interface EmailRenderDefaults {
|
|
7
|
+
borderColor: string | null;
|
|
8
|
+
headerBackground: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Kept in sync by hand with `.ProseMirror td, .ProseMirror th { padding: 6px 8px }`. */
|
|
12
|
+
const CELL_PADDING: Record<string, string> = {
|
|
13
|
+
paddingTop: '6px',
|
|
14
|
+
paddingRight: '8px',
|
|
15
|
+
paddingBottom: '6px',
|
|
16
|
+
paddingLeft: '8px',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const CELL_BORDERS = ['borderTop', 'borderRight', 'borderBottom', 'borderLeft'];
|
|
20
|
+
|
|
21
|
+
/** `.ProseMirror td > *` zeroes block margins inside cells. */
|
|
22
|
+
const CELL_CHILD_MARGIN = 'margin-top: 0px; margin-bottom: 0px';
|
|
23
|
+
|
|
24
|
+
type Context = 'data-table' | 'data-cell' | null;
|
|
25
|
+
|
|
26
|
+
/** Returns a copy of `doc` with the editor's data-table rendering filled into node attrs. */
|
|
27
|
+
export function applyEmailRenderDefaults(doc: PMNode, defaults: EmailRenderDefaults): PMNode {
|
|
28
|
+
return fill(doc, null, defaults);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function fill(node: PMNode, context: Context, defaults: EmailRenderDefaults): PMNode {
|
|
32
|
+
if (node.isLeaf) return node;
|
|
33
|
+
const name = node.type.name;
|
|
34
|
+
let attrs = node.attrs;
|
|
35
|
+
let childContext: Context = null;
|
|
36
|
+
|
|
37
|
+
if (name === 'table') {
|
|
38
|
+
if (!attrs.layout) {
|
|
39
|
+
childContext = 'data-table';
|
|
40
|
+
if (!attrs.width) attrs = { ...attrs, width: '100%' };
|
|
41
|
+
}
|
|
42
|
+
} else if (name === 'table_row') {
|
|
43
|
+
childContext = context;
|
|
44
|
+
} else if ((name === 'table_cell' || name === 'table_header') && context === 'data-table') {
|
|
45
|
+
attrs = cellDefaults(attrs, name === 'table_header', defaults);
|
|
46
|
+
childContext = 'data-cell';
|
|
47
|
+
} else if (context === 'data-cell' && 'margin' in attrs && !attrs.margin) {
|
|
48
|
+
attrs = { ...attrs, margin: CELL_CHILD_MARGIN };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const children: PMNode[] = [];
|
|
52
|
+
node.forEach(child => children.push(fill(child, childContext, defaults)));
|
|
53
|
+
return node.type.create(attrs, Fragment.fromArray(children), node.marks);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cellDefaults(
|
|
57
|
+
attrs: Record<string, any>,
|
|
58
|
+
header: boolean,
|
|
59
|
+
defaults: EmailRenderDefaults
|
|
60
|
+
): Record<string, any> {
|
|
61
|
+
const next = { ...attrs };
|
|
62
|
+
for (const [key, value] of Object.entries(CELL_PADDING)) if (!next[key]) next[key] = value;
|
|
63
|
+
if (!next.valign) next.valign = 'top';
|
|
64
|
+
if (defaults.borderColor) {
|
|
65
|
+
const border = `1px solid ${defaults.borderColor}`;
|
|
66
|
+
for (const key of CELL_BORDERS) if (!next[key]) next[key] = border;
|
|
67
|
+
}
|
|
68
|
+
// A resized column keeps its width in data-colwidth, which mail clients ignore.
|
|
69
|
+
const colwidth = next.colwidth as number[] | null;
|
|
70
|
+
if (!next.width && Array.isArray(colwidth) && colwidth.length && colwidth.every(w => w > 0)) {
|
|
71
|
+
next.width = `${colwidth.reduce((sum, w) => sum + w, 0)}px`;
|
|
72
|
+
}
|
|
73
|
+
if (header) {
|
|
74
|
+
if (!next.background && defaults.headerBackground) next.background = defaults.headerBackground;
|
|
75
|
+
if (!next.textAlign) next.textAlign = 'left';
|
|
76
|
+
const textStyle = (next.textStyle as string | null) ?? '';
|
|
77
|
+
if (!/font-weight/i.test(textStyle)) {
|
|
78
|
+
next.textStyle = textStyle ? `${textStyle}; font-weight: 600` : 'font-weight: 600';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Collapsed borders and no cell spacing on every table, fixed columns on data tables. */
|
|
85
|
+
export function finishEmailTables(container: HTMLElement): void {
|
|
86
|
+
for (const table of Array.from(container.querySelectorAll('table'))) {
|
|
87
|
+
if (!table.hasAttribute('cellpadding')) table.setAttribute('cellpadding', '0');
|
|
88
|
+
if (!table.hasAttribute('cellspacing')) table.setAttribute('cellspacing', '0');
|
|
89
|
+
const style = table.getAttribute('style') ?? '';
|
|
90
|
+
const add: string[] = [];
|
|
91
|
+
if (!/(^|;)\s*border-collapse\s*:/i.test(style)) add.push('border-collapse: collapse');
|
|
92
|
+
const layout = table.getAttribute('role') === 'presentation';
|
|
93
|
+
if (!layout && !/(^|;)\s*table-layout\s*:/i.test(style)) add.push('table-layout: fixed');
|
|
94
|
+
if (add.length) {
|
|
95
|
+
const base = style.trim().replace(/;\s*$/, '');
|
|
96
|
+
table.setAttribute('style', base ? `${base}; ${add.join('; ')}` : add.join('; '));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Conservative check: does a text/plain paste look like HTML rather than prose or Markdown? */
|
|
2
|
+
|
|
3
|
+
/** Block-level tags whose balanced open+close pair marks real HTML structure. */
|
|
4
|
+
const BLOCK_TAGS = 'table|div|p|h1|h2|h3|h4|h5|h6|ul|ol|blockquote|section|body';
|
|
5
|
+
|
|
6
|
+
const BALANCED_BLOCK = new RegExp(
|
|
7
|
+
`<(${BLOCK_TAGS})(?:\\s[^>]*)?>[\\s\\S]*?</\\1\\s*>`,
|
|
8
|
+
'i'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export function looksLikeHtml(text: string): boolean {
|
|
12
|
+
if (!text) return false;
|
|
13
|
+
// Skip leading whitespace and HTML comments so a doctype/`<html>` preamble
|
|
14
|
+
// behind a comment still counts.
|
|
15
|
+
let head = text.replace(/^\s+/, '');
|
|
16
|
+
while (head.startsWith('<!--')) {
|
|
17
|
+
const end = head.indexOf('-->');
|
|
18
|
+
if (end === -1) break;
|
|
19
|
+
head = head.slice(end + 3).replace(/^\s+/, '');
|
|
20
|
+
}
|
|
21
|
+
const lower = head.toLowerCase();
|
|
22
|
+
if (lower.startsWith('<!doctype html') || lower.startsWith('<html')) return true;
|
|
23
|
+
// A balanced block-level element anywhere is enough; the backreference keeps
|
|
24
|
+
// the open and close tag names matched.
|
|
25
|
+
return BALANCED_BLOCK.test(text);
|
|
26
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
export { createSchema, defaultSchema } from './schema';
|
|
2
2
|
export type { Alignment } from './schema';
|
|
3
|
-
export { sanitizeHtml, isSafeUrl } from './sanitize';
|
|
3
|
+
export { sanitizeHtml, isSafeUrl, relocateEmailContainers } from './sanitize';
|
|
4
|
+
export {
|
|
5
|
+
normalizeEmailAllowedStyles,
|
|
6
|
+
parseAllowedStylesAttr,
|
|
7
|
+
isExtensionPropValid,
|
|
8
|
+
isExtensionValueValid,
|
|
9
|
+
sanitizeExtraStyleForWrite,
|
|
10
|
+
} from './email-css';
|
|
11
|
+
export type { NormalizedAllowedStyles } from './email-css';
|
|
12
|
+
export { looksLikeHtml } from './html-detect';
|
|
4
13
|
export {
|
|
5
14
|
htmlToDoc,
|
|
6
15
|
htmlToSlice,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
InputRule,
|
|
6
6
|
} from 'prosemirror-inputrules';
|
|
7
7
|
import { MarkType, Schema } from 'prosemirror-model';
|
|
8
|
-
import { Plugin } from 'prosemirror-state';
|
|
8
|
+
import { Plugin, TextSelection } from 'prosemirror-state';
|
|
9
9
|
|
|
10
10
|
function escapeRegex(text: string): string {
|
|
11
11
|
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -128,6 +128,21 @@ function linkifyRule(schema: Schema): InputRule {
|
|
|
128
128
|
* (**bold**, *italic*, `code`, ~~strike~~), and (unless disabled) the
|
|
129
129
|
* typography autocorrect rules.
|
|
130
130
|
*/
|
|
131
|
+
/** "---", "***" or "___" alone in a top-level paragraph → horizontal rule. */
|
|
132
|
+
function horizontalRuleRule(schema: Schema): InputRule {
|
|
133
|
+
return new InputRule(/^(?:---|\*\*\*|___)$/, (state, _match, start) => {
|
|
134
|
+
const $start = state.doc.resolve(start);
|
|
135
|
+
const { paragraph, horizontal_rule: hr } = schema.nodes;
|
|
136
|
+
// The rule's two earlier characters must be the whole paragraph.
|
|
137
|
+
if ($start.parent.type !== paragraph || $start.parent.content.size !== 2) return null;
|
|
138
|
+
const index = $start.index(-1);
|
|
139
|
+
if (!$start.node(-1).canReplaceWith(index, index + 1, hr)) return null;
|
|
140
|
+
const before = $start.before();
|
|
141
|
+
const tr = state.tr.replaceWith(before, $start.after(), [hr.create(), paragraph.create()]);
|
|
142
|
+
return tr.setSelection(TextSelection.create(tr.doc, before + 2)).scrollIntoView();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
131
146
|
export function buildInputRules(schema: Schema, options: InputRulesOptions = {}): Plugin {
|
|
132
147
|
const transforms = options.textTransform === false ? [] : textTransformRules();
|
|
133
148
|
return inputRules({
|
|
@@ -152,6 +167,8 @@ export function buildInputRules(schema: Schema, options: InputRulesOptions = {})
|
|
|
152
167
|
),
|
|
153
168
|
// "```" → code block
|
|
154
169
|
textblockTypeInputRule(/^```$/, schema.nodes.code_block),
|
|
170
|
+
// "---" / "***" / "___" → horizontal rule
|
|
171
|
+
horizontalRuleRule(schema),
|
|
155
172
|
// "[] " or "[ ] " → checklist
|
|
156
173
|
wrappingInputRule(/^\s*\[[ ]?\]\s$/, schema.nodes.task_list),
|
|
157
174
|
// Inline marks. Bold before italic so '**' is never read as italic;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Node as PMNode } from 'prosemirror-model';
|
|
2
2
|
import { EditorView, NodeView } from 'prosemirror-view';
|
|
3
3
|
import { NodeSelection } from 'prosemirror-state';
|
|
4
|
+
import { imageWidthCss } from '../schema';
|
|
4
5
|
|
|
5
6
|
const MIN_WIDTH = 40;
|
|
6
7
|
|
|
@@ -92,7 +93,7 @@ export class ImageView implements NodeView {
|
|
|
92
93
|
else this.img.removeAttribute('alt');
|
|
93
94
|
if (title) this.img.title = title;
|
|
94
95
|
else this.img.removeAttribute('title');
|
|
95
|
-
this.img.style.width = width
|
|
96
|
+
this.img.style.width = imageWidthCss(width) ?? '';
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
update(node: PMNode): boolean {
|
|
@@ -183,7 +184,7 @@ export class FigureView implements NodeView {
|
|
|
183
184
|
else this.img.removeAttribute('alt');
|
|
184
185
|
if (title) this.img.title = title;
|
|
185
186
|
else this.img.removeAttribute('title');
|
|
186
|
-
this.dom.style.width = width
|
|
187
|
+
this.dom.style.width = imageWidthCss(width) ?? '';
|
|
187
188
|
if (align) this.dom.setAttribute('data-align', align);
|
|
188
189
|
else this.dom.removeAttribute('data-align');
|
|
189
190
|
this.dom.classList.toggle(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Plugin } from 'prosemirror-state';
|
|
1
|
+
import { Plugin, PluginKey, EditorState } from 'prosemirror-state';
|
|
2
|
+
import { Mark } from 'prosemirror-model';
|
|
2
3
|
import { isSafeUrl } from '../sanitize';
|
|
3
4
|
|
|
4
5
|
const URL_PATTERN = /^https?:\/\/\S+$/i;
|
|
@@ -8,14 +9,69 @@ export interface LinkPluginOptions {
|
|
|
8
9
|
onOpenLink?: (href: string) => void;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
/** Where retyped link text continues: the caret position and the link it keeps. */
|
|
13
|
+
interface RetypeState {
|
|
14
|
+
pos: number;
|
|
15
|
+
mark: Mark;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const retypeKey = new PluginKey<RetypeState | null>('nile-wysiwyg-link-retype');
|
|
19
|
+
|
|
20
|
+
/** The link mark covering every text node in [from, to), or null. */
|
|
21
|
+
function linkCovering(state: EditorState, from: number, to: number): Mark | null {
|
|
22
|
+
const type = state.schema.marks.link;
|
|
23
|
+
let mark: Mark | null = null;
|
|
24
|
+
let covered = true;
|
|
25
|
+
state.doc.nodesBetween(from, to, node => {
|
|
26
|
+
if (!covered || !node.isText) return;
|
|
27
|
+
const found = type.isInSet(node.marks);
|
|
28
|
+
if (!found || (mark && !found.eq(mark))) covered = false;
|
|
29
|
+
else mark = found;
|
|
30
|
+
});
|
|
31
|
+
return covered ? mark : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
11
34
|
/**
|
|
12
35
|
* Link behaviors that belong to the document, not the chrome:
|
|
13
36
|
* - Mod/Ctrl-click opens the link.
|
|
14
37
|
* - Pasting a bare URL over a text selection turns the selection into a link.
|
|
38
|
+
* - Typing over a selection that is entirely one link keeps the link, for the
|
|
39
|
+
* replaced text and for the characters typed straight after it (the link mark
|
|
40
|
+
* is non-inclusive, so without this the new label drops out of the link — an
|
|
41
|
+
* email button renamed by retyping lost its href).
|
|
15
42
|
*/
|
|
16
43
|
export function linkPlugin(options: LinkPluginOptions = {}): Plugin {
|
|
17
|
-
return new Plugin({
|
|
44
|
+
return new Plugin<RetypeState | null>({
|
|
45
|
+
key: retypeKey,
|
|
46
|
+
state: {
|
|
47
|
+
init: () => null,
|
|
48
|
+
apply(tr, prev) {
|
|
49
|
+
const meta = tr.getMeta(retypeKey) as RetypeState | null | undefined;
|
|
50
|
+
if (meta !== undefined) return meta;
|
|
51
|
+
if (!prev) return null;
|
|
52
|
+
// Any caret move, or an edit elsewhere, ends the retype run.
|
|
53
|
+
if (!tr.docChanged) return tr.selectionSet ? null : prev;
|
|
54
|
+
const pos = tr.mapping.map(prev.pos);
|
|
55
|
+
return tr.selection.empty && tr.selection.from === pos ? { ...prev, pos } : null;
|
|
56
|
+
},
|
|
57
|
+
},
|
|
18
58
|
props: {
|
|
59
|
+
handleTextInput(view, from, to, text) {
|
|
60
|
+
const { state } = view;
|
|
61
|
+
let mark: Mark | null = null;
|
|
62
|
+
if (from < to) mark = linkCovering(state, from, to);
|
|
63
|
+
else {
|
|
64
|
+
const run = retypeKey.getState(state);
|
|
65
|
+
if (run && run.pos === from) mark = run.mark;
|
|
66
|
+
}
|
|
67
|
+
if (!mark) return false;
|
|
68
|
+
const end = from + text.length;
|
|
69
|
+
const tr = state.tr.insertText(text, from, to).addMark(from, end, mark);
|
|
70
|
+
tr.setMeta(retypeKey, { pos: end, mark });
|
|
71
|
+
view.dispatch(tr);
|
|
72
|
+
return true;
|
|
73
|
+
},
|
|
74
|
+
|
|
19
75
|
handleClick(view, _pos, event) {
|
|
20
76
|
if (!(event.metaKey || event.ctrlKey)) return false;
|
|
21
77
|
const target = (event.target as HTMLElement | null)?.closest?.('a[href]');
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Plugin } from 'prosemirror-state';
|
|
2
2
|
import { Slice, Fragment, Node as PMNode, Schema } from 'prosemirror-model';
|
|
3
3
|
import { markdownToHtml, looksLikeMarkdown } from '../markdown-parse';
|
|
4
|
-
import {
|
|
4
|
+
import { looksLikeHtml } from '../html-detect';
|
|
5
|
+
import { htmlToDoc, htmlToSlice } from '../serializer';
|
|
6
|
+
import { NormalizedAllowedStyles } from '../email-css';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Builds a slice of plain paragraphs from text, splitting on newlines. Open
|
|
@@ -36,6 +38,10 @@ export interface PasteOptions {
|
|
|
36
38
|
getMarkdownPaste?: () => boolean;
|
|
37
39
|
/** Read live: whether pasted HTML is sanitized. Defaults to on. */
|
|
38
40
|
getSanitize?: () => boolean;
|
|
41
|
+
/** Read live: when true, HTML-looking plain text is parsed as HTML (email fidelity). */
|
|
42
|
+
getEmailFidelity?: () => boolean;
|
|
43
|
+
/** Read live: the validated per-app email-fidelity style allowlist. */
|
|
44
|
+
getAllowedStyles?: () => NormalizedAllowedStyles;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
/**
|
|
@@ -47,20 +53,37 @@ export function pastePlugin(schema: Schema, options: PasteOptions = {}): Plugin
|
|
|
47
53
|
return new Plugin({
|
|
48
54
|
props: {
|
|
49
55
|
handlePaste(view, event) {
|
|
50
|
-
if (options.getForcePlainText?.()) return false;
|
|
51
|
-
if (options.getMarkdownPaste && !options.getMarkdownPaste()) return false;
|
|
52
56
|
// Only plain-text pastes qualify: an HTML flavor means the source
|
|
53
57
|
// was already rich, and code blocks take text verbatim.
|
|
54
58
|
const html = event.clipboardData?.getData('text/html') ?? '';
|
|
55
59
|
const text = event.clipboardData?.getData('text/plain') ?? '';
|
|
56
60
|
if (html.trim() || !text.trim()) return false;
|
|
57
61
|
if (view.state.selection.$from.parent.type.spec.code) return false;
|
|
62
|
+
const forcePlain = options.getForcePlainText?.() ?? false;
|
|
63
|
+
const sanitize = options.getSanitize?.() ?? true;
|
|
64
|
+
|
|
65
|
+
// Precedence: paste-as-plain-text → HTML detection (fidelity) → Markdown.
|
|
66
|
+
if (looksLikeHtml(text)) {
|
|
67
|
+
if (options.getEmailFidelity?.() && !forcePlain) {
|
|
68
|
+
// Closed slice so the whole document (tables, blocks) lands intact
|
|
69
|
+
// rather than being opened and merged into the target paragraph.
|
|
70
|
+
const doc = htmlToDoc(schema, text, sanitize, {
|
|
71
|
+
emailFidelity: true,
|
|
72
|
+
allowedStyles: options.getAllowedStyles?.(),
|
|
73
|
+
});
|
|
74
|
+
const slice = new Slice(doc.content, 0, 0);
|
|
75
|
+
view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
|
|
76
|
+
} else {
|
|
77
|
+
const slice = plainTextSlice(schema, text);
|
|
78
|
+
view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (forcePlain) return false;
|
|
84
|
+
if (options.getMarkdownPaste && !options.getMarkdownPaste()) return false;
|
|
58
85
|
if (!looksLikeMarkdown(text)) return false;
|
|
59
|
-
const slice = htmlToSlice(
|
|
60
|
-
schema,
|
|
61
|
-
markdownToHtml(text),
|
|
62
|
-
options.getSanitize?.() ?? true
|
|
63
|
-
);
|
|
86
|
+
const slice = htmlToSlice(schema, markdownToHtml(text), sanitize);
|
|
64
87
|
view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
|
|
65
88
|
return true;
|
|
66
89
|
},
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { Plugin } from 'prosemirror-state';
|
|
2
2
|
import { Node as PMNode } from 'prosemirror-model';
|
|
3
3
|
import { tableEditing, columnResizing, TableView } from 'prosemirror-tables';
|
|
4
|
+
import { tableStyles } from '../schema';
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
-
* TableView that also reflects the author's `class`/`id` onto the inner
|
|
7
|
-
* `<table>` element (the base TableView leaves it bare and puts its own class on
|
|
8
|
-
* the wrapper div). The attributes round-trip via the schema's toDOM on export;
|
|
9
|
-
* this makes `table.grid …` style rules match in the live editor too. Passed to
|
|
10
|
-
* `columnResizing` as its `View`, so column resizing keeps working.
|
|
11
|
-
*/
|
|
6
|
+
/** TableView that also puts the author's class/id and fidelity table styles on the inner `<table>`. */
|
|
12
7
|
class HtmlAttrTableView extends TableView {
|
|
13
8
|
constructor(node: PMNode, defaultCellMinWidth: number) {
|
|
14
9
|
super(node, defaultCellMinWidth);
|
|
@@ -21,6 +16,33 @@ class HtmlAttrTableView extends TableView {
|
|
|
21
16
|
this.table.className = htmlClass ?? '';
|
|
22
17
|
if (htmlId) this.table.id = htmlId;
|
|
23
18
|
else this.table.removeAttribute('id');
|
|
19
|
+
// Email-fidelity attrs (width, max-width, background, border, centring).
|
|
20
|
+
// Width is only overridden when set, so column-resize sizing still applies.
|
|
21
|
+
const style = this.table.style;
|
|
22
|
+
// tableStyles() entries can hold several declarations ('color: x; font-family: y'
|
|
23
|
+
// for the table text style or an allowedStyles extension), so flatten them first.
|
|
24
|
+
const decls = tableStyles(node.attrs)
|
|
25
|
+
.flatMap(entry => entry.split(';'))
|
|
26
|
+
.map(decl => decl.trim())
|
|
27
|
+
.filter(decl => decl.includes(':'));
|
|
28
|
+
for (const prop of [
|
|
29
|
+
'max-width', 'background-color', 'border', 'margin-left', 'margin-right',
|
|
30
|
+
'color', 'font-family', 'font-size', 'font-weight', 'line-height',
|
|
31
|
+
]) {
|
|
32
|
+
style.removeProperty(prop);
|
|
33
|
+
}
|
|
34
|
+
// Clear any per-app extension properties first so a value that changed (or an
|
|
35
|
+
// entry that was removed) doesn't linger from the previous application.
|
|
36
|
+
for (const decl of decls) style.removeProperty(decl.slice(0, decl.indexOf(':')).trim());
|
|
37
|
+
for (const decl of decls) {
|
|
38
|
+
const idx = decl.indexOf(':');
|
|
39
|
+
style.setProperty(decl.slice(0, idx).trim(), decl.slice(idx + 1).trim());
|
|
40
|
+
}
|
|
41
|
+
// A layout table with no width of its own (an email button) shrinks to fit
|
|
42
|
+
// under email-fidelity; the host CSS keys off this hook.
|
|
43
|
+
this.table.toggleAttribute('data-autowidth', Boolean(node.attrs.layout && !node.attrs.width));
|
|
44
|
+
// Lets the host CSS drop column resizing's min-width on layout tables.
|
|
45
|
+
this.table.toggleAttribute('data-layout-table', Boolean(node.attrs.layout));
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
update(node: PMNode): boolean {
|
|
@@ -948,6 +948,79 @@ describe('figure & image commands', () => {
|
|
|
948
948
|
});
|
|
949
949
|
});
|
|
950
950
|
|
|
951
|
+
describe('image percentage widths (fix 1, global)', () => {
|
|
952
|
+
const ON = { emailFidelity: true } as const;
|
|
953
|
+
|
|
954
|
+
/** First <img> style.width after a full htmlToDoc→docToHtml round-trip. */
|
|
955
|
+
function imgWidth(html: string, on = false): string {
|
|
956
|
+
const doc = on
|
|
957
|
+
? htmlToDoc(defaultSchema, html, true, ON)
|
|
958
|
+
: htmlToDoc(defaultSchema, html);
|
|
959
|
+
const out = docToHtml(doc);
|
|
960
|
+
const m = new DOMParser().parseFromString(out, 'text/html');
|
|
961
|
+
return m.querySelector('img')?.style.width ?? '';
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
it('round-trips width:100% as a percentage with the flag off', () => {
|
|
965
|
+
expect(imgWidth('<p><img src="https://x.co/i.png" style="width:100%"></p>')).to.equal('100%');
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
it('round-trips width:100% as a percentage with the flag on', () => {
|
|
969
|
+
expect(
|
|
970
|
+
imgWidth('<p><img src="https://x.co/i.png" style="width:100%"></p>', true)
|
|
971
|
+
).to.equal('100%');
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('keeps a fractional percentage (50.5%)', () => {
|
|
975
|
+
expect(imgWidth('<p><img src="https://x.co/i.png" style="width:50.5%"></p>')).to.equal('50.5%');
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
it('still emits a pixel width for width:320px', () => {
|
|
979
|
+
expect(imgWidth('<p><img src="https://x.co/i.png" style="width:320px"></p>')).to.equal('320px');
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
it('falls back to the width attribute (px) when there is no style width', () => {
|
|
983
|
+
expect(imgWidth('<p><img src="https://x.co/i.png" width="1232"></p>')).to.equal('1232px');
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
it('drops an out-of-range percentage (150%)', () => {
|
|
987
|
+
const out = docToHtml(
|
|
988
|
+
htmlToDoc(defaultSchema, '<p><img src="https://x.co/i.png" style="width:150%"></p>')
|
|
989
|
+
);
|
|
990
|
+
expect(out).to.not.include('150%');
|
|
991
|
+
expect(out).to.not.include('width: 150');
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it('drops a calc() width', () => {
|
|
995
|
+
const out = docToHtml(
|
|
996
|
+
htmlToDoc(
|
|
997
|
+
defaultSchema,
|
|
998
|
+
'<p><img src="https://x.co/i.png" style="width:calc(100% - 2px)"></p>',
|
|
999
|
+
true,
|
|
1000
|
+
ON
|
|
1001
|
+
)
|
|
1002
|
+
);
|
|
1003
|
+
expect(out).to.not.include('calc(');
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
it('round-trips a percentage width on a figure', () => {
|
|
1007
|
+
const out = docToHtml(
|
|
1008
|
+
htmlToDoc(
|
|
1009
|
+
defaultSchema,
|
|
1010
|
+
'<figure><img src="https://x.co/i.png" style="width:50%"><figcaption>c</figcaption></figure>'
|
|
1011
|
+
)
|
|
1012
|
+
);
|
|
1013
|
+
expect(out).to.include('width: 50%');
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
it('serialize → parse → serialize is a fixpoint for a percentage width', () => {
|
|
1017
|
+
const src = '<p><img src="https://x.co/i.png" style="width:100%"></p>';
|
|
1018
|
+
const once = docToHtml(htmlToDoc(defaultSchema, src, true, ON));
|
|
1019
|
+
const twice = docToHtml(htmlToDoc(defaultSchema, once, true, ON));
|
|
1020
|
+
expect(twice).to.equal(once);
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
|
|
951
1024
|
describe('paste from Office', () => {
|
|
952
1025
|
it('detects Word and Google Docs clipboard HTML', () => {
|
|
953
1026
|
expect(isOfficeHtml('<p class="MsoNormal">x</p>')).to.equal(true);
|