Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Changelog

## Unreleased
## 0.1.0 — 2026-10-08

First stable release, published on npm `latest`. Exports labeled stable in [API stability](./docs/API_STABILITY.md) follow semantic versioning; experimental exports may change in minor releases. See the [release notes](./RELEASE_NOTES.md).

### Added

Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ CI checks package lint/types/tests, distribution contents, and example builds on

## Publishing

The public package name is `@cawrestler/react-native-duo`. npm keeps a `latest` tag on every package; an attempt to remove the first preview's alias returned HTTP 400. Until a stable `0.1.0` exists, `latest` follows the newest preview so an unqualified install never resolves to an older preview (as of 2026-10-07 both tags point to `0.1.0-preview.2`). The alias does not make the preview stable. Previews still publish through the workflow to `next`; afterwards, move `latest` with `npm dist-tag add @cawrestler/react-native-duo@<version> latest`. Check current tags rather than assuming this historical state is unchanged. Preparing a version and notes is not permission to publish it. Confirm trusted-publisher setup before relying on the OIDC workflow.
The public package name is `@cawrestler/react-native-duo`. npm keeps a `latest` tag on every package; an attempt to remove the first preview's alias returned HTTP 400. From `0.1.0` on, `latest` holds stable releases only. Previews publish to `next` and must not move `latest`. Check current tags rather than assuming this historical state is unchanged. Preparing a version and notes is not permission to publish it. Confirm trusted-publisher setup before relying on the OIDC workflow.

### Prepare a release pull request

Expand All @@ -92,7 +92,7 @@ yarn validate
git diff -- package.json CHANGELOG.md yarn.lock
```

Use `minor`, `major`, or an exact unused version as appropriate. For the already-prepared `0.1.0-preview.2` candidate, do not increment the version again. Before committing any release:
Use `minor`, `major`, or an exact unused version as appropriate. Before committing any release:

- Check `package.json`, [CHANGELOG.md](./CHANGELOG.md), and [RELEASE_NOTES.md](./RELEASE_NOTES.md) refer to the same version.
- Make the first line of `RELEASE_NOTES.md` exactly `# @cawrestler/react-native-duo <version>` and review the body. Include supported capabilities, requirements, known gaps, and installation guidance; do not claim unverified hardware behavior or independent RN windows.
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ You do not need Duo hardware to start: run the **iPhone Duo** simulator in **Xco
Template: [react-native-duo-expo-starter](https://github.com/CAWRESTLER/react-native-duo-expo-starter) · Full lab: [`example/`](./example/) · npm: [`@cawrestler/react-native-duo`](https://www.npmjs.com/package/@cawrestler/react-native-duo)

```sh
npm install @cawrestler/react-native-duo@next
npm install @cawrestler/react-native-duo
npx expo install expo-build-properties
npx expo run:ios
```
Expand Down Expand Up @@ -56,7 +56,7 @@ function Pane({ title, detail }: { title: string; detail: string }) {
}
```

Preview releases ship on npm's `next` tag (`0.1.0-preview.2` at time of writing)—not a stable compatibility promise. See [release notes](./RELEASE_NOTES.md) and [compatibility](./docs/COMPATIBILITY.md). Deeper guides: [navigation](./docs/NAVIGATION.md), [API coverage matrix](./docs/API_COVERAGE.md).
`0.1.0` is the first stable release, on npm's `latest` tag. Exports labeled stable in [API stability](./docs/API_STABILITY.md) follow semantic versioning; experimental ones may still change. See [release notes](./RELEASE_NOTES.md) and [compatibility](./docs/COMPATIBILITY.md). Deeper guides: [navigation](./docs/NAVIGATION.md), [API coverage matrix](./docs/API_COVERAGE.md).

## What you get

Expand Down Expand Up @@ -137,21 +137,21 @@ The [compatibility guide](./docs/COMPATIBILITY.md) separates the locked baseline

For local or not-yet-published changes, run `yarn pack:check` and then `npm pack --ignore-scripts` in this repository. Install the resulting `.tgz` file in your app with `npm install /absolute/path/to/package.tgz` and rebuild its native development client. This tests the actual distribution without publishing it. See the [maintainer publication steps](./CONTRIBUTING.md#publishing).

To follow published previews, explicitly choose the preview channel with the command used by your app:
Install the current stable release with the command your app uses:

```sh
npm install @cawrestler/react-native-duo@next
npm install @cawrestler/react-native-duo
```

```sh
yarn add @cawrestler/react-native-duo@next
yarn add @cawrestler/react-native-duo
```

```sh
pnpm add @cawrestler/react-native-duo@next
pnpm add @cawrestler/react-native-duo
```

To reproduce this version once published, use `@cawrestler/react-native-duo@0.1.0-preview.2` instead of `@next`. Tags can move; verify them with `npm view @cawrestler/react-native-duo dist-tags`. The first preview was also assigned `latest`; a retained alias is not a stable-support guarantee. Preview evaluation requires a native rebuild; this is not an Expo Go or JavaScript-only update.
Pin `@cawrestler/react-native-duo@0.1.0` to reproduce this release exactly. Future previews publish to `@next`. Installing or upgrading requires a native rebuild; this is not an Expo Go or JavaScript-only update.

### Expo

Expand Down
81 changes: 31 additions & 50 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,46 @@
# @cawrestler/react-native-duo 0.1.0-preview.2
# @cawrestler/react-native-duo 0.1.0

This developer preview builds on the first preview's native layout, hinge, toolbar, camera-preview, and companion-surface APIs. App developers compose React/TypeScript; the package provides the UIKit and AVFoundation bridge. Installing `0.1.0-preview.1` does not include the changes described here.
The first stable release of the React Native bridge to Apple's iPhone Duo APIs: arrangements, adaptive vertical toolbars, hinge and reserved regions, inner/outer cameras, and scene accessories. This release also adds Android foldable support, so the same provider and arrangement code adapts to Pixel Fold–class devices.

This is for evaluation, not a stable production-support promise. APIs and integration details may change before `0.1.0`. This release targets npm `next` and a GitHub prerelease; it is not a stable release. Because npm requires a `latest` tag, `latest` was later moved from `0.1.0-preview.1` to this preview (both tags point to `0.1.0-preview.2` as of 2026-10-07); that alias is not a stable-release claim. Registry availability, not the presence of these notes, confirms publication.
`0.1.0` is published on npm's `latest` tag. The exports labeled **stable** in [API stability](./docs/API_STABILITY.md) follow semantic versioning from here: breaking changes need a new minor version while the package is `0.x`, with a changelog migration note. Exports labeled **experimental** (`@experimental` in editor hover info) may still change in any minor release.

## Changes since preview.1
## What's new since 0.1.0-preview.2

- **Navigator-owned integration:** `DuoNavigationToolbar` attaches screen actions to an existing native navigation stack without replacing its controllers. The host navigator retains header/back/tab/modal ownership; use screen focus to activate the appropriate toolbar. Explicit `horizontalPresentation="inline"` hosts a measured, screen-local UIKit action toolbar for wrapped native tabs whose horizontal stack toolbar does not render; vertical actions still use the existing stack. The default remains `navigator`. Standalone `DuoAdaptiveToolbar` remains available for screens that own their bars.
- **Isolated provider subscriptions:** existing hooks keep their signatures; new `useDuoGeometry()` and `useDuoWindow()` subscribe to their respective fields. Structurally shared snapshots keep unrelated narrow-hook consumers from rerendering on hinge-angle updates. `useDuo()` still observes the complete environment. A stable native-provider wrapper contains siblings and RN modals without an app-specific wrapper workaround; simulated fold/rotation tests verify draft state is not remounted by telemetry updates.
- **Camera lifecycle diagnostics and recovery:** additive optional `status`, `interrupted`, `interruptionReason`, `interruptionReasonCode`, and structured `errorDetails` distinguish interruption/runtime failure from an idle or stopped preview. The existing `error` string remains. Normal interruptions resume only a mounted, active, foreground, authorized session; media-services reset gets at most one automatic retry per explicit activation/configuration. Other fatal runtime failures require `active={false}` followed by `true`. Errors from asynchronous aspect-ratio and smart-framing application are reported. Diagnostics can persist after automatic recovery; inspect `running`/`status` rather than treating a non-null error as proof capture is still stopped.
- **Reproducible compatibility checks:** a recorded RN `0.88.0-rc.1` / React `19.3.0` / locked Expo `58.0.0` baseline is checked against manifests and Yarn resolutions in local validation, CI, and release verification. Checks do not certify all RN/Expo stacks or uninspected apps.
- **Explicit validation boundaries:** the compatibility guide documents fold/rotation/scroll/navigation/accessibility/draft-preservation checks and distinguishes automated, manual, and physical-device evidence. Connie remains unverified pending inspection of its actual host and versions.
- **Android foldables.** On devices Jetpack WindowManager supports, `DuoProvider` is native on Android. It reports hinge posture (`fullyOpen` / `partiallyOpen`, or `closed` from the hinge-angle sensor), the hinge angle where the device has a hinge-angle sensor, and the fold as a reserved region that is active while it separates content. `DuoArrangementView` places its panes on either side of an active fold. `isDuo` and `supportsDuoApis` stay `false` on Android because they describe iPhone Duo; check `hinge.available` for foldable-aware layouts.
- **React content in scene accessories.** `DuoSceneAccessory` accepts React `children` and renders them into the connected external-display or camera-capture scene at its size. State adds `connected` and `size`. Declarative `content` still works and becomes optional when you pass children.
- **Jest mock.** `@cawrestler/react-native-duo/jest` renders components with the JavaScript fallbacks. `setMockDuoEnvironment` simulates a Duo, a hinge pose, or reserved regions mid-test.
- **API stability labels.** Each export is classified as stable or experimental in [docs/API_STABILITY.md](./docs/API_STABILITY.md).
- **End-to-end tests.** Maestro flows for the Duo Lab example (`yarn example e2e`) cover arrangement switching, toolbar placement, and scene accessory registration on the iPhone Duo simulator.
- **Release-channel docs** match the registry, and the README has a demo GIF and a compatibility report issue template.

## Highlights
## Install

- **Live Duo environment:** `DuoProvider`, `useDuo()`, `useDuoHinge()`, `useDuoReservedRegions()`, and `useDuoCameras()` expose capability detection, hinge angle/status, native size classes, camera discovery, and window metrics.
- **View-local geometry:** `DuoGeometryView` measures reserved regions and safe areas in the coordinates of the actual content view, including inactive regions when requested.
- **Adaptive panes:** `DuoArrangementView` hosts ordinary React content in UIKit split or overlay arrangements, with axis, size, animation, and live visibility controls.
- **Native adaptive bars:** `DuoAdaptiveToolbar` provides actions, tabs, selection, badges, overflow, alternate vertical symbols, and compression preferences. UIKit chooses the placement and may show separate toolbar/tab groups when both fit.
- **Safe content and full-width backgrounds:** `contentLayout="safeArea"` remains the default. The decorative `background` slot fills the host; `contentLayout="edgeToEdge"` enables intentionally immersive content. Removing app bars no longer reserves a phantom app rail, while system-reserved areas remain protected. Reported host dimensions and safe insets remain available in both modes.
- **Camera preview:** `DuoCameraView` exposes physical and direction-relative camera selection, permission, session control, preview sizing/rotation, supported dynamic aspect ratios, sensor compensation, and smart-framing monitoring/application where available.
- **Companion surfaces:** `DuoSceneAccessory` registers native external-display or camera-capture accessories with declarative text, SF Symbols, colors, gradients, and typography.
- **Duo Lab RN:** the repository includes seven guided screens modeled on the SwiftUI lab, plus the Duo Studio canvas experiment with Day/Dusk/Night artwork, region-aware controls, bar visibility options, and opt-in layout diagnostics.
```sh
npm install @cawrestler/react-native-duo
```

The distribution includes JavaScript, TypeScript declarations, Fabric/codegen sources, native implementations, the podspec, and the Expo camera-permission plugin. The demo stays in the GitHub repository, not the npm package.
Expo apps need a development build (Expo Go can't load native code). Add the config plugin and rebuild after installing. See the [README](./README.md) for Expo and bare React Native setup.

## Requirements and compatibility

- React Native's New Architecture/Fabric must be enabled.
- Building the native iOS implementation requires Xcode 27.1+ and the iOS 27.1 SDK. Native Duo features require iOS 27.1+ and eligible hardware/window state.
- The reproducible demo baseline is React Native `0.88.0-rc.1`, React `19.3.0`, and Expo `58.0.0`, as resolved by the repository lockfile. Other version combinations are not a tested compatibility guarantee.
- Node `v24.21.0` and Yarn `4.11.0` define the repository development toolchain. See the [compatibility guide](./docs/COMPATIBILITY.md) for what automated checks establish and the manual/device validation still required.
- Provider/navigation/camera bridge tests use mocked native hosts; the compiled camera-policy tests exercise restart guards, not AVFoundation devices. None establishes compatibility with Connie or other uninspected consuming apps, real navigation ownership, physical-camera recovery, accessibility, or a performance benchmark.
- Expo apps require a native development build; Expo Go cannot load the package. Rebuild after installing or changing native sources/codegen.
- Android/web provide React Native arrangement and horizontal toolbar fallbacks. Older iOS runtimes use compatibility paths, but an older SDK cannot compile the Duo native source. Fallbacks do not emulate Duo hardware telemetry, vertical UIKit rails, cameras, or scene accessories.
- Camera and companion-surface availability depends on the device, permission, display connections, and system eligibility. API support or successful registration alone does not mean the feature can currently be presented.

## Known limitations

- **Independent React Native windows are not implemented yet.** `supportsMultipleWindows` reports the app/runtime declaration; it does not open or coordinate additional React-rendered scenes. The demo's diagnostics preview is an in-app modal. SwiftUI's `WindowGroup` equivalent requires separate native scene-hosting and RN/Expo lifecycle work, not a JavaScript setting.
- **No camera capture pipeline.** This preview does not take photos, record video, or deliver camera frames to JavaScript.
- **Accessories use declarative native content.** They do not host another React component tree.
- **Automatic arrangement is UIKit policy.** The package does not promise identical decisions to SwiftUI's `automaticArrangement` in every fold/window state, nor complete visual parity with the SwiftUI app.
- **Bar placement remains system-owned.** Compression is a preference, not an unconditional hide/show instruction. Vertical-bar opt-out affects the component's controls, not the app's window-wide status-bar axis.
- **Edge-to-edge is opt-in.** Apps must position important controls around safe areas and reserved regions. Duo Studio's small control-lane policy is an example, not a general collision-layout engine; it does not change the package's safe defaults.
- **Simulator testing is not physical hardware certification.** Camera, companion-display, accessibility, and touch/scroll behavior must be checked on the intended device/runtime before shipping a consuming app. Complete and record the candidate's manual checklist; source tests and a successful build are not interaction or visual-parity evidence.
- React Native's New Architecture (Fabric) must be enabled.
- iOS: building needs Xcode 27.1+ and the iOS 27.1 SDK. Native Duo features need iOS 27.1+ on iPhone Duo; older iOS runtimes use compatibility paths.
- Android: foldable support uses `androidx.window` 1.3.0, and the hinge angle needs API 30+ on a device with a hinge-angle sensor. Phones without a hinge get the same fallback values as before.
- The tested baseline is React Native `0.88.0-rc.1`, React `19.3.0`, and Expo `58.0.0`, as resolved by the repository lockfile. Other combinations are not a tested compatibility guarantee; see the [compatibility guide](./docs/COMPATIBILITY.md).

## Try the preview
## What was tested for this release

Once this release is published, pin its exact version:
- Automated: lint, type checks, 77 Jest tests (library, Jest mock, fold split, scene accessory, provider, navigation, camera state), 59 tooling and release-safeguard tests, and the packed-tarball check. CI builds the example on Android, iOS (Xcode 27.1), and web.
- iPhone Duo simulator (Xcode 27.1): the Maestro flows pass, and the React-content scene accessory registers and reports that no scene is connected.
- Pixel Fold emulator (Android 16 / API 36): hinge posture and angle are reported (`partiallyOpen`, 90°) and the fold arrives as an active division region.

```sh
npm install @cawrestler/react-native-duo@0.1.0-preview.2
```

Use `@next` instead of the exact version to follow published previews. Verify availability with `npm view @cawrestler/react-native-duo dist-tags`; before publication, evaluate a locally built `.tgz` instead. In either case, follow the Expo or bare React Native setup and rebuild the native app before testing.

Use this version's [README](./README.md), [navigation guide](./docs/NAVIGATION.md), and [example guide](./example/README.md). The preview.1 reference remains available in its [versioned README](https://github.com/CAWRESTLER/react-native-duo/blob/v0.1.0-preview.1/README.md) and [versioned example guide](https://github.com/CAWRESTLER/react-native-duo/blob/v0.1.0-preview.1/example/README.md).

## Feedback
## Known limitations

Report issues with the package and RN/Expo versions, Xcode/iOS version, device or simulator, fold pose/window size, affected screen, and reproduction steps. Screenshots or recordings of layout/interaction differences are especially useful. Please distinguish unsupported hardware from incorrect supported behavior.
- **Not verified on physical hardware:** iPhone Duo cameras, smart framing, and interruption recovery; accessory scenes actually connecting (the simulator can't attach an external display or run camera capture); and Android foldable hardware. Test these on the intended devices before shipping.
- **No independent React Native windows.** `supportsMultipleWindows` reports the app's declaration; the package doesn't open additional React-rendered scenes.
- **No camera capture pipeline.** No photos, video recording, or frames delivered to JavaScript.
- **Accessory content is noninteractive** and shares the calling React tree.
- **Android is foldables-first.** Toolbars, cameras, scene accessories, and `DuoGeometryView` still use the JavaScript fallbacks on Android.
- **Automatic arrangement and bar placement are system policy.** The package doesn't promise identical decisions to SwiftUI's `automaticArrangement`, and toolbar compression is a preference, not a guarantee.

[Report an issue](https://github.com/CAWRESTLER/react-native-duo/issues/new/choose).
See the [changelog](./CHANGELOG.md) for the full list of changes.
Loading
Loading