Skip to content

[Feature] Add Message component#446

Merged
djalmaaraujo merged 5 commits into
mainfrom
feat/message-component
Jun 30, 2026
Merged

[Feature] Add Message component#446
djalmaaraujo merged 5 commits into
mainfrom
feat/message-component

Conversation

@djalmaaraujo

@djalmaaraujo djalmaaraujo commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description

Ports the shadcn Message component to RubyUI — the higher-level chat layout that pairs an avatar with bubbles, headers, and footers.

Stacked on #445 (Bubble). Base is feat/bubble-component; this PR shows only the Message diff. Retarget to main once #445 merges.

Reference:

Closes #444.

What's included

  • Message — row container, align: (start/end); flips avatar/content via flex-row-reverse
  • MessageGroup — stacks messages
  • MessageAvatar — wraps the existing Avatar; anchors to the row bottom and lifts when a footer is present
  • MessageContent — column holding bubbles; drives bubble alignment via group-data-[align=end]/message
  • MessageHeader / MessageFooter — muted meta lines (name, timestamp, "Delivered")

Reuse — no new primitives

Bubble alignment inside a message is handled entirely by Message — Bubble is unchanged. dependencies.yml: message depends on Avatar + Bubble.

Notes

  • shadcn ships these styles as a custom cn-message-* CSS layer; this port translates every rule to Tailwind v4 utilities. No custom CSS, no JS.
  • Marker (the "is typing…" indicator) is a separate shadcn component, out of scope here; the demo uses plain footer text instead.

Testing

  • cd gem && bundle exec rake → tests + StandardRB green.
  • Docs page rendered locally at /docs/message: conversation, header/footer, alignment, and group all verified visually against the shadcn demo.

Screenshots

Add before/after screenshots of /docs/message.


Summary by cubic

Add a chat Message layout that pairs an avatar with bubbles, headers, and footers to build aligned, grouped conversations. Ships with Tailwind v4 utilities and a docs page; no custom CSS or JS.

  • New Features

    • Message with align: :start|:end; controls bubble alignment and flips rows.
    • Subcomponents: MessageGroup, MessageAvatar, MessageContent, MessageHeader, MessageFooter.
    • Docs at /docs/message with examples; routes, controller, nav, sitemap, and LLM docs maps updated.
    • Tests for slots, alignment, and grouping.
    • MCP registry updated with Message files, metadata, and examples.
  • Dependencies

    • Updated dependencies.yml: message depends on Avatar and Bubble; Bubble is unchanged.

Written for commit c9dd5e7. Summary will update on new commits.

Review in cubic

Port the shadcn Bubble component: a chat bubble surface with 7 variants
(default, secondary, muted, tinted, outline, ghost, destructive),
start/end alignment, grouping, and edge-anchored reactions.

- Translates shadcn's cn-bubble-* CSS layer to real Tailwind v4 utilities
  and RubyUI theme tokens (no custom CSS shipped).
- BubbleContent is polymorphic via as: (:div/:a/:button) for link/button
  bubbles — the idiomatic Phlex equivalent of shadcn's asChild.
- No JS. Composes with Tooltip/Popover (docs examples included).

Docs page, route, controller, menu entry and site_files updated.
@djalmaaraujo djalmaaraujo requested a review from cirdes as a code owner June 30, 2026 15:23

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 17 files

Re-trigger cubic

@djalmaaraujo djalmaaraujo force-pushed the feat/message-component branch from 9e0d56b to 75db4cf Compare June 30, 2026 15:30
Collapse the duplicated [&_button]/[&_a] pairs into [&:is(button,a)]
selectors targeting the content element itself (the polymorphic as:
:button/:a case), matching the variant hover selectors. Halves the
class string with identical behavior.
@djalmaaraujo djalmaaraujo force-pushed the feat/message-component branch from 75db4cf to 8173b90 Compare June 30, 2026 15:37
Port the shadcn Message component: the higher-level chat layout that pairs
an avatar with bubbles, headers, and footers. Built on top of Bubble (#445).

- 6 parts: Message (align start/end), MessageGroup, MessageAvatar,
  MessageContent, MessageHeader, MessageFooter.
- Reuses the existing Avatar and the new Bubble components — no new
  primitives. Bubble alignment inside a message is driven by Message via
  group-data-[align=end]/message; Bubble itself is unchanged.
- Translates shadcn's cn-message-* CSS layer to Tailwind v4 utilities.
- dependencies.yml: message depends on Avatar + Bubble.

Closes #444.

Docs page, route, controller, menu entry and site_files updated.
@djalmaaraujo djalmaaraujo force-pushed the feat/message-component branch from 8173b90 to c9dd5e7 Compare June 30, 2026 15:45
Base automatically changed from feat/bubble-component to main June 30, 2026 16:53
@djalmaaraujo djalmaaraujo merged commit 43a75db into main Jun 30, 2026
7 checks passed
@djalmaaraujo djalmaaraujo deleted the feat/message-component branch June 30, 2026 20:04
djalmaaraujo added a commit that referenced this pull request Jun 30, 2026
Port the shadcn Message Scroller: a chat transcript scroller that follows
the live edge, anchors new turns near the top, and jumps to the latest
message. Built on top of Message (#446) and Bubble (#445).

shadcn delegates to a closed React primitive (@shadcn/react); this is a
from-scratch Stimulus controller (ruby-ui--message-scroller) — our own
code, no external lib:

- autoScroll follow-edge: pins to the bottom while the reader is there,
  releases on wheel/touch/keyboard/scrollbar away, re-engages on jump.
- scrollAnchor: settles an appended turn near the top keeping a peek of
  the previous item (previous_item_peek).
- defaultPosition: open at end / start / last-anchor.
- preserveOnPrepend: hold the visible row when history loads in above.
- Public API for streaming/ActionCable: scrollToEnd/scrollToStart/
  scrollToMessage; new rows are picked up via MutationObserver.
- rAF-based smooth scrolling (native smooth is unreliable on a contained
  viewport), honors prefers-reduced-motion.
- a11y: content role=log + aria-relevant, button sr-only label, button
  removed from tab order while inert.

Parts: MessageScrollerProvider, MessageScroller, MessageScrollerViewport,
MessageScrollerContent, MessageScrollerItem, MessageScrollerButton.

dependencies.yml: message_scroller depends on Message + Bubble. MCP
registry, docs page, route, controller, menu and site_files updated.
djalmaaraujo added a commit that referenced this pull request Jun 30, 2026
Port the shadcn Message Scroller: a chat transcript scroller that follows
the live edge, anchors new turns near the top, and jumps to the latest
message. Built on top of Message (#446) and Bubble (#445).

shadcn delegates to a closed React primitive (@shadcn/react); this is a
from-scratch Stimulus controller (ruby-ui--message-scroller) — our own
code, no external lib:

- autoScroll follow-edge: pins to the bottom while the reader is there,
  releases on wheel/touch/keyboard/scrollbar away, re-engages on jump.
- scrollAnchor: settles an appended turn near the top keeping a peek of
  the previous item (previous_item_peek).
- defaultPosition: open at end / start / last-anchor.
- preserveOnPrepend: hold the visible row when history loads in above.
- Public API for streaming/ActionCable: scrollToEnd/scrollToStart/
  scrollToMessage; new rows are picked up via MutationObserver.
- rAF-based smooth scrolling (native smooth is unreliable on a contained
  viewport), honors prefers-reduced-motion.
- a11y: content role=log + aria-relevant, button sr-only label, button
  removed from tab order while inert.

Parts: MessageScrollerProvider, MessageScroller, MessageScrollerViewport,
MessageScrollerContent, MessageScrollerItem, MessageScrollerButton.

dependencies.yml: message_scroller depends on Message + Bubble. MCP
registry, docs page, route, controller, menu and site_files updated.
cirdes pushed a commit that referenced this pull request Jun 30, 2026
* [Feature] Add Empty component

Port the shadcn Empty component: a centered empty-state surface for when
there is no data or content. Parts: Empty, EmptyHeader, EmptyMedia
(default/icon variants), EmptyTitle, EmptyDescription, EmptyContent.

Translates shadcn's cn-empty-* CSS layer to Tailwind v4 utilities. No JS.

Docs page, route, controller, menu, site_files and MCP registry updated.

* [Feature] Add Message Scroller component

Port the shadcn Message Scroller: a chat transcript scroller that follows
the live edge, anchors new turns near the top, and jumps to the latest
message. Built on top of Message (#446) and Bubble (#445).

shadcn delegates to a closed React primitive (@shadcn/react); this is a
from-scratch Stimulus controller (ruby-ui--message-scroller) — our own
code, no external lib:

- autoScroll follow-edge: pins to the bottom while the reader is there,
  releases on wheel/touch/keyboard/scrollbar away, re-engages on jump.
- scrollAnchor: settles an appended turn near the top keeping a peek of
  the previous item (previous_item_peek).
- defaultPosition: open at end / start / last-anchor.
- preserveOnPrepend: hold the visible row when history loads in above.
- Public API for streaming/ActionCable: scrollToEnd/scrollToStart/
  scrollToMessage; new rows are picked up via MutationObserver.
- rAF-based smooth scrolling (native smooth is unreliable on a contained
  viewport), honors prefers-reduced-motion.
- a11y: content role=log + aria-relevant, button sr-only label, button
  removed from tab order while inert.

Parts: MessageScrollerProvider, MessageScroller, MessageScrollerViewport,
MessageScrollerContent, MessageScrollerItem, MessageScrollerButton.

dependencies.yml: message_scroller depends on Message + Bubble. MCP
registry, docs page, route, controller, menu and site_files updated.

* [Bug Fix] Message Scroller: address review feedback

- Gate anchored-turn scrolling behind autoScroll/following, so a new turn
  never yanks a reader who scrolled up to older content (P1).
- Scroll button honors data-direction: a start-direction button now jumps
  to the start instead of the end (renamed action jumpToEnd → jump) (P2).
- Guard last-anchor opening position with hasContentTarget; the Stimulus
  target getter throws rather than returning undefined (P2).
- Include the flex row gap in prepend preservation so the visible row no
  longer drifts down by one gap per history insertion (P2).
- Only treat direct content children as transcript rows; markup mutated
  inside a message (subtree) is handled as streaming, not history (P2).

Rebuilt MCP registry.

* [Documentation] Message Scroller: faithful chat-window demo with Empty state

Add the shadcn-style chat window as the hero example: a Card with an Empty
state until the first message, then a scrolling transcript that follows the
live edge, plus an input footer. A docs-only Stimulus demo harness
(message-scroller-chat) clones server-rendered user/assistant templates on
send so the scroller's autoscroll/anchoring is demonstrated live — standing
in for a real ActionCable/streaming source. Uses the new Empty component.

* [Bug Fix] Message Scroller: direction-aware scroll button visibility

updateButton now derives each button's active state from its own
data-direction — an end button activates when away from the bottom, a
start button when away from the top — and iterates all button targets.
Previously a start-direction button used end logic, so it was inert at
the live edge and showed at the wrong end. (cubic P2)
@djalmaaraujo djalmaaraujo mentioned this pull request Jul 1, 2026
3 tasks
djalmaaraujo added a commit that referenced this pull request Jul 1, 2026
Bump RubyUI to 1.5.0 (minor: new components since v1.4.0).

- gem/lib/ruby_ui.rb → 1.5.0; regenerate gem/ and docs/ Gemfile.lock
- docs home hero badge → headline features (Bubble, Message, Empty)
- rebuild mcp/data/registry.json

Highlights since v1.4.0:
- New components: Bubble (#445), Message (#446), Message Scroller (#447), Empty (#448)
- Port hover_card & context_menu to Floating UI, drop Popper.js (#438)
- Bug fixes: Dialog closed-state + docs controller (#458), DropdownMenu z-index (#440)
djalmaaraujo added a commit that referenced this pull request Jul 1, 2026
Bump RubyUI to 1.5.0 (minor: new components since v1.4.0).

- gem/lib/ruby_ui.rb → 1.5.0; regenerate gem/ and docs/ Gemfile.lock
- docs home hero badge → headline features (Bubble, Message, Empty)
- rebuild mcp/data/registry.json

Highlights since v1.4.0:
- New components: Bubble (#445), Message (#446), Message Scroller (#447), Empty (#448)
- Port hover_card & context_menu to Floating UI, drop Popper.js (#438)
- Bug fixes: Dialog closed-state + docs controller (#458), DropdownMenu z-index (#440)
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.

[Feature] Message component (chat message layout: avatar + bubble + header/footer)

2 participants