Repository navigation
ui: fixes and improvements for the panel shell, popup and accessibility #353
Copy link
Copy link
Closed
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Description
Context
Fixes and improvements from a code audit of the overlay, popup and panel shell, panel ux and accessibility, done in October 2026. Roadmap work lives in #340, #341, #342 and #343. Items marked (to confirm) were not reproduced; check them before fixing.
The overlay, popup and panel shell
Problems
Medium
- No retry after a failed first connection:
connectDevframeruns once, and on rejection the panel setsconnectionFailedand asks the user to reload. A dev server that starts slowly or restarts needs a manual reload. Retry with backoff (1, 2, 5, 10 s) whileconnectionFailedis set and add a "Retry now" button. The popup'sfindTargetalso only retries on the next open (app/src/app.ts:913-947,app/src/app.ts:372-375,packages/devtools/src/popup.ts:114-121).
Low
- Popup accepts theme messages from any window:
onThemeMessageonly rejectse.source === window, so any iframe or script on the host page can flip the popup theme, and the value is saved tolocalStorage. Accept the message only fromiframe.contentWindow(or a frame inside it) with a matching origin (packages/devtools/src/popup.ts:827-832). - Theme posted to every ancestor with
'*':ThemeServicewalkswindow.parentand postspangular:theme-changewith target origin'*', which leaks to unknown origins when the panel is embedded cross-origin. Post to the parent's origin (fromdocument.referrerorancestorOrigins), or rely on theBroadcastChannelfor same-origin cases (app/src/theme.service.ts:36-47). - Tab strip is not a tablist: the inspector tabs are buttons with
aria-current="page", with no roving tabindex or arrow keys, so ten tabs are ten tab stops before the content. Userole=tablist/tab/tabpanelwitharia-selectedand roving tabindex, or keep the nav pattern and add arrow-key handling (app/src/app.ts:346-358). - Opening the panel leaves focus on the FAB:
togglePanelonly moves focus on close. When opened from the keyboard (clickwithdetail === 0), focus the iframe or itsmainonce loaded (packages/devtools/src/popup.ts:670-675). - Hub views listed twice:
HUB_VIEWSis defined in both the panel and the package, and thepangular:dock prefix is repeated too. Adding a dock in one place breaksfollowHubDocksand view titles silently. ExportHUB_VIEWSanddockIdfrom one module and import them in the app (app/src/app.ts:43-50,packages/devtools/src/hub-docks.ts:9-16,packages/devtools/src/hub-docks.ts:48,app/src/hub-dock-sync.ts:2,app/src/app.ts:1088). - Overlay and popup probe different bases: the overlay tries
['./', '/__pangular/', '/__devframes/pangular/']and the popup tries['/__pangular/', '/__devframes/pangular/', '/__devframe/', '/'], so one can find a server the other misses. Keep one exported list inpanel-frame.tsand use it in both (packages/devtools/src/overlay.ts:177,packages/devtools/src/popup.ts:61). - Untyped
hub:docks:activatecall: the fallback dock switch casts the RPC clientas unknownand callshub:docks:activate. If the client has no top-levelcall, the panel quietly switches to the fallback tab instead of the dock. Use the typed hub client API and add a test that the hub path activates the dock (app/src/app.ts:1073-1094) (to confirm). - Short page id: the page id is 4 base36 characters from
Math.random, and theBroadcastChannelclaim only catches collisions inside one browser. Pages from other devices (NativeScript, Angular Native, a phone) reporting to one hub can collide. Usecrypto.randomUUID().slice(0, 8)or longer, staying under the server's 50-character limit (packages/devtools/src/overlay.ts:96,packages/devtools/src/devframe.ts:272).
Improvements
- Move
limits,redaction,refreshMsand the action toggles from config-file-only toctx.settings, with a Settings view in the panel (packages/devtools/src/config.ts,packages/devtools/src/devframe.ts,app/src/app.ts). - Export a programmatic
buildReport()next to the CLIbuild, so CI and Analog/Vite builds can produce the static report without the CLI wrapper (packages/devtools/src/cli.ts,packages/devtools/package.json). - Remember scroll position and selection per tab, not only the tab id, in
sessionStoragekeyed bypageId, so a popup or extension reload returns to the same item (app/src/tab-memory.ts,app/src/app.ts). - A minimized "pill" launcher that shows the render count and error count on the FAB (
packages/devtools/src/popup.ts).
Already tracked elsewhere
- Command palette, Cmd/Ctrl+K and a global popup shortcut: phase 3 of 4: a shell you live in #342
- Splitter for the docked popup (fixed
40vh/40vwtoday) and pop-out window: phase 3 of 4: a shell you live in #342 - Theme toggle in the panel (settings page): phase 3 of 4: a shell you live in #342
- Deep links to an item (
#tab=&id=&page=) and anopen-in-panelagent tool: phase 3 of 4: a shell you live in #342 - Cross-inspector search from the palette: phase 3 of 4: a shell you live in #342
- Terminals dock (including the Analog server log): phase 3 of 4: a shell you live in #342
- Delta pushes instead of polling for heavy timelines: phase 3 of 4: a shell you live in #342
- Hub messages for agent writes and NG errors, and dock badges: phase 1 of 4: safe by default and wired into your workflow #340
- Component-picker shortcut and open in editor: phase 1 of 4: safe by default and wired into your workflow #340
Panel UX and accessibility
Problems
Medium
- Error states without Retry: several error states tell the user to reload or reopen the panel and offer no button. Add a Retry button that calls the existing
refresh()or load function, aspipes-inspector.ts:188-192already does. In the shell, retry the devframe connection with backoff and show a "Reconnect" button (app/src/pages/network-inspector.ts:296-303,app/src/pages/dashboard.ts:234-241,app/src/app.ts:372-375). - Store has no loading state: before the first shared state arrives, the store page shows "No page is reporting NgRx state yet.", so NgRx users see a wrong message for a moment. Set a
loadedflag on the first shared state value and show arole="status""Loading…" state until then (app/src/pages/store-inspector.ts:130-143). - No arrow-key navigation in long lists: the network timeline (up to 2,000 rows), the store change and event lists and the signals node list need one Tab per row. Add roving tabindex with ArrowUp/Down and Home/End, like
onListKeyinpipes-inspector.ts:1341. A sharedappRovingListdirective would cover all of them (app/src/pages/network-inspector.ts:595-616,app/src/pages/store-inspector.ts:362,499).
Low
- Search fields differ from page to page: the guidelines ask for a search field with an icon, Escape to clear and a count. Only six pages have the search icon. It is missing on
signal-inspector.ts:159,store-inspector.ts:100,forms-inspector.ts:358,route-tree.ts:115,route-timeline.ts:37androute-inspector.ts:55. Placeholders mix "Filter" and "Find", and only some end with "…". Extract an<app-search-field>inapp/src/uiwith the icon, Escape,autocomplete="off",spellcheck="false"and an optional count, and pick one verb. - Route tree has no tree semantics: depth is shown only by
padding-left, with noaria-level,aria-expandedor treegrid role, so screen readers lose the hierarchy. Userole="treegrid"witharia-levelandaria-expanded, and support Left/Right to collapse and expand (app/src/pages/route-tree.ts:176-177). - Filtered lists tracked by
$index: when the filter changes, rows are reused by position, so focus and selection jump. Track by a stable key such asroute.file + route.fullPath, a finding's code and location, or a component id (app/src/pages/route-inspector.ts:118,route-current.ts:95,167,route-lint.ts:65,forms-report.ts:162,cd-recording.ts:89). - Hex colors in the Analog view: the info and kind colors are hard-coded hex values outside the token system and the tint-contrast test. Move them to
app/src/styles/_palette.scssorapp/src/view-accents.tswith dark and light pairs, and add them toapp/src/__tests__/tint-contrast.test.ts(app/src/pages/analog-inspector.ts:1133-1138,1693-1725). - "Inspector turned off" gives no next step: the message names the
inspectorskey but has no docs link and no snippet. Add a link (asLimitNotedoes withLIMITS_DOCS_URL) and the exact option to turn it on, for exampleinspectors: { store: true }(app/src/app.ts:380-384). - No shortcut to focus search or switch tabs: there is no
/to focus the current page's search and no key to switch tabs. Add ahostlistener inapp.tsfor/(expose each page's search through a small service) and Alt+1..9 for tabs, skipped while focus is in an input. The Cmd/Ctrl+K palette is tracked in phase 3 of 4: a shell you live in #342 (app/src/app.ts:149-153).
Improvements
- One shared
<app-state kind="loading|error|empty|no-match">component with Retry and "Clear filters" slots, plus a spec that checks every page renders all four states (app/src/ui,app/src/pages/*.ts). - A spec that lists the pages and checks each one uses
<app-search-field>and the roving-list directive (app/src/ui,app/src/pages/*.ts). - Resizable list and detail panes with a keyboard-accessible splitter (
role="separator",aria-valuenow, arrow keys), width saved per view (app/src/pages/network-inspector.ts,store-inspector.ts,di-inspector.ts,app/src/tab-memory.ts). - "Pause live updates" and "Auto-scroll to newest" toggles on the network timeline, store change log and route timeline (
app/src/pages/network-inspector.ts,store-inspector.ts,route-timeline.ts). - Sortable column headers with
aria-sorton the network table (time, status, duration) and the route config table (app/src/pages/network-inspector.ts:580-592,app/src/pages/route-inspector.ts). - Saved filter chips per view, for example "Failed only" and "SSR" on the network page, stored in
localStorageinside try/catch (app/src/pages/network-inspector.ts,app/src/tab-memory.ts). - Run
pnpm test:axeon the loading, error and no-match states too, not only the populated static report.
Already tracked elsewhere
- DI and Signals pages show an empty state when loading fails: phase 1 of 4: safe by default and wired into your workflow #340.
- No virtualization of long lists and trees (network table, component tree, store log): phase 3 of 4: a shell you live in #342.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Activity
fix: audit findings for the panel shell, hub, extension and native ov…