create-foldkit-app 0.7.0 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-foldkit-app",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Create Foldkit applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,8 +15,8 @@
15
15
  "@effect/platform-node": "4.0.0-beta.59",
16
16
  "chalk": "^5.6.2",
17
17
  "effect": "4.0.0-beta.59",
18
- "rimraf": "^6.1.2",
19
- "typescript": "^6.0.2"
18
+ "rimraf": "^6.1.3",
19
+ "typescript": "^6.0.3"
20
20
  },
21
21
  "keywords": [
22
22
  "create-foldkit-app",
@@ -89,31 +89,38 @@ Command definitions live where they're produced — colocated with the update fu
89
89
 
90
90
  ### Mount
91
91
 
92
- For per-element DOM work focusing an input, handing the live `Element` to a third-party library define a Mount with `Mount.define` and attach it to a view element with `OnMount`. The runtime runs the Effect when the element mounts, dispatches its result Message back through `update`, and runs the paired cleanup on unmount.
92
+ For per-element DOM work that needs the live `Element` handle (anchor positioning, portaling an overlay, attaching observers, handing the element to a third-party library), define a Mount with `Mount.define` and attach it to a view element with `OnMount`. The runtime runs the Effect when the element mounts, dispatches its result Message back through `update`, and runs the paired cleanup on unmount.
93
93
 
94
94
  ```ts
95
- const CompletedFocusInput = m('CompletedFocusInput')
95
+ const CompletedPortalToBody = m('CompletedPortalToBody')
96
96
 
97
- const FocusInput = Mount.define('FocusInput', CompletedFocusInput)
97
+ const PortalToBody = Mount.define('PortalToBody', CompletedPortalToBody)
98
98
 
99
- const focusInput = FocusInput(element =>
99
+ const portalToBody = PortalToBody(element =>
100
100
  Effect.sync(() => {
101
- if (element instanceof HTMLInputElement) {
102
- element.focus()
103
- }
101
+ document.body.appendChild(element)
104
102
  return {
105
- message: CompletedFocusInput(),
106
- cleanup: Function.constVoid,
103
+ message: CompletedPortalToBody(),
104
+ cleanup: () => element.remove(),
107
105
  }
108
106
  }),
109
107
  )
110
108
 
111
109
  // In view:
112
- input([Type('search'), OnMount(focusInput)])
110
+ div([Class('fixed inset-0 bg-black/50'), OnMount(portalToBody)])
113
111
  ```
114
112
 
115
113
  Cleanup is data, paired with setup as a single value. For setup with no cleanup, pass `Function.constVoid`. The `Completed*` Message marks the lifecycle without forcing a meaningful response in update.
116
114
 
115
+ Two rules for Mount, both must hold:
116
+
117
+ 1. **The Effect uses the element parameter.** Mount provides the live element handle, and that handle is what makes Mount distinct from Command. If your Effect doesn't read or write the element, pick a different primitive.
118
+ 2. **The work is DOM measurement or DOM manipulation on that element.** Read its geometry, mutate its CSS, attach an observer to it, portal it, hand it to a third-party library. Anything else (network, storage, focus-on-transition, scroll lock for the page) belongs in a Command returned from `update`.
119
+
120
+ Mount Effects re-run during DevTools time-travel renders. The two rules above keep Mount work inherently replay-safe (read-only measurement, idempotent DOM mutation, paired observer attach + cleanup).
121
+
122
+ Don't reach for Mount just because the work happens to coincide with an element appearing. Check what causes the work. If a Message just dispatched (like `Opened`), the cause is the Message, not the element. Use a Command returned from `update`'s handler instead. Example: focusing a search input when its dialog opens. The cause is `Opened`, not the input's existence; return a `FocusInput` Command from the `Opened` handler.
123
+
117
124
  ### File Organization
118
125
 
119
126
  Use uppercase section headers (`// MODEL`, `// MESSAGE`, `// INIT`, `// UPDATE`, `// COMMAND`, `// VIEW`) to make files easier to skim. These are for wayfinding — they make it clear where things live and where new code should go. Use domain-specific headers too when it helps (e.g. `// PHYSICS`, `// ROUTING`).
@@ -187,7 +194,7 @@ Use `typeof ClickedSubmit` in type positions to reference a schema value's type.
187
194
  - Avoid `let`. Use `const` and prefer immutable patterns.
188
195
  - Always use braces for control flow. `if (foo) { return true }` not `if (foo) return true`.
189
196
  - Use `is*` for boolean naming e.g. `isPlaying`, `isValid`.
190
- - Don't add inline or block comments to explain code if code needs explanation, refactor for clarity or use better names. Exceptions: section headers (see File Organization above) and TSDoc (`/** ... */`) on public exports.
197
+ - Don't add inline or block comments to explain code. If code needs explanation, refactor for clarity or use better names. Exceptions: section headers (see File Organization above), TSDoc (`/** ... */`) on public exports, and `// NOTE:` comments. Reserve `// NOTE:` for behavior that would mislead a careful reader into breaking things: a timing dependency that's silent if violated, a workaround for an upstream bug, a browser quirk that costs real debugging time to rediscover. The bar is high. When in doubt, delete it.
191
198
  - Use capitalized string literals for Schema literal types: `S.Literals(['Horizontal', 'Vertical'])` not `S.Literals(['horizontal', 'vertical'])`.
192
199
  - Capitalize namespace imports: `import * as Command from './command'` not `import * as command from './command'`.
193
200
  - Extract magic numbers to named constants. No raw numeric literals in logic.