refactor(desktop): migrate integration directory onto the design system

Merged
CH
5d ago
+678-141

D24 of the design-system migration (Desktop stack, base feature/desktop-d23-coding-agent-defaults, #3402).

What

  • New shared SearchField in @luke/ui (per the orchestrator's addendum), used here for the directory's search:
    • search glyph, controlled value/onChange, label required by type (the accessible name)
    • end slot: a typed tally ({ count, at?, more? } → "No results" / "4 results" / "500+ results" / "3 of 12", in a polite live region while a query stands) or a trailing ReactNode
    • shortcut: the summoning chord, shown while empty and unfocused
    • onClear: a built-in "Clear search" button while a query stands, which hands the caret back
    • onEnter / onShiftEnter / onEscape, ignored while an IME composes (isComposing or keyCode 229). An Escape the field answers is stopped from reaching the surface behind it even mid-composition, which is what find-in-tab and this page already did.
    • id, keyShortcuts (aria-keyshortcuts), placeholder, ref. No className/style.
    • Desktop tokens: control height (30px), control radius, control icon, body type; same well, fill, border, inset and nested clear as the old .settings-search-well.
    • Catalog example (search-fields), jsdom tests for typing, Enter/Shift+Enter/Escape, IME, tally, chord and clear.
    • The command palette, find-in-tab, the Settings sidebar search and the file tree still use the bespoke well; they move in follow-ups, so .settings-search-* stays.
  • Settings › Integrations:
    • The page's search is SearchField. Command-F still lands in it, Escape still clears a query and then lets go of the caret, and Settings' own search still hands its query down.
    • Connected and Not connected (and the read-failed card) are SettingsSection instead of the desktop's SettingsGroup.
    • Each row is MenuRow's SettingsRow form, as D20 and D21 did: the provider mark is leading, the name (search words still marked) is the label, and the status line (connected with its tool count toggle, checking, waiting with its ring, expired, refused) is the description. Rename still swaps the label for the name field. The ⋯ menu, right-click, search anchor and the GitHub heading are unchanged.
    • Each row, its tools panel and its refusal share an .integration-row wrapper. That wrapper joins the existing refusal-under-row rules, and the panel's hairline now hangs off it rather than .settings-card.
  • Left for their own rows: the Connect / Reconnect / Reopen / Cancel buttons are still the window's own buttons (D26, connection controls), and the tools panel is untouched (D25). This page imported nothing from @luke/ui/compat.

Selectors

  • Moved: .settings-card > .integration-tools-panel becomes .integration-row > .integration-tools-panel.
  • None removed: .settings-search-well/-input/-shortcut/-clear, .field-well, .settings-group*, .settings-card* and .settings-note all still have other callers. .integrations-filter and .integration-account-input had no rules; the page no longer uses either class.

Visible changes

  • Row names use the label role (13/18, 500). They were <strong> in .settings-copy.
  • Status lines use the caption role (12/16, subtle ink). They were 12px/1.4, tertiary.
  • Section headings are the shared section heading. They were the old 12px/600 group title.
  • Below a 380px row, the row's button and ⋯ drop below the words, per the shared row contract.
  • The search field looks the same (30px, 8px radius, same fill, border, inset and nested clear). It now draws in --luke-font-sans rather than inheriting, which is the same face.

Tests

  • integrations-page.test.ts passes (42 tests) with its locators moved to the shared row (.luke-settings-row, [id$="label"]) and section (.luke-settings-section, h2). That covers search filtering, grouping order, row status lines, Clear search, and Command-F/Escape not reaching the window.
  • New search-field.test.ts (7, jsdom).
  • pnpm check:affected exits 0.

Evidence

Captured on Linux with headless Chrome at 1280×840, animations held still. A throwaway, uncommitted copy of the page's test rendered the real IntegrationsSection in jsdom, with synthetic statuses and tools. The result was placed in the built desktop stylesheet inside the SettingsPage frame. The ⌘ in the chord shows as a missing glyph only because Linux has no font for it.

LightDark
Connected with Linear's tools open, Notion expired, Vercel connected
"deploy": one row kept, opened onto the tool it found, with the clear button
A query that finds nothing

Not verified on a Mac

  • SF Pro rendering of the rows and the ⌘F chord
  • Command-F landing in the field in the real window (covered by the jsdom test only)
  • Japanese/Chinese IME composition in the real field (covered by the jsdom tests only)
  • Connecting, reconnecting and disconnecting against the live service, which only the tests cover

🤖 Generated with Claude Code