bitwrench 2.0.25 → 2.0.30
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/README.md +10 -4
- package/dist/bitwrench-bccl.cjs.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
- package/dist/bitwrench-bccl.esm.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
- package/dist/bitwrench-bccl.umd.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.cjs.min.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +1 -1
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
- package/dist/bitwrench-debug.js +1 -1
- package/dist/bitwrench-debug.min.js +1 -1
- package/dist/bitwrench-lean.cjs.js +623 -155
- package/dist/bitwrench-lean.cjs.min.js +7 -7
- package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
- package/dist/bitwrench-lean.es5.js +650 -157
- package/dist/bitwrench-lean.es5.min.js +5 -5
- package/dist/bitwrench-lean.es5.min.js.gz +0 -0
- package/dist/bitwrench-lean.esm.js +623 -155
- package/dist/bitwrench-lean.esm.min.js +6 -6
- package/dist/bitwrench-lean.esm.min.js.gz +0 -0
- package/dist/bitwrench-lean.umd.js +623 -155
- package/dist/bitwrench-lean.umd.min.js +7 -7
- package/dist/bitwrench-lean.umd.min.js.gz +0 -0
- package/dist/bitwrench-util-css.cjs.js +1 -1
- package/dist/bitwrench-util-css.cjs.min.js +1 -1
- package/dist/bitwrench-util-css.es5.js +1 -1
- package/dist/bitwrench-util-css.es5.min.js +1 -1
- package/dist/bitwrench-util-css.esm.js +1 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -1
- package/dist/bitwrench-util-css.umd.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
- package/dist/bitwrench.cjs.js +621 -153
- package/dist/bitwrench.cjs.min.js +6 -6
- package/dist/bitwrench.cjs.min.js.gz +0 -0
- package/dist/bitwrench.css +1 -1
- package/dist/bitwrench.d.ts +18 -11
- package/dist/bitwrench.es5.js +647 -154
- package/dist/bitwrench.es5.min.js +6 -6
- package/dist/bitwrench.es5.min.js.gz +0 -0
- package/dist/bitwrench.esm.js +621 -153
- package/dist/bitwrench.esm.min.js +5 -5
- package/dist/bitwrench.esm.min.js.gz +0 -0
- package/dist/bitwrench.umd.js +621 -153
- package/dist/bitwrench.umd.min.js +6 -6
- package/dist/bitwrench.umd.min.js.gz +0 -0
- package/dist/builds.json +95 -95
- package/dist/bwserve.cjs.js +140 -7
- package/dist/bwserve.esm.js +141 -8
- package/dist/sri.json +45 -45
- package/docs/bitwrench-for-wasm.md +851 -0
- package/docs/bitwrench_api.md +133 -23
- package/docs/llm-bitwrench-guide.md +6 -5
- package/docs/state-management.md +27 -3
- package/docs/thinking-in-bitwrench.md +3 -2
- package/package.json +11 -9
- package/readme.html +17 -8
- package/src/bitwrench.d.ts +18 -11
- package/src/bitwrench.js +617 -148
- package/src/bwserve/bwclient.js +3 -3
- package/src/bwserve/client.js +26 -0
- package/src/bwserve/index.js +110 -3
- package/src/cli/attach.js +7 -5
- package/src/cli/serve.js +53 -10
- package/src/version.js +3 -3
package/docs/bitwrench_api.md
CHANGED
|
@@ -4,27 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
| Field | Value |
|
|
6
6
|
|-------|-------|
|
|
7
|
-
| Version | 2.0.
|
|
8
|
-
| Generated | 2026-
|
|
9
|
-
| Total APIs |
|
|
10
|
-
| Categories |
|
|
11
|
-
| bitwrench.js |
|
|
7
|
+
| Version | 2.0.30 |
|
|
8
|
+
| Generated | 2026-04-12 |
|
|
9
|
+
| Total APIs | 105 |
|
|
10
|
+
| Categories | 14 |
|
|
11
|
+
| bitwrench.js | 4100 lines |
|
|
12
12
|
| bitwrench-bccl.js | 3793 lines |
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
16
16
|
- [Core](#core) (5)
|
|
17
17
|
- [DOM Generation](#dom-generation) (10)
|
|
18
|
+
- [DOM Selection](#dom-selection) (1)
|
|
18
19
|
- [Identifiers](#identifiers) (4)
|
|
19
20
|
- [State Management](#state-management) (3)
|
|
20
21
|
- [Events (DOM)](#events-dom-) (2)
|
|
21
|
-
- [Pub/Sub](#pub-sub) (
|
|
22
|
+
- [Pub/Sub](#pub-sub) (4)
|
|
22
23
|
- [CSS & Styling](#css-styling) (10)
|
|
23
24
|
- [Component Builders](#component-builders) (50)
|
|
24
25
|
- [Browser Utilities](#browser-utilities) (4)
|
|
25
26
|
- [Utilities](#utilities) (1)
|
|
26
27
|
- [Function Registry](#function-registry) (5)
|
|
27
|
-
- [Component](#component) (
|
|
28
|
+
- [Component](#component) (5)
|
|
29
|
+
- [Data Utilities](#data-utilities) (1)
|
|
28
30
|
|
|
29
31
|
---
|
|
30
32
|
|
|
@@ -293,6 +295,28 @@ Get all registered component handles as a Map.
|
|
|
293
295
|
|
|
294
296
|
---
|
|
295
297
|
|
|
298
|
+
## DOM Selection
|
|
299
|
+
|
|
300
|
+
### `bw.el(target, apply)`
|
|
301
|
+
|
|
302
|
+
Look up a single DOM element by ID, CSS selector, UUID, or element ref. Optionally apply content or a function to the resolved element. Resolution order for string targets: 1. Check `bw._nodeMap[id]` cache (O(1), stale entries auto-pruned) 2. `document.getElementById(id)` 3. `document.querySelector(id)` for selectors starting with # or . 4. Class-based lookup for `bw_uuid_*` tokens With one argument, returns the element (or null). With two arguments, applies the second argument to the element and returns the element: - string/number: sets `el.textContent` - function: calls `apply(el)`, returns el - TACO object: clears children, mounts TACO via `bw.createDOM()` - array: clears children, appends each item (string -> text node, TACO -> element)
|
|
303
|
+
|
|
304
|
+
**Parameters:**
|
|
305
|
+
|
|
306
|
+
| Name | Type | Description |
|
|
307
|
+
|------|------|-------------|
|
|
308
|
+
| `target` | `string|Element` | - Element ref, ID, CSS selector, or bw_uuid_* class |
|
|
309
|
+
| `apply` | `string|number|Function|Object|Array` | - Content or function to apply |
|
|
310
|
+
|
|
311
|
+
**Returns:** `Element|null` — DOM element, or null if not found
|
|
312
|
+
|
|
313
|
+
**Example:**
|
|
314
|
+
```javascript
|
|
315
|
+
bw.el('#title') // lookup bw.el('#title', 'Hello') // set text content bw.el('#app', { t: 'h1', c: 'Hi' }) // mount TACO bw.el('.card', function(el) { // apply function el.style.opacity = '0.5'; })
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
296
320
|
## Identifiers
|
|
297
321
|
|
|
298
322
|
### `bw.uuid(prefix)`
|
|
@@ -488,32 +512,32 @@ Publish to a topic, calling all subscribers in registration order. Application-s
|
|
|
488
512
|
| `topic` | `string` | - Topic name (plain string, no prefix) |
|
|
489
513
|
| `detail` | `*` | - Data to pass to subscribers |
|
|
490
514
|
|
|
491
|
-
**Returns:** `number` — of successfully called subscribers
|
|
515
|
+
**Returns:** `number` — of successfully called subscribers (including wildcard matches)
|
|
492
516
|
|
|
493
517
|
**Example:**
|
|
494
518
|
```javascript
|
|
495
|
-
bw.pub('score:updated', { player: 'X', score: 10 });
|
|
519
|
+
bw.pub('score:updated', { player: 'X', score: 10 }); // Wildcard subscribers matching 'score:*' will also fire
|
|
496
520
|
```
|
|
497
521
|
|
|
498
522
|
---
|
|
499
523
|
|
|
500
524
|
### `bw.sub(topic, handler, el)`
|
|
501
525
|
|
|
502
|
-
Subscribe to a topic. Returns an unsub() function. Optional third argument ties the subscription to a DOM element's lifecycle
|
|
526
|
+
Subscribe to a topic. Returns an unsub() function. Supports wildcard patterns: a topic ending in `*` matches any published topic that starts with the prefix before the `*`. For example, `'agui:*'` matches `'agui:ready'`, `'agui:error'`, etc. The handler receives `(detail, topic)` so it can distinguish which topic fired. Optional third argument ties the subscription to a DOM element's lifecycle -- when `bw.cleanup()` is called on that element, the subscription is automatically removed, preventing memory leaks.
|
|
503
527
|
|
|
504
528
|
**Parameters:**
|
|
505
529
|
|
|
506
530
|
| Name | Type | Description |
|
|
507
531
|
|------|------|-------------|
|
|
508
|
-
| `topic` | `string` | - Topic name |
|
|
509
|
-
| `handler` | `Function` | - Called with (detail) on each publish |
|
|
532
|
+
| `topic` | `string` | - Topic name, or wildcard pattern ending in '*' |
|
|
533
|
+
| `handler` | `Function` | - Called with (detail, topic) on each publish |
|
|
510
534
|
| `el` | `Element` | - Optional DOM element to tie lifecycle to |
|
|
511
535
|
|
|
512
536
|
**Returns:** `Function` — to unsubscribe
|
|
513
537
|
|
|
514
538
|
**Example:**
|
|
515
539
|
```javascript
|
|
516
|
-
var unsub = bw.sub('score:updated', function(detail) { console.log(detail.player, 'scored', detail.score); }); // Later: unsub() to stop listening
|
|
540
|
+
var unsub = bw.sub('score:updated', function(detail) { console.log(detail.player, 'scored', detail.score); }); // Later: unsub() to stop listening // Wildcard: listen to all 'agui:' topics bw.sub('agui:*', function(detail, topic) { console.log('Got', topic, detail); });
|
|
517
541
|
```
|
|
518
542
|
|
|
519
543
|
---
|
|
@@ -533,6 +557,27 @@ Unsubscribe a handler by reference from a topic. Removes ALL instances of the gi
|
|
|
533
557
|
|
|
534
558
|
---
|
|
535
559
|
|
|
560
|
+
### `bw.once(topic, handler, el)`
|
|
561
|
+
|
|
562
|
+
Subscribe to a topic for a single event only. The subscription is automatically removed after the first publish. Equivalent to manually calling unsub() inside a bw.sub() handler, but avoids the common bug of forgetting to unsubscribe.
|
|
563
|
+
|
|
564
|
+
**Parameters:**
|
|
565
|
+
|
|
566
|
+
| Name | Type | Description |
|
|
567
|
+
|------|------|-------------|
|
|
568
|
+
| `topic` | `string` | - Topic name |
|
|
569
|
+
| `handler` | `Function` | - Called once with (detail) on the next publish |
|
|
570
|
+
| `el` | `Element` | - Optional DOM element to tie lifecycle to |
|
|
571
|
+
|
|
572
|
+
**Returns:** `Function` — to cancel the subscription before it fires
|
|
573
|
+
|
|
574
|
+
**Example:**
|
|
575
|
+
```javascript
|
|
576
|
+
bw.once('data:loaded', function(detail) { console.log('Received:', detail); // No need to unsubscribe -- already done automatically }); // Cancel before it fires: var cancel = bw.once('timeout', handler); cancel(); // handler will never be called
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
536
581
|
## CSS & Styling
|
|
537
582
|
|
|
538
583
|
### `bw.css(rules, options = {})`
|
|
@@ -694,21 +739,21 @@ bw.loadReset(); // inject once, safe to call multiple times
|
|
|
694
739
|
|
|
695
740
|
---
|
|
696
741
|
|
|
697
|
-
### `bw.
|
|
742
|
+
### `bw.toggleThemeMode(scope)`
|
|
698
743
|
|
|
699
|
-
Toggle between primary and alternate palettes. Adds/removes the `bw_theme_alt` class on the scoping element. Without a scope, toggles on `<html>` (global). With a scope, toggles on
|
|
744
|
+
Toggle between primary and alternate theme palettes. Adds/removes the `bw_theme_alt` class on the scoping element(s). Without a scope, toggles on `<html>` (global). With a scope, toggles on ALL matching elements.
|
|
700
745
|
|
|
701
746
|
**Parameters:**
|
|
702
747
|
|
|
703
748
|
| Name | Type | Description |
|
|
704
749
|
|------|------|-------------|
|
|
705
|
-
| `scope` | `string` | -
|
|
750
|
+
| `scope` | `string|Element` | - Selector or element. Omit for global. |
|
|
706
751
|
|
|
707
|
-
**Returns:** `string` — mode after toggle: 'primary' or 'alternate'
|
|
752
|
+
**Returns:** `string` — mode after toggle: 'primary' or 'alternate' (based on first element)
|
|
708
753
|
|
|
709
754
|
**Example:**
|
|
710
755
|
```javascript
|
|
711
|
-
bw.
|
|
756
|
+
bw.toggleThemeMode(); // global toggle on <html> bw.toggleThemeMode('#my-dashboard'); // scoped toggle bw.toggleThemeMode('.panel'); // toggle on ALL .panel elements
|
|
712
757
|
```
|
|
713
758
|
|
|
714
759
|
---
|
|
@@ -2212,21 +2257,86 @@ bw.message('my_carousel', 'goToSlide', 2); // Or from SSE handler: es.onmessage
|
|
|
2212
2257
|
|
|
2213
2258
|
---
|
|
2214
2259
|
|
|
2215
|
-
### `bw.
|
|
2260
|
+
### `bw.formData(target)`
|
|
2261
|
+
|
|
2262
|
+
Collect form data from all input, select, and textarea elements within a container. Each element's `name` attribute (or `id` if no name) becomes a key in the returned object. This provides a lightweight alternative to the browser FormData API that returns a plain object suitable for JSON serialization or bw.pub(). Handles all standard HTML form controls: - text/number/email/etc inputs: string value - checkboxes: boolean (true/false) - radio buttons: string value of the checked radio (unchecked groups omitted) - multi-select: array of selected option values - textarea: string value Elements without both `name` and `id` attributes are silently skipped.
|
|
2263
|
+
|
|
2264
|
+
**Parameters:**
|
|
2265
|
+
|
|
2266
|
+
| Name | Type | Description |
|
|
2267
|
+
|------|------|-------------|
|
|
2268
|
+
| `target` | `string|Element` | - CSS selector, UUID string, or DOM element |
|
|
2269
|
+
|
|
2270
|
+
**Returns:** `Object` — object mapping field names to values
|
|
2271
|
+
|
|
2272
|
+
**Example:**
|
|
2273
|
+
```javascript
|
|
2274
|
+
// Given a form with name="email" input and name="agree" checkbox: var data = bw.formData('#signup-form'); // => { email: 'user@example.com', agree: true } // Collect and publish in one step: bw.pub('form:submit', bw.formData('#my-form')); // Works with any container, not just <form>: bw.pub('settings:changed', bw.formData('.settings-panel'));
|
|
2275
|
+
```
|
|
2276
|
+
|
|
2277
|
+
---
|
|
2278
|
+
|
|
2279
|
+
### `bw.inspect(target, depth)`
|
|
2280
|
+
|
|
2281
|
+
Inspect a DOM element and its subtree, returning a plain-object representation with bitwrench metadata at each node. Useful for debugging, devtools, MCP/AG-UI tool discovery, and automated testing. Each node in the returned tree includes: - `tag` -- lowercase tag name (or '#text' for text nodes) - `id` -- element id (if set) - `uuid` -- bitwrench UUID class (if lifecycle-managed) - `type` -- component type from o.type (if set, e.g. 'card', 'tabs') - `classes` -- first 5 CSS classes (string, space-separated) - `handles` -- array of el.bw method names (if any) - `state` -- copy of _bw_state (if any) - `hasRender` -- true if _bw_render is set - `hasSubs` -- true if element has pub/sub subscriptions - `refs` -- copy of _bw_refs keys (if any) - `children` -- array of child node trees (up to depth limit, max 50 per level)
|
|
2282
|
+
|
|
2283
|
+
**Parameters:**
|
|
2284
|
+
|
|
2285
|
+
| Name | Type | Description |
|
|
2286
|
+
|------|------|-------------|
|
|
2287
|
+
| `target` | `string|Element` | - CSS selector, UUID, or DOM element |
|
|
2288
|
+
| `depth` | `number` | - Maximum recursion depth (0 = target only, no children) |
|
|
2289
|
+
|
|
2290
|
+
**Returns:** `Object|null` — object tree, or null if element not found
|
|
2291
|
+
|
|
2292
|
+
**Example:**
|
|
2293
|
+
```javascript
|
|
2294
|
+
// Get full tree from #app, 3 levels deep (default): var info = bw.inspect('#app'); // Shallow inspection (just the element, no children): var info = bw.inspect('#my-carousel', 0); console.log(info.handles); // ['next', 'prev', 'goToSlide'] console.log(info.type); // 'carousel' // Deep inspection for debugging: console.log(JSON.stringify(bw.inspect('#app', 5), null, 2));
|
|
2295
|
+
```
|
|
2296
|
+
|
|
2297
|
+
---
|
|
2298
|
+
|
|
2299
|
+
### `bw.catalog(type)`
|
|
2300
|
+
|
|
2301
|
+
Query the BCCL component registry. Returns metadata about registered component types -- their names and factory function names. Useful for tooling, introspection, documentation generators, and auto-complete systems (including MCP/AG-UI tool discovery). With no arguments, returns an array of all registered component types. With a type name, returns metadata for that single type (or null if the type is not registered).
|
|
2302
|
+
|
|
2303
|
+
**Parameters:**
|
|
2304
|
+
|
|
2305
|
+
| Name | Type | Description |
|
|
2306
|
+
|------|------|-------------|
|
|
2307
|
+
| `type` | `string` | - Optional component type name to look up |
|
|
2308
|
+
|
|
2309
|
+
**Returns:** `Array<Object>|Object|null` — of {type, factory} objects, a single {type, factory} object, or null if the type is not found
|
|
2310
|
+
|
|
2311
|
+
**Example:**
|
|
2312
|
+
```javascript
|
|
2313
|
+
// List all available component types: bw.catalog(); // => [{ type: 'card', factory: 'makeCard' }, // { type: 'button', factory: 'makeButton' }, ...] // Look up a specific type: bw.catalog('accordion'); // => { type: 'accordion', factory: 'makeAccordion' } // Check if a type exists: if (bw.catalog('chart')) { ... } // Get just the type names: bw.catalog().map(function(c) { return c.type; }); // => ['card', 'button', 'container', 'row', ...]
|
|
2314
|
+
```
|
|
2315
|
+
|
|
2316
|
+
---
|
|
2317
|
+
|
|
2318
|
+
## Data Utilities
|
|
2319
|
+
|
|
2320
|
+
### `bw.jsonPatch(obj, ops)`
|
|
2216
2321
|
|
|
2217
|
-
|
|
2322
|
+
Apply RFC 6902 JSON Patch operations to a plain object. Supported operations: add, remove, replace, move, copy, test. Paths use JSON Pointer (RFC 6901) notation: `/foo/bar/0`. Mutates the target object in place and returns it.
|
|
2218
2323
|
|
|
2219
2324
|
**Parameters:**
|
|
2220
2325
|
|
|
2221
2326
|
| Name | Type | Description |
|
|
2222
2327
|
|------|------|-------------|
|
|
2223
|
-
| `
|
|
2328
|
+
| `obj` | `Object` | - Target object to patch |
|
|
2329
|
+
| `ops` | `Array<Object>` | - Array of patch operations |
|
|
2330
|
+
| `ops[].op` | `string` | - Operation: 'add', 'remove', 'replace', 'move', 'copy', 'test' |
|
|
2331
|
+
| `ops[].path` | `string` | - JSON Pointer path (e.g. '/a/b/0') |
|
|
2332
|
+
| `ops[].value` | `*` | - Value for add/replace/test |
|
|
2333
|
+
| `ops[].from` | `string` | - Source path for move/copy |
|
|
2224
2334
|
|
|
2225
|
-
**Returns:** `
|
|
2335
|
+
**Returns:** `Object` — patched object (same reference)
|
|
2226
2336
|
|
|
2227
2337
|
**Example:**
|
|
2228
2338
|
```javascript
|
|
2229
|
-
bw.
|
|
2339
|
+
var obj = { a: 1, b: { c: 2 } }; bw.jsonPatch(obj, [ { op: 'replace', path: '/a', value: 10 }, { op: 'add', path: '/b/d', value: 3 }, { op: 'remove', path: '/b/c' } ]); // obj => { a: 10, b: { d: 3 } }
|
|
2230
2340
|
```
|
|
2231
2341
|
|
|
2232
2342
|
---
|
|
@@ -406,7 +406,7 @@ card.bw.setContent({ t: 'b', c: '$42k' });
|
|
|
406
406
|
var el = bw.$('#app')[0];
|
|
407
407
|
el._bw_state; // current state
|
|
408
408
|
el._bw_render; // render function
|
|
409
|
-
bw.inspect(el); //
|
|
409
|
+
bw.inspect(el, 0); // introspect element (state, handles, type, classes)
|
|
410
410
|
```
|
|
411
411
|
|
|
412
412
|
### bwcli attach -- remote debugging REPL
|
|
@@ -612,13 +612,14 @@ bwcli serve # dev server (port 7902)
|
|
|
612
612
|
| `bw.mount(sel, taco)` | Mount + return root element |
|
|
613
613
|
| `bw.cleanup(el)` | Run unmount hooks, clear subscriptions |
|
|
614
614
|
| `bw.patch(id, content)` | Update element by id or UUID |
|
|
615
|
-
| `bw.inspect(el)` |
|
|
615
|
+
| `bw.inspect(el, depth)` | Introspect DOM subtree with bitwrench metadata |
|
|
616
616
|
|
|
617
617
|
### Communication
|
|
618
618
|
| Function | Description |
|
|
619
619
|
|----------|-------------|
|
|
620
|
-
| `bw.pub(topic, data)` | App-wide publish |
|
|
621
|
-
| `bw.sub(topic, fn, el?)` | Subscribe (optional lifecycle tie
|
|
620
|
+
| `bw.pub(topic, data)` | App-wide publish (fires exact + wildcard matches) |
|
|
621
|
+
| `bw.sub(topic, fn, el?)` | Subscribe (supports wildcard `'ns:*'`; optional lifecycle tie) |
|
|
622
|
+
| `bw.once(topic, fn, el?)` | One-shot subscribe (auto-unsub after first fire) |
|
|
622
623
|
| `bw.message(target, action, data)` | Dispatch to `el.bw[action](data)` |
|
|
623
624
|
| `bw.emit(el, event, detail)` | DOM-scoped CustomEvent |
|
|
624
625
|
|
|
@@ -658,7 +659,7 @@ bwcli serve # dev server (port 7902)
|
|
|
658
659
|
9. **CSS classes use `bw-` prefix**: `bw-card`, `bw-btn`, `bw-container`.
|
|
659
660
|
10. **Routing is built in** -- `bw.router()` for SPAs. Hash mode by default, history mode optional.
|
|
660
661
|
11. **Use `bw.mount()` + `el.bw`** for targeted updates. `o.handle` for methods, `o.slots` for content areas. Avoids re-render side effects (lost focus, scroll reset).
|
|
661
|
-
12. **Debug**: `bw.inspect(el)`, `el._bw_state`, `bwcli attach` for remote REPL.
|
|
662
|
+
12. **Debug**: `bw.inspect(el, 0)`, `el._bw_state`, `bwcli attach` for remote REPL.
|
|
662
663
|
|
|
663
664
|
---
|
|
664
665
|
|
package/docs/state-management.md
CHANGED
|
@@ -567,6 +567,20 @@ var results = {
|
|
|
567
567
|
|
|
568
568
|
Pub/sub is app-scoped -- publishers and subscribers do not need to know about each other. Pass the element as the third argument to `bw.sub()` to tie the subscription's lifetime to the element (auto-cleaned on `bw.cleanup()`).
|
|
569
569
|
|
|
570
|
+
Wildcard subscriptions let you listen to a group of related topics at once:
|
|
571
|
+
|
|
572
|
+
```javascript
|
|
573
|
+
// Listen to ALL search-related topics
|
|
574
|
+
bw.sub('search:*', function(detail, topic) {
|
|
575
|
+
console.log('Search event:', topic, detail);
|
|
576
|
+
}, el);
|
|
577
|
+
|
|
578
|
+
// These all fire the wildcard handler above:
|
|
579
|
+
bw.pub('search:changed', { query: 'foo' });
|
|
580
|
+
bw.pub('search:cleared');
|
|
581
|
+
bw.pub('search:submitted', { query: 'foo' });
|
|
582
|
+
```
|
|
583
|
+
|
|
570
584
|
### Updating child widgets within a parent component
|
|
571
585
|
|
|
572
586
|
When a parent component contains child sub-components (like a progress bar inside a dashboard card), use pub/sub to update the child:
|
|
@@ -622,8 +636,9 @@ These primitives are the building blocks of the stateful TACO model. They are al
|
|
|
622
636
|
| `bw.uuid(prefix)` | Generate a UUID class for addressing |
|
|
623
637
|
| `bw.emit(el, event, detail)` | Dispatch a CustomEvent on a DOM element |
|
|
624
638
|
| `bw.on(el, event, handler)` | Listen for a CustomEvent on a DOM element |
|
|
625
|
-
| `bw.pub(topic, detail)` | Publish to app-wide topic |
|
|
626
|
-
| `bw.sub(topic, handler, el?)` | Subscribe to
|
|
639
|
+
| `bw.pub(topic, detail)` | Publish to app-wide topic (fires exact + wildcard matches) |
|
|
640
|
+
| `bw.sub(topic, handler, el?)` | Subscribe to topic (supports wildcard `'ns:*'` patterns) |
|
|
641
|
+
| `bw.once(topic, handler, el?)` | One-shot subscribe (auto-unsub after first fire) |
|
|
627
642
|
| `bw.unsub(topic, handler)` | Unsubscribe from topic |
|
|
628
643
|
| `bw.cleanup(el)` | Run unmount hooks and clear subscriptions |
|
|
629
644
|
|
|
@@ -725,6 +740,15 @@ bw.sub('store:todos', renderTodos, todosEl);
|
|
|
725
740
|
bw.sub('store:projects', renderProjects, projectsEl);
|
|
726
741
|
```
|
|
727
742
|
|
|
743
|
+
If you need a global listener (e.g. for logging or debug), use a wildcard:
|
|
744
|
+
|
|
745
|
+
```javascript
|
|
746
|
+
// OK for debug/logging -- not for rendering
|
|
747
|
+
bw.sub('store:*', function(data, topic) {
|
|
748
|
+
console.log('[store]', topic, data);
|
|
749
|
+
});
|
|
750
|
+
```
|
|
751
|
+
|
|
728
752
|
### When to use
|
|
729
753
|
|
|
730
754
|
- Multi-view SPAs where views share data
|
|
@@ -758,7 +782,7 @@ bw.router({
|
|
|
758
782
|
| Server pushes UI updates | Level 1 -- `bw.patch()` / `bw.DOM()` |
|
|
759
783
|
| Components need to talk to each other | `bw.pub()`/`bw.sub()` |
|
|
760
784
|
| URL-driven views (SPA) | `bw.router()` -- see [Routing](routing.md) |
|
|
761
|
-
| Debugging component state | `el._bw_state` in the console, or `bw.inspect(selector)` |
|
|
785
|
+
| Debugging component state | `el._bw_state` in the console, or `bw.inspect(selector, 0)` |
|
|
762
786
|
|
|
763
787
|
---
|
|
764
788
|
|
|
@@ -1381,9 +1381,10 @@ Key things this example proves:
|
|
|
1381
1381
|
|
|
1382
1382
|
| Function | What it does |
|
|
1383
1383
|
|----------|-------------|
|
|
1384
|
-
| `bw.pub(topic, data)` | Publish to all subscribers |
|
|
1385
|
-
| `bw.sub(topic, fn)` | Subscribe (returns unsub function) |
|
|
1384
|
+
| `bw.pub(topic, data)` | Publish to all subscribers (exact + wildcard) |
|
|
1385
|
+
| `bw.sub(topic, fn)` | Subscribe (returns unsub function; supports wildcard `'ns:*'`) |
|
|
1386
1386
|
| `bw.sub(topic, fn, owner)` | Subscribe with auto-cleanup when owner is removed |
|
|
1387
|
+
| `bw.once(topic, fn, el?)` | One-shot subscribe (auto-unsub after first fire) |
|
|
1387
1388
|
|
|
1388
1389
|
### Routing
|
|
1389
1390
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bitwrench",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "A library for javascript UI functions.",
|
|
5
5
|
"main": "./dist/bitwrench.umd.js",
|
|
6
6
|
"repository": {
|
|
@@ -63,32 +63,34 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@babel/core": "^7.25.2",
|
|
66
|
-
"@babel/preset-env": "^7.
|
|
66
|
+
"@babel/preset-env": "^7.29.2",
|
|
67
67
|
"@babel/preset-react": "^7.24.7",
|
|
68
|
-
"@
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@playwright/test": "^1.59.1",
|
|
69
70
|
"@rollup/plugin-babel": "^6.0.4",
|
|
70
71
|
"@rollup/plugin-commonjs": "^26.0.1",
|
|
71
72
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
72
73
|
"@rollup/plugin-terser": "^1.0.0",
|
|
73
74
|
"c8": "^8.0.1",
|
|
74
75
|
"chai": "^3.0.0",
|
|
75
|
-
"comment-parser": "^1.4.
|
|
76
|
-
"eslint": "^
|
|
77
|
-
"
|
|
76
|
+
"comment-parser": "^1.4.6",
|
|
77
|
+
"eslint": "^10.2.0",
|
|
78
|
+
"globals": "^17.4.0",
|
|
79
|
+
"jsdom": "^26.1.0",
|
|
78
80
|
"jsdom-global": "3.0.2",
|
|
79
81
|
"karma": "^6.3.16",
|
|
80
82
|
"karma-chai": "^0.1.0",
|
|
81
83
|
"karma-chrome-launcher": "^3.1.0",
|
|
82
84
|
"karma-cli": "^2.0.0",
|
|
83
85
|
"karma-coverage": "^2.0.3",
|
|
84
|
-
"karma-coverage-istanbul-reporter": "^
|
|
86
|
+
"karma-coverage-istanbul-reporter": "^3.0.3",
|
|
85
87
|
"karma-firefox-launcher": "^1.3.0",
|
|
86
88
|
"karma-ie-launcher": "^1.0.0",
|
|
87
89
|
"karma-mocha": "^2.0.1",
|
|
88
90
|
"mocha": "^11.0.0",
|
|
89
91
|
"pixelmatch": "^7.1.0",
|
|
90
92
|
"pngjs": "^7.0.0",
|
|
91
|
-
"rollup": "^4.
|
|
93
|
+
"rollup": "^4.60.1",
|
|
92
94
|
"rollup-plugin-css-only": "^4.5.2",
|
|
93
95
|
"rollup-plugin-postcss": "^4.0.2",
|
|
94
96
|
"uglify-js": "^3.19.3",
|
|
@@ -105,7 +107,7 @@
|
|
|
105
107
|
"update_rm": "echo 'DEPRECATED: use build:index instead'",
|
|
106
108
|
"cleanbuild": "npm run clean && npm run build && npm run build:generated",
|
|
107
109
|
"oldtest": "./node_modules/mocha/bin/mocha test/bitwrench_test.js --reporter spec",
|
|
108
|
-
"test": "c8 --reporter=text --reporter=json-summary mocha ./test/bitwrench_ci.js ./test/bitwrench_test_coverage.js ./test/bitwrench_test_pubsub.js ./test/bitwrench_test_theme.js ./test/bitwrench_test_nodemap.js ./test/bitwrench_test_components.js ./test/bitwrench_test_coverage_gaps.js ./test/bitwrench_test_bwserve.js ./test/bitwrench_test_attach.js ./test/bitwrench_test_serve.js ./test/bitwrench_test_code_edit.js ./test/bitwrench_test_html_page.js ./test/bitwrench_test_util_css.js ./test/bitwrench_test_handle.js ./test/bitwrench_test_debug.js ./test/bitwrench_test_router.js ./test/bitwrench_test_mcp_transport.js ./test/bitwrench_test_mcp_server.js ./test/bitwrench_test_mcp_tools.js ./test/bitwrench_test_mcp_knowledge.js ./test/bitwrench_test_mcp_live.js -r jsdom-global/register --exit",
|
|
110
|
+
"test": "c8 --reporter=text --reporter=json-summary mocha ./test/bitwrench_ci.js ./test/bitwrench_test_coverage.js ./test/bitwrench_test_pubsub.js ./test/bitwrench_test_theme.js ./test/bitwrench_test_nodemap.js ./test/bitwrench_test_components.js ./test/bitwrench_test_coverage_gaps.js ./test/bitwrench_test_bwserve.js ./test/bitwrench_test_attach.js ./test/bitwrench_test_serve.js ./test/bitwrench_test_code_edit.js ./test/bitwrench_test_html_page.js ./test/bitwrench_test_util_css.js ./test/bitwrench_test_handle.js ./test/bitwrench_test_debug.js ./test/bitwrench_test_router.js ./test/bitwrench_test_mcp_transport.js ./test/bitwrench_test_mcp_server.js ./test/bitwrench_test_mcp_tools.js ./test/bitwrench_test_mcp_knowledge.js ./test/bitwrench_test_mcp_live.js ./test/bitwrench_test_new_apis.js -r jsdom-global/register --exit",
|
|
109
111
|
"test:bwserve": "mocha ./test/bitwrench_test_bwserve.js -r jsdom-global/register",
|
|
110
112
|
"test:attach": "mocha ./test/bitwrench_test_attach.js -r jsdom-global/register",
|
|
111
113
|
"test:serve": "mocha ./test/bitwrench_test_serve.js -r jsdom-global/register --exit",
|
package/readme.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="generator" content="bitwrench v2.0.
|
|
6
|
+
<meta name="generator" content="bitwrench v2.0.30">
|
|
7
7
|
<title>bitwrench.js - README</title>
|
|
8
8
|
<link rel="icon" type="image/x-icon" href="images/favicon.ico">
|
|
9
9
|
<script src="dist/bitwrench.umd.min.js"></script>
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
<a class="quikdown-a" href="https://opensource.org/licenses/BSD-2-Clause" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/badge/License-BSD%202--Clause-blue.svg" alt="License"></a>
|
|
73
73
|
<a class="quikdown-a" href="https://www.npmjs.com/package/bitwrench" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/npm/v/bitwrench.svg?style=flat-square" alt="NPM version"></a>
|
|
74
74
|
<a class="quikdown-a" href="https://github.com/deftio/bitwrench/actions/workflows/ci.yml" rel="noopener noreferrer"><img class="quikdown-img" src="https://github.com/deftio/bitwrench/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
75
|
-
<a class="quikdown-a" href="https://github.com/deftio/bitwrench" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/badge/coverage-97.
|
|
75
|
+
<a class="quikdown-a" href="https://github.com/deftio/bitwrench" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/badge/coverage-97.6%25-brightgreen.svg" alt="Coverage"></a></p><p><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/" rel="noopener noreferrer"><img class="quikdown-img" src="./images/bitwrench-logo-med.png" alt="bitwrench"></a></p><p>Bitwrench builds UI from plain JavaScript objects -- one format for components, styling, state, and server rendering, with no build step and zero dependencies.</p><pre class="quikdown-pre"><code class="language-javascript">// Describe UI as a JavaScript object (a "TACO")
|
|
76
76
|
var page = {
|
|
77
77
|
t: 'div', a: { class: 'card' },
|
|
78
78
|
c: [
|
|
@@ -208,7 +208,12 @@ bw.DOM('#app', counter);</code></pre><blockquote class="quikdown-blockqu
|
|
|
208
208
|
console.log('New item:', detail.name);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
bw.pub('item-added', { name: 'Widget' })
|
|
211
|
+
bw.pub('item-added', { name: 'Widget' });
|
|
212
|
+
|
|
213
|
+
// Wildcard: listen to a group of related topics
|
|
214
|
+
bw.sub('item:*', function(detail, topic) {
|
|
215
|
+
console.log(topic, detail); // e.g. 'item:added', 'item:removed'
|
|
216
|
+
});</code></pre><h2 class="quikdown-h2">CSS from JavaScript</h2>
|
|
212
217
|
<p><code class="quikdown-code">bw.css()</code> generates CSS from objects. <code class="quikdown-code">bw.s()</code> composes inline styles from reusable utility objects:</p><pre class="quikdown-pre"><code class="language-javascript">// Generate and inject a stylesheet
|
|
213
218
|
bw.injectCSS(bw.css({
|
|
214
219
|
'.my-card': { padding: '1rem', borderRadius: '8px' }
|
|
@@ -298,15 +303,19 @@ bw.toggleStyles(); // switch between primary and alternate palettes</code></pre
|
|
|
298
303
|
</tr>
|
|
299
304
|
<tr class="quikdown-tr">
|
|
300
305
|
<td class="quikdown-td"><code class="quikdown-code">bw.pub(topic, detail)</code></td>
|
|
301
|
-
<td class="quikdown-td">Publish
|
|
306
|
+
<td class="quikdown-td">Publish to subscribers (exact + wildcard matches)</td>
|
|
307
|
+
</tr>
|
|
308
|
+
<tr class="quikdown-tr">
|
|
309
|
+
<td class="quikdown-td"><code class="quikdown-code">bw.sub(topic, handler, el?)</code></td>
|
|
310
|
+
<td class="quikdown-td">Subscribe to topic (supports wildcard <code class="quikdown-code">'ns:*'</code>); returns unsub function</td>
|
|
302
311
|
</tr>
|
|
303
312
|
<tr class="quikdown-tr">
|
|
304
|
-
<td class="quikdown-td"><code class="quikdown-code">bw.
|
|
305
|
-
<td class="quikdown-td">
|
|
313
|
+
<td class="quikdown-td"><code class="quikdown-code">bw.once(topic, handler, el?)</code></td>
|
|
314
|
+
<td class="quikdown-td">One-shot subscribe; auto-unsub after first fire</td>
|
|
306
315
|
</tr>
|
|
307
316
|
<tr class="quikdown-tr">
|
|
308
|
-
<td class="quikdown-td"><code class="quikdown-code">bw.inspect(target)</code></td>
|
|
309
|
-
<td class="quikdown-td">
|
|
317
|
+
<td class="quikdown-td"><code class="quikdown-code">bw.inspect(target, depth)</code></td>
|
|
318
|
+
<td class="quikdown-td">Introspect a DOM subtree with bitwrench metadata (state, handles, type)</td>
|
|
310
319
|
</tr>
|
|
311
320
|
<tr class="quikdown-tr">
|
|
312
321
|
<td class="quikdown-td"><code class="quikdown-code">bw.apply(msg)</code></td>
|
package/src/bitwrench.d.ts
CHANGED
|
@@ -315,8 +315,8 @@ export interface Bitwrench {
|
|
|
315
315
|
html(taco: Taco | TacoContent, options?: { raw?: boolean; state?: Record<string, any> }): string;
|
|
316
316
|
/** Generate complete HTML page string */
|
|
317
317
|
htmlPage(opts: { title?: string; css?: string; content?: TacoContent; favicon?: string; [key: string]: any }): string;
|
|
318
|
-
/** Create DOM element from TACO (browser only) */
|
|
319
|
-
createDOM(taco: Taco | TacoContent, options?: Record<string, any>): HTMLElement | DocumentFragment;
|
|
318
|
+
/** Create DOM element from TACO (browser only). SVG TACOs ({t:'svg',...}) use createElementNS. */
|
|
319
|
+
createDOM(taco: Taco | TacoContent, options?: Record<string, any>): HTMLElement | SVGElement | DocumentFragment;
|
|
320
320
|
/** Mount TACO into target, replacing contents */
|
|
321
321
|
DOM(target: string | HTMLElement, taco: Taco | TacoContent, options?: Record<string, any>): void;
|
|
322
322
|
/** Mount TACO and return root element (for el.bw access) */
|
|
@@ -331,24 +331,28 @@ export interface Bitwrench {
|
|
|
331
331
|
update(target: string | HTMLElement): void;
|
|
332
332
|
/** Quick-patch element content or attribute */
|
|
333
333
|
patch(id: string | HTMLElement, content?: TacoContent, attr?: string): HTMLElement | null;
|
|
334
|
+
/** RFC 6902 JSON Patch on plain objects. Mutates and returns obj. @see bw.patch */
|
|
335
|
+
jsonPatch(obj: object, ops: Array<{ op: string; path: string; value?: any; from?: string }>): object;
|
|
334
336
|
/** Batch patch multiple elements */
|
|
335
337
|
patchAll(patches: Record<string, TacoContent> | Array<{ id: string; content?: TacoContent; attr?: string }>): Record<string, HTMLElement>;
|
|
336
338
|
/** Clean up lifecycle hooks, subscriptions, cache */
|
|
337
339
|
cleanup(element: HTMLElement): void;
|
|
338
340
|
|
|
339
341
|
// -- DOM Selection --------------------------------------------------------
|
|
340
|
-
/**
|
|
341
|
-
|
|
342
|
+
/** Resolve target to first matching element. Optional apply: string (textContent), function, TACO (mount), or array. @see bw.$ */
|
|
343
|
+
el(target: string | HTMLElement | null, apply?: string | number | boolean | Function | object | any[]): HTMLElement | null;
|
|
344
|
+
/** CSS selector to array of elements. Optional apply applied to each. @see bw.el */
|
|
345
|
+
$(selector: string | HTMLElement | HTMLElement[], apply?: string | number | boolean | Function | object | any[]): HTMLElement[];
|
|
342
346
|
/** Dispatch DOM event */
|
|
343
347
|
emit(target: string | HTMLElement, eventName: string, detail?: any): void;
|
|
344
348
|
/** Add event listener */
|
|
345
349
|
on(target: string | HTMLElement, eventName: string, handler: (e: Event) => void): void;
|
|
346
350
|
|
|
347
351
|
// -- Pub/Sub --------------------------------------------------------------
|
|
348
|
-
/** Publish to topic */
|
|
352
|
+
/** Publish to topic. Fires exact-match and wildcard subscribers. */
|
|
349
353
|
pub(topic: string, detail?: any): number;
|
|
350
|
-
/** Subscribe to topic; returns unsub() */
|
|
351
|
-
sub(topic: string, handler: (detail: any) => void, el?: HTMLElement): () => void;
|
|
354
|
+
/** Subscribe to topic (or wildcard pattern ending in '*'); returns unsub(). Handler receives (detail, topic). */
|
|
355
|
+
sub(topic: string, handler: (detail: any, topic?: string) => void, el?: HTMLElement): () => void;
|
|
352
356
|
/** Unsubscribe handler from topic */
|
|
353
357
|
unsub(topic: string, handler: Function): number;
|
|
354
358
|
|
|
@@ -365,8 +369,8 @@ export interface Bitwrench {
|
|
|
365
369
|
message(target: string | HTMLElement, action: string, data?: any): any;
|
|
366
370
|
/** Execute wire-protocol message object */
|
|
367
371
|
apply(msg: Record<string, any>): any;
|
|
368
|
-
/** Inspect element
|
|
369
|
-
inspect(target: string | HTMLElement): Record<string, any
|
|
372
|
+
/** Inspect DOM element and subtree, returning plain-object tree with bitwrench metadata */
|
|
373
|
+
inspect(target: string | HTMLElement, depth?: number): Record<string, any> | null;
|
|
370
374
|
|
|
371
375
|
// -- Function Registry ----------------------------------------------------
|
|
372
376
|
funcRegister(fn: Function, name?: string): string;
|
|
@@ -395,8 +399,10 @@ export interface Bitwrench {
|
|
|
395
399
|
loadStyles(config?: StyleConfig, scope?: string): StylesResult | void;
|
|
396
400
|
/** Load CSS reset */
|
|
397
401
|
loadReset(): void;
|
|
398
|
-
/**
|
|
399
|
-
|
|
402
|
+
/** Toggle between primary/alternate theme palettes on all matching elements. @see bw.applyStyles */
|
|
403
|
+
toggleThemeMode(scope?: string | HTMLElement): string;
|
|
404
|
+
/** @deprecated Use bw.toggleThemeMode() instead. Alias kept for one release cycle. */
|
|
405
|
+
toggleStyles(scope?: string | HTMLElement): string;
|
|
400
406
|
/** Remove injected styles */
|
|
401
407
|
clearStyles(scope?: string): void;
|
|
402
408
|
/** Generate type scale from base + ratio */
|
|
@@ -586,6 +592,7 @@ export interface Bitwrench {
|
|
|
586
592
|
|
|
587
593
|
// -- Internal (access at own risk) ----------------------------------------
|
|
588
594
|
_nodeMap: Record<string, HTMLElement>;
|
|
595
|
+
/** @deprecated Use bw.el() instead. Alias kept for one release cycle. */
|
|
589
596
|
_el(id: string): HTMLElement | null;
|
|
590
597
|
_registerNode(el: HTMLElement, uuid: string): void;
|
|
591
598
|
_deregisterNode(el: HTMLElement, uuid: string): void;
|