@dxos/ui-editor 0.10.0 → 0.11.1
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/dist/lib/chunk-diff.mjs +935 -0
- package/dist/lib/chunk-diff.mjs.map +1 -0
- package/dist/lib/headless.mjs +2 -0
- package/dist/lib/index.mjs +9937 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/types.mjs +18 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/extensions/{bookmarks.d.ts → behavior/bookmarks.d.ts} +3 -0
- package/dist/types/src/extensions/behavior/bookmarks.d.ts.map +1 -0
- package/dist/types/src/extensions/behavior/dnd.d.ts.map +1 -0
- package/dist/types/src/extensions/behavior/index.d.ts +6 -0
- package/dist/types/src/extensions/behavior/index.d.ts.map +1 -0
- package/dist/types/src/extensions/{listener.d.ts → behavior/listener.d.ts} +3 -0
- package/dist/types/src/extensions/behavior/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/{spacing.d.ts → behavior/spacing.d.ts} +3 -0
- package/dist/types/src/extensions/behavior/spacing.d.ts.map +1 -0
- package/dist/types/src/extensions/behavior/submit.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/automerge/automerge.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/cursor.d.ts +1 -1
- package/dist/types/src/extensions/collab/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/defs.d.ts +3 -1
- package/dist/types/src/extensions/collab/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/automerge/index.d.ts +3 -0
- package/dist/types/src/extensions/collab/automerge/index.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/automerge/sync.d.ts.map +1 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/update-automerge.d.ts +4 -0
- package/dist/types/src/extensions/collab/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/update-codemirror.d.ts +4 -0
- package/dist/types/src/extensions/collab/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/awareness/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/extensions/{awareness → collab/awareness}/awareness.d.ts +4 -1
- package/dist/types/src/extensions/collab/awareness/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/awareness/index.d.ts.map +1 -0
- package/dist/types/src/extensions/collab/index.d.ts +3 -0
- package/dist/types/src/extensions/collab/index.d.ts.map +1 -0
- package/dist/types/src/extensions/completion/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/{autocomplete → completion}/index.d.ts +1 -0
- package/dist/types/src/extensions/completion/index.d.ts.map +1 -0
- package/dist/types/src/extensions/{autocomplete → completion}/match.d.ts +2 -2
- package/dist/types/src/extensions/completion/match.d.ts.map +1 -0
- package/dist/types/src/extensions/{mention.d.ts → completion/mention.d.ts} +3 -0
- package/dist/types/src/extensions/completion/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/completion/placeholder.d.ts.map +1 -0
- package/dist/types/src/extensions/{autocomplete → completion}/typeahead.d.ts +2 -2
- package/dist/types/src/extensions/completion/typeahead.d.ts.map +1 -0
- package/dist/types/src/extensions/{factories.d.ts → core/factories.d.ts} +23 -2
- package/dist/types/src/extensions/core/factories.d.ts.map +1 -0
- package/dist/types/src/extensions/core/factories.test.d.ts.map +1 -0
- package/dist/types/src/extensions/core/index.d.ts +3 -0
- package/dist/types/src/extensions/core/index.d.ts.map +1 -0
- package/dist/types/src/extensions/{modes.d.ts → core/modes.d.ts} +3 -0
- package/dist/types/src/extensions/core/modes.d.ts.map +1 -0
- package/dist/types/src/extensions/{debug.d.ts → debug/debug.d.ts} +3 -0
- package/dist/types/src/extensions/debug/debug.d.ts.map +1 -0
- package/dist/types/src/extensions/debug/index.d.ts +2 -0
- package/dist/types/src/extensions/debug/index.d.ts.map +1 -0
- package/dist/types/src/extensions/{annotations.d.ts → decoration/annotations.d.ts} +1 -1
- package/dist/types/src/extensions/decoration/annotations.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/assistant.d.ts +27 -0
- package/dist/types/src/extensions/decoration/assistant.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/fader.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/hashtag.d.ts +6 -0
- package/dist/types/src/extensions/decoration/hashtag.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/index.d.ts +8 -0
- package/dist/types/src/extensions/decoration/index.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/marker.d.ts.map +1 -0
- package/dist/types/src/extensions/{pos.d.ts → decoration/pos.d.ts} +40 -14
- package/dist/types/src/extensions/decoration/pos.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/pos.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{replacer.d.ts → decoration/replacer.d.ts} +8 -8
- package/dist/types/src/extensions/decoration/replacer.d.ts.map +1 -0
- package/dist/types/src/extensions/decoration/replacer.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{blast.d.ts → demo/blast.d.ts} +3 -0
- package/dist/types/src/extensions/demo/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/demo/index.d.ts +3 -0
- package/dist/types/src/extensions/demo/index.d.ts.map +1 -0
- package/dist/types/src/extensions/demo/snippets.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +10 -33
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/language/index.d.ts +5 -0
- package/dist/types/src/extensions/language/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/json/index.d.ts +2 -0
- package/dist/types/src/extensions/language/json/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/json/json.d.ts +12 -0
- package/dist/types/src/extensions/language/json/json.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/action.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/changes.d.ts +1 -1
- package/dist/types/src/extensions/language/markdown/changes.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/changes.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/debug.d.ts +3 -0
- package/dist/types/src/extensions/language/markdown/debug.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/decorate.d.ts +5 -1
- package/dist/types/src/extensions/language/markdown/decorate.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/formatting.d.ts +11 -2
- package/dist/types/src/extensions/language/markdown/formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/formatting.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/highlight.d.ts +16 -12
- package/dist/types/src/extensions/language/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/image.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/image.test.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/link.d.ts +25 -0
- package/dist/types/src/extensions/language/markdown/link.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/link.test.d.ts +2 -0
- package/dist/types/src/extensions/language/markdown/link.test.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/parser.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/styles.d.ts +3 -0
- package/dist/types/src/extensions/language/markdown/styles.d.ts.map +1 -0
- package/dist/types/src/extensions/language/markdown/table.d.ts.map +1 -0
- package/dist/types/src/extensions/language/mermaid/index.d.ts +2 -0
- package/dist/types/src/extensions/language/mermaid/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/mermaid/mermaid.d.ts +18 -0
- package/dist/types/src/extensions/language/mermaid/mermaid.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/extended-markdown.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/extended-markdown.test.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/stub.d.ts +76 -0
- package/dist/types/src/extensions/language/xml/stub.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/widgets/anchor.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/widgets/index.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/xml-block-decoration.d.ts +48 -0
- package/dist/types/src/extensions/language/xml/xml-block-decoration.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/xml-formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/{tags → language/xml}/xml-tags.d.ts +12 -1
- package/dist/types/src/extensions/language/xml/xml-tags.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/xml-tags.test.d.ts +2 -0
- package/dist/types/src/extensions/language/xml/xml-tags.test.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/xml-util.d.ts.map +1 -0
- package/dist/types/src/extensions/language/xml/xml-util.test.d.ts.map +1 -0
- package/dist/types/src/extensions/review/change-bar.d.ts +29 -0
- package/dist/types/src/extensions/review/change-bar.d.ts.map +1 -0
- package/dist/types/src/extensions/{comments.d.ts → review/comments.d.ts} +56 -38
- package/dist/types/src/extensions/review/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/review/comments.test.d.ts +2 -0
- package/dist/types/src/extensions/review/comments.test.d.ts.map +1 -0
- package/dist/types/src/extensions/review/diff.d.ts +131 -0
- package/dist/types/src/extensions/review/diff.d.ts.map +1 -0
- package/dist/types/src/extensions/review/diff.test.d.ts +2 -0
- package/dist/types/src/extensions/review/diff.test.d.ts.map +1 -0
- package/dist/types/src/extensions/review/index.d.ts +7 -0
- package/dist/types/src/extensions/review/index.d.ts.map +1 -0
- package/dist/types/src/extensions/review/suggest.d.ts +89 -0
- package/dist/types/src/extensions/review/suggest.d.ts.map +1 -0
- package/dist/types/src/extensions/review/suggest.test.d.ts +2 -0
- package/dist/types/src/extensions/review/suggest.test.d.ts.map +1 -0
- package/dist/types/src/extensions/review/suggestions-overlay.d.ts +25 -0
- package/dist/types/src/extensions/review/suggestions-overlay.d.ts.map +1 -0
- package/dist/types/src/extensions/review/suggestions-overlay.test.d.ts +2 -0
- package/dist/types/src/extensions/review/suggestions-overlay.test.d.ts.map +1 -0
- package/dist/types/src/extensions/review/track-changes.d.ts +27 -0
- package/dist/types/src/extensions/review/track-changes.d.ts.map +1 -0
- package/dist/types/src/extensions/review/track-changes.test.d.ts +2 -0
- package/dist/types/src/extensions/review/track-changes.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{busy.d.ts → state/busy.d.ts} +1 -0
- package/dist/types/src/extensions/state/busy.d.ts.map +1 -0
- package/dist/types/src/extensions/{focus.d.ts → state/focus.d.ts} +4 -0
- package/dist/types/src/extensions/state/focus.d.ts.map +1 -0
- package/dist/types/src/extensions/state/index.d.ts +5 -0
- package/dist/types/src/extensions/state/index.d.ts.map +1 -0
- package/dist/types/src/extensions/state/modal.d.ts.map +1 -0
- package/dist/types/src/extensions/{selection.d.ts → state/selection.d.ts} +1 -1
- package/dist/types/src/extensions/state/selection.d.ts.map +1 -0
- package/dist/types/src/extensions/state/selection.test.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/index.d.ts +4 -0
- package/dist/types/src/extensions/streaming/index.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/pending/index.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/pending/pending-text-stream.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/pending/pending-text-stream.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{pending → streaming/pending}/pending-text.d.ts +1 -0
- package/dist/types/src/extensions/streaming/pending/pending-text.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/pending/pending-text.test.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/auto-scroll.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/crawler.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/index.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/scroll-past-end.d.ts +4 -0
- package/dist/types/src/extensions/streaming/scrolling/scroll-past-end.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/scrollbar-autohide.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/scrolling/scroller.d.ts.map +1 -0
- package/dist/types/src/extensions/{typewriter.d.ts → streaming/typewriter.d.ts} +12 -12
- package/dist/types/src/extensions/streaming/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/streaming/typewriter.test.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/blocks.d.ts +52 -0
- package/dist/types/src/extensions/structure/blocks/blocks.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/blocks.test.d.ts +2 -0
- package/dist/types/src/extensions/structure/blocks/blocks.test.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/drag.d.ts +57 -0
- package/dist/types/src/extensions/structure/blocks/drag.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/index.d.ts +5 -0
- package/dist/types/src/extensions/structure/blocks/index.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/selection.d.ts +47 -0
- package/dist/types/src/extensions/structure/blocks/selection.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/selection.test.d.ts +2 -0
- package/dist/types/src/extensions/structure/blocks/selection.test.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/blocks/types.d.ts +17 -0
- package/dist/types/src/extensions/structure/blocks/types.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/folding/folding.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/folding/index.d.ts +3 -0
- package/dist/types/src/extensions/structure/folding/index.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/folding/turns.d.ts +55 -0
- package/dist/types/src/extensions/structure/folding/turns.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/folding/turns.test.d.ts +2 -0
- package/dist/types/src/extensions/structure/folding/turns.test.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/index.d.ts +4 -0
- package/dist/types/src/extensions/structure/index.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/commands.d.ts +31 -0
- package/dist/types/src/extensions/structure/outliner/commands.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/dnd.d.ts +43 -0
- package/dist/types/src/extensions/structure/outliner/dnd.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/dnd.test.d.ts +2 -0
- package/dist/types/src/extensions/structure/outliner/dnd.test.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/editor.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/editor.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/index.d.ts +1 -0
- package/dist/types/src/extensions/structure/outliner/index.d.ts.map +1 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/menu.d.ts +1 -0
- package/dist/types/src/extensions/structure/outliner/menu.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/outliner.test.d.ts.map +1 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/tree.d.ts +1 -1
- package/dist/types/src/extensions/structure/outliner/tree.d.ts.map +1 -0
- package/dist/types/src/extensions/structure/outliner/tree.test.d.ts.map +1 -0
- package/dist/types/src/headless.d.ts +4 -0
- package/dist/types/src/headless.d.ts.map +1 -0
- package/dist/types/src/styles/theme.d.ts +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/util/util.d.ts +2 -0
- package/dist/types/src/util/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -42
- package/src/defaults.ts +1 -1
- package/src/extensions/{bookmarks.ts → behavior/bookmarks.ts} +24 -14
- package/src/extensions/behavior/index.ts +9 -0
- package/src/extensions/{listener.ts → behavior/listener.ts} +4 -1
- package/src/extensions/{spacing.ts → behavior/spacing.ts} +8 -2
- package/src/extensions/{submit.ts → behavior/submit.ts} +2 -10
- package/src/extensions/{automerge → collab/automerge}/automerge.test.tsx +4 -4
- package/src/extensions/{automerge → collab/automerge}/automerge.ts +7 -4
- package/src/extensions/{automerge → collab/automerge}/cursor.ts +1 -1
- package/src/extensions/{automerge → collab/automerge}/defs.ts +4 -1
- package/src/extensions/{automerge → collab/automerge}/index.ts +1 -0
- package/src/extensions/{automerge → collab/automerge}/sync.ts +1 -2
- package/src/extensions/{automerge → collab/automerge}/update-automerge.ts +4 -0
- package/src/extensions/{automerge → collab/automerge}/update-codemirror.ts +5 -1
- package/src/extensions/{awareness → collab/awareness}/awareness-provider.ts +3 -4
- package/src/extensions/{awareness → collab/awareness}/awareness.ts +19 -16
- package/src/extensions/collab/index.ts +6 -0
- package/src/extensions/{autocomplete → completion}/autocomplete.ts +7 -5
- package/src/extensions/{autocomplete → completion}/index.ts +2 -1
- package/src/extensions/{autocomplete → completion}/match.ts +2 -2
- package/src/extensions/{mention.ts → completion/mention.ts} +3 -0
- package/src/extensions/{autocomplete → completion}/placeholder.ts +1 -1
- package/src/extensions/completion/typeahead.ts +97 -0
- package/src/extensions/{factories.ts → core/factories.ts} +35 -13
- package/src/extensions/core/index.ts +6 -0
- package/src/extensions/{modes.ts → core/modes.ts} +4 -1
- package/src/extensions/{debug.ts → debug/debug.ts} +3 -0
- package/src/extensions/debug/index.ts +5 -0
- package/src/extensions/{annotations.ts → decoration/annotations.ts} +12 -10
- package/src/extensions/decoration/assistant.ts +225 -0
- package/src/extensions/{fader.ts → decoration/fader.ts} +17 -19
- package/src/extensions/{hashtag.ts → decoration/hashtag.ts} +35 -28
- package/src/extensions/decoration/index.ts +11 -0
- package/src/extensions/{marker.ts → decoration/marker.ts} +18 -18
- package/src/extensions/{pos.test.ts → decoration/pos.test.ts} +57 -1
- package/src/extensions/{pos.ts → decoration/pos.ts} +165 -31
- package/src/extensions/{blast.ts → demo/blast.ts} +3 -0
- package/src/extensions/demo/index.ts +6 -0
- package/src/extensions/{snippets.ts → demo/snippets.ts} +5 -1
- package/src/extensions/index.ts +10 -33
- package/src/extensions/language/index.ts +8 -0
- package/src/extensions/language/json/index.ts +5 -0
- package/src/extensions/{json.ts → language/json/json.ts} +11 -6
- package/src/extensions/{markdown → language/markdown}/action.ts +1 -1
- package/src/extensions/{markdown → language/markdown}/bundle.ts +7 -1
- package/src/extensions/{markdown → language/markdown}/changes.test.ts +2 -2
- package/src/extensions/{markdown → language/markdown}/changes.ts +2 -2
- package/src/extensions/{markdown → language/markdown}/debug.ts +3 -0
- package/src/extensions/{markdown → language/markdown}/decorate.ts +96 -85
- package/src/extensions/{markdown → language/markdown}/formatting.test.ts +88 -1
- package/src/extensions/{markdown → language/markdown}/formatting.ts +193 -74
- package/src/extensions/{markdown → language/markdown}/highlight.ts +42 -41
- package/src/extensions/{markdown → language/markdown}/image.test.ts +1 -1
- package/src/extensions/{markdown → language/markdown}/image.ts +6 -6
- package/src/extensions/language/markdown/link.test.ts +58 -0
- package/src/extensions/language/markdown/link.ts +109 -0
- package/src/extensions/{markdown → language/markdown}/parser.test.ts +0 -28
- package/src/extensions/{markdown → language/markdown}/styles.ts +38 -7
- package/src/extensions/{markdown → language/markdown}/table.ts +5 -5
- package/src/extensions/language/mermaid/index.ts +5 -0
- package/src/extensions/language/mermaid/mermaid.ts +44 -0
- package/src/extensions/{tags → language/xml}/DESIGN.md +3 -3
- package/src/extensions/{tags → language/xml}/extended-markdown.test.ts +2 -1
- package/src/extensions/{tags → language/xml}/extended-markdown.ts +2 -4
- package/src/extensions/language/xml/stub.ts +233 -0
- package/src/extensions/language/xml/xml-block-decoration.ts +165 -0
- package/src/extensions/{tags → language/xml}/xml-formatting.ts +54 -38
- package/src/extensions/language/xml/xml-tags.test.ts +588 -0
- package/src/extensions/{tags → language/xml}/xml-tags.ts +165 -66
- package/src/extensions/{tags → language/xml}/xml-util.test.ts +2 -2
- package/src/extensions/review/change-bar.ts +94 -0
- package/src/extensions/review/comments.test.ts +72 -0
- package/src/extensions/{comments.ts → review/comments.ts} +395 -267
- package/src/extensions/review/diff.test.ts +405 -0
- package/src/extensions/review/diff.ts +502 -0
- package/src/extensions/review/index.ts +10 -0
- package/src/extensions/review/suggest.test.ts +170 -0
- package/src/extensions/review/suggest.ts +512 -0
- package/src/extensions/review/suggestions-overlay.test.ts +58 -0
- package/src/extensions/review/suggestions-overlay.ts +50 -0
- package/src/extensions/review/track-changes.test.ts +101 -0
- package/src/extensions/review/track-changes.ts +215 -0
- package/src/extensions/{busy.ts → state/busy.ts} +1 -0
- package/src/extensions/{focus.ts → state/focus.ts} +4 -0
- package/src/extensions/state/index.ts +8 -0
- package/src/extensions/{selection.ts → state/selection.ts} +3 -3
- package/src/extensions/streaming/index.ts +7 -0
- package/src/extensions/{pending → streaming/pending}/pending-text-stream.ts +10 -3
- package/src/extensions/{pending → streaming/pending}/pending-text.test.ts +1 -1
- package/src/extensions/{pending → streaming/pending}/pending-text.ts +20 -19
- package/src/extensions/{scrolling → streaming/scrolling}/auto-scroll.ts +9 -6
- package/src/extensions/{scrolling → streaming/scrolling}/scroll-past-end.ts +1 -0
- package/src/extensions/{scrolling → streaming/scrolling}/scrollbar-autohide.ts +13 -11
- package/src/extensions/{typewriter.ts → streaming/typewriter.ts} +46 -47
- package/src/extensions/structure/blocks/DESIGN.md +134 -0
- package/src/extensions/structure/blocks/blocks.test.ts +147 -0
- package/src/extensions/structure/blocks/blocks.ts +219 -0
- package/src/extensions/structure/blocks/drag.ts +996 -0
- package/src/extensions/structure/blocks/index.ts +8 -0
- package/src/extensions/structure/blocks/selection.test.ts +56 -0
- package/src/extensions/structure/blocks/selection.ts +279 -0
- package/src/extensions/structure/blocks/types.ts +22 -0
- package/src/extensions/{folding.ts → structure/folding/folding.ts} +12 -10
- package/src/extensions/structure/folding/index.ts +6 -0
- package/src/extensions/structure/folding/turns.test.ts +72 -0
- package/src/extensions/structure/folding/turns.ts +209 -0
- package/src/extensions/structure/index.ts +7 -0
- package/src/extensions/structure/outliner/DESIGN.md +183 -0
- package/src/extensions/structure/outliner/SPEC.mdl +305 -0
- package/src/extensions/{outliner → structure/outliner}/commands.ts +125 -42
- package/src/extensions/structure/outliner/dnd.test.ts +142 -0
- package/src/extensions/structure/outliner/dnd.ts +375 -0
- package/src/extensions/structure/outliner/editor.test.ts +85 -0
- package/src/extensions/{outliner → structure/outliner}/editor.ts +14 -9
- package/src/extensions/{outliner → structure/outliner}/index.ts +1 -0
- package/src/extensions/{outliner → structure/outliner}/menu.ts +48 -25
- package/src/extensions/structure/outliner/outliner.test.ts +193 -0
- package/src/extensions/structure/outliner/outliner.ts +183 -0
- package/src/extensions/{outliner → structure/outliner}/tree.test.ts +2 -2
- package/src/extensions/{outliner → structure/outliner}/tree.ts +1 -1
- package/src/headless.ts +13 -0
- package/src/styles/theme.ts +3 -1
- package/src/util/util.ts +3 -0
- package/dist/lib/browser/index.mjs +0 -9594
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/types/index.mjs +0 -33
- package/dist/lib/browser/types/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -9596
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/types/index.mjs +0 -35
- package/dist/lib/node-esm/types/index.mjs.map +0 -7
- package/dist/types/src/extensions/annotations.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete/index.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete/match.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/defs.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/index.d.ts +0 -2
- package/dist/types/src/extensions/automerge/index.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/sync.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +0 -1
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness/awareness.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness/index.d.ts.map +0 -1
- package/dist/types/src/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/extensions/blocks.d.ts +0 -2
- package/dist/types/src/extensions/blocks.d.ts.map +0 -1
- package/dist/types/src/extensions/bookmarks.d.ts.map +0 -1
- package/dist/types/src/extensions/busy.d.ts.map +0 -1
- package/dist/types/src/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/extensions/debug.d.ts.map +0 -1
- package/dist/types/src/extensions/dnd.d.ts.map +0 -1
- package/dist/types/src/extensions/factories.d.ts.map +0 -1
- package/dist/types/src/extensions/factories.test.d.ts.map +0 -1
- package/dist/types/src/extensions/fader.d.ts.map +0 -1
- package/dist/types/src/extensions/focus.d.ts.map +0 -1
- package/dist/types/src/extensions/folding.d.ts.map +0 -1
- package/dist/types/src/extensions/hashtag.d.ts +0 -3
- package/dist/types/src/extensions/hashtag.d.ts.map +0 -1
- package/dist/types/src/extensions/json.d.ts +0 -7
- package/dist/types/src/extensions/json.d.ts.map +0 -1
- package/dist/types/src/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/action.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/changes.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/changes.test.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/debug.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/image.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/image.test.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/link.d.ts +0 -7
- package/dist/types/src/extensions/markdown/link.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/parser.test.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/styles.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/table.d.ts.map +0 -1
- package/dist/types/src/extensions/marker.d.ts.map +0 -1
- package/dist/types/src/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/extensions/modal.d.ts.map +0 -1
- package/dist/types/src/extensions/modes.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/commands.d.ts +0 -10
- package/dist/types/src/extensions/outliner/commands.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/editor.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/editor.test.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/index.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/menu.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/selection.d.ts +0 -12
- package/dist/types/src/extensions/outliner/selection.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/tree.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner/tree.test.d.ts.map +0 -1
- package/dist/types/src/extensions/pending/index.d.ts.map +0 -1
- package/dist/types/src/extensions/pending/pending-text-stream.d.ts.map +0 -1
- package/dist/types/src/extensions/pending/pending-text-stream.test.d.ts.map +0 -1
- package/dist/types/src/extensions/pending/pending-text.d.ts.map +0 -1
- package/dist/types/src/extensions/pending/pending-text.test.d.ts.map +0 -1
- package/dist/types/src/extensions/pos.d.ts.map +0 -1
- package/dist/types/src/extensions/pos.test.d.ts.map +0 -1
- package/dist/types/src/extensions/replacer.d.ts.map +0 -1
- package/dist/types/src/extensions/replacer.test.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/crawler.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/index.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts +0 -3
- package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/scrollbar-autohide.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling/scroller.d.ts.map +0 -1
- package/dist/types/src/extensions/selection.d.ts.map +0 -1
- package/dist/types/src/extensions/selection.test.d.ts.map +0 -1
- package/dist/types/src/extensions/snippets.d.ts.map +0 -1
- package/dist/types/src/extensions/spacing.d.ts.map +0 -1
- package/dist/types/src/extensions/state.d.ts +0 -2
- package/dist/types/src/extensions/state.d.ts.map +0 -1
- package/dist/types/src/extensions/submit.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/index.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/stub.d.ts +0 -35
- package/dist/types/src/extensions/tags/stub.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/widgets/anchor.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/widgets/index.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +0 -31
- package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/xml-util.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/xml-util.test.d.ts.map +0 -1
- package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
- package/dist/types/src/extensions/typewriter.test.d.ts.map +0 -1
- package/src/extensions/autocomplete/typeahead.ts +0 -87
- package/src/extensions/blocks.ts +0 -131
- package/src/extensions/markdown/link.ts +0 -49
- package/src/extensions/outliner/editor.test.ts +0 -33
- package/src/extensions/outliner/outliner.test.ts +0 -99
- package/src/extensions/outliner/outliner.ts +0 -166
- package/src/extensions/outliner/selection.ts +0 -50
- package/src/extensions/state.ts +0 -7
- package/src/extensions/tags/stub.ts +0 -80
- package/src/extensions/tags/xml-block-decoration.ts +0 -123
- package/dist/types/src/extensions/{dnd.d.ts → behavior/dnd.d.ts} +0 -0
- package/dist/types/src/extensions/{submit.d.ts → behavior/submit.d.ts} +0 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/automerge.d.ts +0 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/automerge.test.d.ts +0 -0
- package/dist/types/src/extensions/{automerge → collab/automerge}/sync.d.ts +0 -0
- package/dist/types/src/extensions/{awareness → collab/awareness}/awareness-provider.d.ts +0 -0
- package/dist/types/src/extensions/{awareness → collab/awareness}/index.d.ts +0 -0
- package/dist/types/src/extensions/{autocomplete → completion}/autocomplete.d.ts +0 -0
- package/dist/types/src/extensions/{autocomplete → completion}/placeholder.d.ts +0 -0
- package/dist/types/src/extensions/{factories.test.d.ts → core/factories.test.d.ts} +0 -0
- package/dist/types/src/extensions/{fader.d.ts → decoration/fader.d.ts} +0 -0
- package/dist/types/src/extensions/{marker.d.ts → decoration/marker.d.ts} +0 -0
- package/dist/types/src/extensions/{pos.test.d.ts → decoration/pos.test.d.ts} +0 -0
- package/dist/types/src/extensions/{replacer.test.d.ts → decoration/replacer.test.d.ts} +0 -0
- package/dist/types/src/extensions/{snippets.d.ts → demo/snippets.d.ts} +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/action.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/bundle.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/changes.test.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/formatting.test.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/image.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/image.test.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/index.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/parser.test.d.ts +0 -0
- package/dist/types/src/extensions/{markdown → language/markdown}/table.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/extended-markdown.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/extended-markdown.test.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/index.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/widgets/anchor.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/widgets/index.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/xml-formatting.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/xml-util.d.ts +0 -0
- package/dist/types/src/extensions/{tags → language/xml}/xml-util.test.d.ts +0 -0
- package/dist/types/src/extensions/{modal.d.ts → state/modal.d.ts} +0 -0
- package/dist/types/src/extensions/{selection.test.d.ts → state/selection.test.d.ts} +0 -0
- package/dist/types/src/extensions/{pending → streaming/pending}/index.d.ts +0 -0
- package/dist/types/src/extensions/{pending → streaming/pending}/pending-text-stream.d.ts +0 -0
- package/dist/types/src/extensions/{pending → streaming/pending}/pending-text-stream.test.d.ts +0 -0
- package/dist/types/src/extensions/{pending → streaming/pending}/pending-text.test.d.ts +0 -0
- package/dist/types/src/extensions/{scrolling → streaming/scrolling}/auto-scroll.d.ts +0 -0
- package/dist/types/src/extensions/{scrolling → streaming/scrolling}/crawler.d.ts +0 -0
- package/dist/types/src/extensions/{scrolling → streaming/scrolling}/index.d.ts +0 -0
- package/dist/types/src/extensions/{scrolling → streaming/scrolling}/scrollbar-autohide.d.ts +0 -0
- package/dist/types/src/extensions/{scrolling → streaming/scrolling}/scroller.d.ts +0 -0
- package/dist/types/src/extensions/{typewriter.test.d.ts → streaming/typewriter.test.d.ts} +0 -0
- package/dist/types/src/extensions/{folding.d.ts → structure/folding/folding.d.ts} +0 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/editor.d.ts +0 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/editor.test.d.ts +0 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/outliner.d.ts +0 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/outliner.test.d.ts +0 -0
- package/dist/types/src/extensions/{outliner → structure/outliner}/tree.test.d.ts +0 -0
- package/src/extensions/{dnd.ts → behavior/dnd.ts} +29 -29
- package/src/extensions/{awareness → collab/awareness}/index.ts +0 -0
- package/src/extensions/{factories.test.ts → core/factories.test.ts} +0 -0
- package/src/extensions/{replacer.test.ts → decoration/replacer.test.ts} +0 -0
- package/src/extensions/{replacer.ts → decoration/replacer.ts} +25 -25
- /package/src/extensions/{markdown → language/markdown}/index.ts +0 -0
- /package/src/extensions/{tags → language/xml}/index.ts +0 -0
- /package/src/extensions/{tags → language/xml}/testing/text.md +0 -0
- /package/src/extensions/{tags → language/xml}/testing/text.txt +0 -0
- /package/src/extensions/{tags → language/xml}/widgets/anchor.ts +0 -0
- /package/src/extensions/{tags → language/xml}/widgets/index.ts +0 -0
- /package/src/extensions/{tags → language/xml}/xml-util.ts +0 -0
- /package/src/extensions/{modal.ts → state/modal.ts} +0 -0
- /package/src/extensions/{selection.test.ts → state/selection.test.ts} +0 -0
- /package/src/extensions/{pending → streaming/pending}/index.ts +0 -0
- /package/src/extensions/{pending → streaming/pending}/pending-text-stream.test.ts +0 -0
- /package/src/extensions/{scrolling → streaming/scrolling}/crawler.ts +0 -0
- /package/src/extensions/{scrolling → streaming/scrolling}/index.ts +0 -0
- /package/src/extensions/{scrolling → streaming/scrolling}/scroller.ts +0 -0
- /package/src/extensions/{typewriter.test.ts → streaming/typewriter.test.ts} +0 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { forceParsing } from '@codemirror/language';
|
|
6
|
+
import { EditorState } from '@codemirror/state';
|
|
7
|
+
import { EditorView, WidgetType } from '@codemirror/view';
|
|
8
|
+
import { describe, test } from 'vitest';
|
|
9
|
+
|
|
10
|
+
import { trim } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { decorationSetToArray } from '../../../util';
|
|
13
|
+
import { extendedMarkdown } from './extended-markdown';
|
|
14
|
+
import { StubWidget } from './stub';
|
|
15
|
+
import {
|
|
16
|
+
type XmlWidgetDef,
|
|
17
|
+
type XmlWidgetProps,
|
|
18
|
+
type XmlWidgetState,
|
|
19
|
+
navigateNextEffect,
|
|
20
|
+
navigatePreviousEffect,
|
|
21
|
+
xmlTagContextEffect,
|
|
22
|
+
xmlTagRebuildEffect,
|
|
23
|
+
xmlTagResetEffect,
|
|
24
|
+
xmlTags,
|
|
25
|
+
xmlTagUpdateEffect,
|
|
26
|
+
} from './xml-tags';
|
|
27
|
+
|
|
28
|
+
//
|
|
29
|
+
// Harness.
|
|
30
|
+
//
|
|
31
|
+
// These are characterization tests: they lock in the CURRENT observable behavior of `xmlTags`'s
|
|
32
|
+
// decoration builder before it is optimized (viewport-aware rebuilds). They read the real decoration
|
|
33
|
+
// field through the public `EditorView.decorations` facet — not the DOM — so they are independent of
|
|
34
|
+
// happy-dom's lack of layout and survive internal refactors. Each registry entry is given a recording
|
|
35
|
+
// `factory` widget so the built widget's props (id) can be inspected.
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
/** Widget whose props are inspectable so tests can assert the id the builder assigned. */
|
|
39
|
+
class TestWidget extends WidgetType {
|
|
40
|
+
constructor(readonly props: XmlWidgetProps) {
|
|
41
|
+
super();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override eq(other: this): boolean {
|
|
45
|
+
return other instanceof TestWidget && other.props.id === this.props.id;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override toDOM(): HTMLElement {
|
|
49
|
+
const el = document.createElement('span');
|
|
50
|
+
el.className = 'cm-test-xml';
|
|
51
|
+
el.dataset.tag = String(this.props._tag ?? '');
|
|
52
|
+
return el;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override ignoreEvent(): boolean {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Attach a recording factory to each registry entry. */
|
|
61
|
+
const withFactory = (defs: Record<string, Omit<XmlWidgetDef, 'factory'>>): Record<string, XmlWidgetDef> =>
|
|
62
|
+
Object.fromEntries(
|
|
63
|
+
Object.entries(defs).map(([tag, def]) => [tag, { ...def, factory: (props) => new TestWidget(props) }]),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
type Descriptor = {
|
|
67
|
+
from: number;
|
|
68
|
+
to: number;
|
|
69
|
+
tag: string;
|
|
70
|
+
block: boolean;
|
|
71
|
+
streaming: boolean;
|
|
72
|
+
id: string | undefined;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** Read the live xmlTags decoration field via the public decorations facet. */
|
|
76
|
+
const xmlDecorations = (view: EditorView): Descriptor[] => {
|
|
77
|
+
const out: Descriptor[] = [];
|
|
78
|
+
for (const source of view.state.facet(EditorView.decorations)) {
|
|
79
|
+
const set = typeof source === 'function' ? source(view) : source;
|
|
80
|
+
if (!set) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
for (const { from, to, value } of decorationSetToArray(set)) {
|
|
84
|
+
const spec: {
|
|
85
|
+
tag?: string;
|
|
86
|
+
block?: boolean;
|
|
87
|
+
streaming?: boolean;
|
|
88
|
+
widget?: { props?: { id?: string }; id?: string };
|
|
89
|
+
} = value.spec ?? {};
|
|
90
|
+
// Only xmlTags decorations carry a `tag` spec.
|
|
91
|
+
if (spec.tag === undefined) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
out.push({
|
|
95
|
+
from,
|
|
96
|
+
to,
|
|
97
|
+
tag: spec.tag,
|
|
98
|
+
block: !!spec.block,
|
|
99
|
+
streaming: !!spec.streaming,
|
|
100
|
+
id: spec.widget?.props?.id ?? spec.widget?.id,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return out.sort((a, b) => a.from - b.from || a.to - b.to);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const flush = () => new Promise<void>((resolve) => queueMicrotask(resolve));
|
|
109
|
+
|
|
110
|
+
/** The live StubWidget for an id, as CodeMirror would render it. */
|
|
111
|
+
const stubWidget = (view: EditorView, id: string): StubWidget<XmlWidgetProps> => {
|
|
112
|
+
for (const source of view.state.facet(EditorView.decorations)) {
|
|
113
|
+
const set = typeof source === 'function' ? source(view) : source;
|
|
114
|
+
if (!set) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
for (const { value } of decorationSetToArray(set)) {
|
|
118
|
+
const widget = value.spec?.widget;
|
|
119
|
+
if (widget instanceof StubWidget && widget.id === id) {
|
|
120
|
+
return widget;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new Error(`no widget: ${id}`);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const widgetProps = (view: EditorView, id: string): XmlWidgetProps => stubWidget(view, id).props;
|
|
129
|
+
|
|
130
|
+
const createView = (
|
|
131
|
+
doc: string,
|
|
132
|
+
{ registry = {}, bookmarks, setWidgets }: Parameters<typeof xmlTags>[0] = {},
|
|
133
|
+
): EditorView => {
|
|
134
|
+
const parent = document.createElement('div');
|
|
135
|
+
return new EditorView({
|
|
136
|
+
state: EditorState.create({
|
|
137
|
+
doc,
|
|
138
|
+
extensions: [extendedMarkdown({ registry }), xmlTags({ registry, bookmarks, setWidgets })],
|
|
139
|
+
}),
|
|
140
|
+
parent,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/** Force a complete parse + full decoration rebuild, then read — the ground-truth builder output. */
|
|
145
|
+
const rebuild = async (view: EditorView): Promise<Descriptor[]> => {
|
|
146
|
+
forceParsing(view, view.state.doc.length, 5_000);
|
|
147
|
+
view.dispatch({ effects: xmlTagRebuildEffect.of(null) });
|
|
148
|
+
await flush();
|
|
149
|
+
return xmlDecorations(view);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
describe('xmlTags decorations', () => {
|
|
153
|
+
test('empty document yields no decorations', async ({ expect }) => {
|
|
154
|
+
const view = createView('', { registry: withFactory({ prompt: { block: true } }) });
|
|
155
|
+
expect(await rebuild(view)).toEqual([]);
|
|
156
|
+
view.destroy();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('no registry yields no decorations', async ({ expect }) => {
|
|
160
|
+
const view = createView('<prompt>hello</prompt>');
|
|
161
|
+
expect(await rebuild(view)).toEqual([]);
|
|
162
|
+
view.destroy();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('block element becomes a block widget spanning the whole element', async ({ expect }) => {
|
|
166
|
+
const doc = '<prompt>hello</prompt>';
|
|
167
|
+
const view = createView(doc, { registry: withFactory({ prompt: { block: true } }) });
|
|
168
|
+
const decorations = await rebuild(view);
|
|
169
|
+
expect(decorations).toEqual([
|
|
170
|
+
{ from: 0, to: doc.length, tag: 'prompt', block: true, streaming: false, id: `cm-xml-0-${doc.length}` },
|
|
171
|
+
]);
|
|
172
|
+
view.destroy();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('inline element becomes an inline widget', async ({ expect }) => {
|
|
176
|
+
const doc = 'see <reference>x</reference> here';
|
|
177
|
+
const view = createView(doc, { registry: withFactory({ reference: { block: false } }) });
|
|
178
|
+
const [decoration] = await rebuild(view);
|
|
179
|
+
expect(decoration.tag).toBe('reference');
|
|
180
|
+
expect(decoration.block).toBe(false);
|
|
181
|
+
expect(decoration.from).toBe(4);
|
|
182
|
+
expect(decoration.to).toBe('see <reference>x</reference>'.length);
|
|
183
|
+
view.destroy();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('unregistered tags are ignored', async ({ expect }) => {
|
|
187
|
+
const view = createView('<mystery>x</mystery>', { registry: withFactory({ prompt: { block: true } }) });
|
|
188
|
+
expect(await rebuild(view)).toEqual([]);
|
|
189
|
+
view.destroy();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('self-closing element is decorated', async ({ expect }) => {
|
|
193
|
+
const view = createView('<toolkit />', { registry: withFactory({ toolkit: { block: true } }) });
|
|
194
|
+
const decorations = await rebuild(view);
|
|
195
|
+
expect(decorations).toHaveLength(1);
|
|
196
|
+
expect(decorations[0].tag).toBe('toolkit');
|
|
197
|
+
view.destroy();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('explicit id attribute wins over the position-derived id', async ({ expect }) => {
|
|
201
|
+
const view = createView('<prompt id="chosen">x</prompt>', { registry: withFactory({ prompt: { block: true } }) });
|
|
202
|
+
const [decoration] = await rebuild(view);
|
|
203
|
+
expect(decoration.id).toBe('chosen');
|
|
204
|
+
view.destroy();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('multiple elements are decorated in document order', async ({ expect }) => {
|
|
208
|
+
const doc = trim`
|
|
209
|
+
<prompt>a</prompt>
|
|
210
|
+
|
|
211
|
+
middle
|
|
212
|
+
|
|
213
|
+
<summary>b</summary>
|
|
214
|
+
`;
|
|
215
|
+
const view = createView(doc, { registry: withFactory({ prompt: { block: true }, summary: { block: true } }) });
|
|
216
|
+
const decorations = await rebuild(view);
|
|
217
|
+
expect(decorations.map((decoration) => decoration.tag)).toEqual(['prompt', 'summary']);
|
|
218
|
+
expect(decorations[0].from).toBeLessThan(decorations[1].from);
|
|
219
|
+
view.destroy();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('tags inside a fenced code block are not decorated', async ({ expect }) => {
|
|
223
|
+
const doc = trim`
|
|
224
|
+
\`\`\`xml
|
|
225
|
+
<prompt>ignored</prompt>
|
|
226
|
+
\`\`\`
|
|
227
|
+
`;
|
|
228
|
+
const view = createView(doc, { registry: withFactory({ prompt: { block: true } }) });
|
|
229
|
+
expect(await rebuild(view)).toEqual([]);
|
|
230
|
+
view.destroy();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('streaming tags', () => {
|
|
234
|
+
test('an unclosed streaming tag decorates from the tag to the document end', async ({ expect }) => {
|
|
235
|
+
const doc = 'intro\n\n<think>partial output';
|
|
236
|
+
const view = createView(doc, { registry: withFactory({ think: { streaming: true } }) });
|
|
237
|
+
const decorations = await rebuild(view);
|
|
238
|
+
expect(decorations).toHaveLength(1);
|
|
239
|
+
const [decoration] = decorations;
|
|
240
|
+
expect(decoration.tag).toBe('think');
|
|
241
|
+
expect(decoration.streaming).toBe(true);
|
|
242
|
+
expect(decoration.from).toBe(doc.indexOf('<think>'));
|
|
243
|
+
expect(decoration.to).toBe(doc.length);
|
|
244
|
+
// Streaming widgets keep an open-position id so the portal survives the closing tag arriving.
|
|
245
|
+
expect(decoration.id).toBe(`cm-xml-${doc.indexOf('<think>')}`);
|
|
246
|
+
view.destroy();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('a closed streaming tag decorates the element and is no longer streaming', async ({ expect }) => {
|
|
250
|
+
const doc = '<think>done</think>';
|
|
251
|
+
const view = createView(doc, { registry: withFactory({ think: { streaming: true } }) });
|
|
252
|
+
const decorations = await rebuild(view);
|
|
253
|
+
expect(decorations).toHaveLength(1);
|
|
254
|
+
const [decoration] = decorations;
|
|
255
|
+
expect(decoration.streaming).toBe(false);
|
|
256
|
+
expect(decoration.from).toBe(0);
|
|
257
|
+
expect(decoration.to).toBe(doc.length);
|
|
258
|
+
// A streaming def keeps the open-position id form even when closed.
|
|
259
|
+
expect(decoration.id).toBe('cm-xml-0');
|
|
260
|
+
view.destroy();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('only the first unclosed streaming tag is decorated', async ({ expect }) => {
|
|
264
|
+
const doc = '<think>one</think>\n\n<think>two unclosed';
|
|
265
|
+
const view = createView(doc, { registry: withFactory({ think: { streaming: true } }) });
|
|
266
|
+
const decorations = await rebuild(view);
|
|
267
|
+
const streaming = decorations.filter((decoration) => decoration.streaming);
|
|
268
|
+
expect(streaming).toHaveLength(1);
|
|
269
|
+
expect(streaming[0].from).toBe(doc.lastIndexOf('<think>'));
|
|
270
|
+
view.destroy();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('url-scheme widgets', () => {
|
|
275
|
+
const registry = withFactory({
|
|
276
|
+
embed: { block: true, urlSchemes: ['dxn:'] },
|
|
277
|
+
chip: { block: false, urlSchemes: ['dxn:'] },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test('image node with a matching scheme becomes a block widget', async ({ expect }) => {
|
|
281
|
+
const doc = '';
|
|
282
|
+
const view = createView(doc, { registry });
|
|
283
|
+
const [decoration] = await rebuild(view);
|
|
284
|
+
expect(decoration.tag).toBe('embed');
|
|
285
|
+
expect(decoration.block).toBe(true);
|
|
286
|
+
expect(decoration.id).toBe('cm-url-dxn:123-0');
|
|
287
|
+
view.destroy();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('link node with a matching scheme becomes an inline widget', async ({ expect }) => {
|
|
291
|
+
const doc = '[label](dxn:123)';
|
|
292
|
+
const view = createView(doc, { registry });
|
|
293
|
+
const [decoration] = await rebuild(view);
|
|
294
|
+
expect(decoration.tag).toBe('chip');
|
|
295
|
+
expect(decoration.block).toBe(false);
|
|
296
|
+
view.destroy();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('repeated occurrences of the same url get stable incrementing ids', async ({ expect }) => {
|
|
300
|
+
const doc = '\n\n';
|
|
301
|
+
const view = createView(doc, { registry });
|
|
302
|
+
const ids = (await rebuild(view)).map((decoration) => decoration.id);
|
|
303
|
+
expect(ids).toEqual(['cm-url-dxn:x-0', 'cm-url-dxn:x-1']);
|
|
304
|
+
view.destroy();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('non-matching schemes are ignored', async ({ expect }) => {
|
|
308
|
+
const view = createView('', { registry });
|
|
309
|
+
expect(await rebuild(view)).toEqual([]);
|
|
310
|
+
view.destroy();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('effects', () => {
|
|
315
|
+
test('reset effect clears decorations when the document is unchanged', async ({ expect }) => {
|
|
316
|
+
const view = createView('<prompt>x</prompt>', { registry: withFactory({ prompt: { block: true } }) });
|
|
317
|
+
expect(await rebuild(view)).toHaveLength(1);
|
|
318
|
+
view.dispatch({ effects: xmlTagResetEffect.of(null) });
|
|
319
|
+
await flush();
|
|
320
|
+
expect(xmlDecorations(view)).toEqual([]);
|
|
321
|
+
view.destroy();
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('incremental rebuild matches a full rebuild (optimization invariant)', () => {
|
|
326
|
+
const registry = withFactory({ prompt: { block: true }, summary: { block: true }, reference: { block: false } });
|
|
327
|
+
|
|
328
|
+
const seed = trim`
|
|
329
|
+
# Title
|
|
330
|
+
|
|
331
|
+
<prompt>first</prompt>
|
|
332
|
+
|
|
333
|
+
Some prose with a <reference>ref</reference> inline.
|
|
334
|
+
|
|
335
|
+
<summary>done</summary>
|
|
336
|
+
`;
|
|
337
|
+
|
|
338
|
+
// KNOWN BUG (to fix during the viewport optimization). Appending after the last widget takes the
|
|
339
|
+
// partial-merge branch, which re-walks the last element and ADDS its decoration again without
|
|
340
|
+
// removing the old one (the merge only filters out `streaming` decorations), so the last widget is
|
|
341
|
+
// duplicated. In a live editor the parse-completion full rebuild (see the "settles" test below)
|
|
342
|
+
// masks this transiently. When the optimization fixes the merge, flip `test.fails` → `test`.
|
|
343
|
+
test.fails('appending at the tail — synchronous incremental result matches a full rebuild', async ({ expect }) => {
|
|
344
|
+
const view = createView(seed, { registry });
|
|
345
|
+
await rebuild(view);
|
|
346
|
+
view.dispatch({ changes: { from: view.state.doc.length, insert: '\n\ntrailing text' } });
|
|
347
|
+
const incremental = xmlDecorations(view);
|
|
348
|
+
const full = await rebuild(view);
|
|
349
|
+
expect(incremental).toEqual(full);
|
|
350
|
+
view.destroy();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test('appending at the tail — settles to a correct decoration set', async ({ expect }) => {
|
|
354
|
+
const view = createView(seed, { registry });
|
|
355
|
+
await rebuild(view);
|
|
356
|
+
view.dispatch({ changes: { from: view.state.doc.length, insert: '\n\ntrailing text' } });
|
|
357
|
+
await flush(); // Let the parse-completion plugin issue its full rebuild.
|
|
358
|
+
const settled = xmlDecorations(view);
|
|
359
|
+
const full = await rebuild(view);
|
|
360
|
+
expect(settled).toEqual(full);
|
|
361
|
+
view.destroy();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test('inserting at the head', async ({ expect }) => {
|
|
365
|
+
const view = createView(seed, { registry });
|
|
366
|
+
await rebuild(view);
|
|
367
|
+
view.dispatch({ changes: { from: 0, insert: 'PREFIX\n\n' } });
|
|
368
|
+
const incremental = xmlDecorations(view);
|
|
369
|
+
const full = await rebuild(view);
|
|
370
|
+
expect(incremental).toEqual(full);
|
|
371
|
+
view.destroy();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('editing in the middle', async ({ expect }) => {
|
|
375
|
+
const view = createView(seed, { registry });
|
|
376
|
+
await rebuild(view);
|
|
377
|
+
const at = seed.indexOf('Some prose');
|
|
378
|
+
view.dispatch({ changes: { from: at, insert: 'EDIT ' } });
|
|
379
|
+
const incremental = xmlDecorations(view);
|
|
380
|
+
const full = await rebuild(view);
|
|
381
|
+
expect(incremental).toEqual(full);
|
|
382
|
+
view.destroy();
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe('bookmark navigation', () => {
|
|
387
|
+
const registry = withFactory({ prompt: { block: true }, summary: { block: true } });
|
|
388
|
+
const doc = trim`
|
|
389
|
+
intro
|
|
390
|
+
|
|
391
|
+
<prompt>one</prompt>
|
|
392
|
+
|
|
393
|
+
middle
|
|
394
|
+
|
|
395
|
+
<summary>two</summary>
|
|
396
|
+
|
|
397
|
+
end
|
|
398
|
+
`;
|
|
399
|
+
|
|
400
|
+
test('navigate-next moves the selection toward the next bookmarked tag', async ({ expect }) => {
|
|
401
|
+
const view = createView(doc, { registry, bookmarks: ['prompt', 'summary'] });
|
|
402
|
+
await rebuild(view);
|
|
403
|
+
view.dispatch({ selection: { anchor: 0 } });
|
|
404
|
+
view.dispatch({ effects: navigateNextEffect.of() });
|
|
405
|
+
await flush();
|
|
406
|
+
expect(view.state.selection.main.head).toBeGreaterThan(0);
|
|
407
|
+
view.destroy();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('navigate-previous moves the selection toward an earlier bookmarked tag', async ({ expect }) => {
|
|
411
|
+
const view = createView(doc, { registry, bookmarks: ['prompt', 'summary'] });
|
|
412
|
+
await rebuild(view);
|
|
413
|
+
view.dispatch({ selection: { anchor: view.state.doc.length } });
|
|
414
|
+
view.dispatch({ effects: navigatePreviousEffect.of() });
|
|
415
|
+
await flush();
|
|
416
|
+
expect(view.state.selection.main.head).toBeLessThan(view.state.doc.length);
|
|
417
|
+
view.destroy();
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
//
|
|
423
|
+
// Widget state.
|
|
424
|
+
//
|
|
425
|
+
// `xmlTags` documents that widget state may be updated BEFORE the widget mounts (a thread returning
|
|
426
|
+
// from a remount replaces the document, then rehydrates each widget's state out-of-band). These lock
|
|
427
|
+
// in that contract at the editor level: the effect must survive the reset, must not be dropped when it
|
|
428
|
+
// shares a transaction, and must reach a widget that mounts afterwards.
|
|
429
|
+
//
|
|
430
|
+
|
|
431
|
+
describe('xmlTags widget state', () => {
|
|
432
|
+
const stateRegistry: Record<string, XmlWidgetDef> = {
|
|
433
|
+
toolCall: { block: true, Component: () => null },
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const doc = trim`
|
|
437
|
+
<toolCall id="a" />
|
|
438
|
+
|
|
439
|
+
<toolCall id="b" />
|
|
440
|
+
`;
|
|
441
|
+
|
|
442
|
+
test('applies every widget update effect in a single transaction', async ({ expect }) => {
|
|
443
|
+
const view = createView(doc, { registry: stateRegistry });
|
|
444
|
+
await rebuild(view);
|
|
445
|
+
view.dispatch({
|
|
446
|
+
effects: [
|
|
447
|
+
xmlTagUpdateEffect.of({ id: 'a', value: { blocks: ['A'] } }),
|
|
448
|
+
xmlTagUpdateEffect.of({ id: 'b', value: { blocks: ['B'] } }),
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
view.dispatch({ effects: xmlTagRebuildEffect.of(null) });
|
|
452
|
+
await flush();
|
|
453
|
+
expect(widgetProps(view, 'a').blocks).toEqual(['A']);
|
|
454
|
+
expect(widgetProps(view, 'b').blocks).toEqual(['B']);
|
|
455
|
+
view.destroy();
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test('widget state survives the reset that replaces the document', async ({ expect }) => {
|
|
459
|
+
const view = createView('placeholder', { registry: stateRegistry });
|
|
460
|
+
await rebuild(view);
|
|
461
|
+
|
|
462
|
+
// What `setContent` dispatches on remount: replace the document and clear accumulated state.
|
|
463
|
+
view.dispatch({
|
|
464
|
+
changes: { from: 0, to: view.state.doc.length, insert: doc },
|
|
465
|
+
effects: xmlTagResetEffect.of(null),
|
|
466
|
+
});
|
|
467
|
+
view.dispatch({ effects: xmlTagUpdateEffect.of({ id: 'a', value: { blocks: ['A'] } }) });
|
|
468
|
+
view.dispatch({ effects: xmlTagRebuildEffect.of(null) });
|
|
469
|
+
await flush();
|
|
470
|
+
expect(widgetProps(view, 'a').blocks).toEqual(['A']);
|
|
471
|
+
view.destroy();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test('state reaches a mounted widget after a rebuild replaced its decoration instance', async ({ expect }) => {
|
|
475
|
+
let mounted: XmlWidgetState[] = [];
|
|
476
|
+
const view = createView(doc, { registry: stateRegistry, setWidgets: (widgets) => (mounted = widgets) });
|
|
477
|
+
await rebuild(view);
|
|
478
|
+
stubWidget(view, 'a').toDOM(view);
|
|
479
|
+
|
|
480
|
+
// Replacing the document rebuilds decorations into fresh widget instances, but `StubWidget.eq`
|
|
481
|
+
// (id equality) makes CodeMirror keep the rendered DOM — so the rebuilt instance has no root and
|
|
482
|
+
// an update routed through the decoration set would find nothing to re-render.
|
|
483
|
+
view.dispatch({
|
|
484
|
+
changes: { from: 0, to: view.state.doc.length, insert: doc },
|
|
485
|
+
effects: xmlTagResetEffect.of(null),
|
|
486
|
+
});
|
|
487
|
+
expect(stubWidget(view, 'a').root).toBeNull();
|
|
488
|
+
|
|
489
|
+
view.dispatch({ effects: xmlTagUpdateEffect.of({ id: 'a', value: { blocks: ['A'] } }) });
|
|
490
|
+
expect(mounted.find((state) => state.id === 'a')?.props.blocks).toEqual(['A']);
|
|
491
|
+
view.destroy();
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test('a widget mounting after its state arrives receives that state', async ({ expect }) => {
|
|
495
|
+
let mounted: XmlWidgetState[] = [];
|
|
496
|
+
const view = createView(doc, { registry: stateRegistry, setWidgets: (widgets) => (mounted = widgets) });
|
|
497
|
+
|
|
498
|
+
// The one-shot parse-completion rebuild lands first and is never re-armed without a document
|
|
499
|
+
// change, so the state arriving afterwards cannot be picked up by another rebuild.
|
|
500
|
+
await rebuild(view);
|
|
501
|
+
view.dispatch({ effects: xmlTagUpdateEffect.of({ id: 'a', value: { blocks: ['A'] } }) });
|
|
502
|
+
|
|
503
|
+
// The widget mounts now — scrolled into CodeMirror's viewport, or portaled after the initial render.
|
|
504
|
+
const widget = stubWidget(view, 'a');
|
|
505
|
+
widget.toDOM(view);
|
|
506
|
+
expect(mounted.find((state) => state.id === 'a')?.props.blocks).toEqual(['A']);
|
|
507
|
+
view.destroy();
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
//
|
|
512
|
+
// Widget context.
|
|
513
|
+
//
|
|
514
|
+
// The host publishes the context widgets call back through (`MarkdownStreamController.setContext`),
|
|
515
|
+
// and it necessarily arrives *after* the first decoration build — the controller does not exist until
|
|
516
|
+
// the editor mounts. A widget captures its props when its decoration is built, so unless the context
|
|
517
|
+
// effect both rebuilds decorations and defeats widget reuse, every widget is left holding
|
|
518
|
+
// `context: undefined` for the life of the document and every callback through it silently no-ops.
|
|
519
|
+
//
|
|
520
|
+
|
|
521
|
+
describe('xmlTags widget context', () => {
|
|
522
|
+
const contextRegistry: Record<string, XmlWidgetDef> = {
|
|
523
|
+
branch: { block: true, Component: () => null },
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
const doc = '<branch id="a" />';
|
|
527
|
+
|
|
528
|
+
test('a widget built before the context still receives it', async ({ expect }) => {
|
|
529
|
+
const view = createView(doc, { registry: contextRegistry });
|
|
530
|
+
await rebuild(view);
|
|
531
|
+
expect(widgetProps(view, 'a').context).toBeUndefined();
|
|
532
|
+
|
|
533
|
+
const context = { rewind: () => {} };
|
|
534
|
+
view.dispatch({ effects: xmlTagContextEffect.of(context) });
|
|
535
|
+
await flush();
|
|
536
|
+
|
|
537
|
+
expect(widgetProps(view, 'a').context).toBe(context);
|
|
538
|
+
view.destroy();
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test('a later context replaces an earlier one', async ({ expect }) => {
|
|
542
|
+
const view = createView(doc, { registry: contextRegistry });
|
|
543
|
+
await rebuild(view);
|
|
544
|
+
|
|
545
|
+
const first = { rewind: () => {} };
|
|
546
|
+
view.dispatch({ effects: xmlTagContextEffect.of(first) });
|
|
547
|
+
await flush();
|
|
548
|
+
expect(widgetProps(view, 'a').context).toBe(first);
|
|
549
|
+
|
|
550
|
+
const second = { rewind: () => {} };
|
|
551
|
+
view.dispatch({ effects: xmlTagContextEffect.of(second) });
|
|
552
|
+
await flush();
|
|
553
|
+
expect(widgetProps(view, 'a').context).toBe(second);
|
|
554
|
+
view.destroy();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test('the mounted widget state carries the context', async ({ expect }) => {
|
|
558
|
+
let mounted: XmlWidgetState[] = [];
|
|
559
|
+
const view = createView(doc, { registry: contextRegistry, setWidgets: (widgets) => (mounted = widgets) });
|
|
560
|
+
await rebuild(view);
|
|
561
|
+
|
|
562
|
+
const context = { rewind: () => {} };
|
|
563
|
+
view.dispatch({ effects: xmlTagContextEffect.of(context) });
|
|
564
|
+
await flush();
|
|
565
|
+
|
|
566
|
+
// Mount the way the portal host does, then read what the notifier published.
|
|
567
|
+
stubWidget(view, 'a').toDOM(view);
|
|
568
|
+
expect(mounted.find((state) => state.id === 'a')?.props.context).toBe(context);
|
|
569
|
+
view.destroy();
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('re-publishing the same context does not churn the widget', async ({ expect }) => {
|
|
573
|
+
const view = createView(doc, { registry: contextRegistry });
|
|
574
|
+
await rebuild(view);
|
|
575
|
+
|
|
576
|
+
const context = { rewind: () => {} };
|
|
577
|
+
view.dispatch({ effects: xmlTagContextEffect.of(context) });
|
|
578
|
+
await flush();
|
|
579
|
+
const widget = stubWidget(view, 'a');
|
|
580
|
+
|
|
581
|
+
view.dispatch({ effects: xmlTagContextEffect.of(context) });
|
|
582
|
+
await flush();
|
|
583
|
+
|
|
584
|
+
// Same context: the widget is interchangeable, so CodeMirror should keep the mounted instance.
|
|
585
|
+
expect(stubWidget(view, 'a').eq(widget)).toBe(true);
|
|
586
|
+
view.destroy();
|
|
587
|
+
});
|
|
588
|
+
});
|