bitwrench 2.0.18 → 2.0.20

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 (58) hide show
  1. package/README.md +86 -81
  2. package/dist/bitwrench-bccl.cjs.js +221 -48
  3. package/dist/bitwrench-bccl.cjs.min.js +3 -3
  4. package/dist/bitwrench-bccl.esm.js +221 -48
  5. package/dist/bitwrench-bccl.esm.min.js +3 -3
  6. package/dist/bitwrench-bccl.umd.js +221 -48
  7. package/dist/bitwrench-bccl.umd.min.js +3 -3
  8. package/dist/bitwrench-code-edit.cjs.js +7 -9
  9. package/dist/bitwrench-code-edit.cjs.min.js +5 -7
  10. package/dist/bitwrench-code-edit.es5.js +6 -8
  11. package/dist/bitwrench-code-edit.es5.min.js +5 -7
  12. package/dist/bitwrench-code-edit.esm.js +7 -9
  13. package/dist/bitwrench-code-edit.esm.min.js +5 -7
  14. package/dist/bitwrench-code-edit.umd.js +7 -9
  15. package/dist/bitwrench-code-edit.umd.min.js +5 -7
  16. package/dist/bitwrench-debug.js +268 -0
  17. package/dist/bitwrench-debug.min.js +3 -0
  18. package/dist/bitwrench-lean.cjs.js +250 -1574
  19. package/dist/bitwrench-lean.cjs.min.js +6 -6
  20. package/dist/bitwrench-lean.es5.js +344 -1661
  21. package/dist/bitwrench-lean.es5.min.js +4 -4
  22. package/dist/bitwrench-lean.esm.js +250 -1574
  23. package/dist/bitwrench-lean.esm.min.js +6 -6
  24. package/dist/bitwrench-lean.umd.js +250 -1574
  25. package/dist/bitwrench-lean.umd.min.js +6 -6
  26. package/dist/bitwrench-util-css.cjs.js +1 -1
  27. package/dist/bitwrench-util-css.cjs.min.js +1 -1
  28. package/dist/bitwrench-util-css.es5.js +1 -1
  29. package/dist/bitwrench-util-css.es5.min.js +1 -1
  30. package/dist/bitwrench-util-css.esm.js +1 -1
  31. package/dist/bitwrench-util-css.esm.min.js +1 -1
  32. package/dist/bitwrench-util-css.umd.js +1 -1
  33. package/dist/bitwrench-util-css.umd.min.js +1 -1
  34. package/dist/bitwrench.cjs.js +510 -1660
  35. package/dist/bitwrench.cjs.min.js +7 -7
  36. package/dist/bitwrench.css +80 -33
  37. package/dist/bitwrench.es5.js +569 -1694
  38. package/dist/bitwrench.es5.min.js +5 -5
  39. package/dist/bitwrench.esm.js +510 -1660
  40. package/dist/bitwrench.esm.min.js +7 -7
  41. package/dist/bitwrench.min.css +1 -1
  42. package/dist/bitwrench.umd.js +510 -1660
  43. package/dist/bitwrench.umd.min.js +7 -7
  44. package/dist/builds.json +133 -111
  45. package/dist/bwserve.cjs.js +2 -2
  46. package/dist/bwserve.esm.js +2 -2
  47. package/dist/sri.json +46 -44
  48. package/package.json +5 -3
  49. package/readme.html +86 -75
  50. package/src/bitwrench-bccl-entry.js +3 -4
  51. package/src/bitwrench-bccl.js +217 -43
  52. package/src/bitwrench-code-edit.js +6 -8
  53. package/src/bitwrench-debug.js +245 -0
  54. package/src/bitwrench-styles.js +35 -8
  55. package/src/bitwrench.js +212 -1563
  56. package/src/cli/attach.js +53 -21
  57. package/src/cli/serve.js +179 -3
  58. package/src/version.js +3 -3
@@ -1,4 +1,4 @@
1
- /*! bitwrench v2.0.18 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
1
+ /*! bitwrench v2.0.20 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -190,14 +190,14 @@
190
190
  */
191
191
 
192
192
  var VERSION_INFO = {
193
- version: '2.0.18',
193
+ version: '2.0.20',
194
194
  name: 'bitwrench',
195
195
  description: 'A library for javascript UI functions.',
196
196
  license: 'BSD-2-Clause',
197
197
  homepage: 'https://deftio.github.com/bitwrench/pages',
198
198
  repository: 'git+https://github.com/deftio/bitwrench.git',
199
199
  author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
200
- buildDate: '2026-03-17T00:50:09.505Z'
200
+ buildDate: '2026-03-23T05:19:31.951Z'
201
201
  };
202
202
 
203
203
  /**
@@ -3088,7 +3088,9 @@
3088
3088
  '.bw_modal_header': {
3089
3089
  'display': 'flex',
3090
3090
  'align-items': 'center',
3091
- 'justify-content': 'space-between'
3091
+ 'justify-content': 'space-between',
3092
+ 'padding': '1rem 1.25rem',
3093
+ 'border-bottom': '1px solid transparent'
3092
3094
  },
3093
3095
  '.bw_modal_title': {
3094
3096
  'margin': '0',
@@ -3098,14 +3100,17 @@
3098
3100
  },
3099
3101
  '.bw_modal_body': {
3100
3102
  'position': 'relative',
3101
- 'flex': '1 1 auto'
3103
+ 'flex': '1 1 auto',
3104
+ 'padding': '1rem 1.25rem'
3102
3105
  },
3103
3106
  '.bw_modal_footer': {
3104
3107
  'display': 'flex',
3105
3108
  'flex-wrap': 'wrap',
3106
3109
  'align-items': 'center',
3107
3110
  'justify-content': 'flex-end',
3108
- 'gap': '0.5rem'
3111
+ 'gap': '0.5rem',
3112
+ 'padding': '0.75rem 1.25rem',
3113
+ 'border-top': '1px solid transparent'
3109
3114
  }
3110
3115
  },
3111
3116
  // ---- Toast ----
@@ -3163,9 +3168,12 @@
3163
3168
  'display': 'flex',
3164
3169
  'align-items': 'center',
3165
3170
  'justify-content': 'space-between',
3166
- 'font-size': '0.875rem'
3171
+ 'padding': '0.5rem 0.75rem',
3172
+ 'font-size': '0.875rem',
3173
+ 'border-bottom': '1px solid transparent'
3167
3174
  },
3168
3175
  '.bw_toast_body': {
3176
+ 'padding': '0.5rem 0.75rem',
3169
3177
  'font-size': '0.9375rem'
3170
3178
  }
3171
3179
  },
@@ -3195,6 +3203,7 @@
3195
3203
  'padding': '0.5rem 0',
3196
3204
  'margin': '0.125rem 0 0',
3197
3205
  'background-clip': 'padding-box',
3206
+ 'border': '1px solid transparent',
3198
3207
  'opacity': '0',
3199
3208
  'visibility': 'hidden',
3200
3209
  'pointer-events': 'none'
@@ -3211,6 +3220,7 @@
3211
3220
  '.bw_dropdown_item': {
3212
3221
  'display': 'block',
3213
3222
  'width': '100%',
3223
+ 'padding': '0.4rem 1rem',
3214
3224
  'clear': 'both',
3215
3225
  'font-weight': '400',
3216
3226
  'text-align': 'inherit',
@@ -3218,7 +3228,8 @@
3218
3228
  'white-space': 'nowrap',
3219
3229
  'background-color': 'transparent',
3220
3230
  'border': '0',
3221
- 'font-size': '0.9375rem'
3231
+ 'font-size': '0.9375rem',
3232
+ 'cursor': 'pointer'
3222
3233
  },
3223
3234
  '.bw_dropdown_item:focus-visible': {
3224
3235
  'outline': '2px solid currentColor',
@@ -4104,6 +4115,67 @@
4104
4115
  rules['.bw_text_center'] = {
4105
4116
  'text-align': 'center'
4106
4117
  };
4118
+ rules['.bw_text_justify'] = {
4119
+ 'text-align': 'justify'
4120
+ };
4121
+
4122
+ // Font weight
4123
+ rules['.bw_fw_bold'] = {
4124
+ 'font-weight': '700'
4125
+ };
4126
+ rules['.bw_fw_semibold'] = {
4127
+ 'font-weight': '600'
4128
+ };
4129
+ rules['.bw_fw_normal'] = {
4130
+ 'font-weight': '400'
4131
+ };
4132
+ rules['.bw_fw_light'] = {
4133
+ 'font-weight': '300'
4134
+ };
4135
+
4136
+ // Font style
4137
+ rules['.bw_fst_italic'] = {
4138
+ 'font-style': 'italic'
4139
+ };
4140
+ rules['.bw_fst_normal'] = {
4141
+ 'font-style': 'normal'
4142
+ };
4143
+
4144
+ // Text decoration
4145
+ rules['.bw_text_underline'] = {
4146
+ 'text-decoration': 'underline'
4147
+ };
4148
+ rules['.bw_text_line_through'] = {
4149
+ 'text-decoration': 'line-through'
4150
+ };
4151
+ rules['.bw_text_decoration_none'] = {
4152
+ 'text-decoration': 'none'
4153
+ };
4154
+
4155
+ // Text transform
4156
+ rules['.bw_text_uppercase'] = {
4157
+ 'text-transform': 'uppercase'
4158
+ };
4159
+ rules['.bw_text_lowercase'] = {
4160
+ 'text-transform': 'lowercase'
4161
+ };
4162
+ rules['.bw_text_capitalize'] = {
4163
+ 'text-transform': 'capitalize'
4164
+ };
4165
+
4166
+ // Font size
4167
+ rules['.bw_fs_sm'] = {
4168
+ 'font-size': '0.875rem'
4169
+ };
4170
+ rules['.bw_fs_base'] = {
4171
+ 'font-size': '1rem'
4172
+ };
4173
+ rules['.bw_fs_lg'] = {
4174
+ 'font-size': '1.25rem'
4175
+ };
4176
+ rules['.bw_fs_xl'] = {
4177
+ 'font-size': '1.5rem'
4178
+ };
4107
4179
 
4108
4180
  // Flexbox
4109
4181
  var jc = {
@@ -5420,7 +5492,12 @@
5420
5492
  },
5421
5493
  o: {
5422
5494
  type: 'card',
5423
- state: props.state || {}
5495
+ state: props.state || {},
5496
+ slots: {
5497
+ title: '.bw_card_title',
5498
+ content: '.bw_card_body',
5499
+ footer: '.bw_card_footer'
5500
+ }
5424
5501
  }
5425
5502
  };
5426
5503
  }
@@ -5433,7 +5510,12 @@
5433
5510
  c: cardContent,
5434
5511
  o: {
5435
5512
  type: 'card',
5436
- state: props.state || {}
5513
+ state: props.state || {},
5514
+ slots: {
5515
+ title: '.bw_card_title',
5516
+ content: '.bw_card_body',
5517
+ footer: '.bw_card_footer'
5518
+ }
5437
5519
  }
5438
5520
  };
5439
5521
  }
@@ -5790,6 +5872,26 @@
5790
5872
  actualActiveIndex = index;
5791
5873
  }
5792
5874
  });
5875
+
5876
+ // Shared tab switching logic
5877
+ function switchTab(el, index) {
5878
+ var allTabs = el.querySelectorAll('.bw_nav_link');
5879
+ var allPanes = el.querySelectorAll('.bw_tab_pane');
5880
+ if (index < 0 || index >= allTabs.length) return;
5881
+ allTabs.forEach(function (t) {
5882
+ t.classList.remove('active');
5883
+ t.setAttribute('aria-selected', 'false');
5884
+ t.setAttribute('tabindex', '-1');
5885
+ });
5886
+ allPanes.forEach(function (p) {
5887
+ p.classList.remove('active');
5888
+ });
5889
+ allTabs[index].classList.add('active');
5890
+ allTabs[index].setAttribute('aria-selected', 'true');
5891
+ allTabs[index].setAttribute('tabindex', '0');
5892
+ allPanes[index].classList.add('active');
5893
+ if (el._bw_state) el._bw_state.activeIndex = index;
5894
+ }
5793
5895
  return {
5794
5896
  t: 'div',
5795
5897
  a: {
@@ -5816,24 +5918,8 @@
5816
5918
  role: 'tab',
5817
5919
  tabindex: index === actualActiveIndex ? '0' : '-1',
5818
5920
  'aria-selected': index === actualActiveIndex ? 'true' : 'false',
5819
- 'data-tab-index': index,
5820
5921
  onclick: function onclick(e) {
5821
- var tabsContainer = e.target.closest('.bw_tabs');
5822
- var allTabs = tabsContainer.querySelectorAll('.bw_nav_link');
5823
- var allPanes = tabsContainer.querySelectorAll('.bw_tab_pane');
5824
- allTabs.forEach(function (t) {
5825
- t.classList.remove('active');
5826
- t.setAttribute('aria-selected', 'false');
5827
- t.setAttribute('tabindex', '-1');
5828
- });
5829
- allPanes.forEach(function (p) {
5830
- return p.classList.remove('active');
5831
- });
5832
- e.target.classList.add('active');
5833
- e.target.setAttribute('aria-selected', 'true');
5834
- e.target.setAttribute('tabindex', '0');
5835
- var targetIndex = parseInt(e.target.getAttribute('data-tab-index'));
5836
- allPanes[targetIndex].classList.add('active');
5922
+ switchTab(e.target.closest('.bw_tabs'), index);
5837
5923
  }
5838
5924
  },
5839
5925
  c: tab.label
@@ -5861,6 +5947,12 @@
5861
5947
  state: {
5862
5948
  activeIndex: actualActiveIndex
5863
5949
  },
5950
+ handle: {
5951
+ setActiveTab: switchTab,
5952
+ getActiveTab: function getActiveTab(el) {
5953
+ return el._bw_state && el._bw_state.activeIndex || 0;
5954
+ }
5955
+ },
5864
5956
  mounted: function mounted(el) {
5865
5957
  var tablist = el.querySelector('[role="tablist"]');
5866
5958
  if (!tablist) return;
@@ -5950,7 +6042,15 @@
5950
6042
  }
5951
6043
  },
5952
6044
  c: '×'
5953
- }].filter(Boolean)
6045
+ }].filter(Boolean),
6046
+ o: {
6047
+ type: 'alert',
6048
+ handle: {
6049
+ dismiss: function dismiss(el) {
6050
+ if (el && el.parentNode) el.parentNode.removeChild(el);
6051
+ }
6052
+ }
6053
+ }
5954
6054
  };
5955
6055
  }
5956
6056
 
@@ -6052,6 +6152,24 @@
6052
6152
  'aria-valuemax': max
6053
6153
  },
6054
6154
  c: label || "".concat(percentage, "%")
6155
+ },
6156
+ o: {
6157
+ type: 'progress',
6158
+ handle: {
6159
+ setValue: function setValue(el, n) {
6160
+ var bar = el.querySelector('.bw_progress_bar');
6161
+ if (!bar) return;
6162
+ var maxVal = parseInt(bar.getAttribute('aria-valuemax')) || 100;
6163
+ var pct = Math.round(n / maxVal * 100);
6164
+ bar.style.width = pct + '%';
6165
+ bar.setAttribute('aria-valuenow', n);
6166
+ bar.textContent = pct + '%';
6167
+ },
6168
+ getValue: function getValue(el) {
6169
+ var bar = el.querySelector('.bw_progress_bar');
6170
+ return bar ? parseInt(bar.getAttribute('aria-valuenow')) || 0 : 0;
6171
+ }
6172
+ }
6055
6173
  }
6056
6174
  };
6057
6175
  }
@@ -7183,6 +7301,29 @@
7183
7301
  "class": "bw_pagination ".concat(size ? 'bw_pagination_' + size : '', " ").concat(className).trim()
7184
7302
  },
7185
7303
  c: items
7304
+ },
7305
+ o: {
7306
+ type: 'pagination',
7307
+ state: {
7308
+ currentPage: currentPage,
7309
+ pages: pages
7310
+ },
7311
+ handle: {
7312
+ setPage: function setPage(el, n) {
7313
+ if (n < 1 || n > pages) return;
7314
+ var allItems = el.querySelectorAll('.bw_page_item');
7315
+ for (var i = 0; i < allItems.length; i++) {
7316
+ allItems[i].classList.remove('bw_active');
7317
+ }
7318
+ // +1 offset: first item is prev arrow
7319
+ if (allItems[n]) allItems[n].classList.add('bw_active');
7320
+ if (el._bw_state) el._bw_state.currentPage = n;
7321
+ if (onPageChange) onPageChange(n);
7322
+ },
7323
+ getPage: function getPage(el) {
7324
+ return el._bw_state && el._bw_state.currentPage || 1;
7325
+ }
7326
+ }
7186
7327
  }
7187
7328
  };
7188
7329
  }
@@ -7339,7 +7480,6 @@
7339
7480
  "class": "bw_accordion_button ".concat(item.open ? '' : 'bw_collapsed').trim(),
7340
7481
  type: 'button',
7341
7482
  'aria-expanded': item.open ? 'true' : 'false',
7342
- 'data-accordion-index': index,
7343
7483
  onclick: function onclick(e) {
7344
7484
  var btn = e.target.closest('.bw_accordion_button');
7345
7485
  var accordionEl = btn.closest('.bw_accordion');
@@ -7416,6 +7556,42 @@
7416
7556
  type: 'accordion',
7417
7557
  state: {
7418
7558
  multiOpen: multiOpen
7559
+ },
7560
+ handle: {
7561
+ toggle: function toggle(el, index) {
7562
+ var items = el.querySelectorAll('.bw_accordion_item');
7563
+ if (index < 0 || index >= items.length) return;
7564
+ var btn = items[index].querySelector('.bw_accordion_button');
7565
+ if (btn) btn.click();
7566
+ },
7567
+ openAll: function openAll(el) {
7568
+ var items = el.querySelectorAll('.bw_accordion_item');
7569
+ for (var i = 0; i < items.length; i++) {
7570
+ var collapse = items[i].querySelector('.bw_accordion_collapse');
7571
+ var btn = items[i].querySelector('.bw_accordion_button');
7572
+ if (!collapse.classList.contains('bw_collapse_show')) {
7573
+ collapse.classList.add('bw_collapse_show');
7574
+ collapse.style.maxHeight = 'none';
7575
+ btn.classList.remove('bw_collapsed');
7576
+ btn.setAttribute('aria-expanded', 'true');
7577
+ }
7578
+ }
7579
+ },
7580
+ closeAll: function closeAll(el) {
7581
+ var items = el.querySelectorAll('.bw_accordion_item');
7582
+ for (var i = 0; i < items.length; i++) {
7583
+ var collapse = items[i].querySelector('.bw_accordion_collapse');
7584
+ var btn = items[i].querySelector('.bw_accordion_button');
7585
+ if (collapse.classList.contains('bw_collapse_show')) {
7586
+ collapse.style.maxHeight = collapse.scrollHeight + 'px';
7587
+ collapse.offsetHeight;
7588
+ collapse.style.maxHeight = '0px';
7589
+ collapse.classList.remove('bw_collapse_show');
7590
+ btn.classList.add('bw_collapsed');
7591
+ btn.setAttribute('aria-expanded', 'false');
7592
+ }
7593
+ }
7594
+ }
7419
7595
  }
7420
7596
  }
7421
7597
  };
@@ -7518,6 +7694,16 @@
7518
7694
  },
7519
7695
  o: {
7520
7696
  type: 'modal',
7697
+ handle: {
7698
+ open: function open(el) {
7699
+ el.classList.add('bw_modal_show');
7700
+ el.style.display = 'flex';
7701
+ document.body.style.overflow = 'hidden';
7702
+ },
7703
+ close: function close(el) {
7704
+ closeModal(el);
7705
+ }
7706
+ },
7521
7707
  mounted: function mounted(el) {
7522
7708
  // Click backdrop to close
7523
7709
  el.addEventListener('click', function (e) {
@@ -7579,9 +7765,8 @@
7579
7765
  return {
7580
7766
  t: 'div',
7581
7767
  a: {
7582
- "class": "bw_toast ".concat(variantClass(variant), " ").concat(className).trim(),
7583
- role: 'alert',
7584
- 'data-position': position
7768
+ "class": "bw_toast ".concat(variantClass(variant), " bw_toast_").concat(position.replace(/-/g, '_'), " ").concat(className).trim(),
7769
+ role: 'alert'
7585
7770
  },
7586
7771
  c: [title && {
7587
7772
  t: 'div',
@@ -7618,6 +7803,14 @@
7618
7803
  }].filter(Boolean),
7619
7804
  o: {
7620
7805
  type: 'toast',
7806
+ handle: {
7807
+ dismiss: function dismiss(el) {
7808
+ el.classList.add('bw_toast_hiding');
7809
+ setTimeout(function () {
7810
+ if (el.parentNode) el.parentNode.removeChild(el);
7811
+ }, 300);
7812
+ }
7813
+ },
7621
7814
  mounted: function mounted(el) {
7622
7815
  // Trigger show animation
7623
7816
  requestAnimationFrame(function () {
@@ -7993,11 +8186,11 @@
7993
8186
  className = _props$className26 === void 0 ? '' : _props$className26;
7994
8187
 
7995
8188
  // Shared navigation logic
7996
- function goToSlide(carouselEl, index) {
8189
+ function _goToSlide(carouselEl, index) {
7997
8190
  var total = carouselEl.querySelectorAll('.bw_carousel_slide').length;
7998
8191
  if (index < 0) index = total - 1;
7999
8192
  if (index >= total) index = 0;
8000
- carouselEl.setAttribute('data-carousel-index', index);
8193
+ carouselEl._bw_carouselIndex = index;
8001
8194
  var track = carouselEl.querySelector('.bw_carousel_track');
8002
8195
  track.style.transform = 'translateX(-' + index * 100 + '%)';
8003
8196
  // Update indicators
@@ -8051,8 +8244,8 @@
8051
8244
  'aria-label': 'Previous slide',
8052
8245
  onclick: function onclick(e) {
8053
8246
  var carousel = e.target.closest('.bw_carousel');
8054
- var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
8055
- goToSlide(carousel, idx - 1);
8247
+ var idx = carousel._bw_carouselIndex || 0;
8248
+ _goToSlide(carousel, idx - 1);
8056
8249
  }
8057
8250
  },
8058
8251
  c: {
@@ -8072,8 +8265,8 @@
8072
8265
  'aria-label': 'Next slide',
8073
8266
  onclick: function onclick(e) {
8074
8267
  var carousel = e.target.closest('.bw_carousel');
8075
- var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
8076
- goToSlide(carousel, idx + 1);
8268
+ var idx = carousel._bw_carouselIndex || 0;
8269
+ _goToSlide(carousel, idx + 1);
8077
8270
  }
8078
8271
  },
8079
8272
  c: {
@@ -8101,11 +8294,9 @@
8101
8294
  "class": 'bw_carousel_indicator' + (i === startIndex ? ' active' : ''),
8102
8295
  type: 'button',
8103
8296
  'aria-label': 'Go to slide ' + (i + 1),
8104
- 'data-slide-index': i,
8105
8297
  onclick: function onclick(e) {
8106
8298
  var carousel = e.target.closest('.bw_carousel');
8107
- var idx = parseInt(e.target.getAttribute('data-slide-index'));
8108
- goToSlide(carousel, idx);
8299
+ _goToSlide(carousel, i);
8109
8300
  }
8110
8301
  }
8111
8302
  };
@@ -8118,8 +8309,7 @@
8118
8309
  "class": ('bw_carousel ' + className).trim(),
8119
8310
  style: 'height: ' + height,
8120
8311
  tabindex: '0',
8121
- 'aria-roledescription': 'carousel',
8122
- 'data-carousel-index': startIndex
8312
+ 'aria-roledescription': 'carousel'
8123
8313
  },
8124
8314
  c: children,
8125
8315
  o: {
@@ -8129,23 +8319,52 @@
8129
8319
  autoPlay: autoPlay,
8130
8320
  interval: interval
8131
8321
  },
8322
+ handle: {
8323
+ goToSlide: function goToSlide(el, index) {
8324
+ _goToSlide(el, index);
8325
+ },
8326
+ next: function next(el) {
8327
+ _goToSlide(el, (el._bw_carouselIndex || 0) + 1);
8328
+ },
8329
+ prev: function prev(el) {
8330
+ _goToSlide(el, (el._bw_carouselIndex || 0) - 1);
8331
+ },
8332
+ getActiveIndex: function getActiveIndex(el) {
8333
+ return el._bw_carouselIndex || 0;
8334
+ },
8335
+ pause: function pause(el) {
8336
+ if (el._bw_carouselInterval) {
8337
+ clearInterval(el._bw_carouselInterval);
8338
+ el._bw_carouselInterval = null;
8339
+ }
8340
+ },
8341
+ play: function play(el) {
8342
+ if (!el._bw_carouselInterval && el._bw_state) {
8343
+ var ms = el._bw_state.interval || 5000;
8344
+ el._bw_carouselInterval = setInterval(function () {
8345
+ _goToSlide(el, (el._bw_carouselIndex || 0) + 1);
8346
+ }, ms);
8347
+ }
8348
+ }
8349
+ },
8132
8350
  mounted: function mounted(el) {
8351
+ el._bw_carouselIndex = startIndex;
8133
8352
  // Keyboard navigation
8134
8353
  el.addEventListener('keydown', function (e) {
8135
- var idx = parseInt(el.getAttribute('data-carousel-index') || '0');
8354
+ var idx = el._bw_carouselIndex || 0;
8136
8355
  if (e.key === 'ArrowLeft') {
8137
8356
  e.preventDefault();
8138
- goToSlide(el, idx - 1);
8357
+ _goToSlide(el, idx - 1);
8139
8358
  } else if (e.key === 'ArrowRight') {
8140
8359
  e.preventDefault();
8141
- goToSlide(el, idx + 1);
8360
+ _goToSlide(el, idx + 1);
8142
8361
  }
8143
8362
  });
8144
8363
  // Auto-play
8145
8364
  if (autoPlay) {
8146
8365
  var intervalId = setInterval(function () {
8147
- var idx = parseInt(el.getAttribute('data-carousel-index') || '0');
8148
- goToSlide(el, idx + 1);
8366
+ var idx = el._bw_carouselIndex || 0;
8367
+ _goToSlide(el, idx + 1);
8149
8368
  }, interval);
8150
8369
  el._bw_carouselInterval = intervalId;
8151
8370
  // Pause on hover/focus for usability
@@ -8154,8 +8373,8 @@
8154
8373
  });
8155
8374
  el.addEventListener('mouseleave', function () {
8156
8375
  el._bw_carouselInterval = setInterval(function () {
8157
- var idx = parseInt(el.getAttribute('data-carousel-index') || '0');
8158
- goToSlide(el, idx + 1);
8376
+ var idx = el._bw_carouselIndex || 0;
8377
+ _goToSlide(el, idx + 1);
8159
8378
  }, interval);
8160
8379
  });
8161
8380
  }
@@ -8276,7 +8495,11 @@
8276
8495
  },
8277
8496
  c: children,
8278
8497
  o: {
8279
- type: 'stat-card'
8498
+ type: 'stat-card',
8499
+ slots: {
8500
+ value: '.bw_stat_value',
8501
+ label: '.bw_stat_label'
8502
+ }
8280
8503
  }
8281
8504
  };
8282
8505
  }
@@ -9013,8 +9236,7 @@
9013
9236
  return {
9014
9237
  t: 'span',
9015
9238
  a: {
9016
- "class": 'bw_chip',
9017
- 'data-chip-value': text
9239
+ "class": 'bw_chip'
9018
9240
  },
9019
9241
  c: [text, {
9020
9242
  t: 'button',
@@ -9024,9 +9246,8 @@
9024
9246
  'aria-label': 'Remove ' + text,
9025
9247
  onclick: function onclick(e) {
9026
9248
  var chip = e.target.closest('.bw_chip');
9027
- var val = chip.getAttribute('data-chip-value');
9028
9249
  chip.parentNode.removeChild(chip);
9029
- if (onRemove) onRemove(val);
9250
+ if (onRemove) onRemove(text);
9030
9251
  }
9031
9252
  },
9032
9253
  c: "\xD7"
@@ -9052,7 +9273,7 @@
9052
9273
  // Insert chip before the input
9053
9274
  var chipEl = document.createElement('span');
9054
9275
  chipEl.className = 'bw_chip';
9055
- chipEl.setAttribute('data-chip-value', val);
9276
+ chipEl._bw_chipValue = val;
9056
9277
  chipEl.innerHTML = '';
9057
9278
  chipEl.textContent = val;
9058
9279
  var removeBtn = document.createElement('button');
@@ -9075,7 +9296,7 @@
9075
9296
  var chipEls = wrapper.querySelectorAll('.bw_chip');
9076
9297
  if (chipEls.length) {
9077
9298
  var last = chipEls[chipEls.length - 1];
9078
- var removedVal = last.getAttribute('data-chip-value');
9299
+ var removedVal = last._bw_chipValue || last.firstChild.textContent;
9079
9300
  last.parentNode.removeChild(last);
9080
9301
  if (onRemove) onRemove(removedVal);
9081
9302
  }
@@ -9084,7 +9305,50 @@
9084
9305
  }
9085
9306
  }]),
9086
9307
  o: {
9087
- type: 'chip-input'
9308
+ type: 'chip-input',
9309
+ handle: {
9310
+ addChip: function addChip(el, text) {
9311
+ if (!text) return;
9312
+ var input = el.querySelector('.bw_chip_field');
9313
+ var chipEl = document.createElement('span');
9314
+ chipEl.className = 'bw_chip';
9315
+ chipEl._bw_chipValue = text;
9316
+ chipEl.textContent = text;
9317
+ var removeBtn = document.createElement('button');
9318
+ removeBtn.type = 'button';
9319
+ removeBtn.className = 'bw_chip_remove';
9320
+ removeBtn.setAttribute('aria-label', 'Remove ' + text);
9321
+ removeBtn.textContent = "\xD7";
9322
+ removeBtn.onclick = function () {
9323
+ chipEl.parentNode.removeChild(chipEl);
9324
+ };
9325
+ chipEl.appendChild(removeBtn);
9326
+ el.insertBefore(chipEl, input);
9327
+ },
9328
+ removeChip: function removeChip(el, text) {
9329
+ var chips = el.querySelectorAll('.bw_chip');
9330
+ for (var i = 0; i < chips.length; i++) {
9331
+ if ((chips[i]._bw_chipValue || chips[i].firstChild.textContent) === text) {
9332
+ chips[i].parentNode.removeChild(chips[i]);
9333
+ return;
9334
+ }
9335
+ }
9336
+ },
9337
+ getChips: function getChips(el) {
9338
+ var chips = el.querySelectorAll('.bw_chip');
9339
+ var values = [];
9340
+ for (var i = 0; i < chips.length; i++) {
9341
+ values.push(chips[i]._bw_chipValue || chips[i].firstChild.textContent);
9342
+ }
9343
+ return values;
9344
+ },
9345
+ clear: function clear(el) {
9346
+ var chips = el.querySelectorAll('.bw_chip');
9347
+ for (var i = chips.length - 1; i >= 0; i--) {
9348
+ chips[i].parentNode.removeChild(chips[i]);
9349
+ }
9350
+ }
9351
+ }
9088
9352
  }
9089
9353
  };
9090
9354
  }
@@ -9362,12 +9626,11 @@
9362
9626
  // monotonic ID for subscriptions
9363
9627
 
9364
9628
  // ── Node reference cache ──────────────────────────────────────────────
9365
- // Fast O(1) lookup for elements by bw_id, id attribute, or bw_uuid.
9629
+ // Fast O(1) lookup for elements by id attribute or bw_uuid_* class.
9366
9630
  //
9367
9631
  // Populated by bw.createDOM() when elements have:
9368
- // - data-bw_id attribute (user-declared addressable elements)
9369
9632
  // - id attribute (standard HTML id)
9370
- // - bw_uuid (internal, for lifecycle-managed elements)
9633
+ // - bw_uuid_* class (lifecycle-managed or explicitly addressed elements)
9371
9634
  //
9372
9635
  // Cleaned up by bw.cleanup() when elements are destroyed via bitwrench APIs.
9373
9636
  // On cache miss, falls back to querySelector/getElementById — never fails,
@@ -9375,7 +9638,7 @@
9375
9638
  // via parentNode === null check (IE11-safe, unlike el.isConnected).
9376
9639
  //
9377
9640
  // Elements created via bw.createDOM() also get el._bw_refs — a local map of
9378
- // child bw_id DOM node ref for fast parentchild access in o.render.
9641
+ // child id/UUID -> DOM node ref for fast parent->child access in o.render.
9379
9642
  // This is the bitwrench equivalent of React's compiled template "holes".
9380
9643
  //
9381
9644
  // Contract: if you remove elements outside of bitwrench APIs (raw el.remove()),
@@ -9454,7 +9717,6 @@
9454
9717
  // _cw console.warn 8
9455
9718
  // _cl console.log 11
9456
9719
  // _ce console.error 4
9457
- // _chp ComponentHandle.prototype 28 (defined after constructor)
9458
9720
  //
9459
9721
  // Note: document.createElement etc. are NOT aliased because they require
9460
9722
  // `this === document` and .bind() would add overhead on every call.
@@ -9638,15 +9900,15 @@
9638
9900
  * 1. Check `bw._nodeMap[id]` — if found and still attached (parentNode !== null), return it
9639
9901
  * 2. If cached ref is detached (parentNode === null), remove stale entry
9640
9902
  * 3. Fall back to `document.getElementById(id)` then `document.querySelector(...)`
9641
- * 4. If fallback finds the element, cache it for next time
9642
- * 5. If not found anywhere, return null
9903
+ * 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
9904
+ * 5. Cache the result for next time
9643
9905
  *
9644
9906
  * Accepts a DOM element directly (pass-through) or a string identifier.
9645
9907
  * String identifiers are tried as: direct map key, getElementById,
9646
9908
  * querySelector (for CSS selectors starting with . or #), and
9647
- * data-bw_id attribute selector.
9909
+ * bw_uuid_* class selector.
9648
9910
  *
9649
- * @param {string|Element} id - Element ID, CSS selector, data-bw_id value, or DOM element
9911
+ * @param {string|Element} id - Element ID, CSS selector, bw_uuid_* class, or DOM element
9650
9912
  * @returns {Element|null} The DOM element, or null if not found
9651
9913
  * @category Internal
9652
9914
  */
@@ -9675,17 +9937,12 @@
9675
9937
  el = document.querySelector(id);
9676
9938
  }
9677
9939
 
9678
- // 4. Try data-bw_id attribute (for bw.uuid-generated IDs)
9679
- if (!el) {
9680
- el = document.querySelector('[data-bw_id="' + id + '"]');
9681
- }
9682
-
9683
- // 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
9940
+ // 4. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
9684
9941
  if (!el && id.indexOf('bw_uuid_') === 0) {
9685
9942
  el = document.querySelector('.' + id);
9686
9943
  }
9687
9944
 
9688
- // 6. Cache the result for next time
9945
+ // 5. Cache the result for next time
9689
9946
  if (el) {
9690
9947
  bw._nodeMap[id] = el;
9691
9948
  }
@@ -9696,17 +9953,17 @@
9696
9953
  * Register a DOM element in the node cache under one or more keys.
9697
9954
  *
9698
9955
  * Called internally by `bw.createDOM()`. Registers elements that have
9699
- * id attributes, data-bw_id attributes, or both.
9956
+ * id attributes, UUID classes, or both.
9700
9957
  *
9701
9958
  * @param {Element} el - DOM element to register
9702
- * @param {string} [bwId] - data-bw_id value to register under
9959
+ * @param {string} [uuid] - bw_uuid_* class token to register under
9703
9960
  * @category Internal
9704
9961
  */
9705
- bw._registerNode = function (el, bwId) {
9962
+ bw._registerNode = function (el, uuid) {
9706
9963
  if (!el) return;
9707
- // Register under data-bw_id
9708
- if (bwId) {
9709
- bw._nodeMap[bwId] = el;
9964
+ // Register under UUID class token
9965
+ if (uuid) {
9966
+ bw._nodeMap[uuid] = el;
9710
9967
  }
9711
9968
  // Register under id attribute
9712
9969
  var htmlId = el.getAttribute ? el.getAttribute('id') : null;
@@ -9722,13 +9979,13 @@
9722
9979
  * through bitwrench APIs.
9723
9980
  *
9724
9981
  * @param {Element} el - DOM element to deregister
9725
- * @param {string} [bwId] - data-bw_id value to remove
9982
+ * @param {string} [uuid] - bw_uuid_* class token to remove
9726
9983
  * @category Internal
9727
9984
  */
9728
- bw._deregisterNode = function (el, bwId) {
9729
- // Remove data-bw_id entry
9730
- if (bwId) {
9731
- delete bw._nodeMap[bwId];
9985
+ bw._deregisterNode = function (el, uuid) {
9986
+ // Remove UUID class entry
9987
+ if (uuid) {
9988
+ delete bw._nodeMap[uuid];
9732
9989
  }
9733
9990
  // Remove id attribute entry
9734
9991
  var htmlId = el && el.getAttribute ? el.getAttribute('id') : null;
@@ -9741,6 +9998,13 @@
9741
9998
  // bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
9742
9999
  // ===================================================================================
9743
10000
 
10001
+ /**
10002
+ * Marker class for elements with lifecycle hooks (mounted/unmount/render/state).
10003
+ * Used by cleanup() to find lifecycle-managed elements via querySelectorAll('.bw_lc').
10004
+ * @private
10005
+ */
10006
+ var _BW_LC = 'bw_lc';
10007
+
9744
10008
  /**
9745
10009
  * Regex to match a bw_uuid_* token in a class string.
9746
10010
  * @private
@@ -9926,20 +10190,10 @@
9926
10190
  * // => '<div class="card"><p>Content here</p></div>'
9927
10191
  */
9928
10192
  bw.html = function (taco) {
9929
- var _attrs$class;
9930
10193
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
9931
10194
  // Handle null/undefined
9932
10195
  if (taco == null) return '';
9933
10196
 
9934
- // Handle ComponentHandle — use its .taco
9935
- if (taco && taco._bwComponent === true) {
9936
- var compOptions = Object.assign({}, options);
9937
- if (!compOptions.state && taco._state) {
9938
- compOptions.state = taco._state;
9939
- }
9940
- return bw.html(taco.taco, compOptions);
9941
- }
9942
-
9943
10197
  // Handle arrays of TACOs
9944
10198
  if (_isA(taco)) {
9945
10199
  return taco.map(function (t) {
@@ -9952,24 +10206,6 @@
9952
10206
  return taco.v;
9953
10207
  }
9954
10208
 
9955
- // Handle bw.when() markers
9956
- if (taco && taco._bwWhen && options.state) {
9957
- var whenExpr = taco.expr.replace(/^\$\{|\}$/g, '');
9958
- var whenVal = options.compile ? bw._resolveTemplate('${' + whenExpr + '}', options.state, true) : bw._evaluatePath(options.state, whenExpr);
9959
- var branch = whenVal ? taco.branches[0] : taco.branches[1] || null;
9960
- return branch ? bw.html(branch, options) : '';
9961
- }
9962
-
9963
- // Handle bw.each() markers
9964
- if (taco && taco._bwEach && options.state) {
9965
- var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
9966
- var arr = bw._evaluatePath(options.state, eachExpr);
9967
- if (!_isA(arr)) return '';
9968
- return arr.map(function (item, idx) {
9969
- return bw.html(taco.factory(item, idx), options);
9970
- }).join('');
9971
- }
9972
-
9973
10209
  // Handle primitives and non-TACO objects
9974
10210
  if (!_is(taco, 'object') || !taco.t) {
9975
10211
  var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
@@ -10043,14 +10279,14 @@
10043
10279
  }
10044
10280
  }
10045
10281
 
10046
- // Add bw_id as a class if lifecycle hooks present
10047
- if ((opts.mounted || opts.unmount) && !((_attrs$class = attrs["class"]) !== null && _attrs$class !== void 0 && _attrs$class.includes('bw_id_'))) {
10048
- var id = opts.bw_id || bw.uuid();
10282
+ // Add bw_uuid + bw_lc classes if lifecycle hooks present
10283
+ if ((opts.mounted || opts.unmount) && !_UUID_RE.test(attrs["class"] || '')) {
10284
+ var uuid = bw.uuid('uuid');
10049
10285
  attrStr = attrStr.replace(/class="([^"]*)"/, function (_match, classes) {
10050
- return "class=\"".concat(classes, " bw_id_").concat(id, "\"").trim();
10286
+ return "class=\"".concat(classes, " ").concat(uuid, " ").concat(_BW_LC, "\"").trim();
10051
10287
  });
10052
10288
  if (!attrStr.includes('class=')) {
10053
- attrStr += " class=\"bw_id_".concat(id, "\"");
10289
+ attrStr += " class=\"".concat(uuid, " ").concat(_BW_LC, "\"");
10054
10290
  }
10055
10291
  }
10056
10292
 
@@ -10277,11 +10513,6 @@
10277
10513
  return frag;
10278
10514
  }
10279
10515
 
10280
- // Handle ComponentHandle — extract .taco for DOM creation
10281
- if (taco && taco._bwComponent === true) {
10282
- return bw.createDOM(taco.taco, options);
10283
- }
10284
-
10285
10516
  // Handle text nodes
10286
10517
  if (!_is(taco, 'object') || !taco.t) {
10287
10518
  return document.createTextNode(String(taco));
@@ -10328,24 +10559,19 @@
10328
10559
  }
10329
10560
 
10330
10561
  // Add children, building _bw_refs for fast parent→child access.
10331
- // Children with data-bw_id or id attributes get local refs on the parent,
10562
+ // Children with id attributes or bw_uuid_* classes get local refs on the parent,
10332
10563
  // so o.render functions can access them without any DOM lookup.
10333
10564
  if (content != null) {
10334
10565
  if (_isA(content)) {
10335
10566
  content.forEach(function (child) {
10336
10567
  if (child != null) {
10337
- // Handle ComponentHandle in content arrays (Level 2 children)
10338
- if (child._bwComponent === true) {
10339
- child.mount(el);
10340
- return;
10341
- }
10342
10568
  var childEl = bw.createDOM(child, options);
10343
10569
  el.appendChild(childEl);
10344
10570
  // Build local refs for addressable children
10345
- var childBwId = child && child.a ? child.a['data-bw_id'] || child.a.id : null;
10346
- if (childBwId) {
10571
+ var childRefId = child && child.a ? child.a.id || bw.getUUID(child) : null;
10572
+ if (childRefId) {
10347
10573
  if (!el._bw_refs) el._bw_refs = {};
10348
- el._bw_refs[childBwId] = childEl;
10574
+ el._bw_refs[childRefId] = childEl;
10349
10575
  }
10350
10576
  // Bubble up grandchild refs (flatten one level)
10351
10577
  if (childEl._bw_refs) {
@@ -10361,16 +10587,13 @@
10361
10587
  } else if (_is(content, 'object') && content.__bw_raw) {
10362
10588
  // Raw HTML content — inject via innerHTML
10363
10589
  el.innerHTML = content.v;
10364
- } else if (content._bwComponent === true) {
10365
- // Single ComponentHandle as content
10366
- content.mount(el);
10367
10590
  } else if (_is(content, 'object') && content.t) {
10368
10591
  var childEl = bw.createDOM(content, options);
10369
10592
  el.appendChild(childEl);
10370
- var childBwId = content.a ? content.a['data-bw_id'] || content.a.id : null;
10371
- if (childBwId) {
10593
+ var childRefId = content.a ? content.a.id || bw.getUUID(content) : null;
10594
+ if (childRefId) {
10372
10595
  if (!el._bw_refs) el._bw_refs = {};
10373
- el._bw_refs[childBwId] = childEl;
10596
+ el._bw_refs[childRefId] = childEl;
10374
10597
  }
10375
10598
  if (childEl._bw_refs) {
10376
10599
  if (!el._bw_refs) el._bw_refs = {};
@@ -10400,56 +10623,87 @@
10400
10623
 
10401
10624
  // Handle lifecycle hooks and state
10402
10625
  if (opts.mounted || opts.unmount || opts.render || opts.state) {
10403
- var id = attrs['data-bw_id'] || bw.uuid();
10404
- el.setAttribute('data-bw_id', id);
10626
+ // Ensure element has a UUID class for identity
10627
+ var uuid = bw.getUUID(el) || bw.uuid('uuid');
10628
+ el.classList.add(uuid);
10629
+ el.classList.add(_BW_LC);
10405
10630
 
10406
- // Register in node cache under data-bw_id
10407
- bw._registerNode(el, id);
10631
+ // Register in node cache under UUID class
10632
+ bw._registerNode(el, uuid);
10408
10633
 
10409
10634
  // Store state
10410
10635
  if (opts.state) {
10411
10636
  el._bw_state = opts.state;
10412
10637
  }
10413
10638
 
10414
- // o.render — first-class render function (replaces mounted boilerplate)
10639
+ // o.render — store the render function for bw.update()
10415
10640
  if (opts.render) {
10416
10641
  el._bw_render = opts.render;
10417
- if (opts.mounted) {
10418
- _cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
10419
- }
10642
+ }
10420
10643
 
10421
- // Queue initial render (same timing as mounted)
10644
+ // Determine what to call on mount:
10645
+ // - If o.mounted exists, call it (it can call el._bw_render() for initial render)
10646
+ // - Otherwise if o.render exists, auto-call it as a convenience shorthand
10647
+ var mountFn = opts.mounted || (opts.render ? function (mountEl) {
10648
+ opts.render(mountEl, mountEl._bw_state || {});
10649
+ } : null);
10650
+ if (mountFn) {
10422
10651
  if (document.body.contains(el)) {
10423
- opts.render(el, el._bw_state || {});
10652
+ mountFn(el, el._bw_state || {});
10424
10653
  } else {
10425
10654
  requestAnimationFrame(function () {
10426
10655
  if (document.body.contains(el)) {
10427
- opts.render(el, el._bw_state || {});
10428
- }
10429
- });
10430
- }
10431
- } else if (opts.mounted) {
10432
- // Queue mounted callback (legacy pattern)
10433
- if (document.body.contains(el)) {
10434
- opts.mounted(el, el._bw_state || {});
10435
- } else {
10436
- requestAnimationFrame(function () {
10437
- if (document.body.contains(el)) {
10438
- opts.mounted(el, el._bw_state || {});
10656
+ mountFn(el, el._bw_state || {});
10439
10657
  }
10440
10658
  });
10441
10659
  }
10442
10660
  }
10443
10661
 
10444
- // Store unmount callback
10662
+ // Store unmount callback keyed by UUID class
10445
10663
  if (opts.unmount) {
10446
- bw._unmountCallbacks.set(id, function () {
10664
+ bw._unmountCallbacks.set(uuid, function () {
10447
10665
  opts.unmount(el, el._bw_state || {});
10448
10666
  });
10449
10667
  }
10450
- } else if (attrs['data-bw_id']) {
10451
- // Element has explicit data-bw_id but no lifecycle hooks — still register it
10452
- bw._registerNode(el, attrs['data-bw_id']);
10668
+ }
10669
+
10670
+ // Component handle: attach methods to el.bw namespace
10671
+ if (opts.handle || opts.slots) {
10672
+ if (!el.bw) el.bw = {};
10673
+
10674
+ // Explicit handle methods: fn(el, ...args) -> el.bw.method(...args)
10675
+ if (opts.handle) {
10676
+ for (var hk in opts.handle) {
10677
+ if (_hop.call(opts.handle, hk)) {
10678
+ el.bw[hk] = opts.handle[hk].bind(null, el);
10679
+ }
10680
+ }
10681
+ }
10682
+
10683
+ // Slot declarations: auto-generate setX/getX pairs
10684
+ if (opts.slots) {
10685
+ for (var sk in opts.slots) {
10686
+ if (_hop.call(opts.slots, sk)) {
10687
+ (function (name, selector) {
10688
+ var cap = name.charAt(0).toUpperCase() + name.slice(1);
10689
+ el.bw['set' + cap] = function (value) {
10690
+ var t = el.querySelector(selector);
10691
+ if (!t) return;
10692
+ if (value != null && _typeof(value) === 'object' && value.t) {
10693
+ t.innerHTML = '';
10694
+ t.appendChild(bw.createDOM(value));
10695
+ } else {
10696
+ t.textContent = value != null ? String(value) : '';
10697
+ }
10698
+ };
10699
+ el.bw['get' + cap] = function () {
10700
+ var t = el.querySelector(selector);
10701
+ return t ? t.textContent : '';
10702
+ };
10703
+ })(sk, opts.slots[sk]);
10704
+ }
10705
+ }
10706
+ }
10453
10707
  }
10454
10708
  return el;
10455
10709
  };
@@ -10495,7 +10749,7 @@
10495
10749
  // the target is the mount point, not the content being replaced)
10496
10750
  var savedState = targetEl._bw_state;
10497
10751
  var savedRender = targetEl._bw_render;
10498
- var savedBwId = targetEl.getAttribute('data-bw_id');
10752
+ var savedUuid = bw.getUUID(targetEl);
10499
10753
  var savedSubs = targetEl._bw_subs;
10500
10754
 
10501
10755
  // Temporarily remove _bw_subs so cleanup doesn't call them
@@ -10506,35 +10760,20 @@
10506
10760
  // Restore the target's own state/render/subs after cleanup
10507
10761
  if (savedState !== undefined) targetEl._bw_state = savedState;
10508
10762
  if (savedRender) targetEl._bw_render = savedRender;
10509
- if (savedBwId) {
10510
- targetEl.setAttribute('data-bw_id', savedBwId);
10511
- // Re-register mount point in node cache (cleanup deregistered it)
10512
- bw._registerNode(targetEl, savedBwId);
10763
+ if (savedUuid) {
10764
+ // UUID class stays on element through cleanup; re-register in cache
10765
+ bw._registerNode(targetEl, savedUuid);
10513
10766
  }
10514
10767
  if (savedSubs) targetEl._bw_subs = savedSubs;
10515
10768
 
10516
10769
  // Clear and mount new content
10517
10770
  targetEl.innerHTML = '';
10518
10771
  if (taco != null) {
10519
- // Handle ComponentHandle (reactive components from bw.component())
10520
- if (taco._bwComponent === true) {
10521
- taco.mount(targetEl);
10522
- }
10523
- // Handle component handles (objects with element property)
10524
- else if (taco.element instanceof Element) {
10525
- targetEl.appendChild(taco.element);
10526
- }
10527
10772
  // Handle arrays
10528
- else if (_isA(taco)) {
10773
+ if (_isA(taco)) {
10529
10774
  taco.forEach(function (t) {
10530
10775
  if (t != null) {
10531
- if (t._bwComponent === true) {
10532
- t.mount(targetEl);
10533
- } else if (t.element instanceof Element) {
10534
- targetEl.appendChild(t.element);
10535
- } else {
10536
- targetEl.appendChild(bw.createDOM(t, options));
10537
- }
10776
+ targetEl.appendChild(bw.createDOM(t, options));
10538
10777
  }
10539
10778
  });
10540
10779
  }
@@ -10546,197 +10785,40 @@
10546
10785
  return targetEl;
10547
10786
  };
10548
10787
 
10549
- /**
10550
- * Compile props into getter/setter functions for reactive updates.
10551
- *
10552
- * Used internally by `bw.renderComponent()`. Creates a proxy-like object
10553
- * where setting a property triggers `handle.onPropChange()`.
10554
- *
10555
- * @param {Object} handle - Component handle
10556
- * @param {Object} props - Initial props
10557
- * @returns {Object} Compiled props object with getters/setters
10558
- * @category DOM Generation
10559
- */
10560
- bw.compileProps = function (handle) {
10561
- var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
10562
- var compiledProps = {};
10563
- _keys(props).forEach(function (key) {
10564
- // Create getter/setter for each prop
10565
- Object.defineProperty(compiledProps, key, {
10566
- get: function get() {
10567
- return handle._props[key];
10568
- },
10569
- set: function set(value) {
10570
- var oldValue = handle._props[key];
10571
- if (oldValue !== value) {
10572
- handle._props[key] = value;
10573
- // Trigger update if prop changed
10574
- if (handle.onPropChange) {
10575
- handle.onPropChange(key, value, oldValue);
10576
- }
10577
- }
10578
- },
10579
- enumerable: true,
10580
- configurable: true
10581
- });
10582
- });
10583
- return compiledProps;
10788
+ // Deprecation stubs for removed ComponentHandle APIs
10789
+ bw.compileProps = function () {
10790
+ throw new Error('bw.compileProps() removed in v2.0.19. Use o.handle/o.slots instead.');
10791
+ };
10792
+ bw.renderComponent = function () {
10793
+ throw new Error('bw.renderComponent() removed in v2.0.19. Use bw.mount() with o.handle/o.slots instead.');
10584
10794
  };
10585
10795
 
10586
10796
  /**
10587
- * Render a TACO component and return an enhanced handle object.
10797
+ * Mount a TACO into a target element and return the created root element.
10798
+ * Like bw.DOM() but returns the root element of the TACO (not the container),
10799
+ * giving direct access to el.bw handle methods.
10588
10800
  *
10589
- * The handle provides compiled props, state management, child registration,
10590
- * and a destroy method. Used internally by `bw.createCard()`, `bw.createTable()`, etc.
10591
- *
10592
- * @param {Object} taco - TACO object to render
10593
- * @param {Object} [options] - Render options
10594
- * @returns {Object} Component handle with element, props, state, update(), destroy()
10801
+ * @param {string|Element} target - CSS selector or DOM element
10802
+ * @param {Object} taco - TACO to render
10803
+ * @param {Object} [options] - Mount options
10804
+ * @returns {Element} The created root element
10595
10805
  * @category DOM Generation
10596
- */
10597
- bw.renderComponent = function (taco) {
10598
- var _taco$o3;
10599
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
10600
- var element = bw.createDOM(taco, options);
10601
-
10602
- // Enhanced handle with prop compilation
10603
- var handle = {
10604
- element: element,
10605
- taco: taco,
10606
- _props: _objectSpread2({}, taco.a),
10607
- // Store props internally
10608
- _state: ((_taco$o3 = taco.o) === null || _taco$o3 === void 0 ? void 0 : _taco$o3.state) || {},
10609
- _children: {},
10610
- // Store child component references
10611
-
10612
- // Get compiled props with getters/setters
10613
- get props() {
10614
- if (!this._compiledProps) {
10615
- this._compiledProps = bw.compileProps(this, this._props);
10616
- }
10617
- return this._compiledProps;
10618
- },
10619
- /**
10620
- * Query all matching elements within this component
10621
- * @param {string} selector - CSS selector
10622
- * @returns {NodeList} Matching elements
10623
- */
10624
- $: function $(selector) {
10625
- return this.element.querySelectorAll(selector);
10626
- },
10627
- /**
10628
- * Query the first matching element within this component
10629
- * @param {string} selector - CSS selector
10630
- * @returns {Element|null} First matching element or null
10631
- */
10632
- $first: function $first(selector) {
10633
- return this.element.querySelector(selector);
10634
- },
10635
- /**
10636
- * Update component with new props and re-render in place
10637
- * @param {Object} newProps - Properties to merge into current props
10638
- * @returns {Object} this handle (for chaining)
10639
- */
10640
- update: function update(newProps) {
10641
- // Update internal props
10642
- Object.assign(this._props, newProps);
10643
-
10644
- // Rebuild TACO with new props
10645
- var newTaco = _objectSpread2(_objectSpread2({}, this.taco), {}, {
10646
- a: _objectSpread2(_objectSpread2({}, this.taco.a), newProps)
10647
- });
10648
- var newElement = bw.createDOM(newTaco, options);
10649
-
10650
- // Replace in DOM
10651
- this.element.replaceWith(newElement);
10652
- this.element = newElement;
10653
- this.taco = newTaco;
10654
- return this;
10655
- },
10656
- /**
10657
- * Re-render the component from its current TACO, replacing the DOM element
10658
- * @returns {Object} this handle (for chaining)
10659
- */
10660
- render: function render() {
10661
- var newElement = bw.createDOM(this.taco, options);
10662
- this.element.replaceWith(newElement);
10663
- this.element = newElement;
10664
- return this;
10665
- },
10666
- /**
10667
- * Called when a compiled prop value changes. Override to customize behavior.
10668
- * Default implementation triggers a full re-render.
10669
- * @param {string} key - Property name that changed
10670
- * @param {*} newValue - New property value
10671
- * @param {*} oldValue - Previous property value
10672
- */
10673
- onPropChange: function onPropChange(_key, _newValue, _oldValue) {
10674
- // Auto re-render on prop change by default
10675
- this.render();
10676
- },
10677
- // State management
10678
- get state() {
10679
- return this._state;
10680
- },
10681
- set state(newState) {
10682
- this._state = newState;
10683
- this.render();
10684
- },
10685
- /**
10686
- * Merge state updates and re-render the component
10687
- * @param {Object} updates - State properties to merge
10688
- * @returns {Object} this handle (for chaining)
10689
- */
10690
- setState: function setState(updates) {
10691
- Object.assign(this._state, updates);
10692
- this.render();
10693
- return this;
10694
- },
10695
- /**
10696
- * Register a child component under a name for later retrieval
10697
- * @param {string} name - Child name key
10698
- * @param {Object} component - Child component handle
10699
- * @returns {Object} this handle (for chaining)
10700
- */
10701
- addChild: function addChild(name, component) {
10702
- this._children[name] = component;
10703
- return this;
10704
- },
10705
- /**
10706
- * Retrieve a registered child component by name
10707
- * @param {string} name - Child name key
10708
- * @returns {Object|undefined} Child component handle
10709
- */
10710
- getChild: function getChild(name) {
10711
- return this._children[name];
10712
- },
10713
- /**
10714
- * Destroy this component and all registered children
10715
- *
10716
- * Calls destroy() recursively on children, runs bw.cleanup(),
10717
- * removes the element from DOM, and clears all internal references.
10718
- */
10719
- destroy: function destroy() {
10720
- // Destroy children first
10721
- Object.values(this._children).forEach(function (child) {
10722
- if (child && child.destroy) child.destroy();
10723
- });
10724
-
10725
- // Clean up this component
10726
- bw.cleanup(this.element);
10727
- this.element.remove();
10728
-
10729
- // Clear references
10730
- this._children = {};
10731
- this._props = {};
10732
- this._state = {};
10733
- this._compiledProps = null;
10734
- }
10735
- };
10736
-
10737
- // Store handle reference on element
10738
- element._bwHandle = handle;
10739
- return handle;
10806
+ * @example
10807
+ * var el = bw.mount('#app', bw.makeCarousel({ items: slides }));
10808
+ * el.bw.goToSlide(2);
10809
+ * el.bw.next();
10810
+ */
10811
+ bw.mount = function (target, taco, options) {
10812
+ var container = _is(target, 'string') ? bw.$(target)[0] : target;
10813
+ if (!container) {
10814
+ _cw('bw.mount: target not found');
10815
+ return null;
10816
+ }
10817
+ bw.cleanup(container);
10818
+ container.innerHTML = '';
10819
+ var el = bw.createDOM(taco, options || {});
10820
+ container.appendChild(el);
10821
+ return el;
10740
10822
  };
10741
10823
 
10742
10824
  /**
@@ -10757,32 +10839,27 @@
10757
10839
  bw.cleanup = function (element) {
10758
10840
  if (!bw._isBrowser || !element) return;
10759
10841
 
10760
- // Deregister UUID classes from node cache (element + descendants)
10761
- // Covers elements that have UUID but no data-bw_id
10762
- var selfUuidMatch = element.className && element.className.match(_UUID_RE);
10763
- if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
10842
+ // Deregister UUID classes from node cache for non-lifecycle UUID elements
10764
10843
  var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
10765
10844
  uuidEls.forEach(function (uel) {
10766
10845
  var m = uel.className && uel.className.match(_UUID_RE);
10767
10846
  if (m) delete bw._nodeMap[m[0]];
10768
10847
  });
10769
10848
 
10770
- // Find all elements with data-bw_id
10771
- var elements = element.querySelectorAll('[data-bw_id]');
10849
+ // Find all lifecycle-managed elements (have bw_lc marker class)
10850
+ var elements = element.querySelectorAll('.' + _BW_LC);
10772
10851
  elements.forEach(function (el) {
10773
- var id = el.getAttribute('data-bw_id');
10774
- var callback = bw._unmountCallbacks.get(id);
10775
- if (callback) {
10776
- callback();
10777
- bw._unmountCallbacks["delete"](id);
10778
- }
10779
-
10780
- // Deregister from node cache
10781
- bw._deregisterNode(el, id);
10852
+ var uuid = bw.getUUID(el);
10853
+ if (uuid) {
10854
+ var callback = bw._unmountCallbacks.get(uuid);
10855
+ if (callback) {
10856
+ callback();
10857
+ bw._unmountCallbacks["delete"](uuid);
10858
+ }
10782
10859
 
10783
- // Deregister UUID class from node cache
10784
- var uuidMatch = el.className && el.className.match(_UUID_RE);
10785
- if (uuidMatch) delete bw._nodeMap[uuidMatch[0]];
10860
+ // Deregister from node cache
10861
+ bw._deregisterNode(el, uuid);
10862
+ }
10786
10863
 
10787
10864
  // Clean up pub/sub subscriptions tied to this element
10788
10865
  if (el._bw_subs) {
@@ -10799,20 +10876,17 @@
10799
10876
  });
10800
10877
 
10801
10878
  // Check element itself
10802
- var id = element.getAttribute('data-bw_id');
10803
- if (id) {
10804
- var callback = bw._unmountCallbacks.get(id);
10879
+ var selfUuid = bw.getUUID(element);
10880
+ if (selfUuid) {
10881
+ delete bw._nodeMap[selfUuid];
10882
+ var callback = bw._unmountCallbacks.get(selfUuid);
10805
10883
  if (callback) {
10806
10884
  callback();
10807
- bw._unmountCallbacks["delete"](id);
10885
+ bw._unmountCallbacks["delete"](selfUuid);
10808
10886
  }
10809
10887
 
10810
10888
  // Deregister from node cache
10811
- bw._deregisterNode(element, id);
10812
-
10813
- // Deregister UUID class from node cache
10814
- var elemUuidMatch = element.className && element.className.match(_UUID_RE);
10815
- if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
10889
+ bw._deregisterNode(element, selfUuid);
10816
10890
 
10817
10891
  // Clean up pub/sub subscriptions tied to element itself
10818
10892
  if (element._bw_subs) {
@@ -10824,12 +10898,13 @@
10824
10898
  delete element._bw_state;
10825
10899
  delete element._bw_render;
10826
10900
  delete element._bw_refs;
10827
-
10828
- // Clean up ComponentHandle back-reference
10829
- if (element._bwComponentHandle) {
10830
- element._bwComponentHandle.mounted = false;
10831
- element._bwComponentHandle.element = null;
10832
- delete element._bwComponentHandle;
10901
+ } else {
10902
+ // No UUID on element itself, but still check for _bw_subs (from bw.sub())
10903
+ if (element._bw_subs) {
10904
+ element._bw_subs.forEach(function (unsub) {
10905
+ unsub();
10906
+ });
10907
+ delete element._bw_subs;
10833
10908
  }
10834
10909
  }
10835
10910
  };
@@ -10845,7 +10920,7 @@
10845
10920
  * Calls `el._bw_render(el, state)` and emits `bw:statechange` so other
10846
10921
  * components can react without tight coupling.
10847
10922
  *
10848
- * @param {string|Element} target - Element ID, data-bw_id, CSS selector, or DOM element
10923
+ * @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element
10849
10924
  * @returns {Element|null} The element, or null if not found / no render function
10850
10925
  * @category State Management
10851
10926
  * @see bw.patch
@@ -10870,7 +10945,7 @@
10870
10945
  * Use `bw.patch()` for lightweight value updates (scores, labels, counters)
10871
10946
  * and `bw.update()` for full structural re-renders.
10872
10947
  *
10873
- * @param {string|Element} id - Element ID, data-bw_id, CSS selector, or DOM element.
10948
+ * @param {string|Element} id - Element ID, bw_uuid_* class, CSS selector, or DOM element.
10874
10949
  * Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
10875
10950
  * @param {string|Object} content - New text content, or TACO object to replace children
10876
10951
  * @param {string} [attr] - If provided, sets this attribute instead of content
@@ -10944,7 +11019,7 @@
10944
11019
  * bubble by default so ancestor elements can listen. Use with `bw.on()` for
10945
11020
  * DOM-scoped communication between components.
10946
11021
  *
10947
- * @param {string|Element} target - Element ID, data-bw_id, CSS selector, or DOM element.
11022
+ * @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
10948
11023
  * Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
10949
11024
  * @param {string} eventName - Event name (will be prefixed with 'bw:')
10950
11025
  * @param {*} [detail] - Data to pass with the event
@@ -10971,7 +11046,7 @@
10971
11046
  * is the first argument so you don't need to destructure `e.detail`.
10972
11047
  * Events bubble, so you can listen on an ancestor element.
10973
11048
  *
10974
- * @param {string|Element} target - Element ID, data-bw_id, CSS selector, or DOM element.
11049
+ * @param {string|Element} target - Element ID, bw_uuid_* class, CSS selector, or DOM element.
10975
11050
  * Uses node cache for O(1) lookup; falls back to DOM query on cache miss.
10976
11051
  * @param {string} eventName - Event name (will be prefixed with 'bw:')
10977
11052
  * @param {Function} handler - Called with (detail, event)
@@ -11073,10 +11148,12 @@
11073
11148
  if (el) {
11074
11149
  if (!el._bw_subs) el._bw_subs = [];
11075
11150
  el._bw_subs.push(unsub);
11076
- // Ensure element has data-bw_id so bw.cleanup() finds it
11077
- if (!el.getAttribute('data-bw_id')) {
11078
- var bwId = 'bw_sub_' + id;
11079
- el.setAttribute('data-bw_id', bwId);
11151
+ // Ensure element has UUID + bw_lc so bw.cleanup() finds it
11152
+ if (!bw.getUUID(el)) {
11153
+ el.classList.add(bw.uuid('uuid'));
11154
+ }
11155
+ if (!el.classList.contains(_BW_LC)) {
11156
+ el.classList.add(_BW_LC);
11080
11157
  }
11081
11158
  }
11082
11159
  return unsub;
@@ -11299,1170 +11376,77 @@
11299
11376
  return result;
11300
11377
  };
11301
11378
 
11302
- /**
11303
- * Extract top-level state keys that an expression depends on.
11304
- * @param {string} expr - Expression string
11305
- * @param {string[]} stateKeys - Declared state keys
11306
- * @returns {string[]} Matching dependency keys
11307
- * @private
11308
- */
11309
- bw._extractDeps = function (expr, stateKeys) {
11310
- var deps = [];
11311
- for (var i = 0; i < stateKeys.length; i++) {
11312
- var key = stateKeys[i];
11313
- // Match word boundary: key must be preceded by start/non-word and followed by non-word/end
11314
- var re = new RegExp('(?:^|[^\\w$.])' + key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '(?:[^\\w$]|$)');
11315
- if (re.test(expr) || expr === key || expr.indexOf(key + '.') === 0) {
11316
- deps.push(key);
11317
- }
11318
- }
11319
- return deps;
11320
- };
11321
-
11322
11379
  // ===================================================================================
11323
- // Microtask Batching
11380
+ // Deprecation stubs for removed ComponentHandle APIs (v2.0.19)
11324
11381
  // ===================================================================================
11325
11382
 
11326
- bw._dirtyComponents = [];
11327
- bw._flushScheduled = false;
11383
+ bw._extractDeps = undefined;
11384
+ bw._dirtyComponents = undefined;
11385
+ bw._flushScheduled = undefined;
11386
+ bw._scheduleFlush = undefined;
11387
+ bw._doFlush = undefined;
11388
+ bw._ComponentHandle = undefined;
11328
11389
 
11329
11390
  /**
11330
- * Schedule a microtask flush for dirty components.
11331
- * @private
11391
+ * No-op flush (ComponentHandle removed in v2.0.19).
11392
+ * Kept as no-op for backward compatibility.
11393
+ * @category Component
11332
11394
  */
11333
- bw._scheduleFlush = function () {
11334
- if (bw._flushScheduled) return;
11335
- bw._flushScheduled = true;
11336
- if (typeof Promise !== 'undefined') {
11337
- Promise.resolve().then(bw._doFlush);
11338
- } else {
11339
- setTimeout(bw._doFlush, 0);
11340
- }
11395
+ bw.flush = function () {};
11396
+ bw.when = function () {
11397
+ throw new Error('bw.when() removed in v2.0.19. Use conditional logic in o.render instead.');
11341
11398
  };
11342
-
11343
- /**
11344
- * Flush all dirty components. Deduplicates by _bwId.
11345
- * @private
11346
- */
11347
- bw._doFlush = function () {
11348
- bw._flushScheduled = false;
11349
- var queue = bw._dirtyComponents.slice();
11350
- bw._dirtyComponents = [];
11351
- // Deduplicate by _bwId
11352
- var seen = {};
11353
- for (var i = 0; i < queue.length; i++) {
11354
- var comp = queue[i];
11355
- if (!seen[comp._bwId]) {
11356
- seen[comp._bwId] = true;
11357
- comp._flush();
11358
- }
11359
- }
11399
+ bw.each = function () {
11400
+ throw new Error('bw.each() removed in v2.0.19. Use array mapping in o.render instead.');
11401
+ };
11402
+ bw.component = function () {
11403
+ throw new Error('bw.component() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.');
11360
11404
  };
11361
11405
 
11406
+ // ===================================================================================
11407
+ // bw.message() — SendMessage() for the web
11408
+ // ===================================================================================
11409
+
11362
11410
  /**
11363
- * Synchronous flush for testing and imperative code.
11364
- * Forces immediate re-render of all dirty components.
11411
+ * Dispatch a message to a component by UUID, CSS class, or selector.
11412
+ * Finds the element, looks up el.bw, and calls the named method.
11413
+ * This is the bitwrench equivalent of Win32 SendMessage(hwnd, msg, wParam, lParam).
11365
11414
  *
11415
+ * @param {string} target - Component UUID (bw_uuid_*), CSS class, or selector
11416
+ * @param {string} action - Method name to call on el.bw
11417
+ * @param {*} data - Data to pass to the method
11418
+ * @returns {boolean} True if message was dispatched successfully
11366
11419
  * @category Component
11420
+ * @example
11421
+ * bw.message('my_carousel', 'goToSlide', 2);
11422
+ * // Or from SSE handler:
11423
+ * es.onmessage = function(e) {
11424
+ * var msg = JSON.parse(e.data);
11425
+ * bw.message(msg.target, msg.action, msg.data);
11426
+ * };
11367
11427
  */
11368
- bw.flush = function () {
11369
- bw._doFlush();
11428
+ bw.message = function (target, action, data) {
11429
+ var el = bw._el(target);
11430
+ if (!el) el = bw.$('.' + target)[0];
11431
+ if (!el || !el.bw || typeof el.bw[action] !== 'function') {
11432
+ _cw('bw.message: no handle method "' + action + '" on ' + target);
11433
+ return false;
11434
+ }
11435
+ el.bw[action](data);
11436
+ return true;
11370
11437
  };
11371
11438
 
11372
11439
  // ===================================================================================
11373
- // ComponentHandle unified reactive component (Phase 1)
11440
+ // bw.apply() / bw.parseJSONFlex() Server-driven UI protocol
11374
11441
  // ===================================================================================
11375
11442
 
11376
11443
  /**
11377
- * ComponentHandle constructor.
11378
- * Wraps a TACO definition with reactive state, lifecycle hooks,
11379
- * template bindings, and named actions.
11380
- *
11381
- * @param {Object} taco - TACO definition {t, a, c, o}
11382
- * @constructor
11444
+ * Registry of named functions sent via register messages.
11445
+ * Populated by bw.apply({ type: 'register', name, body }).
11446
+ * Invoked by bw.apply({ type: 'call', name, args }).
11383
11447
  * @private
11384
11448
  */
11385
- function ComponentHandle(taco) {
11386
- this._bwComponent = true; // duck-type marker
11387
- this._bwId = bw.uuid('comp');
11388
- this.taco = taco;
11389
- this.element = null;
11390
- this.mounted = false;
11391
- var o = taco.o || {};
11392
- // Copy initial state
11393
- this._state = {};
11394
- if (o.state) {
11395
- for (var k in o.state) {
11396
- if (_hop.call(o.state, k)) {
11397
- this._state[k] = o.state[k];
11398
- }
11399
- }
11400
- }
11401
- // Copy actions
11402
- this._actions = {};
11403
- if (o.actions) {
11404
- for (var k2 in o.actions) {
11405
- if (_hop.call(o.actions, k2)) {
11406
- this._actions[k2] = o.actions[k2];
11407
- }
11408
- }
11409
- }
11410
- // Promote o.methods to handle API (MFC/Qt pattern: component owns its methods)
11411
- this._methods = {};
11412
- if (o.methods) {
11413
- var self = this;
11414
- for (var k3 in o.methods) {
11415
- if (_hop.call(o.methods, k3)) {
11416
- this._methods[k3] = o.methods[k3];
11417
- (function (methodName, methodFn) {
11418
- self[methodName] = function () {
11419
- var args = [self].concat(Array.prototype.slice.call(arguments));
11420
- return methodFn.apply(null, args);
11421
- };
11422
- })(k3, o.methods[k3]);
11423
- }
11424
- }
11425
- }
11426
- // User tag for addressing via bw.message()
11427
- this._userTag = null;
11428
- // Lifecycle hooks
11429
- this._hooks = {
11430
- willMount: o.willMount || null,
11431
- mounted: o.mounted || null,
11432
- willUpdate: o.willUpdate || null,
11433
- onUpdate: o.onUpdate || o.updated || null,
11434
- unmount: o.unmount || null,
11435
- willDestroy: o.willDestroy || null
11436
- };
11437
- // Binding tracking
11438
- this._bindings = [];
11439
- this._dirtyKeys = {};
11440
- this._scheduled = false;
11441
- this._subs = [];
11442
- this._eventListeners = [];
11443
- this._registeredActions = [];
11444
- this._prevValues = {};
11445
- this._compile = !!o.compile;
11446
- this._bw_refs = {};
11447
- this._refCounter = 0;
11448
- // Child component ownership (Bug #5)
11449
- this._children = [];
11450
- this._parent = null;
11451
- // Factory metadata for BCCL rebuild (Bug #6)
11452
- this._factory = taco._bwFactory || null;
11453
- }
11454
-
11455
- // Short alias for ComponentHandle.prototype (see alias block at top of file).
11456
- // 28 method definitions × 25 chars = ~700B raw savings in minified output.
11457
- var _chp = ComponentHandle.prototype;
11458
-
11459
- // ── State Methods ──
11460
-
11461
- /**
11462
- * Get a state value. Dot-path supported: `get('user.name')`
11463
- */
11464
- _chp.get = function (key) {
11465
- return bw._evaluatePath(this._state, key);
11466
- };
11467
-
11468
- /**
11469
- * Set a state value. Dot-path supported. Schedules re-render.
11470
- * @param {string} key - State key (dot-path)
11471
- * @param {*} value - New value
11472
- * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
11473
- */
11474
- _chp.set = function (key, value, opts) {
11475
- // Dot-path set
11476
- var parts = key.split('.');
11477
- var obj = this._state;
11478
- for (var i = 0; i < parts.length - 1; i++) {
11479
- if (!_is(obj[parts[i]], 'object')) {
11480
- if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
11481
- obj[parts[i]] = {};
11482
- }
11483
- obj = obj[parts[i]];
11484
- }
11485
- obj[parts[parts.length - 1]] = value;
11486
- // Mark top-level key dirty
11487
- this._dirtyKeys[parts[0]] = true;
11488
- if (this.mounted) {
11489
- if (opts && opts.sync) {
11490
- this._flush();
11491
- } else {
11492
- this._scheduleDirty();
11493
- }
11494
- }
11495
- };
11496
-
11497
- /**
11498
- * Get a shallow clone of the full state.
11499
- */
11500
- _chp.getState = function () {
11501
- var clone = {};
11502
- for (var k in this._state) {
11503
- if (_hop.call(this._state, k)) {
11504
- clone[k] = this._state[k];
11505
- }
11506
- }
11507
- return clone;
11508
- };
11509
-
11510
- /**
11511
- * Merge multiple state keys. Schedules re-render.
11512
- * @param {Object} updates - Key-value pairs to merge
11513
- * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
11514
- */
11515
- _chp.setState = function (updates, opts) {
11516
- for (var k in updates) {
11517
- if (_hop.call(updates, k)) {
11518
- this._state[k] = updates[k];
11519
- this._dirtyKeys[k] = true;
11520
- }
11521
- }
11522
- if (this.mounted) {
11523
- if (opts && opts.sync) {
11524
- this._flush();
11525
- } else {
11526
- this._scheduleDirty();
11527
- }
11528
- }
11529
- };
11530
-
11531
- /**
11532
- * Push a value onto an array in state. Clones the array.
11533
- */
11534
- _chp.push = function (key, val) {
11535
- var arr = this.get(key);
11536
- var newArr = _isA(arr) ? arr.slice() : [];
11537
- newArr.push(val);
11538
- this.set(key, newArr);
11539
- };
11540
-
11541
- /**
11542
- * Splice an array in state. Clones the array.
11543
- */
11544
- _chp.splice = function (key, start, deleteCount) {
11545
- var arr = this.get(key);
11546
- var newArr = _isA(arr) ? arr.slice() : [];
11547
- var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
11548
- Array.prototype.splice.apply(newArr, args);
11549
- this.set(key, newArr);
11550
- };
11551
-
11552
- // ── Scheduling ──
11553
-
11554
- _chp._scheduleDirty = function () {
11555
- if (!this._scheduled) {
11556
- this._scheduled = true;
11557
- bw._dirtyComponents.push(this);
11558
- bw._scheduleFlush();
11559
- }
11560
- };
11561
-
11562
- // ── Binding Compilation ──
11563
-
11564
- /**
11565
- * Walk the TACO tree and extract ${expr} bindings.
11566
- * Creates binding descriptors with refIds for targeted DOM updates.
11567
- * @private
11568
- */
11569
- _chp._compileBindings = function () {
11570
- this._bindings = [];
11571
- this._refCounter = 0;
11572
- var stateKeys = _keys(this._state);
11573
- var self = this;
11574
- function walkTaco(taco, path) {
11575
- if (!_is(taco, 'object') || !taco.t) return taco;
11576
-
11577
- // Check content for bindings
11578
- if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
11579
- var refId = 'bw_ref_' + self._refCounter++;
11580
- var parsed = bw._parseBindings(taco.c);
11581
- var deps = [];
11582
- for (var j = 0; j < parsed.length; j++) {
11583
- deps = deps.concat(bw._extractDeps(parsed[j].expr, stateKeys));
11584
- }
11585
- self._bindings.push({
11586
- expr: taco.c,
11587
- type: 'content',
11588
- refId: refId,
11589
- deps: deps,
11590
- template: taco.c
11591
- });
11592
- // Inject data-bw_ref on the TACO for createDOM to pick up
11593
- if (!taco.a) taco.a = {};
11594
- taco.a['data-bw_ref'] = refId;
11595
- }
11596
-
11597
- // Check attributes for bindings
11598
- if (taco.a) {
11599
- for (var attrName in taco.a) {
11600
- if (!_hop.call(taco.a, attrName)) continue;
11601
- if (attrName === 'data-bw_ref') continue;
11602
- var attrVal = taco.a[attrName];
11603
- if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
11604
- var refId2 = 'bw_ref_' + self._refCounter++;
11605
- var parsed2 = bw._parseBindings(attrVal);
11606
- var deps2 = [];
11607
- for (var j2 = 0; j2 < parsed2.length; j2++) {
11608
- deps2 = deps2.concat(bw._extractDeps(parsed2[j2].expr, stateKeys));
11609
- }
11610
- self._bindings.push({
11611
- expr: attrVal,
11612
- type: 'attribute',
11613
- attrName: attrName,
11614
- refId: refId2,
11615
- deps: deps2,
11616
- template: attrVal
11617
- });
11618
- if (!taco.a) taco.a = {};
11619
- taco.a['data-bw_ref'] = taco.a['data-bw_ref'] || refId2;
11620
- // If multiple attribute bindings on same element, store additional marker
11621
- if (taco.a['data-bw_ref'] !== refId2) {
11622
- taco.a['data-bw_ref_' + attrName] = refId2;
11623
- }
11624
- }
11625
- }
11626
- }
11627
-
11628
- // Recurse into children
11629
- if (_isA(taco.c)) {
11630
- for (var i = 0; i < taco.c.length; i++) {
11631
- // Wrap string children with ${expr} in a span so patches target the span, not the parent
11632
- if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
11633
- var mixedRefId = 'bw_ref_' + self._refCounter++;
11634
- var mixedParsed = bw._parseBindings(taco.c[i]);
11635
- var mixedDeps = [];
11636
- for (var mi = 0; mi < mixedParsed.length; mi++) {
11637
- mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
11638
- }
11639
- self._bindings.push({
11640
- expr: taco.c[i],
11641
- type: 'content',
11642
- refId: mixedRefId,
11643
- deps: mixedDeps,
11644
- template: taco.c[i]
11645
- });
11646
- // Replace string with a span wrapper so textContent targets the span only
11647
- taco.c[i] = {
11648
- t: 'span',
11649
- a: {
11650
- 'data-bw_ref': mixedRefId,
11651
- style: 'display:contents'
11652
- },
11653
- c: taco.c[i]
11654
- };
11655
- }
11656
- if (_is(taco.c[i], 'object') && taco.c[i].t) {
11657
- walkTaco(taco.c[i], path.concat(i));
11658
- }
11659
- // Handle bw.when/bw.each markers
11660
- if (taco.c[i] && taco.c[i]._bwWhen) {
11661
- var whenRefId = 'bw_ref_' + self._refCounter++;
11662
- var whenDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
11663
- self._bindings.push({
11664
- expr: taco.c[i].expr,
11665
- type: 'structural',
11666
- subtype: 'when',
11667
- refId: whenRefId,
11668
- deps: whenDeps,
11669
- branches: taco.c[i].branches,
11670
- index: i,
11671
- parentPath: path
11672
- });
11673
- taco.c[i]._refId = whenRefId;
11674
- }
11675
- if (taco.c[i] && taco.c[i]._bwEach) {
11676
- var eachRefId = 'bw_ref_' + self._refCounter++;
11677
- var eachDeps = bw._extractDeps(taco.c[i].expr.replace(/^\$\{|\}$/g, ''), stateKeys);
11678
- self._bindings.push({
11679
- expr: taco.c[i].expr,
11680
- type: 'structural',
11681
- subtype: 'each',
11682
- refId: eachRefId,
11683
- deps: eachDeps,
11684
- factory: taco.c[i].factory,
11685
- index: i,
11686
- parentPath: path
11687
- });
11688
- taco.c[i]._refId = eachRefId;
11689
- }
11690
- }
11691
- } else if (_is(taco.c, 'object') && taco.c.t) {
11692
- walkTaco(taco.c, path.concat(0));
11693
- }
11694
- return taco;
11695
- }
11696
- walkTaco(this.taco, []);
11697
- };
11698
-
11699
- // ── DOM Reference Collection ──
11700
-
11701
- /**
11702
- * Build ref map from the live DOM after createDOM.
11703
- * @private
11704
- */
11705
- _chp._collectRefs = function () {
11706
- this._bw_refs = {};
11707
- if (!this.element) return;
11708
- var els = this.element.querySelectorAll('[data-bw_ref]');
11709
- for (var i = 0; i < els.length; i++) {
11710
- this._bw_refs[els[i].getAttribute('data-bw_ref')] = els[i];
11711
- }
11712
- // Also check root element
11713
- var rootRef = this.element.getAttribute && this.element.getAttribute('data-bw_ref');
11714
- if (rootRef) {
11715
- this._bw_refs[rootRef] = this.element;
11716
- }
11717
- };
11718
-
11719
- // ── Lifecycle ──
11720
-
11721
- /**
11722
- * Mount the component into a parent DOM element.
11723
- * Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
11724
- * @param {Element} parentEl - DOM element to mount into
11725
- */
11726
- _chp.mount = function (parentEl) {
11727
- // willMount hook
11728
- if (this._hooks.willMount) this._hooks.willMount(this);
11729
-
11730
- // Save original TACO for re-renders (structural changes clone from this)
11731
- if (!this._originalTaco) {
11732
- this._originalTaco = this.taco;
11733
- }
11734
-
11735
- // Deep-clone TACO so binding annotations don't mutate original.
11736
- // Custom clone to preserve _bwWhen/_bwEach markers and their factory functions.
11737
- this.taco = this._deepCloneTaco(this._originalTaco);
11738
-
11739
- // Compile bindings (annotates TACO with data-bw_ref attributes)
11740
- this._compileBindings();
11741
-
11742
- // Prepare TACO: resolve initial binding values, evaluate when/each
11743
- this._prepareTaco(this.taco);
11744
-
11745
- // Register named actions in function registry
11746
- var self = this;
11747
- for (var actionName in this._actions) {
11748
- if (_hop.call(this._actions, actionName)) {
11749
- var registeredName = this._bwId + '_' + actionName;
11750
- (function (aName) {
11751
- bw.funcRegister(function (evt) {
11752
- self._actions[aName](self, evt);
11753
- }, registeredName);
11754
- })(actionName);
11755
- this._registeredActions.push(registeredName);
11756
- }
11757
- }
11758
-
11759
- // Wire action names in onclick etc. to dispatch strings
11760
- this._wireActions(this.taco);
11761
-
11762
- // Create DOM (strip o before createDOM to prevent double lifecycle)
11763
- var tacoForDOM = this._tacoForDOM(this.taco);
11764
- this.element = bw.createDOM(tacoForDOM);
11765
- this.element._bwComponentHandle = this;
11766
- this.element.setAttribute('data-bw_comp_id', this._bwId);
11767
-
11768
- // Restore o.render from original TACO (stripped by _tacoForDOM)
11769
- if (this.taco.o && this.taco.o.render) {
11770
- this.element._bw_render = this.taco.o.render;
11771
- }
11772
- if (this._userTag) {
11773
- this.element.classList.add(this._userTag);
11774
- }
11775
-
11776
- // Append to parent
11777
- parentEl.appendChild(this.element);
11778
-
11779
- // Collect refs from live DOM
11780
- this._collectRefs();
11781
-
11782
- // Resolve initial bindings and apply to DOM
11783
- this._resolveAndApplyAll();
11784
- this.mounted = true;
11785
-
11786
- // Scan for child ComponentHandles and link parent/child (Bug #5)
11787
- var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
11788
- for (var ci = 0; ci < childEls.length; ci++) {
11789
- var ch = childEls[ci]._bwComponentHandle;
11790
- if (ch && ch !== this && !ch._parent) {
11791
- ch._parent = this;
11792
- this._children.push(ch);
11793
- }
11794
- }
11795
-
11796
- // mounted hook (backward compat: fn.length === 2 wraps (el, state))
11797
- if (this._hooks.mounted) {
11798
- if (this._hooks.mounted.length === 2) {
11799
- this._hooks.mounted(this.element, this.getState());
11800
- } else {
11801
- this._hooks.mounted(this);
11802
- }
11803
- }
11804
-
11805
- // Invoke o.render on initial mount (if present)
11806
- if (this.element._bw_render) {
11807
- this.element._bw_render(this.element, this._state);
11808
- }
11809
- };
11810
-
11811
- /**
11812
- * Prepare TACO for initial render: resolve when/each markers.
11813
- * @private
11814
- */
11815
- _chp._prepareTaco = function (taco) {
11816
- if (!_is(taco, 'object')) return;
11817
- if (_isA(taco.c)) {
11818
- for (var i = taco.c.length - 1; i >= 0; i--) {
11819
- var child = taco.c[i];
11820
- if (child && child._bwWhen) {
11821
- var exprStr = child.expr.replace(/^\$\{|\}$/g, '');
11822
- var val;
11823
- if (this._compile) {
11824
- try {
11825
- val = new Function('state', 'with(state){return (' + exprStr + ');}')(this._state);
11826
- } catch (e) {
11827
- val = false;
11828
- }
11829
- } else {
11830
- val = bw._evaluatePath(this._state, exprStr);
11831
- }
11832
- var branch = val ? child.branches[0] : child.branches[1] || null;
11833
- if (branch) {
11834
- // Wrap in a container so we can track it
11835
- taco.c[i] = {
11836
- t: 'span',
11837
- a: {
11838
- 'data-bw_when': child._refId,
11839
- style: 'display:contents'
11840
- },
11841
- c: branch
11842
- };
11843
- } else {
11844
- taco.c[i] = {
11845
- t: 'span',
11846
- a: {
11847
- 'data-bw_when': child._refId,
11848
- style: 'display:contents'
11849
- },
11850
- c: ''
11851
- };
11852
- }
11853
- }
11854
- if (child && child._bwEach) {
11855
- var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
11856
- var arr = bw._evaluatePath(this._state, eachExprStr);
11857
- var items = [];
11858
- if (_isA(arr)) {
11859
- for (var j = 0; j < arr.length; j++) {
11860
- items.push(child.factory(arr[j], j));
11861
- }
11862
- }
11863
- taco.c[i] = {
11864
- t: 'span',
11865
- a: {
11866
- 'data-bw_each': child._refId,
11867
- style: 'display:contents'
11868
- },
11869
- c: items
11870
- };
11871
- }
11872
- if (_is(taco.c[i], 'object') && taco.c[i].t) {
11873
- this._prepareTaco(taco.c[i]);
11874
- }
11875
- }
11876
- } else if (_is(taco.c, 'object') && taco.c.t) {
11877
- this._prepareTaco(taco.c);
11878
- }
11879
- };
11880
-
11881
- /**
11882
- * Wire action name strings (in onclick etc.) to dispatch function calls.
11883
- * @private
11884
- */
11885
- _chp._wireActions = function (taco) {
11886
- if (!_is(taco, 'object') || !taco.t) return;
11887
- if (taco.a) {
11888
- for (var key in taco.a) {
11889
- if (!_hop.call(taco.a, key)) continue;
11890
- if (key.startsWith('on') && _is(taco.a[key], 'string')) {
11891
- var actionName = taco.a[key];
11892
- if (actionName in this._actions) {
11893
- var registeredName = this._bwId + '_' + actionName;
11894
- // Replace string with actual function for createDOM event binding
11895
- (function (rName) {
11896
- taco.a[key] = function (evt) {
11897
- bw.funcGetById(rName)(evt);
11898
- };
11899
- })(registeredName);
11900
- }
11901
- }
11902
- }
11903
- }
11904
- if (_isA(taco.c)) {
11905
- for (var i = 0; i < taco.c.length; i++) {
11906
- this._wireActions(taco.c[i]);
11907
- }
11908
- } else if (_is(taco.c, 'object') && taco.c.t) {
11909
- this._wireActions(taco.c);
11910
- }
11911
- };
11912
-
11913
- /**
11914
- * Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
11915
- * @private
11916
- */
11917
- _chp._deepCloneTaco = function (taco) {
11918
- if (taco == null) return taco;
11919
- // Preserve _bwWhen / _bwEach markers (contain functions)
11920
- if (taco._bwWhen) {
11921
- return {
11922
- _bwWhen: true,
11923
- expr: taco.expr,
11924
- branches: [this._deepCloneTaco(taco.branches[0]), taco.branches[1] ? this._deepCloneTaco(taco.branches[1]) : null],
11925
- _refId: taco._refId
11926
- };
11927
- }
11928
- if (taco._bwEach) {
11929
- return {
11930
- _bwEach: true,
11931
- expr: taco.expr,
11932
- factory: taco.factory,
11933
- _refId: taco._refId
11934
- };
11935
- }
11936
- if (!_is(taco, 'object') || !taco.t) return taco;
11937
- var result = {
11938
- t: taco.t
11939
- };
11940
- if (taco.a) {
11941
- result.a = {};
11942
- for (var k in taco.a) {
11943
- if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
11944
- }
11945
- }
11946
- if (taco.c != null) {
11947
- if (_isA(taco.c)) {
11948
- result.c = taco.c.map(function (child) {
11949
- return this._deepCloneTaco(child);
11950
- }.bind(this));
11951
- } else if (_is(taco.c, 'object')) {
11952
- result.c = this._deepCloneTaco(taco.c);
11953
- } else {
11954
- result.c = taco.c;
11955
- }
11956
- }
11957
- if (taco.o) result.o = taco.o; // Keep o reference (not deep-cloned; hooks are functions)
11958
- return result;
11959
- };
11960
-
11961
- /**
11962
- * Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
11963
- * @private
11964
- */
11965
- _chp._tacoForDOM = function (taco) {
11966
- if (!_is(taco, 'object') || !taco.t) return taco;
11967
- var result = {
11968
- t: taco.t
11969
- };
11970
- if (taco.a) result.a = taco.a;
11971
- if (taco.c != null) {
11972
- if (_isA(taco.c)) {
11973
- result.c = taco.c.map(function (child) {
11974
- return this._tacoForDOM(child);
11975
- }.bind(this));
11976
- } else if (_is(taco.c, 'object') && taco.c.t) {
11977
- result.c = this._tacoForDOM(taco.c);
11978
- } else {
11979
- result.c = taco.c;
11980
- }
11981
- }
11982
- // Intentionally strip o (no mounted/unmount/state/render on sub-elements)
11983
- if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
11984
- _cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t + '>. Use onclick attribute or bw.component() for child interactivity.');
11985
- }
11986
- return result;
11987
- };
11988
-
11989
- /**
11990
- * Unmount: remove from DOM, deactivate, preserve state for re-mount.
11991
- */
11992
- _chp.unmount = function () {
11993
- if (!this.mounted) return;
11994
-
11995
- // unmount hook
11996
- if (this._hooks.unmount) {
11997
- this._hooks.unmount(this);
11998
- }
11999
-
12000
- // Remove DOM event listeners
12001
- for (var i = 0; i < this._eventListeners.length; i++) {
12002
- var l = this._eventListeners[i];
12003
- if (this.element) {
12004
- this.element.removeEventListener(l.event, l.handler);
12005
- }
12006
- }
12007
- this._eventListeners = [];
12008
-
12009
- // Unsubscribe pub/sub
12010
- for (var j = 0; j < this._subs.length; j++) {
12011
- this._subs[j]();
12012
- }
12013
- this._subs = [];
12014
-
12015
- // Remove from DOM
12016
- if (this.element && this.element.parentNode) {
12017
- this.element.parentNode.removeChild(this.element);
12018
- }
12019
- this.mounted = false;
12020
- // State preserved — can re-mount
12021
- };
12022
-
12023
- /**
12024
- * Destroy: unmount + clear state + unregister actions.
12025
- */
12026
- _chp.destroy = function () {
12027
- // willDestroy hook
12028
- if (this._hooks.willDestroy) {
12029
- this._hooks.willDestroy(this);
12030
- }
12031
-
12032
- // Cascade destroy to children depth-first (Bug #5)
12033
- for (var ci = this._children.length - 1; ci >= 0; ci--) {
12034
- this._children[ci].destroy();
12035
- }
12036
- this._children = [];
12037
- if (this._parent) {
12038
- var idx = this._parent._children.indexOf(this);
12039
- if (idx >= 0) this._parent._children.splice(idx, 1);
12040
- this._parent = null;
12041
- }
12042
- this.unmount();
12043
-
12044
- // Unregister actions from function registry
12045
- for (var i = 0; i < this._registeredActions.length; i++) {
12046
- bw.funcUnregister(this._registeredActions[i]);
12047
- }
12048
- this._registeredActions = [];
12049
-
12050
- // Clear state
12051
- this._state = {};
12052
- this._bindings = [];
12053
- this._bw_refs = {};
12054
- this._prevValues = {};
12055
- this._dirtyKeys = {};
12056
- if (this.element) {
12057
- delete this.element._bwComponentHandle;
12058
- this.element = null;
12059
- }
12060
- };
12061
-
12062
- // ── Flush & Binding Resolution ──
12063
-
12064
- /**
12065
- * Flush dirty state: resolve changed bindings and apply to DOM.
12066
- * @private
12067
- */
12068
- _chp._flush = function () {
12069
- this._scheduled = false;
12070
- var changedKeys = _keys(this._dirtyKeys);
12071
- this._dirtyKeys = {};
12072
- if (changedKeys.length === 0 || !this.mounted) return;
12073
-
12074
- // Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
12075
- // rebuild the TACO from the factory with merged state (Bug #6)
12076
- if (this._factory) {
12077
- var rebuildNeeded = false;
12078
- for (var fi = 0; fi < changedKeys.length; fi++) {
12079
- if (_hop.call(this._factory.props, changedKeys[fi])) {
12080
- rebuildNeeded = true;
12081
- break;
12082
- }
12083
- }
12084
- if (rebuildNeeded) {
12085
- var merged = {};
12086
- for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
12087
- for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
12088
- this._factory.props = merged;
12089
- var newTaco = bw.make(this._factory.type, merged);
12090
- newTaco._bwFactory = this._factory;
12091
- this.taco = newTaco;
12092
- this._originalTaco = this._deepCloneTaco(newTaco);
12093
- this._render();
12094
- if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
12095
- return;
12096
- }
12097
- }
12098
-
12099
- // willUpdate hook
12100
- if (this._hooks.willUpdate) {
12101
- this._hooks.willUpdate(this, changedKeys);
12102
- }
12103
-
12104
- // Check if any structural bindings are affected
12105
- var needsFullRender = false;
12106
- for (var i = 0; i < this._bindings.length; i++) {
12107
- var b = this._bindings[i];
12108
- if (b.type === 'structural') {
12109
- for (var j = 0; j < b.deps.length; j++) {
12110
- if (changedKeys.indexOf(b.deps[j]) >= 0) {
12111
- needsFullRender = true;
12112
- break;
12113
- }
12114
- }
12115
- if (needsFullRender) break;
12116
- }
12117
- }
12118
- if (needsFullRender) {
12119
- this._render();
12120
- } else {
12121
- var patches = this._resolveBindings(changedKeys);
12122
- this._applyPatches(patches);
12123
- }
12124
-
12125
- // onUpdate hook
12126
- if (this._hooks.onUpdate) {
12127
- this._hooks.onUpdate(this, changedKeys);
12128
- }
12129
- };
12130
-
12131
- /**
12132
- * Resolve bindings whose deps intersect with changedKeys.
12133
- * Returns list of patches to apply.
12134
- * @private
12135
- */
12136
- _chp._resolveBindings = function (changedKeys) {
12137
- var patches = [];
12138
- for (var i = 0; i < this._bindings.length; i++) {
12139
- var b = this._bindings[i];
12140
- if (b.type === 'structural') continue;
12141
-
12142
- // Check if any dep matches
12143
- var affected = false;
12144
- for (var j = 0; j < b.deps.length; j++) {
12145
- if (changedKeys.indexOf(b.deps[j]) >= 0) {
12146
- affected = true;
12147
- break;
12148
- }
12149
- }
12150
- if (!affected) continue;
12151
-
12152
- // Evaluate
12153
- var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
12154
- var prevKey = b.refId + '_' + (b.attrName || 'content');
12155
- if (this._prevValues[prevKey] !== newVal) {
12156
- this._prevValues[prevKey] = newVal;
12157
- patches.push({
12158
- refId: b.refId,
12159
- type: b.type,
12160
- attrName: b.attrName,
12161
- value: newVal
12162
- });
12163
- }
12164
- }
12165
- return patches;
12166
- };
12167
-
12168
- /**
12169
- * Apply patches to DOM.
12170
- * @private
12171
- */
12172
- _chp._applyPatches = function (patches) {
12173
- for (var i = 0; i < patches.length; i++) {
12174
- var p = patches[i];
12175
- var el = this._bw_refs[p.refId];
12176
- if (!el) {
12177
- if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
12178
- continue;
12179
- }
12180
- if (p.type === 'content') {
12181
- el.textContent = p.value;
12182
- } else if (p.type === 'attribute') {
12183
- if (p.attrName === 'class') {
12184
- el.className = p.value;
12185
- } else {
12186
- el.setAttribute(p.attrName, p.value);
12187
- }
12188
- }
12189
- }
12190
- };
12191
-
12192
- /**
12193
- * Resolve all bindings and apply (used for initial render).
12194
- * @private
12195
- */
12196
- _chp._resolveAndApplyAll = function () {
12197
- var patches = [];
12198
- for (var i = 0; i < this._bindings.length; i++) {
12199
- var b = this._bindings[i];
12200
- if (b.type === 'structural') continue;
12201
- var newVal = bw._resolveTemplate(b.template, this._state, this._compile);
12202
- var prevKey = b.refId + '_' + (b.attrName || 'content');
12203
- this._prevValues[prevKey] = newVal;
12204
- patches.push({
12205
- refId: b.refId,
12206
- type: b.type,
12207
- attrName: b.attrName,
12208
- value: newVal
12209
- });
12210
- }
12211
- this._applyPatches(patches);
12212
- };
12213
-
12214
- /**
12215
- * Full re-render for structural changes (when/each branch switches).
12216
- * @private
12217
- */
12218
- _chp._render = function () {
12219
- if (!this.element || !this.element.parentNode) return;
12220
- var parent = this.element.parentNode;
12221
- var nextSibling = this.element.nextSibling;
12222
-
12223
- // Remove old DOM
12224
- parent.removeChild(this.element);
12225
-
12226
- // Re-prepare TACO with current state (deep clone preserving functions)
12227
- this.taco = this._deepCloneTaco(this._originalTaco || this.taco);
12228
-
12229
- // Re-compile bindings and prepare
12230
- this._compileBindings();
12231
- this._prepareTaco(this.taco);
12232
- this._wireActions(this.taco);
12233
- var tacoForDOM = this._tacoForDOM(this.taco);
12234
- this.element = bw.createDOM(tacoForDOM);
12235
- this.element._bwComponentHandle = this;
12236
- this.element.setAttribute('data-bw_comp_id', this._bwId);
12237
-
12238
- // Re-insert at same position
12239
- if (nextSibling) {
12240
- parent.insertBefore(this.element, nextSibling);
12241
- } else {
12242
- parent.appendChild(this.element);
12243
- }
12244
-
12245
- // Re-collect refs and apply all bindings
12246
- this._collectRefs();
12247
- this._resolveAndApplyAll();
12248
- };
12249
-
12250
- // ── Event & Pub/Sub Methods ──
12251
-
12252
- /**
12253
- * Add a DOM event listener on the component's root element.
12254
- * @param {string} event - Event name (e.g., 'click')
12255
- * @param {Function} handler - Event handler
12256
- */
12257
- _chp.on = function (event, handler) {
12258
- if (this.element) {
12259
- this.element.addEventListener(event, handler);
12260
- }
12261
- this._eventListeners.push({
12262
- event: event,
12263
- handler: handler
12264
- });
12265
- };
12266
-
12267
- /**
12268
- * Remove a DOM event listener.
12269
- * @param {string} event - Event name
12270
- * @param {Function} handler - Handler to remove
12271
- */
12272
- _chp.off = function (event, handler) {
12273
- if (this.element) {
12274
- this.element.removeEventListener(event, handler);
12275
- }
12276
- this._eventListeners = this._eventListeners.filter(function (l) {
12277
- return !(l.event === event && l.handler === handler);
12278
- });
12279
- };
12280
-
12281
- /**
12282
- * Subscribe to a pub/sub topic. Lifecycle-tied: auto-unsubs on destroy.
12283
- * @param {string} topic - Topic name
12284
- * @param {Function} handler - Handler function
12285
- * @returns {Function} Unsubscribe function
12286
- */
12287
- _chp.sub = function (topic, handler) {
12288
- var unsub = bw.sub(topic, handler);
12289
- this._subs.push(unsub);
12290
- return unsub;
12291
- };
12292
-
12293
- /**
12294
- * Call a named action.
12295
- * @param {string} name - Action name
12296
- * @param {...*} args - Arguments passed after comp
12297
- */
12298
- _chp.action = function (name) {
12299
- var fn = this._actions[name];
12300
- if (!fn) {
12301
- _cw('ComponentHandle.action: unknown action "' + name + '"');
12302
- return;
12303
- }
12304
- var args = [this].concat(Array.prototype.slice.call(arguments, 1));
12305
- return fn.apply(null, args);
12306
- };
12307
-
12308
- /**
12309
- * querySelector within the component's DOM.
12310
- * @param {string} sel - CSS selector
12311
- * @returns {Element|null}
12312
- */
12313
- _chp.select = function (sel) {
12314
- return this.element ? this.element.querySelector(sel) : null;
12315
- };
12316
-
12317
- /**
12318
- * querySelectorAll within the component's DOM.
12319
- * @param {string} sel - CSS selector
12320
- * @returns {Element[]}
12321
- */
12322
- _chp.selectAll = function (sel) {
12323
- if (!this.element) return [];
12324
- return Array.prototype.slice.call(this.element.querySelectorAll(sel));
12325
- };
12326
-
12327
- /**
12328
- * Tag this component with a user-defined ID for addressing via bw.message().
12329
- * The tag is added as a CSS class on the root element (DOM IS the registry).
12330
- * @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
12331
- * @returns {ComponentHandle} this (for chaining)
12332
- */
12333
- _chp.userTag = function (tag) {
12334
- this._userTag = tag;
12335
- if (this.element) {
12336
- this.element.classList.add(tag);
12337
- }
12338
- return this;
12339
- };
12340
-
12341
- // Expose ComponentHandle on bw (for testing and advanced use)
12342
- bw._ComponentHandle = ComponentHandle;
12343
-
12344
- // ===================================================================================
12345
- // Control Flow Helpers
12346
- // ===================================================================================
12347
-
12348
- /**
12349
- * Conditional rendering helper.
12350
- * Returns a marker object that ComponentHandle detects during binding compilation.
12351
- * In static contexts (bw.html with state), evaluates immediately.
12352
- *
12353
- * @param {string} expr - Expression string like '${loggedIn}'
12354
- * @param {Object} tacoTrue - TACO to render when truthy
12355
- * @param {Object} [tacoFalse] - TACO to render when falsy
12356
- * @returns {Object} Marker object with _bwWhen flag
12357
- * @category Component
12358
- */
12359
- bw.when = function (expr, tacoTrue, tacoFalse) {
12360
- return {
12361
- _bwWhen: true,
12362
- expr: expr,
12363
- branches: [tacoTrue, tacoFalse || null]
12364
- };
12365
- };
12366
-
12367
- /**
12368
- * List rendering helper.
12369
- * Returns a marker object that ComponentHandle detects during binding compilation.
12370
- *
12371
- * @param {string} expr - Expression string like '${items}'
12372
- * @param {Function} fn - Factory function(item, index) returning TACO
12373
- * @returns {Object} Marker object with _bwEach flag
12374
- * @category Component
12375
- */
12376
- bw.each = function (expr, fn) {
12377
- return {
12378
- _bwEach: true,
12379
- expr: expr,
12380
- factory: fn
12381
- };
12382
- };
12383
-
12384
- // ===================================================================================
12385
- // bw.component() — Factory for ComponentHandle
12386
- // ===================================================================================
12387
-
12388
- /**
12389
- * Create a ComponentHandle from a TACO definition.
12390
- * The returned handle has .get(), .set(), .mount(), .destroy(), etc.
12391
- *
12392
- * @param {Object} taco - TACO definition with {t, a, c, o}
12393
- * @returns {ComponentHandle} Reactive component handle
12394
- * @category Component
12395
- * @see bw.DOM
12396
- * @example
12397
- * var counter = bw.component({
12398
- * t: 'div', c: [{ t: 'h3', c: 'Count: ${count}' }],
12399
- * o: { state: { count: 0 } }
12400
- * });
12401
- * bw.DOM('#app', counter);
12402
- * counter.set('count', 42); // DOM auto-updates
12403
- */
12404
- bw.component = function (taco) {
12405
- return new ComponentHandle(taco);
12406
- };
12407
-
12408
- // ===================================================================================
12409
- // bw.message() — SendMessage() for the web
12410
- // ===================================================================================
12411
-
12412
- /**
12413
- * Dispatch a message to a component by UUID or user tag.
12414
- * Finds the component's DOM element, looks up its ComponentHandle,
12415
- * and calls the named method. This is the bitwrench equivalent of
12416
- * Win32 SendMessage(hwnd, msg, wParam, lParam).
12417
- *
12418
- * @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
12419
- * @param {string} action - Method name to call on the component
12420
- * @param {*} data - Data to pass to the method
12421
- * @returns {boolean} True if message was dispatched successfully
12422
- * @category Component
12423
- * @example
12424
- * // Tag a component
12425
- * myDash.userTag('dashboard_prod');
12426
- * // Dispatch locally
12427
- * bw.message('dashboard_prod', 'addAlert', { severity: 'warning', text: 'CPU spike' });
12428
- * // Or from SSE handler:
12429
- * es.onmessage = function(e) {
12430
- * var msg = JSON.parse(e.data);
12431
- * bw.message(msg.target, msg.action, msg.data);
12432
- * };
12433
- */
12434
- bw.message = function (target, action, data) {
12435
- // Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
12436
- var el = bw._el(target);
12437
- // Then try data-bw_comp_id attribute
12438
- if (!el || !el._bwComponentHandle) {
12439
- el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
12440
- }
12441
- // Then try CSS class (user tag)
12442
- if (!el || !el._bwComponentHandle) {
12443
- el = bw.$('.' + target)[0];
12444
- }
12445
- if (!el || !el._bwComponentHandle) return false;
12446
- var comp = el._bwComponentHandle;
12447
- if (!_is(comp[action], 'function')) {
12448
- _cw('bw.message: unknown action "' + action + '" on component ' + target);
12449
- return false;
12450
- }
12451
- comp[action](data);
12452
- return true;
12453
- };
12454
-
12455
- // ===================================================================================
12456
- // bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
12457
- // ===================================================================================
12458
-
12459
- /**
12460
- * Registry of named functions sent via register messages.
12461
- * Populated by bw.apply({ type: 'register', name, body }).
12462
- * Invoked by bw.apply({ type: 'call', name, args }).
12463
- * @private
12464
- */
12465
- bw._clientFunctions = {};
11449
+ bw._clientFunctions = {};
12466
11450
 
12467
11451
  /**
12468
11452
  * Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
@@ -12655,142 +11639,32 @@
12655
11639
  // ===================================================================================
12656
11640
 
12657
11641
  /**
12658
- * Inspect a component's state, bindings, methods, and metadata.
12659
- * Works with DOM elements, CSS selectors, or ComponentHandle objects.
12660
- * Returns the ComponentHandle for console chaining.
11642
+ * Inspect a DOM element's bitwrench state, handle methods, and metadata.
11643
+ * Works with DOM elements or CSS selectors.
12661
11644
  *
12662
- * @param {string|Element|ComponentHandle} target - Selector, element, or handle
12663
- * @returns {ComponentHandle|null} The component handle, or null if not found
11645
+ * @param {string|Element} target - Selector or DOM element
11646
+ * @returns {Element|null} The element, or null if not found
12664
11647
  * @category Component
12665
11648
  * @example
12666
- * // In browser console, click element in Elements panel then:
11649
+ * bw.inspect('#my-carousel');
12667
11650
  * bw.inspect($0);
12668
- * // Or by selector:
12669
- * var h = bw.inspect('#my-dashboard');
12670
- * h.set('count', 99); // chain from returned handle
12671
11651
  */
12672
11652
  bw.inspect = function (target) {
12673
- var el = target;
12674
- var comp;
12675
- if (target && target._bwComponent === true) {
12676
- el = target.element;
12677
- comp = target;
12678
- } else {
12679
- if (_is(target, 'string')) {
12680
- el = bw.$(target)[0];
12681
- }
12682
- if (!el) {
12683
- _cw('bw.inspect: element not found');
12684
- return null;
12685
- }
12686
- comp = el._bwComponentHandle;
12687
- }
12688
- if (!comp) {
12689
- _cl('bw.inspect: no ComponentHandle on this element');
12690
- _cl(' Tag:', el.tagName);
12691
- _cl(' Classes:', el.className);
12692
- _cl(' _bw_state:', el._bw_state || '(none)');
11653
+ var el = _is(target, 'string') ? bw.$(target)[0] : target;
11654
+ if (!el) {
11655
+ _cw('bw.inspect: element not found');
12693
11656
  return null;
12694
11657
  }
12695
- var deps = comp._bindings.reduce(function (s, b) {
12696
- return s.concat(b.deps || []);
12697
- }, []).filter(function (v, i, a) {
12698
- return a.indexOf(v) === i;
12699
- });
12700
- console.group('Component: ' + comp._bwId);
12701
- _cl('State:', comp._state);
12702
- _cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
12703
- _cl('Methods:', _keys(comp._methods));
12704
- _cl('Actions:', _keys(comp._actions));
12705
- _cl('User tag:', comp._userTag || '(none)');
12706
- _cl('Mounted:', comp.mounted);
12707
- _cl('Element:', comp.element);
11658
+ console.group('Element: ' + (bw.getUUID(el) || el.id || el.tagName));
11659
+ _cl('State:', el._bw_state || '(none)');
11660
+ _cl('Handle:', el.bw ? _keys(el.bw) : '(none)');
11661
+ _cl('Classes:', el.className);
11662
+ _cl('Refs:', el._bw_refs || '(none)');
12708
11663
  console.groupEnd();
12709
- return comp;
11664
+ return el;
12710
11665
  };
12711
-
12712
- // ===================================================================================
12713
- // bw.compile() — Pre-compile TACO into optimized factory
12714
- // ===================================================================================
12715
-
12716
- /**
12717
- * Pre-compile a TACO definition into a factory function.
12718
- * The factory produces ComponentHandles with pre-compiled binding evaluators.
12719
- *
12720
- * Phase 1: validates API surface. Template cloning optimization deferred.
12721
- *
12722
- * @param {Object} taco - TACO definition
12723
- * @returns {Function} Factory function(initialState?) → ComponentHandle
12724
- * @category Component
12725
- */
12726
- bw.compile = function (taco) {
12727
- // Pre-extract all binding expressions
12728
- var precompiled = [];
12729
- function walkExpressions(node) {
12730
- if (!_is(node, 'object')) return;
12731
- if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
12732
- var parsed = bw._parseBindings(node.c);
12733
- for (var i = 0; i < parsed.length; i++) {
12734
- try {
12735
- precompiled.push({
12736
- expr: parsed[i].expr,
12737
- fn: new Function('state', 'with(state){return (' + parsed[i].expr + ');}')
12738
- });
12739
- } catch (e) {
12740
- precompiled.push({
12741
- expr: parsed[i].expr,
12742
- fn: function fn() {
12743
- return '';
12744
- }
12745
- });
12746
- }
12747
- }
12748
- }
12749
- if (node.a) {
12750
- for (var key in node.a) {
12751
- if (_hop.call(node.a, key)) {
12752
- var v = node.a[key];
12753
- if (_is(v, 'string') && v.indexOf('${') >= 0) {
12754
- var parsed2 = bw._parseBindings(v);
12755
- for (var j = 0; j < parsed2.length; j++) {
12756
- try {
12757
- precompiled.push({
12758
- expr: parsed2[j].expr,
12759
- fn: new Function('state', 'with(state){return (' + parsed2[j].expr + ');}')
12760
- });
12761
- } catch (e2) {
12762
- precompiled.push({
12763
- expr: parsed2[j].expr,
12764
- fn: function fn() {
12765
- return '';
12766
- }
12767
- });
12768
- }
12769
- }
12770
- }
12771
- }
12772
- }
12773
- }
12774
- if (_isA(node.c)) {
12775
- for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
12776
- } else if (_is(node.c, 'object') && node.c.t) {
12777
- walkExpressions(node.c);
12778
- }
12779
- }
12780
- walkExpressions(taco);
12781
- return function (initialState) {
12782
- var handle = new ComponentHandle(taco);
12783
- handle._compile = true;
12784
- handle._precompiledBindings = precompiled;
12785
- if (initialState) {
12786
- for (var k in initialState) {
12787
- if (_hop.call(initialState, k)) {
12788
- handle._state[k] = initialState[k];
12789
- }
12790
- }
12791
- }
12792
- return handle;
12793
- };
11666
+ bw.compile = function () {
11667
+ throw new Error('bw.compile() removed in v2.0.19. Use o.handle/o.slots on TACO options instead.');
12794
11668
  };
12795
11669
 
12796
11670
  /**
@@ -14096,7 +12970,7 @@
14096
12970
  * handle.destroy();
14097
12971
  */
14098
12972
  bw.render = function (element, position, taco) {
14099
- var _taco$o4, _taco$o5, _taco$o6;
12973
+ var _taco$o3, _taco$o4, _taco$o5;
14100
12974
  // Get target element
14101
12975
  var targetEl = _is(element, 'string') ? document.querySelector(element) : element;
14102
12976
  if (!targetEl) {
@@ -14108,8 +12982,8 @@
14108
12982
  };
14109
12983
  }
14110
12984
 
14111
- // Generate unique ID if not provided
14112
- var componentId = ((_taco$o4 = taco.o) === null || _taco$o4 === void 0 ? void 0 : _taco$o4.id) || bw.uuid();
12985
+ // Generate unique UUID class if not provided
12986
+ var componentId = ((_taco$o3 = taco.o) === null || _taco$o3 === void 0 ? void 0 : _taco$o3.id) || bw.uuid('uuid');
14113
12987
 
14114
12988
  // Create DOM element
14115
12989
  var domElement;
@@ -14124,8 +12998,9 @@
14124
12998
  };
14125
12999
  }
14126
13000
 
14127
- // Add component ID to element
14128
- domElement.setAttribute('data-bw_id', componentId);
13001
+ // Add component ID as class + lifecycle marker
13002
+ domElement.classList.add(componentId);
13003
+ domElement.classList.add(_BW_LC);
14129
13004
 
14130
13005
  // Insert into DOM based on position
14131
13006
  try {
@@ -14165,7 +13040,7 @@
14165
13040
  status_code: 'success',
14166
13041
  // Reference to original TACO
14167
13042
  _taco: _objectSpread2({}, taco),
14168
- _state: _objectSpread2({}, ((_taco$o5 = taco.o) === null || _taco$o5 === void 0 ? void 0 : _taco$o5.state) || {}),
13043
+ _state: _objectSpread2({}, ((_taco$o4 = taco.o) === null || _taco$o4 === void 0 ? void 0 : _taco$o4.state) || {}),
14169
13044
  _mounted: true,
14170
13045
  // Get DOM element
14171
13046
  get element() {
@@ -14196,7 +13071,8 @@
14196
13071
 
14197
13072
  // Re-render
14198
13073
  var newElement = bw.createDOM(this._taco);
14199
- newElement.setAttribute('data-bw_id', componentId);
13074
+ newElement.classList.add(componentId);
13075
+ newElement.classList.add(_BW_LC);
14200
13076
 
14201
13077
  // Replace in DOM
14202
13078
  parent.replaceChild(newElement, this.element);
@@ -14323,7 +13199,7 @@
14323
13199
  bw._componentRegistry.set(componentId, handle);
14324
13200
 
14325
13201
  // Call mounted lifecycle
14326
- if ((_taco$o6 = taco.o) !== null && _taco$o6 !== void 0 && _taco$o6.mounted) {
13202
+ if ((_taco$o5 = taco.o) !== null && _taco$o5 !== void 0 && _taco$o5.mounted) {
14327
13203
  taco.o.mounted(domElement, handle);
14328
13204
  }
14329
13205
  return handle;
@@ -14371,16 +13247,15 @@
14371
13247
  // Variant class helper: bw.variantClass('primary') → 'bw_primary'
14372
13248
  bw.variantClass = variantClass;
14373
13249
 
14374
- // Create functions that return handles (plain renderComponent, no Handle overlay)
13250
+ // Create functions that return DOM elements (createCard, createTable, etc.)
14375
13251
  Object.entries(components).forEach(function (_ref11) {
14376
13252
  var _ref12 = _slicedToArray(_ref11, 2),
14377
13253
  name = _ref12[0],
14378
13254
  fn = _ref12[1];
14379
13255
  if (name.startsWith('make')) {
14380
- var createName = 'create' + name.substring(4); // createCard, createTable, etc.
13256
+ var createName = 'create' + name.substring(4);
14381
13257
  bw[createName] = function (props) {
14382
- var taco = fn(props);
14383
- return bw.renderComponent(taco);
13258
+ return bw.createDOM(fn(props));
14384
13259
  };
14385
13260
  }
14386
13261
  });