react-native-ai-debugger 1.0.24 → 1.0.26

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.
@@ -415,4 +415,728 @@ export async function reloadApp() {
415
415
  };
416
416
  }
417
417
  }
418
+ function formatTreeToTonl(node, indent = 0) {
419
+ const prefix = ' '.repeat(indent);
420
+ let result = `${prefix}${node.component}`;
421
+ // Add props inline if present
422
+ if (node.props && Object.keys(node.props).length > 0) {
423
+ const propsStr = Object.entries(node.props)
424
+ .map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)
425
+ .join(',');
426
+ result += ` (${propsStr})`;
427
+ }
428
+ // Add layout inline if present
429
+ if (node.layout && Object.keys(node.layout).length > 0) {
430
+ const layoutStr = Object.entries(node.layout)
431
+ .map(([k, v]) => `${k}:${v}`)
432
+ .join(',');
433
+ result += ` [${layoutStr}]`;
434
+ }
435
+ result += '\n';
436
+ // Recurse children
437
+ if (node.children && node.children.length > 0) {
438
+ for (const child of node.children) {
439
+ result += formatTreeToTonl(child, indent + 1);
440
+ }
441
+ }
442
+ return result;
443
+ }
444
+ function formatScreenLayoutToTonl(elements) {
445
+ const lines = ['#elements{component,path,depth,layout,id}'];
446
+ for (const el of elements) {
447
+ const layout = el.layout ? Object.entries(el.layout).map(([k, v]) => `${k}:${v}`).join(';') : '';
448
+ const id = el.identifiers?.testID || el.identifiers?.accessibilityLabel || '';
449
+ lines.push(`${el.component}|${el.path}|${el.depth}|${layout}|${id}`);
450
+ }
451
+ return lines.join('\n');
452
+ }
453
+ function formatFoundComponentsToTonl(components) {
454
+ const lines = ['#found{component,path,depth,key,layout}'];
455
+ for (const c of components) {
456
+ const layout = c.layout ? Object.entries(c.layout).map(([k, v]) => `${k}:${v}`).join(';') : '';
457
+ lines.push(`${c.component}|${c.path}|${c.depth}|${c.key || ''}|${layout}`);
458
+ }
459
+ return lines.join('\n');
460
+ }
461
+ function formatSummaryToTonl(components, total) {
462
+ const lines = [`#summary total=${total}`];
463
+ for (const c of components) {
464
+ lines.push(`${c.component}:${c.count}`);
465
+ }
466
+ return lines.join('\n');
467
+ }
468
+ /**
469
+ * Get the React component tree from the running app.
470
+ * This traverses the fiber tree to extract component hierarchy with names.
471
+ */
472
+ export async function getComponentTree(options = {}) {
473
+ const { maxDepth = 50, includeProps = false, includeStyles = false, hideInternals = true, format = 'tonl' } = options;
474
+ const expression = `
475
+ (function() {
476
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
477
+ if (!hook) return { error: 'React DevTools hook not found. Make sure you are running a development build.' };
478
+
479
+ // Try to get fiber roots (renderer ID is usually 1)
480
+ let roots = [];
481
+ if (hook.getFiberRoots) {
482
+ roots = [...(hook.getFiberRoots(1) || [])];
483
+ }
484
+ if (roots.length === 0 && hook.renderers) {
485
+ // Try all renderers
486
+ for (const [id] of hook.renderers) {
487
+ const r = hook.getFiberRoots ? [...(hook.getFiberRoots(id) || [])] : [];
488
+ if (r.length > 0) {
489
+ roots = r;
490
+ break;
491
+ }
492
+ }
493
+ }
494
+ if (roots.length === 0) return { error: 'No fiber roots found. The app may not have rendered yet.' };
495
+
496
+ const maxDepth = ${maxDepth};
497
+ const includeProps = ${includeProps};
498
+ const includeStyles = ${includeStyles};
499
+ const hideInternals = ${hideInternals};
500
+
501
+ // Internal RN components to hide
502
+ const internalPatterns = /^(RCT|RNS|Animated\\(|AnimatedComponent|VirtualizedList|CellRenderer|ScrollViewContext|PerformanceLoggerContext|RootTagContext|HeaderShownContext|HeaderHeightContext|HeaderBackContext|SafeAreaFrameContext|SafeAreaInsetsContext|VirtualizedListContext|VirtualizedListCellContextProvider|StaticContainer|DelayedFreeze|Freeze|Suspender|DebugContainer|MaybeNestedStack|SceneView|NavigationContent|PreventRemoveProvider|EnsureSingleNavigator)/;
503
+
504
+ function getComponentName(fiber) {
505
+ if (!fiber || !fiber.type) return null;
506
+ if (typeof fiber.type === 'string') return fiber.type; // Host component (View, Text, etc.)
507
+ return fiber.type.displayName || fiber.type.name || null;
508
+ }
509
+
510
+ function shouldHide(name) {
511
+ if (!hideInternals || !name) return false;
512
+ return internalPatterns.test(name);
513
+ }
514
+
515
+ function extractLayoutStyles(style) {
516
+ if (!style) return null;
517
+ const merged = Array.isArray(style)
518
+ ? Object.assign({}, ...style.filter(Boolean).map(s => typeof s === 'object' ? s : {}))
519
+ : (typeof style === 'object' ? style : {});
520
+
521
+ const layout = {};
522
+ const layoutKeys = [
523
+ 'padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight',
524
+ 'paddingHorizontal', 'paddingVertical',
525
+ 'margin', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight',
526
+ 'marginHorizontal', 'marginVertical',
527
+ 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
528
+ 'flex', 'flexDirection', 'flexWrap', 'flexGrow', 'flexShrink',
529
+ 'justifyContent', 'alignItems', 'alignSelf', 'alignContent',
530
+ 'position', 'top', 'bottom', 'left', 'right',
531
+ 'gap', 'rowGap', 'columnGap',
532
+ 'borderWidth', 'borderTopWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderRightWidth'
533
+ ];
534
+
535
+ for (const key of layoutKeys) {
536
+ if (merged[key] !== undefined) layout[key] = merged[key];
537
+ }
538
+ return Object.keys(layout).length > 0 ? layout : null;
539
+ }
540
+
541
+ function walkFiber(fiber, depth) {
542
+ if (!fiber || depth > maxDepth) return null;
543
+
544
+ const name = getComponentName(fiber);
545
+
546
+ // Skip anonymous/internal components unless they have meaningful children
547
+ if (!name || shouldHide(name)) {
548
+ // Still traverse children
549
+ let child = fiber.child;
550
+ const children = [];
551
+ while (child) {
552
+ const childResult = walkFiber(child, depth);
553
+ if (childResult) children.push(childResult);
554
+ child = child.sibling;
555
+ }
556
+ // Return first meaningful child or null
557
+ return children.length === 1 ? children[0] : (children.length > 1 ? { component: '(Fragment)', children } : null);
558
+ }
559
+
560
+ const node = { component: name };
561
+
562
+ // Include props if requested (excluding children and style for cleaner output)
563
+ if (includeProps && fiber.memoizedProps) {
564
+ const props = {};
565
+ for (const key of Object.keys(fiber.memoizedProps)) {
566
+ if (key === 'children' || key === 'style') continue;
567
+ const val = fiber.memoizedProps[key];
568
+ if (typeof val === 'function') {
569
+ props[key] = '[Function]';
570
+ } else if (typeof val === 'object' && val !== null) {
571
+ props[key] = Array.isArray(val) ? '[Array]' : '[Object]';
572
+ } else {
573
+ props[key] = val;
574
+ }
575
+ }
576
+ if (Object.keys(props).length > 0) node.props = props;
577
+ }
578
+
579
+ // Include layout styles if requested
580
+ if (includeStyles && fiber.memoizedProps?.style) {
581
+ const layout = extractLayoutStyles(fiber.memoizedProps.style);
582
+ if (layout) node.layout = layout;
583
+ }
584
+
585
+ // Traverse children
586
+ let child = fiber.child;
587
+ const children = [];
588
+ while (child) {
589
+ const childResult = walkFiber(child, depth + 1);
590
+ if (childResult) children.push(childResult);
591
+ child = child.sibling;
592
+ }
593
+ if (children.length > 0) node.children = children;
594
+
595
+ return node;
596
+ }
597
+
598
+ const tree = walkFiber(roots[0].current, 0);
599
+ return { tree };
600
+ })()
601
+ `;
602
+ const result = await executeInApp(expression, false);
603
+ // Apply TONL formatting if requested
604
+ if (format === 'tonl' && result.success && result.result) {
605
+ try {
606
+ const parsed = JSON.parse(result.result);
607
+ if (parsed.tree) {
608
+ const tonl = formatTreeToTonl(parsed.tree);
609
+ return { success: true, result: tonl };
610
+ }
611
+ }
612
+ catch {
613
+ // If parsing fails, return original result
614
+ }
615
+ }
616
+ return result;
617
+ }
618
+ /**
619
+ * Get layout styles for all components on the current screen.
620
+ * Useful for verifying layout without screenshots.
621
+ */
622
+ export async function getScreenLayout(options = {}) {
623
+ const { maxDepth = 60, componentsOnly = false, shortPath = true, summary = false, format = 'tonl' } = options;
624
+ const expression = `
625
+ (function() {
626
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
627
+ if (!hook) return { error: 'React DevTools hook not found.' };
628
+
629
+ let roots = [];
630
+ if (hook.getFiberRoots) {
631
+ roots = [...(hook.getFiberRoots(1) || [])];
632
+ }
633
+ if (roots.length === 0 && hook.renderers) {
634
+ for (const [id] of hook.renderers) {
635
+ const r = hook.getFiberRoots ? [...(hook.getFiberRoots(id) || [])] : [];
636
+ if (r.length > 0) { roots = r; break; }
637
+ }
638
+ }
639
+ if (roots.length === 0) return { error: 'No fiber roots found.' };
640
+
641
+ const maxDepth = ${maxDepth};
642
+ const componentsOnly = ${componentsOnly};
643
+ const shortPath = ${shortPath};
644
+ const summaryMode = ${summary};
645
+ const pathSegments = 3; // Number of path segments to show in shortPath mode
646
+
647
+ function getComponentName(fiber) {
648
+ if (!fiber || !fiber.type) return null;
649
+ if (typeof fiber.type === 'string') return fiber.type;
650
+ return fiber.type.displayName || fiber.type.name || null;
651
+ }
652
+
653
+ function isHostComponent(fiber) {
654
+ return typeof fiber?.type === 'string';
655
+ }
656
+
657
+ function formatPath(pathArray) {
658
+ if (!shortPath || pathArray.length <= pathSegments) {
659
+ return pathArray.join(' > ');
660
+ }
661
+ return '... > ' + pathArray.slice(-pathSegments).join(' > ');
662
+ }
663
+
664
+ function extractAllStyles(style) {
665
+ if (!style) return null;
666
+ const merged = Array.isArray(style)
667
+ ? Object.assign({}, ...style.filter(Boolean).map(s => typeof s === 'object' ? s : {}))
668
+ : (typeof style === 'object' ? style : {});
669
+ return Object.keys(merged).length > 0 ? merged : null;
670
+ }
671
+
672
+ function extractLayoutStyles(style) {
673
+ if (!style) return null;
674
+ const merged = Array.isArray(style)
675
+ ? Object.assign({}, ...style.filter(Boolean).map(s => typeof s === 'object' ? s : {}))
676
+ : (typeof style === 'object' ? style : {});
677
+
678
+ const layout = {};
679
+ const layoutKeys = [
680
+ 'padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight',
681
+ 'paddingHorizontal', 'paddingVertical',
682
+ 'margin', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight',
683
+ 'marginHorizontal', 'marginVertical',
684
+ 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
685
+ 'flex', 'flexDirection', 'flexWrap', 'flexGrow', 'flexShrink',
686
+ 'justifyContent', 'alignItems', 'alignSelf', 'alignContent',
687
+ 'position', 'top', 'bottom', 'left', 'right',
688
+ 'gap', 'rowGap', 'columnGap',
689
+ 'borderWidth', 'borderTopWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
690
+ 'backgroundColor', 'borderColor', 'borderRadius'
691
+ ];
692
+
693
+ for (const key of layoutKeys) {
694
+ if (merged[key] !== undefined) layout[key] = merged[key];
695
+ }
696
+ return Object.keys(layout).length > 0 ? layout : null;
697
+ }
698
+
699
+ const elements = [];
700
+
701
+ function walkFiber(fiber, depth, path) {
702
+ if (!fiber || depth > maxDepth) return;
703
+
704
+ const name = getComponentName(fiber);
705
+ const isHost = isHostComponent(fiber);
706
+
707
+ // Include host components (View, Text, etc.) or named components
708
+ if (name && (!componentsOnly || !isHost)) {
709
+ const style = fiber.memoizedProps?.style;
710
+ const layout = extractLayoutStyles(style);
711
+
712
+ // Get text content if it's a Text component
713
+ let textContent = null;
714
+ if (name === 'Text' || name === 'RCTText') {
715
+ const children = fiber.memoizedProps?.children;
716
+ if (typeof children === 'string') textContent = children;
717
+ else if (typeof children === 'number') textContent = String(children);
718
+ }
719
+
720
+ const element = {
721
+ component: name,
722
+ path: formatPath(path),
723
+ depth
724
+ };
725
+
726
+ if (layout) element.layout = layout;
727
+ if (textContent) element.text = textContent.slice(0, 100);
728
+
729
+ // Include key props for identification
730
+ if (fiber.memoizedProps) {
731
+ const identifiers = {};
732
+ if (fiber.memoizedProps.testID) identifiers.testID = fiber.memoizedProps.testID;
733
+ if (fiber.memoizedProps.accessibilityLabel) identifiers.accessibilityLabel = fiber.memoizedProps.accessibilityLabel;
734
+ if (fiber.memoizedProps.nativeID) identifiers.nativeID = fiber.memoizedProps.nativeID;
735
+ if (fiber.key) identifiers.key = fiber.key;
736
+ if (Object.keys(identifiers).length > 0) element.identifiers = identifiers;
737
+ }
738
+
739
+ elements.push(element);
740
+ }
741
+
742
+ // Traverse children
743
+ let child = fiber.child;
744
+ while (child) {
745
+ const childName = getComponentName(child);
746
+ walkFiber(child, depth + 1, childName ? [...path, childName] : path);
747
+ child = child.sibling;
748
+ }
749
+ }
750
+
751
+ walkFiber(roots[0].current, 0, []);
752
+
753
+ // Summary mode: return counts by component name
754
+ if (summaryMode) {
755
+ const counts = {};
756
+ for (const el of elements) {
757
+ counts[el.component] = (counts[el.component] || 0) + 1;
758
+ }
759
+ // Sort by count descending
760
+ const sorted = Object.entries(counts)
761
+ .sort((a, b) => b[1] - a[1])
762
+ .map(([name, count]) => ({ component: name, count }));
763
+ return {
764
+ totalElements: elements.length,
765
+ uniqueComponents: sorted.length,
766
+ components: sorted
767
+ };
768
+ }
769
+
770
+ return {
771
+ totalElements: elements.length,
772
+ elements: elements
773
+ };
774
+ })()
775
+ `;
776
+ const result = await executeInApp(expression, false);
777
+ // Apply TONL formatting if requested
778
+ if (format === 'tonl' && result.success && result.result) {
779
+ try {
780
+ const parsed = JSON.parse(result.result);
781
+ if (parsed.components) {
782
+ // Summary mode
783
+ const tonl = formatSummaryToTonl(parsed.components, parsed.totalElements);
784
+ return { success: true, result: tonl };
785
+ }
786
+ else if (parsed.elements) {
787
+ // Full element list
788
+ const tonl = formatScreenLayoutToTonl(parsed.elements);
789
+ return { success: true, result: tonl };
790
+ }
791
+ }
792
+ catch {
793
+ // If parsing fails, return original result
794
+ }
795
+ }
796
+ return result;
797
+ }
798
+ /**
799
+ * Inspect a specific component by name, returning its props, state, and layout.
800
+ */
801
+ export async function inspectComponent(componentName, options = {}) {
802
+ const { index = 0, includeState = true, includeChildren = false, shortPath = true, simplifyHooks = true } = options;
803
+ const escapedName = componentName.replace(/'/g, "\\'");
804
+ const expression = `
805
+ (function() {
806
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
807
+ if (!hook) return { error: 'React DevTools hook not found.' };
808
+
809
+ let roots = [];
810
+ if (hook.getFiberRoots) {
811
+ roots = [...(hook.getFiberRoots(1) || [])];
812
+ }
813
+ if (roots.length === 0 && hook.renderers) {
814
+ for (const [id] of hook.renderers) {
815
+ const r = hook.getFiberRoots ? [...(hook.getFiberRoots(id) || [])] : [];
816
+ if (r.length > 0) { roots = r; break; }
817
+ }
818
+ }
819
+ if (roots.length === 0) return { error: 'No fiber roots found.' };
820
+
821
+ const targetName = '${escapedName}';
822
+ const targetIndex = ${index};
823
+ const includeState = ${includeState};
824
+ const includeChildren = ${includeChildren};
825
+ const shortPath = ${shortPath};
826
+ const simplifyHooks = ${simplifyHooks};
827
+ const pathSegments = 3;
828
+
829
+ function getComponentName(fiber) {
830
+ if (!fiber || !fiber.type) return null;
831
+ if (typeof fiber.type === 'string') return fiber.type;
832
+ return fiber.type.displayName || fiber.type.name || null;
833
+ }
834
+
835
+ function formatPath(pathArray) {
836
+ if (!shortPath || pathArray.length <= pathSegments) {
837
+ return pathArray.join(' > ');
838
+ }
839
+ return '... > ' + pathArray.slice(-pathSegments).join(' > ');
840
+ }
841
+
842
+ function extractStyles(style) {
843
+ if (!style) return null;
844
+ const merged = Array.isArray(style)
845
+ ? Object.assign({}, ...style.filter(Boolean).map(s => typeof s === 'object' ? s : {}))
846
+ : (typeof style === 'object' ? style : {});
847
+ return Object.keys(merged).length > 0 ? merged : null;
848
+ }
849
+
850
+ function serializeValue(val, depth = 0) {
851
+ if (depth > 3) return '[Max depth]';
852
+ if (val === null) return null;
853
+ if (val === undefined) return undefined;
854
+ if (typeof val === 'function') return '[Function]';
855
+ if (typeof val !== 'object') return val;
856
+ if (Array.isArray(val)) {
857
+ if (val.length > 10) return '[Array(' + val.length + ')]';
858
+ return val.map(v => serializeValue(v, depth + 1));
859
+ }
860
+ // Object
861
+ const keys = Object.keys(val);
862
+ if (keys.length > 20) return '[Object(' + keys.length + ' keys)]';
863
+ const result = {};
864
+ for (const k of keys) {
865
+ result[k] = serializeValue(val[k], depth + 1);
866
+ }
867
+ return result;
868
+ }
869
+
870
+ function getChildNames(fiber) {
871
+ const names = [];
872
+ let child = fiber?.child;
873
+ while (child && names.length < 20) {
874
+ const name = getComponentName(child);
875
+ if (name) names.push(name);
876
+ child = child.sibling;
877
+ }
878
+ return names;
879
+ }
880
+
881
+ const matches = [];
882
+
883
+ function findComponent(fiber, path) {
884
+ if (!fiber) return;
885
+
886
+ const name = getComponentName(fiber);
887
+ if (name === targetName) {
888
+ matches.push({ fiber, path: [...path, name] });
889
+ }
890
+
891
+ let child = fiber.child;
892
+ while (child) {
893
+ const childName = getComponentName(child);
894
+ findComponent(child, childName ? [...path, childName] : path);
895
+ child = child.sibling;
896
+ }
897
+ }
898
+
899
+ findComponent(roots[0].current, []);
900
+
901
+ if (matches.length === 0) {
902
+ return { error: 'Component "' + targetName + '" not found in the component tree.' };
903
+ }
904
+
905
+ if (targetIndex >= matches.length) {
906
+ return { error: 'Component "' + targetName + '" found ' + matches.length + ' times, but index ' + targetIndex + ' requested.' };
907
+ }
908
+
909
+ const { fiber, path } = matches[targetIndex];
910
+
911
+ const result = {
912
+ component: targetName,
913
+ path: formatPath(path),
914
+ instancesFound: matches.length,
915
+ instanceIndex: targetIndex
916
+ };
917
+
918
+ // Props (excluding children)
919
+ if (fiber.memoizedProps) {
920
+ const props = {};
921
+ for (const key of Object.keys(fiber.memoizedProps)) {
922
+ if (key === 'children') continue;
923
+ props[key] = serializeValue(fiber.memoizedProps[key]);
924
+ }
925
+ result.props = props;
926
+ }
927
+
928
+ // Style separately for clarity
929
+ if (fiber.memoizedProps?.style) {
930
+ result.style = extractStyles(fiber.memoizedProps.style);
931
+ }
932
+
933
+ // State (for hooks, this is a linked list)
934
+ if (includeState && fiber.memoizedState) {
935
+ // Simplified hook value serialization
936
+ function serializeHookValue(val, depth = 0) {
937
+ if (depth > 2) return '[...]';
938
+ if (val === null || val === undefined) return val;
939
+ if (typeof val === 'function') return '[Function]';
940
+ if (typeof val !== 'object') return val;
941
+ // Skip React internal structures (effects, refs with destroy/create)
942
+ if (val.create && val.destroy !== undefined) return '[Effect]';
943
+ if (val.inst && val.deps) return '[Effect]';
944
+ if (val.current !== undefined && Object.keys(val).length === 1) {
945
+ // Ref object - just show current value
946
+ return { current: serializeHookValue(val.current, depth + 1) };
947
+ }
948
+ if (Array.isArray(val)) {
949
+ if (val.length > 5) return '[Array(' + val.length + ')]';
950
+ return val.slice(0, 5).map(v => serializeHookValue(v, depth + 1));
951
+ }
952
+ const keys = Object.keys(val);
953
+ if (keys.length > 10) return '[Object(' + keys.length + ' keys)]';
954
+ const result = {};
955
+ for (const k of keys.slice(0, 10)) {
956
+ result[k] = serializeHookValue(val[k], depth + 1);
957
+ }
958
+ return result;
959
+ }
960
+
961
+ // For function components with hooks
962
+ const states = [];
963
+ let state = fiber.memoizedState;
964
+ let hookIndex = 0;
965
+ while (state && hookIndex < 20) {
966
+ if (state.memoizedState !== undefined) {
967
+ const hookVal = simplifyHooks
968
+ ? serializeHookValue(state.memoizedState)
969
+ : serializeValue(state.memoizedState);
970
+ // Skip effect hooks in simplified mode
971
+ if (!simplifyHooks || (hookVal !== '[Effect]' && hookVal !== undefined)) {
972
+ states.push({
973
+ hookIndex,
974
+ value: hookVal
975
+ });
976
+ }
977
+ }
978
+ state = state.next;
979
+ hookIndex++;
980
+ }
981
+ if (states.length > 0) result.hooks = states;
982
+
983
+ // For class components, memoizedState is the state object directly
984
+ if (states.length === 0 && typeof fiber.memoizedState === 'object') {
985
+ result.state = serializeValue(fiber.memoizedState);
986
+ }
987
+ }
988
+
989
+ // Direct children names
990
+ if (includeChildren) {
991
+ result.children = getChildNames(fiber);
992
+ }
993
+
994
+ return result;
995
+ })()
996
+ `;
997
+ return executeInApp(expression, false);
998
+ }
999
+ /**
1000
+ * Find all components matching a name pattern and return summary info.
1001
+ */
1002
+ export async function findComponents(pattern, options = {}) {
1003
+ const { maxResults = 20, includeLayout = false, shortPath = true, summary = false, format = 'tonl' } = options;
1004
+ const escapedPattern = pattern.replace(/'/g, "\\'").replace(/\\/g, "\\\\");
1005
+ const expression = `
1006
+ (function() {
1007
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
1008
+ if (!hook) return { error: 'React DevTools hook not found.' };
1009
+
1010
+ let roots = [];
1011
+ if (hook.getFiberRoots) {
1012
+ roots = [...(hook.getFiberRoots(1) || [])];
1013
+ }
1014
+ if (roots.length === 0 && hook.renderers) {
1015
+ for (const [id] of hook.renderers) {
1016
+ const r = hook.getFiberRoots ? [...(hook.getFiberRoots(id) || [])] : [];
1017
+ if (r.length > 0) { roots = r; break; }
1018
+ }
1019
+ }
1020
+ if (roots.length === 0) return { error: 'No fiber roots found.' };
1021
+
1022
+ const pattern = '${escapedPattern}';
1023
+ const regex = new RegExp(pattern, 'i');
1024
+ const maxResults = ${maxResults};
1025
+ const includeLayout = ${includeLayout};
1026
+ const shortPath = ${shortPath};
1027
+ const summaryMode = ${summary};
1028
+ const pathSegments = 3;
1029
+
1030
+ function getComponentName(fiber) {
1031
+ if (!fiber || !fiber.type) return null;
1032
+ if (typeof fiber.type === 'string') return fiber.type;
1033
+ return fiber.type.displayName || fiber.type.name || null;
1034
+ }
1035
+
1036
+ function formatPath(pathArray) {
1037
+ if (!shortPath || pathArray.length <= pathSegments) {
1038
+ return pathArray.join(' > ');
1039
+ }
1040
+ return '... > ' + pathArray.slice(-pathSegments).join(' > ');
1041
+ }
1042
+
1043
+ function extractLayoutStyles(style) {
1044
+ if (!style) return null;
1045
+ const merged = Array.isArray(style)
1046
+ ? Object.assign({}, ...style.filter(Boolean).map(s => typeof s === 'object' ? s : {}))
1047
+ : (typeof style === 'object' ? style : {});
1048
+
1049
+ const layout = {};
1050
+ const keys = ['padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight',
1051
+ 'paddingHorizontal', 'paddingVertical', 'margin', 'marginTop', 'marginBottom',
1052
+ 'marginLeft', 'marginRight', 'marginHorizontal', 'marginVertical',
1053
+ 'width', 'height', 'flex', 'flexDirection', 'justifyContent', 'alignItems'];
1054
+ for (const k of keys) {
1055
+ if (merged[k] !== undefined) layout[k] = merged[k];
1056
+ }
1057
+ return Object.keys(layout).length > 0 ? layout : null;
1058
+ }
1059
+
1060
+ const results = [];
1061
+
1062
+ function search(fiber, path, depth) {
1063
+ if (!fiber || results.length >= maxResults) return;
1064
+
1065
+ const name = getComponentName(fiber);
1066
+ if (name && regex.test(name)) {
1067
+ const entry = {
1068
+ component: name,
1069
+ path: formatPath(path),
1070
+ depth
1071
+ };
1072
+
1073
+ if (fiber.memoizedProps?.testID) entry.testID = fiber.memoizedProps.testID;
1074
+ if (fiber.key) entry.key = fiber.key;
1075
+
1076
+ if (includeLayout && fiber.memoizedProps?.style) {
1077
+ const layout = extractLayoutStyles(fiber.memoizedProps.style);
1078
+ if (layout) entry.layout = layout;
1079
+ }
1080
+
1081
+ results.push(entry);
1082
+ }
1083
+
1084
+ let child = fiber.child;
1085
+ while (child && results.length < maxResults) {
1086
+ const childName = getComponentName(child);
1087
+ search(child, childName ? [...path, childName] : path, depth + 1);
1088
+ child = child.sibling;
1089
+ }
1090
+ }
1091
+
1092
+ search(roots[0].current, [], 0);
1093
+
1094
+ // Summary mode: just return counts by component name
1095
+ if (summaryMode) {
1096
+ const counts = {};
1097
+ for (const r of results) {
1098
+ counts[r.component] = (counts[r.component] || 0) + 1;
1099
+ }
1100
+ const sorted = Object.entries(counts)
1101
+ .sort((a, b) => b[1] - a[1])
1102
+ .map(([name, count]) => ({ component: name, count }));
1103
+ return {
1104
+ pattern,
1105
+ totalMatches: results.length,
1106
+ uniqueComponents: sorted.length,
1107
+ components: sorted
1108
+ };
1109
+ }
1110
+
1111
+ return {
1112
+ pattern,
1113
+ found: results.length,
1114
+ components: results
1115
+ };
1116
+ })()
1117
+ `;
1118
+ const result = await executeInApp(expression, false);
1119
+ // Apply TONL formatting if requested
1120
+ if (format === 'tonl' && result.success && result.result) {
1121
+ try {
1122
+ const parsed = JSON.parse(result.result);
1123
+ if (parsed.components) {
1124
+ if (parsed.totalMatches !== undefined) {
1125
+ // Summary mode
1126
+ const tonl = formatSummaryToTonl(parsed.components, parsed.totalMatches);
1127
+ return { success: true, result: `pattern: ${parsed.pattern}\n${tonl}` };
1128
+ }
1129
+ else {
1130
+ // Full list mode
1131
+ const tonl = formatFoundComponentsToTonl(parsed.components);
1132
+ return { success: true, result: `pattern: ${parsed.pattern}\nfound: ${parsed.found}\n${tonl}` };
1133
+ }
1134
+ }
1135
+ }
1136
+ catch {
1137
+ // If parsing fails, return original result
1138
+ }
1139
+ }
1140
+ return result;
1141
+ }
418
1142
  //# sourceMappingURL=executor.js.map