git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
75 files changed+142−5264
since splitsnapshots
unifiedsplit
.gitignore−1 README.md+1 example-macos/babel.config.js+1−2 example-macos/bun.lock−7 example-macos/index.js+1 example-macos/ios/build/generated/autolinking/autolinking.json+2−28 example-macos/macos/Podfile.lock−153 example-macos/macos/YogaReactExample-macOS/RCTGhosttyTerminalManager.m−721 example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+19−533 example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+1−1 example-macos/macos/YogaReactExample-macOS/emoji/congaparrot.gif example-macos/macos/YogaReactExample-macOS/emoji/cooldoge.gif example-macos/macos/YogaReactExample-macOS/emoji/dealwithit.gif example-macos/macos/YogaReactExample-macOS/emoji/fastparrot.gif example-macos/macos/YogaReactExample-macOS/emoji/haskell.png example-macos/macos/YogaReactExample-macOS/emoji/opensourceparrot.gif example-macos/macos/YogaReactExample-macOS/emoji/parrot.gif example-macos/macos/YogaReactExample-macOS/emoji/party.png example-macos/macos/YogaReactExample-macOS/emoji/partyblob.gif example-macos/macos/YogaReactExample-macOS/emoji/purescript.png example-macos/macos/YogaReactExample-macOS/emoji/shipit.png example-macos/macos/YogaReactExample-macOS/emoji/typingcat.gif example-macos/macos/YogaReactExample-macOS/emoji/ultraparrot.gif example-macos/macos/YogaReactExample.xcodeproj/project.pbxproj+4−104 example-macos/macos/YogaReactExample.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plist−19 example-macos/macos/YogaReactExample.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate example-macos/macos/build/generated/ios/rnreanimated/rnreanimated-generated.mm−63 example-macos/macos/build/generated/ios/rnreanimated/rnreanimated.h−88 example-macos/macos/build/generated/ios/rnreanimatedJSI-generated.cpp−37 example-macos/macos/build/generated/ios/rnreanimatedJSI.h−125 example-macos/metro.config.js+1−15 example-macos/package.json−1 example-macos/patches/fix-normalizeColor.sh−65 example-macos/src/Demo/Chat.js+5−18 example-macos/src/Demo/Chat.purs+53−203 example-macos/src/Demo/Navigation.purs−8 example-macos/src/Demo/Signal.js−12 example-macos/src/Demo/Signal.purs−68 example-macos/src/Demo/SpringAnimation.purs+35−68 example-macos/src/Demo/Terminal.purs−19 example-macos/src/Main.purs+1−1 signal-backend/.gitignore−34 signal-backend/bun.lock−36 signal-backend/package.json−15 signal-backend/run.js−2 signal-backend/spago.lock−1769 signal-backend/spago.yaml−51 signal-backend/src/Main.js−26 signal-backend/src/Main.purs−53 signal-backend/src/Signal/Keys.js−15 signal-backend/src/Signal/Keys.purs−32 signal-backend/src/Signal/Net.js−15 signal-backend/src/Signal/Net.purs−56 signal-backend/src/Signal/Provisioning.purs−45 signal-backend/src/Signal/Provisioning/Cipher.js−144 signal-backend/src/Signal/Provisioning/Cipher.purs−60 signal-backend/src/Signal/Provisioning/QRCode.js−9 signal-backend/src/Signal/Provisioning/QRCode.purs−13 spago.yaml+1−1 src/Yoga/React/Native/MacOS/Button.js+3−7 src/Yoga/React/Native/MacOS/Button.purs−1 src/Yoga/React/Native/MacOS/GhosttyTerminal.js−14 src/Yoga/React/Native/MacOS/GhosttyTerminal.purs−21 src/Yoga/React/Native/MacOS/HoverView.js−14 src/Yoga/React/Native/MacOS/HoverView.purs−17 src/Yoga/React/Native/MacOS/Popover.purs−3 src/Yoga/React/Native/MacOS/RichTextLabel.js−3 src/Yoga/React/Native/MacOS/RichTextLabel.purs−23 src/Yoga/React/Native/MacOS/ScrollView.purs−1 src/Yoga/React/Native/MacOS/SplitView.js+2 src/Yoga/React/Native/MacOS/SplitView.purs+12−103 src/Yoga/React/Native/MacOS/Types.purs−9 src/Yoga/React/Native/Pressable.purs−5 src/Yoga/React/Native/Reanimated.js−104 src/Yoga/React/Native/Reanimated.purs−203
.gitignore−1
@@ -5,4 +5,3 @@ node_modules
55.DS_Store
66test/visual/diff/
77docs/dist/
8−example-macos/macos/build/DerivedData/
README.md+1
@@ -87,3 +87,4 @@ myButton = nativeButton
8787- Node.js 20+ / Bun
8888- PureScript 0.15+ and Spago
8989- CocoaPods
90+
example-macos/babel.config.js+1−2
@@ -1,4 +1,3 @@
11module.exports = {
2− presets: ["module:@react-native/babel-preset"],
3− plugins: ["react-native-reanimated/plugin"],
2+ presets: ['module:@react-native/babel-preset'],
43};
example-macos/bun.lock−7
@@ -9,7 +9,6 @@
99 "react-native": "0.81.5",
1010 "react-native-fs": "^2.20.0",
1111 "react-native-macos": "0.81.2",
12− "react-native-reanimated": "3.19.5",
1312 "react-native-safe-area-context": "^5.5.2",
1413 "twrnc": "^4.16.0",
1514 },
@@ -266,8 +265,6 @@
266265
267266 "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="],
268267
269− "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="],
270−
271268 "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
272269
273270 "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
@@ -1314,12 +1311,8 @@
13141311
13151312 "react-native-fs": ["react-native-fs@2.20.0", "", { "dependencies": { "base-64": "^0.1.0", "utf8": "^3.0.0" }, "peerDependencies": { "react-native": "*", "react-native-windows": "*" }, "optionalPeers": ["react-native-windows"] }, "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ=="],
13161313
1317− "react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.1.7", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w=="],
1318−
13191314 "react-native-macos": ["react-native-macos@0.81.2", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native-macos/virtualized-lists": "0.81.2", "@react-native/assets-registry": "0.81.5", "@react-native/codegen": "0.81.5", "@react-native/community-cli-plugin": "0.81.5", "@react-native/gradle-plugin": "0.81.5", "@react-native/js-polyfills": "0.81.5", "@react-native/normalize-colors": "0.81.5", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", "babel-jest": "^29.7.0", "babel-plugin-syntax-hermes-parser": "0.29.1", "base64-js": "^1.5.1", "commander": "^12.0.0", "flow-enums-runtime": "^0.0.6", "glob": "^7.1.1", "invariant": "^2.2.4", "jest-environment-node": "^29.7.0", "memoize-one": "^5.0.0", "metro-runtime": "^0.83.1", "metro-source-map": "^0.83.1", "nullthrows": "^1.1.1", "pretty-format": "^29.7.0", "promise": "^8.3.0", "react-devtools-core": "^6.1.5", "react-refresh": "^0.14.0", "regenerator-runtime": "^0.13.2", "scheduler": "0.26.0", "semver": "^7.1.3", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0", "ws": "^6.2.3", "yargs": "^17.6.2" }, "peerDependencies": { "@types/react": "^19.1.4", "react": "^19.1.4", "react-native": "0.81.5" }, "optionalPeers": ["@types/react"], "bin": { "react-native": "cli.js", "react-native-macos": "cli.js" } }, "sha512-S3vvbnJTbdkZdItUolof8pjl6FIVrS1v6r+3Ifd90J6NTTQZE2Fx68+bD0ba0NC8knudx7qvV7QFHS/9Td5SOw=="],
13201315
1321− "react-native-reanimated": ["react-native-reanimated@3.19.5", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4", "react-native-is-edge-to-edge": "1.1.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-bd4AwIkBAaY4BjrgpSoKjEaRG/tXD756F5nGuiH5IMBSKN8tRdUEA8hWZCyIo/R6/kha/tVSoCqodVUACh7ZWw=="],
1322−
13231316 "react-native-safe-area-context": ["react-native-safe-area-context@5.6.2", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg=="],
13241317
13251318 "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
example-macos/index.js+1
@@ -5,6 +5,7 @@
55import { AppRegistry, Text, View } from "react-native";
66import React from "react";
77
8+// Register a fallback error screen first
89let startupError = null;
910
1011function ErrorScreen() {
example-macos/ios/build/generated/autolinking/autolinking.json+2−28
@@ -28,32 +28,6 @@
2828 "macos": null
2929 }
3030 },
31− "react-native-reanimated": {
32− "root": "/Users/mark/Developer/purescript-yoga-react-native/example-macos/node_modules/react-native-reanimated",
33− "name": "react-native-reanimated",
34− "platforms": {
35− "ios": {
36− "podspecPath": "/Users/mark/Developer/purescript-yoga-react-native/example-macos/node_modules/react-native-reanimated/RNReanimated.podspec",
37− "version": "3.19.5",
38− "configurations": [],
39− "scriptPhases": []
40− },
41− "android": {
42− "sourceDir": "/Users/mark/Developer/purescript-yoga-react-native/example-macos/node_modules/react-native-reanimated/android",
43− "packageImportPath": "import com.swmansion.reanimated.ReanimatedPackage;",
44− "packageInstance": "new ReanimatedPackage()",
45− "buildTypes": [],
46− "libraryName": "rnreanimated",
47− "componentDescriptors": [],
48− "cmakeListsPath": "/Users/mark/Developer/purescript-yoga-react-native/example-macos/node_modules/react-native-reanimated/android/build/generated/source/codegen/jni/CMakeLists.txt",
49− "cxxModuleCMakeListsModuleName": null,
50− "cxxModuleCMakeListsPath": null,
51− "cxxModuleHeaderName": null,
52− "isPureCxxDependency": false
53− },
54− "macos": null
55− }
56− },
5731 "react-native-safe-area-context": {
5832 "root": "/Users/mark/Developer/purescript-yoga-react-native/example-macos/node_modules/react-native-safe-area-context",
5933 "name": "react-native-safe-area-context",
@@ -303,7 +277,7 @@
303277 {
304278 "name": "--terminal <string>",
305279 "description": "Launches the Metro Bundler in a new window using the specified terminal path.",
306− "default": "iTerm.app"
280+ "default": "zed"
307281 },
308282 {
309283 "name": "--binary-path <string>",
@@ -471,7 +445,7 @@
471445 {
472446 "name": "--terminal <string>",
473447 "description": "Launches the Metro Bundler in a new window using the specified terminal path.",
474− "default": "iTerm.app"
448+ "default": "zed"
475449 },
476450 {
477451 "name": "--appId <string>",
example-macos/macos/Podfile.lock−153
@@ -2344,155 +2344,6 @@ PODS:
23442344 - RiveRuntime (6.15.0)
23452345 - RNFS (2.20.0):
23462346 - React-Core
2347− - RNReanimated (3.19.5):
2348− - boost
2349− - DoubleConversion
2350− - fast_float
2351− - fmt
2352− - glog
2353− - hermes-engine
2354− - RCT-Folly
2355− - RCT-Folly/Fabric
2356− - RCTRequired
2357− - RCTTypeSafety
2358− - React-Core
2359− - React-debug
2360− - React-Fabric
2361− - React-featureflags
2362− - React-graphics
2363− - React-hermes
2364− - React-ImageManager
2365− - React-jsi
2366− - React-NativeModulesApple
2367− - React-RCTFabric
2368− - React-renderercss
2369− - React-rendererdebug
2370− - React-utils
2371− - ReactCodegen
2372− - ReactCommon/turbomodule/bridging
2373− - ReactCommon/turbomodule/core
2374− - RNReanimated/reanimated (= 3.19.5)
2375− - RNReanimated/worklets (= 3.19.5)
2376− - SocketRocket
2377− - Yoga
2378− - RNReanimated/reanimated (3.19.5):
2379− - boost
2380− - DoubleConversion
2381− - fast_float
2382− - fmt
2383− - glog
2384− - hermes-engine
2385− - RCT-Folly
2386− - RCT-Folly/Fabric
2387− - RCTRequired
2388− - RCTTypeSafety
2389− - React-Core
2390− - React-debug
2391− - React-Fabric
2392− - React-featureflags
2393− - React-graphics
2394− - React-hermes
2395− - React-ImageManager
2396− - React-jsi
2397− - React-NativeModulesApple
2398− - React-RCTFabric
2399− - React-renderercss
2400− - React-rendererdebug
2401− - React-utils
2402− - ReactCodegen
2403− - ReactCommon/turbomodule/bridging
2404− - ReactCommon/turbomodule/core
2405− - RNReanimated/reanimated/apple (= 3.19.5)
2406− - SocketRocket
2407− - Yoga
2408− - RNReanimated/reanimated/apple (3.19.5):
2409− - boost
2410− - DoubleConversion
2411− - fast_float
2412− - fmt
2413− - glog
2414− - hermes-engine
2415− - RCT-Folly
2416− - RCT-Folly/Fabric
2417− - RCTRequired
2418− - RCTTypeSafety
2419− - React-Core
2420− - React-debug
2421− - React-Fabric
2422− - React-featureflags
2423− - React-graphics
2424− - React-hermes
2425− - React-ImageManager
2426− - React-jsi
2427− - React-NativeModulesApple
2428− - React-RCTFabric
2429− - React-renderercss
2430− - React-rendererdebug
2431− - React-utils
2432− - ReactCodegen
2433− - ReactCommon/turbomodule/bridging
2434− - ReactCommon/turbomodule/core
2435− - SocketRocket
2436− - Yoga
2437− - RNReanimated/worklets (3.19.5):
2438− - boost
2439− - DoubleConversion
2440− - fast_float
2441− - fmt
2442− - glog
2443− - hermes-engine
2444− - RCT-Folly
2445− - RCT-Folly/Fabric
2446− - RCTRequired
2447− - RCTTypeSafety
2448− - React-Core
2449− - React-debug
2450− - React-Fabric
2451− - React-featureflags
2452− - React-graphics
2453− - React-hermes
2454− - React-ImageManager
2455− - React-jsi
2456− - React-NativeModulesApple
2457− - React-RCTFabric
2458− - React-renderercss
2459− - React-rendererdebug
2460− - React-utils
2461− - ReactCodegen
2462− - ReactCommon/turbomodule/bridging
2463− - ReactCommon/turbomodule/core
2464− - RNReanimated/worklets/apple (= 3.19.5)
2465− - SocketRocket
2466− - Yoga
2467− - RNReanimated/worklets/apple (3.19.5):
2468− - boost
2469− - DoubleConversion
2470− - fast_float
2471− - fmt
2472− - glog
2473− - hermes-engine
2474− - RCT-Folly
2475− - RCT-Folly/Fabric
2476− - RCTRequired
2477− - RCTTypeSafety
2478− - React-Core
2479− - React-debug
2480− - React-Fabric
2481− - React-featureflags
2482− - React-graphics
2483− - React-hermes
2484− - React-ImageManager
2485− - React-jsi
2486− - React-NativeModulesApple
2487− - React-RCTFabric
2488− - React-renderercss
2489− - React-rendererdebug
2490− - React-utils
2491− - ReactCodegen
2492− - ReactCommon/turbomodule/bridging
2493− - ReactCommon/turbomodule/core
2494− - SocketRocket
2495− - Yoga
24962347 - SocketRocket (0.7.1)
24972348 - Yoga (0.0.0)
24982349
@@ -2571,7 +2422,6 @@ DEPENDENCIES:
25712422 - ReactCommon/turbomodule/core (from `../node_modules/react-native-macos/ReactCommon`)
25722423 - RiveRuntime
25732424 - RNFS (from `../node_modules/react-native-fs`)
2574− - RNReanimated (from `../node_modules/react-native-reanimated`)
25752425 - SocketRocket (~> 0.7.1)
25762426 - Yoga (from `../node_modules/react-native-macos/ReactCommon/yoga`)
25772427
@@ -2726,8 +2576,6 @@ EXTERNAL SOURCES:
27262576 :path: "../node_modules/react-native-macos/ReactCommon"
27272577 RNFS:
27282578 :path: "../node_modules/react-native-fs"
2729− RNReanimated:
2730− :path: "../node_modules/react-native-reanimated"
27312579 Yoga:
27322580 :path: "../node_modules/react-native-macos/ReactCommon/yoga"
27332581
@@ -2805,7 +2653,6 @@ SPEC CHECKSUMS:
28052653 ReactCommon: 9f8189efbc1aa52926df2791a2e47b3340353849
28062654 RiveRuntime: 6c9f2f9e07e3411532e9187e1708d007468b87dd
28072655 RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8
2808− RNReanimated: 9af1b9f7d221d1cc2f99d935bab08419cae7c1ce
28092656 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
28102657 Yoga: 6048a55441c73f8e3916a8eac6b83886708c77f9
28112658
example-macos/macos/YogaReactExample-macOS/RCTGhosttyTerminalManager.mdeleted−721
@@ -1,721 +0,0 @@
1−#import <React/RCTViewManager.h>
2−#import <React/RCTBridge.h>
3−#import <React/RCTUIManager.h>
4−#import <AppKit/AppKit.h>
5−#import <Metal/Metal.h>
6−#import <QuartzCore/CAMetalLayer.h>
7−#include "ghostty.h"
8−
9−// ---------------------------------------------------------------------------
10−// GhosttyAppManager — singleton that owns the ghostty_app_t
11−// ---------------------------------------------------------------------------
12−
13−@interface GhosttyAppManager : NSObject
14−@property (nonatomic, assign) ghostty_app_t app;
15−+ (instancetype)shared;
16−@end
17−
18−// Forward-declare the view so callbacks can reach it
19−@interface RCTGhosttyTerminalView : NSView <NSTextInputClient>
20−@property (nonatomic, assign) ghostty_surface_t surface;
21−@property (nonatomic, copy) RCTBubblingEventBlock onTitle;
22−@property (nonatomic, copy) RCTBubblingEventBlock onBell;
23−@property (nonatomic, copy) RCTBubblingEventBlock onExit;
24−@property (nonatomic, copy) NSString *cwd;
25−@property (nonatomic, copy) NSString *command;
26−@property (nonatomic, assign) float fontSize;
27−@property (nonatomic, copy) NSString *fontFamily;
28−@end
29−
30−// ---------------------------------------------------------------------------
31−// Runtime callbacks (C functions)
32−// ---------------------------------------------------------------------------
33−
34−static void rt_wakeup(void *ud) {
35− dispatch_async(dispatch_get_main_queue(), ^{
36− ghostty_app_t app = [GhosttyAppManager shared].app;
37− if (app) ghostty_app_tick(app);
38− });
39−}
40−
41−static bool rt_action(ghostty_app_t app, ghostty_target_s target,
42− ghostty_action_s action) {
43− if (target.tag != GHOSTTY_TARGET_SURFACE) return false;
44− ghostty_surface_t surface = target.target.surface;
45− if (!surface) return false;
46−
47− RCTGhosttyTerminalView *view =
48− (__bridge RCTGhosttyTerminalView *)ghostty_surface_userdata(surface);
49− if (!view) return false;
50−
51− switch (action.tag) {
52− case GHOSTTY_ACTION_SET_TITLE: {
53− NSString *title = action.action.set_title.title
54− ? [NSString stringWithUTF8String:action.action.set_title.title]
55− : @"";
56− if (view.onTitle) {
57− view.onTitle(@{@"title": title});
58− }
59− return true;
60− }
61− case GHOSTTY_ACTION_RING_BELL: {
62− NSBeep();
63− if (view.onBell) {
64− view.onBell(@{});
65− }
66− return true;
67− }
68− case GHOSTTY_ACTION_SHOW_CHILD_EXITED: {
69− if (view.onExit) {
70− view.onExit(@{@"exitCode": @(action.action.child_exited.exit_code)});
71− }
72− return true;
73− }
74− case GHOSTTY_ACTION_CLOSE_WINDOW:
75− return true;
76− default:
77− return false;
78− }
79−}
80−
81−static void rt_read_clipboard(void *ud, ghostty_clipboard_e clipboard,
82− void *state) {
83− NSPasteboard *pb = [NSPasteboard generalPasteboard];
84− NSString *str = [pb stringForType:NSPasteboardTypeString];
85− ghostty_surface_t surface = ud;
86− if (str) {
87− ghostty_surface_complete_clipboard_request(surface,
88− str.UTF8String,
89− state, false);
90− }
91−}
92−
93−static void rt_confirm_read_clipboard(void *ud, const char *text,
94− void *state,
95− ghostty_clipboard_request_e req) {
96− ghostty_surface_t surface = ud;
97− ghostty_surface_complete_clipboard_request(surface, text, state, true);
98−}
99−
100−static void rt_write_clipboard(void *ud, ghostty_clipboard_e clipboard,
101− const ghostty_clipboard_content_s *content,
102− size_t content_len, bool confirm) {
103− if (content_len == 0) return;
104− NSPasteboard *pb = [NSPasteboard generalPasteboard];
105− [pb clearContents];
106− NSString *str = [NSString stringWithUTF8String:content[0].data];
107− if (str) [pb setString:str forType:NSPasteboardTypeString];
108−}
109−
110−static void rt_close_surface(void *ud, bool processAlive) {
111− // no-op — surface lifecycle managed by React
112−}
113−
114−// ---------------------------------------------------------------------------
115−// GhosttyAppManager implementation
116−// ---------------------------------------------------------------------------
117−
118−@implementation GhosttyAppManager
119−
120−+ (instancetype)shared {
121− static GhosttyAppManager *instance;
122− static dispatch_once_t onceToken;
123− dispatch_once(&onceToken, ^{
124− instance = [[GhosttyAppManager alloc] init];
125− });
126− return instance;
127−}
128−
129−- (instancetype)init {
130− if (self = [super init]) {
131− _app = NULL;
132− ghostty_init(0, NULL);
133−
134− ghostty_config_t cfg = ghostty_config_new();
135− ghostty_config_finalize(cfg);
136−
137− ghostty_runtime_config_s rt = {
138− .userdata = (__bridge void *)self,
139− .supports_selection_clipboard = false,
140− .wakeup_cb = rt_wakeup,
141− .action_cb = rt_action,
142− .read_clipboard_cb = rt_read_clipboard,
143− .confirm_read_clipboard_cb = rt_confirm_read_clipboard,
144− .write_clipboard_cb = rt_write_clipboard,
145− .close_surface_cb = rt_close_surface,
146− };
147−
148− _app = ghostty_app_new(&rt, cfg);
149− ghostty_config_free(cfg);
150−
151− if (!_app) {
152− NSLog(@"[Ghostty] Failed to create ghostty_app_t");
153− }
154− }
155− return self;
156−}
157−
158−@end
159−
160−// ---------------------------------------------------------------------------
161−// macOS keyCode → ghostty_input_key_e translation
162−// ---------------------------------------------------------------------------
163−
164−static ghostty_input_key_e keyCodeToGhostty(unsigned short keyCode) {
165− switch (keyCode) {
166− // Writing System Keys
167− case 0x32: return GHOSTTY_KEY_BACKQUOTE;
168− case 0x2A: return GHOSTTY_KEY_BACKSLASH;
169− case 0x21: return GHOSTTY_KEY_BRACKET_LEFT;
170− case 0x1E: return GHOSTTY_KEY_BRACKET_RIGHT;
171− case 0x2B: return GHOSTTY_KEY_COMMA;
172− case 0x1D: return GHOSTTY_KEY_DIGIT_0;
173− case 0x12: return GHOSTTY_KEY_DIGIT_1;
174− case 0x13: return GHOSTTY_KEY_DIGIT_2;
175− case 0x14: return GHOSTTY_KEY_DIGIT_3;
176− case 0x15: return GHOSTTY_KEY_DIGIT_4;
177− case 0x17: return GHOSTTY_KEY_DIGIT_5;
178− case 0x16: return GHOSTTY_KEY_DIGIT_6;
179− case 0x1A: return GHOSTTY_KEY_DIGIT_7;
180− case 0x1C: return GHOSTTY_KEY_DIGIT_8;
181− case 0x19: return GHOSTTY_KEY_DIGIT_9;
182− case 0x18: return GHOSTTY_KEY_EQUAL;
183− case 0x0A: return GHOSTTY_KEY_INTL_BACKSLASH;
184− case 0x5E: return GHOSTTY_KEY_INTL_RO;
185− case 0x5D: return GHOSTTY_KEY_INTL_YEN;
186− case 0x00: return GHOSTTY_KEY_A;
187− case 0x0B: return GHOSTTY_KEY_B;
188− case 0x08: return GHOSTTY_KEY_C;
189− case 0x02: return GHOSTTY_KEY_D;
190− case 0x0E: return GHOSTTY_KEY_E;
191− case 0x03: return GHOSTTY_KEY_F;
192− case 0x05: return GHOSTTY_KEY_G;
193− case 0x04: return GHOSTTY_KEY_H;
194− case 0x22: return GHOSTTY_KEY_I;
195− case 0x26: return GHOSTTY_KEY_J;
196− case 0x28: return GHOSTTY_KEY_K;
197− case 0x25: return GHOSTTY_KEY_L;
198− case 0x2E: return GHOSTTY_KEY_M;
199− case 0x2D: return GHOSTTY_KEY_N;
200− case 0x1F: return GHOSTTY_KEY_O;
201− case 0x23: return GHOSTTY_KEY_P;
202− case 0x0C: return GHOSTTY_KEY_Q;
203− case 0x0F: return GHOSTTY_KEY_R;
204− case 0x01: return GHOSTTY_KEY_S;
205− case 0x11: return GHOSTTY_KEY_T;
206− case 0x20: return GHOSTTY_KEY_U;
207− case 0x09: return GHOSTTY_KEY_V;
208− case 0x0D: return GHOSTTY_KEY_W;
209− case 0x07: return GHOSTTY_KEY_X;
210− case 0x10: return GHOSTTY_KEY_Y;
211− case 0x06: return GHOSTTY_KEY_Z;
212− case 0x1B: return GHOSTTY_KEY_MINUS;
213− case 0x2F: return GHOSTTY_KEY_PERIOD;
214− case 0x27: return GHOSTTY_KEY_QUOTE;
215− case 0x29: return GHOSTTY_KEY_SEMICOLON;
216− case 0x2C: return GHOSTTY_KEY_SLASH;
217−
218− // Functional Keys
219− case 0x3A: return GHOSTTY_KEY_ALT_LEFT;
220− case 0x3D: return GHOSTTY_KEY_ALT_RIGHT;
221− case 0x33: return GHOSTTY_KEY_BACKSPACE;
222− case 0x39: return GHOSTTY_KEY_CAPS_LOCK;
223− case 0x6E: return GHOSTTY_KEY_CONTEXT_MENU;
224− case 0x3B: return GHOSTTY_KEY_CONTROL_LEFT;
225− case 0x3E: return GHOSTTY_KEY_CONTROL_RIGHT;
226− case 0x24: return GHOSTTY_KEY_ENTER;
227− case 0x37: return GHOSTTY_KEY_META_LEFT;
228− case 0x36: return GHOSTTY_KEY_META_RIGHT;
229− case 0x38: return GHOSTTY_KEY_SHIFT_LEFT;
230− case 0x3C: return GHOSTTY_KEY_SHIFT_RIGHT;
231− case 0x31: return GHOSTTY_KEY_SPACE;
232− case 0x30: return GHOSTTY_KEY_TAB;
233−
234− // Control Pad Section
235− case 0x75: return GHOSTTY_KEY_DELETE;
236− case 0x77: return GHOSTTY_KEY_END;
237− case 0x73: return GHOSTTY_KEY_HOME;
238− case 0x72: return GHOSTTY_KEY_INSERT;
239− case 0x79: return GHOSTTY_KEY_PAGE_DOWN;
240− case 0x74: return GHOSTTY_KEY_PAGE_UP;
241−
242− // Arrow Pad Section
243− case 0x7D: return GHOSTTY_KEY_ARROW_DOWN;
244− case 0x7B: return GHOSTTY_KEY_ARROW_LEFT;
245− case 0x7C: return GHOSTTY_KEY_ARROW_RIGHT;
246− case 0x7E: return GHOSTTY_KEY_ARROW_UP;
247−
248− // Numpad Section
249− case 0x47: return GHOSTTY_KEY_NUM_LOCK;
250− case 0x52: return GHOSTTY_KEY_NUMPAD_0;
251− case 0x53: return GHOSTTY_KEY_NUMPAD_1;
252− case 0x54: return GHOSTTY_KEY_NUMPAD_2;
253− case 0x55: return GHOSTTY_KEY_NUMPAD_3;
254− case 0x56: return GHOSTTY_KEY_NUMPAD_4;
255− case 0x57: return GHOSTTY_KEY_NUMPAD_5;
256− case 0x58: return GHOSTTY_KEY_NUMPAD_6;
257− case 0x59: return GHOSTTY_KEY_NUMPAD_7;
258− case 0x5B: return GHOSTTY_KEY_NUMPAD_8;
259− case 0x5C: return GHOSTTY_KEY_NUMPAD_9;
260− case 0x45: return GHOSTTY_KEY_NUMPAD_ADD;
261− case 0x5F: return GHOSTTY_KEY_NUMPAD_COMMA;
262− case 0x41: return GHOSTTY_KEY_NUMPAD_DECIMAL;
263− case 0x4B: return GHOSTTY_KEY_NUMPAD_DIVIDE;
264− case 0x4C: return GHOSTTY_KEY_NUMPAD_ENTER;
265− case 0x51: return GHOSTTY_KEY_NUMPAD_EQUAL;
266− case 0x43: return GHOSTTY_KEY_NUMPAD_MULTIPLY;
267− case 0x4E: return GHOSTTY_KEY_NUMPAD_SUBTRACT;
268−
269− // Function Section
270− case 0x35: return GHOSTTY_KEY_ESCAPE;
271− case 0x7A: return GHOSTTY_KEY_F1;
272− case 0x78: return GHOSTTY_KEY_F2;
273− case 0x63: return GHOSTTY_KEY_F3;
274− case 0x76: return GHOSTTY_KEY_F4;
275− case 0x60: return GHOSTTY_KEY_F5;
276− case 0x61: return GHOSTTY_KEY_F6;
277− case 0x62: return GHOSTTY_KEY_F7;
278− case 0x64: return GHOSTTY_KEY_F8;
279− case 0x65: return GHOSTTY_KEY_F9;
280− case 0x6D: return GHOSTTY_KEY_F10;
281− case 0x67: return GHOSTTY_KEY_F11;
282− case 0x6F: return GHOSTTY_KEY_F12;
283− case 0x69: return GHOSTTY_KEY_F13;
284− case 0x6B: return GHOSTTY_KEY_F14;
285− case 0x71: return GHOSTTY_KEY_F15;
286− case 0x6A: return GHOSTTY_KEY_F16;
287− case 0x40: return GHOSTTY_KEY_F17;
288− case 0x4F: return GHOSTTY_KEY_F18;
289− case 0x50: return GHOSTTY_KEY_F19;
290− case 0x5A: return GHOSTTY_KEY_F20;
291−
292− // Media Keys
293− case 0x49: return GHOSTTY_KEY_AUDIO_VOLUME_DOWN;
294− case 0x4A: return GHOSTTY_KEY_AUDIO_VOLUME_MUTE;
295− case 0x48: return GHOSTTY_KEY_AUDIO_VOLUME_UP;
296−
297− default: return GHOSTTY_KEY_UNIDENTIFIED;
298− }
299−}
300−
301−// ---------------------------------------------------------------------------
302−// NSEvent modifier flags → ghostty_input_mods_e
303−// ---------------------------------------------------------------------------
304−
305−static ghostty_input_mods_e modsFromFlags(NSEventModifierFlags flags) {
306− uint32_t mods = GHOSTTY_MODS_NONE;
307− if (flags & NSEventModifierFlagShift) mods |= GHOSTTY_MODS_SHIFT;
308− if (flags & NSEventModifierFlagControl) mods |= GHOSTTY_MODS_CTRL;
309− if (flags & NSEventModifierFlagOption) mods |= GHOSTTY_MODS_ALT;
310− if (flags & NSEventModifierFlagCommand) mods |= GHOSTTY_MODS_SUPER;
311− if (flags & NSEventModifierFlagCapsLock) mods |= GHOSTTY_MODS_CAPS;
312−
313− // Sided modifiers via device-dependent bits
314− NSUInteger raw = flags;
315− if (raw & 0x00000004) mods |= GHOSTTY_MODS_SHIFT_RIGHT; // NX_DEVICERSHIFTKEYMASK
316− if (raw & 0x00002000) mods |= GHOSTTY_MODS_CTRL_RIGHT; // NX_DEVICERCTLKEYMASK
317− if (raw & 0x00000040) mods |= GHOSTTY_MODS_ALT_RIGHT; // NX_DEVICERALTKEYMASK
318− if (raw & 0x00000010) mods |= GHOSTTY_MODS_SUPER_RIGHT; // NX_DEVICERCMDKEYMASK
319−
320− return (ghostty_input_mods_e)mods;
321−}
322−
323−// consumed_mods: everything except control and command (Ghostty convention)
324−static ghostty_input_mods_e consumedModsFromFlags(NSEventModifierFlags flags) {
325− NSEventModifierFlags cleaned = flags & ~(NSEventModifierFlagControl | NSEventModifierFlagCommand);
326− return modsFromFlags(cleaned);
327−}
328−
329−// ---------------------------------------------------------------------------
330−// RCTGhosttyTerminalView
331−// ---------------------------------------------------------------------------
332−
333−@implementation RCTGhosttyTerminalView {
334− BOOL _surfaceCreated;
335− NSTrackingArea *_trackingArea;
336−}
337−
338−- (instancetype)initWithFrame:(NSRect)frame {
339− if (self = [super initWithFrame:frame]) {
340− self.wantsLayer = YES;
341− _surfaceCreated = NO;
342− _fontSize = 0; // 0 means use default
343− }
344− return self;
345−}
346−
347−- (CALayer *)makeBackingLayer {
348− CAMetalLayer *layer = [CAMetalLayer layer];
349− layer.device = MTLCreateSystemDefaultDevice();
350− layer.contentsScale = self.window.backingScaleFactor ?: 2.0;
351− layer.opaque = YES;
352− return layer;
353−}
354−
355−- (void)viewDidMoveToWindow {
356− [super viewDidMoveToWindow];
357− if (self.window && !_surfaceCreated) {
358− [self createSurface];
359− }
360−}
361−
362−- (void)createSurface {
363− ghostty_app_t app = [GhosttyAppManager shared].app;
364− if (!app) return;
365−
366− ghostty_surface_config_s cfg = ghostty_surface_config_new();
367− cfg.platform_tag = GHOSTTY_PLATFORM_MACOS;
368− cfg.platform.macos.nsview = (__bridge void *)self;
369− cfg.userdata = (__bridge void *)self;
370− cfg.scale_factor = self.window.backingScaleFactor;
371− cfg.font_size = _fontSize;
372−
373− if (_cwd) cfg.working_directory = _cwd.UTF8String;
374− if (_command) cfg.command = _command.UTF8String;
375−
376− _surface = ghostty_surface_new(app, &cfg);
377− if (_surface) {
378− _surfaceCreated = YES;
379− uint32_t w = (uint32_t)self.bounds.size.width;
380− uint32_t h = (uint32_t)self.bounds.size.height;
381− if (w > 0 && h > 0) {
382− ghostty_surface_set_size(_surface, w, h);
383− }
384− } else {
385− NSLog(@"[Ghostty] Failed to create surface");
386− }
387−}
388−
389−- (void)removeFromSuperview {
390− if (_surface) {
391− ghostty_surface_free(_surface);
392− _surface = NULL;
393− _surfaceCreated = NO;
394− }
395− [super removeFromSuperview];
396−}
397−
398−- (void)dealloc {
399− if (_surface) {
400− ghostty_surface_free(_surface);
401− _surface = NULL;
402− }
403−}
404−
405−// ---------------------------------------------------------------------------
406−// Layout
407−// ---------------------------------------------------------------------------
408−
409−- (void)setFrameSize:(NSSize)newSize {
410− [super setFrameSize:newSize];
411− if (_surface) {
412− uint32_t w = (uint32_t)newSize.width;
413− uint32_t h = (uint32_t)newSize.height;
414− if (w > 0 && h > 0) {
415− ghostty_surface_set_size(_surface, w, h);
416− }
417− }
418−}
419−
420−- (void)viewDidChangeBackingProperties {
421− [super viewDidChangeBackingProperties];
422− if (_surface && self.window) {
423− double scale = self.window.backingScaleFactor;
424− ghostty_surface_set_content_scale(_surface, scale, scale);
425−
426− CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
427− if ([metalLayer isKindOfClass:[CAMetalLayer class]]) {
428− metalLayer.contentsScale = scale;
429− }
430− }
431−}
432−
433−// ---------------------------------------------------------------------------
434−// First responder
435−// ---------------------------------------------------------------------------
436−
437−- (BOOL)acceptsFirstResponder { return YES; }
438−
439−- (BOOL)becomeFirstResponder {
440− if (_surface) ghostty_surface_set_focus(_surface, true);
441− return YES;
442−}
443−
444−- (BOOL)resignFirstResponder {
445− if (_surface) ghostty_surface_set_focus(_surface, false);
446− return YES;
447−}
448−
449−// ---------------------------------------------------------------------------
450−// Mouse tracking
451−// ---------------------------------------------------------------------------
452−
453−- (void)updateTrackingAreas {
454− [super updateTrackingAreas];
455− if (_trackingArea) [self removeTrackingArea:_trackingArea];
456− _trackingArea = [[NSTrackingArea alloc]
457− initWithRect:self.bounds
458− options:(NSTrackingMouseEnteredAndExited |
459− NSTrackingMouseMoved |
460− NSTrackingActiveInKeyWindow |
461− NSTrackingInVisibleRect)
462− owner:self
463− userInfo:nil];
464− [self addTrackingArea:_trackingArea];
465−}
466−
467−- (NSPoint)surfacePoint:(NSEvent *)event {
468− NSPoint p = [self convertPoint:event.locationInWindow fromView:nil];
469− // Flip Y for ghostty (top-left origin)
470− p.y = self.bounds.size.height - p.y;
471− return p;
472−}
473−
474−- (void)mouseDown:(NSEvent *)event {
475− if (!_surface) return;
476− NSPoint p = [self surfacePoint:event];
477− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
478− ghostty_surface_mouse_button(_surface, GHOSTTY_MOUSE_PRESS, GHOSTTY_MOUSE_LEFT,
479− modsFromFlags(event.modifierFlags));
480−}
481−
482−- (void)mouseUp:(NSEvent *)event {
483− if (!_surface) return;
484− NSPoint p = [self surfacePoint:event];
485− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
486− ghostty_surface_mouse_button(_surface, GHOSTTY_MOUSE_RELEASE, GHOSTTY_MOUSE_LEFT,
487− modsFromFlags(event.modifierFlags));
488−}
489−
490−- (void)rightMouseDown:(NSEvent *)event {
491− if (!_surface) return;
492− NSPoint p = [self surfacePoint:event];
493− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
494− ghostty_surface_mouse_button(_surface, GHOSTTY_MOUSE_PRESS, GHOSTTY_MOUSE_RIGHT,
495− modsFromFlags(event.modifierFlags));
496−}
497−
498−- (void)rightMouseUp:(NSEvent *)event {
499− if (!_surface) return;
500− NSPoint p = [self surfacePoint:event];
501− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
502− ghostty_surface_mouse_button(_surface, GHOSTTY_MOUSE_RELEASE, GHOSTTY_MOUSE_RIGHT,
503− modsFromFlags(event.modifierFlags));
504−}
505−
506−- (void)mouseMoved:(NSEvent *)event {
507− if (!_surface) return;
508− NSPoint p = [self surfacePoint:event];
509− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
510−}
511−
512−- (void)mouseDragged:(NSEvent *)event {
513− if (!_surface) return;
514− NSPoint p = [self surfacePoint:event];
515− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
516−}
517−
518−- (void)rightMouseDragged:(NSEvent *)event {
519− if (!_surface) return;
520− NSPoint p = [self surfacePoint:event];
521− ghostty_surface_mouse_pos(_surface, p.x, p.y, modsFromFlags(event.modifierFlags));
522−}
523−
524−- (void)scrollWheel:(NSEvent *)event {
525− if (!_surface) return;
526− double dx = event.scrollingDeltaX;
527− double dy = event.scrollingDeltaY;
528− ghostty_input_scroll_mods_t smods = 0;
529− if (event.hasPreciseScrollingDeltas) smods |= 1;
530− ghostty_surface_mouse_scroll(_surface, dx, dy, smods);
531−}
532−
533−// ---------------------------------------------------------------------------
534−// Keyboard input
535−// ---------------------------------------------------------------------------
536−
537−- (void)keyDown:(NSEvent *)event {
538− if (!_surface) return;
539−
540− ghostty_input_key_s key_ev = {0};
541− key_ev.action = GHOSTTY_ACTION_PRESS;
542− if (event.isARepeat) key_ev.action = GHOSTTY_ACTION_REPEAT;
543− key_ev.keycode = event.keyCode;
544− key_ev.mods = modsFromFlags(event.modifierFlags);
545− key_ev.consumed_mods = consumedModsFromFlags(event.modifierFlags);
546−
547− // unshifted codepoint
548− NSString *unshifted = [event charactersByApplyingModifiers:0];
549− if (unshifted.length > 0) {
550− key_ev.unshifted_codepoint = [unshifted characterAtIndex:0];
551− }
552−
553− // text for character input
554− NSString *chars = event.characters;
555− if (chars.length == 1) {
556− unichar ch = [chars characterAtIndex:0];
557− // Skip control characters (handled by Ghostty) and PUA (function keys)
558− if (ch < 0x20) {
559− NSString *noCtrl = [event charactersByApplyingModifiers:
560− event.modifierFlags & ~NSEventModifierFlagControl];
561− if (noCtrl.length > 0) chars = noCtrl;
562− else chars = nil;
563− } else if (ch >= 0xF700 && ch <= 0xF8FF) {
564− chars = nil;
565− }
566− }
567−
568− key_ev.text = chars ? chars.UTF8String : NULL;
569− key_ev.composing = false;
570−
571− bool handled = ghostty_surface_key(_surface, key_ev);
572− if (!handled) {
573− [self interpretKeyEvents:@[event]];
574− }
575−}
576−
577−- (void)keyUp:(NSEvent *)event {
578− if (!_surface) return;
579−
580− ghostty_input_key_s key_ev = {0};
581− key_ev.action = GHOSTTY_ACTION_RELEASE;
582− key_ev.keycode = event.keyCode;
583− key_ev.mods = modsFromFlags(event.modifierFlags);
584− key_ev.consumed_mods = consumedModsFromFlags(event.modifierFlags);
585− key_ev.unshifted_codepoint = 0;
586− key_ev.text = NULL;
587− key_ev.composing = false;
588−
589− ghostty_surface_key(_surface, key_ev);
590−}
591−
592−- (void)flagsChanged:(NSEvent *)event {
593− if (!_surface) return;
594−
595− ghostty_input_key_e key = keyCodeToGhostty(event.keyCode);
596− if (key == GHOSTTY_KEY_UNIDENTIFIED) return;
597−
598− // Determine press/release by checking if the modifier bit is set
599− BOOL pressed = NO;
600− switch (event.keyCode) {
601− case 0x38: case 0x3C: pressed = !!(event.modifierFlags & NSEventModifierFlagShift); break;
602− case 0x3B: case 0x3E: pressed = !!(event.modifierFlags & NSEventModifierFlagControl); break;
603− case 0x3A: case 0x3D: pressed = !!(event.modifierFlags & NSEventModifierFlagOption); break;
604− case 0x37: case 0x36: pressed = !!(event.modifierFlags & NSEventModifierFlagCommand); break;
605− case 0x39: pressed = !!(event.modifierFlags & NSEventModifierFlagCapsLock); break;
606− default: return;
607− }
608−
609− ghostty_input_key_s key_ev = {0};
610− key_ev.action = pressed ? GHOSTTY_ACTION_PRESS : GHOSTTY_ACTION_RELEASE;
611− key_ev.keycode = event.keyCode;
612− key_ev.mods = modsFromFlags(event.modifierFlags);
613− key_ev.consumed_mods = (ghostty_input_mods_e)GHOSTTY_MODS_NONE;
614− key_ev.text = NULL;
615− key_ev.composing = false;
616−
617− ghostty_surface_key(_surface, key_ev);
618−}
619−
620−// ---------------------------------------------------------------------------
621−// NSTextInputClient (IME)
622−// ---------------------------------------------------------------------------
623−
624−- (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
625− if (!_surface) return;
626− NSString *str = [string isKindOfClass:[NSAttributedString class]]
627− ? [string string] : string;
628− if (str.length > 0) {
629− ghostty_surface_text(_surface, str.UTF8String, strlen(str.UTF8String));
630− }
631−}
632−
633−- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange
634− replacementRange:(NSRange)replacementRange {
635− if (!_surface) return;
636− NSString *str = [string isKindOfClass:[NSAttributedString class]]
637− ? [string string] : string;
638− if (str.length > 0) {
639− ghostty_surface_preedit(_surface, str.UTF8String, strlen(str.UTF8String));
640− }
641−}
642−
643−- (void)unmarkText {
644− if (_surface) ghostty_surface_preedit(_surface, NULL, 0);
645−}
646−
647−- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); }
648−- (NSRange)markedRange { return NSMakeRange(NSNotFound, 0); }
649−- (BOOL)hasMarkedText { return NO; }
650−- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
651− actualRange:(NSRangePointer)actualRange {
652− return nil;
653−}
654−- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { return @[]; }
655−- (NSRect)firstRectForCharacterRange:(NSRange)range
656− actualRange:(NSRangePointer)actualRange {
657− if (_surface) {
658− double x = 0, y = 0, w = 0, h = 0;
659− ghostty_surface_ime_point(_surface, &x, &y, &w, &h);
660− NSRect r = NSMakeRect(x, self.bounds.size.height - y - h, w, h);
661− return [self.window convertRectToScreen:[self convertRect:r toView:nil]];
662− }
663− return NSZeroRect;
664−}
665−- (NSUInteger)characterIndexForPoint:(NSPoint)point { return NSNotFound; }
666−
667−// ---------------------------------------------------------------------------
668−// React props
669−// ---------------------------------------------------------------------------
670−
671−- (void)setCwd:(NSString *)cwd {
672− _cwd = [cwd copy];
673− // Prop can only be set before surface creation
674−}
675−
676−- (void)setCommand:(NSString *)command {
677− _command = [command copy];
678−}
679−
680−- (void)setFontSize:(float)fontSize {
681− _fontSize = fontSize;
682−}
683−
684−- (void)setFontFamily:(NSString *)fontFamily {
685− _fontFamily = [fontFamily copy];
686−}
687−
688−@end
689−
690−// ---------------------------------------------------------------------------
691−// RCTGhosttyTerminalManager (RCTViewManager)
692−// ---------------------------------------------------------------------------
693−
694−@interface RCTGhosttyTerminalManager : RCTViewManager
695−@end
696−
697−@implementation RCTGhosttyTerminalManager
698−
699−RCT_EXPORT_MODULE(GhosttyTerminal)
700−
701−- (NSView *)view {
702− return [[RCTGhosttyTerminalView alloc] initWithFrame:CGRectZero];
703−}
704−
705−RCT_EXPORT_VIEW_PROPERTY(onTitle, RCTBubblingEventBlock)
706−RCT_EXPORT_VIEW_PROPERTY(onBell, RCTBubblingEventBlock)
707−RCT_EXPORT_VIEW_PROPERTY(onExit, RCTBubblingEventBlock)
708−RCT_CUSTOM_VIEW_PROPERTY(cwd, NSString, RCTGhosttyTerminalView) {
709− if (json) view.cwd = [json description];
710−}
711−RCT_CUSTOM_VIEW_PROPERTY(command, NSString, RCTGhosttyTerminalView) {
712− if (json) view.command = [json description];
713−}
714−RCT_CUSTOM_VIEW_PROPERTY(fontSize, float, RCTGhosttyTerminalView) {
715− if (json) view.fontSize = [json floatValue];
716−}
717−RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTGhosttyTerminalView) {
718− if (json) view.fontFamily = [json description];
719−}
720−
721−@end
example-macos/macos/YogaReactExample-macOS/RCTMacOSControlsManager.m+19−533
@@ -1,8 +1,6 @@
11#import <React/RCTViewManager.h>
22#import <React/RCTBridgeModule.h>
33#import <React/RCTBridge.h>
4−#import <React/RCTUIManager.h>
5−
64#import <AppKit/AppKit.h>
75#import <WebKit/WebKit.h>
86#import <AVKit/AVKit.h>
@@ -22,38 +20,15 @@
2220// 1. NSButton
2321// ============================================================
2422
25−@interface RCTBorderlessButtonCell : NSButtonCell
26−@property (nonatomic, assign) BOOL hovered;
27−@end
28−
29−@implementation RCTBorderlessButtonCell
30−- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
31− if (_hovered) {
32− BOOL isDark = [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameDarkAqua]] != nil;
33− NSColor *color = isDark
34− ? [NSColor colorWithWhite:1.0 alpha:0.15]
35− : [NSColor colorWithWhite:0.0 alpha:0.1];
36− [color setFill];
37− NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:cellFrame
38− xRadius:cellFrame.size.height / 2.0
39− yRadius:cellFrame.size.height / 2.0];
40− [path fill];
41− }
42− [super drawWithFrame:cellFrame inView:controlView];
43−}
44−@end
45−
46−@interface RCTNativeButtonView : NSView <NSTextInputClient>
23+@interface RCTNativeButtonView : NSView
4724@property (nonatomic, strong) NSButton *button;
48−@property (nonatomic, assign) BOOL borderlessHover;
49−@property (nonatomic, copy) RCTDirectEventBlock onPressButton;
25+@property (nonatomic, copy) RCTBubblingEventBlock onPress;
5026@property (nonatomic, copy) NSString *title;
5127@property (nonatomic, copy) NSString *sfSymbol;
5228@property (nonatomic, copy) NSString *bezelStyle;
5329@property (nonatomic, assign) BOOL destructive;
5430@property (nonatomic, assign) BOOL primary;
5531@property (nonatomic, assign) BOOL buttonEnabled;
56−@property (nonatomic, copy) RCTDirectEventBlock onEmojiPick;
5732@end
5833
5934@implementation RCTNativeButtonView
@@ -68,52 +43,8 @@
6843 return self;
6944}
7045
71−- (BOOL)acceptsFirstResponder { return _onEmojiPick != nil; }
72−
7346- (void)handlePress {
74− if (_onEmojiPick) {
75− [self.window makeFirstResponder:self];
76− [NSApp orderFrontCharacterPalette:nil];
77− return;
78− }
79− if (_onPressButton) _onPressButton(@{});
80−}
81−
82−// NSTextInputClient required methods
83−- (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
84− NSString *text = [string isKindOfClass:[NSAttributedString class]]
85− ? [(NSAttributedString *)string string] : string;
86− if (_onEmojiPick && text.length > 0) {
87− _onEmojiPick(@{@"emoji": text});
88− }
89−}
90−
91−- (void)doCommandBySelector:(SEL)selector {}
92−- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {}
93−- (void)unmarkText {}
94−- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); }
95−- (NSRange)markedRange { return NSMakeRange(NSNotFound, 0); }
96−- (BOOL)hasMarkedText { return NO; }
97−- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange { return nil; }
98−- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { return @[]; }
99−- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange {
100− NSRect r = [self convertRect:self.bounds toView:nil];
101− return [self.window convertRectToScreen:r];
102−}
103−- (NSUInteger)characterIndexForPoint:(NSPoint)point { return NSNotFound; }
104−
105−- (void)mouseEntered:(NSEvent *)event {
106− if (_borderlessHover) {
107− ((RCTBorderlessButtonCell *)_button.cell).hovered = YES;
108− [_button setNeedsDisplay:YES];
109− }
110−}
111−
112−- (void)mouseExited:(NSEvent *)event {
113− if (_borderlessHover) {
114− ((RCTBorderlessButtonCell *)_button.cell).hovered = NO;
115− [_button setNeedsDisplay:YES];
116− }
47+ if (_onPress) _onPress(@{});
11748}
11849
11950- (void)setTitle:(NSString *)title {
@@ -147,23 +78,6 @@
14778 if ([bezelStyle isEqualToString:@"toolbar"]) _button.bezelStyle = NSBezelStyleToolbar;
14879 else if ([bezelStyle isEqualToString:@"texturedSquare"]) _button.bezelStyle = NSBezelStyleTexturedSquare;
14980 else if ([bezelStyle isEqualToString:@"inline"]) _button.bezelStyle = NSBezelStyleInline;
150− else if ([bezelStyle isEqualToString:@"circular"]) _button.bezelStyle = NSBezelStyleCircular;
151− else if ([bezelStyle isEqualToString:@"accessoryBarAction"]) _button.bezelStyle = NSBezelStyleAccessoryBarAction;
152− else if ([bezelStyle isEqualToString:@"borderless"]) {
153− _button.bordered = NO;
154− RCTBorderlessButtonCell *cell = [[RCTBorderlessButtonCell alloc] initTextCell:_button.title];
155− cell.bordered = NO;
156− cell.target = self;
157− cell.action = @selector(handlePress);
158− _button.cell = cell;
159− _borderlessHover = YES;
160− NSTrackingArea *ta = [[NSTrackingArea alloc]
161− initWithRect:NSZeroRect
162− options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect
163− owner:self
164− userInfo:nil];
165− [self addTrackingArea:ta];
166− }
16781 else _button.bezelStyle = NSBezelStylePush;
16882}
16983
@@ -187,7 +101,6 @@
187101- (void)layout {
188102 [super layout];
189103 _button.frame = self.bounds;
190−
191104}
192105@end
193106
@@ -201,8 +114,7 @@ RCT_EXPORT_VIEW_PROPERTY(bezelStyle, NSString)
201114RCT_EXPORT_VIEW_PROPERTY(destructive, BOOL)
202115RCT_EXPORT_VIEW_PROPERTY(primary, BOOL)
203116RCT_EXPORT_VIEW_PROPERTY(buttonEnabled, BOOL)
204−RCT_EXPORT_VIEW_PROPERTY(onPressButton, RCTDirectEventBlock)
205−RCT_EXPORT_VIEW_PROPERTY(onEmojiPick, RCTDirectEventBlock)
117+RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
206118@end
207119
208120// ============================================================
@@ -1831,14 +1743,7 @@ RCT_EXPORT_METHOD(show:(NSString *)style
18311743 }];
18321744 [task resume];
18331745 } else {
1834− NSString *filePath = source;
1835− if (![source hasPrefix:@"/"]) {
1836− NSString *ext = [source pathExtension];
1837− NSString *name = [source stringByDeletingPathExtension];
1838− NSString *bundlePath = [[NSBundle mainBundle] pathForResource:name ofType:ext];
1839− if (bundlePath) filePath = bundlePath;
1840− }
1841− _image = [[NSImage alloc] initByReferencingFile:filePath];
1746+ _image = [[NSImage alloc] initByReferencingFile:source];
18421747 [self setupAnimation];
18431748 [self updateLayerContents];
18441749 }
@@ -2510,24 +2415,14 @@ RCT_EXPORT_VIEW_PROPERTY(radius, CGFloat)
25102415// MARK: - NSPopover
25112416// ===========================================================================
25122417
2513−@interface RCTFlippedPopoverContentView : NSView
2514−@end
2515−@implementation RCTFlippedPopoverContentView
2516−- (BOOL)isFlipped { return YES; }
2517−@end
2518−
25192418@interface RCTPopoverView : NSView <NSPopoverDelegate>
25202419@property (nonatomic, strong) NSPopover *popover;
25212420@property (nonatomic, strong) NSViewController *popoverVC;
25222421@property (nonatomic, strong) NSView *contentContainer;
2523−
25242422@property (nonatomic, strong) NSMutableArray<NSView *> *reactChildren;
25252423@property (nonatomic, assign) BOOL visible;
25262424@property (nonatomic, copy) NSString *preferredEdge;
25272425@property (nonatomic, copy) NSString *behavior;
2528−@property (nonatomic, assign) CGFloat popoverWidth;
2529−@property (nonatomic, assign) CGFloat popoverHeight;
2530−@property (nonatomic, assign) CGFloat popoverPadding;
25312426@property (nonatomic, copy) RCTBubblingEventBlock onClose;
25322427@end
25332428
@@ -2536,7 +2431,7 @@ RCT_EXPORT_VIEW_PROPERTY(radius, CGFloat)
25362431- (instancetype)initWithFrame:(NSRect)frame {
25372432 if (self = [super initWithFrame:frame]) {
25382433 _reactChildren = [NSMutableArray new];
2539− _contentContainer = [[NSView alloc] initWithFrame:NSZeroRect];
2434+ _contentContainer = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 200, 200)];
25402435 _popoverVC = [[NSViewController alloc] init];
25412436 _popoverVC.view = _contentContainer;
25422437
@@ -2546,7 +2441,6 @@ RCT_EXPORT_VIEW_PROPERTY(radius, CGFloat)
25462441 _popover.animates = YES;
25472442 _popover.delegate = self;
25482443 _preferredEdge = @"bottom";
2549−
25502444 }
25512445 return self;
25522446}
@@ -2555,30 +2449,10 @@ RCT_EXPORT_VIEW_PROPERTY(radius, CGFloat)
25552449 _visible = visible;
25562450 if (visible) {
25572451 if (!_popover.isShown && self.window) {
2558− // Reset children to origin before measuring (centerContent may have shifted them)
2559− for (NSView *child in _contentContainer.subviews) {
2560− CGRect cf = child.frame;
2561− child.frame = CGRectMake(cf.origin.x, 0, cf.size.width, cf.size.height);
2562− }
2563− // Measure RN content that Yoga has already laid out
2564− CGFloat w = _popoverWidth;
2565− CGFloat h = _popoverHeight;
2566− if (w <= 0 || h <= 0) {
2567− CGRect contentRect = CGRectZero;
2568− for (NSView *child in _contentContainer.subviews) {
2569− contentRect = CGRectUnion(contentRect, child.frame);
2570− }
2571− if (w <= 0) w = contentRect.size.width;
2572− if (h <= 0) h = contentRect.size.height - _popoverPadding;
2573− }
2574− if (w < 20) w = 20;
2575− if (h < 10) h = 10;
2576− _popover.contentSize = NSMakeSize(w, h);
25772452 NSRectEdge edge = [self edgeFromString:_preferredEdge];
2453+ // Anchor to first child (the button) if present, else self
25782454 NSView *anchor = _reactChildren.count > 0 ? _reactChildren[0] : self;
25792455 [_popover showRelativeToRect:anchor.bounds ofView:anchor preferredEdge:edge];
2580− // Center content vertically after the container is sized
2581− [self performSelector:@selector(centerContent) withObject:nil afterDelay:0.01];
25822456 }
25832457 } else {
25842458 if (_popover.isShown) [_popover close];
@@ -2621,33 +2495,22 @@ RCT_EXPORT_VIEW_PROPERTY(radius, CGFloat)
26212495 return [_reactChildren copy];
26222496}
26232497
2624−- (void)setPopoverWidth:(CGFloat)popoverWidth {
2625− _popoverWidth = popoverWidth;
2626− [self updatePopoverSize];
2627−}
2628−
2629−- (void)setPopoverHeight:(CGFloat)popoverHeight {
2630− _popoverHeight = popoverHeight;
2631− [self updatePopoverSize];
2632−}
2633−
2634−- (void)updatePopoverSize {
2635− if (_popoverWidth > 0 && _popoverHeight > 0) {
2636− _popover.contentSize = NSMakeSize(_popoverWidth, _popoverHeight);
2637− }
2498+- (void)didUpdateReactSubviews {
2499+ [self performSelector:@selector(layoutPopoverContent) withObject:nil afterDelay:0];
26382500}
26392501
2640−- (void)centerContent {
2641− CGFloat containerH = _contentContainer.bounds.size.height;
2502+- (void)layoutPopoverContent {
2503+ CGFloat maxW = 0, maxH = 0;
26422504 for (NSView *child in _contentContainer.subviews) {
2643− CGRect cf = child.frame;
2644− CGFloat y = (containerH - cf.size.height) / 2.0;
2645− if (y < 0) y = 0;
2646− child.frame = CGRectMake(cf.origin.x, y, cf.size.width, cf.size.height);
2505+ CGFloat r = CGRectGetMaxX(child.frame);
2506+ CGFloat b = CGRectGetMaxY(child.frame);
2507+ if (r > maxW) maxW = r;
2508+ if (b > maxH) maxH = b;
2509+ }
2510+ if (maxW > 0 && maxH > 0) {
2511+ _popover.contentSize = NSMakeSize(maxW, maxH);
2512+ _contentContainer.frame = NSMakeRect(0, 0, maxW, maxH);
26472513 }
2648−}
2649−
2650−- (void)didUpdateReactSubviews {
26512514}
26522515
26532516- (void)popoverDidClose:(NSNotification *)notification {
@@ -2665,9 +2528,6 @@ RCT_EXPORT_MODULE(MacOSPopover)
26652528RCT_EXPORT_VIEW_PROPERTY(visible, BOOL)
26662529RCT_EXPORT_VIEW_PROPERTY(preferredEdge, NSString)
26672530RCT_EXPORT_VIEW_PROPERTY(behavior, NSString)
2668−RCT_EXPORT_VIEW_PROPERTY(popoverWidth, CGFloat)
2669−RCT_EXPORT_VIEW_PROPERTY(popoverHeight, CGFloat)
2670−RCT_EXPORT_VIEW_PROPERTY(popoverPadding, CGFloat)
26712531RCT_EXPORT_VIEW_PROPERTY(onClose, RCTBubblingEventBlock)
26722532@end
26732533
@@ -4471,377 +4331,3 @@ RCT_EXPORT_MODULE(MacOSCameraView)
44714331- (NSView *)view { return [[RCTCameraView alloc] initWithFrame:CGRectZero]; }
44724332RCT_EXPORT_VIEW_PROPERTY(active, BOOL)
44734333@end
4474−
4475−// ============================================================
4476−// Rich Text Label — read-only NSTextView with inline emoji images
4477−// ============================================================
4478−
4479−// Holds pre-rendered frames for one animated GIF
4480−@interface RCTGifAnimation : NSObject
4481−@property (nonatomic, strong) NSArray<id> *cgFrames; // array of CGImageRef (bridged as id)
4482−@property (nonatomic, strong) CALayer *layer;
4483−@property (nonatomic, assign) NSUInteger charIndex; // position in attributed string
4484−@property (nonatomic, assign) NSUInteger currentFrame;
4485−@end
4486−
4487−@implementation RCTGifAnimation
4488−@end
4489−
4490−@interface RCTNativeRichTextLabelView : NSView
4491−@property (nonatomic, strong) NSTextStorage *textStorage;
4492−@property (nonatomic, strong) NSLayoutManager *layoutManager;
4493−@property (nonatomic, strong) NSTextContainer *textContainer;
4494−@property (nonatomic, copy) NSString *text;
4495−@property (nonatomic, copy) NSDictionary *emojiMap;
4496−@property (nonatomic, copy) NSString *textColor;
4497−@property (nonatomic, assign) CGFloat fontSize;
4498−@property (nonatomic, assign) CGFloat emojiSize;
4499−@property (nonatomic, strong) NSTimer *gifTimer;
4500−@property (nonatomic, strong) NSMutableArray<RCTGifAnimation *> *gifAnimations;
4501−@property (nonatomic, weak) RCTBridge *bridge;
4502−@property (nonatomic, assign) CGSize measuredContentSize;
4503−@end
4504−
4505−@implementation RCTNativeRichTextLabelView
4506−
4507−- (BOOL)isFlipped { return YES; }
4508−
4509−- (instancetype)initWithFrame:(NSRect)frame {
4510− if (self = [super initWithFrame:frame]) {
4511− _fontSize = 14.0;
4512− _emojiSize = 0;
4513− _textColor = @"#FFFFFF";
4514−
4515− _textStorage = [[NSTextStorage alloc] init];
4516− _layoutManager = [[NSLayoutManager alloc] init];
4517− _textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(frame.size.width, FLT_MAX)];
4518− _textContainer.lineFragmentPadding = 0;
4519− [_layoutManager addTextContainer:_textContainer];
4520− [_textStorage addLayoutManager:_layoutManager];
4521−
4522− // Layer-back self for GIF animation sublayers only
4523− self.wantsLayer = YES;
4524− self.layer.contentsScale = [[NSScreen mainScreen] backingScaleFactor] ?: 2.0;
4525− }
4526− return self;
4527−}
4528−
4529−- (void)dealloc {
4530− [_gifTimer invalidate];
4531−}
4532−
4533−- (void)drawRect:(NSRect)dirtyRect {
4534− NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
4535− [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:NSZeroPoint];
4536− [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:NSZeroPoint];
4537−}
4538−
4539−- (void)stopGifAnimation {
4540− [_gifTimer invalidate];
4541− _gifTimer = nil;
4542− for (RCTGifAnimation *anim in _gifAnimations) {
4543− [anim.layer removeFromSuperlayer];
4544− }
4545− _gifAnimations = nil;
4546−}
4547−
4548−- (void)startGifAnimationIfNeeded {
4549− if (_gifAnimations.count == 0) return;
4550− if (_gifTimer) return;
4551− [self positionGifLayers];
4552− _gifTimer = [NSTimer scheduledTimerWithTimeInterval:0.06
4553− target:self
4554− selector:@selector(advanceGifFrames)
4555− userInfo:nil
4556− repeats:YES];
4557−}
4558−
4559−- (void)positionGifLayers {
4560− CGFloat viewHeight = self.frame.size.height;
4561− CGFloat es = _emojiSize > 0 ? _emojiSize : (_fontSize > 0 ? _fontSize : 14.0) * 1.3;
4562−
4563− for (RCTGifAnimation *anim in _gifAnimations) {
4564− NSUInteger charIdx = anim.charIndex;
4565− if (charIdx >= _textStorage.length) continue;
4566− NSRange glyphRange = [_layoutManager glyphRangeForCharacterRange:NSMakeRange(charIdx, 1) actualCharacterRange:NULL];
4567− NSRect glyphRect = [_layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textContainer];
4568− // Flip Y for CALayer (CALayer origin is bottom-left, view is flipped)
4569− CGFloat layerY = viewHeight - glyphRect.origin.y - es;
4570− anim.layer.frame = CGRectMake(glyphRect.origin.x, layerY, es, es);
4571− }
4572−}
4573−
4574−- (void)advanceGifFrames {
4575− for (RCTGifAnimation *anim in _gifAnimations) {
4576− anim.currentFrame = (anim.currentFrame + 1) % anim.cgFrames.count;
4577− anim.layer.contents = anim.cgFrames[anim.currentFrame];
4578− }
4579−}
4580−
4581−- (void)setText:(NSString *)text {
4582− _text = text;
4583− [self rebuildAttributedString];
4584−}
4585−
4586−- (void)setEmojiMap:(NSDictionary *)emojiMap {
4587− _emojiMap = emojiMap;
4588− [self rebuildAttributedString];
4589−}
4590−
4591−- (void)setTextColor:(NSString *)textColor {
4592− _textColor = textColor;
4593− [self rebuildAttributedString];
4594−}
4595−
4596−- (void)setFontSize:(CGFloat)fontSize {
4597− _fontSize = fontSize;
4598− [self rebuildAttributedString];
4599−}
4600−
4601−- (void)setEmojiSize:(CGFloat)emojiSize {
4602− _emojiSize = emojiSize;
4603− [self rebuildAttributedString];
4604−}
4605−
4606−- (NSColor *)parsedTextColor {
4607− NSString *hex = _textColor ?: @"#FFFFFF";
4608− if ([hex hasPrefix:@"#"]) hex = [hex substringFromIndex:1];
4609− unsigned int rgb = 0;
4610− [[NSScanner scannerWithString:hex] scanHexInt:&rgb];
4611− return [NSColor colorWithSRGBRed:((rgb >> 16) & 0xFF) / 255.0
4612− green:((rgb >> 8) & 0xFF) / 255.0
4613− blue:(rgb & 0xFF) / 255.0
4614− alpha:1.0];
4615−}
4616−
4617−- (void)rebuildAttributedString {
4618− if (!_text) return;
4619−
4620− [self stopGifAnimation];
4621− _gifAnimations = [NSMutableArray new];
4622−
4623− CGFloat fs = _fontSize > 0 ? _fontSize : 14.0;
4624− NSFont *font = [NSFont systemFontOfSize:fs];
4625− CGFloat es = _emojiSize > 0 ? _emojiSize : (font.ascender - font.descender);
4626− NSColor *color = [self parsedTextColor];
4627− NSDictionary *textAttrs = @{
4628− NSFontAttributeName: font,
4629− NSForegroundColorAttributeName: color,
4630− };
4631−
4632− NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init];
4633−
4634− NSRegularExpression *regex = [NSRegularExpression
4635− regularExpressionWithPattern:@":([a-zA-Z0-9_]+):"
4636− options:0 error:nil];
4637−
4638− NSUInteger pos = 0;
4639− NSArray *matches = [regex matchesInString:_text options:0 range:NSMakeRange(0, _text.length)];
4640−
4641− for (NSTextCheckingResult *match in matches) {
4642− NSRange fullRange = [match range];
4643− if (fullRange.location > pos) {
4644− NSString *before = [_text substringWithRange:NSMakeRange(pos, fullRange.location - pos)];
4645− [result appendAttributedString:[[NSAttributedString alloc] initWithString:before attributes:textAttrs]];
4646− }
4647−
4648− NSString *emojiName = [_text substringWithRange:[match rangeAtIndex:1]];
4649− NSString *imagePath = _emojiMap[emojiName];
4650− NSImage *image = nil;
4651− NSArray *gifFrames = nil;
4652− NSInteger gifFrameCount = 0;
4653−
4654− if (imagePath) {
4655− NSString *bundlePath = nil;
4656− if ([imagePath hasPrefix:@"/"]) {
4657− bundlePath = imagePath;
4658− } else if ([imagePath hasPrefix:@"http"]) {
4659− NSURL *url = [NSURL URLWithString:imagePath];
4660− image = [[NSImage alloc] initWithContentsOfURL:url];
4661− } else {
4662− bundlePath = [[NSBundle mainBundle] pathForResource:[imagePath stringByDeletingPathExtension]
4663− ofType:[imagePath pathExtension]];
4664− }
4665−
4666− if (bundlePath) {
4667− if ([[imagePath pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
4668− NSData *data = [NSData dataWithContentsOfFile:bundlePath];
4669− if (data) {
4670− NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithData:data];
4671− NSNumber *frameCountNum = [rep valueForProperty:NSImageFrameCount];
4672− if (frameCountNum && [frameCountNum integerValue] > 1) {
4673− gifFrameCount = [frameCountNum integerValue];
4674− NSMutableArray *frames = [NSMutableArray arrayWithCapacity:gifFrameCount];
4675− for (NSInteger f = 0; f < gifFrameCount; f++) {
4676− [rep setProperty:NSImageCurrentFrame withValue:@(f)];
4677− CGImageRef cgImg = [rep CGImageForProposedRect:NULL context:nil hints:nil];
4678− if (cgImg) [frames addObject:(__bridge id)cgImg];
4679− }
4680− if (frames.count > 0) {
4681− gifFrames = frames;
4682− image = [[NSImage alloc] initWithSize:NSMakeSize(es, es)];
4683− }
4684− } else {
4685− image = [[NSImage alloc] initWithContentsOfFile:bundlePath];
4686− }
4687− }
4688− } else {
4689− image = [[NSImage alloc] initWithContentsOfFile:bundlePath];
4690− }
4691− }
4692− }
4693−
4694− if (image) {
4695− NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
4696− CGFloat yOffset = font.descender;
4697− attachment.bounds = CGRectMake(0, yOffset, es, es);
4698− attachment.image = image;
4699− NSAttributedString *emojiStr = [NSAttributedString attributedStringWithAttachment:attachment];
4700− [result appendAttributedString:emojiStr];
4701−
4702− if (gifFrames && gifFrames.count > 1) {
4703− RCTGifAnimation *anim = [[RCTGifAnimation alloc] init];
4704− anim.cgFrames = gifFrames;
4705− anim.charIndex = result.length - 1;
4706− anim.currentFrame = 0;
4707− anim.layer = [CALayer layer];
4708− anim.layer.contentsGravity = kCAGravityResizeAspect;
4709− anim.layer.contents = gifFrames[0];
4710− [self.layer addSublayer:anim.layer];
4711− [_gifAnimations addObject:anim];
4712− }
4713− } else {
4714− NSString *fallback = [_text substringWithRange:fullRange];
4715− [result appendAttributedString:[[NSAttributedString alloc] initWithString:fallback attributes:textAttrs]];
4716− }
4717−
4718− pos = NSMaxRange(fullRange);
4719− }
4720−
4721− if (pos < _text.length) {
4722− NSString *tail = [_text substringFromIndex:pos];
4723− [result appendAttributedString:[[NSAttributedString alloc] initWithString:tail attributes:textAttrs]];
4724− }
4725−
4726− [_textStorage setAttributedString:result];
4727− [self startGifAnimationIfNeeded];
4728− [self setNeedsDisplay:YES];
4729−
4730− // Measure the content size using a temp layout manager (unbounded width)
4731− NSTextContainer *tempContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
4732− tempContainer.lineFragmentPadding = 0;
4733− NSLayoutManager *tempLayoutManager = [[NSLayoutManager alloc] init];
4734− [tempLayoutManager addTextContainer:tempContainer];
4735− NSTextStorage *tempStorage = [[NSTextStorage alloc] initWithAttributedString:result];
4736− [tempStorage addLayoutManager:tempLayoutManager];
4737− [tempLayoutManager ensureLayoutForTextContainer:tempContainer];
4738− NSRect usedRect = [tempLayoutManager usedRectForTextContainer:tempContainer];
4739− _measuredContentSize = CGSizeMake(ceil(usedRect.size.width), ceil(usedRect.size.height));
4740− NSLog(@"[RichTextLabel] rebuildAttributedString: text=%@ measuredSize=%@", _text, NSStringFromSize(_measuredContentSize));
4741−}
4742−
4743−- (void)didSetProps:(NSArray<NSString *> *)changedProps
4744−{
4745− if (_bridge && !CGSizeEqualToSize(_measuredContentSize, CGSizeZero)) {
4746− CGSize size = _measuredContentSize;
4747− RCTUIManager *uiManager = [_bridge moduleForName:@"UIManager"];
4748− NSLog(@"[RichTextLabel] setIntrinsicContentSize:%@ tag=%@ uiManager=%@ responds=%d", NSStringFromSize(size), self.reactTag, uiManager, [uiManager respondsToSelector:@selector(setIntrinsicContentSize:forView:)]);
4749− [uiManager setIntrinsicContentSize:size forView:self];
4750− }
4751−}
4752−
4753−- (NSSize)intrinsicContentSize {
4754− return NSMakeSize(NSViewNoIntrinsicMetric, NSViewNoIntrinsicMetric);
4755−}
4756−
4757−
4758−- (void)reactSetFrame:(CGRect)frame {
4759− [super reactSetFrame:frame];
4760− _textContainer.containerSize = NSMakeSize(frame.size.width, FLT_MAX);
4761− [_layoutManager ensureLayoutForTextContainer:_textContainer];
4762− [self positionGifLayers];
4763− [self setNeedsDisplay:YES];
4764−}
4765−
4766−- (void)layout {
4767− [super layout];
4768− _textContainer.containerSize = NSMakeSize(self.bounds.size.width, FLT_MAX);
4769− [_layoutManager ensureLayoutForTextContainer:_textContainer];
4770− [self positionGifLayers];
4771− [self setNeedsDisplay:YES];
4772−}
4773−
4774−@end
4775−
4776−@interface RCTNativeRichTextLabelViewManager : RCTViewManager @end
4777−@implementation RCTNativeRichTextLabelViewManager
4778−RCT_EXPORT_MODULE(NativeRichTextLabel)
4779−- (NSView *)view {
4780− RCTNativeRichTextLabelView *view = [[RCTNativeRichTextLabelView alloc] initWithFrame:CGRectZero];
4781− view.bridge = self.bridge;
4782− return view;
4783−}
4784−RCT_EXPORT_VIEW_PROPERTY(text, NSString)
4785−RCT_EXPORT_VIEW_PROPERTY(emojiMap, NSDictionary)
4786−RCT_EXPORT_VIEW_PROPERTY(textColor, NSString)
4787−RCT_EXPORT_VIEW_PROPERTY(fontSize, CGFloat)
4788−RCT_EXPORT_VIEW_PROPERTY(emojiSize, CGFloat)
4789−@end
4790−
4791−// ============================================================
4792−// Hover View — container with NSTrackingArea for hover detection
4793−// ============================================================
4794−
4795−@interface RCTHoverView : NSView
4796−@property (nonatomic, copy) RCTBubblingEventBlock onHoverChange;
4797−@property (nonatomic, copy) RCTBubblingEventBlock onPress;
4798−@property (nonatomic, strong) NSTrackingArea *trackingArea;
4799−@end
4800−
4801−@implementation RCTHoverView
4802−
4803−- (instancetype)initWithFrame:(NSRect)frame {
4804− if (self = [super initWithFrame:frame]) {
4805− [self setupTracking];
4806− }
4807− return self;
4808−}
4809−
4810−- (void)setupTracking {
4811− if (_trackingArea) [self removeTrackingArea:_trackingArea];
4812− _trackingArea = [[NSTrackingArea alloc]
4813− initWithRect:self.bounds
4814− options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect)
4815− owner:self
4816− userInfo:nil];
4817− [self addTrackingArea:_trackingArea];
4818−}
4819−
4820−- (void)updateTrackingAreas {
4821− [super updateTrackingAreas];
4822− [self setupTracking];
4823−}
4824−
4825−- (void)mouseEntered:(NSEvent *)event {
4826− if (_onHoverChange) _onHoverChange(@{@"hovered": @(YES)});
4827−}
4828−
4829−- (void)mouseExited:(NSEvent *)event {
4830− if (_onHoverChange) _onHoverChange(@{@"hovered": @(NO)});
4831−}
4832−
4833−- (void)mouseDown:(NSEvent *)event {
4834− if (_onPress) _onPress(@{});
4835− else [super mouseDown:event];
4836−}
4837−
4838−- (NSSize)intrinsicContentSize { return NSMakeSize(NSViewNoIntrinsicMetric, NSViewNoIntrinsicMetric); }
4839−@end
4840−
4841−@interface RCTHoverViewManager : RCTViewManager @end
4842−@implementation RCTHoverViewManager
4843−RCT_EXPORT_MODULE(MacOSHoverView)
4844−- (NSView *)view { return [[RCTHoverView alloc] initWithFrame:CGRectZero]; }
4845−RCT_EXPORT_VIEW_PROPERTY(onHoverChange, RCTBubblingEventBlock)
4846−RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
4847−@end
example-macos/macos/YogaReactExample-macOS/YogaReactExample.entitlements+1−1
@@ -3,7 +3,7 @@
33<plist version="1.0">
44<dict>
55 <key>com.apple.security.app-sandbox</key>
6− <false/>
6+ <true/>
77 <key>com.apple.security.files.user-selected.read-only</key>
88 <true/>
99 <key>com.apple.security.network.client</key>
example-macos/macos/YogaReactExample-macOS/emoji/congaparrot.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/cooldoge.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/dealwithit.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/fastparrot.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/haskell.pngdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/opensourceparrot.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/parrot.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/party.pngdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/partyblob.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/purescript.pngdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/shipit.pngdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/typingcat.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample-macOS/emoji/ultraparrot.gifdeleted

Binary file not shown.

example-macos/macos/YogaReactExample.xcodeproj/project.pbxproj+4−104
@@ -16,28 +16,14 @@
1616 AA0000032437B4B30078DB4F /* RCTFinderViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0000022437B4B30078DB4F /* RCTFinderViewManager.m */; };
1717 AA0000052437B4B30078DB4F /* RCTNativeShowcaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0000042437B4B30078DB4F /* RCTNativeShowcaseManager.m */; };
1818 AA0000072437B4B30078DB4F /* RCTMacOSControlsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0000062437B4B30078DB4F /* RCTMacOSControlsManager.m */; };
19+ BE70DEDEE32B54947FC49AFF /* libPods-YogaReactExample-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3E14041CD325948C5922BE0 /* libPods-YogaReactExample-macOS.a */; };
20+ DEB487CCA36791EC34548205 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 2F264B867F71A9A4C58140DE /* PrivacyInfo.xcprivacy */; };
1921 BB0000012437B4B30078DB4F /* windy_tree.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB0000002437B4B30078DB4F /* windy_tree.riv */; };
2022 BB0000032437B4B30078DB4F /* light_switch.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB0000022437B4B30078DB4F /* light_switch.riv */; };
2123 BB0000052437B4B30078DB4F /* rating_animation.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB0000042437B4B30078DB4F /* rating_animation.riv */; };
2224 BB0000072437B4B30078DB4F /* switch_event_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB0000062437B4B30078DB4F /* switch_event_example.riv */; };
2325 BB0000092437B4B30078DB4F /* cat_following_mouse.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB0000082437B4B30078DB4F /* cat_following_mouse.riv */; };
2426 BB00000B2437B4B30078DB4F /* cursor_tracking.riv in Resources */ = {isa = PBXBuildFile; fileRef = BB00000A2437B4B30078DB4F /* cursor_tracking.riv */; };
25− BE70DEDEE32B54947FC49AFF /* libPods-YogaReactExample-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C3E14041CD325948C5922BE0 /* libPods-YogaReactExample-macOS.a */; };
26− CC0000012437B4B30078DB4F /* purescript.png in Resources */ = {isa = PBXBuildFile; fileRef = CC0000002437B4B30078DB4F /* purescript.png */; };
27− CC0000032437B4B30078DB4F /* haskell.png in Resources */ = {isa = PBXBuildFile; fileRef = CC0000022437B4B30078DB4F /* haskell.png */; };
28− CC0000052437B4B30078DB4F /* shipit.png in Resources */ = {isa = PBXBuildFile; fileRef = CC0000042437B4B30078DB4F /* shipit.png */; };
29− CC0000072437B4B30078DB4F /* party.png in Resources */ = {isa = PBXBuildFile; fileRef = CC0000062437B4B30078DB4F /* party.png */; };
30− CC0000092437B4B30078DB4F /* parrot.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC00000A2437B4B30078DB4F /* parrot.gif */; };
31− CC00000B2437B4B30078DB4F /* congaparrot.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC00000C2437B4B30078DB4F /* congaparrot.gif */; };
32− CC00000D2437B4B30078DB4F /* cooldoge.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC00000E2437B4B30078DB4F /* cooldoge.gif */; };
33− CC00000F2437B4B30078DB4F /* dealwithit.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC0000102437B4B30078DB4F /* dealwithit.gif */; };
34− CC0000112437B4B30078DB4F /* fastparrot.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC0000122437B4B30078DB4F /* fastparrot.gif */; };
35− CC0000132437B4B30078DB4F /* opensourceparrot.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC0000142437B4B30078DB4F /* opensourceparrot.gif */; };
36− CC0000152437B4B30078DB4F /* partyblob.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC0000162437B4B30078DB4F /* partyblob.gif */; };
37− CC0000172437B4B30078DB4F /* typingcat.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC0000182437B4B30078DB4F /* typingcat.gif */; };
38− CC0000192437B4B30078DB4F /* ultraparrot.gif in Resources */ = {isa = PBXBuildFile; fileRef = CC00001A2437B4B30078DB4F /* ultraparrot.gif */; };
39− DD0000012437B4B30078DB4F /* RCTGhosttyTerminalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0000002437B4B30078DB4F /* RCTGhosttyTerminalManager.m */; };
40− DEB487CCA36791EC34548205 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 2F264B867F71A9A4C58140DE /* PrivacyInfo.xcprivacy */; };
4127/* End PBXBuildFile section */
4228
4329/* Begin PBXFileReference section */
@@ -57,28 +43,14 @@
5743 AA0000022437B4B30078DB4F /* RCTFinderViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTFinderViewManager.m; sourceTree = "<group>"; };
5844 AA0000042437B4B30078DB4F /* RCTNativeShowcaseManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTNativeShowcaseManager.m; sourceTree = "<group>"; };
5945 AA0000062437B4B30078DB4F /* RCTMacOSControlsManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTMacOSControlsManager.m; sourceTree = "<group>"; };
46+ C3E14041CD325948C5922BE0 /* libPods-YogaReactExample-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-YogaReactExample-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
47+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
6048 BB0000002437B4B30078DB4F /* windy_tree.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = windy_tree.riv; sourceTree = "<group>"; };
6149 BB0000022437B4B30078DB4F /* light_switch.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = light_switch.riv; sourceTree = "<group>"; };
6250 BB0000042437B4B30078DB4F /* rating_animation.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = rating_animation.riv; sourceTree = "<group>"; };
6351 BB0000062437B4B30078DB4F /* switch_event_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = switch_event_example.riv; sourceTree = "<group>"; };
6452 BB0000082437B4B30078DB4F /* cat_following_mouse.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = cat_following_mouse.riv; sourceTree = "<group>"; };
6553 BB00000A2437B4B30078DB4F /* cursor_tracking.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = cursor_tracking.riv; sourceTree = "<group>"; };
66− C3E14041CD325948C5922BE0 /* libPods-YogaReactExample-macOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-YogaReactExample-macOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
67− CC0000002437B4B30078DB4F /* purescript.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = purescript.png; sourceTree = "<group>"; };
68− CC0000022437B4B30078DB4F /* haskell.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = haskell.png; sourceTree = "<group>"; };
69− CC0000042437B4B30078DB4F /* shipit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = shipit.png; sourceTree = "<group>"; };
70− CC0000062437B4B30078DB4F /* party.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = party.png; sourceTree = "<group>"; };
71− CC00000A2437B4B30078DB4F /* parrot.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = parrot.gif; sourceTree = "<group>"; };
72− CC00000C2437B4B30078DB4F /* congaparrot.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = congaparrot.gif; sourceTree = "<group>"; };
73− CC00000E2437B4B30078DB4F /* cooldoge.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = cooldoge.gif; sourceTree = "<group>"; };
74− CC0000102437B4B30078DB4F /* dealwithit.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = dealwithit.gif; sourceTree = "<group>"; };
75− CC0000122437B4B30078DB4F /* fastparrot.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = fastparrot.gif; sourceTree = "<group>"; };
76− CC0000142437B4B30078DB4F /* opensourceparrot.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = opensourceparrot.gif; sourceTree = "<group>"; };
77− CC0000162437B4B30078DB4F /* partyblob.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = partyblob.gif; sourceTree = "<group>"; };
78− CC0000182437B4B30078DB4F /* typingcat.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = typingcat.gif; sourceTree = "<group>"; };
79− CC00001A2437B4B30078DB4F /* ultraparrot.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = ultraparrot.gif; sourceTree = "<group>"; };
80− DD0000002437B4B30078DB4F /* RCTGhosttyTerminalManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGhosttyTerminalManager.m; sourceTree = "<group>"; };
81− ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
8254/* End PBXFileReference section */
8355
8456/* Begin PBXFrameworksBuildPhase section */
@@ -123,14 +95,12 @@
12395 AA0000022437B4B30078DB4F /* RCTFinderViewManager.m */,
12496 AA0000042437B4B30078DB4F /* RCTNativeShowcaseManager.m */,
12597 AA0000062437B4B30078DB4F /* RCTMacOSControlsManager.m */,
126− DD0000002437B4B30078DB4F /* RCTGhosttyTerminalManager.m */,
12798 BB0000002437B4B30078DB4F /* windy_tree.riv */,
12899 BB0000022437B4B30078DB4F /* light_switch.riv */,
129100 BB0000042437B4B30078DB4F /* rating_animation.riv */,
130101 BB0000062437B4B30078DB4F /* switch_event_example.riv */,
131102 BB0000082437B4B30078DB4F /* cat_following_mouse.riv */,
132103 BB00000A2437B4B30078DB4F /* cursor_tracking.riv */,
133− CC0000082437B4B30078DB4F /* emoji */,
134104 );
135105 path = "YogaReactExample-macOS";
136106 sourceTree = "<group>";
@@ -176,26 +146,6 @@
176146 name = Products;
177147 sourceTree = "<group>";
178148 };
179− CC0000082437B4B30078DB4F /* emoji */ = {
180− isa = PBXGroup;
181− children = (
182− CC0000002437B4B30078DB4F /* purescript.png */,
183− CC0000022437B4B30078DB4F /* haskell.png */,
184− CC0000042437B4B30078DB4F /* shipit.png */,
185− CC0000062437B4B30078DB4F /* party.png */,
186− CC00000A2437B4B30078DB4F /* parrot.gif */,
187− CC00000C2437B4B30078DB4F /* congaparrot.gif */,
188− CC00000E2437B4B30078DB4F /* cooldoge.gif */,
189− CC0000102437B4B30078DB4F /* dealwithit.gif */,
190− CC0000122437B4B30078DB4F /* fastparrot.gif */,
191− CC0000142437B4B30078DB4F /* opensourceparrot.gif */,
192− CC0000162437B4B30078DB4F /* partyblob.gif */,
193− CC0000182437B4B30078DB4F /* typingcat.gif */,
194− CC00001A2437B4B30078DB4F /* ultraparrot.gif */,
195− );
196− path = emoji;
197− sourceTree = "<group>";
198− };
199149/* End PBXGroup section */
200150
201151/* Begin PBXNativeTarget section */
@@ -296,19 +246,6 @@
296246 BB0000072437B4B30078DB4F /* switch_event_example.riv in Resources */,
297247 BB0000092437B4B30078DB4F /* cat_following_mouse.riv in Resources */,
298248 BB00000B2437B4B30078DB4F /* cursor_tracking.riv in Resources */,
299− CC0000012437B4B30078DB4F /* purescript.png in Resources */,
300− CC0000032437B4B30078DB4F /* haskell.png in Resources */,
301− CC0000052437B4B30078DB4F /* shipit.png in Resources */,
302− CC0000072437B4B30078DB4F /* party.png in Resources */,
303− CC0000092437B4B30078DB4F /* parrot.gif in Resources */,
304− CC00000B2437B4B30078DB4F /* congaparrot.gif in Resources */,
305− CC00000D2437B4B30078DB4F /* cooldoge.gif in Resources */,
306− CC00000F2437B4B30078DB4F /* dealwithit.gif in Resources */,
307− CC0000112437B4B30078DB4F /* fastparrot.gif in Resources */,
308− CC0000132437B4B30078DB4F /* opensourceparrot.gif in Resources */,
309− CC0000152437B4B30078DB4F /* partyblob.gif in Resources */,
310− CC0000172437B4B30078DB4F /* typingcat.gif in Resources */,
311− CC0000192437B4B30078DB4F /* ultraparrot.gif in Resources */,
312249 );
313250 runOnlyForDeploymentPostprocessing = 0;
314251 };
@@ -437,7 +374,6 @@
437374 AA0000032437B4B30078DB4F /* RCTFinderViewManager.m in Sources */,
438375 AA0000052437B4B30078DB4F /* RCTNativeShowcaseManager.m in Sources */,
439376 AA0000072437B4B30078DB4F /* RCTMacOSControlsManager.m in Sources */,
440− DD0000012437B4B30078DB4F /* RCTGhosttyTerminalManager.m in Sources */,
441377 );
442378 runOnlyForDeploymentPostprocessing = 0;
443379 };
@@ -506,31 +442,13 @@
506442 CODE_SIGN_ENTITLEMENTS = "YogaReactExample-macOS/YogaReactExample.entitlements";
507443 CURRENT_PROJECT_VERSION = 1;
508444 DEAD_CODE_STRIPPING = NO;
509− HEADER_SEARCH_PATHS = (
510− "$(inherited)",
511− "$(HOME)/code/ghostty/include",
512− );
513445 INFOPLIST_FILE = "YogaReactExample-macos/Info.plist";
514446 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
515− LIBRARY_SEARCH_PATHS = (
516− "$(SDKROOT)/usr/lib/swift",
517− "$(inherited)",
518− "$(HOME)/code/ghostty/zig-out/lib",
519− );
520447 MACOSX_DEPLOYMENT_TARGET = 14.0;
521448 OTHER_LDFLAGS = (
522449 "$(inherited)",
523450 "-ObjC",
524451 "-lc++",
525− "-lghostty",
526− "-framework Metal",
527− "-framework MetalKit",
528− "-framework CoreText",
529− "-framework CoreGraphics",
530− "-framework QuartzCore",
531− "-framework IOSurface",
532− "-framework Carbon",
533− "-framework GameController",
534452 );
535453 PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
536454 PRODUCT_NAME = YogaReactExample;
@@ -548,31 +466,13 @@
548466 CLANG_ENABLE_MODULES = YES;
549467 CODE_SIGN_ENTITLEMENTS = "YogaReactExample-macOS/YogaReactExample.entitlements";
550468 CURRENT_PROJECT_VERSION = 1;
551− HEADER_SEARCH_PATHS = (
552− "$(inherited)",
553− "$(HOME)/code/ghostty/include",
554− );
555469 INFOPLIST_FILE = "YogaReactExample-macos/Info.plist";
556470 LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
557− LIBRARY_SEARCH_PATHS = (
558− "$(SDKROOT)/usr/lib/swift",
559− "$(inherited)",
560− "$(HOME)/code/ghostty/zig-out/lib",
561− );
562471 MACOSX_DEPLOYMENT_TARGET = 14.0;
563472 OTHER_LDFLAGS = (
564473 "$(inherited)",
565474 "-ObjC",
566475 "-lc++",
567− "-lghostty",
568− "-framework Metal",
569− "-framework MetalKit",
570− "-framework CoreText",
571− "-framework CoreGraphics",
572− "-framework QuartzCore",
573− "-framework IOSurface",
574− "-framework Carbon",
575− "-framework GameController",
576476 );
577477 PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.$(PRODUCT_NAME:rfc1034identifier)";
578478 PRODUCT_NAME = YogaReactExample;
example-macos/macos/YogaReactExample.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plistdeleted−19
@@ -1,19 +0,0 @@
1−<?xml version="1.0" encoding="UTF-8"?>
2−<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3−<plist version="1.0">
4−<dict>
5− <key>SchemeUserState</key>
6− <dict>
7− <key>YogaReactExample-iOS.xcscheme_^#shared#^_</key>
8− <dict>
9− <key>orderHint</key>
10− <integer>84</integer>
11− </dict>
12− <key>YogaReactExample-macOS.xcscheme_^#shared#^_</key>
13− <dict>
14− <key>orderHint</key>
15− <integer>83</integer>
16− </dict>
17− </dict>
18−</dict>
19−</plist>
example-macos/macos/YogaReactExample.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstatedeleted

Binary file not shown.

example-macos/macos/build/generated/ios/rnreanimated/rnreanimated-generated.mmdeleted−63
@@ -1,63 +0,0 @@
1−/**
2− * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
3− *
4− * Do not edit this file as changes may cause incorrect behavior and will be lost
5− * once the code is regenerated.
6− *
7− * @generated by codegen project: GenerateModuleObjCpp
8− *
9− * We create an umbrella header (and corresponding implementation) here since
10− * Cxx compilation in BUCK has a limitation: source-code producing genrule()s
11− * must have a single output. More files => more genrule()s => slower builds.
12− */
13−
14−#import "rnreanimated.h"
15−
16−
17−@implementation NativeReanimatedModuleSpecBase
18−
19−
20−- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper
21−{
22− _eventEmitterCallback = std::move(eventEmitterCallbackWrapper->_eventEmitterCallback);
23−}
24−@end
25−
26−
27−namespace facebook::react {
28−
29− static facebook::jsi::Value __hostFunction_NativeReanimatedModuleSpecJSI_installTurboModule(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
30− return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, BooleanKind, "installTurboModule", @selector(installTurboModule), args, count);
31− }
32−
33− NativeReanimatedModuleSpecJSI::NativeReanimatedModuleSpecJSI(const ObjCTurboModule::InitParams &params)
34− : ObjCTurboModule(params) {
35−
36− methodMap_["installTurboModule"] = MethodMetadata {0, __hostFunction_NativeReanimatedModuleSpecJSI_installTurboModule};
37−
38− }
39−} // namespace facebook::react
40−
41−@implementation NativeWorkletsModuleSpecBase
42−
43−
44−- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper
45−{
46− _eventEmitterCallback = std::move(eventEmitterCallbackWrapper->_eventEmitterCallback);
47−}
48−@end
49−
50−
51−namespace facebook::react {
52−
53− static facebook::jsi::Value __hostFunction_NativeWorkletsModuleSpecJSI_installTurboModule(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
54− return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, BooleanKind, "installTurboModule", @selector(installTurboModule:), args, count);
55− }
56−
57− NativeWorkletsModuleSpecJSI::NativeWorkletsModuleSpecJSI(const ObjCTurboModule::InitParams &params)
58− : ObjCTurboModule(params) {
59−
60− methodMap_["installTurboModule"] = MethodMetadata {1, __hostFunction_NativeWorkletsModuleSpecJSI_installTurboModule};
61−
62− }
63−} // namespace facebook::react
example-macos/macos/build/generated/ios/rnreanimated/rnreanimated.hdeleted−88
@@ -1,88 +0,0 @@
1−/**
2− * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
3− *
4− * Do not edit this file as changes may cause incorrect behavior and will be lost
5− * once the code is regenerated.
6− *
7− * @generated by codegen project: GenerateModuleObjCpp
8− *
9− * We create an umbrella header (and corresponding implementation) here since
10− * Cxx compilation in BUCK has a limitation: source-code producing genrule()s
11− * must have a single output. More files => more genrule()s => slower builds.
12− */
13−
14−#ifndef __cplusplus
15−#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm.
16−#endif
17−
18−// Avoid multiple includes of rnreanimated symbols
19−#ifndef rnreanimated_H
20−#define rnreanimated_H
21−
22−#import <Foundation/Foundation.h>
23−#import <RCTRequired/RCTRequired.h>
24−#import <RCTTypeSafety/RCTConvertHelpers.h>
25−#import <RCTTypeSafety/RCTTypedModuleConstants.h>
26−#import <React/RCTBridgeModule.h>
27−#import <React/RCTCxxConvert.h>
28−#import <React/RCTManagedPointer.h>
29−#import <ReactCommon/RCTTurboModule.h>
30−#import <optional>
31−#import <vector>
32−
33−
34−NS_ASSUME_NONNULL_BEGIN
35−
36−@protocol NativeReanimatedModuleSpec <RCTBridgeModule, RCTTurboModule>
37−
38−- (NSNumber *)installTurboModule;
39−
40−@end
41−
42−@interface NativeReanimatedModuleSpecBase : NSObject {
43−@protected
44−facebook::react::EventEmitterCallback _eventEmitterCallback;
45−}
46−- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
47−
48−
49−@end
50−
51−namespace facebook::react {
52− /**
53− * ObjC++ class for module 'NativeReanimatedModule'
54− */
55− class JSI_EXPORT NativeReanimatedModuleSpecJSI : public ObjCTurboModule {
56− public:
57− NativeReanimatedModuleSpecJSI(const ObjCTurboModule::InitParams &params);
58− };
59−} // namespace facebook::react
60−
61−@protocol NativeWorkletsModuleSpec <RCTBridgeModule, RCTTurboModule>
62−
63−- (NSNumber *)installTurboModule:(NSString *)valueUnpackerCode;
64−
65−@end
66−
67−@interface NativeWorkletsModuleSpecBase : NSObject {
68−@protected
69−facebook::react::EventEmitterCallback _eventEmitterCallback;
70−}
71−- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
72−
73−
74−@end
75−
76−namespace facebook::react {
77− /**
78− * ObjC++ class for module 'NativeWorkletsModule'
79− */
80− class JSI_EXPORT NativeWorkletsModuleSpecJSI : public ObjCTurboModule {
81− public:
82− NativeWorkletsModuleSpecJSI(const ObjCTurboModule::InitParams &params);
83− };
84−} // namespace facebook::react
85−
86−
87−NS_ASSUME_NONNULL_END
88−#endif // rnreanimated_H
example-macos/macos/build/generated/ios/rnreanimatedJSI-generated.cppdeleted−37
@@ -1,37 +0,0 @@
1−/**
2− * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
3− *
4− * Do not edit this file as changes may cause incorrect behavior and will be lost
5− * once the code is regenerated.
6− *
7− * @generated by codegen project: GenerateModuleCpp.js
8− */
9−
10−#include "rnreanimatedJSI.h"
11−
12−namespace facebook::react {
13−
14−static jsi::Value __hostFunction_NativeReanimatedModuleCxxSpecJSI_installTurboModule(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
15− return static_cast<NativeReanimatedModuleCxxSpecJSI *>(&turboModule)->installTurboModule(
16− rt
17− );
18−}
19−
20−NativeReanimatedModuleCxxSpecJSI::NativeReanimatedModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
21− : TurboModule("ReanimatedModule", jsInvoker) {
22− methodMap_["installTurboModule"] = MethodMetadata {0, __hostFunction_NativeReanimatedModuleCxxSpecJSI_installTurboModule};
23−}
24−static jsi::Value __hostFunction_NativeWorkletsModuleCxxSpecJSI_installTurboModule(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
25− return static_cast<NativeWorkletsModuleCxxSpecJSI *>(&turboModule)->installTurboModule(
26− rt,
27− count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
28− );
29−}
30−
31−NativeWorkletsModuleCxxSpecJSI::NativeWorkletsModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
32− : TurboModule("WorkletsModule", jsInvoker) {
33− methodMap_["installTurboModule"] = MethodMetadata {1, __hostFunction_NativeWorkletsModuleCxxSpecJSI_installTurboModule};
34−}
35−
36−
37−} // namespace facebook::react
example-macos/macos/build/generated/ios/rnreanimatedJSI.hdeleted−125
@@ -1,125 +0,0 @@
1−/**
2− * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
3− *
4− * Do not edit this file as changes may cause incorrect behavior and will be lost
5− * once the code is regenerated.
6− *
7− * @generated by codegen project: GenerateModuleH.js
8− */
9−
10−#pragma once
11−
12−#include <ReactCommon/TurboModule.h>
13−#include <react/bridging/Bridging.h>
14−
15−namespace facebook::react {
16−
17−
18− class JSI_EXPORT NativeReanimatedModuleCxxSpecJSI : public TurboModule {
19−protected:
20− NativeReanimatedModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
21−
22−public:
23− virtual bool installTurboModule(jsi::Runtime &rt) = 0;
24−
25−};
26−
27−template <typename T>
28−class JSI_EXPORT NativeReanimatedModuleCxxSpec : public TurboModule {
29−public:
30− jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &propName) override {
31− return delegate_.create(rt, propName);
32− }
33−
34− std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& runtime) override {
35− return delegate_.getPropertyNames(runtime);
36− }
37−
38− static constexpr std::string_view kModuleName = "ReanimatedModule";
39−
40−protected:
41− NativeReanimatedModuleCxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
42− : TurboModule(std::string{NativeReanimatedModuleCxxSpec::kModuleName}, jsInvoker),
43− delegate_(reinterpret_cast<T*>(this), jsInvoker) {}
44−
45−
46−private:
47− class Delegate : public NativeReanimatedModuleCxxSpecJSI {
48− public:
49− Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) :
50− NativeReanimatedModuleCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {
51−
52− }
53−
54− bool installTurboModule(jsi::Runtime &rt) override {
55− static_assert(
56− bridging::getParameterCount(&T::installTurboModule) == 1,
57− "Expected installTurboModule(...) to have 1 parameters");
58−
59− return bridging::callFromJs<bool>(
60− rt, &T::installTurboModule, jsInvoker_, instance_);
61− }
62−
63− private:
64− friend class NativeReanimatedModuleCxxSpec;
65− T *instance_;
66− };
67−
68− Delegate delegate_;
69−};
70−
71−
72− class JSI_EXPORT NativeWorkletsModuleCxxSpecJSI : public TurboModule {
73−protected:
74− NativeWorkletsModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
75−
76−public:
77− virtual bool installTurboModule(jsi::Runtime &rt, jsi::String valueUnpackerCode) = 0;
78−
79−};
80−
81−template <typename T>
82−class JSI_EXPORT NativeWorkletsModuleCxxSpec : public TurboModule {
83−public:
84− jsi::Value create(jsi::Runtime &rt, const jsi::PropNameID &propName) override {
85− return delegate_.create(rt, propName);
86− }
87−
88− std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& runtime) override {
89− return delegate_.getPropertyNames(runtime);
90− }
91−
92− static constexpr std::string_view kModuleName = "WorkletsModule";
93−
94−protected:
95− NativeWorkletsModuleCxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
96− : TurboModule(std::string{NativeWorkletsModuleCxxSpec::kModuleName}, jsInvoker),
97− delegate_(reinterpret_cast<T*>(this), jsInvoker) {}
98−
99−
100−private:
101− class Delegate : public NativeWorkletsModuleCxxSpecJSI {
102− public:
103− Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) :
104− NativeWorkletsModuleCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {
105−
106− }
107−
108− bool installTurboModule(jsi::Runtime &rt, jsi::String valueUnpackerCode) override {
109− static_assert(
110− bridging::getParameterCount(&T::installTurboModule) == 2,
111− "Expected installTurboModule(...) to have 2 parameters");
112−
113− return bridging::callFromJs<bool>(
114− rt, &T::installTurboModule, jsInvoker_, instance_, std::move(valueUnpackerCode));
115− }
116−
117− private:
118− friend class NativeWorkletsModuleCxxSpec;
119− T *instance_;
120− };
121−
122− Delegate delegate_;
123−};
124−
125−} // namespace facebook::react
example-macos/metro.config.js+1−15
@@ -7,25 +7,11 @@ const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
77 *
88 * @type {import('@react-native/metro-config').MetroConfig}
99 */
10−const modules = path.resolve(__dirname, "node_modules");
1110const config = {
1211 watchFolders: [path.resolve(__dirname, "..", "output")],
1312 resolver: {
1413 unstable_enableSymlinks: true,
15− nodeModulesPaths: [modules],
16− resolveRequest: (context, moduleName, platform) => {
17− if (context.originModulePath.includes("/output/")) {
18− return context.resolveRequest(
19− {
20− ...context,
21− nodeModulesPaths: [modules, ...context.nodeModulesPaths],
22− },
23− moduleName,
24− platform
25− );
26− }
27− return context.resolveRequest(context, moduleName, platform);
28− },
14+ nodeModulesPaths: [path.resolve(__dirname, "node_modules")],
2915 },
3016};
3117
example-macos/package.json−1
@@ -16,7 +16,6 @@
1616 "react-native": "0.81.5",
1717 "react-native-fs": "^2.20.0",
1818 "react-native-macos": "0.81.2",
19− "react-native-reanimated": "3.19.5",
2019 "react-native-safe-area-context": "^5.5.2",
2120 "twrnc": "^4.16.0"
2221 },
example-macos/patches/fix-normalizeColor.sh−65
@@ -11,68 +11,3 @@ if [ -f "$FILE" ]; then
1111 sed -i '' "s|require('./normalizeColor')|require('./normalizeColor').default|g" "$FILE"
1212 echo "Patched $FILE: normalizeColor require fix applied"
1313fi
14−
15−# Fixes missing ReactDevToolsSettingsManager.macos.js — react-native ships
16−# ios/android variants but no macOS one. Stub with no-ops (can't use iOS
17−# version because it imports Settings which depends on Platform.OS).
18−DEVTOOLS_DIR="node_modules/react-native/src/private/devsupport/rndevtools"
19−DEVTOOLS_MACOS="$DEVTOOLS_DIR/ReactDevToolsSettingsManager.macos.js"
20−
21−if [ -d "$DEVTOOLS_DIR" ] && [ ! -f "$DEVTOOLS_MACOS" ]; then
22− cat > "$DEVTOOLS_MACOS" << 'STUB'
23−export function setGlobalHookSettings(settings) {}
24−export function getGlobalHookSettings() { return null; }
25−STUB
26− echo "Patched $DEVTOOLS_DIR: created macOS ReactDevToolsSettingsManager stub"
27−fi
28−
29−# Fixes upstream react-native missing Platform.macos.js — Metro resolves
30−# Platform.ios.js for iOS but has no macOS variant, leaving Platform.OS
31−# undefined. Create one from the iOS version with OS set to 'macos'.
32−PLATFORM_DIR="node_modules/react-native/Libraries/Utilities"
33−PLATFORM_MACOS="$PLATFORM_DIR/Platform.macos.js"
34−
35−if [ -d "$PLATFORM_DIR" ] && [ ! -f "$PLATFORM_MACOS" ]; then
36− cat > "$PLATFORM_MACOS" << 'PLAT'
37−const Platform = {
38− OS: 'macos',
39− select: (spec) => 'macos' in spec ? spec.macos : 'ios' in spec ? spec.ios : 'native' in spec ? spec.native : spec.default,
40− get Version() { return ''; },
41− get constants() { return { osVersion: '', reactNativeVersion: { major: 0, minor: 81, patch: 5 } }; },
42− get isPad() { return false; },
43− get isTV() { return false; },
44− get isTesting() { return false; },
45−};
46−export default Platform;
47−PLAT
48− echo "Patched $PLATFORM_DIR: created Platform.macos.js"
49−fi
50−
51−# Fixes New Architecture (Fabric) and general macOS support: many files in
52−# react-native are backwards-compat shims that import themselves, expecting
53−# Metro to resolve a platform-specific variant (.ios.js, .macos.js, etc).
54−# react-native-macos ships .macos.js variants but they only exist in the
55−# react-native-macos package. Since Metro resolves from react-native (not
56−# react-native-macos), we must copy them over.
57−MACOS_FILES="
58− Libraries/NativeComponent/BaseViewConfig.macos.js
59− Libraries/Image/Image.macos.js
60− Libraries/Alert/RCTAlertManager.macos.js
61− Libraries/Components/AccessibilityInfo/legacySendAccessibilityEvent.macos.js
62− Libraries/DevToolsSettings/DevToolsSettingsManager.macos.js
63− Libraries/Network/RCTNetworking.macos.js
64− Libraries/Settings/Settings.macos.js
65− Libraries/StyleSheet/PlatformColorValueTypes.macos.js
66− Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js
67− Libraries/Utilities/BackHandler.macos.js
68−"
69−
70−for f in $MACOS_FILES; do
71− SRC="node_modules/react-native-macos/$f"
72− DST="node_modules/react-native/$f"
73− if [ -f "$SRC" ] && [ ! -f "$DST" ]; then
74− mkdir -p "$(dirname "$DST")"
75− cp "$SRC" "$DST"
76− echo "Patched $DST: copied .macos.js variant from react-native-macos"
77− fi
78−done
example-macos/src/Demo/Chat.js+5−18
@@ -125,6 +125,11 @@ var emojiMap = {
125125 hugs: "🤗",
126126 salute: "🫡",
127127 shrug: "🤷",
128+ purescript: "⊳",
129+ haskell: "λ",
130+ rust: "🦀",
131+ js: "📜",
132+ ts: "🔷",
128133};
129134export const replaceEmoji = (str) =>
130135 str.replace(
@@ -134,21 +139,3 @@ export const replaceEmoji = (str) =>
134139export const setTimeout_ = (ms, fn) => {
135140 setTimeout(fn, ms);
136141};
137−export const emojiDir =
138− "/Users/mark/Developer/purescript-yoga-react-native/example-macos/macos/YogaReactExample-macOS/emoji";
139−
140−// Replace custom emoji shortcodes with a placeholder character for sizing
141−export const stripCustomEmoji_ = (emojiMap) => (str) =>
142− str.replace(/:([a-zA-Z0-9_]+):/g, (m, name) =>
143− emojiMap[name] ? "Wl" : m
144− );
145−
146−// Returns the custom emoji filename if the message is a single custom emoji, otherwise null
147−export const singleCustomEmoji_ = (emojiMap) => (str) => {
148− var trimmed = str.trim();
149− var match = trimmed.match(/^:([a-zA-Z0-9_]+):$/);
150− if (!match) return null;
151− var name = match[1];
152− var file = emojiMap[name];
153− return file || null;
154−};
example-macos/src/Demo/Chat.purs+53−203
@@ -3,37 +3,30 @@ module Demo.Chat (chatDemo) where
33import Prelude
44
55import Data.Array (mapWithIndex, length, filter, snoc, (!!), modifyAt)
6−import Data.Foldable (foldl, traverse_)
6+import Data.Foldable (foldl)
77import Data.Int (toNumber)
8−import Data.Maybe (Maybe(..), fromMaybe, isJust)
9−import Data.Nullable (Nullable, toMaybe)
8+import Data.Maybe (Maybe(..), fromMaybe)
109import Data.String (take)
1110import Data.String as String
12−
1311import Demo.Shared (DemoProps)
1412import Effect (Effect)
1513import Effect.Uncurried (EffectFn2, runEffectFn2)
16−
1714import React.Basic (JSX)
1815import React.Basic.Events (handler_)
19−import React.Basic.Hooks (useEffect, useState, useState', (/\))
16+import React.Basic.Hooks (useState, useState', (/\))
2017import React.Basic.Hooks as React
2118import Yoga.React (component)
2219import Yoga.React.Native (text, tw, view)
23−import Yoga.React.Native.MacOS.AnimatedImage (nativeAnimatedImage)
24−import Yoga.React.Native.MacOS.RichTextLabel (EmojiMap, nativeRichTextLabel, emojiMap)
2520import Yoga.React.Native.MacOS.Button (nativeButton)
26−import Yoga.React.Native.MacOS.HoverView (nativeHoverView)
27−import Yoga.React.Native.MacOS.Popover (nativePopover)
2821import Yoga.React.Native.MacOS.PatternBackground (nativePatternBackground)
22+import Yoga.React.Native.MacOS.Popover (nativePopover)
2923import Yoga.React.Native.MacOS.ScrollView (nativeScrollView)
3024import Yoga.React.Native.MacOS.Sidebar (sidebarLayout)
3125import Yoga.React.Native.MacOS.TextField (nativeTextField)
3226import Yoga.React.Native.MacOS.Types as T
33−import Yoga.React.Native.Reanimated as R
3427import Yoga.React.Native.Style as Style
3528
36−type Reaction = { emoji :: String, count :: Int, mine :: Boolean }
29+type Reaction = { emoji :: String, count :: Int }
3730
3831type Message =
3932 { sender :: String
@@ -65,41 +58,11 @@ foreign import abs :: Int -> Int
6558foreign import isSingleEmoji :: String -> Boolean
6659foreign import replaceEmoji :: String -> String
6760foreign import setTimeout_ :: EffectFn2 Int (Effect Unit) Unit
68−foreign import emojiDir :: String
69−foreign import stripCustomEmoji_ :: EmojiMap -> String -> String
70−foreign import singleCustomEmoji_ :: EmojiMap -> String -> Nullable String
71−
72−stripCustomEmoji :: String -> String
73−stripCustomEmoji = stripCustomEmoji_ customEmojiMap
74−
75−singleCustomEmoji :: String -> Maybe String
76−singleCustomEmoji = toMaybe <<< singleCustomEmoji_ customEmojiMap
77−
78−customEmojiMap :: EmojiMap
79−customEmojiMap = emojiMap
80− { ps: "purescript.png"
81− , haskell: "haskell.png"
82− , shipit: "shipit.png"
83− , party: "party.png"
84− , parrot: "parrot.gif"
85− , fastparrot: "fastparrot.gif"
86− , ultraparrot: "ultraparrot.gif"
87− , congaparrot: "congaparrot.gif"
88− , opensourceparrot: "opensourceparrot.gif"
89− , dealwithit: "dealwithit.gif"
90− , doge: "cooldoge.gif"
91− , partyblob: "partyblob.gif"
92− , typingcat: "typingcat.gif"
93− }
9461
9562addReaction :: String -> Array Reaction -> Array Reaction
9663addReaction emoji rs = case filter (\r -> r.emoji == emoji) rs of
97− [] -> snoc rs { emoji, count: 1, mine: true }
98− _ -> map (\r -> if r.emoji == emoji then r { count = r.count + 1, mine = true } else r) rs
99−
100−removeReaction :: String -> Array Reaction -> Array Reaction
101−removeReaction emoji rs = filter (\r -> r.count > 0) $
102− map (\r -> if r.emoji == emoji && r.mine then r { count = r.count - 1, mine = false } else r) rs
64+ [] -> snoc rs { emoji, count: 1 }
65+ _ -> map (\r -> if r.emoji == emoji then r { count = r.count + 1 } else r) rs
10366
10467mockRooms :: Array Room
10568mockRooms =
@@ -115,43 +78,34 @@ mockMessages room = map (\m -> m { body = replaceEmoji m.body }) (mockMessagesRa
11578noReactions :: Array Reaction
11679noReactions = []
11780
118−mkReaction :: String -> Int -> Reaction
119−mkReaction emoji count = { emoji, count, mine: false }
120−
12181mockMessagesRaw :: String -> Array Message
12282mockMessagesRaw = case _ of
12383 "general" ->
124− [ { sender: "Alice", body: "Hey everyone! Welcome to the chat demo :wave:", isMine: false, reactions: [ mkReaction "👋" 3 ], replyTo: Nothing }
125− , { sender: "Bob", body: "This is built with :ps: + React Native macOS", isMine: false, reactions: [ mkReaction "🔥" 2, mkReaction "❤️" 1 ], replyTo: Nothing }
84+ [ { sender: "Alice", body: "Hey everyone! Welcome to the chat demo :wave:", isMine: false, reactions: [ { emoji: "👋", count: 3 } ], replyTo: Nothing }
85+ , { sender: "Bob", body: "This is built with PureScript + React Native macOS :purescript:", isMine: false, reactions: [ { emoji: "🔥", count: 2 }, { emoji: "❤️", count: 1 } ], replyTo: Nothing }
12686 , { sender: "You", body: "That's awesome! :fire:", isMine: true, reactions: noReactions, replyTo: Just 1 }
12787 , { sender: "Alice", body: "Try typing :rocket: or :heart: in a message", isMine: false, reactions: noReactions, replyTo: Nothing }
128− , { sender: "You", body: "The bubbles look great :sparkles:", isMine: true, reactions: [ mkReaction "👍" 2 ], replyTo: Nothing }
88+ , { sender: "You", body: "The bubbles look great :sparkles:", isMine: true, reactions: [ { emoji: "👍", count: 2 } ], replyTo: Nothing }
12989 , { sender: "Bob", body: "Click the smiley to react!", isMine: false, reactions: noReactions, replyTo: Just 4 }
130− , { sender: "Alice", body: ":party: :shipit:", isMine: false, reactions: [ mkReaction "🎉" 5 ], replyTo: Nothing }
131− , { sender: "Bob", body: ":parrot: Party Parrot! :parrot:", isMine: false, reactions: [ mkReaction "🦜" 3 ], replyTo: Nothing }
132− , { sender: "You", body: ":dealwithit: deal with it :doge:", isMine: true, reactions: noReactions, replyTo: Nothing }
133− , { sender: "Alice", body: ":typingcat:", isMine: false, reactions: noReactions, replyTo: Nothing }
90+ , { sender: "Alice", body: ":tada:", isMine: false, reactions: [ { emoji: "🎉", count: 5 } ], replyTo: Nothing }
13491 ]
13592 "purescript" ->
136− [ { sender: "Phil", body: "Has anyone tried the new compiler release?", isMine: false, reactions: [ mkReaction "👍" 4 ], replyTo: Nothing }
93+ [ { sender: "Phil", body: "Has anyone tried the new compiler release?", isMine: false, reactions: [ { emoji: "👍", count: 4 } ], replyTo: Nothing }
13794 , { sender: "You", body: "Yes! The build times are much better", isMine: true, reactions: noReactions, replyTo: Just 0 }
138− , { sender: "Phil", body: "Row polymorphism makes FFI so clean", isMine: false, reactions: [ mkReaction "❤️" 3 ], replyTo: Nothing }
95+ , { sender: "Phil", body: "Row polymorphism makes FFI so clean", isMine: false, reactions: [ { emoji: "❤️", count: 3 } ], replyTo: Nothing }
13996 , { sender: "Jordan", body: "The ecosystem keeps getting better", isMine: false, reactions: noReactions, replyTo: Nothing }
140− , { sender: "You", body: "Agreed, especially for React Native", isMine: true, reactions: [ mkReaction "🔥" 1 ], replyTo: Just 2 }
141− , { sender: "Jordan", body: ":opensourceparrot: :ps: :haskell: :opensourceparrot:", isMine: false, reactions: [ mkReaction "❤️" 4 ], replyTo: Nothing }
97+ , { sender: "You", body: "Agreed, especially for React Native", isMine: true, reactions: [ { emoji: "🔥", count: 1 } ], replyTo: Just 2 }
14298 ]
14399 "react-native" ->
144− [ { sender: "Christoph", body: "macOS support is looking solid", isMine: false, reactions: [ mkReaction "🔥" 3 ], replyTo: Nothing }
100+ [ { sender: "Christoph", body: "macOS support is looking solid", isMine: false, reactions: [ { emoji: "🔥", count: 3 } ], replyTo: Nothing }
145101 , { sender: "You", body: "Native controls feel right at home", isMine: true, reactions: noReactions, replyTo: Just 0 }
146102 , { sender: "Christoph", body: "NSOutlineView, NSSplitView, toolbars...", isMine: false, reactions: noReactions, replyTo: Nothing }
147− , { sender: "You", body: "Even the vibrancy effects work", isMine: true, reactions: [ mkReaction "😮" 2 ], replyTo: Nothing }
148− , { sender: "Christoph", body: "🚀", isMine: false, reactions: [ mkReaction "🚀" 7 ], replyTo: Nothing }
149− , { sender: "You", body: ":fastparrot: :congaparrot: :ultraparrot: conga line!", isMine: true, reactions: [ mkReaction "🎉" 3 ], replyTo: Nothing }
103+ , { sender: "You", body: "Even the vibrancy effects work", isMine: true, reactions: [ { emoji: "😮", count: 2 } ], replyTo: Nothing }
104+ , { sender: "Christoph", body: "🚀", isMine: false, reactions: [ { emoji: "🚀", count: 7 } ], replyTo: Nothing }
150105 ]
151106 "random" ->
152107 [ { sender: "Eve", body: "Anyone here?", isMine: false, reactions: noReactions, replyTo: Nothing }
153− , { sender: "You", body: "👋", isMine: true, reactions: [ mkReaction "👋" 1 ], replyTo: Just 0 }
154− , { sender: "Eve", body: ":partyblob: :partyblob: :partyblob:", isMine: false, reactions: [ mkReaction "🎉" 2 ], replyTo: Nothing }
108+ , { sender: "You", body: "👋", isMine: true, reactions: [ { emoji: "👋", count: 1 } ], replyTo: Just 0 }
155109 ]
156110 _ -> []
157111
@@ -172,37 +126,6 @@ chatDemo = component "ChatDemo" \dp -> React.do
172126 scrollBottomTrigger /\ setScrollBottomTrigger <- useState 1
173127 scrollY /\ setScrollY <- useState' 0.0
174128 scrollTrigger /\ setScrollTrigger <- useState 0
175− hoveredIdx /\ setHoveredIdx <- useState' (Nothing :: Maybe Int)
176− opac0 <- R.useSharedValue 0.0
177− opac1 <- R.useSharedValue 0.0
178− opac2 <- R.useSharedValue 0.0
179− opac3 <- R.useSharedValue 0.0
180− opac4 <- R.useSharedValue 0.0
181− opac5 <- R.useSharedValue 0.0
182− scl0 <- R.useSharedValue 0.3
183− scl1 <- R.useSharedValue 0.3
184− scl2 <- R.useSharedValue 0.3
185− scl3 <- R.useSharedValue 0.3
186− scl4 <- R.useSharedValue 0.3
187− scl5 <- R.useSharedValue 0.3
188− let emojiOpacities = [ opac0, opac1, opac2, opac3, opac4, opac5 ]
189− let emojiScales = [ scl0, scl1, scl2, scl3, scl4, scl5 ]
190− useEffect (isJust reactPopover) do
191− let springCfg = { stiffness: 300.0, damping: 18.0 }
192− mapWithIndex
193− ( \i _ -> do
194− case emojiOpacities !! i, emojiScales !! i of
195− Just opac, Just scl ->
196− if isJust reactPopover then do
197− R.animate opac (R.withDelay (i * 40) (R.withSpring 1.0 springCfg))
198− R.animate scl (R.withDelay (i * 40) (R.withSpring 1.0 springCfg))
199− else do
200− R.writeSharedValue opac 0.0
201− R.writeSharedValue scl 0.3
202− _, _ -> pure unit
203− )
204− reactionEmoji # traverse_ identity
205− pure (pure unit)
206129 let
207130 selectRoom rid = do
208131 setActiveRoom (Just rid)
@@ -271,50 +194,26 @@ chatDemo = component "ChatDemo" \dp -> React.do
271194 ]
272195 )
273196
274− toggleReaction idx emoji = do
275− setMessages \msgs -> fromMaybe msgs
276− ( modifyAt idx
277− ( \m ->
278− let
279− existing = filter (\rx -> rx.emoji == emoji) m.reactions
280− in
281− case existing !! 0 of
282− Just rx | rx.mine -> m { reactions = removeReaction emoji m.reactions }
283− _ -> m { reactions = addReaction emoji m.reactions }
284− )
285− msgs
286− )
287−
288− reactionPills idx msg =
197+ reactionPills msg =
289198 if length msg.reactions == 0 then mempty
290199 else view { style: tw "flex-row mt-1" <> Style.style { alignSelf: if msg.isMine then "flex-end" else "flex-start" } }
291− ( msg.reactions <#> \rx ->
200+ ( msg.reactions <#> \r ->
292201 view
293202 { style: tw "flex-row items-center rounded-full px-2 py-0.5 mr-1"
294− <> Style.style
295− { backgroundColor: if rx.mine then sentBubbleBg else reactionBg }
203+ <> Style.style { backgroundColor: reactionBg }
296204 }
297− [ text { style: Style.style { fontSize: 12.0 } } rx.emoji
298− , text { style: tw "text-xs ml-1" <> Style.style { color: if rx.mine then "#FFFFFF" else dp.dimFg } } (show rx.count)
299− , nativeButton
300− { title: ""
301− , bezelStyle: T.toolbar
302− , onPress: toggleReaction idx rx.emoji
303− , style: Style.style { position: "absolute", top: 0.0, left: 0.0, right: 0.0, bottom: 0.0, opacity: 0.0 }
304− }
205+ [ text { style: Style.style { fontSize: 12.0 } } r.emoji
206+ , text { style: tw "text-xs ml-1" <> Style.style { color: dp.dimFg } } (show r.count)
305207 ]
306208 )
307209
308− showSmiley idx = hoveredIdx == Just idx || reactPopover == Just idx
309−
310210 reactionPicker idx =
311211 nativePopover
312212 { visible: reactPopover == Just idx
313213 , preferredEdge: T.bottom
314214 , behavior: T.transient
315− , popoverPadding: 0.0
316215 , onClose: setReactPopover Nothing
317− , style: Style.style { opacity: if showSmiley idx then 1.0 else 0.0 }
216+ , style: Style.style {}
318217 }
319218 [ nativeButton
320219 { title: "☺"
@@ -322,40 +221,16 @@ chatDemo = component "ChatDemo" \dp -> React.do
322221 , onPress:
323222 if reactPopover == Just idx then setReactPopover Nothing
324223 else setReactPopover (Just idx)
325− , style: Style.style { height: 24.0, width: 28.0 }
326− , buttonEnabled: showSmiley idx
224+ , style: Style.style { height: 20.0, width: 24.0 }
327225 }
328− , view { style: tw "flex-row items-center" }
329− ( mapWithIndex
330− ( \i emoji ->
331− case emojiOpacities !! i, emojiScales !! i of
332− Just opac, Just scl ->
333− R.reanimatedView
334− { style: Style.style { opacity: opac, transform: [ { scale: scl } ] } }
335− [ nativeButton
336− { title: emoji
337− , bezelStyle: T.borderless
338− , onPress: reactToMessage idx emoji
339− , style: Style.style { height: 28.0, width: 28.0 }
340− }
341− ]
342− _, _ ->
343− nativeButton
344− { title: emoji
345− , bezelStyle: T.borderless
346− , onPress: reactToMessage idx emoji
347− , style: Style.style { height: 28.0, width: 28.0 }
348− }
349− )
350− reactionEmoji
351− <>
352− [ nativeButton
353− { title: "+"
354− , bezelStyle: T.borderless
355− , onEmojiPick: reactToMessage idx
356− , style: Style.style { height: 28.0, width: 28.0 }
357− }
358− ]
226+ , view { style: tw "flex-row items-center p-2" <> Style.style { height: 40.0 } }
227+ ( reactionEmoji <#> \emoji ->
228+ nativeButton
229+ { title: emoji
230+ , bezelStyle: T.toolbar
231+ , onPress: reactToMessage idx emoji
232+ , style: Style.style { height: 32.0, width: 32.0 }
233+ }
359234 )
360235 ]
361236
@@ -384,16 +259,10 @@ chatDemo = component "ChatDemo" \dp -> React.do
384259 messageBubble idx msg = do
385260 let align = if msg.isMine then "flex-end" else "flex-start"
386261 let bigEmoji = isSingleEmoji msg.body
387− let singleGif = singleCustomEmoji msg.body
388262 let senderLabel = if msg.isMine then "" else msg.sender
389263 let isHighlighted = highlightIdx == Just idx
390− nativeHoverView
391− { onHoverChange: \hovered ->
392− if hovered then setHoveredIdx (Just idx)
393− else setHoveredIdx Nothing
394− , style: tw "px-3 mb-1 rounded-lg" <> Style.style { backgroundColor: if isHighlighted then highlightBg else "transparent" }
395− }
396− [ view { style: Style.style { alignSelf: align } }
264+ view { style: tw "px-3 mb-1 rounded-lg" <> Style.style { backgroundColor: if isHighlighted then highlightBg else "transparent" } }
265+ [ view { style: tw "flex-row items-end" <> Style.style { alignSelf: align } }
397266 [ view
398267 { onDoubleClick: handler_ (setReplyingTo (Just idx))
399268 , style: Style.style { maxWidth: 360.0 }
@@ -401,44 +270,25 @@ chatDemo = component "ChatDemo" \dp -> React.do
401270 [ if senderLabel == "" then text { style: Style.style { height: 0.0 } } ""
402271 else text { style: tw "text-xs mb-0.5" <> Style.style { color: dp.dimFg } } senderLabel
403272 , replyQuote msg
404− , view { style: tw "flex-row items-end" <> Style.style { alignSelf: "flex-start" } }
405− [ case singleGif of
406− Just gifFile ->
407− nativeAnimatedImage
408− { source: gifFile
409− , animating: true
410− , cornerRadius: 8.0
411− , style: Style.style { width: 120.0, height: 120.0 }
412− }
413− Nothing ->
414− if bigEmoji then
415− text
416− { style: Style.style { fontSize: 64.0, lineHeight: 72.0 } }
417− msg.body
418− else
419− view
420− { style: tw "rounded-2xl px-3 py-2"
421− <> Style.style
422− { backgroundColor: if msg.isMine then sentBubbleBg else receivedBubbleBg }
423− }
424− [ view { style: Style.style { overflow: "hidden" } }
425− [ text
426− { style: Style.style { fontSize: 14.0, opacity: 0.0 } }
427− (stripCustomEmoji msg.body)
428− , nativeRichTextLabel
429− { text: msg.body
430− , emojiMap: customEmojiMap
431− , textColor: if msg.isMine then "#FFFFFF" else (if dp.isDark then "#FFFFFF" else "#000000")
432− , fontSize: 14.0
433− , emojiSize: 0.0
434− , style: Style.style { position: "absolute", top: 0.0, left: 0.0, right: 0.0, bottom: 0.0 }
435− }
436− ]
437− ]
438− , if not msg.isMine then reactionPicker idx else mempty
439− ]
440− , reactionPills idx msg
273+ , if bigEmoji then
274+ text
275+ { style: Style.style { fontSize: 64.0, lineHeight: 72.0 } }
276+ msg.body
277+ else
278+ view
279+ { style: tw "rounded-2xl px-3 py-2"
280+ <> Style.style
281+ { backgroundColor: if msg.isMine then sentBubbleBg else receivedBubbleBg }
282+ }
283+ [ text
284+ { style: tw "text-sm"
285+ <> Style.style { color: if msg.isMine then "#FFFFFF" else (if dp.isDark then "#FFFFFF" else "#000000") }
286+ }
287+ msg.body
288+ ]
289+ , reactionPills msg
441290 ]
291+ , if msg.isMine then mempty else reactionPicker idx
442292 ]
443293 ]
444294
example-macos/src/Demo/Navigation.purs−8
@@ -8,7 +8,6 @@ import Demo.AiMl (cameraDemo, naturalLanguageDemo, ocrDemo, speechRecognitionDem
88import Demo.Animation (riveDemo)
99import Demo.SpringAnimation (springDemo)
1010import Demo.Chat (chatDemo)
11−import Demo.Signal (signalDemo)
1211import Demo.DataViews (outlineViewDemo, tableViewDemo)
1312import Demo.Display (animatedImageDemo, imageDemo, levelIndicatorDemo, pathControlDemo, progressDemo, separatorDemo, videoPlayerDemo)
1413import Demo.DragDropFiles (dropZoneDemo, filePickerDemo)
@@ -18,7 +17,6 @@ import Demo.MapsDocs (mapViewDemo, pdfViewDemo)
1817import Demo.Overlays (alertDemo, contextMenuDemo, menuDemo, popoverDemo, sheetDemo)
1918import Demo.Shared (DemoProps)
2019import Demo.SystemServices (clipboardDemo, colorPanelDemo, fontPanelDemo, notificationsDemo, quickLookDemo, shareDemo, soundDemo, speechDemo, statusBarDemo)
21−import Demo.Terminal (terminalDemo)
2220import Demo.TextEditing (textEditorDemo, textFieldDemo)
2321import Demo.WebBrowser (webViewDemo)
2422import Effect (Effect)
@@ -112,10 +110,6 @@ outlineTree = do
112110 ]
113111 , folder "chat" "Chat" "bubble.left.and.bubble.right"
114112 [ leaf "matrix" "Matrix" "bubble.left.and.bubble.right"
115− , leaf "signal" "Signal" "lock.shield"
116− ]
117− , folder "terminal" "Terminal" "terminal"
118− [ leaf "ghostty" "Ghostty Terminal" "terminal"
119113 ]
120114 ]
121115
@@ -207,8 +201,6 @@ demoContent dp = case _ of
207201 "pdfview" -> pdfViewDemo dp
208202 "webview" -> webViewDemo dp
209203 "matrix" -> chatDemo dp
210− "signal" -> signalDemo dp
211− "ghostty" -> terminalDemo dp
212204 _ -> placeholder dp
213205
214206placeholder :: DemoProps -> JSX
example-macos/src/Demo/Signal.jsdeleted−12
@@ -1,12 +0,0 @@
1−export const createWebSocketImpl = (url, onOpen, onMessage, onClose, onError) => {
2− var ws = new WebSocket(url);
3− ws.onopen = () => onOpen();
4− ws.onmessage = (e) => onMessage(e.data)();
5− ws.onclose = () => onClose();
6− ws.onerror = (e) => onError(e.message || "WebSocket error")();
7− return ws;
8−};
9−
10−export const closeWebSocket = (ws) => () => {
11− ws.close();
12−};
example-macos/src/Demo/Signal.pursdeleted−68
@@ -1,68 +0,0 @@
1−module Demo.Signal (signalDemo) where
2−
3−import Prelude
4−
5−import Data.Maybe (Maybe(..))
6−import Demo.Shared (DemoProps)
7−import Effect (Effect)
8−import Effect.Uncurried (EffectFn5, runEffectFn5)
9−import React.Basic (JSX)
10−import React.Basic.Hooks (useEffect, useState', (/\))
11−import React.Basic.Hooks as React
12−import Yoga.React (component)
13−import Yoga.React.Native (text, tw, view)
14−import Yoga.React.Native.Style as Style
15−
16−foreign import data WebSocket :: Type
17−foreign import createWebSocketImpl :: EffectFn5 String (Effect Unit) (String -> Effect Unit) (Effect Unit) (String -> Effect Unit) WebSocket
18−foreign import closeWebSocket :: WebSocket -> Effect Unit
19−
20−createWebSocket :: String -> Effect Unit -> (String -> Effect Unit) -> Effect Unit -> (String -> Effect Unit) -> Effect WebSocket
21−createWebSocket url onOpen onMessage onClose onError =
22− runEffectFn5 createWebSocketImpl url onOpen onMessage onClose onError
23−
24−signalDemo :: DemoProps -> JSX
25−signalDemo dp = signalComponent { dp }
26− where
27− signalComponent = component "SignalDemo" \{ dp: dp' } -> React.do
28− status /\ setStatus <- useState' "Connecting to backend..."
29− qrUrl /\ setQrUrl <- useState' (Nothing :: Maybe String)
30− useEffect unit do
31− ws <- createWebSocket "ws://127.0.0.1:8765"
32− (setStatus "Connected, waiting for QR code...")
33− (\msg -> do
34− setQrUrl (Just msg)
35− setStatus "QR code ready - scan with Signal on your phone"
36− )
37− (setStatus "Disconnected from backend")
38− (\err -> setStatus $ "Error: " <> err)
39− pure (closeWebSocket ws)
40−
41− pure do
42− view { style: tw "flex-1 p-6" <> Style.style { backgroundColor: dp'.bg } }
43− [ text
44− { style: tw "text-2xl font-bold mb-4" <> Style.style { color: dp'.fg } }
45− "Signal Device Linking"
46− , text
47− { style: tw "text-sm mb-4" <> Style.style { color: dp'.dimFg } }
48− status
49− , case qrUrl of
50− Nothing ->
51− view { style: tw "items-center justify-center p-8" }
52− [ text
53− { style: tw "text-sm" <> Style.style { color: dp'.dimFg } }
54− "Waiting for provisioning URL..."
55− ]
56− Just url ->
57− view { style: tw "rounded-lg p-4" <> Style.style { backgroundColor: dp'.cardBg } }
58− [ text
59− { style: tw "text-sm font-semibold mb-2" <> Style.style { color: dp'.fg } }
60− "Provisioning URL:"
61− , text
62− { style: tw "text-xs font-mono" <> Style.style { color: dp'.dimFg } }
63− url
64− , text
65− { style: tw "text-xs mt-4" <> Style.style { color: dp'.dimFg } }
66− "Open Signal on your phone > Settings > Linked Devices > Link New Device"
67− ]
68− ]
example-macos/src/Demo/SpringAnimation.purs+35−68
@@ -4,15 +4,15 @@ import Prelude
44
55import Demo.Shared (DemoProps, card, desc, scrollWrap, sectionTitle)
66import React.Basic (JSX)
7−import React.Basic.Events (handler_)
8−import React.Basic.Hooks ((/\), useState')
7+import React.Basic.Hooks (useState', (/\))
98import React.Basic.Hooks as React
109import Yoga.React (component)
1110import Yoga.React.Native (text, tw, view)
11+import Yoga.React.Native.Animated (animatedView, animatedView_, interpolate, useSpring)
12+import React.Basic.Events (handler_)
13+import Yoga.React.Native.Pressable (pressable)
1214import Yoga.React.Native.MacOS.Button (nativeButton)
1315import Yoga.React.Native.MacOS.Types as T
14−import Yoga.React.Native.Pressable (pressable)
15−import Yoga.React.Native.Reanimated as R
1616import Yoga.React.Native.Style as Style
1717
1818springDemo :: DemoProps -> JSX
@@ -21,61 +21,35 @@ springDemo = component "SpringDemo" \dp -> React.do
2121 toggled /\ setToggled <- useState' false
2222 fadeIn /\ setFadeIn <- useState' false
2323 showItems /\ setShowItems <- useState' false
24− scale <- R.useSharedValue 1.0
25− translateX <- R.useSharedValue 0.0
26− opacity <- R.useSharedValue 0.0
27− item0 <- R.useSharedValue 0.0
28− item1 <- R.useSharedValue 0.0
29− item2 <- R.useSharedValue 0.0
30− item3 <- R.useSharedValue 0.0
31− slide0 <- R.useSharedValue 20.0
32− slide1 <- R.useSharedValue 20.0
33− slide2 <- R.useSharedValue 20.0
34− slide3 <- R.useSharedValue 20.0
24+ let scaleTarget = if pressed then 0.92 else 1.0
25+ scale <- useSpring scaleTarget { stiffness: 400.0, damping: 15.0 }
26+ let toggleTarget = if toggled then 200.0 else 0.0
27+ translateX <- useSpring toggleTarget { stiffness: 180.0, damping: 14.0 }
28+ let fadeTarget = if fadeIn then 1.0 else 0.0
29+ opacity <- useSpring fadeTarget { stiffness: 120.0, damping: 20.0 }
30+ item0 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 200.0, damping: 18.0 }
31+ item1 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 180.0, damping: 18.0, delay: 50 }
32+ item2 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 160.0, damping: 18.0, delay: 100 }
33+ item3 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 140.0, damping: 18.0, delay: 150 }
3534 pure do
36− let
37− springScale target = R.springTo scale target { stiffness: 400.0, damping: 15.0 }
38− springToggle on = R.springTo translateX (if on then 200.0 else 0.0) { stiffness: 180.0, damping: 14.0 }
39− springFade on = R.springTo opacity (if on then 1.0 else 0.0) { stiffness: 120.0, damping: 20.0 }
40− springItems on = do
41− let cfg = { stiffness: 200.0, damping: 18.0 }
42− if on then do
43− let
44− fadeSlide sv slideSv delay = do
45− R.animate sv (R.withDelay delay (R.withSpring 1.0 cfg))
46− R.animate slideSv (R.withDelay delay (R.withSpring 0.0 cfg))
47− fadeSlide item0 slide0 0
48− fadeSlide item1 slide1 60
49− fadeSlide item2 slide2 120
50− fadeSlide item3 slide3 180
51− else do
52− let fastCfg = { duration: 150.0 }
53− let
54− fadeOut sv slideSv delay = do
55− R.animate sv (R.withDelay delay (R.withTiming 0.0 fastCfg))
56− R.animate slideSv (R.withDelay delay (R.withTiming (-10.0) fastCfg))
57− fadeOut item3 slide3 0
58− fadeOut item2 slide2 40
59− fadeOut item1 slide1 80
60− fadeOut item0 slide0 120
6135 scrollWrap dp
6236 [ sectionTitle dp.fg "Spring Animations"
63− , desc dp "Reanimated 4 — springs run on the UI thread"
37+ , desc dp "Declarative spring physics using useSpring"
6438
6539 , sectionTitle dp.fg "Scale on Press"
6640 , desc dp "Hold down the card — it springs to 0.92x, release to bounce back"
6741 , pressable
68− { onPressIn: handler_ (setPressed true *> springScale 0.92)
69− , onPressOut: handler_ (setPressed false *> springScale 1.0)
42+ { onPressIn: handler_ (setPressed true)
43+ , onPressOut: handler_ (setPressed false)
7044 }
71− [ R.reanimatedView
45+ [ animatedView
7246 { style: Style.style { transform: [ { scale } ] } }
7347 [ card dp.cardBg
7448 [ view { style: tw "items-center py-4" }
7549 [ text { style: tw "text-sm font-semibold" <> Style.style { color: dp.fg } }
7650 (if pressed then "Pressing..." else "Hold me!")
7751 , text { style: tw "text-xs mt-1" <> Style.style { color: dp.dimFg } }
78− "Reanimated withSpring on UI thread"
52+ "useSpring { stiffness: 400, damping: 15 }"
7953 ]
8054 ]
8155 ]
@@ -86,13 +60,10 @@ springDemo = component "SpringDemo" \dp -> React.do
8660 , nativeButton
8761 { title: if toggled then "Spring Left" else "Spring Right"
8862 , bezelStyle: T.push
89− , onPress: do
90− let next = not toggled
91− setToggled next
92− springToggle next
63+ , onPress: setToggled (not toggled)
9364 , style: Style.style { height: 24.0, width: 140.0, marginBottom: 8.0 }
9465 }
95− , R.reanimatedView_
66+ , animatedView_
9667 { style: tw "rounded-lg"
9768 <> Style.style
9869 { width: 60.0
@@ -107,13 +78,10 @@ springDemo = component "SpringDemo" \dp -> React.do
10778 , nativeButton
10879 { title: if fadeIn then "Fade Out" else "Fade In"
10980 , bezelStyle: T.push
110− , onPress: do
111− let next = not fadeIn
112− setFadeIn next
113− springFade next
81+ , onPress: setFadeIn (not fadeIn)
11482 , style: Style.style { height: 24.0, width: 120.0, marginBottom: 8.0 }
11583 }
116− , R.reanimatedView
84+ , animatedView
11785 { style: Style.style { opacity } }
11886 [ card dp.cardBg
11987 [ view { style: tw "items-center py-3" }
@@ -123,28 +91,25 @@ springDemo = component "SpringDemo" \dp -> React.do
12391 ]
12492
12593 , sectionTitle dp.fg "Staggered Items"
126− , desc dp "Each item springs in with decreasing stiffness"
94+ , desc dp "Each item springs in with increasing delay"
12795 , nativeButton
12896 { title: if showItems then "Hide Items" else "Show Items"
12997 , bezelStyle: T.push
130− , onPress: do
131− let next = not showItems
132− setShowItems next
133− springItems next
98+ , onPress: setShowItems (not showItems)
13499 , style: Style.style { height: 24.0, width: 120.0, marginBottom: 8.0 }
135100 }
136− , staggeredItem "PureScript" "#007AFF" item0 slide0
137− , staggeredItem "React Native" "#34C759" item1 slide1
138− , staggeredItem "macOS" "#FF9500" item2 slide2
139− , staggeredItem "Spring Physics" "#AF52DE" item3 slide3
101+ , staggeredItem dp "PureScript" "#007AFF" item0
102+ , staggeredItem dp "React Native" "#34C759" item1
103+ , staggeredItem dp "macOS" "#FF9500" item2
104+ , staggeredItem dp "Spring Physics" "#AF52DE" item3
140105 ]
141106 where
142− staggeredItem title color sv slideSv =
143− R.reanimatedView
107+ staggeredItem _ title color anim =
108+ animatedView
144109 { style: Style.style
145− { opacity: sv
110+ { opacity: anim
111+ , transform: [ { translateX: slideX } ]
146112 , marginBottom: 4.0
147− , transform: [ { translateY: slideSv } ]
148113 }
149114 }
150115 [ view
@@ -154,3 +119,5 @@ springDemo = component "SpringDemo" \dp -> React.do
154119 [ text { style: tw "text-sm font-semibold" <> Style.style { color: "#FFFFFF" } } title
155120 ]
156121 ]
122+ where
123+ slideX = interpolate anim { inputRange: [ 0.0, 1.0 ], outputRange: [ -30.0, 0.0 ], extrapolate: "clamp" }
example-macos/src/Demo/Terminal.pursdeleted−19
@@ -1,19 +0,0 @@
1−module Demo.Terminal (terminalDemo) where
2−
3−import Prelude
4−
5−import Demo.Shared (DemoProps, sectionTitle)
6−import React.Basic (JSX)
7−import Yoga.React (component)
8−import Yoga.React.Native (tw, view)
9−import Yoga.React.Native.MacOS.GhosttyTerminal (ghosttyTerminal)
10−import Yoga.React.Native.Style as Style
11−
12−terminalDemo :: DemoProps -> JSX
13−terminalDemo = component "TerminalDemo" \dp -> pure do
14− view { style: tw "flex-1 px-4" }
15− [ sectionTitle dp.fg "Ghostty Terminal"
16− , ghosttyTerminal
17− { style: tw "flex-1" <> Style.style { minHeight: 400.0 }
18− }
19− ]
example-macos/src/Main.purs+1−1
@@ -24,7 +24,7 @@ main = registerComponent "YogaReactExample" \_ -> app {}
2424
2525app :: {} -> JSX
2626app = component "App" \_ -> React.do
27− selectedItem /\ setSelectedItem <- useState' "matrix"
27+ selectedItem /\ setSelectedItem <- useState' "button"
2828 colorScheme <- useColorScheme
2929 let isDark = toNullable (Just "dark") == colorScheme
3030 let fg = if isDark then "#FFFFFF" else "#000000"
signal-backend/.gitignoredeleted−34
@@ -1,34 +0,0 @@
1−# dependencies (bun install)
2−node_modules
3−
4−# output
5−out
6−dist
7−*.tgz
8−
9−# code coverage
10−coverage
11−*.lcov
12−
13−# logs
14−logs
15−_.log
16−report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17−
18−# dotenv environment variable files
19−.env
20−.env.development.local
21−.env.test.local
22−.env.production.local
23−.env.local
24−
25−# caches
26−.eslintcache
27−.cache
28−*.tsbuildinfo
29−
30−# IntelliJ based IDEs
31−.idea
32−
33−# Finder (MacOS) folder config
34−.DS_Store
signal-backend/bun.lockdeleted−36
@@ -1,36 +0,0 @@
1−{
2− "lockfileVersion": 1,
3− "workspaces": {
4− "": {
5− "name": "signal-backend",
6− "dependencies": {
7− "@signalapp/libsignal-client": "^0.87.2",
8− },
9− "devDependencies": {
10− "@types/bun": "latest",
11− },
12− "peerDependencies": {
13− "typescript": "^5",
14− },
15− },
16− },
17− "packages": {
18− "@signalapp/libsignal-client": ["@signalapp/libsignal-client@0.87.2", "", { "dependencies": { "node-gyp-build": "^4.8.0", "type-fest": "^4.26.0", "uuid": "^11" } }, "sha512-cKDJMEmG5cHaNfhEZkdaAFvh8AJ4XHb/lyENjPPKKZ4LrMThCoX0TdKuZDgHZbaeHr3NnORrCNavLI0QQMVvgA=="],
19−
20− "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
21−
22− "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
23−
24− "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
25−
26− "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
27−
28− "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
29−
30− "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
31−
32− "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
33−
34− "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
35− }
36−}
signal-backend/package.jsondeleted−15
@@ -1,15 +0,0 @@
1−{
2− "name": "signal-backend",
3− "module": "index.ts",
4− "type": "module",
5− "private": true,
6− "devDependencies": {
7− "@types/bun": "latest"
8− },
9− "peerDependencies": {
10− "typescript": "^5"
11− },
12− "dependencies": {
13− "@signalapp/libsignal-client": "^0.87.2"
14− }
15−}
signal-backend/run.jsdeleted−2
@@ -1,2 +0,0 @@
1−import { main } from "./output/Main/index.js";
2−main();
signal-backend/spago.lockdeleted−1769
@@ -1,1769 +0,0 @@
1−{
2− "workspace": {
3− "packages": {
4− "signal-backend": {
5− "path": "./",
6− "core": {
7− "dependencies": [
8− "aff",
9− "arraybuffer-types",
10− "arrays",
11− "avar",
12− "console",
13− "effect",
14− "either",
15− "foldable-traversable",
16− "foreign",
17− "foreign-object",
18− "js-promise",
19− "js-promise-aff",
20− "maybe",
21− "newtype",
22− "nullable",
23− "prelude",
24− "refs",
25− "strings",
26− "tuples",
27− "unsafe-coerce",
28− "variant",
29− "web-fetch",
30− "yoga-bun-yoga",
31− "yoga-json",
32− "yoga-om",
33− "yoga-om-layer",
34− "yoga-sqlite",
35− "yoga-sqlite-om"
36− ]
37− },
38− "test": {
39− "dependencies": []
40− }
41− }
42− },
43− "package_set": {
44− "address": {
45− "registry": "73.0.0"
46− },
47− "compiler": ">=0.15.15 <0.16.0",
48− "content": {
49− "abc-parser": "2.1.0",
50− "ace": "9.1.0",
51− "address-rfc2821": "0.1.1",
52− "aff": "8.0.0",
53− "aff-bus": "6.0.0",
54− "aff-coroutines": "9.0.0",
55− "aff-promise": "4.0.0",
56− "aff-retry": "2.0.0",
57− "affjax": "13.0.0",
58− "affjax-node": "1.0.0",
59− "affjax-web": "1.0.0",
60− "ansi": "7.0.0",
61− "apexcharts": "0.5.0",
62− "applicative-phases": "1.0.0",
63− "argonaut": "9.0.0",
64− "argonaut-aeson-generic": "0.4.1",
65− "argonaut-codecs": "9.1.0",
66− "argonaut-core": "7.0.0",
67− "argonaut-generic": "8.0.0",
68− "argonaut-traversals": "10.0.0",
69− "argparse-basic": "2.0.0",
70− "array-builder": "0.1.2",
71− "array-search": "0.6.0",
72− "arraybuffer": "13.2.0",
73− "arraybuffer-builder": "3.1.0",
74− "arraybuffer-types": "3.0.2",
75− "arrays": "7.3.0",
76− "arrays-extra": "0.6.1",
77− "arrays-zipper": "2.0.1",
78− "ask": "1.0.0",
79− "assert": "6.0.0",
80− "assert-multiple": "0.4.0",
81− "avar": "5.0.1",
82− "axon": "0.0.3",
83− "b64": "0.0.8",
84− "barbies": "1.0.1",
85− "barlow-lens": "0.9.0",
86− "benchlib": "0.0.4",
87− "bifunctors": "6.1.0",
88− "bigints": "7.0.1",
89− "blessed": "1.0.0",
90− "bolson": "0.3.9",
91− "bookhound": "0.1.7",
92− "bower-json": "3.0.0",
93− "call-by-name": "4.0.1",
94− "canvas": "6.0.0",
95− "canvas-action": "9.0.0",
96− "cartesian": "1.0.6",
97− "catenable-lists": "7.0.0",
98− "cbor-stream": "1.3.0",
99− "chameleon": "1.0.0",
100− "chameleon-halogen": "1.0.3",
101− "chameleon-react-basic": "1.1.0",
102− "chameleon-styled": "2.5.0",
103− "chameleon-transformers": "1.0.0",
104− "channel": "1.0.0",
105− "checked-exceptions": "3.1.1",
106− "choku": "1.0.2",
107− "classless": "0.1.1",
108− "classless-arbitrary": "0.1.1",
109− "classless-decode-json": "0.1.1",
110− "classless-encode-json": "0.1.3",
111− "classnames": "2.0.0",
112− "codec": "6.1.0",
113− "codec-argonaut": "10.0.0",
114− "codec-json": "2.0.0",
115− "colors": "7.0.1",
116− "concur-core": "0.5.0",
117− "concur-react": "0.5.0",
118− "concurrent-queues": "3.0.0",
119− "console": "6.1.0",
120− "const": "6.0.0",
121− "contravariant": "6.0.0",
122− "control": "6.0.0",
123− "convertable-options": "1.0.0",
124− "coroutines": "7.0.0",
125− "css": "6.0.0",
126− "css-class-name-extractor": "0.0.4",
127− "css-frameworks": "1.1.0",
128− "csv-stream": "2.3.0",
129− "data-mvc": "0.0.2",
130− "datetime": "6.1.0",
131− "datetime-parsing": "0.2.0",
132− "debounce": "0.1.0",
133− "debug": "6.0.2",
134− "decimals": "7.1.0",
135− "default-values": "1.0.1",
136− "deku": "0.9.23",
137− "deno": "0.0.5",
138− "dissect": "1.0.0",
139− "distributive": "6.0.0",
140− "dodo-printer": "2.2.3",
141− "dom-filereader": "7.0.0",
142− "dom-indexed": "13.0.0",
143− "dom-simple": "0.4.0",
144− "dotenv": "4.0.3",
145− "droplet": "0.6.0",
146− "dts": "1.0.0",
147− "dual-numbers": "1.0.3",
148− "dynamic-buffer": "3.0.1",
149− "echarts-simple": "0.0.1",
150− "effect": "4.0.0",
151− "either": "6.1.0",
152− "elmish": "0.13.0",
153− "elmish-enzyme": "0.1.1",
154− "elmish-hooks": "0.11.0",
155− "elmish-html": "0.10.0",
156− "elmish-testing-library": "0.3.2",
157− "elmish-time-machine": "0.4.2",
158− "email-validate": "7.0.0",
159− "encoding": "0.0.9",
160− "enums": "6.0.1",
161− "env-names": "0.4.0",
162− "environment": "2.0.0",
163− "error": "2.0.0",
164− "eta-conversion": "0.3.2",
165− "exceptions": "6.1.0",
166− "exists": "6.0.0",
167− "exitcodes": "4.0.0",
168− "expect-inferred": "3.0.0",
169− "express": "0.9.1",
170− "ezfetch": "1.1.1",
171− "fahrtwind": "2.0.0",
172− "fakerjs": "0.0.1",
173− "fallback": "0.1.0",
174− "fast-vect": "1.2.0",
175− "fetch": "4.1.0",
176− "fetch-argonaut": "1.0.1",
177− "fetch-core": "5.1.0",
178− "fetch-yoga-json": "1.1.0",
179− "ffi-simple": "0.5.1",
180− "fft": "0.3.0",
181− "fft-js": "0.1.0",
182− "filterable": "5.0.0",
183− "fix-functor": "0.1.0",
184− "fixed-points": "7.0.0",
185− "fixed-precision": "5.0.0",
186− "flame": "1.6.0",
187− "float32": "2.0.0",
188− "fmt": "0.2.1",
189− "foldable-traversable": "6.0.0",
190− "foldable-traversable-extra": "0.0.6",
191− "foreign": "7.0.0",
192− "foreign-object": "4.1.0",
193− "foreign-readwrite": "3.4.0",
194− "forgetmenot": "0.1.0",
195− "fork": "6.0.0",
196− "form-urlencoded": "7.0.0",
197− "formatters": "7.0.0",
198− "framer-motion": "2.0.1",
199− "free": "7.1.0",
200− "freeap": "7.0.0",
201− "freer-free": "0.0.1",
202− "freet": "7.0.0",
203− "functions": "6.0.0",
204− "functor1": "3.0.0",
205− "functors": "5.0.0",
206− "fuzzy": "0.4.0",
207− "gen": "4.0.0",
208− "generate-values": "1.0.1",
209− "generic-router": "0.0.1",
210− "geojson": "0.0.5",
211− "geometria": "2.2.0",
212− "gesso": "1.0.1",
213− "gojs": "0.1.1",
214− "golem-fetch": "0.1.0",
215− "grain": "3.0.0",
216− "grain-router": "3.0.0",
217− "grain-virtualized": "3.0.0",
218− "graphql-client": "10.1.1",
219− "graphs": "8.1.0",
220− "group": "4.1.1",
221− "halogen": "7.0.0",
222− "halogen-bootstrap5": "5.3.2",
223− "halogen-canvas": "1.0.0",
224− "halogen-css": "10.0.0",
225− "halogen-declarative-canvas": "0.0.8",
226− "halogen-echarts-simple": "0.0.4",
227− "halogen-formless": "4.0.3",
228− "halogen-helix": "1.1.0",
229− "halogen-hooks": "0.6.3",
230− "halogen-hooks-extra": "0.9.0",
231− "halogen-infinite-scroll": "1.1.0",
232− "halogen-store": "0.5.4",
233− "halogen-storybook": "2.0.0",
234− "halogen-subscriptions": "2.0.0",
235− "halogen-svg-elems": "8.0.0",
236− "halogen-typewriter": "1.0.4",
237− "halogen-use-trigger-hooks": "1.0.0",
238− "halogen-vdom": "8.0.0",
239− "halogen-vdom-string-renderer": "0.5.0",
240− "halogen-xterm": "2.0.0",
241− "heckin": "2.0.1",
242− "heterogeneous": "0.7.0",
243− "homogeneous": "0.4.0",
244− "http-methods": "6.0.0",
245− "httpurple": "4.0.0",
246− "huffman": "0.4.0",
247− "humdrum": "0.0.1",
248− "hylograph-canvas": "0.1.0",
249− "hylograph-d3-kernel": "0.1.0",
250− "hylograph-graph": "0.1.0",
251− "hylograph-layout": "0.1.0",
252− "hylograph-music": "0.1.0",
253− "hylograph-optics": "0.1.0",
254− "hylograph-selection": "0.1.1",
255− "hylograph-simulation": "0.1.0",
256− "hylograph-simulation-core": "0.1.0",
257− "hylograph-simulation-halogen": "0.1.0",
258− "hylograph-transitions": "0.1.0",
259− "hylograph-wasm-kernel": "0.1.0",
260− "hyrule": "2.3.8",
261− "identity": "6.0.0",
262− "identy": "4.0.1",
263− "indexed-db": "1.0.0",
264− "indexed-monad": "3.0.0",
265− "ink": "0.0.1",
266− "int64": "3.0.0",
267− "integers": "6.0.0",
268− "interpolate": "5.0.2",
269− "intersection-observer": "1.0.1",
270− "invariant": "6.0.0",
271− "jarilo": "1.0.1",
272− "jelly": "0.10.0",
273− "jelly-router": "0.3.0",
274− "jelly-signal": "0.4.0",
275− "jest": "1.0.0",
276− "js-abort-controller": "1.0.0",
277− "js-bigints": "2.2.1",
278− "js-date": "8.0.0",
279− "js-fetch": "0.2.1",
280− "js-fileio": "3.0.0",
281− "js-intl": "1.1.4",
282− "js-iterators": "0.1.1",
283− "js-maps": "0.1.2",
284− "js-promise": "1.0.0",
285− "js-promise-aff": "1.0.0",
286− "js-timers": "6.1.0",
287− "js-uri": "3.1.0",
288− "jsdom": "1.0.0",
289− "json": "1.1.0",
290− "json-codecs": "5.0.0",
291− "justifill": "0.5.0",
292− "jwt": "0.0.9",
293− "labeled-data": "0.2.0",
294− "language-cst-parser": "0.14.1",
295− "lazy": "6.0.0",
296− "lazy-joe": "1.0.0",
297− "lcg": "4.0.0",
298− "leibniz": "5.0.0",
299− "leveldb": "1.0.1",
300− "liminal": "1.0.1",
301− "linalg": "6.0.0",
302− "lists": "7.0.0",
303− "literals": "1.0.2",
304− "logging": "3.0.0",
305− "logging-journald": "0.4.0",
306− "lumi-components": "18.0.0",
307− "machines": "7.0.0",
308− "maps-eager": "0.5.0",
309− "marionette": "1.0.0",
310− "marionette-react-basic-hooks": "0.1.1",
311− "marked": "0.1.0",
312− "matrices": "5.0.1",
313− "matryoshka": "1.0.0",
314− "maybe": "6.0.0",
315− "media-types": "6.0.0",
316− "meowclient": "1.0.0",
317− "midi": "4.0.0",
318− "milkis": "9.0.0",
319− "mimetype": "0.0.1",
320− "minibench": "4.0.1",
321− "mmorph": "7.0.0",
322− "monad-control": "5.0.0",
323− "monad-logger": "1.3.1",
324− "monad-loops": "0.5.0",
325− "monad-unlift": "1.0.1",
326− "monoid-extras": "0.0.1",
327− "monoidal": "0.16.0",
328− "morello": "0.4.0",
329− "mote": "3.0.0",
330− "motsunabe": "2.0.0",
331− "mvc": "0.0.1",
332− "mysql": "6.0.1",
333− "n3": "0.1.0",
334− "nano-id": "1.1.0",
335− "nanoid": "0.1.0",
336− "naturals": "3.0.0",
337− "nested-functor": "0.2.1",
338− "newtype": "5.0.0",
339− "nextjs": "0.1.1",
340− "nextui": "0.2.0",
341− "node-buffer": "9.0.0",
342− "node-child-process": "11.1.0",
343− "node-event-emitter": "3.0.0",
344− "node-execa": "5.0.0",
345− "node-fs": "9.2.0",
346− "node-glob-basic": "2.0.0",
347− "node-http": "9.1.0",
348− "node-http2": "1.1.1",
349− "node-human-signals": "1.0.0",
350− "node-net": "5.1.0",
351− "node-os": "5.1.0",
352− "node-path": "5.0.1",
353− "node-process": "11.2.0",
354− "node-readline": "8.1.1",
355− "node-sqlite3": "8.0.0",
356− "node-stream-pipes": "2.1.6",
357− "node-streams": "9.0.1",
358− "node-tls": "0.3.1",
359− "node-url": "7.0.1",
360− "node-workerbees": "0.3.1",
361− "node-zlib": "0.4.0",
362− "nonempty": "7.0.0",
363− "now": "6.0.0",
364− "npm-package-json": "2.0.0",
365− "nullable": "6.0.0",
366− "numberfield": "0.2.2",
367− "numbers": "9.0.1",
368− "oak": "3.1.1",
369− "oak-debug": "1.2.2",
370− "object-maps": "0.3.0",
371− "ocarina": "1.5.4",
372− "oooooooooorrrrrrrmm-lib": "0.0.1",
373− "open-colors-scales-and-schemes": "1.0.0",
374− "open-drawing": "6.0.4",
375− "open-folds": "6.4.0",
376− "open-foreign-generic": "11.0.3",
377− "open-memoize": "6.2.0",
378− "open-mkdirp-aff": "1.2.0",
379− "open-pairing": "6.2.0",
380− "open-smolder": "12.0.2",
381− "options": "7.0.0",
382− "optparse": "5.0.1",
383− "ordered-collections": "3.2.0",
384− "ordered-set": "0.4.0",
385− "orders": "6.0.0",
386− "org-doc": "0.1.0",
387− "owoify": "1.2.0",
388− "pairs": "9.0.1",
389− "parallel": "7.0.0",
390− "parsing": "11.0.0",
391− "parsing-dataview": "3.2.4",
392− "partial": "4.0.0",
393− "pathy": "9.0.0",
394− "pha": "0.13.0",
395− "phaser": "0.7.0",
396− "phylio": "1.1.2",
397− "pipes": "8.0.0",
398− "pirates-charm": "0.0.1",
399− "play": "1.0.0",
400− "pmock": "0.9.0",
401− "point-free": "1.0.0",
402− "pointed-list": "0.5.1",
403− "polymorphic-vectors": "4.0.0",
404− "posix-types": "6.0.0",
405− "postgresql": "2.0.21",
406− "precise": "6.0.0",
407− "precise-datetime": "7.0.0",
408− "prelude": "6.0.2",
409− "prettier-printer": "3.0.0",
410− "printf": "0.1.0",
411− "priority-queue": "0.1.2",
412− "profunctor": "6.0.1",
413− "profunctor-lenses": "8.0.0",
414− "protobuf": "4.4.0",
415− "psa-utils": "8.0.0",
416− "psci-support": "6.0.0",
417− "punycode": "1.0.0",
418− "pursfmt": "0.17.0",
419− "qualified-do": "2.2.0",
420− "quantities": "12.2.0",
421− "quickcheck": "8.0.1",
422− "quickcheck-combinators": "0.1.3",
423− "quickcheck-laws": "7.0.0",
424− "quickcheck-utf8": "0.0.0",
425− "random": "6.0.0",
426− "rationals": "6.0.0",
427− "rdf": "0.1.0",
428− "react": "11.0.0",
429− "react-aria": "0.2.0",
430− "react-basic": "17.0.0",
431− "react-basic-classic": "3.0.0",
432− "react-basic-dnd": "10.1.0",
433− "react-basic-dom": "7.0.0",
434− "react-basic-dom-beta": "0.1.1",
435− "react-basic-emotion": "7.1.0",
436− "react-basic-hooks": "9.0.0",
437− "react-basic-storybook": "2.0.0",
438− "react-dom": "8.0.0",
439− "react-halo": "3.0.0",
440− "react-icons": "1.1.5",
441− "react-markdown": "0.1.0",
442− "react-testing-library": "4.0.1",
443− "react-virtuoso": "1.0.0",
444− "reactix": "0.6.1",
445− "read": "1.0.1",
446− "recharts": "1.1.0",
447− "record": "4.0.0",
448− "record-extra": "5.0.1",
449− "record-extra-srghma": "0.2.8",
450− "record-ptional-fields": "0.1.2",
451− "record-studio": "1.0.4",
452− "refs": "6.0.0",
453− "remotedata": "5.0.1",
454− "repr": "0.5.0",
455− "resize-arrays": "0.0.1",
456− "resize-observer": "1.0.0",
457− "resource": "2.0.1",
458− "resourcet": "1.0.0",
459− "result": "1.0.3",
460− "return": "0.2.0",
461− "ring-modules": "5.0.1",
462− "rito": "0.3.4",
463− "roman": "0.4.0",
464− "rough-notation": "1.0.2",
465− "routing": "11.0.0",
466− "routing-duplex": "0.7.0",
467− "run": "5.0.0",
468− "safe-coerce": "2.0.0",
469− "safely": "4.0.1",
470− "school-of-music": "1.3.0",
471− "selection-foldable": "0.2.0",
472− "selective-functors": "1.0.1",
473− "semirings": "7.0.0",
474− "shuffle": "2.0.0",
475− "signal": "13.0.0",
476− "simple-emitter": "3.0.1",
477− "simple-i18n": "2.0.1",
478− "simple-json": "9.0.0",
479− "simple-json-generics": "0.2.1",
480− "simple-ulid": "3.0.0",
481− "sized-matrices": "1.0.0",
482− "sized-vectors": "5.0.2",
483− "slug": "3.2.0",
484− "small-ffi": "4.0.1",
485− "soundfonts": "4.1.0",
486− "sparse-matrices": "2.0.1",
487− "sparse-polynomials": "3.0.1",
488− "spec": "8.1.1",
489− "spec-discovery": "8.4.0",
490− "spec-mocha": "5.1.1",
491− "spec-node": "0.0.3",
492− "spec-quickcheck": "5.0.2",
493− "spec-reporter-xunit": "0.7.1",
494− "splitmix": "2.1.0",
495− "ssrs": "1.0.0",
496− "st": "6.2.0",
497− "statistics": "0.3.2",
498− "strictlypositiveint": "1.0.1",
499− "string-parsers": "8.0.0",
500− "strings": "6.0.1",
501− "strings-extra": "4.0.0",
502− "stringutils": "0.0.12",
503− "structured-logging": "1.0.0",
504− "substitute": "0.2.3",
505− "supply": "0.2.0",
506− "svg-parser": "3.0.0",
507− "systemd-journald": "0.3.0",
508− "tagged": "4.0.2",
509− "tailrec": "6.1.0",
510− "tanstack-query": "2.0.0",
511− "tecton": "0.2.1",
512− "tecton-halogen": "0.2.0",
513− "test-unit": "17.0.0",
514− "text-formatting": "0.1.0",
515− "thermite": "6.3.1",
516− "thermite-dom": "0.3.1",
517− "these": "6.0.0",
518− "threading": "0.0.3",
519− "tidy": "0.11.1",
520− "tidy-codegen": "4.0.1",
521− "tldr": "0.0.0",
522− "toestand": "0.9.0",
523− "transformation-matrix": "1.0.1",
524− "transformers": "6.1.0",
525− "transit": "1.0.0",
526− "tree-rose": "4.0.2",
527− "trivial-unfold": "0.5.0",
528− "ts-bridge": "4.0.0",
529− "tuples": "7.0.0",
530− "two-or-more": "1.0.0",
531− "type-equality": "4.0.1",
532− "typedenv": "2.0.1",
533− "typelevel": "6.0.0",
534− "typelevel-lists": "2.1.0",
535− "typelevel-peano": "1.0.1",
536− "typelevel-prelude": "7.0.0",
537− "typelevel-regex": "0.0.3",
538− "typelevel-rows": "0.1.0",
539− "typisch": "0.4.0",
540− "uint": "7.0.0",
541− "ulid": "3.0.1",
542− "uncurried-transformers": "1.1.0",
543− "undefined": "2.0.0",
544− "undefined-is-not-a-problem": "1.1.0",
545− "unfoldable": "6.0.0",
546− "unicode": "6.0.0",
547− "unique": "0.6.1",
548− "unlift": "1.0.1",
549− "unordered-collections": "3.1.0",
550− "unsafe-coerce": "6.0.0",
551− "unsafe-reference": "5.0.0",
552− "untagged-to-tagged": "0.1.4",
553− "untagged-union": "1.0.0",
554− "uri": "9.0.0",
555− "url-immutable": "1.0.0",
556− "url-regex-safe": "0.1.1",
557− "uuid": "9.0.0",
558− "uuidv4": "1.0.0",
559− "validation": "6.0.0",
560− "variant": "8.0.0",
561− "variant-encodings": "2.0.0",
562− "variant-gen": "1.0.0",
563− "vectorfield": "1.0.1",
564− "vectors": "2.1.0",
565− "versions": "7.0.0",
566− "visx": "0.0.2",
567− "vitest": "2.0.0",
568− "web-clipboard": "6.0.0",
569− "web-cssom": "2.0.0",
570− "web-cssom-view": "0.1.0",
571− "web-dom": "6.0.0",
572− "web-dom-parser": "8.0.0",
573− "web-dom-xpath": "3.0.0",
574− "web-encoding": "3.0.0",
575− "web-events": "4.0.0",
576− "web-fetch": "4.0.1",
577− "web-file": "4.0.0",
578− "web-geometry": "0.1.0",
579− "web-html": "4.1.1",
580− "web-pointerevents": "2.0.0",
581− "web-proletarian": "1.0.0",
582− "web-promise": "3.2.0",
583− "web-resize-observer": "2.1.0",
584− "web-router": "1.0.0",
585− "web-socket": "4.0.0",
586− "web-storage": "5.0.0",
587− "web-streams": "4.0.0",
588− "web-touchevents": "4.0.0",
589− "web-uievents": "5.0.0",
590− "web-url": "2.0.0",
591− "web-workers": "1.1.0",
592− "web-xhr": "5.0.1",
593− "webb-aff-list": "0.0.2",
594− "webb-array": "0.0.2",
595− "webb-channel": "0.0.3",
596− "webb-commandline": "0.0.3",
597− "webb-directory": "0.0.7",
598− "webb-file": "0.0.1",
599− "webb-map": "0.0.1",
600− "webb-monad": "0.0.2",
601− "webb-mutex": "0.0.7",
602− "webb-parsing": "0.0.5",
603− "webb-random": "0.0.1",
604− "webb-refer": "0.0.3",
605− "webb-set": "0.0.1",
606− "webb-slot": "0.0.1",
607− "webb-stateful": "0.0.3",
608− "webb-string": "0.0.1",
609− "webb-test": "0.0.1",
610− "webb-thread": "0.0.2",
611− "webb-writer": "0.0.3",
612− "webextension-polyfill": "0.1.0",
613− "webgpu": "0.0.1",
614− "which": "2.0.0",
615− "whine-core": "0.0.33",
616− "xterm": "1.0.0",
617− "yaml-next": "3.1.1",
618− "yoga-fetch": "1.0.1",
619− "yoga-json": "5.1.0",
620− "yoga-om": "0.1.0",
621− "yoga-postgres": "6.0.0",
622− "yoga-react-dom": "1.0.1",
623− "yoga-redis": "0.1.0",
624− "yoga-sql-types": "0.1.0",
625− "yoga-subtlecrypto": "0.1.0",
626− "yoga-test-docker": "0.1.0",
627− "yoga-tree": "1.0.0",
628− "yoga-tree-svg": "0.1.0",
629− "yoga-tree-utils": "1.0.0",
630− "z3": "0.0.2",
631− "zipperarray": "2.0.0"
632− }
633− },
634− "extra_packages": {
635− "ref-lifted": {
636− "path": "/Users/mark/code/purescript-ref-lifted"
637− },
638− "yoga-bun-yoga": {
639− "path": "/Users/mark/code/purescript-yoga-bun-yoga"
640− },
641− "yoga-json": "5.2.0",
642− "yoga-om": {
643− "path": "/Users/mark/code/purescript-yoga-om"
644− },
645− "yoga-om-layer": {
646− "path": "/Users/mark/code/purescript-yoga-om-layer"
647− },
648− "yoga-sql-types": {
649− "path": "/Users/mark/code/purescript-yoga-sql-types"
650− },
651− "yoga-sqlite": {
652− "path": "/Users/mark/code/purescript-yoga-sqlite"
653− },
654− "yoga-sqlite-om": {
655− "path": "/Users/mark/code/purescript-yoga-sqlite-om"
656− }
657− }
658− },
659− "packages": {
660− "aff": {
661− "type": "registry",
662− "version": "8.0.0",
663− "integrity": "sha256-9BRIrSdRoYybfiUmBqgKJ66cPqxYHb277bVWQMn/9sg=",
664− "dependencies": [
665− "control",
666− "datetime",
667− "effect",
668− "either",
669− "exceptions",
670− "functions",
671− "newtype",
672− "parallel",
673− "partial",
674− "prelude",
675− "st",
676− "tailrec",
677− "transformers",
678− "unsafe-coerce"
679− ]
680− },
681− "aff-retry": {
682− "type": "registry",
683− "version": "2.0.0",
684− "integrity": "sha256-rKZNcP4IKSyBFQBVzrCvE0ULoJt0N/uDLLJ0dPotr4k=",
685− "dependencies": [
686− "aff",
687− "arrays",
688− "datetime",
689− "effect",
690− "either",
691− "exceptions",
692− "integers",
693− "maybe",
694− "newtype",
695− "numbers",
696− "prelude",
697− "random",
698− "transformers"
699− ]
700− },
701− "arraybuffer-types": {
702− "type": "registry",
703− "version": "3.0.2",
704− "integrity": "sha256-p05cJnSkyeoB7VHzMyc2Eb1RwUaB7Nl0sQSqEGNEUD8=",
705− "dependencies": []
706− },
707− "arrays": {
708− "type": "registry",
709− "version": "7.3.0",
710− "integrity": "sha256-GD4z7LCi9wzi7FCQqbWhvs8paoUK9NO+HwgXZ+KP8Rk=",
711− "dependencies": [
712− "bifunctors",
713− "control",
714− "foldable-traversable",
715− "functions",
716− "maybe",
717− "nonempty",
718− "partial",
719− "prelude",
720− "safe-coerce",
721− "st",
722− "tailrec",
723− "tuples",
724− "unfoldable",
725− "unsafe-coerce"
726− ]
727− },
728− "avar": {
729− "type": "registry",
730− "version": "5.0.1",
731− "integrity": "sha256-8V4SxF4TIWZ9Ik1P4lPzIRUAZeFBe8w5WA2VcCEKsIk=",
732− "dependencies": [
733− "aff",
734− "effect",
735− "either",
736− "exceptions",
737− "functions",
738− "maybe",
739− "prelude"
740− ]
741− },
742− "bifunctors": {
743− "type": "registry",
744− "version": "6.1.0",
745− "integrity": "sha256-CvZRb8uI75eKIqiYntR+k05Wk6tKlMS5w8sCA2AFna0=",
746− "dependencies": [
747− "const",
748− "either",
749− "newtype",
750− "prelude",
751− "tuples"
752− ]
753− },
754− "catenable-lists": {
755− "type": "registry",
756− "version": "7.0.0",
757− "integrity": "sha256-7+i/MHBLIRh604iCNT4ENWRWE/wuHIotER4kpdX9Ve0=",
758− "dependencies": [
759− "control",
760− "foldable-traversable",
761− "lists",
762− "maybe",
763− "prelude",
764− "tuples",
765− "unfoldable"
766− ]
767− },
768− "console": {
769− "type": "registry",
770− "version": "6.1.0",
771− "integrity": "sha256-3T5PPz1mATZmV0C4GzkCkpLlCa5xG7u8ZNgnLrrrI1Q=",
772− "dependencies": [
773− "effect",
774− "prelude"
775− ]
776− },
777− "const": {
778− "type": "registry",
779− "version": "6.0.0",
780− "integrity": "sha256-S+m0lQsxnji0z1XRUmkn+3RFuWJ5CXWuIr1u3i8Bfoc=",
781− "dependencies": [
782− "invariant",
783− "newtype",
784− "prelude"
785− ]
786− },
787− "contravariant": {
788− "type": "registry",
789− "version": "6.0.0",
790− "integrity": "sha256-nuckEWqL80NaPmR/l9AUyrfycNh18XcvlZV9PAamwqc=",
791− "dependencies": [
792− "const",
793− "either",
794− "newtype",
795− "prelude",
796− "tuples"
797− ]
798− },
799− "control": {
800− "type": "registry",
801− "version": "6.0.0",
802− "integrity": "sha256-0vxyYh5FL+ilxgLo954w1LqCUC0RSIivNNlb9CaNUQg=",
803− "dependencies": [
804− "newtype",
805− "prelude"
806− ]
807− },
808− "datetime": {
809− "type": "registry",
810− "version": "6.1.0",
811− "integrity": "sha256-tbv6eDXy6QOD7YSknxYSbsDF32OC2JrNbSBpzMs7Doc=",
812− "dependencies": [
813− "bifunctors",
814− "control",
815− "either",
816− "enums",
817− "foldable-traversable",
818− "functions",
819− "gen",
820− "integers",
821− "lists",
822− "maybe",
823− "newtype",
824− "numbers",
825− "ordered-collections",
826− "partial",
827− "prelude",
828− "tuples"
829− ]
830− },
831− "distributive": {
832− "type": "registry",
833− "version": "6.0.0",
834− "integrity": "sha256-netyJK4B32PDYHD0tF2AenKgo3urzbI770ycM0pArAo=",
835− "dependencies": [
836− "identity",
837− "newtype",
838− "prelude",
839− "tuples",
840− "type-equality"
841− ]
842− },
843− "effect": {
844− "type": "registry",
845− "version": "4.0.0",
846− "integrity": "sha256-DdqU3bncRTjgMVPPRlAuGHVeajSPEcRaRhNuio7if6s=",
847− "dependencies": [
848− "prelude"
849− ]
850− },
851− "either": {
852− "type": "registry",
853− "version": "6.1.0",
854− "integrity": "sha256-tBHx3PgtH4GZOApZCDkcM7szRTYJinrUrVGWazQCUUg=",
855− "dependencies": [
856− "control",
857− "invariant",
858− "maybe",
859− "prelude"
860− ]
861− },
862− "enums": {
863− "type": "registry",
864− "version": "6.0.1",
865− "integrity": "sha256-sdZOmLX5+5pASGpvVyT0vSD5BBYQjZiayjV5XiPutso=",
866− "dependencies": [
867− "control",
868− "either",
869− "gen",
870− "maybe",
871− "newtype",
872− "nonempty",
873− "partial",
874− "prelude",
875− "tuples",
876− "unfoldable"
877− ]
878− },
879− "exceptions": {
880− "type": "registry",
881− "version": "6.1.0",
882− "integrity": "sha256-nRrr+N0wk0TUFOWLR7e1n1oxcXo/X345bQUtoY9lW6A=",
883− "dependencies": [
884− "effect",
885− "either",
886− "maybe",
887− "prelude"
888− ]
889− },
890− "exists": {
891− "type": "registry",
892− "version": "6.0.0",
893− "integrity": "sha256-vtLbrWNaI+pzx/2fw2AQnCovoLtcosClIhjO26QKkh8=",
894− "dependencies": [
895− "unsafe-coerce"
896− ]
897− },
898− "foldable-traversable": {
899− "type": "registry",
900− "version": "6.0.0",
901− "integrity": "sha256-NGGWyCio/xjce6fPP7y3wwg7CheqllID6aJudDxbWhA=",
902− "dependencies": [
903− "bifunctors",
904− "const",
905− "control",
906− "either",
907− "functors",
908− "identity",
909− "maybe",
910− "newtype",
911− "orders",
912− "prelude",
913− "tuples"
914− ]
915− },
916− "foreign": {
917− "type": "registry",
918− "version": "7.0.0",
919− "integrity": "sha256-jiDRMVQfovGsbbA/FzbzYf6iWk9i2b5gNrIjz+gFfsI=",
920− "dependencies": [
921− "either",
922− "functions",
923− "integers",
924− "lists",
925− "maybe",
926− "prelude",
927− "strings",
928− "transformers",
929− "unsafe-coerce"
930− ]
931− },
932− "foreign-object": {
933− "type": "registry",
934− "version": "4.1.0",
935− "integrity": "sha256-x/Q7r80z/vmHRKvPwhYmHP/DnJciPtsyGfRTMzayKIU=",
936− "dependencies": [
937− "arrays",
938− "foldable-traversable",
939− "functions",
940− "gen",
941− "lists",
942− "maybe",
943− "prelude",
944− "st",
945− "tailrec",
946− "tuples",
947− "typelevel-prelude",
948− "unfoldable",
949− "unsafe-coerce"
950− ]
951− },
952− "free": {
953− "type": "registry",
954− "version": "7.1.0",
955− "integrity": "sha256-tUBInfUpRQEw4u94GWcYW7EYdmSdDHSRn88a+C0eltU=",
956− "dependencies": [
957− "catenable-lists",
958− "control",
959− "distributive",
960− "either",
961− "exists",
962− "foldable-traversable",
963− "invariant",
964− "lazy",
965− "maybe",
966− "prelude",
967− "tailrec",
968− "transformers",
969− "tuples",
970− "unsafe-coerce"
971− ]
972− },
973− "functions": {
974− "type": "registry",
975− "version": "6.0.0",
976− "integrity": "sha256-0jgtOr+SFO4UScKc40oA/9Vdcf2rLn3h3vZlY0KfqVo=",
977− "dependencies": [
978− "prelude"
979− ]
980− },
981− "functors": {
982− "type": "registry",
983− "version": "5.0.0",
984− "integrity": "sha256-W1o/4wcp6S3chodYL8uixlXK2mfr1L+SzM1R+whqUco=",
985− "dependencies": [
986− "bifunctors",
987− "const",
988− "contravariant",
989− "control",
990− "distributive",
991− "either",
992− "invariant",
993− "maybe",
994− "newtype",
995− "prelude",
996− "profunctor",
997− "tuples",
998− "unsafe-coerce"
999− ]
1000− },
1001− "gen": {
1002− "type": "registry",
1003− "version": "4.0.0",
1004− "integrity": "sha256-kyOateeOtQa07vfTMYeSuCKdelgKE1R65fwKkZ10wsY=",
1005− "dependencies": [
1006− "either",
1007− "foldable-traversable",
1008− "identity",
1009− "maybe",
1010− "newtype",
1011− "nonempty",
1012− "prelude",
1013− "tailrec",
1014− "tuples",
1015− "unfoldable"
1016− ]
1017− },
1018− "heterogeneous": {
1019− "type": "registry",
1020− "version": "0.7.0",
1021− "integrity": "sha256-pQwcFvhWL8noktxaoD7FlLs2F2lYf3QHoyO0VOWO3I0=",
1022− "dependencies": [
1023− "either",
1024− "foldable-traversable",
1025− "functors",
1026− "prelude",
1027− "record",
1028− "tuples",
1029− "variant"
1030− ]
1031− },
1032− "http-methods": {
1033− "type": "registry",
1034− "version": "6.0.0",
1035− "integrity": "sha256-S0UjrJSgwJxEsFHM/pX77dM8V4HfvvDqdHPckQrtxMs=",
1036− "dependencies": [
1037− "either",
1038− "prelude",
1039− "strings"
1040− ]
1041− },
1042− "identity": {
1043− "type": "registry",
1044− "version": "6.0.0",
1045− "integrity": "sha256-RY/iXPpxpqvKHYfJeFVNI1J06kohB9rbtWYuha8t0LE=",
1046− "dependencies": [
1047− "control",
1048− "invariant",
1049− "newtype",
1050− "prelude"
1051− ]
1052− },
1053− "integers": {
1054− "type": "registry",
1055− "version": "6.0.0",
1056− "integrity": "sha256-Y8yozC1uHRLcruobEyv5/bMKzE1BoKJ/olHv6o+bgm8=",
1057− "dependencies": [
1058− "maybe",
1059− "numbers",
1060− "prelude"
1061− ]
1062− },
1063− "invariant": {
1064− "type": "registry",
1065− "version": "6.0.0",
1066− "integrity": "sha256-HyoT1I5qIHRq8RSZji6zZDvqnpVjLw748utrKwZtqsw=",
1067− "dependencies": [
1068− "control",
1069− "prelude"
1070− ]
1071− },
1072− "js-bigints": {
1073− "type": "registry",
1074− "version": "2.2.1",
1075− "integrity": "sha256-r1rE6Olv5b3JrP32lY2uKJ+eBRTsrVnC983K8VWwqe4=",
1076− "dependencies": [
1077− "integers",
1078− "maybe",
1079− "prelude"
1080− ]
1081− },
1082− "js-date": {
1083− "type": "registry",
1084− "version": "8.0.0",
1085− "integrity": "sha256-DNQrp4xYc8o80jtxo0aOkUeXGi6h0Xidkk2ZQ8Y4l5Y=",
1086− "dependencies": [
1087− "datetime",
1088− "effect",
1089− "enums",
1090− "foreign",
1091− "functions",
1092− "integers",
1093− "maybe",
1094− "prelude"
1095− ]
1096− },
1097− "js-promise": {
1098− "type": "registry",
1099− "version": "1.0.0",
1100− "integrity": "sha256-u+6+ofIwT5XhT2fbMu4dShwkyWGfPRt9642Cnzw/Wvk=",
1101− "dependencies": [
1102− "effect",
1103− "exceptions",
1104− "foldable-traversable",
1105− "functions",
1106− "maybe",
1107− "newtype",
1108− "prelude"
1109− ]
1110− },
1111− "js-promise-aff": {
1112− "type": "registry",
1113− "version": "1.0.0",
1114− "integrity": "sha256-gEKAdNnTWjXS+tTp/AhYl/ezloKfuoQFcBKrdOx2USo=",
1115− "dependencies": [
1116− "aff",
1117− "control",
1118− "effect",
1119− "either",
1120− "exceptions",
1121− "foreign",
1122− "js-promise",
1123− "maybe",
1124− "prelude",
1125− "transformers"
1126− ]
1127− },
1128− "lazy": {
1129− "type": "registry",
1130− "version": "6.0.0",
1131− "integrity": "sha256-MHRC+1Pc+AjortBp4b/e1e6KpEpsDwaJBNBB7TOL+1I=",
1132− "dependencies": [
1133− "control",
1134− "foldable-traversable",
1135− "invariant",
1136− "prelude"
1137− ]
1138− },
1139− "lists": {
1140− "type": "registry",
1141− "version": "7.0.0",
1142− "integrity": "sha256-/mGd7wLoU+wsTcqii7SSUByxsvwvjnu2PjwAD47yYb4=",
1143− "dependencies": [
1144− "bifunctors",
1145− "control",
1146− "foldable-traversable",
1147− "lazy",
1148− "maybe",
1149− "newtype",
1150− "nonempty",
1151− "partial",
1152− "prelude",
1153− "tailrec",
1154− "tuples",
1155− "unfoldable"
1156− ]
1157− },
1158− "maybe": {
1159− "type": "registry",
1160− "version": "6.0.0",
1161− "integrity": "sha256-8X6XEgtZ5lqfkEuc1hWOofnL6mwhk8gSJTxwvbR/SbY=",
1162− "dependencies": [
1163− "control",
1164− "invariant",
1165− "newtype",
1166− "prelude"
1167− ]
1168− },
1169− "media-types": {
1170− "type": "registry",
1171− "version": "6.0.0",
1172− "integrity": "sha256-3Wyq+vfqJFTmy1k6DGVnXStxtZxgK631wusDPzlFVxg=",
1173− "dependencies": [
1174− "newtype",
1175− "prelude"
1176− ]
1177− },
1178− "newtype": {
1179− "type": "registry",
1180− "version": "5.0.0",
1181− "integrity": "sha256-dt6w0cty4OS+Pjt9fsh3iU6ottqSU83mdAZkaEEvo6c=",
1182− "dependencies": [
1183− "prelude",
1184− "safe-coerce"
1185− ]
1186− },
1187− "nonempty": {
1188− "type": "registry",
1189− "version": "7.0.0",
1190− "integrity": "sha256-Ctkq8/+KiGqCE53Y/LvibA/coy7ev9HtRZU+GYS7yfQ=",
1191− "dependencies": [
1192− "control",
1193− "foldable-traversable",
1194− "maybe",
1195− "prelude",
1196− "tuples",
1197− "unfoldable"
1198− ]
1199− },
1200− "nullable": {
1201− "type": "registry",
1202− "version": "6.0.0",
1203− "integrity": "sha256-CDAZk+L9Sc0GCFTkE8n+qdp4Ys8avvnkU+m2hVALt7M=",
1204− "dependencies": [
1205− "functions",
1206− "maybe",
1207− "prelude"
1208− ]
1209− },
1210− "numbers": {
1211− "type": "registry",
1212− "version": "9.0.1",
1213− "integrity": "sha256-ZydYckgwRAnRaFC7pu6fADhzdwX+UqHZrfJyet64zls=",
1214− "dependencies": [
1215− "functions",
1216− "maybe",
1217− "prelude"
1218− ]
1219− },
1220− "ordered-collections": {
1221− "type": "registry",
1222− "version": "3.2.0",
1223− "integrity": "sha256-NaEXc2cz/7lJfxPWEja4XlZbGxuRqJwFKQJjsJf51kQ=",
1224− "dependencies": [
1225− "arrays",
1226− "control",
1227− "foldable-traversable",
1228− "functions",
1229− "gen",
1230− "lists",
1231− "maybe",
1232− "newtype",
1233− "partial",
1234− "prelude",
1235− "safe-coerce",
1236− "tailrec",
1237− "tuples",
1238− "unfoldable"
1239− ]
1240− },
1241− "orders": {
1242− "type": "registry",
1243− "version": "6.0.0",
1244− "integrity": "sha256-nSMxZK7wxyDULkjZqvMyB71mVo6b0AyLLHosDVbv1XM=",
1245− "dependencies": [
1246− "newtype",
1247− "prelude"
1248− ]
1249− },
1250− "parallel": {
1251− "type": "registry",
1252− "version": "7.0.0",
1253− "integrity": "sha256-bAGpVhrFz2IyHkA4MR7+tE/19FZ7dzYBdYPgK5svkqw=",
1254− "dependencies": [
1255− "control",
1256− "effect",
1257− "either",
1258− "foldable-traversable",
1259− "functors",
1260− "maybe",
1261− "newtype",
1262− "prelude",
1263− "profunctor",
1264− "refs",
1265− "transformers"
1266− ]
1267− },
1268− "partial": {
1269− "type": "registry",
1270− "version": "4.0.0",
1271− "integrity": "sha256-mdrlJBAgFMh79hNRW39uv7c+BwnnaOrAMmdh7+VhEhs=",
1272− "dependencies": []
1273− },
1274− "prelude": {
1275− "type": "registry",
1276− "version": "6.0.2",
1277− "integrity": "sha256-IqykrDRquGUD/LRSad7y75hZv3bUsGQ4knc8tdez8Qw=",
1278− "dependencies": []
1279− },
1280− "profunctor": {
1281− "type": "registry",
1282− "version": "6.0.1",
1283− "integrity": "sha256-Ow0mJC+PIIRq/a3mzCWY5vxuTgR/e+Ee68+mWeSRnWY=",
1284− "dependencies": [
1285− "control",
1286− "distributive",
1287− "either",
1288− "exists",
1289− "invariant",
1290− "newtype",
1291− "prelude",
1292− "tuples"
1293− ]
1294− },
1295− "random": {
1296− "type": "registry",
1297− "version": "6.0.0",
1298− "integrity": "sha256-7sZRo6P9UIiXDZ9s2QL9T8h3wlIwLdQrR4+oZKOijQw=",
1299− "dependencies": [
1300− "effect",
1301− "integers",
1302− "prelude"
1303− ]
1304− },
1305− "record": {
1306− "type": "registry",
1307− "version": "4.0.0",
1308− "integrity": "sha256-WQ8fqp4X1wTA70J4qUcPW4Z7DCwx6e5KJnT/EuTdWSc=",
1309− "dependencies": [
1310− "functions",
1311− "prelude",
1312− "unsafe-coerce"
1313− ]
1314− },
1315− "record-studio": {
1316− "type": "registry",
1317− "version": "1.0.4",
1318− "integrity": "sha256-CRnFvSXpDDzio+buibUxFU9zPZDd7/lzrYC1Ya3FuIw=",
1319− "dependencies": [
1320− "heterogeneous",
1321− "lists",
1322− "prelude",
1323− "record",
1324− "typelevel-prelude",
1325− "unsafe-coerce"
1326− ]
1327− },
1328− "ref-lifted": {
1329− "type": "local",
1330− "path": "/Users/mark/code/purescript-ref-lifted",
1331− "dependencies": [
1332− "effect",
1333− "prelude",
1334− "refs"
1335− ]
1336− },
1337− "refs": {
1338− "type": "registry",
1339− "version": "6.0.0",
1340− "integrity": "sha256-KKD5G9dR2SauuDGULUJsxjqDOEi1Jqm+2Sq5U2mY8YU=",
1341− "dependencies": [
1342− "effect",
1343− "prelude"
1344− ]
1345− },
1346− "safe-coerce": {
1347− "type": "registry",
1348− "version": "2.0.0",
1349− "integrity": "sha256-EJrxtKt5xC7TYjBZrSBO/J7Aw3DmtilYjt4olpAXWVc=",
1350− "dependencies": [
1351− "unsafe-coerce"
1352− ]
1353− },
1354− "st": {
1355− "type": "registry",
1356− "version": "6.2.0",
1357− "integrity": "sha256-WI4MEkkwUd4pnwZQ3G8VKWgX5t1RSNJludm5e9boaRo=",
1358− "dependencies": [
1359− "effect",
1360− "partial",
1361− "prelude",
1362− "tailrec",
1363− "unsafe-coerce"
1364− ]
1365− },
1366− "strings": {
1367− "type": "registry",
1368− "version": "6.0.1",
1369− "integrity": "sha256-FNto2hoNW5Da6W6crIk77YXMagBvOGHZE1kO7eZ1vLM=",
1370− "dependencies": [
1371− "arrays",
1372− "control",
1373− "either",
1374− "enums",
1375− "foldable-traversable",
1376− "gen",
1377− "integers",
1378− "maybe",
1379− "newtype",
1380− "nonempty",
1381− "partial",
1382− "prelude",
1383− "tailrec",
1384− "tuples",
1385− "unfoldable",
1386− "unsafe-coerce"
1387− ]
1388− },
1389− "tailrec": {
1390− "type": "registry",
1391− "version": "6.1.0",
1392− "integrity": "sha256-xrorCfP0xV1wubs27idQCHQqdvSUuqmmL/am7Ji1wVU=",
1393− "dependencies": [
1394− "bifunctors",
1395− "effect",
1396− "either",
1397− "identity",
1398− "maybe",
1399− "partial",
1400− "prelude",
1401− "refs"
1402− ]
1403− },
1404− "transformers": {
1405− "type": "registry",
1406− "version": "6.1.0",
1407− "integrity": "sha256-QJmgNT/y7ljPHSsXaW4zxF/982BFiQ90KNL1g/NtEOQ=",
1408− "dependencies": [
1409− "control",
1410− "distributive",
1411− "effect",
1412− "either",
1413− "exceptions",
1414− "foldable-traversable",
1415− "identity",
1416− "lazy",
1417− "maybe",
1418− "newtype",
1419− "prelude",
1420− "st",
1421− "tailrec",
1422− "tuples",
1423− "unfoldable"
1424− ]
1425− },
1426− "tuples": {
1427− "type": "registry",
1428− "version": "7.0.0",
1429− "integrity": "sha256-BKy7EfK8S1KeTCwyWqyW1wwcpSAQuh3IsczSVgS6fzY=",
1430− "dependencies": [
1431− "control",
1432− "invariant",
1433− "prelude"
1434− ]
1435− },
1436− "type-equality": {
1437− "type": "registry",
1438− "version": "4.0.1",
1439− "integrity": "sha256-BBqYOSnAKmayRvR5mtZoCf4SlYs2ipatD/+5nQx73aw=",
1440− "dependencies": []
1441− },
1442− "typelevel-prelude": {
1443− "type": "registry",
1444− "version": "7.0.0",
1445− "integrity": "sha256-+pHi14/40mTj/+lmCWqL0T7iXIXD1+NSg/KItyyoB0A=",
1446− "dependencies": [
1447− "prelude",
1448− "type-equality"
1449− ]
1450− },
1451− "uncurried-transformers": {
1452− "type": "registry",
1453− "version": "1.1.0",
1454− "integrity": "sha256-AqqBMzVLzozVoDorE3QcVVRDkLd7O3624mRHCDnPhaI=",
1455− "dependencies": [
1456− "control",
1457− "effect",
1458− "either",
1459− "functions",
1460− "identity",
1461− "prelude",
1462− "safe-coerce",
1463− "tailrec",
1464− "transformers",
1465− "tuples"
1466− ]
1467− },
1468− "unfoldable": {
1469− "type": "registry",
1470− "version": "6.0.0",
1471− "integrity": "sha256-BK/6By1sKp/ztk+9CF3q43SV0QDTLVdsGqFerKHSmbg=",
1472− "dependencies": [
1473− "foldable-traversable",
1474− "maybe",
1475− "partial",
1476− "prelude",
1477− "tuples"
1478− ]
1479− },
1480− "unsafe-coerce": {
1481− "type": "registry",
1482− "version": "6.0.0",
1483− "integrity": "sha256-0L1QsaY20OILjfU5TV72d3U/tSjhmL9hJ32WMp857Lk=",
1484− "dependencies": []
1485− },
1486− "variant": {
1487− "type": "registry",
1488− "version": "8.0.0",
1489− "integrity": "sha256-Ydrgl8liWEwYQOWW76PdZtg3vxKpoKgsnmFyTLgZkoU=",
1490− "dependencies": [
1491− "control",
1492− "enums",
1493− "foldable-traversable",
1494− "lists",
1495− "maybe",
1496− "partial",
1497− "prelude",
1498− "type-equality",
1499− "unsafe-coerce"
1500− ]
1501− },
1502− "web-events": {
1503− "type": "registry",
1504− "version": "4.0.0",
1505− "integrity": "sha256-wZNeldG0OU+8qGycUJ9e6vxf1/GC4Ep4OlRMhvjs9LA=",
1506− "dependencies": [
1507− "datetime",
1508− "effect",
1509− "enums",
1510− "foreign",
1511− "functions",
1512− "maybe",
1513− "newtype",
1514− "nullable",
1515− "prelude",
1516− "unsafe-coerce"
1517− ]
1518− },
1519− "web-fetch": {
1520− "type": "registry",
1521− "version": "4.0.1",
1522− "integrity": "sha256-04+Kgtwk6BYT9WJfmIkYtwgXbfe8TDKP0tTBMcu+jBw=",
1523− "dependencies": [
1524− "arraybuffer-types",
1525− "arrays",
1526− "effect",
1527− "foldable-traversable",
1528− "foreign-object",
1529− "functions",
1530− "http-methods",
1531− "js-promise",
1532− "maybe",
1533− "newtype",
1534− "nullable",
1535− "prelude",
1536− "record",
1537− "tuples",
1538− "typelevel-prelude",
1539− "unfoldable",
1540− "web-file",
1541− "web-streams"
1542− ]
1543− },
1544− "web-file": {
1545− "type": "registry",
1546− "version": "4.0.0",
1547− "integrity": "sha256-txsdkeWBKU/+5v9m5c6kysgJQSDyV1r7MHaeT4STpjQ=",
1548− "dependencies": [
1549− "datetime",
1550− "effect",
1551− "enums",
1552− "foreign",
1553− "integers",
1554− "maybe",
1555− "media-types",
1556− "nullable",
1557− "numbers",
1558− "partial",
1559− "prelude",
1560− "tuples",
1561− "unfoldable",
1562− "unsafe-coerce",
1563− "web-events"
1564− ]
1565− },
1566− "web-streams": {
1567− "type": "registry",
1568− "version": "4.0.0",
1569− "integrity": "sha256-iKQbHh/3wNOWyGTZmWJZ1Yuri9J/wQIJKXcxrELaHVo=",
1570− "dependencies": [
1571− "arraybuffer-types",
1572− "effect",
1573− "exceptions",
1574− "js-promise",
1575− "maybe",
1576− "nullable",
1577− "prelude",
1578− "tuples"
1579− ]
1580− },
1581− "yoga-bun-yoga": {
1582− "type": "local",
1583− "path": "/Users/mark/code/purescript-yoga-bun-yoga",
1584− "dependencies": [
1585− "aff",
1586− "effect",
1587− "either",
1588− "exceptions",
1589− "foreign",
1590− "foreign-object",
1591− "js-promise",
1592− "js-promise-aff",
1593− "prelude",
1594− "web-fetch",
1595− "yoga-json"
1596− ]
1597− },
1598− "yoga-json": {
1599− "type": "registry",
1600− "version": "5.2.0",
1601− "integrity": "sha256-M+kkz6E1Gf+C4bKnhqoBFIoVcc74loi3rLULePbB204=",
1602− "dependencies": [
1603− "arrays",
1604− "bifunctors",
1605− "control",
1606− "datetime",
1607− "effect",
1608− "either",
1609− "exceptions",
1610− "foldable-traversable",
1611− "foreign",
1612− "foreign-object",
1613− "free",
1614− "identity",
1615− "integers",
1616− "js-bigints",
1617− "js-date",
1618− "lists",
1619− "maybe",
1620− "newtype",
1621− "nullable",
1622− "numbers",
1623− "ordered-collections",
1624− "partial",
1625− "prelude",
1626− "record",
1627− "safe-coerce",
1628− "strings",
1629− "transformers",
1630− "tuples",
1631− "typelevel-prelude",
1632− "unsafe-coerce",
1633− "variant",
1634− "yoga-tree"
1635− ]
1636− },
1637− "yoga-om": {
1638− "type": "local",
1639− "path": "/Users/mark/code/purescript-yoga-om",
1640− "dependencies": [
1641− "aff",
1642− "aff-retry",
1643− "avar",
1644− "console",
1645− "control",
1646− "datetime",
1647− "effect",
1648− "either",
1649− "exceptions",
1650− "functions",
1651− "maybe",
1652− "newtype",
1653− "parallel",
1654− "prelude",
1655− "record",
1656− "record-studio",
1657− "ref-lifted",
1658− "refs",
1659− "tailrec",
1660− "transformers",
1661− "tuples",
1662− "typelevel-prelude",
1663− "uncurried-transformers",
1664− "unsafe-coerce",
1665− "variant"
1666− ]
1667− },
1668− "yoga-om-layer": {
1669− "type": "local",
1670− "path": "/Users/mark/code/purescript-yoga-om-layer",
1671− "dependencies": [
1672− "aff",
1673− "aff-retry",
1674− "arrays",
1675− "effect",
1676− "exceptions",
1677− "foldable-traversable",
1678− "maybe",
1679− "nullable",
1680− "ordered-collections",
1681− "parallel",
1682− "prelude",
1683− "record",
1684− "record-studio",
1685− "ref-lifted",
1686− "refs",
1687− "transformers",
1688− "type-equality",
1689− "typelevel-prelude",
1690− "unsafe-coerce",
1691− "variant",
1692− "yoga-om"
1693− ]
1694− },
1695− "yoga-sql-types": {
1696− "type": "local",
1697− "path": "/Users/mark/code/purescript-yoga-sql-types",
1698− "dependencies": [
1699− "arrays",
1700− "bifunctors",
1701− "either",
1702− "foldable-traversable",
1703− "foreign",
1704− "foreign-object",
1705− "heterogeneous",
1706− "identity",
1707− "lists",
1708− "maybe",
1709− "newtype",
1710− "nullable",
1711− "ordered-collections",
1712− "partial",
1713− "prelude",
1714− "record",
1715− "safe-coerce",
1716− "transformers",
1717− "tuples",
1718− "typelevel-prelude",
1719− "unsafe-coerce",
1720− "yoga-json"
1721− ]
1722− },
1723− "yoga-sqlite": {
1724− "type": "local",
1725− "path": "/Users/mark/code/purescript-yoga-sqlite",
1726− "dependencies": [
1727− "aff",
1728− "arrays",
1729− "effect",
1730− "either",
1731− "foldable-traversable",
1732− "js-promise",
1733− "js-promise-aff",
1734− "maybe",
1735− "newtype",
1736− "nullable",
1737− "prelude",
1738− "unsafe-coerce",
1739− "yoga-sql-types"
1740− ]
1741− },
1742− "yoga-sqlite-om": {
1743− "type": "local",
1744− "path": "/Users/mark/code/purescript-yoga-sqlite-om",
1745− "dependencies": [
1746− "console",
1747− "effect",
1748− "prelude",
1749− "yoga-om",
1750− "yoga-om-layer",
1751− "yoga-sqlite"
1752− ]
1753− },
1754− "yoga-tree": {
1755− "type": "registry",
1756− "version": "1.0.0",
1757− "integrity": "sha256-s8rIVEQkRi1VrMc92T9ceXcY0X6r5A4QqT6f7GgmDQI=",
1758− "dependencies": [
1759− "arrays",
1760− "control",
1761− "foldable-traversable",
1762− "free",
1763− "maybe",
1764− "prelude",
1765− "tailrec"
1766− ]
1767− }
1768− }
1769−}
signal-backend/spago.yamldeleted−51
@@ -1,51 +0,0 @@
1−workspace:
2− packageSet:
3− registry: 73.0.0
4− extraPackages:
5− ref-lifted:
6− path: /Users/mark/code/purescript-ref-lifted
7− yoga-om:
8− path: /Users/mark/code/purescript-yoga-om
9− yoga-om-layer:
10− path: /Users/mark/code/purescript-yoga-om-layer
11− yoga-bun-yoga:
12− path: /Users/mark/code/purescript-yoga-bun-yoga
13− yoga-sqlite:
14− path: /Users/mark/code/purescript-yoga-sqlite
15− yoga-sqlite-om:
16− path: /Users/mark/code/purescript-yoga-sqlite-om
17− yoga-sql-types:
18− path: /Users/mark/code/purescript-yoga-sql-types
19− yoga-json: "5.2.0"
20−
21−package:
22− name: signal-backend
23− dependencies:
24− - aff
25− - arraybuffer-types
26− - arrays
27− - avar
28− - console
29− - effect
30− - either
31− - foldable-traversable
32− - foreign
33− - foreign-object
34− - js-promise
35− - js-promise-aff
36− - maybe
37− - newtype
38− - nullable
39− - prelude
40− - refs
41− - strings
42− - tuples
43− - unsafe-coerce
44− - variant
45− - web-fetch
46− - yoga-json
47− - yoga-om
48− - yoga-om-layer
49− - yoga-bun-yoga
50− - yoga-sqlite
51− - yoga-sqlite-om
signal-backend/src/Main.jsdeleted−26
@@ -1,26 +0,0 @@
1−export const sendToWs = (ws) => (msg) => () => {
2− ws.send(JSON.stringify({ type: "qrcode", url: msg }));
3−};
4−
5−export const serveWithWsImpl = (options) => {
6− const server = Bun.serve({
7− port: options.port,
8− hostname: options.host,
9− fetch(req, server) {
10− if (server.upgrade(req)) return undefined;
11− return new Response("Signal Backend", { status: 200 });
12− },
13− websocket: {
14− open(ws) {
15− options.wsOpen(ws);
16− },
17− message(ws, msg) {
18− options.wsMessage(ws, msg);
19− },
20− close(ws, code) {
21− options.wsClose(ws, code);
22− },
23− },
24− });
25− return server;
26−};
signal-backend/src/Main.pursdeleted−53
@@ -1,53 +0,0 @@
1−module Main where
2−
3−import Prelude
4−
5−import Data.Maybe (Maybe(..))
6−import Effect (Effect)
7−import Effect.Aff (launchAff_)
8−import Effect.Class (liftEffect)
9−import Effect.Console as Console
10−import Effect.Ref as Ref
11−import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2, runEffectFn1)
12−import Foreign (Foreign)
13−import Signal.Net as Net
14−import Signal.Provisioning as Provisioning
15−
16−foreign import sendToWs :: Foreign -> String -> Effect Unit
17−foreign import serveWithWsImpl :: EffectFn1
18− { port :: Int
19− , host :: String
20− , wsOpen :: EffectFn1 Foreign Unit
21− , wsMessage :: EffectFn2 Foreign Foreign Unit
22− , wsClose :: EffectFn2 Foreign Foreign Unit
23− }
24− Unit
25−
26−main :: Effect Unit
27−main = launchAff_ do
28− liftEffect $ Console.log "Signal backend starting..."
29−
30− net <- liftEffect $ Net.createNet Net.Production "SignalPureScript/0.1"
31− wsClientRef <- liftEffect $ Ref.new (Nothing :: Maybe Foreign)
32−
33− liftEffect $ runEffectFn1 serveWithWsImpl
34− { port: 8765
35− , host: "127.0.0.1"
36− , wsOpen: mkEffectFn1 \ws -> do
37− Console.log "WebSocket client connected"
38− Ref.write (Just ws) wsClientRef
39− launchAff_ do
40− msg <- Provisioning.provision net \url -> liftEffect do
41− Console.log $ "QR URL: " <> url
42− mWs <- Ref.read wsClientRef
43− case mWs of
44− Just ws' -> sendToWs ws' url
45− Nothing -> pure unit
46− liftEffect $ Console.log $ "Provisioned! Number: " <> show msg.number
47− , wsMessage: mkEffectFn2 \_ _ -> pure unit
48− , wsClose: mkEffectFn2 \_ _ -> do
49− Console.log "WebSocket client disconnected"
50− Ref.write Nothing wsClientRef
51− }
52−
53− liftEffect $ Console.log "Server running on ws://127.0.0.1:8765"
signal-backend/src/Signal/Keys.jsdeleted−15
@@ -1,15 +0,0 @@
1−import {
2− PrivateKey,
3− PublicKey,
4−} from "@signalapp/libsignal-client";
5−
6−export const generatePrivateKeyImpl = () => PrivateKey.generate();
7−
8−export const getPublicKeyImpl = (privateKey) => privateKey.getPublicKey();
9−
10−export const serializePublicKeyImpl = (publicKey) => publicKey.serialize();
11−
12−export const agreeImpl = ({ privateKey, otherKey }) =>
13− privateKey.agree(otherKey);
14−
15−export const deserializePublicKeyImpl = (buf) => PublicKey.deserialize(buf);
signal-backend/src/Signal/Keys.pursdeleted−32
@@ -1,32 +0,0 @@
1−module Signal.Keys where
2−
3−import Prelude
4−
5−import Effect (Effect)
6−import Effect.Uncurried (EffectFn1, runEffectFn1)
7−import Data.ArrayBuffer.Types (Uint8Array)
8−
9−foreign import data PrivateKey :: Type
10−foreign import data PublicKey :: Type
11−foreign import data IdentityKeyPair :: Type
12−
13−foreign import generatePrivateKeyImpl :: Effect PrivateKey
14−foreign import getPublicKeyImpl :: EffectFn1 PrivateKey PublicKey
15−foreign import serializePublicKeyImpl :: EffectFn1 PublicKey Uint8Array
16−foreign import agreeImpl :: EffectFn1 { privateKey :: PrivateKey, otherKey :: PublicKey } Uint8Array
17−foreign import deserializePublicKeyImpl :: EffectFn1 Uint8Array PublicKey
18−
19−generatePrivateKey :: Effect PrivateKey
20−generatePrivateKey = generatePrivateKeyImpl
21−
22−getPublicKey :: PrivateKey -> Effect PublicKey
23−getPublicKey = runEffectFn1 getPublicKeyImpl
24−
25−serializePublicKey :: PublicKey -> Effect Uint8Array
26−serializePublicKey = runEffectFn1 serializePublicKeyImpl
27−
28−agree :: PrivateKey -> PublicKey -> Effect Uint8Array
29−agree privateKey otherKey = runEffectFn1 agreeImpl { privateKey, otherKey }
30−
31−deserializePublicKey :: Uint8Array -> Effect PublicKey
32−deserializePublicKey = runEffectFn1 deserializePublicKeyImpl
signal-backend/src/Signal/Net.jsdeleted−15
@@ -1,15 +0,0 @@
1−import * as SignalNet from "@signalapp/libsignal-client/dist/net.js";
2−
3−export const createNetImpl = ({ env, userAgent }) =>
4− new SignalNet.Net({ env, userAgent });
5−
6−export const connectProvisioningImpl = (net, listener) =>
7− net.connectProvisioning({
8− onReceivedAddress: (address, ack) => listener.onReceivedAddress(address, ack),
9− onReceivedEnvelope: (envelope, ack) => listener.onReceivedEnvelope(envelope, ack),
10− onConnectionInterrupted: (cause) => listener.onConnectionInterrupted(cause || {}),
11− });
12−
13−export const disconnectProvisioningImpl = (conn) => conn.disconnect();
14−
15−export const ackImpl = (ack, statusCode) => ack.send(statusCode);
signal-backend/src/Signal/Net.pursdeleted−56
@@ -1,56 +0,0 @@
1−module Signal.Net where
2−
3−import Prelude
4−
5−import Data.ArrayBuffer.Types (Uint8Array)
6−import Effect (Effect)
7−import Effect.Aff (Aff)
8−import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2, runEffectFn1, runEffectFn2)
9−import Promise (Promise)
10−import Promise.Aff (toAffE) as Promise
11−
12−foreign import data SignalNet :: Type
13−foreign import data ProvisioningConnection :: Type
14−foreign import data ChatServerMessageAck :: Type
15−
16−data Environment = Staging | Production
17−
18−foreign import createNetImpl :: EffectFn1 { env :: Int, userAgent :: String } SignalNet
19−
20−createNet :: Environment -> String -> Effect SignalNet
21−createNet env userAgent = runEffectFn1 createNetImpl { env: envToInt env, userAgent }
22− where
23− envToInt Staging = 0
24− envToInt Production = 1
25−
26−type ProvisioningListenerImpl =
27− { onReceivedAddress :: EffectFn2 String ChatServerMessageAck Unit
28− , onReceivedEnvelope :: EffectFn2 Uint8Array ChatServerMessageAck Unit
29− , onConnectionInterrupted :: EffectFn1 {} Unit
30− }
31−
32−type ProvisioningListener =
33− { onReceivedAddress :: String -> ChatServerMessageAck -> Effect Unit
34− , onReceivedEnvelope :: Uint8Array -> ChatServerMessageAck -> Effect Unit
35− , onConnectionInterrupted :: Effect Unit
36− }
37−
38−foreign import connectProvisioningImpl :: EffectFn2 SignalNet ProvisioningListenerImpl (Promise ProvisioningConnection)
39−foreign import disconnectProvisioningImpl :: EffectFn1 ProvisioningConnection (Promise Unit)
40−foreign import ackImpl :: EffectFn2 ChatServerMessageAck Int Unit
41−
42−connectProvisioning :: SignalNet -> ProvisioningListener -> Aff ProvisioningConnection
43−connectProvisioning net listener = do
44− let
45− listenerImpl =
46− { onReceivedAddress: mkEffectFn2 listener.onReceivedAddress
47− , onReceivedEnvelope: mkEffectFn2 listener.onReceivedEnvelope
48− , onConnectionInterrupted: mkEffectFn1 \_ -> listener.onConnectionInterrupted
49− }
50− runEffectFn2 connectProvisioningImpl net listenerImpl # Promise.toAffE
51−
52−disconnectProvisioning :: ProvisioningConnection -> Aff Unit
53−disconnectProvisioning conn = runEffectFn1 disconnectProvisioningImpl conn # Promise.toAffE
54−
55−ack :: ChatServerMessageAck -> Int -> Effect Unit
56−ack = runEffectFn2 ackImpl
signal-backend/src/Signal/Provisioning.pursdeleted−45
@@ -1,45 +0,0 @@
1−module Signal.Provisioning where
2−
3−import Prelude
4−
5−import Effect.Aff (Aff)
6−import Effect.Aff.AVar as AffAVar
7−import Effect.AVar as AVar
8−import Effect.Class (liftEffect)
9−import Effect.Console as Console
10−import Signal.Keys as Keys
11−import Signal.Net as Net
12−import Signal.Provisioning.Cipher (ProvisionMessage)
13−import Signal.Provisioning.Cipher as Cipher
14−import Signal.Provisioning.QRCode as QRCode
15−
16−provision :: Net.SignalNet -> (String -> Aff Unit) -> Aff ProvisionMessage
17−provision net onQRCode = do
18− privateKey <- liftEffect Keys.generatePrivateKey
19− publicKey <- liftEffect $ Keys.getPublicKey privateKey
20− publicKeySerialized <- liftEffect $ Keys.serializePublicKey publicKey
21− messageVar <- liftEffect AVar.empty
22− urlVar <- liftEffect AVar.empty
23−
24− let
25− listener =
26− { onReceivedAddress: \address ack -> do
27− url <- QRCode.buildProvisioningUrl address publicKeySerialized
28− Console.log "Provisioning: QR code ready"
29− void $ AVar.tryPut url urlVar
30− Net.ack ack 200
31− , onReceivedEnvelope: \envelope ack -> do
32− Console.log "Provisioning: decrypting envelope..."
33− msg <- Cipher.decryptProvisionEnvelope envelope privateKey
34− void $ AVar.tryPut msg messageVar
35− Net.ack ack 200
36− , onConnectionInterrupted:
37− Console.log "Provisioning: connection closed"
38− }
39−
40− _conn <- Net.connectProvisioning net listener
41−
42− url <- AffAVar.read urlVar
43− onQRCode url
44−
45− AffAVar.read messageVar
signal-backend/src/Signal/Provisioning/Cipher.jsdeleted−144
@@ -1,144 +0,0 @@
1−import { PrivateKey, PublicKey, hkdf } from "@signalapp/libsignal-client";
2−import { createDecipheriv, createHmac } from "node:crypto";
3−
4−// Protobuf decoding for ProvisionEnvelope and ProvisionMessage
5−// These are simple enough to decode manually without a protobuf library
6−
7−function decodeVarint(buf, offset) {
8− let result = 0;
9− let shift = 0;
10− let pos = offset;
11− while (pos < buf.length) {
12− const byte = buf[pos];
13− result |= (byte & 0x7f) << shift;
14− pos++;
15− if ((byte & 0x80) === 0) break;
16− shift += 7;
17− }
18− return { value: result, bytesRead: pos - offset };
19−}
20−
21−function decodeProtobuf(buf) {
22− const fields = {};
23− let pos = 0;
24− while (pos < buf.length) {
25− const { value: tag, bytesRead: tagBytes } = decodeVarint(buf, pos);
26− pos += tagBytes;
27− const fieldNumber = tag >>> 3;
28− const wireType = tag & 0x7;
29−
30− if (wireType === 0) {
31− // varint
32− const { value, bytesRead } = decodeVarint(buf, pos);
33− pos += bytesRead;
34− fields[fieldNumber] = value;
35− } else if (wireType === 2) {
36− // length-delimited
37− const { value: len, bytesRead } = decodeVarint(buf, pos);
38− pos += bytesRead;
39− fields[fieldNumber] = buf.slice(pos, pos + len);
40− pos += len;
41− } else {
42− break; // unknown wire type
43− }
44− }
45− return fields;
46−}
47−
48−function decodeProvisionEnvelope(buf) {
49− const fields = decodeProtobuf(buf);
50− return {
51− publicKey: fields[1] || null,
52− body: fields[2] || null,
53− };
54−}
55−
56−function decodeProvisionMessage(buf) {
57− const fields = decodeProtobuf(buf);
58− return {
59− aciIdentityKeyPublic: fields[1] || null,
60− aciIdentityKeyPrivate: fields[2] || null,
61− number: fields[3] ? new TextDecoder().decode(fields[3]) : null,
62− provisioningCode: fields[4]
63− ? new TextDecoder().decode(fields[4])
64− : null,
65− userAgent: fields[5] ? new TextDecoder().decode(fields[5]) : null,
66− profileKey: fields[6] || null,
67− readReceipts: fields[7] || null,
68− aci: fields[8] ? new TextDecoder().decode(fields[8]) : null,
69− provisioningVersion: fields[9] ?? null,
70− pni: fields[10] ? new TextDecoder().decode(fields[10]) : null,
71− pniIdentityKeyPublic: fields[11] || null,
72− pniIdentityKeyPrivate: fields[12] || null,
73− masterKey: fields[13] || null,
74− ephemeralBackupKey: fields[14] || null,
75− accountEntropyPool: fields[15]
76− ? new TextDecoder().decode(fields[15])
77− : null,
78− mediaRootBackupKey: fields[16] || null,
79− };
80−}
81−
82−export const decryptProvisionEnvelopeImpl = ({ envelope, privateKey }) => {
83− // 1. Decode the ProvisionEnvelope protobuf
84− const env = decodeProvisionEnvelope(
85− envelope instanceof Uint8Array ? envelope : new Uint8Array(envelope)
86− );
87− if (!env.publicKey || !env.body) {
88− throw new Error("Invalid provision envelope: missing publicKey or body");
89− }
90−
91− // 2. Verify version byte
92− const message = new Uint8Array(env.body);
93− if (message[0] !== 1) {
94− throw new Error(`Unsupported provisioning version: ${message[0]}`);
95− }
96−
97− // 3. Extract iv, ciphertext, mac
98− const iv = message.slice(1, 17);
99− const mac = message.slice(message.length - 32);
100− const ivAndCiphertext = message.slice(0, message.length - 32);
101− const ciphertext = message.slice(17, message.length - 32);
102−
103− // 4. ECDH agreement
104− const theirPublicKey = PublicKey.deserialize(Buffer.from(env.publicKey));
105− const sharedSecret = privateKey.agree(theirPublicKey);
106−
107− // 5. Derive keys via HKDF
108− const salt = new Uint8Array(32); // 32 zero bytes
109− const info = new TextEncoder().encode("TextSecure Provisioning Message");
110− const derivedKeys = hkdf(96, sharedSecret, info, salt);
111− const aesKey = derivedKeys.slice(0, 32);
112− const hmacKey = derivedKeys.slice(32, 64);
113−
114− // 6. Verify HMAC
115− const hmac = createHmac("sha256", Buffer.from(hmacKey));
116− hmac.update(Buffer.from(ivAndCiphertext));
117− const computedMac = new Uint8Array(hmac.digest());
118− if (!constantTimeEqual(computedMac, mac)) {
119− throw new Error("HMAC verification failed");
120− }
121−
122− // 7. Decrypt AES-256-CBC
123− const decipher = createDecipheriv(
124− "aes-256-cbc",
125− Buffer.from(aesKey),
126− Buffer.from(iv)
127− );
128− const decrypted = Buffer.concat([
129− decipher.update(Buffer.from(ciphertext)),
130− decipher.final(),
131− ]);
132−
133− // 8. Decode ProvisionMessage protobuf
134− return decodeProvisionMessage(new Uint8Array(decrypted));
135−};
136−
137−function constantTimeEqual(a, b) {
138− if (a.length !== b.length) return false;
139− let result = 0;
140− for (let i = 0; i < a.length; i++) {
141− result |= a[i] ^ b[i];
142− }
143− return result === 0;
144−}
signal-backend/src/Signal/Provisioning/Cipher.pursdeleted−60
@@ -1,60 +0,0 @@
1−module Signal.Provisioning.Cipher where
2−
3−import Prelude
4−
5−import Data.ArrayBuffer.Types (Uint8Array)
6−import Data.Maybe (Maybe)
7−import Data.Nullable (Nullable, toMaybe)
8−import Effect (Effect)
9−import Effect.Uncurried (EffectFn1, runEffectFn1)
10−import Signal.Keys (PrivateKey)
11−
12−type ProvisionMessage =
13− { aciIdentityKeyPublic :: Maybe Uint8Array
14− , aciIdentityKeyPrivate :: Maybe Uint8Array
15− , pniIdentityKeyPublic :: Maybe Uint8Array
16− , pniIdentityKeyPrivate :: Maybe Uint8Array
17− , number :: Maybe String
18− , provisioningCode :: Maybe String
19− , aci :: Maybe String
20− , pni :: Maybe String
21− , profileKey :: Maybe Uint8Array
22− , masterKey :: Maybe Uint8Array
23− , accountEntropyPool :: Maybe String
24− , provisioningVersion :: Maybe Int
25− }
26−
27−type ProvisionMessageImpl =
28− { aciIdentityKeyPublic :: Nullable Uint8Array
29− , aciIdentityKeyPrivate :: Nullable Uint8Array
30− , pniIdentityKeyPublic :: Nullable Uint8Array
31− , pniIdentityKeyPrivate :: Nullable Uint8Array
32− , number :: Nullable String
33− , provisioningCode :: Nullable String
34− , aci :: Nullable String
35− , pni :: Nullable String
36− , profileKey :: Nullable Uint8Array
37− , masterKey :: Nullable Uint8Array
38− , accountEntropyPool :: Nullable String
39− , provisioningVersion :: Nullable Int
40− }
41−
42−foreign import decryptProvisionEnvelopeImpl :: EffectFn1 { envelope :: Uint8Array, privateKey :: PrivateKey } ProvisionMessageImpl
43−
44−decryptProvisionEnvelope :: Uint8Array -> PrivateKey -> Effect ProvisionMessage
45−decryptProvisionEnvelope envelope privateKey = do
46− r <- runEffectFn1 decryptProvisionEnvelopeImpl { envelope, privateKey }
47− pure
48− { aciIdentityKeyPublic: toMaybe r.aciIdentityKeyPublic
49− , aciIdentityKeyPrivate: toMaybe r.aciIdentityKeyPrivate
50− , pniIdentityKeyPublic: toMaybe r.pniIdentityKeyPublic
51− , pniIdentityKeyPrivate: toMaybe r.pniIdentityKeyPrivate
52− , number: toMaybe r.number
53− , provisioningCode: toMaybe r.provisioningCode
54− , aci: toMaybe r.aci
55− , pni: toMaybe r.pni
56− , profileKey: toMaybe r.profileKey
57− , masterKey: toMaybe r.masterKey
58− , accountEntropyPool: toMaybe r.accountEntropyPool
59− , provisioningVersion: toMaybe r.provisioningVersion
60− }
signal-backend/src/Signal/Provisioning/QRCode.jsdeleted−9
@@ -1,9 +0,0 @@
1−export const buildProvisioningUrlImpl = ({ address, publicKeySerialized }) => {
2− const pubKeyBase64 = Buffer.from(publicKeySerialized).toString("base64");
3− const params = new URLSearchParams({
4− uuid: address,
5− pub_key: pubKeyBase64,
6− capabilities: "",
7− });
8− return `sgnl://linkdevice?${params.toString()}`;
9−};
signal-backend/src/Signal/Provisioning/QRCode.pursdeleted−13
@@ -1,13 +0,0 @@
1−module Signal.Provisioning.QRCode where
2−
3−import Prelude
4−
5−import Data.ArrayBuffer.Types (Uint8Array)
6−import Effect (Effect)
7−import Effect.Uncurried (EffectFn1, runEffectFn1)
8−
9−foreign import buildProvisioningUrlImpl :: EffectFn1 { address :: String, publicKeySerialized :: Uint8Array } String
10−
11−buildProvisioningUrl :: String -> Uint8Array -> Effect String
12−buildProvisioningUrl address publicKeySerialized =
13− runEffectFn1 buildProvisioningUrlImpl { address, publicKeySerialized }
spago.yaml+1−1
@@ -1,7 +1,7 @@
11package:
22 name: yoga-react-native
33 publish:
4− version: 0.1.0
4+ version: 0.2.1
55 license: MIT
66 location:
77 githubOwner: rowtype-yoga
src/Yoga/React/Native/MacOS/Button.js+3−7
@@ -2,13 +2,9 @@ import React from "react";
22import { requireNativeComponent } from "react-native";
33const NativeButton = requireNativeComponent("NativeButton");
44export const buttonImpl = React.forwardRef((props, ref) => {
5− const { onPress, onEmojiPick, ...rest } = props;
6− const nativeProps = { ...rest };
7− if (typeof onPress === "function") {
8− nativeProps.onPressButton = () => onPress();
9− }
10− if (typeof onEmojiPick === "function") {
11− nativeProps.onEmojiPick = (e) => onEmojiPick(e.nativeEvent.emoji)();
5+ const nativeProps = { ...props };
6+ if (typeof props.onPress === "function") {
7+ nativeProps.onPress = () => props.onPress();
128 }
139 return React.createElement(NativeButton, { ...nativeProps, ref });
1410});
src/Yoga/React/Native/MacOS/Button.purs−1
@@ -20,5 +20,4 @@ type NativeButtonAttributes = BaseAttributes
2020 , primary :: Boolean
2121 , buttonEnabled :: Boolean
2222 , onPress :: Effect Unit
23− , onEmojiPick :: String -> Effect Unit
2423 )
src/Yoga/React/Native/MacOS/GhosttyTerminal.jsdeleted−14
@@ -1,14 +0,0 @@
1−import { requireNativeComponent, Platform, UIManager, View } from "react-native";
2−
3−let _impl = View;
4−if (Platform.OS === "macos") {
5− try {
6− const config = UIManager.getViewManagerConfig("GhosttyTerminal");
7− if (config) {
8− _impl = requireNativeComponent("GhosttyTerminal");
9− }
10− } catch (e) {
11− // fallback to View
12− }
13−}
14−export const _ghosttyTerminalImpl = _impl;
src/Yoga/React/Native/MacOS/GhosttyTerminal.pursdeleted−21
@@ -1,21 +0,0 @@
1−module Yoga.React.Native.MacOS.GhosttyTerminal (ghosttyTerminal, GhosttyTerminalAttributes) where
2−
3−import React.Basic (ReactComponent)
4−import React.Basic.Events (EventHandler)
5−import Yoga.React.Native.Attributes (BaseAttributes)
6−import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
7−
8−foreign import _ghosttyTerminalImpl :: forall props. ReactComponent props
9−
10−ghosttyTerminal :: FFINativeComponent_ GhosttyTerminalAttributes
11−ghosttyTerminal = createNativeElement_ _ghosttyTerminalImpl
12−
13−type GhosttyTerminalAttributes = BaseAttributes
14− ( cwd :: String
15− , command :: String
16− , fontSize :: Number
17− , fontFamily :: String
18− , onTitle :: EventHandler
19− , onBell :: EventHandler
20− , onExit :: EventHandler
21− )
src/Yoga/React/Native/MacOS/HoverView.jsdeleted−14
@@ -1,14 +0,0 @@
1−import React from "react";
2−import { requireNativeComponent } from "react-native";
3−const NativeHoverView = requireNativeComponent("MacOSHoverView");
4−export const hoverViewImpl = React.forwardRef((props, ref) => {
5− const nativeProps = { ...props };
6− if (typeof props.onHoverChange === "function") {
7− nativeProps.onHoverChange = (e) =>
8− props.onHoverChange(e.nativeEvent.hovered)();
9− }
10− if (typeof props.onPress === "function") {
11− nativeProps.onPress = () => props.onPress();
12− }
13− return React.createElement(NativeHoverView, { ...nativeProps, ref });
14−});
src/Yoga/React/Native/MacOS/HoverView.pursdeleted−17
@@ -1,17 +0,0 @@
1−module Yoga.React.Native.MacOS.HoverView (nativeHoverView, NativeHoverViewAttributes) where
2−
3−import Prelude
4−import Effect (Effect)
5−import React.Basic (ReactComponent)
6−import Yoga.React.Native.Attributes (BaseAttributes)
7−import Yoga.React.Native.Internal (FFINativeComponent, createNativeElement)
8−
9−foreign import hoverViewImpl :: forall props. ReactComponent props
10−
11−nativeHoverView :: FFINativeComponent NativeHoverViewAttributes
12−nativeHoverView = createNativeElement hoverViewImpl
13−
14−type NativeHoverViewAttributes = BaseAttributes
15− ( onHoverChange :: Boolean -> Effect Unit
16− , onPress :: Effect Unit
17− )
src/Yoga/React/Native/MacOS/Popover.purs−3
@@ -19,8 +19,5 @@ type NativePopoverAttributes = BaseAttributes
1919 ( visible :: Boolean
2020 , preferredEdge :: PopoverEdge
2121 , behavior :: PopoverBehavior
22− , popoverWidth :: Number
23− , popoverHeight :: Number
24− , popoverPadding :: Number
2522 , onClose :: Effect Unit
2623 )
src/Yoga/React/Native/MacOS/RichTextLabel.jsdeleted−3
@@ -1,3 +0,0 @@
1−import { requireNativeComponent } from "react-native";
2−export const richTextLabelImpl = requireNativeComponent("NativeRichTextLabel");
3−export const emojiMapImpl = (obj) => obj;
src/Yoga/React/Native/MacOS/RichTextLabel.pursdeleted−23
@@ -1,23 +0,0 @@
1−module Yoga.React.Native.MacOS.RichTextLabel (nativeRichTextLabel, NativeRichTextLabelAttributes, EmojiMap, emojiMap) where
2−
3−import React.Basic (ReactComponent)
4−import Yoga.React.Native.Attributes (BaseAttributes)
5−import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
6−
7−foreign import data EmojiMap :: Type
8−foreign import richTextLabelImpl :: forall props. ReactComponent props
9−foreign import emojiMapImpl :: forall r. { | r } -> EmojiMap
10−
11−emojiMap :: forall r. { | r } -> EmojiMap
12−emojiMap = emojiMapImpl
13−
14−nativeRichTextLabel :: FFINativeComponent_ NativeRichTextLabelAttributes
15−nativeRichTextLabel = createNativeElement_ richTextLabelImpl
16−
17−type NativeRichTextLabelAttributes = BaseAttributes
18− ( text :: String
19− , emojiMap :: EmojiMap
20− , textColor :: String
21− , fontSize :: Number
22− , emojiSize :: Number
23− )
src/Yoga/React/Native/MacOS/ScrollView.purs−1
@@ -13,5 +13,4 @@ type NativeScrollViewAttributes = BaseAttributes
1313 ( scrollToBottom :: Int
1414 , scrollToY :: Number
1515 , scrollToYTrigger :: Int
16− , maintainScrollPosition :: Boolean
1716 )
src/Yoga/React/Native/MacOS/SplitView.jsnew file+2
@@ -0,0 +1,2 @@
1+import { requireNativeComponent } from "react-native";
2+export const splitViewImpl = requireNativeComponent("MacOSSplitView");
src/Yoga/React/Native/MacOS/SplitView.purs+12−103
@@ -1,109 +1,18 @@
11module Yoga.React.Native.MacOS.SplitView
2− ( splitView
2+ ( nativeSplitView
3+ , NativeSplitViewAttributes
34 ) where
45
5−import Prelude
6+import React.Basic (ReactComponent)
7+import Yoga.React.Native.Attributes (BaseAttributes)
8+import Yoga.React.Native.Internal (FFINativeComponent, createNativeElement)
69
7−import Data.Tuple.Nested ((/\))
8−import Effect.Uncurried (mkEffectFn2)
9−import Effect.Unsafe (unsafePerformEffect)
10−import React.Basic (JSX)
11−import React.Basic.Events (EventHandler, handler, handler_, unsafeEventFn)
12−import React.Basic.Hooks as React
13−import Unsafe.Coerce (unsafeCoerce)
14−import Yoga.React (component)
15−import Yoga.React.Native (view, tw)
16−import Yoga.React.Native.PanResponder as Pan
17−import Yoga.React.Native.Style as Style
10+foreign import splitViewImpl :: forall props. ReactComponent props
1811
19−type SplitViewProps =
20− { initialPosition :: Number
21− , minLeftWidth :: Number
22− , minRightWidth :: Number
23− , left :: JSX
24− , right :: JSX
25− }
12+nativeSplitView :: FFINativeComponent NativeSplitViewAttributes
13+nativeSplitView = createNativeElement splitViewImpl
2614
27−splitView :: SplitViewProps -> JSX
28−splitView = component "SplitView" \props -> React.do
29− sidebarWidth /\ setSidebarWidth <- React.useState' props.initialPosition
30− hovered /\ setHovered <- React.useState' false
31− widthRef <- React.useRef props.initialPosition
32− startWidthRef <- React.useRef 0.0
33− containerWidthRef <- React.useRef 1000.0
34−
35− React.useEffect sidebarWidth do
36− React.writeRef widthRef sidebarWidth
37− pure (pure unit)
38−
39− let dividerWidth = 5.0
40− let clampWidth w = do
41− cw <- React.readRef containerWidthRef
42− let maxW = cw - props.minRightWidth - dividerWidth
43− pure (clamp props.minLeftWidth maxW w)
44−
45− pr <- React.useMemo unit \_ -> unsafePerformEffect $ Pan.create
46− { onStartShouldSetPanResponder: mkEffectFn2 \_ _ -> pure true
47− , onMoveShouldSetPanResponder: mkEffectFn2 \_ _ -> pure true
48− , onPanResponderGrant: mkEffectFn2 \_ _ -> do
49− w <- React.readRef widthRef
50− React.writeRef startWidthRef w
51− , onPanResponderMove: mkEffectFn2 \_ (gs :: Pan.GestureState) -> do
52− sw <- React.readRef startWidthRef
53− newWidth <- clampWidth (sw + gs.dx)
54− setSidebarWidth newWidth
55− }
56−
57− let handlers = toResponderHandlers (Pan.panHandlers pr)
58− let dColor = if hovered then "rgba(128,128,128,0.6)" else "rgba(128,128,128,0.3)"
59−
60− let onLayout = handler (unsafeEventFn layoutWidth) \w ->
61− React.writeRef containerWidthRef w
62−
63− pure do
64− view { style: tw "flex-1 flex-row", onLayout }
65− [ view
66− { style: Style.style { width: sidebarWidth, overflow: "hidden" }
67− , allowsVibrancy: true
68− }
69− [ props.left ]
70− , view
71− { style: Style.style { width: 5.0, cursor: "col-resize", alignItems: "center", justifyContent: "center" }
72− , onStartShouldSetResponder: handlers.onStartShouldSetResponder
73− , onMoveShouldSetResponder: handlers.onMoveShouldSetResponder
74− , onResponderGrant: handlers.onResponderGrant
75− , onResponderMove: handlers.onResponderMove
76− , onResponderRelease: handlers.onResponderRelease
77− , onResponderTerminate: handlers.onResponderTerminate
78− , onResponderTerminationRequest: handlers.onResponderTerminationRequest
79− , onMouseEnter: handler_ (setHovered true)
80− , onMouseLeave: handler_ (setHovered false)
81− }
82− [ view
83− { style: Style.style { width: 1.0, height: "100%", backgroundColor: dColor }
84− , pointerEvents: "none"
85− }
86− (mempty :: Array JSX)
87− ]
88− , view
89− { style: tw "flex-1" <> Style.style { overflow: "hidden" } }
90− [ props.right ]
91− ]
92−
93−-- PanResponder.panHandlers returns responder-named event handlers
94−-- but the PureScript type uses pan-prefixed names, so we coerce
95−layoutWidth :: forall a. a -> Number
96−layoutWidth e = (unsafeCoerce e).nativeEvent.layout.width
97−
98−toResponderHandlers :: Pan.PanResponderHandlers -> ResponderHandlers
99−toResponderHandlers = unsafeCoerce
100−
101−type ResponderHandlers =
102− { onStartShouldSetResponder :: EventHandler
103− , onMoveShouldSetResponder :: EventHandler
104− , onResponderGrant :: EventHandler
105− , onResponderMove :: EventHandler
106− , onResponderRelease :: EventHandler
107− , onResponderTerminate :: EventHandler
108− , onResponderTerminationRequest :: EventHandler
109− }
15+type NativeSplitViewAttributes = BaseAttributes
16+ ( isVertical :: Boolean
17+ , dividerThicknessValue :: Number
18+ )
src/Yoga/React/Native/MacOS/Types.purs−9
@@ -17,15 +17,6 @@ texturedSquare = BezelStyle "texturedSquare"
1717inline :: BezelStyle
1818inline = BezelStyle "inline"
1919
20−circular :: BezelStyle
21−circular = BezelStyle "circular"
22−
23−borderless :: BezelStyle
24−borderless = BezelStyle "borderless"
25−
26−accessoryBarAction :: BezelStyle
27−accessoryBarAction = BezelStyle "accessoryBarAction"
28−
2920newtype VisualEffectMaterial = VisualEffectMaterial String
3021
3122sidebar :: VisualEffectMaterial
src/Yoga/React/Native/Pressable.purs−5
@@ -1,8 +1,5 @@
11module Yoga.React.Native.Pressable (pressable, pressable_, PressableAttributes) where
22
3−import Prelude
4−import Effect.Uncurried (EffectFn1)
5−import Foreign (Foreign)
63import React.Basic (ReactComponent)
74import React.Basic.Events (EventHandler)
85import Yoga.React.Native.Attributes (BaseAttributes)
@@ -21,7 +18,5 @@ type PressableAttributes = BaseAttributes
2118 , onPressIn :: EventHandler
2219 , onPressOut :: EventHandler
2320 , onLongPress :: EventHandler
24− , onHoverIn :: EffectFn1 Foreign Unit
25− , onHoverOut :: EffectFn1 Foreign Unit
2621 , disabled :: Boolean
2722 )
src/Yoga/React/Native/Reanimated.jsdeleted−104
@@ -1,104 +0,0 @@
1−import Animated, {
2− useSharedValue,
3− useAnimatedStyle,
4− withSpring,
5− withTiming,
6− withDecay,
7− withDelay,
8− withSequence,
9− withRepeat,
10− withClamp,
11− interpolate,
12− cancelAnimation,
13− Easing,
14− Extrapolation,
15−} from "react-native-reanimated";
16−
17−// Animated components
18−export const _reanimatedViewImpl = Animated.View;
19−export const _reanimatedTextImpl = Animated.Text;
20−export const _reanimatedImageImpl = Animated.Image;
21−export const _reanimatedScrollViewImpl = Animated.ScrollView;
22−
23−// useSharedValue hook
24−export const useSharedValueImpl = (initial) => () => useSharedValue(initial);
25−
26−// useAnimatedStyle hook
27−export const useAnimatedStyleImpl = (updater) => () =>
28− useAnimatedStyle(updater);
29−
30−// Read shared value
31−export const readSharedValue = (sv) => () => sv.value;
32−
33−// Write shared value (plain assignment)
34−export const writeSharedValue = (sv) => (val) => () => {
35− sv.value = val;
36−};
37−
38−// Write shared value with spring
39−export const writeSharedValueWithSpring =
40− (sv) => (target) => (config) => () => {
41− sv.value = withSpring(target, config);
42− };
43−
44−// Write shared value with timing
45−export const writeSharedValueWithTiming =
46− (sv) => (target) => (config) => () => {
47− sv.value = withTiming(target, config);
48− };
49−
50−// Write shared value with decay
51−export const writeSharedValueWithDecay = (sv) => (config) => () => {
52− sv.value = withDecay(config);
53−};
54−
55−// Write shared value with delay
56−export const writeSharedValueWithDelay = (sv) => (ms) => (animation) => () => {
57− sv.value = withDelay(ms, animation);
58−};
59−
60−// Standalone animation builders (for composing)
61−export const withSpringImpl = (target) => (config) =>
62− withSpring(target, config);
63−export const withTimingImpl = (target) => (config) =>
64− withTiming(target, config);
65−export const withDecayImpl = (config) => withDecay(config);
66−export const withDelayImpl = (ms) => (anim) => withDelay(ms, anim);
67−export const withSequenceImpl = (anims) => withSequence(...anims);
68−export const withRepeatImpl = (anim) => (count) => (reverse) =>
69− withRepeat(anim, count, reverse);
70−
71−export const withClampImpl = (config) => (anim) => withClamp(config, anim);
72−
73−// Interpolation
74−export const interpolateImpl =
75− (value) => (inputRange) => (outputRange) => (extrapolation) =>
76− interpolate(value, inputRange, outputRange, extrapolation);
77−
78−// Write a composed animation value to a shared value
79−export const animateImpl = (sv) => (animValue) => () => {
80− sv.value = animValue;
81−};
82−
83−// Cancel animation on a shared value
84−export const cancelAnimationImpl = (sv) => () => cancelAnimation(sv);
85−
86−// Extrapolation constants
87−export const extrapolationClamp = Extrapolation.CLAMP;
88−export const extrapolationExtend = Extrapolation.EXTEND;
89−export const extrapolationIdentity = Extrapolation.IDENTITY;
90−
91−// Easing functions
92−export const easingLinear = Easing.linear;
93−export const easingEase = Easing.ease;
94−export const easingQuad = Easing.quad;
95−export const easingCubic = Easing.cubic;
96−export const easingSin = Easing.sin;
97−export const easingCircle = Easing.circle;
98−export const easingExp = Easing.exp;
99−export const easingBounce = Easing.bounce;
100−export const easingIn = (e) => Easing.in(e);
101−export const easingOut = (e) => Easing.out(e);
102−export const easingInOut = (e) => Easing.inOut(e);
103−export const easingBezier = (x1) => (y1) => (x2) => (y2) =>
104− Easing.bezier(x1, y1, x2, y2);
src/Yoga/React/Native/Reanimated.pursdeleted−203
@@ -1,203 +0,0 @@
1−module Yoga.React.Native.Reanimated
2− ( SharedValue
3− , UseSharedValue
4− , UseAnimatedStyle
5− , AnimationValue
6− , EasingFunction
7− , ExtrapolationType
8− , useSharedValue
9− , useAnimatedStyle
10− , readSharedValue
11− , writeSharedValue
12− , springTo
13− , timingTo
14− , cancelAnimation
15− , withSpring
16− , withTiming
17− , withDecay
18− , withDelay
19− , withSequence
20− , withRepeat
21− , withClamp
22− , interpolate
23− , reanimatedView
24− , reanimatedView_
25− , reanimatedText
26− , reanimatedText_
27− , reanimatedImage
28− , reanimatedScrollView
29− , reanimatedScrollView_
30− , animate
31− , extrapolationClamp
32− , extrapolationExtend
33− , extrapolationIdentity
34− , easingLinear
35− , easingEase
36− , easingQuad
37− , easingCubic
38− , easingSin
39− , easingCircle
40− , easingExp
41− , easingBounce
42− , easingIn
43− , easingOut
44− , easingInOut
45− , easingBezier
46− ) where
47−
48−import Prelude
49−
50−import Effect (Effect)
51−import React.Basic (ReactComponent)
52−import React.Basic.Hooks (Hook, unsafeHook)
53−import Yoga.React.Native.Attributes (BaseAttributes)
54−import Yoga.React.Native.Internal (FFINativeComponent, FFINativeComponent_, createNativeElement, createNativeElement_)
55−import Yoga.React.Native.Style (Style)
56−
57−-- | A shared value that lives on both the JS and UI threads.
58−foreign import data SharedValue :: Type -> Type
59−
60−-- | An opaque animation value used to compose animations (withSpring, withTiming, etc.)
61−foreign import data AnimationValue :: Type
62−
63−-- | Easing function for timing animations
64−foreign import data EasingFunction :: Type
65−
66−-- | Extrapolation type for interpolation
67−foreign import data ExtrapolationType :: Type
68−
69−-- Hook phantom types
70−foreign import data UseSharedValue :: Type -> Type -> Type
71−foreign import data UseAnimatedStyle :: Type -> Type
72−
73−-- Hooks
74−
75−foreign import useSharedValueImpl :: forall a. a -> Effect (SharedValue a)
76−
77−useSharedValue :: forall a. a -> Hook (UseSharedValue a) (SharedValue a)
78−useSharedValue initial = unsafeHook (useSharedValueImpl initial)
79−
80−foreign import useAnimatedStyleImpl :: (Unit -> Style) -> Effect Style
81−
82−useAnimatedStyle :: (Unit -> Style) -> Hook UseAnimatedStyle Style
83−useAnimatedStyle updater = unsafeHook (useAnimatedStyleImpl updater)
84−
85−-- Shared value operations
86−
87−foreign import readSharedValue :: forall a. SharedValue a -> Effect a
88−foreign import writeSharedValue :: forall a. SharedValue a -> a -> Effect Unit
89−
90−-- | Animate a shared value to a target with spring physics
91−foreign import writeSharedValueWithSpring :: forall r. SharedValue Number -> Number -> { | r } -> Effect Unit
92−
93−springTo :: forall r. SharedValue Number -> Number -> { | r } -> Effect Unit
94−springTo = writeSharedValueWithSpring
95−
96−-- | Animate a shared value to a target with timing
97−foreign import writeSharedValueWithTiming :: forall r. SharedValue Number -> Number -> { | r } -> Effect Unit
98−
99−timingTo :: forall r. SharedValue Number -> Number -> { | r } -> Effect Unit
100−timingTo = writeSharedValueWithTiming
101−
102−-- | Write a composed animation value to a shared value
103−foreign import animateImpl :: forall a. SharedValue a -> AnimationValue -> Effect Unit
104−
105−animate :: forall a. SharedValue a -> AnimationValue -> Effect Unit
106−animate = animateImpl
107−
108−-- | Cancel any running animation on a shared value
109−foreign import cancelAnimationImpl :: forall a. SharedValue a -> Effect Unit
110−
111−cancelAnimation :: forall a. SharedValue a -> Effect Unit
112−cancelAnimation = cancelAnimationImpl
113−
114−-- Animation builders (for composing into sequences, repeats, etc.)
115−
116−foreign import withSpringImpl :: forall r. Number -> { | r } -> AnimationValue
117−foreign import withTimingImpl :: forall r. Number -> { | r } -> AnimationValue
118−foreign import withDecayImpl :: forall r. { | r } -> AnimationValue
119−foreign import withDelayImpl :: Int -> AnimationValue -> AnimationValue
120−foreign import withSequenceImpl :: Array AnimationValue -> AnimationValue
121−foreign import withRepeatImpl :: AnimationValue -> Int -> Boolean -> AnimationValue
122−
123−withSpring :: forall r. Number -> { | r } -> AnimationValue
124−withSpring = withSpringImpl
125−
126−withTiming :: forall r. Number -> { | r } -> AnimationValue
127−withTiming = withTimingImpl
128−
129−withDecay :: forall r. { | r } -> AnimationValue
130−withDecay = withDecayImpl
131−
132−withDelay :: Int -> AnimationValue -> AnimationValue
133−withDelay = withDelayImpl
134−
135−withSequence :: Array AnimationValue -> AnimationValue
136−withSequence = withSequenceImpl
137−
138−withRepeat :: AnimationValue -> Int -> Boolean -> AnimationValue
139−withRepeat = withRepeatImpl
140−
141−foreign import withClampImpl :: forall r. { | r } -> AnimationValue -> AnimationValue
142−
143−withClamp :: forall r. { | r } -> AnimationValue -> AnimationValue
144−withClamp = withClampImpl
145−
146−-- Interpolation
147−
148−foreign import interpolateImpl :: Number -> Array Number -> Array Number -> ExtrapolationType -> Number
149−
150−interpolate :: Number -> Array Number -> Array Number -> ExtrapolationType -> Number
151−interpolate = interpolateImpl
152−
153−foreign import extrapolationClamp :: ExtrapolationType
154−foreign import extrapolationExtend :: ExtrapolationType
155−foreign import extrapolationIdentity :: ExtrapolationType
156−
157−-- Easing
158−
159−foreign import easingLinear :: EasingFunction
160−foreign import easingEase :: EasingFunction
161−foreign import easingQuad :: EasingFunction
162−foreign import easingCubic :: EasingFunction
163−foreign import easingSin :: EasingFunction
164−foreign import easingCircle :: EasingFunction
165−foreign import easingExp :: EasingFunction
166−foreign import easingBounce :: EasingFunction
167−foreign import easingIn :: EasingFunction -> EasingFunction
168−foreign import easingOut :: EasingFunction -> EasingFunction
169−foreign import easingInOut :: EasingFunction -> EasingFunction
170−foreign import easingBezier :: Number -> Number -> Number -> Number -> EasingFunction
171−
172−-- Animated components
173−
174−type ReanimatedAttributes r = BaseAttributes
175− ( opacity :: SharedValue Number
176− | r
177− )
178−
179−foreign import _reanimatedViewImpl :: forall props. ReactComponent props
180−foreign import _reanimatedTextImpl :: forall props. ReactComponent props
181−foreign import _reanimatedImageImpl :: forall props. ReactComponent props
182−foreign import _reanimatedScrollViewImpl :: forall props. ReactComponent props
183−
184−reanimatedView :: FFINativeComponent (ReanimatedAttributes ())
185−reanimatedView = createNativeElement _reanimatedViewImpl
186−
187−reanimatedView_ :: FFINativeComponent_ (ReanimatedAttributes ())
188−reanimatedView_ = createNativeElement_ _reanimatedViewImpl
189−
190−reanimatedText :: FFINativeComponent (ReanimatedAttributes ())
191−reanimatedText = createNativeElement _reanimatedTextImpl
192−
193−reanimatedText_ :: FFINativeComponent_ (ReanimatedAttributes ())
194−reanimatedText_ = createNativeElement_ _reanimatedTextImpl
195−
196−reanimatedImage :: FFINativeComponent_ (ReanimatedAttributes ())
197−reanimatedImage = createNativeElement_ _reanimatedImageImpl
198−
199−reanimatedScrollView :: FFINativeComponent (ReanimatedAttributes ())
200−reanimatedScrollView = createNativeElement _reanimatedScrollViewImpl
201−
202−reanimatedScrollView_ :: FFINativeComponent_ (ReanimatedAttributes ())
203−reanimatedScrollView_ = createNativeElement_ _reanimatedScrollViewImpl