@wsxjs/wsx-core 0.0.27 → 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) {
@@ -97,6 +105,17 @@ function flattenChildren(children, skipHTMLDetection = false, depth = 0) {
97
105
  } else {
98
106
  result.push(child);
99
107
  }
108
+ } else if (child instanceof DocumentFragment) {
109
+ const fragmentChildren = Array.from(child.childNodes);
110
+ for (const fragChild of fragmentChildren) {
111
+ if (fragChild instanceof HTMLElement || fragChild instanceof SVGElement) {
112
+ result.push(fragChild);
113
+ } else if (fragChild.nodeType === Node.TEXT_NODE) {
114
+ result.push(fragChild.textContent || "");
115
+ } else if (fragChild instanceof DocumentFragment) {
116
+ result.push(...flattenChildren([fragChild], skipHTMLDetection, depth + 1));
117
+ }
118
+ }
100
119
  } else {
101
120
  result.push(child);
102
121
  }
@@ -197,12 +216,12 @@ function isCreatedByH(element) {
197
216
  }
198
217
  function shouldPreserveElement(element) {
199
218
  if (!(element instanceof HTMLElement || element instanceof SVGElement)) {
200
- return true;
219
+ return false;
201
220
  }
202
- if (!isCreatedByH(element)) {
221
+ if (!isCreatedByH(element) && element.__wsxManaged !== true) {
203
222
  return true;
204
223
  }
205
- if (element.hasAttribute("data-wsx-preserve")) {
224
+ if (element instanceof HTMLElement && element.hasAttribute("data-wsx-preserve")) {
206
225
  return true;
207
226
  }
208
227
  return false;
@@ -648,7 +667,9 @@ function appendChildrenToElement(element, children) {
648
667
  return;
649
668
  }
650
669
  if (typeof child === "string" || typeof child === "number") {
651
- element.appendChild(document.createTextNode(String(child)));
670
+ const textNode = document.createTextNode(String(child));
671
+ textNode.__wsxManaged = true;
672
+ element.appendChild(textNode);
652
673
  } else if (child instanceof HTMLElement || child instanceof SVGElement) {
653
674
  element.appendChild(child);
654
675
  } else if (child instanceof DocumentFragment) {
@@ -668,6 +689,9 @@ function collectPreservedElements(element) {
668
689
  const preserved = [];
669
690
  for (let i = 0; i < element.childNodes.length; i++) {
670
691
  const child = element.childNodes[i];
692
+ if (child.nodeType === Node.TEXT_NODE) {
693
+ continue;
694
+ }
671
695
  if (shouldPreserveElement(child)) {
672
696
  preserved.push(child);
673
697
  }
@@ -701,38 +725,40 @@ function findElementNode(oldChild, parent) {
701
725
  }
702
726
  return null;
703
727
  }
704
- function findTextNode(parent, domIndex) {
705
- while (domIndex.value < parent.childNodes.length) {
706
- const node = parent.childNodes[domIndex.value];
707
- if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent) {
708
- const textNode = node;
709
- domIndex.value++;
710
- return textNode;
728
+ function findTextNode(parent, domIndex, processedNodes) {
729
+ for (let i = domIndex.value; i < parent.childNodes.length; i++) {
730
+ const node = parent.childNodes[i];
731
+ if (node.nodeType === Node.TEXT_NODE && node.__wsxManaged === true && !processedNodes.has(node)) {
732
+ domIndex.value = i + 1;
733
+ return node;
711
734
  }
712
- domIndex.value++;
713
735
  }
714
736
  return null;
715
737
  }
716
- function updateOrCreateTextNode(parent, oldNode, newText) {
738
+ function updateOrCreateTextNode(parent, oldNode, newText, insertBeforeNode) {
739
+ if (shouldPreserveElement(parent)) {
740
+ if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
741
+ return oldNode;
742
+ }
743
+ return document.createTextNode(newText);
744
+ }
717
745
  if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
718
746
  if (oldNode.textContent !== newText) {
719
747
  oldNode.textContent = newText;
720
748
  }
721
- return oldNode;
722
- } else {
723
- if (!oldNode) {
724
- for (let i = 0; i < parent.childNodes.length; i++) {
725
- const node = parent.childNodes[i];
726
- if (node.nodeType === Node.TEXT_NODE && node.parentNode === parent && node.textContent === newText) {
727
- return node;
728
- }
749
+ if (insertBeforeNode !== void 0) {
750
+ if (oldNode !== insertBeforeNode && oldNode.nextSibling !== insertBeforeNode) {
751
+ parent.insertBefore(oldNode, insertBeforeNode);
729
752
  }
730
753
  }
754
+ return oldNode;
755
+ } else {
731
756
  const newTextNode = document.createTextNode(newText);
757
+ newTextNode.__wsxManaged = true;
732
758
  if (oldNode && !shouldPreserveElement(oldNode)) {
733
759
  parent.replaceChild(newTextNode, oldNode);
734
760
  } else {
735
- parent.insertBefore(newTextNode, oldNode || null);
761
+ parent.insertBefore(newTextNode, insertBeforeNode ?? null);
736
762
  }
737
763
  return newTextNode;
738
764
  }
@@ -742,25 +768,36 @@ function removeNodeIfNotPreserved(parent, node) {
742
768
  parent.removeChild(node);
743
769
  }
744
770
  }
745
- function replaceOrInsertElement(parent, newChild, oldNode) {
746
- const targetNextSibling = oldNode && shouldPreserveElement(oldNode) ? oldNode : oldNode?.nextSibling || null;
747
- replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling);
748
- }
749
- function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling) {
771
+ function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextSibling, processedNodes) {
750
772
  if (newChild.parentNode && newChild.parentNode !== parent) {
751
773
  newChild.parentNode.removeChild(newChild);
752
774
  }
753
- const isInCorrectPosition = newChild.parentNode === parent && newChild.nextSibling === targetNextSibling;
775
+ const isInCorrectPosition = newChild.parentNode === parent && (newChild.nextSibling === targetNextSibling || targetNextSibling === newChild);
754
776
  if (isInCorrectPosition) {
777
+ if (processedNodes) processedNodes.add(newChild);
755
778
  return;
756
779
  }
757
780
  if (newChild.parentNode === parent) {
758
781
  parent.insertBefore(newChild, targetNextSibling);
782
+ if (processedNodes) processedNodes.add(newChild);
759
783
  return;
760
784
  }
761
785
  if (oldNode && oldNode.parentNode === parent && !shouldPreserveElement(oldNode)) {
762
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
+ }
763
797
  parent.replaceChild(newChild, oldNode);
798
+ if (processedNodes) processedNodes.add(newChild);
799
+ } else {
800
+ if (processedNodes) processedNodes.add(newChild);
764
801
  }
765
802
  } else {
766
803
  if (newChild.parentNode === parent) {
@@ -775,6 +812,12 @@ function replaceOrInsertElementAtPosition(parent, newChild, oldNode, targetNextS
775
812
  if (existingNode instanceof HTMLElement || existingNode instanceof SVGElement) {
776
813
  const existingCacheKey = getElementCacheKey(existingNode);
777
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);
778
821
  return;
779
822
  }
780
823
  }
@@ -789,6 +832,7 @@ function appendNewChild(parent, child, processedNodes) {
789
832
  }
790
833
  if (typeof child === "string" || typeof child === "number") {
791
834
  const newTextNode = document.createTextNode(String(child));
835
+ newTextNode.__wsxManaged = true;
792
836
  parent.appendChild(newTextNode);
793
837
  if (processedNodes) {
794
838
  processedNodes.add(newTextNode);
@@ -805,6 +849,11 @@ function appendNewChild(parent, child, processedNodes) {
805
849
  processedNodes.add(child);
806
850
  }
807
851
  } else if (child instanceof DocumentFragment) {
852
+ if (processedNodes) {
853
+ for (let i = 0; i < child.childNodes.length; i++) {
854
+ processedNodes.add(child.childNodes[i]);
855
+ }
856
+ }
808
857
  parent.appendChild(child);
809
858
  }
810
859
  }
@@ -825,21 +874,32 @@ function buildNewChildrenMaps(flatNew) {
825
874
  return { elementSet, cacheKeyMap };
826
875
  }
827
876
  function shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes) {
828
- if (shouldPreserveElement(node)) {
829
- return false;
830
- }
831
- if (node.nodeType === Node.TEXT_NODE && processedNodes && processedNodes.has(node)) {
832
- return false;
833
- }
834
- if (node.nodeType === Node.TEXT_NODE && processedNodes) {
835
- let parent = node.parentNode;
836
- while (parent) {
837
- if (processedNodes.has(parent) && parent.parentNode) {
877
+ if (node.nodeType === Node.TEXT_NODE) {
878
+ if (node.__wsxManaged === true) {
879
+ if (processedNodes && processedNodes.has(node)) {
838
880
  return false;
839
881
  }
840
- parent = parent.parentNode;
882
+ return true;
883
+ }
884
+ const parent = node.parentNode;
885
+ if (parent && (parent instanceof HTMLElement || parent instanceof SVGElement)) {
886
+ if (shouldPreserveElement(parent)) {
887
+ return false;
888
+ }
889
+ }
890
+ return true;
891
+ } else {
892
+ if (shouldPreserveElement(node)) {
893
+ return false;
841
894
  }
842
895
  }
896
+ const isProcessed = processedNodes && processedNodes.has(node);
897
+ if (isProcessed) {
898
+ return false;
899
+ }
900
+ if (shouldPreserveElement(node)) {
901
+ return false;
902
+ }
843
903
  if (node instanceof HTMLElement || node instanceof SVGElement || node instanceof DocumentFragment) {
844
904
  if (elementSet.has(node)) {
845
905
  return false;
@@ -881,7 +941,8 @@ function collectNodesToRemove(parent, elementSet, cacheKeyMap, processedNodes) {
881
941
  const nodesToRemove = [];
882
942
  for (let i = 0; i < parent.childNodes.length; i++) {
883
943
  const node = parent.childNodes[i];
884
- if (shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes)) {
944
+ const removed = shouldRemoveNode(node, elementSet, cacheKeyMap, processedNodes);
945
+ if (removed) {
885
946
  nodesToRemove.push(node);
886
947
  }
887
948
  }
@@ -1083,6 +1144,7 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
1083
1144
  const preservedElements = collectPreservedElements(element);
1084
1145
  const minLength = Math.min(flatOld.length, flatNew.length);
1085
1146
  const domIndex = { value: 0 };
1147
+ const insertionIndex = { value: 0 };
1086
1148
  const processedNodes = /* @__PURE__ */ new Set();
1087
1149
  for (let i = 0; i < minLength; i++) {
1088
1150
  const oldChild = flatOld[i];
@@ -1097,48 +1159,54 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
1097
1159
  }
1098
1160
  }
1099
1161
  } else if (typeof oldChild === "string" || typeof oldChild === "number") {
1100
- oldNode = findTextNode(element, domIndex);
1101
- if (!oldNode && element.childNodes.length > 0) {
1102
- const oldText = String(oldChild);
1103
- for (let j = domIndex.value; j < element.childNodes.length; j++) {
1104
- const node = element.childNodes[j];
1105
- if (node.nodeType === Node.TEXT_NODE && node.parentNode === element && node.textContent === oldText) {
1106
- oldNode = node;
1107
- domIndex.value = j + 1;
1108
- break;
1162
+ if (shouldPreserveElement(element)) {
1163
+ oldNode = null;
1164
+ } else {
1165
+ oldNode = findTextNode(element, domIndex, processedNodes);
1166
+ if (oldNode) {
1167
+ const nodeIndex = Array.from(element.childNodes).indexOf(oldNode);
1168
+ if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
1169
+ domIndex.value = nodeIndex + 1;
1109
1170
  }
1110
1171
  }
1111
1172
  }
1112
1173
  }
1113
1174
  if (typeof oldChild === "string" || typeof oldChild === "number") {
1114
1175
  if (typeof newChild === "string" || typeof newChild === "number") {
1115
- const oldText = String(oldChild);
1116
1176
  const newText = String(newChild);
1117
- const needsUpdate = oldText !== newText || oldNode && oldNode.nodeType === Node.TEXT_NODE && oldNode.textContent !== newText;
1118
- if (needsUpdate) {
1119
- const updatedNode = updateOrCreateTextNode(element, oldNode, newText);
1120
- if (updatedNode && !processedNodes.has(updatedNode)) {
1121
- processedNodes.add(updatedNode);
1122
- }
1123
- } else {
1124
- if (oldNode && oldNode.parentNode === element) {
1125
- processedNodes.add(oldNode);
1126
- } else if (!oldNode && oldText === newText) {
1127
- for (let j = domIndex.value; j < element.childNodes.length; j++) {
1128
- const node = element.childNodes[j];
1129
- if (node.nodeType === Node.TEXT_NODE && node.parentNode === element && node.textContent === newText && !processedNodes.has(node)) {
1130
- processedNodes.add(node);
1131
- break;
1132
- }
1133
- }
1134
- }
1177
+ if (shouldPreserveElement(element)) {
1178
+ continue;
1179
+ }
1180
+ const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1181
+ const updatedNode = updateOrCreateTextNode(
1182
+ element,
1183
+ oldNode,
1184
+ newText,
1185
+ insertBeforeNode
1186
+ );
1187
+ if (updatedNode) {
1188
+ processedNodes.add(updatedNode);
1189
+ insertionIndex.value++;
1135
1190
  }
1136
1191
  } else {
1137
- removeNodeIfNotPreserved(element, oldNode);
1192
+ const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1138
1193
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
1139
- replaceOrInsertElement(element, newChild, oldNode);
1194
+ if (oldNode && oldNode === targetNode && oldNode.parentNode === element) {
1195
+ element.replaceChild(newChild, oldNode);
1196
+ } else {
1197
+ element.insertBefore(newChild, targetNode);
1198
+ removeNodeIfNotPreserved(element, oldNode);
1199
+ }
1200
+ processedNodes.add(newChild);
1201
+ insertionIndex.value++;
1140
1202
  } else if (newChild instanceof DocumentFragment) {
1141
- element.insertBefore(newChild, oldNode || null);
1203
+ if (processedNodes) {
1204
+ for (let i2 = 0; i2 < newChild.childNodes.length; i2++) {
1205
+ processedNodes.add(newChild.childNodes[i2]);
1206
+ }
1207
+ }
1208
+ element.insertBefore(newChild, targetNode);
1209
+ removeNodeIfNotPreserved(element, oldNode);
1142
1210
  }
1143
1211
  }
1144
1212
  } else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
@@ -1146,49 +1214,36 @@ function updateChildren(element, oldChildren, newChildren, _cacheManager) {
1146
1214
  continue;
1147
1215
  }
1148
1216
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
1149
- let targetNextSibling = null;
1150
- let foundPreviousElement = false;
1151
- for (let j = i - 1; j >= 0; j--) {
1152
- const prevChild = flatNew[j];
1153
- if (prevChild instanceof HTMLElement || prevChild instanceof SVGElement) {
1154
- if (prevChild.parentNode === element) {
1155
- targetNextSibling = prevChild.nextSibling;
1156
- foundPreviousElement = true;
1157
- break;
1158
- }
1159
- }
1160
- }
1161
- if (!foundPreviousElement) {
1162
- const firstChild = Array.from(element.childNodes).find(
1163
- (node) => !shouldPreserveElement(node) && !processedNodes.has(node)
1164
- );
1165
- targetNextSibling = firstChild || null;
1166
- }
1167
- const isInCorrectPosition = newChild.parentNode === element && newChild.nextSibling === targetNextSibling;
1168
- if (newChild === oldChild && isInCorrectPosition) {
1169
- if (oldNode) processedNodes.add(oldNode);
1170
- processedNodes.add(newChild);
1171
- continue;
1172
- }
1173
- const referenceNode = oldNode && oldNode.parentNode === element ? oldNode : null;
1217
+ const insertBeforeNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1174
1218
  replaceOrInsertElementAtPosition(
1175
1219
  element,
1176
1220
  newChild,
1177
- referenceNode,
1178
- targetNextSibling
1221
+ oldNode,
1222
+ insertBeforeNode,
1223
+ processedNodes
1179
1224
  );
1180
- if (oldNode && oldNode !== newChild) {
1181
- processedNodes.delete(oldNode);
1182
- }
1183
- processedNodes.add(newChild);
1225
+ insertionIndex.value++;
1184
1226
  } else {
1185
- removeNodeIfNotPreserved(element, oldNode);
1227
+ const targetNode = insertionIndex.value < element.childNodes.length ? element.childNodes[insertionIndex.value] : null;
1186
1228
  if (typeof newChild === "string" || typeof newChild === "number") {
1187
1229
  const newTextNode = document.createTextNode(String(newChild));
1188
- element.insertBefore(newTextNode, oldNode?.nextSibling || null);
1230
+ newTextNode.__wsxManaged = true;
1231
+ if (oldNode && oldNode === targetNode && oldNode.parentNode === element) {
1232
+ element.replaceChild(newTextNode, oldNode);
1233
+ } else {
1234
+ element.insertBefore(newTextNode, targetNode);
1235
+ removeNodeIfNotPreserved(element, oldNode);
1236
+ }
1189
1237
  processedNodes.add(newTextNode);
1238
+ insertionIndex.value++;
1190
1239
  } else if (newChild instanceof DocumentFragment) {
1191
- element.insertBefore(newChild, oldNode?.nextSibling || null);
1240
+ if (processedNodes) {
1241
+ for (let i2 = 0; i2 < newChild.childNodes.length; i2++) {
1242
+ processedNodes.add(newChild.childNodes[i2]);
1243
+ }
1244
+ }
1245
+ element.insertBefore(newChild, targetNode);
1246
+ removeNodeIfNotPreserved(element, oldNode);
1192
1247
  }
1193
1248
  }
1194
1249
  }
@@ -1213,6 +1268,55 @@ function updateElement(element, newProps, newChildren, tag, cacheManager) {
1213
1268
  updateProps(element, oldProps, newProps, tag);
1214
1269
  updateChildren(element, oldChildren, newChildren, cacheManager);
1215
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
+ }
1216
1320
 
1217
1321
  // src/jsx-factory.ts
1218
1322
  var logger3 = createLogger("JSX Factory");
@@ -1310,7 +1414,9 @@ function Fragment(_props, children) {
1310
1414
  return;
1311
1415
  }
1312
1416
  if (typeof child === "string" || typeof child === "number") {
1313
- fragment.appendChild(document.createTextNode(String(child)));
1417
+ const textNode = document.createTextNode(String(child));
1418
+ textNode.__wsxManaged = true;
1419
+ fragment.appendChild(textNode);
1314
1420
  } else if (child instanceof HTMLElement || child instanceof SVGElement) {
1315
1421
  fragment.appendChild(child);
1316
1422
  } else if (child instanceof DocumentFragment) {
@@ -1841,7 +1947,7 @@ var BaseComponent = class extends HTMLElement {
1841
1947
  }
1842
1948
  }
1843
1949
  if (activeElement) {
1844
- 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");
1845
1951
  const forceRender = activeElement.hasAttribute("data-wsx-force-render");
1846
1952
  if (isInputElement && !forceRender) {
1847
1953
  this._pendingRerender = true;
@@ -1857,14 +1963,8 @@ var BaseComponent = class extends HTMLElement {
1857
1963
  }
1858
1964
  this._hasScheduledRender = true;
1859
1965
  requestAnimationFrame(() => {
1860
- console.warn("[scheduleRerender] RAF callback:", {
1861
- component: this.constructor.name,
1862
- connected: this.connected,
1863
- isRendering: this._isRendering
1864
- });
1865
1966
  this._hasScheduledRender = false;
1866
1967
  if (this.connected && !this._isRendering) {
1867
- console.warn("[scheduleRerender] calling _rerender()");
1868
1968
  this._isRendering = true;
1869
1969
  this._rerender();
1870
1970
  } else if (!this.connected) {
@@ -1893,7 +1993,8 @@ var BaseComponent = class extends HTMLElement {
1893
1993
  clearTimeout(this._rerenderDebounceTimer);
1894
1994
  this._rerenderDebounceTimer = null;
1895
1995
  }
1896
- document.removeEventListener("blur", this.handleGlobalBlur, true);
1996
+ const root = this.getActiveRoot();
1997
+ root.removeEventListener("blur", this.handleGlobalBlur, true);
1897
1998
  this._pendingRerender = false;
1898
1999
  }
1899
2000
  /**
@@ -1901,7 +2002,8 @@ var BaseComponent = class extends HTMLElement {
1901
2002
  * @internal
1902
2003
  */
1903
2004
  initializeEventListeners() {
1904
- document.addEventListener("blur", this.handleGlobalBlur, true);
2005
+ const root = this.getActiveRoot();
2006
+ root.addEventListener("blur", this.handleGlobalBlur, true);
1905
2007
  }
1906
2008
  /**
1907
2009
  * 获取配置值
@@ -2211,7 +2313,7 @@ var WebComponent = class extends BaseComponent {
2211
2313
  if (!isContentAlreadyInShadowRoot) {
2212
2314
  this.shadowRoot.appendChild(content);
2213
2315
  }
2214
- const oldChildren = Array.from(this.shadowRoot.children).filter((child) => {
2316
+ const oldNodes = Array.from(this.shadowRoot.childNodes).filter((child) => {
2215
2317
  if (child === content) {
2216
2318
  return false;
2217
2319
  }
@@ -2223,7 +2325,7 @@ var WebComponent = class extends BaseComponent {
2223
2325
  }
2224
2326
  return true;
2225
2327
  });
2226
- oldChildren.forEach((child) => child.remove());
2328
+ oldNodes.forEach((node) => node.remove());
2227
2329
  const hasStylesAfterDOM = this.shadowRoot.adoptedStyleSheets && this.shadowRoot.adoptedStyleSheets.length > 0;
2228
2330
  const hasStyleElementAfterDOM = Array.from(this.shadowRoot.children).some(
2229
2331
  (child) => child instanceof HTMLStyleElement
@@ -2358,6 +2460,10 @@ var LightComponent = class extends BaseComponent {
2358
2460
  querySelectorAll(selector) {
2359
2461
  return HTMLElement.prototype.querySelectorAll.call(this, selector);
2360
2462
  }
2463
+ /**
2464
+ * 递归协调子元素
2465
+ * 更新现有子元素的属性和内容,而不是替换整个子树
2466
+ */
2361
2467
  /**
2362
2468
  * 内部重渲染实现
2363
2469
  * 包含从 rerender() 方法迁移的实际渲染逻辑
@@ -2374,9 +2480,9 @@ var LightComponent = class extends BaseComponent {
2374
2480
  this._pendingFocusState = focusState;
2375
2481
  const jsxChildren = this.getJSXChildren();
2376
2482
  try {
2377
- const content = RenderContext.runInContext(this, () => this.render());
2483
+ const newContent = RenderContext.runInContext(this, () => this.render());
2378
2484
  if (focusState && focusState.key && focusState.value !== void 0) {
2379
- const target = content.querySelector(
2485
+ const target = newContent.querySelector(
2380
2486
  `[data-wsx-key="${focusState.key}"]`
2381
2487
  );
2382
2488
  if (target) {
@@ -2401,36 +2507,50 @@ var LightComponent = class extends BaseComponent {
2401
2507
  }
2402
2508
  }
2403
2509
  requestAnimationFrame(() => {
2404
- this.appendChild(content);
2405
2510
  const oldChildren = Array.from(this.children).filter((child) => {
2406
- if (child === content) {
2407
- return false;
2408
- }
2409
2511
  if (stylesToApply && child instanceof HTMLStyleElement && child.getAttribute("data-wsx-light-component") === styleName) {
2410
2512
  return false;
2411
2513
  }
2412
2514
  if (child instanceof HTMLElement && jsxChildren.includes(child)) {
2413
2515
  return false;
2414
2516
  }
2415
- if (shouldPreserveElement(child)) {
2416
- return false;
2417
- }
2418
2517
  return true;
2419
2518
  });
2420
- 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
+ }
2421
2541
  if (stylesToApply) {
2422
- let styleElement = this.querySelector(
2542
+ let styleEl = this.querySelector(
2423
2543
  `style[data-wsx-light-component="${styleName}"]`
2424
2544
  );
2425
- if (!styleElement) {
2426
- styleElement = document.createElement("style");
2427
- styleElement.setAttribute("data-wsx-light-component", styleName);
2428
- styleElement.textContent = stylesToApply;
2429
- this.insertBefore(styleElement, this.firstChild);
2430
- } else if (styleElement.textContent !== stylesToApply) {
2431
- styleElement.textContent = stylesToApply;
2432
- } else if (styleElement !== this.firstChild) {
2433
- 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);
2434
2554
  }
2435
2555
  }
2436
2556
  requestAnimationFrame(() => {
@@ -2634,6 +2754,7 @@ function state(targetOrContext, propertyKey) {
2634
2754
  jsx,
2635
2755
  jsxs,
2636
2756
  logger,
2757
+ parseHTMLToNodes,
2637
2758
  registerComponent,
2638
2759
  state
2639
2760
  });