2026-10-04 03:50:57 +00:00
|
|
|
|
# DOM reference: notifications & activity (muse.ai)
|
|
|
|
|
|
|
|
|
|
|
|
Inspected live via CDP on the opm browser (warp-opm netns, port 9440),
|
2026-10-04 04:31:11 +00:00
|
|
|
|
2026-10-04. Re-verified 2026-10-04 across all three fleet nodes
|
|
|
|
|
|
(muse 9410, pip 9420, opm 9440).
|
|
|
|
|
|
Companion docs: `DOM-CHAT-PANEL.md`, `DOM-MESSAGES.md`,
|
2026-10-04 03:50:57 +00:00
|
|
|
|
`DOM-PAGE-STRUCTURE.md`.
|
|
|
|
|
|
|
|
|
|
|
|
## TL;DR
|
|
|
|
|
|
|
|
|
|
|
|
muse.ai has **no bell icon and no notification center/dropdown**. The
|
2026-10-04 04:31:11 +00:00
|
|
|
|
notification surface is four separate things:
|
2026-10-04 03:50:57 +00:00
|
|
|
|
|
|
|
|
|
|
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`
|
2026-10-04 04:31:11 +00:00
|
|
|
|
becomes `"Chat, N notification(s)"` when chats are unread (observed live on
|
|
|
|
|
|
pip: `"Chat, 1 notification"`, later cleared to `"Chat"`).
|
|
|
|
|
|
4. **Unread dot badges** — transient blue-dot `SPAN`s (see §5). **Correction
|
|
|
|
|
|
2026-10-04:** these DO exist; the earlier "no badge elements" claim was
|
|
|
|
|
|
wrong — the first sweep ran on opm while it was clean, and the badges only
|
|
|
|
|
|
render while there is unread activity.
|
2026-10-04 03:50:57 +00:00
|
|
|
|
|
2026-10-04 04:31:11 +00:00
|
|
|
|
There is no bell icon. The one `data-testid` containing `notif` is
|
|
|
|
|
|
`hatch-dock-chat-notification-badge` (see §5).
|
2026-10-04 03:50:57 +00:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 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
|
|
|
|
|
|
|
2026-10-04 04:31:11 +00:00
|
|
|
|
Verified absent (sweeps on all three nodes, 2026-10-04):
|
2026-10-04 03:50:57 +00:00
|
|
|
|
|
2026-10-04 04:31:11 +00:00
|
|
|
|
- No bell icon element (zero `aria-label` matches for /bell/i on every node).
|
2026-10-04 03:50:57 +00:00
|
|
|
|
- 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).
|
|
|
|
|
|
|
2026-10-04 04:31:11 +00:00
|
|
|
|
## 5a. Unread dot badges — CORRECTION 2026-10-04
|
|
|
|
|
|
|
|
|
|
|
|
The earlier claim ("no badge / dot / pill elements anywhere") is **wrong**.
|
|
|
|
|
|
Three badge testids exist; they render only while there is unread activity and
|
|
|
|
|
|
are absent when clean (which is why the first sweep on opm missed them):
|
|
|
|
|
|
|
|
|
|
|
|
| `data-testid` | Observed on | Markup |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `hatch-dock-chat-notification-badge` | pip (while `"Chat, 1 notification"`) | blue dot |
|
|
|
|
|
|
| `hatch-chat-switcher-unread-indicator` | muse | `<span class="bg-fill-blue absolute -end-0.5 size-2 rounded-full">` — 8×8 blue dot, no text, no `aria-label` |
|
|
|
|
|
|
| `hatch-invite-activity-badge` | muse, pip | `<span class="bg-fill-blue absolute -end-0.5 -top-0.5 size-2 rounded-full">` — 8×8 blue dot |
|
|
|
|
|
|
|
|
|
|
|
|
All are empty `SPAN`s (`innerText === ""`, no `aria-label`, no `role`) —
|
|
|
|
|
|
pure CSS dots (`bg-fill-blue`, `rounded-full`, `size-2`). **They are transient**:
|
|
|
|
|
|
pip's `hatch-dock-chat-notification-badge` was present during one sweep and gone
|
|
|
|
|
|
minutes later after the unread cleared. Never match on class names (Tailwind);
|
|
|
|
|
|
match on `data-testid` presence:
|
|
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
|
// Any unread-dot badges currently rendered?
|
|
|
|
|
|
const unreadDots = [
|
|
|
|
|
|
'hatch-dock-chat-notification-badge',
|
|
|
|
|
|
'hatch-chat-switcher-unread-indicator',
|
|
|
|
|
|
'hatch-invite-activity-badge',
|
|
|
|
|
|
].filter(tid => document.querySelector(`[data-testid="${tid}"]`));
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
|
|
|
|
- `hatch-invite-activity-badge` was present on muse and pip even when the chat
|
|
|
|
|
|
nav label was clean (`"Chat"`), so it likely signals invites/activity rather
|
|
|
|
|
|
than chat unread — treat it as its own signal, not as "unread chats".
|
|
|
|
|
|
- `hatch-chat-switcher-unread-indicator` sits on the chat switcher (rect ≈
|
|
|
|
|
|
(110,20), 8×8 on muse) — useful when the panel is open and the nav label
|
|
|
|
|
|
is out of view.
|
|
|
|
|
|
|
|
|
|
|
|
Unread state is therefore conveyed through:
|
|
|
|
|
|
|
|
|
|
|
|
1. the `hatch-nav-chat` aria-label suffix (`"Chat, N notification(s)"`),
|
|
|
|
|
|
2. the transient dot badges above (`data-testid` presence),
|
|
|
|
|
|
3. (presumably) unread styling on individual `[data-testid="hatch-thread-row"]`
|
2026-10-04 03:50:57 +00:00
|
|
|
|
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'));
|
|
|
|
|
|
|
2026-10-04 04:31:11 +00:00
|
|
|
|
// Any unread dots currently rendered? (transient — absent when clean)
|
|
|
|
|
|
['hatch-dock-chat-notification-badge',
|
|
|
|
|
|
'hatch-chat-switcher-unread-indicator',
|
|
|
|
|
|
'hatch-invite-activity-badge'
|
|
|
|
|
|
].filter(tid => document.querySelector(`[data-testid="${tid}"]`));
|
|
|
|
|
|
|
2026-10-04 03:50:57 +00:00
|
|
|
|
// 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).
|
2026-10-04 04:31:11 +00:00
|
|
|
|
- What clears `hatch-dock-chat-notification-badge` vs the aria-label suffix
|
|
|
|
|
|
(observed independently transient on pip/muse — may be separate signals).
|