git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
Move batch 4+5 demos to new AI tab
commit60d9324f79ba96c7c8170e426225d6d34620270f authorMark <mark.eibes@gmail.com> dateSun 15 Feb 2026, 16:47 (8 months ago) parentf7dc587
3 files changed+30−11
example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+19 example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+2 example-macos/src/Main.purs+9−11
example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+19
@@ -4053,7 +4053,9 @@ RCT_EXPORT_MODULE()
40534053RCT_EXPORT_METHOD(start:(RCTPromiseResolveBlock)resolve
40544054 rejecter:(RCTPromiseRejectBlock)reject)
40554055{
4056+ NSLog(@"[SpeechRecognition] Requesting authorization...");
40564057 [SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) {
4058+ NSLog(@"[SpeechRecognition] Authorization status: %ld", (long)status);
40574059 if (status != SFSpeechRecognizerAuthorizationStatusAuthorized) {
40584060 reject(@"DENIED", @"Speech recognition permission denied", nil);
40594061 return;
@@ -4068,12 +4070,20 @@ RCT_EXPORT_METHOD(start:(RCTPromiseResolveBlock)resolve
40684070
40694071 self.latestTranscript = @"";
40704072 self.recognizer = [[SFSpeechRecognizer alloc] initWithLocale:[NSLocale currentLocale]];
4073+ NSLog(@"[SpeechRecognition] Recognizer available: %d, locale: %@", self.recognizer.isAvailable, self.recognizer.locale.localeIdentifier);
4074+
4075+ if (!self.recognizer.isAvailable) {
4076+ reject(@"UNAVAILABLE", @"Speech recognizer is not available for this locale", nil);
4077+ return;
4078+ }
4079+
40714080 self.audioEngine = [[AVAudioEngine alloc] init];
40724081 self.recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
40734082 self.recognitionRequest.shouldReportPartialResults = YES;
40744083
40754084 AVAudioInputNode *inputNode = self.audioEngine.inputNode;
40764085 AVAudioFormat *format = [inputNode outputFormatForBus:0];
4086+ NSLog(@"[SpeechRecognition] Audio format: %@", format);
40774087
40784088 [inputNode installTapOnBus:0 bufferSize:1024 format:format block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
40794089 [self.recognitionRequest appendAudioPCMBuffer:buffer];
@@ -4081,21 +4091,30 @@ RCT_EXPORT_METHOD(start:(RCTPromiseResolveBlock)resolve
40814091
40824092 self.recognitionTask = [self.recognizer recognitionTaskWithRequest:self.recognitionRequest
40834093 resultHandler:^(SFSpeechRecognitionResult *result, NSError *error) {
4094+ if (error) {
4095+ NSLog(@"[SpeechRecognition] Error: %@", error.localizedDescription);
4096+ }
40844097 if (result) {
40854098 self.latestTranscript = result.bestTranscription.formattedString;
4099+ NSLog(@"[SpeechRecognition] Transcript: %@", self.latestTranscript);
40864100 }
40874101 if (error || result.isFinal) {
40884102 [self.audioEngine stop];
40894103 [inputNode removeTapOnBus:0];
4104+ self.recognitionRequest = nil;
4105+ self.recognitionTask = nil;
40904106 }
40914107 }];
40924108
4109+ [self.audioEngine prepare];
40934110 NSError *err = nil;
40944111 [self.audioEngine startAndReturnError:&err];
40954112 if (err) {
4113+ NSLog(@"[SpeechRecognition] Engine start error: %@", err.localizedDescription);
40964114 reject(@"ENGINE_FAIL", err.localizedDescription, err);
40974115 return;
40984116 }
4117+ NSLog(@"[SpeechRecognition] Engine started successfully");
40994118 resolve(@"started");
41004119 });
41014120 }];
example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+2
@@ -8,5 +8,7 @@
88 <true/>
99 <key>com.apple.security.network.client</key>
1010 <true/>
11+ <key>com.apple.security.device.audio-input</key>
12+ <true/>
1113</dict>
1214</plist>
example-macos/src/Main.purs+9−11
@@ -1079,6 +1079,12 @@ aiTab = component "AITab" \p -> React.do
10791079 transcript /\ setTranscript <- useState' ""
10801080 nlText /\ setNlText <- useState' "I love this amazing app! C'est magnifique."
10811081 nlResult /\ setNlResult <- useState' ""
1082+ useEffect listening do
1083+ if listening then do
1084+ timerId <- setInterval 500 do
1085+ macosGetTranscript \t -> setTranscript t
1086+ pure (clearInterval timerId)
1087+ else pure (pure unit)
10821088 pure do
10831089 nativeScrollView { style: tw "flex-1" <> Style.style { backgroundColor: "transparent" } }
10841090 ( view { style: tw "px-4 pb-4" }
@@ -1194,10 +1200,10 @@ aiTab = component "AITab" \p -> React.do
11941200 else view {} []
11951201 , sectionTitle p.fg "Speech Recognition"
11961202 , text { style: tw "text-xs mb-2" <> Style.style { color: p.dimFg } }
1197− "Live microphone transcription (SFSpeechRecognizer)"
1203+ "Live microphone-to-text (click Start, speak, click Stop)"
11981204 , view { style: tw "flex-row items-center mb-2" }
11991205 [ nativeButton
1200− { title: if listening then "Listening..." else "Start Listening"
1206+ { title: if listening then "Listening..." else "Start"
12011207 , sfSymbol: if listening then "mic.fill" else "mic"
12021208 , bezelStyle: T.push
12031209 , onPress: handler_ do
@@ -1206,7 +1212,7 @@ aiTab = component "AITab" \p -> React.do
12061212 setListening true
12071213 setTranscript ""
12081214 macosStartListening
1209− , style: Style.style { height: 24.0, width: 160.0 }
1215+ , style: Style.style { height: 24.0, width: 130.0 }
12101216 }
12111217 , nativeButton
12121218 { title: "Stop"
@@ -1218,14 +1224,6 @@ aiTab = component "AITab" \p -> React.do
12181224 setListening false
12191225 , style: Style.style { height: 24.0, width: 80.0, marginLeft: 8.0 }
12201226 }
1221− , nativeButton
1222− { title: "Poll"
1223− , sfSymbol: "arrow.clockwise"
1224− , bezelStyle: T.push
1225− , onPress: handler_ do
1226− macosGetTranscript \t -> setTranscript t
1227− , style: Style.style { height: 24.0, width: 80.0, marginLeft: 8.0 }
1228− }
12291227 ]
12301228 , if transcript /= "" then view { style: tw "p-2 rounded mb-4" <> Style.style { backgroundColor: p.cardBg } }
12311229 [ text { style: tw "text-xs font-mono" <> Style.style { color: p.fg } } transcript ]