git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
Remove redundant _ prefix from Impl names
commitddce2a70dcca935b73f2871d2a657a0f93cb7525 authorMark <mark.eibes@gmail.com> dateSun 15 Feb 2026, 23:55 (8 months ago) parent3203be9
6 files changed+129
example-macos/macos/YogaReactExample-macOS/Info.plist+2 example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+88 example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+2 example-macos/src/Main.purs+18 src/Yoga/React/Native/MacOS/CameraView.js+2 src/Yoga/React/Native/MacOS/CameraView.purs+17
example-macos/macos/YogaReactExample-macOS/Info.plist+2
@@ -49,5 +49,7 @@
4949 <string>This app uses the microphone for speech recognition.</string>
5050 <key>NSSpeechRecognitionUsageDescription</key>
5151 <string>This app uses speech recognition to convert speech to text.</string>
52+ <key>NSCameraUsageDescription</key>
53+ <string>This app uses the camera for live video preview.</string>
5254</dict>
5355</plist>
example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+88
@@ -4201,3 +4201,91 @@ RCT_EXPORT_METHOD(tokenize:(NSString *)text
42014201}
42024202
42034203@end
4204+
4205+// ── Section 54: Camera View ─────────────────────────────────────────────────
4206+// Live camera preview using AVCaptureVideoPreviewLayer
4207+
4208+@interface RCTCameraView : NSView
4209+@property (nonatomic, assign) BOOL active;
4210+@property (nonatomic, strong) AVCaptureSession *session;
4211+@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
4212+@end
4213+
4214+@implementation RCTCameraView
4215+
4216+- (instancetype)initWithFrame:(NSRect)frame {
4217+ if (self = [super initWithFrame:frame]) {
4218+ self.wantsLayer = YES;
4219+ _active = NO;
4220+ }
4221+ return self;
4222+}
4223+
4224+- (NSSize)intrinsicContentSize {
4225+ return NSMakeSize(NSViewNoIntrinsicMetric, NSViewNoIntrinsicMetric);
4226+}
4227+
4228+- (void)setActive:(BOOL)active {
4229+ _active = active;
4230+ if (active) {
4231+ [self startCapture];
4232+ } else {
4233+ [self stopCapture];
4234+ }
4235+}
4236+
4237+- (void)startCapture {
4238+ if (_session) return;
4239+
4240+ AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
4241+ if (!device) {
4242+ NSLog(@"[CameraView] No video device found");
4243+ return;
4244+ }
4245+
4246+ NSError *error = nil;
4247+ AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
4248+ if (error) {
4249+ NSLog(@"[CameraView] Input error: %@", error);
4250+ return;
4251+ }
4252+
4253+ _session = [[AVCaptureSession alloc] init];
4254+ _session.sessionPreset = AVCaptureSessionPresetMedium;
4255+ [_session addInput:input];
4256+
4257+ _previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
4258+ _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
4259+ _previewLayer.frame = self.bounds;
4260+ [self.layer addSublayer:_previewLayer];
4261+
4262+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4263+ [self->_session startRunning];
4264+ });
4265+}
4266+
4267+- (void)stopCapture {
4268+ if (!_session) return;
4269+ [_session stopRunning];
4270+ [_previewLayer removeFromSuperlayer];
4271+ _previewLayer = nil;
4272+ _session = nil;
4273+}
4274+
4275+- (void)layout {
4276+ [super layout];
4277+ _previewLayer.frame = self.bounds;
4278+}
4279+
4280+- (void)dealloc {
4281+ [self stopCapture];
4282+}
4283+
4284+@end
4285+
4286+@interface RCTCameraViewManager : RCTViewManager @end
4287+@implementation RCTCameraViewManager
4288+RCT_EXPORT_MODULE(MacOSCameraView)
4289+- (NSView *)view { return [[RCTCameraView alloc] initWithFrame:CGRectZero]; }
4290+RCT_EXPORT_VIEW_PROPERTY(active, BOOL)
4291+@end
example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+2
@@ -10,5 +10,7 @@
1010 <true/>
1111 <key>com.apple.security.device.audio-input</key>
1212 <true/>
13+ <key>com.apple.security.device.camera</key>
14+ <true/>
1315</dict>
1416</plist>
example-macos/src/Main.purs+18
@@ -74,6 +74,7 @@ import Yoga.React.Native.MacOS.FontPanel (macosShowFontPanel)
7474import Yoga.React.Native.MacOS.OCR (recognizeText)
7575import Yoga.React.Native.MacOS.SpeechRecognition (useSpeechRecognition)
7676import Yoga.React.Native.MacOS.NaturalLanguage (detectLanguage, analyzeSentiment, tokenize)
77+import Yoga.React.Native.MacOS.CameraView (nativeCameraView)
7778import Yoga.React.Native.MacOS.Events as E
7879import Yoga.React.Native.MacOS.Types as T
7980import Yoga.React.Native.Matrix as Matrix
@@ -1049,6 +1050,7 @@ aiTab = component "AITab" \p -> React.do
10491050 speech <- useSpeechRecognition
10501051 nlText /\ setNlText <- useState' "I love this amazing app! C'est magnifique."
10511052 nlResult /\ setNlResult <- useState' ""
1053+ cameraOn /\ setCameraOn <- useState' false
10521054 pure do
10531055 nativeScrollView { style: tw "flex-1" <> Style.style { backgroundColor: "transparent" } }
10541056 ( view { style: tw "px-4 pb-4" }
@@ -1223,6 +1225,22 @@ aiTab = component "AITab" \p -> React.do
12231225 , if nlResult /= "" then view { style: tw "p-2 rounded mb-4" <> Style.style { backgroundColor: p.cardBg } }
12241226 [ text { style: tw "text-xs font-mono" <> Style.style { color: p.fg } } nlResult ]
12251227 else view {} []
1228+
1229+ , sectionTitle p.fg "Camera"
1230+ , text { style: tw "text-xs mb-2" <> Style.style { color: p.dimFg } }
1231+ "Live camera preview (AVCaptureVideoPreviewLayer)"
1232+ , nativeButton
1233+ { title: if cameraOn then "Stop Camera" else "Start Camera"
1234+ , sfSymbol: if cameraOn then "video.slash" else "video"
1235+ , bezelStyle: T.push
1236+ , onPress: handler_ (setCameraOn (not cameraOn))
1237+ , style: Style.style { height: 24.0, width: 140.0 } <> tw "mb-2"
1238+ }
1239+ , if cameraOn then nativeCameraView
1240+ { active: true
1241+ , style: Style.style { height: 240.0 } <> tw "rounded-lg overflow-hidden mb-2"
1242+ }
1243+ else view {} []
12261244 ]
12271245 )
12281246
src/Yoga/React/Native/MacOS/CameraView.jsnew file+2
@@ -0,0 +1,2 @@
1+import { requireNativeComponent } from "react-native";
2+export const cameraViewImpl = requireNativeComponent("MacOSCameraView");
src/Yoga/React/Native/MacOS/CameraView.pursnew file+17
@@ -0,0 +1,17 @@
1+module Yoga.React.Native.MacOS.CameraView
2+ ( nativeCameraView
3+ , NativeCameraViewAttributes
4+ ) where
5+
6+import React.Basic (ReactComponent)
7+import Yoga.React.Native.Attributes (BaseAttributes)
8+import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
9+
10+foreign import cameraViewImpl :: forall props. ReactComponent props
11+
12+nativeCameraView :: FFINativeComponent_ NativeCameraViewAttributes
13+nativeCameraView = createNativeElement_ cameraViewImpl
14+
15+type NativeCameraViewAttributes = BaseAttributes
16+ ( active :: Boolean
17+ )