Skip to content

Dev#12

Merged
BuckyMcYolo merged 5 commits intomainfrom
dev
Mar 5, 2026
Merged

Dev#12
BuckyMcYolo merged 5 commits intomainfrom
dev

Conversation

@BuckyMcYolo
Copy link
Copy Markdown
Owner

@BuckyMcYolo BuckyMcYolo commented Mar 5, 2026

Pull Request Summary: Dev (PR #12)

Overview

This PR enhances chat UX across the web app by adding syntax-highlighted code blocks with copy support, editor code-block language selection and richer composer formatting (bubble menus), a scaffolded slash-command system, emoji reaction UI, a message action bar, date-aware message grouping with a date divider component, and new date utilities.

Changes

Dependencies

  • apps/web/package.json
    • Added:
      • highlight.js ^11.11.1
      • rehype-highlight ^7.0.2

Editor / Composer

  • apps/web/src/components/chat/composer/message-input.tsx
    • Integrated richer TipTap editor features (EditorContent, BubbleMenu, ReactRenderer).
    • Added code-block language support:
      • DEFAULT_CODE_BLOCK_LANGUAGE ("plaintext"), CODE_BLOCK_LANGUAGE_OPTIONS (typescript, javascript, python, json, bash).
      • Helpers to read/validate/set code-block language attributes and a BubbleMenu UI to change language.
    • Added inline formatting BubbleMenu (bold, italic, inline code, strikethrough) and editor state tracking via useEditorState.
    • Implemented SuggestionPopupManager for mentions and provided slash-command scaffolding (createSlashCommandSuggestion, createSlashCommandExtension, SLASH_COMMANDS) — scaffolded but not enabled by default.
    • Positioning helpers for active code blocks (getActiveCodeBlockRect/Pos).

Slash Command List

  • apps/web/src/components/chat/composer/slash-command-list.tsx
    • New forwardRef component SlashCommandList with keyboard navigation (ArrowUp/Down, Enter), wrap-around selection, mouse interaction.
    • New exported types/interfaces: SlashCommandItem, SlashCommandListRef, SlashCommandListProps.
    • Renders label/description, optional language/search on items, shows "No commands" placeholder; Code2 icon for code-block commands.

Markdown Rendering / Code Blocks

  • apps/web/src/components/chat/message-markdown.tsx
    • Added CodeBlock rendering with:
      • Language detection from class names.
      • Copy-to-clipboard UI with "Copy" / "Copied" feedback (1.2s reset).
      • Integration of rehype-highlight / highlight.js for syntax highlighting.
    • Added helpers getTextContent and parseCodeBlock for robust extraction.
    • Adjusted mention rendering to support @everyone and improved link/code rendering in markdown.
    • Normalized language labels for UI (e.g., "plaintext" => "plain text").

Message List & Date Dividers

  • apps/web/src/components/chat/message-list.tsx
    • Added date-aware grouping logic (isDateBoundary, isWithinGroupWindow) using new MESSAGE_GROUP_WINDOW_MINUTES and existing timestamps; renders DateDivider before messages crossing date or grouping boundaries.
    • Enabled text selection on the message list container (select-text Tailwind class).
  • apps/web/src/components/chat/date-divider.tsx
    • New DateDivider component using formatDateDivider for centered rounded date labels.
  • packages/utils/src/date.ts
    • New utilities exported:
      • differenceInMinutes(date1, date2): number
      • isSameDay(date1, date2): boolean
      • formatDateDivider(date): string
    • Implemented using existing toDateTime helper and Luxon-based operations in local time.

Message Item / Actions / Reactions

  • apps/web/src/components/chat/message-item.tsx
    • Added copy-to-clipboard handler for message text.
    • Integrated hover-revealed MessageActionBar and surfaced a right-aligned timestamp for non-header messages.
    • Adjusted container positioning to support the absolute action bar.
  • apps/web/src/components/chat/message-action-bar.tsx
    • New MessageActionBar component (exported) composing EmojiReactionPicker, Reply button, and a More Actions dropdown.
    • Props: onReact?: (emoji: string) => void, onReply?: () => void, onCopyText?: () => void.
  • apps/web/src/components/chat/emoji-reaction-picker.tsx
    • New EmojiReactionPicker component with quick and extended emoji modes and an optional onSelect callback.

App-level

  • apps/web/src/main.tsx
    • Wrapped RouterProvider with TooltipProvider to provide tooltip context globally.

Mention Candidates

  • apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
    • mentionCandidates now always includes a hardcoded "everyone" candidate at the start of the list so @everyone is available even when members are undefined.

Observations / Notes

  • Slash commands are scaffolded but intentionally not enabled in the editor configuration.
  • New UI components lack accompanying unit/integration tests in this PR.
  • Syntax highlighting adds highlight.js and rehype-highlight; verify bundle impact and styling import.
  • Date utilities use local time semantics and are exported for reuse.

Suggested Reviews / Focus Areas

  • Verify editor transactions correctly apply code-block language attributes (edge cases when code blocks are nested or absent).
  • Accessibility and keyboard interaction checks for BubbleMenu, SlashCommandList, EmojiReactionPicker, and MessageActionBar.
  • Confirm copy-to-clipboard behavior across browsers and the 1.2s copied state resets reliably.
  • Evaluate bundle size impact of highlight.js + rehype-highlight and ensure CSS import/SSR behavior is acceptable.
  • Add tests for SlashCommandList, CodeBlock copy behavior, and date grouping logic before merging if coverage is required.

Confidence Score: 3/5

Rationale: The changes are substantial and generally follow existing patterns. The implementation appears well-scoped and integrates multiple UI improvements. However, the PR introduces many new components and behavior without tests, includes scaffolded but disabled features (slash commands) that may need documentation, and adds syntax-highlighting dependencies that should be assessed for bundle impact. These points suggest the PR is mostly solid but would benefit from follow-up fixes/tests.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

Warning

Rate limit exceeded

@BuckyMcYolo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 04462dc8-44da-4489-b902-84aad6c99654

📥 Commits

Reviewing files that changed from the base of the PR and between eb7b541 and 3efda46.

📒 Files selected for processing (1)
  • apps/web/src/components/chat/message-markdown.tsx
📝 Walkthrough

Walkthrough

Adds code-block language selection and syntax highlighting to chat composer and renderer; introduces copy-to-clipboard for code blocks, slash-command & mention popup scaffolding, date-aware message grouping, reaction picker and message action bar components, tooltip provider wiring, and new date utilities. Two highlight deps added.

Changes

Cohort / File(s) Summary
Dependencies
apps/web/package.json
Added highlight.js and rehype-highlight for Markdown syntax highlighting.
Composer / Editor
apps/web/src/components/chat/composer/message-input.tsx, apps/web/src/components/chat/composer/slash-command-list.tsx
Added SuggestionPopupManager, code-block language constants and BubbleMenu UI, slash-command scaffolding, inline formatting bubble menu, and new SlashCommandList component with keyboard navigation.
Markdown Rendering / Code Blocks
apps/web/src/components/chat/message-markdown.tsx
Integrated rehype-highlight, implemented CodeBlock component with language detection and copy-to-clipboard UI, mention:@everyone handling, and code markup styling.
Message List / Grouping
apps/web/src/components/chat/message-list.tsx, apps/web/src/components/chat/date-divider.tsx
Added date-aware grouping (MESSAGE_GROUP_WINDOW_MINUTES), DateDivider component, and adjusted header logic to insert date dividers and grouping boundaries.
Message Item / Actions
apps/web/src/components/chat/message-item.tsx, apps/web/src/components/chat/message-action-bar.tsx, apps/web/src/components/chat/emoji-reaction-picker.tsx
Added MessageActionBar and EmojiReactionPicker components; message items now surface an action bar on hover with copy, reply, and reaction flows; added local copy-to-clipboard handling and visible timestamps.
App Context
apps/web/src/main.tsx
Wrapped RouterProvider with TooltipProvider to enable tooltip context across the app.
Routes / Mentions
apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
Always prepends an "everyone" mention candidate into mentionCandidates array.
Utilities
packages/utils/src/date.ts
Added differenceInMinutes, isSameDay, and formatDateDivider for local-time date comparison and divider labeling.

Sequence Diagram

sequenceDiagram
    participant User
    participant Editor as Message Input (TipTap)
    participant Bubble as BubbleMenu
    participant Composer as Composer Logic
    participant Renderer as Markdown Renderer
    participant Highlighter as rehype-highlight / highlight.js
    participant Clipboard as Clipboard

    User->>Editor: Type code block / select text
    Editor->>Bubble: Detect code block -> show language selector
    User->>Bubble: Choose language
    Bubble->>Editor: Update codeBlock language attribute
    User->>Composer: Send message
    Composer->>Renderer: Render markdown with code block
    Renderer->>Highlighter: Apply highlighting (language aware)
    Renderer->>User: Display highlighted code + copy button
    User->>Clipboard: Click copy
    Clipboard->>User: Provide copied feedback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through code with whiskers bright,

I painted snippets, gave languages light.
I added a menu, a copy, and cheer —
Now chats glow and rabbits cheer here! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev' is vague and does not convey meaningful information about the changeset, which includes significant features like code block language support, mentions, syntax highlighting, emoji reactions, message timestamps, and date-aware grouping. Provide a more descriptive title that captures the main feature or set of features being added, such as 'Add rich code formatting and message UI enhancements' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/chat/composer/message-input.tsx`:
- Around line 221-347: The suggestion popup lifecycle (mounting the popup
element, positioning via positionPopup, adding resize/scroll listeners,
creating/destroying ReactRenderer, and cleanup) is duplicated between
createSlashCommandSuggestion and createMentionSuggestion; extract this into a
shared helper (e.g., createSuggestionPopupManager) that accepts params like
editor, initialProps, rendererComponent (SlashCommandList / MentionList), and a
callback to update props/handle key events, then replace the duplicated logic in
createSlashCommandSuggestion and createMentionSuggestion to call that helper and
use its returned API (onStart/onUpdate/onKeyDown/onExit), preserving existing
identifiers such as positionPopup, cleanup, ReactRenderer, currentProps, popup,
and reactRenderer to keep behavior identical.
- Around line 573-575: isSuggestionMenuOpen currently only checks for
"[data-mention-suggestion-open='true']" so Enter can still send when slash
suggestions are open; update isSuggestionMenuOpen (and the same guard used
around Enter-to-send checks) to query for a broader selector that includes slash
and any generic suggestion flags (e.g., add
"[data-slash-suggestion-open='true']" and/or "[data-suggestion-open='true']" to
the querySelector list) so it returns true whenever any suggestion menu is open.

In `@apps/web/src/components/chat/composer/slash-command-list.tsx`:
- Around line 53-58: The effect currently only watches items.length so selection
can persist incorrectly when the items array identity or content changes but
keeps the same length; update the useEffect that calls setSelectedIndex in
slash-command-list.tsx to depend on the full items array (or a derived stable
key like items.map(i => i.id)) instead of items.length, and reset the selection
to a safe default (e.g., setSelectedIndex(0) when items updates) so the selected
command is cleared on full list updates; reference the useEffect and
setSelectedIndex calls and replace the dependency array accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0a714673-1999-40e6-b88c-9b063f8092fb

📥 Commits

Reviewing files that changed from the base of the PR and between 3b77984 and 23785d4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/web/package.json
  • apps/web/src/components/chat/composer/message-input.tsx
  • apps/web/src/components/chat/composer/slash-command-list.tsx
  • apps/web/src/components/chat/message-list.tsx
  • apps/web/src/components/chat/message-markdown.tsx

Comment thread apps/web/src/components/chat/composer/message-input.tsx
Comment thread apps/web/src/components/chat/composer/message-input.tsx
Comment thread apps/web/src/components/chat/composer/slash-command-list.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/chat/composer/message-input.tsx`:
- Around line 465-473: Replace the commented-out slash-command wiring with a
toggled, active code path: add a boolean feature flag (e.g., useSlashCommands)
near the top of the MessageInput component and conditionally initialize
slashCommandSuggestion via useMemo(() => createSlashCommandSuggestion(), []) and
slashCommandExtension via useMemo(() =>
createSlashCommandExtension(slashCommandSuggestion), [slashCommandSuggestion])
only when useSlashCommands is true; remove the commented blocks and ensure all
references to slashCommandSuggestion and slashCommandExtension are guarded by
the same flag (also update the second commented occurrence mentioned in the
review).
- Around line 700-710: The BubbleMenu shouldShow callback currently destructures
a non-existent element prop and calls element.contains(activeElement), causing a
runtime TypeError; update the callback signature to use the official props
(e.g., shouldShow={({ editor: tiptapEditor, view }) => { ... }}) and replace
element.contains(activeElement) with view.dom.contains(activeElement) (keeping
the existing document activeElement guard and the isEditable / isActive checks)
so containment is checked against the editor DOM provided by the view.

In `@apps/web/src/components/chat/message-item.tsx`:
- Around line 20-24: The handler handleCopyText currently calls
navigator.clipboard.writeText(message.content) without ensuring
navigator.clipboard exists, which can throw synchronously; update handleCopyText
to first check that message.content is present and that typeof navigator !==
'undefined' && navigator.clipboard is truthy before calling writeText, and only
then call navigator.clipboard.writeText(message.content). Keep the existing
Promise.catch to handle async rejections, and ensure the dependency array still
references message.content.

In `@apps/web/src/components/chat/message-markdown.tsx`:
- Around line 167-176: The user-mention replacement is running before
EVERYONE_MENTION_REGEX which lets " `@everyone`" inside a user label get rewritten
into nested/invalid markdown; swap the order so EVERYONE_MENTION_REGEX is
applied first on normalizedContent, then run the USER_MENTION_TOKEN_REGEX
replacement (the code that uses mentionById, getMentionLabel and
escapeMarkdownText) — i.e., change the return chain so EVERYONE_MENTION_REGEX
replacement happens before the USER_MENTION_TOKEN_REGEX replacement to prevent
altering text inside the generated [@...](mention:...) links.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3e53c451-ab7f-4923-b288-88edb5668983

📥 Commits

Reviewing files that changed from the base of the PR and between 23785d4 and 1a0e3ac.

📒 Files selected for processing (11)
  • apps/web/src/components/chat/composer/message-input.tsx
  • apps/web/src/components/chat/composer/slash-command-list.tsx
  • apps/web/src/components/chat/date-divider.tsx
  • apps/web/src/components/chat/emoji-reaction-picker.tsx
  • apps/web/src/components/chat/message-action-bar.tsx
  • apps/web/src/components/chat/message-item.tsx
  • apps/web/src/components/chat/message-list.tsx
  • apps/web/src/components/chat/message-markdown.tsx
  • apps/web/src/main.tsx
  • apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
  • packages/utils/src/date.ts

Comment thread apps/web/src/components/chat/composer/message-input.tsx
Comment thread apps/web/src/components/chat/composer/message-input.tsx
Comment thread apps/web/src/components/chat/message-item.tsx
Comment thread apps/web/src/components/chat/message-markdown.tsx Outdated
Process @everyone mentions before user mentions to prevent
partial matches when user mention regex runs first.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
apps/web/src/components/chat/composer/message-input.tsx (2)

699-709: ⚠️ Potential issue | 🔴 Critical

Fix BubbleMenu shouldShow props usage (element is invalid here).

Line 699 destructures element, and Line 708 calls element.contains(...). In Tiptap BubbleMenu shouldShow, the documented props use view (not element), so this can throw at runtime when that branch executes.

🛠️ Proposed fix
-              shouldShow={({ editor: tiptapEditor, element }) => {
+              shouldShow={({ editor: tiptapEditor, view }) => {
                 const activeElement =
                   typeof document !== "undefined"
                     ? document.activeElement
                     : null

                 return (
                   tiptapEditor.isEditable &&
                   (tiptapEditor.isActive("codeBlock") ||
-                    (activeElement ? element.contains(activeElement) : false))
+                    (activeElement ? view.dom.contains(activeElement) : false))
                 )
               }}
For `@tiptap/react`@3.20.0 BubbleMenu (from "@tiptap/react/menus"), what is the `shouldShow` callback parameter shape, and does it include `element`?

Reference: https://tiptap.dev/docs/editor/extensions/functionality/bubble-menu

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/chat/composer/message-input.tsx` around lines 699 -
709, The BubbleMenu shouldShow callback is using a non-existent element
parameter; update the predicate to accept ({ editor: tiptapEditor, view })
instead of ({ editor: tiptapEditor, element }) and replace
element.contains(activeElement) with view.dom.contains(activeElement) (keep the
existing document.activeElement null-safe check and the tiptapEditor.isEditable
/ isActive("codeBlock") logic). Ensure you reference the shouldShow prop and
tiptapEditor/view variables so the runtime uses the correct DOM node for
containment checks.

464-472: 🧹 Nitpick | 🔵 Trivial

Avoid shipping commented-out slash-command wiring.

Lines 464-472 and Line 491 keep disabled code paths as comments, which tends to drift. Prefer a small feature flag with live code paths (or remove until enabled).

♻️ Feature-flag pattern (hook-safe, no commented code)
-  // Slash commands temporarily disabled.
-  // const slashCommandSuggestion = useMemo(
-  //   () => createSlashCommandSuggestion(),
-  //   []
-  // )
-  // const slashCommandExtension = useMemo(
-  //   () => createSlashCommandExtension(slashCommandSuggestion),
-  //   [slashCommandSuggestion]
-  // )
+  const useSlashCommands = false
+  const slashCommandSuggestion = useMemo(
+    () => (useSlashCommands ? createSlashCommandSuggestion() : null),
+    [useSlashCommands]
+  )
+  const slashCommandExtension = useMemo(
+    () =>
+      slashCommandSuggestion
+        ? createSlashCommandExtension(slashCommandSuggestion)
+        : null,
+    [slashCommandSuggestion]
+  )
...
-        // slashCommandExtension,
+        ...(slashCommandExtension ? [slashCommandExtension] : []),

Also applies to: 491-492

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/chat/composer/message-input.tsx` around lines 464 -
472, Remove the commented-out slash-command wiring and either implement a small
runtime feature flag or delete the code; specifically, replace the commented
createSlashCommandSuggestion/createSlashCommandExtension lines with a
feature-flagged implementation (e.g., const enableSlashCommands =
useFeatureFlag('slashCommands') or a prop) and conditionally create the values:
const slashCommandSuggestion = useMemo(() => enableSlashCommands ?
createSlashCommandSuggestion() : null, [enableSlashCommands]) and const
slashCommandExtension = useMemo(() => enableSlashCommands &&
slashCommandSuggestion ? createSlashCommandExtension(slashCommandSuggestion) :
null, [enableSlashCommands, slashCommandSuggestion]); ensure hook rules are
preserved (hooks not called conditionally) and remove the commented blocks at
lines referencing slashCommandSuggestion/slashCommandExtension.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/web/src/components/chat/composer/message-input.tsx`:
- Around line 699-709: The BubbleMenu shouldShow callback is using a
non-existent element parameter; update the predicate to accept ({ editor:
tiptapEditor, view }) instead of ({ editor: tiptapEditor, element }) and replace
element.contains(activeElement) with view.dom.contains(activeElement) (keep the
existing document.activeElement null-safe check and the tiptapEditor.isEditable
/ isActive("codeBlock") logic). Ensure you reference the shouldShow prop and
tiptapEditor/view variables so the runtime uses the correct DOM node for
containment checks.
- Around line 464-472: Remove the commented-out slash-command wiring and either
implement a small runtime feature flag or delete the code; specifically, replace
the commented createSlashCommandSuggestion/createSlashCommandExtension lines
with a feature-flagged implementation (e.g., const enableSlashCommands =
useFeatureFlag('slashCommands') or a prop) and conditionally create the values:
const slashCommandSuggestion = useMemo(() => enableSlashCommands ?
createSlashCommandSuggestion() : null, [enableSlashCommands]) and const
slashCommandExtension = useMemo(() => enableSlashCommands &&
slashCommandSuggestion ? createSlashCommandExtension(slashCommandSuggestion) :
null, [enableSlashCommands, slashCommandSuggestion]); ensure hook rules are
preserved (hooks not called conditionally) and remove the commented blocks at
lines referencing slashCommandSuggestion/slashCommandExtension.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c493c911-eaa2-4e4c-91d3-655c0f04acf2

📥 Commits

Reviewing files that changed from the base of the PR and between 1a0e3ac and eb7b541.

📒 Files selected for processing (1)
  • apps/web/src/components/chat/composer/message-input.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