252 lines
8.3 KiB
Markdown
252 lines
8.3 KiB
Markdown
|
|
# DOM Reference: Settings, Profile & Dock Rail
|
||
|
|
|
||
|
|
Inspected live via CDP on the opm browser (warp-opm netns, CDP 9440), 2026-10-04.
|
||
|
|
Structure only — no credential, token, or personal values are recorded here.
|
||
|
|
|
||
|
|
Related: `DOM-CHAT-PANEL.md` (chat panel), `DOM-MESSAGES.md` (messages),
|
||
|
|
`DOM-PAGE-STRUCTURE.md` (page states).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Dock rail
|
||
|
|
|
||
|
|
The left vertical nav rail. Container:
|
||
|
|
|
||
|
|
```html
|
||
|
|
<div data-testid="hatch-dock-rail" class="relative flex h-full">
|
||
|
|
```
|
||
|
|
|
||
|
|
### Rail items (top to bottom)
|
||
|
|
|
||
|
|
| # | Selector | Tag | `aria-label` | Notes |
|
||
|
|
|---|----------|-----|--------------|-------|
|
||
|
|
| 1 | `[data-testid="hatch-nav-chat"]` | `<a href="/">` | `Chat` | The chat/message button from the user's screenshot. Navigates `/thread/new` → `https://muse.ai/`. See "chat activation" below. |
|
||
|
|
| 2 | `[data-testid="hatch-nav-search"]` | `<button>` | `Search` | Opens quick-search dialog `[data-testid="hatch-quick-search-frame"]` (heading "Search Muse"). |
|
||
|
|
| 3 | *(none)* | `<button>` | `Get the Muse app` | App download upsell. No `data-testid`. |
|
||
|
|
| 4 | `[data-testid="hatch-dock-more"]` | `<button>` | `Settings` | Opens the settings menu (radix menu). Bottom of rail (`div.mt-auto`). |
|
||
|
|
|
||
|
|
Parent chain of the settings button (4 levels):
|
||
|
|
|
||
|
|
```
|
||
|
|
div[data-testid="hatch-dock-rail"].relative.flex.h-full
|
||
|
|
> div.relative.z-10.flex
|
||
|
|
> div.mt-auto.flex.flex-col
|
||
|
|
> button#radix-_[data-testid="hatch-dock-more"].hatch-dock-reveal-item.group.relative
|
||
|
|
```
|
||
|
|
|
||
|
|
Key attributes on the button:
|
||
|
|
|
||
|
|
```html
|
||
|
|
<button type="button"
|
||
|
|
aria-label="Settings"
|
||
|
|
data-testid="hatch-dock-more"
|
||
|
|
aria-haspopup="menu"
|
||
|
|
aria-expanded="false" <!-- unreliable: stays "false" even when menu is open -->
|
||
|
|
data-state="closed" <!-- unreliable: stays "closed" even when menu is open -->
|
||
|
|
data-pel-click="settings_menu_open_click">
|
||
|
|
```
|
||
|
|
|
||
|
|
> **Automation note:** `data-state` / `aria-expanded` do NOT reflect the real
|
||
|
|
> open state. Detect the open menu by counting `[role="menuitem"]` elements
|
||
|
|
> (`> 0` means open). Synthetic `.click()` does not open the radix menu;
|
||
|
|
> use real mouse events via CDP `Input.dispatchMouseEvent`
|
||
|
|
> (`mousePressed` + `mouseReleased` at the button center). The button toggles,
|
||
|
|
> so click only when `[role="menuitem"]` count is 0.
|
||
|
|
|
||
|
|
### Chat activation via `hatch-nav-chat`
|
||
|
|
|
||
|
|
Clicking `[data-testid="hatch-nav-chat"]` from the stripped `/thread/new` state
|
||
|
|
navigates to `https://muse.ai/` and the chat switcher appears (~1s). This is
|
||
|
|
the reliable "activate chat" primitive — see the `_ensure_chat_active()`
|
||
|
|
logic in `muse-chat-api.py` (priority: compose → switcher → nav-chat).
|
||
|
|
`aria-label` on this link is unstable (`"Chat"` vs `"Chat, N notification"`);
|
||
|
|
always match on the `data-testid`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Settings menu (dock "more" menu)
|
||
|
|
|
||
|
|
Radix menu rendered in a popper. Items (`role="menuitem"`, no `data-testid`):
|
||
|
|
|
||
|
|
| Text | `data-pel-click` | Action (observed) |
|
||
|
|
|------|------------------|-------------------|
|
||
|
|
| Keyboard shortcuts | `keyboard_shortcuts_nav_click` | No dialog rendered under automation (see §5) |
|
||
|
|
| Download apps | `mobile_app_upsell_menu_click` | — (not opened) |
|
||
|
|
| Report an issue | `bug_report_nav_click` | — (not opened) |
|
||
|
|
| Settings | `settings_nav_click` | Opens the Settings dialog (§3) |
|
||
|
|
|
||
|
|
Menu container selectors (either may match):
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
document.querySelector('[role="menu"]')
|
||
|
|
document.querySelector('[data-radix-popper-content-wrapper]')
|
||
|
|
```
|
||
|
|
|
||
|
|
Find a menuitem reliably:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
[...document.querySelectorAll('[role="menuitem"]')]
|
||
|
|
.find(m => m.getAttribute('data-pel-click') === 'settings_nav_click')
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Settings dialog
|
||
|
|
|
||
|
|
A radix dialog (`div[role="dialog]"`, id like `radix-_r_10g_`; the id suffix
|
||
|
|
is random per render — do not hardcode it).
|
||
|
|
|
||
|
|
```
|
||
|
|
div#radix-_<role=dialog>
|
||
|
|
```
|
||
|
|
|
||
|
|
- Heading is an **`H2`** with text `Settings` (not `h1`).
|
||
|
|
- Layout: left nav column + right content column.
|
||
|
|
|
||
|
|
### 3a. Left nav (section buttons)
|
||
|
|
|
||
|
|
Plain `<button>` elements, no `data-testid`s. In order:
|
||
|
|
|
||
|
|
```
|
||
|
|
General
|
||
|
|
Connectors
|
||
|
|
Wallet
|
||
|
|
Secure store
|
||
|
|
Permissions
|
||
|
|
Messaging channels
|
||
|
|
Devices
|
||
|
|
Data controls
|
||
|
|
Help & support
|
||
|
|
Legal info
|
||
|
|
Log out ← the sign-out control; no confirmation dialog observed at open
|
||
|
|
Close
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3b. General section
|
||
|
|
|
||
|
|
Headings (all `h2`): `General`, `Usage`, `Appearance`.
|
||
|
|
|
||
|
|
**Usage** — one button:
|
||
|
|
|
||
|
|
| Control | Label | Notes |
|
||
|
|
|---------|-------|-------|
|
||
|
|
| `<button>` | `Upgrade` | Plan upgrade upsell |
|
||
|
|
|
||
|
|
**Appearance** — theme + accent color:
|
||
|
|
|
||
|
|
| Control | Label |
|
||
|
|
|---------|-------|
|
||
|
|
| `<button>` | `Light` |
|
||
|
|
| `<button>` | `Dark` |
|
||
|
|
| `<button>` | `System` |
|
||
|
|
| `<input type="radio">` | `Match my avatar` |
|
||
|
|
| `<input type="radio">` | `Default` |
|
||
|
|
| `<input type="radio">` | `Blue` |
|
||
|
|
| `<input type="radio">` | `Purple` |
|
||
|
|
| `<input type="radio">` | `Pink` |
|
||
|
|
| `<input type="radio">` | `Orange` |
|
||
|
|
| `<input type="radio">` | `Green` |
|
||
|
|
| `<input type="radio">` | `Beige` |
|
||
|
|
| `<input type="radio">` | `Monochrome` |
|
||
|
|
|
||
|
|
### 3c. Profile / avatar
|
||
|
|
|
||
|
|
- Avatar image: `<img>` with empty `alt`, `src` present (value not recorded).
|
||
|
|
No `data-testid` on the img.
|
||
|
|
- `Edit avatar and name` button (`aria-label="Edit avatar and name"`,
|
||
|
|
`<button>`, no `data-testid`) appears in the profile area of the settings
|
||
|
|
dialog. Chain (3 levels):
|
||
|
|
|
||
|
|
```
|
||
|
|
div.pointer-events-auto.absolute.z-20
|
||
|
|
> div.group-has-[[data-hatch-avatar-media-loading]]/status-avatar:hidden
|
||
|
|
> span.contents
|
||
|
|
> button[aria-label="Edit avatar and name"].inline-flex.shrink-0.cursor-pointer
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3d. Other sections (not opened)
|
||
|
|
|
||
|
|
`Connectors`, `Wallet`, `Secure store`, `Permissions`, `Messaging channels`,
|
||
|
|
`Devices`, `Data controls`, `Help & support`, `Legal info` were not opened
|
||
|
|
during mapping (each is a left-nav `<button>`; clicking one swaps the right
|
||
|
|
content column). `Secure store` / `Wallet` likely contain credential-adjacent
|
||
|
|
UI — map with care, structure only, never values.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Account switching
|
||
|
|
|
||
|
|
**No multi-account switcher was observed** in the settings dialog or dock
|
||
|
|
rail. The only account control found is the `Log out` button in the settings
|
||
|
|
left nav. Each Muse account runs in its own isolated browser profile
|
||
|
|
(`muse`, `pip`, `646`, `opm` nodes), so in this fleet account switching
|
||
|
|
happens at the profile level, not inside the app UI.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Keyboard shortcuts modal
|
||
|
|
|
||
|
|
Menu item exists:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
[...document.querySelectorAll('[role="menuitem"]')]
|
||
|
|
.find(m => m.getAttribute('data-pel-click') === 'keyboard_shortcuts_nav_click')
|
||
|
|
```
|
||
|
|
|
||
|
|
Clicking it via automation (menuitem `.click()` after opening the dock menu
|
||
|
|
with real mouse events) produced **no `[role="dialog"]`** within 3s and no
|
||
|
|
URL change. The modal likely requires a trusted user gesture deeper in the
|
||
|
|
radix event chain, or renders on a longer delay. **Status: selector documented,
|
||
|
|
modal content not captured** — needs a headed/manual pass or longer wait.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Quick reference: reliable selectors
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Dock rail
|
||
|
|
'[data-testid="hatch-dock-rail"]'
|
||
|
|
'[data-testid="hatch-nav-chat"]' // chat button (stable; aria-label unstable)
|
||
|
|
'[data-testid="hatch-nav-search"]' // search
|
||
|
|
'[data-testid="hatch-dock-more"]' // settings menu trigger
|
||
|
|
|
||
|
|
// Settings menu (after real mouse click on hatch-dock-more)
|
||
|
|
'[role="menuitem"]' // count > 0 => menu open
|
||
|
|
'[data-pel-click="settings_nav_click"]'
|
||
|
|
'[data-pel-click="keyboard_shortcuts_nav_click"]'
|
||
|
|
'[data-pel-click="bug_report_nav_click"]'
|
||
|
|
'[data-pel-click="mobile_app_upsell_menu_click"]'
|
||
|
|
|
||
|
|
// Settings dialog
|
||
|
|
'div[role="dialog"]' // id is random per render
|
||
|
|
'button[aria-label="Edit avatar and name"]'
|
||
|
|
|
||
|
|
// Search dialog
|
||
|
|
'[data-testid="hatch-quick-search-frame"]'
|
||
|
|
```
|
||
|
|
|
||
|
|
## 7. Automation recipes
|
||
|
|
|
||
|
|
**Open the settings menu:**
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// 1. real mouse click at button center (synthetic .click() does NOT work)
|
||
|
|
const b = document.querySelector('[data-testid="hatch-dock-more"]');
|
||
|
|
const r = b.getBoundingClientRect();
|
||
|
|
// CDP: Input.dispatchMouseEvent mousePressed + mouseReleased at (r.x + r.width/2, r.y + r.height/2)
|
||
|
|
// 2. verify: document.querySelectorAll('[role="menuitem"]').length > 0
|
||
|
|
// (the button toggles — only click when count is 0)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Open the Settings dialog:**
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
[...document.querySelectorAll('[role="menuitem"]')]
|
||
|
|
.find(m => m.getAttribute('data-pel-click') === 'settings_nav_click')
|
||
|
|
.click();
|
||
|
|
// dialog appears in ~2s: document.querySelector('div[role="dialog"]')
|
||
|
|
// heading: [...document.querySelectorAll('h2')].find(h => h.innerText.trim() === 'Settings')
|
||
|
|
```
|
||
|
|
|
||
|
|
**Close any dialog:** `Escape` keydown (works for the settings dialog).
|