@ktjs/core 0.22.2 → 0.23.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/dist/index.d.ts CHANGED
@@ -21,6 +21,10 @@ declare class KTRef<T> {
21
21
  */
22
22
  get value(): T;
23
23
  set value(newValue: T);
24
+ /**
25
+ * Register a callback when the value changes
26
+ * @param callback (newValue, oldValue) => xxx
27
+ */
24
28
  addOnChange(callback: RefChangeHandler<T>): void;
25
29
  removeOnChange(callback: RefChangeHandler<T>): boolean;
26
30
  }
@@ -29,9 +33,11 @@ declare const isKTRef: <T = any>(obj: any) => obj is KTRef<T>;
29
33
  * Reference to the created HTML element.
30
34
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
31
35
  * - can alse be used to store normal values, but it is not reactive.
36
+ * - if the value is already a `KTRef`, it will be returned **directly**.
32
37
  * @param value mostly an HTMLElement
33
38
  */
34
- declare function ref<T = JSX.Element>(value?: T, onChange?: RefChangeHandler<T>): KTRef<T>;
39
+ declare function ref<T = JSX.Element>(value?: T | KTRef<T>, onChange?: RefChangeHandler<T>): KTRef<T>;
40
+ declare function deref<T = JSX.Element>(value: T | KTRef<T>): T;
35
41
  type KTSurfaceRef<T extends Object> = {
36
42
  [K in keyof T]: KTRef<T[K]>;
37
43
  } & {
@@ -45,6 +51,10 @@ type KTSurfaceRef<T extends Object> = {
45
51
  * - `obj.a.b` is not reactive
46
52
  */
47
53
  declare const surfaceRef: <T extends Object>(obj: T) => KTSurfaceRef<T>;
54
+ /**
55
+ * Assert k-model to be a ref object
56
+ */
57
+ declare const $modelOrRef: <T = any>(props: any, defaultValue?: T) => KTRef<T>;
48
58
 
49
59
  type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag
50
60
  ? SVGElementTagNameMap[T]
@@ -85,6 +95,12 @@ interface KTBaseAttribute {
85
95
  */
86
96
  'k-model'?: KTRef<any>;
87
97
 
98
+ /**
99
+ * Directly apply html string to `innerHTML`.
100
+ * - Would be reactive if `KTRef` instance is provided
101
+ */
102
+ 'k-html'?: any;
103
+
88
104
  // # normal HTML attributes
89
105
  id?: string;
90
106
  class?: string;
@@ -162,7 +178,7 @@ type KTComponent = (
162
178
  * ## About
163
179
  * @package @ktjs/core
164
180
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
165
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
181
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
166
182
  * @license MIT
167
183
  * @link https://github.com/baendlorel/kt.js
168
184
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -1467,5 +1483,5 @@ interface KTForProps<T> {
1467
1483
  */
1468
1484
  declare function KTFor<T>(props: KTForProps<T>): KTForElement;
1469
1485
 
1470
- export { Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, h, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
1486
+ export { $modelOrRef, Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, deref, h, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
1471
1487
  export type { EventHandler, HTMLTag, InputElementTag, KTAttribute, KTForElement, KTForProps, KTRawAttr, KTRawContent, KTRawContents, KTSurfaceRef, MathMLTag, SVGTag };
@@ -8,6 +8,7 @@ var __ktjs_core__ = (function (exports) {
8
8
 
9
9
  // DOM manipulation utilities
10
10
  // # dom natives
11
+ const $isNode = (x) => x?.nodeType > 0;
11
12
  /**
12
13
  * & Remove `bind` because it is shockingly slower than wrapper
13
14
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -54,8 +55,9 @@ var __ktjs_core__ = (function (exports) {
54
55
  };
55
56
 
56
57
  // Shared utilities and cached native methods for kt.js framework
58
+ // import './misc/symbol-polyfill.js';
57
59
  // Re-export all utilities
58
- Object.defineProperty(window, '__ktjs__', { value: '0.22.2' });
60
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
59
61
 
60
62
  class KTRef {
61
63
  /**
@@ -97,7 +99,14 @@ var __ktjs_core__ = (function (exports) {
97
99
  this._onChanges[i](newValue, oldValue);
98
100
  }
99
101
  }
102
+ /**
103
+ * Register a callback when the value changes
104
+ * @param callback (newValue, oldValue) => xxx
105
+ */
100
106
  addOnChange(callback) {
107
+ if (typeof callback !== 'function') {
108
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
109
+ }
101
110
  this._onChanges.push(callback);
102
111
  }
103
112
  removeOnChange(callback) {
@@ -110,18 +119,26 @@ var __ktjs_core__ = (function (exports) {
110
119
  return false;
111
120
  }
112
121
  }
113
- const isKTRef = (obj) => {
114
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
115
- };
122
+ const isKTRef = (obj) => obj?.isKT === true;
116
123
  /**
117
124
  * Reference to the created HTML element.
118
125
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
119
126
  * - can alse be used to store normal values, but it is not reactive.
127
+ * - if the value is already a `KTRef`, it will be returned **directly**.
120
128
  * @param value mostly an HTMLElement
121
129
  */
122
130
  function ref(value, onChange) {
131
+ if (isKTRef(value)) {
132
+ if (onChange) {
133
+ value.addOnChange(onChange);
134
+ }
135
+ return value;
136
+ }
123
137
  return new KTRef(value, onChange ? [onChange] : []);
124
138
  }
139
+ function deref(value) {
140
+ return isKTRef(value) ? value.value : value;
141
+ }
125
142
  function kcollect() {
126
143
  const newObj = {};
127
144
  const entries = $entries(this);
@@ -146,6 +163,21 @@ var __ktjs_core__ = (function (exports) {
146
163
  }
147
164
  return newObj;
148
165
  };
166
+ // # asserts
167
+ /**
168
+ * Assert k-model to be a ref object
169
+ */
170
+ const $modelOrRef = (props, defaultValue) => {
171
+ // & props is an object. Won't use it in any other place
172
+ if ('k-model' in props) {
173
+ const kmodel = props['k-model'];
174
+ if (!kmodel?.isKT) {
175
+ throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
176
+ }
177
+ return kmodel;
178
+ }
179
+ return ref(defaultValue);
180
+ };
149
181
 
150
182
  const booleanHandler = (element, key, value) => {
151
183
  if (key in element) {
@@ -205,13 +237,24 @@ var __ktjs_core__ = (function (exports) {
205
237
  }
206
238
  }
207
239
  }
240
+ if ('k-html' in attr) {
241
+ const html = attr['k-html'];
242
+ if (isKTRef(html)) {
243
+ element.innerHTML = html.value;
244
+ html.addOnChange((v) => (element.innerHTML = v));
245
+ }
246
+ else {
247
+ element.innerHTML = html;
248
+ }
249
+ }
208
250
  for (const key in attr) {
209
251
  if (key === 'class' ||
210
252
  key === 'className' ||
211
253
  key === 'style' ||
212
254
  key === 'children' ||
213
255
  key === 'k-if' ||
214
- key.startsWith('k-model') ||
256
+ key === 'k-model' ||
257
+ key === 'k-html' ||
215
258
  key === 'ref') {
216
259
  continue;
217
260
  }
@@ -222,6 +265,7 @@ var __ktjs_core__ = (function (exports) {
222
265
  }
223
266
  // normal attributes
224
267
  else {
268
+ // todo 这里也可以绑定ref的
225
269
  (handlers[key] || defaultHandler)(element, key, o);
226
270
  }
227
271
  }
@@ -238,13 +282,25 @@ var __ktjs_core__ = (function (exports) {
238
282
  }
239
283
  }
240
284
 
285
+ const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
241
286
  function apdSingle(element, c) {
242
287
  // & JSX should ignore false, undefined, and null
243
288
  if (c === false || c === undefined || c === null) {
244
289
  return;
245
290
  }
246
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
247
- $append.call(element, c.value);
291
+ if (isKTRef(c)) {
292
+ let node = assureNode(c.value);
293
+ $append.call(element, node);
294
+ c.addOnChange((newValue, oldValue) => {
295
+ if ($isNode(newValue) && $isNode(oldValue)) {
296
+ // & this case is handled automically in `class KTRef`
297
+ // todo 2 cases might be able to merge into 1
298
+ return;
299
+ }
300
+ const oldNode = node;
301
+ node = assureNode(newValue);
302
+ oldNode.replaceWith(node);
303
+ });
248
304
  }
249
305
  else {
250
306
  $append.call(element, c);
@@ -330,7 +386,7 @@ var __ktjs_core__ = (function (exports) {
330
386
  * ## About
331
387
  * @package @ktjs/core
332
388
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
333
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
389
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
334
390
  * @license MIT
335
391
  * @link https://github.com/baendlorel/kt.js
336
392
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -704,12 +760,14 @@ var __ktjs_core__ = (function (exports) {
704
760
  return result;
705
761
  }
706
762
 
763
+ exports.$modelOrRef = $modelOrRef;
707
764
  exports.Fragment = Fragment;
708
765
  exports.KTAsync = KTAsync;
709
766
  exports.KTFor = KTFor;
710
767
  exports.KTRef = KTRef;
711
768
  exports.createElement = h;
712
769
  exports.createRedrawable = createRedrawable;
770
+ exports.deref = deref;
713
771
  exports.h = h;
714
772
  exports.isKTRef = isKTRef;
715
773
  exports.jsx = jsx;
@@ -8,6 +8,7 @@ var __ktjs_core__ = (function (exports) {
8
8
 
9
9
  // DOM manipulation utilities
10
10
  // # dom natives
11
+ const $isNode = (x) => x?.nodeType > 0;
11
12
  /**
12
13
  * & Remove `bind` because it is shockingly slower than wrapper
13
14
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -54,8 +55,9 @@ var __ktjs_core__ = (function (exports) {
54
55
  };
55
56
 
56
57
  // Shared utilities and cached native methods for kt.js framework
58
+ // import './misc/symbol-polyfill.js';
57
59
  // Re-export all utilities
58
- Object.defineProperty(window, '__ktjs__', { value: '0.22.2' });
60
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
59
61
 
60
62
  var KTRef = /** @class */ (function () {
61
63
  function KTRef(_value, _onChanges) {
@@ -93,7 +95,14 @@ var __ktjs_core__ = (function (exports) {
93
95
  enumerable: false,
94
96
  configurable: true
95
97
  });
98
+ /**
99
+ * Register a callback when the value changes
100
+ * @param callback (newValue, oldValue) => xxx
101
+ */
96
102
  KTRef.prototype.addOnChange = function (callback) {
103
+ if (typeof callback !== 'function') {
104
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
105
+ }
97
106
  this._onChanges.push(callback);
98
107
  };
99
108
  KTRef.prototype.removeOnChange = function (callback) {
@@ -107,18 +116,26 @@ var __ktjs_core__ = (function (exports) {
107
116
  };
108
117
  return KTRef;
109
118
  }());
110
- var isKTRef = function (obj) {
111
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
112
- };
119
+ var isKTRef = function (obj) { return (obj === null || obj === void 0 ? void 0 : obj.isKT) === true; };
113
120
  /**
114
121
  * Reference to the created HTML element.
115
122
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
116
123
  * - can alse be used to store normal values, but it is not reactive.
124
+ * - if the value is already a `KTRef`, it will be returned **directly**.
117
125
  * @param value mostly an HTMLElement
118
126
  */
119
127
  function ref(value, onChange) {
128
+ if (isKTRef(value)) {
129
+ if (onChange) {
130
+ value.addOnChange(onChange);
131
+ }
132
+ return value;
133
+ }
120
134
  return new KTRef(value, onChange ? [onChange] : []);
121
135
  }
136
+ function deref(value) {
137
+ return isKTRef(value) ? value.value : value;
138
+ }
122
139
  function kcollect() {
123
140
  var newObj = {};
124
141
  var entries = $entries(this);
@@ -143,6 +160,21 @@ var __ktjs_core__ = (function (exports) {
143
160
  }
144
161
  return newObj;
145
162
  };
163
+ // # asserts
164
+ /**
165
+ * Assert k-model to be a ref object
166
+ */
167
+ var $modelOrRef = function (props, defaultValue) {
168
+ // & props is an object. Won't use it in any other place
169
+ if ('k-model' in props) {
170
+ var kmodel = props['k-model'];
171
+ if (!(kmodel === null || kmodel === void 0 ? void 0 : kmodel.isKT)) {
172
+ throw new Error("[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.");
173
+ }
174
+ return kmodel;
175
+ }
176
+ return ref(defaultValue);
177
+ };
146
178
 
147
179
  var booleanHandler = function (element, key, value) {
148
180
  if (key in element) {
@@ -204,13 +236,24 @@ var __ktjs_core__ = (function (exports) {
204
236
  }
205
237
  }
206
238
  }
239
+ if ('k-html' in attr) {
240
+ var html = attr['k-html'];
241
+ if (isKTRef(html)) {
242
+ element.innerHTML = html.value;
243
+ html.addOnChange(function (v) { return (element.innerHTML = v); });
244
+ }
245
+ else {
246
+ element.innerHTML = html;
247
+ }
248
+ }
207
249
  for (var key in attr) {
208
250
  if (key === 'class' ||
209
251
  key === 'className' ||
210
252
  key === 'style' ||
211
253
  key === 'children' ||
212
254
  key === 'k-if' ||
213
- key.startsWith('k-model') ||
255
+ key === 'k-model' ||
256
+ key === 'k-html' ||
214
257
  key === 'ref') {
215
258
  continue;
216
259
  }
@@ -221,6 +264,7 @@ var __ktjs_core__ = (function (exports) {
221
264
  }
222
265
  // normal attributes
223
266
  else {
267
+ // todo 这里也可以绑定ref的
224
268
  (handlers[key] || defaultHandler)(element, key, o);
225
269
  }
226
270
  }
@@ -237,13 +281,25 @@ var __ktjs_core__ = (function (exports) {
237
281
  }
238
282
  }
239
283
 
284
+ var assureNode = function (o) { return ($isNode(o) ? o : document.createTextNode(o)); };
240
285
  function apdSingle(element, c) {
241
286
  // & JSX should ignore false, undefined, and null
242
287
  if (c === false || c === undefined || c === null) {
243
288
  return;
244
289
  }
245
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
246
- $append.call(element, c.value);
290
+ if (isKTRef(c)) {
291
+ var node_1 = assureNode(c.value);
292
+ $append.call(element, node_1);
293
+ c.addOnChange(function (newValue, oldValue) {
294
+ if ($isNode(newValue) && $isNode(oldValue)) {
295
+ // & this case is handled automically in `class KTRef`
296
+ // todo 2 cases might be able to merge into 1
297
+ return;
298
+ }
299
+ var oldNode = node_1;
300
+ node_1 = assureNode(newValue);
301
+ oldNode.replaceWith(node_1);
302
+ });
247
303
  }
248
304
  else {
249
305
  $append.call(element, c);
@@ -332,7 +388,7 @@ var __ktjs_core__ = (function (exports) {
332
388
  * ## About
333
389
  * @package @ktjs/core
334
390
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
335
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
391
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
336
392
  * @license MIT
337
393
  * @link https://github.com/baendlorel/kt.js
338
394
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -713,12 +769,14 @@ var __ktjs_core__ = (function (exports) {
713
769
  return result;
714
770
  }
715
771
 
772
+ exports.$modelOrRef = $modelOrRef;
716
773
  exports.Fragment = Fragment;
717
774
  exports.KTAsync = KTAsync;
718
775
  exports.KTFor = KTFor;
719
776
  exports.KTRef = KTRef;
720
777
  exports.createElement = h;
721
778
  exports.createRedrawable = createRedrawable;
779
+ exports.deref = deref;
722
780
  exports.h = h;
723
781
  exports.isKTRef = isKTRef;
724
782
  exports.jsx = jsx;
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
5
5
 
6
6
  // DOM manipulation utilities
7
7
  // # dom natives
8
+ const $isNode = (x) => x?.nodeType > 0;
8
9
  /**
9
10
  * & Remove `bind` because it is shockingly slower than wrapper
10
11
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -51,8 +52,9 @@ const applyModel = (element, valueRef, propName, eventName) => {
51
52
  };
52
53
 
53
54
  // Shared utilities and cached native methods for kt.js framework
55
+ // import './misc/symbol-polyfill.js';
54
56
  // Re-export all utilities
55
- Object.defineProperty(window, '__ktjs__', { value: '0.22.2' });
57
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
56
58
 
57
59
  class KTRef {
58
60
  /**
@@ -94,7 +96,14 @@ class KTRef {
94
96
  this._onChanges[i](newValue, oldValue);
95
97
  }
96
98
  }
99
+ /**
100
+ * Register a callback when the value changes
101
+ * @param callback (newValue, oldValue) => xxx
102
+ */
97
103
  addOnChange(callback) {
104
+ if (typeof callback !== 'function') {
105
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
106
+ }
98
107
  this._onChanges.push(callback);
99
108
  }
100
109
  removeOnChange(callback) {
@@ -107,18 +116,26 @@ class KTRef {
107
116
  return false;
108
117
  }
109
118
  }
110
- const isKTRef = (obj) => {
111
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
112
- };
119
+ const isKTRef = (obj) => obj?.isKT === true;
113
120
  /**
114
121
  * Reference to the created HTML element.
115
122
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
116
123
  * - can alse be used to store normal values, but it is not reactive.
124
+ * - if the value is already a `KTRef`, it will be returned **directly**.
117
125
  * @param value mostly an HTMLElement
118
126
  */
119
127
  function ref(value, onChange) {
128
+ if (isKTRef(value)) {
129
+ if (onChange) {
130
+ value.addOnChange(onChange);
131
+ }
132
+ return value;
133
+ }
120
134
  return new KTRef(value, onChange ? [onChange] : []);
121
135
  }
136
+ function deref(value) {
137
+ return isKTRef(value) ? value.value : value;
138
+ }
122
139
  function kcollect() {
123
140
  const newObj = {};
124
141
  const entries = $entries(this);
@@ -143,6 +160,21 @@ const surfaceRef = (obj) => {
143
160
  }
144
161
  return newObj;
145
162
  };
163
+ // # asserts
164
+ /**
165
+ * Assert k-model to be a ref object
166
+ */
167
+ const $modelOrRef = (props, defaultValue) => {
168
+ // & props is an object. Won't use it in any other place
169
+ if ('k-model' in props) {
170
+ const kmodel = props['k-model'];
171
+ if (!kmodel?.isKT) {
172
+ throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
173
+ }
174
+ return kmodel;
175
+ }
176
+ return ref(defaultValue);
177
+ };
146
178
 
147
179
  const booleanHandler = (element, key, value) => {
148
180
  if (key in element) {
@@ -202,13 +234,24 @@ function attrIsObject(element, attr) {
202
234
  }
203
235
  }
204
236
  }
237
+ if ('k-html' in attr) {
238
+ const html = attr['k-html'];
239
+ if (isKTRef(html)) {
240
+ element.innerHTML = html.value;
241
+ html.addOnChange((v) => (element.innerHTML = v));
242
+ }
243
+ else {
244
+ element.innerHTML = html;
245
+ }
246
+ }
205
247
  for (const key in attr) {
206
248
  if (key === 'class' ||
207
249
  key === 'className' ||
208
250
  key === 'style' ||
209
251
  key === 'children' ||
210
252
  key === 'k-if' ||
211
- key.startsWith('k-model') ||
253
+ key === 'k-model' ||
254
+ key === 'k-html' ||
212
255
  key === 'ref') {
213
256
  continue;
214
257
  }
@@ -219,6 +262,7 @@ function attrIsObject(element, attr) {
219
262
  }
220
263
  // normal attributes
221
264
  else {
265
+ // todo 这里也可以绑定ref的
222
266
  (handlers[key] || defaultHandler)(element, key, o);
223
267
  }
224
268
  }
@@ -235,13 +279,25 @@ function applyAttr(element, attr) {
235
279
  }
236
280
  }
237
281
 
282
+ const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
238
283
  function apdSingle(element, c) {
239
284
  // & JSX should ignore false, undefined, and null
240
285
  if (c === false || c === undefined || c === null) {
241
286
  return;
242
287
  }
243
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
244
- $append.call(element, c.value);
288
+ if (isKTRef(c)) {
289
+ let node = assureNode(c.value);
290
+ $append.call(element, node);
291
+ c.addOnChange((newValue, oldValue) => {
292
+ if ($isNode(newValue) && $isNode(oldValue)) {
293
+ // & this case is handled automically in `class KTRef`
294
+ // todo 2 cases might be able to merge into 1
295
+ return;
296
+ }
297
+ const oldNode = node;
298
+ node = assureNode(newValue);
299
+ oldNode.replaceWith(node);
300
+ });
245
301
  }
246
302
  else {
247
303
  $append.call(element, c);
@@ -327,7 +383,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
327
383
  * ## About
328
384
  * @package @ktjs/core
329
385
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
330
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
386
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
331
387
  * @license MIT
332
388
  * @link https://github.com/baendlorel/kt.js
333
389
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -701,4 +757,4 @@ function getSequence(arr) {
701
757
  return result;
702
758
  }
703
759
 
704
- export { Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, h, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
760
+ export { $modelOrRef, Fragment, KTAsync, KTFor, KTRef, h as createElement, createRedrawable, deref, h, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
@@ -19,6 +19,10 @@ declare class KTRef<T> {
19
19
  */
20
20
  get value(): T;
21
21
  set value(newValue: T);
22
+ /**
23
+ * Register a callback when the value changes
24
+ * @param callback (newValue, oldValue) => xxx
25
+ */
22
26
  addOnChange(callback: RefChangeHandler<T>): void;
23
27
  removeOnChange(callback: RefChangeHandler<T>): boolean;
24
28
  }
@@ -27,9 +31,11 @@ declare const isKTRef: <T = any>(obj: any) => obj is KTRef<T>;
27
31
  * Reference to the created HTML element.
28
32
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
29
33
  * - can alse be used to store normal values, but it is not reactive.
34
+ * - if the value is already a `KTRef`, it will be returned **directly**.
30
35
  * @param value mostly an HTMLElement
31
36
  */
32
- declare function ref<T = JSX.Element>(value?: T, onChange?: RefChangeHandler<T>): KTRef<T>;
37
+ declare function ref<T = JSX.Element>(value?: T | KTRef<T>, onChange?: RefChangeHandler<T>): KTRef<T>;
38
+ declare function deref<T = JSX.Element>(value: T | KTRef<T>): T;
33
39
  type KTSurfaceRef<T extends Object> = {
34
40
  [K in keyof T]: KTRef<T[K]>;
35
41
  } & {
@@ -43,6 +49,10 @@ type KTSurfaceRef<T extends Object> = {
43
49
  * - `obj.a.b` is not reactive
44
50
  */
45
51
  declare const surfaceRef: <T extends Object>(obj: T) => KTSurfaceRef<T>;
52
+ /**
53
+ * Assert k-model to be a ref object
54
+ */
55
+ declare const $modelOrRef: <T = any>(props: any, defaultValue?: T) => KTRef<T>;
46
56
 
47
57
  type HTML<T extends (HTMLTag | SVGTag | MathMLTag) & otherstring> = T extends SVGTag
48
58
  ? SVGElementTagNameMap[T]
@@ -77,6 +87,12 @@ interface KTBaseAttribute {
77
87
  */
78
88
  'k-model'?: KTRef<any>;
79
89
 
90
+ /**
91
+ * Directly apply html string to `innerHTML`.
92
+ * - Would be reactive if `KTRef` instance is provided
93
+ */
94
+ 'k-html'?: any;
95
+
80
96
  // # normal HTML attributes
81
97
  id?: string;
82
98
  class?: string;
@@ -146,7 +162,7 @@ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
146
162
  * ## About
147
163
  * @package @ktjs/core
148
164
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
149
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
165
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
150
166
  * @license MIT
151
167
  * @link https://github.com/baendlorel/kt.js
152
168
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -1425,5 +1441,5 @@ declare global {
1425
1441
  }
1426
1442
  }
1427
1443
 
1428
- export { Fragment, KTRef, h as createElement, createRedrawable, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
1444
+ export { $modelOrRef, Fragment, KTRef, h as createElement, createRedrawable, deref, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
1429
1445
  export type { KTSurfaceRef };
@@ -5,6 +5,7 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
5
5
 
6
6
  // DOM manipulation utilities
7
7
  // # dom natives
8
+ const $isNode = (x) => x?.nodeType > 0;
8
9
  /**
9
10
  * & Remove `bind` because it is shockingly slower than wrapper
10
11
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -51,8 +52,9 @@ const applyModel = (element, valueRef, propName, eventName) => {
51
52
  };
52
53
 
53
54
  // Shared utilities and cached native methods for kt.js framework
55
+ // import './misc/symbol-polyfill.js';
54
56
  // Re-export all utilities
55
- Object.defineProperty(window, '__ktjs__', { value: '0.22.2' });
57
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
56
58
 
57
59
  class KTRef {
58
60
  /**
@@ -94,7 +96,14 @@ class KTRef {
94
96
  this._onChanges[i](newValue, oldValue);
95
97
  }
96
98
  }
99
+ /**
100
+ * Register a callback when the value changes
101
+ * @param callback (newValue, oldValue) => xxx
102
+ */
97
103
  addOnChange(callback) {
104
+ if (typeof callback !== 'function') {
105
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
106
+ }
98
107
  this._onChanges.push(callback);
99
108
  }
100
109
  removeOnChange(callback) {
@@ -107,18 +116,26 @@ class KTRef {
107
116
  return false;
108
117
  }
109
118
  }
110
- const isKTRef = (obj) => {
111
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
112
- };
119
+ const isKTRef = (obj) => obj?.isKT === true;
113
120
  /**
114
121
  * Reference to the created HTML element.
115
122
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
116
123
  * - can alse be used to store normal values, but it is not reactive.
124
+ * - if the value is already a `KTRef`, it will be returned **directly**.
117
125
  * @param value mostly an HTMLElement
118
126
  */
119
127
  function ref(value, onChange) {
128
+ if (isKTRef(value)) {
129
+ if (onChange) {
130
+ value.addOnChange(onChange);
131
+ }
132
+ return value;
133
+ }
120
134
  return new KTRef(value, onChange ? [onChange] : []);
121
135
  }
136
+ function deref(value) {
137
+ return isKTRef(value) ? value.value : value;
138
+ }
122
139
  function kcollect() {
123
140
  const newObj = {};
124
141
  const entries = $entries(this);
@@ -143,6 +160,21 @@ const surfaceRef = (obj) => {
143
160
  }
144
161
  return newObj;
145
162
  };
163
+ // # asserts
164
+ /**
165
+ * Assert k-model to be a ref object
166
+ */
167
+ const $modelOrRef = (props, defaultValue) => {
168
+ // & props is an object. Won't use it in any other place
169
+ if ('k-model' in props) {
170
+ const kmodel = props['k-model'];
171
+ if (!kmodel?.isKT) {
172
+ throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
173
+ }
174
+ return kmodel;
175
+ }
176
+ return ref(defaultValue);
177
+ };
146
178
 
147
179
  const booleanHandler = (element, key, value) => {
148
180
  if (key in element) {
@@ -202,13 +234,24 @@ function attrIsObject(element, attr) {
202
234
  }
203
235
  }
204
236
  }
237
+ if ('k-html' in attr) {
238
+ const html = attr['k-html'];
239
+ if (isKTRef(html)) {
240
+ element.innerHTML = html.value;
241
+ html.addOnChange((v) => (element.innerHTML = v));
242
+ }
243
+ else {
244
+ element.innerHTML = html;
245
+ }
246
+ }
205
247
  for (const key in attr) {
206
248
  if (key === 'class' ||
207
249
  key === 'className' ||
208
250
  key === 'style' ||
209
251
  key === 'children' ||
210
252
  key === 'k-if' ||
211
- key.startsWith('k-model') ||
253
+ key === 'k-model' ||
254
+ key === 'k-html' ||
212
255
  key === 'ref') {
213
256
  continue;
214
257
  }
@@ -219,6 +262,7 @@ function attrIsObject(element, attr) {
219
262
  }
220
263
  // normal attributes
221
264
  else {
265
+ // todo 这里也可以绑定ref的
222
266
  (handlers[key] || defaultHandler)(element, key, o);
223
267
  }
224
268
  }
@@ -235,13 +279,25 @@ function applyAttr(element, attr) {
235
279
  }
236
280
  }
237
281
 
282
+ const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
238
283
  function apdSingle(element, c) {
239
284
  // & JSX should ignore false, undefined, and null
240
285
  if (c === false || c === undefined || c === null) {
241
286
  return;
242
287
  }
243
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
244
- $append.call(element, c.value);
288
+ if (isKTRef(c)) {
289
+ let node = assureNode(c.value);
290
+ $append.call(element, node);
291
+ c.addOnChange((newValue, oldValue) => {
292
+ if ($isNode(newValue) && $isNode(oldValue)) {
293
+ // & this case is handled automically in `class KTRef`
294
+ // todo 2 cases might be able to merge into 1
295
+ return;
296
+ }
297
+ const oldNode = node;
298
+ node = assureNode(newValue);
299
+ oldNode.replaceWith(node);
300
+ });
245
301
  }
246
302
  else {
247
303
  $append.call(element, c);
@@ -327,7 +383,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
327
383
  * ## About
328
384
  * @package @ktjs/core
329
385
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
330
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
386
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
331
387
  * @license MIT
332
388
  * @link https://github.com/baendlorel/kt.js
333
389
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -477,4 +533,4 @@ function createRedrawable(creator) {
477
533
  return elRef;
478
534
  }
479
535
 
480
- export { Fragment, KTRef, h as createElement, createRedrawable, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
536
+ export { $modelOrRef, Fragment, KTRef, h as createElement, createRedrawable, deref, isKTRef, jsx, jsxDEV, jsxs, ref, surfaceRef };
@@ -19,6 +19,10 @@ declare class KTRef<T> {
19
19
  */
20
20
  get value(): T;
21
21
  set value(newValue: T);
22
+ /**
23
+ * Register a callback when the value changes
24
+ * @param callback (newValue, oldValue) => xxx
25
+ */
22
26
  addOnChange(callback: RefChangeHandler<T>): void;
23
27
  removeOnChange(callback: RefChangeHandler<T>): boolean;
24
28
  }
@@ -56,6 +60,12 @@ interface KTBaseAttribute {
56
60
  */
57
61
  'k-model'?: KTRef<any>;
58
62
 
63
+ /**
64
+ * Directly apply html string to `innerHTML`.
65
+ * - Would be reactive if `KTRef` instance is provided
66
+ */
67
+ 'k-html'?: any;
68
+
59
69
  // # normal HTML attributes
60
70
  id?: string;
61
71
  class?: string;
@@ -125,7 +135,7 @@ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
125
135
  * ## About
126
136
  * @package @ktjs/core
127
137
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
128
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
138
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
129
139
  * @license MIT
130
140
  * @link https://github.com/baendlorel/kt.js
131
141
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -4,6 +4,7 @@ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then
4
4
 
5
5
  // DOM manipulation utilities
6
6
  // # dom natives
7
+ const $isNode = (x) => x?.nodeType > 0;
7
8
  /**
8
9
  * & Remove `bind` because it is shockingly slower than wrapper
9
10
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -50,8 +51,9 @@ const applyModel = (element, valueRef, propName, eventName) => {
50
51
  };
51
52
 
52
53
  // Shared utilities and cached native methods for kt.js framework
54
+ // import './misc/symbol-polyfill.js';
53
55
  // Re-export all utilities
54
- Object.defineProperty(window, '__ktjs__', { value: '0.22.2' });
56
+ Object.defineProperty(window, '__ktjs__', { value: '0.22.3' });
55
57
 
56
58
  class KTRef {
57
59
  /**
@@ -93,7 +95,14 @@ class KTRef {
93
95
  this._onChanges[i](newValue, oldValue);
94
96
  }
95
97
  }
98
+ /**
99
+ * Register a callback when the value changes
100
+ * @param callback (newValue, oldValue) => xxx
101
+ */
96
102
  addOnChange(callback) {
103
+ if (typeof callback !== 'function') {
104
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
105
+ }
97
106
  this._onChanges.push(callback);
98
107
  }
99
108
  removeOnChange(callback) {
@@ -106,16 +115,18 @@ class KTRef {
106
115
  return false;
107
116
  }
108
117
  }
109
- const isKTRef = (obj) => {
110
- return typeof obj === 'object' && obj !== null && obj.isKT === true;
111
- };
118
+ const isKTRef = (obj) => obj?.isKT === true;
112
119
  /**
113
120
  * Reference to the created HTML element.
114
121
  * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
115
122
  * - can alse be used to store normal values, but it is not reactive.
123
+ * - if the value is already a `KTRef`, it will be returned **directly**.
116
124
  * @param value mostly an HTMLElement
117
125
  */
118
126
  function ref(value, onChange) {
127
+ if (isKTRef(value)) {
128
+ return value;
129
+ }
119
130
  return new KTRef(value, []);
120
131
  }
121
132
 
@@ -177,13 +188,24 @@ function attrIsObject(element, attr) {
177
188
  }
178
189
  }
179
190
  }
191
+ if ('k-html' in attr) {
192
+ const html = attr['k-html'];
193
+ if (isKTRef(html)) {
194
+ element.innerHTML = html.value;
195
+ html.addOnChange((v) => (element.innerHTML = v));
196
+ }
197
+ else {
198
+ element.innerHTML = html;
199
+ }
200
+ }
180
201
  for (const key in attr) {
181
202
  if (key === 'class' ||
182
203
  key === 'className' ||
183
204
  key === 'style' ||
184
205
  key === 'children' ||
185
206
  key === 'k-if' ||
186
- key.startsWith('k-model') ||
207
+ key === 'k-model' ||
208
+ key === 'k-html' ||
187
209
  key === 'ref') {
188
210
  continue;
189
211
  }
@@ -194,6 +216,7 @@ function attrIsObject(element, attr) {
194
216
  }
195
217
  // normal attributes
196
218
  else {
219
+ // todo 这里也可以绑定ref的
197
220
  (handlers[key] || defaultHandler)(element, key, o);
198
221
  }
199
222
  }
@@ -210,13 +233,25 @@ function applyAttr(element, attr) {
210
233
  }
211
234
  }
212
235
 
236
+ const assureNode = (o) => ($isNode(o) ? o : document.createTextNode(o));
213
237
  function apdSingle(element, c) {
214
238
  // & JSX should ignore false, undefined, and null
215
239
  if (c === false || c === undefined || c === null) {
216
240
  return;
217
241
  }
218
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
219
- $append.call(element, c.value);
242
+ if (isKTRef(c)) {
243
+ let node = assureNode(c.value);
244
+ $append.call(element, node);
245
+ c.addOnChange((newValue, oldValue) => {
246
+ if ($isNode(newValue) && $isNode(oldValue)) {
247
+ // & this case is handled automically in `class KTRef`
248
+ // todo 2 cases might be able to merge into 1
249
+ return;
250
+ }
251
+ const oldNode = node;
252
+ node = assureNode(newValue);
253
+ oldNode.replaceWith(node);
254
+ });
220
255
  }
221
256
  else {
222
257
  $append.call(element, c);
@@ -302,7 +337,7 @@ const MATHML_ATTR_FLAG = '__kt_mathml__';
302
337
  * ## About
303
338
  * @package @ktjs/core
304
339
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
305
- * @version 0.22.2 (Last Update: 2026.02.02 20:28:37.901)
340
+ * @version 0.23.0 (Last Update: 2026.02.03 17:15:01.812)
306
341
  * @license MIT
307
342
  * @link https://github.com/baendlorel/kt.js
308
343
  * @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.22.2",
3
+ "version": "0.23.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",
@@ -44,7 +44,7 @@
44
44
  "directory": "packages/core"
45
45
  },
46
46
  "dependencies": {
47
- "@ktjs/shared": "0.22.2"
47
+ "@ktjs/shared": "0.22.3"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "rollup -c rollup.config.mjs",