ReviewOS

also looking at this

craft-native/craft

fix(mobile): preserve native editing, navigation, and durable media requests

#369
Merged chris wants to merge codex/native-vertical-scroll-tab-swipes into main
36 files +864 -185

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/android/fixtures/native-navigation/NativeNavigationTest.ktmodified+2-1
Changes to packages/android/fixtures/native-navigation/NativeNavigationTest.kt
@@ -49,6 +49,7 @@ import org.junit.Assert.assertTrue
4949import org.junit.Test
5050import org.junit.runner.RunWith
5151import kotlin.math.abs
52import kotlin.math.roundToInt
5253
5354@RunWith(AndroidJUnit4::class)
5455class NativeNavigationTest {
@@ -1006,7 +1007,7 @@ class NativeNavigationTest {
10061007 awaitText(activity, "safe-area-status", "Safe edges: top,bottom")
10071008 assertEquals("Restoring the top edge changed the original native safe-area padding", initialRootTop, root.paddingTop)
10081009 val retained = awaitView(activity, "retained-safe-area")
1009 val retainedAuthoredPadding = (4 * activity.resources.displayMetrics.density).toInt()
1010 val retainedAuthoredPadding = (4 * activity.resources.displayMetrics.density).roundToInt()
10101011 val plainRetainedPadding = retained.paddingTop
10111012 assertEquals("plain retained container did not keep its authored padding", retainedAuthoredPadding, plainRetainedPadding)
10121013 click(activity, "toggle-retained-safe-area")
packages/android/src/grid-rows.test.tsmodified+28-0
Changes to packages/android/src/grid-rows.test.ts
@@ -2,6 +2,34 @@
22import { test } from 'bun:test'
33import { runKotlinFixture } from './testing'
44
5test('snaps authored padding to the nearest physical pixel at fractional density', async () => {
6 const template = await Bun.file(new URL('../templates/MainActivityNative.kt.template', import.meta.url)).text()
7 const padding = template.match(/internal fun craftNativePadding\([^]*?\n\}/)?.[0]
8 const number = template.match(/private fun craftNativeNumber\([^]*?\n \?\.takeIf \{ it\.isFinite\(\) \}/)?.[0]
9 if (!padding || !number) throw new Error('Native padding normalization is missing')
10 await runKotlinFixture(`
11import kotlin.math.roundToInt
12
13class JSONObject(private val values: Map<String, Any?>) {
14 fun opt(name: String): Any? = values[name]
15}
16${number}
17${padding}
18
19fun main() {
20 val density = 2.625f
21 check(craftNativePadding(JSONObject(mapOf("padding" to 4)), "padding", 0, density) == 11)
22 check(craftNativePadding(JSONObject(mapOf("padding" to 8)), "padding", 0, density) == 21)
23 check(craftNativePadding(JSONObject(mapOf("padding" to 4)), "padding", 0, 1f) == 4)
24 for (invalid in listOf(null, -1, Double.NaN, Double.POSITIVE_INFINITY, true, "4")) {
25 check(craftNativePadding(JSONObject(mapOf("padding" to invalid)), "padding", 7, density) == 7)
26 }
27 check(craftNativePadding(null, "padding", 7, density) == 7)
28 println("PASS native padding rounds once to the nearest physical pixel and retains invalid-value defaults")
29}
30`)
31}, 180_000)
32
533test('sizes occupied and empty explicit grid rows after padding and fractional gaps', async () => {
634 const template = await Bun.file(new URL('../templates/MainActivityNative.kt.template', import.meta.url)).text()
735 const allocation = template.match(/ val rowHeights = if \(gridRows != null\) \{[\s\S]*?\n \} else naturalRowHeights/)?.[0]
packages/android/templates/MainActivityNative.kt.templatemodified+1-1
Changes to packages/android/templates/MainActivityNative.kt.template
@@ -167,7 +167,7 @@ private fun craftNativeFontWeight(value: Any?): String? = when (value) {
167167internal fun craftNativePadding(style: JSONObject?, name: String, fallback: Int, density: Float): Int {
168168 val value = craftNativeNumber(style?.opt(name)) ?: Double.NaN
169169 val pixels = value * density
170 return if (value.isFinite() && value >= 0 && pixels.isFinite()) pixels.toInt() else fallback
170 return if (value.isFinite() && value >= 0 && pixels.isFinite()) pixels.roundToInt() else fallback
171171}
172172
173173internal fun craftNativeFiniteFloat(style: JSONObject?, name: String, fallback: Float): Float {