@ktjs/core 0.12.0 β†’ 0.13.2

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
@@ -2,26 +2,39 @@
2
2
 
3
3
  <img src="https://raw.githubusercontent.com/baendlorel/kt.js/dev/.assets/ktjs-0.0.1.svg" alt="KT.js Logo" width="150"/>
4
4
 
5
- > πŸ“¦ Part of [KT.js](https://raw.githubusercontent.com/baendlorel/kt.js/dev/README.md) - A simple and easy-to-use web framework that never re-renders.
5
+ [![npm version](https://img.shields.io/npm/v/@ktjs/core.svg)](https://www.npmjs.com/package/@ktjs/core)
6
6
 
7
- Core DOM manipulation utilities for KT.js framework.
7
+ > πŸ“¦ Part of [KT.js](https://github.com/baendlorel/kt.js) - A simple and easy-to-use web framework that never re-renders.
8
+
9
+ Core DOM manipulation utilities for KT.js framework with built-in JSX/TSX support.
8
10
 
9
11
  ## Overview
10
12
 
11
13
  `@ktjs/core` is the foundation of KT.js, providing the essential `h` function and DOM utilities for building web applications with direct DOM manipulation. It emphasizes performance, type safety, and minimal abstraction over native DOM APIs.
12
14
 
15
+ **Current Version:** 0.13.0
16
+
13
17
  ## Features
14
18
 
15
19
  - **`h` Function**: Create HTMLElements with a simple, flexible API
16
20
  - Support for attributes, content, and event handlers
17
- - Special `@<eventName>` syntax for event handlers
21
+ - `on:<eventName>` syntax for event handlers (e.g., `on:click`)
18
22
  - Function attributes automatically treated as event listeners
19
23
  - Full TypeScript support with intelligent type inference
24
+ - **JSX/TSX Support**: Built-in JSX runtime (no separate package needed)
25
+ - Zero virtual DOM - JSX compiles directly to `h()` function calls
26
+ - Full HTML element type inference (`<button>` returns `HTMLButtonElement`)
27
+ - Support for function components
28
+ - **NEW**: `redraw()` method for controlled re-rendering
20
29
  - **KTAsync Component**: Handle async components with ease
21
30
  - Automatic handling of Promise-based components
22
31
  - Seamless integration with JSX/TSX
23
32
  - Fallback placeholder during async loading
24
33
  - Type-safe async component support
34
+ - **Redraw Mechanism**: Fine-grained control over component updates
35
+ - Update props and children selectively
36
+ - Efficient replacement strategy
37
+ - Works with both native elements and function components
25
38
  - **DOM Utilities**: Helper functions for common DOM operations
26
39
  - Native method caching for performance
27
40
  - Symbol-based private properties for internal state
@@ -68,20 +81,20 @@ const card = h('div', { class: 'card' }, [
68
81
  ```typescript
69
82
  import { h } from '@ktjs/core';
70
83
 
71
- // Function attribute (treated as event listener)
84
+ // on: prefixed attribute (event handler)
72
85
  const button1 = h(
73
86
  'button',
74
87
  {
75
- click: () => alert('Clicked!'),
88
+ 'on:click': () => alert('Clicked!'),
76
89
  },
77
90
  'Button 1'
78
91
  );
79
92
 
80
- // @-prefixed attribute (explicitly an event handler)
93
+ // Function attribute (also treated as event listener)
81
94
  const button2 = h(
82
95
  'button',
83
96
  {
84
- '@click': (e) => console.log('Event:', e),
97
+ click: (e) => console.log('Event:', e),
85
98
  'data-id': '123', // Regular attribute
86
99
  },
87
100
  'Button 2'
@@ -89,18 +102,75 @@ const button2 = h(
89
102
 
90
103
  // Both regular and event handler for same name
91
104
  const input = h('input', {
92
- change: 'change-value', // Regular attribute
93
- '@change': (e) => console.log('Changed'), // Event listener
105
+ value: 'initial', // Regular attribute
106
+ 'on:change': (e) => console.log('Changed'), // Event listener
94
107
  });
95
108
  ```
96
109
 
110
+ ### JSX/TSX Support
111
+
112
+ ```tsx
113
+ import { h } from '@ktjs/core';
114
+
115
+ // Configure tsconfig.json
116
+ {
117
+ "compilerOptions": {
118
+ "jsx": "react-jsx",
119
+ "jsxImportSource": "@ktjs/core"
120
+ }
121
+ }
122
+
123
+ // Use JSX syntax
124
+ const App = () => (
125
+ <div class="app">
126
+ <h1>Hello KT.js</h1>
127
+ <button on:click={() => alert('Hi')}>Click me</button>
128
+ </div>
129
+ );
130
+
131
+ // Function components
132
+ const Greeting = ({ name }: { name: string }) => (
133
+ <div class="greeting">Hello, {name}!</div>
134
+ );
135
+
136
+ const app = <Greeting name="World" />;
137
+ ```
138
+
139
+ ### Redraw Mechanism (v0.11+)
140
+
141
+ The new `redraw()` method allows you to update components efficiently:
142
+
143
+ ```tsx
144
+ import { h, KTHTMLElement } from '@ktjs/core';
145
+
146
+ // With JSX - get element with redraw method
147
+ const counter = (<button on:click={() => counter.redraw({ count: count + 1 })}>Count: {0}</button>) as KTHTMLElement;
148
+
149
+ // Function component with redraw
150
+ const Counter = ({ count = 0 }: { count?: number }) => (
151
+ <div>
152
+ <div>Count: {count}</div>
153
+ <button on:click={() => element.redraw({ count: count + 1 })}>Increment</button>
154
+ </div>
155
+ );
156
+
157
+ const element = (<Counter />) as KTHTMLElement;
158
+
159
+ // Update props manually
160
+ element.redraw({ count: 10 });
161
+
162
+ // Update children (for native elements)
163
+ const div = (<div>Old content</div>) as KTHTMLElement;
164
+ div.redraw(undefined, 'New content');
165
+ ```
166
+
97
167
  ### Async Components
98
168
 
99
169
  ```typescript
100
- import { KTAsync } from '@ktjs/core';
170
+ import { KTAsync, h } from '@ktjs/core';
101
171
 
102
172
  // Define an async component that returns a Promise
103
- const AsyncComponent = function () {
173
+ const AsyncComponent = () => {
104
174
  return new Promise<HTMLElement>((resolve) => {
105
175
  setTimeout(() => {
106
176
  const element = h('div', { class: 'loaded' }, 'Content loaded!');
@@ -122,11 +192,21 @@ const App = () => (
122
192
  <KTAsync component={AsyncComponent} />
123
193
  </div>
124
194
  );
195
+
196
+ // With custom placeholder
197
+ const AppWithSkeleton = () => (
198
+ <div>
199
+ <KTAsync
200
+ component={AsyncComponent}
201
+ skeleton={<div class="skeleton">Loading...</div>}
202
+ />
203
+ </div>
204
+ );
125
205
  ```
126
206
 
127
207
  **How it works:**
128
208
 
129
- - `KTAsync` creates a placeholder comment node immediately
209
+ - `KTAsync` creates a placeholder (comment node or custom skeleton) immediately
130
210
  - When the Promise resolves, it automatically replaces the placeholder with the actual element
131
211
  - If the component returns a non-Promise value, it's used directly
132
212
  - No manual DOM manipulation needed - just return a Promise from your component
package/dist/index.d.ts CHANGED
@@ -1,14 +1,3 @@
1
- interface KTRuntime {
2
- throws: (message: string) => never;
3
- defines: <T>(o: T, properties: PropertyDescriptorMap & ThisType<any>) => T;
4
- mark: (func: (...args: any[]) => any, tag: string) => void;
5
- }
6
- declare global {
7
- interface Window {
8
- readonly __ktjs__: KTRuntime;
9
- }
10
- }
11
-
12
1
  type otherstring = string & {};
13
2
 
14
3
  /**
@@ -27,6 +16,33 @@ interface KTRef<T> {
27
16
  */
28
17
  declare function ref<T = HTMLElement>(value?: T): KTRef<T>;
29
18
 
19
+ type KTHTMLElement = HTMLElement & {
20
+ /**
21
+ * Automically generate a redraw function if it is not provided
22
+ * @param props
23
+ */
24
+ redraw: (props?: KTAttribute, children?: KTRawContent) => void;
25
+ };
26
+
27
+ declare global {
28
+ namespace JSX {
29
+ type Element = KTHTMLElement;
30
+
31
+ interface IntrinsicElements {
32
+ [tag: string]: KTAttribute & { children?: KTRawContent };
33
+ }
34
+
35
+ // interface IntrinsicAttributes {
36
+ // key?: string | number;
37
+ // }
38
+ type IntrinsicAttributes = KTAttribute;
39
+
40
+ interface ElementChildrenAttribute {
41
+ children: {};
42
+ }
43
+ }
44
+ }
45
+
30
46
  type KTAvailableContent =
31
47
  | KTRef<any>
32
48
  | HTMLElement
@@ -109,15 +125,25 @@ type KTPrefixedEventHandlers = {
109
125
  [EventName in keyof HTMLElementEventMap as `on:${EventName}`]?: (ev: HTMLElementEventMap[EventName]) => void;
110
126
  };
111
127
 
112
- type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers;
128
+ type KTSpecialEventHandlers = {
129
+ 'on:ktchange'?: (value: string) => void;
130
+ 'ontrim:ktchange'?: (value: string) => void;
131
+ 'on:ktchangenumber'?: (value: number) => void;
132
+
133
+ 'on:ktinput'?: (value: string) => void;
134
+ 'ontrim:ktinput'?: (value: string) => void;
135
+ 'on:ktinputnumber'?: (value: number) => void;
136
+ };
137
+
138
+ type KTAttribute = KTBaseAttribute & KTPrefixedEventHandlers & KTSpecialEventHandlers;
113
139
 
114
140
  type KTComponent = (
115
141
  props: {
116
- ref?: KTRef<HTMLElement>;
142
+ ref?: KTRef<KTHTMLElement>;
117
143
  children?: KTRawContent;
118
144
  } & KTAttribute &
119
145
  any
120
- ) => HTMLElement | Promise<HTMLElement>;
146
+ ) => KTHTMLElement | Promise<KTHTMLElement> | any;
121
147
 
122
148
  type HTML<T extends HTMLTag & otherstring> = T extends HTMLTag ? HTMLElementTagNameMap[T] : HTMLElement;
123
149
  type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent) => HTML<T>) & {
@@ -134,7 +160,7 @@ type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent)
134
160
  * ## About
135
161
  * @package @ktjs/core
136
162
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
137
- * @version 0.12.0 (Last Update: 2026.01.14 15:48:26.320)
163
+ * @version 0.13.2 (Last Update: 2026.01.16 19:38:40.621)
138
164
  * @license MIT
139
165
  * @link https://github.com/baendlorel/kt.js
140
166
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -143,12 +169,12 @@ type H = (<T extends HTMLTag>(tag: T, attr?: KTRawAttr, content?: KTRawContent)
143
169
  */
144
170
  declare const h: H;
145
171
 
172
+ type JSXTag = HTMLTag | ((props?: any) => HTMLElement) | ((props?: any) => Promise<HTMLElement>) | ((props?: any) => KTHTMLElement) | ((props?: any) => Promise<KTHTMLElement>);
146
173
  /**
147
174
  * @param tag html tag or function component
148
175
  * @param props properties/attributes
149
- * @param _metadata metadata is ignored
150
176
  */
151
- declare function jsx<T extends HTMLTag>(tag: T | Function, props: KTRawAttr, ..._metadata: any[]): HTMLElementTagNameMap[T] | HTMLElement;
177
+ declare function jsx(tag: JSXTag, props?: KTAttribute): KTHTMLElement;
152
178
  /**
153
179
  * Fragment support - returns an array of children
154
180
  * Note: kt.js doesn't have a real Fragment concept,
@@ -166,35 +192,16 @@ declare const jsxDEV: typeof jsx;
166
192
  */
167
193
  declare const jsxs: typeof jsx;
168
194
 
169
- declare global {
170
- namespace JSX {
171
- type Element = HTMLElementTagNameMap[keyof HTMLElementTagNameMap];
172
-
173
- interface IntrinsicElements {
174
- [tag: string]: KTAttribute & { children?: KTRawContent };
175
- }
176
-
177
- // interface IntrinsicAttributes {
178
- // key?: string | number;
179
- // }
180
- type IntrinsicAttributes = KTAttribute;
181
-
182
- interface ElementChildrenAttribute {
183
- children: {};
184
- }
185
- }
186
- }
187
-
188
195
  /**
189
196
  * Extract component props type (excluding ref and children)
190
197
  */
191
198
  type ExtractComponentProps<T> = T extends (props: infer P) => any ? Omit<P, 'ref' | 'children'> : {};
192
199
  declare function KTAsync<T extends KTComponent>(props: {
193
- ref?: KTRef<HTMLElement>;
194
- skeleton?: HTMLElement;
200
+ ref?: KTRef<KTHTMLElement>;
201
+ skeleton?: KTHTMLElement;
195
202
  component: T;
196
203
  children?: KTRawContent;
197
- } & ExtractComponentProps<T>): HTMLElement;
204
+ } & ExtractComponentProps<T>): KTHTMLElement;
198
205
 
199
206
  export { Fragment, KTAsync, h as createElement, h, jsx, jsxDEV, jsxs, ref };
200
- export type { EventHandler, HTMLTag, KTAttribute, KTRawAttr, KTRawContent, KTRawContents, KTRef, KTRuntime };
207
+ export type { EventHandler, HTMLTag, KTAttribute, KTHTMLElement, KTRawAttr, KTRawContent, KTRawContents, KTRef };
@@ -5,33 +5,6 @@ var __ktjs_core__ = (function (exports) {
5
5
  throw new Error('kt.js: ' + message);
6
6
  };
7
7
 
8
- const $isArray = Array.isArray;
9
- const $keys = Object.keys;
10
- const $defines = Object.defineProperties;
11
- const $mark = (func, tag) => $defines(func, { __ktjs_h__: { value: tag, configurable: true } });
12
- const emptyPromiseHandler = () => ({});
13
- if (typeof Promise === 'undefined') {
14
- window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
15
- }
16
- const $isThenable = (o) => typeof o === 'object' && o !== null && 'then' in o && typeof o.then === 'function';
17
-
18
- (() => {
19
- const runtimeKey = '__ktjs__';
20
- if (runtimeKey in window) {
21
- return;
22
- }
23
- const __ktjs__ = Object.create(null);
24
- // & We can add new functions when we need more
25
- const descriptor = {
26
- throws: { value: $throw, enumerable: true },
27
- defines: { value: $defines, enumerable: true },
28
- mark: { value: $mark, enumerable: true },
29
- };
30
- $defines(__ktjs__, descriptor);
31
- $defines(window, { [runtimeKey]: { value: __ktjs__, enumerable: true } });
32
- return {};
33
- })();
34
-
35
8
  /**
36
9
  * & Remove `bind` because it is shockingly slower than wrapper
37
10
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -70,22 +43,30 @@ var __ktjs_core__ = (function (exports) {
70
43
  }
71
44
  };
72
45
 
73
- function booleanHandler(element, key, value) {
46
+ const $isArray = Array.isArray;
47
+ const $keys = Object.keys;
48
+ const emptyPromiseHandler = () => ({});
49
+ if (typeof Promise === 'undefined') {
50
+ window.Promise = { resolve: emptyPromiseHandler, reject: emptyPromiseHandler };
51
+ }
52
+ const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
53
+
54
+ const booleanHandler = (element, key, value) => {
74
55
  if (key in element) {
75
56
  element[key] = !!value;
76
57
  }
77
58
  else {
78
59
  element.setAttribute(key, value);
79
60
  }
80
- }
81
- function valueHandler(element, key, value) {
61
+ };
62
+ const valueHandler = (element, key, value) => {
82
63
  if (key in element) {
83
64
  element[key] = value;
84
65
  }
85
66
  else {
86
67
  element.setAttribute(key, value);
87
68
  }
88
- }
69
+ };
89
70
  // Attribute handlers map for optimized lookup
90
71
  const handlers = {
91
72
  checked: booleanHandler,
@@ -108,13 +89,18 @@ var __ktjs_core__ = (function (exports) {
108
89
  muted: booleanHandler,
109
90
  defer: booleanHandler,
110
91
  async: booleanHandler,
111
- hidden: function (element, _key, value) {
112
- element.hidden = !!value;
113
- },
92
+ hidden: (element, _key, value) => (element.hidden = !!value),
114
93
  };
115
- const defaultHandler = function (element, key, value) {
116
- return element.setAttribute(key, value);
94
+ const ktEventHandlers = {
95
+ 'on:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value)),
96
+ 'ontrim:ktchange': (element, handler) => element.addEventListener('change', () => handler(element.value.trim())),
97
+ 'on:ktchangenumber': (element, handler) => element.addEventListener('change', () => handler(Number(element.value))),
98
+ 'on:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value)),
99
+ 'ontrim:ktinput': (element, handler) => element.addEventListener('input', () => handler(element.value.trim())),
100
+ 'on:ktinputnumber': (element, handler) => element.addEventListener('input', () => handler(Number(element.value))),
117
101
  };
102
+
103
+ const defaultHandler = (element, key, value) => element.setAttribute(key, value);
118
104
  function attrIsObject(element, attr) {
119
105
  const classValue = attr.class;
120
106
  const style = attr.style;
@@ -134,11 +120,19 @@ var __ktjs_core__ = (function (exports) {
134
120
  delete attr.style;
135
121
  }
136
122
  const keys = $keys(attr);
123
+ // todo θΏ™ι‡Œηš„ε€„η†ζ―ζ¬‘ιεŽ†ιƒ½θ¦ifζ‰€ζœ‰ηš„ζƒ…ε†΅οΌŒθƒ½ε¦η”¨mapζˆ–θ€…ε―Ήθ±‘ζ₯δΌ˜εŒ–οΌŸ
137
124
  for (let i = keys.length - 1; i >= 0; i--) {
138
125
  const key = keys[i];
139
126
  const o = attr[key];
140
127
  // force register on:xxx as an event handler
141
128
  // !if o is not valid, the throwing job will be done by `on`, not kt.js
129
+ // # special handling for kt.js specific events
130
+ const ktEvent = ktEventHandlers[key];
131
+ if (ktEvent) {
132
+ ktEvent(element, o);
133
+ continue;
134
+ }
135
+ // # normal event handler
142
136
  if (key.startsWith('on:')) {
143
137
  element.addEventListener(key.slice(3), o); // chop off the `@`
144
138
  continue;
@@ -225,7 +219,7 @@ var __ktjs_core__ = (function (exports) {
225
219
  * ## About
226
220
  * @package @ktjs/core
227
221
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
228
- * @version 0.12.0 (Last Update: 2026.01.14 15:48:26.320)
222
+ * @version 0.13.2 (Last Update: 2026.01.16 19:38:40.621)
229
223
  * @license MIT
230
224
  * @link https://github.com/baendlorel/kt.js
231
225
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -243,45 +237,67 @@ var __ktjs_core__ = (function (exports) {
243
237
  applyContent(element, content);
244
238
  return element;
245
239
  });
246
- $mark(h, 'h');
247
240
 
248
241
  /**
249
242
  * @param tag html tag or function component
250
243
  * @param props properties/attributes
251
- * @param _metadata metadata is ignored
252
244
  */
253
- function jsx(tag, props, ..._metadata) {
245
+ function jsx(tag, props = {}) {
246
+ const ref = props.ref?.isKT ? props.ref : null;
247
+ if (ref) {
248
+ delete props.ref;
249
+ }
254
250
  // Handle function components
255
251
  if (typeof tag === 'function') {
256
- const propObj = typeof props === 'string' ? { class: props } : props || {};
257
- const children = propObj.children;
258
- return tag({ ...propObj, children });
259
- }
260
- // Handle regular HTML tags
261
- const propObj = typeof props === 'string' ? { class: props } : props;
262
- if (propObj === undefined || propObj === null) {
263
- return h(tag);
264
- }
265
- const children = propObj.children;
266
- delete propObj.children;
267
- // deal with ref attribute
268
- const ref = propObj.ref?.isKT ? propObj.ref : null;
269
- if (ref) {
270
- delete propObj.ref;
252
+ let el = tag(props);
253
+ if (!el.redraw) {
254
+ el.redraw = (newProps) => {
255
+ props = newProps ? { ...props, ...newProps } : props;
256
+ // $ same as below
257
+ const old = el;
258
+ el = tag(props);
259
+ el.redraw = old.redraw; // inherit redraw
260
+ if (ref) {
261
+ ref.value = el;
262
+ }
263
+ old.replaceWith(el);
264
+ };
265
+ }
266
+ if (ref) {
267
+ ref.value = el;
268
+ }
269
+ return el;
271
270
  }
272
- const el = h(tag, propObj, children);
273
- if (ref) {
274
- ref.value = el;
271
+ else {
272
+ // & deal children here
273
+ let children = props.children;
274
+ delete props.children;
275
+ let el = h(tag, props, children);
276
+ if (ref) {
277
+ ref.value = el;
278
+ }
279
+ el.redraw = (newProps, newChildren) => {
280
+ props = newProps ? { ...props, ...newProps } : props;
281
+ children = (newChildren ?? children);
282
+ // $ same as above
283
+ const old = el;
284
+ el = h(tag, props, children);
285
+ el.redraw = old.redraw; // inherit redraw
286
+ if (ref) {
287
+ ref.value = el;
288
+ }
289
+ old.replaceWith(el);
290
+ };
291
+ return el;
275
292
  }
276
- return el;
277
293
  }
278
294
  /**
279
295
  * Fragment support - returns an array of children
280
296
  * Note: kt.js doesn't have a real Fragment concept,
281
297
  */
282
298
  function Fragment(props) {
283
- window.__ktjs__.throws("kt.js doesn't have a Fragment concept");
284
- // const { children } = props || {};
299
+ throw new Error("kt.js doesn't have a Fragment concept");
300
+ // const { children } = props ?? {};
285
301
  // if (!children) {
286
302
  // return ;
287
303
  // }