Dashboard chrome: an icon picker, one segmented shape, a rail without bars

- IconPicker replaces the three icon selects (rail icon, icon-widget rule,
  "Otherwise"): the glyphs in a grid, and a button that clears back to none —
  which a Radix SelectItem could never offer.
- ModePicker/StylePicker drop out in favour of a shared ui/Segmented, the same
  sliding-thumb shape RangePicker and the widget-side control already wear.
- PanelRail draws no scrollbars at all: hiding them also takes back the gutter
  a vertical bar claimed from a column exactly as wide as its buttons, which is
  what pushed a horizontal bar under them.
- The panels dialog can re-pair one screen (POST /panels/{id}/unpair) without
  deleting the panel it hangs on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tULRZJUkZsw7rMJ3h4xvu
This commit is contained in:
2026-08-22 12:16:17 +02:00
co-authored by Claude Opus 5
parent 3674099758
commit 6be6f18dc2
5 changed files with 264 additions and 118 deletions
@@ -68,6 +68,21 @@ export function useSavePanels() {
})
}
/**
* Stop honouring one panel's credential, and keep the panel.
*
* The screen goes back to showing a pairing code while its dashboards and their
* arrangement stay — deleting the panel is what throws all three away.
* Superuser-only on the server.
*/
export function useUnpairPanel(id: string) {
const queryClient = useQueryClient()
return useMutation({
mutationFn: () => PanelsService.unpairPanel({ panelId: id }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: panelKeys.all }),
})
}
/** Every message any flow declares — what a widget can be pointed at. */
export const messageCatalogQueryOptions = () => ({
queryKey: dashboardKeys.messages,