bitwrench 2.0.32 → 2.1.1

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 (126) hide show
  1. package/README.md +211 -125
  2. package/dist/bitwrench-bccl.cjs.js +349 -188
  3. package/dist/bitwrench-bccl.cjs.min.js +2 -39
  4. package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
  5. package/dist/bitwrench-bccl.esm.js +349 -188
  6. package/dist/bitwrench-bccl.esm.min.js +2 -39
  7. package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
  8. package/dist/bitwrench-bccl.umd.js +349 -188
  9. package/dist/bitwrench-bccl.umd.min.js +2 -39
  10. package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
  11. package/dist/bitwrench-code-edit.cjs.js +17 -6
  12. package/dist/bitwrench-code-edit.cjs.min.js +2 -20
  13. package/dist/bitwrench-code-edit.es5.js +8 -3
  14. package/dist/bitwrench-code-edit.es5.min.js +2 -19
  15. package/dist/bitwrench-code-edit.esm.js +17 -6
  16. package/dist/bitwrench-code-edit.esm.min.js +2 -19
  17. package/dist/bitwrench-code-edit.umd.js +17 -6
  18. package/dist/bitwrench-code-edit.umd.min.js +2 -19
  19. package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
  20. package/dist/bitwrench-debug.js +1 -1
  21. package/dist/bitwrench-debug.min.js +1 -1
  22. package/dist/bitwrench-lean.cjs.js +2492 -1628
  23. package/dist/bitwrench-lean.cjs.min.js +2 -80
  24. package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
  25. package/dist/bitwrench-lean.es5.js +2740 -1838
  26. package/dist/bitwrench-lean.es5.min.js +2 -49
  27. package/dist/bitwrench-lean.es5.min.js.gz +0 -0
  28. package/dist/bitwrench-lean.esm.js +2492 -1628
  29. package/dist/bitwrench-lean.esm.min.js +2 -80
  30. package/dist/bitwrench-lean.esm.min.js.gz +0 -0
  31. package/dist/bitwrench-lean.umd.js +2492 -1628
  32. package/dist/bitwrench-lean.umd.min.js +2 -80
  33. package/dist/bitwrench-lean.umd.min.js.gz +0 -0
  34. package/dist/bitwrench-util-color.cjs.js +251 -0
  35. package/dist/bitwrench-util-color.cjs.min.js +3 -0
  36. package/dist/bitwrench-util-color.es5.js +256 -0
  37. package/dist/bitwrench-util-color.es5.min.js +3 -0
  38. package/dist/bitwrench-util-color.esm.js +241 -0
  39. package/dist/bitwrench-util-color.esm.min.js +3 -0
  40. package/dist/bitwrench-util-color.umd.js +257 -0
  41. package/dist/bitwrench-util-color.umd.min.js +3 -0
  42. package/dist/bitwrench-util-css.cjs.js +2 -1
  43. package/dist/bitwrench-util-css.cjs.min.js +2 -21
  44. package/dist/bitwrench-util-css.es5.js +2 -1
  45. package/dist/bitwrench-util-css.es5.min.js +2 -20
  46. package/dist/bitwrench-util-css.esm.js +2 -1
  47. package/dist/bitwrench-util-css.esm.min.js +1 -19
  48. package/dist/bitwrench-util-css.umd.js +2 -1
  49. package/dist/bitwrench-util-css.umd.min.js +2 -20
  50. package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
  51. package/dist/bitwrench.cjs.js +2826 -1801
  52. package/dist/bitwrench.cjs.min.js +2 -99
  53. package/dist/bitwrench.cjs.min.js.gz +0 -0
  54. package/dist/bitwrench.css +403 -479
  55. package/dist/bitwrench.d.ts +70 -73
  56. package/dist/bitwrench.es5.js +3106 -2020
  57. package/dist/bitwrench.es5.min.js +2 -67
  58. package/dist/bitwrench.es5.min.js.gz +0 -0
  59. package/dist/bitwrench.esm.js +2826 -1801
  60. package/dist/bitwrench.esm.min.js +2 -99
  61. package/dist/bitwrench.esm.min.js.gz +0 -0
  62. package/dist/bitwrench.min.css +1 -1
  63. package/dist/bitwrench.umd.js +2826 -1801
  64. package/dist/bitwrench.umd.min.js +2 -99
  65. package/dist/bitwrench.umd.min.js.gz +0 -0
  66. package/dist/builds.json +222 -134
  67. package/dist/bwserve.cjs.js +289 -282
  68. package/dist/bwserve.d.ts +157 -0
  69. package/dist/bwserve.esm.js +290 -283
  70. package/dist/sri.json +54 -46
  71. package/docs/README.md +6 -3
  72. package/docs/app-patterns.md +7 -6
  73. package/docs/bitwrench-for-wasm.md +53 -54
  74. package/docs/bitwrench-mcp.md +2 -2
  75. package/docs/bitwrench-northstar-principles.md +406 -0
  76. package/docs/bitwrench-taco-schema-discussion.md +2 -2
  77. package/docs/bitwrench_api.md +191 -106
  78. package/docs/bitwrench_typescript_usage.md +5 -5
  79. package/docs/bw-attach.md +29 -75
  80. package/docs/bwserve.md +200 -168
  81. package/docs/cli.md +36 -12
  82. package/docs/component-cheatsheet.md +2 -2
  83. package/docs/component-library.md +4 -4
  84. package/docs/component-lifecycle.md +234 -0
  85. package/docs/drift-lint.md +268 -0
  86. package/docs/framework-translation-table.md +4 -4
  87. package/docs/llm-bitwrench-guide.md +60 -50
  88. package/docs/routing.md +11 -13
  89. package/docs/state-management.md +110 -109
  90. package/docs/taco-format.md +13 -14
  91. package/docs/theming.md +13 -3
  92. package/docs/thinking-in-bitwrench.md +858 -983
  93. package/docs/tutorial-bwserve.md +37 -36
  94. package/docs/tutorial-embedded.md +10 -21
  95. package/docs/tutorial-website.md +2 -2
  96. package/package.json +38 -7
  97. package/readme.html +262 -161
  98. package/src/bitwrench-bccl-entry.js +2 -2
  99. package/src/bitwrench-bccl.js +346 -185
  100. package/src/bitwrench-code-edit.js +16 -5
  101. package/src/bitwrench-color-utils.js +117 -181
  102. package/src/bitwrench-file-ops.js +2 -2
  103. package/src/bitwrench-lean.js +4 -3
  104. package/src/bitwrench-router.js +5 -2
  105. package/src/bitwrench-styles.js +420 -504
  106. package/src/bitwrench-util-color.js +240 -0
  107. package/src/bitwrench-util-css.js +1 -0
  108. package/src/bitwrench-utils.js +4 -0
  109. package/src/bitwrench.d.ts +70 -73
  110. package/src/bitwrench.h +5 -0
  111. package/src/bitwrench.js +1939 -933
  112. package/src/bwserve/attach.js +0 -1
  113. package/src/bwserve/bwclient.js +172 -32
  114. package/src/bwserve/bwshell.js +0 -4
  115. package/src/bwserve/client.js +59 -220
  116. package/src/bwserve/index.js +59 -26
  117. package/src/bwserve.d.ts +157 -0
  118. package/src/bwserve.h +5 -0
  119. package/src/cli/attach.js +12 -75
  120. package/src/cli/convert.js +2 -2
  121. package/src/cli/serve.js +37 -35
  122. package/src/generate-css.js +1 -1
  123. package/src/mcp/knowledge.js +4 -4
  124. package/src/mcp/live.js +21 -13
  125. package/src/mcp/tools.js +0 -1
  126. package/src/version.js +3 -7
@@ -85,7 +85,10 @@ function tokenizeJS(code) {
85
85
  var buf = '';
86
86
 
87
87
  function flush(type) {
88
- if (buf.length) { tokens.push({ type: type || 'plain', text: buf }); buf = ''; }
88
+ if (buf.length) {
89
+ /* c8 ignore next -- flush() is always called with an explicit type argument */
90
+ tokens.push({ type: type || 'plain', text: buf }); buf = '';
91
+ }
89
92
  }
90
93
 
91
94
  while (i < len) {
@@ -279,7 +282,10 @@ function tokenizeCSS(code) {
279
282
  var buf = '';
280
283
 
281
284
  function flush(type) {
282
- if (buf.length) { tokens.push({ type: type || 'plain', text: buf }); buf = ''; }
285
+ if (buf.length) {
286
+ /* c8 ignore next -- flush() is always called with an explicit type argument */
287
+ tokens.push({ type: type || 'plain', text: buf }); buf = '';
288
+ }
283
289
  }
284
290
 
285
291
  while (i < len) {
@@ -316,7 +322,7 @@ function tokenizeCSS(code) {
316
322
  flush('selector');
317
323
  var aBuf = '@';
318
324
  i++;
319
- while (i < len && /[a-zA-Z\-]/.test(code[i])) { aBuf += code[i]; i++; }
325
+ while (i < len && /[a-zA-Z-]/.test(code[i])) { aBuf += code[i]; i++; }
320
326
  tokens.push({ type: 'at-rule', text: aBuf });
321
327
  continue;
322
328
  }
@@ -389,7 +395,10 @@ function tokenizeHTML(code) {
389
395
  var buf = '';
390
396
 
391
397
  function flush(type) {
392
- if (buf.length) { tokens.push({ type: type || 'plain', text: buf }); buf = ''; }
398
+ if (buf.length) {
399
+ /* c8 ignore next -- flush() is always called with an explicit type argument */
400
+ tokens.push({ type: type || 'plain', text: buf }); buf = '';
401
+ }
393
402
  }
394
403
 
395
404
  while (i < len) {
@@ -413,7 +422,7 @@ function tokenizeHTML(code) {
413
422
  i++;
414
423
  if (i < len && code[i] === '/') { tBuf += '/'; i++; }
415
424
  // Tag name
416
- while (i < len && /[a-zA-Z0-9\-]/.test(code[i])) { tBuf += code[i]; i++; }
425
+ while (i < len && /[a-zA-Z0-9-]/.test(code[i])) { tBuf += code[i]; i++; }
417
426
  tokens.push({ type: 'tag', text: tBuf });
418
427
 
419
428
  // Attributes
@@ -614,6 +623,7 @@ function codeEditor(opts) {
614
623
 
615
624
  // Scroll sync: keep gutter aligned with code
616
625
  if (gutterEl) {
626
+ /* c8 ignore next -- codeEditor always wraps in .bw_ce; defensive fallback */
617
627
  var scrollParent = codeEl.closest('.bw_ce') || el;
618
628
  scrollParent.addEventListener('scroll', function() {
619
629
  gutterEl.style.transform = 'translateY(' + (-scrollParent.scrollTop) + 'px)';
@@ -666,6 +676,7 @@ function install(bw) {
666
676
  }
667
677
 
668
678
  // Auto-install if bw is on window (script tag usage)
679
+ /* c8 ignore next 3 -- module-level auto-install; only runs at import time in browser with script tags */
669
680
  if (typeof window !== 'undefined' && window.bw) {
670
681
  install(window.bw);
671
682
  }
@@ -1,189 +1,150 @@
1
1
  /**
2
2
  * Bitwrench Color Utilities
3
3
  *
4
- * Standalone color math helpers used by both bitwrench.js and bitwrench-styles.js.
5
- * Extracted to avoid circular dependencies. bitwrench.js re-exports these as
6
- * bw.colorParse, bw.colorRgbToHsl, etc.
4
+ * Hex-only color math for the theme derivation pipeline, plus legacy
5
+ * tagged-array color functions (colorParse, colorRgbToHsl, colorHslToRgb,
6
+ * colorInterp) for the public API.
7
7
  *
8
8
  * @module bitwrench-color-utils
9
- * @license BSD-2-Clause
9
+ * @license BSD-2-Clause
10
10
  * @copy Manu Chatterjee @deftio
11
11
  */
12
12
 
13
13
  function _xs (x) {
14
- return ('0' + x.toString(16)).slice(-2)
14
+ return ('0' + x.toString(16)).slice(-2);
15
15
  }
16
- /**
17
- * Clamp a value between min and max.
18
- * @param {number} val
19
- * @param {number} min
20
- * @param {number} max
21
- * @returns {number}
22
- */
23
- export function clip(val, min, max) {
16
+
17
+ function clip(val, min, max) {
24
18
  return Math.max(min, Math.min(max, val));
25
19
  }
26
20
 
27
- /**
28
- * Parse a CSS color string to [r, g, b, a, "rgb"].
29
- * Handles #hex, rgb(), rgba(), hsl(), hsla(), and bitwrench color arrays.
30
- * @param {string|Array} s - Color string or array
31
- * @param {number} [defAlpha=255] - Default alpha
32
- * @returns {Array} [r, g, b, a, "rgb"]
33
- */
21
+ // Parse hex string to [r, g, b].
22
+ function _ph(hex) {
23
+ var h = hex.charAt(0) === '#' ? hex.slice(1) : hex;
24
+ if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
25
+ return [parseInt(h.slice(0,2),16), parseInt(h.slice(2,4),16), parseInt(h.slice(4,6),16)];
26
+ }
27
+
28
+ // RGB [0-255] to HSL [h 0-360, s 0-100, l 0-100], float precision.
29
+ function _r2h(r, g, b) {
30
+ r /= 255; g /= 255; b /= 255;
31
+ var mx = Math.max(r, g, b), mn = Math.min(r, g, b);
32
+ var h, s, l = (mx + mn) / 2;
33
+ if (mx === mn) {
34
+ h = s = 0;
35
+ } else {
36
+ var d = mx - mn;
37
+ s = l > 0.5 ? d / (2 - mx - mn) : d / (mx + mn);
38
+ switch (mx) {
39
+ case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
40
+ case g: h = ((b - r) / d + 2) / 6; break;
41
+ case b: h = ((r - g) / d + 4) / 6; break;
42
+ }
43
+ }
44
+ return [h * 360, s * 100, l * 100];
45
+ }
46
+
47
+ // HSL [h 0-360, s 0-100, l 0-100] to RGB [0-255], rounded.
48
+ function _h2r(h, s, l) {
49
+ var hN = h / 360, sN = s / 100, lN = l / 100;
50
+ var r, g, b;
51
+ if (sN === 0) {
52
+ r = g = b = lN * 255;
53
+ } else {
54
+ var hue2rgb = function(p, q, t) {
55
+ if (t < 0) t += 1;
56
+ if (t > 1) t -= 1;
57
+ if (t < 1/6) return p + (q - p) * 6 * t;
58
+ if (t < 1/2) return q;
59
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
60
+ return p;
61
+ };
62
+ var q = lN < 0.5 ? lN * (1 + sN) : lN + sN - lN * sN;
63
+ var p = 2 * lN - q;
64
+ r = hue2rgb(p, q, hN + 1/3) * 255;
65
+ g = hue2rgb(p, q, hN) * 255;
66
+ b = hue2rgb(p, q, hN - 1/3) * 255;
67
+ }
68
+ return [Math.round(r), Math.round(g), Math.round(b)];
69
+ }
70
+
71
+ // =========================================================================
72
+ // Legacy color API — tagged 5-element arrays [r,g,b,a,"rgb"/"hsl"]
73
+ // =========================================================================
74
+
75
+ function _ms(x, i0, i1, o0, o1, opts) {
76
+ var n = (x - i0) / (i1 - i0);
77
+ if (opts && opts.expScale && opts.expScale !== 1) n = Math.pow(n, opts.expScale);
78
+ var r = n * (o1 - o0) + o0;
79
+ if (opts && opts.clip) r = clip(r, Math.min(o0, o1), Math.max(o0, o1));
80
+ return r;
81
+ }
82
+
83
+ export function colorHslToRgb(h, s, l, a, rnd) {
84
+ if (a === undefined) a = 255;
85
+ if (rnd === undefined) rnd = true;
86
+ if (Array.isArray(h)) { s = h[1]; l = h[2]; a = h[3] !== undefined ? h[3] : 255; h = h[0]; }
87
+ var rgb = _h2r(h, s, l);
88
+ if (rnd) a = Math.round(a);
89
+ return [rgb[0], rgb[1], rgb[2], a, "rgb"];
90
+ }
91
+
92
+ export function colorRgbToHsl(r, g, b, a, rnd) {
93
+ if (a === undefined) a = 255;
94
+ if (rnd === undefined) rnd = true;
95
+ if (Array.isArray(r)) { g = r[1]; b = r[2]; a = r[3] !== undefined ? r[3] : 255; r = r[0]; }
96
+ var hsl = _r2h(r, g, b);
97
+ if (rnd) { hsl[0] = Math.round(hsl[0]); hsl[1] = Math.round(hsl[1]); hsl[2] = Math.round(hsl[2]); a = Math.round(a); }
98
+ return [hsl[0], hsl[1], hsl[2], a, "hsl"];
99
+ }
100
+
34
101
  export function colorParse(s, defAlpha) {
35
102
  if (defAlpha === undefined) defAlpha = 255;
36
103
  var r = [0, 0, 0, defAlpha, "rgb"];
37
-
38
104
  if (Array.isArray(s)) {
39
105
  var df = [0, 0, 0, 255, "rgb"];
40
- for (var p = 0; p < s.length && p < df.length; p++) {
41
- df[p] = s[p];
42
- }
106
+ for (var p = 0; p < s.length && p < df.length; p++) df[p] = s[p];
43
107
  return df;
44
108
  }
45
-
46
109
  s = String(s).replace(/\s/g, "");
47
-
48
110
  if (s[0] === "#") {
49
111
  var hex = s.slice(1);
50
112
  if (hex.length === 3 || hex.length === 4) {
51
- for (var i = 0; i < hex.length; i++) {
52
- r[i] = parseInt(hex[i] + hex[i], 16);
53
- }
113
+ for (var i = 0; i < hex.length; i++) r[i] = parseInt(hex[i] + hex[i], 16);
54
114
  } else if (hex.length === 6 || hex.length === 8) {
55
- for (var j = 0; j < hex.length; j += 2) {
56
- r[j / 2] = parseInt(hex.substring(j, j + 2), 16);
57
- }
115
+ for (var j = 0; j < hex.length; j += 2) r[j / 2] = parseInt(hex.substring(j, j + 2), 16);
58
116
  }
59
117
  } else {
60
118
  var match = s.match(/^(rgb|hsl)a?\(([^)]+)\)$/i);
61
119
  if (match) {
62
120
  var type = match[1].toLowerCase();
63
121
  var values = match[2].split(",").map(function(v) { return parseFloat(v); });
64
-
65
122
  if (type === "rgb") {
66
- r[0] = values[0] || 0;
67
- r[1] = values[1] || 0;
68
- r[2] = values[2] || 0;
123
+ r[0] = values[0] || 0; r[1] = values[1] || 0; r[2] = values[2] || 0;
69
124
  r[3] = values[3] !== undefined ? values[3] * 255 : defAlpha;
70
- r[4] = "rgb";
71
125
  } else if (type === "hsl") {
72
- var rgb = colorHslToRgb(values[0] || 0, values[1] || 0, values[2] || 0,
73
- values[3] !== undefined ? values[3] * 255 : defAlpha);
74
- return rgb;
126
+ return colorHslToRgb(values[0] || 0, values[1] || 0, values[2] || 0,
127
+ values[3] !== undefined ? values[3] * 255 : defAlpha);
75
128
  }
76
129
  }
77
130
  }
78
-
79
131
  return r;
80
132
  }
81
133
 
82
- /**
83
- * Convert RGB to HSL.
84
- * @param {number|Array} r - Red 0-255, or [r,g,b,a] array
85
- * @param {number} [g] - Green 0-255
86
- * @param {number} [b] - Blue 0-255
87
- * @param {number} [a=255] - Alpha 0-255
88
- * @param {boolean} [rnd=true] - Round results
89
- * @returns {Array} [h, s, l, a, "hsl"]
90
- */
91
- export function colorRgbToHsl(r, g, b, a, rnd) {
92
- if (a === undefined) a = 255;
93
- if (rnd === undefined) rnd = true;
94
- if (Array.isArray(r)) {
95
- g = r[1]; b = r[2]; a = r[3] !== undefined ? r[3] : 255; r = r[0];
96
- }
97
-
98
- r /= 255;
99
- g /= 255;
100
- b /= 255;
101
-
102
- var max = Math.max(r, g, b);
103
- var min = Math.min(r, g, b);
104
- var h, s, l = (max + min) / 2;
105
-
106
- if (max === min) {
107
- h = s = 0;
108
- } else {
109
- var d = max - min;
110
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
111
-
112
- switch (max) {
113
- case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
114
- case g: h = ((b - r) / d + 2) / 6; break;
115
- case b: h = ((r - g) / d + 4) / 6; break;
116
- }
117
- }
118
-
119
- h *= 360;
120
- s *= 100;
121
- l *= 100;
122
-
123
- if (rnd) {
124
- h = Math.round(h);
125
- s = Math.round(s);
126
- l = Math.round(l);
127
- a = Math.round(a);
128
- }
129
-
130
- return [h, s, l, a, "hsl"];
131
- }
132
-
133
- /**
134
- * Convert HSL to RGB.
135
- * @param {number|Array} h - Hue 0-360, or [h,s,l,a] array
136
- * @param {number} [s] - Saturation 0-100
137
- * @param {number} [l] - Lightness 0-100
138
- * @param {number} [a=255] - Alpha 0-255
139
- * @param {boolean} [rnd=true] - Round results
140
- * @returns {Array} [r, g, b, a, "rgb"]
141
- */
142
- export function colorHslToRgb(h, s, l, a, rnd) {
143
- if (a === undefined) a = 255;
144
- if (rnd === undefined) rnd = true;
145
- if (Array.isArray(h)) {
146
- s = h[1]; l = h[2]; a = h[3] !== undefined ? h[3] : 255; h = h[0];
147
- }
148
-
149
- var hNorm = h / 360;
150
- var sNorm = s / 100;
151
- var lNorm = l / 100;
152
-
153
- var r, g, b;
154
-
155
- if (sNorm === 0) {
156
- r = g = b = lNorm * 255;
157
- } else {
158
- var hue2rgb = function(p, q, t) {
159
- if (t < 0) t += 1;
160
- if (t > 1) t -= 1;
161
- if (t < 1/6) return p + (q - p) * 6 * t;
162
- if (t < 1/2) return q;
163
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
164
- return p;
165
- };
166
-
167
- var q = lNorm < 0.5 ? lNorm * (1 + sNorm) : lNorm + sNorm - lNorm * sNorm;
168
- var p = 2 * lNorm - q;
169
-
170
- r = hue2rgb(p, q, hNorm + 1/3) * 255;
171
- g = hue2rgb(p, q, hNorm) * 255;
172
- b = hue2rgb(p, q, hNorm - 1/3) * 255;
173
- }
174
-
175
- if (rnd) {
176
- r = Math.round(r);
177
- g = Math.round(g);
178
- b = Math.round(b);
179
- a = Math.round(a);
180
- }
181
-
182
- return [r, g, b, a, "rgb"];
134
+ export function colorInterp(x, in0, in1, colors, stretch) {
135
+ var c = Array.isArray(colors) ? colors : ["#000", "#fff"];
136
+ c = c.length === 0 ? ["#000", "#fff"] : c;
137
+ if (c.length === 1) return c[0];
138
+ c = c.map(function(col) { return colorParse(col); });
139
+ var a = _ms(x, in0, in1, 0, c.length - 1, { clip: true, expScale: stretch });
140
+ var i = clip(Math.floor(a), 0, c.length - 2);
141
+ var r = a - i;
142
+ var interp = function(idx) { return _ms(r, 0, 1, c[i][idx], c[i + 1][idx], { clip: true }); };
143
+ return [interp(0), interp(1), interp(2), interp(3), "rgb"];
183
144
  }
184
145
 
185
146
  // =========================================================================
186
- // New theme derivation helpers
147
+ // Public theme derivation helpers
187
148
  // =========================================================================
188
149
 
189
150
  /**
@@ -192,9 +153,8 @@ export function colorHslToRgb(h, s, l, a, rnd) {
192
153
  * @returns {Array} [h, s, l] where h=0-360, s=0-100, l=0-100
193
154
  */
194
155
  export function hexToHsl(hex) {
195
- var rgb = colorParse(hex);
196
- var hsl = colorRgbToHsl(rgb[0], rgb[1], rgb[2], 255, false);
197
- return [hsl[0], hsl[1], hsl[2]];
156
+ var rgb = _ph(hex);
157
+ return _r2h(rgb[0], rgb[1], rgb[2]);
198
158
  }
199
159
 
200
160
  /**
@@ -203,7 +163,7 @@ export function hexToHsl(hex) {
203
163
  * @returns {string} Hex color e.g. '#006666'
204
164
  */
205
165
  export function hslToHex(hsl) {
206
- var rgb = colorHslToRgb(hsl[0], hsl[1], hsl[2], 255, true);
166
+ var rgb = _h2r(hsl[0], hsl[1], hsl[2]);
207
167
  return '#' + _xs(rgb[0])+_xs(rgb[1])+_xs(rgb[2]);
208
168
  }
209
169
 
@@ -228,8 +188,8 @@ export function adjustLightness(hex, amount) {
228
188
  * @returns {string} Mixed hex color
229
189
  */
230
190
  export function mixColor(hex1, hex2, ratio) {
231
- var c1 = colorParse(hex1);
232
- var c2 = colorParse(hex2);
191
+ var c1 = _ph(hex1);
192
+ var c2 = _ph(hex2);
233
193
  var r = Math.round(c1[0] + (c2[0] - c1[0]) * ratio);
234
194
  var g = Math.round(c1[1] + (c2[1] - c1[1]) * ratio);
235
195
  var b = Math.round(c1[2] + (c2[2] - c1[2]) * ratio);
@@ -242,7 +202,7 @@ export function mixColor(hex1, hex2, ratio) {
242
202
  * @returns {number} Relative luminance 0-1
243
203
  */
244
204
  export function relativeLuminance(hex) {
245
- var rgb = colorParse(hex);
205
+ var rgb = _ph(hex);
246
206
  var vals = [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255].map(function(v) {
247
207
  return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
248
208
  });
@@ -273,7 +233,6 @@ export function harmonize(sourceHex, targetHex, amount) {
273
233
  var srcHsl = hexToHsl(sourceHex);
274
234
  var tgtHsl = hexToHsl(targetHex);
275
235
 
276
- // Shortest-arc hue interpolation
277
236
  var diff = tgtHsl[0] - srcHsl[0];
278
237
  if (diff > 180) diff -= 360;
279
238
  if (diff < -180) diff += 360;
@@ -288,9 +247,7 @@ export function harmonize(sourceHex, targetHex, amount) {
288
247
  * @returns {Object} { base, hover, active, light, darkText, border, focus, textOn }
289
248
  */
290
249
  export function deriveShades(hex) {
291
- var rgb = colorParse(hex);
292
- // For light input colors (L > 75), mixing toward white produces invisible borders.
293
- // Darken instead so borders remain visible against light backgrounds.
250
+ var rgb = _ph(hex);
294
251
  var borderColor = hexToHsl(hex)[2] > 75
295
252
  ? adjustLightness(hex, -18)
296
253
  : mixColor(hex, '#ffffff', 0.60);
@@ -318,14 +275,10 @@ export function deriveAlternateSeed(hex) {
318
275
  var altL, altS;
319
276
 
320
277
  if (l > 50) {
321
- // Light color → make dark. Map 50-100 → 30-10 range
322
278
  altL = clip(100 - l - 10, 8, 40);
323
- // Reduce saturation slightly — vivid colors at low lightness look garish
324
279
  altS = clip(s * 0.85, 0, 100);
325
280
  } else {
326
- // Dark color → make light. Map 0-50 → 65-92 range
327
281
  altL = clip(100 - l + 10, 60, 92);
328
- // Slightly increase saturation for light variant
329
282
  altS = clip(s * 1.1, 0, 100);
330
283
  }
331
284
 
@@ -354,35 +307,27 @@ export function isLightPalette(config) {
354
307
  */
355
308
  export function deriveAlternateConfig(config) {
356
309
  var alt = {};
357
- // Invert the user's seed colors
358
310
  alt.primary = deriveAlternateSeed(config.primary);
359
- alt.secondary = deriveAlternateSeed(config.secondary);
311
+ alt.secondary = deriveAlternateSeed(config.secondary || config.primary);
360
312
  alt.tertiary = config.tertiary ? deriveAlternateSeed(config.tertiary) : alt.primary;
361
313
 
362
- // Derive alternate surface colors from primary hue.
363
- // Check actual page surface brightness (not seed color brightness) to decide
364
- // whether alternate should be dark or light. The page surface is what the
365
- // user sees; seeds can be dark while the page is still light (default L=96).
366
314
  var priHsl = hexToHsl(config.primary);
367
315
  var h = priHsl[0];
368
316
  var primarySurface = config.surface || hslToHex([h, 8, 96]);
369
317
  var isLight = relativeLuminance(primarySurface) > 0.179;
370
318
 
371
319
  if (isLight) {
372
- // Page surface is light → alternate needs dark surfaces
373
320
  alt.light = hslToHex([h, Math.min(priHsl[1], 15), 15]);
374
321
  alt.dark = hslToHex([h, 5, 88]);
375
322
  alt.surface = hslToHex([h, 12, 18]);
376
323
  alt.background = hslToHex([h, 10, 14]);
377
324
  } else {
378
- // Page surface is dark → alternate needs light surfaces
379
325
  alt.light = hslToHex([h, Math.min(priHsl[1], 10), 96]);
380
326
  alt.dark = hslToHex([h, 10, 18]);
381
327
  alt.surface = hslToHex([h, 8, 96]);
382
328
  alt.background = hslToHex([h, 6, 98]);
383
329
  }
384
330
 
385
- // Semantic colors: harmonize toward primary, then invert for alternate
386
331
  var amt = config.harmonize !== undefined ? config.harmonize : 0.20;
387
332
  var semanticDefaults = {
388
333
  success: '#198754', danger: '#dc3545',
@@ -396,7 +341,6 @@ export function deriveAlternateConfig(config) {
396
341
  alt[key] = deriveAlternateSeed(harmonized);
397
342
  }
398
343
 
399
- // Semantic colors are already harmonized+inverted — don't re-harmonize in derivePalette
400
344
  alt.harmonize = 0;
401
345
 
402
346
  return alt;
@@ -404,8 +348,6 @@ export function deriveAlternateConfig(config) {
404
348
 
405
349
  /**
406
350
  * Derive complete palette from a theme config object.
407
- * Semantic colors are harmonized toward the primary hue (configurable).
408
- * Light/dark surface colors are tinted with the primary hue.
409
351
  * @param {Object} config - Theme config with primary, secondary, tertiary, etc.
410
352
  * @param {number} [config.harmonize=0.20] - Hue shift amount for semantic colors (0-1)
411
353
  * @returns {Object} Full palette with shades for all 9 semantic colors
@@ -413,37 +355,31 @@ export function deriveAlternateConfig(config) {
413
355
  export function derivePalette(config) {
414
356
  var amt = config.harmonize !== undefined ? config.harmonize : 0.20;
415
357
  var pri = config.primary;
358
+ var sec = config.secondary || pri;
359
+ var ter = config.tertiary || pri;
416
360
  var priHsl = hexToHsl(pri);
417
361
  var h = priHsl[0];
418
362
 
419
- // Semantic defaults — harmonized toward primary hue
420
363
  var successBase = harmonize(config.success || '#198754', pri, amt);
421
364
  var dangerBase = harmonize(config.danger || '#dc3545', pri, amt);
422
365
  var warningBase = harmonize(config.warning || '#f0ad4e', pri, amt);
423
366
  var infoBase = harmonize(config.info || '#17a2b8', pri, amt);
424
367
 
425
- // Light/dark: derive from primary hue with low saturation (if not user-supplied)
426
368
  var lightBase = config.light || hslToHex([h, 8, 97]);
427
369
  var darkBase = config.dark || hslToHex([h, 10, 13]);
428
370
 
429
- // Background & surface tokens — tinted with primary hue for theme personality.
430
- // Saturation high enough that the hue is visible (each theme feels distinct)
431
- // but low enough to stay neutral and readable.
432
- // User can override with config.background / config.surface.
433
371
  var bgBase = config.background || hslToHex([h, 22, 96]);
434
372
  var surfBase = config.surface || hslToHex([h, 25, 94]);
435
373
 
436
- // surfaceAlt: subtle background variant for striped rows, hover states, headers.
437
- // Slightly lighter than surface in dark mode, slightly darker in light mode.
438
374
  var surfHsl = hexToHsl(surfBase);
439
375
  var surfAlt = surfHsl[2] <= 50
440
376
  ? hslToHex([surfHsl[0], surfHsl[1], Math.min(surfHsl[2] + 8, 100)])
441
377
  : hslToHex([surfHsl[0], surfHsl[1], Math.max(surfHsl[2] - 3, 0)]);
442
378
 
443
379
  var palette = {
444
- primary: deriveShades(config.primary),
445
- secondary: deriveShades(config.secondary),
446
- tertiary: deriveShades(config.tertiary),
380
+ primary: deriveShades(pri),
381
+ secondary: deriveShades(sec),
382
+ tertiary: deriveShades(ter),
447
383
  success: deriveShades(successBase),
448
384
  danger: deriveShades(dangerBase),
449
385
  warning: deriveShades(warningBase),
@@ -40,7 +40,7 @@ export function bindFileOps(bw) {
40
40
  } else {
41
41
  var blob = new Blob([data], { type: "application/octet-stream" });
42
42
  var url = window.URL.createObjectURL(blob);
43
- var a = bw.createDOM({
43
+ var a = bw.create({
44
44
  t: 'a',
45
45
  a: {
46
46
  href: url,
@@ -144,7 +144,7 @@ export function bindFileOps(bw) {
144
144
  return;
145
145
  }
146
146
 
147
- var input = bw.createDOM({
147
+ var input = bw.create({
148
148
  t: 'input',
149
149
  a: {
150
150
  type: 'file',
@@ -7,8 +7,9 @@
7
7
  *
8
8
  * Includes: HTML/DOM generation, CSS generation, color utilities,
9
9
  * state management, pub/sub, file I/O, random/lorem,
10
- * cookies, URL params, logging, makeTable, makeDataTable.
11
- * Excludes: All make* component helpers from bitwrench-bccl.js
12
- * (makeButton, makeCard, makeAlert, makeTabs, etc.)
10
+ * cookies, URL params, logging, makeTable, makeTableFromArray,
11
+ * makeDataTable, makeBarChart.
12
+ * Excludes: BCCL component helpers from bitwrench-bccl.js
13
+ * (makeButton, makeCard, makeAlert, makeTabs, makeModal, etc.)
13
14
  */
14
15
  export { default } from './bitwrench.js';
@@ -130,7 +130,8 @@ export function initRouter(bw) {
130
130
  return p + s;
131
131
  }
132
132
 
133
- function handleRoute(toRaw, opts) {
133
+ function handleRoute(toRaw, _opts) {
134
+ /* c8 ignore next -- all callers (navigate, onHashChange, onPopState) check destroyed first */
134
135
  if (destroyed) return;
135
136
  var fromPath = currentPath;
136
137
  var toPath = normalizePath(toRaw);
@@ -180,6 +181,7 @@ export function initRouter(bw) {
180
181
  window.location.hash = path;
181
182
  }
182
183
  // hashchange listener will fire handleRoute; but if same hash, trigger manually
184
+ /* c8 ignore next -- fallback for bare '#' hash; navigate always sets a path */
183
185
  var currentHash = window.location.hash.replace(/^#/, '') || '/';
184
186
  if (normalizePath(currentHash) === normalizePath(path)) {
185
187
  handleRoute(path, opts);
@@ -196,11 +198,13 @@ export function initRouter(bw) {
196
198
  }
197
199
 
198
200
  function onHashChange() {
201
+ /* c8 ignore next -- destroy() removes the hashchange listener; belt-and-suspenders guard */
199
202
  if (destroyed) return;
200
203
  handleRoute(getPath());
201
204
  }
202
205
 
203
206
  function onPopState() {
207
+ /* c8 ignore next -- destroy() removes the popstate listener; belt-and-suspenders guard */
204
208
  if (destroyed) return;
205
209
  handleRoute(getPath());
206
210
  }
@@ -259,7 +263,6 @@ export function initRouter(bw) {
259
263
  }
260
264
  if (_activeRouter) {
261
265
  // determine href based on mode -- check hash by looking at current location
262
- var isHash = window.location.hash !== undefined; // always true, but we default hash
263
266
  a.href = '#' + path;
264
267
  } else {
265
268
  a.href = path;