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
12 changes: 8 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
The working contract for anyone — human or agent — modifying code in this
repo. This file is the canonical entry point: read it first, every time.

<!-- One paragraph on what this repo is: the problem it solves, what it
ships, and the major moving parts. Delete this comment once filled. -->
This repo is the UI half of Dunky: the components and everything needed to
render them. Each primitive's behavior is a framework-free state machine
(`packages/core/<name>`, built on `@dunky.dev/state-machine`); framework-free
DOM helpers live in `packages/dom`; and a thin per-substrate binding
(`packages/<substrate>/<name>`, e.g. `react`) turns that machine into
something on screen. Behavior is written once and every substrate inherits it.
`pnpm scaffold <name>` stamps a new primitive across the substrates.

## Preflight

Expand Down Expand Up @@ -111,8 +116,7 @@ whenever possible, otherwise open it up for discussion.

Check whether the SPEC still describes the code: loop back or ship it.
Before shipping: tests, lint, and type-check pass, and the change is
verified across all scopes. If something's off, loop back to SPEC or
TEST; if not, ship it!
verified across all scopes. If something's off, loop back to SPEC; if not, ship it!

## Code

Expand Down
79 changes: 60 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,63 @@
# dunky-dev template repo

A pnpm-workspace monorepo starter with the shared dunky-dev tooling baked in.
Use it as a GitHub **template repository** (_Use this template_) or copy it with
`npx degit dunky-dev/template-repo my-new-repo`.

## What's included

| Tool | File | Purpose |
| --------------- | ------------------------------- | ---------------------------------------------------------------------------------------- |
| **oxlint** | `.oxlintrc.json` | Linting (eslint/import/react/unicorn/vitest plugins). |
| **oxfmt** | `.oxfmtrc.json` | Formatting — house style: no semicolons, single quotes, width 100, kebab-case filenames. |
| **lint-staged** | `.lintstagedrc.json` | Runs `oxlint --fix` + `oxfmt` on staged `*.{ts,tsx}`. |
| **husky** | `.husky/pre-commit` | Runs lint-staged before each commit. Installed via the `prepare` script. |
| **knip** | `knip.config.ts` | Unused-export/dependency detection (minimal config — extend per package). |
| **TypeScript** | `tsconfig.json` | Strict, ESM, bundler resolution. One `@dunky-dev/core` path alias. |
| **Vitest** | `vitest.config.ts` | Node test environment. |
| **pnpm** | `.npmrc`, `pnpm-workspace.yaml` | Workspace tuning; packages live under `packages/**`. |
| **CI** | `.github/workflows/ci.yml` | Lint + typecheck + test on push/PR (Node 24, pnpm 10.20.0). |
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/dunky-dev/logo/main/logo-white%402x.png" />
<img src="https://raw.githubusercontent.com/dunky-dev/logo/main/logo%402x.png" alt="Dunky" width="400px" />
</picture>
</p>

# UI

**The components (powered by [Dunky's state-machine](https://github.com/dunky-dev/state-machine))**

Dunky splits a UI component into two things: _behavior_ and _render_. The
state-machine repo is the engine — it models behavior as a plain, framework-free
state machine. **This repo is the other half: the UI.** It's where the components
live, and everything needed to render them — each primitive's behavior, the DOM
utilities they share, and the thin per-substrate bindings that turn a machine into
something you can put on screen.

Every primitive is modeled once as a framework-free
machine (its **core**) and delivered through a thin binding per host environment.

```
@dunky.dev/state-machine the engine + bindings
|
v
+-------------------------------+
| core primitive | packages/core/<name>
| states . events . a11y | pure behavior — no DOM, no framework
+---------------+---------------+
|
| connect() -> logical bindings
| (onPress, role, labelledBy, data-state, ...)
|
+--------------+--------------+
v v v
+-----------+ +-----------+ +-----------+
| substrate | | substrate | | substrate | packages/<substrate>/<name>
| (react) | | (vue) | | (native) | render + host wiring
+-----------+ +-----------+ +-----------+
same behavior, same a11y — only the render differs
```

## Layout

`packages/` is a grid: one directory per layer, one package per primitive.

- **`core/`** — the behavior. One package per primitive: a framework-free state
machine built on `@dunky.dev/state-machine`. No DOM, no framework. Published
as `@dunky.dev/<name>`.
- **`dom/`** — framework-free DOM utilities shared across primitives and
substrates (focus trap, scroll lock, the bindings translation). Published as
`@dunky.dev/dom-<name>`.
- **`<substrate>/`** — the render. A thin binding per host that wires the machine
to real elements. Published as `@dunky.dev/<substrate>-<name>` (e.g.
`@dunky.dev/react-dialog`).

The dependency direction is one-way: `substrate -> core -> engine`. A binding
adds no behavior of its own — if a decision is needed, it moves into the core
machine so every substrate inherits it. The deep reference is
[ARCHITECTURE.md](./ARCHITECTURE.md).

## License

Expand Down
Loading