Component Library

The reusable building blocks the KAP screens are assembled from — the web/components/ folder structure, the UI kit, and the workspace widget library.

Components, in plain terms

Screens are not built pixel by pixel — they are assembled from components: self-contained, reusable pieces of interface. A button, a dialog box, a data table, a whole chat panel — each is a component, and bigger components are built out of smaller ones, like standard parts on an assembly line. Working this way buys three things: every screen looks and behaves consistently (one Button component means every button in the product is the same button), teams build faster (new screens are mostly assembly), and fixes land everywhere at once (repair the part, and every screen using it is repaired).

KAP keeps its parts in a deliberate hierarchy: a foundation of ~50 generic UI primitives that know nothing about inspections or datasets, a workspace widget library of KAP-specific building blocks, and feature components that compose those parts into the actual product surfaces.

The folder structure

Everything lives under web/components/, organized by how reusable it is:

web/components/
├── ui/                  ← the UI kit: ~50 generic primitives (buttons,
│   │                      dialogs, tables …) — the foundation layer
│   └── index.ts         ← single entry point: import { Button, Card }
│                          from '@/components/ui'
├── workspaces/          ← the workspace shell: context bar, sidebar,
│   │                      assistant panel, help panel, module header
│   └── widgets/         ← the workspace widget library — presentational,
│                          data-free building blocks (see below)
├── chat/                ← the AG-UI chat interface and its parts
│   └── hooks/             (message processing)
├── notes/               ← location-notes UI (dialogs, list/card views,
│                          filter bar, pagination)
├── settings/            ← the settings dialog and its cards (theme,
│                          audio, voice, tool-trace, system)
├── copilot/             ← assistant streaming-response renderer and its
│                          error boundary
├── tour/                ← the guided product-tour provider
├── admin/               ← admin utilities (thumbnail generator)
└── *.tsx                ← ~45 top-level feature components — dataset
                           grids and tables, organization management,
                           markdown/message renderers, headers, image
                           search and upload, error boundaries …

Two related places hold components that are deliberately not here:

  • web/modules/<name>/ — each feature module (gallery, photo-map-3d, gas-heatmap, thermal-pair-viewer, cad-viewer, dataset-onboarding) is self-contained and keeps its own internal ui/, shared/, core/, and services/ folders. Module-internal components stay in the module; see Workspace Modules.
  • web/hooks/ — shared behavior (data fetching, auth tokens, AG-UI client state) lives in hooks, not components; components stay presentational where possible.

The UI kit — components/ui/

The foundation layer is a shadcn/ui component kit. shadcn/ui is not an installed dependency — it is a generator that copies component source into the repo (configured by web/components.json), so KAP owns and can restyle every primitive. Under the hood each primitive pairs Radix UI (accessible behavior — focus handling, keyboard navigation, screen-reader support) with Tailwind CSS (styling) and class-variance-authority for declared variants (a Button knows its default, destructive, outline, secondary, ghost, and link looks).

Everything re-exports through one entry point, so feature code imports from a single place:

import { Button, Card, Input } from '@/components/ui';

The ~50 primitives, by what they do:

Group Components
Forms & input button, input, textarea, label, checkbox, radio-group, select, switch, slider, form, input-otp, calendar, toggle, toggle-group
Layout & structure card, separator, aspect-ratio, resizable, scroll-area, sidebar, table, tabs, accordion, collapsible, carousel
Overlays & menus dialog, alert-dialog, sheet, drawer, popover, hover-card, tooltip, context-menu, dropdown-menu, menubar, command
Navigation breadcrumb, navigation-menu, pagination
Feedback & status alert, badge, progress, skeleton, toast + toaster, sonner
Data display avatar, chart

Alongside the primitives live a few kit-level helpers: use-toast (the toast queue), use-mobile (viewport breakpoint hook), client-only and the hydration-safe-* wrappers (components that must only render in the browser).

Adding a primitive follows the shadcn convention: generate it into components/ui/ (npx shadcn@latest add <name> — components.json routes it to the right folder and aliases), restyle to taste, and add its exports to components/ui/index.ts.


The workspace widget library — components/workspaces/widgets/

One level above the primitives sits a KAP-specific library: presentational building blocks shared by the Data Explorer and Integrity Engineer workspaces (resource browsers, context-chip panels, status badges, section bars, input/display/feedback/navigation widgets, the IDMS push panel). Its defining rule, from its own README:

These components do not fetch data; workspace modules own Supabase and pass props.

That keeps the widgets reusable across workspaces and trivially testable — all state arrives as props. They too have a single entry point:

import { ResourceBrowser, ContextChipsPanel, StatusBadge } from '@/components/workspaces/widgets';

The widget library has its own reference README at web/components/workspaces/widgets/README.md (import paths, variant tokens, examples).


Where does a new component go?

The four placement rules — generic, shared-presentational, feature-specific, module-internal — are stated once, in the knowledge bundle: Component library → Placement decision rules.

What the rules do not say, and this chapter does: why the hierarchy is shaped that way, and how to judge the middle cases.

The ordering runs from most reusable to least, and the reason to respect it is that promotion is cheap while demotion is not. A component that starts in components/ui/ and turns out to encode a KAP assumption has to be untangled from every consumer that adopted it; one that starts in a feature folder and proves generally useful is a move and a re-export. So when in doubt, start less reusable and promote once a second consumer actually appears — the second consumer is the evidence, not the anticipation of one.

The rule that catches most mistakes is the widget library’s: it holds presentational parts only, and they never fetch data. That is what keeps them reusable across workspaces and trivially testable, since every state arrives as props. A component that needs Supabase is a feature component wearing a widget’s clothes, however generic its name sounds.


Last Updated: 2026-08-11 — placement rules moved to the knowledge bundle (D1); this chapter keeps the reasoning.