bitwrench 2.0.22 → 2.0.24

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.
Files changed (88) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +4 -3
  3. package/bin/bwmcp.js +3 -0
  4. package/dist/bitwrench-bccl.cjs.js +1 -1
  5. package/dist/bitwrench-bccl.cjs.min.js +1 -1
  6. package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
  7. package/dist/bitwrench-bccl.esm.js +1 -1
  8. package/dist/bitwrench-bccl.esm.min.js +1 -1
  9. package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
  10. package/dist/bitwrench-bccl.umd.js +1 -1
  11. package/dist/bitwrench-bccl.umd.min.js +1 -1
  12. package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
  13. package/dist/bitwrench-code-edit.cjs.js +1 -1
  14. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  15. package/dist/bitwrench-code-edit.es5.js +1 -1
  16. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  17. package/dist/bitwrench-code-edit.esm.js +1 -1
  18. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  19. package/dist/bitwrench-code-edit.umd.js +1 -1
  20. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  21. package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
  22. package/dist/bitwrench-debug.js +1 -1
  23. package/dist/bitwrench-debug.min.js +1 -1
  24. package/dist/bitwrench-lean.cjs.js +3 -3
  25. package/dist/bitwrench-lean.cjs.min.js +2 -2
  26. package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
  27. package/dist/bitwrench-lean.es5.js +3 -3
  28. package/dist/bitwrench-lean.es5.min.js +2 -2
  29. package/dist/bitwrench-lean.es5.min.js.gz +0 -0
  30. package/dist/bitwrench-lean.esm.js +3 -3
  31. package/dist/bitwrench-lean.esm.min.js +2 -2
  32. package/dist/bitwrench-lean.esm.min.js.gz +0 -0
  33. package/dist/bitwrench-lean.umd.js +3 -3
  34. package/dist/bitwrench-lean.umd.min.js +2 -2
  35. package/dist/bitwrench-lean.umd.min.js.gz +0 -0
  36. package/dist/bitwrench-util-css.cjs.js +1 -1
  37. package/dist/bitwrench-util-css.cjs.min.js +1 -1
  38. package/dist/bitwrench-util-css.es5.js +1 -1
  39. package/dist/bitwrench-util-css.es5.min.js +1 -1
  40. package/dist/bitwrench-util-css.esm.js +1 -1
  41. package/dist/bitwrench-util-css.esm.min.js +1 -1
  42. package/dist/bitwrench-util-css.umd.js +1 -1
  43. package/dist/bitwrench-util-css.umd.min.js +1 -1
  44. package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
  45. package/dist/bitwrench.cjs.js +3 -3
  46. package/dist/bitwrench.cjs.min.js +2 -2
  47. package/dist/bitwrench.cjs.min.js.gz +0 -0
  48. package/dist/bitwrench.css +1 -1
  49. package/dist/bitwrench.es5.js +3 -3
  50. package/dist/bitwrench.es5.min.js +2 -2
  51. package/dist/bitwrench.es5.min.js.gz +0 -0
  52. package/dist/bitwrench.esm.js +3 -3
  53. package/dist/bitwrench.esm.min.js +2 -2
  54. package/dist/bitwrench.esm.min.js.gz +0 -0
  55. package/dist/bitwrench.umd.js +3 -3
  56. package/dist/bitwrench.umd.min.js +2 -2
  57. package/dist/bitwrench.umd.min.js.gz +0 -0
  58. package/dist/builds.json +65 -65
  59. package/dist/bwserve.cjs.js +2 -2
  60. package/dist/bwserve.esm.js +2 -2
  61. package/dist/sri.json +45 -45
  62. package/docs/README.md +76 -0
  63. package/docs/app-patterns.md +264 -0
  64. package/docs/bitwrench-mcp.md +426 -0
  65. package/docs/bitwrench_api.md +2232 -0
  66. package/docs/bw-attach.md +399 -0
  67. package/docs/bwserve.md +841 -0
  68. package/docs/cli.md +307 -0
  69. package/docs/component-cheatsheet.md +144 -0
  70. package/docs/component-library.md +1099 -0
  71. package/docs/framework-translation-table.md +33 -0
  72. package/docs/llm-bitwrench-guide.md +672 -0
  73. package/docs/routing.md +562 -0
  74. package/docs/state-management.md +767 -0
  75. package/docs/taco-format.md +373 -0
  76. package/docs/theming.md +309 -0
  77. package/docs/thinking-in-bitwrench.md +1457 -0
  78. package/docs/tutorial-bwserve.md +297 -0
  79. package/docs/tutorial-embedded.md +314 -0
  80. package/docs/tutorial-website.md +255 -0
  81. package/package.json +11 -3
  82. package/readme.html +5 -4
  83. package/src/mcp/knowledge.js +231 -0
  84. package/src/mcp/live.js +226 -0
  85. package/src/mcp/server.js +216 -0
  86. package/src/mcp/tools.js +369 -0
  87. package/src/mcp/transport.js +55 -0
  88. package/src/version.js +3 -3
@@ -0,0 +1,672 @@
1
+ # Bitwrench LLM Guide
2
+
3
+ > Compact tutorial for AI-assisted bitwrench development.
4
+ > For the full teaching narrative, see `docs/thinking-in-bitwrench.md`.
5
+
6
+ ---
7
+
8
+ ## Step 1: Build Something
9
+
10
+ Start here. This is a complete bitwrench page:
11
+
12
+ ```html
13
+ <!DOCTYPE html>
14
+ <html lang="en">
15
+ <head>
16
+ <meta charset="UTF-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ <title>My Page</title>
19
+ <script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>
20
+ </head>
21
+ <body>
22
+ <div id="app"></div>
23
+ <script>
24
+ bw.loadStyles(); // structural CSS (buttons, cards, tables)
25
+ bw.loadStyles({ primary: '#336699', secondary: '#cc6633' }); // themed colors
26
+
27
+ bw.DOM('#app', {
28
+ t: 'div', a: { class: 'bw-container' },
29
+ c: [
30
+ bw.makeNavbar({ brand: 'My App', items: [{ text: 'Home', href: '#' }] }),
31
+ bw.makeCard({ title: 'Hello', content: 'Built with bitwrench.' }),
32
+ bw.makeTable({ data: [
33
+ { name: 'Alice', role: 'Admin' },
34
+ { name: 'Bob', role: 'User' }
35
+ ], sortable: true })
36
+ ]
37
+ });
38
+ </script>
39
+ </body>
40
+ </html>
41
+ ```
42
+
43
+ That's it. No build step. No npm install. Open the file in a browser.
44
+
45
+ **What happened:**
46
+ 1. `bw.loadStyles()` injected Bootstrap-like CSS
47
+ 2. `bw.loadStyles({...})` generated a themed color palette from two seed colors
48
+ 3. `bw.makeNavbar()`, `bw.makeCard()`, `bw.makeTable()` returned TACO objects
49
+ 4. `bw.DOM('#app', taco)` rendered them into the page
50
+
51
+ ---
52
+
53
+ ## Step 2: Understand TACO
54
+
55
+ Every UI element is a plain JS object `{t, a, c, o}`:
56
+
57
+ ```javascript
58
+ { t: 'div', a: { class: 'card', id: 'x' }, c: 'Hello world' }
59
+ // tag attributes content
60
+ // => <div class="card" id="x">Hello world</div>
61
+ ```
62
+
63
+ - `t` -- tag name (defaults to `'div'` if omitted)
64
+ - `a` -- object of HTML attributes (including event handlers)
65
+ - `c` -- string, TACO, or array of TACOs (nested arbitrarily deep)
66
+ - `o` -- bitwrench-only metadata (lifecycle, state) -- never in HTML output
67
+
68
+ Content is HTML-escaped by default. Use `bw.raw(str)` for trusted HTML.
69
+ `null`/`undefined`/`false` in content arrays are silently skipped.
70
+
71
+ ### Nesting
72
+
73
+ ```javascript
74
+ { t: 'div', c: [
75
+ { t: 'h2', c: 'Title' },
76
+ { t: 'p', c: 'Body text' },
77
+ { t: 'ul', c: items.map(function(i) { return { t: 'li', c: i }; }) }
78
+ ]}
79
+ ```
80
+
81
+ ### TACO is computation
82
+
83
+ Every field is a JS expression. This is the key insight:
84
+
85
+ ```javascript
86
+ var isAdmin = true;
87
+ var items = ['Apples', 'Bananas'];
88
+
89
+ {
90
+ t: isAdmin ? 'h1' : 'h2', // computed tag
91
+ a: { class: 'header ' + (isAdmin ? 'admin' : '') }, // computed attrs
92
+ c: items.map(function(i) { return { t: 'li', c: i }; }) // .map() => children
93
+ }
94
+ ```
95
+
96
+ **Functions are components. Arrays are slots. `.map()` is iteration. Ternaries are conditionals.**
97
+
98
+ ```javascript
99
+ // A "component" is just a function that returns TACO
100
+ function colorCard(title, body, color) {
101
+ return {
102
+ t: 'div', a: { style: 'border-left:4px solid ' + color + '; padding:1rem' },
103
+ c: [{ t: 'h3', c: title }, { t: 'p', c: body }]
104
+ };
105
+ }
106
+
107
+ bw.DOM('#app', { t: 'div', c: [
108
+ colorCard('Warning', 'Disk low', '#e67e22'),
109
+ colorCard('OK', 'All clear', '#27ae60')
110
+ ]});
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Step 3: Three Levels
116
+
117
+ | Level | What | How | When |
118
+ |-------|------|-----|------|
119
+ | **0 -- Data** | Plain JS object | `bw.makeCard({...})` or `{t,a,c}` | Static content, SSR |
120
+ | **1 -- DOM** | Rendered tree | `bw.DOM('#x', taco)` | Re-render on demand |
121
+ | **2 -- Stateful** | Reactive component | `o.state` + `o.render` + `bw.update()` | Interactive UI |
122
+
123
+ **Most UI should be Level 0.** Escalate only when needed.
124
+
125
+ ### Level 1 -- re-render when data changes
126
+
127
+ ```javascript
128
+ var filter = 'all';
129
+ function render() {
130
+ var items = filter === 'all' ? all : all.filter(function(i) { return i.type === filter; });
131
+ bw.DOM('#list', { t: 'div', c: items.map(function(i) {
132
+ return bw.makeCard({ title: i.name, content: i.desc });
133
+ })});
134
+ }
135
+ render();
136
+ ```
137
+
138
+ ### Level 2 -- stateful TACO
139
+
140
+ ```javascript
141
+ bw.DOM('#app', {
142
+ t: 'div',
143
+ o: {
144
+ state: { count: 0 },
145
+ render: function(el) {
146
+ var s = el._bw_state;
147
+ bw.DOM(el, { t: 'div', c: [
148
+ { t: 'h3', c: 'Count: ' + s.count },
149
+ bw.makeButton({ text: '+1', onclick: function() {
150
+ s.count++;
151
+ bw.update(el);
152
+ }})
153
+ ]});
154
+ }
155
+ }
156
+ });
157
+ ```
158
+
159
+ **How it works:** `createDOM()` copies `o.state` to `el._bw_state`, stores `o.render` as `el._bw_render`, calls it immediately. On state change, call `bw.update(el)` to re-invoke render.
160
+
161
+ ---
162
+
163
+ ## Step 4: Events -- the #1 Mistake
164
+
165
+ **Always put event handlers in `a: { onclick: fn }`, never in `o.mounted`.**
166
+
167
+ When a stateful component re-renders (after `bw.update()`), old DOM children are replaced. Listeners attached via `addEventListener` in `o.mounted` are silently lost.
168
+
169
+ ```javascript
170
+ // CORRECT -- re-attached on every render
171
+ { t: 'button', a: { onclick: function() { save(); } }, c: 'Save' }
172
+ bw.makeButton({ text: 'Save', onclick: function() { save(); } })
173
+
174
+ // WRONG -- silently breaks after first re-render
175
+ { t: 'button', c: 'Save',
176
+ o: { mounted: function(el) { el.addEventListener('click', save); } } }
177
+ ```
178
+
179
+ **When is `o.mounted` OK?** Only for non-event setup: IntersectionObserver, ResizeObserver, measuring dimensions. Never for click/input handlers.
180
+
181
+ ### Cross-component: pub/sub
182
+
183
+ ```javascript
184
+ // Publisher
185
+ bw.pub('cart:updated', { count: cart.length });
186
+
187
+ // Subscriber (auto-cleans when element is removed)
188
+ bw.sub('cart:updated', function(d) {
189
+ el._bw_state.n = d.count;
190
+ bw.update(el);
191
+ }, el);
192
+ ```
193
+
194
+ ### Component handles (o.handle / o.slots)
195
+
196
+ **This is the recommended way to update rendered components without re-rendering.** Use `bw.mount()` + `el.bw.method()` instead of re-rendering the entire component when you only need to change a slot, advance a carousel, or toggle an accordion.
197
+
198
+ BCCL components expose methods via `el.bw`:
199
+
200
+ ```javascript
201
+ var el = bw.mount('#target', bw.makeCarousel({ items: slides }));
202
+ el.bw.goToSlide(2);
203
+ el.bw.next();
204
+ el.bw.pause();
205
+
206
+ var card = bw.mount('#info', bw.makeCard({ title: 'Stats', content: '0' }));
207
+ card.bw.setTitle('Updated');
208
+ card.bw.setContent({ t: 'b', c: '42' });
209
+ ```
210
+
211
+ Build your own with `o.handle` and `o.slots`:
212
+
213
+ ```javascript
214
+ {
215
+ t: 'div', c: [
216
+ { t: 'h3', a: { class: 'title' }, c: 'Default' },
217
+ { t: 'div', a: { class: 'body' }, c: 'Content' }
218
+ ],
219
+ o: {
220
+ slots: { title: '.title', body: '.body' }, // => el.bw.setTitle(), el.bw.getTitle()
221
+ handle: {
222
+ reset: function(el) { el.bw.setTitle('Default'); el.bw.setBody('Content'); }
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Step 5: CSS and Theming
231
+
232
+ ### Inline styles -- JS variables
233
+
234
+ ```javascript
235
+ var brand = '#336699', radius = '12px';
236
+ { t: 'div', a: { style: 'background:' + brand + '; border-radius:' + radius }, c: 'Hi' }
237
+ ```
238
+
239
+ ### bw.s() -- merge style objects
240
+
241
+ ```javascript
242
+ { t: 'div', a: { style: bw.s({ display: 'flex' }, { gap: '1rem' }, { padding: '1rem' }) }, c: '...' }
243
+ // Conditional: null args skipped
244
+ { t: 'div', a: { style: bw.s({ padding: '1rem' }, isActive ? { fontWeight: '700' } : null) } }
245
+ ```
246
+
247
+ ### bw.css() -- generate stylesheet from JS objects
248
+
249
+ ```javascript
250
+ bw.injectCSS(bw.css({
251
+ '.card': { borderRadius: '12px', padding: '1.5rem', border: '1px solid #ddd' },
252
+ '.card:hover': { boxShadow: '0 4px 12px rgba(0,0,0,.1)' },
253
+ '@keyframes fadeIn': { '0%': { opacity: '0' }, '100%': { opacity: '1' } },
254
+ '@media (max-width: 768px)': { '.card': { padding: '0.75rem' } }
255
+ }));
256
+ ```
257
+
258
+ CamelCase auto-converts to kebab-case. All `@`-prefix keys nest recursively.
259
+
260
+ ### Functions as CSS generators (like Sass mixins)
261
+
262
+ ```javascript
263
+ function cardStyles(accent) {
264
+ var s = bw.deriveShades(accent);
265
+ return { background: s.light, border: '1px solid ' + s.border, color: s.darkText };
266
+ }
267
+ bw.injectCSS(bw.css({ '.warn': cardStyles('#e67e22'), '.ok': cardStyles('#27ae60') }));
268
+ ```
269
+
270
+ ### Theme system -- complete palette from 2 colors
271
+
272
+ ```javascript
273
+ bw.loadStyles(); // structural CSS only (call once)
274
+
275
+ bw.loadStyles({
276
+ primary: '#336699', secondary: '#cc6633',
277
+ spacing: 'normal', // 'compact'|'normal'|'spacious'
278
+ radius: 'md', // 'none'|'sm'|'md'|'lg'|'pill'
279
+ elevation: 'md', // 'flat'|'sm'|'md'|'lg'
280
+ harmonize: 0.20 // hue shift semantics toward primary (0-1)
281
+ });
282
+
283
+ bw.toggleStyles(); // switch primary <=> alternate palette
284
+
285
+ // Or generate separately:
286
+ var theme = bw.makeStyles({ primary: '#336699', secondary: '#cc6633' });
287
+ bw.applyStyles(theme);
288
+ // Use tokens: theme.palette.primary.base, theme.palette.secondary.light, etc.
289
+
290
+ // 12 built-in presets:
291
+ bw.loadStyles(bw.THEME_PRESETS.ocean);
292
+ // teal, ocean, sunset, forest, slate, rose, indigo, amber, emerald, nord, coral, midnight
293
+ ```
294
+
295
+ ### Responsive breakpoints
296
+
297
+ ```javascript
298
+ bw.injectCSS(bw.responsive('.grid', {
299
+ base: { gridTemplateColumns: '1fr' },
300
+ md: { gridTemplateColumns: 'repeat(2, 1fr)' },
301
+ lg: { gridTemplateColumns: 'repeat(4, 1fr)' }
302
+ }));
303
+ ```
304
+
305
+ ### bw.u() -- Tailwind-style shorthand (optional plugin)
306
+
307
+ Load `bitwrench-util-css.umd.min.js` after bitwrench (~1KB gzipped):
308
+
309
+ ```javascript
310
+ bw.u('flex gap4 p4 alignCenter') // => { display:'flex', gap:'1rem', ... }
311
+ bw.u.css('flex gap4 p4 alignCenter') // => "display:flex;gap:1rem;padding:1rem;..."
312
+ bw.u.cls('flex gap4 p4') // => "bw_flex bw_gap_4 bw_p_4"
313
+
314
+ // Compose with bw.s()
315
+ a: { style: bw.s(bw.u('flex gap4'), { borderBottom: '2px solid #336699' }) }
316
+
317
+ // Scale: {n} = n * 0.25rem. p4 = 1rem, gap8 = 2rem, m1 = 0.25rem
318
+ // Tokens: p/m/pt/pb/pl/pr/px/py/mt/mb/ml/mr/mx/my + {n}, gap{n}, w{n}, h{n}
319
+ // Keywords: flex, flexCol, flexRow, block, hidden, bold, textCenter, justifyCenter, alignCenter
320
+ // Colors: bg-[#hex], text-[#hex], bg-name, text-name
321
+ // Sizes: textSm, textBase, textLg, textXl, text2xl, text3xl
322
+
323
+ // Custom tokens
324
+ bw.u.extend({ shadow: { boxShadow: '0 2px 8px rgba(0,0,0,0.1)' } });
325
+ bw.u.css('p4 shadow') // includes your custom token
326
+ ```
327
+
328
+ ---
329
+
330
+ ## Step 6: BCCL Components
331
+
332
+ **Bitwrench ships 50+ ready-made components. Check the table below BEFORE writing custom TACO for common UI patterns.** All `bw.make*()` return Level 0 TACO objects. Factory dispatcher: `bw.make('card', props)`.
333
+
334
+ ### Most-Used Components
335
+
336
+ | Component | Key Props | Capabilities |
337
+ |-----------|-----------|-------------|
338
+ | makeTable | data, columns, sortable, pageSize, onRowClick | **Click-to-sort (default!)**, pagination, row selection, column renderers |
339
+ | makeCard | title, content, footer, image, variant | Image positions, shadow variants, **slots: setTitle/setContent/setFooter** |
340
+ | makeModal | title, content, footer, onClose | ESC dismiss, backdrop close, **handles: open(), close()** |
341
+ | makeToast | title, content, variant, delay, position | Auto-dismiss 5s, 6 positions, **handle: dismiss()** |
342
+ | makeTabs | tabs [{label,content}], activeIndex | Arrow/Home/End keys, WAI-ARIA, **handles: setActiveTab(i), getActiveTab()** |
343
+ | makeAccordion | items [{title,content}], multiOpen | Animations, ARIA, **handles: toggle(i), openAll(), closeAll()** |
344
+ | makeCarousel | items, autoPlay, interval | Auto-play, keyboard, **handles: goToSlide(i), next(), prev(), pause(), play()** |
345
+ | makeFormGroup | label, input, help, validation, required | Required indicator, validation feedback -- **don't reinvent this** |
346
+ | makeTextarea | placeholder, value, rows | bw_form_control styling -- **use this, not raw `{t:'textarea'}`** |
347
+ | makeInput | type, placeholder, value, oninput | All HTML5 types with bw_form_control styling |
348
+ | makeSelect | options [{value,text}], value | Dropdown select with bw_form_control styling |
349
+ | makeProgress | value, max, variant, striped, animated | Striped + animated, **handles: setValue(n), getValue()** |
350
+ | makeStatCard | value, label, change, variant | Dashboard KPI with change arrows, **slots: setValue/setLabel** |
351
+ | makeSearchInput | placeholder, onSearch, onInput | Enter to search, clear button |
352
+ | makeChipInput | chips, placeholder, onAdd, onRemove | Enter to add, X to remove, **handles: addChip(), removeChip(), getChips(), clear()** |
353
+ | makeNav | items [{text,href,active}], pills | Tab/pill/vertical navigation |
354
+ | makeNavbar | brand, items, dark | Navigation bar with brand |
355
+ | makeButton | text, variant, size, onclick | 8 variants + outline-* variants |
356
+ | makeDropdown | trigger, items, align | Click menu with outside-click-to-close |
357
+ | makeAlert | content, variant, dismissible | Dismissible notification banner |
358
+
359
+ For the full 47-component table with all props and handles, see [Component Cheat Sheet](component-cheatsheet.md).
360
+
361
+ ### Components with Imperative Handles
362
+
363
+ Six components expose `el.bw` methods for direct control. Use `bw.mount()` to get the element:
364
+
365
+ ```javascript
366
+ var el = bw.mount('#app', bw.makeCarousel({ items: slides }));
367
+ el.bw.goToSlide(2); // handle method
368
+ el.bw.pause();
369
+
370
+ var card = bw.mount('#info', bw.makeCard({ title: 'Stats', content: '0' }));
371
+ card.bw.setTitle('Revenue'); // slot setter
372
+ card.bw.setContent({ t: 'b', c: '$42k' });
373
+ ```
374
+
375
+ | Factory | Handle Methods | Slot Methods |
376
+ |---------|---------------|-------------|
377
+ | makeCarousel | goToSlide, next, prev, getActiveIndex, pause, play | -- |
378
+ | makeTabs | setActiveTab, getActiveTab | -- |
379
+ | makeAccordion | toggle, openAll, closeAll | -- |
380
+ | makeModal | open, close | -- |
381
+ | makeProgress | setValue, getValue | -- |
382
+ | makeChipInput | addChip, removeChip, getChips, clear | -- |
383
+ | makeCard | -- | setTitle/getTitle, setContent/getContent, setFooter/getFooter |
384
+ | makeStatCard | -- | setValue/getValue, setLabel/getLabel |
385
+
386
+ ### Other Components
387
+
388
+ **Layout**: makeContainer, makeRow, makeCol ({xs,sm,md,lg,xl}), makeStack
389
+ **Content**: makeBadge, makeHero, makeSection, makeFeatureGrid, makeCTA, makeCodeDemo, makeMediaObject, makeTimeline, makeStepper, makeListGroup, makeAvatar, makeSkeleton, makeSpinner
390
+ **Forms**: makeForm, makeCheckbox, makeRadio, makeSwitch, makeRange, makeFileUpload
391
+ **Buttons**: makeButtonGroup (vertical/horizontal)
392
+ **Tables**: makeTableFromArray (2D arrays), makeDataTable (with title + scroll wrapper), makeBarChart (CSS-only)
393
+ **Overlays**: makeTooltip, makePopover
394
+ **Navigation**: makeBreadcrumb, makePagination
395
+
396
+ ### Variants
397
+ `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark`
398
+
399
+ ---
400
+
401
+ ## Step 7: Debugging with bwcli
402
+
403
+ ### Browser console
404
+
405
+ ```javascript
406
+ var el = bw.$('#app')[0];
407
+ el._bw_state; // current state
408
+ el._bw_render; // render function
409
+ bw.inspect(el); // formatted debug output (el.bw methods, state, classes)
410
+ ```
411
+
412
+ ### bwcli attach -- remote debugging REPL
413
+
414
+ Connect a terminal REPL to any page for live inspection:
415
+
416
+ ```bash
417
+ npm install -g bitwrench
418
+ bwcli attach # starts on port 7902
419
+ bwcli attach --port 3000 --allow-screenshot # custom port + screenshots
420
+ ```
421
+
422
+ Add this to your page:
423
+ ```html
424
+ <script src="http://localhost:7902/bw/attach.js"></script>
425
+ ```
426
+
427
+ Then from the terminal:
428
+ ```
429
+ > bw.$('#app')[0]._bw_state // evaluate any JS
430
+ > /tree // DOM tree of current page
431
+ > /tree #app // subtree of #app
432
+ > /screenshot // capture page as PNG
433
+ > /screenshot #my-card // capture specific element
434
+ > /listen // stream DOM events
435
+ > /mount #target {t:'h1',c:'Hi'} // mount TACO into page
436
+ > /render {t:'div',c:'test'} // render TACO to HTML string
437
+ > /patch #status "Loading..." // update element text
438
+ ```
439
+
440
+ ### LLM visual feedback loop
441
+
442
+ Use bwserve screenshots for iterative UI refinement:
443
+
444
+ ```javascript
445
+ import bwserve from 'bitwrench/bwserve';
446
+ var app = bwserve.create({ port: 7902, allowScreenshot: true });
447
+
448
+ app.page('/', function(client) {
449
+ client.render('#app', myTaco);
450
+
451
+ // Capture what the user sees
452
+ var img = await client.screenshot('#app', { maxWidth: 800 });
453
+ // img.data is a Buffer (PNG) -- send to vision model for evaluation
454
+ // Vision model says "button is too small" => adjust TACO => re-render => screenshot again
455
+ });
456
+ ```
457
+
458
+ ---
459
+
460
+ ## Step 8: bwserve -- Server-Driven UI
461
+
462
+ Push TACO from any server to the browser via SSE. No client-side app logic needed.
463
+
464
+ ```javascript
465
+ import bwserve from 'bitwrench/bwserve';
466
+ var app = bwserve.create({ port: 7902 });
467
+
468
+ app.page('/', function(client) {
469
+ // Render initial UI
470
+ client.render('#app', {
471
+ t: 'div', c: [
472
+ { t: 'h1', c: 'Hello from server' },
473
+ { t: 'p', a: { id: 'status' }, c: 'Connected.' },
474
+ { t: 'button', a: { 'data-bw-action': 'greet' }, c: 'Say hello' }
475
+ ]
476
+ });
477
+
478
+ // Incremental updates
479
+ client.patch('#status', 'Processing...');
480
+ client.append('#log', { t: 'p', c: 'New entry' });
481
+ client.remove('.old-item');
482
+
483
+ // Handle user actions (data-bw-action elements)
484
+ client.on('greet', function() { client.patch('#status', 'Hello!'); });
485
+
486
+ // Register + call client-side functions
487
+ client.register('showAlert', 'function(msg) { alert(msg); }');
488
+ client.call('showAlert', 'Server says hi!');
489
+ });
490
+ app.listen();
491
+ ```
492
+
493
+ **Protocol**: `replace`, `patch`, `append`, `remove`, `batch`, `message`, `register`, `call`, `exec`.
494
+ **Language-agnostic**: any server that writes SSE works (Python, Go, Rust, C, shell scripts).
495
+
496
+ ---
497
+
498
+ ## Step 9: Client-Side Routing
499
+
500
+ Map URLs to views with `bw.router()`. Hash mode (default) works everywhere; history mode uses `pushState`.
501
+
502
+ ```javascript
503
+ bw.router({
504
+ target: '#app',
505
+ routes: {
506
+ '/': function() { return { t: 'h1', c: 'Home' }; },
507
+ '/about': function() { return { t: 'h1', c: 'About' }; },
508
+ '/users/:id': function(params) { return { t: 'div', c: 'User ' + params.id }; },
509
+ '/docs/*': function(params) { return { t: 'div', c: 'Doc: ' + params._rest }; },
510
+ '*': function() { return { t: 'h1', c: '404' }; }
511
+ },
512
+ before: function(to, from) {
513
+ if (to === '/admin' && !loggedIn) return '/login'; // redirect
514
+ },
515
+ after: function(to) { console.log('navigated to', to); }
516
+ });
517
+
518
+ // Programmatic navigation
519
+ bw.navigate('/users/42');
520
+ bw.navigate('/about', { replace: true });
521
+
522
+ // Navigation links (returns TACO <a> with onclick wired)
523
+ bw.link('/about', 'About Us', { class: 'nav-item' })
524
+
525
+ // Query strings: /search?q=hello => params._query.q === 'hello'
526
+
527
+ // React to route changes via pub/sub
528
+ bw.sub('bw:route', function(d) { navEl.bw.setActive(d.path); }, navEl);
529
+
530
+ // Cleanup
531
+ var r = bw.router({ ... });
532
+ r.destroy(); // remove listeners, stop routing
533
+ ```
534
+
535
+ **Route priority**: exact > parameterized (`:id`) > catch-all (`/prefix/*`) > global wildcard (`*`).
536
+
537
+ **Modes**: `mode: 'hash'` (default, `#/path`) or `mode: 'history'` (pushState, needs server SPA fallback). History mode supports `base: '/app'` for prefix stripping.
538
+
539
+ ---
540
+
541
+ ## Step 10: HTML Generation and CLI
542
+
543
+ ### bw.html() -- TACO to HTML string
544
+
545
+ ```javascript
546
+ bw.html({ t: 'button', a: { onclick: function() { alert('hi'); } }, c: 'Click' })
547
+ // => '<button onclick="bw.funcGetById(\'bw_fn_0\')(event)">Click</button>'
548
+ ```
549
+
550
+ Event handlers are auto-serialized via `funcRegister`.
551
+
552
+ ### bw.htmlPage() -- complete standalone document
553
+
554
+ ```javascript
555
+ var page = bw.htmlPage({
556
+ title: 'My App',
557
+ body: [{ t: 'h1', c: 'Hello' }, bw.makeButton({ text: 'Click', onclick: fn })],
558
+ runtime: 'shim', // 'inline'(120KB offline)|'cdn'|'shim'(500B)|'none'
559
+ theme: 'ocean', // preset name or { primary: '#hex', secondary: '#hex' }
560
+ css: '.custom { color: red; }'
561
+ });
562
+ // page is a complete <!DOCTYPE html> string with working event handlers
563
+ ```
564
+
565
+ ### bwcli -- command-line conversion
566
+
567
+ ```bash
568
+ bwcli input.md -o output.html # markdown to HTML
569
+ bwcli input.md -o output.html --theme ocean # with theme preset
570
+ bwcli input.md -o output.html --standalone # bitwrench inlined (offline)
571
+ bwcli input.md --theme "#336699,#cc6633" # custom seed colors
572
+ bwcli serve # dev server (port 7902)
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Core API Quick Reference
578
+
579
+ ### Rendering
580
+ | Function | Description |
581
+ |----------|-------------|
582
+ | `bw.html(taco)` | TACO to HTML string |
583
+ | `bw.createDOM(taco)` | TACO to detached DOM element |
584
+ | `bw.DOM(sel, taco)` | Mount TACO into existing element |
585
+ | `bw.mount(sel, taco)` | Like DOM() but returns root element (for el.bw access) |
586
+ | `bw.h(tag, a?, c?, o?)` | TACO constructor from positional args |
587
+ | `bw.raw(str)` | Mark string as pre-escaped HTML |
588
+ | `bw.htmlPage(opts)` | TACO to complete HTML document |
589
+
590
+ ### CSS and Styling
591
+ | Function | Description |
592
+ |----------|-------------|
593
+ | `bw.css(rules)` | JS object to CSS string (handles @media, @keyframes) |
594
+ | `bw.injectCSS(css, {id})` | Insert CSS into document |
595
+ | `bw.s(...styles)` | Merge style objects into style string |
596
+ | `bw.responsive(sel, bp)` | Generate @media CSS from breakpoint object |
597
+ | `bw.loadStyles()` | Load structural CSS (no args) or generate+apply theme (with config) |
598
+ | `bw.makeStyles(cfg)` | Generate styles from seed colors (returns styles object) |
599
+ | `bw.applyStyles(styles)` | Inject generated styles into document |
600
+ | `bw.toggleStyles()` | Switch primary/alternate palettes |
601
+
602
+ ### State and Lifecycle
603
+ | Function | Description |
604
+ |----------|-------------|
605
+ | `o.state` | Initial state (copied to `el._bw_state`) |
606
+ | `o.render(el, state)` | Render function, called on mount and `bw.update()` |
607
+ | `o.handle` | Methods attached to `el.bw` namespace |
608
+ | `o.slots` | `{name: '.selector'}` => auto `el.bw.setName()`/`el.bw.getName()` |
609
+ | `o.mounted(el)` | After DOM insertion (NOT for event handlers) |
610
+ | `o.unmount(el)` | Before DOM removal |
611
+ | `bw.update(el)` | Re-invoke render function |
612
+ | `bw.mount(sel, taco)` | Mount + return root element |
613
+ | `bw.cleanup(el)` | Run unmount hooks, clear subscriptions |
614
+ | `bw.patch(id, content)` | Update element by id or UUID |
615
+ | `bw.inspect(el)` | Debug: log el.bw methods, state, classes |
616
+
617
+ ### Communication
618
+ | Function | Description |
619
+ |----------|-------------|
620
+ | `bw.pub(topic, data)` | App-wide publish |
621
+ | `bw.sub(topic, fn, el?)` | Subscribe (optional lifecycle tie to element) |
622
+ | `bw.message(target, action, data)` | Dispatch to `el.bw[action](data)` |
623
+ | `bw.emit(el, event, detail)` | DOM-scoped CustomEvent |
624
+
625
+ ### Routing
626
+ | Function | Description |
627
+ |----------|-------------|
628
+ | `bw.router(config)` | Create and start client-side router. Returns `{ navigate, current, destroy }` |
629
+ | `bw.navigate(path, opts)` | Programmatic navigation (delegates to active router) |
630
+ | `bw.link(path, content, attrs)` | Returns TACO `<a>` with navigation wired |
631
+
632
+ ### Utilities
633
+ | Function | Description |
634
+ |----------|-------------|
635
+ | `bw.$('selector')` | querySelectorAll as array |
636
+ | `bw.uuid(prefix)` | Generate unique ID |
637
+ | `bw.typeOf(x)` | Enhanced typeof: 'array', 'null', 'date' |
638
+ | `bw.escapeHTML(str)` | Escape HTML special chars |
639
+ | `bw.deriveShades(hex)` | 8 shade variants from one color |
640
+ | `bw.textOnColor(hex)` | Contrast-safe text color ('#fff' or '#000') |
641
+ | `bw.random(min, max)` | Random integer (or array variant) |
642
+ | `bw.loremIpsum(n)` | Placeholder text |
643
+ | `bw.parseRJSON(str)` | Relaxed JSON (unquoted keys, trailing commas) |
644
+ | `bw.saveClientFile(name, data)` | Browser file download |
645
+
646
+ ---
647
+
648
+ ## Key Rules Summary
649
+
650
+ 1. **Events in `a: { onclick: fn }`** -- never in `o.mounted`. This is the #1 mistake.
651
+ 2. **Call `bw.loadStyles()`** before rendering. Use `bw.loadStyles(config)` for themed colors.
652
+ 3. **Content is escaped by default.** Use `bw.raw(str)` for trusted HTML only.
653
+ 4. **All `make*()` return Level 0 TACOs** -- pass to `bw.DOM()` or `bw.html()`.
654
+ 5. **TACO is computation** -- every field is a JS expression. Use variables, `.map()`, ternaries.
655
+ 6. **CSS is just strings** -- store in variables, compose with `bw.s()`, generate with `bw.css()`.
656
+ 7. **Three levels are explicit** -- you always know if you have data (L0), DOM (L1), or stateful (L2).
657
+ 8. **No raw DOM** -- use `bw.DOM()`, not `innerHTML` or `document.querySelector`.
658
+ 9. **CSS classes use `bw-` prefix**: `bw-card`, `bw-btn`, `bw-container`.
659
+ 10. **Routing is built in** -- `bw.router()` for SPAs. Hash mode by default, history mode optional.
660
+ 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
+
663
+ ---
664
+
665
+ ## Removed APIs (v2.0.19)
666
+
667
+ `bw.component()`, `bw.compile()`, `bw.when()`, `bw.each()` -- all throw Error.
668
+ Replaced by `o.handle` + `o.slots` + `bw.mount()`. See Step 4.
669
+
670
+ ---
671
+
672
+ *Bitwrench: 40KB gzipped, zero dependencies, no build step. [github.com/deftio/bitwrench](https://github.com/deftio/bitwrench)*