{"id":51510297,"url":"https://github.com/man4ish/omnibioai-ui","last_synced_at":"2026-07-08T05:01:58.590Z","repository":{"id":362115908,"uuid":"1246445655","full_name":"man4ish/omnibioai-ui","owner":"man4ish","description":"Shared React component library for OmniBioAI — Button, Badge, Card, Input, StatusDot, and Spinner built on @man4ish/design-tokens. TypeScript-first, tree-shakeable ES+CJS build via Vite, published to GitHub Packages. Zero hardcoded colors; all styling delegated to the token layer.","archived":false,"fork":false,"pushed_at":"2026-06-13T23:29:57.000Z","size":138,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T01:14:57.952Z","etag":null,"topics":["bioinformatics","component-library","css","design-system","design-tokens","github-packages","npm-package","omnibioai","react","typescript","vite"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/man4ish.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-22T07:44:27.000Z","updated_at":"2026-06-13T23:26:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/man4ish/omnibioai-ui","commit_stats":null,"previous_names":["man4ish/omnibioai-ui"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/man4ish/omnibioai-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/man4ish%2Fomnibioai-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/man4ish%2Fomnibioai-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/man4ish%2Fomnibioai-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/man4ish%2Fomnibioai-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/man4ish","download_url":"https://codeload.github.com/man4ish/omnibioai-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/man4ish%2Fomnibioai-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35252324,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bioinformatics","component-library","css","design-system","design-tokens","github-packages","npm-package","omnibioai","react","typescript","vite"],"created_at":"2026-07-08T05:01:57.778Z","updated_at":"2026-07-08T05:01:58.581Z","avatar_url":"https://github.com/man4ish.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @man4ish/ui\n\nShared React component library for the [OmniBioAI](https://github.com/man4ish/omnibioai-studio) platform. Provides `Button`, `Badge`, `Card`, `Input`, `StatusDot`, `Spinner`, `Table`, `Tabs`, `ProgressBar`, `Tooltip`, and `Select` components built on unified design tokens with zero hardcoded colors.\n\n---\n\n## Overview\n\n- **TypeScript-first** — full type definitions shipped with the package\n- **Design-token driven** — all colors, spacing, and radii come from `@man4ish/design-tokens`; no hardcoded hex values anywhere in the library\n- **Tree-shakeable** — built as ES module + CJS with Vite library mode; only import what you use\n- **React 18+ peer dep** — works with any React 18+ project including the OmniBioAI Electron app and any downstream consumer\n- **Fully tested** — 50+ unit tests with Vitest + React Testing Library\n- **Storybook catalogue** — visual stories for every component and variant\n- **Type-safe exports** — full `.d.ts` declarations via vite-plugin-dts\n- **CI token lint** — GitHub Actions workflow blocks hardcoded hex values in CSS and TSX on every push and pull request\n\n---\n\n## Installation\n\nThis package is published to the GitHub Packages registry under `@man4ish`.\n\n### 1. Authenticate with GitHub Packages\n\nCreate or update `.npmrc` in your project root:\n\n```\n@man4ish:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN\n```\n\nYour token needs `read:packages` scope. Generate one at GitHub → Settings → Developer settings → Personal access tokens.\n\n### 2. Install the package\n\n```bash\nnpm install @man4ish/ui @man4ish/design-tokens\n```\n\n`@man4ish/design-tokens` is a required peer — it provides the CSS custom properties that all components reference.\n\n### 3. Load design tokens\n\nIn your app entry point, import the token stylesheet once:\n\n```ts\nimport '@man4ish/design-tokens/dist/tokens.css';\n```\n\nAll components will pick up the correct colors, spacing, and radii automatically from there.\n\n---\n\n## Components\n\n### `Button`\n\n```tsx\nimport { Button } from '@man4ish/ui';\n\n\u003cButton variant=\"primary\" onClick={handleRun}\u003e\n  Run analysis\n\u003c/Button\u003e\n\n\u003cButton variant=\"secondary\" size=\"sm\" disabled\u003e\n  Cancel\n\u003c/Button\u003e\n\n\u003cButton variant=\"danger\" loading\u003e\n  Deleting...\n\u003c/Button\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `variant` | `'primary' \\| 'secondary' \\| 'ghost' \\| 'danger'` | `'primary'` | Visual style |\n| `size` | `'sm' \\| 'md' \\| 'lg'` | `'md'` | Button size |\n| `loading` | `boolean` | `false` | Shows spinner, disables interaction |\n| `disabled` | `boolean` | `false` | Disables button |\n| `onClick` | `() =\u003e void` | — | Click handler |\n\n\u003e **Note:** When `loading` is `true`, a `Spinner` is shown and the button is automatically disabled.\n\n---\n\n### `Badge`\n\n```tsx\nimport { Badge } from '@man4ish/ui';\n\n\u003cBadge variant=\"success\"\u003eRunning\u003c/Badge\u003e\n\u003cBadge variant=\"warning\"\u003eQueued\u003c/Badge\u003e\n\u003cBadge variant=\"danger\"\u003eFailed\u003c/Badge\u003e\n\u003cBadge variant=\"info\"\u003ev1.0.0\u003c/Badge\u003e\n\u003cBadge variant=\"neutral\"\u003eDraft\u003c/Badge\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `variant` | `'success' \\| 'warning' \\| 'danger' \\| 'info' \\| 'neutral' \\| 'default'` | `'neutral'` | Semantic color |\n\n\u003e **Note:** `'default'` is a legacy alias for `'neutral'` and maps to the same visual style.\n\n---\n\n### `Card`\n\n```tsx\nimport { Card } from '@man4ish/ui';\n\n\u003cCard\u003e\n  \u003ch3\u003ePlugin output\u003c/h3\u003e\n  \u003cp\u003eRun completed in 4.2s\u003c/p\u003e\n\u003c/Card\u003e\n\n\u003cCard elevated onClick={handleClick}\u003e\n  Clickable card\n\u003c/Card\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `elevated` | `boolean` | `false` | Adds shadow for visual prominence |\n| `onClick` | `() =\u003e void` | — | Makes card interactive |\n| `className` | `string` | — | Additional CSS classes |\n\n---\n\n### `Input`\n\n```tsx\nimport { Input } from '@man4ish/ui';\n\n\u003cInput\n  label=\"Sample ID\"\n  placeholder=\"Enter sample identifier\"\n  value={sampleId}\n  onChange={e =\u003e setSampleId(e.target.value)}\n/\u003e\n\n\u003cInput\n  label=\"VCF path\"\n  error=\"File not found\"\n  value={vcfPath}\n  onChange={e =\u003e setVcfPath(e.target.value)}\n/\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `label` | `string` | — | Label above the input |\n| `error` | `string` | — | Error message below; turns border red |\n| `placeholder` | `string` | — | Placeholder text |\n| `value` | `string` | — | Controlled value |\n| `onChange` | `ChangeEventHandler` | — | Change handler |\n| `disabled` | `boolean` | `false` | Disables input |\n\n---\n\n### `StatusDot`\n\nCompact status indicator used in run lists, job monitors, and pipeline dashboards.\n\n```tsx\nimport { StatusDot } from '@man4ish/ui';\n\n\u003cStatusDot status=\"running\" /\u003e   // animated pulse\n\u003cStatusDot status=\"success\" /\u003e\n\u003cStatusDot status=\"failed\" /\u003e\n\u003cStatusDot status=\"queued\" /\u003e\n\u003cStatusDot status=\"idle\" /\u003e\n\n\u003cStatusDot status=\"running\" label=\"Job in progress\" /\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `status` | `'running' \\| 'success' \\| 'failed' \\| 'queued' \\| 'idle'` | — | Determines color and animation |\n| `label` | `string` | — | Optional text label beside the dot |\n\n---\n\n### `Spinner`\n\n```tsx\nimport { Spinner } from '@man4ish/ui';\n\n\u003cSpinner /\u003e\n\u003cSpinner size=\"sm\" /\u003e\n\u003cSpinner size=\"lg\" /\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `size` | `'sm' \\| 'md' \\| 'lg'` | `'md'` | Spinner diameter |\n\n---\n\n### `Table`\n\nGeneric sortable, paginated table. Columns are fully typed.\n\n```tsx\nimport { Table } from '@man4ish/ui';\nimport type { Column } from '@man4ish/ui';\n\nconst columns: Column\u003cMyRow\u003e[] = [\n  { key: 'name',   label: 'Repository', sortable: true },\n  { key: 'code',   label: 'Code lines', sortable: true, align: 'right' },\n  { key: 'status', label: 'Status',\n    render: (v) =\u003e \u003cBadge variant=\"success\"\u003e{String(v)}\u003c/Badge\u003e },\n];\n\n\u003cTable columns={columns} data={rows} pageSize={10} /\u003e\n```\n\n**Props:**\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `columns` | `Column\u003cT\u003e[]` | — | Column definitions |\n| `data` | `T[]` | — | Row data |\n| `pageSize` | `number` | `10` | Rows per page |\n| `emptyMessage` | `string` | `'No results'` | Empty state text |\n\n**Column definition:**\n\n| Key | Type | Default | Description |\n|---|---|---|---|\n| `key` | `keyof T` | — | Data key |\n| `label` | `string` | — | Column header text |\n| `sortable` | `boolean` | `false` | Enable sort on click |\n| `align` | `'left' \\| 'right'` | `'left'` | Text alignment |\n| `render` | `(value, row) =\u003e ReactNode` | — | Custom cell renderer |\n\n---\n\n### `Tabs`\n\n```tsx\nimport { Tabs } from '@man4ish/ui';\nimport type { Tab } from '@man4ish/ui';\n\n\u003cTabs\n  tabs={[\n    { key: 'arch',   label: 'Architecture', content: \u003cArchView /\u003e },\n    { key: 'health', label: 'Health',        content: \u003cHealthView /\u003e },\n  ]}\n  defaultTab=\"arch\"\n  onChange={(key) =\u003e console.log('switched to', key)}\n/\u003e\n```\n\n**Props:**\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `tabs` | `Tab[]` | — | Tab definitions |\n| `defaultTab` | `string` | first | Initially active tab key |\n| `onChange` | `(key: string) =\u003e void` | — | Called on tab switch |\n\n**Tab definition:**\n\n| Key | Type | Default | Description |\n|---|---|---|---|\n| `key` | `string` | — | Unique identifier |\n| `label` | `string` | — | Tab button text |\n| `content` | `ReactNode` | — | Panel content |\n\n---\n\n### `ProgressBar`\n\n```tsx\nimport { ProgressBar } from '@man4ish/ui';\n\n\u003cProgressBar value={98.7} variant=\"success\" label=\"98.7%\" /\u003e\n\u003cProgressBar value={60}   variant=\"accent\" /\u003e\n\u003cProgressBar value={25}   variant=\"danger\" size=\"lg\" /\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `value` | `number` | — | Current value (0–max) |\n| `max` | `number` | `100` | Maximum value |\n| `size` | `'sm' \\| 'md' \\| 'lg'` | `'md'` | Bar height |\n| `variant` | `'success' \\| 'danger' \\| 'warning' \\| 'accent' \\| 'info'` | `'accent'` | Fill color |\n| `showLabel` | `boolean` | `true` | Show percentage label |\n| `label` | `string` | — | Override auto label text |\n\n---\n\n### `Tooltip`\n\n```tsx\nimport { Tooltip } from '@man4ish/ui';\n\n\u003cTooltip content=\"27/27 services healthy\"\u003e\n  \u003cBadge variant=\"success\"\u003eUP\u003c/Badge\u003e\n\u003c/Tooltip\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `content` | `string` | — | Tooltip text shown on hover |\n| `children` | `ReactNode` | — | Element that triggers hover |\n\n---\n\n### `Select`\n\n```tsx\nimport { Select } from '@man4ish/ui';\nimport type { SelectOption } from '@man4ish/ui';\n\n\u003cSelect\n  label=\"Category\"\n  options={[\n    { value: 'all',  label: 'All categories' },\n    { value: 'core', label: 'Core'           },\n  ]}\n  value={selected}\n  onChange={setSelected}\n  placeholder=\"Choose a category\"\n/\u003e\n```\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `options` | `SelectOption[]` | — | Option list |\n| `value` | `string` | — | Controlled value |\n| `onChange` | `(v: string) =\u003e void` | — | Change handler |\n| `label` | `string` | — | Label above the select |\n| `placeholder` | `string` | — | Placeholder option |\n| `disabled` | `boolean` | `false` | Disables the select |\n\n---\n\n## Importing\n\nEverything is exported from the package root:\n\n```ts\nimport {\n  Button, Badge, Card, Input, StatusDot, Spinner,\n  Table, Tabs, ProgressBar, Tooltip, Select,\n} from '@man4ish/ui';\n\nimport type { Column, Tab, SelectOption } from '@man4ish/ui';\n```\n\nTree-shaking is automatic — unused components are excluded from your bundle at build time.\n\n---\n\n## Local development\n\n```bash\ngit clone https://github.com/man4ish/omnibioai-ui\ncd omnibioai-ui\nnpm install\n\n# Build once\nnpm run build\n\n# Watch mode (rebuilds on every save)\nnpm run dev\n\n# Run tests\nnpm run test\n\n# Watch mode\nnpm run test:watch\n\n# View Storybook (visual catalogue of all components)\nnpm run storybook\n```\n\nOutput lands in `dist/` as `index.js` (ES module) and `index.cjs` (CommonJS) with `index.d.ts` type declarations.\n\n### Using in another local repo without publishing\n\n```bash\n# In this repo\nnpm run build\nnpm link\n\n# In your consuming repo\nnpm link @man4ish/ui\n```\n\n---\n\n## Repository layout\n\n```\nomnibioai-ui/\n├── src/\n│   ├── index.ts\n│   ├── test-setup.ts\n│   ├── components/\n│   │   ├── Button/      (Button.tsx, Button.css, Button.test.tsx, Button.stories.tsx)\n│   │   ├── Badge/       (Badge.tsx, Badge.css, Badge.test.tsx, Badge.stories.tsx)\n│   │   ├── Card/        (Card.tsx, Card.css, Card.test.tsx, Card.stories.tsx)\n│   │   ├── Input/       (Input.tsx, Input.css, Input.test.tsx, Input.stories.tsx)\n│   │   ├── StatusDot/   (StatusDot.tsx, StatusDot.css, StatusDot.test.tsx, StatusDot.stories.tsx)\n│   │   ├── Spinner/     (Spinner.tsx, Spinner.css, Spinner.test.tsx, Spinner.stories.tsx)\n│   │   ├── Table/       (Table.tsx, Table.css, Table.test.tsx, Table.stories.tsx)\n│   │   ├── Tabs/        (Tabs.tsx, Tabs.css, Tabs.test.tsx, Tabs.stories.tsx)\n│   │   ├── ProgressBar/ (ProgressBar.tsx, ProgressBar.css, ProgressBar.test.tsx, ProgressBar.stories.tsx)\n│   │   ├── Tooltip/     (Tooltip.tsx, Tooltip.css, Tooltip.test.tsx, Tooltip.stories.tsx)\n│   │   └── Select/      (Select.tsx, Select.css, Select.test.tsx, Select.stories.tsx)\n├── .storybook/\n├── vite.config.ts        ← library build config\n├── vitest.config.ts      ← test config (separate from vite)\n├── tsconfig.json\n├── CHANGELOG.md          ← release history (Keep a Changelog format)\n└── package.json\n```\n\n---\n\n## Design token contract\n\nComponents reference CSS custom properties from `@man4ish/design-tokens`. No component contains a hardcoded color, spacing value, or radius — all visual decisions are delegated to the token layer. This means:\n\n- Swapping themes (light/dark, brand variants) requires only a token override\n- All OmniBioAI surfaces (Electron app, web, plugin UIs) stay visually consistent from a single source of truth\n- No `!important` hacks or specificity wars when overriding in consuming apps\n\n---\n\n## Related packages\n\n| Package | Purpose |\n|---|---|\n| [`@man4ish/design-tokens`](https://github.com/man4ish/omnibioai-design-tokens) | CSS custom properties — required peer dependency |\n| [`omnibioai-studio`](https://github.com/man4ish/omnibioai-studio) | Electron + React app — primary consumer |\n| [`omnibioai`](https://github.com/man4ish/omnibioai) | Django backend — 200+ plugin bioinformatics platform |\n| `omnibioai-control-center` | Health dashboard + ecosystem report (consumes design tokens) |\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fman4ish%2Fomnibioai-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fman4ish%2Fomnibioai-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fman4ish%2Fomnibioai-ui/lists"}