Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/verification.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Workspace verification

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: verify-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-latest
timeout-minutes: 25
env:
CI: "true"
NEXT_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "22"
- name: Activate the repository-pinned pnpm
run: corepack enable
- name: Frozen dependency installation
run: pnpm install --frozen-lockfile
- name: Sharing source contract
run: node --test scripts/social-metadata.test.mjs
- name: Lint
run: pnpm lint
- name: Type checks
run: pnpm typecheck
- name: Production build
run: pnpm build
- name: Install Chromium
run: pnpm --dir apps/web exec playwright install --with-deps chromium
- name: Browser and rendered-image verification
run: pnpm test:e2e
- name: Retain synthetic browser evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: ui-browser-evidence-${{ github.sha }}
path: apps/web/test-results/
if-no-files-found: ignore
retention-days: 14
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,20 @@ Run from the repository root:
pnpm lint
pnpm typecheck
pnpm build
pnpm test:e2e
node --test scripts/social-metadata.test.mjs
```

For UI changes, also inspect the component's Preview and Source tabs, Install and Import examples, `/lab`, keyboard behavior, light/dark modes, narrow screens, and RTL. `pnpm format` writes files; avoid unrelated formatting churn.

There is no root unit-test or E2E script at this revision. Do not invent `pnpm test`, report manual inspection as automated coverage, or claim a build proves accessibility. State which checks ran, their results, and any environment limitation.
There is no root unit-test script named `test`; the root does define `test:e2e`. Do not invent `pnpm test`, report manual inspection as automated coverage, or claim a build proves accessibility. The standalone social-metadata check validates source invariants, not a rendered HTTP response. State which checks ran, their results, and any environment limitation.

## Documentation and handoff

Keep README.md and CONTRIBUTING.md consistent with the manifests. Keep CLAUDE.md as a pointer to this file rather than a competing policy. Preserve attribution to shadcn/ui, Base UI, TypeSafe-inspired branding, and the OpenCoven layout inspiration; do not imply vendor endorsement.

Read `docs/discovery/README.md` for sharing and screenshot guidance. The generated OG route must use public editorial copy only, never request content, credentials, or live model calls. Preserve existing per-page metadata and do not canonicalize all routes to the homepage.

`repository-metadata.json` records intended GitHub About text/topics only. Applying it requires a separate authorized GitHub settings action; editing the file alone does not publish topics. Do not create release tags, publish packages, change licensing, or change repository visibility unless explicitly requested.

<!-- BEGIN:nextjs-agent-rules -->
Expand Down
95 changes: 51 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,96 +2,103 @@

Reusable React components and interactive interface patterns for TypeSafe AI projects, built with shadcn/ui, Base UI, and Tailwind CSS.

**Small parts. Clear interfaces.** An independent community project maintained under `TypeSafeAI`, not an official TypeSafe AI component library or SDK.
**Small parts. Clear interfaces.** This is an independent community project under `TypeSafeAI`, not an official TypeSafe AI component library or SDK. The community organization was created by VC Moderator [@BunsDev](https://github.com/BunsDev).

[Contributing](CONTRIBUTING.md) · [Agent guide](AGENTS.md) · [TypeSafe API documentation](https://docs.typesafe.ai/api)
[Developer guide](docs/discovery/README.md) · [Contributing](CONTRIBUTING.md) · [Agent instructions](AGENTS.md) · [Jev Labs](docs/jev-labs.md) · [Official API documentation](https://docs.typesafe.ai/introduction/quickstart)

## What this repository provides
![TypeSafe UI: an unofficial community component workspace](docs/discovery/assets/social-preview.svg)

A Turborepo + pnpm workspace with a Next.js component browser, source previews, install/import examples, and an interactive Lab. The UI uses the shadcn `base-nova` style, Base UI primitives, Tailwind v4, and RTL-aware components.
## What is here

The site pairs an original optical-glass photographic introduction with an OpenCoven-inspired workspace: a sticky topbar, grouped component rail, per-component Preview/Source and Install/Import tabs, and an “On this page” outline. Its TypeSafe-inspired theme uses pink primary, teal live-state accents, dark mode by default, IBM Plex typography, a dot-grid background, and softly illuminated preview surfaces.
A Turborepo and pnpm workspace with a Next.js component browser, source previews, install/import examples, and an interactive Lab. It uses the shadcn `base-nova` style, Base UI primitives, Tailwind v4, and RTL-aware components. The interface supports light/dark themes and TypeSafe-inspired pink/teal styling.

`@workspace/ui` is a **private workspace package**, not a published npm package. The imports below work inside this monorepo. For another application, deliberately port the components, styles, dependencies, and aliases you need; do not assume `npm install typesafe-ui` or a hosted registry exists. A visual “live” state is not proof of a real Jev API call.
`@workspace/ui` is a **private workspace package**, not a published npm package. Imports work inside this monorepo. For another application, deliberately port the components, styles, dependencies, and aliases you need; do not assume `npm install typesafe-ui` or a hosted registry exists.

## Getting started
The Jev Labs guide describes the upstream catalog examples, workspace scenarios, and interface patterns. Outputs in these previews are local fixtures. A visual live-state indicator is not proof of a Jev API call.

Use the pnpm version pinned in [package.json](package.json), currently `10.33.4`. The root manifest declares Node.js `>=20`; use a Node version supported by the checked-in Next.js dependency as well. Node.js 22+ is a practical development baseline.
## Run locally

Use the exact pnpm version declared in [package.json](package.json). The manifest declares Node.js `>=20`; use a version supported by the checked-in Next.js dependency as well. Node.js 22+ is a practical development baseline.

```sh
git clone https://github.com/TypeSafeAI/typesafe-ui.git
cd typesafe-ui
# Install/activate the pnpm version declared in package.json.
# Where Corepack is installed, `corepack enable` enables its package-manager shims.
# Activate the pnpm version declared in package.json.
# Corepack can enable package-manager shims where it is installed.
pnpm install --frozen-lockfile
pnpm dev
```

The [Jev Labs guide](docs/jev-labs.md) covers 169 local examples: all 110 upstream catalog examples, 56 workspace scenarios, and three interface patterns. Labs share the library’s sidebar, source inspection, and installation/import workflow. All outputs are explicitly local fixtures.

Open the address printed by the development server, normally `http://localhost:3000`. The library is at `/`, and interactive scenes are at `/lab`. Press `d` to toggle dark mode and `⌘K` to search components.
Open the address printed by the development server, normally `http://localhost:3000`. The library is at `/` and the interactive scenes are at `/lab`. Press `d` to toggle dark mode and `Cmd+K` to search components. No provider credentials are needed to browse local fixtures.

| Command | Purpose |
| --- | --- |
| `pnpm dev` | Run the web application through Turborepo. |
| `pnpm build` | Build the workspace. |
| `pnpm lint` | Run workspace lint tasks. |
| `pnpm typecheck` | Run workspace TypeScript checks. |
| `pnpm test:e2e` | Run Playwright browser checks for the catalog, Lab, navigation, themes, and motion. |
| `pnpm format` | Format source files; this writes changes. |
| `pnpm dev` | Run the web application through Turborepo |
| `pnpm build` | Build the workspace |
| `pnpm lint` | Run workspace lint tasks |
| `pnpm typecheck` | Run workspace TypeScript checks |
| `pnpm test:e2e` | Run Playwright browser checks |
| `node --test scripts/social-metadata.test.mjs` | Check sharing-source invariants without installing dependencies |
| `pnpm format` | Format source files; this writes changes |

There is no root unit-test script. `pnpm test:e2e` runs the checked-in Playwright suite (local Google Chrome; bundled Chromium in CI). Browser checks complement visual and keyboard inspection; they do not prove accessibility.
There is no root `pnpm test` script. The E2E suite uses local Google Chrome, or bundled Chromium in CI. Source checks do not prove that a deployed image loads, and browser automation does not establish complete accessibility.

## Layout
## Project map

```text
apps/web/
app/ Next.js routes, layout, fonts, and metadata
components/ Site shell, component cards, demos, and Lab scenes
lib/ Site config, component registry, source loader, and shiki
app/ routes, layout, fonts, and social metadata
components/ shell, component cards, previews, and Lab scenes
lib/ site configuration, registries, source loader, and shiki
packages/
ui/ @workspace/ui: components, hooks, utilities, and styles
eslint-config/ Shared lint configuration
typescript-config/ Shared TypeScript configuration
ui/ @workspace/ui components, hooks, utilities, and styles
eslint-config/ shared lint configuration
typescript-config/ shared TypeScript configuration
```

## Adding a component
## Add a component or lab

1. Add a component against the web app. The existing shadcn configuration places reusable components in `packages/ui/src/components`.
1. Add a component against the web app; the shadcn configuration places reusable components in `packages/ui/src/components`.

```sh
pnpm dlx shadcn@latest add popover -c apps/web
```

This is a generator operation that can change source and dependencies. Review its diff and the lockfile rather than treating it as a read-only command.
This is a generator operation that can modify source and dependencies. Review its complete diff and lockfile changes; it is not a read-only command.

2. Register the component in `apps/web/lib/registry.ts`: id, title, group, description, exports, and supported states.
3. Add a demo using the same id in `apps/web/components/demos.tsx`.
4. Verify its preview, source, import example, keyboard behavior, themes, narrow layout, and RTL behavior.
2. Register the component in `apps/web/lib/registry.ts` with its ID, group, description, exports, and supported states.
3. Add its demo using the same ID in `apps/web/components/demos.tsx`.
4. Verify preview, displayed source, imports, keyboard behavior, themes, narrow layout, and RTL.

The library reads component source from disk at build time and highlights it with shiki. Keep registry ids, exported paths, source paths, and demos aligned.
For a Jev Lab, follow [the Lab guide](docs/jev-labs.md) and preserve upstream IDs, declared A/B differences, local-fixture labels, and attribution. The library reads component source at build time and highlights it with shiki; keep registry IDs, source paths, exports, and demos aligned.

## Using components
## Use components

```tsx
import { Button } from "@workspace/ui/components/button"
```

Base UI triggers compose through the `render` prop rather than Radix-style `asChild`. A `Button` rendered as a link needs `nativeButton={false}`. Keep provider credentials and application-specific network clients out of reusable client components.
Base UI triggers compose through `render`, rather than Radix-style `asChild`. A Button rendered as a link needs `nativeButton={false}`. Keep provider credentials and application-specific network clients out of reusable client components.

## Themes and RTL

Tokens live in `packages/ui/src/styles/globals.css`: light values in `:root` and dark values in `.dark`. TypeSafe-oriented tokens include `--teal`, `--success`, `--warning`, and `--dot`. Fonts are wired through `next/font` in `apps/web/app/layout.tsx` and exposed as `--font-sans` and `--font-mono`.

Site identity, links, navigation, language, and direction live in `apps/web/lib/site.ts`. The shadcn configuration has `rtl: true`. Set `dir` and `lang` deliberately for a locale, preserve DirectionProvider, and prefer logical CSS properties.

## Theming and RTL
## Sharing and screenshots

Tokens live in `packages/ui/src/styles/globals.css`: light values in `:root`, dark values in `.dark`. TypeSafe-oriented tokens include `--teal`, `--success`, `--warning`, and `--dot`. Fonts are wired through `next/font` in `apps/web/app/layout.tsx` and exposed as `--font-sans` and `--font-mono`.
`apps/web/app/opengraph-image.tsx` renders a public editorial PNG. Twitter metadata uses the same route. Verify the rendered tags and image response after building and deploying; do not claim production publication from a source diff alone. The SVG shown above is editable artwork, not an application screenshot.

Site name, tagline, links, navigation, language, and direction live in `apps/web/lib/site.ts`. The shadcn configuration has `"rtl": true`. Set `dir` to `"rtl"` and `lang` to the intended locale to update the root layout and `DirectionProvider`. Prefer logical CSS properties so components work in both directions.
The [developer guide](docs/discovery/README.md) links publishing and screenshot protocols. Use synthetic local previews, keep fixture labels visible, and record the source revision, viewport, theme, direction, and capture environment. Review media for private data. `repository-metadata.json` documents proposed About text/topics; it does not apply GitHub settings.

## Related community projects

| Repository | Role |
| --- | --- |
| [typesafe-ai-playground](https://github.com/BunsDev/typesafe-ai-playground) | Interactive Jev experiments and integration demos. |
| [clarity-judge](https://github.com/BunsDev/clarity-judge) | Separate, named writing-quality checks. |
| [typesafe-router](https://github.com/BunsDev/typesafe-router) | Closed-set tool and model routing, separate from execution. |
| [typesafe-ui](https://github.com/TypeSafeAI/typesafe-ui) | Reusable components and interface patterns. |
| [typesafe-playground](https://github.com/TypeSafeAI/typesafe-playground) | Interactive Jev experiments and integration demos |
| [clarity-judge](https://github.com/TypeSafeAI/clarity-judge) | Separate named writing-quality checks |
| [typesafe-router](https://github.com/TypeSafeAI/typesafe-router) | Closed-set selection, separate from authorization and execution |
| [jev-harness](https://github.com/TypeSafeAI/jev-harness) | Proposal-review evidence and synthetic fixtures |

These are separate repositories, not an automatically integrated or officially supported product suite. The proposed GitHub description and discovery topics are recorded in [repository-metadata.json](repository-metadata.json); that file does not change GitHub settings automatically.
These are independent community projects, not an automatically integrated or officially supported suite. Preserve shadcn/ui and Base UI attribution, TypeSafe-inspired branding, the OpenCoven layout inspiration, and existing source/license notices. Review the applicable licenses before reusing code; this change does not alter licensing.
1 change: 1 addition & 0 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const metadata: Metadata = {
},
twitter: {
card: "summary_large_image",
images: [{ url: "/opengraph-image", alt: "TypeSafe UI — unofficial community component workspace" }],
},
}

Expand Down
28 changes: 28 additions & 0 deletions apps/web/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ImageResponse } from "next/og"
import { site } from "@/lib/site"

export const alt = "TypeSafe UI — unofficial community component workspace"
export const size = { width: 1200, height: 630 }
export const contentType = "image/png"

// Public editorial artwork only: no request data, provider calls, or credentials.
export default function Image() {
return new ImageResponse(
<div style={{ display: "flex", flexDirection: "column", width: "100%", height: "100%", padding: "56px 64px", background: "#080e16", color: "#f5f7fb", borderTop: "8px solid #f0a5ca", fontFamily: "sans-serif" }}>
<div style={{ display: "flex", fontSize: 22, letterSpacing: 2, color: "#a3e7d7" }}>TYPESAFEAI / UNOFFICIAL COMMUNITY</div>
<div style={{ display: "flex", marginTop: 48, fontSize: 72, fontWeight: 700 }}>{site.name}</div>
<div style={{ display: "flex", marginTop: 18, fontSize: 34, color: "#f0a5ca" }}>{site.tagline}</div>
<div style={{ display: "flex", marginTop: 28, fontSize: 25, color: "#acb8c9" }}>React components · Source previews · Interface patterns</div>
<div style={{ display: "flex", gap: 20, marginTop: 42 }}>
{["Preview", "Inspect source", "Reuse deliberately"].map((label) => (
<div key={label} style={{ display: "flex", flex: 1, padding: "22px 24px", border: "1px solid #34404f", borderRadius: 12, background: "#101924", fontSize: 25 }}>{label}</div>
))}
</div>
<div style={{ display: "flex", justifyContent: "space-between", marginTop: "auto", paddingTop: 24, borderTop: "1px solid #34404f", fontSize: 20 }}>
<span>TypeSafeAI / typesafe-ui</span>
<span style={{ color: "#a3e7d7" }}>Community-built. Not official.</span>
</div>
</div>,
size
)
}
Loading
Loading