elit 3.5.6 → 3.5.7

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.
Files changed (113) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +1 -1
  3. package/desktop/build.rs +83 -0
  4. package/desktop/icon.rs +106 -0
  5. package/desktop/lib.rs +2 -0
  6. package/desktop/main.rs +235 -0
  7. package/desktop/native_main.rs +128 -0
  8. package/desktop/native_renderer/action_widgets.rs +184 -0
  9. package/desktop/native_renderer/app_models.rs +171 -0
  10. package/desktop/native_renderer/app_runtime.rs +140 -0
  11. package/desktop/native_renderer/container_rendering.rs +610 -0
  12. package/desktop/native_renderer/content_widgets.rs +634 -0
  13. package/desktop/native_renderer/css_models.rs +371 -0
  14. package/desktop/native_renderer/embedded_surfaces.rs +414 -0
  15. package/desktop/native_renderer/form_controls.rs +516 -0
  16. package/desktop/native_renderer/interaction_dispatch.rs +89 -0
  17. package/desktop/native_renderer/runtime_support.rs +135 -0
  18. package/desktop/native_renderer/utilities.rs +495 -0
  19. package/desktop/native_renderer/vector_drawing.rs +491 -0
  20. package/desktop/native_renderer.rs +4122 -0
  21. package/desktop/runtime/external.rs +422 -0
  22. package/desktop/runtime/mod.rs +67 -0
  23. package/desktop/runtime/quickjs.rs +106 -0
  24. package/desktop/window.rs +383 -0
  25. package/package.json +6 -3
  26. package/dist/build.d.mts +0 -20
  27. package/dist/chokidar.d.mts +0 -134
  28. package/dist/cli.d.mts +0 -81
  29. package/dist/config.d.mts +0 -254
  30. package/dist/coverage.d.mts +0 -85
  31. package/dist/database.d.mts +0 -52
  32. package/dist/desktop.d.mts +0 -68
  33. package/dist/dom.d.mts +0 -87
  34. package/dist/el.d.mts +0 -208
  35. package/dist/fs.d.mts +0 -255
  36. package/dist/hmr.d.mts +0 -38
  37. package/dist/http.d.mts +0 -169
  38. package/dist/https.d.mts +0 -108
  39. package/dist/index.d.mts +0 -13
  40. package/dist/mime-types.d.mts +0 -48
  41. package/dist/native.d.mts +0 -136
  42. package/dist/path.d.mts +0 -163
  43. package/dist/router.d.mts +0 -49
  44. package/dist/runtime.d.mts +0 -97
  45. package/dist/server-D0Dp4R5z.d.mts +0 -449
  46. package/dist/server.d.mts +0 -7
  47. package/dist/state.d.mts +0 -117
  48. package/dist/style.d.mts +0 -232
  49. package/dist/test-reporter.d.mts +0 -77
  50. package/dist/test-runtime.d.mts +0 -122
  51. package/dist/test.d.mts +0 -39
  52. package/dist/types.d.mts +0 -586
  53. package/dist/universal.d.mts +0 -21
  54. package/dist/ws.d.mts +0 -200
  55. package/dist/wss.d.mts +0 -108
  56. package/src/build.ts +0 -362
  57. package/src/chokidar.ts +0 -427
  58. package/src/cli.ts +0 -1162
  59. package/src/config.ts +0 -509
  60. package/src/coverage.ts +0 -1479
  61. package/src/database.ts +0 -1410
  62. package/src/desktop-auto-render.ts +0 -317
  63. package/src/desktop-cli.ts +0 -1533
  64. package/src/desktop.ts +0 -99
  65. package/src/dev-build.ts +0 -340
  66. package/src/dom.ts +0 -901
  67. package/src/el.ts +0 -183
  68. package/src/fs.ts +0 -609
  69. package/src/hmr.ts +0 -149
  70. package/src/http.ts +0 -856
  71. package/src/https.ts +0 -411
  72. package/src/index.ts +0 -16
  73. package/src/mime-types.ts +0 -222
  74. package/src/mobile-cli.ts +0 -2313
  75. package/src/native-background.ts +0 -444
  76. package/src/native-border.ts +0 -343
  77. package/src/native-canvas.ts +0 -260
  78. package/src/native-cli.ts +0 -414
  79. package/src/native-color.ts +0 -904
  80. package/src/native-estimation.ts +0 -194
  81. package/src/native-grid.ts +0 -590
  82. package/src/native-interaction.ts +0 -1289
  83. package/src/native-layout.ts +0 -568
  84. package/src/native-link.ts +0 -76
  85. package/src/native-render-support.ts +0 -361
  86. package/src/native-spacing.ts +0 -231
  87. package/src/native-state.ts +0 -318
  88. package/src/native-strings.ts +0 -46
  89. package/src/native-transform.ts +0 -120
  90. package/src/native-types.ts +0 -439
  91. package/src/native-typography.ts +0 -254
  92. package/src/native-units.ts +0 -441
  93. package/src/native-vector.ts +0 -910
  94. package/src/native.ts +0 -5606
  95. package/src/path.ts +0 -493
  96. package/src/pm-cli.ts +0 -2498
  97. package/src/preview-build.ts +0 -294
  98. package/src/render-context.ts +0 -138
  99. package/src/router.ts +0 -260
  100. package/src/runtime.ts +0 -97
  101. package/src/server.ts +0 -2294
  102. package/src/state.ts +0 -556
  103. package/src/style.ts +0 -1790
  104. package/src/test-globals.d.ts +0 -184
  105. package/src/test-reporter.ts +0 -609
  106. package/src/test-runtime.ts +0 -1359
  107. package/src/test.ts +0 -368
  108. package/src/types.ts +0 -381
  109. package/src/universal.ts +0 -81
  110. package/src/wapk-cli.ts +0 -3213
  111. package/src/workspace-package.ts +0 -102
  112. package/src/ws.ts +0 -648
  113. package/src/wss.ts +0 -241
package/src/state.ts DELETED
@@ -1,556 +0,0 @@
1
- /**
2
- * Elit - State Management
3
- */
4
-
5
- import type { State, StateOptions, VNode, VirtualListController, Child, Props } from './types';
6
- import { dom } from './dom';
7
-
8
- export type NativeBindingKind = 'value' | 'checked';
9
-
10
- export interface NativeBindingMetadata<T = unknown> {
11
- kind: NativeBindingKind;
12
- state: State<T>;
13
- }
14
-
15
- export const ELIT_NATIVE_BINDING = Symbol.for('elit.native.binding');
16
- const ELIT_INTERNAL_WS_PATH = '/__elit_ws';
17
-
18
- // State management helpers
19
- export const createState = <T>(initial: T, options?: StateOptions): State<T> =>
20
- dom.createState(initial, options);
21
-
22
- export const computed = <T extends any[], R>(
23
- states: { [K in keyof T]: State<T[K]> },
24
- fn: (...values: T) => R
25
- ): State<R> => dom.computed(states, fn);
26
-
27
- export const effect = (fn: () => void): void => dom.effect(fn);
28
-
29
- // Performance helpers
30
- export const batchRender = (container: string | HTMLElement, vNodes: VNode[]): HTMLElement =>
31
- dom.batchRender(container, vNodes);
32
-
33
- export const renderChunked = (
34
- container: string | HTMLElement,
35
- vNodes: VNode[],
36
- chunkSize?: number,
37
- onProgress?: (current: number, total: number) => void
38
- ): HTMLElement => dom.renderChunked(container, vNodes, chunkSize, onProgress);
39
-
40
- export const createVirtualList = <T>(
41
- container: HTMLElement,
42
- items: T[],
43
- renderItem: (item: T, index: number) => VNode,
44
- itemHeight?: number,
45
- bufferSize?: number
46
- ): VirtualListController => dom.createVirtualList(container, items, renderItem, itemHeight, bufferSize);
47
-
48
- export const lazy = <T extends any[], R>(loadFn: () => Promise<(...args: T) => R>) =>
49
- dom.lazy(loadFn);
50
-
51
- export const cleanupUnused = (root: HTMLElement): number =>
52
- dom.cleanupUnusedElements(root);
53
-
54
- // Throttle helper
55
- export const throttle = <T extends any[]>(fn: (...args: T) => void, delay: number) => {
56
- let timer: NodeJS.Timeout | null = null;
57
- return (...args: T) => {
58
- if (!timer) {
59
- timer = setTimeout(() => {
60
- timer = null;
61
- fn(...args);
62
- }, delay);
63
- }
64
- };
65
- };
66
-
67
- // Debounce helper
68
- export const debounce = <T extends any[]>(fn: (...args: T) => void, delay: number) => {
69
- let timer: NodeJS.Timeout | null = null;
70
- return (...args: T) => {
71
- timer && clearTimeout(timer);
72
- timer = setTimeout(() => fn(...args), delay);
73
- };
74
- };
75
-
76
- export function bindValue<T extends string | number | string[]>(state: State<T>): Props {
77
- const props = {
78
- value: state.value,
79
- onInput: (event: Event) => {
80
- const target = event.target as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null;
81
- if (!target) {
82
- return;
83
- }
84
-
85
- if (target instanceof HTMLSelectElement && target.multiple) {
86
- state.value = Array.from(target.selectedOptions).map((option) => option.value) as T;
87
- return;
88
- }
89
-
90
- const nextValue = typeof state.value === 'number'
91
- ? Number(target.value)
92
- : target.value;
93
-
94
- state.value = (typeof state.value === 'number' && Number.isNaN(nextValue))
95
- ? state.value
96
- : nextValue as T;
97
- },
98
- } as Props & { [ELIT_NATIVE_BINDING]: NativeBindingMetadata<T> };
99
-
100
- props[ELIT_NATIVE_BINDING] = {
101
- kind: 'value',
102
- state,
103
- };
104
-
105
- return props;
106
- }
107
-
108
- export function bindChecked(state: State<boolean>): Props {
109
- const props = {
110
- checked: state.value,
111
- onInput: (event: Event) => {
112
- const target = event.target as HTMLInputElement | null;
113
- if (!target) {
114
- return;
115
- }
116
-
117
- state.value = Boolean(target.checked);
118
- },
119
- } as Props & { [ELIT_NATIVE_BINDING]: NativeBindingMetadata<boolean> };
120
-
121
- props[ELIT_NATIVE_BINDING] = {
122
- kind: 'checked',
123
- state,
124
- };
125
-
126
- return props;
127
- }
128
-
129
- // ===== Shared State - syncs with elit-server =====
130
-
131
- type StateChangeCallback<T = any> = (value: T, oldValue: T) => void;
132
-
133
- interface StateMessage {
134
- type: 'state:init' | 'state:update' | 'state:subscribe' | 'state:unsubscribe' | 'state:change';
135
- key: string;
136
- value?: any;
137
- timestamp?: number;
138
- }
139
-
140
- function resolveSharedStateWebSocketUrl(wsUrl?: string): string {
141
- const protocol = typeof location !== 'undefined' && location.protocol === 'https:' ? 'wss:' : 'ws:';
142
- const origin = typeof location !== 'undefined' ? `${protocol}//${location.host}` : `${protocol}//localhost`;
143
-
144
- if (!wsUrl) {
145
- return `${origin}${ELIT_INTERNAL_WS_PATH}`;
146
- }
147
-
148
- if (/^wss?:\/\//i.test(wsUrl)) {
149
- const parsedUrl = new URL(wsUrl);
150
- if (!parsedUrl.pathname || parsedUrl.pathname === '/') {
151
- parsedUrl.pathname = ELIT_INTERNAL_WS_PATH;
152
- }
153
- return parsedUrl.toString();
154
- }
155
-
156
- if (wsUrl.startsWith('/')) {
157
- return `${origin}${wsUrl}`;
158
- }
159
-
160
- return wsUrl;
161
- }
162
-
163
- /**
164
- * Shared State - syncs with elit-server
165
- */
166
- export class SharedState<T = any> {
167
- private localState: State<T>;
168
- private ws: WebSocket | null = null;
169
- private pendingUpdates: T[] = [];
170
- private previousValue: T;
171
-
172
- constructor(
173
- public readonly key: string,
174
- defaultValue: T,
175
- private wsUrl?: string
176
- ) {
177
- this.localState = createState(defaultValue);
178
- this.previousValue = defaultValue;
179
- this.connect();
180
- }
181
-
182
- /**
183
- * Get current value
184
- */
185
- get value(): T {
186
- return this.localState.value;
187
- }
188
-
189
- /**
190
- * Set new value and sync to server
191
- */
192
- set value(newValue: T) {
193
- this.previousValue = this.localState.value;
194
- this.localState.value = newValue;
195
- this.sendToServer(newValue);
196
- }
197
-
198
- /**
199
- * Get the underlying Elit State (for reactive binding)
200
- */
201
- get state(): State<T> {
202
- return this.localState;
203
- }
204
-
205
- /**
206
- * Subscribe to changes (returns Elit State for reactive)
207
- */
208
- onChange(callback: StateChangeCallback<T>): () => void {
209
- return this.localState.subscribe((newValue) => {
210
- const oldValue = this.previousValue;
211
- this.previousValue = newValue;
212
- callback(newValue, oldValue);
213
- });
214
- }
215
-
216
- /**
217
- * Update value using a function
218
- */
219
- update(updater: (current: T) => T): void {
220
- this.value = updater(this.value);
221
- }
222
-
223
- /**
224
- * Connect to WebSocket
225
- */
226
- private connect(): void {
227
- if (typeof window === 'undefined') return;
228
-
229
- const url = resolveSharedStateWebSocketUrl(this.wsUrl);
230
- this.ws = new WebSocket(url);
231
-
232
- this.ws.addEventListener('open', () => {
233
- this.subscribe();
234
-
235
- // Send pending updates
236
- while (this.pendingUpdates.length > 0) {
237
- const value = this.pendingUpdates.shift();
238
- this.sendToServer(value!);
239
- }
240
- });
241
-
242
- this.ws.addEventListener('message', (event) => {
243
- this.handleMessage(event.data);
244
- });
245
-
246
- this.ws.addEventListener('close', () => {
247
- // Reconnect after delay
248
- setTimeout(() => this.connect(), 1000);
249
- });
250
-
251
- this.ws.addEventListener('error', (error) => {
252
- console.error('[SharedState] WebSocket error:', error);
253
- });
254
- }
255
-
256
- /**
257
- * Subscribe to server state
258
- */
259
- private subscribe(): void {
260
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
261
-
262
- this.ws.send(JSON.stringify({
263
- type: 'state:subscribe',
264
- key: this.key
265
- }));
266
- }
267
-
268
- /**
269
- * Handle message from server
270
- */
271
- private handleMessage(data: string): void {
272
- try {
273
- const msg = JSON.parse(data) as StateMessage;
274
-
275
- if (msg.key !== this.key) return;
276
-
277
- if (msg.type === 'state:init' || msg.type === 'state:update') {
278
- // Update local state without sending back to server
279
- this.localState.value = msg.value;
280
- }
281
- } catch (error) {
282
- // Ignore parse errors (could be HMR messages)
283
- }
284
- }
285
-
286
- /**
287
- * Send value to server
288
- */
289
- private sendToServer(value: T): void {
290
- if (!this.ws) return;
291
-
292
- if (this.ws.readyState !== WebSocket.OPEN) {
293
- // Queue update for when connection is ready
294
- this.pendingUpdates.push(value);
295
- return;
296
- }
297
-
298
- this.ws.send(JSON.stringify({
299
- type: 'state:change',
300
- key: this.key,
301
- value
302
- }));
303
- }
304
-
305
- /**
306
- * Disconnect
307
- */
308
- disconnect(): void {
309
- if (this.ws) {
310
- this.ws.close();
311
- this.ws = null;
312
- }
313
- }
314
-
315
- /**
316
- * Destroy state and cleanup
317
- */
318
- destroy(): void {
319
- this.disconnect();
320
- this.localState.destroy();
321
- }
322
- }
323
-
324
- /**
325
- * Create a shared state that syncs with elit-server
326
- */
327
- export function createSharedState<T>(
328
- key: string,
329
- defaultValue: T,
330
- wsUrl?: string
331
- ): SharedState<T> {
332
- return new SharedState(key, defaultValue, wsUrl);
333
- }
334
-
335
- /**
336
- * Shared State Manager for managing multiple shared states
337
- */
338
- class SharedStateManager {
339
- private states = new Map<string, SharedState<any>>();
340
-
341
- /**
342
- * Create or get a shared state
343
- */
344
- create<T>(key: string, defaultValue: T, wsUrl?: string): SharedState<T> {
345
- if (this.states.has(key)) {
346
- return this.states.get(key) as SharedState<T>;
347
- }
348
-
349
- const state = new SharedState<T>(key, defaultValue, wsUrl);
350
- this.states.set(key, state);
351
- return state;
352
- }
353
-
354
- /**
355
- * Get existing state
356
- */
357
- get<T>(key: string): SharedState<T> | undefined {
358
- return this.states.get(key) as SharedState<T>;
359
- }
360
-
361
- /**
362
- * Delete a state
363
- */
364
- delete(key: string): boolean {
365
- const state = this.states.get(key);
366
- if (state) {
367
- state.destroy();
368
- return this.states.delete(key);
369
- }
370
- return false;
371
- }
372
-
373
- /**
374
- * Clear all states
375
- */
376
- clear(): void {
377
- this.states.forEach(state => state.destroy());
378
- this.states.clear();
379
- }
380
- }
381
-
382
- // Export singleton instance
383
- export const sharedStateManager = new SharedStateManager();
384
-
385
- // ===== Reactive Rendering Helpers =====
386
-
387
- // Helper function to schedule RAF updates (reused in reactive and reactiveAs)
388
- const scheduleRAFUpdate = (rafId: number | null, updateFn: () => void): number => {
389
- rafId && cancelAnimationFrame(rafId);
390
- return requestAnimationFrame(() => {
391
- updateFn();
392
- });
393
- };
394
-
395
- // Helper function to render content to fragment (reused in reactive and reactiveAs)
396
- const renderToFragment = (content: VNode | Child | Child[], isVNode?: boolean): DocumentFragment => {
397
- const fragment = document.createDocumentFragment();
398
-
399
- if (Array.isArray(content)) {
400
- // Handle array of children
401
- for (const child of content) {
402
- dom.renderToDOM(child, fragment);
403
- }
404
- } else if (isVNode && content && typeof content === 'object' && 'tagName' in content) {
405
- const { children } = content as VNode;
406
- for (const child of children) {
407
- dom.renderToDOM(child, fragment);
408
- }
409
- } else {
410
- dom.renderToDOM(content, fragment);
411
- }
412
-
413
- return fragment;
414
- };
415
-
416
- // Helper function to update element props (reused in reactive)
417
- const updateElementProps = (element: HTMLElement | SVGElement, props: Props): void => {
418
- for (const key in props) {
419
- const value = props[key];
420
- if (key === 'ref') continue;
421
-
422
- if (key === 'class' || key === 'className') {
423
- (element as HTMLElement).className = Array.isArray(value) ? value.join(' ') : (value || '');
424
- } else if (key === 'style' && typeof value === 'object') {
425
- const s = (element as HTMLElement).style;
426
- for (const k in value) (s as any)[k] = value[k];
427
- } else if (key.startsWith('on')) {
428
- (element as any)[key.toLowerCase()] = value;
429
- } else if (value != null && value !== false) {
430
- element.setAttribute(key, String(value === true ? '' : value));
431
- } else {
432
- element.removeAttribute(key);
433
- }
434
- }
435
- };
436
-
437
- // Reactive element helpers
438
- export const reactive = <T>(state: State<T>, renderFn: (value: T) => VNode | Child | Child[]): VNode => {
439
- let rafId: number | null = null;
440
- let elementRef: HTMLElement | SVGElement | null = null;
441
- let placeholder: Comment | null = null;
442
- let isInDOM = true;
443
-
444
- const initialResult = renderFn(state.value);
445
- const isVNodeResult = initialResult && typeof initialResult === 'object' && 'tagName' in initialResult;
446
- const initialIsNull = initialResult == null || initialResult === false;
447
-
448
- const updateElement = () => {
449
- if (!elementRef && !placeholder) return;
450
-
451
- const newResult = renderFn(state.value);
452
- const resultIsNull = newResult == null || newResult === false;
453
-
454
- if (resultIsNull) {
455
- if (isInDOM && elementRef) {
456
- placeholder = document.createComment('reactive');
457
- elementRef.parentNode?.replaceChild(placeholder, elementRef);
458
- isInDOM = false;
459
- }
460
- } else {
461
- if (!isInDOM && placeholder && elementRef) {
462
- placeholder.parentNode?.replaceChild(elementRef, placeholder);
463
- placeholder = null;
464
- isInDOM = true;
465
- }
466
-
467
- if (elementRef) {
468
- const isCurrentVNode = !!(isVNodeResult && newResult && typeof newResult === 'object' && 'tagName' in newResult);
469
- if (isCurrentVNode) {
470
- const { props } = newResult as VNode;
471
- updateElementProps(elementRef, props);
472
- }
473
- const fragment = renderToFragment(newResult as any, isCurrentVNode);
474
- elementRef.textContent = '';
475
- elementRef.appendChild(fragment);
476
- dom.getElementCache().set(elementRef, true);
477
- }
478
- }
479
- };
480
-
481
- state.subscribe(() => {
482
- rafId = scheduleRAFUpdate(rafId, () => {
483
- updateElement();
484
- rafId = null;
485
- });
486
- });
487
-
488
- const refCallback = (el: HTMLElement | SVGElement) => {
489
- elementRef = el;
490
- if (initialIsNull && el.parentNode) {
491
- placeholder = document.createComment('reactive');
492
- el.parentNode.replaceChild(placeholder, el);
493
- isInDOM = false;
494
- }
495
- };
496
-
497
- if (isVNodeResult) {
498
- const vnode = initialResult as VNode;
499
- return {
500
- tagName: vnode.tagName,
501
- props: { ...vnode.props, ref: refCallback },
502
- children: vnode.children
503
- };
504
- }
505
-
506
- // Handle array result - wrap in fragment-like VNode
507
- const initialChildren = Array.isArray(initialResult) ? initialResult : [initialResult];
508
-
509
- // Use span with display: contents as wrapper (doesn't affect layout)
510
- return { tagName: 'span', props: { ref: refCallback, style: { display: 'contents' } }, children: initialChildren };
511
- };
512
-
513
- // Reactive element with custom wrapper tag
514
- export const reactiveAs = <T>(
515
- tagName: string,
516
- state: State<T>,
517
- renderFn: (value: T) => VNode | Child | Child[],
518
- props: Props = {}
519
- ): VNode => {
520
- let rafId: number | null = null;
521
- let elementRef: HTMLElement | SVGElement | null = null;
522
-
523
- state.subscribe(() => {
524
- rafId = scheduleRAFUpdate(rafId, () => {
525
- if (elementRef) {
526
- const newResult = renderFn(state.value);
527
-
528
- if (newResult == null || newResult === false) {
529
- (elementRef as HTMLElement).style.display = 'none';
530
- elementRef.textContent = '';
531
- } else {
532
- (elementRef as HTMLElement).style.display = '';
533
- const fragment = renderToFragment(newResult as any, false);
534
- elementRef.textContent = '';
535
- elementRef.appendChild(fragment);
536
- }
537
- dom.getElementCache().set(elementRef, true);
538
- }
539
- rafId = null;
540
- });
541
- });
542
-
543
- const refCallback = (el: HTMLElement | SVGElement) => {
544
- elementRef = el;
545
- };
546
-
547
- const initialResult = renderFn(state.value);
548
- const initialChildren = Array.isArray(initialResult) ? initialResult : [initialResult];
549
-
550
- return { tagName, props: { ...props, ref: refCallback }, children: initialChildren };
551
- };
552
-
553
- export const text = (state: State<any> | any): VNode | string =>
554
- (state && state.value !== undefined)
555
- ? reactive(state, v => ({ tagName: 'span', props: {}, children: [String(v)] }))
556
- : String(state);