From 13d231b86a91798f52bc1ea507bc045e31e9dfae Mon Sep 17 00:00:00 2001 From: operator-main Date: Sun, 4 Oct 2026 03:53:52 +0000 Subject: [PATCH] DOM reference: message actions and thread interactions\n\n- Messages use .group/msg (no testid); action via More options radix menu\n- 6 quick reactions documented; Reply is composer-quote only, no Edit UI\n- Headless quirk: full pointer event sequence needed for radix --- docs/DOM-MESSAGE-ACTIONS.md | 362 ++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 docs/DOM-MESSAGE-ACTIONS.md diff --git a/docs/DOM-MESSAGE-ACTIONS.md b/docs/DOM-MESSAGE-ACTIONS.md new file mode 100644 index 0000000..bca9cc1 --- /dev/null +++ b/docs/DOM-MESSAGE-ACTIONS.md @@ -0,0 +1,362 @@ +# DOM Reference: Message Actions & Thread Interactions + +Inspected live via CDP on the opm browser (warp-opm netns, CDP 9440), 2026-10-04. +Covers main chat (`https://muse.ai/`) and sidechat threads (`/thread/`). +Read-mostly probes; the only state-changing probes were opening/closing the +"More options" menu and one Reply-draft open/cancel cycle (composer verified clean after). + +Related: `DOM-CHAT-PANEL.md` (panel/sidechat create), `DOM-MESSAGES.md`, `DOM-PAGE-STRUCTURE.md`. + +--- + +## 1. Message wrapper + +Messages carry **no `data-testid`**. The structural hook is the Tailwind group class: + +```html +
+``` + +Selector: + +```javascript +document.querySelectorAll('.group\\/msg') // note: escaped slash in class name +``` + +Parent chain (own message, bottom-up): + +``` +DIV.group/msg (ms-auto, items-end → right-aligned = own message) +└── DIV.touch:select-none.flex.w-full.flex-col.items-end + └── DIV.relative.flex.w-full.flex-col.items-end + ├── DIV. (absolute inset-y-0 end-full …) + └── DIV.relative.max-w-full + └── DIV.relative.max-w-full + └── DIV.hatch-chat-groupable-bubble + ├── SPAN.sr-only "You:" (author label, a11y only) + └── P.text-[16px].leading-[22px].whitespace-pre-wrap (body) +``` + +Message list container: + +```javascript +document.querySelector('div[role="log"][aria-label="Chat messages"]') +``` + +### Bubble variants + +| Variant | Bubble classes | Alignment | +|---|---|---| +| Own (user) message | `hatch-chat-groupable-bubble … bg-chat-user-bubble text-chat-user-text` | `group/msg` has `ms-auto items-end` (right) | +| Agent message | `hatch-chat-groupable-bubble … bg-[var(--hatch-agent-bubble-bg)]` | left-aligned | +| Agent body text | `DIV.!text-[15px].text-text-primary` (prose container) | — | + +--- + +## 2. Action rail (hover-revealed) + +Each `div.group/msg` contains exactly one action-rail div: + +```javascript +// selector: the rail inside a message +msg.querySelector(':scope > div > div > div[class*="end-full"]') +``` + +Full classes (desktop): + +``` +touch:static touch:mb-0 touch:h-auto touch:scale-100 touch:opacity-100 +absolute inset-y-0 end-full my-auto flex h-fit shrink-0 scale-95 opacity-0 +transition-[opacity,scale] +group-hover/msg:scale-100 group-hover/msg:opacity-100 +``` + +Key facts: + +- Positioned `absolute inset-y-0 end-full` — vertically centered on the message's + outer (end) side, `my-auto`. +- Hidden with `opacity-0`, revealed purely by CSS `group-hover/msg` — **no JS + hover needed for automation**; query the button directly. +- `touch:` variants force it visible on touch devices. +- Width driven by `--hatch-message-compact-action-rail-width: 2.75rem`. +- ⚠️ `getComputedStyle().display/visibility` report the button as visible even + when the rail is `opacity-0` — check `opacity` for true on-screen visibility. + +### The single action button: "More options" + +Every message (own AND agent) exposes exactly **one** action button — a Radix +dropdown trigger: + +```html + +``` + +```javascript +msg.querySelector('button[aria-label="More options"]') +``` + +- 28×28 px, `rounded-full`, icon-only (SVG, no text content). +- `id` follows the Radix pattern `radix-_r__` — **unstable across renders, + do not match on id**; match on `aria-label="More options"`. +- This button is the gateway to ALL message actions (reply, react, copy, delete). + There are no separate always-visible reply/react/copy buttons. + +--- + +## 3. "More options" menu (Radix dropdown) + +### Opening it (headless quirk) + +A plain `.click()` did **not** open the menu. The full pointer sequence was required: + +```javascript +const b = msg.querySelector('button[aria-label="More options"]'); +b.scrollIntoView({block: 'center'}); +const r = b.getBoundingClientRect(); +const opts = {bubbles: true, cancelable: true, + clientX: r.x + r.width/2, clientY: r.y + r.height/2, button: 0}; +b.dispatchEvent(new PointerEvent('pointerdown', opts)); +b.dispatchEvent(new MouseEvent('mousedown', opts)); +b.dispatchEvent(new PointerEvent('pointerup', opts)); +b.dispatchEvent(new MouseEvent('mouseup', opts)); +b.dispatchEvent(new MouseEvent('click', opts)); +``` + +- When open, the trigger gets `aria-expanded="true"` and `data-state="open"`. +- Opening was flaky in headless (~1 in 4 attempts needed a retry): loop up to 4×, + checking for `[role="menu"][data-state="open"]`, Escape between attempts. +- Close with: `document.dispatchEvent(new KeyboardEvent('keydown', + {key: 'Escape', keyCode: 27, bubbles: true}))`. + +### Menu container + +```html +