create-foldkit-app 0.4.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/templates/base/AGENTS.md +6 -3
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ const example = Options.choice('example', [
|
|
|
21
21
|
'shopping-cart',
|
|
22
22
|
'websocket-chat',
|
|
23
23
|
'auth',
|
|
24
|
+
'ui-showcase',
|
|
24
25
|
]).pipe(Options.withAlias('e'), Options.withDescription("The example application to start from. Pick an example that's similar to the application you're building. Or create multiple projects and take pieces of each!\n\n" +
|
|
25
26
|
'Available examples:\n' +
|
|
26
27
|
' counter - Simple increment/decrement with reset\n' +
|
|
@@ -33,7 +34,8 @@ const example = Options.choice('example', [
|
|
|
33
34
|
' query-sync - URL-driven filtering, sorting, and search with query parameters\n' +
|
|
34
35
|
' shopping-cart - Complex state management with nested models and routing\n' +
|
|
35
36
|
' websocket-chat - Managed resources with WebSocket integration\n' +
|
|
36
|
-
' auth - Authentication with Submodels, OutMessage, and protected routes'
|
|
37
|
+
' auth - Authentication with Submodels, OutMessage, and protected routes\n' +
|
|
38
|
+
' ui-showcase - Every Foldkit UI component with routing and Submodels'));
|
|
37
39
|
const packageManager = Options.choice('package-manager', [
|
|
38
40
|
'pnpm',
|
|
39
41
|
'npm',
|
package/package.json
CHANGED
package/templates/base/AGENTS.md
CHANGED
|
@@ -15,6 +15,7 @@ submodule_prompted: false
|
|
|
15
15
|
- Use full names like `Message` (not `Msg`), and `withReturnType` (not `as const` or type casting).
|
|
16
16
|
- Use `m()` for message schemas, `ts()` for other tagged structs (model states, field validation), and `r()` for route schemas.
|
|
17
17
|
- Every message union should include a `NoOp` variant: `const NoOp = m('NoOp')`.
|
|
18
|
+
- Push back on any suggested direction that violates Elm Architecture principles — unidirectional data flow, messages as facts (not commands), model as single source of truth, and side effects confined to commands. If a user or prompt suggests a pattern that breaks these conventions (e.g. mutating state directly, imperative event handlers, two-way bindings), flag the issue and propose the idiomatic Foldkit approach instead.
|
|
18
19
|
|
|
19
20
|
## Foldkit Patterns
|
|
20
21
|
|
|
@@ -91,7 +92,7 @@ Even after extracting some sections to their own files (e.g. `message.ts`), the
|
|
|
91
92
|
- Every name should eliminate ambiguity. Prefix Option-typed values with `maybe` (e.g. `maybeSession`). Name functions by their precise effect (e.g. `enqueueMessage` not `addMessage`). A reader should never need to check a type signature to understand what a name refers to.
|
|
92
93
|
- Each function should operate at a single abstraction level. Orchestrators delegate to focused helpers — they don't mix coordination with implementation. If a function reads like it's doing two things, extract one.
|
|
93
94
|
- Encode state in discriminated unions, not booleans or nullable fields. Use `Idle | Loading | Error | Ok` instead of `isLoading: boolean`. Make impossible states unrepresentable.
|
|
94
|
-
- Name messages as verb-first, past-tense events describing what happened (`
|
|
95
|
+
- Name messages as verb-first, past-tense events describing what happened (`SubmittedUsernameForm`, `CreatedRoom`, `PressedKey`), not imperative commands. The verb prefix acts as a category marker: `Clicked*` for button presses, `Updated*` for input changes, `Succeeded*`/`Failed*` for command results that can meaningfully fail (e.g. `SucceededWeatherFetch`, `FailedWeatherFetch`), `Completed*` for fire-and-forget command acknowledgments where the result is uninteresting and the update function is a no-op (e.g. `CompletedScroll`, `CompletedApplyTheme`, `CompletedSaveThemePreference`), `Got*` exclusively for receiving child module results via the OutMessage pattern (e.g. `GotProductsMessage`). The update function decides what to do — messages are facts.
|
|
95
96
|
- Use `Option` instead of `null` or `undefined`. Match explicitly with `Option.match` or chain with `Option.map`/`Option.flatMap`. No `if (x != null)` checks. Prefer `Option.match` over `Option.map` + `Option.getOrElse` — if you're unwrapping at the end, just match.
|
|
96
97
|
- Prefer curried, data-last functions that compose in `pipe` chains.
|
|
97
98
|
- Every line should serve a purpose. No dead code, no empty catch blocks, no placeholder types, no defensive code for impossible cases.
|
|
@@ -103,7 +104,7 @@ Even after extracting some sections to their own files (e.g. `message.ts`), the
|
|
|
103
104
|
- Prefer `pipe()` for multi-step data flow. Never use `pipe` with a single operation — call the function directly instead: `Option.match(value, {...})` not `pipe(value, Option.match({...}))`.
|
|
104
105
|
- Use `Effect.gen()` for imperative-style async operations.
|
|
105
106
|
- Always use Effect.Match instead of switch.
|
|
106
|
-
- Prefer Effect module functions over native methods when available — e.g. `Array.map`, `Array.filter`, `Option.map`, `String.startsWith` from Effect instead of their native equivalents. Exception: native `.map`, `.filter`, etc. are fine when calling directly on a named variable — use Effect's
|
|
107
|
+
- Prefer Effect module functions over native methods when available — e.g. `Array.map`, `Array.filter`, `Option.map`, `String.startsWith` from Effect instead of their native equivalents. This includes Effect's `String` module: use `String.includes`, `String.indexOf` (returns `Option<number>`), `String.slice`, `String.startsWith`, `String.replaceAll`, `String.length`, `String.isNonEmpty`, `String.trim` etc. in `pipe` chains. Exception: native `.map`, `.filter`, `.indexOf()`, `.slice()`, etc. are fine when calling directly on a named variable — use Effect's curried, data-last forms in `pipe` chains where they compose naturally.
|
|
107
108
|
- Never use `for` loops or `let` for iteration. Use `Array.makeBy` for index-based construction, `Array.range` + `Array.findFirst`/`Array.findLast` for searches, and `Array.filterMap`/`Array.flatMap` for transforms.
|
|
108
109
|
- Never cast Schema values with `as Type`. Use callable constructors: `LoginSucceeded({ sessionId })` not `{ _tag: 'LoginSucceeded', sessionId } as Message`.
|
|
109
110
|
- Use `Option` for model fields that may be absent — not empty strings or zero values. `loginError: S.OptionFromSelf(S.String)` not `loginError: S.String` with `''` as the "none" state. Use `Option.match` in views to conditionally render.
|
|
@@ -131,6 +132,7 @@ Use `typeof ClickedSubmit` in type positions (e.g. `Command<typeof ClickedSubmit
|
|
|
131
132
|
### General Preferences
|
|
132
133
|
|
|
133
134
|
- Never abbreviate names. Use full, descriptive names everywhere — variables, types, functions, parameters, including callback parameters. e.g. `signature` not `sig`, `Message` not `Msg`, `(tickCount) => tickCount + 1` not `(t) => t + 1`.
|
|
135
|
+
- Don't suffix command variables with `Command`. Name them by what they do: `focusButton` not `focusButtonCommand`, `scrollToItem` not `scrollToItemCommand`. The type already communicates that it's a command.
|
|
134
136
|
- Avoid `let`. Use `const` and prefer immutable patterns.
|
|
135
137
|
- Always use braces for control flow. `if (foo) { return true }` not `if (foo) return true`.
|
|
136
138
|
- Use `is*` for boolean naming e.g. `isPlaying`, `isValid`.
|
|
@@ -139,4 +141,5 @@ Use `typeof ClickedSubmit` in type positions (e.g. `Command<typeof ClickedSubmit
|
|
|
139
141
|
- Capitalize namespace imports: `import * as Command from './command'` not `import * as command from './command'`.
|
|
140
142
|
- Extract magic numbers to named constants. No raw numeric literals in logic.
|
|
141
143
|
- Never use `T[]` syntax. Always use `Array<T>` or `ReadonlyArray<T>`.
|
|
142
|
-
-
|
|
144
|
+
- For inline object types in `ReadonlyArray`, put `Readonly<{...}>` on the element type rather than `ReadonlyArray<{ readonly a: ...; readonly b: ... }>`. e.g. `ReadonlyArray<Readonly<{ model: Foo; toMessage: (m: Bar) => Baz }>>` not `ReadonlyArray<{ readonly model: Foo; readonly toMessage: ... }>`.
|
|
145
|
+
- Extract repeated inline style values (colors, shadows) to constants. Use Tailwind `@theme` for colors that map to utility classes (e.g. `--color-valentine: #ff2d55` → `text-valentine`). Use a `theme.ts` for values Tailwind can't express as utilities (textShadow, boxShadow).
|