@ktjs/core 0.16.0 → 0.17.0

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/README.md CHANGED
@@ -29,11 +29,10 @@ Core DOM manipulation utilities for KT.js framework with built-in JSX/TSX suppor
29
29
  - **k-if directive**: Conditional element creation with `k-if` attribute
30
30
  - Array children support for seamless list rendering
31
31
  - **KTFor Component**: Efficient list rendering with key-based optimization (v0.16.0)
32
- - Key-based DOM reuse similar to Svelte's `{#each}` blocks
33
- - Minimal DOM operations - only updates what changed
34
- - Returns Comment anchor node for flexible positioning
32
+ - Returns Comment anchor with `__kt_for_list__` array storing rendered elements
33
+ - Auto-appends list items when anchor is added via `applyContent`
34
+ - Key-based DOM reuse with customizable key function (default: identity)
35
35
  - Type-safe with full TypeScript generics support
36
- - `KTForStatic` variant for simple lists without key optimization
37
36
  - **KTAsync Component**: Handle async components with ease
38
37
  - Automatic handling of Promise-based components
39
38
  - Seamless integration with JSX/TSX
@@ -211,7 +210,7 @@ div.redraw(undefined, 'New content');
211
210
  The `KTFor` component provides efficient list rendering with key-based DOM reuse:
212
211
 
213
212
  ```tsx
214
- import { KTFor, KTForStatic, KTForAnchor } from '@ktjs/core';
213
+ import { KTFor } from '@ktjs/core';
215
214
 
216
215
  interface Todo {
217
216
  id: number;
@@ -228,8 +227,8 @@ let todos: Todo[] = [
228
227
  const todoList = (
229
228
  <KTFor
230
229
  list={todos}
231
- key={(item) => item.id} // Stable key for efficient updates
232
- mapper={(item, index) => (
230
+ key={(item) => item.id} // Optional, defaults to identity function
231
+ map={(item, index) => (
233
232
  <div class={`todo ${item.done ? 'done' : ''}`}>
234
233
  <input type="checkbox" checked={item.done} />
235
234
  <span>{item.text}</span>
@@ -237,28 +236,22 @@ const todoList = (
237
236
  </div>
238
237
  )}
239
238
  />
240
- ) as KTForAnchor;
239
+ );
241
240
 
242
- // Add to DOM (anchor + all items are rendered)
241
+ // Add to DOM - anchor comment + __kt_for_list__ items are appended
243
242
  document.body.appendChild(todoList);
244
243
 
245
244
  // Update the list - only changed items are updated
246
245
  todos = [...todos, { id: 3, text: 'New task', done: false }];
247
246
  todoList.redraw({ list: todos });
248
-
249
- // For simple static lists, use KTForStatic (no key optimization)
250
- const simpleList = (
251
- <KTForStatic list={['Apple', 'Banana', 'Orange']} mapper={(item) => <div>{item}</div>} />
252
- ) as KTForAnchor;
253
247
  ```
254
248
 
255
249
  **How it works:**
256
250
 
257
- - Returns a Comment node (`<!-- kt-for -->`) as anchor point
258
- - All list items are rendered as siblings after the anchor
259
- - Uses key-based diff algorithm to reuse DOM nodes
251
+ - Returns Comment anchor (`<!-- kt-for -->`) with `__kt_for_list__` array property
252
+ - When appended via `applyContent`, anchor and all list items are added to DOM
253
+ - Uses key-based diff to reuse DOM nodes on `redraw()`
260
254
  - Only adds/removes/moves nodes that changed
261
- - Similar to Svelte's `{#each}` blocks
262
255
 
263
256
  ````
264
257
 
package/dist/index.d.ts CHANGED
@@ -17,12 +17,13 @@ interface KTRef<T> {
17
17
  */
18
18
  declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
19
19
 
20
- type KTHTMLElement<El extends HTMLElement = HTMLElement> = El & {
20
+ type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
21
+ type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
21
22
  /**
22
23
  * Automically generate a redraw function if it is not provided
23
24
  * @param props
24
25
  */
25
- redraw: (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
26
+ redraw: R;
26
27
  };
27
28
 
28
29
  declare global {
@@ -44,7 +45,7 @@ declare global {
44
45
  }
45
46
  }
46
47
 
47
- type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
48
+ type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
48
49
  type KTAvailableContent = SingleContent | KTAvailableContent[];
49
50
  type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
50
51
  type KTRawAttr = KTAttribute | string;
@@ -61,9 +62,11 @@ type EventHandler<T extends Event = Event> = (this: HTMLElement, ev: T) => any;
61
62
  interface KTBaseAttribute {
62
63
  [k: string]: any;
63
64
 
65
+ // # kt-specific attributes
64
66
  ref?: KTRef<HTMLElement>;
65
67
  'k-if'?: any;
66
68
 
69
+ // # normal HTML attributes
67
70
  id?: string;
68
71
  class?: string;
69
72
  style?: string | Partial<CSSStyleDeclaration>;
@@ -168,7 +171,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
168
171
  * ## About
169
172
  * @package @ktjs/core
170
173
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
171
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
174
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
172
175
  * @license MIT
173
176
  * @link https://github.com/baendlorel/kt.js
174
177
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -242,63 +245,21 @@ declare function KTAsync<T extends KTComponent>(props: {
242
245
  children?: KTRawContent;
243
246
  } & ExtractComponentProps<T>): KTHTMLElement;
244
247
 
248
+ type KForElement = KTHTMLElement & {
249
+ __kt_for_list__: HTMLElement[];
250
+ redraw: (newProps?: KTAttribute) => void;
251
+ };
245
252
  interface KTForProps<T> {
253
+ ref?: KTRef<KForElement>;
246
254
  list: T[];
247
- /**
248
- * Key function to uniquely identify each item
249
- * Using stable keys enables efficient DOM reuse and updates
250
- */
251
- key: (item: T, index: number) => string | number;
252
- /**
253
- * Mapper function to render each item
254
- */
255
- mapper: (item: T, index: number) => HTMLElement;
256
- }
257
- /**
258
- * Extended Comment node with redraw capability for KTFor
259
- */
260
- interface KTForAnchor extends Comment {
261
- redraw: (newProps?: {
262
- list?: any[];
263
- mapper?: (item: any, index: number) => HTMLElement;
264
- }) => void;
255
+ key?: (item: T, index: number, array: T[]) => any;
256
+ map: (item: T, index: number, array: T[]) => HTMLElement;
265
257
  }
266
258
  /**
267
- * KTFor component - Efficient list rendering with key-based DOM reuse
268
- * Similar to Svelte's {#each} blocks, provides optimized updates
269
- *
270
- * Returns a Comment node as anchor point. All list items are rendered after this anchor.
271
- *
272
- * @example
273
- * ```tsx
274
- * const listAnchor = <KTFor
275
- * list={items}
276
- * key={(item) => item.id}
277
- * mapper={(item) => <div>{item.name}</div>}
278
- * /> as KTForAnchor;
279
- *
280
- * document.body.appendChild(listAnchor); // Anchor + all items are rendered
281
- *
282
- * // Update the list
283
- * listAnchor.redraw({ list: newItems });
284
- * ```
285
- */
286
- declare function KTFor<T>(props: KTForProps<T>): KTForAnchor;
287
- /**
288
- * Simple list rendering without key optimization
289
- * Rebuilds the entire list on each update - use for small static lists
290
- *
291
- * Returns a Comment anchor node. All items are rendered after this anchor.
292
- *
293
- * @example
294
- * ```tsx
295
- * const listAnchor = <KTForStatic
296
- * list={items}
297
- * mapper={(item) => <div>{item}</div>}
298
- * /> as KTForAnchor;
299
- * ```
259
+ * KTFor - List rendering component with key-based optimization
260
+ * Returns a Comment anchor node with rendered elements in __kt_for_list__
300
261
  */
301
- declare function KTForStatic<T>(props: Omit<KTForProps<T>, 'key'>): KTForAnchor;
262
+ declare function KTFor<T>(props: KTForProps<T>): KForElement;
302
263
 
303
- export { Fragment, KTAsync, KTFor, KTForStatic, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
304
- export type { EventHandler, HTMLTag, KTAttribute, KTForAnchor, KTForProps, KTHTMLElement, KTRawAttr, KTRawContent, KTRawContents, KTRef };
264
+ export { Fragment, KTAsync, KTFor, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
265
+ export type { EventHandler, HTMLTag, KTAttribute, KTForProps, KTHTMLElement, KTRawAttr, KTRawContent, KTRawContents, KTRef, Redraw };
@@ -158,6 +158,11 @@ var __ktjs_core__ = (function (exports) {
158
158
  }
159
159
  else {
160
160
  $append.call(element, c);
161
+ // Handle KTFor anchor
162
+ const list = c.__kt_for_list__;
163
+ if ($isArray(list)) {
164
+ apd(element, list);
165
+ }
161
166
  }
162
167
  }
163
168
  function apd(element, c) {
@@ -206,7 +211,7 @@ var __ktjs_core__ = (function (exports) {
206
211
  * ## About
207
212
  * @package @ktjs/core
208
213
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
209
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
214
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
210
215
  * @license MIT
211
216
  * @link https://github.com/baendlorel/kt.js
212
217
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -379,205 +384,107 @@ var __ktjs_core__ = (function (exports) {
379
384
  }
380
385
 
381
386
  /**
382
- * KTFor component - Efficient list rendering with key-based DOM reuse
383
- * Similar to Svelte's {#each} blocks, provides optimized updates
384
- *
385
- * Returns a Comment node as anchor point. All list items are rendered after this anchor.
386
- *
387
- * @example
388
- * ```tsx
389
- * const listAnchor = <KTFor
390
- * list={items}
391
- * key={(item) => item.id}
392
- * mapper={(item) => <div>{item.name}</div>}
393
- * /> as KTForAnchor;
394
- *
395
- * document.body.appendChild(listAnchor); // Anchor + all items are rendered
396
- *
397
- * // Update the list
398
- * listAnchor.redraw({ list: newItems });
399
- * ```
387
+ * KTFor - List rendering component with key-based optimization
388
+ * Returns a Comment anchor node with rendered elements in __kt_for_list__
400
389
  */
401
390
  function KTFor(props) {
402
- const { list: initList, key: getKey, mapper } = props;
403
- // Cache: key -> { node: HTMLElement, item: T }
404
- const nodeCache = new Map();
405
- // Current key order
406
- let currentKeys = [];
407
- // Create anchor comment node - marks the position of the list
391
+ const { list, map } = props;
392
+ const key = props.key ?? ((item) => item);
393
+ // Create anchor comment node
408
394
  const anchor = document.createComment('kt-for');
409
- /**
410
- * Get all nodes currently in the DOM that belong to this list
411
- * They are all siblings after the anchor node
412
- */
413
- const getListNodes = () => {
414
- const nodes = [];
415
- if (!anchor.parentNode)
416
- return nodes;
417
- let current = anchor.nextSibling;
418
- new Set(currentKeys);
419
- // Collect nodes that belong to this list
420
- while (current) {
421
- const isListNode = Array.from(nodeCache.values()).some((cached) => cached.node === current);
422
- if (!isListNode)
423
- break;
424
- nodes.push(current);
425
- current = current.nextSibling;
426
- }
427
- return nodes;
428
- };
429
- /**
430
- * Initialize list - append all initial items after anchor
431
- */
432
- const initialize = () => {
433
- for (let i = 0; i < initList.length; i++) {
434
- const item = initList[i];
435
- const key = getKey(item, i);
436
- const node = mapper(item, i);
437
- nodeCache.set(key, { node, item });
438
- currentKeys.push(key);
439
- // Append to parent if anchor is already in DOM
440
- if (anchor.parentNode) {
441
- const lastNode = getListNodes()[getListNodes().length - 1];
442
- if (lastNode) {
443
- lastNode.after(node);
444
- }
445
- else {
446
- anchor.after(node);
447
- }
448
- }
449
- }
450
- };
451
- /**
452
- * Smart update method - only modifies DOM nodes that changed
453
- * Uses key-based diff algorithm to minimize DOM operations
454
- */
455
- const smartUpdate = (newList, newMapper) => {
456
- const mapperFn = newMapper ?? mapper;
457
- const newKeys = newList.map((item, index) => getKey(item, index));
458
- if (!anchor.parentNode) {
459
- // If anchor is not in DOM yet, just update cache
460
- nodeCache.clear();
461
- currentKeys = [];
462
- for (let i = 0; i < newList.length; i++) {
463
- const item = newList[i];
464
- const key = newKeys[i];
465
- const node = mapperFn(item, i);
466
- nodeCache.set(key, { node, item });
467
- currentKeys.push(key);
468
- }
469
- return;
470
- }
471
- // Find keys to remove (exist in current but not in new)
472
- const keysToRemove = currentKeys.filter((k) => !newKeys.includes(k));
473
- for (let i = 0; i < keysToRemove.length; i++) {
474
- const key = keysToRemove[i];
475
- const cached = nodeCache.get(key);
476
- if (cached && cached.node.parentNode) {
477
- cached.node.parentNode.removeChild(cached.node);
395
+ // Store current state
396
+ let currentList = list;
397
+ let currentKey = key;
398
+ let currentMap = map;
399
+ // Map to track rendered nodes by key
400
+ const nodeMap = new Map();
401
+ // Render initial list
402
+ const elements = [];
403
+ for (let index = 0; index < currentList.length; index++) {
404
+ const item = currentList[index];
405
+ const itemKey = currentKey(item, index, currentList);
406
+ const node = currentMap(item, index, currentList);
407
+ nodeMap.set(itemKey, node);
408
+ elements.push(node);
409
+ }
410
+ // Attach elements array to anchor
411
+ anchor.__kt_for_list__ = elements;
412
+ // Redraw function for updates
413
+ anchor.redraw = (newProps = props) => {
414
+ const newList = (newProps.list ?? currentList);
415
+ const newKey = (newProps.key ?? currentKey);
416
+ const newMap = (newProps.map ?? currentMap);
417
+ // Update stored values
418
+ currentList = newList;
419
+ currentKey = newKey;
420
+ currentMap = newMap;
421
+ const parent = anchor.parentNode;
422
+ if (!parent) {
423
+ // If not in DOM yet, just rebuild the list
424
+ const newElements = [];
425
+ for (let index = 0; index < currentList.length; index++) {
426
+ const item = currentList[index];
427
+ const itemKey = currentKey(item, index, currentList);
428
+ const node = currentMap(item, index, currentList);
429
+ nodeMap.set(itemKey, node);
430
+ newElements.push(node);
478
431
  }
479
- nodeCache.delete(key);
432
+ anchor.__kt_for_list__ = newElements;
433
+ return anchor;
480
434
  }
481
- // Update/add nodes in correct order
482
- let previousNode = anchor;
483
- for (let i = 0; i < newList.length; i++) {
484
- const item = newList[i];
485
- const key = newKeys[i];
486
- let cached = nodeCache.get(key);
487
- // New item: create and cache
488
- if (!cached) {
489
- const node = mapperFn(item, i);
490
- cached = { node, item };
491
- nodeCache.set(key, cached);
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
444
+ if (nodeMap.has(itemKey)) {
445
+ const existingNode = nodeMap.get(itemKey);
446
+ newNodeMap.set(itemKey, existingNode);
447
+ newElements.push(existingNode);
492
448
  }
493
449
  else {
494
- // Update cached item reference
495
- cached.item = item;
496
- }
497
- // Ensure node is at correct position (right after previousNode)
498
- if (previousNode.nextSibling !== cached.node) {
499
- if (previousNode.nextSibling) {
500
- anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
501
- }
502
- else {
503
- anchor.parentNode.appendChild(cached.node);
504
- }
450
+ // Create new node
451
+ const node = newMap(item, index, newList);
452
+ newNodeMap.set(itemKey, node);
453
+ newElements.push(node);
505
454
  }
506
- previousNode = cached.node;
507
- }
508
- currentKeys = newKeys;
509
- };
510
- // Mount redraw method on anchor
511
- anchor.redraw = (newProps) => {
512
- if (newProps?.list) {
513
- smartUpdate(newProps.list, newProps.mapper);
514
455
  }
515
- };
516
- // Initialize the list
517
- initialize();
518
- return anchor;
519
- }
520
- /**
521
- * Simple list rendering without key optimization
522
- * Rebuilds the entire list on each update - use for small static lists
523
- *
524
- * Returns a Comment anchor node. All items are rendered after this anchor.
525
- *
526
- * @example
527
- * ```tsx
528
- * const listAnchor = <KTForStatic
529
- * list={items}
530
- * mapper={(item) => <div>{item}</div>}
531
- * /> as KTForAnchor;
532
- * ```
533
- */
534
- function KTForStatic(props) {
535
- const { list: initList, mapper } = props;
536
- // Create anchor comment node
537
- const anchor = document.createComment('kt-for-static');
538
- let nodes = [];
539
- // Simple rebuild on redraw
540
- const rebuild = (newList) => {
541
- // Remove all old nodes
542
- for (let i = 0; i < nodes.length; i++) {
543
- const node = nodes[i];
544
- if (node.parentNode) {
545
- node.parentNode.removeChild(node);
456
+ // Remove nodes that are no longer in the list
457
+ nodeMap.forEach((node, key) => {
458
+ if (!newKeys.has(key)) {
459
+ node.remove();
546
460
  }
547
- }
548
- nodes = [];
549
- // Create new nodes
550
- let previousNode = anchor;
551
- for (let i = 0; i < newList.length; i++) {
552
- const item = newList[i];
553
- const node = mapper(item, i);
554
- nodes.push(node);
555
- if (anchor.parentNode) {
556
- if (previousNode.nextSibling) {
557
- anchor.parentNode.insertBefore(node, previousNode.nextSibling);
558
- }
559
- else {
560
- anchor.parentNode.appendChild(node);
561
- }
562
- previousNode = node;
461
+ });
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);
563
469
  }
470
+ referenceNode = node.nextSibling;
564
471
  }
472
+ // Update node map and element list
473
+ nodeMap.clear();
474
+ newNodeMap.forEach((node, key) => nodeMap.set(key, node));
475
+ anchor.__kt_for_list__ = newElements;
476
+ return anchor;
565
477
  };
566
- // Initial render
567
- rebuild(initList);
568
- // Mount redraw
569
- anchor.redraw = (newProps) => {
570
- if (newProps?.list) {
571
- rebuild(newProps.list);
572
- }
573
- };
478
+ // Set ref if provided
479
+ if (props.ref?.isKT) {
480
+ props.ref.value = anchor;
481
+ }
574
482
  return anchor;
575
483
  }
576
484
 
577
485
  exports.Fragment = Fragment;
578
486
  exports.KTAsync = KTAsync;
579
487
  exports.KTFor = KTFor;
580
- exports.KTForStatic = KTForStatic;
581
488
  exports.createElement = h;
582
489
  exports.createRedrawable = createRedrawable;
583
490
  exports.createRedrawableNoref = createRedrawableNoref;
@@ -180,6 +180,11 @@ var __ktjs_core__ = (function (exports) {
180
180
  }
181
181
  else {
182
182
  $append.call(element, c);
183
+ // Handle KTFor anchor
184
+ var list = c.__kt_for_list__;
185
+ if ($isArray(list)) {
186
+ apd(element, list);
187
+ }
183
188
  }
184
189
  }
185
190
  function apd(element, c) {
@@ -231,7 +236,7 @@ var __ktjs_core__ = (function (exports) {
231
236
  * ## About
232
237
  * @package @ktjs/core
233
238
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
234
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
239
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
235
240
  * @license MIT
236
241
  * @link https://github.com/baendlorel/kt.js
237
242
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -444,205 +449,110 @@ var __ktjs_core__ = (function (exports) {
444
449
  }
445
450
 
446
451
  /**
447
- * KTFor component - Efficient list rendering with key-based DOM reuse
448
- * Similar to Svelte's {#each} blocks, provides optimized updates
449
- *
450
- * Returns a Comment node as anchor point. All list items are rendered after this anchor.
451
- *
452
- * @example
453
- * ```tsx
454
- * const listAnchor = <KTFor
455
- * list={items}
456
- * key={(item) => item.id}
457
- * mapper={(item) => <div>{item.name}</div>}
458
- * /> as KTForAnchor;
459
- *
460
- * document.body.appendChild(listAnchor); // Anchor + all items are rendered
461
- *
462
- * // Update the list
463
- * listAnchor.redraw({ list: newItems });
464
- * ```
452
+ * KTFor - List rendering component with key-based optimization
453
+ * Returns a Comment anchor node with rendered elements in __kt_for_list__
465
454
  */
466
455
  function KTFor(props) {
467
- var initList = props.list, getKey = props.key, mapper = props.mapper;
468
- // Cache: key -> { node: HTMLElement, item: T }
469
- var nodeCache = new Map();
470
- // Current key order
471
- var currentKeys = [];
472
- // Create anchor comment node - marks the position of the list
456
+ var _a, _b;
457
+ var list = props.list, map = props.map;
458
+ var key = (_a = props.key) !== null && _a !== void 0 ? _a : (function (item) { return item; });
459
+ // Create anchor comment node
473
460
  var anchor = document.createComment('kt-for');
474
- /**
475
- * Get all nodes currently in the DOM that belong to this list
476
- * They are all siblings after the anchor node
477
- */
478
- var getListNodes = function () {
479
- var nodes = [];
480
- if (!anchor.parentNode)
481
- return nodes;
482
- var current = anchor.nextSibling;
483
- new Set(currentKeys);
484
- // Collect nodes that belong to this list
485
- while (current) {
486
- var isListNode = Array.from(nodeCache.values()).some(function (cached) { return cached.node === current; });
487
- if (!isListNode)
488
- break;
489
- nodes.push(current);
490
- current = current.nextSibling;
491
- }
492
- return nodes;
493
- };
494
- /**
495
- * Initialize list - append all initial items after anchor
496
- */
497
- var initialize = function () {
498
- for (var i = 0; i < initList.length; i++) {
499
- var item = initList[i];
500
- var key = getKey(item, i);
501
- var node = mapper(item, i);
502
- nodeCache.set(key, { node: node, item: item });
503
- currentKeys.push(key);
504
- // Append to parent if anchor is already in DOM
505
- if (anchor.parentNode) {
506
- var lastNode = getListNodes()[getListNodes().length - 1];
507
- if (lastNode) {
508
- lastNode.after(node);
509
- }
510
- else {
511
- anchor.after(node);
512
- }
513
- }
514
- }
515
- };
516
- /**
517
- * Smart update method - only modifies DOM nodes that changed
518
- * Uses key-based diff algorithm to minimize DOM operations
519
- */
520
- var smartUpdate = function (newList, newMapper) {
521
- var mapperFn = newMapper !== null && newMapper !== void 0 ? newMapper : mapper;
522
- var newKeys = newList.map(function (item, index) { return getKey(item, index); });
523
- if (!anchor.parentNode) {
524
- // If anchor is not in DOM yet, just update cache
525
- nodeCache.clear();
526
- currentKeys = [];
527
- for (var i = 0; i < newList.length; i++) {
528
- var item = newList[i];
529
- var key = newKeys[i];
530
- var node = mapperFn(item, i);
531
- nodeCache.set(key, { node: node, item: item });
532
- currentKeys.push(key);
533
- }
534
- return;
535
- }
536
- // Find keys to remove (exist in current but not in new)
537
- var keysToRemove = currentKeys.filter(function (k) { return !newKeys.includes(k); });
538
- for (var i = 0; i < keysToRemove.length; i++) {
539
- var key = keysToRemove[i];
540
- var cached = nodeCache.get(key);
541
- if (cached && cached.node.parentNode) {
542
- cached.node.parentNode.removeChild(cached.node);
461
+ // Store current state
462
+ var currentList = list;
463
+ var currentKey = key;
464
+ var currentMap = map;
465
+ // Map to track rendered nodes by key
466
+ var nodeMap = new Map();
467
+ // Render initial list
468
+ var elements = [];
469
+ for (var index = 0; index < currentList.length; index++) {
470
+ var item = currentList[index];
471
+ var itemKey = currentKey(item, index, currentList);
472
+ var node = currentMap(item, index, currentList);
473
+ nodeMap.set(itemKey, node);
474
+ elements.push(node);
475
+ }
476
+ // Attach elements array to anchor
477
+ anchor.__kt_for_list__ = elements;
478
+ // Redraw function for updates
479
+ anchor.redraw = function (newProps) {
480
+ var _a, _b, _c;
481
+ if (newProps === void 0) { newProps = props; }
482
+ var newList = ((_a = newProps.list) !== null && _a !== void 0 ? _a : currentList);
483
+ var newKey = ((_b = newProps.key) !== null && _b !== void 0 ? _b : currentKey);
484
+ var newMap = ((_c = newProps.map) !== null && _c !== void 0 ? _c : currentMap);
485
+ // Update stored values
486
+ currentList = newList;
487
+ currentKey = newKey;
488
+ currentMap = newMap;
489
+ var parent = anchor.parentNode;
490
+ if (!parent) {
491
+ // If not in DOM yet, just rebuild the list
492
+ var newElements_1 = [];
493
+ for (var index = 0; index < currentList.length; index++) {
494
+ var item = currentList[index];
495
+ var itemKey = currentKey(item, index, currentList);
496
+ var node = currentMap(item, index, currentList);
497
+ nodeMap.set(itemKey, node);
498
+ newElements_1.push(node);
543
499
  }
544
- nodeCache.delete(key);
500
+ anchor.__kt_for_list__ = newElements_1;
501
+ return anchor;
545
502
  }
546
- // Update/add nodes in correct order
547
- var previousNode = anchor;
548
- for (var i = 0; i < newList.length; i++) {
549
- var item = newList[i];
550
- var key = newKeys[i];
551
- var cached = nodeCache.get(key);
552
- // New item: create and cache
553
- if (!cached) {
554
- var node = mapperFn(item, i);
555
- cached = { node: node, item: item };
556
- nodeCache.set(key, cached);
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
512
+ if (nodeMap.has(itemKey)) {
513
+ var existingNode = nodeMap.get(itemKey);
514
+ newNodeMap.set(itemKey, existingNode);
515
+ newElements.push(existingNode);
557
516
  }
558
517
  else {
559
- // Update cached item reference
560
- cached.item = item;
518
+ // Create new node
519
+ var node = newMap(item, index, newList);
520
+ newNodeMap.set(itemKey, node);
521
+ newElements.push(node);
561
522
  }
562
- // Ensure node is at correct position (right after previousNode)
563
- if (previousNode.nextSibling !== cached.node) {
564
- if (previousNode.nextSibling) {
565
- anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
566
- }
567
- else {
568
- anchor.parentNode.appendChild(cached.node);
569
- }
570
- }
571
- previousNode = cached.node;
572
- }
573
- currentKeys = newKeys;
574
- };
575
- // Mount redraw method on anchor
576
- anchor.redraw = function (newProps) {
577
- if (newProps === null || newProps === void 0 ? void 0 : newProps.list) {
578
- smartUpdate(newProps.list, newProps.mapper);
579
523
  }
580
- };
581
- // Initialize the list
582
- initialize();
583
- return anchor;
584
- }
585
- /**
586
- * Simple list rendering without key optimization
587
- * Rebuilds the entire list on each update - use for small static lists
588
- *
589
- * Returns a Comment anchor node. All items are rendered after this anchor.
590
- *
591
- * @example
592
- * ```tsx
593
- * const listAnchor = <KTForStatic
594
- * list={items}
595
- * mapper={(item) => <div>{item}</div>}
596
- * /> as KTForAnchor;
597
- * ```
598
- */
599
- function KTForStatic(props) {
600
- var initList = props.list, mapper = props.mapper;
601
- // Create anchor comment node
602
- var anchor = document.createComment('kt-for-static');
603
- var nodes = [];
604
- // Simple rebuild on redraw
605
- var rebuild = function (newList) {
606
- // Remove all old nodes
607
- for (var i = 0; i < nodes.length; i++) {
608
- var node = nodes[i];
609
- if (node.parentNode) {
610
- node.parentNode.removeChild(node);
524
+ // Remove nodes that are no longer in the list
525
+ nodeMap.forEach(function (node, key) {
526
+ if (!newKeys.has(key)) {
527
+ node.remove();
611
528
  }
612
- }
613
- nodes = [];
614
- // Create new nodes
615
- var previousNode = anchor;
616
- for (var i = 0; i < newList.length; i++) {
617
- var item = newList[i];
618
- var node = mapper(item, i);
619
- nodes.push(node);
620
- if (anchor.parentNode) {
621
- if (previousNode.nextSibling) {
622
- anchor.parentNode.insertBefore(node, previousNode.nextSibling);
623
- }
624
- else {
625
- anchor.parentNode.appendChild(node);
626
- }
627
- previousNode = node;
529
+ });
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);
628
537
  }
538
+ referenceNode = node.nextSibling;
629
539
  }
540
+ // Update node map and element list
541
+ nodeMap.clear();
542
+ newNodeMap.forEach(function (node, key) { return nodeMap.set(key, node); });
543
+ anchor.__kt_for_list__ = newElements;
544
+ return anchor;
630
545
  };
631
- // Initial render
632
- rebuild(initList);
633
- // Mount redraw
634
- anchor.redraw = function (newProps) {
635
- if (newProps === null || newProps === void 0 ? void 0 : newProps.list) {
636
- rebuild(newProps.list);
637
- }
638
- };
546
+ // Set ref if provided
547
+ if ((_b = props.ref) === null || _b === void 0 ? void 0 : _b.isKT) {
548
+ props.ref.value = anchor;
549
+ }
639
550
  return anchor;
640
551
  }
641
552
 
642
553
  exports.Fragment = Fragment;
643
554
  exports.KTAsync = KTAsync;
644
555
  exports.KTFor = KTFor;
645
- exports.KTForStatic = KTForStatic;
646
556
  exports.createElement = h;
647
557
  exports.createRedrawable = createRedrawable;
648
558
  exports.createRedrawableNoref = createRedrawableNoref;
package/dist/index.mjs CHANGED
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
155
155
  }
156
156
  else {
157
157
  $append.call(element, c);
158
+ // Handle KTFor anchor
159
+ const list = c.__kt_for_list__;
160
+ if ($isArray(list)) {
161
+ apd(element, list);
162
+ }
158
163
  }
159
164
  }
160
165
  function apd(element, c) {
@@ -203,7 +208,7 @@ let creator = defaultCreator;
203
208
  * ## About
204
209
  * @package @ktjs/core
205
210
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
206
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
211
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
207
212
  * @license MIT
208
213
  * @link https://github.com/baendlorel/kt.js
209
214
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -376,199 +381,102 @@ function KTAsync(props) {
376
381
  }
377
382
 
378
383
  /**
379
- * KTFor component - Efficient list rendering with key-based DOM reuse
380
- * Similar to Svelte's {#each} blocks, provides optimized updates
381
- *
382
- * Returns a Comment node as anchor point. All list items are rendered after this anchor.
383
- *
384
- * @example
385
- * ```tsx
386
- * const listAnchor = <KTFor
387
- * list={items}
388
- * key={(item) => item.id}
389
- * mapper={(item) => <div>{item.name}</div>}
390
- * /> as KTForAnchor;
391
- *
392
- * document.body.appendChild(listAnchor); // Anchor + all items are rendered
393
- *
394
- * // Update the list
395
- * listAnchor.redraw({ list: newItems });
396
- * ```
384
+ * KTFor - List rendering component with key-based optimization
385
+ * Returns a Comment anchor node with rendered elements in __kt_for_list__
397
386
  */
398
387
  function KTFor(props) {
399
- const { list: initList, key: getKey, mapper } = props;
400
- // Cache: key -> { node: HTMLElement, item: T }
401
- const nodeCache = new Map();
402
- // Current key order
403
- let currentKeys = [];
404
- // Create anchor comment node - marks the position of the list
388
+ const { list, map } = props;
389
+ const key = props.key ?? ((item) => item);
390
+ // Create anchor comment node
405
391
  const anchor = document.createComment('kt-for');
406
- /**
407
- * Get all nodes currently in the DOM that belong to this list
408
- * They are all siblings after the anchor node
409
- */
410
- const getListNodes = () => {
411
- const nodes = [];
412
- if (!anchor.parentNode)
413
- return nodes;
414
- let current = anchor.nextSibling;
415
- new Set(currentKeys);
416
- // Collect nodes that belong to this list
417
- while (current) {
418
- const isListNode = Array.from(nodeCache.values()).some((cached) => cached.node === current);
419
- if (!isListNode)
420
- break;
421
- nodes.push(current);
422
- current = current.nextSibling;
423
- }
424
- return nodes;
425
- };
426
- /**
427
- * Initialize list - append all initial items after anchor
428
- */
429
- const initialize = () => {
430
- for (let i = 0; i < initList.length; i++) {
431
- const item = initList[i];
432
- const key = getKey(item, i);
433
- const node = mapper(item, i);
434
- nodeCache.set(key, { node, item });
435
- currentKeys.push(key);
436
- // Append to parent if anchor is already in DOM
437
- if (anchor.parentNode) {
438
- const lastNode = getListNodes()[getListNodes().length - 1];
439
- if (lastNode) {
440
- lastNode.after(node);
441
- }
442
- else {
443
- anchor.after(node);
444
- }
445
- }
446
- }
447
- };
448
- /**
449
- * Smart update method - only modifies DOM nodes that changed
450
- * Uses key-based diff algorithm to minimize DOM operations
451
- */
452
- const smartUpdate = (newList, newMapper) => {
453
- const mapperFn = newMapper ?? mapper;
454
- const newKeys = newList.map((item, index) => getKey(item, index));
455
- if (!anchor.parentNode) {
456
- // If anchor is not in DOM yet, just update cache
457
- nodeCache.clear();
458
- currentKeys = [];
459
- for (let i = 0; i < newList.length; i++) {
460
- const item = newList[i];
461
- const key = newKeys[i];
462
- const node = mapperFn(item, i);
463
- nodeCache.set(key, { node, item });
464
- currentKeys.push(key);
465
- }
466
- return;
467
- }
468
- // Find keys to remove (exist in current but not in new)
469
- const keysToRemove = currentKeys.filter((k) => !newKeys.includes(k));
470
- for (let i = 0; i < keysToRemove.length; i++) {
471
- const key = keysToRemove[i];
472
- const cached = nodeCache.get(key);
473
- if (cached && cached.node.parentNode) {
474
- cached.node.parentNode.removeChild(cached.node);
392
+ // Store current state
393
+ let currentList = list;
394
+ let currentKey = key;
395
+ let currentMap = map;
396
+ // Map to track rendered nodes by key
397
+ const nodeMap = new Map();
398
+ // Render initial list
399
+ const elements = [];
400
+ for (let index = 0; index < currentList.length; index++) {
401
+ const item = currentList[index];
402
+ const itemKey = currentKey(item, index, currentList);
403
+ const node = currentMap(item, index, currentList);
404
+ nodeMap.set(itemKey, node);
405
+ elements.push(node);
406
+ }
407
+ // Attach elements array to anchor
408
+ anchor.__kt_for_list__ = elements;
409
+ // Redraw function for updates
410
+ anchor.redraw = (newProps = props) => {
411
+ const newList = (newProps.list ?? currentList);
412
+ const newKey = (newProps.key ?? currentKey);
413
+ const newMap = (newProps.map ?? currentMap);
414
+ // Update stored values
415
+ currentList = newList;
416
+ currentKey = newKey;
417
+ currentMap = newMap;
418
+ const parent = anchor.parentNode;
419
+ if (!parent) {
420
+ // If not in DOM yet, just rebuild the list
421
+ const newElements = [];
422
+ for (let index = 0; index < currentList.length; index++) {
423
+ const item = currentList[index];
424
+ const itemKey = currentKey(item, index, currentList);
425
+ const node = currentMap(item, index, currentList);
426
+ nodeMap.set(itemKey, node);
427
+ newElements.push(node);
475
428
  }
476
- nodeCache.delete(key);
429
+ anchor.__kt_for_list__ = newElements;
430
+ return anchor;
477
431
  }
478
- // Update/add nodes in correct order
479
- let previousNode = anchor;
480
- for (let i = 0; i < newList.length; i++) {
481
- const item = newList[i];
482
- const key = newKeys[i];
483
- let cached = nodeCache.get(key);
484
- // New item: create and cache
485
- if (!cached) {
486
- const node = mapperFn(item, i);
487
- cached = { node, item };
488
- nodeCache.set(key, cached);
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
441
+ if (nodeMap.has(itemKey)) {
442
+ const existingNode = nodeMap.get(itemKey);
443
+ newNodeMap.set(itemKey, existingNode);
444
+ newElements.push(existingNode);
489
445
  }
490
446
  else {
491
- // Update cached item reference
492
- cached.item = item;
493
- }
494
- // Ensure node is at correct position (right after previousNode)
495
- if (previousNode.nextSibling !== cached.node) {
496
- if (previousNode.nextSibling) {
497
- anchor.parentNode.insertBefore(cached.node, previousNode.nextSibling);
498
- }
499
- else {
500
- anchor.parentNode.appendChild(cached.node);
501
- }
447
+ // Create new node
448
+ const node = newMap(item, index, newList);
449
+ newNodeMap.set(itemKey, node);
450
+ newElements.push(node);
502
451
  }
503
- previousNode = cached.node;
504
- }
505
- currentKeys = newKeys;
506
- };
507
- // Mount redraw method on anchor
508
- anchor.redraw = (newProps) => {
509
- if (newProps?.list) {
510
- smartUpdate(newProps.list, newProps.mapper);
511
452
  }
512
- };
513
- // Initialize the list
514
- initialize();
515
- return anchor;
516
- }
517
- /**
518
- * Simple list rendering without key optimization
519
- * Rebuilds the entire list on each update - use for small static lists
520
- *
521
- * Returns a Comment anchor node. All items are rendered after this anchor.
522
- *
523
- * @example
524
- * ```tsx
525
- * const listAnchor = <KTForStatic
526
- * list={items}
527
- * mapper={(item) => <div>{item}</div>}
528
- * /> as KTForAnchor;
529
- * ```
530
- */
531
- function KTForStatic(props) {
532
- const { list: initList, mapper } = props;
533
- // Create anchor comment node
534
- const anchor = document.createComment('kt-for-static');
535
- let nodes = [];
536
- // Simple rebuild on redraw
537
- const rebuild = (newList) => {
538
- // Remove all old nodes
539
- for (let i = 0; i < nodes.length; i++) {
540
- const node = nodes[i];
541
- if (node.parentNode) {
542
- node.parentNode.removeChild(node);
453
+ // Remove nodes that are no longer in the list
454
+ nodeMap.forEach((node, key) => {
455
+ if (!newKeys.has(key)) {
456
+ node.remove();
543
457
  }
544
- }
545
- nodes = [];
546
- // Create new nodes
547
- let previousNode = anchor;
548
- for (let i = 0; i < newList.length; i++) {
549
- const item = newList[i];
550
- const node = mapper(item, i);
551
- nodes.push(node);
552
- if (anchor.parentNode) {
553
- if (previousNode.nextSibling) {
554
- anchor.parentNode.insertBefore(node, previousNode.nextSibling);
555
- }
556
- else {
557
- anchor.parentNode.appendChild(node);
558
- }
559
- previousNode = node;
458
+ });
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);
560
466
  }
467
+ referenceNode = node.nextSibling;
561
468
  }
469
+ // Update node map and element list
470
+ nodeMap.clear();
471
+ newNodeMap.forEach((node, key) => nodeMap.set(key, node));
472
+ anchor.__kt_for_list__ = newElements;
473
+ return anchor;
562
474
  };
563
- // Initial render
564
- rebuild(initList);
565
- // Mount redraw
566
- anchor.redraw = (newProps) => {
567
- if (newProps?.list) {
568
- rebuild(newProps.list);
569
- }
570
- };
475
+ // Set ref if provided
476
+ if (props.ref?.isKT) {
477
+ props.ref.value = anchor;
478
+ }
571
479
  return anchor;
572
480
  }
573
481
 
574
- export { Fragment, KTAsync, KTFor, KTForStatic, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
482
+ export { Fragment, KTAsync, KTFor, h as createElement, createRedrawable, createRedrawableNoref, h, jsx, jsxDEV, jsxs, ref };
@@ -17,12 +17,13 @@ interface KTRef<T> {
17
17
  */
18
18
  declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
19
19
 
20
- type KTHTMLElement<El extends HTMLElement = HTMLElement> = El & {
20
+ type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
21
+ type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
21
22
  /**
22
23
  * Automically generate a redraw function if it is not provided
23
24
  * @param props
24
25
  */
25
- redraw: (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
26
+ redraw: R;
26
27
  };
27
28
 
28
29
  declare global {
@@ -44,7 +45,7 @@ declare global {
44
45
  }
45
46
  }
46
47
 
47
- type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
48
+ type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
48
49
  type KTAvailableContent = SingleContent | KTAvailableContent[];
49
50
  type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
50
51
  type KTRawAttr = KTAttribute | string;
@@ -55,9 +56,11 @@ type KTRawAttr = KTAttribute | string;
55
56
  interface KTBaseAttribute {
56
57
  [k: string]: any;
57
58
 
59
+ // # kt-specific attributes
58
60
  ref?: KTRef<HTMLElement>;
59
61
  'k-if'?: any;
60
62
 
63
+ // # normal HTML attributes
61
64
  id?: string;
62
65
  class?: string;
63
66
  style?: string | Partial<CSSStyleDeclaration>;
@@ -154,7 +157,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
154
157
  * ## About
155
158
  * @package @ktjs/core
156
159
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
157
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
160
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
158
161
  * @license MIT
159
162
  * @link https://github.com/baendlorel/kt.js
160
163
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -218,4 +221,4 @@ declare function createRedrawableNoref(creator: () => KTHTMLElement): KTHTMLElem
218
221
  declare function createRedrawable(creator: () => KTHTMLElement): KTRef<KTHTMLElement>;
219
222
 
220
223
  export { Fragment, h as createElement, createRedrawable, createRedrawableNoref, jsx, jsxDEV, jsxs, ref };
221
- export type { KTHTMLElement, KTRef };
224
+ export type { KTHTMLElement, KTRef, Redraw };
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
155
155
  }
156
156
  else {
157
157
  $append.call(element, c);
158
+ // Handle KTFor anchor
159
+ const list = c.__kt_for_list__;
160
+ if ($isArray(list)) {
161
+ apd(element, list);
162
+ }
158
163
  }
159
164
  }
160
165
  function apd(element, c) {
@@ -203,7 +208,7 @@ let creator = defaultCreator;
203
208
  * ## About
204
209
  * @package @ktjs/core
205
210
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
206
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
211
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
207
212
  * @license MIT
208
213
  * @link https://github.com/baendlorel/kt.js
209
214
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -11,12 +11,13 @@ interface KTRef<T> {
11
11
  isKT: true;
12
12
  }
13
13
 
14
- type KTHTMLElement<El extends HTMLElement = HTMLElement> = El & {
14
+ type Redraw = (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
15
+ type KTHTMLElement<El extends HTMLElement = HTMLElement, R extends Function = Redraw> = El & {
15
16
  /**
16
17
  * Automically generate a redraw function if it is not provided
17
18
  * @param props
18
19
  */
19
- redraw: (props?: KTAttribute, ...args: any[]) => KTHTMLElement;
20
+ redraw: R;
20
21
  };
21
22
 
22
23
  declare global {
@@ -38,7 +39,7 @@ declare global {
38
39
  }
39
40
  }
40
41
 
41
- type SingleContent = KTRef<any> | HTMLElement | Element | string | number | boolean | null | undefined;
42
+ type SingleContent = KTRef<any> | HTMLElement | Element | Node | string | number | boolean | null | undefined;
42
43
  type KTAvailableContent = SingleContent | KTAvailableContent[];
43
44
  type KTRawContent = KTAvailableContent | Promise<KTAvailableContent>;
44
45
  type KTRawAttr = KTAttribute | string;
@@ -49,9 +50,11 @@ type KTRawAttr = KTAttribute | string;
49
50
  interface KTBaseAttribute {
50
51
  [k: string]: any;
51
52
 
53
+ // # kt-specific attributes
52
54
  ref?: KTRef<HTMLElement>;
53
55
  'k-if'?: any;
54
56
 
57
+ // # normal HTML attributes
55
58
  id?: string;
56
59
  class?: string;
57
60
  style?: string | Partial<CSSStyleDeclaration>;
@@ -148,7 +151,7 @@ type HTML<T extends (HTMLTag | SVGTag) & otherstring> = T extends SVGTag ? SVGEl
148
151
  * ## About
149
152
  * @package @ktjs/core
150
153
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
151
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
154
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
152
155
  * @license MIT
153
156
  * @link https://github.com/baendlorel/kt.js
154
157
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -155,6 +155,11 @@ function apdSingle(element, c) {
155
155
  }
156
156
  else {
157
157
  $append.call(element, c);
158
+ // Handle KTFor anchor
159
+ const list = c.__kt_for_list__;
160
+ if ($isArray(list)) {
161
+ apd(element, list);
162
+ }
158
163
  }
159
164
  }
160
165
  function apd(element, c) {
@@ -203,7 +208,7 @@ let creator = defaultCreator;
203
208
  * ## About
204
209
  * @package @ktjs/core
205
210
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
206
- * @version 0.16.0 (Last Update: 2026.01.27 17:30:39.673)
211
+ * @version 0.17.0 (Last Update: 2026.01.28 15:11:59.247)
207
212
  * @license MIT
208
213
  * @link https://github.com/baendlorel/kt.js
209
214
  * @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.16.0",
3
+ "version": "0.17.0",
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",