anthropics / anthropics/claude-code

[BUG] Claude Code Desktop (Windows): slash command menu in the Code tab composer is silent for screen readers (NVDA)

Aperta
#94,353 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:a11y area:desktop bug platform:windows
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code

### What's Wrong?

In Claude Code Desktop (the Code tab of Claude Desktop 1.52386.6.0 on Windows 11, Claude Code 2.1.270), the slash command menu that opens when typing `/` in the prompt composer (for example `/rename`) is completely silent for the NVDA screen reader. NVDA announces nothing when the menu opens, nothing while arrowing through it, and not which command is highlighted. Because ArrowUp/Down, Tab and Enter are taken over by the menu, a blind user cannot tell what Enter or Tab will do, so entering a command such as `/rename New title` becomes guesswork.

### What Should Happen?

The composer exposes the menu as an autocomplete popup (ARIA 1.2 combobox pattern): NVDA announces that suggestions are available, reads the highlighted command and its description as the arrow keys move, and Enter/Tab behave predictably (WCAG 4.1.2 Name, Role, Value; 1.3.1 Info and Relationships; 4.1.3 Status Messages). The `/resume` and `/rewind` pickers in the same app already do this correctly.

### Error Messages/Logs

```shell
None; this is an accessibility-tree problem.
```

### Steps to Reproduce

1. On Windows 11, start NVDA and open Claude Desktop → Code tab, with a Claude Code session.
2. Focus the prompt composer and type `/ren`.
3. Press ArrowDown/ArrowUp a few times, then press Enter or Tab.
4. Observe: NVDA says nothing at any step; the menu is only visible on screen.

### Claude Model

Opus

### Is this a regression?

I don't know

### Last Working Version

_No response_

### Claude Code Version

2.1.270 (Claude Code), bundled with Claude Desktop 1.52386.6.0

### Platform

Anthropic API

### Operating System

Windows

### Terminal/Shell

Other (Claude Code Desktop, Code tab)

### Additional Information

#### Root cause (from the shipped bundle `resources/ion-dist/assets/v1/`, minified names)

1. **Focus stays in the editor, and the editor does not reference the menu.** The menu is a TipTap suggestion plugin (`slash-command-suggestion`, `char:"/"`) in `shared-13-D4O01Gj8.js`. On mount the menu calls `i.view.dom.focus()`, so DOM focus stays in the ProseMirror contenteditable. That element never gets `role="combobox"`, `aria-expanded`, `aria-controls`/`aria-owns` or `aria-activedescendant`: none of these attributes occur in that file. The popup element is appended straight to `document.body` (`...ownerDocument.body.appendChild(o)`), with no relationship to the editor in the accessibility tree.
2. **The highlighted row is only highlighted visually.** In the lazy chunk `ccc334009-BDcyQYO5.js` (`SlashCommandMenuPopupBody`) each row is `role:"menuitem", tabIndex:-1` with `"data-highlighted": n ? "" : void 0`. Rows have no `id` and no `aria-selected`, and never receive focus. The active row element is tracked (`setActiveRowElement`) only to `scrollIntoView` it. The container in `shared-12-BzDv77xE.js` is a `role:"menu"` div without an `id` or accessible name. Long lists are virtualised (`getVirtualItems()`), so the highlighted row may not be in the DOM until scrolled to.
3. **Navigation keys are swallowed before the screen reader can observe a change.** The menu provider (`OL`, "useSlashCommandMenu", `shared-12-BzDv77xE.js`) adds a capture-phase `keydown` listener on the document (`s.addEventListener("keydown",B,!0)`). For ArrowUp/Down/Left/Right, Tab and Enter it calls `preventDefault()` and `stopImmediatePropagation()` and only updates React state (`activeIndex`). The suggestion plugin's `onKeyDown` in `shared-13` also returns `true` for these keys while items are visible. Neither the caret, the focus nor `aria-activedescendant` changes, so NVDA has nothing to announce.
4. **No live-region fallback.** Nothing announces "menu opened", the result count or the highlighted item. The only live region in this area is the skill argument hint (`data-skill-arg-hint-sr`); the "Type to filter" hint (`$D`) is a purely visual span.
5. **Enter/Tab depend on state the user cannot perceive.** In `OL`'s key handler Tab always activates the highlighted row; Enter either activates it or only closes the menu, depending on whether the user navigated with the arrows and whether the highlighted row matches the typed text (`e.key==="Enter"&&D===0&&!O&&!A&&(...)?r()`). Typed skills are also auto-resolved on a timer once a space follows a unique match (`claudeai.slash_command_menu.auto_resolved`).

#### Suggested fix

- On the ProseMirror contenteditable while the menu is open: `role="combobox"` (or keep `textbox` and add the combobox attributes), `aria-haspopup="listbox"`, `aria-autocomplete="list"`, `aria-expanded` bound to the menu state, and `aria-controls` pointing to the menu's id (TipTap: `editorProps.attributes`, or set them in the suggestion's `onStart`/`onExit`).
- Popup `role="listbox"` with an id and an accessible name ("Commands"); rows `role="option"` with a stable `id` and `aria-selected`; keep `aria-activedescendant` on the editor in sync with `activeIndex` (and the submenu index). The `/resume` picker (`cli-resume-listbox`, `cli-resume-opt-N`) and the `/rewind` list (`rewind-opt-N`) in `c5610fbe3-0RHH64gR.js` already implement this pattern.
- Keep the active option in the DOM whenever it is referenced (virtualised list).
- Add a polite live region with the result count when the menu opens or the filter changes, since `aria-activedescendant` changes on a contenteditable are not always announced by Chromium + NVDA.
- Make Enter predictable: when the user has not navigated and the typed text is a complete command with arguments (`/rename New title`), submit the text as typed.

#### Notes

The root cause was established by reading the shipped bundle; the symptom is reproduced by the reporter with NVDA. No live instrumentation was run. Related report by the same reporter about focus handling in Claude Code Desktop: #94246.

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.