@unrulysystems/native-motion 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/android/build.gradle +24 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/unrulysystems/nativemotion/GestureExclusionModule.kt +55 -0
- package/android/src/main/java/com/unrulysystems/nativemotion/NativeMotionPackage.kt +27 -0
- package/package.json +57 -0
- package/react-native.config.js +14 -0
- package/src/native/driver/instantWindow.ts +31 -0
- package/src/native/driver/strictModeReplay.ts +20 -0
- package/src/native/driver/uiLayoutEngine.ts +845 -0
- package/src/native/driver/uiLayoutGraph.ts +338 -0
- package/src/native/driver/uiValueChannel.ts +4640 -0
- package/src/native/driver/workletDriver.ts +3860 -0
- package/src/native/motion/AnimatePresence.tsx +1605 -0
- package/src/native/motion/LayoutGroup.tsx +163 -0
- package/src/native/motion/MotionConfig.tsx +400 -0
- package/src/native/motion/MotionRoot.tsx +250 -0
- package/src/native/motion/MotionView.tsx +6761 -0
- package/src/native/motion/addScaleCorrector.ts +97 -0
- package/src/native/motion/colorEndpointUiFeed.ts +32 -0
- package/src/native/motion/colorProjection.ts +39 -0
- package/src/native/motion/composeTransform.ts +271 -0
- package/src/native/motion/constraintMeasure.ts +157 -0
- package/src/native/motion/deferredPendingSnapshots.ts +25 -0
- package/src/native/motion/discreteProjection.ts +36 -0
- package/src/native/motion/dragAncestorPanContext.ts +28 -0
- package/src/native/motion/dragControls.ts +152 -0
- package/src/native/motion/dragGestureWiring.ts +1168 -0
- package/src/native/motion/dragHandoffBinding.ts +179 -0
- package/src/native/motion/dragHubStream.ts +756 -0
- package/src/native/motion/dragPropagationLock.ts +57 -0
- package/src/native/motion/driverValueChannel.ts +3288 -0
- package/src/native/motion/externalDragDriver.ts +214 -0
- package/src/native/motion/frameData.ts +31 -0
- package/src/native/motion/gestureBinding.ts +107 -0
- package/src/native/motion/gestureStateGate.ts +563 -0
- package/src/native/motion/gestureStateResolver.ts +286 -0
- package/src/native/motion/identityValueChannelControllerAdapter.ts +2043 -0
- package/src/native/motion/identityValueChannelControllerReconciliation.ts +176 -0
- package/src/native/motion/identityValueChannelLaneMarker.ts +14 -0
- package/src/native/motion/imperativeAnimate.ts +816 -0
- package/src/native/motion/keyframeTiming.ts +7 -0
- package/src/native/motion/layoutIdBinding.ts +261 -0
- package/src/native/motion/layoutIdFlightConfig.ts +51 -0
- package/src/native/motion/layoutProjection.ts +89 -0
- package/src/native/motion/layoutScroll.ts +204 -0
- package/src/native/motion/layoutTransition.ts +1302 -0
- package/src/native/motion/lengthLayoutContext.tsx +100 -0
- package/src/native/motion/lengthLayoutHost.ts +44 -0
- package/src/native/motion/mappedKeys.ts +184 -0
- package/src/native/motion/motionViewController.ts +3180 -0
- package/src/native/motion/nativeHostMarker.ts +22 -0
- package/src/native/motion/panSessionWiring.ts +127 -0
- package/src/native/motion/pathTransition.ts +101 -0
- package/src/native/motion/popLayout.ts +176 -0
- package/src/native/motion/presenceBinding.ts +382 -0
- package/src/native/motion/scaleCorrectorRegistry.ts +123 -0
- package/src/native/motion/scrollValues.ts +260 -0
- package/src/native/motion/serializablePayload.ts +151 -0
- package/src/native/motion/severity.ts +16 -0
- package/src/native/motion/shippedSurface.ts +843 -0
- package/src/native/motion/staticLengthGate.ts +86 -0
- package/src/native/motion/styleBaseGate.ts +72 -0
- package/src/native/motion/styleValueBinding.ts +657 -0
- package/src/native/motion/systemGestureExclusion.ts +61 -0
- package/src/native/motion/tapGestureWiring.ts +215 -0
- package/src/native/motion/transformOrder.ts +38 -0
- package/src/native/motion/transformStringBinding.ts +200 -0
- package/src/native/motion/transformTemplateGate.ts +45 -0
- package/src/native/motion/transitionGate.ts +435 -0
- package/src/native/motion/useAnimate.ts +52 -0
- package/src/native/motion/useCycle.ts +44 -0
- package/src/native/motion/useInstantTransition.ts +68 -0
- package/src/native/motion/useReducedMotion.ts +58 -0
- package/src/native/motion/useScroll.ts +162 -0
- package/src/native/motion/useViewportScroll.ts +29 -0
- package/src/native/motion/valueChannel.ts +7121 -0
- package/src/native/motion/valueHooks.ts +901 -0
- package/src/native/motion/variantChildRegistry.ts +67 -0
- package/src/native/motion/variantContext.tsx +172 -0
- package/src/native/motion/variantProps.ts +598 -0
- package/src/native.ts +159 -0
- package/src/verification/harnessMetrics.ts +78 -0
- package/src/verification/probe/LayoutIdentityWorkletProbe.tsx +251 -0
- package/src/verification/probe/WorkletParityProbe.tsx +162 -0
- package/src/verification/probe/layoutIdentityProbeEngine.ts +429 -0
- package/src/verification/screens/ArcPathChecksScreen.tsx +273 -0
- package/src/verification/screens/BooleanAnimateChecksScreen.tsx +358 -0
- package/src/verification/screens/ChoreographyGalleryScreen.tsx +1010 -0
- package/src/verification/screens/ColorBindingChecksScreen.tsx +568 -0
- package/src/verification/screens/CompletionChecksScreen.tsx +294 -0
- package/src/verification/screens/ConformanceScreen.tsx +573 -0
- package/src/verification/screens/ContentionProbeScreen.tsx +224 -0
- package/src/verification/screens/DriverSmokeScreen.tsx +99 -0
- package/src/verification/screens/DurationOnlyTweenChecksScreen.tsx +294 -0
- package/src/verification/screens/DynamicDragConfigChecksScreen.tsx +237 -0
- package/src/verification/screens/FrameDataChecksScreen.tsx +252 -0
- package/src/verification/screens/GestureChecksScreen.tsx +685 -0
- package/src/verification/screens/InstantTransitionChecksScreen.tsx +839 -0
- package/src/verification/screens/LayoutAnimationStartChecksScreen.tsx +648 -0
- package/src/verification/screens/LayoutChecksScreen.tsx +822 -0
- package/src/verification/screens/LayoutCommitSpikeScreen.tsx +142 -0
- package/src/verification/screens/MotionViewChecksScreen.tsx +930 -0
- package/src/verification/screens/PresenceChecksScreen.tsx +614 -0
- package/src/verification/screens/ReducedMotionChecksScreen.tsx +614 -0
- package/src/verification/screens/RestThresholdChecksScreen.tsx +634 -0
- package/src/verification/screens/ScaleCorrectorChecksScreen.tsx +425 -0
- package/src/verification/screens/SharedLayoutContinuityChecksScreen.tsx +338 -0
- package/src/verification/screens/SharedLayoutCrossfadeChecksScreen.tsx +425 -0
- package/src/verification/screens/TransitionDefaultSelectionChecksScreen.tsx +514 -0
- package/src/verification/screens/ViewportScrollAliasChecksScreen.tsx +303 -0
- package/src/verification/screens/conformanceBanner.ts +21 -0
- package/src/verification/screens/proofConsoleTap.ts +18 -0
- package/src/verification.ts +47 -0
- package/src/web.ts +98 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// U7i useViewportScroll device checks: catalog useTransform-with-useLayoutEffect on the
|
|
2
|
+
// public path. iOS has no tap injection, so the screen self-drives. Discriminators (Decision
|
|
3
|
+
// 86 analog of 77/82): the alias is exported and a container-less call refuses with the
|
|
4
|
+
// named viewport message (F1 — shipping the name is not a viewport). The catalog observable
|
|
5
|
+
// (scrollY → opacity) uses the standing named-container substitution (useScroll({ container })
|
|
6
|
+
// + useTransform). Host window-scroll is the web e2e.
|
|
7
|
+
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
9
|
+
import { StyleSheet, Text, View } from 'react-native'
|
|
10
|
+
import Animated, { scrollTo, useAnimatedRef } from 'react-native-reanimated'
|
|
11
|
+
import { scheduleOnUI } from 'react-native-worklets'
|
|
12
|
+
import { MotionRoot } from '../../native/motion/MotionRoot'
|
|
13
|
+
import { MotionView } from '../../native/motion/MotionView'
|
|
14
|
+
import { ambientNativeSeverity } from '../../native/motion/severity'
|
|
15
|
+
import { useScroll } from '../../native/motion/useScroll'
|
|
16
|
+
import { useViewportScroll } from '../../native/motion/useViewportScroll'
|
|
17
|
+
import { useMotionValueEvent, useTransform } from '../../native/motion/valueHooks'
|
|
18
|
+
import { armProofConsoleTap } from './proofConsoleTap'
|
|
19
|
+
|
|
20
|
+
const ROW_ALIAS = 'container-less useViewportScroll refuses with the named viewport message'
|
|
21
|
+
const ROW_TRACK = 'named-container useScroll + useTransform: opacity drops on scroll'
|
|
22
|
+
const ROW_CLEAN = 'clean run: only the expected viewport refusal'
|
|
23
|
+
const ROWS = [ROW_ALIAS, ROW_TRACK, ROW_CLEAN] as const
|
|
24
|
+
|
|
25
|
+
const T_ALIAS = 400
|
|
26
|
+
const T_SCROLL = 900
|
|
27
|
+
const T_SAMPLE = 2200
|
|
28
|
+
const T_CLEAN = 2600
|
|
29
|
+
const SCROLL_Y = 180
|
|
30
|
+
const OPACITY_RANGE = [0, 200] as const
|
|
31
|
+
|
|
32
|
+
type ScrollHost = {
|
|
33
|
+
readonly scrollTo?: (args: { readonly y: number; readonly animated?: boolean }) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function driveNamedContainerScroll(
|
|
37
|
+
ref: ReturnType<typeof useAnimatedRef<Animated.ScrollView>>,
|
|
38
|
+
y: number,
|
|
39
|
+
): void {
|
|
40
|
+
// Reanimated scrollTo(animated=false) and Android programmatic scrollTo without
|
|
41
|
+
// animation skip onScroll, so useScroll never sees the offset. Host
|
|
42
|
+
// scrollTo({ animated: true }) emits the native events the UI handler is bound
|
|
43
|
+
// to. iOS has no tap injection — this is the self-drive (Decision 86 analog).
|
|
44
|
+
const host = ref.current as ScrollHost | number | null | undefined
|
|
45
|
+
if (
|
|
46
|
+
host !== null &&
|
|
47
|
+
host !== undefined &&
|
|
48
|
+
typeof host === 'object' &&
|
|
49
|
+
typeof host.scrollTo === 'function'
|
|
50
|
+
) {
|
|
51
|
+
host.scrollTo({ y, animated: true })
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
scheduleOnUI(() => {
|
|
55
|
+
'worklet'
|
|
56
|
+
scrollTo(ref, 0, y, true)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface RowResult {
|
|
61
|
+
readonly pass: boolean
|
|
62
|
+
readonly detail: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isViewportRefusal(message: string): boolean {
|
|
66
|
+
return /no window viewport analog|requires a container ref to a scrollable source/i.test(message)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function AliasRefusalProbe(): null {
|
|
70
|
+
useViewportScroll()
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function ScrollOpacityStage(props: {
|
|
75
|
+
readonly onOpacity: (value: number) => void
|
|
76
|
+
readonly onScroll: (value: number) => void
|
|
77
|
+
readonly scrollRef: ReturnType<typeof useAnimatedRef<Animated.ScrollView>>
|
|
78
|
+
}) {
|
|
79
|
+
const { scrollY } = useScroll({ container: props.scrollRef })
|
|
80
|
+
const opacity = useTransform(scrollY, [OPACITY_RANGE[0], OPACITY_RANGE[1]], [1, 0])
|
|
81
|
+
useMotionValueEvent(opacity, 'change', (value: number) => {
|
|
82
|
+
props.onOpacity(value)
|
|
83
|
+
})
|
|
84
|
+
useMotionValueEvent(scrollY, 'change', (value: number) => {
|
|
85
|
+
props.onScroll(value)
|
|
86
|
+
})
|
|
87
|
+
return (
|
|
88
|
+
<Animated.ScrollView
|
|
89
|
+
ref={props.scrollRef}
|
|
90
|
+
style={styles.scroller}
|
|
91
|
+
scrollEventThrottle={16}
|
|
92
|
+
accessibilityLabel="u7i-viewport-scroll-scroller"
|
|
93
|
+
>
|
|
94
|
+
<View style={styles.scrollContent}>
|
|
95
|
+
<MotionView
|
|
96
|
+
style={[styles.box, { opacity }]}
|
|
97
|
+
accessibilityLabel="u7i-viewport-scroll-box"
|
|
98
|
+
/>
|
|
99
|
+
</View>
|
|
100
|
+
</Animated.ScrollView>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function ViewportScrollAliasChecksScreen() {
|
|
105
|
+
const severity = ambientNativeSeverity()
|
|
106
|
+
const scrollRef = useAnimatedRef<Animated.ScrollView>()
|
|
107
|
+
const [stage, setStage] = useState('boot')
|
|
108
|
+
const [rows, setRows] = useState<Partial<Record<(typeof ROWS)[number], RowResult>>>({})
|
|
109
|
+
const [opacityNow, setOpacityNow] = useState(1)
|
|
110
|
+
const [scrollNow, setScrollNow] = useState(0)
|
|
111
|
+
const [probeOn, setProbeOn] = useState(true)
|
|
112
|
+
|
|
113
|
+
const reportsRef = useRef<string[]>([])
|
|
114
|
+
const opacityRef = useRef(1)
|
|
115
|
+
const scrollYRef = useRef(0)
|
|
116
|
+
const restoreTapRef = useRef<null | (() => void)>(null)
|
|
117
|
+
if (restoreTapRef.current === null) {
|
|
118
|
+
restoreTapRef.current = armProofConsoleTap(reportsRef.current, (message) =>
|
|
119
|
+
/motion|useScroll|viewport|container|animate/i.test(message),
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const finalize = (id: (typeof ROWS)[number], pass: boolean, detail: string): void => {
|
|
124
|
+
setRows((previous) => ({ ...previous, [id]: { pass, detail } }))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const onOpacity = useCallback((value: number): void => {
|
|
128
|
+
opacityRef.current = value
|
|
129
|
+
setOpacityNow(value)
|
|
130
|
+
}, [])
|
|
131
|
+
const onScroll = useCallback((value: number): void => {
|
|
132
|
+
scrollYRef.current = value
|
|
133
|
+
setScrollNow(value)
|
|
134
|
+
}, [])
|
|
135
|
+
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
const timers: ReturnType<typeof setTimeout>[] = []
|
|
138
|
+
const at = (ms: number, run: () => void): void => {
|
|
139
|
+
timers.push(setTimeout(run, ms))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
at(T_ALIAS, () => {
|
|
143
|
+
const hit = reportsRef.current.some(isViewportRefusal)
|
|
144
|
+
finalize(
|
|
145
|
+
ROW_ALIAS,
|
|
146
|
+
hit,
|
|
147
|
+
hit
|
|
148
|
+
? 'container-less alias reported the named viewport refusal'
|
|
149
|
+
: `no viewport refusal (reports=${reportsRef.current.length})`,
|
|
150
|
+
)
|
|
151
|
+
setProbeOn(false)
|
|
152
|
+
setStage('alias refusal sampled')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
at(T_SCROLL, () => {
|
|
156
|
+
driveNamedContainerScroll(scrollRef, SCROLL_Y)
|
|
157
|
+
setStage(`scrolled to y=${SCROLL_Y}`)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
at(T_SAMPLE, () => {
|
|
161
|
+
const opacity = opacityRef.current
|
|
162
|
+
const y = scrollYRef.current
|
|
163
|
+
const pass = y >= 80 && opacity < 0.75
|
|
164
|
+
finalize(
|
|
165
|
+
ROW_TRACK,
|
|
166
|
+
pass,
|
|
167
|
+
pass
|
|
168
|
+
? `scrollY=${y.toFixed(1)} opacity=${opacity.toFixed(3)} (named container)`
|
|
169
|
+
: `scrollY=${y.toFixed(1)} opacity=${opacity.toFixed(3)} — expected scroll≥80 and opacity<0.75`,
|
|
170
|
+
)
|
|
171
|
+
setStage(pass ? 'track row finalized' : 'track row finalized (FAIL)')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
at(T_CLEAN, () => {
|
|
175
|
+
const unexpected = reportsRef.current.filter((message) => !isViewportRefusal(message))
|
|
176
|
+
const expected = reportsRef.current.filter(isViewportRefusal)
|
|
177
|
+
finalize(
|
|
178
|
+
ROW_CLEAN,
|
|
179
|
+
unexpected.length === 0,
|
|
180
|
+
unexpected.length === 0
|
|
181
|
+
? `0 unexpected; ${expected.length} expected viewport refusal(s)`
|
|
182
|
+
: `${unexpected.length} unexpected: ${unexpected.map((message) => message.slice(0, 80)).join(' | ')}`,
|
|
183
|
+
)
|
|
184
|
+
setStage('done')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return () => {
|
|
188
|
+
for (const timer of timers) clearTimeout(timer)
|
|
189
|
+
restoreTapRef.current?.()
|
|
190
|
+
restoreTapRef.current = null
|
|
191
|
+
}
|
|
192
|
+
}, [scrollRef])
|
|
193
|
+
|
|
194
|
+
const done = ROWS.every((id) => rows[id] !== undefined)
|
|
195
|
+
const failing = ROWS.filter((id) => rows[id] !== undefined && !rows[id]!.pass)
|
|
196
|
+
const verdict = !done ? 'RUNNING' : failing.length === 0 ? 'PASS' : 'FAIL'
|
|
197
|
+
const passed = Object.keys(rows).length - failing.length
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<MotionRoot>
|
|
201
|
+
<View style={styles.scroll}>
|
|
202
|
+
<View style={styles.container}>
|
|
203
|
+
<Text style={styles.title}>U7i viewport-scroll alias checks</Text>
|
|
204
|
+
<Text style={styles.subtitle}>
|
|
205
|
+
catalog scrollY→opacity + named-container substitution ({severity} severity)
|
|
206
|
+
</Text>
|
|
207
|
+
<View
|
|
208
|
+
style={[
|
|
209
|
+
styles.banner,
|
|
210
|
+
verdict === 'PASS' ? styles.pass : verdict === 'FAIL' ? styles.fail : styles.running,
|
|
211
|
+
]}
|
|
212
|
+
>
|
|
213
|
+
<Text style={styles.bannerText}>{verdict}</Text>
|
|
214
|
+
</View>
|
|
215
|
+
<Text style={styles.tally}>
|
|
216
|
+
{done
|
|
217
|
+
? `${passed} PASS / ${failing.length} FAIL of ${ROWS.length} rows`
|
|
218
|
+
: `${Object.keys(rows).length}/${ROWS.length} rows complete — RUNNING`}
|
|
219
|
+
</Text>
|
|
220
|
+
<Text style={styles.tallyDetail}>
|
|
221
|
+
{done
|
|
222
|
+
? failing.length === 0
|
|
223
|
+
? 'no failing rows'
|
|
224
|
+
: `FAILING: ${failing.join(' | ')}`
|
|
225
|
+
: `stage: ${stage}`}
|
|
226
|
+
</Text>
|
|
227
|
+
<Text style={styles.status}>
|
|
228
|
+
y={scrollNow.toFixed(1)} · opacity={opacityNow.toFixed(3)} · {severity}
|
|
229
|
+
</Text>
|
|
230
|
+
|
|
231
|
+
{probeOn ? <AliasRefusalProbe /> : null}
|
|
232
|
+
<ScrollOpacityStage scrollRef={scrollRef} onOpacity={onOpacity} onScroll={onScroll} />
|
|
233
|
+
|
|
234
|
+
{ROWS.map((id) => {
|
|
235
|
+
const row = rows[id]
|
|
236
|
+
return (
|
|
237
|
+
<View key={id} style={styles.row}>
|
|
238
|
+
<Text
|
|
239
|
+
style={[
|
|
240
|
+
styles.verdict,
|
|
241
|
+
row === undefined ? styles.pending : row.pass ? styles.ok : styles.bad,
|
|
242
|
+
]}
|
|
243
|
+
>
|
|
244
|
+
{row === undefined ? '…' : row.pass ? 'PASS' : 'FAIL'}
|
|
245
|
+
</Text>
|
|
246
|
+
<View style={styles.rowBody}>
|
|
247
|
+
<Text style={styles.rowId}>{id}</Text>
|
|
248
|
+
<Text style={styles.rowDetail} numberOfLines={3}>
|
|
249
|
+
{row === undefined ? 'pending' : row.detail}
|
|
250
|
+
</Text>
|
|
251
|
+
</View>
|
|
252
|
+
</View>
|
|
253
|
+
)
|
|
254
|
+
})}
|
|
255
|
+
</View>
|
|
256
|
+
</View>
|
|
257
|
+
</MotionRoot>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const styles = StyleSheet.create({
|
|
262
|
+
scroll: { flex: 1, backgroundColor: '#0b0b12' },
|
|
263
|
+
container: { paddingVertical: 72, paddingHorizontal: 8 },
|
|
264
|
+
title: { color: '#fff', fontSize: 22, fontWeight: '700', textAlign: 'center' },
|
|
265
|
+
subtitle: { color: '#8a8aa0', fontSize: 12, marginTop: 6, textAlign: 'center' },
|
|
266
|
+
banner: {
|
|
267
|
+
alignSelf: 'center',
|
|
268
|
+
paddingHorizontal: 36,
|
|
269
|
+
paddingVertical: 10,
|
|
270
|
+
borderRadius: 10,
|
|
271
|
+
marginTop: 18,
|
|
272
|
+
marginBottom: 10,
|
|
273
|
+
},
|
|
274
|
+
pass: { backgroundColor: '#1d7a3e' },
|
|
275
|
+
fail: { backgroundColor: '#a32633' },
|
|
276
|
+
running: { backgroundColor: '#7a641d' },
|
|
277
|
+
bannerText: { color: '#fff', fontSize: 22, fontWeight: '800', letterSpacing: 2 },
|
|
278
|
+
tally: { color: '#e6e6f0', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
279
|
+
tallyDetail: { color: '#f87171', fontSize: 11, marginTop: 3, textAlign: 'center' },
|
|
280
|
+
status: {
|
|
281
|
+
color: '#c9c9d8',
|
|
282
|
+
fontSize: 11,
|
|
283
|
+
marginTop: 8,
|
|
284
|
+
textAlign: 'center',
|
|
285
|
+
fontFamily: 'Menlo',
|
|
286
|
+
},
|
|
287
|
+
scroller: { height: 160, marginTop: 14, backgroundColor: '#1a1a24', borderRadius: 12 },
|
|
288
|
+
scrollContent: { height: 520, padding: 12 },
|
|
289
|
+
box: {
|
|
290
|
+
width: 120,
|
|
291
|
+
height: 120,
|
|
292
|
+
backgroundColor: '#f9cb29',
|
|
293
|
+
borderRadius: 8,
|
|
294
|
+
},
|
|
295
|
+
row: { flexDirection: 'row', marginTop: 10 },
|
|
296
|
+
verdict: { width: 52, fontSize: 12, fontWeight: '800', paddingTop: 1 },
|
|
297
|
+
ok: { color: '#4ade80' },
|
|
298
|
+
bad: { color: '#f87171' },
|
|
299
|
+
pending: { color: '#8a8aa0' },
|
|
300
|
+
rowBody: { flex: 1 },
|
|
301
|
+
rowId: { color: '#e6e6f0', fontSize: 13, fontWeight: '600' },
|
|
302
|
+
rowDetail: { color: '#6f6f85', fontSize: 11, marginTop: 2 },
|
|
303
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The device conformance banner's VERDICT law (REQ-CONFORM-011; M2 r5 major 6e730d4c1a82):
|
|
2
|
+
// unassertable never counts as pass — any routed obligation makes the overall verdict FAIL,
|
|
3
|
+
// UNCONDITIONALLY. This holds even when parity is owned at another altitude:
|
|
4
|
+
// CMP-R7-cross-engine-parity is discharged by the web e2e + device proofs, but that parity does
|
|
5
|
+
// not execute in this suite, so it stays routed and still fails the banner. Styling may
|
|
6
|
+
// distinguish "failed assertion" from "routed obligation", but the VERDICT — what a proof
|
|
7
|
+
// screenshot claims — admits no qualified pass.
|
|
8
|
+
|
|
9
|
+
export type ConformanceBannerVerdict = 'PASS' | 'FAIL' | 'RUNNING'
|
|
10
|
+
|
|
11
|
+
export function conformanceBannerVerdict(state: {
|
|
12
|
+
readonly allDone: boolean
|
|
13
|
+
readonly rowsPass: boolean
|
|
14
|
+
readonly suitesClean: boolean
|
|
15
|
+
readonly routed: number
|
|
16
|
+
}): ConformanceBannerVerdict {
|
|
17
|
+
if (!state.allDone) return 'RUNNING'
|
|
18
|
+
if (!state.rowsPass || !state.suitesClean) return 'FAIL'
|
|
19
|
+
if (state.routed > 0) return 'FAIL'
|
|
20
|
+
return 'PASS'
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Proof-only console tap. Production MotionView reports typed refusals synchronously
|
|
2
|
+
// during render (resolveAnimateProp → consoleReporter). A parent useEffect tap is too
|
|
3
|
+
// late: children have already rendered. Arm during the parent's render body, before
|
|
4
|
+
// returning children.
|
|
5
|
+
|
|
6
|
+
export function armProofConsoleTap(sink: string[], keep: (message: string) => boolean): () => void {
|
|
7
|
+
const original = console.error
|
|
8
|
+
console.error = (...args: unknown[]) => {
|
|
9
|
+
for (const arg of args) {
|
|
10
|
+
const message = arg instanceof Error ? arg.message : typeof arg === 'string' ? arg : null
|
|
11
|
+
if (message !== null && keep(message)) sink.push(message)
|
|
12
|
+
}
|
|
13
|
+
original.apply(console, args)
|
|
14
|
+
}
|
|
15
|
+
return () => {
|
|
16
|
+
console.error = original
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Engine-intrinsic verification surface (REQ-EXHARNESS-010,
|
|
2
|
+
// specs/DOGFOOD-MIGRATION-BUILD-PACKET.md FLAG 1): the probes and deterministic check screens
|
|
3
|
+
// that prove the engine ON DEVICE live with the engine — they are engine code, exempt from the
|
|
4
|
+
// consumer-isolation ban only because they live here, never because an app path is allowlisted.
|
|
5
|
+
// This entry exists for the proof-harness app shell to mount them; it is NOT the product surface
|
|
6
|
+
// and nothing here may appear in a consumer example (that is the escape hatch the dogfood law
|
|
7
|
+
// forbids). Native-only by design: verification drives the worklet runtime, so there is no web
|
|
8
|
+
// route for this subpath.
|
|
9
|
+
|
|
10
|
+
export { ChoreographyGalleryScreen } from './verification/screens/ChoreographyGalleryScreen'
|
|
11
|
+
export { ColorBindingChecksScreen } from './verification/screens/ColorBindingChecksScreen'
|
|
12
|
+
export { ConformanceScreen } from './verification/screens/ConformanceScreen'
|
|
13
|
+
export { ContentionProbeScreen } from './verification/screens/ContentionProbeScreen'
|
|
14
|
+
export { DriverSmokeScreen } from './verification/screens/DriverSmokeScreen'
|
|
15
|
+
export { BooleanAnimateChecksScreen } from './verification/screens/BooleanAnimateChecksScreen'
|
|
16
|
+
export { DynamicDragConfigChecksScreen } from './verification/screens/DynamicDragConfigChecksScreen'
|
|
17
|
+
export { GestureChecksScreen } from './verification/screens/GestureChecksScreen'
|
|
18
|
+
export { InstantTransitionChecksScreen } from './verification/screens/InstantTransitionChecksScreen'
|
|
19
|
+
export { LayoutAnimationStartChecksScreen } from './verification/screens/LayoutAnimationStartChecksScreen'
|
|
20
|
+
export { LayoutChecksScreen } from './verification/screens/LayoutChecksScreen'
|
|
21
|
+
export { LayoutCommitSpikeScreen } from './verification/screens/LayoutCommitSpikeScreen'
|
|
22
|
+
export { MotionViewChecksScreen } from './verification/screens/MotionViewChecksScreen'
|
|
23
|
+
export { PresenceChecksScreen } from './verification/screens/PresenceChecksScreen'
|
|
24
|
+
export { ReducedMotionChecksScreen } from './verification/screens/ReducedMotionChecksScreen'
|
|
25
|
+
export { RestThresholdChecksScreen } from './verification/screens/RestThresholdChecksScreen'
|
|
26
|
+
export { ScaleCorrectorChecksScreen } from './verification/screens/ScaleCorrectorChecksScreen'
|
|
27
|
+
export { ViewportScrollAliasChecksScreen } from './verification/screens/ViewportScrollAliasChecksScreen'
|
|
28
|
+
export { FrameDataChecksScreen } from './verification/screens/FrameDataChecksScreen'
|
|
29
|
+
export { ArcPathChecksScreen } from './verification/screens/ArcPathChecksScreen'
|
|
30
|
+
export { DurationOnlyTweenChecksScreen } from './verification/screens/DurationOnlyTweenChecksScreen'
|
|
31
|
+
export { CompletionChecksScreen } from './verification/screens/CompletionChecksScreen'
|
|
32
|
+
export { TransitionDefaultSelectionChecksScreen } from './verification/screens/TransitionDefaultSelectionChecksScreen'
|
|
33
|
+
export { SharedLayoutContinuityChecksScreen } from './verification/screens/SharedLayoutContinuityChecksScreen'
|
|
34
|
+
export { SharedLayoutCrossfadeChecksScreen } from './verification/screens/SharedLayoutCrossfadeChecksScreen'
|
|
35
|
+
export { LayoutIdentityWorkletProbe } from './verification/probe/LayoutIdentityWorkletProbe'
|
|
36
|
+
export { WorkletParityProbe } from './verification/probe/WorkletParityProbe'
|
|
37
|
+
// Seal-r16 F2: proof-only settle/layout-callback witnesses — NOT on the consumer-facing package
|
|
38
|
+
// root (BRIEF Motion-shaped public-API law). The dogfood purity ban still forbids this subpath
|
|
39
|
+
// inside apps/mobile/src/**; the proof gallery reads JS-side last-pushed cells instead, and
|
|
40
|
+
// isolation/verification screens may import these readers here.
|
|
41
|
+
export {
|
|
42
|
+
armUiProofWitnesses,
|
|
43
|
+
lastPushedProofLayoutCallbackWitness,
|
|
44
|
+
lastPushedProofSettleEmissionCount,
|
|
45
|
+
uiProofLayoutCallbackWitness,
|
|
46
|
+
uiProofSettleEmissionCount,
|
|
47
|
+
} from './native/driver/workletDriver'
|
package/src/web.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
// Web route of the single public import (REQ-UNIVERSAL-001/REQ-WEB-010): re-export the web
|
|
3
|
+
// driver-peer. Web bundlers land here via the `browser`/`import`/`default` exports conditions;
|
|
4
|
+
// the shim (not this file) owns normalization and the motion/react binding.
|
|
5
|
+
//
|
|
6
|
+
// Explicit Native Motion exports below exclude shim-only helpers such as `createView`. Web remains
|
|
7
|
+
// a superset while the native namespace gate still requires every portable native name. Pinned
|
|
8
|
+
// additions are re-exported explicitly by the web driver so each one remains census-classified.
|
|
9
|
+
|
|
10
|
+
export type {
|
|
11
|
+
DragControlOptions,
|
|
12
|
+
DragControlsHandle,
|
|
13
|
+
DragInfo,
|
|
14
|
+
TapCallback,
|
|
15
|
+
TapInfo,
|
|
16
|
+
MotionTargetProps,
|
|
17
|
+
Target,
|
|
18
|
+
TransformProperties,
|
|
19
|
+
TransformTemplate,
|
|
20
|
+
Transition,
|
|
21
|
+
TransitionType,
|
|
22
|
+
} from '@unrulysystems/native-motion-core'
|
|
23
|
+
// Product dual-authority release helpers — key parity with the native entry.
|
|
24
|
+
export { absoluteDragOrigin, selectReleaseTarget } from '@unrulysystems/native-motion-core'
|
|
25
|
+
export {
|
|
26
|
+
AnimatePresence,
|
|
27
|
+
AnimateSharedLayout,
|
|
28
|
+
Image,
|
|
29
|
+
LazyMotion,
|
|
30
|
+
LayoutGroup,
|
|
31
|
+
LayoutScrollOffsetProvider,
|
|
32
|
+
createLayoutScrollOffsetRef,
|
|
33
|
+
recordLayoutScrollOffsetFromEvent,
|
|
34
|
+
MotionConfig,
|
|
35
|
+
MotionLengthLayoutProvider,
|
|
36
|
+
MotionRoot,
|
|
37
|
+
MotionValue,
|
|
38
|
+
Reorder,
|
|
39
|
+
disableInstantTransitions,
|
|
40
|
+
domAnimation,
|
|
41
|
+
domMax,
|
|
42
|
+
motion,
|
|
43
|
+
m,
|
|
44
|
+
mix,
|
|
45
|
+
addScaleCorrector,
|
|
46
|
+
useIsPresent,
|
|
47
|
+
useMotionValue,
|
|
48
|
+
useMotionValueEvent,
|
|
49
|
+
usePresence,
|
|
50
|
+
usePresenceData,
|
|
51
|
+
animate,
|
|
52
|
+
useAnimate,
|
|
53
|
+
useAnimateMini,
|
|
54
|
+
useAnimationControls,
|
|
55
|
+
useCycle,
|
|
56
|
+
type Cycle,
|
|
57
|
+
type CycleState,
|
|
58
|
+
type AnimateFn,
|
|
59
|
+
type AnimationPlaybackControls,
|
|
60
|
+
type AnimationScope,
|
|
61
|
+
useDragControls,
|
|
62
|
+
DragControls,
|
|
63
|
+
useInView,
|
|
64
|
+
useInstantTransition,
|
|
65
|
+
useMotionTemplate,
|
|
66
|
+
useReducedMotion,
|
|
67
|
+
useScroll,
|
|
68
|
+
useViewportScroll,
|
|
69
|
+
useSpring,
|
|
70
|
+
useTransform,
|
|
71
|
+
useTime,
|
|
72
|
+
useVelocity,
|
|
73
|
+
frameData,
|
|
74
|
+
arc,
|
|
75
|
+
stagger,
|
|
76
|
+
Text,
|
|
77
|
+
View,
|
|
78
|
+
wrap,
|
|
79
|
+
type ImageProps,
|
|
80
|
+
type LayoutGroupInherit,
|
|
81
|
+
type LayoutGroupProps,
|
|
82
|
+
type LayoutScrollOffset,
|
|
83
|
+
type LayoutScrollOffsetProviderProps,
|
|
84
|
+
type LayoutScrollOffsetRef,
|
|
85
|
+
type MotionConfigProps,
|
|
86
|
+
type MotionConfigTransition,
|
|
87
|
+
type MotionLengthLayoutProviderProps,
|
|
88
|
+
type MotionRootProps,
|
|
89
|
+
type ReducedMotionConfig,
|
|
90
|
+
type ScrollMotionValues,
|
|
91
|
+
type SpringValueOptions,
|
|
92
|
+
type TextProps,
|
|
93
|
+
type TransformOptions,
|
|
94
|
+
type UseScrollOptions,
|
|
95
|
+
type ViewProps,
|
|
96
|
+
type FrameData,
|
|
97
|
+
type ArcOptions,
|
|
98
|
+
} from '@unrulysystems/native-motion-web'
|