@wsxjs/wsx-core 0.0.28 → 0.0.30

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.
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  jsx: () => h,
33
33
  jsxs: () => h,
34
34
  logger: () => import_wsx_logger4.logger,
35
+ parseHTMLToNodes: () => parseHTMLToNodes,
35
36
  registerComponent: () => registerComponent,
36
37
  state: () => state
37
38
  });
@@ -44,6 +45,7 @@ function parseHTMLToNodes(html) {
44
45
  temp.innerHTML = html;
45
46
  return Array.from(temp.childNodes).map((node) => {
46
47
  if (node instanceof HTMLElement || node instanceof SVGElement) {
48
+ node.__wsxManaged = true;
47
49
  return node;
48
50
  } else {
49
51
  return node.textContent || "";
@@ -56,7 +58,13 @@ function isHTMLString(str) {
56
58
  const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
57
59
  const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
58
60
  if (looksLikeMath) return false;
59
- return htmlTagPattern.test(trimmed);
61
+ const result = htmlTagPattern.test(trimmed);
62
+ if (result) {
63
+ console.log(`[WSX Debug] isHTMLString("${trimmed.substring(0, 50)}..."): ${result}`, {
64
+ looksLikeMath
65
+ });
66
+ }
67
+ return result;
60
68
  }
61
69
  function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
62
70
  if (depth > 10) {
@@ -210,10 +218,10 @@ function shouldPreserveElement(element) {
210
218
  if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
211
219
  return false;
212
220
  }
213
- if (!isCreatedByH(element)) {
221
+ if (!isCreatedByH(element) && element.__wsxManaged !== true) {
214
222
  return true;
215
223
  }
216
- if (element.hasAttribute("data-wsx-preserve")) {
224
+ if (element instanceof HTMLElement && element.hasAttribute("data-wsx-preserve")) {
217
225
  return true;
218
226
  }
219
227
  return false;
@@ -760,6 +768,64 @@ function removeNodeIfNotPreserved(parent, node) {
760
768
  parent.removeChild(node);
761
769
  }
762
770
  }
771
+ function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling, processedNodes) {
772
+ if (newChild.parentNode && newChild.parentNode !== parent) {
773
+ newChild.parentNode.removeChild(newChild);
774
+ }
775
+ const isInCorrectPosition = newChild.parentNode === parent && (newChild.nextSibling === targetNextSibling || targetNextSibling === newChild);
776
+ if (isInCorrectPosition) {
777
+ if (processedNodes) processedNodes.add(newChild);
778
+ return;
779
+ }
780
+ if (newChild.parentNode === parent) {
781
+ parent.insertBefore(newChild, targetNextSibling);
782
+ if (processedNodes) processedNodes.add(newChild);
783
+ return;
784
+ }
785
+ if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
786
+ if (oldNode !== newChild) {
787
+ const oldCacheKey = getElementCacheKey(oldNode);
788
+ const newCacheKey = getElementCacheKey(newChild);
789
+ if (!oldCacheKey && !newCacheKey && oldNode.__wsxManaged === true) {
790
+ const oldTag = oldNode.tagName.toLowerCase();
791
+ const newTag = newChild.tagName.toLowerCase();
792
+ if (oldTag === newTag && oldNode.textContent === newChild.textContent) {
793
+ if (processedNodes) processedNodes.add(oldNode);
794
+ return;
795
+ }
796
+ }
797
+ parent.replaceChild(newChild, oldNode);
798
+ if (processedNodes) processedNodes.add(newChild);
799
+ } else {
800
+ if (processedNodes) processedNodes.add(newChild);
801
+ }
802
+ } else {
803
+ if (newChild.parentNode === parent) {
804
+ return;
805
+ }
806
+ const newChildCacheKey = getElementCacheKey(newChild);
807
+ if (!newChildCacheKey) {
808
+ const newChildContent = newChild.textContent || "";
809
+ const newChildTag = newChild.tagName.toLowerCase();
810
+ for (let i = 0; i < parent.childNodes.length; i++) {
811
+ const existingNode = parent.childNodes[i];
812
+ if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
813
+ const existingCacheKey = getElementCacheKey(existingNode);
814
+ if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
815
+ console.log(
816
+ "[WSX Debug] Found duplicate content, keeping existing:",
817
+ existingNode.tagName,
818
+ existingNode.textContent
819
+ );
820
+ if (processedNodes) processedNodes.add(existingNode);
821
+ return;
822
+ }
823
+ }
824
+ }
825
+ }
826
+ parent.insertBefore(newChild, targetNextSibling);
827
+ }
828
+ }
763
829
  function appendNewChild(parent, child, processedNodes) {
764
830
  if (child === null || child === void 0 || child === false) {
765
831
  return;
@@ -828,10 +894,10 @@ function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
828
894
  }
829
895
  }
830
896
  const isProcessed = processedNodes && processedNodes.has(node);
831
- if (shouldPreserveElement(node)) {
897
+ if (isProcessed) {
832
898
  return false;
833
899
  }
834
- if (node.nodeType === Node.TEXT_NODE && isProcessed) {
900
+ if (shouldPreserveElement(node)) {
835
901
  return false;
836
902
  }
837
903
  if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
@@ -1149,14 +1215,13 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
1149
1215
  }
1150
1216
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
1151
1217
  const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1152
- if (newChild === oldNode) {
1153
- if (newChild.nextSibling !== insertBeforeNode) {
1154
- element.insertBefore(newChild, insertBeforeNode);
1155
- }
1156
- } else {
1157
- element.insertBefore(newChild, insertBeforeNode);
1158
- }
1159
- processedNodes.add(newChild);
1218
+ replaceOrInsertElementAtPosition(
1219
+ element,
1220
+ newChild,
1221
+ oldNode,
1222
+ insertBeforeNode,
1223
+ processedNodes
1224
+ );
1160
1225
  insertionIndex.value++;
1161
1226
  } else {
1162
1227
  const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
@@ -1203,6 +1268,55 @@ function updateElement(element, newProps, newChildren, tag, cacheManager) {
1203
1268
  updateProps(element, oldProps, newProps, tag);
1204
1269
  updateChildren(element, oldChildren, newChildren, cacheManager);
1205
1270
  }
1271
+ function reconcileElement(oldParent, newParent) {
1272
+ const oldChildren = Array.from(oldParent.childNodes);
1273
+ const newChildren = Array.from(newParent.childNodes);
1274
+ const maxLength = Math.max(oldChildren.length, newChildren.length);
1275
+ for (let i = 0; i < maxLength; i++) {
1276
+ const oldChild = oldChildren[i];
1277
+ const newChild = newChildren[i];
1278
+ if (!newChild) {
1279
+ oldChild?.remove();
1280
+ } else if (!oldChild) {
1281
+ oldParent.appendChild(newChild.cloneNode(true));
1282
+ } else if (oldChild.nodeType !== newChild.nodeType) {
1283
+ oldParent.replaceChild(newChild.cloneNode(true), oldChild);
1284
+ } else if (oldChild.nodeType === Node.TEXT_NODE) {
1285
+ if (oldChild.textContent !== newChild.textContent) {
1286
+ oldChild.textContent = newChild.textContent;
1287
+ }
1288
+ } else if (oldChild.nodeType === Node.ELEMENT_NODE) {
1289
+ const oldEl = oldChild;
1290
+ const newEl = newChild;
1291
+ if (oldEl.tagName !== newEl.tagName) {
1292
+ oldParent.replaceChild(newEl.cloneNode(true), oldEl);
1293
+ } else {
1294
+ Array.from(oldEl.attributes).forEach((attr) => {
1295
+ if (!newEl.hasAttribute(attr.name)) {
1296
+ oldEl.removeAttribute(attr.name);
1297
+ }
1298
+ });
1299
+ Array.from(newEl.attributes).forEach((attr) => {
1300
+ if (oldEl.getAttribute(attr.name) !== attr.value) {
1301
+ oldEl.setAttribute(attr.name, attr.value);
1302
+ }
1303
+ });
1304
+ if (oldEl.className !== newEl.className) {
1305
+ oldEl.className = newEl.className;
1306
+ }
1307
+ if (oldEl instanceof HTMLInputElement && newEl instanceof HTMLInputElement) {
1308
+ if (oldEl.checked !== newEl.checked) {
1309
+ oldEl.checked = newEl.checked;
1310
+ }
1311
+ if (oldEl.value !== newEl.value) {
1312
+ oldEl.value = newEl.value;
1313
+ }
1314
+ }
1315
+ reconcileElement(oldEl, newEl);
1316
+ }
1317
+ }
1318
+ }
1319
+ }
1206
1320
 
1207
1321
  // src/jsx-factory.ts
1208
1322
  var logger3 = createLogger("JSX Factory");
@@ -1833,7 +1947,7 @@ var BaseComponent = class extends HTMLElement {
1833
1947
  }
1834
1948
  }
1835
1949
  if (activeElement) {
1836
- const isInputElement = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
1950
+ const isInputElement = activeElement instanceof HTMLInputElement && activeElement.type !== "radio" && activeElement.type !== "checkbox" || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
1837
1951
  const forceRender = activeElement.hasAttribute("data-wsx-force-render");
1838
1952
  if (isInputElement && !forceRender) {
1839
1953
  this._pendingRerender = true;
@@ -1849,14 +1963,8 @@ var BaseComponent = class extends HTMLElement {
1849
1963
  }
1850
1964
  this._hasScheduledRender = true;
1851
1965
  requestAnimationFrame(() => {
1852
- console.warn("[scheduleRerender] RAF callback:", {
1853
- component: this.constructor.name,
1854
- connected: this.connected,
1855
- isRendering: this._isRendering
1856
- });
1857
1966
  this._hasScheduledRender = false;
1858
1967
  if (this.connected && !this._isRendering) {
1859
- console.warn("[scheduleRerender] calling _rerender()");
1860
1968
  this._isRendering = true;
1861
1969
  this._rerender();
1862
1970
  } else if (!this.connected) {
@@ -1885,7 +1993,8 @@ var BaseComponent = class extends HTMLElement {
1885
1993
  clearTimeout(this._rerenderDebounceTimer);
1886
1994
  this._rerenderDebounceTimer = null;
1887
1995
  }
1888
- document.removeEventListener("blur", this.handleGlobalBlur, true);
1996
+ const root = this.getActiveRoot();
1997
+ root.removeEventListener("blur", this.handleGlobalBlur, true);
1889
1998
  this._pendingRerender = false;
1890
1999
  }
1891
2000
  /**
@@ -1893,7 +2002,8 @@ var BaseComponent = class extends HTMLElement {
1893
2002
  * @internal
1894
2003
  */
1895
2004
  initializeEventListeners() {
1896
- document.addEventListener("blur", this.handleGlobalBlur, true);
2005
+ const root = this.getActiveRoot();
2006
+ root.addEventListener("blur", this.handleGlobalBlur, true);
1897
2007
  }
1898
2008
  /**
1899
2009
  * 获取配置值
@@ -2350,6 +2460,10 @@ var LightComponent = class extends BaseComponent {
2350
2460
  querySelectorAll(selector) {
2351
2461
  return HTMLElement.prototype.querySelectorAll.call(this, selector);
2352
2462
  }
2463
+ /**
2464
+ * 递归协调子元素
2465
+ * 更新现有子元素的属性和内容,而不是替换整个子树
2466
+ */
2353
2467
  /**
2354
2468
  * 内部重渲染实现
2355
2469
  * 包含从 rerender() 方法迁移的实际渲染逻辑
@@ -2366,9 +2480,9 @@ var LightComponent = class extends BaseComponent {
2366
2480
  this._pendingFocusState = focusState;
2367
2481
  const jsxChildren = this.getJSXChildren();
2368
2482
  try {
2369
- const content = RenderContext.runInContext(this, () => this.render());
2483
+ const newContent = RenderContext.runInContext(this, () => this.render());
2370
2484
  if (focusState && focusState.key && focusState.value !== void 0) {
2371
- const target = content.querySelector(
2485
+ const target = newContent.querySelector(
2372
2486
  `[data-wsx-key="${focusState.key}"]`
2373
2487
  );
2374
2488
  if (target) {
@@ -2393,36 +2507,50 @@ var LightComponent = class extends BaseComponent {
2393
2507
  }
2394
2508
  }
2395
2509
  requestAnimationFrame(() => {
2396
- this.appendChild(content);
2397
2510
  const oldChildren = Array.from(this.children).filter((child) => {
2398
- if (child === content) {
2399
- return false;
2400
- }
2401
2511
  if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
2402
2512
  return false;
2403
2513
  }
2404
2514
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
2405
2515
  return false;
2406
2516
  }
2407
- if (shouldPreserveElement(child)) {
2408
- return false;
2409
- }
2410
2517
  return true;
2411
2518
  });
2412
- oldChildren.forEach((child) => child.remove());
2519
+ if (oldChildren.length === 1 && newContent instanceof HTMLElement) {
2520
+ const oldElement = oldChildren[0];
2521
+ if (oldElement instanceof HTMLElement && oldElement.tagName === newContent.tagName) {
2522
+ Array.from(oldElement.attributes).forEach((attr) => {
2523
+ if (!newContent.hasAttribute(attr.name)) {
2524
+ oldElement.removeAttribute(attr.name);
2525
+ }
2526
+ });
2527
+ Array.from(newContent.attributes).forEach((attr) => {
2528
+ if (oldElement.getAttribute(attr.name) !== attr.value) {
2529
+ oldElement.setAttribute(attr.name, attr.value);
2530
+ }
2531
+ });
2532
+ reconcileElement(oldElement, newContent);
2533
+ } else {
2534
+ oldElement.remove();
2535
+ this.appendChild(newContent);
2536
+ }
2537
+ } else {
2538
+ oldChildren.forEach((child) => child.remove());
2539
+ this.appendChild(newContent);
2540
+ }
2413
2541
  if (stylesToApply) {
2414
- let styleElement = this.querySelector(
2542
+ let styleEl = this.querySelector(
2415
2543
  `style[data-wsx-light-component="${styleName}"]`
2416
2544
  );
2417
- if (!styleElement) {
2418
- styleElement = document.createElement("style");
2419
- styleElement.setAttribute("data-wsx-light-component", styleName);
2420
- styleElement.textContent = stylesToApply;
2421
- this.insertBefore(styleElement, this.firstChild);
2422
- } else if (styleElement.textContent !== stylesToApply) {
2423
- styleElement.textContent = stylesToApply;
2424
- } else if (styleElement !== this.firstChild) {
2425
- this.insertBefore(styleElement, this.firstChild);
2545
+ if (!styleEl) {
2546
+ styleEl = document.createElement("style");
2547
+ styleEl.setAttribute("data-wsx-light-component", styleName);
2548
+ styleEl.textContent = stylesToApply;
2549
+ this.insertBefore(styleEl, this.firstChild);
2550
+ } else if (styleEl.textContent !== stylesToApply) {
2551
+ styleEl.textContent = stylesToApply;
2552
+ } else if (styleEl !== this.firstChild) {
2553
+ this.insertBefore(styleEl, this.firstChild);
2426
2554
  }
2427
2555
  }
2428
2556
  requestAnimationFrame(() => {
@@ -2626,6 +2754,7 @@ function state(targetOrContext, propertyKey) {
2626
2754
  jsx,
2627
2755
  jsxs,
2628
2756
  logger,
2757
+ parseHTMLToNodes,
2629
2758
  registerComponent,
2630
2759
  state
2631
2760
  });
package/dist/index.mjs CHANGED
@@ -3,8 +3,10 @@ import {
3
3
  RenderContext,
4
4
  createLogger,
5
5
  h,
6
+ parseHTMLToNodes,
7
+ reconcileElement,
6
8
  shouldPreserveElement
7
- } from "./chunk-34PNC5CJ.mjs";
9
+ } from "./chunk-2ER76KOQ.mjs";
8
10
 
9
11
  // src/styles/style-manager.ts
10
12
  var StyleManager = class {
@@ -527,7 +529,7 @@ var BaseComponent = class extends HTMLElement {
527
529
  }
528
530
  }
529
531
  if (activeElement) {
530
- const isInputElement = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
532
+ const isInputElement = activeElement instanceof HTMLInputElement && activeElement.type !== "radio" && activeElement.type !== "checkbox" || activeElement instanceof HTMLTextAreaElement || activeElement instanceof HTMLSelectElement || activeElement.hasAttribute("contenteditable");
531
533
  const forceRender = activeElement.hasAttribute("data-wsx-force-render");
532
534
  if (isInputElement && !forceRender) {
533
535
  this._pendingRerender = true;
@@ -543,14 +545,8 @@ var BaseComponent = class extends HTMLElement {
543
545
  }
544
546
  this._hasScheduledRender = true;
545
547
  requestAnimationFrame(() => {
546
- console.warn("[scheduleRerender] RAF callback:", {
547
- component: this.constructor.name,
548
- connected: this.connected,
549
- isRendering: this._isRendering
550
- });
551
548
  this._hasScheduledRender = false;
552
549
  if (this.connected && !this._isRendering) {
553
- console.warn("[scheduleRerender] calling _rerender()");
554
550
  this._isRendering = true;
555
551
  this._rerender();
556
552
  } else if (!this.connected) {
@@ -579,7 +575,8 @@ var BaseComponent = class extends HTMLElement {
579
575
  clearTimeout(this._rerenderDebounceTimer);
580
576
  this._rerenderDebounceTimer = null;
581
577
  }
582
- document.removeEventListener("blur", this.handleGlobalBlur, true);
578
+ const root = this.getActiveRoot();
579
+ root.removeEventListener("blur", this.handleGlobalBlur, true);
583
580
  this._pendingRerender = false;
584
581
  }
585
582
  /**
@@ -587,7 +584,8 @@ var BaseComponent = class extends HTMLElement {
587
584
  * @internal
588
585
  */
589
586
  initializeEventListeners() {
590
- document.addEventListener("blur", this.handleGlobalBlur, true);
587
+ const root = this.getActiveRoot();
588
+ root.addEventListener("blur", this.handleGlobalBlur, true);
591
589
  }
592
590
  /**
593
591
  * 获取配置值
@@ -1044,6 +1042,10 @@ var LightComponent = class extends BaseComponent {
1044
1042
  querySelectorAll(selector) {
1045
1043
  return HTMLElement.prototype.querySelectorAll.call(this, selector);
1046
1044
  }
1045
+ /**
1046
+ * 递归协调子元素
1047
+ * 更新现有子元素的属性和内容,而不是替换整个子树
1048
+ */
1047
1049
  /**
1048
1050
  * 内部重渲染实现
1049
1051
  * 包含从 rerender() 方法迁移的实际渲染逻辑
@@ -1060,9 +1062,9 @@ var LightComponent = class extends BaseComponent {
1060
1062
  this._pendingFocusState = focusState;
1061
1063
  const jsxChildren = this.getJSXChildren();
1062
1064
  try {
1063
- const content = RenderContext.runInContext(this, () => this.render());
1065
+ const newContent = RenderContext.runInContext(this, () => this.render());
1064
1066
  if (focusState && focusState.key && focusState.value !== void 0) {
1065
- const target = content.querySelector(
1067
+ const target = newContent.querySelector(
1066
1068
  `[data-wsx-key="${focusState.key}"]`
1067
1069
  );
1068
1070
  if (target) {
@@ -1087,36 +1089,50 @@ var LightComponent = class extends BaseComponent {
1087
1089
  }
1088
1090
  }
1089
1091
  requestAnimationFrame(() => {
1090
- this.appendChild(content);
1091
1092
  const oldChildren = Array.from(this.children).filter((child) => {
1092
- if (child === content) {
1093
- return false;
1094
- }
1095
1093
  if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
1096
1094
  return false;
1097
1095
  }
1098
1096
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
1099
1097
  return false;
1100
1098
  }
1101
- if (shouldPreserveElement(child)) {
1102
- return false;
1103
- }
1104
1099
  return true;
1105
1100
  });
1106
- oldChildren.forEach((child) => child.remove());
1101
+ if (oldChildren.length === 1 && newContent instanceof HTMLElement) {
1102
+ const oldElement = oldChildren[0];
1103
+ if (oldElement instanceof HTMLElement && oldElement.tagName === newContent.tagName) {
1104
+ Array.from(oldElement.attributes).forEach((attr) => {
1105
+ if (!newContent.hasAttribute(attr.name)) {
1106
+ oldElement.removeAttribute(attr.name);
1107
+ }
1108
+ });
1109
+ Array.from(newContent.attributes).forEach((attr) => {
1110
+ if (oldElement.getAttribute(attr.name) !== attr.value) {
1111
+ oldElement.setAttribute(attr.name, attr.value);
1112
+ }
1113
+ });
1114
+ reconcileElement(oldElement, newContent);
1115
+ } else {
1116
+ oldElement.remove();
1117
+ this.appendChild(newContent);
1118
+ }
1119
+ } else {
1120
+ oldChildren.forEach((child) => child.remove());
1121
+ this.appendChild(newContent);
1122
+ }
1107
1123
  if (stylesToApply) {
1108
- let styleElement = this.querySelector(
1124
+ let styleEl = this.querySelector(
1109
1125
  `style[data-wsx-light-component="${styleName}"]`
1110
1126
  );
1111
- if (!styleElement) {
1112
- styleElement = document.createElement("style");
1113
- styleElement.setAttribute("data-wsx-light-component", styleName);
1114
- styleElement.textContent = stylesToApply;
1115
- this.insertBefore(styleElement, this.firstChild);
1116
- } else if (styleElement.textContent !== stylesToApply) {
1117
- styleElement.textContent = stylesToApply;
1118
- } else if (styleElement !== this.firstChild) {
1119
- this.insertBefore(styleElement, this.firstChild);
1127
+ if (!styleEl) {
1128
+ styleEl = document.createElement("style");
1129
+ styleEl.setAttribute("data-wsx-light-component", styleName);
1130
+ styleEl.textContent = stylesToApply;
1131
+ this.insertBefore(styleEl, this.firstChild);
1132
+ } else if (styleEl.textContent !== stylesToApply) {
1133
+ styleEl.textContent = stylesToApply;
1134
+ } else if (styleEl !== this.firstChild) {
1135
+ this.insertBefore(styleEl, this.firstChild);
1120
1136
  }
1121
1137
  }
1122
1138
  requestAnimationFrame(() => {
@@ -1319,6 +1335,7 @@ export {
1319
1335
  h as jsx,
1320
1336
  h as jsxs,
1321
1337
  logger5 as logger,
1338
+ parseHTMLToNodes,
1322
1339
  registerComponent,
1323
1340
  state
1324
1341
  };
@@ -33,6 +33,7 @@ function parseHTMLToNodes(html) {
33
33
  temp.innerHTML = html;
34
34
  return Array.from(temp.childNodes).map((node) => {
35
35
  if (node instanceof HTMLElement || node instanceof SVGElement) {
36
+ node.__wsxManaged = true;
36
37
  return node;
37
38
  } else {
38
39
  return node.textContent || "";
@@ -45,7 +46,13 @@ function isHTMLString(str) {
45
46
  const htmlTagPattern = /<[a-z][a-z0-9]*(\s[^>]*)?(\/>|>)/i;
46
47
  const looksLikeMath = /^[^<]*<[^>]*>[^>]*$/.test(trimmed) && !htmlTagPattern.test(trimmed);
47
48
  if (looksLikeMath) return false;
48
- return htmlTagPattern.test(trimmed);
49
+ const result = htmlTagPattern.test(trimmed);
50
+ if (result) {
51
+ console.log(`[WSX Debug] isHTMLString("${trimmed.substring(0, 50)}..."): ${result}`, {
52
+ looksLikeMath
53
+ });
54
+ }
55
+ return result;
49
56
  }
50
57
  function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
51
58
  if (depth > 10) {
@@ -199,10 +206,10 @@ function shouldPreserveElement(element) {
199
206
  if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
200
207
  return false;
201
208
  }
202
- if (!isCreatedByH(element)) {
209
+ if (!isCreatedByH(element) && element.__wsxManaged !== true) {
203
210
  return true;
204
211
  }
205
- if (element.hasAttribute("data-wsx-preserve")) {
212
+ if (element instanceof HTMLElement && element.hasAttribute("data-wsx-preserve")) {
206
213
  return true;
207
214
  }
208
215
  return false;
@@ -749,6 +756,64 @@ function removeNodeIfNotPreserved(parent, node) {
749
756
  parent.removeChild(node);
750
757
  }
751
758
  }
759
+ function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling, processedNodes) {
760
+ if (newChild.parentNode && newChild.parentNode !== parent) {
761
+ newChild.parentNode.removeChild(newChild);
762
+ }
763
+ const isInCorrectPosition = newChild.parentNode === parent && (newChild.nextSibling === targetNextSibling || targetNextSibling === newChild);
764
+ if (isInCorrectPosition) {
765
+ if (processedNodes) processedNodes.add(newChild);
766
+ return;
767
+ }
768
+ if (newChild.parentNode === parent) {
769
+ parent.insertBefore(newChild, targetNextSibling);
770
+ if (processedNodes) processedNodes.add(newChild);
771
+ return;
772
+ }
773
+ if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
774
+ if (oldNode !== newChild) {
775
+ const oldCacheKey = getElementCacheKey(oldNode);
776
+ const newCacheKey = getElementCacheKey(newChild);
777
+ if (!oldCacheKey && !newCacheKey && oldNode.__wsxManaged === true) {
778
+ const oldTag = oldNode.tagName.toLowerCase();
779
+ const newTag = newChild.tagName.toLowerCase();
780
+ if (oldTag === newTag && oldNode.textContent === newChild.textContent) {
781
+ if (processedNodes) processedNodes.add(oldNode);
782
+ return;
783
+ }
784
+ }
785
+ parent.replaceChild(newChild, oldNode);
786
+ if (processedNodes) processedNodes.add(newChild);
787
+ } else {
788
+ if (processedNodes) processedNodes.add(newChild);
789
+ }
790
+ } else {
791
+ if (newChild.parentNode === parent) {
792
+ return;
793
+ }
794
+ const newChildCacheKey = getElementCacheKey(newChild);
795
+ if (!newChildCacheKey) {
796
+ const newChildContent = newChild.textContent || "";
797
+ const newChildTag = newChild.tagName.toLowerCase();
798
+ for (let i = 0; i < parent.childNodes.length; i++) {
799
+ const existingNode = parent.childNodes[i];
800
+ if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
801
+ const existingCacheKey = getElementCacheKey(existingNode);
802
+ if (!existingCacheKey && existingNode.tagName.toLowerCase() === newChildTag && existingNode.textContent === newChildContent && existingNode !== newChild) {
803
+ console.log(
804
+ "[WSX Debug] Found duplicate content, keeping existing:",
805
+ existingNode.tagName,
806
+ existingNode.textContent
807
+ );
808
+ if (processedNodes) processedNodes.add(existingNode);
809
+ return;
810
+ }
811
+ }
812
+ }
813
+ }
814
+ parent.insertBefore(newChild, targetNextSibling);
815
+ }
816
+ }
752
817
  function appendNewChild(parent, child, processedNodes) {
753
818
  if (child === null || child === void 0 || child === false) {
754
819
  return;
@@ -817,10 +882,10 @@ function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
817
882
  }
818
883
  }
819
884
  const isProcessed = processedNodes && processedNodes.has(node);
820
- if (shouldPreserveElement(node)) {
885
+ if (isProcessed) {
821
886
  return false;
822
887
  }
823
- if (node.nodeType === Node.TEXT_NODE && isProcessed) {
888
+ if (shouldPreserveElement(node)) {
824
889
  return false;
825
890
  }
826
891
  if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
@@ -1138,14 +1203,13 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
1138
1203
  }
1139
1204
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
1140
1205
  const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1141
- if (newChild === oldNode) {
1142
- if (newChild.nextSibling !== insertBeforeNode) {
1143
- element.insertBefore(newChild, insertBeforeNode);
1144
- }
1145
- } else {
1146
- element.insertBefore(newChild, insertBeforeNode);
1147
- }
1148
- processedNodes.add(newChild);
1206
+ replaceOrInsertElementAtPosition(
1207
+ element,
1208
+ newChild,
1209
+ oldNode,
1210
+ insertBeforeNode,
1211
+ processedNodes
1212
+ );
1149
1213
  insertionIndex.value++;
1150
1214
  } else {
1151
1215
  const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
@@ -2,7 +2,7 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-34PNC5CJ.mjs";
5
+ } from "./chunk-2ER76KOQ.mjs";
6
6
  export {
7
7
  Fragment,
8
8
  jsx,