duo-craft
Makes a React Native app look right on iPhone Duo, Apple's foldable. It puts the list on one side of the fold and the detail on the other, moves the bars into the side column, and fixes the crash an Expo app hits at launch on the new SDK. Then it says which of the nine poses it actually saw running.
/plugin install duo-craft@fledgeling-pluginsNeeds the marketplace added first — how to do that.
Reach for it when
Build or adapt a React Native / Expo app for iPhone Duo, Apple's foldable iPhone — two panes split at the fold, the vertical side bar, asymmetric safe-area insets, book and tabletop poses, Split View halves, and the iOS 27 scene-lifecycle crash.
What ships with it
- Scripts it runs itself
- 5 reference files
- Measured evals
Say any of this
- make it look right on the Duo
Taken from the skill’s own trigger description — these are the phrases it listens for. You do not have to match them exactly.
iPhone Duo is an iPhone to every API that names a device. Platform.isPad says no and expo-device says phone, so a React Native app runs on it unchanged and looks wrong in ways only the Duo shows. The bars stand down one side and take 84pt off that side only. The open display is 951 by 669 points with the fold at 475.5, so a phone column stretched across it puts a card over the hinge. Split View hands the left app a half whose side bar is on its left. And an Expo SDK 57 app built with the iOS 27 SDK doesn't reach its first screen at all; it crashes at launch, every time.
React Native has no fold API of its own. The fold libraries on npm are weeks old, and the one you're most likely to find, react-native-nitro-hinge, has its fold path compiled out unless you set a build flag. Install it as it comes and the fold code is never compiled into your app, and nothing warns you.
What it does
The skill carries the code rather than describing it. It came out of adapting a shipping Expo app to the Duo; the fold module, the two-pane split and the starter all ran on the Xcode 27.1 Duo simulator before they went in.
- A fold module the app owns. About 150 lines of Swift reading Apple's
reservedRegionsandUIHingeInteraction. It needs the iOS 27.1 SDK and fails to compile without it, so it can't quietly report no fold. - List and detail split at the fold.
DetailPaneLayoutkeeps your list on the left and renders what it would have pushed on the right, selects the first item so the right half is never blank, and pushes the chosen item full screen when the device closes. - Bars that can move to the side.
NativeTabsandStack.Toolbaritems, because a tab bar or header drawn in aViewstays where it was drawn, across your content. - Insets per side, titles that clear the top. Each edge pads for its own inset, and titles sit at 59 + 8pt when the top inset is 0. The app this came from was told 20pt was still touching the edge.
- The launch crash, fixed Expo's way. On SDK 57 it turns on
expo-build-propertieswithios.enableSceneSupport, which is Expo's own opt-in. - An audit before anything changes.
duo_audit.pyruns 13 checks for the assumptions that break on a foldable (a staleDimensions.get, symmetric insets, branching on the device model, a locked orientation, no scene delegate) and printsfile:linefor each. - A starter. One command turns a fresh
create-expo-appinto a two-pane app with native tabs and a toolbar.
Then it builds with Xcode 27.1 and reports all nine poses (closed, open in both orientations, book, tabletop, both Split View halves, picture in picture) as seen, not reached or not built. A pose marked seen has a screenshot behind it that was opened and read.
Does it actually work?
Four tasks ran twice each, once with the skill and once with nothing. Three model families then judged each pair blind, in both orders.
Report card. 29 checkable properties across the four tasks. With the skill: 28 of 29. Without: 19 of 29. The gap is where you'd expect it. Without the skill, neither run gave the app a fold signal, the new app shipped locked to portrait with no fix for the launch crash, and the adapted app kept its JavaScript tab bar and hand-drawn header buttons, which can't move into the side bar.
Blind taste test. GPT, Grok and Gemini each picked the skill's version of the two build tasks (adapting an existing app, starting a new one), three for three, in both orders.
It lost the crash-fix task, and that's worth saying plainly. The no-skill run found Expo's official enableSceneSupport flag; the skill had shipped its own config plugin making the same three edits. It was measured on the Duo the same day (prebuild, Release build, launch, two-pane split showing), and the skill now uses it. Three more rounds closed most of the rest of the gap (a regression check, a tighter report), ending at one loss and two splits; on a well-documented crash a strong model finds the fix without help. On the "should I use nitro-hinge?" question the first answer ran to 44 lines with asides; by the third round it was 31 lines and two of the three judges preferred it in both orders. EVALS.md has the tables, the judges' reasons and the re-runs.
Note: tabletop, the book pose with the hinge partly closed, both Split View halves and picture in picture are built and unit-tested but haven't been seen running yet. The skill tells the run to mark them not reached rather than assume them.
What it costs
On the adapt task the skill's run took about 20 minutes and 277k tokens against the baseline's 6 minutes and 136k, mostly spent mocking nine poses in HTML that no one was there to review. The mock now only happens when someone will look at it.
It needs Xcode 27.1 and the iOS 27.1 simulator runtime to build and look; without them it does the code, typechecks it, and reports every pose as not built. It sets DEVELOPER_DIR per command and leaves xcode-select alone.
Install
/plugin marketplace add fledgeling-co/fledgeling-plugins
/plugin install duo-craft@fledgeling-plugins
Getting started
python3 scripts/duo_audit.py <app-root>
python3 scripts/install_templates.py <app-root> # adapt an existing app
python3 scripts/install_templates.py <app-root> --starter # a fresh create-expo-app
The skill fires on its own when an Expo or React Native app needs to work on iPhone Duo. references/simulator.md covers building against Xcode 27.1 without switching the machine's Xcode, capturing each display, and the Device Hub fix for an empty device list.