ReviewOS

also looking at this

stacks/wildloop

feat: native trail maps and complete native mobile screens

#1017
Draft chris wants to merge codex/trail-experience into main
60 files +2505 -823

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.

tests/unit/native-trail-detail-screen.test.tsmodified+5-2
Changes to tests/unit/native-trail-detail-screen.test.ts
@@ -11,7 +11,7 @@ async function open(signedIn = true, geometry: string | null = TRAIL.geometry, r
1111 answer(screen, await waitForRequest(screen, 0, r => r.payload?.module === 'Network'), ok({ success: true, trail: { ...TRAIL, geometry } }))
1212 const review = await waitForRequest(screen, 0, r => r.payload?.args?.[0]?.url?.endsWith('/reviews'))
1313 answer(screen, review, ok({ reviews: reviewRows }))
14 answer(screen, await waitForRequest(screen, 0, r => r.payload?.args?.[0]?.url?.endsWith('/forecast')), ok({ current: { temperature: 68, label: 'Clear' }, days: [{ date: '2026-10-17', low: 50, high: 70, label: 'Clear' }], attribution: { label: 'MET Norway', url: 'https://api.met.no' } }))
14 answer(screen, await waitForRequest(screen, 0, r => r.payload?.args?.[0]?.url?.endsWith('/forecast')), ok({ current: { temperature: 68, label: 'Clear' }, days: [{ date: '2026-10-17', low: 50, high: 70, label: 'Clear', windMph: 5, precipitationMm: 2.54 }], attribution: { label: 'MET Norway', url: 'https://api.met.no' } }))
1515 answer(screen, await waitForRequest(screen, 0, r => r.payload?.args?.[0]?.url?.endsWith('/profile')), ok({ points: geometry ? [{ mi: 0, ft: 4000, lat: 37.7, lng: -119.5 }, { mi: 3, ft: 4500, lat: 37.72, lng: -119.5 }] : [] }))
1616 if (signedIn) answer(screen, await waitForRequest(screen, 0, r => r.payload?.args?.[0]?.url?.endsWith('/viewer')), {...ok({ userId:12, saved:false, review:personalReview }),status:personalStatus})
1717 await waitForVisibility(screen, 'trail-detail-loading', 'none')
@@ -30,7 +30,10 @@ describe('native trail details', () => {
3030 expect(nativeNodeProps(screen,'trail-write-review').title).toBe('Edit your review')
3131 expect(shownText(screen)).toContain('Natural')
3232 expect(shownText(screen)).toContain('Rock')
33 expect(shownText(screen)).toContain('68°F, Clear')
33 expect(shownText(screen)).toContain('68°F')
34 expect(shownText(screen)).toContain('Clear')
35 expect(shownText(screen)).toContain('50°–70°F')
36 expect(shownText(screen)).toContain('5 mph wind · 0.10 in precipitation')
3437 expect(shownText(screen)).toContain('3.0 mi · 4000–4500 ft')
3538 expect(nativeNodeProps(screen,'trail-native-map')).toMatchObject({scrollEnabled:false,zoomEnabled:false,rotateEnabled:false})
3639 for (const id of ['trail-save','trail-directions','trail-share']) expect(nativeNodeStyle(screen,id).flexGrow).toBe(1)