Final step of the editor migration (#32 seam, #33 engine). Do not start until the end condition holds.
End condition
Deletions
packages/chat/src/composer/textarea-legacy.tsx (+ TextareaLegacy from the compound and ComposerTextareaLegacyProps export)
packages/chat/src/composer/document.ts (TipTap conversions + adapter; keep nothing — serializeSegments replaced serializeDocument)
packages/chat/src/composer/mention-chip.tsx
packages/chat/src/composer/prefix-plugin.ts (pure kernel already lives in prefix-detection.ts)
packages/chat/tests/composer-document.test.ts (its serialize fixtures are already mirrored in composer-segments.test.ts)
- Deps from
packages/chat/package.json: @tiptap/core, @tiptap/extension-document, @tiptap/extension-paragraph, @tiptap/extension-text, @tiptap/pm, @tiptap/react, prosemirror-model, prosemirror-transform, prosemirror-view
- Dead index exports:
commandListPluginKey, createMentionChipExtension, MentionChipOptions, applySnapshotToEditor/snapshotFromEditor/serializeEditorContent, createTiptapRegisteredEditor
Breaking cleanups (batched here while pre-1.0)
- Rename
CommandListPluginState → engine-neutral (e.g. ActiveTokenState) — no plugin exists anymore; update prefix-detection.ts, trigger-tracker.ts, index exports, tests
- Rename
ComposerSnapshot.__pmDoc → neutral field name (payload is already engine-neutral paragraph JSON; snapshots are cross-engine compatible so no migration needed)
ComposerState.textarea slice goes data-only ({ hasContent }) — imperative methods live on store.controller / useComposerController only
- Rename the
TriggerRule value "after-whitespace" → "word-boundary" (it also fires at line start — the current name is factually wrong). Touches types.ts, prefix-detection.ts docs, demo command configs, docs tables. Decided 2026-07-10: keep kind: "insert" | "execute" and the trigger field name as-is (minimal rename).
- Docs: drop the TextareaLegacy mention from COMPOSER.md; final sweep for TipTap references
Verification
bun test, tsc --noEmit, biome — green
rg -i "tiptap|prosemirror" packages/chat → zero hits
- Package builds; bundle no longer contains PM (size drop is the payoff)
- Demo smoke test after dep removal
Final step of the editor migration (#32 seam, #33 engine). Do not start until the end condition holds.
End condition
TextareaLegacyDeletions
packages/chat/src/composer/textarea-legacy.tsx(+TextareaLegacyfrom the compound andComposerTextareaLegacyPropsexport)packages/chat/src/composer/document.ts(TipTap conversions + adapter; keep nothing —serializeSegmentsreplacedserializeDocument)packages/chat/src/composer/mention-chip.tsxpackages/chat/src/composer/prefix-plugin.ts(pure kernel already lives inprefix-detection.ts)packages/chat/tests/composer-document.test.ts(its serialize fixtures are already mirrored incomposer-segments.test.ts)packages/chat/package.json:@tiptap/core,@tiptap/extension-document,@tiptap/extension-paragraph,@tiptap/extension-text,@tiptap/pm,@tiptap/react,prosemirror-model,prosemirror-transform,prosemirror-viewcommandListPluginKey,createMentionChipExtension,MentionChipOptions,applySnapshotToEditor/snapshotFromEditor/serializeEditorContent,createTiptapRegisteredEditorBreaking cleanups (batched here while pre-1.0)
CommandListPluginState→ engine-neutral (e.g.ActiveTokenState) — no plugin exists anymore; updateprefix-detection.ts,trigger-tracker.ts, index exports, testsComposerSnapshot.__pmDoc→ neutral field name (payload is already engine-neutral paragraph JSON; snapshots are cross-engine compatible so no migration needed)ComposerState.textareaslice goes data-only ({ hasContent }) — imperative methods live onstore.controller/useComposerControlleronlyTriggerRulevalue"after-whitespace"→"word-boundary"(it also fires at line start — the current name is factually wrong). Touchestypes.ts,prefix-detection.tsdocs, demo command configs, docs tables. Decided 2026-07-10: keepkind: "insert" | "execute"and thetriggerfield name as-is (minimal rename).Verification
bun test,tsc --noEmit, biome — greenrg -i "tiptap|prosemirror" packages/chat→ zero hits