Add composer autocomplete for #roomname (#47 follow-up)

Mirrors the @mention autocomplete exactly -- same trigger-detection
logic (factored into a shared detectTriggerQuery helper, parameterized
on '@' vs '#'), same arrow-key/Enter/Tab/Escape keyboard handling, same
dropdown. Suggests rooms the user belongs to, filtered by name prefix,
showing the room's description as a subtitle when it has one.

MentionAutocomplete.css is renamed to ComposerAutocomplete.css with
generic class names (composer-autocomplete-primary/-secondary instead of
-username/-display-name), now shared by both the mention and room-
reference dropdowns instead of being mention-specific.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ksmithandClaude Sonnet 5 committed 2026-08-17 19:12:51 -06:00
1 parent 12264b4d18
commit 2a84a9c9bd
5 files changed
+141 -27

No files matched your search

@@ -1,5 +1,5 @@
import type { RoomMember } from '../types'
import './MentionAutocomplete.css'
import './ComposerAutocomplete.css'
interface MentionAutocompleteProps {
matches: RoomMember[]
@@ -10,14 +10,14 @@ interface MentionAutocompleteProps {
export function MentionAutocomplete({ matches, activeIndex, onPick, onHover }: MentionAutocompleteProps) {
return (
<div className="mention-autocomplete" role="listbox">
<div className="composer-autocomplete" role="listbox">
{matches.map((member, i) => (
<button
key={member.user_id}
type="button"
role="option"
aria-selected={i === activeIndex}
className={`mention-autocomplete-item${i === activeIndex ? ' mention-autocomplete-item-active' : ''}`}
className={`composer-autocomplete-item${i === activeIndex ? ' composer-autocomplete-item-active' : ''}`}
// Selecting must survive the textarea's blur (which would
// otherwise fire first and could dismiss the dropdown) --
// onMouseDown fires before blur, onClick fires after.
@@ -25,9 +25,9 @@ export function MentionAutocomplete({ matches, activeIndex, onPick, onHover }: M
onClick={() => onPick(member.username)}
onMouseEnter={() => onHover(i)}
>
<span className="mention-autocomplete-username">@{member.username}</span>
<span className="composer-autocomplete-primary">@{member.username}</span>
{member.display_name && (
<span className="mention-autocomplete-display-name">{member.display_name}</span>
<span className="composer-autocomplete-secondary">{member.display_name}</span>
)}
</button>
))}