kerfjs 4.4.0 → 5.0.0-beta.3
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 +116 -0
- package/LICENSE +1 -1
- package/README.md +34 -35
- package/ai/cursorrules +46 -1
- package/ai/manifest.json +62 -5
- package/ai/skill.md +53 -2
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +4 -4
- package/dist/actions.js.map +1 -1
- package/dist/array-signal.js +5 -5
- package/dist/async.js +17 -10
- package/dist/async.js.map +1 -1
- package/dist/attach.d.ts +11 -8
- package/dist/attach.js +52 -4
- package/dist/attach.js.map +1 -1
- package/dist/chunk-GY4XV2UV.js +1 -1
- package/dist/{chunk-VVDJLWMP.js → chunk-HW7KSM2Y.js} +2 -2
- package/dist/chunk-HW7KSM2Y.js.map +1 -0
- package/dist/{chunk-KEZTD6H4.js → chunk-KPXIOG2C.js} +3 -3
- package/dist/{chunk-KEZTD6H4.js.map → chunk-KPXIOG2C.js.map} +1 -1
- package/dist/{chunk-SAYPJ6XR.js → chunk-KZJXHFIB.js} +10 -6
- package/dist/chunk-KZJXHFIB.js.map +1 -0
- package/dist/{chunk-MRYM3O3V.js → chunk-LVH3GC6B.js} +11 -8
- package/dist/chunk-LVH3GC6B.js.map +1 -0
- package/dist/chunk-QIP723L4.js +1 -1
- package/dist/{chunk-SRWQKB33.js → chunk-SVATPF5R.js} +97 -74
- package/dist/chunk-SVATPF5R.js.map +1 -0
- package/dist/{chunk-3APBEVHF.js → chunk-U6FK33SG.js} +3 -3
- package/dist/{chunk-3APBEVHF.js.map → chunk-U6FK33SG.js.map} +1 -1
- package/dist/{chunk-U32TFTGZ.js → chunk-UZJ6I4T6.js} +3 -3
- package/dist/chunk-UZJ6I4T6.js.map +1 -0
- package/dist/{chunk-YHH7OUFA.js → chunk-V46JKE44.js} +3 -3
- package/dist/chunk-V46JKE44.js.map +1 -0
- package/dist/{chunk-SUPUPSBE.js → chunk-ZDCJZCNO.js} +9 -8
- package/dist/chunk-ZDCJZCNO.js.map +1 -0
- package/dist/dev.d.ts +9 -6
- package/dist/dev.js +5 -5
- package/dist/dev.js.map +1 -1
- package/dist/html.d.ts +1 -1
- package/dist/html.js +5 -5
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -11
- package/dist/jsx-runtime.js +5 -5
- package/dist/list.d.ts +1 -1
- package/dist/list.js +273 -211
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +190 -280
- package/dist/overlay.js +401 -361
- package/dist/overlay.js.map +1 -1
- package/dist/remount.d.ts +5 -3
- package/dist/remount.js +29 -10
- package/dist/remount.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.js +44 -22
- package/dist/router.js.map +1 -1
- package/dist/scope.d.ts +4 -3
- package/dist/scope.js +10 -10
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +4 -4
- package/dist/timing.d.ts +3 -2
- package/dist/timing.js +5 -5
- package/dist/timing.js.map +1 -1
- package/llms.txt +7 -4
- package/package.json +12 -10
- package/dist/chunk-MRYM3O3V.js.map +0 -1
- package/dist/chunk-SAYPJ6XR.js.map +0 -1
- package/dist/chunk-SRWQKB33.js.map +0 -1
- package/dist/chunk-SUPUPSBE.js.map +0 -1
- package/dist/chunk-U32TFTGZ.js.map +0 -1
- package/dist/chunk-VVDJLWMP.js.map +0 -1
- package/dist/chunk-YHH7OUFA.js.map +0 -1
- /package/dist/{attrSelector-Cmu2ZoGO.d.ts → attr-Cmu2ZoGO.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,122 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
- Fixed the complete kanban example's mobile layout by stacking its columns and header controls without horizontal overflow while preserving the wider board layout.
|
|
10
|
+
- Fixed the complete dashboard example's mobile layout so its status header, chart, and table fit the viewport without horizontal overflow.
|
|
11
|
+
- Decomposed `bindList` into focused keyed-row and virtualization controllers while keeping `list.ts` as the public orchestration entry; snapshot and granular updates now share one item-replacement contract.
|
|
12
|
+
- Corrected the canonical, published, and AI-facing dev-warning documentation to reflect the hook-only production boundary: kerf never gates diagnostics on `NODE_ENV`; omitting `kerfjs/dev` makes them unreachable, while installed opt-in warnings check only their own switch.
|
|
13
|
+
- Added the missing always-on row-key warning to the published diagnostics guide and made the documentation gate require every canonical diagnostic section on the public site.
|
|
14
|
+
- Restored the site-wide Playwright visual QA command with dynamic discovery of every built HTML surface (including `404.html`), desktop/tablet/mobile full-page captures, root- and URL-aware route filtering, collision-safe evidence names, and automated page-health checks for overflow, settled images, and collapsed content.
|
|
15
|
+
- Kept `observeRemovals()` scopes alive when their nodes move between parents or reorder within the observed root; automatic disposal now waits for permanent removal from that root.
|
|
16
|
+
- Made `remountOn()` disposal idempotent and ownership-safe: the first call tears down its mounted subtree and stops the key watcher, while repeated calls leave any later external content in the parent untouched.
|
|
17
|
+
- Made `throttle().cancel()` effective from inside both leading and trailing callbacks: the callback can now reset the active rate window immediately instead of having a new cooldown installed after it returns.
|
|
18
|
+
- Removed the overlay barrel/helper cycle by extracting the lifecycle core and splitting `confirm`, `prompt`, `form`, and `choice` into focused internal modules, without changing the public `kerfjs/overlay` API.
|
|
19
|
+
- Fixed `attach()` for nodes prepared before insertion: setup still runs immediately, teardown waits through the initially-disconnected phase, then fires once after the node has connected and is later removed (including direct insertion into an already-connected shadow root, ancestor removal, same-batch light-DOM insertion/removal, and removal through a shadow host).
|
|
20
|
+
- Made `bindList` reject duplicate keys before changing the DOM, with errors that name the key and both indices; rejected `arraySignal` batches are drained and the next valid state snapshot-recovers before granular updates resume.
|
|
21
|
+
- Made `bindList` recover after a granular row render throws: the failing call still reports the error, while the next source update reconciles the authoritative snapshot before granular patches resume, including when an earlier patch in the same batch already changed the DOM.
|
|
22
|
+
- Fixed stores created before `kerfjs/dev` installation never emitting the opt-in narrow-set warning. Store actions now resolve the warning hook at `set()` call time and allocate per-store dedup state only when diagnostics are present.
|
|
23
|
+
- Added a deterministic `bindList` transition matrix covering empty/refill recovery, granular-to-snapshot-to-granular sequences, and mixed batched structural changes with node-identity assertions.
|
|
24
|
+
- Made concurrent fallback overlays arbitrate dismissal from the top down, so one Escape, backdrop, or outside click closes only the active modal or non-modal surface.
|
|
25
|
+
- Made `prompt()` and `form()` reject malformed bring-your-own dialog markup immediately: missing required input markers now close the incomplete overlay and throw an error naming the exact marker (and form field) instead of failing later on interaction.
|
|
26
|
+
- Covered `each()` count-drift recovery through the public API: after a granular insert drains its patches and row rendering fails, a subsequent update now has a regression test proving the snapshot path repairs the DOM.
|
|
27
|
+
- Fixed tooltips hiding when just one of their pointer/focus triggers left; pointer and focus presence are now tracked independently, so either interaction keeps the tooltip open until both end.
|
|
28
|
+
- Strengthened the `bindList` feature index with exact existing guards for same-key rebuilds, granular patch sequences, and teardown semantics.
|
|
29
|
+
- Extracted pure granular-list index-shift and cache-key-drift stages from the `each()` transition coordinator for direct testing.
|
|
30
|
+
- Normalized internal helper module filenames to the repository's kebab-case or primary-export convention and updated all live imports and documentation paths.
|
|
31
|
+
- Made list-binding rationale comments self-contained and named the bounded template/URL diagnostic excerpt limits.
|
|
32
|
+
- Split `mount()`'s render effect into named synchronous static-render and list-reconcile/commit phases without changing lifecycle order.
|
|
33
|
+
- Replaced terse router and overlay-position internals with descriptive route-segment, event, rectangle, and viewport names.
|
|
34
|
+
- Preserved edited stale AI-assistant configs as forks by validating their versioned canonical section against shipped historical hashes before offering an autofix.
|
|
35
|
+
- Required every complete example app to have its own feature-coverage row mapped to a smoke test inside that app's Playwright suite.
|
|
36
|
+
- Made router parameter decoding fail closed to no-match on malformed percent escapes instead of throwing `URIError`, including named and wildcard captures.
|
|
37
|
+
- Pointed `@kerfjs/ui` package metadata at the existing published component-packages documentation route and added a source-backed metadata regression test.
|
|
38
|
+
- Fixed the complete router example's broken documentation links and taught the docs-example gate to validate published routes and heading fragments.
|
|
39
|
+
- Added full-pipeline coverage for missing-row-key diagnostics across initial, snapshot, granular, and in-place list reconciliation, including per-binding deduplication and production silence.
|
|
40
|
+
- Extended the feature-completeness gate to inventory router-subpath exports and deduplicate values re-exported across public surfaces.
|
|
41
|
+
- Kept `kerfjs/ai-assistant-configs` filesystem checks read-only during plain ESLint runs while retaining explicit `eslint --fix` installation and stale-file updates.
|
|
42
|
+
- Required history-router bases to match an exact path or path-segment boundary, so `/app` no longer strips or intercepts `/apple`.
|
|
43
|
+
- Added the missing MIT `LICENSE` to generated `create-kerf-component` packages, including package-name token replacement and publish-contents coverage.
|
|
44
|
+
- Made dangerous-URL attribute-name matching ASCII-case-insensitive so mixed-case spellings cannot bypass static or bound screening.
|
|
45
|
+
- Normalized synchronous `resource().run()` fetcher throws into the same stale-guarded failed state and resolving promise used for asynchronous rejections.
|
|
46
|
+
- Escaped plain-string `toast()` content as text while preserving trusted `SafeHtml` and render-function markup, closing a stored-markup injection path.
|
|
47
|
+
- Rejected non-finite and fractional indices in every indexed `arraySignal` mutator before changing source state or emitting a patch; even equal `move()` indices are now validated before the no-op path.
|
|
48
|
+
- Made `DisclosureArrow` animate configurable directions over the shortest rotation path, including the left-to-up demo's natural 90-degree clockwise turn; closed-to-open 180-degree ties use counterclockwise rotation.
|
|
49
|
+
- Reworked `DialogHeader` as a true top toolbar: its 24px icon in a 34px circle and first title line align with automatically grouped actions, while an optional subtitle remains below; added localized action-group labels and compatibility for existing pre-grouped actions.
|
|
50
|
+
- Gave every `ValueTableRow` 8px of root-scaled top and bottom padding while preserving its semantic inline inset and icon-aware separator alignment.
|
|
51
|
+
- Fixed the composer-form recipe's Reset action so upgraded Web Awesome input and textarea controls clear their live displayed values together with the controlled Kerf state, while retaining the `Draft reset` announcement.
|
|
52
|
+
- Removed the unsolicited command-palette catalog demo and production recipe while retaining the earlier, independent application-local adapter example for missing-concept guidance.
|
|
53
|
+
- Kept the `ResizableRegion` UX specimen readable by moving committed-width status below the controlled pane at narrow widths, preserving its 200% root-scaled split, and retaining local scroll access to the handle at its maximum size.
|
|
54
|
+
- Reworked the composer-form recipe into one coherent visible form surface with exactly three transparent 8/1/8 content sections, 24px major rhythm, and a conditional `StateBanner` as its only nested semantic surface.
|
|
55
|
+
- Made `MenuHeader` fill its available inline width, align separate actions at the logical end in both LTR and RTL, and use an overridable 18px visible action glyph without shrinking the 44px target.
|
|
56
|
+
- Added first-class `MenuHeader` count semantics with a required localized `countLabel`, zero-safe neutral pill presentation, accessible heading/disclosure naming, legacy non-count badge exclusivity, and runtime filtering for invalid counts and protected presence flags.
|
|
57
|
+
- Made `MenuHeader` toggle mode supply the production 18px `DisclosureArrow` by default while retaining custom `actionIcon` replacement, removed competing raw SVG sizing/rotation, and made the menu-composition Tools disclosure control real content instead of showing a false Projects chevron.
|
|
58
|
+
- Repaired the `DisclosureArrow` UX specimen by replacing its ambiguous sorting glyph with a right-facing arrow, putting both examples in independently controlled native buttons with stable names and `aria-expanded`, and demonstrating custom directions through delegated pointer and keyboard activation across narrow and 200%-zoom layouts.
|
|
59
|
+
- Set `DisclosureArrow` to an 18px root-scaled default, removed the catalog-only 24px enlargement, retained `--kui-disclosure-arrow-size` for scoped consumer overrides, and kept Kerf `Select` on its independent `.5` Web Awesome expand-glyph scale.
|
|
60
|
+
- Added a site-scoped CI dependency audit that includes the complete static-site build tree and fails on high or critical advisories while keeping low/moderate development-tool findings informational.
|
|
61
|
+
- Aligned the UX catalog's header-composition dialog with the shared 8px inline layout gutter while preserving its 16px vertical separation.
|
|
62
|
+
- Added visible `View demo source` and `Read guidance` affordances to every UI catalog detail, plus `View component source` for first-party components, backed by deterministic repository-relative paths for the main renderer, individual recipe files, Web Awesome specimens, and canonical browser-import implementations. Web Awesome entries explicitly label local documentation as Kerf integration guidance; all links use deploy-safe GitHub URLs and a responsive accessible layout.
|
|
63
|
+
- Added runtime-safe `AppTab.rootAttributes` for domain `data-*` metadata plus decorative `AppTab.closeIcon` and `ResizableRegion.handleIcon` slots, preserving existing tab and resize wiring ownership while blocking case-folded component/action/drag/drop collisions.
|
|
64
|
+
- Added `MenuActionRow`, a full-width navigation row with sibling primary and trailing native-button actions, independent names and disabled states, controlled selection semantics, safe metadata/popover extension slots, and responsive 44px targets. `MenuItem.trailing` is now explicitly documented as dormant content.
|
|
65
|
+
- Added safe typed MenuItem/MenuHeader extension slots for application `data-*` metadata and native popover trigger relationships while protecting component-owned action, selection, disclosure, accessible-name, disabled, icon, and native-button semantics.
|
|
66
|
+
- Fixed keyboard deletion of a controlled `TokenSearchField` chip so `wireTokenSearchFields` restores focus and the text-relative caret after the application rerenders the editor.
|
|
67
|
+
- Aligned `@kerfjs/ui` CSS guidance and AI regression scoring with the catalog's exact public anatomy: documented public-class composition selectors are supported, private tag/id/attribute/unlisted-class descendants remain rejected, and historical measured runs replay under their recorded stricter oracle.
|
|
68
|
+
- Added typed `ValueTableRow` composition with a first-class optional icon hook. Value-table separators now align 8px from both edges for iconless rows and 40px from the left plus 8px from the right for rows with a 24px leading icon.
|
|
69
|
+
- Added first-class collapsible `TokenSearchField` support: an empty closed field becomes one iconic search action, controlled activation animates to the complete searchbox, and text or tokens keep it expanded after blur. It works standalone or when composed inside `ToolbarControlGroup`; the UX demo demonstrates that common toolbar composition at wide and narrow sizes.
|
|
70
|
+
- Restored the public documentation site to its Astro + Starlight implementation while the Kerf UI redesign is reconsidered, and removed the in-progress `@kerfjs/ui` page and promotional links from public navigation.
|
|
71
|
+
- Fixed `TokenSearchField` first-line alignment so the leading icon, editable text, clear action, and trailing content share the field's vertical center while remaining pinned when text wraps.
|
|
72
|
+
- Replaced sidebar-specific and wrapper-inset UI geometry with one shared pane/content model across sidebars, main areas, inspectors, and dialogs. Unpadded panes now compose optional toolbars, one scrolling content stack, and optional footers; content children own consistent 8px margin, 1px transparent-or-visible border, 8px padding, 12px or 22px radii, and 24px major separation. Toolbar groups retain 44px geometry even with transparent chrome, and `MenuHeader` now supports a badge while separating dormant title content from its action.
|
|
73
|
+
- Added pixel-first `remify(<px>)` authoring for `@kerfjs/ui` styles. The build converts values exactly against a 16px baseline into ordinary `rem`, package CSS exports now deliver compiled `dist/styles/` files, and the UX catalog applies the same transform during Vite development so CSS edits remain hot-reloaded.
|
|
74
|
+
- Added a side-effect-free `@kerfjs/ui/webawesome` TypeScript declaration subpath for all 70 catalog-supported Web Awesome elements, synchronized against the installed custom-elements manifest and covered by a downstream package-export compile fixture.
|
|
75
|
+
- Added a deterministic internal `@kerfjs/ui` AI-regression foundation with seven task-shaped prompts, frozen versioned context conditions, AST-aware structural scoring, generated public declaration signatures, equivalent supported import scoring in suite v2, and an opt-in non-executing TypeScript compile-evidence sidecar with exact response/config/package/declaration hashes. Adversarial fixtures catch component substitution, duplicated primitives, and spacing/layout ownership regressions without invoking a live model in CI.
|
|
76
|
+
- Added seven lazy, production-backed `@kerfjs/ui` composition recipes for application shells, sidebars, workspace headers, master-detail dialogs, composer forms, list-state lifecycles, and mixed-control toolbars, with stable catalog routes, canonical AI metadata, drift gates, responsive/keyboard/state coverage, and a copyable stable-root adapter that wires delegated actions, form/dialog events, resize commits, and idempotent disposal outside the catalog.
|
|
77
|
+
- Added a shipped, schema-described `@kerfjs/ui` machine-readable component catalog with deterministic typed UX projection and drift gates for public exports, package/CSS/registration paths, relationships, Web Awesome manifest coverage, AI guidance, routes, CSS hooks, and documentation links.
|
|
78
|
+
- Added a canonical `@kerfjs/ui/layout.css` vocabulary for responsive page, pane, section, control, metadata, surface, dialog, and scroll ownership, with semantic variables, compact/narrow behavior, consumer and AI guidance, normalized catalog compositions, and wide/intermediate/narrow/200%-zoom geometry coverage.
|
|
79
|
+
- Added a need-first `@kerfjs/ui` component-selection matrix and automated drift gate so AI tools choose among reuse, composition, thin application adapters, custom semantics, and overlapping Web Awesome components without inferring contracts from implementation CSS. A typed application-local command-palette example now shows how a missing package concept still uses canonical layout, one inset owner, and related-control grouping while keeping semantics honest and proposing recurring behavior upstream.
|
|
80
|
+
- Fixed `@kerfjs/ui` `Select` option icons disappearing after a Kerf rerender. Icon slots now have stable per-select/per-choice keys and preserve Web Awesome-owned slot state, while custom selected content is keyed by the controlled value so it updates cleanly.
|
|
81
|
+
- Added a shared `@kerfjs/ui` sidebar spacing composition with distinct 10px interaction and 20px content rails, 24px icon slots plus 10px gaps, and 44px row/header/toolbar targets. Menu headers, icon-bearing and iconless rows, bordered surfaces, and `.kui-sidebar-toolbar` now share public geometry tokens, consumer/AI guidance, responsive and RTL specimens, and browser assertions.
|
|
82
|
+
- Added a reusable controlled `TokenSearchField` to `@kerfjs/ui`, extracting Hot Sheet 2's free-text plus ordered atomic filter-chip field with accessible edit/remove/clear actions, DOM read and caret helpers, tree-shakeable styles, and full catalog coverage.
|
|
83
|
+
- Reorganized the largest catch-all unit suites into behavior-focused files while preserving all 1,554 existing tests, assertion counts, transition matrices, coverage, and feature-index mappings.
|
|
84
|
+
- Expanded the `@kerfjs/ui` pull-request browser gate from Chromium-only coverage to the full Chromium, Firefox, and WebKit suite, with Playwright binary caching keyed to the UI lockfile.
|
|
85
|
+
- Added a strict npm install-script policy for the site: only the reviewed `esbuild` and `sharp` binary installers run, the linked root package's Husky-only `prepare` is denied, and a preinstall checker detects lockfile installer drift.
|
|
86
|
+
- Updated the root and UI browser-test harnesses for Node 26: Playwright 1.63 removes the deprecated ESM loader registration, and normalized color variables keep web-server and worker output free of `NO_COLOR`/`FORCE_COLOR` conflict warnings.
|
|
87
|
+
- Cleaned generated root and `@kerfjs/ui` ESM shims so downstream bundlers no longer report ignored bare chunk imports, and deduplicated source-map directives without weakening package tree shaking.
|
|
88
|
+
- Added explicit Web Awesome component-selection guidance and aligned UX descriptions: prefer Kerf Select, SegmentedControl, TabBar, LucideIcon, and ResizableRegion for common app patterns; consider Popup for custom anchoring; reserve specialized ecosystem alternatives for concrete requirements. ResizableRegion now includes Hot Sheet 2's overridable 1px separator plus hover/focus grip.
|
|
89
|
+
- Made Web Awesome Tooltip and Popover arrowless by default in the optional Kerf theme, with public per-theme, scoped, and per-instance overrides plus focused UX and browser coverage.
|
|
90
|
+
- Documented Web Awesome Markdown's trusted-input-only security boundary, client-only rendering, and shared mutable Marked configuration, with an explicitly labeled UX specimen and a regression assertion for the warning.
|
|
91
|
+
- Clarified that Web Awesome Badge and Tag are the tree-shakeable generic primitives: status badges use the pill treatment, tags remain rounded rectangles, and domain adapters stay application-owned when they add mappings or mutation behavior.
|
|
92
|
+
- Unified Accordion, Details, Breadcrumb, and Kerf Select chevron scale through an overridable shared disclosure-icon token.
|
|
93
|
+
- Matched Web Awesome Carousel navigation to Kerf's compact disclosure geometry, using overridable 16px arrows and 7px visible page dots with accessible macOS-sized hit targets.
|
|
94
|
+
- Expanded the Web Awesome Animation UX specimen into an interactive settings panel with preset, easing, timing, playback, transport, lifecycle, and reduced-motion behavior.
|
|
95
|
+
- Fixed the Web Awesome Toast UX specimen to use the component's programmatic stack API, with visible browser coverage and an explicit note about Hot Sheet 2's current custom toast implementation.
|
|
96
|
+
- Made the Web Awesome intersection, mutation, and resize observer UX specimens interactive, with deliberate triggers, visible live-event results, and browser coverage.
|
|
97
|
+
- Updated the UI package's Vitest toolchain to 4.1.11 and resolved the remaining development-only npm audit advisories without changing its runtime dependency surface.
|
|
98
|
+
- Added a first-class controlled `SegmentedControl` to `@kerfjs/ui`, with Hot Sheet 2-aligned toolbar, rounded-rectangle, pill, equal-width, small, and disabled presentations; native pressed-button semantics; scoped palette overrides; tree-shakeable component CSS; and a complete UX-catalog route.
|
|
99
|
+
- Aligned `@kerfjs/ui` defaults with Hot Sheet 2, added overridable semantic state palettes, completed ToolbarControlGroup and StateBanner catalog variants, simplified related-component navigation, corrected MenuHeader action alignment, and added a controlled horizontally scrolling TabBar with pointer and keyboard reordering plus proximity-based edge autoscroll.
|
|
100
|
+
- Updated `@kerfjs/ui` to develop and test against Lucide 1.43.0 and Web Awesome 3.12.0, with matching peer baselines.
|
|
101
|
+
- Added an opt-in `@kerfjs/ui/webawesome.css` theme for Web Awesome's free components. It carries the Hot Sheet 2-aligned semantic palette, form and panel geometry, focus, tooltip, radius, and shadow choices without registering component JavaScript; the UX catalog now lists all 70 free Web Awesome 3.12 components under a collapsible ecosystem section, gives each a focused themed route and dependency links, and retains the aggregate light, dark, narrow, and scoped-override gallery.
|
|
102
|
+
- Fixed trapped overlays losing sequential focus in WebKit after the second control. Implicitly focusable descendants now receive `tabindex="0"` inside the trap, while authored tabindex values (including `-1`) remain intact.
|
|
103
|
+
- Fixed Firefox contenteditable carets jumping to the list container during keyed `moveBefore()` reorders. `each()` and `bindList` now restore exact contenteditable Selection boundary nodes and offsets after every move pass, even when the browser keeps `activeElement` unchanged.
|
|
104
|
+
- Repaired lockstep version metadata for the ESLint plugin and component scaffold, and added a release-time sync plus repository gate so package manifests, locks, plugin metadata, and published examples cannot drift again.
|
|
105
|
+
- Added the lockstep `@kerfjs/ui` package: accessible toolbar, menu, tab, header, resize, select, and feedback primitives; semantic CSS tokens and browser component subpaths that automatically include only reachable component styles; CSS-free root/SSR entries and an optional complete stylesheet; opt-in Web Awesome registration; AI-oriented docs; a production-backed UX catalog; bundle/unit/three-engine browser gates; and a least-privilege release workflow.
|
|
106
|
+
- The virtual-list demo animation opens on the full app again (header, the "N in the DOM" badge, and the list) before the scroll; the list now scrolls inside its own box while the header stays put (previously the whole frame was a full-bleed list strip), holds at row 0 before gliding instead of starting a few rows down, and the loop stays in phase on every pass. Captured with domotion-svg 0.28.2.
|
|
107
|
+
|
|
108
|
+
## [4.4.1] - 2026-08-26
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
- Overhauled the README landing page: new one-line tagline, npm/size/license/TypeScript-types badges, a hoisted "Quick start" (install + `tsconfig`) right under the hook, and a jump nav (Quick start · Why kerf · Quick tour · Docs). The counter example now shows `delegate()` wiring the click handler.
|
|
113
|
+
- Reworked the homepage hero to lead with the value proposition ("Reactive UI that touches only the bytes that changed"), with "Introducing Kerf" demoted to an eyebrow, a right-sized logo, tighter vertical spacing, and "Get started" as the single filled primary action ("View examples" now a secondary/outline button).
|
|
114
|
+
- Redesigned the complete-apps index as a card grid — each app appears once with an animated preview, a one-line summary, and "Run live" / "Source" buttons.
|
|
115
|
+
- Every complete-app page now shows "Run live" and "View source" as buttons above the demo instead of small inline text links.
|
|
116
|
+
- Rewrote the site's docs pages as hand-authored consumer content (cleaner headings, no internal section numbers) rather than verbatim copies of the internal design docs.
|
|
117
|
+
- Renamed the migration guides' "Side-by-side code" heading to "Section by section" (the sections stack code blocks rather than showing true columns), across all framework pages.
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
- The getting-started and Markdown-editor demo animations now open on a non-blank frame — real code and a rendered heading are visible immediately instead of an empty pane.
|
|
121
|
+
- Added a horizontal-scroll shadow affordance to wide tables so off-screen columns are discoverable on narrow viewports.
|
|
122
|
+
- Shortened the longest demo alt text on the router and virtual-list pages for more concise screen-reader output.
|
|
123
|
+
- Moved framework version labels out of the performance table's framework column onto a methodology line below, keeping the column a clean label.
|
|
124
|
+
|
|
9
125
|
## [4.4.0] - 2026-08-23
|
|
10
126
|
|
|
11
127
|
|
package/LICENSE
CHANGED
|
@@ -54,7 +54,7 @@ Steele-Idem, which is also distributed under the MIT License:
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
`src/
|
|
57
|
+
`src/attr.ts`'s `cssEscapeIdent` adapts the CSS identifier escaping of
|
|
58
58
|
the [CSS.escape polyfill](https://github.com/mathiasbynens/CSS.escape) by
|
|
59
59
|
Mathias Bynens, which is also distributed under the MIT License:
|
|
60
60
|
|
package/README.md
CHANGED
|
@@ -4,61 +4,72 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Kerf</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center"><em>The smallest cut.</em></p>
|
|
7
|
+
<p align="center"><em>A tiny reactive UI framework. The smallest cut.</em></p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://brianwestphal.github.io/kerf/"><strong>brianwestphal.github.io/kerf</strong></a> — docs · examples · live demo
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://www.npmjs.com/package/kerfjs"><img src="https://img.shields.io/npm/v/kerfjs.svg" alt="npm version" /></a>
|
|
15
|
+
<img src="https://img.shields.io/badge/min%2Bgzip-~12%20KB-brightgreen.svg" alt="~12 KB minified and gzipped" />
|
|
16
|
+
<img src="https://img.shields.io/npm/l/kerfjs.svg" alt="MIT license" />
|
|
17
|
+
<img src="https://img.shields.io/badge/types-included-3178c6.svg" alt="TypeScript types included" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
13
20
|
---
|
|
14
21
|
|
|
15
|
-
> Introducing Kerf.
|
|
16
|
-
> The smallest cut.
|
|
17
|
-
>
|
|
18
22
|
> ~12 KB. No virtual DOM. No compiler. No magic.
|
|
19
23
|
> Reactive UI that touches only the bytes that changed.
|
|
20
24
|
|
|
21
25
|
```ts
|
|
22
|
-
import { signal, mount } from 'kerfjs';
|
|
26
|
+
import { signal, mount, delegate } from 'kerfjs';
|
|
23
27
|
|
|
24
28
|
const count = signal(0);
|
|
29
|
+
const app = document.getElementById('app')!;
|
|
25
30
|
|
|
26
|
-
mount(
|
|
31
|
+
mount(app, () => (
|
|
27
32
|
<div>
|
|
28
33
|
<button data-action="inc">+</button>
|
|
29
34
|
<span>{count.value}</span>
|
|
30
35
|
</div>
|
|
31
36
|
));
|
|
37
|
+
|
|
38
|
+
delegate(app, 'click', '[data-action="inc"]', () => count.value++);
|
|
32
39
|
```
|
|
33
40
|
|
|
34
41
|
That's it. Your JSX renders to HTML strings, kerf's native diff applies the minimum DOM mutations to make the live tree match, and signals re-run the render only when something they read actually changed.
|
|
35
42
|
|
|
36
43
|
Here's the whole development loop — write a component, run the dev server, click around, edit, watch the browser pick it up:
|
|
37
44
|
|
|
38
|
-
[](https://brianwestphal.github.io/kerf/getting-started/)
|
|
46
|
+
**[Quick start](#quick-start) · [Why kerf](#why-kerf) · [Quick tour](#quick-tour) · [Docs & examples](https://brianwestphal.github.io/kerf/)**
|
|
41
47
|
|
|
42
|
-
|
|
48
|
+
## Quick start
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
```bash
|
|
51
|
+
npm install kerfjs
|
|
52
|
+
```
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
```jsonc
|
|
55
|
+
// tsconfig.json — point JSX at kerf
|
|
56
|
+
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "kerfjs" } }
|
|
57
|
+
```
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
Write plain `.tsx` and build with your existing esbuild / Vite / tsup — no extra plugin. New here? Read the [5-minute orientation](https://github.com/brianwestphal/kerf/blob/main/docs/orientation.md), or open a [complete example](https://brianwestphal.github.io/kerf/examples/complete/).
|
|
60
|
+
## Why Kerf
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
1. **~12 KB, one dependency.** ~12 KB minified + gzipped including `@preact/signals-core` (~13 KB with `arraySignal`). No virtual DOM, no scheduler, no concurrent-mode machinery. On the official [krausest benchmark](https://krausest.github.io/js-framework-benchmark/current.html) kerf sits in the same cluster as Vue, Lit, and vanjs; Solid's compiler leads the update-path benchmarks, which kerf doesn't try to match by design — no compiler.
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
2. **No virtual DOM, no compiler.** JSX → HTML strings → native diff. DevTools shows the real DOM because it *is* the DOM.
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
3. **Values bind, structure re-renders.** Hand a signal *itself* into a JSX hole — `class={selectedId}` — and kerf binds that one node: on change, only that attribute updates, with no render re-run and no list reconcile. Moving selection between rows in a 10,000-row table touches at most the old and new row classes. ([more →](#fine-grained-updates-bind-a-signal-into-a-hole))
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
4. **Focus, selection, and listeners survive re-renders — even mid-list.** The reconciler morphs instead of rebuilding, so caret position, IME composition, scroll, and delegated listeners survive every update; keyed rows are patched in place rather than recreated.
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
5. **Safe by default.** Text and attributes are HTML-escaped automatically, URL attributes are scheme-screened (`javascript:` dropped), and inline `on*` handlers are rejected outright — so untrusted data stays inert. `raw()` is the explicit, auditable opt-out.
|
|
61
71
|
|
|
72
|
+
**Plus, nothing you don't ask for:** JSX typed against the HTML standard (not React's props) · a ~18-export API with no hooks, lifecycle, or per-instance state · **nine** tree-shakeable companion subpaths (`router`, `list`, `overlay`, `async`, …) that stay out of the core until imported · an [ESLint plugin](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) + opt-in dev warnings + `create-kerf-component` scaffold · plain TS/JSX/ESM that drops into esbuild / Vite / tsup — or **no** build at all via the `html` tagged template.
|
|
62
73
|
## When to use Kerf
|
|
63
74
|
|
|
64
75
|
- **Hybrid desktop apps (Tauri / Electron)** — small bundle, predictable diff, debuggable runtime; ideal for the embedded webview.
|
|
@@ -147,7 +158,7 @@ mount(root, () => (
|
|
|
147
158
|
status.value = 'saving'; // updates the class + the text node directly — no re-render
|
|
148
159
|
```
|
|
149
160
|
|
|
150
|
-
The headline use is external state driving
|
|
161
|
+
The headline use is external state driving a hot spot: a `selectedId` moving between rows inside a 10,000-row `each()` list updates at most the old and new row class attributes, with no reconcile. Works in static content and inside `each()` rows (a row's binding is torn down with the row); outside a `mount()` (SSR / `SafeHtml.toString()`) a bound signal just snapshots its current value.
|
|
151
162
|
|
|
152
163
|
This is kerf's guiding idiom — *values bind, structure re-renders*: pass the signal itself wherever a hole is just a value, and read `.value` in the render function only where the JSX structure depends on it. A render that reads no `.value` runs exactly once; from then on every update is a direct write to the node it concerns. See [`docs/2-reactivity.md`](./docs/2-reactivity.md) §2.9.
|
|
153
164
|
|
|
@@ -247,21 +258,9 @@ mount(app, () => <div><nav>{/* <a href> links, auto-intercepted */}</nav>{router
|
|
|
247
258
|
|
|
248
259
|
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`, `router`, `overlay`, `scope`, `async`, `timing`, `remount`, `attach`, `actions`).
|
|
249
260
|
|
|
250
|
-
##
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
npm install kerfjs
|
|
254
|
-
```
|
|
261
|
+
## Optional tooling
|
|
255
262
|
|
|
256
|
-
|
|
257
|
-
// tsconfig.json
|
|
258
|
-
{
|
|
259
|
-
"compilerOptions": {
|
|
260
|
-
"jsx": "react-jsx",
|
|
261
|
-
"jsxImportSource": "kerfjs"
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
263
|
+
Install and JSX setup are in [Quick start](#quick-start) above. These companion packages are opt-in.
|
|
265
264
|
|
|
266
265
|
### Optional: `eslint-plugin-kerfjs`
|
|
267
266
|
|
package/ai/cursorrules
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- kerf-skill-version: 1.
|
|
1
|
+
<!-- kerf-skill-version: 1.20.0 -->
|
|
2
2
|
# kerf.cursorrules — rules for building apps with kerf
|
|
3
3
|
#
|
|
4
4
|
# Drop this file into your project as `.cursorrules` (Cursor will pick it
|
|
@@ -17,6 +17,51 @@ You are writing a UI in kerf — a ~12 KB reactive framework (~13 KB with `array
|
|
|
17
17
|
- **A component package must NEVER import `kerfjs/dev`.** The hooks are process-global, so installing them is the consuming app's decision — a library that does it forces the diagnostics (and the chunk) on every consumer. Put the import in your demo page or test harness instead.
|
|
18
18
|
- Recommended: also install `eslint-plugin-kerfjs` (`npm install --save-dev eslint-plugin-kerfjs`) and add `kerfjs.configs.recommended` to the project's eslint config. It enforces five of the hard rules below (no inline JSX event handlers, require `data-key` in `each()`, capture `delegate()` disposers, no nested `mount()`, prefer module JSX augmentation) at edit time so violations surface as IDE squiggles before any code runs.
|
|
19
19
|
|
|
20
|
+
## Optional first-party UI
|
|
21
|
+
|
|
22
|
+
Install `@kerfjs/ui` for shared toolbars, menu rows, controlled tab bars, headers/value tables,
|
|
23
|
+
resizable regions, selects, banners, empty states, and loading indicators.
|
|
24
|
+
Import visual components from explicit subpaths so a CSS-aware browser bundler
|
|
25
|
+
includes only their reachable styles. The root barrel and
|
|
26
|
+
`@kerfjs/ui/unstyled` are CSS-free; pair the barrel with `styles.css` only when
|
|
27
|
+
the complete layer is intentional. Reuse these primitives before creating
|
|
28
|
+
parallel local ones; keep domain state, commands, routing, and tab/menu policy
|
|
29
|
+
in the app.
|
|
30
|
+
|
|
31
|
+
Use MenuItem/MenuActionRow/MenuHeader `rootAttributes` only for application
|
|
32
|
+
`data-*` metadata. MenuActionRow `trailingActionAttributes` and MenuHeader
|
|
33
|
+
`triggerAttributes` additionally support native popover target/action and
|
|
34
|
+
`aria-controls`/`aria-haspopup`. Do not use these slots to override action,
|
|
35
|
+
item identity, selection, disclosure, naming, disabled, icon, or role
|
|
36
|
+
semantics; one `role="menuitem"` does not make a complete menu widget.
|
|
37
|
+
`MenuItem.trailing` is dormant. Use `MenuActionRow` for sibling primary and
|
|
38
|
+
trailing native buttons, and keep its `label`, `icon`, and
|
|
39
|
+
`trailingActionIcon` SafeHtml slots free of controls.
|
|
40
|
+
|
|
41
|
+
Wire emitted `data-action` hooks at the mount root and retain disposers.
|
|
42
|
+
`wireResizableRegions()` and `wireTabBars()` are eventful helpers and return
|
|
43
|
+
disposers. Compose `AppTab` inside controlled `TabBar`; `wireTabBars()` supplies
|
|
44
|
+
horizontal edge autoscroll while dragging. Apply reorder reports
|
|
45
|
+
with `reorderTabs()`, and keep order, selection, close policy, routing, panels,
|
|
46
|
+
and persistence in the host. Icons/spinners are decorative unless
|
|
47
|
+
labeled, and assertive banners are only for urgent interruption.
|
|
48
|
+
|
|
49
|
+
The opinionated semantic ramps match Hot Sheet 2 and Web Awesome. Override
|
|
50
|
+
`--kui-color-*` globally or component properties such as
|
|
51
|
+
`--kui-state-banner-background` at a tone or instance boundary; do not replace
|
|
52
|
+
private descendant selectors.
|
|
53
|
+
|
|
54
|
+
For Web Awesome's broader free component set, import the CSS-only
|
|
55
|
+
`@kerfjs/ui/webawesome.css` theme once, then import only each Web Awesome
|
|
56
|
+
component module the app renders. The theme registers no component JavaScript
|
|
57
|
+
and remains overridable through later or scoped `--wa-*` values.
|
|
58
|
+
|
|
59
|
+
`Select` is pure markup until the app explicitly imports
|
|
60
|
+
`@kerfjs/ui/select/register`. Do that once in the application entry that uses
|
|
61
|
+
Select; never hide registration inside a component. Web Awesome is an optional
|
|
62
|
+
peer and stays out of bundles that use neither Select nor the Web Awesome theme. More detail ships at
|
|
63
|
+
`node_modules/@kerfjs/ui/ai/skill.md`.
|
|
64
|
+
|
|
20
65
|
## Public API — one import path
|
|
21
66
|
|
|
22
67
|
```ts
|
package/ai/manifest.json
CHANGED
|
@@ -1,21 +1,78 @@
|
|
|
1
1
|
{
|
|
2
|
-
"kerfjsVersion": "
|
|
2
|
+
"kerfjsVersion": "5.0.0-beta.3",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"name": "skill",
|
|
6
6
|
"source": "kerf.claude-skill.md",
|
|
7
7
|
"bundle": "ai/skill.md",
|
|
8
8
|
"dest": ".claude/skills/kerf-app/SKILL.md",
|
|
9
|
-
"version": "1.
|
|
10
|
-
"sha256": "
|
|
9
|
+
"version": "1.21.0",
|
|
10
|
+
"sha256": "265b747eeb9c3d445c60d66a1109eec42705075b7cd154d107bd79d543c1e7e5",
|
|
11
|
+
"history": {
|
|
12
|
+
"1.0.0": "bef982cb743fd4c73e666ee5cf774112e57c93ebb13b24bbacbf08577db0930a",
|
|
13
|
+
"1.1.0": "f2aadb21e33dd389028951ae1ecbd9f8ba68648b3c5beba43fb4dfa2ab29f195",
|
|
14
|
+
"1.1.1": "f163d33ce1007dd24551503398160881377eedad1fb4f6d37d9d9b8c8100f143",
|
|
15
|
+
"1.2.0": "76550ec18da8e063123bfabc575d9ad3a43583d41a498c6e4b4be03c17dbd188",
|
|
16
|
+
"1.2.1": "290e57bfc9b4af0630c586770e61063742d5f5fac3d75406f5acbc2e5322440f",
|
|
17
|
+
"1.3.0": "789cfd9bbe0fa8a01b779595912b7fef569b3c021be412d35f453476e576bac6",
|
|
18
|
+
"1.4.0": "2c42d476174847cfca1aa941c1026195b6a1786074fa9c2bd0b5a287639543b7",
|
|
19
|
+
"1.5.0": "c4fae8806907e79507ac095a2e6fed1761ddf61e5c9bfa5ec28719897ea56613",
|
|
20
|
+
"1.6.0": "797b48ef0736b9113df9e300ab47109c83c5cdd582f110d0cde7f388a37f4a26",
|
|
21
|
+
"1.7.0": "033c6e538d3a48f36c52a1ba154c6c7782f6c7cca9399adee0fbd519cfaf5919",
|
|
22
|
+
"1.8.0": "76b8a88bc7aaf9d50e6f3eee7b57a976b3279127f5d4970599aef1f839a06655",
|
|
23
|
+
"1.8.1": "32b8239399d94b014927563c3ff4251df97c36dc31478d832dba5a4309c221fb",
|
|
24
|
+
"1.9.0": "3225206e0339a8f6e2f9c728b76cf22728983f5810d614eab6224f16c8f9c6d4",
|
|
25
|
+
"1.10.0": "4b8e73a44fba060cb2fc4c01ed87f3853ac13906ee759e982c5c140025dfd493",
|
|
26
|
+
"1.11.0": "4d7f6099cd47f76a9e85cfe953886eaeb650eeb11a540cbac45104362b32eda5",
|
|
27
|
+
"1.12.0": "7acf72fd1b2781054520cc5af66cd73ee2f44b43c643e981f1f7a92ead60f747",
|
|
28
|
+
"1.13.0": "6f9a7d00ff6ad4799c51b52da613dfc5c11f09abaf9b6a3b9ca960f05f30b927",
|
|
29
|
+
"1.14.0": "15786c4e2b5f5d51dcd17d973f29aef8348faaa1410e40d86259be599fb459c9",
|
|
30
|
+
"1.14.1": "15573973dabf6d53f67e51696c276732fb1d17a59bb843300e88200389305b1f",
|
|
31
|
+
"1.14.2": "3a9e4dd8d4ecfcf963e8d16f98ea98800a3c243f11bbb829134d8f7792a8d06d",
|
|
32
|
+
"1.15.0": "72db3f328ce4457bbecd31bfab6ee69b2d423d9bee7db8e2a01e7aa3ae369af9",
|
|
33
|
+
"1.16.0": "0fa5d8ef354059fe7e20843dcd0c6f38234f3018d47856503bf47ddff4541231",
|
|
34
|
+
"1.17.0": "e0e1f370a97bc94aa33ed7723ebcf0059a9c7613f1083e3b77f0c62aecc108a7",
|
|
35
|
+
"1.18.0": "d2d092b1e4d4d71012f9ec36921b3f5c1421f0a8b2ced50351d56daa9e35f79b",
|
|
36
|
+
"1.19.0": "bbbe87446cc4e1be250d27152adfd385431f4498cde320534f65f10f853ab7f9",
|
|
37
|
+
"1.20.0": "aa88ccdbf969624bbc6cefadba76ada1f090fae436597a7bab564e24c467287f",
|
|
38
|
+
"1.21.0": "265b747eeb9c3d445c60d66a1109eec42705075b7cd154d107bd79d543c1e7e5"
|
|
39
|
+
}
|
|
11
40
|
},
|
|
12
41
|
{
|
|
13
42
|
"name": "cursorrules",
|
|
14
43
|
"source": "kerf.cursorrules",
|
|
15
44
|
"bundle": "ai/cursorrules",
|
|
16
45
|
"dest": ".cursorrules",
|
|
17
|
-
"version": "1.
|
|
18
|
-
"sha256": "
|
|
46
|
+
"version": "1.20.0",
|
|
47
|
+
"sha256": "c4dac40630fc456c6c7e6ba9d1b75215bc76bd76e6f31ba36d3bc9ab867d46e0",
|
|
48
|
+
"history": {
|
|
49
|
+
"1.0.0": "98f7faff2b7585ef9cc511add42ea5d9bf0cbae8307b066af090e9281136d2ab",
|
|
50
|
+
"1.1.0": "a2ec8c221dc694c98b81009cd2d99f9159a45fda94585de2f54471b227099eda",
|
|
51
|
+
"1.1.1": "f28b963171b7c2cbc36823d7220080730cda85a41f105b90e81790c1188e6186",
|
|
52
|
+
"1.2.0": "dcf2fc03f5dfca3b9bfea8cfefc65ac8ce31e1c601c613e91af69b6195c5e267",
|
|
53
|
+
"1.2.1": "2fcd5e9e0fd889febdab19bcc7e415c342b10ae81e937852b8280014b8d374e0",
|
|
54
|
+
"1.3.0": "2524a42cc3b488f8cc3cea736cf5b8d3e57d868c85260342ec50905243aa65f1",
|
|
55
|
+
"1.4.0": "f2c0fb328d487cc4a64ccd0b671c3385f1a25074c8c5e502c507ee799cd65e80",
|
|
56
|
+
"1.5.0": "2c012cbffa2fd9f96a3b8cdca48b55e584530ee6e5be73c922d3bb49470b6c31",
|
|
57
|
+
"1.6.0": "9370876e58ac634936ca321bf5313bb339659cb7c7c1b38a9b10f2bf4b03836d",
|
|
58
|
+
"1.7.0": "8ce556caf8fa95f783f5423bf882a735fc3ce6b7eb83e823aaa0417b3272af15",
|
|
59
|
+
"1.8.0": "f91402df009941b4198ea659498f458d2433843d47a3b524b0ca5e2d3d2fc949",
|
|
60
|
+
"1.8.1": "eed385b8edf3b96e88d8f00ea45ee4146004a74d3f0e495b870a27c137d0ec44",
|
|
61
|
+
"1.9.0": "c3242e1aa8c5ca51fca3e2e3c3df8e55dffb5d6c7b5bc8d677949d48ff5563cc",
|
|
62
|
+
"1.10.0": "7adefcc5ab66fcc3e5ae10625d7c762e86ac02ca297648aa58b0b201b35b04f8",
|
|
63
|
+
"1.11.0": "506c3338ec1c2268912f5eb9530a6af04ad937061281b5ea84529fc8b10486ff",
|
|
64
|
+
"1.12.0": "48ff3d0f483a9fcd7f57ca20c17c0aadfce0e4a0b39d9e96e964ef9260f782d3",
|
|
65
|
+
"1.13.0": "3a6bf9b1c74e0be00053faddc2af3c979ba5bb26c11db93bfcb8b1ecbe66284d",
|
|
66
|
+
"1.14.0": "b83de9cff13789cdebf572f2e98d3d61fdd4f31c7eb237a0a4999b6a1dfe4d9b",
|
|
67
|
+
"1.14.1": "9d2cc9da69e974a56d79393ad3b3a9d76c068fbd46b392621790a82fc0772d00",
|
|
68
|
+
"1.14.2": "09d9f0290fe84d3244e1655c8284c8431b47cdbe220f7161f38bc96fc8ef8ba3",
|
|
69
|
+
"1.15.0": "eece0b3075ceeeec5f6a59393a82c105d28ad1a34374412fbf6a4e0c23d7e8b9",
|
|
70
|
+
"1.16.0": "49794d543ab1f9112f9f6a1dcdc7aad4347b8965e1b4003bb7884086a02411c3",
|
|
71
|
+
"1.17.0": "6ad51fb0eec468f584346d1d7b4d1c2eca2cb52b04891b38ba44e93b57eb9e1c",
|
|
72
|
+
"1.18.0": "de3eea1bc181da629694d19a0817d252a868e4704437fbdd9e923856b8a0890a",
|
|
73
|
+
"1.19.0": "c5b91e449f6feccaa36a04cb7f8550d3e414eb2795ea6c5dccdf6071206b3261",
|
|
74
|
+
"1.20.0": "c4dac40630fc456c6c7e6ba9d1b75215bc76bd76e6f31ba36d3bc9ab867d46e0"
|
|
75
|
+
}
|
|
19
76
|
}
|
|
20
77
|
]
|
|
21
78
|
}
|
package/ai/skill.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kerf-app
|
|
3
|
-
description: Build UIs in the kerf reactive framework (https://github.com/brianwestphal/kerf). Use this skill whenever the user is writing or modifying code that imports `kerfjs`, asks to add a feature to a kerf app, or asks "how do I do X in kerf?". Use it proactively the moment you spot a kerf import in the file you're editing.
|
|
4
|
-
kerf-skill-version: 1.
|
|
3
|
+
description: Build UIs in the kerf reactive framework and its @kerfjs/ui component package (https://github.com/brianwestphal/kerf). Use this skill whenever the user is writing or modifying code that imports `kerfjs` or `@kerfjs/ui`, asks to add a feature to a kerf app, or asks "how do I do X in kerf?". Use it proactively the moment you spot a kerf import in the file you're editing.
|
|
4
|
+
kerf-skill-version: 1.21.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Building apps with kerf
|
|
@@ -22,6 +22,57 @@ kerf is a ~12 KB reactive UI framework (~13 KB with `arraySignal`): signals + DO
|
|
|
22
22
|
- **A component package must NEVER import `kerfjs/dev`.** The hooks are process-global, so installing them is the consuming app's decision — a library that does it forces the diagnostics (and the chunk) on every consumer. Put the import in your demo page or test harness instead.
|
|
23
23
|
- Recommended companion: `npm install --save-dev eslint-plugin-kerfjs` and add `kerfjs.configs.recommended` to the project's eslint config. Enforces five of the hard rules below (no inline JSX event handlers, require `data-key` in `each()`, capture `delegate()` disposers, no nested `mount()`, prefer module JSX augmentation) at edit time — useful as a self-correction signal when authoring kerf code.
|
|
24
24
|
|
|
25
|
+
## Optional first-party UI
|
|
26
|
+
|
|
27
|
+
Install `@kerfjs/ui` when the app needs shared toolbars, menu rows, controlled tab bars,
|
|
28
|
+
headers/value tables, resizable regions, selects, banners, empty states, or
|
|
29
|
+
loading indicators. Import visual components from explicit subpaths so a
|
|
30
|
+
CSS-aware browser bundler includes only their reachable styles. The root barrel
|
|
31
|
+
and `@kerfjs/ui/unstyled` are CSS-free; pair the barrel with `styles.css` only
|
|
32
|
+
when the complete layer is intentional. Prefer a component before inventing a
|
|
33
|
+
parallel local primitive, but keep domain state, routing, commands, and tab/menu
|
|
34
|
+
policy in the app.
|
|
35
|
+
|
|
36
|
+
Use MenuItem/MenuActionRow/MenuHeader/AppTab `rootAttributes` only for application
|
|
37
|
+
`data-*` metadata. MenuActionRow `trailingActionAttributes` and MenuHeader
|
|
38
|
+
`triggerAttributes` additionally support native popover target/action and
|
|
39
|
+
`aria-controls`/`aria-haspopup`. Do not use these slots to override action,
|
|
40
|
+
item identity, selection, disclosure, naming, disabled, icon, or role
|
|
41
|
+
semantics; one `role="menuitem"` does not make a complete menu widget.
|
|
42
|
+
`MenuItem.trailing` is dormant. Use `MenuActionRow` for sibling primary and
|
|
43
|
+
trailing native buttons, and keep its `label`, `icon`, and
|
|
44
|
+
`trailingActionIcon` SafeHtml slots free of controls.
|
|
45
|
+
AppTab protects its component/action/identity/selection and transient drag/drop
|
|
46
|
+
attributes at runtime. Keep `AppTab.closeIcon` and
|
|
47
|
+
`ResizableRegion.handleIcon` decorative and free of controls; the existing
|
|
48
|
+
named controls and wiring retain interaction and disposal ownership.
|
|
49
|
+
|
|
50
|
+
Components emit stable `data-action` hooks; wire them at the mount root and
|
|
51
|
+
retain every disposer. `wireResizableRegions()` and `wireTabBars()` are the
|
|
52
|
+
explicit behavioral helpers and return disposers. Compose `AppTab` inside a
|
|
53
|
+
controlled `TabBar`; `wireTabBars()` supplies horizontal edge autoscroll during
|
|
54
|
+
dragging. Apply reorder reports with `reorderTabs()`, and keep order,
|
|
55
|
+
selection, close policy, routing, panels, and persistence in the host.
|
|
56
|
+
Icons and spinners are decorative unless labeled; use assertive banners only
|
|
57
|
+
for urgent interruption.
|
|
58
|
+
|
|
59
|
+
The opinionated semantic ramps match Hot Sheet 2 and Web Awesome. Override
|
|
60
|
+
`--kui-color-*` globally or component properties such as
|
|
61
|
+
`--kui-state-banner-background` at a tone or instance boundary; do not replace
|
|
62
|
+
private descendant selectors.
|
|
63
|
+
|
|
64
|
+
For Web Awesome's broader free component set, import the CSS-only
|
|
65
|
+
`@kerfjs/ui/webawesome.css` theme once, then import only each Web Awesome
|
|
66
|
+
component module the app renders. The theme registers no component JavaScript
|
|
67
|
+
and remains overridable through later or scoped `--wa-*` values.
|
|
68
|
+
|
|
69
|
+
`Select` renders pure Web Awesome markup. Import
|
|
70
|
+
`@kerfjs/ui/select/register` once in an application entry that uses it; never
|
|
71
|
+
hide that registration inside another component. Web Awesome is an optional
|
|
72
|
+
peer and must remain absent from bundles that use neither Select nor the Web
|
|
73
|
+
Awesome theme. The package's
|
|
74
|
+
full AI contract is at `node_modules/@kerfjs/ui/ai/skill.md`.
|
|
75
|
+
|
|
25
76
|
## Public API — one import path
|
|
26
77
|
|
|
27
78
|
```ts
|
package/dist/actions.d.ts
CHANGED
package/dist/actions.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { attr } from './chunk-
|
|
2
|
-
import { delegate } from './chunk-
|
|
3
|
-
|
|
1
|
+
import { attr } from './chunk-UZJ6I4T6.js';
|
|
2
|
+
import { delegate } from './chunk-KPXIOG2C.js';
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
// src/actions.ts
|
|
6
6
|
var DEFAULT_ACTION_ATTR = "data-action";
|
|
@@ -22,5 +22,5 @@ function delegateActions(root, eventType, table, options) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export { action, delegateActions };
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
//# sourceMappingURL=actions.js.map
|
package/dist/actions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/actions.ts"],"names":[],"mappings":";;;;;AAmCA,IAAM,mBAAA,GAAsB,aAAA;AAerB,SAAS,OAAyB,KAAA,EAAsC;AAC7E,EAAA,OAAO,IAAA,CAAK,qBAAqB,KAAK,CAAA;AACxC;AAwBO,SAAS,eAAA,CACd,IAAA,EACA,SAAA,EACA,KAAA,EACA,OAAA,EACY;AACZ,EAAA,MAAM,QAAA,GAAW,SAAS,IAAA,IAAQ,mBAAA;AAClC,EAAA,OAAO,QAAA;AAAA,IACL,IAAA;AAAA,IACA,SAAA;AAAA,IACA,IAAI,QAAQ,CAAA,CAAA,CAAA;AAAA,IACZ,CAAC,OAAO,EAAA,KAAO;AAEb,MAAA,MAAM,OAAA,GAAU,KAAA,CAAM,EAAA,CAAG,YAAA,CAAa,QAAQ,CAAW,CAAA;AACzD,MAAA,IAAI,OAAA,KAAY,MAAA,EAAW,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAAA,IAC9C,CAAA;AAAA,IACA;AAAA,GACF;AACF","file":"actions.js","sourcesContent":["/**\n * `kerfjs/actions` — the delegated action-table helper.\n *\n * The most-reinvented idiom across real kerf apps: one table of `data-action`\n * attribute specs used as the single source of truth for BOTH the JSX attribute\n * and the delegate selector, plus a hand-rolled `switch (dataset.action)`\n * dispatcher. This subpath blesses it as two thin helpers over the existing\n * `attr()` + `delegate()` — it does NOT replace them.\n *\n * import { action, delegateActions } from 'kerfjs/actions';\n *\n * const A = {\n * select: action('select-file'),\n * remove: action('remove-file'),\n * };\n *\n * // JSX — spread the attr (rename-safe; no hardcoded attribute name):\n * // <button {...A.select.attrs} data-id={id}>…</button>\n *\n * // Wire the whole table with ONE delegated listener; returns a disposer:\n * const dispose = delegateActions(root, 'click', {\n * [A.select.value]: (_e, el) => selectFile(el.getAttribute('data-id')),\n * [A.remove.value]: (_e, el) => removeFile(el.getAttribute('data-id')),\n * });\n *\n * Contract: `delegateActions` returns a `() => void` disposer and holds no\n * per-instance state — the same shape as `delegate()`, which it builds on (so\n * it inherits the single-listener dispatch and the capture auto-promotion for\n * well-known non-bubbling event types). One event type per call, mirroring\n * `delegate()`; collect the disposers for a root that needs several.\n */\nimport { attr, type AttrSpec } from './
|
|
1
|
+
{"version":3,"sources":["../src/actions.ts"],"names":[],"mappings":";;;;;AAmCA,IAAM,mBAAA,GAAsB,aAAA;AAerB,SAAS,OAAyB,KAAA,EAAsC;AAC7E,EAAA,OAAO,IAAA,CAAK,qBAAqB,KAAK,CAAA;AACxC;AAwBO,SAAS,eAAA,CACd,IAAA,EACA,SAAA,EACA,KAAA,EACA,OAAA,EACY;AACZ,EAAA,MAAM,QAAA,GAAW,SAAS,IAAA,IAAQ,mBAAA;AAClC,EAAA,OAAO,QAAA;AAAA,IACL,IAAA;AAAA,IACA,SAAA;AAAA,IACA,IAAI,QAAQ,CAAA,CAAA,CAAA;AAAA,IACZ,CAAC,OAAO,EAAA,KAAO;AAEb,MAAA,MAAM,OAAA,GAAU,KAAA,CAAM,EAAA,CAAG,YAAA,CAAa,QAAQ,CAAW,CAAA;AACzD,MAAA,IAAI,OAAA,KAAY,MAAA,EAAW,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAAA,IAC9C,CAAA;AAAA,IACA;AAAA,GACF;AACF","file":"actions.js","sourcesContent":["/**\n * `kerfjs/actions` — the delegated action-table helper.\n *\n * The most-reinvented idiom across real kerf apps: one table of `data-action`\n * attribute specs used as the single source of truth for BOTH the JSX attribute\n * and the delegate selector, plus a hand-rolled `switch (dataset.action)`\n * dispatcher. This subpath blesses it as two thin helpers over the existing\n * `attr()` + `delegate()` — it does NOT replace them.\n *\n * import { action, delegateActions } from 'kerfjs/actions';\n *\n * const A = {\n * select: action('select-file'),\n * remove: action('remove-file'),\n * };\n *\n * // JSX — spread the attr (rename-safe; no hardcoded attribute name):\n * // <button {...A.select.attrs} data-id={id}>…</button>\n *\n * // Wire the whole table with ONE delegated listener; returns a disposer:\n * const dispose = delegateActions(root, 'click', {\n * [A.select.value]: (_e, el) => selectFile(el.getAttribute('data-id')),\n * [A.remove.value]: (_e, el) => removeFile(el.getAttribute('data-id')),\n * });\n *\n * Contract: `delegateActions` returns a `() => void` disposer and holds no\n * per-instance state — the same shape as `delegate()`, which it builds on (so\n * it inherits the single-listener dispatch and the capture auto-promotion for\n * well-known non-bubbling event types). One event type per call, mirroring\n * `delegate()`; collect the disposers for a root that needs several.\n */\nimport { attr, type AttrSpec } from './attr.js';\nimport { delegate, type DelegateOptions } from './delegate.js';\n\n/** The attribute an action table keys on by default. */\nconst DEFAULT_ACTION_ATTR = 'data-action';\n\n/**\n * A handler in a {@link delegateActions} table. Receives the DOM event and the\n * matched element (walk-up `closest()` match by default) — the same shape as a\n * `delegate()` handler.\n */\nexport type ActionHandler<E extends Element = Element> = (event: Event, el: E) => void;\n\n/**\n * `action(value)` — an {@link AttrSpec} on `data-action`. A thin specialization\n * of `attr('data-action', value)`: spread its `.attrs` in JSX and use its\n * `.value` as the handler-table key, so the action name lives in exactly one\n * place and can't drift between the markup and the dispatcher.\n */\nexport function action<V extends string>(value: V): AttrSpec<'data-action', V> {\n return attr(DEFAULT_ACTION_ATTR, value);\n}\n\n/** Options for {@link delegateActions}. Extends {@link DelegateOptions}. */\nexport interface DelegateActionsOptions extends DelegateOptions {\n /**\n * The attribute the table keys on. Default `'data-action'`. Override it only\n * if you also author the specs with `attr(yourName, …)` instead of `action()`.\n */\n attr?: string;\n}\n\n/**\n * Wire a whole table of action handlers with ONE delegated listener.\n *\n * On `eventType`, the nearest element carrying the action attribute (walk-up\n * `closest()` by default; pass `{ match: 'direct' }` for an exact-element match)\n * is looked up in `table` by its attribute value, and the matching handler\n * runs. An element whose action is absent from the table is ignored — the same\n * behavior as a `switch (dataset.action)` with no matching `case`.\n *\n * Returns a `() => void` disposer. One event type per call (the smallest\n * surface, mirroring `delegate()`); collect the disposers when a root needs\n * several event types.\n */\nexport function delegateActions<E extends Element = Element>(\n root: HTMLElement,\n eventType: string,\n table: Readonly<Record<string, ActionHandler<E>>>,\n options?: DelegateActionsOptions,\n): () => void {\n const attrName = options?.attr ?? DEFAULT_ACTION_ATTR;\n return delegate<E>(\n root,\n eventType,\n `[${attrName}]`,\n (event, el) => {\n // `el` matched `[${attrName}]`, so the attribute is always present.\n const handler = table[el.getAttribute(attrName) as string];\n if (handler !== undefined) handler(event, el);\n },\n options,\n );\n}\n"]}
|
package/dist/array-signal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { ARRAY_SIGNAL_BRAND, ArraySignal, arraySignal } from './chunk-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export { ARRAY_SIGNAL_BRAND, ArraySignal, arraySignal } from './chunk-LVH3GC6B.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
6
|
//# sourceMappingURL=array-signal.js.map
|