@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.
- package/README.md +3 -0
- package/dist/index-178e1e57.cjs.js +2 -0
- package/dist/index-178e1e57.cjs.js.map +1 -0
- package/dist/index-1faa963c.esm.js +1 -0
- package/dist/index-249de90a.esm.js +1 -0
- package/dist/index-353172ed.esm.js +1 -0
- package/dist/index-6f3fa4ab.esm.js +1 -0
- package/dist/index-89c520e9.cjs.js +5 -0
- package/dist/index-89c520e9.cjs.js.map +1 -0
- package/dist/index-920675c4.esm.js +1 -0
- package/dist/index-b387a9f4.cjs.js +2 -0
- package/dist/index-b387a9f4.cjs.js.map +1 -0
- package/dist/index-cc1385e0.cjs.js +2 -0
- package/dist/index-cc1385e0.cjs.js.map +1 -0
- package/dist/index-dedc38a6.esm.js +1 -0
- package/dist/index-df196058.cjs.js +2 -0
- package/dist/index-df196058.cjs.js.map +1 -0
- package/dist/index-f956f56d.cjs.js +7 -0
- package/dist/index-f956f56d.cjs.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +3595 -629
- package/dist/internal/glyph.cjs.js +2 -0
- package/dist/internal/glyph.cjs.js.map +1 -0
- package/dist/internal/glyph.esm.js +5 -0
- 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 +7 -7
- package/dist/nile-code-editor/extensionSetup.cjs.js.map +1 -1
- package/dist/nile-code-editor/extensionSetup.esm.js +1 -1
- package/dist/nile-code-editor/index.cjs.js +1 -1
- package/dist/nile-code-editor/index.esm.js +1 -1
- package/dist/nile-code-editor/nile-code-editor.cjs.js +2 -2
- package/dist/nile-code-editor/nile-code-editor.cjs.js.map +1 -1
- package/dist/nile-code-editor/nile-code-editor.esm.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/index.esm.js +1 -1
- package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.esm.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/index.esm.js +1 -1
- package/dist/nile-detail/nile-detail.cjs.js +1 -1
- package/dist/nile-detail/nile-detail.esm.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-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-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/emoji.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/emoji.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/emoji.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/export.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/export.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/export.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/index.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/index.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/inputrules.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/markdown-parse.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/markdown-parse.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/markdown-parse.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/markdown.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/markdown.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/markdown.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/office-paste.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/office-paste.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/office-paste.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/format-painter.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image-view.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/link.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/link.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/max-length.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/paste.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/single-line.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/task-list.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/task-list.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/task-list.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/serializer.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/serializer.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/index.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/index.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/index.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +780 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +97 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.esm.js +244 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.esm.js +137 -0
- package/dist/nile-wysiwyg-editor/types.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/types.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/types.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/bookmark-panel.esm.js +97 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +67 -0
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +65 -0
- package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/embed-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/embed-menu.esm.js +64 -0
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +68 -0
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/emoji-picker.esm.js +96 -0
- package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/find-replace.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/find-replace.esm.js +112 -0
- package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/image-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/image-menu.esm.js +64 -0
- package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/image-panel.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/image-panel.esm.js +171 -0
- package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/insert-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/insert-menu.esm.js +90 -0
- package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/link-editor.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/link-editor.esm.js +114 -0
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/markdown-panel.esm.js +80 -0
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +92 -0
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/shortcut-help.esm.js +147 -0
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +79 -0
- package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/special-chars.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/special-chars.esm.js +96 -0
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +67 -0
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +100 -0
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +58 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal/glyph.d.ts +6 -0
- package/dist/src/internal/glyph.js +13 -0
- package/dist/src/internal/glyph.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/emoji.d.ts +5 -0
- package/dist/src/nile-wysiwyg-editor/emoji.js +138 -0
- package/dist/src/nile-wysiwyg-editor/emoji.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/commands.d.ts +69 -0
- package/dist/src/nile-wysiwyg-editor/engine/commands.js +863 -0
- package/dist/src/nile-wysiwyg-editor/engine/commands.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.d.ts +35 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.js +67 -0
- package/dist/src/nile-wysiwyg-editor/engine/create-state.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.d.ts +38 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.js +208 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.test.js +96 -0
- package/dist/src/nile-wysiwyg-editor/engine/export.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +37 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js +29 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.d.ts +12 -0
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.js +144 -0
- package/dist/src/nile-wysiwyg-editor/engine/inputrules.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/keymap.d.ts +15 -0
- package/dist/src/nile-wysiwyg-editor/engine/keymap.js +94 -0
- package/dist/src/nile-wysiwyg-editor/engine/keymap.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.d.ts +22 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.js +287 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.js +106 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown-parse.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown.d.ts +10 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown.js +162 -0
- package/dist/src/nile-wysiwyg-editor/engine/markdown.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/office-paste.d.ts +13 -0
- package/dist/src/nile-wysiwyg-editor/engine/office-paste.js +182 -0
- package/dist/src/nile-wysiwyg-editor/engine/office-paste.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.d.ts +10 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.js +165 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/code-highlight.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.d.ts +17 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js +69 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.d.ts +17 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js +96 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.d.ts +26 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.js +123 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/find-replace.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.d.ts +28 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.js +72 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/format-painter.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.d.ts +41 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js +176 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image.d.ts +15 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image.js +64 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/image.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.d.ts +11 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js +42 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/link.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.d.ts +18 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js +46 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.d.ts +7 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.js +26 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/max-length.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.d.ts +31 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js +55 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/paste.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.d.ts +7 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.js +25 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/placeholder.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.d.ts +10 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.js +48 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/single-line.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.d.ts +29 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.js +87 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/suggestions.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.d.ts +8 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +11 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.d.ts +16 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.js +49 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/task-list.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +883 -0
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/productivity.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/productivity.test.js +129 -0
- package/dist/src/nile-wysiwyg-editor/engine/productivity.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +13 -0
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +155 -0
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +20 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.js +823 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/selection-state.d.ts +70 -0
- package/dist/src/nile-wysiwyg-editor/engine/selection-state.js +138 -0
- package/dist/src/nile-wysiwyg-editor/engine/selection-state.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/serializer.d.ts +18 -0
- package/dist/src/nile-wysiwyg-editor/engine/serializer.js +63 -0
- package/dist/src/nile-wysiwyg-editor/engine/serializer.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/index.d.ts +3 -0
- package/dist/src/nile-wysiwyg-editor/index.js +3 -0
- package/dist/src/nile-wysiwyg-editor/index.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +782 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +364 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +2038 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +636 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js +2 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.d.ts +6 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js +251 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.d.ts +41 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js +700 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/types.d.ts +49 -0
- package/dist/src/nile-wysiwyg-editor/types.js +8 -0
- package/dist/src/nile-wysiwyg-editor/types.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.d.ts +20 -0
- package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js +159 -0
- package/dist/src/nile-wysiwyg-editor/ui/bookmark-panel.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.d.ts +21 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +125 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.d.ts +18 -0
- package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.js +107 -0
- package/dist/src/nile-wysiwyg-editor/ui/code-block-menu.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-menu.d.ts +19 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js +123 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-menu.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.d.ts +27 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +146 -0
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.d.ts +21 -0
- package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js +154 -0
- package/dist/src/nile-wysiwyg-editor/ui/emoji-picker.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/find-replace.d.ts +26 -0
- package/dist/src/nile-wysiwyg-editor/ui/find-replace.js +178 -0
- package/dist/src/nile-wysiwyg-editor/ui/find-replace.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-menu.d.ts +18 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-menu.js +129 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-menu.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-panel.d.ts +56 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-panel.js +349 -0
- package/dist/src/nile-wysiwyg-editor/ui/image-panel.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/insert-menu.d.ts +25 -0
- package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js +142 -0
- package/dist/src/nile-wysiwyg-editor/ui/insert-menu.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/link-editor.d.ts +30 -0
- package/dist/src/nile-wysiwyg-editor/ui/link-editor.js +189 -0
- package/dist/src/nile-wysiwyg-editor/ui/link-editor.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.d.ts +24 -0
- package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js +144 -0
- package/dist/src/nile-wysiwyg-editor/ui/markdown-panel.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.d.ts +31 -0
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +172 -0
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.d.ts +30 -0
- package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js +296 -0
- package/dist/src/nile-wysiwyg-editor/ui/shortcut-help.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/source-view.d.ts +19 -0
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js +120 -0
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/special-chars.d.ts +20 -0
- package/dist/src/nile-wysiwyg-editor/ui/special-chars.js +199 -0
- package/dist/src/nile-wysiwyg-editor/ui/special-chars.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.d.ts +31 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +133 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.d.ts +23 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +177 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.d.ts +16 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +101 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -0
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -2
- package/src/index.ts +57 -1
- package/src/internal/glyph.ts +13 -0
- package/src/nile-wysiwyg-editor/emoji.ts +137 -0
- package/src/nile-wysiwyg-editor/engine/commands.ts +1035 -0
- package/src/nile-wysiwyg-editor/engine/create-state.ts +117 -0
- package/src/nile-wysiwyg-editor/engine/export.test.ts +113 -0
- package/src/nile-wysiwyg-editor/engine/export.ts +242 -0
- package/src/nile-wysiwyg-editor/engine/index.ts +69 -0
- package/src/nile-wysiwyg-editor/engine/inputrules.ts +169 -0
- package/src/nile-wysiwyg-editor/engine/keymap.ts +146 -0
- package/src/nile-wysiwyg-editor/engine/markdown-parse.test.ts +148 -0
- package/src/nile-wysiwyg-editor/engine/markdown-parse.ts +337 -0
- package/src/nile-wysiwyg-editor/engine/markdown.ts +165 -0
- package/src/nile-wysiwyg-editor/engine/office-paste.ts +199 -0
- package/src/nile-wysiwyg-editor/engine/plugins/code-highlight.ts +173 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embed-view.ts +85 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embeds.ts +99 -0
- package/src/nile-wysiwyg-editor/engine/plugins/find-replace.ts +155 -0
- package/src/nile-wysiwyg-editor/engine/plugins/format-painter.ts +80 -0
- package/src/nile-wysiwyg-editor/engine/plugins/image-view.ts +220 -0
- package/src/nile-wysiwyg-editor/engine/plugins/image.ts +80 -0
- package/src/nile-wysiwyg-editor/engine/plugins/link.ts +42 -0
- package/src/nile-wysiwyg-editor/engine/plugins/math-view.ts +56 -0
- package/src/nile-wysiwyg-editor/engine/plugins/max-length.ts +24 -0
- package/src/nile-wysiwyg-editor/engine/plugins/paste.ts +74 -0
- package/src/nile-wysiwyg-editor/engine/plugins/placeholder.ts +24 -0
- package/src/nile-wysiwyg-editor/engine/plugins/single-line.ts +47 -0
- package/src/nile-wysiwyg-editor/engine/plugins/suggestions.ts +110 -0
- package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +12 -0
- package/src/nile-wysiwyg-editor/engine/plugins/task-list.ts +65 -0
- package/src/nile-wysiwyg-editor/engine/pm.test.ts +1088 -0
- package/src/nile-wysiwyg-editor/engine/productivity.test.ts +163 -0
- package/src/nile-wysiwyg-editor/engine/sanitize.ts +156 -0
- package/src/nile-wysiwyg-editor/engine/schema.ts +859 -0
- package/src/nile-wysiwyg-editor/engine/selection-state.ts +210 -0
- package/src/nile-wysiwyg-editor/engine/serializer.ts +78 -0
- package/src/nile-wysiwyg-editor/index.ts +3 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +782 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +750 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +2155 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.ts +1 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.ts +251 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.ts +772 -0
- package/src/nile-wysiwyg-editor/types.ts +83 -0
- package/src/nile-wysiwyg-editor/ui/bookmark-panel.ts +155 -0
- package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +135 -0
- package/src/nile-wysiwyg-editor/ui/code-block-menu.ts +105 -0
- package/src/nile-wysiwyg-editor/ui/embed-menu.ts +133 -0
- package/src/nile-wysiwyg-editor/ui/embed-panel.ts +143 -0
- package/src/nile-wysiwyg-editor/ui/emoji-picker.ts +152 -0
- package/src/nile-wysiwyg-editor/ui/find-replace.ts +174 -0
- package/src/nile-wysiwyg-editor/ui/image-menu.ts +140 -0
- package/src/nile-wysiwyg-editor/ui/image-panel.ts +337 -0
- package/src/nile-wysiwyg-editor/ui/insert-menu.ts +142 -0
- package/src/nile-wysiwyg-editor/ui/link-editor.ts +180 -0
- package/src/nile-wysiwyg-editor/ui/markdown-panel.ts +143 -0
- package/src/nile-wysiwyg-editor/ui/math-panel.ts +169 -0
- package/src/nile-wysiwyg-editor/ui/shortcut-help.ts +311 -0
- package/src/nile-wysiwyg-editor/ui/source-view.ts +119 -0
- package/src/nile-wysiwyg-editor/ui/special-chars.ts +209 -0
- package/src/nile-wysiwyg-editor/ui/suggest-list.ts +138 -0
- package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +173 -0
- package/src/nile-wysiwyg-editor/ui/table-menu.ts +109 -0
- package/vscode-html-custom-data.json +450 -0
- package/web-dev-server.config.mjs +6 -2
- package/dist/index-5fef1a0d.esm.js +0 -1
- package/dist/index-8f38fae3.cjs.js +0 -2
- package/dist/index-8f38fae3.cjs.js.map +0 -1
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → HTML conversion, the import counterpart of ./markdown
|
|
3
|
+
* (docToMarkdown). Dependency-free and scoped to the same structural subset
|
|
4
|
+
* the serializer emits: headings, blockquotes, bullet/ordered/task lists
|
|
5
|
+
* (nested), fenced code blocks, GFM tables, horizontal rules, and the inline
|
|
6
|
+
* marks (**bold**, *italic*, ~~strike~~, `code`, [links](…), images).
|
|
7
|
+
*
|
|
8
|
+
* The output is intentionally plain HTML that the schema's parseDOM rules
|
|
9
|
+
* understand (e.g. <pre data-language>, <ul data-task-list>), and every
|
|
10
|
+
* consumer feeds it through htmlToDoc/htmlToSlice — so the schema and the
|
|
11
|
+
* sanitizer stay the single source of truth for what lands in the document.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const BULLET_RE = /^(\s*)([-*+])\s+(.*)$/;
|
|
15
|
+
const ORDERED_RE = /^(\s*)(\d+)[.)]\s+(.*)$/;
|
|
16
|
+
const TASK_RE = /^\[([ xX])\]\s+(.*)$/;
|
|
17
|
+
const FENCE_RE = /^\s{0,3}(```+|~~~+)\s*(\S*)\s*$/;
|
|
18
|
+
const HEADING_RE = /^\s{0,3}(#{1,6})\s+(.*)$/;
|
|
19
|
+
const HR_RE = /^\s{0,3}(?:-{3,}|\*{3,}|_{3,})\s*$/;
|
|
20
|
+
const QUOTE_RE = /^\s{0,3}>\s?/;
|
|
21
|
+
const TABLE_DIVIDER_RE = /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;
|
|
22
|
+
|
|
23
|
+
function escapeHtml(text: string): string {
|
|
24
|
+
return text
|
|
25
|
+
.replace(/&/g, '&')
|
|
26
|
+
.replace(/</g, '<')
|
|
27
|
+
.replace(/>/g, '>')
|
|
28
|
+
.replace(/"/g, '"');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Swaps backslash-escaped Markdown characters for placeholders so the inline
|
|
33
|
+
* formatter never reads them as syntax; restoreEscapes() puts them back.
|
|
34
|
+
*/
|
|
35
|
+
function protectEscapes(text: string): string {
|
|
36
|
+
return text.replace(
|
|
37
|
+
/\\([\\`*_[\]()#+\-.!~|])/g,
|
|
38
|
+
(_, ch: string) => `\u0000${ch.charCodeAt(0)}\u0000`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function restoreEscapes(text: string): string {
|
|
43
|
+
return text.replace(/\u0000(\d+)\u0000/g, (_, code: string) =>
|
|
44
|
+
String.fromCharCode(Number(code))
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Inline formatting for one already-HTML-escaped, code-span-free segment. */
|
|
49
|
+
function formatSpans(escaped: string): string {
|
|
50
|
+
let out = escaped;
|
|
51
|
+
// Images before links so  is never read as a link.
|
|
52
|
+
out = out.replace(
|
|
53
|
+
/!\[([^\]]*)\]\(([^()\s]+)(?:\s+[^)]*)?\)/g,
|
|
54
|
+
(_, alt: string, src: string) => `<img src="${src}" alt="${alt}">`
|
|
55
|
+
);
|
|
56
|
+
out = out.replace(
|
|
57
|
+
/\[([^\]]+)\]\(([^()\s]+)(?:\s+[^)]*)?\)/g,
|
|
58
|
+
(_, label: string, href: string) => `<a href="${href}">${label}</a>`
|
|
59
|
+
);
|
|
60
|
+
// Bold before italic so '**' is never read as two italics.
|
|
61
|
+
out = out.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
62
|
+
out = out.replace(/__([^_]+)__/g, '<strong>$1</strong>');
|
|
63
|
+
out = out.replace(/(^|[^*\w])\*([^*\s](?:[^*]*[^*\s])?)\*/g, '$1<em>$2</em>');
|
|
64
|
+
out = out.replace(/(^|[^_\w])_([^_\s](?:[^_]*[^_\s])?)_(?![\w])/g, '$1<em>$2</em>');
|
|
65
|
+
out = out.replace(/~~([^~]+)~~/g, '<s>$1</s>');
|
|
66
|
+
// Newlines inside one paragraph become hard breaks.
|
|
67
|
+
out = out.replace(/ *\n/g, '<br>');
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Inline Markdown → HTML. Code spans are lifted out first to stay literal. */
|
|
72
|
+
export function inlineToHtml(source: string): string {
|
|
73
|
+
const protectedSrc = protectEscapes(source);
|
|
74
|
+
const codeRe = /(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
|
|
75
|
+
let rest = protectedSrc;
|
|
76
|
+
let out = '';
|
|
77
|
+
for (;;) {
|
|
78
|
+
const match = codeRe.exec(rest);
|
|
79
|
+
if (!match) {
|
|
80
|
+
out += formatSpans(escapeHtml(rest));
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
out += formatSpans(escapeHtml(rest.slice(0, match.index)));
|
|
84
|
+
out += `<code>${escapeHtml(match[2])}</code>`;
|
|
85
|
+
rest = rest.slice(match.index + match[0].length);
|
|
86
|
+
}
|
|
87
|
+
return restoreEscapes(out);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** True when a line begins a new block construct (ends a paragraph). */
|
|
91
|
+
function isBlockStart(line: string): boolean {
|
|
92
|
+
return (
|
|
93
|
+
HEADING_RE.test(line) ||
|
|
94
|
+
FENCE_RE.test(line) ||
|
|
95
|
+
HR_RE.test(line) ||
|
|
96
|
+
QUOTE_RE.test(line) ||
|
|
97
|
+
BULLET_RE.test(line) ||
|
|
98
|
+
ORDERED_RE.test(line)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface ListItem {
|
|
103
|
+
lines: string[];
|
|
104
|
+
checked: boolean | null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* One list region → <ul>/<ol>/task <ul>. Items deeper than the base indent
|
|
109
|
+
* are re-parsed recursively, so nested and mixed lists come out right. A
|
|
110
|
+
* base-indent item of the other marker type (bullet vs ordered) ends the
|
|
111
|
+
* list and starts a sibling one, matching GFM.
|
|
112
|
+
*/
|
|
113
|
+
function parseListRegion(lines: string[]): string {
|
|
114
|
+
const firstMatch = BULLET_RE.exec(lines[0]) ?? ORDERED_RE.exec(lines[0])!;
|
|
115
|
+
const baseIndent = firstMatch[1].length;
|
|
116
|
+
const ordered = ORDERED_RE.test(lines[0]) && !BULLET_RE.test(lines[0]);
|
|
117
|
+
const startOrder = ordered ? Number(firstMatch[2]) : 1;
|
|
118
|
+
|
|
119
|
+
const items: ListItem[] = [];
|
|
120
|
+
let rest: string[] | null = null;
|
|
121
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
122
|
+
const line = lines[index];
|
|
123
|
+
const match = ordered ? ORDERED_RE.exec(line) : BULLET_RE.exec(line);
|
|
124
|
+
const other = ordered ? BULLET_RE.exec(line) : ORDERED_RE.exec(line);
|
|
125
|
+
if (match && match[1].length <= baseIndent) {
|
|
126
|
+
let text = match[3];
|
|
127
|
+
let checked: boolean | null = null;
|
|
128
|
+
if (!ordered) {
|
|
129
|
+
const task = TASK_RE.exec(text);
|
|
130
|
+
if (task) {
|
|
131
|
+
checked = task[1].toLowerCase() === 'x';
|
|
132
|
+
text = task[2];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
items.push({ lines: [text], checked });
|
|
136
|
+
} else if (other && other[1].length <= baseIndent) {
|
|
137
|
+
// Marker type switched at base level: a sibling list starts here.
|
|
138
|
+
rest = lines.slice(index);
|
|
139
|
+
break;
|
|
140
|
+
} else if (items.length > 0) {
|
|
141
|
+
// Continuation or nested content: drop the base marker's indent and
|
|
142
|
+
// keep the relative indent so recursion sees the nesting.
|
|
143
|
+
items[items.length - 1].lines.push(
|
|
144
|
+
line.replace(new RegExp(`^\\s{0,${baseIndent + 2}}`), '')
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Consecutive runs of task/non-task items become separate lists, so a
|
|
150
|
+
// checklist after a plain list never converts its neighbours.
|
|
151
|
+
const runs: { task: boolean; items: ListItem[] }[] = [];
|
|
152
|
+
for (const item of items) {
|
|
153
|
+
const task = item.checked !== null;
|
|
154
|
+
const lastRun = runs[runs.length - 1];
|
|
155
|
+
if (lastRun && lastRun.task === task) lastRun.items.push(item);
|
|
156
|
+
else runs.push({ task, items: [item] });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const rendered = runs
|
|
160
|
+
.map(run => {
|
|
161
|
+
const body = run.items
|
|
162
|
+
.map(item => {
|
|
163
|
+
const content = parseBlocks(item.lines);
|
|
164
|
+
return run.task
|
|
165
|
+
? `<li data-task-item="true" data-checked="${item.checked === true}">${content}</li>`
|
|
166
|
+
: `<li>${content}</li>`;
|
|
167
|
+
})
|
|
168
|
+
.join('');
|
|
169
|
+
if (run.task) return `<ul data-task-list="true">${body}</ul>`;
|
|
170
|
+
if (ordered) {
|
|
171
|
+
const startAttr = startOrder !== 1 ? ` start="${startOrder}"` : '';
|
|
172
|
+
return `<ol${startAttr}>${body}</ol>`;
|
|
173
|
+
}
|
|
174
|
+
return `<ul>${body}</ul>`;
|
|
175
|
+
})
|
|
176
|
+
.join('');
|
|
177
|
+
|
|
178
|
+
return rest ? rendered + parseListRegion(rest) : rendered;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** A GFM table region (header, divider, body rows) → <table>. */
|
|
182
|
+
function parseTableRegion(lines: string[]): string {
|
|
183
|
+
const splitRow = (line: string): string[] =>
|
|
184
|
+
line
|
|
185
|
+
.trim()
|
|
186
|
+
.replace(/^\|/, '')
|
|
187
|
+
.replace(/\|$/, '')
|
|
188
|
+
.split(/(?<!\\)\|/)
|
|
189
|
+
.map(cell => cell.trim().replace(/\\\|/g, '|'));
|
|
190
|
+
|
|
191
|
+
const header = splitRow(lines[0]);
|
|
192
|
+
const rows = lines.slice(2).map(splitRow);
|
|
193
|
+
const renderRow = (cells: string[], tag: 'th' | 'td'): string =>
|
|
194
|
+
`<tr>${header
|
|
195
|
+
.map((_, index) => `<${tag}><p>${inlineToHtml(cells[index] ?? '')}</p></${tag}>`)
|
|
196
|
+
.join('')}</tr>`;
|
|
197
|
+
|
|
198
|
+
return `<table>${renderRow(header, 'th')}${rows
|
|
199
|
+
.map(row => renderRow(row, 'td'))
|
|
200
|
+
.join('')}</table>`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function parseBlocks(lines: string[]): string {
|
|
204
|
+
const out: string[] = [];
|
|
205
|
+
let i = 0;
|
|
206
|
+
|
|
207
|
+
while (i < lines.length) {
|
|
208
|
+
const line = lines[i];
|
|
209
|
+
if (!line.trim()) {
|
|
210
|
+
i += 1;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Fenced code block.
|
|
215
|
+
const fence = FENCE_RE.exec(line);
|
|
216
|
+
if (fence) {
|
|
217
|
+
const closing = new RegExp(`^\\s{0,3}${fence[1]}\\s*$`);
|
|
218
|
+
const buffer: string[] = [];
|
|
219
|
+
i += 1;
|
|
220
|
+
while (i < lines.length && !closing.test(lines[i])) {
|
|
221
|
+
buffer.push(lines[i]);
|
|
222
|
+
i += 1;
|
|
223
|
+
}
|
|
224
|
+
i += 1; // Skip the closing fence.
|
|
225
|
+
const langAttr = fence[2] ? ` data-language="${escapeHtml(fence[2])}"` : '';
|
|
226
|
+
out.push(`<pre${langAttr}><code>${escapeHtml(buffer.join('\n'))}</code></pre>`);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Heading (with optional trailing #'s, per CommonMark).
|
|
231
|
+
const heading = HEADING_RE.exec(line);
|
|
232
|
+
if (heading) {
|
|
233
|
+
const level = heading[1].length;
|
|
234
|
+
const text = heading[2].replace(/\s+#+\s*$/, '');
|
|
235
|
+
out.push(`<h${level}>${inlineToHtml(text)}</h${level}>`);
|
|
236
|
+
i += 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Horizontal rule (checked before lists: "---" vs "- item").
|
|
241
|
+
if (HR_RE.test(line)) {
|
|
242
|
+
out.push('<hr>');
|
|
243
|
+
i += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Blockquote: consecutive quoted lines, including lazy continuations.
|
|
248
|
+
if (QUOTE_RE.test(line)) {
|
|
249
|
+
const buffer: string[] = [];
|
|
250
|
+
while (
|
|
251
|
+
i < lines.length &&
|
|
252
|
+
(QUOTE_RE.test(lines[i]) ||
|
|
253
|
+
(buffer.length > 0 && lines[i].trim() !== '' && !isBlockStart(lines[i])))
|
|
254
|
+
) {
|
|
255
|
+
buffer.push(lines[i].replace(QUOTE_RE, ''));
|
|
256
|
+
i += 1;
|
|
257
|
+
}
|
|
258
|
+
out.push(`<blockquote>${parseBlocks(buffer)}</blockquote>`);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// List: the region runs until a blank line not followed by more list
|
|
263
|
+
// content, or a non-indented non-list line.
|
|
264
|
+
if (BULLET_RE.test(line) || ORDERED_RE.test(line)) {
|
|
265
|
+
const buffer: string[] = [];
|
|
266
|
+
while (i < lines.length) {
|
|
267
|
+
const current = lines[i];
|
|
268
|
+
if (current.trim() === '') {
|
|
269
|
+
const next = lines[i + 1];
|
|
270
|
+
if (next !== undefined && (BULLET_RE.test(next) || ORDERED_RE.test(next) || /^\s{2,}\S/.test(next))) {
|
|
271
|
+
i += 1;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
if (
|
|
277
|
+
!BULLET_RE.test(current) &&
|
|
278
|
+
!ORDERED_RE.test(current) &&
|
|
279
|
+
!/^\s{2,}\S/.test(current)
|
|
280
|
+
) {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
buffer.push(current);
|
|
284
|
+
i += 1;
|
|
285
|
+
}
|
|
286
|
+
out.push(parseListRegion(buffer));
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// GFM table: a pipe row followed by a divider row.
|
|
291
|
+
if (
|
|
292
|
+
line.includes('|') &&
|
|
293
|
+
i + 1 < lines.length &&
|
|
294
|
+
TABLE_DIVIDER_RE.test(lines[i + 1]) &&
|
|
295
|
+
lines[i + 1].includes('|')
|
|
296
|
+
) {
|
|
297
|
+
const buffer: string[] = [lines[i], lines[i + 1]];
|
|
298
|
+
i += 2;
|
|
299
|
+
while (i < lines.length && lines[i].includes('|') && lines[i].trim() !== '') {
|
|
300
|
+
buffer.push(lines[i]);
|
|
301
|
+
i += 1;
|
|
302
|
+
}
|
|
303
|
+
out.push(parseTableRegion(buffer));
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Paragraph: collect until a blank line or the start of another block.
|
|
308
|
+
const buffer: string[] = [line];
|
|
309
|
+
i += 1;
|
|
310
|
+
while (i < lines.length && lines[i].trim() !== '' && !isBlockStart(lines[i])) {
|
|
311
|
+
buffer.push(lines[i]);
|
|
312
|
+
i += 1;
|
|
313
|
+
}
|
|
314
|
+
out.push(`<p>${inlineToHtml(buffer.join('\n'))}</p>`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return out.join('');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Parses a Markdown string into schema-compatible HTML. */
|
|
321
|
+
export function markdownToHtml(markdown: string): string {
|
|
322
|
+
return parseBlocks(markdown.replace(/\r\n?/g, '\n').split('\n'));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Conservative sniff for "this plain text is probably Markdown": one block
|
|
327
|
+
* construct or one unambiguous inline construct is required. HTML-looking
|
|
328
|
+
* text is never treated as Markdown.
|
|
329
|
+
*/
|
|
330
|
+
export function looksLikeMarkdown(text: string): boolean {
|
|
331
|
+
if (/<\/?[a-z][^>]*>/i.test(text)) return false;
|
|
332
|
+
const block =
|
|
333
|
+
/^\s{0,3}(#{1,6}\s+\S|```|(?:[-*+]|\d+[.)])\s+\S|>\s?\S|\|.+\|)/m;
|
|
334
|
+
const inline =
|
|
335
|
+
/\*\*[^*\n]+\*\*|~~[^~\n]+~~|\[[^\]\n]+\]\([^)\n]+\)|`[^`\n]+`/;
|
|
336
|
+
return block.test(text) || inline.test(text);
|
|
337
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document → Markdown projection. A best-effort, dependency-free serializer
|
|
3
|
+
* covering the structural nodes and the marks Markdown can represent. Styling
|
|
4
|
+
* marks with no Markdown equivalent (color, background, font family/size) are
|
|
5
|
+
* dropped to plain text; underline/sub/sup fall back to inline HTML, which
|
|
6
|
+
* GitHub-flavored Markdown renders. For loss-free round-trips use getHTML().
|
|
7
|
+
*/
|
|
8
|
+
import { Node as PMNode, Mark } from 'prosemirror-model';
|
|
9
|
+
|
|
10
|
+
/** Escapes the inline characters that would otherwise be read as Markdown. */
|
|
11
|
+
function escapeText(text: string): string {
|
|
12
|
+
return text.replace(/([\\`*_[\]<>])/g, '\\$1');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Wraps a text run in its marks, innermost (code) to outermost (link). */
|
|
16
|
+
function serializeMarks(text: string, marks: readonly Mark[]): string {
|
|
17
|
+
const has = (name: string): boolean => marks.some(m => m.type.name === name);
|
|
18
|
+
let out: string;
|
|
19
|
+
if (has('code')) {
|
|
20
|
+
const ticks = text.includes('`') ? '``' : '`';
|
|
21
|
+
out = `${ticks}${text}${ticks}`;
|
|
22
|
+
} else {
|
|
23
|
+
out = escapeText(text);
|
|
24
|
+
}
|
|
25
|
+
if (has('subscript')) out = `<sub>${out}</sub>`;
|
|
26
|
+
if (has('superscript')) out = `<sup>${out}</sup>`;
|
|
27
|
+
if (has('underline')) out = `<u>${out}</u>`;
|
|
28
|
+
if (has('strike')) out = `~~${out}~~`;
|
|
29
|
+
if (has('italic')) out = `*${out}*`;
|
|
30
|
+
if (has('bold')) out = `**${out}**`;
|
|
31
|
+
const link = marks.find(m => m.type.name === 'link');
|
|
32
|
+
if (link) out = `[${out}](${link.attrs.href})`;
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Inline content of a textblock (text runs, breaks, images, mentions). */
|
|
37
|
+
function serializeInline(node: PMNode): string {
|
|
38
|
+
let result = '';
|
|
39
|
+
node.forEach(child => {
|
|
40
|
+
if (child.isText) {
|
|
41
|
+
result += serializeMarks(child.text ?? '', child.marks);
|
|
42
|
+
} else if (child.type.name === 'hard_break') {
|
|
43
|
+
result += ' \n';
|
|
44
|
+
} else if (child.type.name === 'image') {
|
|
45
|
+
result += ``;
|
|
46
|
+
} else if (child.type.name === 'mention') {
|
|
47
|
+
result += `${child.attrs.trigger}${child.attrs.label}`;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function serializeChildren(node: PMNode): string {
|
|
54
|
+
const parts: string[] = [];
|
|
55
|
+
node.forEach(child => parts.push(serializeBlock(child)));
|
|
56
|
+
return parts.join('\n\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** One list item: first paragraph on the marker line, the rest indented. */
|
|
60
|
+
function serializeListItem(item: PMNode, marker: string, indent: string): string {
|
|
61
|
+
const contPad = indent + ' '.repeat(marker.length);
|
|
62
|
+
const parts: string[] = [];
|
|
63
|
+
item.forEach(child => {
|
|
64
|
+
const name = child.type.name;
|
|
65
|
+
if (name === 'bullet_list') {
|
|
66
|
+
parts.push(serializeList(child, contPad, null));
|
|
67
|
+
} else if (name === 'ordered_list') {
|
|
68
|
+
parts.push(serializeList(child, contPad, child.attrs.order ?? 1));
|
|
69
|
+
} else if (name === 'task_list') {
|
|
70
|
+
parts.push(serializeTaskList(child, contPad));
|
|
71
|
+
} else {
|
|
72
|
+
parts.push(
|
|
73
|
+
serializeBlock(child)
|
|
74
|
+
.split('\n')
|
|
75
|
+
.map(line => contPad + line)
|
|
76
|
+
.join('\n')
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// The first line was padded like a continuation; swap that pad for the
|
|
81
|
+
// marker (same width, so nested content stays aligned).
|
|
82
|
+
const body = parts.join('\n');
|
|
83
|
+
return indent + marker + body.slice(contPad.length);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function serializeList(list: PMNode, indent: string, startOrder: number | null): string {
|
|
87
|
+
const lines: string[] = [];
|
|
88
|
+
let order = startOrder ?? 1;
|
|
89
|
+
list.forEach(item => {
|
|
90
|
+
lines.push(serializeListItem(item, startOrder === null ? '- ' : `${order}. `, indent));
|
|
91
|
+
order += 1;
|
|
92
|
+
});
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function serializeTaskList(list: PMNode, indent: string): string {
|
|
97
|
+
const lines: string[] = [];
|
|
98
|
+
list.forEach(item => {
|
|
99
|
+
const box = item.attrs.checked ? '[x] ' : '[ ] ';
|
|
100
|
+
lines.push(serializeListItem(item, `- ${box}`, indent));
|
|
101
|
+
});
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** GitHub-flavored table. Cells use the first paragraph's inline text. */
|
|
106
|
+
function serializeTable(table: PMNode): string {
|
|
107
|
+
const rows: string[][] = [];
|
|
108
|
+
table.forEach(row => {
|
|
109
|
+
const cells: string[] = [];
|
|
110
|
+
row.forEach(cell => {
|
|
111
|
+
const text = cell.firstChild ? serializeInline(cell.firstChild) : '';
|
|
112
|
+
cells.push(text.replace(/\n/g, ' ').replace(/\|/g, '\\|').trim());
|
|
113
|
+
});
|
|
114
|
+
rows.push(cells);
|
|
115
|
+
});
|
|
116
|
+
if (rows.length === 0) return '';
|
|
117
|
+
const cols = Math.max(...rows.map(r => r.length));
|
|
118
|
+
const pad = (r: string[]): string[] => {
|
|
119
|
+
const copy = r.slice();
|
|
120
|
+
while (copy.length < cols) copy.push('');
|
|
121
|
+
return copy;
|
|
122
|
+
};
|
|
123
|
+
const fmt = (r: string[]): string => `| ${pad(r).join(' | ')} |`;
|
|
124
|
+
return [
|
|
125
|
+
fmt(rows[0]),
|
|
126
|
+
fmt(new Array(cols).fill('---')),
|
|
127
|
+
...rows.slice(1).map(fmt),
|
|
128
|
+
].join('\n');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function serializeBlock(node: PMNode): string {
|
|
132
|
+
switch (node.type.name) {
|
|
133
|
+
case 'paragraph':
|
|
134
|
+
return serializeInline(node);
|
|
135
|
+
case 'heading':
|
|
136
|
+
return `${'#'.repeat(node.attrs.level as number)} ${serializeInline(node)}`;
|
|
137
|
+
case 'blockquote':
|
|
138
|
+
return serializeChildren(node)
|
|
139
|
+
.split('\n')
|
|
140
|
+
.map(line => (line ? `> ${line}` : '>'))
|
|
141
|
+
.join('\n');
|
|
142
|
+
case 'code_block':
|
|
143
|
+
return `\`\`\`${node.attrs.language ?? ''}\n${node.textContent}\n\`\`\``;
|
|
144
|
+
case 'horizontal_rule':
|
|
145
|
+
return '---';
|
|
146
|
+
case 'bullet_list':
|
|
147
|
+
return serializeList(node, '', null);
|
|
148
|
+
case 'ordered_list':
|
|
149
|
+
return serializeList(node, '', (node.attrs.order as number) ?? 1);
|
|
150
|
+
case 'task_list':
|
|
151
|
+
return serializeTaskList(node, '');
|
|
152
|
+
case 'table':
|
|
153
|
+
return serializeTable(node);
|
|
154
|
+
case 'embed':
|
|
155
|
+
return node.attrs.src as string;
|
|
156
|
+
default:
|
|
157
|
+
return node.isTextblock ? serializeInline(node) : serializeChildren(node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Serializes a document node to a Markdown string. */
|
|
162
|
+
export function docToMarkdown(doc: PMNode): string {
|
|
163
|
+
const body = serializeChildren(doc).replace(/\n{3,}/g, '\n\n').trim();
|
|
164
|
+
return body ? `${body}\n` : '';
|
|
165
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paste-from-Office cleanup. Word and Google Docs clipboard HTML carries
|
|
3
|
+
* heavy proprietary markup — conditional comments, o:/w:/v: namespaced tags,
|
|
4
|
+
* mso-* styles, and (in Word's case) lists faked as styled paragraphs. This
|
|
5
|
+
* pre-pass normalizes that into plain HTML *before* the sanitizer runs, so
|
|
6
|
+
* the schema receives real <ul>/<ol> structures instead of paragraph soup.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Does this clipboard HTML come from Word, Outlook or Google Docs? */
|
|
10
|
+
export function isOfficeHtml(html: string): boolean {
|
|
11
|
+
return (
|
|
12
|
+
/class="?Mso|style=["'][^"']*mso-|urn:schemas-microsoft-com|<o:p>|<w:WordDocument/i.test(
|
|
13
|
+
html
|
|
14
|
+
) || /id="?docs-internal-guid/i.test(html)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Does this clipboard HTML come from Excel (or another spreadsheet)? */
|
|
19
|
+
export function isExcelHtml(html: string): boolean {
|
|
20
|
+
return /urn:schemas-microsoft-com:office:excel|ProgId["']?[^>]*Excel\.Sheet|<x:ExcelWorkbook/i.test(
|
|
21
|
+
html
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Excel clipboard tables carry cell formatting as `align`/`bgcolor` attributes
|
|
27
|
+
* and inline styles. The schema keeps a cell's background and a paragraph's
|
|
28
|
+
* alignment/fonts — so promote the cell's background to a style the cell node
|
|
29
|
+
* reads, and wrap bare cell text in a paragraph carrying the alignment.
|
|
30
|
+
*/
|
|
31
|
+
function convertExcelCells(body: HTMLElement): void {
|
|
32
|
+
const doc = body.ownerDocument;
|
|
33
|
+
for (const cell of Array.from(body.querySelectorAll<HTMLElement>('td, th'))) {
|
|
34
|
+
const bg = cell.getAttribute('bgcolor');
|
|
35
|
+
if (bg && !/background/i.test(cell.getAttribute('style') ?? '')) {
|
|
36
|
+
cell.style.backgroundColor = bg;
|
|
37
|
+
}
|
|
38
|
+
const align = cell.getAttribute('align') || cell.style.textAlign;
|
|
39
|
+
const hasBlock = cell.querySelector('p, div, ul, ol, table, h1, h2, h3, h4, h5, h6');
|
|
40
|
+
if (!hasBlock && cell.childNodes.length > 0) {
|
|
41
|
+
const p = doc.createElement('p');
|
|
42
|
+
if (align) p.style.textAlign = align;
|
|
43
|
+
while (cell.firstChild) p.appendChild(cell.firstChild);
|
|
44
|
+
cell.appendChild(p);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface WordListItem {
|
|
50
|
+
level: number;
|
|
51
|
+
ordered: boolean;
|
|
52
|
+
paragraph: HTMLElement;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Word marks list paragraphs with an `mso-list: lX levelY` style. */
|
|
56
|
+
function wordListInfo(el: HTMLElement): { level: number } | null {
|
|
57
|
+
const style = el.getAttribute('style') ?? '';
|
|
58
|
+
const match = /mso-list:[^;"]*\blevel(\d+)/i.exec(style);
|
|
59
|
+
if (match) return { level: Number(match[1]) };
|
|
60
|
+
// Some Word variants only set the class.
|
|
61
|
+
if (/\bMsoListParagraph/i.test(el.className) && /mso-list/i.test(style)) {
|
|
62
|
+
return { level: 1 };
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The bullet/number marker Word renders as a literal span. Ordered markers
|
|
69
|
+
* look like "1." / "a)" / "iv."; bullets are symbol characters (·, o, §).
|
|
70
|
+
*/
|
|
71
|
+
function extractWordMarker(paragraph: HTMLElement): { ordered: boolean } {
|
|
72
|
+
let ordered = false;
|
|
73
|
+
// The marker lives in a span with mso-list:Ignore (or inside the
|
|
74
|
+
// conditional comment we already stripped, leaving the bare text).
|
|
75
|
+
const spans = paragraph.querySelectorAll('span');
|
|
76
|
+
for (const span of Array.from(spans)) {
|
|
77
|
+
if (/mso-list:\s*ignore/i.test(span.getAttribute('style') ?? '')) {
|
|
78
|
+
ordered = /^\s*(\d+|[a-z]{1,4}|[ivxlc]+)[.)]/i.test(span.textContent ?? '');
|
|
79
|
+
span.remove();
|
|
80
|
+
return { ordered };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Fallback: leading "1." / "a)" text in the paragraph itself.
|
|
84
|
+
const text = paragraph.textContent ?? '';
|
|
85
|
+
const lead = /^\s*(\d+|[a-z])[.)]\s+/i.exec(text);
|
|
86
|
+
if (lead) {
|
|
87
|
+
ordered = true;
|
|
88
|
+
const first = paragraph.firstChild;
|
|
89
|
+
if (first?.nodeType === Node.TEXT_NODE) {
|
|
90
|
+
first.textContent = (first.textContent ?? '').replace(/^\s*\S+[.)]\s+/, '');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { ordered };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Converts consecutive Word list paragraphs into real nested ul/ol lists. */
|
|
97
|
+
function convertWordLists(body: HTMLElement): void {
|
|
98
|
+
const doc = body.ownerDocument;
|
|
99
|
+
const paragraphs = Array.from(body.querySelectorAll('p'));
|
|
100
|
+
let run: WordListItem[] = [];
|
|
101
|
+
|
|
102
|
+
const flush = (): void => {
|
|
103
|
+
if (run.length === 0) return;
|
|
104
|
+
const anchor = run[0].paragraph;
|
|
105
|
+
// Build nested lists by level (Word levels start at 1).
|
|
106
|
+
const roots: HTMLElement[] = [];
|
|
107
|
+
const stack: { level: number; list: HTMLElement }[] = [];
|
|
108
|
+
for (const item of run) {
|
|
109
|
+
while (stack.length > 0 && stack[stack.length - 1].level > item.level) {
|
|
110
|
+
stack.pop();
|
|
111
|
+
}
|
|
112
|
+
if (stack.length === 0 || stack[stack.length - 1].level < item.level) {
|
|
113
|
+
const list = doc.createElement(item.ordered ? 'ol' : 'ul');
|
|
114
|
+
if (stack.length === 0) {
|
|
115
|
+
roots.push(list);
|
|
116
|
+
} else {
|
|
117
|
+
const parentItems = stack[stack.length - 1].list.children;
|
|
118
|
+
const parentLi = parentItems[parentItems.length - 1] ?? null;
|
|
119
|
+
(parentLi ?? stack[stack.length - 1].list).appendChild(list);
|
|
120
|
+
}
|
|
121
|
+
stack.push({ level: item.level, list });
|
|
122
|
+
}
|
|
123
|
+
const li = doc.createElement('li');
|
|
124
|
+
const p = doc.createElement('p');
|
|
125
|
+
p.innerHTML = item.paragraph.innerHTML;
|
|
126
|
+
li.appendChild(p);
|
|
127
|
+
stack[stack.length - 1].list.appendChild(li);
|
|
128
|
+
}
|
|
129
|
+
anchor.before(...roots);
|
|
130
|
+
for (const item of run) item.paragraph.remove();
|
|
131
|
+
run = [];
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
for (const paragraph of paragraphs) {
|
|
135
|
+
const info = wordListInfo(paragraph);
|
|
136
|
+
if (info) {
|
|
137
|
+
const { ordered } = extractWordMarker(paragraph);
|
|
138
|
+
run.push({ level: info.level, ordered, paragraph });
|
|
139
|
+
} else {
|
|
140
|
+
flush();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
flush();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Normalizes Word/Google Docs clipboard HTML into plain semantic HTML. */
|
|
147
|
+
export function cleanOfficeHtml(html: string): string {
|
|
148
|
+
// Conditional comments carry list markers and VML — drop them first, while
|
|
149
|
+
// this is still a string ("<![if !supportLists]>…<![endif]>" variants too).
|
|
150
|
+
let cleaned = html
|
|
151
|
+
.replace(/<!--\[if[\s\S]*?<!\[endif\]-->/gi, '')
|
|
152
|
+
.replace(/<!\[if[\s\S]*?<!\[endif\]>/gi, '')
|
|
153
|
+
.replace(/<!--[\s\S]*?-->/g, '');
|
|
154
|
+
|
|
155
|
+
const doc = new DOMParser().parseFromString(cleaned, 'text/html');
|
|
156
|
+
const body = doc.body;
|
|
157
|
+
|
|
158
|
+
// Namespaced Office elements (<o:p>, <w:…>, <v:…>): unwrap, keeping text.
|
|
159
|
+
for (const el of Array.from(body.querySelectorAll('*'))) {
|
|
160
|
+
if (el.tagName.includes(':')) {
|
|
161
|
+
el.replaceWith(...Array.from(el.childNodes));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Google Docs wraps the payload in <b id="docs-internal-guid-…"
|
|
166
|
+
// style="font-weight:normal"> — unwrap so nothing reads it as bold.
|
|
167
|
+
for (const el of Array.from(body.querySelectorAll('b[id^="docs-internal-guid"]'))) {
|
|
168
|
+
el.replaceWith(...Array.from(el.childNodes));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
convertWordLists(body);
|
|
172
|
+
|
|
173
|
+
// Excel: preserve cell background and per-cell text alignment.
|
|
174
|
+
if (isExcelHtml(html)) convertExcelCells(body);
|
|
175
|
+
|
|
176
|
+
// Strip Mso classes and mso-* style declarations; the sanitizer would drop
|
|
177
|
+
// most of this anyway, but clearing it here keeps parsing predictable.
|
|
178
|
+
for (const el of Array.from(body.querySelectorAll<HTMLElement>('*'))) {
|
|
179
|
+
if (/\bMso/i.test(el.className)) el.removeAttribute('class');
|
|
180
|
+
const style = el.getAttribute('style');
|
|
181
|
+
if (style && /mso-/i.test(style)) {
|
|
182
|
+
const kept = style
|
|
183
|
+
.split(';')
|
|
184
|
+
.filter(decl => decl.trim() && !/^\s*mso-/i.test(decl))
|
|
185
|
+
.join(';');
|
|
186
|
+
if (kept.trim()) el.setAttribute('style', kept);
|
|
187
|
+
else el.removeAttribute('style');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Empty paragraphs Word uses for spacing (only inside).
|
|
192
|
+
for (const p of Array.from(body.querySelectorAll('p'))) {
|
|
193
|
+
if ((p.textContent ?? '').replace(/ /g, '').trim() === '' && !p.querySelector('img')) {
|
|
194
|
+
p.remove();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return body.innerHTML;
|
|
199
|
+
}
|