# @oshon-ai/primitives

Headless behavior primitives for the Oshon design system. Behavior, accessibility, keyboard handling, focus management, RBAC hooks, audit hooks, i18n slots — **no styling**. Consumers who want the Oshon visual language should pull from `@oshon-ai/components` (Phase 4); this package is the substrate.

See [`OSHON.md`](../../OSHON.md) §3 for the layering rationale.

## Install

```bash
pnpm add @oshon-ai/primitives
```

Peer dependencies: `react`, `react-dom` (18.x or 19.x).

## Usage

```tsx
import { Button, Dialog, Tooltip } from '@oshon-ai/primitives';

function Example() {
  return (
    <Tooltip.Provider>
      <Dialog.Root>
        <Dialog.Trigger asChild>
          <Button>Delete record</Button>
        </Dialog.Trigger>
        <Dialog.Portal>
          <Dialog.Overlay />
          <Dialog.Content>
            <Dialog.Title>Are you sure?</Dialog.Title>
            <Dialog.Description>This cannot be undone.</Dialog.Description>
            <Dialog.Close asChild>
              <Button>Cancel</Button>
            </Dialog.Close>
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>
    </Tooltip.Provider>
  );
}
```

## What's in Phase 3a

Three primitives — one proprietary, two Radix-wrapped:

| Primitive | Strategy | ADR |
|---|---|---|
| `Button` | Proprietary headless, RSC-safe | [ADR-001](../../docs/adr/primitives/001-button-rsc-default.md) |
| `Dialog` | Thin wrap over `@radix-ui/react-dialog` | [ADR-002](../../docs/adr/primitives/002-dialog-radix-wrap.md) |
| `Tooltip` | Thin wrap over `@radix-ui/react-tooltip` | [ADR-003](../../docs/adr/primitives/003-tooltip-radix-wrap.md) |

Phase 3b will add Input, Select, Popover, Tabs, and Toggle using the same template.

## Package-level contract

Three shared primitives that every component consumes:

- **`ComponentManifestSchema`** — Zod schema for per-component manifests, ported verbatim from MCP_SCHEMA.md §6. Every `manifest.ts` in this package is validated at test time via `scripts/validate-manifests.mjs`.
- **`PermissionContext`** + `usePermissions()` + `PermissionProvider` — RBAC plumbing. Every primitive respects `canView` (hides the UI) and `canEdit` (disables interaction).
- **`AuditProvider`** + `useAudit()` — audit event plumbing. Every interactive primitive fires events with shape `{ component, action, meta?, timestamp }`. Button and Dialog emit; Tooltip does not (see ADR-003).

Plus `resolveMessage()` for i18n-aware string resolution. The full i18n runtime lands in Phase 5.

## Design principles honored

- **#3 RBAC + audit by default** — every primitive accepts `permissions` and reads `AuditProvider` (Tooltip carve-out documented in ADR-003)
- **#5 RSC-default** — Button stays a server component; Dialog and Tooltip declare `'use client'` because they wrap Radix
- **#8 Manifest-driven** — every primitive ships `manifest.ts` validated against `ComponentManifestSchema`
- **#9 Trust-boundary Zod** — manifests parse at test time; malformed manifests fail CI
- **#11 i18n by default** — primitives take a `messages` prop instead of inlining user-visible strings (skeleton; full runtime in Phase 5)

## Development

```bash
pnpm --filter @oshon-ai/primitives test      # validates manifests + runs vitest
pnpm --filter @oshon-ai/primitives build     # tsup + tsc + .d.cts emit
pnpm --filter @oshon-ai/primitives typecheck # tsc --noEmit
```

## License

MIT.
