bitwrench 2.0.16 → 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 (68) hide show
  1. package/README.md +127 -38
  2. package/dist/bitwrench-bccl.cjs.js +13 -9
  3. package/dist/bitwrench-bccl.cjs.min.js +2 -2
  4. package/dist/bitwrench-bccl.esm.js +13 -9
  5. package/dist/bitwrench-bccl.esm.min.js +2 -2
  6. package/dist/bitwrench-bccl.umd.js +13 -9
  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 +1438 -920
  17. package/dist/bitwrench-lean.cjs.min.js +20 -20
  18. package/dist/bitwrench-lean.es5.js +1518 -1105
  19. package/dist/bitwrench-lean.es5.min.js +18 -18
  20. package/dist/bitwrench-lean.esm.js +1437 -920
  21. package/dist/bitwrench-lean.esm.min.js +20 -20
  22. package/dist/bitwrench-lean.umd.js +1438 -920
  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 +1450 -928
  33. package/dist/bitwrench.cjs.min.js +21 -21
  34. package/dist/bitwrench.css +456 -132
  35. package/dist/bitwrench.es5.js +1563 -1140
  36. package/dist/bitwrench.es5.min.js +19 -19
  37. package/dist/bitwrench.esm.js +1450 -929
  38. package/dist/bitwrench.esm.min.js +21 -21
  39. package/dist/bitwrench.min.css +1 -1
  40. package/dist/bitwrench.umd.js +1450 -928
  41. package/dist/bitwrench.umd.min.js +21 -21
  42. package/dist/builds.json +178 -90
  43. package/dist/bwserve.cjs.js +528 -68
  44. package/dist/bwserve.esm.js +527 -69
  45. package/dist/sri.json +44 -36
  46. package/package.json +5 -2
  47. package/readme.html +136 -49
  48. package/src/bitwrench-bccl.js +12 -8
  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 +979 -630
  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 +139 -29
  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/layout-default.js +47 -32
  64. package/src/cli/serve.js +6 -2
  65. package/src/generate-css.js +11 -4
  66. package/src/vendor/html2canvas.min.js +20 -0
  67. package/src/version.js +3 -3
  68. package/src/bwserve/shell.js +0 -103
package/dist/sri.json CHANGED
@@ -1,41 +1,49 @@
1
1
  {
2
- "version": "2.0.16",
2
+ "version": "2.0.18",
3
3
  "algorithm": "sha384",
4
- "generated": "2026-03-12",
4
+ "generated": "2026-03-17",
5
5
  "files": {
6
- "bitwrench-bccl.cjs.js": "sha384-GwR2880Z60SZfdNaioTeRhUBXGyYoVU3MLylTyIPeUFxvodT/3Iet6V/WxyBMD/s",
7
- "bitwrench-bccl.cjs.min.js": "sha384-Qwl5UPvUPdEYdMTcGKqPBJBsp8QhidEz8fk8yKfbS/3cqtMl5NaXbMJA2BoiwfKh",
8
- "bitwrench-bccl.esm.js": "sha384-kHXaRUebkB7AJJjvfRyo55LwAljwnGo9QvTxdSAqbEqOYj+3X4O6IpNdKWBJHLcF",
9
- "bitwrench-bccl.esm.min.js": "sha384-hcQjn5nawieZdF4y8RPH77VCeEFlIGNP+ZU6U/fYnxWfNEyO6d5ss2MRyQ5XFKyx",
10
- "bitwrench-bccl.umd.js": "sha384-ICOeQchsjmDw5JKGRiTpzOZQFTeHdws4wxVtA9DAo+UVG7OnndU2yUcA9l+FHwH6",
11
- "bitwrench-bccl.umd.min.js": "sha384-+v9FNivy9NRfWFRXRtkP6ZiEO9BMhazy+wzQtiQt1t2rE5CZW1vuOMNcJvBpzJHc",
12
- "bitwrench-code-edit.cjs.js": "sha384-khga6vBpxusqbLfrzDgaUuyCoXgpWq74miYSMk7qhkiQP3AfXuAEoZpQnf0DttiS",
13
- "bitwrench-code-edit.cjs.min.js": "sha384-hYMmfOiiw6x31dBGA2XuVxam6Z9hJlZrp+q3iti0kfSq3/BEDchNLF0y6VR7R4kX",
14
- "bitwrench-code-edit.es5.js": "sha384-Ck1ishImBFkM4fRKK7oi9zJky7DDeOmXgkK0bSbWgogCMeW7DRpkm47hQRLVVRJw",
15
- "bitwrench-code-edit.es5.min.js": "sha384-u9ykQEvMQIafDdWpMU3ecIrmx56v4MUCCOMkuyrYgGqR9vwTZZ/+1e/RbsOlNRc5",
16
- "bitwrench-code-edit.esm.js": "sha384-E324MY+95O5lfS9pNSJo4INbAH19cW757B9nDcWAOtuna8TKMpYGV4ISB0fwOPDe",
17
- "bitwrench-code-edit.esm.min.js": "sha384-KHv/eyr8QgXCTX0aPVH8K6Yy89AJ/0Kw7furwi5GleEDRWXDIaM2TzAcvJC7BAAA",
18
- "bitwrench-code-edit.umd.js": "sha384-xXdbDll04ajtBsQX0axgamUFGN6QFMaJ2mRF+NltLk8W6DswOTcuTO3+HeUVowVr",
19
- "bitwrench-code-edit.umd.min.js": "sha384-rVetnnzXOSu1IUM3rQMdMUy4aPpbzDXZJ6edtYhaf14Wsc3qjsfV/oaE1D3/ic0J",
20
- "bitwrench-lean.cjs.js": "sha384-6G/GY13bZxw2fJnRYFTKMe5ID44pG00mI5fscEtcGOWsyXU66Z9x96tJ9gW2YFoJ",
21
- "bitwrench-lean.cjs.min.js": "sha384-jPRTwWe3KtSyw0Jeihuds2mt78akR8sJLrg6KsGpapYOQu4jBEDluhlBxeYTV924",
22
- "bitwrench-lean.es5.js": "sha384-HALcGiNdWAwTXHOSWBIM5Z8HNLqEiZxKkpshXV8aQYQEmi7ExZk5wVUSFnszt4Gx",
23
- "bitwrench-lean.es5.min.js": "sha384-SesnIRrNdnucw77WgRDRqGYBoUOxLNuSjAvp9PncwRNLOHvE8xqSwq8O7lUCoHB6",
24
- "bitwrench-lean.esm.js": "sha384-nuPtftCDpQZkupIb1qenEZgKMZSzvvKtwRomD8zIK4hJTsGYnvxA7z/ZhcmnjvAz",
25
- "bitwrench-lean.esm.min.js": "sha384-96C4eWKwPY/T4/ZsVpoIldnThALDBRiE6RZ31U/gwyVVfoH2MZNAxzhH3IMPpqbf",
26
- "bitwrench-lean.umd.js": "sha384-HRRlhaZtoFpQ+OJDjgTwsSs1pOJYcounIAVWNG8JB6OigF0dd/WAYV/kYpdfz1Hj",
27
- "bitwrench-lean.umd.min.js": "sha384-F7r80Hn3REIH3VWG5YUtY+KujQE0cpGeQq833CJCfcaE5/c0dWoNvsNS1OGf2XE0",
28
- "bitwrench.cjs.js": "sha384-Ew7bPhGDSA1Byb6Taso9W66aZo8H+/85RjHd1aH3uVfNz09qnPclOxbLAnHBhksT",
29
- "bitwrench.cjs.min.js": "sha384-BdutYGxq7G9Tn/9J06jDSSCc1iK6aKEJ1KxjRbknZk8Et1gNmF0MomzELHmZfKQC",
30
- "bitwrench.css": "sha384-TLf4tWABJynZ7cb9epG9VhRM1IvuK5k4VD5wHE2esbGXcuKzG6iArqimHPzZDLD2",
31
- "bitwrench.es5.js": "sha384-gVwhjeTvWn5LhP1OqLAtS8sE/L8prV0zVV9KNJf+rKX3tJYdDAVNxLq1bvuXgggd",
32
- "bitwrench.es5.min.js": "sha384-T7NCr/fxfLL7stlgkfrwZGSIYVXIF21e9M8K5QcWdElNFCBIGWlT59usFIDLgyMm",
33
- "bitwrench.esm.js": "sha384-9a7cFRy55OaBzng4m6ixCUiSYWb+FN+7RcG2NyboJVQF0j6SnR0WpYR3ur8Hh8PH",
34
- "bitwrench.esm.min.js": "sha384-XO2NYqeiOAvrmCZnFSPuC8yctfeZ6bfcPQhWMSbiDRwMDqqBwXyOpsVhUxjpflCo",
35
- "bitwrench.min.css": "sha384-Sl7+BxWfINNnNdN+ZCL1s/p4Hn+5WeJPciWS9svNuzal8h9baX19xGeOJNzradRY",
36
- "bitwrench.umd.js": "sha384-SBGSd5W8l78ZzK8XUU5FeHLE2fBB+WDgHJPtt9LkKBGkvP0TPVmfTwxcsLYA4MFO",
37
- "bitwrench.umd.min.js": "sha384-jW9jzpLC07lcZ0KD7uXE8hi9J75zyCaogD5Goam0Dvmm4aofdJhy6iCVXe8Aj0jY",
38
- "bwserve.cjs.js": "sha384-0mM2vNKrqS6RjlakI/4hmnnNte8/tjhiCiXZ+h+6+/IbhuyEWp1+CGXXnX97ZRZO",
39
- "bwserve.esm.js": "sha384-dr2iHz8bI12AK7TMIzLc48Kvb4sIQ90w34isDil4EKtcasOeYg3Y5PjTGF3SWKqe"
6
+ "bitwrench-bccl.cjs.js": "sha384-97lxNxsJ943yAFgdsGuThcexgw1H/LYsMZvctuArTBq8Z1Dtfijx98V2F4d0KYSg",
7
+ "bitwrench-bccl.cjs.min.js": "sha384-MYtFH6IOfYgEBHyWrKHK4ijXc3wys3nypVIBNYV0xXCDdfQu7NaI2BVAwUhtMw1A",
8
+ "bitwrench-bccl.esm.js": "sha384-/oduwWIsuFsHgkWg8G5I8X6ci203OOCSMVVkOLE8SHyfTNSXnhodXSyPx5KJfnK1",
9
+ "bitwrench-bccl.esm.min.js": "sha384-um8u9y9aMreWN9Qf/xbL+4NPG+BjyRqffHzIqeYWYsPkJUYkgbR2MdAR6pNYBTKz",
10
+ "bitwrench-bccl.umd.js": "sha384-T8Y7WK/bfR0qsEthhonw17Uvc4GbpGCZyWGNvvqo7fZkrf2r994zlRJGkLJOikZZ",
11
+ "bitwrench-bccl.umd.min.js": "sha384-mylsm8Q+K7ZGMoxz6J3/9PrArvea4e0KJDSEMMIBhUDzXZNotzRbV8J8n/xLXbtG",
12
+ "bitwrench-code-edit.cjs.js": "sha384-eD7kA6fzkUuwgH84uOuPZb9zEvHSNJaYnxPmiSmpKzKxbmsuAwb5jtHNdPKzjhGj",
13
+ "bitwrench-code-edit.cjs.min.js": "sha384-vMircu/dAfERhhyaAHxXcjS7k2ZV43Uc+rfVLjdK1BQGfhsYaXqKy1ONwZ5eJ42A",
14
+ "bitwrench-code-edit.es5.js": "sha384-Uh6/1C2kSnmMVdCiMOlFDK0wDIz/FQ89StJXwQS5U+8BLWpeYitxJRVbXh28JrF0",
15
+ "bitwrench-code-edit.es5.min.js": "sha384-3xLz9CiAm/OTx1EVLV9GPVA5t5nAXAzwXV7ItxwlOcOUHjxvuEhBxvaz2MHU584Z",
16
+ "bitwrench-code-edit.esm.js": "sha384-zvcF0qY6rcpYt5K9o6gORkZpCA9XtiuAMocz4oYT34U0nZhp/65rBC9F4GbrEbQ8",
17
+ "bitwrench-code-edit.esm.min.js": "sha384-GBaXjBmcl0Faro4yGOhDzZekie5Ao5G5bQU7Rl9dRvSg1vtnBHSH0zz6IJBkRypl",
18
+ "bitwrench-code-edit.umd.js": "sha384-SApqDWYJATq6BDr1sj13rGXpr8+d590S+bG03Iuhcbm/6DB++Hxp3sDLVSanfuqC",
19
+ "bitwrench-code-edit.umd.min.js": "sha384-1HZO7M7901ex8J9B902cAI0stT1YbI34fpLoyNlv9+DY4B4to6AF5LmVPoA5hfYO",
20
+ "bitwrench-lean.cjs.js": "sha384-dAkymK30IGH1anOiADEvxFaJvgS37nrHyLXMx+4D4R/jn7xBunASkDbFSeGW2acu",
21
+ "bitwrench-lean.cjs.min.js": "sha384-gp6mLzVlYQHysMzgrw8uOcD03/5/Zn3Gqq0kzT1vFH6m7rlDzdUpF+XLos3ZFlj+",
22
+ "bitwrench-lean.es5.js": "sha384-8bzRIuravBPlP/XQGZT5opCdcdxwxg+ia44NDyxbc693+aPKAk9u2koER0f6hPjF",
23
+ "bitwrench-lean.es5.min.js": "sha384-nh0CvCajmgmEJhYoy9QfGzYGUiTHgyrMVZHWY0oDCW4p8AONoZ1HLHe2OrtPI4HR",
24
+ "bitwrench-lean.esm.js": "sha384-O9MYspI5NPZdHd0UBF0brnXE3AvMXAJwL0yKhZ39MLma0nv+aNPx0+Yxo24Z8OVm",
25
+ "bitwrench-lean.esm.min.js": "sha384-AToyDo6g4hVQzn9S7RtmRJ9rL7KvvpZq2Xs22w74wn2UUPUGHqjk0vPFk0vyUsdJ",
26
+ "bitwrench-lean.umd.js": "sha384-ItPAXRKLQJTE5OG2j4mtteKmF3lJEWc+VNTefDaSkZ4aUXzRgikySBDLJjewEHYg",
27
+ "bitwrench-lean.umd.min.js": "sha384-59DBhjfFHQjrXOMnGR5ZYmHRrhst7HhvMlmfn9GSuHVGV9l6z6VZM6+tgmiDQ7vF",
28
+ "bitwrench-util-css.cjs.js": "sha384-AqKsW+lDWKJ5CTpJmZO3Za04fZKmvOswf5BrepWMN1UHmIRBjakBrIbIeh6KqZ7J",
29
+ "bitwrench-util-css.cjs.min.js": "sha384-PVEORrb3H7E5BcHzWN9yaJKOhTFGUu/PK36fQZVk5XODcIVJ7KCKz6gcTKITLy9f",
30
+ "bitwrench-util-css.es5.js": "sha384-roMO7JpMD8MVfREIC0XQI6SSJC9BuOK8zZOPfdEUGmCfWMzbb+tY2+E/msYovErm",
31
+ "bitwrench-util-css.es5.min.js": "sha384-eCIKVMpbra/Xph9G7yHdFKs9DSu52iFbp0C1J+BeX9AcNUhPa8Zv98zroA6GscVb",
32
+ "bitwrench-util-css.esm.js": "sha384-1JTj1P2bDsNkeFbGsn1ztDtHt2sMC32O5G21qclsgnsBbHBo2EnrI/wTxALHM5OG",
33
+ "bitwrench-util-css.esm.min.js": "sha384-hdFDGI+SW5cIR8i06D8fhPss6grt3ne7D1oPfTGGfyNsdpfgjCgEULfUaAPT9lXw",
34
+ "bitwrench-util-css.umd.js": "sha384-1IXVY7rzxrqiEgqRaVLbX2cZqb92h0NmxdW1qGf9WrEL7hMTspiKcnTcDDGSchD6",
35
+ "bitwrench-util-css.umd.min.js": "sha384-F1bwBKL0ftJe0J0H6LlgPgQej54ODG+dD90cWaIaGrC2yWrvnAc9n/xfw3IfVbkq",
36
+ "bitwrench.cjs.js": "sha384-8Py3ZQbesTdr/Nk6Sb+v27LRJJKT8/kyJyB0xAAmRFbEdvL+bNc+Io4F/y1DLX1F",
37
+ "bitwrench.cjs.min.js": "sha384-crCKn7uRyo/06To1Ms5K2buSHLAvixSsx1ZWGjI8jEyH7XDjFoZLtQryVVQx+vXF",
38
+ "bitwrench.css": "sha384-SxT17YrsGzI+MZpPXHaPdBeki24pSK1nqyNzshNh6EWU4zXRFP4g+21LC1Dlh+YU",
39
+ "bitwrench.es5.js": "sha384-+rZTBmXYipKqS/eCHYwSVQxFCh5YKVjnHQeTzkbtkQ2JzQcHVbRZPcPK5LMau6U2",
40
+ "bitwrench.es5.min.js": "sha384-/N7p37IA8HxOEGXhOF2Y4aEGllv8IQvrMbLAQnnZ7znalyiJvqTSIZXxA951+UcY",
41
+ "bitwrench.esm.js": "sha384-iGz+hH8CSL92LiiHVKSOzkjVojvzYM2BpXcLleUj3eUKyx58Qdh/Eiz9mYVtYmao",
42
+ "bitwrench.esm.min.js": "sha384-bN1Mj511R+uL+z3ZmPMuhPn+vL9iEEaUWYT/iNFkh219BuJuLOsCJpx2ZdIQGM2R",
43
+ "bitwrench.min.css": "sha384-D5Yl1DxoYlVwewe1CTZ2GCeof3BOD8D38YuuMiTLWb1WXS6fsP05wwKDzMGxrkRh",
44
+ "bitwrench.umd.js": "sha384-oi3tmqmiKCCk2j8do9t/A3tjhD14kzImnxAXpH6pBcbigEO3m7ToxrgT9/IECb5Z",
45
+ "bitwrench.umd.min.js": "sha384-koZisx/3J1+mZz9Y8SQ0uCuU21B6bSF1BHkjYAXdTMGFSBocQzqnHm1LoV7PJkWh",
46
+ "bwserve.cjs.js": "sha384-klAeGiVLGQ+v8a/8DOGuD6c46GaWNIhIYAKZkQpRipj57COKzsBxagMDVif0IaJj",
47
+ "bwserve.esm.js": "sha384-C1MUV7+IMPbdewQWvcXLMMZQpdp2X5PaO5oDb7FdgeOLS2aPExD+aGoMQrlfaOiZ"
40
48
  }
41
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitwrench",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "description": "A library for javascript UI functions.",
5
5
  "main": "./dist/bitwrench.umd.js",
6
6
  "repository": {
@@ -95,8 +95,9 @@
95
95
  "build_1_x": "./tools/update-bw-package.js package.json package.json && ./tools/export-bw-default-css.js bitwrench.css && uglifyjs bitwrench.js -o bitwrench.min.js && uglifyjs bitwrench_ESM.js -o bitwrench_ESM.min.js && ./tools/umd2ModuleHack.js && npm pack",
96
96
  "cleanbuild": "npm run clean && npm run build && npm run build:generated",
97
97
  "oldtest": "./node_modules/mocha/bin/mocha test/bitwrench_test.js --reporter spec",
98
- "test": "c8 --reporter=text mocha ./test/bitwrench_ci.js ./test/bitwrench_test_coverage.js ./test/bitwrench_test_pubsub.js ./test/bitwrench_test_theme.js ./test/bitwrench_test_nodemap.js ./test/bitwrench_test_components.js ./test/bitwrench_test_component_handle.js ./test/bitwrench_test_coverage_gaps.js ./test/bitwrench_test_bwserve.js -r jsdom-global/register",
98
+ "test": "c8 --reporter=text mocha ./test/bitwrench_ci.js ./test/bitwrench_test_coverage.js ./test/bitwrench_test_pubsub.js ./test/bitwrench_test_theme.js ./test/bitwrench_test_nodemap.js ./test/bitwrench_test_components.js ./test/bitwrench_test_component_handle.js ./test/bitwrench_test_coverage_gaps.js ./test/bitwrench_test_bwserve.js ./test/bitwrench_test_attach.js ./test/bitwrench_test_code_edit.js ./test/bitwrench_test_html_page.js ./test/bitwrench_test_util_css.js -r jsdom-global/register",
99
99
  "test:bwserve": "mocha ./test/bitwrench_test_bwserve.js -r jsdom-global/register",
100
+ "test:attach": "mocha ./test/bitwrench_test_attach.js -r jsdom-global/register",
100
101
  "test:components": "mocha ./test/bitwrench_test_components.js -r jsdom-global/register",
101
102
  "test:v1": "c8 --reporter=text mocha ./test/bitwrench_test.js -r jsdom-global/",
102
103
  "test:pending": "mocha ./test/bitwrench_test_pending.js -r jsdom-global/",
@@ -111,6 +112,8 @@
111
112
  "generate-sri": "node tools/generate-sri.js",
112
113
  "test:nodemap": "mocha ./test/bitwrench_test_nodemap.js -r jsdom-global/register",
113
114
  "test:cli": "mocha ./test/bitwrench_test_cli.js",
115
+ "test:code-edit": "mocha ./test/bitwrench_test_code_edit.js -r jsdom-global/register",
116
+ "test:html-page": "mocha ./test/bitwrench_test_html_page.js -r jsdom-global/register",
114
117
  "test:e2e": "playwright test",
115
118
  "test:e2e:headed": "playwright test --headed",
116
119
  "test:e2e:debug": "playwright test --debug",
package/readme.html CHANGED
@@ -3,12 +3,11 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <meta name="generator" content="bitwrench v2.0.16">
6
+ <meta name="generator" content="bitwrench v2.0.18">
7
7
  <title>bitwrench.js - README</title>
8
8
  <link rel="icon" type="image/x-icon" href="images/favicon.ico">
9
- <script src="dist/bitwrench.umd.js"></script>
10
- <link rel="stylesheet" href="dist/bitwrench.css">
11
- <link rel="stylesheet" href="pages/shared-theme.css">
9
+ <script src="dist/bitwrench.umd.min.js"></script>
10
+ <script src="pages/shared-theme.js"></script>
12
11
  <script src="pages/shared-nav.js"></script>
13
12
  <style>
14
13
  .quikdown-h1 { font-size:2em;font-weight:600;margin:.67em 0;text-align:left;color:#333 }
@@ -72,34 +71,84 @@
72
71
  <h1 class="quikdown-h1">bitwrench.js</h1>
73
72
  <a class="quikdown-a" href="https://opensource.org/licenses/BSD-2-Clause" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/badge/License-BSD%202--Clause-blue.svg" alt="License"></a>
74
73
  <a class="quikdown-a" href="https://www.npmjs.com/package/bitwrench" rel="noopener noreferrer"><img class="quikdown-img" src="https://img.shields.io/npm/v/bitwrench.svg?style=flat-square" alt="NPM version"></a>
75
- <a class="quikdown-a" href="https://github.com/deftio/bitwrench/actions/workflows/ci.yml" rel="noopener noreferrer"><img class="quikdown-img" src="https://github.com/deftio/bitwrench/actions/workflows/ci.yml/badge.svg" alt="CI"></a></p><p><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/" rel="noopener noreferrer"><img class="quikdown-img" src="./images/bitwrench-logo-med.png" alt="bitwrench"></a></p><p>Bitwrench is a UI library in a single script that provides HTML generation, reactive state, CSS and theme generation, 30+ components, and a static site CLI all from plain JavaScript objects, with zero dependencies and zero compile steps. Works in browsers (including IE11) and Node.js.</p><h2 class="quikdown-h2">Quick Example</h2>
76
- <p><pre class="quikdown-pre"><code class="language-javascript">const card = {
77
- t: &#39;div&#39;, a: { class: &#39;bw-card&#39; },
74
+ <a class="quikdown-a" href="https://github.com/deftio/bitwrench/actions/workflows/ci.yml" rel="noopener noreferrer"><img class="quikdown-img" src="https://github.com/deftio/bitwrench/actions/workflows/ci.yml/badge.svg" alt="CI"></a></p><p><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/" rel="noopener noreferrer"><img class="quikdown-img" src="./images/bitwrench-logo-med.png" alt="bitwrench"></a></p><p>Bitwrench builds UI from plain JavaScript objects one format for components, styling, state, and server rendering, with no build step and zero dependencies. In bitwrench structure, styles, and state (including client/server) are rendered and managed as javascript objects.</p><pre class="quikdown-pre"><code class="language-javascript">// Describe UI as a JavaScript object (a &quot;TACO&quot;)
75
+ var page = {
76
+ t: &#39;div&#39;, a: { class: &#39;card&#39; },
78
77
  c: [
79
- { t: &#39;h3&#39;, c: &#39;Hello bitwrench&#39; },
80
- { t: &#39;p&#39;, c: &#39;UI as native JavaScript objects.&#39; }
78
+ { t: &#39;h2&#39;, c: &#39;Hello&#39; },
79
+ { t: &#39;p&#39;, c: &#39;UI as native JavaScript objects.&#39; },
80
+ bw.makeButton({ text: &#39;Click me&#39;, variant: &#39;primary&#39;, onclick: fn })
81
81
  ]
82
82
  };
83
83
 
84
- // Mount to the DOM
85
- bw.DOM(&#39;#app&#39;, card);
86
-
87
- // Or render to an HTML string (Node.js, emails, static pages)
88
- const html = bw.html(card);</code></pre></p><p>Each object has four keys: <strong class="quikdown-strong">t</strong> (tag), <strong class="quikdown-strong">a</strong> (attributes), <strong class="quikdown-strong">c</strong> (content), and optionally <strong class="quikdown-strong">o</strong> (options for state and lifecycle). Nest them, loop them, compose them — it&#39;s just JavaScript.</p><h2 class="quikdown-h2">Installation</h2>
84
+ bw.DOM(&#39;#app&#39;, page); // live DOM
85
+ bw.html(page); // → HTML string (Node.js, emails, SSR)</code></pre><p>Each object has four keys: <strong class="quikdown-strong">t</strong> (tag), <strong class="quikdown-strong">a</strong> (attributes), <strong class="quikdown-strong">c</strong> (content), <strong class="quikdown-strong">o</strong> (options for state/lifecycle). Nest them, loop them, compose them — it&#39;s just JavaScript.</p><p><img class="quikdown-img" src="./images/hero-example.png" alt="Hero example output"></p><h3 class="quikdown-h3">Why bitwrench?</h3>
86
+ <strong class="quikdown-strong">One file, everywhere.</strong> At ~39KB 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.</p><p>Structure, styling, state, and server rendering are all handled as JavaScript objects:</p><ul class="quikdown-ul">
87
+ <li class="quikdown-li"><strong class="quikdown-strong">No build toolchain</strong> works with a <code class="quikdown-code">&lt;script&gt;</code> tag</li>
88
+ <li class="quikdown-li"><strong class="quikdown-strong">50+ ready-made components</strong> — buttons, tables, modals, forms, charts, toasts — one <code class="quikdown-code">make*()</code> call each, returns a composable TACO</li>
89
+ <li class="quikdown-li"><strong class="quikdown-strong">CSS from JavaScript</strong> — <code class="quikdown-code">bw.css()</code> generates stylesheets, <code class="quikdown-code">bw.s()</code> composes inline styles, <code class="quikdown-code">bw.loadStyles()</code> or <code class="quikdown-code">bw.makeStyles()</code> derives a complete theme from 2 seed colors</li>
90
+ <li class="quikdown-li"><strong class="quikdown-strong">Reactive state</strong> — <code class="quikdown-code">bw.component()</code> provides <code class="quikdown-code">.get()/.set()</code> with <code class="quikdown-code">${template}</code> bindings; <code class="quikdown-code">bw.pub()</code>/<code class="quikdown-code">bw.sub()</code> for cross-component messaging</li>
91
+ <li class="quikdown-li"><strong class="quikdown-strong">Dual rendering</strong> — same object renders to live DOM (<code class="quikdown-code">bw.DOM()</code>) or HTML string (<code class="quikdown-code">bw.html()</code>) for SSR, emails, or static sites</li>
92
+ <li class="quikdown-li"><strong class="quikdown-strong">Server-driven UI</strong> — push UI updates from any backend (Python, C, Rust, Go) over SSE; <code class="quikdown-code">client.screenshot()</code> captures the page back as PNG/JPEG</li>
93
+ <li class="quikdown-li"><strong class="quikdown-strong">CLI</strong> — <code class="quikdown-code">bwcli</code> converts Markdown, HTML, and JSON to styled standalone pages</li>
94
+ <li class="quikdown-li"><strong class="quikdown-strong">Utilities</strong> — color interpolation, random data, lorem ipsum, cookies, URL params, file I/O</li>
95
+ </ul><h3 class="quikdown-h3">Coming from other Frameworks</h3>
96
+ <p>Bitwrench uses JavaScript equivalents for most forms of front-end development. Here is a quick mapping (see the <a class="quikdown-a" href="docs/README.md">docs</a> and <a class="quikdown-a" href="docs/thinking-in-bitwrench.md">Thinking in Bitwrench</a> for more details).</p><table class="quikdown-table">
97
+ <thead class="quikdown-thead">
98
+ <tr class="quikdown-tr">
99
+ <th class="quikdown-th">You&#39;re using</th>
100
+ <th class="quikdown-th">For</th>
101
+ <th class="quikdown-th">Bitwrench equivalent</th>
102
+ </tr>
103
+ </thead>
104
+ <tbody class="quikdown-tbody">
105
+ <tr class="quikdown-tr">
106
+ <td class="quikdown-td">React / Vue / Svelte</td>
107
+ <td class="quikdown-td">Components + reactivity</td>
108
+ <td class="quikdown-td"><code class="quikdown-code">{t, a, c, o}</code> objects + <code class="quikdown-code">bw.component()</code></td>
109
+ </tr>
110
+ <tr class="quikdown-tr">
111
+ <td class="quikdown-td">JSX / templates</td>
112
+ <td class="quikdown-td">Markup-in-JS</td>
113
+ <td class="quikdown-td">Native JS objects — no compiler</td>
114
+ </tr>
115
+ <tr class="quikdown-tr">
116
+ <td class="quikdown-td">Tailwind / CSS-in-JS</td>
117
+ <td class="quikdown-td">Styling</td>
118
+ <td class="quikdown-td"><code class="quikdown-code">bw.css()</code>, <code class="quikdown-code">bw.s()</code> style composition</td>
119
+ </tr>
120
+ <tr class="quikdown-tr">
121
+ <td class="quikdown-td">Sass / PostCSS</td>
122
+ <td class="quikdown-td">CSS generation</td>
123
+ <td class="quikdown-td"><code class="quikdown-code">bw.css()</code> from JS objects (supports @media, @keyframes)</td>
124
+ </tr>
125
+ <tr class="quikdown-tr">
126
+ <td class="quikdown-td">ThemeProvider / CSS vars</td>
127
+ <td class="quikdown-td">Theming</td>
128
+ <td class="quikdown-td"><code class="quikdown-code">bw.loadStyles()</code> / <code class="quikdown-code">bw.makeStyles()</code> from 2 seed colors</td>
129
+ </tr>
130
+ <tr class="quikdown-tr">
131
+ <td class="quikdown-td">Streamlit / Gradio</td>
132
+ <td class="quikdown-td">Server-driven UI</td>
133
+ <td class="quikdown-td">bwserve SSE — from any language (Python, Go, C, Rust)</td>
134
+ </tr>
135
+ <tr class="quikdown-tr">
136
+ <td class="quikdown-td">Redux / Zustand / Pinia</td>
137
+ <td class="quikdown-td">State management</td>
138
+ <td class="quikdown-td"><code class="quikdown-code">bw.component()</code> <code class="quikdown-code">.get()/.set()</code> + <code class="quikdown-code">bw.pub()/sub()</code></td>
139
+ </tr>
140
+ <tr class="quikdown-tr">
141
+ <td class="quikdown-td">Vite / webpack / Babel</td>
142
+ <td class="quikdown-td">Build tooling</td>
143
+ <td class="quikdown-td">Not needed — open the HTML file</td>
144
+ </tr>
145
+ </tbody>
146
+ </table><p>See the <a class="quikdown-a" href="docs/framework-translation-table.md">Framework Translation Table</a> for side-by-side code comparisons across 22 operations.</p><h2 class="quikdown-h2">Installation</h2>
89
147
  <p><pre class="quikdown-pre"><code class="language-bash">npm install bitwrench</code></pre></p><pre class="quikdown-pre"><code class="language-javascript">// ES module
90
148
  import bw from &#39;bitwrench&#39;;
91
149
 
92
150
  // CommonJS
93
- const bw = require(&#39;bitwrench&#39;);</code></pre><p>Or include directly in a page:</p><pre class="quikdown-pre"><code class="language-html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bitwrench/dist/bitwrench.umd.min.js&quot;&gt;&lt;/script&gt;</code></pre><h2 class="quikdown-h2">Features</h2>
94
- <ul class="quikdown-ul">
95
- <li class="quikdown-li"><strong class="quikdown-strong">HTML from plain objects</strong> — describe UI as JavaScript objects, render to live DOM with <code class="quikdown-code">bw.DOM()</code> or to HTML strings with <code class="quikdown-code">bw.html()</code> for server-side rendering, emails, and static pages</li>
96
- <li class="quikdown-li"><strong class="quikdown-strong">Built-in reactivity</strong> — <code class="quikdown-code">bw.update()</code> re-renders components when state changes, <code class="quikdown-code">bw.patch()</code> updates individual elements by ID, <code class="quikdown-code">bw.pub()</code>/<code class="quikdown-code">bw.sub()</code> provides decoupled messaging between any part of the application</li>
97
- <li class="quikdown-li"><strong class="quikdown-strong">CSS and theme generation</strong> — <code class="quikdown-code">bw.css()</code> generates stylesheets from objects, <code class="quikdown-code">bw.generateTheme()</code> derives a complete visual theme (buttons, alerts, badges, cards, forms, tables, dark mode) from 2-3 seed colors</li>
98
- <li class="quikdown-li"><strong class="quikdown-strong">45+ ready-made components</strong> — cards, buttons, sortable tables, form inputs, modals, dropdowns, accordions, tooltips, popovers, toasts, timelines, steppers, file uploads, stat cards — each a single function call that returns a composable object</li>
99
- <li class="quikdown-li"><strong class="quikdown-strong">Server-driven UI (bwserve)</strong> — push TACO rendering commands from Node.js to the browser over SSE; same protocol works from C (ESP32), Python, Rust, or any language via the <code class="quikdown-code">bwcli serve</code> pipe server</li>
100
- <li class="quikdown-li"><strong class="quikdown-strong">Static site CLI</strong> — the <code class="quikdown-code">bwcli</code> command converts Markdown, HTML, and JSON files into styled, self-contained pages with theme support</li>
101
- <li class="quikdown-li"><strong class="quikdown-strong">Utilities</strong> — color interpolation, random data generation, lorem ipsum, cookies, URL params, file I/O for both browser and Node.js</li>
102
- </ul><h2 class="quikdown-h2">Getting Started</h2>
151
+ const bw = require(&#39;bitwrench&#39;);</code></pre><p>Or include directly in a page:</p><pre class="quikdown-pre"><code class="language-html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bitwrench/dist/bitwrench.umd.min.js&quot;&gt;&lt;/script&gt;</code></pre><h2 class="quikdown-h2">Getting Started</h2>
103
152
  <p><pre class="quikdown-pre"><code class="language-html">&lt;!DOCTYPE html&gt;
104
153
  &lt;html lang=&quot;en&quot;&gt;
105
154
  &lt;head&gt;
@@ -108,7 +157,7 @@ const bw = require(&#39;bitwrench&#39;);</code></pre><p>Or include directly in a
108
157
  &lt;body&gt;
109
158
  &lt;div id=&quot;app&quot;&gt;&lt;/div&gt;
110
159
  &lt;script&gt;
111
- bw.loadDefaultStyles();
160
+ bw.loadStyles();
112
161
 
113
162
  bw.DOM(&#39;#app&#39;, {
114
163
  t: &#39;div&#39;, a: { class: &#39;bw-container&#39; },
@@ -118,7 +167,11 @@ const bw = require(&#39;bitwrench&#39;);</code></pre><p>Or include directly in a
118
167
  title: &#39;Welcome&#39;,
119
168
  content: &#39;Built with plain JavaScript objects.&#39;
120
169
  }),
121
- bw.makeButton({ text: &#39;Click me&#39;, variant: &#39;primary&#39; })
170
+ bw.makeButton({
171
+ text: &#39;Click me&#39;,
172
+ variant: &#39;primary&#39;,
173
+ onclick: function() { alert(&#39;Hello!&#39;); }
174
+ })
122
175
  ]
123
176
  });
124
177
  &lt;/script&gt;
@@ -141,17 +194,30 @@ const bw = require(&#39;bitwrench&#39;);</code></pre><p>Or include directly in a
141
194
 
142
195
  bw.DOM(&#39;#app&#39;, counter);
143
196
  counter.set(&#39;count&#39;, 42); // DOM updates automatically
144
- counter.reset(); // methods from o.methods are callable on the handle</code></pre><p>For low-level control, you can also use <code class="quikdown-code">o.render</code> + <code class="quikdown-code">bw.update()</code> directly — see the <a class="quikdown-a" href="docs/state-management.md">State Management guide</a>.</p><p>For communication between components, use pub/sub:</p><pre class="quikdown-pre"><code class="language-javascript">bw.sub(&#39;item-added&#39;, function(detail) {
197
+ counter.reset(); // methods from o.methods are callable on the handle</code></pre><blockquote class="quikdown-blockquote"><strong class="quikdown-strong">Important: event handlers go in <code class="quikdown-code">a: { onclick: fn }</code>, not in <code class="quikdown-code">o.mounted</code>.</strong> Handlers attached via <code class="quikdown-code">addEventListener</code> in <code class="quikdown-code">o.mounted</code> are silently lost when a component re-renders. Always use <code class="quikdown-code">onclick</code>/<code class="quikdown-code">onchange</code>/etc. inside <code class="quikdown-code">a:</code> — bitwrench re-attaches them on every render automatically.</blockquote><p>For low-level control, you can also use <code class="quikdown-code">o.render</code> + <code class="quikdown-code">bw.update()</code> directly — see the <a class="quikdown-a" href="docs/state-management.md">State Management guide</a>.</p><p>For communication between components, use pub/sub:</p><pre class="quikdown-pre"><code class="language-javascript">bw.sub(&#39;item-added&#39;, function(detail) {
145
198
  console.log(&#39;New item:&#39;, detail.name);
146
199
  });
147
200
 
148
- bw.pub(&#39;item-added&#39;, { name: &#39;Widget&#39; });</code></pre><h2 class="quikdown-h2">Theming</h2>
149
- <p>Generate a complete theme from two seed colors. All components buttons, alerts, badges, cards, forms, tables — are styled automatically:</p><pre class="quikdown-pre"><code class="language-javascript">bw.generateTheme(&#39;my-theme&#39;, {
201
+ bw.pub(&#39;item-added&#39;, { name: &#39;Widget&#39; });</code></pre><h2 class="quikdown-h2">CSS from JavaScript</h2>
202
+ <p><code class="quikdown-code">bw.css()</code> generates CSS from objects. <code class="quikdown-code">bw.s()</code> composes inline styles from reusable utility objects:</p><pre class="quikdown-pre"><code class="language-javascript">// Generate and inject a stylesheet
203
+ bw.injectCSS(bw.css({
204
+ &#39;.my-card&#39;: { padding: &#39;1rem&#39;, borderRadius: &#39;8px&#39; }
205
+ }));
206
+
207
+ // Compose inline styles from objects
208
+ { t: &#39;div&#39;, a: { style: bw.s({ display: &#39;flex&#39; }, { gap: &#39;1rem&#39; }, { padding: &#39;1rem&#39; }) } }
209
+
210
+ // Responsive breakpoints
211
+ bw.responsive(&#39;.hero&#39;, {
212
+ base: { fontSize: &#39;1.5rem&#39; },
213
+ md: { fontSize: &#39;2.5rem&#39; }
214
+ });</code></pre><h2 class="quikdown-h2">Theming</h2>
215
+ <p><code class="quikdown-code">bw.loadStyles()</code> derives a design system — buttons, alerts, badges, cards, forms, tables, hover states, focus rings — from two seed colors. Themes are scoped to DOM subtrees, so different sections of a page can use different themes. <code class="quikdown-code">toggleStyles()</code> switches between primary and alternate palettes:</p><pre class="quikdown-pre"><code class="language-javascript">bw.loadStyles({
150
216
  primary: &#39;#336699&#39;,
151
217
  secondary: &#39;#cc6633&#39;
152
218
  });
153
219
 
154
- bw.toggleTheme(); // switch between primary and alternate palettes</code></pre><h2 class="quikdown-h2">Core API</h2>
220
+ bw.toggleStyles(); // switch between primary and alternate palettes</code></pre><h2 class="quikdown-h2">Core API</h2>
155
221
  <table class="quikdown-table">
156
222
  <thead class="quikdown-thead">
157
223
  <tr class="quikdown-tr">
@@ -169,6 +235,10 @@ bw.toggleTheme(); // switch between primary and alternate palettes</code></pre>
169
235
  <td class="quikdown-td">Mount an object to a DOM element</td>
170
236
  </tr>
171
237
  <tr class="quikdown-tr">
238
+ <td class="quikdown-td"><code class="quikdown-code">bw.raw(str)</code></td>
239
+ <td class="quikdown-td">Mark a string as pre-escaped HTML (no double-escaping)</td>
240
+ </tr>
241
+ <tr class="quikdown-tr">
172
242
  <td class="quikdown-td"><code class="quikdown-code">bw.component(taco)</code></td>
173
243
  <td class="quikdown-td">Wrap a TACO in a ComponentHandle with <code class="quikdown-code">.get()/.set()</code> reactive API</td>
174
244
  </tr>
@@ -177,12 +247,24 @@ bw.toggleTheme(); // switch between primary and alternate palettes</code></pre>
177
247
  <td class="quikdown-td">Generate CSS from a JS object</td>
178
248
  </tr>
179
249
  <tr class="quikdown-tr">
180
- <td class="quikdown-td"><code class="quikdown-code">bw.loadDefaultStyles()</code></td>
181
- <td class="quikdown-td">Inject the built-in stylesheet</td>
250
+ <td class="quikdown-td"><code class="quikdown-code">bw.s(...objs)</code></td>
251
+ <td class="quikdown-td">Compose inline style objects into a style string</td>
252
+ </tr>
253
+ <tr class="quikdown-tr">
254
+ <td class="quikdown-td"><code class="quikdown-code">bw.responsive(sel, breakpoints)</code></td>
255
+ <td class="quikdown-td">Generate <code class="quikdown-code">@media</code> CSS rules from JS</td>
256
+ </tr>
257
+ <tr class="quikdown-tr">
258
+ <td class="quikdown-td"><code class="quikdown-code">bw.loadStyles()</code></td>
259
+ <td class="quikdown-td">Inject the built-in stylesheet (or pass config to theme)</td>
260
+ </tr>
261
+ <tr class="quikdown-tr">
262
+ <td class="quikdown-td"><code class="quikdown-code">bw.makeStyles(config)</code></td>
263
+ <td class="quikdown-td">Generate a scoped theme from seed colors (returns styles object)</td>
182
264
  </tr>
183
265
  <tr class="quikdown-tr">
184
- <td class="quikdown-td"><code class="quikdown-code">bw.generateTheme(name, config)</code></td>
185
- <td class="quikdown-td">Generate a scoped theme from seed colors</td>
266
+ <td class="quikdown-td"><code class="quikdown-code">bw.toggleStyles()</code></td>
267
+ <td class="quikdown-td">Switch between primary and alternate palettes</td>
186
268
  </tr>
187
269
  <tr class="quikdown-tr">
188
270
  <td class="quikdown-td"><code class="quikdown-code">bw.patch(id, content)</code></td>
@@ -209,17 +291,9 @@ bw.toggleTheme(); // switch between primary and alternate palettes</code></pre>
209
291
  <td class="quikdown-td">Debug a component in the browser console</td>
210
292
  </tr>
211
293
  <tr class="quikdown-tr">
212
- <td class="quikdown-td"><code class="quikdown-code">bw.clientConnect(url, opts)</code></td>
213
- <td class="quikdown-td">Connect to a bwserve SSE endpoint</td>
214
- </tr>
215
- <tr class="quikdown-tr">
216
- <td class="quikdown-td"><code class="quikdown-code">bw.clientApply(msg)</code></td>
294
+ <td class="quikdown-td"><code class="quikdown-code">bw.apply(msg)</code></td>
217
295
  <td class="quikdown-td">Apply a bwserve protocol message to the DOM</td>
218
296
  </tr>
219
- <tr class="quikdown-tr">
220
- <td class="quikdown-td"><code class="quikdown-code">bw.clientParse(str)</code></td>
221
- <td class="quikdown-td">Parse strict or r-prefix relaxed JSON</td>
222
- </tr>
223
297
  </tbody>
224
298
  </table><p>See the full <a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/08-api-reference.html" rel="noopener noreferrer">API Reference</a> for all functions.</p><h2 class="quikdown-h2">CLI</h2>
225
299
  <p>Convert Markdown, HTML, or JSON files to styled standalone pages:</p><pre class="quikdown-pre"><code class="language-bash"># Convert Markdown to a self-contained HTML page
@@ -263,31 +337,44 @@ curl -X POST http://localhost:9000 -d &#39;{&quot;type&quot;:&quot;patch&quot;,&
263
337
  </tr>
264
338
  </tbody>
265
339
  </table><p>All formats include source maps. A separate CSS file (<code class="quikdown-code">bitwrench.css</code>) is also available for use without JavaScript.</p><h2 class="quikdown-h2">Documentation</h2>
266
- <strong class="quikdown-strong">Guides</strong> (in <code class="quikdown-code">docs/</code>):</p><ul class="quikdown-ul">
340
+ <strong class="quikdown-strong">Start here:</strong></p><ul class="quikdown-ul">
341
+ <li class="quikdown-li"><strong class="quikdown-strong"><a class="quikdown-a" href="docs/thinking-in-bitwrench.md">Thinking in Bitwrench</a></strong> — the complete guide. Covers TACO, styling (<code class="quikdown-code">bw.css</code>, <code class="quikdown-code">bw.s</code>, <code class="quikdown-code">bw.responsive</code>), composition, events, the three-level component model, bwserve, and common patterns</li>
342
+ <li class="quikdown-li"><strong class="quikdown-strong"><a class="quikdown-a" href="docs/llm-bitwrench-guide.md">LLM Guide</a></strong> — compact single-file reference with all APIs, patterns, and rules. Designed for AI-assisted development but works as a cheat sheet for anyone</li>
343
+ </ul><p><strong class="quikdown-strong">Reference guides</strong> (in <code class="quikdown-code">docs/</code>):</p><ul class="quikdown-ul">
267
344
  <li class="quikdown-li"><a class="quikdown-a" href="docs/taco-format.md">TACO Format</a> — the <code class="quikdown-code">{t, a, c, o}</code> object format</li>
268
345
  <li class="quikdown-li"><a class="quikdown-a" href="docs/state-management.md">State Management</a> — three-level component model, ComponentHandle, reactive state</li>
269
346
  <li class="quikdown-li"><a class="quikdown-a" href="docs/component-library.md">Component Library</a> — all 50+ <code class="quikdown-code">make*()</code> functions with signatures and examples</li>
270
347
  <li class="quikdown-li"><a class="quikdown-a" href="docs/theming.md">Theming</a> — palette-driven theme generation, presets, design tokens</li>
271
348
  <li class="quikdown-li"><a class="quikdown-a" href="docs/cli.md">CLI</a> — the <code class="quikdown-code">bwcli</code> command for file conversion and pipe server</li>
272
349
  <li class="quikdown-li"><a class="quikdown-a" href="docs/bwserve.md">bwserve</a> — server-driven UI protocol (SSE, actions, embedded devices)</li>
273
- <li class="quikdown-li"><a class="quikdown-a" href="docs/llm-bitwrench-guide.md">LLM Guide</a> — compact single-file reference for AI-assisted development</li>
274
350
  </ul><p><strong class="quikdown-strong">Tutorials:</strong></p><ul class="quikdown-ul">
275
351
  <li class="quikdown-li"><a class="quikdown-a" href="docs/tutorial-website.md">Build a Website</a> — multi-section landing page from TACO objects</li>
276
352
  <li class="quikdown-li"><a class="quikdown-a" href="docs/tutorial-bwserve.md">bwserve Dashboard</a> — Streamlit-style server-push dashboard</li>
277
353
  <li class="quikdown-li"><a class="quikdown-a" href="docs/tutorial-embedded.md">ESP32 IoT Dashboard</a> — embedded sensor dashboard with C macros</li>
278
354
  </ul><p><strong class="quikdown-strong">Interactive demos</strong> (live site):</p><ul class="quikdown-ul">
279
355
  <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/00-quick-start.html" rel="noopener noreferrer">Quick Start</a> — first steps with <code class="quikdown-code">bw.DOM()</code></li>
280
- <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/01-components.html" rel="noopener noreferrer">Components</a> — buttons, cards, alerts, badges, navbars</li>
281
- <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/03-styling.html" rel="noopener noreferrer">Styling &amp; Theming</a> — CSS generation and theming strategies</li>
356
+ <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/01-components.html" rel="noopener noreferrer">Components</a> — all 50+ UI components with live demos</li>
357
+ <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/03-styling.html" rel="noopener noreferrer">Styling &amp; Theming</a> — CSS generation, <code class="quikdown-code">bw.s()</code>, and theming strategies</li>
282
358
  <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/05-state.html" rel="noopener noreferrer">State &amp; Interactivity</a> — state patterns and ComponentHandle</li>
283
359
  <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/06-tic-tac-toe-tutorial.html" rel="noopener noreferrer">Tic Tac Toe Tutorial</a> — step-by-step game with state management</li>
284
360
  <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/07-framework-comparison.html" rel="noopener noreferrer">Framework Comparison</a> — bitwrench vs React, Vue, Svelte</li>
285
361
  <li class="quikdown-li"><a class="quikdown-a" href="https://deftio.github.io/bitwrench/pages/10-themes.html" rel="noopener noreferrer">Themes</a> — interactive theme generator with presets and CSS export</li>
286
- </ul><h2 class="quikdown-h2">Development</h2>
362
+ </ul><p><strong class="quikdown-strong">Example apps</strong> (in <code class="quikdown-code">examples/</code>):</p><ul class="quikdown-ul">
363
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/ember-and-oak/">Ember &amp; Oak Coffee Co.</a> — full landing page: theme, cart, search, charts, accordion, timeline</li>
364
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/landing-page/">SunForge Landing Page</a> — polished marketing page with zero reactive state, pure BCCL composition</li>
365
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/todo-app/">Todo App</a> — bw.component() with pub/sub</li>
366
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/dashboard/">Metrics Dashboard</a> — live stat cards, bar chart, pub/sub, responsive layout</li>
367
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/wizard/">Signup Wizard</a> — multi-step form, state transitions, bw.raw()</li>
368
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/live-feed/">Live Feed</a> — real-time stream, bw.patch(), slide-in animation</li>
369
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/embedded/">IoT Dashboard</a> — ESP32-style sensor dashboard with SSE</li>
370
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/client-server/">bwserve Counter</a> — server-driven UI demo</li>
371
+ <li class="quikdown-li"><a class="quikdown-a" href="examples/llm-chat/">LLM Chat</a> — streaming chat via bwserve + Ollama/OpenAI</li>
372
+ </ul><h2 class="quikdown-h2">FAQ</h2>
373
+ <strong class="quikdown-strong">Is this a framework?</strong> — No. Bitwrench is a library (~39KB gzipped). No lifecycle to learn, no project structure to follow. Import it, call functions, done.</p><p><strong class="quikdown-strong">Does it scale to large apps?</strong> — Bitwrench targets single-page tools, dashboards, prototypes, embedded UIs, and content sites — apps where a single HTML file or a handful of files is the right form factor. For a 500-route SPA with team-scale state management, React or Vue is a better fit.</p><p><strong class="quikdown-strong">How does bitwrench compare to React/Vue?</strong> — 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 — use whichever fits the job.</p><p><strong class="quikdown-strong">How does CSS work?</strong> — Bitwrench doesn&#39;t own your CSS. Use any external stylesheet, Tailwind, or CSS file you want — bitwrench doesn&#39;t interfere. On top of that, <code class="quikdown-code">bw.css()</code> generates CSS from JS objects (with <code class="quikdown-code">@media</code>, <code class="quikdown-code">@keyframes</code>, pseudo-classes), <code class="quikdown-code">bw.s()</code> composes inline style objects, and <code class="quikdown-code">bw.loadStyles()</code> or <code class="quikdown-code">bw.makeStyles()</code> derives a complete theme from 2 seed colors. You can use all three together or none at all.</p><p><strong class="quikdown-strong">What&#39;s the difference between <code class="quikdown-code">bw.DOM()</code> and <code class="quikdown-code">bw.html()</code>?</strong> — Same TACO input, two outputs. <code class="quikdown-code">bw.DOM(&#39;#app&#39;, taco)</code> mounts live DOM elements in a browser. <code class="quikdown-code">bw.html(taco)</code> returns an HTML string — use it in Node.js scripts, email generators, static site builds, or anywhere you need markup without a browser. One object format, two rendering modes.</p><p><strong class="quikdown-strong">What is bwserve?</strong> — bwserve lets any server push UI updates to a browser over SSE. The server sends TACO objects as JSON; the browser renders them. It&#39;s language-agnostic — the server can be Python, Go, Rust, C, or a shell script. Anything that can write JSON to an HTTP response can drive a bitwrench UI. See the <a class="quikdown-a" href="docs/bwserve.md">bwserve docs</a>.</p><p><strong class="quikdown-strong">Can I use bitwrench on embedded devices?</strong> — Yes — this is a primary use case. An ESP32 or Raspberry Pi serves one HTML page with bitwrench loaded, then pushes sensor data as JSON patches over SSE. The device never generates HTML. See the <a class="quikdown-a" href="docs/tutorial-embedded.md">ESP32 tutorial</a>.</p><p><strong class="quikdown-strong">Can I use it with TypeScript?</strong> — Yes. Type declarations are included. TACO objects are plain JSON-compatible objects that TypeScript infers naturally.</p><p><strong class="quikdown-strong">What about accessibility?</strong> — BCCL components emit semantic HTML with ARIA attributes where applicable. You can add any <code class="quikdown-code">aria-*</code> attribute via <code class="quikdown-code">a: { &#39;aria-label&#39;: &#39;...&#39; }</code>.</p><h2 class="quikdown-h2">Development</h2>
287
374
  <p><pre class="quikdown-pre"><code class="language-bash">npm install # install dev dependencies
288
375
  npm run build # build all dist formats (UMD, ESM, CJS, ES5)
289
376
  npm test # run unit tests (1000+ tests)
290
- npm run test:cli # run CLI tests (49 tests)
377
+ npm run test:cli # run CLI tests
291
378
  npm run test:e2e # run Playwright browser tests
292
379
  npm run lint # run ESLint
293
380
  npm run cleanbuild # full production build with SRI hashes</code></pre></p><h2 class="quikdown-h2">License</h2>
@@ -327,7 +327,7 @@ export function makeCol(props = {}) {
327
327
  if (breakpoint === 'xs') {
328
328
  classes.push(`bw_col_${value}`);
329
329
  } else {
330
- classes.push(`bw_col_${breakpoint}-${value}`);
330
+ classes.push(`bw_col_${breakpoint}_${value}`);
331
331
  }
332
332
  });
333
333
  } else if (size) {
@@ -1710,8 +1710,8 @@ export function makePagination(props = {}) {
1710
1710
  t: 'li',
1711
1711
  a: { class: `bw_page_item ${currentPage <= 1 ? 'bw_disabled' : ''}`.trim() },
1712
1712
  c: {
1713
- t: 'a',
1714
- a: { class: 'bw_page_link', href: '#', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous' },
1713
+ t: 'button',
1714
+ a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous', disabled: currentPage <= 1 ? true : undefined },
1715
1715
  c: '\u2039'
1716
1716
  }
1717
1717
  });
@@ -1723,8 +1723,8 @@ export function makePagination(props = {}) {
1723
1723
  t: 'li',
1724
1724
  a: { class: `bw_page_item ${pageNum === currentPage ? 'bw_active' : ''}`.trim() },
1725
1725
  c: {
1726
- t: 'a',
1727
- a: { class: 'bw_page_link', href: '#', onclick: handleClick(pageNum) },
1726
+ t: 'button',
1727
+ a: { class: 'bw_page_link', type: 'button', onclick: handleClick(pageNum), 'aria-current': pageNum === currentPage ? 'page' : undefined },
1728
1728
  c: '' + pageNum
1729
1729
  }
1730
1730
  });
@@ -1736,8 +1736,8 @@ export function makePagination(props = {}) {
1736
1736
  t: 'li',
1737
1737
  a: { class: `bw_page_item ${currentPage >= pages ? 'bw_disabled' : ''}`.trim() },
1738
1738
  c: {
1739
- t: 'a',
1740
- a: { class: 'bw_page_link', href: '#', onclick: handleClick(currentPage + 1), 'aria-label': 'Next' },
1739
+ t: 'button',
1740
+ a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage + 1), 'aria-label': 'Next', disabled: currentPage >= pages ? true : undefined },
1741
1741
  c: '\u203A'
1742
1742
  }
1743
1743
  });
@@ -3610,5 +3610,9 @@ export var BCCL = {
3610
3610
  export function make(type, props) {
3611
3611
  var def = BCCL[type];
3612
3612
  if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
3613
- return def.make(props || {});
3613
+ var taco = def.make(props || {});
3614
+ if (taco && typeof taco === 'object') {
3615
+ taco._bwFactory = { type: type, props: props || {} };
3616
+ }
3617
+ return taco;
3614
3618
  }
@@ -289,13 +289,18 @@ export function harmonize(sourceHex, targetHex, amount) {
289
289
  */
290
290
  export function deriveShades(hex) {
291
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.
294
+ var borderColor = hexToHsl(hex)[2] > 75
295
+ ? adjustLightness(hex, -18)
296
+ : mixColor(hex, '#ffffff', 0.60);
292
297
  return {
293
298
  base: hex,
294
299
  hover: adjustLightness(hex, -10),
295
300
  active: adjustLightness(hex, -15),
296
301
  light: mixColor(hex, '#ffffff', 0.85),
297
302
  darkText: adjustLightness(hex, -40),
298
- border: mixColor(hex, '#ffffff', 0.60),
303
+ border: borderColor,
299
304
  focus: 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',0.25)',
300
305
  textOn: textOnColor(hex)
301
306
  };
@@ -354,19 +359,27 @@ export function deriveAlternateConfig(config) {
354
359
  alt.secondary = deriveAlternateSeed(config.secondary);
355
360
  alt.tertiary = config.tertiary ? deriveAlternateSeed(config.tertiary) : alt.primary;
356
361
 
357
- // Derive alternate surface colors from primary hue
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).
358
366
  var priHsl = hexToHsl(config.primary);
359
367
  var h = priHsl[0];
360
- var isLight = isLightPalette(config);
368
+ var primarySurface = config.surface || hslToHex([h, 8, 96]);
369
+ var isLight = relativeLuminance(primarySurface) > 0.179;
361
370
 
362
371
  if (isLight) {
363
- // Primary is light → alternate needs dark surfaces
372
+ // Page surface is light → alternate needs dark surfaces
364
373
  alt.light = hslToHex([h, Math.min(priHsl[1], 15), 15]);
365
374
  alt.dark = hslToHex([h, 5, 88]);
375
+ alt.surface = hslToHex([h, 12, 18]);
376
+ alt.background = hslToHex([h, 10, 14]);
366
377
  } else {
367
- // Primary is dark → alternate needs light surfaces
378
+ // Page surface is dark → alternate needs light surfaces
368
379
  alt.light = hslToHex([h, Math.min(priHsl[1], 10), 96]);
369
380
  alt.dark = hslToHex([h, 10, 18]);
381
+ alt.surface = hslToHex([h, 8, 96]);
382
+ alt.background = hslToHex([h, 6, 98]);
370
383
  }
371
384
 
372
385
  // Semantic colors: harmonize toward primary, then invert for alternate
@@ -414,10 +427,18 @@ export function derivePalette(config) {
414
427
  var darkBase = config.dark || hslToHex([h, 10, 13]);
415
428
 
416
429
  // Background & surface tokens — tinted with primary hue for theme personality.
417
- // Very subtle: bg at L=98/S=6, surface at L=96/S=8.
430
+ // Saturation high enough that the hue is visible (each theme feels distinct)
431
+ // but low enough to stay neutral and readable.
418
432
  // User can override with config.background / config.surface.
419
- var bgBase = config.background || hslToHex([h, 6, 98]);
420
- var surfBase = config.surface || hslToHex([h, 8, 96]);
433
+ var bgBase = config.background || hslToHex([h, 22, 96]);
434
+ var surfBase = config.surface || hslToHex([h, 25, 94]);
435
+
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
+ var surfHsl = hexToHsl(surfBase);
439
+ var surfAlt = surfHsl[2] <= 50
440
+ ? hslToHex([surfHsl[0], surfHsl[1], Math.min(surfHsl[2] + 8, 100)])
441
+ : hslToHex([surfHsl[0], surfHsl[1], Math.max(surfHsl[2] - 3, 0)]);
421
442
 
422
443
  var palette = {
423
444
  primary: deriveShades(config.primary),
@@ -430,7 +451,8 @@ export function derivePalette(config) {
430
451
  light: deriveShades(lightBase),
431
452
  dark: deriveShades(darkBase),
432
453
  background: bgBase,
433
- surface: surfBase
454
+ surface: surfBase,
455
+ surfaceAlt: surfAlt
434
456
  };
435
457
 
436
458
  return palette;
@@ -0,0 +1,11 @@
1
+ // ESM entry point — adds named exports for tree-shaking.
2
+ // Modern bundlers (Vite, webpack, esbuild) can import individual
3
+ // BCCL components without pulling in the entire library:
4
+ //
5
+ // import bw from 'bitwrench'; // full library
6
+ // import { makeCard, makeButton } from 'bitwrench'; // tree-shakeable
7
+ //
8
+ // UMD/CJS builds use bitwrench.js directly (no named exports).
9
+
10
+ export { default } from './bitwrench.js';
11
+ export * from './bitwrench-bccl.js';