|
721 | 721 | stackTraceTextView.translatesAutoresizingMaskIntoConstraints = NO;
|
722 | 722 | [stackTraceContainer addSubview:stackTraceTextView];
|
723 | 723 |
|
| 724 | + // Copy button for stack trace |
| 725 | + UIButton* copyButton = [UIButton buttonWithType:UIButtonTypeSystem]; |
| 726 | + [copyButton setTitle:@"📋 Copy Stack Trace" forState:UIControlStateNormal]; |
| 727 | + [copyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; |
| 728 | + copyButton.backgroundColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0]; |
| 729 | + copyButton.titleLabel.font = [UIFont systemFontOfSize:16]; |
| 730 | + copyButton.layer.cornerRadius = 8; |
| 731 | + copyButton.translatesAutoresizingMaskIntoConstraints = NO; |
| 732 | + [contentView addSubview:copyButton]; |
| 733 | + |
| 734 | + // Configure copy button action |
| 735 | + void (^copyAction)(void) = ^{ |
| 736 | + UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; |
| 737 | + pasteboard.string = [NSString stringWithUTF8String:stackTrace.c_str()]; |
| 738 | + |
| 739 | + // Show temporary feedback |
| 740 | + [copyButton setTitle:@"✅ Copied!" forState:UIControlStateNormal]; |
| 741 | + copyButton.backgroundColor = [UIColor colorWithRed:0.2 green:0.7 blue:0.2 alpha:1.0]; |
| 742 | + |
| 743 | + dispatch_after( |
| 744 | + dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ |
| 745 | + [copyButton setTitle:@"📋 Copy Stack Trace" forState:UIControlStateNormal]; |
| 746 | + copyButton.backgroundColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0]; |
| 747 | + }); |
| 748 | + }; |
| 749 | + |
| 750 | + if (@available(iOS 14.0, *)) { |
| 751 | + UIAction* action = [UIAction actionWithTitle:@"" |
| 752 | + image:nil |
| 753 | + identifier:nil |
| 754 | + handler:^(UIAction* action) { |
| 755 | + copyAction(); |
| 756 | + }]; |
| 757 | + [copyButton addAction:action forControlEvents:UIControlEventTouchUpInside]; |
| 758 | + } else { |
| 759 | + // For older iOS versions, use target-action pattern |
| 760 | + NSObject* target = [[NSObject alloc] init]; |
| 761 | + objc_setAssociatedObject(target, "copyBlock", copyAction, OBJC_ASSOCIATION_COPY_NONATOMIC); |
| 762 | + |
| 763 | + IMP copyImp = imp_implementationWithBlock(^(id self) { |
| 764 | + void (^block)(void) = objc_getAssociatedObject(self, "copyBlock"); |
| 765 | + if (block) { |
| 766 | + block(); |
| 767 | + } |
| 768 | + }); |
| 769 | + |
| 770 | + class_addMethod([target class], NSSelectorFromString(@"copyStackTrace"), copyImp, "v@:"); |
| 771 | + [copyButton addTarget:target |
| 772 | + action:NSSelectorFromString(@"copyStackTrace") |
| 773 | + forControlEvents:UIControlEventTouchUpInside]; |
| 774 | + } |
| 775 | + |
724 | 776 | // Hot-reload indicator
|
725 | 777 | UILabel* hotReloadLabel = [[UILabel alloc] init];
|
726 | 778 | hotReloadLabel.text = @"Fix the error and save your changes to continue.";
|
|
818 | 870 | [errorTitleLabel.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor
|
819 | 871 | constant:-20],
|
820 | 872 |
|
821 |
| - // Stack trace container (black terminal-like background) |
| 873 | + // Stack trace container (black terminal-like background) - flexible height |
822 | 874 | [stackTraceContainer.topAnchor constraintEqualToAnchor:errorTitleLabel.bottomAnchor
|
823 | 875 | constant:15],
|
824 | 876 | [stackTraceContainer.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor
|
825 | 877 | constant:20],
|
826 | 878 | [stackTraceContainer.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor
|
827 | 879 | constant:-20],
|
828 |
| - [stackTraceContainer.heightAnchor constraintEqualToConstant:320], |
829 | 880 |
|
830 | 881 | // Stack trace text view (terminal green text on black)
|
831 | 882 | [stackTraceTextView.topAnchor constraintEqualToAnchor:stackTraceContainer.topAnchor],
|
832 | 883 | [stackTraceTextView.leadingAnchor constraintEqualToAnchor:stackTraceContainer.leadingAnchor],
|
833 | 884 | [stackTraceTextView.trailingAnchor constraintEqualToAnchor:stackTraceContainer.trailingAnchor],
|
834 | 885 | [stackTraceTextView.bottomAnchor constraintEqualToAnchor:stackTraceContainer.bottomAnchor],
|
835 | 886 |
|
836 |
| - // Hot-reload indicator below stack trace |
837 |
| - [hotReloadLabel.topAnchor constraintEqualToAnchor:stackTraceContainer.bottomAnchor constant:15], |
| 887 | + // Copy button below stack trace |
| 888 | + [copyButton.topAnchor constraintEqualToAnchor:stackTraceContainer.bottomAnchor constant:10], |
| 889 | + [copyButton.centerXAnchor constraintEqualToAnchor:contentView.centerXAnchor], |
| 890 | + [copyButton.widthAnchor constraintEqualToConstant:200], |
| 891 | + [copyButton.heightAnchor constraintEqualToConstant:40], |
| 892 | + |
| 893 | + // Hot-reload indicator below copy button - this will push stack trace up to fill space |
| 894 | + [hotReloadLabel.topAnchor constraintEqualToAnchor:copyButton.bottomAnchor constant:15], |
838 | 895 | [hotReloadLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
839 | 896 | [hotReloadLabel.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
| 897 | + [hotReloadLabel.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor constant:-30], |
840 | 898 |
|
841 | 899 | // Continue button at bottom
|
842 | 900 | // [continueButton.topAnchor constraintEqualToAnchor:hotReloadLabel.bottomAnchor constant:25],
|
|
956 | 1014 | nuclearWindow = [[UIWindow alloc] initWithWindowScene:windowScene];
|
957 | 1015 | NSLog(@"🎨 Created nuclear window with scene");
|
958 | 1016 | } else {
|
959 |
| - NSLog(@"🎨 ☢️ ABSOLUTE NUCLEAR: No scenes exist - attempting to force iOS to create " |
| 1017 | + NSLog(@"🎨 ☢️ ABSOLUTE NUCLEAR: No scenes exist - attempting to force iOS to " |
| 1018 | + @"create " |
960 | 1019 | @"one");
|
961 | 1020 |
|
962 | 1021 | // Try to force iOS to create a window scene by requesting one
|
|
1022 | 1081 | alpha:1.0];
|
1023 | 1082 |
|
1024 | 1083 | UILabel* sceneLabel = [[UILabel alloc] initWithFrame:sceneNuclearWindow.bounds];
|
1025 |
| - sceneLabel.text = [NSString |
1026 |
| - stringWithFormat:@"⚠️ ABSOLUTE NUCLEAR SUCCESS ⚠️\n\nJavaScript Error " |
1027 |
| - @"Detected\n\n%@\n\n🔥 HOT-RELOAD READY 🔥\n\nApp will stay " |
1028 |
| - @"alive for development", |
1029 |
| - [NSString stringWithUTF8String:message.c_str()]]; |
| 1084 | + sceneLabel.text = |
| 1085 | + [NSString stringWithFormat: |
| 1086 | + @"⚠️ ABSOLUTE NUCLEAR SUCCESS ⚠️\n\nJavaScript Error " |
| 1087 | + @"Detected\n\n%@\n\n🔥 HOT-RELOAD READY 🔥\n\nApp will stay " |
| 1088 | + @"alive for development", |
| 1089 | + [NSString stringWithUTF8String:message.c_str()]]; |
1030 | 1090 | sceneLabel.textColor = [UIColor whiteColor];
|
1031 | 1091 | sceneLabel.font = [UIFont boldSystemFontOfSize:16];
|
1032 | 1092 | sceneLabel.textAlignment = NSTextAlignmentCenter;
|
|
1066 | 1126 | UILabel* nuclearLabel = [[UILabel alloc] initWithFrame:nuclearWindow.bounds];
|
1067 | 1127 | nuclearLabel.text = [NSString
|
1068 | 1128 | stringWithFormat:
|
1069 |
| - @"⚠️ JAVASCRIPT ERROR ⚠️\n\n%@\n\n🔥 HOT-RELOAD READY 🔥\n\nFix the error and " |
| 1129 | + @"⚠️ JAVASCRIPT ERROR ⚠️\n\n%@\n\n🔥 HOT-RELOAD READY 🔥\n\nFix the error " |
| 1130 | + @"and " |
1070 | 1131 | @"save your file\nApp will stay alive for development\n\nTap anywhere to dismiss",
|
1071 | 1132 | [NSString stringWithUTF8String:message.c_str()]];
|
1072 | 1133 | nuclearLabel.textColor = [UIColor whiteColor];
|
|
0 commit comments