Skip to content

feat: Rich text editor with formatting toolbar, image ref chips, and TipTap integration#310

Draft
tellaho wants to merge 11 commits intomainfrom
tho/richtext-editor
Draft

feat: Rich text editor with formatting toolbar, image ref chips, and TipTap integration#310
tellaho wants to merge 11 commits intomainfrom
tho/richtext-editor

Conversation

@tellaho
Copy link
Copy Markdown
Collaborator

@tellaho tellaho commented Apr 13, 2026

Summary

Category: new-feature

Replace the plain textarea message composer with a full TipTap-based rich text editor. This adds inline formatting controls, image upload as context chips with autocomplete, and keyboard shortcuts for common actions.

What Changed

  • Rich text editing via TipTapuseRichTextEditor hook encapsulates editor setup, extensions, and state management, replacing the plain <textarea> in MessageComposer
  • Formatting toolbarFormattingToolbar component with toggle buttons for bold, italic, strikethrough, code, and link insertion
  • Image reference chips with autocompleteimageRefExtension TipTap extension, useImageRefSuggestions hook, and ImageRefAutocomplete UI allow users to reference uploaded images inline as context chips
  • Composer attachmentsComposerAttachments component displays uploaded media previews beneath the editor
  • Toggle UI primitive — new shared Toggle component built on Radix UI
  • Removed legacy overlay — deleted ComposerMentionOverlay (replaced by TipTap-native mention/ref system)
  • Keyboard shortcuts — ⌘B bold, ⌘I italic, ⌘K link, ⌘S sidebar toggle
  • Style updatesglobals.css additions for TipTap editor styling; minor layout tweaks in ChannelPane and sidebar
  • Dependency updates — TipTap packages added to package.json; Cargo.toml version bump
File changes (17 files: +1973 −390)

New files (7)

  • desktop/src/features/messages/lib/imageRefExtension.ts — TipTap node extension for image ref chips
  • desktop/src/features/messages/lib/useImageRefSuggestions.ts — hook for image ref autocomplete suggestions
  • desktop/src/features/messages/lib/useRichTextEditor.ts — hook encapsulating TipTap editor setup
  • desktop/src/features/messages/ui/ComposerAttachments.tsx — uploaded media preview component
  • desktop/src/features/messages/ui/FormattingToolbar.tsx — bold/italic/strikethrough/code/link toolbar
  • desktop/src/features/messages/ui/ImageRefAutocomplete.tsx — autocomplete dropdown for image refs
  • desktop/src/shared/ui/toggle.tsx — Radix-based Toggle primitive

Modified files (9)

  • Cargo.toml — version bump
  • desktop/package.json — TipTap + Radix toggle dependencies
  • desktop/pnpm-lock.yaml — lockfile update
  • desktop/src/features/channels/ui/ChannelPane.tsx — layout adjustment
  • desktop/src/features/messages/lib/useMediaUpload.ts — refactored for chip-based uploads
  • desktop/src/features/messages/ui/MessageComposer.tsx — rewired to TipTap editor
  • desktop/src/features/messages/ui/MessageComposerToolbar.tsx — updated toolbar integration
  • desktop/src/shared/styles/globals.css — TipTap editor styles
  • desktop/src/shared/ui/sidebar.tsx — minor tweak

Deleted files (1)

  • desktop/src/features/messages/ui/ComposerMentionOverlay.tsx — replaced by TipTap mention system

How to Test

  1. Check out tho/richtext-editor and run pnpm install && pnpm tauri dev
  2. Open any channel conversation
  3. Click into the message composer — it should render as a rich text editor (not a plain textarea)
  4. Formatting toolbar: Click bold (B), italic (I), strikethrough (S), code (<>), link (🔗) buttons and verify text formatting applies
  5. Keyboard shortcuts: Test ⌘B (bold), ⌘I (italic), ⌘K (link insertion), ⌘S (sidebar toggle)
  6. Image ref chips: Upload an image, then type the trigger character in the editor — autocomplete should appear with uploaded images; selecting one inserts a chip
  7. Attachments: Upload media and verify preview chips appear below the editor
  8. Send message: Compose a formatted message and send — verify it posts correctly

Screenshots

🖼️ TODO: Add screenshots of the new rich text composer, formatting toolbar, and image ref chips


Generated with Goose

tellaho added 11 commits April 13, 2026 10:15
…TipTap integration

Replace plain textarea message composer with TipTap-based rich text editor.

- Add FormattingToolbar with bold, italic, strikethrough, code, and link controls
- Add Toggle UI primitive (shared component)
- Integrate image uploads as context chips with autocomplete suggestions
  (ImageRefAutocomplete, imageRefExtension, useImageRefSuggestions)
- New useRichTextEditor hook encapsulating TipTap setup
- New ComposerAttachments component for uploaded media display
- Remove legacy ComposerMentionOverlay (replaced by TipTap mention/ref system)
- Update MessageComposer and MessageComposerToolbar for new editor
- Keyboard shortcuts: ⌘B bold, ⌘I italic, ⌘K link, ⌘S sidebar toggle
- Update ChannelPane, sidebar, globals.css for layout/style adjustments
- Update dependencies (Cargo.toml, package.json, pnpm-lock.yaml)
Move expanded formatting options (B, I, S, Code, Link, Lists, Quote)
to sit inline in the bottom toolbar right after the Aa toggle, instead
of in a separate row above the editor. File attachment previews remain
as the only element above the textarea.
…[Aa ✕ | formatting]

Passive state shows ingress buttons (mention, attach, emoji) followed by the
Aa formatting toggle. Clicking Aa swaps to expanded mode: Aa toggle, ✕ close
button, separator, then all formatting options. Ingress buttons are hidden
while formatting is open.

Matches the inline-expand design spec from the channel.
…/react

- Aa toggle uses layoutId for smooth slide between positions
- Ingress buttons (@ 📎 😊) fade out + scale down on expand
- Formatting buttons slide in from left + fade in on expand
- ✕ close button scales in alongside Aa
- Reverse animation on collapse
- Uses LayoutGroup + AnimatePresence (popLayout) matching
  existing ComposerAttachments pattern
…bels

- Reset isEmojiPickerOpen when formatting is toggled on (prevents
  emoji picker remounting open on collapse)
- Extract Aa toggle to local variable — single source of truth for
  the layoutId animation across both toolbar states
- Add aria-label to mention button, attach button, and upload spinner
- Pull Aa toggle out of AnimatePresence so it never unmounts —
  layoutId handles the smooth position slide on its own
- Remove React.Fragment wrappers — each branch is a single motion.div
  so AnimatePresence properly tracks enter/exit per element
- popLayout mode pops exiting elements out of flow immediately,
  letting enter animations start at the same time
- Ingress group uses order-[-1] to sit before Aa visually
- No staggering, no sequencing — one fluid concurrent transition
* origin/main:
  Replace inline channel creation with dialog (#312)
  chore: improve chat message layout to left-aligned design (#309)
  Add edit dialog for managed agents with relay profile sync (#277)
  fix(ci): build relay with optimized profile to fix flaky e2e tests (#307)
  Update actions/checkout action to v6 (#305)
  Update dependency @tanstack/react-query to v5.98.0 (#304)
  Update dependency @playwright/test to v1.59.1 (#303)
  Update react monorepo to v19.2.5 (#302)
  feat(mobile): scaffold Flutter app with Riverpod & Catppuccin theme (#306)
  Update dependency @tanstack/react-router to v1.168.13 (#301)
  feat: Markdown-based persona packs (crate + ACP + desktop) (#297)
  feat(desktop): improve Agents page UX (#298)
  feat(desktop): add Pulse social notes surface (#296)
  Fix flaky desktop smoke tests (#294)
  Add agent lifecycle controls to channel members sidebar (#291)

# Conflicts:
#	desktop/pnpm-lock.yaml
* origin/main:
  feat(desktop): deterministic nested thread panels (#308)
  fix: show private channels in channel browser when user is a member (#311)

# Conflicts:
#	desktop/src/features/channels/ui/ChannelPane.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant