bitwrench 2.0.17 → 2.0.18

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 (67) hide show
  1. package/README.md +127 -38
  2. package/dist/bitwrench-bccl.cjs.js +8 -8
  3. package/dist/bitwrench-bccl.cjs.min.js +3 -3
  4. package/dist/bitwrench-bccl.esm.js +8 -8
  5. package/dist/bitwrench-bccl.esm.min.js +3 -3
  6. package/dist/bitwrench-bccl.umd.js +8 -8
  7. package/dist/bitwrench-bccl.umd.min.js +2 -2
  8. package/dist/bitwrench-code-edit.cjs.js +1 -1
  9. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  10. package/dist/bitwrench-code-edit.es5.js +1 -1
  11. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  12. package/dist/bitwrench-code-edit.esm.js +1 -1
  13. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  14. package/dist/bitwrench-code-edit.umd.js +1 -1
  15. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  16. package/dist/bitwrench-lean.cjs.js +941 -775
  17. package/dist/bitwrench-lean.cjs.min.js +20 -20
  18. package/dist/bitwrench-lean.es5.js +1012 -961
  19. package/dist/bitwrench-lean.es5.min.js +18 -18
  20. package/dist/bitwrench-lean.esm.js +941 -775
  21. package/dist/bitwrench-lean.esm.min.js +20 -20
  22. package/dist/bitwrench-lean.umd.js +941 -775
  23. package/dist/bitwrench-lean.umd.min.js +20 -20
  24. package/dist/bitwrench-util-css.cjs.js +236 -0
  25. package/dist/bitwrench-util-css.cjs.min.js +22 -0
  26. package/dist/bitwrench-util-css.es5.js +414 -0
  27. package/dist/bitwrench-util-css.es5.min.js +21 -0
  28. package/dist/bitwrench-util-css.esm.js +230 -0
  29. package/dist/bitwrench-util-css.esm.min.js +21 -0
  30. package/dist/bitwrench-util-css.umd.js +242 -0
  31. package/dist/bitwrench-util-css.umd.min.js +21 -0
  32. package/dist/bitwrench.cjs.js +948 -782
  33. package/dist/bitwrench.cjs.min.js +21 -21
  34. package/dist/bitwrench.css +456 -132
  35. package/dist/bitwrench.es5.js +1024 -970
  36. package/dist/bitwrench.es5.min.js +19 -19
  37. package/dist/bitwrench.esm.js +949 -783
  38. package/dist/bitwrench.esm.min.js +21 -21
  39. package/dist/bitwrench.min.css +1 -1
  40. package/dist/bitwrench.umd.js +948 -782
  41. package/dist/bitwrench.umd.min.js +21 -21
  42. package/dist/builds.json +178 -90
  43. package/dist/bwserve.cjs.js +514 -68
  44. package/dist/bwserve.esm.js +513 -69
  45. package/dist/sri.json +44 -36
  46. package/package.json +3 -2
  47. package/readme.html +136 -49
  48. package/src/bitwrench-bccl.js +7 -7
  49. package/src/bitwrench-color-utils.js +31 -9
  50. package/src/bitwrench-esm-entry.js +11 -0
  51. package/src/bitwrench-styles.js +439 -232
  52. package/src/bitwrench-util-css.js +229 -0
  53. package/src/bitwrench.js +483 -485
  54. package/src/bwserve/attach.js +57 -0
  55. package/src/bwserve/bwclient.js +141 -0
  56. package/src/bwserve/bwshell.js +102 -0
  57. package/src/bwserve/client.js +151 -1
  58. package/src/bwserve/index.js +127 -28
  59. package/src/cli/attach.js +555 -0
  60. package/src/cli/convert.js +2 -5
  61. package/src/cli/index.js +7 -0
  62. package/src/cli/inject.js +1 -1
  63. package/src/cli/serve.js +6 -2
  64. package/src/generate-css.js +11 -4
  65. package/src/vendor/html2canvas.min.js +20 -0
  66. package/src/version.js +3 -3
  67. package/src/bwserve/shell.js +0 -106
@@ -0,0 +1,555 @@
1
+ /**
2
+ * bwcli attach — Interactive remote debugging for bitwrench pages.
3
+ *
4
+ * Starts a bwserve instance and waits for a browser page to connect
5
+ * via the drop-in attach script. Once connected, provides a REPL
6
+ * for evaluating JS, inspecting the DOM tree, taking screenshots,
7
+ * and listening to events in real time.
8
+ *
9
+ * Usage:
10
+ * bwcli attach [options]
11
+ *
12
+ * This is bitwrench's answer to Playwright/Chrome DevTools — a
13
+ * built-in inspector that speaks the bwserve protocol.
14
+ *
15
+ * @module cli/attach
16
+ */
17
+
18
+ import { parseArgs } from 'node:util';
19
+ import { createInterface } from 'node:readline';
20
+ import { writeFileSync } from 'node:fs';
21
+ import { VERSION } from '../version.js';
22
+
23
+ var ATTACH_USAGE = `
24
+ bwcli attach v${VERSION} — Remote debugging REPL for bitwrench pages
25
+
26
+ Usage:
27
+ bwcli attach [options]
28
+
29
+ Description:
30
+ Starts a bwserve instance and waits for a browser to connect via the
31
+ drop-in attach script. Once connected, you get an interactive REPL
32
+ where you can evaluate JS expressions, inspect the DOM, take
33
+ screenshots, and listen to events — all from your terminal.
34
+
35
+ To connect a page, either:
36
+ 1. Add <script src="http://localhost:<port>/bw/attach.js"></script>
37
+ 2. Paste the script URL into devtools console:
38
+ var s=document.createElement('script');s.src='http://localhost:<port>/bw/attach.js';document.head.appendChild(s);
39
+
40
+ Options:
41
+ -p, --port <number> Server port (default: 7902)
42
+ --allow-screenshot Enable /screenshot command (loads html2canvas in browser)
43
+ -v, --verbose Verbose output (show protocol details)
44
+ -h, --help Print this help
45
+
46
+ REPL Commands:
47
+ <expression> Evaluate JS in the connected browser (e.g., document.title)
48
+ /help Show command reference
49
+ /quit, /q Exit
50
+ /tree [selector] [depth] Show DOM tree summary (default: body, depth 3)
51
+ /screenshot [sel] [file] Capture screenshot (requires --allow-screenshot)
52
+ /mount <sel> <comp> [json] Mount a BCCL component on the client
53
+ /render <sel> <taco-json> Render a TACO object at selector
54
+ /patch <id> <content> Patch an element's text content
55
+ /listen <sel> <event> Start listening for DOM events (e.g., /listen button click)
56
+ /unlisten <sel> <event> Stop listening for a previously added listener
57
+ /clients List connected clients
58
+ /exec <code> Execute JS without capturing return value
59
+
60
+ Examples:
61
+ bwcli attach Start on default port 7902
62
+ bwcli attach --port 3000 Use custom port
63
+ bwcli attach --allow-screenshot Enable screenshot support
64
+ bwcli attach -v Verbose mode (shows protocol)
65
+
66
+ # In the REPL:
67
+ bw> document.title
68
+ bw> bw.$('.bw-card').length
69
+ bw> /tree #app 2
70
+ bw> /screenshot body page.png
71
+ bw> /listen .bw-btn click
72
+ bw> /mount #app card {"title":"Hello","content":"World"}
73
+ `.trim();
74
+
75
+ /**
76
+ * Wrap a JS expression for client.query().
77
+ * Statements (var, let, const, if, for, etc.) are sent as-is.
78
+ * Expressions are wrapped in return() so the result comes back.
79
+ *
80
+ * @param {string} code - User input
81
+ * @returns {string} Code suitable for new Function()
82
+ */
83
+ export function wrapExpression(code) {
84
+ code = code.trim();
85
+ if (/^(var |let |const |if |for |while |function |try |switch |throw |class |\{)/.test(code)) {
86
+ return code;
87
+ }
88
+ return 'return (' + code + ')';
89
+ }
90
+
91
+ /**
92
+ * Run the attach subcommand.
93
+ * @param {string[]} argv - arguments after "attach"
94
+ */
95
+ export function runAttach(argv) {
96
+ var values;
97
+
98
+ try {
99
+ var result = parseArgs({
100
+ args: argv,
101
+ strict: true,
102
+ allowPositionals: false,
103
+ options: {
104
+ port: { type: 'string', short: 'p' },
105
+ 'allow-screenshot': { type: 'boolean' },
106
+ verbose: { type: 'boolean', short: 'v' },
107
+ help: { type: 'boolean', short: 'h' }
108
+ }
109
+ });
110
+ values = result.values;
111
+ } catch (err) {
112
+ console.error('Error: ' + err.message);
113
+ console.error('Run "bwcli attach --help" for usage.');
114
+ process.exit(1);
115
+ }
116
+
117
+ if (values.help) {
118
+ console.log(ATTACH_USAGE);
119
+ return;
120
+ }
121
+
122
+ var port = values.port ? parseInt(values.port, 10) : 7902;
123
+ var allowScreenshot = !!values['allow-screenshot'];
124
+ var verbose = !!values.verbose;
125
+
126
+ if (isNaN(port) || port < 1 || port > 65535) {
127
+ console.error('Error: --port must be a number between 1 and 65535.');
128
+ process.exit(1);
129
+ }
130
+
131
+ // Dynamic import of bwserve
132
+ import('../bwserve/index.js').then(function(bwserve) {
133
+ startAttach(bwserve, { port, allowScreenshot, verbose });
134
+ }).catch(function(err) {
135
+ console.error('Failed to load bwserve: ' + err.message);
136
+ process.exit(1);
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Start the attach server and REPL.
142
+ * @private
143
+ */
144
+ function startAttach(bwserve, opts) {
145
+ var app = bwserve.create({
146
+ port: opts.port,
147
+ title: 'bwcli attach',
148
+ allowExec: true,
149
+ allowScreenshot: opts.allowScreenshot
150
+ });
151
+
152
+ // Track the active client (most recent connection)
153
+ var activeClient = null;
154
+ var clients = new Map();
155
+
156
+ // Register a catch-all page handler for attach connections.
157
+ // Attach clients don't hit a registered page — they connect via SSE directly.
158
+ // We need to handle SSE connections from attach clients by intercepting _handleSSE.
159
+ // Since attach clients generate their own clientId (att_...), we store them when they connect.
160
+
161
+ // Monkey-patch _handleSSE to accept attach clients (those without a pending page record)
162
+ var origHandleSSE = app._handleSSE.bind(app);
163
+ app._handleSSE = function(req, res, clientId) {
164
+ // If no pending record exists, this is an attach client
165
+ if (!app._clients.has(clientId)) {
166
+ app._clients.set(clientId, { pagePath: '/_attach', client: null });
167
+ }
168
+ origHandleSSE(req, res, clientId);
169
+
170
+ // After original handler runs, grab the client
171
+ var record = app._clients.get(clientId);
172
+ if (record && record.client) {
173
+ var client = record.client;
174
+ client._allowScreenshot = opts.allowScreenshot;
175
+ clients.set(clientId, client);
176
+
177
+ // Set as active client
178
+ activeClient = client;
179
+
180
+ // Print connection message
181
+ process.stdout.write('\r\x1b[K');
182
+ console.log('[connected] client ' + clientId);
183
+ rl.prompt(true);
184
+
185
+ // Listen for events from _bw_listen
186
+ client.on('_bw_event', function(data) {
187
+ process.stdout.write('\r\x1b[K');
188
+ console.log('[event] ' + data.event + ' on ' + data.selector +
189
+ ' \u2192 ' + data.tagName + (data.id ? '#' + data.id : '') +
190
+ (data.text ? ' "' + data.text.slice(0, 50) + '"' : ''));
191
+ rl.prompt(true);
192
+ });
193
+
194
+ // Handle disconnect
195
+ req.on('close', function() {
196
+ clients.delete(clientId);
197
+ if (activeClient === client) {
198
+ // Switch to another client or null
199
+ activeClient = clients.size > 0 ? clients.values().next().value : null;
200
+ }
201
+ process.stdout.write('\r\x1b[K');
202
+ console.log('[disconnected] client ' + clientId);
203
+ if (activeClient) {
204
+ console.log('[active] client ' + activeClient.id);
205
+ } else {
206
+ console.log('Waiting for connection...');
207
+ }
208
+ rl.prompt(true);
209
+ });
210
+ }
211
+ };
212
+
213
+ // Start the server
214
+ app.listen(function() {
215
+ var url = 'http://localhost:' + opts.port;
216
+ console.log('bwcli attach v' + VERSION);
217
+ console.log(' Server: ' + url);
218
+ console.log(' Drop-in: <script src="' + url + '/bw/attach.js"></script>');
219
+ if (opts.allowScreenshot) {
220
+ console.log(' Screenshot: enabled');
221
+ }
222
+ console.log('');
223
+ console.log('Waiting for connection...');
224
+ console.log('Type /help for commands, /quit to exit.');
225
+ console.log('');
226
+ rl.prompt();
227
+ });
228
+
229
+ // Create readline REPL
230
+ var rl = createInterface({
231
+ input: process.stdin,
232
+ output: process.stdout,
233
+ prompt: 'bw> '
234
+ });
235
+
236
+ rl.on('line', function(line) {
237
+ line = line.trim();
238
+ if (!line) {
239
+ rl.prompt();
240
+ return;
241
+ }
242
+
243
+ // Slash commands
244
+ if (line.charAt(0) === '/') {
245
+ handleSlashCommand(line, activeClient, clients, opts, rl);
246
+ return;
247
+ }
248
+
249
+ // JS expression — requires active client
250
+ if (!activeClient) {
251
+ console.log('No client connected. Add the attach script to a page first.');
252
+ rl.prompt();
253
+ return;
254
+ }
255
+
256
+ var code = wrapExpression(line);
257
+ if (opts.verbose) {
258
+ console.log('[query] ' + code);
259
+ }
260
+
261
+ activeClient.query(code, { timeout: 10000 }).then(function(result) {
262
+ if (result !== undefined && result !== null) {
263
+ try {
264
+ console.log(typeof result === 'string' ? result : JSON.stringify(result, null, 2));
265
+ } catch (e) {
266
+ console.log(String(result));
267
+ }
268
+ } else {
269
+ console.log('undefined');
270
+ }
271
+ rl.prompt();
272
+ }).catch(function(err) {
273
+ console.error('[error] ' + err.message);
274
+ rl.prompt();
275
+ });
276
+ });
277
+
278
+ rl.on('close', function() {
279
+ console.log('\nExiting.');
280
+ app.close().then(function() {
281
+ process.exit(0);
282
+ });
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Handle slash commands in the REPL.
288
+ * @private
289
+ */
290
+ function handleSlashCommand(line, activeClient, clients, opts, rl) {
291
+ var parts = line.split(/\s+/);
292
+ var cmd = parts[0].toLowerCase();
293
+
294
+ switch (cmd) {
295
+ case '/help':
296
+ case '/h':
297
+ printHelp();
298
+ rl.prompt();
299
+ break;
300
+
301
+ case '/quit':
302
+ case '/q':
303
+ rl.close();
304
+ break;
305
+
306
+ case '/clients':
307
+ if (clients.size === 0) {
308
+ console.log('No clients connected.');
309
+ } else {
310
+ for (var [id, c] of clients) {
311
+ var marker = c === activeClient ? ' (active)' : '';
312
+ console.log(' ' + id + marker + (c._closed ? ' [closed]' : ''));
313
+ }
314
+ }
315
+ rl.prompt();
316
+ break;
317
+
318
+ case '/tree':
319
+ if (!activeClient) {
320
+ console.log('No client connected.');
321
+ rl.prompt();
322
+ break;
323
+ }
324
+ var treeSel = parts[1] || 'body';
325
+ var treeDepth = parts[2] ? parseInt(parts[2], 10) : 3;
326
+ var treePend = activeClient._pend(10000);
327
+ activeClient.call('_bw_tree', {
328
+ selector: treeSel,
329
+ depth: treeDepth,
330
+ requestId: treePend.requestId
331
+ });
332
+ treePend.promise.then(function(result) {
333
+ if (!result) {
334
+ console.log('(no element found for "' + treeSel + '")');
335
+ } else {
336
+ printTree(result, 0);
337
+ }
338
+ rl.prompt();
339
+ }).catch(function(err) {
340
+ console.error('[error] ' + err.message);
341
+ rl.prompt();
342
+ });
343
+ break;
344
+
345
+ case '/screenshot':
346
+ if (!activeClient) {
347
+ console.log('No client connected.');
348
+ rl.prompt();
349
+ break;
350
+ }
351
+ if (!opts.allowScreenshot) {
352
+ console.log('Screenshot not enabled. Restart with --allow-screenshot.');
353
+ rl.prompt();
354
+ break;
355
+ }
356
+ var ssSel = parts[1] || 'body';
357
+ var ssFile = parts[2] || 'screenshot-' + Date.now() + '.png';
358
+ console.log('Capturing ' + ssSel + ' ...');
359
+ activeClient.screenshot(ssSel, { timeout: 15000 }).then(function(result) {
360
+ writeFileSync(ssFile, result.data);
361
+ console.log('Saved: ' + ssFile + ' (' + result.width + 'x' + result.height + ', ' + result.data.length + ' bytes)');
362
+ rl.prompt();
363
+ }).catch(function(err) {
364
+ console.error('[error] ' + err.message);
365
+ rl.prompt();
366
+ });
367
+ break;
368
+
369
+ case '/mount':
370
+ if (!activeClient) {
371
+ console.log('No client connected.');
372
+ rl.prompt();
373
+ break;
374
+ }
375
+ if (parts.length < 3) {
376
+ console.log('Usage: /mount <selector> <component> [json-props]');
377
+ rl.prompt();
378
+ break;
379
+ }
380
+ var mountSel = parts[1];
381
+ var mountComp = parts[2];
382
+ var mountProps = {};
383
+ if (parts[3]) {
384
+ try {
385
+ mountProps = JSON.parse(parts.slice(3).join(' '));
386
+ } catch (e) {
387
+ console.error('Invalid JSON props: ' + e.message);
388
+ rl.prompt();
389
+ break;
390
+ }
391
+ }
392
+ activeClient.mount(mountSel, mountComp, mountProps, { timeout: 10000 }).then(function() {
393
+ console.log('Mounted ' + mountComp + ' at ' + mountSel);
394
+ rl.prompt();
395
+ }).catch(function(err) {
396
+ console.error('[error] ' + err.message);
397
+ rl.prompt();
398
+ });
399
+ break;
400
+
401
+ case '/render':
402
+ if (!activeClient) {
403
+ console.log('No client connected.');
404
+ rl.prompt();
405
+ break;
406
+ }
407
+ if (parts.length < 3) {
408
+ console.log('Usage: /render <selector> <taco-json>');
409
+ rl.prompt();
410
+ break;
411
+ }
412
+ var renderSel = parts[1];
413
+ var renderTaco;
414
+ try {
415
+ renderTaco = JSON.parse(parts.slice(2).join(' '));
416
+ } catch (e) {
417
+ console.error('Invalid TACO JSON: ' + e.message);
418
+ rl.prompt();
419
+ break;
420
+ }
421
+ activeClient.render(renderSel, renderTaco);
422
+ console.log('Rendered at ' + renderSel);
423
+ rl.prompt();
424
+ break;
425
+
426
+ case '/patch':
427
+ if (!activeClient) {
428
+ console.log('No client connected.');
429
+ rl.prompt();
430
+ break;
431
+ }
432
+ if (parts.length < 3) {
433
+ console.log('Usage: /patch <id> <content>');
434
+ rl.prompt();
435
+ break;
436
+ }
437
+ var patchId = parts[1];
438
+ var patchContent = parts.slice(2).join(' ');
439
+ activeClient.patch(patchId, patchContent);
440
+ console.log('Patched ' + patchId);
441
+ rl.prompt();
442
+ break;
443
+
444
+ case '/listen':
445
+ if (!activeClient) {
446
+ console.log('No client connected.');
447
+ rl.prompt();
448
+ break;
449
+ }
450
+ if (parts.length < 3) {
451
+ console.log('Usage: /listen <selector> <event>');
452
+ console.log('Examples: /listen button click, /listen .card mouseover');
453
+ rl.prompt();
454
+ break;
455
+ }
456
+ activeClient.call('_bw_listen', {
457
+ selector: parts[1],
458
+ event: parts[2]
459
+ });
460
+ console.log('Listening for ' + parts[2] + ' on ' + parts[1]);
461
+ rl.prompt();
462
+ break;
463
+
464
+ case '/unlisten':
465
+ if (!activeClient) {
466
+ console.log('No client connected.');
467
+ rl.prompt();
468
+ break;
469
+ }
470
+ if (parts.length < 3) {
471
+ console.log('Usage: /unlisten <selector> <event>');
472
+ rl.prompt();
473
+ break;
474
+ }
475
+ activeClient.call('_bw_unlisten', {
476
+ selector: parts[1],
477
+ event: parts[2]
478
+ });
479
+ console.log('Stopped listening for ' + parts[2] + ' on ' + parts[1]);
480
+ rl.prompt();
481
+ break;
482
+
483
+ case '/exec':
484
+ if (!activeClient) {
485
+ console.log('No client connected.');
486
+ rl.prompt();
487
+ break;
488
+ }
489
+ if (parts.length < 2) {
490
+ console.log('Usage: /exec <code>');
491
+ rl.prompt();
492
+ break;
493
+ }
494
+ activeClient.exec(parts.slice(1).join(' '));
495
+ console.log('Executed.');
496
+ rl.prompt();
497
+ break;
498
+
499
+ default:
500
+ console.log('Unknown command: ' + cmd + '. Type /help for available commands.');
501
+ rl.prompt();
502
+ break;
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Pretty-print a DOM tree from _bw_tree.
508
+ * @private
509
+ */
510
+ function printTree(node, indent) {
511
+ if (!node) return;
512
+ var prefix = ' '.repeat(indent);
513
+ var label = node.tag || '?';
514
+ if (node.id) label += '#' + node.id;
515
+ if (node.cls) label += '.' + node.cls.split(' ').join('.');
516
+ console.log(prefix + label);
517
+ if (node.children) {
518
+ for (var i = 0; i < node.children.length; i++) {
519
+ printTree(node.children[i], indent + 1);
520
+ }
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Print the REPL help reference.
526
+ * @private
527
+ */
528
+ function printHelp() {
529
+ console.log([
530
+ '',
531
+ 'bwcli attach — REPL Commands',
532
+ '',
533
+ ' <expression> Evaluate JS in the browser and print result',
534
+ ' Examples: document.title, bw.$(".card").length',
535
+ '',
536
+ ' /help, /h Show this help',
537
+ ' /quit, /q Exit',
538
+ '',
539
+ ' /tree [sel] [depth] Show DOM tree (default: body, depth 3)',
540
+ ' /screenshot [sel] [file] Capture screenshot to PNG file',
541
+ ' Requires --allow-screenshot flag',
542
+ ' /mount <sel> <comp> [json] Mount a BCCL component',
543
+ ' Example: /mount #app card {"title":"Hello"}',
544
+ ' /render <sel> <taco> Render TACO JSON at selector',
545
+ ' Example: /render #app {"t":"h1","c":"Hi"}',
546
+ ' /patch <id> <content> Update element text content',
547
+ ' Example: /patch counter 42',
548
+ ' /listen <sel> <event> Listen for DOM events (prints inline)',
549
+ ' Example: /listen button click',
550
+ ' /unlisten <sel> <event> Remove a listener',
551
+ ' /exec <code> Execute JS without capturing return value',
552
+ ' /clients List connected clients',
553
+ ''
554
+ ].join('\n'));
555
+ }
@@ -67,7 +67,7 @@ function resolveInjectionMode(flags) {
67
67
  /**
68
68
  * Resolve theme config from --theme flag value
69
69
  * @param {string} themeValue - Preset name or hex colors ("primary,secondary" or "primary,secondary,tertiary")
70
- * @returns {Object} Config for bw.generateTheme
70
+ * @returns {Object} Config for bw.makeStyles
71
71
  */
72
72
  function resolveTheme(themeValue) {
73
73
  if (!themeValue) return null;
@@ -165,10 +165,7 @@ export function convertFile(inputPath, flags = {}) {
165
165
  if (flags.theme) {
166
166
  const themeConfig = resolveTheme(flags.theme);
167
167
  if (themeConfig) {
168
- const result = bw.generateTheme('', {
169
- ...themeConfig,
170
- inject: false
171
- });
168
+ const result = bw.makeStyles(themeConfig);
172
169
  css += '\n' + result.css;
173
170
  }
174
171
  }
package/src/cli/index.js CHANGED
@@ -11,6 +11,7 @@ import { parseArgs } from 'node:util';
11
11
  import { VERSION } from '../version.js';
12
12
  import { convertFile } from './convert.js';
13
13
  import { runServe } from './serve.js';
14
+ import { runAttach } from './attach.js';
14
15
 
15
16
  const USAGE = `
16
17
  bwcli v${VERSION} — bitwrench command-line tool
@@ -18,6 +19,7 @@ bwcli v${VERSION} — bitwrench command-line tool
18
19
  Usage:
19
20
  bwcli <file> [options] Convert a file to styled HTML
20
21
  bwcli serve [dir] [options] Start bwserve development server
22
+ bwcli attach [options] Start attach server for remote debugging
21
23
  bwcli --version Print version
22
24
  bwcli --help Print this help
23
25
 
@@ -52,6 +54,8 @@ Examples:
52
54
  bwcli doc.md --theme "#336699,#cc6633" Custom theme colors
53
55
  bwcli serve Serve current directory on port 7902
54
56
  bwcli serve ./site --port 8080 Serve ./site on port 8080
57
+ bwcli attach Start remote debugging REPL on port 7902
58
+ bwcli attach --port 3000 Attach on custom port
55
59
  `.trim();
56
60
 
57
61
  /**
@@ -60,6 +64,9 @@ Examples:
60
64
  */
61
65
  export function run(argv) {
62
66
  // Check for subcommand before parseArgs (subcommands have different options)
67
+ if (argv.length > 0 && argv[0] === 'attach') {
68
+ return runAttach(argv.slice(1));
69
+ }
63
70
  if (argv.length > 0 && argv[0] === 'serve') {
64
71
  return runServe(argv.slice(1));
65
72
  }
package/src/cli/inject.js CHANGED
@@ -48,7 +48,7 @@ export function getInjectionHead(mode) {
48
48
  */
49
49
  export function getInjectionBodyEnd(mode) {
50
50
  if (mode === 'standalone' || mode === 'cdn') {
51
- return `<script>if(typeof bw!=='undefined'){bw.loadDefaultStyles();}</script>`;
51
+ return `<script>if(typeof bw!=='undefined'){bw.loadStyles();}</script>`;
52
52
  }
53
53
  // mode === 'none'
54
54
  return '';
package/src/cli/serve.js CHANGED
@@ -29,6 +29,7 @@ Options:
29
29
  --stdin Read protocol messages from stdin (newline-delimited JSON)
30
30
  -t, --theme <name> Theme preset or hex colors ("#pri,#sec")
31
31
  --title <string> Page title (default: "bwcli serve")
32
+ --allow-exec Enable exec messages (runs JS in browser, use for dev only)
32
33
  --open Open browser on start
33
34
  -v, --verbose Verbose output
34
35
  -h, --help Print this help
@@ -148,6 +149,7 @@ export function runServe(argv) {
148
149
  stdin: { type: 'boolean' },
149
150
  theme: { type: 'string', short: 't' },
150
151
  title: { type: 'string' },
152
+ 'allow-exec': { type: 'boolean' },
151
153
  open: { type: 'boolean' },
152
154
  verbose: { type: 'boolean', short: 'v' },
153
155
  help: { type: 'boolean', short: 'h' }
@@ -193,7 +195,8 @@ export function runServe(argv) {
193
195
  theme: theme,
194
196
  title: title,
195
197
  verbose: verbose,
196
- open: !!values.open
198
+ open: !!values.open,
199
+ allowExec: !!values['allow-exec']
197
200
  });
198
201
  }).catch(function(err) {
199
202
  console.error('Failed to load bwserve: ' + err.message);
@@ -209,7 +212,8 @@ function startServer(bwserve, opts) {
209
212
  port: opts.webPort,
210
213
  title: opts.title,
211
214
  static: opts.dir,
212
- theme: opts.theme
215
+ theme: opts.theme,
216
+ allowExec: opts.allowExec
213
217
  });
214
218
 
215
219
  // Register a passthrough page handler — just keeps clients alive
@@ -8,16 +8,23 @@ import { getAllStyles, getStructuralStyles, generateThemedCSS,
8
8
  import { derivePalette } from './bitwrench-color-utils.js';
9
9
  import fs from 'fs';
10
10
  import path from 'path';
11
+ import { createRequire } from 'module';
12
+ const require = createRequire(import.meta.url);
13
+ const pkg = require('../package.json');
11
14
 
12
15
  // Convert styles object to CSS string
13
16
  function stylesToCSS(styles) {
14
17
  let css = `/**
15
- * Bitwrench v2 CSS
16
- * Class-based styles to prevent framework collisions
17
- * Generated from bitwrench-styles.js
18
+ * bitwrench.css v${pkg.version} — AUTO-GENERATED, DO NOT EDIT
19
+ *
20
+ * Generated by src/generate-css.js from bitwrench-styles.js
21
+ * This is a static snapshot of what bw.loadStyles() produces
22
+ * at runtime. Use one or the other, not both.
23
+ *
24
+ * To regenerate: npm run build:css
25
+ * ${pkg.homepage}
18
26
  */
19
27
 
20
- /* Base styles with .bw namespace */
21
28
  `;
22
29
 
23
30
  // Process each style rule