Harden agent guardrails and fix audited bugs

Gate hardening:
- strict + noUncheckedIndexedAccess in tsconfig.app.json; fix resulting errors
- check-tokens.mjs: scan inline style={{ }} in ts/tsx, ban named colors and
  rgb()/hsl()/oklch()/lab(), read multi-line values, fail on undefined var(--x)
- vitest (24 tests: overlay, icon-registry, useTimeout, token guard) wired into
  npm run check; GitHub Actions CI workflow

Single-source refactors:
- new Overlay primitive owns focus trap/Escape/scroll-lock/focus-restore and
  conditional aria-labelledby/aria-describedby; Dialog + Sheet are thin skins
  (overlayBehavior.ts exports trapTabKey/useOverlayLifecycle for SearchPalette)
- icon-registry.ts is the one icon map: strict IconName, aliases typed, dev
  throws on unknown names instead of silently rendering <Layers/>; IconMatrix
  consumes the shared registry (41 curated)
- useTimeout hook replaces fire-and-forget setTimeout in DeploymentModal,
  SettingsPanel, IconMatrix, CodeSample (timers die on close/unmount)

Bug fixes:
- SearchPalette: role=dialog/aria-modal, focus trap + restore, combobox/listbox
  semantics, labelled input, selection clamped to results
- theme: pre-paint script in index.html (localStorage -> prefers-color-scheme ->
  dark), useTheme no longer writes DOM during state init
- StatCard: direction (arrow) decoupled from trend (sentiment); latency drop
  renders green "↓ -4.2ms" instead of "↑ -4.2ms"
- sidebar collapse button wired (aria-pressed + collapsed rail CSS); pattern
  routing keys on patternId from library.ts, not English labels
- remove 25 dead legacy tokens (--panel*, --line*, --text-soft, --critical,
  --pink, --blue, ...); migrate --bg/--text/--positive/--warning usages
- title "Northstar Design System", real README, AGENTS.md structure/theme/
  overlay/registry/test docs updated; dev error sink (#dev-errors +
  ErrorBoundary) so agents can read runtime errors via inspect
This commit is contained in:
developer committed 2026-09-13 00:22:16 +00:00
1 parent c0a94d3b6d
commit f6e81afe5e
48 files changed
+3299 -696

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
import { act, renderHook } from '@testing-library/react'
import { vi } from 'vitest'
import { useTimeout } from '../src/utils/useTimeout'
describe('useTimeout', () => {
it('fires the callback after the delay', () => {
vi.useFakeTimers()
const { result } = renderHook(() => useTimeout())
const fn = vi.fn()
act(() => {
result.current.schedule(fn, 1000)
})
expect(fn).not.toHaveBeenCalled()
act(() => {
vi.advanceTimersByTime(1000)
})
expect(fn).toHaveBeenCalledTimes(1)
vi.useRealTimers()
})
it('clear() cancels pending timers', () => {
vi.useFakeTimers()
const { result } = renderHook(() => useTimeout())
const fn = vi.fn()
act(() => {
result.current.schedule(fn, 1000)
result.current.clear()
})
act(() => {
vi.advanceTimersByTime(5000)
})
expect(fn).not.toHaveBeenCalled()
vi.useRealTimers()
})
it('clears pending timers on unmount (no setState-after-unmount leaks)', () => {
vi.useFakeTimers()
const { result, unmount } = renderHook(() => useTimeout())
const fn = vi.fn()
act(() => {
result.current.schedule(fn, 1000)
})
unmount()
act(() => {
vi.advanceTimersByTime(5000)
})
expect(fn).not.toHaveBeenCalled()
vi.useRealTimers()
})
})