bitwrench 2.0.32 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -125
- package/dist/bitwrench-bccl.cjs.js +349 -188
- package/dist/bitwrench-bccl.cjs.min.js +2 -39
- package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
- package/dist/bitwrench-bccl.esm.js +349 -188
- package/dist/bitwrench-bccl.esm.min.js +2 -39
- package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
- package/dist/bitwrench-bccl.umd.js +349 -188
- package/dist/bitwrench-bccl.umd.min.js +2 -39
- package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
- package/dist/bitwrench-code-edit.cjs.js +17 -6
- package/dist/bitwrench-code-edit.cjs.min.js +2 -20
- package/dist/bitwrench-code-edit.es5.js +8 -3
- package/dist/bitwrench-code-edit.es5.min.js +2 -19
- package/dist/bitwrench-code-edit.esm.js +17 -6
- package/dist/bitwrench-code-edit.esm.min.js +2 -19
- package/dist/bitwrench-code-edit.umd.js +17 -6
- package/dist/bitwrench-code-edit.umd.min.js +2 -19
- 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 +2492 -1628
- package/dist/bitwrench-lean.cjs.min.js +2 -80
- package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
- package/dist/bitwrench-lean.es5.js +2740 -1838
- package/dist/bitwrench-lean.es5.min.js +2 -49
- package/dist/bitwrench-lean.es5.min.js.gz +0 -0
- package/dist/bitwrench-lean.esm.js +2492 -1628
- package/dist/bitwrench-lean.esm.min.js +2 -80
- package/dist/bitwrench-lean.esm.min.js.gz +0 -0
- package/dist/bitwrench-lean.umd.js +2492 -1628
- package/dist/bitwrench-lean.umd.min.js +2 -80
- package/dist/bitwrench-lean.umd.min.js.gz +0 -0
- package/dist/bitwrench-util-color.cjs.js +251 -0
- package/dist/bitwrench-util-color.cjs.min.js +3 -0
- package/dist/bitwrench-util-color.es5.js +256 -0
- package/dist/bitwrench-util-color.es5.min.js +3 -0
- package/dist/bitwrench-util-color.esm.js +241 -0
- package/dist/bitwrench-util-color.esm.min.js +3 -0
- package/dist/bitwrench-util-color.umd.js +257 -0
- package/dist/bitwrench-util-color.umd.min.js +3 -0
- package/dist/bitwrench-util-css.cjs.js +2 -1
- package/dist/bitwrench-util-css.cjs.min.js +2 -21
- package/dist/bitwrench-util-css.es5.js +2 -1
- package/dist/bitwrench-util-css.es5.min.js +2 -20
- package/dist/bitwrench-util-css.esm.js +2 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -19
- package/dist/bitwrench-util-css.umd.js +2 -1
- package/dist/bitwrench-util-css.umd.min.js +2 -20
- package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
- package/dist/bitwrench.cjs.js +2826 -1801
- package/dist/bitwrench.cjs.min.js +2 -99
- package/dist/bitwrench.cjs.min.js.gz +0 -0
- package/dist/bitwrench.css +403 -479
- package/dist/bitwrench.d.ts +70 -73
- package/dist/bitwrench.es5.js +3106 -2020
- package/dist/bitwrench.es5.min.js +2 -67
- package/dist/bitwrench.es5.min.js.gz +0 -0
- package/dist/bitwrench.esm.js +2826 -1801
- package/dist/bitwrench.esm.min.js +2 -99
- package/dist/bitwrench.esm.min.js.gz +0 -0
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +2826 -1801
- package/dist/bitwrench.umd.min.js +2 -99
- package/dist/bitwrench.umd.min.js.gz +0 -0
- package/dist/builds.json +222 -134
- package/dist/bwserve.cjs.js +289 -282
- package/dist/bwserve.d.ts +157 -0
- package/dist/bwserve.esm.js +290 -283
- package/dist/sri.json +54 -46
- package/docs/README.md +6 -3
- package/docs/app-patterns.md +7 -6
- package/docs/bitwrench-for-wasm.md +53 -54
- package/docs/bitwrench-mcp.md +2 -2
- package/docs/bitwrench-northstar-principles.md +406 -0
- package/docs/bitwrench-taco-schema-discussion.md +2 -2
- package/docs/bitwrench_api.md +191 -106
- package/docs/bitwrench_typescript_usage.md +5 -5
- package/docs/bw-attach.md +29 -75
- package/docs/bwserve.md +200 -168
- package/docs/cli.md +36 -12
- package/docs/component-cheatsheet.md +2 -2
- package/docs/component-library.md +4 -4
- package/docs/component-lifecycle.md +234 -0
- package/docs/drift-lint.md +268 -0
- package/docs/framework-translation-table.md +4 -4
- package/docs/llm-bitwrench-guide.md +60 -50
- package/docs/routing.md +11 -13
- package/docs/state-management.md +110 -109
- package/docs/taco-format.md +13 -14
- package/docs/theming.md +13 -3
- package/docs/thinking-in-bitwrench.md +858 -983
- package/docs/tutorial-bwserve.md +37 -36
- package/docs/tutorial-embedded.md +10 -21
- package/docs/tutorial-website.md +2 -2
- package/package.json +38 -7
- package/readme.html +262 -160
- package/src/bitwrench-bccl-entry.js +2 -2
- package/src/bitwrench-bccl.js +346 -185
- package/src/bitwrench-code-edit.js +16 -5
- package/src/bitwrench-color-utils.js +117 -181
- package/src/bitwrench-file-ops.js +2 -2
- package/src/bitwrench-lean.js +4 -3
- package/src/bitwrench-router.js +5 -2
- package/src/bitwrench-styles.js +420 -504
- package/src/bitwrench-util-color.js +240 -0
- package/src/bitwrench-util-css.js +1 -0
- package/src/bitwrench-utils.js +4 -0
- package/src/bitwrench.d.ts +70 -73
- package/src/bitwrench.h +5 -0
- package/src/bitwrench.js +1939 -933
- package/src/bwserve/attach.js +0 -1
- package/src/bwserve/bwclient.js +172 -32
- package/src/bwserve/bwshell.js +0 -4
- package/src/bwserve/client.js +59 -220
- package/src/bwserve/index.js +59 -26
- package/src/bwserve.d.ts +157 -0
- package/src/bwserve.h +5 -0
- package/src/cli/attach.js +12 -75
- package/src/cli/convert.js +2 -2
- package/src/cli/serve.js +37 -35
- package/src/generate-css.js +1 -1
- package/src/mcp/knowledge.js +4 -4
- package/src/mcp/live.js +21 -13
- package/src/mcp/tools.js +0 -1
- package/src/version.js +3 -7
package/README.md
CHANGED
|
@@ -1,67 +1,32 @@
|
|
|
1
1
|
# bitwrench.js
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
[
|
|
5
|
-
[
|
|
6
|
-
[
|
|
3
|
+
[<img class="quikdown-img" src="https://img.shields.io/badge/License-BSD%202--Clause-blue.svg" alt="License" data-qd-alt="License" data-qd-src="https://img.shields.io/badge/License-BSD%202--Clause-blue.svg" data-qd="!">](https://opensource.org/licenses/BSD-2-Clause)
|
|
4
|
+
[<img class="quikdown-img" src="https://img.shields.io/npm/v/bitwrench.svg?style=flat-square" alt="NPM version" data-qd-alt="NPM version" data-qd-src="https://img.shields.io/npm/v/bitwrench.svg?style=flat-square" data-qd="!">](https://www.npmjs.com/package/bitwrench)
|
|
5
|
+
[<img class="quikdown-img" src="https://github.com/deftio/bitwrench/actions/workflows/ci.yml/badge.svg" alt="CI" data-qd-alt="CI" data-qd-src="https://github.com/deftio/bitwrench/actions/workflows/ci.yml/badge.svg" data-qd="!">](https://github.com/deftio/bitwrench/actions/workflows/ci.yml)
|
|
6
|
+
[<img class="quikdown-img" src="https://img.shields.io/badge/coverage-99.1%25-brightgreen.svg" alt="Coverage" data-qd-alt="Coverage" data-qd-src="https://img.shields.io/badge/coverage-99.1%25-brightgreen.svg" data-qd="!">](https://github.com/deftio/bitwrench)
|
|
7
7
|
|
|
8
|
-
[
|
|
8
|
+
[<img class="quikdown-img" src="./images/bitwrench-logo-med.png" alt="bitwrench" data-qd-alt="bitwrench" data-qd-src="./images/bitwrench-logo-med.png" data-qd="!">](https://deftio.github.io/bitwrench/pages/)
|
|
9
9
|
|
|
10
|
-
Bitwrench
|
|
10
|
+
Bitwrench is a UI library that builds interfaces from plain JavaScript objects -- one format for components, styling, state, and server rendering, with no build step and zero dependencies.
|
|
11
11
|
|
|
12
12
|
```javascript
|
|
13
|
-
//
|
|
13
|
+
// A "TACO" -- Tag, Attributes, Content, Options
|
|
14
14
|
var page = {
|
|
15
15
|
t: 'div', a: { class: 'card' },
|
|
16
16
|
c: [
|
|
17
17
|
{ t: 'h2', c: 'Hello' },
|
|
18
18
|
{ t: 'p', c: 'UI as native JavaScript objects.' },
|
|
19
|
-
|
|
19
|
+
{ t: 'button', a: { onclick: function() { alert('clicked'); } }, c: 'Click me' }
|
|
20
20
|
]
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
bw.
|
|
23
|
+
bw.mount('#app', page); // -> live DOM
|
|
24
24
|
bw.html(page); // -> HTML string (Node.js, emails, SSR)
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Each object has four keys: **t** (tag), **a** (attributes), **c** (content), **o** (options for state
|
|
27
|
+
Each object has four keys: **t** (tag), **a** (attributes, including event handlers like `onclick`), **c** (content -- a string, array, or nested TACO), and **o** (options for state and lifecycle). Nest them, loop them, build them with functions -- they are ordinary JavaScript values.
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
**One file, everywhere.** At ~40KB gzipped with zero dependencies, bitwrench runs on anything with a browser -- phones, tablets, Raspberry Pi, even ESP32 microcontrollers. The device serves a single HTML page and pushes data as JSON; bitwrench handles all rendering, styling, and state on the client. No Node.js, no build step, no internet connection required.
|
|
32
|
-
|
|
33
|
-
Structure, styling, state, and server rendering are all handled as JavaScript objects:
|
|
34
|
-
|
|
35
|
-
- **No build toolchain** -- works with a `<script>` tag
|
|
36
|
-
- **Ready-made components** -- buttons, tables, modals, forms, charts, toasts -- one `make*()` call each, returns a composable TACO
|
|
37
|
-
- **CSS from JavaScript** -- `bw.css()` generates stylesheets, `bw.s()` composes inline styles, `bw.loadStyles()` derives a complete design system from 2 seed colors
|
|
38
|
-
- **Reactive state** -- `o.state` + `o.render` + `bw.update()` for stateful components; `bw.pub()`/`bw.sub()` for cross-component messaging
|
|
39
|
-
- **Dual rendering** -- same object renders to live DOM (`bw.DOM()`) or HTML string (`bw.html()`) for SSR, emails, or static sites
|
|
40
|
-
- **Server-driven UI** -- push UI updates from any backend (Python, C, Rust, Go) over SSE via the biwrench bwserve protocol; `client.screenshot()` captures the page back as PNG/JPEG
|
|
41
|
-
- **CLI** -- `bwcli` converts Markdown, HTML, and JSON to styled standalone pages
|
|
42
|
-
- **Debug tools** -- live client and server debugging with remote incremental inspect, screenshots, and state updates
|
|
43
|
-
- **TypeScript** -- full type declarations ship with the package (`dist/bitwrench.d.ts`); see the [TypeScript Usage Guide](docs/bitwrench_typescript_usage.md)
|
|
44
|
-
- **Utilities** -- color interpolation, random data, lorem ipsum, cookies, URL params, file I/O
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
### Coming from other Frameworks
|
|
49
|
-
|
|
50
|
-
Bitwrench uses JavaScript equivalents for most forms of front-end development. Here is a quick mapping (see the [docs](docs/README.md) and [Thinking in Bitwrench](docs/thinking-in-bitwrench.md) for more details).
|
|
51
|
-
|
|
52
|
-
| You're using | For | Bitwrench equivalent |
|
|
53
|
-
|---|---|---|
|
|
54
|
-
| React / Vue / Svelte | Components + reactivity | `{t, a, c, o}` objects + `o.state` + `o.render` |
|
|
55
|
-
| JSX / templates | Markup-in-JS | Native JS objects -- no compiler |
|
|
56
|
-
| Tailwind / CSS-in-JS | Styling | `bw.css()`, `bw.s()` style composition |
|
|
57
|
-
| Sass / PostCSS | CSS generation | `bw.css()` from JS objects (supports @media, @keyframes) |
|
|
58
|
-
| ThemeProvider / CSS vars | Theming | `bw.loadStyles()` / `bw.makeStyles()` from 2 seed colors |
|
|
59
|
-
| Streamlit / Gradio | Server-driven UI | bwserve SSE -- from any language (Python, Go, C, Rust) |
|
|
60
|
-
| Redux / Zustand / Pinia | State management | `o.state` + `bw.update()` + `bw.pub()/sub()` |
|
|
61
|
-
| Vite / webpack / Babel | Build tooling | Not needed -- open the HTML file |
|
|
62
|
-
| DefinitelyTyped / @types | Type declarations | Ships `dist/bitwrench.d.ts` -- nothing extra to install |
|
|
63
|
-
|
|
64
|
-
See the [Framework Translation Table](docs/framework-translation-table.md) for side-by-side code comparisons across 22 operations.
|
|
29
|
+
A TACO is already a JavaScript object, so there is nothing to compile or transform. This makes bitwrench a good fit for situations where a build pipeline costs more than it buys: dashboards, internal tools, embedded device UIs, server-driven pages, or anything you want to ship as a single HTML file.
|
|
65
30
|
|
|
66
31
|
## Installation
|
|
67
32
|
|
|
@@ -85,6 +50,8 @@ Or include directly in a page:
|
|
|
85
50
|
|
|
86
51
|
## Getting Started
|
|
87
52
|
|
|
53
|
+
A complete page -- no build step, no imports, everything is a plain object:
|
|
54
|
+
|
|
88
55
|
```html
|
|
89
56
|
<!DOCTYPE html>
|
|
90
57
|
<html lang="en">
|
|
@@ -94,21 +61,16 @@ Or include directly in a page:
|
|
|
94
61
|
<body>
|
|
95
62
|
<div id="app"></div>
|
|
96
63
|
<script>
|
|
97
|
-
bw.loadStyles();
|
|
64
|
+
bw.loadStyles(); // structural CSS + design tokens
|
|
98
65
|
|
|
99
|
-
bw.
|
|
100
|
-
t: 'div', a: { class: '
|
|
66
|
+
bw.mount('#app', {
|
|
67
|
+
t: 'div', a: { class: 'bw_container' },
|
|
101
68
|
c: [
|
|
102
69
|
{ t: 'h1', c: 'My App' },
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
bw.makeButton({
|
|
108
|
-
text: 'Click me',
|
|
109
|
-
variant: 'primary',
|
|
110
|
-
onclick: function() { alert('Hello!'); }
|
|
111
|
-
})
|
|
70
|
+
{ t: 'p', c: 'Built from plain JavaScript objects.' },
|
|
71
|
+
{ t: 'button',
|
|
72
|
+
a: { class: 'bw_btn bw_primary', onclick: function() { alert('Hello!'); } },
|
|
73
|
+
c: 'Click me' }
|
|
112
74
|
]
|
|
113
75
|
});
|
|
114
76
|
</script>
|
|
@@ -116,23 +78,43 @@ Or include directly in a page:
|
|
|
116
78
|
</html>
|
|
117
79
|
```
|
|
118
80
|
|
|
119
|
-
##
|
|
81
|
+
## Components
|
|
120
82
|
|
|
121
|
-
|
|
83
|
+
A component is a function that returns a TACO. Bitwrench ships ~50 factory functions (`bw.makeCard()`, `bw.makeTable()`, `bw.makeTabs()`, etc. -- see the [Component Cheat Sheet](docs/component-cheatsheet.md)). Each is a regular function that returns the same `{t, a, c, o}` object you could write by hand. Log the return value and look at it.
|
|
84
|
+
|
|
85
|
+
Your own components work the same way:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
function statusChip(label, ok) {
|
|
89
|
+
return { t: 'span', a: { class: 'bw_badge ' + (ok ? 'bw_success' : 'bw_warning') }, c: label };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Built-in and custom components compose identically
|
|
93
|
+
bw.mount('#app', {
|
|
94
|
+
t: 'div', a: { class: 'bw_container' },
|
|
95
|
+
c: [
|
|
96
|
+
bw.makeCard({ title: 'Server', content: 'Build 2.1.0' }),
|
|
97
|
+
statusChip('online', true)
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## State and Updates
|
|
103
|
+
|
|
104
|
+
Add `o.state` and `o.render` to any TACO to make it stateful. The render function receives `(el, state)`, and you call `bw.refresh(el)` when you want it to re-run:
|
|
122
105
|
|
|
123
106
|
```javascript
|
|
124
107
|
var counter = {
|
|
125
108
|
t: 'div',
|
|
126
109
|
o: {
|
|
127
110
|
state: { count: 0 },
|
|
128
|
-
render: function(el) {
|
|
129
|
-
|
|
130
|
-
bw.DOM(el, {
|
|
111
|
+
render: function(el, state) {
|
|
112
|
+
bw.mount(el, {
|
|
131
113
|
t: 'div', c: [
|
|
132
|
-
{ t: 'h3', c: 'Count: ' +
|
|
114
|
+
{ t: 'h3', c: 'Count: ' + state.count },
|
|
133
115
|
bw.makeButton({ text: '+1', onclick: function() {
|
|
134
|
-
|
|
135
|
-
bw.
|
|
116
|
+
state.count++;
|
|
117
|
+
bw.refresh(el);
|
|
136
118
|
}})
|
|
137
119
|
]
|
|
138
120
|
});
|
|
@@ -140,32 +122,90 @@ var counter = {
|
|
|
140
122
|
}
|
|
141
123
|
};
|
|
142
124
|
|
|
143
|
-
bw.
|
|
125
|
+
bw.mount('#app', counter);
|
|
144
126
|
```
|
|
145
127
|
|
|
146
|
-
|
|
128
|
+
State is also available as `el._bw_state` from outside the render function -- useful for debugging or direct access from event handlers.
|
|
129
|
+
|
|
130
|
+
> Event handlers go in `a: { onclick: fn }`, not in `o.mounted`. Handlers attached via `addEventListener` in `o.mounted` are lost when a component re-renders. Place them in `a:` and bitwrench re-attaches them on every render.
|
|
131
|
+
|
|
132
|
+
Bitwrench has no reactivity system. Mutating state does not trigger anything -- the DOM changes only when you call an update function. This is a deliberate trade: you give up automatic re-renders, and in exchange every DOM mutation is a function call you wrote, with a cost you chose.
|
|
133
|
+
|
|
134
|
+
The update functions form a cost ladder:
|
|
147
135
|
|
|
148
|
-
|
|
136
|
+
| Update verb | Cost | What happens |
|
|
137
|
+
| --- | --- | --- |
|
|
138
|
+
| `el.bw.method()` / slot setters | Surgical | Component updates its own DOM directly |
|
|
139
|
+
| `bw.update(ref, data)` | Dispatch | Calls `el.bw.update(data)` -- never rebuilds |
|
|
140
|
+
| `bw.message(ref, action, data)` | Dispatch | Calls `el.bw[action]()` by selector or UUID |
|
|
141
|
+
| `bw.patch(id, content)` | Targeted | Replaces one element's content |
|
|
142
|
+
| `bw.refresh(ref)` | Full rebuild | Re-runs `o.render`; children are unmounted and rebuilt |
|
|
149
143
|
|
|
150
|
-
|
|
144
|
+
Choosing where you sit on this ladder is the programming model. The full `bw.refresh()` re-render shown above is the simplest but most expensive option. The next section introduces slots and handles, which sit at the top of the ladder.
|
|
145
|
+
|
|
146
|
+
## Component API
|
|
147
|
+
|
|
148
|
+
After mounting, the DOM element is the component. The TACO is consumed at mount time -- there is no virtual DOM and no retained tree. State lives on the element (`el._bw_state`), and so does its public API (`el.bw`).
|
|
149
|
+
|
|
150
|
+
**Slots** map CSS selectors to setter/getter pairs. **Handles** define named methods. Both are attached to `el.bw` at mount time:
|
|
151
151
|
|
|
152
152
|
```javascript
|
|
153
|
-
bw.
|
|
154
|
-
|
|
153
|
+
var card = bw.mount('#stats', {
|
|
154
|
+
t: 'div', a: { class: 'stats-card' },
|
|
155
|
+
c: [
|
|
156
|
+
{ t: 'h3', a: { class: 'card-title' }, c: 'Revenue' },
|
|
157
|
+
{ t: 'span', a: { class: 'card-value' }, c: '$50,000' }
|
|
158
|
+
],
|
|
159
|
+
o: {
|
|
160
|
+
slots: { title: '.card-title', value: '.card-value' },
|
|
161
|
+
handle: {
|
|
162
|
+
update: function(el, data) { el.bw.setValue('$' + data.value.toLocaleString()); }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
155
165
|
});
|
|
156
166
|
|
|
157
|
-
bw.
|
|
167
|
+
card.bw.setTitle('Profit'); // slot setter -- updates one text node
|
|
168
|
+
card.bw.update({ value: 120000 }); // handle method -- runs your logic
|
|
169
|
+
bw.update(card, { value: 99000 }); // same call, dispatched by element or UUID
|
|
170
|
+
```
|
|
158
171
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
`slots: { title: '.card-title' }` generates `el.bw.setTitle()` and `el.bw.getTitle()` automatically. `handle` methods are attached as-is to `el.bw`. Neither causes a re-render -- they update the DOM directly.
|
|
173
|
+
|
|
174
|
+
Because everything lives on the element, debugging needs no extension: select a component in the browser's Elements panel and type `$0._bw_state` or `$0.bw`.
|
|
175
|
+
|
|
176
|
+
A component's lifecycle is four explicit calls:
|
|
177
|
+
|
|
178
|
+
| Phase | You call | Opt-in hook |
|
|
179
|
+
| --- | --- | --- |
|
|
180
|
+
| Define | a function that returns a TACO | -- |
|
|
181
|
+
| Mount | bw.mount('#app', taco) | o.mounted(el) |
|
|
182
|
+
| Update | el.bw.method() / bw.refresh(el) | -- |
|
|
183
|
+
| Unmount | bw.remove(el) | o.unmount(el) |
|
|
184
|
+
|
|
185
|
+
The [Component Lifecycle Walkthrough](docs/component-lifecycle.md) takes one card through all four phases. The [State Management guide](docs/state-management.md) covers the full component model.
|
|
186
|
+
|
|
187
|
+
## Cross-Component Communication
|
|
188
|
+
|
|
189
|
+
Components communicate through pub/sub. `bw.sub()` returns an unsubscribe function. Wildcard topics match any suffix after the colon:
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
bw.sub('item-added', function(detail) { console.log('New:', detail.name); });
|
|
193
|
+
bw.pub('item-added', { name: 'Widget' });
|
|
194
|
+
bw.sub('item:*', function(detail, topic) { /* matches item:added, item:removed, etc. */ });
|
|
163
195
|
```
|
|
164
196
|
|
|
197
|
+
Pass an element as the third argument to tie the subscription's lifetime to that element -- when the element is removed from the DOM, the subscription is automatically cleaned up:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
bw.sub('cart:updated', function(data) {
|
|
201
|
+
el._bw_state.count = data.count;
|
|
202
|
+
bw.refresh(el);
|
|
203
|
+
}, el);
|
|
204
|
+
```
|
|
165
205
|
|
|
166
206
|
## CSS from JavaScript
|
|
167
207
|
|
|
168
|
-
`bw.css()` generates CSS from objects. `bw.s()` composes inline styles from
|
|
208
|
+
`bw.css()` generates CSS strings from objects. `bw.injectCSS()` inserts a CSS string into the document as a `<style>` tag. `bw.s()` composes inline styles. `bw.responsive()` generates `@media` rules from a breakpoint map. These are generation functions -- they return strings, so you can use them anywhere:
|
|
169
209
|
|
|
170
210
|
```javascript
|
|
171
211
|
// Generate and inject a stylesheet
|
|
@@ -173,7 +213,7 @@ bw.injectCSS(bw.css({
|
|
|
173
213
|
'.my-card': { padding: '1rem', borderRadius: '8px' }
|
|
174
214
|
}));
|
|
175
215
|
|
|
176
|
-
// Compose inline styles from objects
|
|
216
|
+
// Compose inline styles from reusable objects
|
|
177
217
|
{ t: 'div', a: { style: bw.s({ display: 'flex' }, { gap: '1rem' }, { padding: '1rem' }) } }
|
|
178
218
|
|
|
179
219
|
// Responsive breakpoints
|
|
@@ -183,9 +223,11 @@ bw.responsive('.hero', {
|
|
|
183
223
|
});
|
|
184
224
|
```
|
|
185
225
|
|
|
226
|
+
Bitwrench does not own your CSS. You can use external stylesheets, Tailwind, or plain CSS alongside any of the above.
|
|
227
|
+
|
|
186
228
|
## Theming
|
|
187
229
|
|
|
188
|
-
`bw.loadStyles()` derives a complete design system -- buttons, alerts, badges, cards, forms, tables, hover states, focus rings -- from two seed colors.
|
|
230
|
+
`bw.loadStyles()` derives a complete design system -- buttons, alerts, badges, cards, forms, tables, hover states, focus rings -- from two seed colors. Call it with no arguments for structural CSS only, or pass a config to generate a full theme. `bw.toggleThemeMode()` switches between primary and alternate palettes:
|
|
189
231
|
|
|
190
232
|
```javascript
|
|
191
233
|
bw.loadStyles({
|
|
@@ -193,40 +235,33 @@ bw.loadStyles({
|
|
|
193
235
|
secondary: '#cc6633'
|
|
194
236
|
});
|
|
195
237
|
|
|
196
|
-
bw.
|
|
238
|
+
bw.toggleThemeMode(); // switch to alternate palette
|
|
197
239
|
```
|
|
198
240
|
|
|
241
|
+
Styles can be scoped to DOM subtrees, so different parts of a page can use different themes. See the [Theming guide](docs/theming.md) for presets, palette structure, and scoping.
|
|
199
242
|
|
|
200
|
-
##
|
|
243
|
+
## Server-Driven UI
|
|
201
244
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
| `bw.message(target, action, data)` | Dispatch a method call to a component's `el.bw` handle |
|
|
219
|
-
| `bw.pub(topic, detail)` | Publish to subscribers (exact + wildcard matches) |
|
|
220
|
-
| `bw.sub(topic, handler, el?)` | Subscribe to topic (supports wildcard `'ns:*'`); returns unsub function |
|
|
221
|
-
| `bw.once(topic, handler, el?)` | One-shot subscribe; auto-unsub after first fire |
|
|
222
|
-
| `bw.inspect(target, depth)` | Introspect a DOM subtree with bitwrench metadata (state, handles, type) |
|
|
223
|
-
| `bw.apply(msg)` | Apply a bwserve protocol message to the DOM |
|
|
224
|
-
|
|
225
|
-
See the full [API Reference](https://deftio.github.io/bitwrench/pages/08-api-reference.html) for all functions.
|
|
245
|
+
Because TACOs are plain objects, they serialize as JSON. This means a backend in any language can push UI updates to the browser.
|
|
246
|
+
|
|
247
|
+
Bitwrench includes bwserve, a protocol that sends TACO objects and patches over SSE. Button clicks come back as actions, `client.inspect()` reads DOM state, and `client.screenshot()` captures the live page as a PNG. The browser becomes a display and input device; the application logic lives wherever you want it.
|
|
248
|
+
|
|
249
|
+
Here is a C program on an ESP32 pushing a sensor reading to the browser:
|
|
250
|
+
|
|
251
|
+
```c
|
|
252
|
+
char msg[96], frame[128];
|
|
253
|
+
BW_PATCH(msg, "office-temp", "23.5");
|
|
254
|
+
BW_SSE_FRAME(frame, msg);
|
|
255
|
+
events.send(frame, NULL, millis()); // the browser updates
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The same protocol works from Python, Go, Rust, or a shell script with `curl`. See the [bwserve docs](docs/bwserve.md) for the full protocol, and the [ESP32 tutorial](docs/tutorial-embedded.md) for a complete embedded walkthrough.
|
|
259
|
+
|
|
260
|
+
The library is ~165KB on disk (~45KB gzipped). A lean build without the component library (BCCL) is ~128KB (~35KB gzipped). Both work entirely self-hosted from a microcontroller's flash -- no CDN and no internet required.
|
|
226
261
|
|
|
227
262
|
## CLI
|
|
228
263
|
|
|
229
|
-
|
|
264
|
+
`bwcli` converts files to styled standalone pages:
|
|
230
265
|
|
|
231
266
|
```bash
|
|
232
267
|
# Convert Markdown to a self-contained HTML page
|
|
@@ -247,17 +282,66 @@ Flags: `--output/-o`, `--standalone/-s`, `--cdn`, `--theme/-t`, `--css/-c`, `--t
|
|
|
247
282
|
|
|
248
283
|
```bash
|
|
249
284
|
bwcli serve --port 8080 --input-port 9000
|
|
250
|
-
curl -X POST http://localhost:9000 -d '{"type":"patch","
|
|
285
|
+
curl -X POST http://localhost:9000 -d '{"type":"patch","ref":"temp","content":"23.5 C"}'
|
|
251
286
|
```
|
|
252
287
|
|
|
288
|
+
## Coming from Other Frameworks
|
|
289
|
+
|
|
290
|
+
| You're using | For | Bitwrench equivalent |
|
|
291
|
+
| --- | --- | --- |
|
|
292
|
+
| React / Vue / Svelte | Components | {t, a, c, o} objects + o.state + o.render |
|
|
293
|
+
| JSX / templates | Markup-in-JS | Native JS objects -- no compiler |
|
|
294
|
+
| Tailwind / CSS-in-JS | Styling | bw.css(), bw.s() |
|
|
295
|
+
| Sass / PostCSS | CSS generation | bw.css() from JS objects (supports @media, @keyframes) |
|
|
296
|
+
| ThemeProvider / CSS vars | Theming | bw.loadStyles() / bw.makeStyles() from seed colors |
|
|
297
|
+
| Streamlit / Gradio | Server-driven UI | bwserve SSE -- from any language |
|
|
298
|
+
| Redux / Zustand / Pinia | State management | o.state + bw.refresh() + bw.pub()/sub() |
|
|
299
|
+
| Vite / webpack / Babel | Build tooling | Not needed -- open the HTML file |
|
|
300
|
+
| DefinitelyTyped / @types | Type declarations | Ships dist/bitwrench.d.ts |
|
|
301
|
+
|
|
302
|
+
See the [Framework Translation Table](docs/framework-translation-table.md) for side-by-side code comparisons across 22 operations.
|
|
303
|
+
|
|
304
|
+
## Core API
|
|
305
|
+
|
|
306
|
+
| Function | Description |
|
|
307
|
+
| --- | --- |
|
|
308
|
+
| bw.html(obj) | Convert a TACO to an HTML string |
|
|
309
|
+
| bw.mount(selector, obj) | Mount a TACO into a DOM element; returns the root element |
|
|
310
|
+
| bw.DOM(selector, obj) | Alias of bw.mount() |
|
|
311
|
+
| bw.create(taco) | Create a detached DOM element from a TACO (not inserted into the page) |
|
|
312
|
+
| bw.el(selector, apply?) | Find an element; optionally apply text, TACO, or function to it |
|
|
313
|
+
| bw.$(selector) | querySelectorAll as an array |
|
|
314
|
+
| bw.raw(str) | Mark a string as pre-escaped HTML (no double-escaping) |
|
|
315
|
+
| bw.css(rules) | Generate CSS from a JS object |
|
|
316
|
+
| bw.injectCSS(css, opts?) | Insert a CSS string into the document as a style tag |
|
|
317
|
+
| bw.s(...objs) | Compose inline style objects into a style string |
|
|
318
|
+
| bw.responsive(sel, breakpoints) | Generate @media CSS rules from a breakpoint map |
|
|
319
|
+
| bw.loadStyles(config?) | Structural CSS (no args) or generate + apply a theme from seed colors |
|
|
320
|
+
| bw.makeStyles(config) | Generate a theme from seed colors (returns styles object) |
|
|
321
|
+
| bw.applyStyles(styles) | Inject a generated styles object into the document |
|
|
322
|
+
| bw.toggleThemeMode(scope?) | Switch between primary and alternate palettes |
|
|
323
|
+
| bw.clearStyles() | Remove injected theme styles |
|
|
324
|
+
| bw.patch(id, content) | Update a specific element by id or UUID |
|
|
325
|
+
| bw.refresh(el) | Re-render a stateful component via its o.render function |
|
|
326
|
+
| bw.update(el, data) | Dispatch to el.bw.update(data) |
|
|
327
|
+
| bw.message(target, action, data) | Dispatch to el.bw[action]() by selector or UUID |
|
|
328
|
+
| bw.pub(topic, detail) | Publish to subscribers (exact + wildcard matches) |
|
|
329
|
+
| bw.sub(topic, handler, el?) | Subscribe to a topic (supports wildcard 'ns:*'); returns unsub function |
|
|
330
|
+
| bw.once(topic, handler, el?) | One-shot subscribe; auto-unsub after first fire |
|
|
331
|
+
| bw.remove(el) | Unmount a component (fires o.unmount hook) |
|
|
332
|
+
| bw.inspect(target, depth) | Introspect a DOM subtree with bitwrench metadata |
|
|
333
|
+
| bw.apply(msg) | Apply a bwserve protocol message to the DOM |
|
|
334
|
+
|
|
335
|
+
The update functions (`bw.patch`, `bw.refresh`, `bw.update`, `bw.message`) form a cost ladder -- see [State and Updates](#state-and-updates). Full [API Reference](https://deftio.github.io/bitwrench/pages/08-api-reference.html).
|
|
336
|
+
|
|
253
337
|
## Build Formats
|
|
254
338
|
|
|
255
339
|
| Format | File | Use case |
|
|
256
|
-
|
|
257
|
-
| UMD |
|
|
258
|
-
| ESM |
|
|
259
|
-
| CJS |
|
|
260
|
-
| ES5 |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| UMD | bitwrench.umd.min.js | Browsers and Node.js |
|
|
342
|
+
| ESM | bitwrench.esm.min.js | Modern bundlers (Vite, webpack, etc.) |
|
|
343
|
+
| CJS | bitwrench.cjs.min.js | Node.js require() |
|
|
344
|
+
| ES5 | bitwrench.es5.min.js | Legacy browsers (IE11) |
|
|
261
345
|
|
|
262
346
|
All formats include source maps. A separate CSS file (`bitwrench.css`) is also available for use without JavaScript.
|
|
263
347
|
|
|
@@ -265,13 +349,14 @@ All formats include source maps. A separate CSS file (`bitwrench.css`) is also a
|
|
|
265
349
|
|
|
266
350
|
**Start here:**
|
|
267
351
|
|
|
268
|
-
- **[Thinking in Bitwrench](docs/thinking-in-bitwrench.md)** -- the complete guide
|
|
269
|
-
- **[LLM Guide](docs/llm-bitwrench-guide.md)** -- compact single-file reference with all APIs, patterns, and rules
|
|
352
|
+
- **[Thinking in Bitwrench](docs/thinking-in-bitwrench.md)** -- the complete guide: TACO format, styling, composition, events, the component model, bwserve, and common patterns
|
|
353
|
+
- **[LLM Guide](docs/llm-bitwrench-guide.md)** -- compact single-file reference with all APIs, patterns, and rules
|
|
270
354
|
|
|
271
355
|
**Reference guides** (in `docs/`):
|
|
272
356
|
|
|
273
357
|
- [TACO Format](docs/taco-format.md) -- the `{t, a, c, o}` object format
|
|
274
|
-
- [
|
|
358
|
+
- [Component Lifecycle Walkthrough](docs/component-lifecycle.md) -- one stats card through all four phases
|
|
359
|
+
- [State Management](docs/state-management.md) -- component model, explicit updates, cross-component communication
|
|
275
360
|
- [Component Library](docs/component-library.md) -- all `make*()` functions with signatures and examples
|
|
276
361
|
- [Theming](docs/theming.md) -- palette-driven theme generation, presets, design tokens
|
|
277
362
|
- [CLI](docs/cli.md) -- the `bwcli` command for file conversion and pipe server
|
|
@@ -296,7 +381,7 @@ All formats include source maps. A separate CSS file (`bitwrench.css`) is also a
|
|
|
296
381
|
**Example apps** (in `examples/`):
|
|
297
382
|
|
|
298
383
|
- [Ember & Oak Coffee Co.](examples/ember-and-oak/) -- full landing page: theme, cart, search, charts, accordion, timeline
|
|
299
|
-
- [SunForge Landing Page](examples/landing-page/) --
|
|
384
|
+
- [SunForge Landing Page](examples/landing-page/) -- marketing page with zero reactive state, pure BCCL composition
|
|
300
385
|
- [Todo App](examples/todo-app/) -- stateful TACO with pub/sub
|
|
301
386
|
- [Metrics Dashboard](examples/dashboard/) -- live stat cards, bar chart, pub/sub, responsive layout
|
|
302
387
|
- [Signup Wizard](examples/wizard/) -- multi-step form, state transitions, bw.raw()
|
|
@@ -307,19 +392,19 @@ All formats include source maps. A separate CSS file (`bitwrench.css`) is also a
|
|
|
307
392
|
|
|
308
393
|
## FAQ
|
|
309
394
|
|
|
310
|
-
**Is this a framework?** -- No.
|
|
395
|
+
**Is this a framework?** -- No. It is a library (165KB on disk, 45KB gzipped). No lifecycle ceremony, no project structure. Import it, call functions, done. Lifecycle hooks (`o.mounted`, `o.unmount`) are opt-in.
|
|
311
396
|
|
|
312
|
-
**How does bitwrench compare to React/Vue?** -- They solve different problems at different scales. React and Vue provide a component model, virtual DOM, and ecosystem for large team-built SPAs. Bitwrench provides rendering and state primitives in a single file with no build step, aimed at single-page tools, dashboards, embedded devices, and server-driven UIs. They coexist fine
|
|
397
|
+
**How does bitwrench compare to React/Vue?** -- They solve different problems at different scales. React and Vue provide a component model, virtual DOM, and ecosystem for large team-built SPAs. Bitwrench provides rendering and state primitives in a single file with no build step, aimed at single-page tools, dashboards, embedded devices, and server-driven UIs. They coexist fine.
|
|
313
398
|
|
|
314
|
-
**How does CSS work?** -- Bitwrench
|
|
399
|
+
**How does CSS work?** -- Bitwrench does not own your CSS. Use any external stylesheet, Tailwind, or CSS file you want. On top of that, `bw.css()` generates CSS from JS objects (with `@media`, `@keyframes`, pseudo-classes), `bw.s()` composes inline style objects, and `bw.loadStyles()` derives a complete design system from seed colors. Use all three or none.
|
|
315
400
|
|
|
316
|
-
**What's the difference between `bw.
|
|
401
|
+
**What's the difference between `bw.mount()` and `bw.html()`?** -- Same TACO input, two outputs. `bw.mount('#app', taco)` mounts live DOM elements in a browser. `bw.html(taco)` returns an HTML string for Node.js scripts, email generators, static site builds, or anywhere you need markup without a browser. (`bw.DOM()` is an alias for `bw.mount()`.)
|
|
317
402
|
|
|
318
|
-
**What is bwserve?** --
|
|
403
|
+
**What is bwserve?** -- A protocol that turns the browser into a display and input device for a program running anywhere. The server pushes TACO objects and patches over SSE; button clicks come back as actions; `client.inspect()` returns DOM state; `client.screenshot()` returns a PNG. Language-agnostic: Python, Go, Rust, C, or a shell script with `curl`. See the [bwserve docs](docs/bwserve.md).
|
|
319
404
|
|
|
320
|
-
**Can I use bitwrench on embedded devices?** -- Yes
|
|
405
|
+
**Can I use bitwrench on embedded devices?** -- Yes. The device serves one HTML page plus the library from flash, no CDN required. Build the UI as TACOs in whatever language the device speaks (C, C++, MicroPython), push updates over SSE, and get button presses back the same way. C macros ship in `embedded_c/`. See the [ESP32 tutorial](docs/tutorial-embedded.md) and the [Pico W example](examples/embedded-pico-w/).
|
|
321
406
|
|
|
322
|
-
**Can I use it with TypeScript?** -- Yes. Type declarations ship with the package (`dist/bitwrench.d.ts`).
|
|
407
|
+
**Can I use it with TypeScript?** -- Yes. Type declarations ship with the package (`dist/bitwrench.d.ts`). See the [TypeScript Usage Guide](docs/bitwrench_typescript_usage.md).
|
|
323
408
|
|
|
324
409
|
**What about accessibility?** -- BCCL components emit semantic HTML with ARIA attributes where applicable. You can add any `aria-*` attribute via `a: { 'aria-label': '...' }`.
|
|
325
410
|
|
|
@@ -337,4 +422,4 @@ npm run cleanbuild # full production build with SRI hashes
|
|
|
337
422
|
|
|
338
423
|
## License
|
|
339
424
|
|
|
340
|
-
[BSD-2-Clause](./LICENSE.txt) -- (c) M. A. Chatterjee / [deftio](https://github.com/deftio)
|
|
425
|
+
[BSD-2-Clause](./LICENSE.txt) -- (c) M. A. Chatterjee / [deftio](https://github.com/deftio) -- use it in your own projects or commercially.
|