Files
box/docs/DOM-NOTIFICATIONS.md
T

243 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DOM reference: notifications & activity (muse.ai)
Inspected live via CDP on the opm browser (warp-opm netns, port 9440),
2026-10-04. Companion docs: `DOM-CHAT-PANEL.md`, `DOM-MESSAGES.md`,
`DOM-PAGE-STRUCTURE.md`.
## TL;DR
muse.ai has **no bell icon and no notification center/dropdown**. The
notification surface is three separate things:
1. **Toast live region** — `section[aria-label="Notifications alt+T"]`
(transient toasts, aria-live).
2. **Activity tab** — `button[aria-label="Activity"]` in the status-panel
toolbar (activity feed; tabs: Activity / Approvals / Upcoming / Identity).
3. **Chat nav unread** — `a[data-testid="hatch-nav-chat"]` whose `aria-label`
becomes `"Chat, N notification(s)"` when chats are unread.
There are no badge/dot/pill unread elements in the DOM, and no element with
`notif` in its `data-testid`.
---
## 1. Toast live region
The only element carrying the word "notification" in an aria-label.
```js
document.querySelector('section[aria-label="Notifications alt+T"]')
```
| Property | Value |
|---|---|
| Tag | `<section>` |
| `aria-label` | `Notifications alt+T` (stable — safe to match on) |
| `tabindex` | `-1` |
| `aria-live` | `polite` |
| `aria-relevant` | `additions text` |
| `aria-atomic` | `false` |
| `data-testid` | none |
| Parent chain | direct child of `<body>` (no intermediate wrappers) |
| Content when idle | empty — `childCount: 0`, `innerHTML === ""` |
Semantics: an aria-live toast container. Toasts are injected as children and
announced politely. `Alt+T` presumably moves focus to it (unverified —
shortcut named in the label).
**Visibility condition:** always in the DOM (even when empty). Presence of
children = a toast is/was showing.
**Automation note:** to detect a toast, poll
`document.querySelector('section[aria-label="Notifications alt+T"]').childElementCount > 0`
or read `innerText`.
---
## 2. Activity tab button
```js
document.querySelector('button[aria-label="Activity"]')
```
| Property | Value |
|---|---|
| Tag | `<button type="button">` |
| `aria-label` | `Activity` (stable — safe to match on) |
| `aria-pressed` | `"true"` / `"false"` — reflects selected tab |
| `data-state` | `"closed"` (tooltip/popover state, not panel state) |
| `data-slot` | `tooltip-trigger` |
| `data-testid` | none |
| Children | single `<span aria-hidden="true">` wrapping an SVG icon (no text) |
Parent chain (bottom-up, 8 levels, coords at 780×493 viewport):
```
div @(420,0)
div[data-testid="hatch-status-panel-sliding-surface"] @(420,0)
div @(420,0)
div @(421,0)
div @(421,274)
div @(437,274)
div @(437,274)
button[aria-label="Activity"] @(441,278)
```
The button lives in the **status-panel toolbar**, not the dock rail. It is a
tab toggle: clicking selects the Activity feed inside the status panel.
### 2a. Sibling tabs (same toolbar)
All icon-only `<button>`s, same structure (span + SVG, no text):
| aria-label | Observed state |
|---|---|
| `Activity` | `aria-pressed="true"` (selected during inspection) |
| `Approvals` | `aria-pressed="false"` |
| `Upcoming` | present (state not sampled) |
| `Identity` | present (state not sampled) |
Selector pattern for any tab:
```js
document.querySelector('button[aria-label="Approvals"]') // etc.
```
**Automation note:** no badge/count was observed on any tab (nothing pending
at inspection time). If counts ever appear they would likely be SVG-adjacent
spans or aria-label suffixes — re-probe when approvals are pending.
---
## 3. Status panel (sliding surface)
```js
document.querySelector('[data-testid="hatch-status-panel-sliding-surface"]')
```
| Property | Value |
|---|---|
| Geometry | 360px wide, full height, at x≈420 (right of the chat column) |
| Visibility | always in DOM; `visible: true` when the panel is open |
Internal structure:
```
[data-testid="hatch-status-panel-sliding-surface"]
├── div
│ ├── div[data-testid="hatch-status-panel-close-drag-handle"]
│ └── span[data-testid="hatch-status-panel-close-drag-handle-divider-line"]
├── div ← tab content ("operator-main", "Today" when Activity selected)
│ ├── div[data-testid="hatch-status-panel-close-toolbar"]
│ │ └── span
│ │ └── button[data-testid="hatch-status-panel-close"][aria-label="Close panel"]
```
Close button:
```js
document.querySelector('button[data-testid="hatch-status-panel-close"]')
// aria-label="Close panel"
```
**Automation note:** the tab toolbar (Activity/Approvals/Upcoming/Identity)
sits between the drag handle and the content area. Tab selection is readable
via `aria-pressed`.
---
## 4. Chat nav unread indicator
The dock-rail chat link doubles as the unread-chats indicator:
```js
document.querySelector('[data-testid="hatch-nav-chat"]')
```
| Property | Value |
|---|---|
| Tag | `<a href="/">` (a link, not a button) |
| `data-testid` | `hatch-nav-chat` (stable — **match on this, not aria-label**) |
| `aria-label` | **unstable**: `"Chat"` when clean, `"Chat, 1 notification"` (etc.) when chats are unread |
| `aria-keyshortcuts` | `Control+J` |
| `aria-current` | `"page"` when the chat view is active |
| `data-pel-click` | `chat_tab_click` |
| `data-hatch-shell-navigation` | `"true"` |
Dock rail context (`[data-testid="hatch-dock-rail"]` children):
```
a[data-testid="hatch-nav-chat"][aria-label="Chat"] ← unread shows here
button[data-testid="hatch-nav-search"][aria-label="Search"]
button[aria-label="Get the Muse app"]
span[data-testid="hatch-dock-rail-divider"]
button[data-testid="hatch-dock-more"][aria-label="Settings"]
```
**Visibility condition:** always in the DOM on chat pages (also present in the
stripped `/thread/new` state — it is the recovery click target there).
**Automation note — detecting unread chats:**
```js
const label = document.querySelector('[data-testid="hatch-nav-chat"]')
.getAttribute('aria-label');
const unread = /notification/.test(label); // "Chat, N notification(s)"
```
Do not match the element itself by aria-label — the count suffix changes.
---
## 5. What does NOT exist
Verified absent (three DOM sweeps, 2026-10-04):
- No bell icon element.
- No `data-testid` containing `notif` (full inventory checked).
- No badge / dot / pill / count / unread elements anywhere in the DOM
(regex sweep over testids, class names, and aria-labels — zero hits).
- No notification dropdown, menu, or dialog container.
- No `[data-radix-portal]` / portal roots hosting hidden dropdowns.
- Clicking the Activity tab revealed no additional notification list UI
(the status panel was already open on the Activity feed).
Unread state is therefore conveyed **only** through:
1. the `hatch-nav-chat` aria-label suffix, and
2. (presumably) unread styling on individual `[data-testid="hatch-thread-row"]`
rows in the chat panel — see `DOM-CHAT-PANEL.md`. The panel was closed
during this inspection so row-level unread markers were not sampled;
re-probe with the panel open if row-level detection is needed.
---
## 6. Automation cheat sheet
```js
// Is there a toast showing?
document.querySelector('section[aria-label="Notifications alt+T"]')
.childElementCount > 0;
// Any unread chats?
/notification/.test(
document.querySelector('[data-testid="hatch-nav-chat"]').getAttribute('aria-label'));
// Open the Activity feed (select tab):
document.querySelector('button[aria-label="Activity"]').click();
// Which status tab is selected?
document.querySelector('button[aria-label="Activity"]').getAttribute('aria-pressed'); // "true"/"false"
// Close the status panel:
document.querySelector('button[data-testid="hatch-status-panel-close"]').click();
// Focus the toast region (keyboard):
// Alt+T (named in the section's aria-label; behavior unverified)
```
## Open questions
- Toast content structure (no toast was firing during inspection).
- Whether tab buttons ever carry counts/badges when items are pending
(Approvals tab is the likely candidate — re-probe with a pending approval).
- Row-level unread markers inside `hatch-thread-row` (needs panel-open probe).