kerfjs 4.2.0-beta.8 → 4.2.0
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/CHANGELOG.md +8 -0
- package/README.md +30 -3
- package/ai/manifest.json +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [4.2.0] - 2026-08-20
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
- Added a **Virtual list** example app — a 10,000-row virtualized list showcasing the companion subpaths together: `kerfjs/list` viewport virtualization (only a screenful in the DOM), `kerfjs/timing` debounced search, and `kerfjs/overlay` confirm-to-delete with a toast. Includes a live "in the DOM" counter that stays flat as you scroll all 10,000 rows.
|
|
14
|
+
- Expanded the README with a companion-subpaths spotlight covering `list`, `overlay`, `async`, `scope`, `timing`, `remount`, `attach`, and `actions`, with `bindList` fixed and measured-height virtualization examples.
|
|
15
|
+
- Corrected the complete-apps index to accurately describe the eight standalone showcase apps and note that the cart and counter-store apps live in the migration guides.
|
|
16
|
+
|
|
9
17
|
- Fixed (beta): `bindList` measured-height virtualization (`virtualize: { rowHeight: { estimate } }`) never pruned reported heights, so a list with key churn (a feed prepending new ids over a long session) grew its internal height map without bound. Reported heights are now pruned to the live key set on each rebuild — no leak, and a key that leaves and later returns is re-measured (uses the estimate again) rather than reusing a stale height. A key that only scrolls out of the window keeps its measurement (it's still in the source).
|
|
10
18
|
- Fixed (beta regression): `popover()` / `tooltip()` (and `positionAnchored` / `autoReposition`) mispositioned horizontally — the anchored element was measured while still `display:block`, so its width read as the full body-content width and the viewport clamp slid it to the body's left edge instead of aligning it to the anchor. `positionAnchored` now sets `position: fixed` before measuring, so it uses the element's real (shrink-to-fit) size.
|
|
11
19
|
- **`renderDocument(node, options?)`** (main barrel) — a tiny SSR helper that prepends the doctype to a rendered document, so server routes stop reinventing `"<!DOCTYPE html>" + page.toString()`. Takes a `SafeHtml` or string; optional `{ doctype }` (default `'html'`). Pure string work, no DOM dependency.
|
package/README.md
CHANGED
|
@@ -51,11 +51,13 @@ Here's the whole development loop — write a component, run the dev server, cli
|
|
|
51
51
|
|
|
52
52
|
6. **JSX typed against HTML, not against React.** Tags and attributes are checked at compile time — `<diiv>` and `<input typo />` don't build. The attribute types are derived from the HTML standard rather than another framework's property table, and that distinction has teeth: `draggable` and `spellcheck` are *enumerated* attributes that take the strings `"true"` / `"false"`, so kerf rejects `draggable={true}` rather than quietly emitting markup that means the opposite. Custom elements and web components slot in with one declaration merge.
|
|
53
53
|
|
|
54
|
-
7. **Small public API.** ~
|
|
54
|
+
7. **Small public API.** ~18 exports from the main barrel (plus `arraySignal`, the `html` tagged template, and the companion-utility subpaths below — each opt-in, none in the core). No hooks, no lifecycle, no per-instance state. Components are plain functions that return JSX.
|
|
55
55
|
|
|
56
|
-
8. **
|
|
56
|
+
8. **Batteries on their own subpaths.** Nine optional, tree-shakeable subpaths cover the patterns every real app otherwise hand-rolls — **`kerfjs/list`** (a keyed list with per-row fine-grained mounts and fixed / app-declared / measured-height viewport **virtualization**), **`kerfjs/overlay`** (modals, `confirm` / `prompt` / `form` / `choice`, anchored popovers + tooltips, toasts), **`kerfjs/async`** (`resource` async-state with a built-in stale-response guard + SWR cache), **`kerfjs/scope`** (dispose-scopes that tie teardown to a DOM node's lifetime), plus `timing`, `remount`, `attach`, and `actions`. None of them grows the ~12 KB core until you import it.
|
|
57
57
|
|
|
58
|
-
9. **
|
|
58
|
+
9. **Plain TS, plain JSX, plain ESM.** Drops into anything using esbuild / Vite / tsup. No plugin chain. And with the `html` tagged template (`import { html } from 'kerfjs/html'` — identical runtime semantics to JSX), a CDN / importmap project needs no build step at all.
|
|
59
|
+
|
|
60
|
+
10. **Grown-up tooling around a tiny core.** An [ESLint plugin](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) that enforces the hard rules at edit time, an opt-in family of `KERF_DEV_WARN_*` runtime warnings that catch the classic mistakes in development (with zero production cost), a `create-kerf-component` scaffold for publishable component packages, drop-in AI-assistant configs, and side-by-side migration guides for a dozen-plus frameworks — none of which grows the core runtime past ~12 KB.
|
|
59
61
|
|
|
60
62
|
## When to use Kerf
|
|
61
63
|
|
|
@@ -202,6 +204,31 @@ Same algorithm `mount()` uses internally — `data-morph-skip`, `data-morph-skip
|
|
|
202
204
|
|
|
203
205
|
Nothing is self-hosted — `kerfjs` is on npm, so every ESM CDN (esm.sh, jsDelivr, unpkg) mirrors it automatically. esm.sh works with a direct import as shown; jsDelivr / unpkg want an importmap so the internal `@preact/signals-core` import resolves. Pin to a major (`@4`, as shown — the latest `4.x`) rather than floating on `latest`, or an exact version (`@4.1.0`) for full reproducibility. Attribute names are written verbatim (`class`, not `className`), and holes are only legal in text positions or as a complete attribute value — anything ambiguous throws with an actionable message. See [`docs/6-jsx-runtime.md`](./docs/6-jsx-runtime.md) §6.11 (§6.11.1 for the full CDN / importmap recipes) — or the [live-poll example](https://brianwestphal.github.io/kerf/examples/complete/live-poll/), a complete app served exactly as authored: no bundler ever touches it.
|
|
204
206
|
|
|
207
|
+
### Batteries when you need them: the companion subpaths
|
|
208
|
+
|
|
209
|
+
The core stays tiny because the patterns every real app rebuilds live in optional, tree-shakeable subpaths — a modal you'd otherwise hand-roll (`kerfjs/overlay`), an async-state container with the stale-response race already solved (`kerfjs/async`), a debounce that composes inside the reactive graph (`kerfjs/timing`), teardown tied to a DOM node's lifetime (`kerfjs/scope`). The largest is `kerfjs/list` — a keyed list that mounts each row individually (so a signal one row reads updates just that row) and virtualizes a long viewport, with fixed, app-declared, or measured row heights:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { bindList, observeRowHeights } from 'kerfjs/list';
|
|
213
|
+
|
|
214
|
+
// Fixed-height windowing: only the visible rows render.
|
|
215
|
+
bindList(scrollEl, rows, {
|
|
216
|
+
key: (r) => r.id,
|
|
217
|
+
render: (r) => <div class="row">{r.label}</div>,
|
|
218
|
+
virtualize: { rowHeight: 32 },
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Measured heights (chat, feeds): kerf estimates, you report the real height.
|
|
222
|
+
const list = bindList(scrollEl, messages, {
|
|
223
|
+
key: (m) => m.id,
|
|
224
|
+
render: (m) => <div class="msg">{m.text}</div>,
|
|
225
|
+
virtualize: { rowHeight: { estimate: 64 } },
|
|
226
|
+
});
|
|
227
|
+
observeRowHeights(list); // one ResizeObserver → kerf anchor-corrects scroll
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Each subpath adds nothing to the main barrel until it's imported. See [`docs/8-api-reference.md`](./docs/8-api-reference.md) for the full list (`list`, `overlay`, `scope`, `async`, `timing`, `remount`, `attach`, `actions`).
|
|
231
|
+
|
|
205
232
|
## Install
|
|
206
233
|
|
|
207
234
|
```bash
|
package/ai/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kerfjs",
|
|
3
|
-
"version": "4.2.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Tiny reactive UI framework — fine-grained signals + DOM morphing + JSX. Apply the smallest possible cut to update your DOM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
"@typescript-eslint/eslint-plugin": "^8.65.0",
|
|
165
165
|
"@typescript-eslint/parser": "^8.65.0",
|
|
166
166
|
"@vitest/coverage-v8": "^4.1.10",
|
|
167
|
-
"domotion-svg": "^0.
|
|
167
|
+
"domotion-svg": "^0.24.0",
|
|
168
168
|
"eslint": "^10.8.0",
|
|
169
169
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
170
170
|
"gitgist": "^1.1.0",
|