@neuronection/assistant-ui
v0.28.1
Published
Shared React component library for the Assistant family (career-, study-, health-assistant). Family look by default, app-flavored via CSS-variable tokens.
Readme
assistant-ui
Shared React component library for the Assistant family — three sibling apps that AI-help people with career, study, and health. One package, three apps: family look by default, app flavor via CSS-variable design tokens. Works in React 18 & 19 and alongside Tailwind 3 or 4 — the library ships precompiled CSS, so your app's Tailwind never compiles library classes.
neuronection.com — the family hub.
| App | What it is | |---|---| | career-assistant | Job discovery, AI matching and university pathways for students | | study-assistant | Study workspace: notes, materials, chat with math & code rendering | | health-assistant | Universal health data platform — self-hosted, privacy-first, open source |
The family shares its UI DNA here: 40+ modules, 260+ tests (keyboard-nav
- axe per component), built on Radix, token-themed so every app looks like a sibling without sharing a stack. Patterns the apps actually use — provider & model settings blocks, AI affordances (ask/fill/act), date & clock pickers, async comboboxes, file attachment surfaces — live in the package, not copy-pasted in three repos.
Install
pnpm add @neuronection/assistant-uiThen do exactly two things in your app entry:
import '@neuronection/assistant-ui/styles.css'
import './theme.css' // your app's token overrides, loaded afterimport { Button, ConfirmationModal, Input } from '@neuronection/assistant-ui'Theming
All visuals flow through --as-* CSS variables. The defaults are the family
look (extracted from study-assistant); each app overrides identity tokens in
a small theme.css:
:root {
--as-primary: oklch(0.62 0.15 152);
--as-radius: 0.75rem;
}Dark mode is the same story: remap the tokens under .dark (health-assistant
ships a class-based dark theme this way) — no component rewrites. Overlay
stacking is token-driven too (--as-z-modal, --as-z-popover) so library
dialogs clear your app chrome. Full guide: docs/theming.md.
Components
Every module has its own entry point (import { Menu } from
'@neuronection/assistant-ui/menu') and everything is re-exported from the
package root. Each module links a reference page under
docs/components/ below; the .d.ts files in the
package's dist/ stay the authoritative API for your installed version.
Visual reference: gallery.
| Module | Exports | Gallery |
|---|---|---|
| button / badge / card | Button, buttonVariants · Badge, badgeVariants · Card, CardHeader/Title/Description/Content/Footer | stories |
| modal (Modal + parts, PanelModal) | Modal + parts (compound Radix dialog) · PanelModal (header/body/footer shell, full-screen on mobile) | stories |
| confirmation-modal / form-modal | ConfirmationModal · FormModal (form-in-modal shell: headerActions, reject button, busy states) | stories |
| popover / popover-button | compound Radix Popover + parts · PopoverButton (self-contained trigger + panel, hover-open, lazy children, closeSignal) | stories |
| menu / context-menu | Menu, MenuTrigger/Content/Item/Separator/Label, ActionMenu (items-driven) · ContextMenu (coordinate-anchored, {x, y, items, onClose}) | stories |
| combobox | Combobox, ComboboxMulti — async mode (onSearchChange + loading), grouping, capability badges, full keyboard nav | stories |
| tooltip | Tooltip + parts, InfoTooltip | stories |
| wizard | Wizard (steps config + renderStep, per-step validation gates, modal/drawer variants), Stepper (dots/labels) | stories |
| input / textarea / search-input / expandable-search | Input (label/hint/error wiring) · Textarea · SearchInput · ExpandableSearch | stories |
| rich-text-editor | RichTextEditor — controlled tiptap markdown editor: full-override extensions, configurable toolbar groups + per-level headings + toolbarExtra slot, onReady/contentClassName composition props, label/icon props, caret-preserving external sync | stories |
| dictation / ai-text-transform | useDictation + DictationButton/DictationStrip (transport-injected STT) · useAiTextTransform (streaming transform state machine) | stories |
| text-diff-view | TextDiffView — side-by-side diff with word-level highlights, line numbers, change navigation, folds, virtualization (+ exported computeLineDiff/wordSegments) | stories |
| chip-input / chip-list | ChipInput (Enter/comma commit, paste-splits, Backspace-removes) · ChipList (variant pills, clickable, removable) | stories |
| time-picker / time-list | TimePicker (clock face, 24h value, 12h UI, editable fields) · TimeList (time chips with per-chip pickers) | stories |
| date-picker | DatePicker (popover calendar: days/months/years views, min/max, allowClear, arrow-key day grid, unstyled variant) | stories |
| range-bar / scale-slider | RangeBar (low–high band + value dot) · ScaleSlider (+ scaleColorForValue) | stories |
| table | Table (headers/rows, opt-in empty state) | stories |
| check-indicator / selection-bar / view-toggle | CheckIndicator (tri-state) · SelectionBar (bulk-select bar) · ViewToggle (grid/list) | stories |
| error-banner / undo-notice / empty-state / spinner | presentational feedback: alert banner with action slot · undo toast · EmptyState · Spinner | stories |
| info-button / field-label / copy-button / breadcrumbs | InfoButton · FieldLabel · CopyButton (clipboard + copied state) · Breadcrumbs (SPA links via linkComponent) | stories |
| ai-button / ai-actions-dropdown / ai-magic-fill | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal — API calls stay app-side | stories |
| chat-core | useChatStream (transport-injected live-turn state machine), liveTurnReducer, ChatMessageView, family ChatStreamEvent vocabulary, branch-tree utils (buildBranchTree, walkActivePath, variantInfo) | stories |
| chat-markdown | MarkdownSurface (read-only streaming markdown: GFM + KaTeX + lazy mermaid + copyable code blocks; no raw HTML) · MarkdownCodeBlock · MermaidDiagram | stories |
| chat-message / chat-reasoning / chat-tool-card / chat-turn-status | ChatMessage (bubble: actions row, variant switcher, edit-and-resend, error/interrupted states) · ChatReasoning (collapsible thinking block) · ChatToolCard (tool observation: status, duration, args/result, app-provided result views via renderResult) · ChatTurnStatus (live pre-text turn status: dots, phase label, elapsed timer) | stories |
| chat-composer | ChatComposer — auto-growing IME-safe input with send/stop states, toolbar/attachment/suggestion slots, drag-drop + paste wiring | stories |
| chat-transcript / chat-branch-tree / chat-session-list | ChatTranscript (log semantics, polite announcements, stick-to-bottom + jump pill, optional virtualization) · ChatBranchTree (OpenWebUI-style branch rail, keyboard tree nav) · ChatSessionList (fuzzy search, date groups, row actions) | stories |
| chat-panel / chat-drawer / chat-launcher | the three chat surfaces from one assembly — ChatPanel host (page | sidebar | bubble) · ChatDrawer (resizable sidepanel on Radix Dialog, keyboard resize) · ChatLauncher (floating bubble → anchored panel, badge) | stories |
| chat-tools-catalog | ChatToolsCatalog — searchable disclosure catalog of the tools an assistant can use (name, title, scope, arguments, example, response); fetching stays app-side | stories |
| chat-export | buildChatMarkdown / chatExportFileName / downloadChatMarkdown — conversation → Markdown export with role-label styles and app-supplied blockquote annotations | stories |
| chat-history-button | ChatHistoryButton — labelled history popover for a chat header (open-refresh hook, close-on-pick); the session list stays app-side | stories |
| chat-trace-meta | ChatTraceMeta — compact model · duration · N tools trace badges; renders nothing without data | stories |
| chat-trace-timeline | ChatTraceTimeline — collapsible per-turn trace: duration-proportional phase/tool bars, tokens, model, reasoning disclosure | stories |
| flow-status | FlowStatusCard (multi-step AI-flow progress from the family event vocabulary: per-node status, current-step emphasis, progress summary, error + retryable, controlled retry/cancel/resume, detail slot for HITL cards) | stories |
| settings-shell / provider-form / connection-test-row | SettingsShell (controlled two-pane nav) · ProviderForm (write-only API-key field) · ConnectionTestRow (idle/testing/ok/fail + latency, inline variant + meta slot) | stories |
| sidebar-nav | SidebarNav (family-standard vertical nav: groups + sections, badges, controlled active id, pinned secondary items, optional collapsed icon rail with hover flyouts, header/footer slots) | stories |
| user-menu | UserMenu (avatar/identity trigger + action menu incl. checkable items; composed over Menu) | stories |
| model-picker / task-assignment-picker / capability-chips | ModelPicker (grouped provider→model combobox, capability chips) · TaskAssignmentPicker (task → model mapping rows, capability-filtered, sections + fallback assignment, meta slot) · CapabilityChips (cap toggle group / badges, min-selected) | stories |
| model-registry | ModelRegistry (provider cards → registered model rows; add/edit in one catalog modal fed by app-fetched remote models; manual id entry; add-all of pending ids) | stories |
| upload-dropzone / file-card / file-queue | file attachment surface: dropzone (block/row) · card (status, include toggle) · queue (summary + reorder) | stories |
| marquee | useMarquee, MarqueeSurface, MarqueeBand (rubber-band selection) | stories |
| portal / theme-scope / tokens | Portal · ThemeScope · token name lists + types | stories |
| logo / about | NeuronectionMark, NeuronectionWordmark, Career/Study/HealthMark · AboutPanel + about-page building blocks (AboutCard, AboutLinkList, FamilyBadge, SponsorCard, …) | stories |
API rules: controlled-first, className merges (never replaces), asChild
where it makes sense, refs forwarded everywhere, English label props with
defaults (translate at call sites), icons as props. No fetching, no stores,
no router — data in, events out.
Documentation
Guides (docs/guides/):
- Import & theming — import paths
(subpath vs barrel vs app shims), CSS load order,
--as-*tokens,data-as-*styling hooks, Vite setup checklist - Utilities —
searchScore/fuzzyScoreranking model,cn, the tokens entry point - AI settings surfaces — the providers →
models → tasks recipe (
ProviderForm,ConnectionTestRow,ModelRegistry,TaskAssignmentPicker) with real app call-sites - Chat surfaces — the bubble/sidepanel/ page assembly from the chat modules: transport adapters, the live tail, branching (versioning) contract, composer slots
Per-component reference (docs/components/<module>.md): purpose, import
line, props table, controlled contract, i18n contract, snippets,
accessibility notes — every module in the table above links its page.
- Adopting in an app — the install/switchover recipe each family app follows (incl. the TW3 cascade notes)
- Theming — tokens, app themes, dark mode, styling hooks
- Accessibility — the keyboard + ARIA contract per module (test-asserted)
- Adding a component — rules, checklist, required companions
- Local development — live-test library changes in the family apps without publishing
- Publishing — the Changesets release flow and CI setup
Development
pnpm install
pnpm dev # Ladle playground on :61000 (CSS watches alongside)
pnpm test --watch
pnpm verify # lint + typecheck + test + build
pnpm test:visual # Playwright screenshots of every story vs committed baselinesReleases happen via Changesets — CI publishes, never a laptop.
License
Apache-2.0 — same as the family apps this library was extracted from.
