react-native-nswindow 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # react-native-nswindow
2
+
3
+ Multi-window support for React Native macOS. Create, modify, close, and observe native `NSWindow` instances from JavaScript.
4
+
5
+ ## Requirements
6
+
7
+ - React Native macOS >= 0.81
8
+ - macOS deployment target >= 14.0
9
+ - New Architecture (TurboModules) enabled
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install react-native-nswindow
15
+ cd macos && pod install
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import NSWindowModule from 'react-native-nswindow';
22
+ import { AppRegistry } from 'react-native';
23
+
24
+ // Register a component to render in the new window
25
+ function MyWindow() {
26
+ return <Text>Hello from a new window!</Text>;
27
+ }
28
+ AppRegistry.registerComponent('MyWindow', () => MyWindow);
29
+
30
+ // Create a window
31
+ const windowId = await NSWindowModule.addWindow({
32
+ componentName: 'MyWindow',
33
+ windowName: 'my-window',
34
+ initialProps: {},
35
+ title: 'My Window',
36
+ width: 600,
37
+ height: 400,
38
+ });
39
+
40
+ // Modify it
41
+ await NSWindowModule.modifyWindow(windowId, {
42
+ title: 'Updated Title',
43
+ backgroundColor: '#1e1e1e',
44
+ vibrancy: 'sidebar',
45
+ });
46
+
47
+ // Listen for events
48
+ const sub = NSWindowModule.onWindowClose((id) => {
49
+ console.log('Window closed:', id);
50
+ });
51
+ sub.remove(); // cleanup
52
+ ```
53
+
54
+ ## API
55
+
56
+ ### Methods
57
+
58
+ | Method | Description |
59
+ |--------|-------------|
60
+ | `addWindow(props: WindowProps)` | Create a new window. Returns its ID. |
61
+ | `closeWindow(windowId)` | Close a window. |
62
+ | `modifyWindow(windowId, props)` | Modify window properties. |
63
+ | `listWindows()` | List all tracked window IDs. |
64
+ | `getWindowState(windowId)` | Get position, size, and state flags. |
65
+ | `focusWindow(windowId)` | Make window key and bring to front. |
66
+ | `hideWindow(windowId)` | Hide (order out) a window. |
67
+ | `showWindow(windowId)` | Show (order front) a window. |
68
+ | `minimizeWindow(windowId)` | Minimize to dock. |
69
+ | `deminimizeWindow(windowId)` | Restore from dock. |
70
+ | `setFullScreen(windowId, bool)` | Enter/exit full screen. |
71
+ | `bringToFront(windowId)` | Order front. |
72
+ | `sendToBack(windowId)` | Order back. |
73
+ | `getScreenInfo()` | Get screen layout info (all screens, total bounds, main screen). |
74
+
75
+ ### Window Props
76
+
77
+ All properties are optional except `componentName`, `windowName`, and `initialProps` (on `addWindow`).
78
+
79
+ | Prop | Type | Default | Description |
80
+ |------|------|---------|-------------|
81
+ | `x`, `y` | number | 100 | Window origin |
82
+ | `width`, `height` | number | 400×300 | Window size |
83
+ | `minWidth`, `minHeight` | number | — | Minimum size constraints |
84
+ | `maxWidth`, `maxHeight` | number | — | Maximum size constraints |
85
+ | `center` | boolean | false | Center on screen |
86
+ | `title` | string | windowName | Title bar text |
87
+ | `titleBarStyle` | string | 'default' | 'default' \| 'hidden' \| 'hiddenInset' \| 'transparent' |
88
+ | `vibrancy` | string | 'none' | 'none' \| 'sidebar' \| 'menu' \| 'popover' \| 'fullScreenUI' \| 'underWindowBackground' \| 'hudWindow' |
89
+ | `backgroundColor` | string | — | Any React Native color string (e.g. '#ff0000', 'rgba(0,0,0,0.5)', 'red') |
90
+ | `transparent` | boolean | false | Set window non-opaque (required for translucent backgrounds) |
91
+ | `hasShadow` | boolean | true | Window shadow |
92
+ | `resizable` | boolean | true | Allow resize |
93
+ | `movable` | boolean | true | Allow drag |
94
+ | `minimizable` | boolean | true | Show minimize button |
95
+ | `closable` | boolean | true | Show close button |
96
+ | `zoomable` | boolean | true | Enable zoom button |
97
+ | `alwaysOnTop` | boolean | false | Float above other windows |
98
+ | `level` | string | 'normal' | 'normal' \| 'floating' \| 'modalPanel' \| 'mainMenu' \| 'statusBar' \| 'screenSaver' |
99
+ | `show` | boolean | true | Show immediately |
100
+ | `focusOnCreate` | boolean | true | Make key on show |
101
+ | `autoSaveFrame` | string | — | Persist frame position across launches |
102
+ | `stopShouldClose` | boolean | false | Intercept close (window stays open) |
103
+
104
+ ### Events
105
+
106
+ | Event | Payload | Description |
107
+ |-------|---------|-------------|
108
+ | `onWindowClose` | windowId | Window was closed |
109
+ | `onWindowWillClose` | windowId | Close was attempted (fires even if blocked) |
110
+ | `onWindowFocus` | windowId | Became key window |
111
+ | `onWindowBlur` | windowId | Resigned key window |
112
+ | `onWindowMove` | `{ windowId, x, y }` | Window moved |
113
+ | `onWindowResize` | `{ windowId, width, height }` | Window resized |
114
+ | `onWindowMinimize` | windowId | Minimized to dock |
115
+ | `onWindowDeminimize` | windowId | Restored from dock |
116
+ | `onWindowEnterFullScreen` | windowId | Entered full screen |
117
+ | `onWindowExitFullScreen` | windowId | Exited full screen |
118
+ | `onWindowOcclusionStateChange` | `{ windowId, isVisible }` | Window occlusion state changed (visible/occluded) |
119
+ | `onWindowBackingPropertiesChange` | windowId | Backing properties changed (e.g. moved between Retina/non-Retina displays) |
120
+ | `onScreenInfoChange` | — | Screen parameters changed (display added/removed/resized) |
121
+
122
+ Events use the New Architecture `EventEmitter` pattern. Each event method takes a callback and returns a subscription with a `.remove()` method:
123
+
124
+ ```tsx
125
+ import { useEffect } from 'react';
126
+ import NSWindowModule from 'react-native-nswindow';
127
+
128
+ useEffect(() => {
129
+ const subs = [
130
+ NSWindowModule.onWindowClose((windowId) => {
131
+ console.log('closed:', windowId);
132
+ }),
133
+ NSWindowModule.onWindowMove(({ windowId, x, y }) => {
134
+ console.log('moved:', windowId, x, y);
135
+ }),
136
+ NSWindowModule.onWindowResize(({ windowId, width, height }) => {
137
+ console.log('resized:', windowId, width, height);
138
+ }),
139
+ ];
140
+ return () => subs.forEach((s) => s.remove());
141
+ }, []);
142
+ ```
143
+
144
+ ## How It Works
145
+
146
+ This is a C++ TurboModule (CxxTurboModule). An ObjC singleton (`RNNSWindowHelper`) manages the window dictionary, acts as `NSWindowDelegate`, and observes `NSNotificationCenter` for all window events. Window operations dispatch to the main thread; promises resolve via the JS invoker.
147
+
148
+ ## License
149
+
150
+ MIT
@@ -0,0 +1,142 @@
1
+ #pragma once
2
+
3
+ #include "RNNSWindowSpecJSI.h"
4
+ #include <ReactCommon/TurboModuleUtils.h>
5
+
6
+ namespace facebook::react {
7
+
8
+ // Concrete types from codegen structs
9
+ using WindowProps = NativeNSWindowWindowProps<
10
+ std::optional<double>, std::optional<double>, std::optional<double>,
11
+ std::optional<double>, std::optional<double>, std::optional<double>,
12
+ std::optional<double>, std::optional<double>, std::optional<bool>,
13
+ std::optional<std::string>, std::optional<std::string>,
14
+ std::optional<std::string>, std::optional<std::string>, std::optional<bool>,
15
+ std::optional<bool>, std::optional<bool>, std::optional<bool>,
16
+ std::optional<bool>, std::optional<bool>, std::optional<bool>,
17
+ std::optional<bool>, std::optional<std::string>, std::optional<bool>,
18
+ std::optional<bool>, std::optional<std::string>, std::optional<bool>,
19
+ std::string, std::string, jsi::Object>;
20
+
21
+ using ModifyProps = NativeNSWindowModifyableWindowProps<
22
+ std::optional<double>, std::optional<double>, std::optional<double>,
23
+ std::optional<double>, std::optional<double>, std::optional<double>,
24
+ std::optional<double>, std::optional<double>, std::optional<bool>,
25
+ std::optional<std::string>, std::optional<std::string>,
26
+ std::optional<std::string>, std::optional<std::string>, std::optional<bool>,
27
+ std::optional<bool>, std::optional<bool>, std::optional<bool>,
28
+ std::optional<bool>, std::optional<bool>, std::optional<bool>,
29
+ std::optional<bool>, std::optional<std::string>, std::optional<bool>,
30
+ std::optional<bool>, std::optional<std::string>, std::optional<bool>>;
31
+
32
+ // Concrete types for events
33
+ using WindowMovePayload =
34
+ NativeNSWindowWindowMovePayload<std::string, double, double>;
35
+ using WindowResizePayload =
36
+ NativeNSWindowWindowResizePayload<std::string, double, double>;
37
+ using WindowOcclusionStatePayload =
38
+ NativeNSWindowWindowOcclusionStatePayload<std::string, bool>;
39
+ using WindowRect = NativeNSWindowRect<double, double, double, double>;
40
+ using WindowStateResult =
41
+ NativeNSWindowWindowState<std::string, std::string, double, double, double,
42
+ double, bool, bool, bool, bool, bool, double,
43
+ std::optional<WindowRect>>;
44
+
45
+ } // namespace facebook::react
46
+
47
+ // bridging support for event payload structs
48
+ namespace facebook::react {
49
+
50
+ template <> struct Bridging<WindowMovePayload> {
51
+ static WindowMovePayload
52
+ fromJs(jsi::Runtime &rt, const jsi::Object &value,
53
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
54
+ return NativeNSWindowWindowMovePayloadBridging<WindowMovePayload>::fromJs(
55
+ rt, value, jsInvoker);
56
+ }
57
+
58
+ static jsi::Object toJs(jsi::Runtime &rt, const WindowMovePayload &value,
59
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
60
+ return NativeNSWindowWindowMovePayloadBridging<WindowMovePayload>::toJs(
61
+ rt, value, jsInvoker);
62
+ }
63
+ };
64
+
65
+ template <> struct Bridging<WindowResizePayload> {
66
+ static WindowResizePayload
67
+ fromJs(jsi::Runtime &rt, const jsi::Object &value,
68
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
69
+ return NativeNSWindowWindowResizePayloadBridging<
70
+ WindowResizePayload>::fromJs(rt, value, jsInvoker);
71
+ }
72
+
73
+ static jsi::Object toJs(jsi::Runtime &rt, const WindowResizePayload &value,
74
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
75
+ return NativeNSWindowWindowResizePayloadBridging<WindowResizePayload>::toJs(
76
+ rt, value, jsInvoker);
77
+ }
78
+ };
79
+
80
+ template <> struct Bridging<WindowOcclusionStatePayload> {
81
+ static WindowOcclusionStatePayload
82
+ fromJs(jsi::Runtime &rt, const jsi::Object &value,
83
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
84
+ return NativeNSWindowWindowOcclusionStatePayloadBridging<
85
+ WindowOcclusionStatePayload>::fromJs(rt, value, jsInvoker);
86
+ }
87
+
88
+ static jsi::Object toJs(jsi::Runtime &rt,
89
+ const WindowOcclusionStatePayload &value,
90
+ const std::shared_ptr<CallInvoker> &jsInvoker) {
91
+ return NativeNSWindowWindowOcclusionStatePayloadBridging<
92
+ WindowOcclusionStatePayload>::toJs(rt, value, jsInvoker);
93
+ }
94
+ };
95
+
96
+ } // namespace facebook::react
97
+
98
+ namespace facebook::react {
99
+
100
+ class RNNSWindow : public NativeNSWindowCxxSpec<RNNSWindow> {
101
+ public:
102
+ RNNSWindow(std::shared_ptr<CallInvoker> jsInvoker);
103
+ ~RNNSWindow();
104
+
105
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowClose;
106
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowWillClose;
107
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowMove;
108
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowResize;
109
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowFocus;
110
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowBlur;
111
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowMinimize;
112
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowDeminimize;
113
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowEnterFullScreen;
114
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowExitFullScreen;
115
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowOcclusionStateChange;
116
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnWindowBackingPropertiesChange;
117
+ using NativeNSWindowCxxSpec<RNNSWindow>::emitOnScreenInfoChange;
118
+
119
+ jsi::Value addWindow(jsi::Runtime &rt, jsi::Object props);
120
+ jsi::Value closeWindow(jsi::Runtime &rt, jsi::String windowId);
121
+ jsi::Value modifyWindow(jsi::Runtime &rt, jsi::String windowId,
122
+ jsi::Object props);
123
+
124
+ jsi::Value listWindows(jsi::Runtime &rt);
125
+ jsi::Value getWindowState(jsi::Runtime &rt, jsi::String windowId);
126
+
127
+ jsi::Value focusWindow(jsi::Runtime &rt, jsi::String windowId);
128
+ jsi::Value hideWindow(jsi::Runtime &rt, jsi::String windowId);
129
+ jsi::Value showWindow(jsi::Runtime &rt, jsi::String windowId);
130
+
131
+ jsi::Value minimizeWindow(jsi::Runtime &rt, jsi::String windowId);
132
+ jsi::Value deminimizeWindow(jsi::Runtime &rt, jsi::String windowId);
133
+ jsi::Value setFullScreen(jsi::Runtime &rt, jsi::String windowId,
134
+ bool fullscreen);
135
+
136
+ jsi::Value bringToFront(jsi::Runtime &rt, jsi::String windowId);
137
+ jsi::Value sendToBack(jsi::Runtime &rt, jsi::String windowId);
138
+
139
+ jsi::Value getScreenInfo(jsi::Runtime &rt);
140
+ };
141
+
142
+ } // namespace facebook::react