mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 16:02:43 +00:00
* feat(mobile): the page owns its safe area, like a native screen The shell reserved both system-bar strips outside the WebView and painted them bgBase, so every page screen showed a flat band above its header, sheet scrims stopped short of the status bar, and the dock floated above the gesture bar. For a page that declares `safe-area-insets` in `ready.accepts`, the shell now draws the WebView edge-to-edge and keeps only the keyboard strip off it. `init` carries the insets the view sits under (bottom 0 while the keyboard ends the view, top 0 under the update banner), and a move is re-sent over the existing route-update `init`. An older page keeps the reserved strips, since it has no reader for the insets. On the page, a root layout (`app/_layout.web.tsx`) feeds those insets to react-native-safe-area-context below ExpoRoot's env()-measuring provider. It also replaces expo-router's DefaultNavigator, an all-edges SafeAreaView that padded a second time. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile-web): re-measure the page pins for the root layout The page's route tree gained `./_layout.tsx` (its web sibling of the native root), so every pin that counts the tree moved: - Script sweep re-measured by building `routes.slice(0, n)` for each n. It reads 69 scripts at 16 routes, which matches the real build. The asset-ceiling crossing moves from 31 routes to 32. - Route closures now enter through both layouts. `entryNames` gains `[dir]` because `app/_layout` and `app/h/_layout` share a name. - Session closure pin 4216 -> 4219. The added modules are bridge-safe-area-insets, page-safe-area-provider and _layout.web. - The web-overrides allowlist names `app/_layout.web.tsx`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): read safe-area ownership from the page-document state Review fixes on the page-owns-safe-area change. - Ownership is page-document state now. `page-ready` carries `accepts` beside `reports`, the patch sets `pageOwnsSafeArea` from `safe-area-insets`, and the session hook projects it like `backClaimed`. The screen's own per-session copy is gone. - Insets moves re-send `init` only to a page that declared `safe-area-insets`. A page that took route updates but not insets was sent a useless `init` on every keyboard show and hide. - The banner wrapper is gone. The root pads the status bar strip while the banner shows. - The shell session defaults the insets inline, with no predicate that mutated its argument. - The provider is folded into its single caller, `app/_layout.web.tsx`. The session closure pin reads 4218 (local 1032). - The screen tests share their module mocks, and the safe-area cases move to a suite of their own: owned page, banner, iOS and Android keyboard, and an older page that gets no re-init. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
import { useMemo, useSyncExternalStore } from 'react'
|
|
import { StyleSheet, useWindowDimensions, View } from 'react-native'
|
|
import { Slot } from 'expo-router'
|
|
import { SafeAreaFrameContext, SafeAreaInsetsContext } from 'react-native-safe-area-context'
|
|
import { ZERO_SAFE_AREA_INSETS } from '../src/mobile-web-shell/bridge/bridge-safe-area-insets'
|
|
import { usePageBridgeClient } from '../src/transport/client-context.web'
|
|
import { colors } from '../src/theme/mobile-theme'
|
|
|
|
/**
|
|
* The page's root layout, standing where the native `_layout.tsx` does. Without one expo-router
|
|
* mounts `DefaultNavigator`, an all-edges `SafeAreaView`, which would pad each screen a second time.
|
|
* Like the native root it adds no padding. It supplies the insets the shell sent in `init` below
|
|
* ExpoRoot's own provider, whose web half reads `env(safe-area-inset-*)`: 0 in both WebViews.
|
|
*/
|
|
export default function PageRootLayout() {
|
|
const client = usePageBridgeClient()
|
|
const insets = useSyncExternalStore(
|
|
client.onSafeAreaInsetsUpdate,
|
|
() => client.getShellSession()?.safeAreaInsets ?? ZERO_SAFE_AREA_INSETS
|
|
)
|
|
const { width, height } = useWindowDimensions()
|
|
const frame = useMemo(() => ({ x: 0, y: 0, width, height }), [width, height])
|
|
return (
|
|
<SafeAreaFrameContext.Provider value={frame}>
|
|
<SafeAreaInsetsContext.Provider value={insets}>
|
|
<View style={styles.root}>
|
|
<Slot />
|
|
</View>
|
|
</SafeAreaInsetsContext.Provider>
|
|
</SafeAreaFrameContext.Provider>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
root: {
|
|
flex: 1,
|
|
backgroundColor: colors.bgBase
|
|
}
|
|
})
|