Files
box/docs/DOM-SETTINGS.md
T

281 lines
10 KiB
Markdown
Raw Normal View History

# DOM Reference: Settings, Profile & Dock Rail
Inspected live via CDP on the opm browser (warp-opm netns, CDP 9440), 2026-10-04.
Re-verified 2026-10-04 across all three fleet nodes (muse 9410, pip 9420, opm 9440).
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` | `data-pel-click` | Notes |
|---|----------|-----|--------------|------------------|-------|
| 1 | `[data-testid="hatch-nav-chat"]` | `<a href="/">` | `Chat` (unstable — see notifications doc) | `chat_tab_click` | 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` | `quick_search_nav_click` | Opens quick-search dialog `[data-testid="hatch-quick-search-frame"]` (heading "Search Muse"). |
| 3 | *(none)* | `<button>` | `Get the Muse app` | `mobile_app_upsell_click` | App download upsell. No `data-testid`. |
| 4 | `[data-testid="hatch-dock-more"]` | `<button>` | `Settings` | `settings_menu_open_click` | Opens the settings menu (radix menu). Bottom of rail (`div.mt-auto`). |
Mid-rail system items (verified 2026-10-04, all three nodes) — `DIV`s, not
buttons, between the search button and the app upsell:
| `data-testid` | `aria-label` |
|---|---|
| `hatch-nav-system-item-66656564` | `Feed` |
| `hatch-nav-system-item-6964656173` | `Ideas` |
| `hatch-nav-system-item-676f616c73` | `Goals` |
| `hatch-nav-system-item-6c696272617279` | `Library` |
(The hex suffixes decode to ascii: `feed`, `ideas`, `goals`, `library`.)
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" <!-- RELIABLE after 2026-10-04 re-verify: "true" when menu open -->
data-state="closed" <!-- unreliable: stays "closed" even when menu is open -->
data-pel-click="settings_menu_open_click">
```
> **Automation note (updated 2026-10-04):** `data-state` does NOT reflect the
> real open state (stays `"closed"`). `aria-expanded`, however, **does**:
> verified `"true"` on opm with the menu open (earlier claim that it stays
> `"false"` was wrong — possibly a timing artifact). Belt and braces: 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 are **`DIV`s with `role="menuitem"`**
(verified 2026-10-04 — not buttons), no `data-testid`:
| Text | `data-pel-click` | Action (observed) |
|------|------------------|-------------------|
| Keyboard shortcuts | `keyboard_shortcuts_nav_click` | No dialog 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`, in order): `General`, `Meta Account`, `Usage`, `Appearance`.
**Meta Account** — row labeled `Password, security, personal details`
(structure only; not opened).
**Usage** — plan and quota rows (structure only, values not recorded):
| Row | Notes |
|-----|-------|
| Plan name + weekly reset line | e.g. plan tier + "Weekly limit resets on <date>" |
| Usage bar(s) with percentage | at least two bars observed (weekly + additional) |
| `Upgrade` button | `data-pel-click="settings_general_upgrade_click"` |
| `Language` | control follows the usage block |
**Appearance** — theme + accent color:
| Control | Label source | Selection state |
|---------|--------------|-----------------|
| `<button aria-label="Light">` | `aria-label` (icon-only, no text) | `aria-pressed` |
| `<button aria-label="Dark">` | `aria-label` | `aria-pressed` |
| `<button aria-label="System">` | `aria-label` | `aria-pressed="true"` on opm (verified) |
| 9× `<input type="radio">` | **`aria-label`**, not innerText | `checked` |
Radio `aria-label`s in order: `Match my avatar` (checked on opm), `Default`,
`Blue`, `Purple`, `Pink`, `Orange`, `Green`, `Beige`, `Monochrome`.
All share one radio group (`name` is random per render, e.g. `_r_184_`).
### 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`, id is a random `radix-_R_…` value).
> **Correction 2026-10-04:** the button is **not a descendant of
> `div[role="dialog"]`**. While the settings dialog is open it renders as a
> sibling overlay inside `[data-testid="hatch-status-panel-sliding-surface"]`
> (nearest testid ancestor at depth 10; button rect ≈ (622,141), 32×32 on a
> 780px-wide viewport). It appears only while the dialog is open and is gone
> after Escape. **Match it by `aria-label`, never by dialog containment.**
> The 3-level parent chain below is still accurate:
```
```
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. Re-tested 2026-10-04 with **real mouse events**
(`Input.dispatchMouseEvent` press+release on the menuitem): still no dialog
within 3s; the menu simply closes. The menuitem carries no kbd hint in its
markup (icon SVG + text "Keyboard shortcuts" only).
**Status: selector documented, modal content not captured** — needs a headed/manual pass.
---
## 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).