Skip to content

Commit 5507a3b

Browse files
committed
Add llms.txt for AI-assisted Reflex project scaffolding and state architecture; update package version to 0.1.25 and include llms.txt in package files
1 parent ace18c4 commit 5507a3b

3 files changed

Lines changed: 218 additions & 3 deletions

File tree

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,39 @@ After many years of building applications with re-frame in the ClojureScript wor
3232
- [Step-by-Step Tutorial](https://reflex.js.org/docs/quick-start.html)
3333
- [Best Practices](https://reflex.js.org/docs/api-reference.html)
3434
- [API Reference](https://reflex.js.org/docs/best-practices.html)
35+
- [AI Context (llms.txt)](./llms.txt) - Compact guide for AI-assisted Reflex project scaffolding and state architecture
3536
- [re-frame Documentation](https://day8.github.io/re-frame/re-frame/) - The original and comprehensive guide to understanding the philosophy and patterns
3637

3738
- Examples
3839
- [TodoMVC](https://github.com/flexsurfer/reflex/tree/main/examples/todomvc) - Classic todo app implementation showcasing core reflex patterns
39-
- [Einbürgerungstest](https://github.com/flexsurfer/einburgerungstest/) - German citizenship test app built with reflex ([Live Demo](https://www.ebtest.org/))
40+
- [Einbürgerungstest](https://github.com/flexsurfer/einburgerungstest/) - Cross-platform web/mobile app built with reflex ([Live Demo](https://www.ebtest.org/))
41+
- [StarRupture Planner](https://github.com/flexsurfer/starrupture-planner) - Production planning tool built with reflex ([Live Demo](https://www.starrupture-planner.com/))
42+
43+
## 🤖 Using with AI Assistants
44+
45+
Reflex ships an [`llms.txt`](./llms.txt) file — a compact, AI-readable guide covering state architecture, event/effect/subscription patterns, and code generation rules. Point your AI tool at it so it generates idiomatic Reflex code from the start.
46+
47+
**Claude Code** — add to `CLAUDE.md` in your project root:
48+
```bash
49+
curl -o CLAUDE.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
50+
```
51+
52+
**Codex (OpenAI)** — add to `AGENTS.md` in your project root:
53+
```bash
54+
curl -o AGENTS.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
55+
```
56+
Codex reads project instructions from `AGENTS.md`, so this gives it Reflex-specific architecture and code generation rules for your repo.
57+
58+
**Cursor** — create `.cursor/rules/reflex.mdc` and paste the contents, or reference via project rules.
59+
60+
**GitHub Copilot** — add to `.github/copilot-instructions.md`:
61+
```bash
62+
curl -o .github/copilot-instructions.md https://raw.githubusercontent.com/flexsurfer/reflex/main/llms.txt
63+
```
64+
65+
**ChatGPT / Claude.ai Projects** — upload `llms.txt` from your `node_modules/@flexsurfer/reflex/` as a project file, or paste the raw URL into the conversation.
66+
67+
The file is also included in the npm package, so after installing Reflex you can find it at `node_modules/@flexsurfer/reflex/llms.txt`.
4068

4169
## 🤝 Contributing
4270

llms.txt

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
## 0) Reflex Quick Info
2+
3+
- Reflex is a React state-management library inspired by ClojureScript re-frame (event-driven updates, subscriptions for derived data, effects/coeffects for side effects).
4+
- Install:
5+
- Runtime: `npm i @flexsurfer/reflex`
6+
- Devtools (dev only): `npm i -D @flexsurfer/reflex-devtools`
7+
- Docs:
8+
- Main docs: [reflex.js.org/docs](https://reflex.js.org/docs)
9+
- Best practices: [reflex.js.org/docs/best-practices.html](https://reflex.js.org/docs/best-practices.html)
10+
- Packages: [@flexsurfer/reflex](https://www.npmjs.com/package/@flexsurfer/reflex), [@flexsurfer/reflex-devtools](https://www.npmjs.com/package/@flexsurfer/reflex-devtools)
11+
12+
## 1) State Architecture
13+
14+
Use this baseline structure:
15+
16+
```text
17+
src/state/
18+
db.ts
19+
event-ids.ts
20+
events.ts
21+
effect-ids.ts
22+
effects.ts
23+
sub-ids.ts
24+
subs.ts
25+
```
26+
27+
Rules:
28+
- Keep IDs centralized in `event-ids.ts`, `effect-ids.ts`, `sub-ids.ts`.
29+
- Keep init in `db.ts` (`initAppDb(...)`).
30+
- Register events/effects/subscriptions via side-effect imports in app bootstrap (`main.tsx` style).
31+
- If `events.ts` or `subs.ts` grows too large, split by feature, keep shared/global state separate.
32+
33+
## 2) State Shape
34+
35+
- Grow horizontally (new top-level feature keys), avoid deep nesting.
36+
- Normalize entity-like data (`byId` maps + id arrays) for fast lookup and simpler updates.
37+
- Keep UI state explicit and separate from domain entities.
38+
- If using `Map`/`Set` in DB, call `enableMapSet()` before `initAppDb`.
39+
40+
## 3) Events `regEvent`
41+
42+
- Events must be synchronous and focused on state transitions.
43+
- Events should read all required data from `draftDb` (or via coeffects) — never rely on callers passing subscription-derived state; dispatch calls from views should only carry user intent (IDs, input values, flags).
44+
- Validate inputs and guard clauses first; return early on invalid state.
45+
- Mutate only required fields on `draftDb`.
46+
- Avoid unnecessary object/array recreation (`{...obj}`, `[...arr]`) when no actual change is needed.
47+
- Never perform async/API/localStorage work directly in events.
48+
- Return effect tuples for side effects.
49+
- When sending mutated draft data to effects, always use `current(...)`.
50+
- Prefer deterministic coeffects (time/id/random/env) instead of direct globals for testability.
51+
52+
Event naming:
53+
- Keep exported constant keys in `UPPER_SNAKE_CASE`.
54+
- Use namespaced string values: `feature/action` (example: `bases/create`).
55+
- Keep key and value aligned:
56+
- `BASES_CREATE` -> `bases/create`
57+
- `PRODUCTION_PLAN_ADD_BUILDINGS_TO_BASE` -> `production_plan/add_buildings_to_base`
58+
59+
## 4) Effects and Coeffects
60+
61+
- Put all I/O here: localStorage, HTTP, timers, analytics, navigation.
62+
- Effects should be small, defensive, and fail-soft (log, do not crash app state flow).
63+
64+
## 5) Subscriptions `regSub`
65+
66+
- Define root subscriptions first `regSub(id, "pathKey")`.
67+
- Build derived subscriptions from other subscriptions only.
68+
- Use parameterized subscriptions for by-id and section-specific queries.
69+
- Keep subscriptions deterministic and lightweight.
70+
- Subscriptions must return data shaped and ready for direct view consumption — all filtering, sorting, formatting, and joining should happen in the subscription layer, not in React components.
71+
- Move heavy computations to events (precompute once, read many); avoid repeated expensive derivations in hot subscriptions.
72+
73+
## 6) React Component Contract
74+
75+
- Components should only:
76+
- subscribe to minimal required data
77+
- dispatch events on user intent (pass only user-provided values — e.g. input text, selected id — never forward subscription data back through dispatch; the event handler should read everything it needs from the DB itself)
78+
- render UI
79+
- Never transform, filter, sort, or reshape subscription data inside a component — if the view needs a different shape, create a dedicated subscription that returns it ready to render.
80+
- Use direct React hooks only for local/ephemeral component concerns:
81+
- temporary form/input draft state before dispatch
82+
- UI-only toggles scoped to one component (hover/open/focus)
83+
- refs, DOM measurement, animation lifecycle
84+
- Do not mirror Reflex global state in `useState`/`useReducer`.
85+
- If state is shared, persisted, or business-relevant, keep it in Reflex DB via events/subscriptions.
86+
- Keep `useEffect` thin in components; business side effects belong in Reflex effects/coeffects.
87+
- Do not place business rules/validation pipelines in component handlers.
88+
- Avoid over-subscription (row/item components should not subscribe to full collections).
89+
90+
## 7) Test Minimum
91+
92+
For every new feature:
93+
- Event tests: mutation correctness + emitted effect tuples.
94+
- Subscription tests: derived outputs from fixed state fixtures.
95+
96+
## 8) AI Generation Checklist
97+
98+
Before finalizing generated code:
99+
- IDs added/updated in all relevant `*-ids.ts` files.
100+
- Event namespaced and descriptive.
101+
- Side effects isolated to effects/coeffects.
102+
- `current(...)` used when passing draft-derived data to effects.
103+
- No unnecessary object/array recreation in events.
104+
- Expensive work not placed in frequently re-run subscriptions.
105+
- Subscriptions return view-ready data; components do not reshape subscription output.
106+
- Dispatch calls from components pass only user intent, not subscription data; events read from DB.
107+
- Tests added for new event/subscription behavior.
108+
109+
## 9) Starter Skeleton (copy pattern)
110+
111+
```ts
112+
import {
113+
initAppDb,
114+
regSub,
115+
regEvent,
116+
regEffect,
117+
regCoeffect,
118+
current,
119+
dispatch,
120+
useSubscription,
121+
} from '@flexsurfer/reflex';
122+
123+
// event-ids.ts
124+
export const EVENT_IDS = {
125+
APP_INIT: 'app/init',
126+
TODOS_ADD: 'todos/add',
127+
} as const;
128+
129+
// effect-ids.ts
130+
export const EFFECT_IDS = {
131+
GET_TODOS: 'storage/get_todos',
132+
SET_TODOS: 'storage/set_todos',
133+
} as const;
134+
135+
// sub-ids.ts
136+
export const SUB_IDS = {
137+
TODOS_LIST: 'todos/list', // root sub
138+
TODOS_OPEN_COUNT: 'todos/open_count', // computed sub
139+
} as const;
140+
141+
// db.ts
142+
type Todo = { id: string; text: string; done: boolean };
143+
initAppDb({ todos: [] as Todo[] });
144+
145+
// effects.ts
146+
regEffect(EFFECT_IDS.SET_TODOS, (todos: Todo[]) => {
147+
localStorage.setItem('todos', JSON.stringify(todos));
148+
});
149+
150+
regCoeffect(EFFECT_IDS.GET_TODOS, (coeffects) => {
151+
const raw = localStorage.getItem('todos');
152+
coeffects.localStoreTodos = raw ? (JSON.parse(raw) as Todo[]) : [];
153+
return coeffects;
154+
});
155+
156+
// events.ts
157+
regEvent(
158+
EVENT_IDS.APP_INIT,
159+
({ draftDb, localStoreTodos }) => {
160+
draftDb.todos = Array.isArray(localStoreTodos) ? localStoreTodos : [];
161+
},
162+
[[EFFECT_IDS.GET_TODOS]]
163+
);
164+
165+
regEvent(EVENT_IDS.TODOS_ADD, ({ draftDb }, text: string) => {
166+
const clean = text.trim();
167+
if (!clean) return;
168+
draftDb.todos.push({ id: `todo_${Date.now()}`, text: clean, done: false });
169+
return [[EFFECT_IDS.SET_TODOS, current(draftDb.todos)]];
170+
});
171+
172+
// subs.ts
173+
regSub(SUB_IDS.TODOS_LIST, 'todos'); // root sub
174+
175+
regSub(
176+
SUB_IDS.TODOS_OPEN_COUNT, // computed sub from root sub
177+
(todos: Todo[]) => todos.filter((t) => !t.done).length,
178+
() => [[SUB_IDS.TODOS_LIST]]
179+
);
180+
181+
// Usage example (React):
182+
// const todos = useSubscription([SUB_IDS.TODOS_LIST]);
183+
// const openCount = useSubscription([SUB_IDS.TODOS_OPEN_COUNT]);
184+
// dispatch([EVENT_IDS.APP_INIT]);
185+
// dispatch([EVENT_IDS.TODOS_ADD, 'Buy milk']);
186+
```

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@flexsurfer/reflex",
3-
"version": "0.1.24",
3+
"version": "0.1.25",
44
"license": "MIT",
55
"repository": {
66
"type": "git",
@@ -23,7 +23,8 @@
2323
"files": [
2424
"dist",
2525
"README.md",
26-
"LICENSE"
26+
"LICENSE",
27+
"llms.txt"
2728
],
2829
"scripts": {
2930
"build": "tsup",

0 commit comments

Comments
 (0)