@ktjs/core 0.17.1 → 0.17.3

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.d.ts CHANGED
@@ -69,6 +69,7 @@ interface KTBaseAttribute {
69
69
  // # normal HTML attributes
70
70
  id?: string;
71
71
  class?: string;
72
+ className?: string;
72
73
  style?: string | Partial<CSSStyleDeclaration>;
73
74
 
74
75
  type?:
@@ -171,7 +172,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
171
172
  * ## About
172
173
  * @package @ktjs/core
173
174
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
174
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
175
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
175
176
  * @license MIT
176
177
  * @link https://github.com/baendlorel/kt.js
177
178
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -56,7 +56,7 @@ var __ktjs_core__ = (function (exports) {
56
56
 
57
57
  const defaultHandler = (element, key, value) => element.setAttribute(key, value);
58
58
  function attrIsObject(element, attr) {
59
- const classValue = attr.class;
59
+ const classValue = attr.class || attr.className;
60
60
  if (classValue !== undefined) {
61
61
  element.setAttribute('class', classValue);
62
62
  }
@@ -72,7 +72,12 @@ var __ktjs_core__ = (function (exports) {
72
72
  }
73
73
  }
74
74
  for (const key in attr) {
75
- if (key === 'class' || key === 'style' || key === 'children' || key === 'k-if' || key === 'ref') {
75
+ if (key === 'class' ||
76
+ key === 'className' ||
77
+ key === 'style' ||
78
+ key === 'children' ||
79
+ key === 'k-if' ||
80
+ key === 'ref') {
76
81
  continue;
77
82
  }
78
83
  const o = attr[key];
@@ -211,7 +216,7 @@ var __ktjs_core__ = (function (exports) {
211
216
  * ## About
212
217
  * @package @ktjs/core
213
218
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
214
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
219
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
215
220
  * @license MIT
216
221
  * @link https://github.com/baendlorel/kt.js
217
222
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -422,6 +427,7 @@ var __ktjs_core__ = (function (exports) {
422
427
  if (!parent) {
423
428
  // If not in DOM yet, just rebuild the list
424
429
  const newElements = [];
430
+ nodeMap.clear();
425
431
  for (let index = 0; index < currentList.length; index++) {
426
432
  const item = currentList[index];
427
433
  const itemKey = currentKey(item, index, currentList);
@@ -432,46 +438,116 @@ var __ktjs_core__ = (function (exports) {
432
438
  anchor.__kt_for_list__ = newElements;
433
439
  return anchor;
434
440
  }
435
- // Build new key map
436
- const newNodeMap = new Map();
437
- const newKeys = new Set();
438
- const newElements = [];
439
- for (let index = 0; index < newList.length; index++) {
440
- const item = newList[index];
441
- const itemKey = newKey(item, index, newList);
442
- newKeys.add(itemKey);
443
- // Reuse existing node if key exists
441
+ const oldLength = anchor.__kt_for_list__.length;
442
+ const newLength = newList.length;
443
+ // Fast path: empty list
444
+ if (newLength === 0) {
445
+ nodeMap.forEach((node) => node.remove());
446
+ nodeMap.clear();
447
+ anchor.__kt_for_list__ = [];
448
+ return anchor;
449
+ }
450
+ // Fast path: all new items
451
+ if (oldLength === 0) {
452
+ const newElements = [];
453
+ const fragment = document.createDocumentFragment();
454
+ for (let i = 0; i < newLength; i++) {
455
+ const item = newList[i];
456
+ const itemKey = newKey(item, i, newList);
457
+ const node = newMap(item, i, newList);
458
+ nodeMap.set(itemKey, node);
459
+ newElements.push(node);
460
+ fragment.appendChild(node);
461
+ }
462
+ parent.insertBefore(fragment, anchor.nextSibling);
463
+ anchor.__kt_for_list__ = newElements;
464
+ return anchor;
465
+ }
466
+ // Build key index map and new elements array in one pass
467
+ const newKeyToNewIndex = new Map();
468
+ const newElements = new Array(newLength);
469
+ let maxNewIndexSoFar = 0;
470
+ let moved = false;
471
+ for (let i = 0; i < newLength; i++) {
472
+ const item = newList[i];
473
+ const itemKey = newKey(item, i, newList);
474
+ newKeyToNewIndex.set(itemKey, i);
444
475
  if (nodeMap.has(itemKey)) {
445
- const existingNode = nodeMap.get(itemKey);
446
- newNodeMap.set(itemKey, existingNode);
447
- newElements.push(existingNode);
476
+ // Reuse existing node
477
+ const node = nodeMap.get(itemKey);
478
+ newElements[i] = node;
479
+ // Track if items moved
480
+ if (i < maxNewIndexSoFar) {
481
+ moved = true;
482
+ }
483
+ else {
484
+ maxNewIndexSoFar = i;
485
+ }
448
486
  }
449
487
  else {
450
488
  // Create new node
451
- const node = newMap(item, index, newList);
452
- newNodeMap.set(itemKey, node);
453
- newElements.push(node);
489
+ newElements[i] = newMap(item, i, newList);
454
490
  }
455
491
  }
456
- // Remove nodes that are no longer in the list
492
+ // Remove nodes not in new list
493
+ const toRemove = [];
457
494
  nodeMap.forEach((node, key) => {
458
- if (!newKeys.has(key)) {
459
- node.remove();
495
+ if (!newKeyToNewIndex.has(key)) {
496
+ toRemove.push(node);
460
497
  }
461
498
  });
462
- // Insert/reorder nodes in correct order
463
- let referenceNode = anchor.nextSibling;
464
- for (let index = 0; index < newElements.length; index++) {
465
- const node = newElements[index];
466
- // If node is not in correct position, insert it
467
- if (referenceNode !== node) {
468
- parent.insertBefore(node, referenceNode);
499
+ for (let i = 0; i < toRemove.length; i++) {
500
+ toRemove[i].remove();
501
+ }
502
+ // Update DOM with minimal operations
503
+ if (moved) {
504
+ // Use longest increasing subsequence to minimize moves
505
+ const seq = getSequence(newElements.map((el, i) => (nodeMap.has(newKey(newList[i], i, newList)) ? i : -1)));
506
+ let j = seq.length - 1;
507
+ let anchor = null;
508
+ // Traverse from end to start for stable insertions
509
+ for (let i = newLength - 1; i >= 0; i--) {
510
+ const node = newElements[i];
511
+ if (j < 0 || i !== seq[j]) {
512
+ // Node needs to be moved or inserted
513
+ if (anchor) {
514
+ parent.insertBefore(node, anchor);
515
+ }
516
+ else {
517
+ // Insert at end
518
+ let nextSibling = anchor.nextSibling;
519
+ let temp = nextSibling;
520
+ while (temp && newElements.includes(temp)) {
521
+ temp = temp.nextSibling;
522
+ }
523
+ parent.insertBefore(node, temp);
524
+ }
525
+ }
526
+ else {
527
+ j--;
528
+ }
529
+ anchor = node;
469
530
  }
470
- referenceNode = node.nextSibling;
471
531
  }
472
- // Update node map and element list
532
+ else {
533
+ // No moves needed, just insert new nodes
534
+ let currentNode = anchor.nextSibling;
535
+ for (let i = 0; i < newLength; i++) {
536
+ const node = newElements[i];
537
+ if (currentNode !== node) {
538
+ parent.insertBefore(node, currentNode);
539
+ }
540
+ else {
541
+ currentNode = currentNode.nextSibling;
542
+ }
543
+ }
544
+ }
545
+ // Update maps
473
546
  nodeMap.clear();
474
- newNodeMap.forEach((node, key) => nodeMap.set(key, node));
547
+ for (let i = 0; i < newLength; i++) {
548
+ const itemKey = newKey(newList[i], i, newList);
549
+ nodeMap.set(itemKey, newElements[i]);
550
+ }
475
551
  anchor.__kt_for_list__ = newElements;
476
552
  return anchor;
477
553
  };
@@ -481,6 +557,48 @@ var __ktjs_core__ = (function (exports) {
481
557
  }
482
558
  return anchor;
483
559
  }
560
+ // Longest Increasing Subsequence algorithm (optimized for diff)
561
+ function getSequence(arr) {
562
+ const p = arr.slice();
563
+ const result = [0];
564
+ let i, j, u, v, c;
565
+ const len = arr.length;
566
+ for (i = 0; i < len; i++) {
567
+ const arrI = arr[i];
568
+ if (arrI === -1)
569
+ continue;
570
+ j = result[result.length - 1];
571
+ if (arr[j] < arrI) {
572
+ p[i] = j;
573
+ result.push(i);
574
+ continue;
575
+ }
576
+ u = 0;
577
+ v = result.length - 1;
578
+ while (u < v) {
579
+ c = ((u + v) / 2) | 0;
580
+ if (arr[result[c]] < arrI) {
581
+ u = c + 1;
582
+ }
583
+ else {
584
+ v = c;
585
+ }
586
+ }
587
+ if (arrI < arr[result[u]]) {
588
+ if (u > 0) {
589
+ p[i] = result[u - 1];
590
+ }
591
+ result[u] = i;
592
+ }
593
+ }
594
+ u = result.length;
595
+ v = result[u - 1];
596
+ while (u-- > 0) {
597
+ result[u] = v;
598
+ v = p[v];
599
+ }
600
+ return result;
601
+ }
484
602
 
485
603
  exports.Fragment = Fragment;
486
604
  exports.KTAsync = KTAsync;
@@ -68,7 +68,7 @@ var __ktjs_core__ = (function (exports) {
68
68
 
69
69
  var defaultHandler = function (element, key, value) { return element.setAttribute(key, value); };
70
70
  function attrIsObject(element, attr) {
71
- var classValue = attr.class;
71
+ var classValue = attr.class || attr.className;
72
72
  if (classValue !== undefined) {
73
73
  element.setAttribute('class', classValue);
74
74
  }
@@ -84,7 +84,12 @@ var __ktjs_core__ = (function (exports) {
84
84
  }
85
85
  }
86
86
  for (var key in attr) {
87
- if (key === 'class' || key === 'style' || key === 'children' || key === 'k-if' || key === 'ref') {
87
+ if (key === 'class' ||
88
+ key === 'className' ||
89
+ key === 'style' ||
90
+ key === 'children' ||
91
+ key === 'k-if' ||
92
+ key === 'ref') {
88
93
  continue;
89
94
  }
90
95
  var o = attr[key];
@@ -236,7 +241,7 @@ var __ktjs_core__ = (function (exports) {
236
241
  * ## About
237
242
  * @package @ktjs/core
238
243
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
239
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
244
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
240
245
  * @license MIT
241
246
  * @link https://github.com/baendlorel/kt.js
242
247
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -490,6 +495,7 @@ var __ktjs_core__ = (function (exports) {
490
495
  if (!parent) {
491
496
  // If not in DOM yet, just rebuild the list
492
497
  var newElements_1 = [];
498
+ nodeMap.clear();
493
499
  for (var index = 0; index < currentList.length; index++) {
494
500
  var item = currentList[index];
495
501
  var itemKey = currentKey(item, index, currentList);
@@ -500,46 +506,116 @@ var __ktjs_core__ = (function (exports) {
500
506
  anchor.__kt_for_list__ = newElements_1;
501
507
  return anchor;
502
508
  }
503
- // Build new key map
504
- var newNodeMap = new Map();
505
- var newKeys = new Set();
506
- var newElements = [];
507
- for (var index = 0; index < newList.length; index++) {
508
- var item = newList[index];
509
- var itemKey = newKey(item, index, newList);
510
- newKeys.add(itemKey);
511
- // Reuse existing node if key exists
509
+ var oldLength = anchor.__kt_for_list__.length;
510
+ var newLength = newList.length;
511
+ // Fast path: empty list
512
+ if (newLength === 0) {
513
+ nodeMap.forEach(function (node) { return node.remove(); });
514
+ nodeMap.clear();
515
+ anchor.__kt_for_list__ = [];
516
+ return anchor;
517
+ }
518
+ // Fast path: all new items
519
+ if (oldLength === 0) {
520
+ var newElements_2 = [];
521
+ var fragment = document.createDocumentFragment();
522
+ for (var i = 0; i < newLength; i++) {
523
+ var item = newList[i];
524
+ var itemKey = newKey(item, i, newList);
525
+ var node = newMap(item, i, newList);
526
+ nodeMap.set(itemKey, node);
527
+ newElements_2.push(node);
528
+ fragment.appendChild(node);
529
+ }
530
+ parent.insertBefore(fragment, anchor.nextSibling);
531
+ anchor.__kt_for_list__ = newElements_2;
532
+ return anchor;
533
+ }
534
+ // Build key index map and new elements array in one pass
535
+ var newKeyToNewIndex = new Map();
536
+ var newElements = new Array(newLength);
537
+ var maxNewIndexSoFar = 0;
538
+ var moved = false;
539
+ for (var i = 0; i < newLength; i++) {
540
+ var item = newList[i];
541
+ var itemKey = newKey(item, i, newList);
542
+ newKeyToNewIndex.set(itemKey, i);
512
543
  if (nodeMap.has(itemKey)) {
513
- var existingNode = nodeMap.get(itemKey);
514
- newNodeMap.set(itemKey, existingNode);
515
- newElements.push(existingNode);
544
+ // Reuse existing node
545
+ var node = nodeMap.get(itemKey);
546
+ newElements[i] = node;
547
+ // Track if items moved
548
+ if (i < maxNewIndexSoFar) {
549
+ moved = true;
550
+ }
551
+ else {
552
+ maxNewIndexSoFar = i;
553
+ }
516
554
  }
517
555
  else {
518
556
  // Create new node
519
- var node = newMap(item, index, newList);
520
- newNodeMap.set(itemKey, node);
521
- newElements.push(node);
557
+ newElements[i] = newMap(item, i, newList);
522
558
  }
523
559
  }
524
- // Remove nodes that are no longer in the list
560
+ // Remove nodes not in new list
561
+ var toRemove = [];
525
562
  nodeMap.forEach(function (node, key) {
526
- if (!newKeys.has(key)) {
527
- node.remove();
563
+ if (!newKeyToNewIndex.has(key)) {
564
+ toRemove.push(node);
528
565
  }
529
566
  });
530
- // Insert/reorder nodes in correct order
531
- var referenceNode = anchor.nextSibling;
532
- for (var index = 0; index < newElements.length; index++) {
533
- var node = newElements[index];
534
- // If node is not in correct position, insert it
535
- if (referenceNode !== node) {
536
- parent.insertBefore(node, referenceNode);
567
+ for (var i = 0; i < toRemove.length; i++) {
568
+ toRemove[i].remove();
569
+ }
570
+ // Update DOM with minimal operations
571
+ if (moved) {
572
+ // Use longest increasing subsequence to minimize moves
573
+ var seq = getSequence(newElements.map(function (el, i) { return (nodeMap.has(newKey(newList[i], i, newList)) ? i : -1); }));
574
+ var j = seq.length - 1;
575
+ var anchor_1 = null;
576
+ // Traverse from end to start for stable insertions
577
+ for (var i = newLength - 1; i >= 0; i--) {
578
+ var node = newElements[i];
579
+ if (j < 0 || i !== seq[j]) {
580
+ // Node needs to be moved or inserted
581
+ if (anchor_1) {
582
+ parent.insertBefore(node, anchor_1);
583
+ }
584
+ else {
585
+ // Insert at end
586
+ var nextSibling = anchor_1.nextSibling;
587
+ var temp = nextSibling;
588
+ while (temp && newElements.includes(temp)) {
589
+ temp = temp.nextSibling;
590
+ }
591
+ parent.insertBefore(node, temp);
592
+ }
593
+ }
594
+ else {
595
+ j--;
596
+ }
597
+ anchor_1 = node;
537
598
  }
538
- referenceNode = node.nextSibling;
539
599
  }
540
- // Update node map and element list
600
+ else {
601
+ // No moves needed, just insert new nodes
602
+ var currentNode = anchor.nextSibling;
603
+ for (var i = 0; i < newLength; i++) {
604
+ var node = newElements[i];
605
+ if (currentNode !== node) {
606
+ parent.insertBefore(node, currentNode);
607
+ }
608
+ else {
609
+ currentNode = currentNode.nextSibling;
610
+ }
611
+ }
612
+ }
613
+ // Update maps
541
614
  nodeMap.clear();
542
- newNodeMap.forEach(function (node, key) { return nodeMap.set(key, node); });
615
+ for (var i = 0; i < newLength; i++) {
616
+ var itemKey = newKey(newList[i], i, newList);
617
+ nodeMap.set(itemKey, newElements[i]);
618
+ }
543
619
  anchor.__kt_for_list__ = newElements;
544
620
  return anchor;
545
621
  };
@@ -549,6 +625,48 @@ var __ktjs_core__ = (function (exports) {
549
625
  }
550
626
  return anchor;
551
627
  }
628
+ // Longest Increasing Subsequence algorithm (optimized for diff)
629
+ function getSequence(arr) {
630
+ var p = arr.slice();
631
+ var result = [0];
632
+ var i, j, u, v, c;
633
+ var len = arr.length;
634
+ for (i = 0; i < len; i++) {
635
+ var arrI = arr[i];
636
+ if (arrI === -1)
637
+ continue;
638
+ j = result[result.length - 1];
639
+ if (arr[j] < arrI) {
640
+ p[i] = j;
641
+ result.push(i);
642
+ continue;
643
+ }
644
+ u = 0;
645
+ v = result.length - 1;
646
+ while (u < v) {
647
+ c = ((u + v) / 2) | 0;
648
+ if (arr[result[c]] < arrI) {
649
+ u = c + 1;
650
+ }
651
+ else {
652
+ v = c;
653
+ }
654
+ }
655
+ if (arrI < arr[result[u]]) {
656
+ if (u > 0) {
657
+ p[i] = result[u - 1];
658
+ }
659
+ result[u] = i;
660
+ }
661
+ }
662
+ u = result.length;
663
+ v = result[u - 1];
664
+ while (u-- > 0) {
665
+ result[u] = v;
666
+ v = p[v];
667
+ }
668
+ return result;
669
+ }
552
670
 
553
671
  exports.Fragment = Fragment;
554
672
  exports.KTAsync = KTAsync;
package/dist/index.mjs CHANGED
@@ -53,7 +53,7 @@ const ktEventHandlers = {
53
53
 
54
54
  const defaultHandler = (element, key, value) => element.setAttribute(key, value);
55
55
  function attrIsObject(element, attr) {
56
- const classValue = attr.class;
56
+ const classValue = attr.class || attr.className;
57
57
  if (classValue !== undefined) {
58
58
  element.setAttribute('class', classValue);
59
59
  }
@@ -69,7 +69,12 @@ function attrIsObject(element, attr) {
69
69
  }
70
70
  }
71
71
  for (const key in attr) {
72
- if (key === 'class' || key === 'style' || key === 'children' || key === 'k-if' || key === 'ref') {
72
+ if (key === 'class' ||
73
+ key === 'className' ||
74
+ key === 'style' ||
75
+ key === 'children' ||
76
+ key === 'k-if' ||
77
+ key === 'ref') {
73
78
  continue;
74
79
  }
75
80
  const o = attr[key];
@@ -208,7 +213,7 @@ let creator = defaultCreator;
208
213
  * ## About
209
214
  * @package @ktjs/core
210
215
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
211
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
216
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
212
217
  * @license MIT
213
218
  * @link https://github.com/baendlorel/kt.js
214
219
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -419,6 +424,7 @@ function KTFor(props) {
419
424
  if (!parent) {
420
425
  // If not in DOM yet, just rebuild the list
421
426
  const newElements = [];
427
+ nodeMap.clear();
422
428
  for (let index = 0; index < currentList.length; index++) {
423
429
  const item = currentList[index];
424
430
  const itemKey = currentKey(item, index, currentList);
@@ -429,46 +435,116 @@ function KTFor(props) {
429
435
  anchor.__kt_for_list__ = newElements;
430
436
  return anchor;
431
437
  }
432
- // Build new key map
433
- const newNodeMap = new Map();
434
- const newKeys = new Set();
435
- const newElements = [];
436
- for (let index = 0; index < newList.length; index++) {
437
- const item = newList[index];
438
- const itemKey = newKey(item, index, newList);
439
- newKeys.add(itemKey);
440
- // Reuse existing node if key exists
438
+ const oldLength = anchor.__kt_for_list__.length;
439
+ const newLength = newList.length;
440
+ // Fast path: empty list
441
+ if (newLength === 0) {
442
+ nodeMap.forEach((node) => node.remove());
443
+ nodeMap.clear();
444
+ anchor.__kt_for_list__ = [];
445
+ return anchor;
446
+ }
447
+ // Fast path: all new items
448
+ if (oldLength === 0) {
449
+ const newElements = [];
450
+ const fragment = document.createDocumentFragment();
451
+ for (let i = 0; i < newLength; i++) {
452
+ const item = newList[i];
453
+ const itemKey = newKey(item, i, newList);
454
+ const node = newMap(item, i, newList);
455
+ nodeMap.set(itemKey, node);
456
+ newElements.push(node);
457
+ fragment.appendChild(node);
458
+ }
459
+ parent.insertBefore(fragment, anchor.nextSibling);
460
+ anchor.__kt_for_list__ = newElements;
461
+ return anchor;
462
+ }
463
+ // Build key index map and new elements array in one pass
464
+ const newKeyToNewIndex = new Map();
465
+ const newElements = new Array(newLength);
466
+ let maxNewIndexSoFar = 0;
467
+ let moved = false;
468
+ for (let i = 0; i < newLength; i++) {
469
+ const item = newList[i];
470
+ const itemKey = newKey(item, i, newList);
471
+ newKeyToNewIndex.set(itemKey, i);
441
472
  if (nodeMap.has(itemKey)) {
442
- const existingNode = nodeMap.get(itemKey);
443
- newNodeMap.set(itemKey, existingNode);
444
- newElements.push(existingNode);
473
+ // Reuse existing node
474
+ const node = nodeMap.get(itemKey);
475
+ newElements[i] = node;
476
+ // Track if items moved
477
+ if (i < maxNewIndexSoFar) {
478
+ moved = true;
479
+ }
480
+ else {
481
+ maxNewIndexSoFar = i;
482
+ }
445
483
  }
446
484
  else {
447
485
  // Create new node
448
- const node = newMap(item, index, newList);
449
- newNodeMap.set(itemKey, node);
450
- newElements.push(node);
486
+ newElements[i] = newMap(item, i, newList);
451
487
  }
452
488
  }
453
- // Remove nodes that are no longer in the list
489
+ // Remove nodes not in new list
490
+ const toRemove = [];
454
491
  nodeMap.forEach((node, key) => {
455
- if (!newKeys.has(key)) {
456
- node.remove();
492
+ if (!newKeyToNewIndex.has(key)) {
493
+ toRemove.push(node);
457
494
  }
458
495
  });
459
- // Insert/reorder nodes in correct order
460
- let referenceNode = anchor.nextSibling;
461
- for (let index = 0; index < newElements.length; index++) {
462
- const node = newElements[index];
463
- // If node is not in correct position, insert it
464
- if (referenceNode !== node) {
465
- parent.insertBefore(node, referenceNode);
496
+ for (let i = 0; i < toRemove.length; i++) {
497
+ toRemove[i].remove();
498
+ }
499
+ // Update DOM with minimal operations
500
+ if (moved) {
501
+ // Use longest increasing subsequence to minimize moves
502
+ const seq = getSequence(newElements.map((el, i) => (nodeMap.has(newKey(newList[i], i, newList)) ? i : -1)));
503
+ let j = seq.length - 1;
504
+ let anchor = null;
505
+ // Traverse from end to start for stable insertions
506
+ for (let i = newLength - 1; i >= 0; i--) {
507
+ const node = newElements[i];
508
+ if (j < 0 || i !== seq[j]) {
509
+ // Node needs to be moved or inserted
510
+ if (anchor) {
511
+ parent.insertBefore(node, anchor);
512
+ }
513
+ else {
514
+ // Insert at end
515
+ let nextSibling = anchor.nextSibling;
516
+ let temp = nextSibling;
517
+ while (temp && newElements.includes(temp)) {
518
+ temp = temp.nextSibling;
519
+ }
520
+ parent.insertBefore(node, temp);
521
+ }
522
+ }
523
+ else {
524
+ j--;
525
+ }
526
+ anchor = node;
466
527
  }
467
- referenceNode = node.nextSibling;
468
528
  }
469
- // Update node map and element list
529
+ else {
530
+ // No moves needed, just insert new nodes
531
+ let currentNode = anchor.nextSibling;
532
+ for (let i = 0; i < newLength; i++) {
533
+ const node = newElements[i];
534
+ if (currentNode !== node) {
535
+ parent.insertBefore(node, currentNode);
536
+ }
537
+ else {
538
+ currentNode = currentNode.nextSibling;
539
+ }
540
+ }
541
+ }
542
+ // Update maps
470
543
  nodeMap.clear();
471
- newNodeMap.forEach((node, key) => nodeMap.set(key, node));
544
+ for (let i = 0; i < newLength; i++) {
545
+ const itemKey = newKey(newList[i], i, newList);
546
+ nodeMap.set(itemKey, newElements[i]);
547
+ }
472
548
  anchor.__kt_for_list__ = newElements;
473
549
  return anchor;
474
550
  };
@@ -478,5 +554,47 @@ function KTFor(props) {
478
554
  }
479
555
  return anchor;
480
556
  }
557
+ // Longest Increasing Subsequence algorithm (optimized for diff)
558
+ function getSequence(arr) {
559
+ const p = arr.slice();
560
+ const result = [0];
561
+ let i, j, u, v, c;
562
+ const len = arr.length;
563
+ for (i = 0; i < len; i++) {
564
+ const arrI = arr[i];
565
+ if (arrI === -1)
566
+ continue;
567
+ j = result[result.length - 1];
568
+ if (arr[j] < arrI) {
569
+ p[i] = j;
570
+ result.push(i);
571
+ continue;
572
+ }
573
+ u = 0;
574
+ v = result.length - 1;
575
+ while (u < v) {
576
+ c = ((u + v) / 2) | 0;
577
+ if (arr[result[c]] < arrI) {
578
+ u = c + 1;
579
+ }
580
+ else {
581
+ v = c;
582
+ }
583
+ }
584
+ if (arrI < arr[result[u]]) {
585
+ if (u > 0) {
586
+ p[i] = result[u - 1];
587
+ }
588
+ result[u] = i;
589
+ }
590
+ }
591
+ u = result.length;
592
+ v = result[u - 1];
593
+ while (u-- > 0) {
594
+ result[u] = v;
595
+ v = p[v];
596
+ }
597
+ return result;
598
+ }
481
599
 
482
600
  export { Fragment, KTAsync, KTFor, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
@@ -63,6 +63,7 @@ interface KTBaseAttribute {
63
63
  // # normal HTML attributes
64
64
  id?: string;
65
65
  class?: string;
66
+ className?: string;
66
67
  style?: string | Partial<CSSStyleDeclaration>;
67
68
 
68
69
  type?:
@@ -157,7 +158,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
157
158
  * ## About
158
159
  * @package @ktjs/core
159
160
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
160
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
161
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
161
162
  * @license MIT
162
163
  * @link https://github.com/baendlorel/kt.js
163
164
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -53,7 +53,7 @@ const ktEventHandlers = {
53
53
 
54
54
  const defaultHandler = (element, key, value) => element.setAttribute(key, value);
55
55
  function attrIsObject(element, attr) {
56
- const classValue = attr.class;
56
+ const classValue = attr.class || attr.className;
57
57
  if (classValue !== undefined) {
58
58
  element.setAttribute('class', classValue);
59
59
  }
@@ -69,7 +69,12 @@ function attrIsObject(element, attr) {
69
69
  }
70
70
  }
71
71
  for (const key in attr) {
72
- if (key === 'class' || key === 'style' || key === 'children' || key === 'k-if' || key === 'ref') {
72
+ if (key === 'class' ||
73
+ key === 'className' ||
74
+ key === 'style' ||
75
+ key === 'children' ||
76
+ key === 'k-if' ||
77
+ key === 'ref') {
73
78
  continue;
74
79
  }
75
80
  const o = attr[key];
@@ -208,7 +213,7 @@ let creator = defaultCreator;
208
213
  * ## About
209
214
  * @package @ktjs/core
210
215
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
211
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
216
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
212
217
  * @license MIT
213
218
  * @link https://github.com/baendlorel/kt.js
214
219
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -57,6 +57,7 @@ interface KTBaseAttribute {
57
57
  // # normal HTML attributes
58
58
  id?: string;
59
59
  class?: string;
60
+ className?: string;
60
61
  style?: string | Partial<CSSStyleDeclaration>;
61
62
 
62
63
  type?:
@@ -151,7 +152,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
151
152
  * ## About
152
153
  * @package @ktjs/core
153
154
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
154
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
155
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
155
156
  * @license MIT
156
157
  * @link https://github.com/baendlorel/kt.js
157
158
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -53,7 +53,7 @@ const ktEventHandlers = {
53
53
 
54
54
  const defaultHandler = (element, key, value) => element.setAttribute(key, value);
55
55
  function attrIsObject(element, attr) {
56
- const classValue = attr.class;
56
+ const classValue = attr.class || attr.className;
57
57
  if (classValue !== undefined) {
58
58
  element.setAttribute('class', classValue);
59
59
  }
@@ -69,7 +69,12 @@ function attrIsObject(element, attr) {
69
69
  }
70
70
  }
71
71
  for (const key in attr) {
72
- if (key === 'class' || key === 'style' || key === 'children' || key === 'k-if' || key === 'ref') {
72
+ if (key === 'class' ||
73
+ key === 'className' ||
74
+ key === 'style' ||
75
+ key === 'children' ||
76
+ key === 'k-if' ||
77
+ key === 'ref') {
73
78
  continue;
74
79
  }
75
80
  const o = attr[key];
@@ -208,7 +213,7 @@ let creator = defaultCreator;
208
213
  * ## About
209
214
  * @package @ktjs/core
210
215
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
211
- * @version 0.17.1 (Last Update: 2026.01.28 15:22:11.881)
216
+ * @version 0.17.3 (Last Update: 2026.01.28 16:50:39.470)
212
217
  * @license MIT
213
218
  * @link https://github.com/baendlorel/kt.js
214
219
  * @link https://baendlorel.github.io/ Welcome to my site!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/core",
3
- "version": "0.17.1",
3
+ "version": "0.17.3",
4
4
  "description": "Core functionality for kt.js - DOM manipulation utilities with JSX/TSX support",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",