Skip to content
Open
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
20 changes: 20 additions & 0 deletions .claude/skills/work-on-issue/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: work-on-issue
description: Pick up a drift-ui native GitHub issue and take it to an open pull request. Use when asked to work on, fix, port or pick up an issue, when given an issue number or URL, or when asked what to work on next.
argument-hint: "[issue-number]"
---

# Work on an issue

Issue: $ARGUMENTS

Follow "Working on a GitHub issue" in `AGENTS.md` step by step. This skill adds the order of operations and the checkpoints.

1. **Choose.** If no issue number was given, list open issues in drift-ui/native, show the top five with labels, and ask which one to take. Stop until the human answers.
2. **Understand.** `gh issue view <n> --comments`, then read the web component, its lib file and its tests in drift-ui/ui. Summarize the ask in three lines, list the files you expect to touch, and name the web features that have no React Native equivalent. If anything about the API is unclear, ask now.
3. **Claim and branch.** Ask before assigning or commenting. Create `<type>/<n>-<slug>` from an up-to-date `main`.
4. **Shared logic first.** If the web component has a lib file without its own `registry:lib` item, that item is a pull request in drift-ui/ui. Ask before opening it.
5. **Implement.** Work through the seven-step checklist in AGENTS.md. Track each step as a todo so none is skipped. For a bug, write the failing test first.
6. **Verify.** Run typecheck, lint, test and `registry:build`, then run the preview on a simulator. Fix failures and re-run until clean. Report anything you could not check, such as Android or a real device.
7. **Show the work.** Before pushing, summarize the diff (`git diff main --stat`), the verification results and the differences from the web component, and ask whether to push and open the pull request.
8. **Open the pull request.** Use the template, include `Closes #<n>` and any drift-ui/ui pull request it depends on, tick only checks that ran, and give the human the URL.
46 changes: 46 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Bug report
description: Something in a drift-ui native component does not work as expected.
labels: ["bug"]
body:
- type: markdown
attributes:
value: Thanks for reporting. Please check the [open issues](https://github.com/drift-ui/native/issues) first in case this is already tracked. Bugs in the web components go to [drift-ui/ui](https://github.com/drift-ui/ui/issues).
- type: input
id: component
attributes:
label: Component
description: The registry item name, for example `key-value-editor`.
placeholder: key-value-editor
validations:
required: true
- type: textarea
id: description
attributes:
label: What happened
description: What you saw, and what you expected to see.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: The smallest set of steps or code that shows the problem. An Expo Snack link helps.
placeholder: |
1. Install key-value-editor
2. Render `<KeyValueEditor />`
3. Paste "A=1" into the first key field
validations:
required: true
- type: input
id: platform
attributes:
label: Platform and device
placeholder: iOS 26 simulator, Pixel 9 with Android 16
validations:
required: true
- type: textarea
id: versions
attributes:
label: Versions
description: Expo SDK, React Native, NativeWind and shadcn CLI versions.
render: text
34 changes: 34 additions & 0 deletions .github/ISSUE_TEMPLATE/component_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Component request
description: Ask for a native version of a drift-ui component, or a feature for an existing one.
title: "feat: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: Only components that exist in [drift-ui](https://drift-ui.dev/components) are ported here. To propose a new component, open a request in [drift-ui/ui](https://github.com/drift-ui/ui/issues/new/choose) first.
- type: input
id: component
attributes:
label: Component
description: The drift-ui component to port, or the native one you want to extend.
placeholder: tags-input
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use case
description: The screen in your app where you need it.
validations:
required: true
- type: textarea
id: platform-notes
attributes:
label: Platform notes
description: Anything that works differently on iOS or Android, or web features that have no native equivalent.
- type: checkboxes
id: contribute
attributes:
label: Contributing
options:
- label: I would like to build this myself
20 changes: 20 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## What and why

<!-- What this changes and the reason for it. Link the issue, e.g. Closes #12, and any drift-ui/ui pull request this depends on. -->

## Differences from the web component

<!-- Props or behavior that differ from drift-ui/ui, and why. Delete if not applicable. -->

## Screenshots

<!-- iOS and/or Android, light and dark. Delete if not applicable. -->

## Checklist

- [ ] `bun run typecheck`, `bun run lint` and `bun run test` pass

@cubic-dev-ai cubic-dev-ai Bot Nov 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This checklist omits bun run format:check and bun run registry:build, both required by CI, so PRs can satisfy the listed checks and still fail CI. Add both commands here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .github/pull_request_template.md, line 15:

<comment>This checklist omits `bun run format:check` and `bun run registry:build`, both required by CI, so PRs can satisfy the listed checks and still fail CI. Add both commands here.</comment>

<file context>
@@ -0,0 +1,20 @@
+
+## Checklist
+
+- [ ] `bun run typecheck`, `bun run lint` and `bun run test` pass
+- [ ] Tests cover what a user does: typing, pressing, submitting
+- [ ] New components are added to `registry.json` and the README table, each at its alphabetical position
</file context>
Suggested change
- [ ] `bun run typecheck`, `bun run lint` and `bun run test` pass
- [ ] `bun run format:check`, `bun run typecheck`, `bun run lint`, `bun run test` and `bun run registry:build` pass
Fix with cubic

- [ ] Tests cover what a user does: typing, pressing, submitting
- [ ] New components are added to `registry.json` and the README table, each at its alphabetical position
- [ ] Checked on iOS
- [ ] Checked on Android
- [ ] Checked in light and dark mode
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

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

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: oven-sh/setup-bun@v2

- run: bun install --frozen-lockfile

- name: Format
run: bun run format:check

- name: Typecheck
run: bun run typecheck

- name: Lint
run: bun run lint

- name: Test
run: bun run test

- name: Registry
run: bun run registry:build
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
.expo/
dist/
coverage/
public/r/
ios/
android/
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js",
"tailwindFunctions": ["cn", "cva"]
}
135 changes: 110 additions & 25 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,126 @@
This is an Expo/React Native mobile application. Prioritize mobile-first patterns, performance, and cross-platform compatibility.
# AGENTS.md

## Expo has changed, do not trust your training data
Instructions for coding agents (Claude Code, Codex, Cursor, Copilot, Gemini and others) working in this repo. Humans should read [CONTRIBUTING.md](CONTRIBUTING.md); this file points at it rather than repeating it.

## Project

Expo ships breaking changes every SDK release. APIs you remember are likely renamed, moved, or removed. Before writing any code that touches an Expo, EAS, or React Native API:
drift-ui native is a shadcn-compatible registry of React Native components: Expo SDK 58, React Native 0.88, NativeWind 4 with Tailwind CSS 3, Bun. Each component ports one from [drift-ui/ui](https://github.com/drift-ui/ui) and keeps its props. Primitives come from React Native Reusables, shared logic from the `@drift-ui` web registry.

1. Read the major version of the `expo` package in `package.json`.
2. Fetch the matching versioned docs: `https://docs.expo.dev/versions/v<major>.0.0/`
3. For anything else, fetch https://docs.expo.dev/llms.txt, an index of all Expo docs with corrections to common LLM misconceptions. Follow its links to the specific page you need; never answer from memory.
Lead maintainer: [@localhostd3veloper](https://github.com/localhostd3veloper). The pilot is run by [@KomalSrinivasan](https://github.com/KomalSrinivasan). Repo: `drift-ui/native`. The pilot is tracked in drift-ui/ui#132.

## Commands

Use `bunx` instead of `npx` if the project uses bun (`bun.lock` present).
```bash
bun install
bun start # Expo dev server, i for iOS, a for Android
bun run registry:build # regenerate public/r/*.json from registry.json
bun run typecheck
bun run lint
bun run test
bun run format
```

## Where things live

| Path | What |
| -------------------------------- | ----------------------------------------------------------- |
| `registry/default/ui/<name>.tsx` | Component source, plus React Native Reusables primitives |
| `registry/default/lib/*.ts` | Shared logic installed from `@drift-ui`. Never edit it here |
| `registry/default/hooks/*.ts` | Hooks installed from `@drift-ui`. Never edit them here |
| `registry.json` | Registry manifest read by `shadcn build` |
| `App.tsx` | Preview screen |
| `tests/<name>.test.tsx` | Jest suites (jest-expo, React Native Testing Library) |
| `public/r/**/*.json` | Build output, gitignored. Regenerate, never hand-edit |

## Working on a GitHub issue

Use the `gh` CLI. If you are not authenticated, ask the human to run `gh auth login`.

### 1. Choose the issue

If you were given a number, use it. Otherwise list candidates and let the human pick:

```bash
npx expo install <package> # ALWAYS use instead of npm/yarn/pnpm/bun add, resolves SDK-compatible versions
npx expo start # start the dev server
npx expo lint # lint
npx tsc --noEmit # typecheck
npx expo-doctor # diagnose dependency and config issues
npx expo install --fix # fix incompatible package versions
gh issue list -R drift-ui/native --state open
```

Run lint and typecheck before declaring any task done.
Skip issues that already have an assignee or a linked open pull request (`gh issue view <n> --json assignees,closedByPullRequestsReferences`).

### 2. Read it fully

```bash
gh issue view <n> --comments
```

Read the web component in drift-ui/ui too: its source, its lib file and its tests. If the issue is ambiguous, ask in the conversation before writing code. Do not guess an API.

@cubic-dev-ai cubic-dev-ai Bot Nov 10, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This asks agents to read a lib file even when the web component has none, although CONTRIBUTING makes that file optional. Change this to “its lib file, if present” so components without shared logic do not send agents searching for nonexistent source.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At AGENTS.md, line 55:

<comment>This asks agents to read a lib file even when the web component has none, although CONTRIBUTING makes that file optional. Change this to “its lib file, if present” so components without shared logic do not send agents searching for nonexistent source.</comment>

<file context>
@@ -1,41 +1,126 @@
+gh issue view <n> --comments
+```
+
+Read the web component in drift-ui/ui too: its source, its lib file and its tests. If the issue is ambiguous, ask in the conversation before writing code. Do not guess an API.
+
+### 3. Claim it and branch
</file context>
Suggested change
Read the web component in drift-ui/ui too: its source, its lib file and its tests. If the issue is ambiguous, ask in the conversation before writing code. Do not guess an API.
Read the web component in drift-ui/ui too: its source, its lib file if present, and its tests. If the issue is ambiguous, ask in the conversation before writing code. Do not guess an API.
Fix with cubic


### 3. Claim it and branch

Ask the human before claiming, since it is visible to others. Maintainers assign themselves (`gh issue edit <n> --add-assignee @me`), others comment on the issue.

```bash
git switch main && git pull
git switch -c <type>/<n>-<short-slug> # e.g. feat/4-tags-input, fix/7-kve-focus
```

## Navigation & Routing
### 4. Implement

Follow every step of [Porting a component](CONTRIBUTING.md#porting-a-component) and the [component guidelines](CONTRIBUTING.md#component-guidelines). The short version:

1. If the web component has a lib file, it needs a `registry:lib` item in drift-ui/ui first (`@drift-ui/<name>-utils`). That is a separate pull request in that repo
2. Install shared files with `bunx shadcn add @drift-ui/<item>` and primitives with `bunx shadcn add https://reactnativereusables.com/r/nativewind/<name>.json`. Never edit installed files
3. `registry/default/ui/<name>.tsx` with the web component's props
4. Entry in `registry.json`, at its alphabetical position
5. A demo in `App.tsx`
6. Tests in `tests/<name>.test.tsx`. React Native Testing Library 14 is async: `await render(...)` and `await fireEvent...`
7. A row in the README component table

Rules that reviewers enforce:

- Same props as the web component. Differences are listed in the pull request
- Controlled and uncontrolled support through `use-controllable-state`
- Accept `className` and spread remaining props onto the outer `View`
- Labels on every control, and `role` where React Native supports it
- Colors come from the theme variables in `global.css`, never hard-coded
- Keep the scope to the issue. No drive-by refactors

### 5. Verify

All of these must pass before you open a pull request:

```bash
bun run typecheck && bun run lint && bun run test && bun run registry:build
```

Run the preview with `bun start` on an iOS simulator or Android emulator, and check light and dark mode. Attach a screenshot to the pull request.

### 6. Commit and open the pull request

Commits follow [Conventional Commits](https://www.conventionalcommits.org), scoped to the component: `feat(tags-input): add React Native tags-input`. One logical change per commit.

Ask the human before pushing or opening the pull request. Then:

```bash
git push -u origin HEAD
gh pr create --title "feat(tags-input): add React Native tags-input" --body-file pr-body.md
```

Write `pr-body.md` outside the repo. Base it on [.github/pull_request_template.md](.github/pull_request_template.md) and tick only what you actually ran. `Closes #<n>` must be in the body, and link the drift-ui/ui pull request it depends on, if any.

## Expo has changed, do not trust your training data

- Use **Expo Router** for all navigation. Routes live in `src/app/`, every file there is a screen, `_layout.tsx` files define navigators. Keep non-route code (components, hooks, utils) outside `src/app/`.
- Import `Link`, `router`, and `useLocalSearchParams` from `expo-router`.
- Docs: https://docs.expo.dev/router/introduction.md
Expo ships breaking changes every SDK release. Before writing code that touches an Expo or React Native API:

## Building with EAS
1. Read the major version of `expo` in `package.json`.
2. Fetch the matching docs: `https://docs.expo.dev/versions/v<major>.0.0/`
3. For anything else, start from https://docs.expo.dev/llms.txt

Use EAS to build, sign, and submit the app in the cloud (`eas build`, `eas submit`) and to ship over-the-air updates (`eas update`), no local Xcode or Android Studio required. Run EAS CLI as `bunx eas-cli <command>` in Bun projects, or `npx eas-cli@latest <command>` otherwise; substitute that for bare `eas` in docs examples.
Docs: https://docs.expo.dev/eas/index.md
Add packages with `bunx expo install <package>`, which picks SDK-compatible versions, not `bun add`. Expo Go only bundles its own native modules; a library with other native code needs a development build (`bunx expo run:ios`). Never create or edit `ios/` or `android/` by hand.

## Rules
## Do not

- If `ios/` and `android/` directories do not exist, they are generated (Continuous Native Generation). Never create or edit them by hand, configure native behavior in `app.json` and config plugins.
- Expo Go only includes its bundled native modules. After adding a library with native code, the app needs a development build: `npx expo run:ios|android` locally, or `eas build --profile development`.
- Prefer recommended Expo modules over third-party libraries, and check your available skills before adding dependencies. Docs: https://docs.expo.dev/versions/latest/index.md
- Push to `main` or force-push a shared branch
- Commit or hand-edit `public/r/*.json`
- Edit files installed from `@drift-ui` or React Native Reusables
- Add dependencies without saying why in the pull request
- Work on more than one issue in a single pull request
34 changes: 26 additions & 8 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import "./global.css";
import { StatusBar } from "expo-status-bar";
import { Text, View } from "react-native";
import "./global.css"
import { StatusBar } from "expo-status-bar"
import { ScrollView } from "react-native"
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"

import { KeyValueEditor } from "@/registry/default/ui/key-value-editor"
import { Text } from "@/registry/default/ui/text"

const rows = [
{ id: "1", key: "DATABASE_URL", value: "postgres://localhost:5432/app" },
{ id: "2", key: "API_KEY", value: "sk_test_123" },
{ id: "3", key: "API_KEY", value: "sk_test_456" },
]

export default function App() {
return (
<View className="flex-1 items-center justify-center bg-white dark:bg-neutral-950">
<Text className="text-lg font-semibold text-neutral-900 dark:text-neutral-50">drift-ui native</Text>
<StatusBar style="auto" />
</View>
);
<SafeAreaProvider>
<SafeAreaView className="flex-1 bg-background">
<ScrollView
contentContainerClassName="gap-4 p-4"
keyboardShouldPersistTaps="handled"
>
<Text className="text-2xl font-semibold">Key Value Editor</Text>
<KeyValueEditor defaultValue={rows} secretValues />
</ScrollView>
<StatusBar style="auto" />
</SafeAreaView>
</SafeAreaProvider>
)
}
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@AGENTS.md

## Claude Code

- `/work-on-issue <number>` runs the issue workflow above from claim to pull request. Run it without a number to choose from open issues.
Loading
Loading