vaderjs-native 1.0.35 → 1.0.37

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 (3) hide show
  1. package/index.ts +494 -340
  2. package/main.ts +36 -1
  3. package/package.json +1 -1
package/index.ts CHANGED
@@ -4,14 +4,14 @@
4
4
  dialogResolver = null;
5
5
  }
6
6
  };
7
- window.nativeHttpCallbacks = {};
7
+ window.nativeHttpCallbacks = {};
8
8
 
9
9
  window.nativeHttpResponse = (response) => {
10
- const callback = window.nativeHttpCallbacks[response.id];
11
- if (callback) {
12
- callback(response);
13
- delete window.nativeHttpCallbacks[response.id]; // Only delete the specific ID
14
- }
10
+ const callback = window.nativeHttpCallbacks[response.id];
11
+ if (callback) {
12
+ callback(response);
13
+ delete window.nativeHttpCallbacks[response.id]; // Only delete the specific ID
14
+ }
15
15
  };
16
16
 
17
17
  // Add these near the top of your file (after imports)
@@ -21,18 +21,18 @@ let globalErrorHandler: ((error: Error, componentStack?: string) => void) | null
21
21
  // Enable dev mode automatically in web environment
22
22
  if (typeof window !== 'undefined') {
23
23
  // Check for dev mode (you could also check URL or localStorage)
24
- isDev = window.location.hostname === 'localhost' ||
25
- window.location.hostname === '127.0.0.1' ||
26
- window.location.protocol === 'http:';
24
+ isDev = window.location.hostname === 'localhost' ||
25
+ window.location.hostname === '127.0.0.1' ||
26
+ window.location.protocol === 'http:';
27
27
  }
28
28
 
29
29
  // Error Boundary Component
30
30
  // Error Boundary Component - FIXED VERSION
31
- export function ErrorBoundary({
32
- children,
31
+ export function ErrorBoundary({
32
+ children,
33
33
  fallback,
34
- onError
35
- }: {
34
+ onError
35
+ }: {
36
36
  children: VNode | VNode[];
37
37
  fallback?: (error: Error, reset: () => void) => VNode;
38
38
  onError?: (error: Error, errorInfo: { componentStack: string }) => void;
@@ -47,7 +47,7 @@ export function ErrorBoundary({
47
47
  if (fallback) {
48
48
  return fallback(errorObj, () => window.location.reload());
49
49
  }
50
-
50
+
51
51
  return createElement(
52
52
  "div",
53
53
  {
@@ -66,11 +66,11 @@ export function ErrorBoundary({
66
66
  }
67
67
 
68
68
  // Inner component that uses hooks
69
- function ErrorBoundaryInner({
70
- children,
69
+ function ErrorBoundaryInner({
70
+ children,
71
71
  fallback,
72
- onError
73
- }: {
72
+ onError
73
+ }: {
74
74
  children: VNode | VNode[];
75
75
  fallback?: (error: Error, reset: () => void) => VNode;
76
76
  onError?: (error: Error, errorInfo: { componentStack: string }) => void;
@@ -122,7 +122,7 @@ function ErrorBoundaryInner({
122
122
  if (fallback) {
123
123
  return fallback(error, resetError);
124
124
  }
125
-
125
+
126
126
  return createElement(
127
127
  "div",
128
128
  {
@@ -142,36 +142,42 @@ function ErrorBoundaryInner({
142
142
  flexDirection: 'column'
143
143
  }
144
144
  },
145
- createElement("h1", { style: {
146
- color: '#ff6b6b',
147
- marginBottom: '20px',
148
- fontSize: '24px'
149
- } }, "⚠️ Application Error"),
150
-
151
- createElement("div", { style: {
152
- backgroundColor: '#2a2a2a',
153
- padding: '15px',
154
- borderRadius: '5px',
155
- marginBottom: '10px',
156
- overflow: 'auto'
157
- } },
158
- createElement("pre", { style: { margin: 0, whiteSpace: 'pre-wrap' } },
145
+ createElement("h1", {
146
+ style: {
147
+ color: '#ff6b6b',
148
+ marginBottom: '20px',
149
+ fontSize: '24px'
150
+ }
151
+ }, "⚠️ Application Error"),
152
+
153
+ createElement("div", {
154
+ style: {
155
+ backgroundColor: '#2a2a2a',
156
+ padding: '15px',
157
+ borderRadius: '5px',
158
+ marginBottom: '10px',
159
+ overflow: 'auto'
160
+ }
161
+ },
162
+ createElement("pre", { style: { margin: 0, whiteSpace: 'pre-wrap' } },
159
163
  error.toString() + (error.stack ? '\n\nStack trace:\n' + error.stack : '')
160
164
  )
161
165
  ),
162
-
163
- errorInfo && createElement("div", { style: {
164
- backgroundColor: '#2a2a2a',
165
- padding: '15px',
166
- borderRadius: '5px',
167
- marginBottom: '10px',
168
- overflow: 'auto'
169
- } },
170
- createElement("pre", { style: { margin: 0, whiteSpace: 'pre-wrap', fontSize: '12px' } },
166
+
167
+ errorInfo && createElement("div", {
168
+ style: {
169
+ backgroundColor: '#2a2a2a',
170
+ padding: '15px',
171
+ borderRadius: '5px',
172
+ marginBottom: '10px',
173
+ overflow: 'auto'
174
+ }
175
+ },
176
+ createElement("pre", { style: { margin: 0, whiteSpace: 'pre-wrap', fontSize: '12px' } },
171
177
  "Component stack:\n" + errorInfo.componentStack
172
178
  )
173
179
  ),
174
-
180
+
175
181
  createElement("div", { style: { display: 'flex', gap: '10px', marginTop: '20px' } },
176
182
  createElement("button", {
177
183
  style: {
@@ -194,11 +200,11 @@ function ErrorBoundaryInner({
194
200
  };
195
201
  deletions = [];
196
202
  nextUnitOfWork = wipRoot;
197
- requestAnimationFrame(workLoop);
203
+ requestIdleCallback(workLoop);
198
204
  }
199
205
  }
200
206
  }, "Try Again"),
201
-
207
+
202
208
  isDev && createElement("button", {
203
209
  style: {
204
210
  backgroundColor: '#4a90e2',
@@ -211,8 +217,8 @@ function ErrorBoundaryInner({
211
217
  },
212
218
  onClick: () => {
213
219
  // Copy error to clipboard
214
- const errorText = error.toString() + (error.stack ? '\n\n' + error.stack : '') +
215
- (errorInfo ? '\n\nComponent stack:\n' + errorInfo.componentStack : '');
220
+ const errorText = error.toString() + (error.stack ? '\n\n' + error.stack : '') +
221
+ (errorInfo ? '\n\nComponent stack:\n' + errorInfo.componentStack : '');
216
222
  navigator.clipboard.writeText(errorText).then(() => {
217
223
  showToast('Error copied to clipboard', 2000);
218
224
  });
@@ -257,7 +263,7 @@ export function reportError(error: Error, componentStack?: string) {
257
263
  if (componentStack) {
258
264
  console.error('[VaderJS Component Stack]', componentStack);
259
265
  }
260
-
266
+
261
267
  // Display error in UI for Android/Windows where console might not be visible
262
268
  if (platform() !== 'web') {
263
269
  if (globalErrorHandler) {
@@ -279,21 +285,25 @@ export function renderWithErrorBoundary(element: VNode, container: Node) {
279
285
  fallback: (error: Error, reset: () => void) => {
280
286
  return createElement(
281
287
  "div",
282
- { style: {
283
- padding: '20px',
284
- backgroundColor: '#f8d7da',
285
- color: '#721c24',
286
- border: '1px solid #f5c6cb',
287
- borderRadius: '5px',
288
- margin: '20px'
289
- }},
288
+ {
289
+ style: {
290
+ padding: '20px',
291
+ backgroundColor: '#f8d7da',
292
+ color: '#721c24',
293
+ border: '1px solid #f5c6cb',
294
+ borderRadius: '5px',
295
+ margin: '20px'
296
+ }
297
+ },
290
298
  createElement("h3", { style: { marginTop: 0 } }, "App Error"),
291
- createElement("pre", { style: {
292
- backgroundColor: '#f5f5f5',
293
- padding: '10px',
294
- borderRadius: '3px',
295
- overflow: 'auto'
296
- }}, error.toString()),
299
+ createElement("pre", {
300
+ style: {
301
+ backgroundColor: '#f5f5f5',
302
+ padding: '10px',
303
+ borderRadius: '3px',
304
+ overflow: 'auto'
305
+ }
306
+ }, error.toString()),
297
307
  createElement("button", {
298
308
  onClick: reset,
299
309
  style: {
@@ -334,76 +344,76 @@ export function navigate(path) {
334
344
  * @returns Promise<{status: number, body: any}>
335
345
  */
336
346
  export async function nativeHttp(options: {
337
- url: string,
338
- method?: string,
339
- headers?: Record<string, string>,
340
- body?: any
347
+ url: string,
348
+ method?: string,
349
+ headers?: Record<string, string>,
350
+ body?: any
341
351
  }): Promise<{ status: number, body: any }> {
342
- const id = Math.random().toString(36).substring(2);
343
- const platformName = platform(); // "android", "windows", "web"
344
-
345
- return new Promise<any>((resolve, reject) => {
346
- if (platformName === "android" && window.Android) {
347
-
348
- // 2. Register this specific request's resolver
349
- window.nativeHttpCallbacks[id] = (response) => {
350
- if (response.success) {
351
- let parsedBody = response.body;
352
- try { parsedBody = JSON.parse(response.body); } catch {}
353
- resolve({ status: response.status, body: parsedBody });
354
- } else {
355
- reject(new Error(response.error || "HTTP Request Failed"));
356
- }
357
- };
358
-
359
- const request = {
360
- id,
361
- url: options.url,
362
- method: options.method ?? "GET",
363
- headers: options.headers ?? {},
364
- body: options.body ? JSON.stringify(options.body) : null
365
- };
366
-
367
- window.Android.nativeHttp(JSON.stringify(request));
368
- return;
352
+ const id = Math.random().toString(36).substring(2);
353
+ const platformName = platform(); // "android", "windows", "web"
354
+
355
+ return new Promise<any>((resolve, reject) => {
356
+ if (platformName === "android" && window.Android) {
357
+
358
+ // 2. Register this specific request's resolver
359
+ window.nativeHttpCallbacks[id] = (response) => {
360
+ if (response.success) {
361
+ let parsedBody = response.body;
362
+ try { parsedBody = JSON.parse(response.body); } catch { }
363
+ resolve({ status: response.status, body: parsedBody });
364
+ } else {
365
+ reject(new Error(response.error || "HTTP Request Failed"));
369
366
  }
367
+ };
370
368
 
371
- if (platformName === "windows" && isWebView) {
372
- function handler(event: any) {
373
- const data = event.data;
374
- if (data.id === id) {
375
- window.chrome.webview.removeEventListener("message", handler);
376
- data.data.body = JSON.parse(data.data.body);
377
- if (data.error) reject(new Error(data.error));
378
- else resolve(data.data);
379
- }
380
- }
381
- window.chrome.webview.addEventListener("message", handler);
382
-
383
- window.chrome.webview.postMessage({
384
- command: "http",
385
- id,
386
- url: options.url,
387
- method: options.method ?? "GET",
388
- headers: options.headers ?? {},
389
- body: options.body ?? null
390
- });
391
- return;
369
+ const request = {
370
+ id,
371
+ url: options.url,
372
+ method: options.method ?? "GET",
373
+ headers: options.headers ?? {},
374
+ body: options.body ? JSON.stringify(options.body) : null
375
+ };
376
+
377
+ window.Android.nativeHttp(JSON.stringify(request));
378
+ return;
379
+ }
380
+
381
+ if (platformName === "windows" && isWebView) {
382
+ function handler(event: any) {
383
+ const data = event.data;
384
+ if (data.id === id) {
385
+ window.chrome.webview.removeEventListener("message", handler);
386
+ data.data.body = JSON.parse(data.data.body);
387
+ if (data.error) reject(new Error(data.error));
388
+ else resolve(data.data);
392
389
  }
390
+ }
391
+ window.chrome.webview.addEventListener("message", handler);
392
+
393
+ window.chrome.webview.postMessage({
394
+ command: "http",
395
+ id,
396
+ url: options.url,
397
+ method: options.method ?? "GET",
398
+ headers: options.headers ?? {},
399
+ body: options.body ?? null
400
+ });
401
+ return;
402
+ }
393
403
 
394
- // Web fallback
395
- fetch(options.url, {
396
- method: options.method ?? "GET",
397
- headers: options.headers,
398
- body: options.body ? JSON.stringify(options.body) : undefined
399
- })
400
- .then(async res => {
401
- const contentType = res.headers.get("Content-Type") || "";
402
- const body = contentType.includes("application/json") ? await res.json() : await res.text();
403
- resolve({ status: res.status, body });
404
- })
405
- .catch(reject);
406
- });
404
+ // Web fallback
405
+ fetch(options.url, {
406
+ method: options.method ?? "GET",
407
+ headers: options.headers,
408
+ body: options.body ? JSON.stringify(options.body) : undefined
409
+ })
410
+ .then(async res => {
411
+ const contentType = res.headers.get("Content-Type") || "";
412
+ const body = contentType.includes("application/json") ? await res.json() : await res.text();
413
+ resolve({ status: res.status, body });
414
+ })
415
+ .catch(reject);
416
+ });
407
417
  }
408
418
 
409
419
  /**
@@ -446,7 +456,7 @@ let deletions: Fiber[] | null = null;
446
456
  let wipFiber: Fiber | null = null;
447
457
  let hookIndex = 0;
448
458
  let isRenderScheduled = false;
449
-
459
+
450
460
 
451
461
 
452
462
  interface Fiber {
@@ -455,6 +465,7 @@ interface Fiber {
455
465
  props: {
456
466
  children: VNode[];
457
467
  [key: string]: any;
468
+ ref?: { current: any };
458
469
  };
459
470
  parent?: Fiber;
460
471
  child?: Fiber;
@@ -474,6 +485,7 @@ export interface VNode {
474
485
  props: {
475
486
  children: VNode[];
476
487
  [key: string]: any;
488
+ ref?: { current: any };
477
489
  };
478
490
  key?: string | number | null;
479
491
  }
@@ -534,12 +546,18 @@ function createDom(fiber: Fiber): Node {
534
546
  : document.createElement(fiber.type as string);
535
547
  }
536
548
 
537
- // Assign ref if it exists
549
+ // Initialize ref to null first
538
550
  if (fiber.props.ref) {
539
- fiber.props.ref.current = dom;
551
+ fiber.props.ref.current = null;
540
552
  }
541
553
 
542
554
  updateDom(dom, {}, fiber.props);
555
+
556
+ // Now assign the DOM element to ref
557
+ if (fiber.props.ref) {
558
+ fiber.props.ref.current = dom;
559
+ }
560
+
543
561
  return dom;
544
562
  }
545
563
 
@@ -574,7 +592,17 @@ function createWebViewSecureStore() {
574
592
  const data = event.data;
575
593
  if (data?.id === id) {
576
594
  window.chrome.webview.removeEventListener("message", handler);
577
- data.error ? reject(new Error(data.error)) : resolve(data.data);
595
+ if (data.error) {
596
+ reject(new Error(data.error))
597
+ } else {
598
+ try {
599
+ resolve(JSON.parse(data.data));
600
+ } catch {
601
+ resolve(data.data); // plain string fallback
602
+ }
603
+ }
604
+
605
+
578
606
  }
579
607
  }
580
608
 
@@ -667,68 +695,68 @@ function createBrowserSecureStore() {
667
695
  /* ─────────────────────────────── */
668
696
 
669
697
  function createAndroidSecureStore() {
670
- return {
671
- async set(key: string, value: any) {
672
- try {
673
- const result = window.Android.secureStoreSet(key, JSON.stringify(value));
674
- return result === true || result === "true";
675
- } catch (err) {
676
- console.error("Android secureStore set error:", err);
677
- return false;
678
- }
679
- },
680
- async get(key: string) {
681
- try {
682
- const result = window.Android.secureStoreGet(key);
683
- return result ? JSON.parse(result) : null;
684
- } catch (err) {
685
- console.error("Android secureStore get error:", err);
686
- return null;
687
- }
688
- },
689
- async delete(key: string) {
690
- try {
691
- const result = window.Android.secureStoreDelete(key);
692
- return result === true || result === "true";
693
- } catch (err) {
694
- console.error("Android secureStore delete error:", err);
695
- return false;
696
- }
697
- },
698
- async clear() {
699
- try {
700
- const result = window.Android.secureStoreClear();
701
- return result === true || result === "true";
702
- } catch (err) {
703
- console.error("Android secureStore clear error:", err);
704
- return false;
705
- }
706
- },
707
- async getAll() {
708
- try {
709
- const result = window.Android.secureStoreGetAll();
710
- return result ? JSON.parse(result) : {};
711
- } catch (err) {
712
- console.error("Android secureStore getAll error:", err);
713
- return {};
714
- }
715
- },
716
- async isAvailable() {
717
- return typeof window.Android?.secureStoreSet === "function";
718
- }
719
- };
698
+ return {
699
+ async set(key: string, value: any) {
700
+ try {
701
+ const result = window.Android.secureStoreSet(key, JSON.stringify(value));
702
+ return result === true || result === "true";
703
+ } catch (err) {
704
+ console.error("Android secureStore set error:", err);
705
+ return false;
706
+ }
707
+ },
708
+ async get(key: string) {
709
+ try {
710
+ const result = window.Android.secureStoreGet(key);
711
+ return result ? JSON.parse(result) : null;
712
+ } catch (err) {
713
+ console.error("Android secureStore get error:", err);
714
+ return null;
715
+ }
716
+ },
717
+ async delete(key: string) {
718
+ try {
719
+ const result = window.Android.secureStoreDelete(key);
720
+ return result === true || result === "true";
721
+ } catch (err) {
722
+ console.error("Android secureStore delete error:", err);
723
+ return false;
724
+ }
725
+ },
726
+ async clear() {
727
+ try {
728
+ const result = window.Android.secureStoreClear();
729
+ return result === true || result === "true";
730
+ } catch (err) {
731
+ console.error("Android secureStore clear error:", err);
732
+ return false;
733
+ }
734
+ },
735
+ async getAll() {
736
+ try {
737
+ const result = window.Android.secureStoreGetAll();
738
+ return result ? JSON.parse(result) : {};
739
+ } catch (err) {
740
+ console.error("Android secureStore getAll error:", err);
741
+ return {};
742
+ }
743
+ },
744
+ async isAvailable() {
745
+ return typeof window.Android?.secureStoreSet === "function";
746
+ }
747
+ };
720
748
  }
721
749
 
722
750
  // Final export
723
751
  export const secureStore = (() => {
724
- if (typeof window !== "undefined") {
725
- if (platform() === "android" && window.Android?.secureStoreSet) {
726
- return createAndroidSecureStore();
727
- }
728
- if (isWebView) return createWebViewSecureStore();
729
- return createBrowserSecureStore();
752
+ if (typeof window !== "undefined") {
753
+ if (platform() === "android" && window.Android?.secureStoreSet) {
754
+ return createAndroidSecureStore();
730
755
  }
756
+ if (isWebView) return createWebViewSecureStore();
731
757
  return createBrowserSecureStore();
758
+ }
759
+ return createBrowserSecureStore();
732
760
  })();
733
761
 
734
762
  /**
@@ -737,12 +765,24 @@ export const secureStore = (() => {
737
765
  * @param {object} prevProps - The previous properties.
738
766
  * @param {object} nextProps - The new properties.
739
767
  */
740
- function updateDom(dom: Node, prevProps: any, nextProps: any): void {
768
+ function updateDom(dom: Node, prevProps: any, nextProps: any): void {
741
769
  prevProps = prevProps || {};
742
770
  nextProps = nextProps || {};
743
771
 
744
772
  const isSvg = dom instanceof SVGElement;
745
773
 
774
+ // Handle ref changes
775
+ if (prevProps.ref !== nextProps.ref) {
776
+ // Clear old ref
777
+ if (prevProps.ref) {
778
+ prevProps.ref.current = null;
779
+ }
780
+ // Set new ref
781
+ if (nextProps.ref) {
782
+ nextProps.ref.current = dom;
783
+ }
784
+ }
785
+
746
786
  // Remove old or changed event listeners
747
787
  Object.keys(prevProps)
748
788
  .filter(isEvent)
@@ -763,6 +803,8 @@ function updateDom(dom: Node, prevProps: any, nextProps: any): void {
763
803
  (dom as Element).removeAttribute('class');
764
804
  } else if (name === 'style') {
765
805
  (dom as HTMLElement).style.cssText = '';
806
+ } else if (name === 'ref') {
807
+ // Already handled above
766
808
  } else {
767
809
  if (isSvg) {
768
810
  (dom as Element).removeAttribute(name);
@@ -788,6 +830,8 @@ function updateDom(dom: Node, prevProps: any, nextProps: any): void {
788
830
  }
789
831
  } else if (name === 'className' || name === 'class') {
790
832
  (dom as Element).setAttribute('class', nextProps[name]);
833
+ } else if (name === 'ref') {
834
+ // Already handled above
791
835
  } else {
792
836
  if (isSvg) {
793
837
  (dom as Element).setAttribute(name, nextProps[name]);
@@ -808,24 +852,10 @@ function updateDom(dom: Node, prevProps: any, nextProps: any): void {
808
852
  (dom as Element).addEventListener(eventType, handler);
809
853
  }
810
854
  });
811
-
812
- Object.keys(nextProps)
813
- .filter(isEvent)
814
- .filter(isNew(prevProps, nextProps))
815
- .forEach(name => {
816
- const eventType = name.toLowerCase().substring(2);
817
- const handler = nextProps[name];
818
- if (typeof handler === 'function') {
819
- // Remove old listener first if it exists
820
- if (prevProps[name]) {
821
- dom.removeEventListener(eventType, prevProps[name]);
822
- }
823
- // Add new listener with passive: true for better performance
824
- dom.addEventListener(eventType, handler, { passive: true });
825
- }
826
- });
827
855
  }
828
856
 
857
+
858
+
829
859
 
830
860
  /**
831
861
  * Commits the entire work-in-progress tree to the DOM.
@@ -855,6 +885,7 @@ function commitWork(fiber: Fiber | null): void {
855
885
 
856
886
  if (fiber.effectTag === "PLACEMENT" && fiber.dom != null) {
857
887
  if (domParent) domParent.appendChild(fiber.dom);
888
+ // Ref already set in createDom
858
889
  } else if (fiber.effectTag === "UPDATE" && fiber.dom != null) {
859
890
  updateDom(fiber.dom, fiber.alternate?.props ?? {}, fiber.props);
860
891
  } else if (fiber.effectTag === "DELETION") {
@@ -870,9 +901,13 @@ function commitWork(fiber: Fiber | null): void {
870
901
  * @param {Fiber} fiber - The fiber to remove.
871
902
  */
872
903
  function commitDeletion(fiber: Fiber | null): void {
873
- if (!fiber) {
874
- return;
904
+ if (!fiber) return;
905
+
906
+ // Only clear ref if this is an actual deletion (not just an update)
907
+ if (fiber.effectTag === "DELETION" && fiber.props?.ref) {
908
+ fiber.props.ref.current = null;
875
909
  }
910
+
876
911
  if (fiber.dom) {
877
912
  if (fiber.dom.parentNode) {
878
913
  fiber.dom.parentNode.removeChild(fiber.dom);
@@ -881,51 +916,104 @@ function commitDeletion(fiber: Fiber | null): void {
881
916
  commitDeletion(fiber.child);
882
917
  }
883
918
  }
884
-
885
919
 
920
+ var framesProcessed = 0;
886
921
  /**
887
922
  * Renders a virtual DOM element into a container.
888
923
  * @param {VNode} element - The root virtual DOM element to render.
889
924
  * @param {Node} container - The DOM container to render into.
890
925
  */
891
- export function render(element: VNode, container: Node): void {
926
+ export function render(element: VNode, container: Node): void {
892
927
  container.innerHTML = "";
893
-
928
+
894
929
  wipRoot = {
895
930
  dom: container,
896
931
  props: {
897
- children: isDev ? [createElement(ErrorBoundary, {}, element)] : [element],
932
+ children: [element], // Remove ErrorBoundary wrapper for now to debug
898
933
  },
899
934
  alternate: currentRoot,
900
935
  };
901
936
  deletions = [];
902
937
  nextUnitOfWork = wipRoot;
903
- requestAnimationFrame(workLoop);
938
+
939
+
940
+ // Force immediate start with requestAnimationFrame first,
941
+ // then continue with requestIdleCallback
942
+ requestAnimationFrame(() => {
943
+ const startTime = performance.now();
944
+
945
+ while (nextUnitOfWork && framesProcessed < 10 && performance.now() - startTime < 8) {
946
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
947
+ framesProcessed++;
948
+ }
949
+
950
+ if (!nextUnitOfWork && wipRoot) {
951
+ commitRoot();
952
+ }
953
+
954
+ // Continue with requestIdleCallback
955
+ if (nextUnitOfWork || wipRoot) {
956
+ requestIdleCallback(workLoop, { timeout: 100 });
957
+ }
958
+ });
904
959
  }
905
960
 
961
+
906
962
  /**
907
963
  * The main work loop for rendering and reconciliation.
908
964
  */
909
- function workLoop(): void {
910
- if (!wipRoot && currentRoot) {
911
- wipRoot = {
912
- dom: currentRoot.dom,
913
- props: currentRoot.props,
914
- alternate: currentRoot,
915
- };
916
- deletions = [];
917
- nextUnitOfWork = wipRoot;
965
+ let isRendering = false;
966
+ let renderScheduled = false;
967
+
968
+ function scheduleRender() {
969
+ if (isRendering || renderScheduled) return;
970
+
971
+ renderScheduled = true;
972
+
973
+ // Try requestIdleCallback first
974
+ if ('requestIdleCallback' in window) {
975
+ requestIdleCallback((deadline) => {
976
+ renderScheduled = false;
977
+ isRendering = true;
978
+ workLoop(deadline);
979
+ isRendering = false;
980
+ }, { timeout: 100 }); // Timeout ensures it runs even if idle time never comes
981
+ } else {
982
+ // Fallback to requestAnimationFrame
983
+ requestAnimationFrame(() => {
984
+ renderScheduled = false;
985
+ isRendering = true;
986
+ workLoop();
987
+ isRendering = false;
988
+ });
918
989
  }
990
+ }
919
991
 
920
- while (nextUnitOfWork) {
992
+ function workLoop(deadline?: IdleDeadline): void {
993
+ // Process units of work
994
+ while (nextUnitOfWork && (!deadline || deadline.timeRemaining() > 1)) {
921
995
  nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
922
996
  }
923
-
997
+
998
+ // If we finished all work, commit to DOM
924
999
  if (!nextUnitOfWork && wipRoot) {
925
1000
  commitRoot();
926
1001
  }
1002
+
1003
+ // If there's still work to do, schedule more
1004
+ if (nextUnitOfWork || wipRoot) {
1005
+ // Use a hybrid approach: requestIdleCallback with fallback
1006
+ if (deadline) {
1007
+ // We came from requestIdleCallback, use it again
1008
+ requestIdleCallback(workLoop);
1009
+ } else {
1010
+ // We came from elsewhere, start with requestIdleCallback
1011
+ requestIdleCallback(workLoop);
1012
+ }
1013
+ }
927
1014
  }
928
1015
 
1016
+
929
1017
 
930
1018
  /**
931
1019
  * Performs work on a single fiber unit.
@@ -957,17 +1045,17 @@ function performUnitOfWork(fiber: Fiber): Fiber | null {
957
1045
  function getComponentStack(fiber: Fiber): string {
958
1046
  const stack: string[] = [];
959
1047
  let currentFiber: Fiber | null = fiber;
960
-
1048
+
961
1049
  while (currentFiber) {
962
1050
  if (currentFiber.type) {
963
- const name = typeof currentFiber.type === 'function'
1051
+ const name = typeof currentFiber.type === 'function'
964
1052
  ? currentFiber.type.name || 'AnonymousComponent'
965
1053
  : String(currentFiber.type);
966
1054
  stack.push(name);
967
1055
  }
968
1056
  currentFiber = currentFiber.parent;
969
1057
  }
970
-
1058
+
971
1059
  return stack.reverse().join(' → ');
972
1060
  }
973
1061
  export const DevTools = {
@@ -976,39 +1064,39 @@ export const DevTools = {
976
1064
  localStorage.setItem('vader-dev-mode', 'true');
977
1065
  console.log('[VaderJS] Dev mode enabled');
978
1066
  },
979
-
1067
+
980
1068
  disable: () => {
981
1069
  isDev = false;
982
1070
  localStorage.removeItem('vader-dev-mode');
983
1071
  console.log('[VaderJS] Dev mode disabled');
984
1072
  },
985
-
1073
+
986
1074
  isEnabled: () => isDev,
987
-
1075
+
988
1076
  // Force error for testing
989
1077
  throwTestError: (message = 'Test error from DevTools') => {
990
1078
  throw new Error(message);
991
1079
  },
992
-
1080
+
993
1081
  // Get component tree
994
1082
  getComponentTree: () => {
995
1083
  const tree: any[] = [];
996
1084
  let fiber: Fiber | null = currentRoot;
997
-
1085
+
998
1086
  function traverse(fiber: Fiber | null, depth = 0) {
999
1087
  if (!fiber) return;
1000
-
1088
+
1001
1089
  tree.push({
1002
1090
  depth,
1003
1091
  type: typeof fiber.type === 'function' ? fiber.type.name : fiber.type,
1004
1092
  props: fiber.props,
1005
1093
  key: fiber.key
1006
1094
  });
1007
-
1095
+
1008
1096
  traverse(fiber.child, depth + 1);
1009
1097
  traverse(fiber.sibling, depth);
1010
1098
  }
1011
-
1099
+
1012
1100
  traverse(fiber);
1013
1101
  return tree;
1014
1102
  }
@@ -1026,7 +1114,7 @@ function updateFunctionComponent(fiber: Fiber) {
1026
1114
  try {
1027
1115
  // Track component stack for better error reporting
1028
1116
  const componentStack = getComponentStack(fiber);
1029
-
1117
+
1030
1118
  // Wrap component execution with error boundary
1031
1119
  if (isDev) {
1032
1120
  children = [(fiber.type as Function)(fiber.props)]
@@ -1043,7 +1131,7 @@ function updateFunctionComponent(fiber: Fiber) {
1043
1131
  // Handle error in component
1044
1132
  const componentStack = getComponentStack(fiber);
1045
1133
  reportError(error as Error, componentStack);
1046
-
1134
+
1047
1135
  // Return error boundary or fallback UI
1048
1136
  children = [createElement(
1049
1137
  "div",
@@ -1086,59 +1174,54 @@ function reconcileChildren(wipFiber: Fiber, elements: VNode[]) {
1086
1174
  let oldFiber = wipFiber.alternate?.child;
1087
1175
  let prevSibling: Fiber | null = null;
1088
1176
 
1089
- // Create map of existing fibers by key
1177
+ // 1. Map existing fibers by key for O(1) lookup
1090
1178
  const existingFibers = new Map<string | number | null, Fiber>();
1091
- while (oldFiber) {
1092
- const key = oldFiber.key ?? index;
1093
- existingFibers.set(key, oldFiber);
1094
- oldFiber = oldFiber.sibling;
1095
- index++;
1179
+ let tempOld = oldFiber;
1180
+ let i = 0;
1181
+ while (tempOld) {
1182
+ const key = tempOld.key ?? i;
1183
+ existingFibers.set(key, tempOld);
1184
+ tempOld = tempOld.sibling;
1185
+ i++;
1096
1186
  }
1097
1187
 
1098
- index = 0;
1099
- for (; index < elements.length; index++) {
1188
+ // 2. Iterate through new elements
1189
+ for (index = 0; index < elements.length; index++) {
1100
1190
  const element = elements[index];
1101
1191
  const key = element?.key ?? index;
1102
- const oldFiber = existingFibers.get(key);
1192
+ const matchedOldFiber = existingFibers.get(key);
1193
+
1194
+ const sameType = matchedOldFiber && element && element.type === matchedOldFiber.type;
1103
1195
 
1104
- const sameType = oldFiber && element && element.type === oldFiber.type;
1105
1196
  let newFiber: Fiber | null = null;
1106
1197
 
1107
1198
  if (sameType) {
1108
- // Reuse the fiber
1109
1199
  newFiber = {
1110
- type: oldFiber.type,
1200
+ type: matchedOldFiber!.type,
1111
1201
  props: element.props,
1112
- dom: oldFiber.dom,
1202
+ dom: matchedOldFiber!.dom,
1113
1203
  parent: wipFiber,
1114
- alternate: oldFiber,
1204
+ alternate: matchedOldFiber!,
1115
1205
  effectTag: "UPDATE",
1116
- hooks: oldFiber.hooks,
1117
- key
1206
+ key: key,
1118
1207
  };
1119
1208
  existingFibers.delete(key);
1120
1209
  } else if (element) {
1121
- // Create new fiber
1122
1210
  newFiber = {
1123
1211
  type: element.type,
1124
1212
  props: element.props,
1125
- dom: null,
1213
+ dom: undefined,
1126
1214
  parent: wipFiber,
1127
- alternate: null,
1215
+ alternate: undefined,
1128
1216
  effectTag: "PLACEMENT",
1129
- key
1217
+ key: key,
1130
1218
  };
1131
1219
  }
1132
1220
 
1133
- if (oldFiber && !sameType) {
1134
- oldFiber.effectTag = "DELETION";
1135
- deletions.push(oldFiber);
1136
- }
1137
-
1138
1221
  if (index === 0) {
1139
- wipFiber.child = newFiber;
1140
- } else if (prevSibling && newFiber) {
1141
- prevSibling.sibling = newFiber;
1222
+ wipFiber.child = newFiber!;
1223
+ } else if (element) {
1224
+ prevSibling!.sibling = newFiber!;
1142
1225
  }
1143
1226
 
1144
1227
  if (newFiber) {
@@ -1146,10 +1229,10 @@ function reconcileChildren(wipFiber: Fiber, elements: VNode[]) {
1146
1229
  }
1147
1230
  }
1148
1231
 
1149
- // Mark remaining old fibers for deletion
1232
+ // 3. Any fibers remaining in the map were not matched and must be deleted
1150
1233
  existingFibers.forEach(fiber => {
1151
1234
  fiber.effectTag = "DELETION";
1152
- deletions.push(fiber);
1235
+ deletions!.push(fiber);
1153
1236
  });
1154
1237
  }
1155
1238
 
@@ -1214,26 +1297,34 @@ export function useState<T>(initial: T | (() => T)): [T, (action: T | ((prevStat
1214
1297
  hook = {
1215
1298
  state: typeof initial === "function" ? (initial as () => T)() : initial,
1216
1299
  queue: [],
1217
- _needsUpdate: false
1218
1300
  };
1219
1301
  wipFiber.hooks[hookIndex] = hook;
1220
1302
  }
1221
1303
 
1222
1304
  const setState = (action: T | ((prevState: T) => T)) => {
1223
- // Calculate new state based on current state
1224
1305
  const newState = typeof action === "function"
1225
1306
  ? (action as (prevState: T) => T)(hook.state)
1226
1307
  : action;
1227
1308
 
1228
1309
  hook.state = newState;
1229
1310
 
1230
- // Reset work-in-progress root to trigger re-r
1231
-
1232
- deletions = [];
1233
- nextUnitOfWork = wipRoot;
1234
-
1235
- // Start the render process
1236
- requestAnimationFrame(workLoop);
1311
+ // Schedule a re-render
1312
+ if (currentRoot) {
1313
+ wipRoot = {
1314
+ dom: currentRoot.dom,
1315
+ props: currentRoot.props,
1316
+ alternate: currentRoot,
1317
+ };
1318
+ deletions = [];
1319
+ nextUnitOfWork = wipRoot;
1320
+
1321
+ if (typeof scheduleRender === 'function') {
1322
+ scheduleRender();
1323
+ } else {
1324
+ // Fallback
1325
+ requestAnimationFrame(workLoop);
1326
+ }
1327
+ }
1237
1328
  };
1238
1329
 
1239
1330
  hookIndex++;
@@ -1290,9 +1381,9 @@ export function Switch({ children }: { children?: VNode | VNode[] }): VNode | nu
1290
1381
  if (match) return match;
1291
1382
  return childrenArray.find(child => child && child.props?.default) || null;
1292
1383
  }
1293
-
1294
-
1295
-
1384
+
1385
+
1386
+
1296
1387
 
1297
1388
 
1298
1389
  /**
@@ -1326,14 +1417,11 @@ export function Show({ when, children }: { when: boolean; children?: VNode | VNo
1326
1417
  * @param duration
1327
1418
  */
1328
1419
  export function showToast(message: string, duration = 3000) {
1329
- if (typeof window !== "undefined" && (window as any).Android?.showToast) {
1330
- console.log("[Vader] Android Toast");
1420
+ if (typeof window !== "undefined" && (window as any).Android?.showToast) {
1331
1421
  (window as any).Android.showToast(message);
1332
1422
  return;
1333
1423
  }
1334
-
1335
- // Web fallback
1336
- console.log("[Toast]", message);
1424
+
1337
1425
 
1338
1426
  const toast = document.createElement("div");
1339
1427
  toast.textContent = message;
@@ -1451,8 +1539,7 @@ const pendingRequests = new Map<string, (data: any) => void>();
1451
1539
 
1452
1540
  // Listen for responses from Windows C#
1453
1541
  if (typeof window !== "undefined" && window.chrome?.webview) {
1454
- window.chrome.webview.addEventListener('message', (event: any) => {
1455
- console.log("Received from C#:", event.data);
1542
+ window.chrome.webview.addEventListener('message', (event: any) => {
1456
1543
  const { id, data, error } = event.data;
1457
1544
 
1458
1545
  if (pendingRequests.has(id)) {
@@ -1479,9 +1566,7 @@ function callWindows(command: string, args: any): Promise<any> {
1479
1566
  id,
1480
1567
  command,
1481
1568
  ...args
1482
- };
1483
-
1484
- console.log("Sending to C#:", message);
1569
+ };
1485
1570
  window.chrome.webview.postMessage(message);
1486
1571
  });
1487
1572
  }
@@ -1518,10 +1603,10 @@ export const FS: FS = {
1518
1603
  if (currentPlatform === "android" && window.Android) {
1519
1604
  const result = window.Android.writeFile(path, content);
1520
1605
  return result === true || result === 'true';
1521
- }else {
1522
- // write to localStorage as fallback
1523
- localStorage.setItem(path, content);
1524
- return true;
1606
+ } else {
1607
+ // write to localStorage as fallback
1608
+ localStorage.setItem(path, content);
1609
+ return true;
1525
1610
  }
1526
1611
  } catch (error) {
1527
1612
  console.error('FS.writeFile error:', error);
@@ -1544,24 +1629,24 @@ export const FS: FS = {
1544
1629
  }
1545
1630
 
1546
1631
  if (currentPlatform === "android" && window.Android) {
1547
- const result = window.Android.readFile(path);
1548
-
1549
- if (typeof result === 'string') {
1550
- try {
1551
- const parsed = JSON.parse(result);
1552
- // If the native side returned an error object, treat it as a failure
1553
- if (parsed && parsed.error) {
1554
- throw new Error(parsed.error);
1632
+ const result = window.Android.readFile(path);
1633
+
1634
+ if (typeof result === 'string') {
1635
+ try {
1636
+ const parsed = JSON.parse(result);
1637
+ // If the native side returned an error object, treat it as a failure
1638
+ if (parsed && parsed.error) {
1639
+ throw new Error(parsed.error);
1640
+ }
1641
+ } catch (e: any) {
1642
+ // If it's the Error we just threw, rethrow it to the caller
1643
+ if (e.message === "File not found") throw e;
1644
+ // Otherwise, it was just normal file content that wasn't JSON,
1645
+ // which is fine, so we let it fall through to return result.
1555
1646
  }
1556
- } catch (e: any) {
1557
- // If it's the Error we just threw, rethrow it to the caller
1558
- if (e.message === "File not found") throw e;
1559
- // Otherwise, it was just normal file content that wasn't JSON,
1560
- // which is fine, so we let it fall through to return result.
1561
1647
  }
1648
+ return result || '';
1562
1649
  }
1563
- return result || '';
1564
- }
1565
1650
  } catch (error) {
1566
1651
  throw error;
1567
1652
  }
@@ -1585,8 +1670,8 @@ export const FS: FS = {
1585
1670
  return result === true || result === 'true';
1586
1671
  }
1587
1672
  return await this.writeFile(path, '');
1588
- }else{
1589
- localStorage.removeItem(path);
1673
+ } else {
1674
+ localStorage.removeItem(path);
1590
1675
  }
1591
1676
  } catch (error) {
1592
1677
  console.error('FS.deleteFile error:', error);
@@ -1617,9 +1702,9 @@ export const FS: FS = {
1617
1702
  return result ? [result] : [];
1618
1703
  }
1619
1704
  }
1620
- }else{
1621
- const keys = Object.keys(localStorage);
1622
- return keys;
1705
+ } else {
1706
+ const keys = Object.keys(localStorage);
1707
+ return keys;
1623
1708
  }
1624
1709
  } catch (error) {
1625
1710
  console.error('FS.listDir error:', error);
@@ -1641,7 +1726,7 @@ let dialogResolver: ((value: boolean) => void) | null = null;
1641
1726
  * Use dialog hook for showing alert and confirm dialogs.
1642
1727
  * @returns {object} An object with alert and confirm methods.
1643
1728
  */
1644
- export function useDialog( ) {
1729
+ export function useDialog() {
1645
1730
  // ---- ANDROID IMPLEMENTATION ----
1646
1731
  if (typeof window !== "undefined" && (window as any).Android?.showDialog) {
1647
1732
  return {
@@ -1711,7 +1796,7 @@ export function useDialog( ) {
1711
1796
  * @param {T} initial - The initial reference value.
1712
1797
  * @returns {{current: T}} A mutable ref object.
1713
1798
  */
1714
- export function useRef<T>(initial: T): { current: T } {
1799
+ export function useRef<T>(initial: T): { current: T } {
1715
1800
  if (!wipFiber) {
1716
1801
  throw new Error("Hooks can only be called inside a Vader.js function component.");
1717
1802
  }
@@ -1723,10 +1808,12 @@ export function useRef<T>(initial: T): { current: T } {
1723
1808
  }
1724
1809
 
1725
1810
  hookIndex++;
1726
- //@ts-ignore
1727
- return hook;
1811
+ return hook as { current: T };
1728
1812
  }
1729
1813
 
1814
+
1815
+
1816
+
1730
1817
  /**
1731
1818
  * A React-like useLayoutEffect hook that runs synchronously after DOM mutations.
1732
1819
  * @param {Function} callback - The effect callback.
@@ -1762,7 +1849,7 @@ export function useLayoutEffect(callback: Function, deps?: any[]): void {
1762
1849
  hook.deps = deps;
1763
1850
  hookIndex++;
1764
1851
  }
1765
- if (platform() === "windows") {
1852
+ if (platform() === "windows") {
1766
1853
  const block = (method: string) => {
1767
1854
  return function () {
1768
1855
  throw new Error(
@@ -2044,16 +2131,84 @@ export function useQuery<T>(
2044
2131
  return { data, loading, error, refetch: fetchData };
2045
2132
  }
2046
2133
 
2047
- export function For<T>({
2048
- each,
2049
- children,
2050
- }: {
2051
- each: T[];
2052
- children: (item: T, index: number) => VNode;
2053
- }): VNode[] {
2054
- return each.map((item, index) => children(item, index));
2055
- }
2134
+ type ForChildren<T> =
2135
+ | ((item: T, index: number) => VNode | VNode[] | null)
2136
+ | VNode
2137
+ | VNode[]
2138
+ | null;
2139
+
2140
+ type RenderFn<T> = (item: T, index: number) => VNode | VNode[] | null;
2056
2141
 
2142
+
2143
+ export function For<T>(props: {
2144
+ each?: readonly T[] | null;
2145
+ children?: RenderFn<T> | RenderFn<T>[];
2146
+ }): VNode | null {
2147
+ const list = props.each;
2148
+ if (!list || list.length === 0) return null;
2149
+
2150
+ // Extract the render function from children
2151
+ // In JSX, children is passed as a prop, not as arguments to createElement
2152
+ let renderFn: RenderFn<T> | null = null;
2153
+
2154
+ if (props.children) {
2155
+ if (Array.isArray(props.children)) {
2156
+ // Find the first function in children array
2157
+ for (const child of props.children) {
2158
+ if (typeof child === "function") {
2159
+ renderFn = child as RenderFn<T>;
2160
+ break;
2161
+ }
2162
+ }
2163
+ } else if (typeof props.children === "function") {
2164
+ renderFn = props.children;
2165
+ }
2166
+ }
2167
+
2168
+ if (!renderFn) {
2169
+ console.warn("For component requires a function as children");
2170
+ return null;
2171
+ }
2172
+
2173
+ // Execute the render function for each item
2174
+ const renderedItems: (VNode | VNode[] | null)[] = [];
2175
+
2176
+ for (let i = 0; i < list.length; i++) {
2177
+ const item = list[i];
2178
+ try {
2179
+ const result = renderFn(item, i);
2180
+ if (result !== null && result !== undefined) {
2181
+ renderedItems.push(result);
2182
+ }
2183
+ } catch (error) {
2184
+ console.error("Error rendering item in For loop:", error);
2185
+ }
2186
+ }
2187
+
2188
+ // Flatten the results (renderFn might return arrays)
2189
+ const flatItems: VNode[] = [];
2190
+ for (const item of renderedItems) {
2191
+ if (Array.isArray(item)) {
2192
+ for (const subItem of item) {
2193
+ if (subItem !== null && subItem !== undefined) {
2194
+ flatItems.push(subItem);
2195
+ }
2196
+ }
2197
+ } else if (item !== null && item !== undefined) {
2198
+ flatItems.push(item);
2199
+ }
2200
+ }
2201
+
2202
+ if (flatItems.length === 0) return null;
2203
+
2204
+ // Return a fragment containing all rendered items
2205
+ return {
2206
+ type: "fragment",
2207
+ props: {
2208
+ children: flatItems
2209
+ }
2210
+ } as VNode;
2211
+ }
2057
2212
  /**
2058
2213
  * A hook for tracking window focus state.
2059
2214
  * @returns {boolean} True if the window is focused.
@@ -2189,7 +2344,7 @@ export function platform(): "windows" | "android" | "web" {
2189
2344
  * @param {(props: P) => VNode} renderFn - The component render function
2190
2345
  * @returns {(props: P) => VNode} A memoized component function
2191
2346
  */
2192
- export function component<P extends object>( renderFn: (props: P) => VNode): (props: P) => VNode {
2347
+ export function component<P extends object>(renderFn: (props: P) => VNode): (props: P) => VNode {
2193
2348
  // Create a wrapper function that will be the actual component
2194
2349
  const ComponentWrapper = (props: P): VNode => {
2195
2350
  // Check if props have changed
@@ -2197,18 +2352,18 @@ export function component<P extends object>( renderFn: (props: P) => VNode): (pr
2197
2352
  while (fiber && fiber.type !== ComponentWrapper) {
2198
2353
  fiber = fiber.alternate;
2199
2354
  }
2200
-
2355
+
2201
2356
  const prevProps = fiber?.alternate?.props || {};
2202
2357
  const nextProps = props;
2203
-
2358
+
2204
2359
  // Create a simple props comparison
2205
2360
  // For now, we'll do a shallow comparison of props
2206
2361
  let shouldUpdate = false;
2207
-
2362
+
2208
2363
  // Check if props count changed
2209
2364
  const prevKeys = Object.keys(prevProps);
2210
2365
  const nextKeys = Object.keys(nextProps);
2211
-
2366
+
2212
2367
  if (prevKeys.length !== nextKeys.length) {
2213
2368
  shouldUpdate = true;
2214
2369
  } else {
@@ -2220,7 +2375,7 @@ export function component<P extends object>( renderFn: (props: P) => VNode): (pr
2220
2375
  }
2221
2376
  }
2222
2377
  }
2223
-
2378
+
2224
2379
  // Mark fiber for memoization
2225
2380
  const currentFiber = wipFiber;
2226
2381
  if (currentFiber) {
@@ -2228,31 +2383,31 @@ export function component<P extends object>( renderFn: (props: P) => VNode): (pr
2228
2383
  currentFiber.__compareProps = (prev: P, next: P) => {
2229
2384
  const prevKeys = Object.keys(prev);
2230
2385
  const nextKeys = Object.keys(next);
2231
-
2386
+
2232
2387
  if (prevKeys.length !== nextKeys.length) return false;
2233
-
2388
+
2234
2389
  for (const key of nextKeys) {
2235
2390
  if (next[key] !== prev[key]) return false;
2236
2391
  }
2237
-
2392
+
2238
2393
  return true;
2239
2394
  };
2240
-
2395
+
2241
2396
  currentFiber.__skipMemo = !shouldUpdate;
2242
2397
  }
2243
-
2398
+
2244
2399
  // If props haven't changed, return the previous fiber's children
2245
2400
  if (!shouldUpdate && fiber?.alternate?.child) {
2246
2401
  return fiber.alternate.child.props.children[0];
2247
2402
  }
2248
-
2403
+
2249
2404
  // Otherwise render with new props
2250
2405
  return renderFn(props);
2251
2406
  };
2252
-
2407
+
2253
2408
  // Set display name for debugging
2254
2409
  (ComponentWrapper as any).displayName = name;
2255
-
2410
+
2256
2411
  return ComponentWrapper;
2257
2412
  }
2258
2413
  export function Fragment({ children }: { children: VNode | VNode[] }): VNode | null {
@@ -2303,4 +2458,3 @@ Object.defineProperty(window, "Vader", {
2303
2458
  configurable: false,
2304
2459
  });
2305
2460
 
2306
-