* MM-67755: WYSIWYG editor for message composition
- Add TipTap-based WYSIWYG editor as an opt-in setting
- Implement formatting bar with text styles dropdown, link popover
- Add custom list extension for Mattermost markdown compatibility
- Add suggestion list integration for mentions and emoji
- Add feature flag and user preference toggle
- Update e2e tests for WYSIWYG editor compatibility
Made-with: Cursor
* Fix package versions to match current master (11.7.0)
Made-with: Cursor
* Add missing PermissionPolicies feature flag from master
Made-with: Cursor
* Fix remaining WYSIWYG UX issues: toolbar overlap, autocomplete, emoji, tables
Made-with: Cursor
* Address CodeRabbit review: null guards, XSS fix, e2e tests, constants alignment
Made-with: Cursor
* UX fixes required by Matt
* linter
* fix e2e
* Fix CI failures: resolve merge conflicts with master
- Fix TS errors from useRewrite hook refactor (additionalControl → rewriteMenuProps)
- Fix onComposition → onCompositionUpdate prop rename on Textbox
- Remove accidentally re-added enzyme-to-json/serializer from jest config
- Extract new i18n strings for WYSIWYG editor components
Made-with: Cursor
* Fix lint: remove unused aiRewriteEnabled and rewriteMenuProps vars
Made-with: Cursor
* Fix jest and E2E test failures
- Add lowlight and ESM deps to jest transformIgnorePatterns
- Replace have.value with have.text for contenteditable elements
- Remove selectionStart/selectionEnd assertions for WYSIWYG editor
Made-with: Cursor
* e2e test
* Expose focus()/blur() on WysiwygEditor handle so focus hooks work
useTextboxFocus drives focus into the composer for many flows (channel
switch, RHS open, post-submit refocus, Reply shortcut, "type to focus
main input"). It calls textboxRef.current?.focus(), but textboxRef is
the legacy <Textbox> ref and is null when the WYSIWYG editor is the
mounted composer, so every focus call becomes a silent no-op.
Add focus() and blur() to WysiwygEditorHandle, backed by editor.commands
.focus()/blur(), and pass wysiwygRef into useTextboxFocus so it picks
whichever ref is mounted. The hook keeps its existing signature for
legacy callers; wysiwygRef is optional.
Verified locally on the WYSIWYG branch:
- messaging/focus_move_spec (3/6 failing -> 6/6 passing)
- messaging/message_edit_post_clear_text_spec (0/1 -> 1/1)
- messaging/message_edit_post_history_spec (0/3 -> 1/3)
- keyboard_shortcuts/dot_menu_spec gets past the Reply-focus assertion
Co-authored-by: Cursor <cursoragent@cursor.com>
* e2e: coerce postMessage args to string in WYSIWYG branch
The legacy textarea path used cy.invoke('val', message) which silently
coerced numbers/undefined to strings. The WYSIWYG branch added in the
previous refactor calls message.replace() and crashes with
"TypeError: message.replace is not a function" whenever a spec passes a
non-string value, e.g. cy.uiPostMessageQuickly(0) or before-each hooks
that pass undefined.
Funnel everything through asPostMessage() so the WYSIWYG branch is just
as forgiving as the textarea branch. Loosen postMessage /
postMessageReplyInRHS / uiPostMessageQuickly typings to `unknown` to
match what callers actually pass.
Fixes failures in: copy_post_text_spec, save_post_spec,
search_filter/{after,before,future_date}_spec, and
scroll/default_images_collapsed_spec.
Co-authored-by: Cursor <cursoragent@cursor.com>
* filtetr by feature flag for now
* coderabbit ai feedback
* coderabbitai comments
* align with code conventions
* fix e2e test
* PR comments
* fix i18n
* anchor link popover to editor selectio
* UX issues
* regression
* Final UX feedback
* linter
* e2e test rename
* Final UX
* Fix playwright lint
* Align e2e mocks with master
* Disable feature flag
* Sanitize urls before handling actions with link popove
---------
Co-authored-by: Nevyana Angelova <nevyangelova@192.168.100.47>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>