Files
box/docs/DOM-INDEX.md
T
dom-inspector-5 e28b10edc8 DOM inspector 5/5: verify page structure + edge states, regenerate index
Session: sidechat/dom-structure
2026-10-04 04:29:10 +00:00

15 KiB

muse.ai DOM Index — Master Reference

Entry point for all muse.ai DOM automation work. Read this first. It indexes every data-testid, every key selector, every automation recipe, and flags known contradictions between the detailed docs.

Rule of thumb from the field: prefer data-testid > aria-label > role/id

class names. Tailwind classes shift with deploys; testids don't.


1. Document Map

Doc Covers Last verified
DOM-CHAT-PANEL.md Chat panel switcher, compose + button, panel shell/tree, thread rows, open-vs-closed detection, 24-testid inventory 2026-10-04 (CDP, opm)
DOM-MESSAGES.md Message composer, send/stop buttons, message list (data-message-id), chat activation button (hatch-nav-chat), loading/empty states 2026-10-04 ~04:30 UTC (CDP, all 3 nodes)
DOM-PAGE-STRUCTURE.md Page states/modes, key selectors, nav rail, approval-dialog summary, overlays, URL patterns, testid inventory, automation sequences 2026-10-04 ~04:35 UTC (CDP, muse/pip/opm)
DOM-EDGE-STATES.md Loading/partial-load (S3), stripped states (S8), empty states, typing indicator, error/toast traps, offline, rate limits, iframes/shadow DOM, readiness cheat-sheet 2026-10-04 ~04:35 UTC (CDP, muse/pip/opm)
DOM-MESSAGE-ACTIONS.md Message actions & thread interactions (.group/msg, more-options menus, reactions, reply drafts) 2026-10-04 ~04:30 UTC (CDP, all 3 nodes)
DOM-NOTIFICATIONS.md Notification surface: toast live region (no bell, no notification center), activity indicators 2026-10-04 (CDP, opm)
DOM-SEARCH.md Global search / command palette: trigger, input, results 2026-10-04 (CDP, opm)
DOM-SETTINGS.md Settings, profile & dock rail structure (no credential values recorded) 2026-10-04 (CDP, opm)
DOM-APPROVALS-SPEC.md Approval/permission dialog detection, classification (trusted IPs), handling, exit-code contract (2 = APPROVAL_NEEDED) 2026-10-03

Naming note: inspector 3/5's brief mentioned DOM-APPROVALS.md; the file on disk is DOM-APPROVALS-SPEC.md (since 2026-10-03). This index references the actual filename.

Cross-reference guide:

  • Sidechat creation mechanics → CHAT-PANEL (§4 safe toggle, §6 automation notes)
  • What to type into / how to send → MESSAGES (§1 composer, §2 send)
  • "Which state am I in?" → PAGE-STRUCTURE (§1 states, §7.2 one-liner) or §3 below
  • "Am I blocked by a dialog?" → APPROVALS-SPEC (detection + exit codes)
  • "Chat function not active" → MESSAGES (§4 hatch-nav-chat) + §4 recipe below

2. Master data-testid Table (deduplicated)

Docs column: C = CHAT-PANEL, M = MESSAGES, P = PAGE-STRUCTURE.

Chat core (panel toggle + creation)

testid Tag Purpose Docs
hatch-chat-switcher-trigger BUTTON Panel open/close toggle. Inner text is the current thread title (not Chats) — never match by text. Never blind-click — check compose first C, P
hatch-side-chats-open-drag-handle-divider-line DIV Divider for the closed-panel drag handle; absent when panel is open (verified 2026-10-04, all nodes) P
hatch-chat-switcher-icon-knockout SPAN Icon wrapper inside switcher C, P
hatch-chat-switcher-navigation-icon SPAN Hamburger SVG inside switcher C, P
hatch-chat-leading-controls DIV Top-left container holding the switcher C, P
hatch-chat-compose BUTTON New side chat +. Icon-only (aria-label="New side chat"). Exists only when panel is open → the canonical "ready" signal C, M, P
hatch-chat-nav-fade DIV Nav fade effect C, P
hatch-chat-search-toolbar DIV Panel search toolbar (60px) C, P
hatch-chat-search-field DIV Search input container C, P
hatch-side-chats-options BUTTON "Side chat options" three-dots dropdown C, P
hatch-chat-switcher-unread-indicator — Unread badge on switcher M

Sidechat panel tree

testid Tag Purpose Docs
hatch-side-chats-panel-shell DIV Outer shell, always in DOM, innerHTML === "" when closed, 240px fixed C, P
hatch-side-chats-panel-sliding-surface DIV Sliding animation surface (open only) C, P
hatch-side-chats-panel-content DIV Content wrapper — presence = panel open C, P
hatch-side-chats-open-drag-handle DIV Panel resize handle C, P
hatch-thread-row DIV[role=button] Every chat row (Main + sidechats). Not links — click to navigate. Active row has aria-current="page" C, P
hatch-side-chat-unread-indicator — Per-chat unread indicator P
active-side-chat-load-more-sentinel DIV Infinite-scroll sentinel at list end C, P

Dock rail (left nav)

testid Tag Purpose Docs
hatch-dock-rail DIV Left dock rail container C, M, P
hatch-dock-rail-divider SPAN Rail divider P
hatch-nav-chat A Chat section link (href="/"). aria-label is dynamic (Chat / Chat, N notifications) — match testid, never exact aria-label. aria-current="page" when Chat active. The reliable chat activator C, M, P
hatch-dock-chat-notification-badge — Notification badge on nav-chat P
hatch-nav-search BUTTON Search nav C, M, P
hatch-nav-system-item-66656564 etc. DIV Feed / Ideas / Goals / Library (hex-encoded ids) C, P
hatch-dock-more BUTTON Settings (aria-label="Settings") C, M, P
app-download-dock-mark — App download mark P
hatch-panel-resize-divider-line DIV Panel resize divider P

Composer & messages

testid Tag Purpose Docs
hatch-composer-placeholder-overlay SPAN "Message" ghost text — visible when composer empty C, M, P
hatch-composer-stop-button BUTTON Stop-generation — present only while streaming M
hatch-chat-typing-indicator — Typing indicator — present only while generating M

Status / misc

testid Tag Purpose Docs
hatch-status-panel-sliding-surface DIV Agent status panel C, P
hatch-status-panel-close BUTTON Status panel close P
hatch-status-panel-close-drag-handle DIV Status panel drag handle P
hatch-status-panel-close-drag-handle-divider-line DIV Divider P
hatch-status-panel-close-toolbar DIV Toolbar P
hatch-invite-friends-button BUTTON Invite (aria-label="Invite") C, P
hatch-browser-task-banner-host DIV Browser task banner (hidden) C, P
hatch-inline-approval-card DIV Browser-task approval card (A task needs review + Review button), no IP in text. Structure TBD — inspector 3/5, DOM-APPROVALS-SPEC.md P

3. Non-testid Selectors (equally load-bearing)

Selector Purpose Doc
textarea[aria-label="Message"] Message composer input. Fallbacks: [contenteditable="true"], textarea[placeholder*="Message"], div[role="textbox"] M
button[aria-label*="send" i] Send button (no testid, SVG-only). Fallback: Enter keydown on input M
#hatch-chat-scroll Message scroll container M
div[role="log"][aria-label="Chat messages"] Message list M
[data-message-id] Each message. assistant-msg-<uuid> = assistant, bare <uuid> = user M
button[aria-label="Back to main chat"] Primary main-chat affordance from any thread state (/thread/<uuid> and /thread/new), panel open or closed. Verified 2026-10-04 on all 3 nodes P (§2.7)
Exact-text Main chat element Main-chat navigation target — match (e.innerText).trim() === 'Main chat' on button, [role="button"], never substring P (§2.7), AGENTS.md

4. Quick-Reference Automation Recipes

4a. Activate chat (recover from ANY state) — replaces Ctrl+J

// Priority: compose (fast path) → switcher → nav-chat (stripped-state recovery)
function ensureChatActive() {
  if (document.querySelector('[data-testid="hatch-chat-compose"]')) return true; // already ready
  const sw = document.querySelector('[data-testid="hatch-chat-switcher-trigger"]');
  if (sw) { sw.click(); return "clicked-switcher"; }                            // closed panel
  const nc = document.querySelector('[data-testid="hatch-nav-chat"]');
  if (nc) { nc.click(); return "clicked-nav-chat"; }                            // /thread/new stripped
  return false; // not on a chat page at all — abort
}
// Poll up to ~20s, then verify compose exists before proceeding.

Why not Ctrl+J: it needs keyboard focus, which stripped states (/thread/new) lack — the keystroke goes nowhere. DOM clicks don't need focus. (Commit 72574ba.)

4b. Detect current state (one-liner)

(() => {
  const url = window.location.href, title = document.title;
  const q = s => !!document.querySelector(s);
  if (title === 'muse.ai') return 'LANDING';
  if (url.includes('/thread/new')) return 'THREAD_NEW_STRIPPED';
  if (url.includes('/thread/')) return q('[data-testid="hatch-chat-switcher-trigger"]') ? 'SIDECHAT' : 'THREAD_NEW_STRIPPED';
  if (url === 'https://muse.ai/') {
    if (!q('[data-testid="hatch-chat-switcher-trigger"]')) return 'UNKNOWN_NO_SWITCHER';
    return q('[data-testid="hatch-chat-compose"]') ? 'MAIN_PANEL_OPEN' : 'MAIN_PANEL_CLOSED';
  }
  return 'UNKNOWN';
})()

State table: LANDING · MAIN_PANEL_CLOSED · MAIN_PANEL_OPEN · THREAD_NEW_STRIPPED (/thread/new, no switcher) · SIDECHAT (/thread/<uuid>). Full matrix: PAGE-STRUCTURE §1.

4c. Create sidechat (robust)

1. ensureChatActive() (§4a) — poll ≤20s, abort if false
2. Verify: !!document.querySelector('[data-testid="hatch-chat-compose"]')
3. Click compose
4. Poll window.location.href ≤15s for "/thread/"  →  /thread/new (placeholder)
5. Send first message directly (browser is on the new sidechat)
6. Poll href for /thread/<uuid> to capture the durable thread URL

4d. Send a message

1. ta = document.querySelector('textarea[aria-label="Message"]') (+ fallbacks)
2. Verify ready: ta.offsetParent !== null && !ta.disabled && !ta.readOnly
3. ta.focus(); document.execCommand('insertText', false, message)
   (React fallback: native value setter + bubbling 'input' event)
4. send = [...document.querySelectorAll('button')].find(b => b.getAttribute('aria-label')?.toLowerCase().includes('send'))
   → send.click()  else Enter-keydown on ta
5. If streaming: [data-testid="hatch-composer-stop-button"] appears; wait for it to vanish

4e. Open a specific sidechat / go to main chat

// By title (rows are DIV[role=button], NOT links — no hrefs exist):
const row = [...document.querySelectorAll('[data-testid="hatch-thread-row"]')]
  .find(r => r.querySelector('span[title]')?.getAttribute('title') === '<title>');
row?.click();
// Main chat: exact text match, never substring:
const main = [...document.querySelectorAll('button, [role="button"]')]
  .find(e => (e.innerText || '').trim() === 'Main chat');
main?.click();

4f. Check for blocking approval dialogs

Text-based (no stable testids observed). See APPROVALS-SPEC for the full contract:

  • Body text contains Allow + to share in an element < 500 chars, and an IPv4 is present
  • Trusted IPs (34.139.37.135, 100.123.153.75, 100.81.31.9) → auto-click allow once/allow
  • Anything else → print APPROVAL_NEEDED, exit 2, do NOT click

5. Known Contradictions (resolved here — detail docs may lag)

# Conflict Resolution (this index wins)
1 PAGE-STRUCTURE §7.1 and §2.7 recommend Ctrl+J as "proven reliable" for main-chat navigation / state recovery Superseded. Ctrl+J needs keyboard focus; stripped states lack it. Use the §4a click sequence (hatch-nav-chat → switcher). CHAT-PANEL §6.1 already agrees Ctrl+J is unreliable
2 PAGE-STRUCTURE §1.3 "Recovery: Send Ctrl+J" from /thread/new Use button[aria-label="Back to main chat"] click or hatch-nav-chat click instead
3 /thread/new title: MESSAGES §5 and PAGE-STRUCTURE §1.3 say Muse; a 2026-10-04 probe table said Chat — … Docs consensus: Muse. Treat title as advisory only — detect via URL + switcher absence (§4b), never title alone
4 "Sidechat create navigates to main first" (older code comment) Unnecessary. The ready state for create is any chat-active view; hatch-nav-chat recovery lands on main anyway
5 CHAT-PANEL / older notes describe switcher text as Chats (or Chats\nUnread chats…) Thread title. Verified 2026-10-04 on all nodes: switcher innerText = <current thread title>\nOpen chat and side chats. Never match the switcher by text

6. Gaps — Unmapped Territory

  1. hatch-inline-approval-card structure — testid + surface text documented (PAGE-STRUCTURE §6, this index §2), full structure deferred to inspector 3/5 in DOM-APPROVALS-SPEC.md.
  2. Feed / Ideas / Goals / Library pages — dock testids listed, page content unmapped.
  3. Search overlay (hatch-nav-search target) — trigger documented in DOM-SEARCH.md; result-row interactions unmapped.
  4. OTP / account-selection states — body-text markers known (To log in, enter the code, Your email matches multiple accounts), DOM unmapped. Automation should abort to human here.
  5. cmd_sidechat_main's exact-text Main chat lookup — documented in AGENTS.md and §3/§4e here, no DOM-doc section.
  6. Partial-load state (title Muse, ~21 buttons, transient) — mentioned in MESSAGES §5, no recovery procedure beyond "navigate to / and re-probe".
  7. Message read-back verification selectors — MESSAGES §3 covers extraction; the dm.py tail-match verification loop is code-only.
  8. /tmp script collisions between inspectors — two inspectors overwrote each other's /tmp/cdp_eval.py on bl mid-task (2026-10-04). Namespace throwaway scripts per-inspector (e.g. /tmp/cdp_eval_<area><n>.py).

Resolved 2026-10-04: button[aria-label="Back to main chat"] now has a dedicated section in PAGE-STRUCTURE §2.7 (was gap #1 in the previous index).


7. Maintenance

  • This index is the entry point; detail docs hold the deep dumps. When a selector changes, update the detail doc and the tables/recipes here.
  • Contradiction rule: newer verified probe beats older doc text; record the resolution in §5 with the commit or date.
  • "Last verified" in §1 is per-doc. If you re-probe a doc's selectors, bump its date.
  • Prefer adding a dated note over rewriting history — these docs are also an audit trail.
  • 2026-10-04 regeneration: document map now covers all 10 DOM-*.md files (was 4); master table carries the two new testids (hatch-side-chats-open-drag-handle-divider-line, hatch-inline-approval-card) and the switcher-text + dock-rail-divider corrections. When a new DOM doc lands, add its row to §1 and its testids to §2 in the same commit.