create-proto 0.7.5 → 0.7.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-proto",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "Scaffold a new Proto prototype: `npm create proto@latest myapp`. Describe a screen, watch your prototype run natively on iPhone — designer-first, paired with Claude Code.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -16,7 +16,24 @@ You're the design tool inside a Prototo project. The designer prompts you in pla
16
16
  - `@expo/ui/swift-ui` — `Button`, `Toggle`, `Form`, `Section`, etc.
17
17
  - `expo-glass-effect` `GlassView` — Liquid Glass surfaces
18
18
 
19
- **Prototo primitives** in `/components/proto` — small set of themed fallbacks: `Screen`, `Stack`, `Row`, `Text`, `Card`, `Button`, `Toggle`, `Slider`, `Stepper`, `Divider`, `Modal`. Read the file when you need the API. `Toggle`, `Slider`, and `Stepper` render real native SwiftUI (`@expo/ui`) on iOS, tinted with the app accent. Card's `glass={true}` uses `expo-glass-effect`'s native iOS 26 material; on older iOS it falls back to a plain View.
19
+ **Prototo primitives** in `/components/proto` — small set of themed fallbacks. Props cheat sheet (source is read-only; only read a file if something here doesn't match):
20
+
21
+ | Primitive | Props |
22
+ |---|---|
23
+ | `Screen` | `scrollable?: boolean` (default true) |
24
+ | `Stack` | `gap?`, `padding?`, `align?: 'start'\|'center'\|'end'` (unset = full-width stretch), `style?` |
25
+ | `Row` | `gap?`, `align?: 'start'\|'center'\|'end'` (default 'start'), `style?` |
26
+ | `Text` | `size?: 'title'\|'headline'\|'body'\|'caption'\|'label'`, `color?: 'primary'\|'secondary'\|'accent'\|'destructive'`, `style?` |
27
+ | `Card` | `glass?: boolean`, `padding?: number` |
28
+ | `Button` | `label: string`, `variant?: 'primary'\|'secondary'\|'ghost'\|'destructive'`, `onPress?`, `disabled?`, `icon?`, `style?`, `textStyle?` |
29
+ | `Toggle` | `label: string`, `value: boolean`, `onChange?(value)` |
30
+ | `Slider` | `value: number`, `onChange?`, `min?`, `max?`, `step?`, `label?` |
31
+ | `Stepper` | `label: string`, `value: number`, `onChange(value)`, `min?`, `max?`, `step?` |
32
+ | `Divider` | `label?: string` |
33
+ | `Input` | React Native `TextInputProps` passthrough |
34
+ | `Modal` | `title: string`, `visible: boolean`, `onClose?` |
35
+ | `Lottie` | `source`, `autoPlay?`, `loop?`, `style?` |
36
+ `Toggle`, `Slider`, and `Stepper` render real native SwiftUI (`@expo/ui`) on iOS, tinted with the app accent. Card's `glass={true}` uses `expo-glass-effect`'s native iOS 26 material; on older iOS it falls back to a plain View.
20
37
 
21
38
  **Prototo motion + graphics** — four subpath modules in `/components/proto` cover animation and drawing. Pick by what the prompt actually asks for:
22
39
 
@@ -32,7 +49,7 @@ Never import `react-native-ease`, `react-native-reanimated`, `lottie-react-nativ
32
49
 
33
50
  ## Adding a library
34
51
 
35
- To add any npm package (a font, an icon set, a utility), run `proto add <package>` — never `npm install` / `pnpm add` directly. `proto add` installs through `expo install`, which picks the version that matches this project and resolves dependencies cleanly, so the project doesn't break. If the package needs native code this Prototo doesn't bundle, `proto add` will say so — that feature won't appear on the device until the Proto team ships an updated Prototo.
52
+ To add any npm package (a font, an icon set, a utility), run `npx proto add <package>` — never `npm install` / `pnpm add` directly (and `proto` alone isn't on PATH — always `npx proto`). `proto add` installs through `expo install`, which picks the version that matches this project and resolves dependencies cleanly, so the project doesn't break. If the package needs native code this Prototo doesn't bundle, `proto add` will say so — that feature won't appear on the device until the Proto team ships an updated Prototo.
36
53
 
37
54
  ## File layout
38
55
 
@@ -48,6 +65,29 @@ To add any npm package (a font, an icon set, a utility), run `proto add <package
48
65
  A new screen `screens/Settings.tsx` needs:
49
66
  - `app/settings.tsx` re-exporting it (`import Settings from '../screens/Settings'; export default function SettingsRoute() { return <Settings />; }`)
50
67
  - A title set in `app/_layout.tsx`: `<Stack.Screen name="settings" options={{ title: 'Settings' }} />`
68
+
69
+ **Tabs (NativeTabs)** — exact shape for this project's pinned `expo-router` (the flat `Icon`/`Label` imports you may know do NOT exist here; they're nested under `Trigger`):
70
+
71
+ ```tsx
72
+ import { NativeTabs } from 'expo-router/unstable-native-tabs';
73
+
74
+ export default function Layout() {
75
+ return (
76
+ <NativeTabs>
77
+ <NativeTabs.Trigger name="index">
78
+ <NativeTabs.Trigger.Icon sf="house.fill" />
79
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
80
+ </NativeTabs.Trigger>
81
+ <NativeTabs.Trigger name="profile">
82
+ <NativeTabs.Trigger.Icon sf="person.fill" />
83
+ <NativeTabs.Trigger.Label>Profile</NativeTabs.Trigger.Label>
84
+ </NativeTabs.Trigger>
85
+ </NativeTabs>
86
+ );
87
+ }
88
+ ```
89
+
90
+ **Root-layout changes need a cold restart.** Swapping the navigator in `app/_layout.tsx` (Stack ↔ NativeTabs) does NOT apply via Fast Refresh, and the stale UI in a screenshot can look plausible. After editing `_layout.tsx`, call the `reload_app` MCP tool, then screenshot.
51
91
  - Route filenames are lowercase kebab-case.
52
92
 
53
93
  ## DESIGN.md is alive
@@ -78,7 +118,7 @@ Do this especially after layout, color, typography, or spacing changes. If `prot
78
118
 
79
119
  ## Proto MCP tools
80
120
 
81
- When the designer runs `proto start`, a local MCP server (`prototo`) connects automatically — no setup. It gives you three tools that close the feedback loop, so you can see what you built instead of asking the designer to relay it.
121
+ When the designer runs `proto start`, a local MCP server (`prototo`) connects automatically — no setup. It gives you four tools that close the feedback loop, so you can see what you built instead of asking the designer to relay it.
82
122
 
83
123
  **At the start of any fix session** (the designer says something is broken, red, or not working):
84
124
 
@@ -89,6 +129,8 @@ When the designer runs `proto start`, a local MCP server (`prototo`) connects au
89
129
  1. Call `compile_check` with the screen name — it type-checks the project and reports any problems in plain language. Fix anything it surfaces before moving on.
90
130
  2. Call `get_simulator_screenshot` — it returns what the prototype actually renders right now. Inspect it for the same defects as above.
91
131
 
132
+ **After editing `app/_layout.tsx` or any navigator:** call `reload_app` — root-layout changes don't Fast-Refresh, and a stale screenshot looks plausible.
133
+
92
134
  Never assume a screen rendered correctly — check the screenshot. Never ask the designer to describe an error you can catch with `get_metro_errors` or `compile_check`. If a tool says the Simulator isn't running (or Metro reports clean while the designer still sees an error), the designer needs to run `proto start` first.
93
135
 
94
136
  (`get_simulator_screenshot` is the automated form of the `proto shot` loop above — prefer the tool when it's available.)
@@ -0,0 +1 @@
1
+ {"nm":"Main Scene","h":480,"w":480,"meta":{"g":"@lottiefiles/creator@1.92.0"},"layers":[{"ty":4,"nm":"light-bot-R","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":18},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":27},{"s":[100,100],"t":34}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[432,368]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":18},{"s":[100],"t":24}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.941,0.894,0.91]},"r":1,"o":{"a":0,"k":100}}],"ind":1},{"ty":4,"nm":"light-bot-L","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":18},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":27},{"s":[100,100],"t":34}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[48,368]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":18},{"s":[100],"t":24}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.941,0.894,0.91]},"r":1,"o":{"a":0,"k":100}}],"ind":2},{"ty":4,"nm":"light-R-mid","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":13},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":22},{"s":[100,100],"t":29}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[336,272]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":13},{"s":[100],"t":19}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.941,0.894,0.91]},"r":1,"o":{"a":0,"k":100}}],"ind":3},{"ty":4,"nm":"light-L-mid","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":13},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":22},{"s":[100,100],"t":29}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[144,272]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":13},{"s":[100],"t":19}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.941,0.894,0.91]},"r":1,"o":{"a":0,"k":100}}],"ind":4},{"ty":4,"nm":"pink-far-R","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":10},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":19},{"s":[100,100],"t":26}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[432,272]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":10},{"s":[100],"t":16}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.91,0.416,0.612]},"r":1,"o":{"a":0,"k":100}}],"ind":5},{"ty":4,"nm":"pink-far-L","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":10},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":19},{"s":[100,100],"t":26}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[48,272]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":10},{"s":[100],"t":16}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.91,0.416,0.612]},"r":1,"o":{"a":0,"k":100}}],"ind":6},{"ty":4,"nm":"light-center","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":7},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":16},{"s":[100,100],"t":23}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[240,176]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":7},{"s":[100],"t":13}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.941,0.894,0.91]},"r":1,"o":{"a":0,"k":100}}],"ind":7},{"ty":4,"nm":"pink-R-mid","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":4},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":13},{"s":[100,100],"t":20}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[336,176]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":4},{"s":[100],"t":10}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.91,0.416,0.612]},"r":1,"o":{"a":0,"k":100}}],"ind":8},{"ty":4,"nm":"pink-L-mid","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":4},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":13},{"s":[100,100],"t":20}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[144,176]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":4},{"s":[100],"t":10}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.91,0.416,0.612]},"r":1,"o":{"a":0,"k":100}}],"ind":9},{"ty":4,"nm":"tip","sr":1,"st":0,"op":120,"ip":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0],"t":0},{"o":{"x":0.34,"y":1.56},"i":{"x":0.64,"y":1},"s":[115,115],"t":9},{"s":[100,100],"t":16}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[240,80]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[100],"t":6}]}},"shapes":[{"ty":"rc","nm":"Rect Shape 1","d":1,"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"s":{"a":0,"k":[96,96]}},{"ty":"fl","nm":"Fill","c":{"a":0,"k":[0.91,0.416,0.612]},"r":1,"o":{"a":0,"k":100}}],"ind":10}],"v":"5.7.0","fr":60,"op":120,"ip":0,"assets":[]}
@@ -4,13 +4,29 @@ import type { ReactNode } from 'react';
4
4
  export type StackProps = {
5
5
  gap?: number;
6
6
  padding?: number;
7
+ // same values as Row — the asymmetry was a guessable-wrong API paper cut
8
+ align?: 'start' | 'center' | 'end';
7
9
  children?: ReactNode;
8
10
  style?: ViewProps['style'];
9
11
  };
10
12
 
11
- export function Stack({ gap = 0, padding = 0, style, children }: StackProps) {
13
+ const alignMap = {
14
+ start: 'flex-start',
15
+ center: 'center',
16
+ end: 'flex-end',
17
+ } as const;
18
+
19
+ // No default: an unset align keeps RN's column default (stretch — children fill
20
+ // the width), so existing prototypes don't reflow.
21
+ export function Stack({ gap = 0, padding = 0, align, style, children }: StackProps) {
12
22
  return (
13
- <View style={[{ flexDirection: 'column', gap, padding }, style]}>
23
+ <View
24
+ style={[
25
+ { flexDirection: 'column', gap, padding },
26
+ align ? { alignItems: alignMap[align] } : null,
27
+ style,
28
+ ]}
29
+ >
14
30
  {children}
15
31
  </View>
16
32
  );
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useState, type ReactNode } from 'react';
2
2
  import { Pressable } from 'react-native';
3
3
  import * as Clipboard from 'expo-clipboard';
4
4
  import * as Haptics from 'expo-haptics';
@@ -6,29 +6,56 @@ import {
6
6
  Animated,
7
7
  useSharedValue,
8
8
  useAnimatedStyle,
9
+ withDelay,
9
10
  withTiming,
10
11
  Easing,
11
12
  } from '../components/proto/gestures';
12
- import { Screen, Stack, Row, Text, Card, Divider } from '../components/proto';
13
+ import { Screen, Stack, Text, Card, Divider, Lottie } from '../components/proto';
13
14
 
14
- const STEPS = [
15
+ // Prototo Desktop sets EXPO_PUBLIC_PROTO_DESKTOP=1 when it runs `proto start`
16
+ // (Metro inlines it at bundle time). In the desktop the terminal sits beside
17
+ // this preview with Claude already running, and the simulator clipboard never
18
+ // reaches the Mac, so the copy and the Copy affordance both change.
19
+ const IN_DESKTOP = process.env.EXPO_PUBLIC_PROTO_DESKTOP === '1';
20
+
21
+ const EXAMPLES = [
22
+ {
23
+ label: 'A whole screen',
24
+ prompt: 'Make me a music player home screen',
25
+ },
15
26
  {
16
- n: '1',
17
- title: 'Change background color',
18
- prompt: 'Fill background to light teal covering safe area top and bottom.',
27
+ label: 'Native feel',
28
+ prompt: 'Add a liquid glass tab bar with Home, Search, Profile',
19
29
  },
20
30
  {
21
- n: '2',
22
- title: 'Add a native component',
23
- prompt: 'add a liquid glass bottom tab bar with Home, Explore, About tabs',
31
+ label: 'A quick change',
32
+ prompt: 'Make the background sunset orange',
24
33
  },
25
34
  ];
26
35
 
27
- function TutorialCard({ step }: { step: (typeof STEPS)[number] }) {
36
+ function Enter({ delay, children }: { delay: number; children: ReactNode }) {
37
+ const opacity = useSharedValue(0);
38
+ const translateY = useSharedValue(12);
39
+
40
+ useEffect(() => {
41
+ const timing = { duration: 500, easing: Easing.out(Easing.quad) };
42
+ opacity.value = withDelay(delay, withTiming(1, timing));
43
+ translateY.value = withDelay(delay, withTiming(0, timing));
44
+ }, [delay, opacity, translateY]);
45
+
46
+ const style = useAnimatedStyle(() => ({
47
+ opacity: opacity.value,
48
+ transform: [{ translateY: translateY.value }],
49
+ }));
50
+
51
+ return <Animated.View style={style}>{children}</Animated.View>;
52
+ }
53
+
54
+ function ExampleCard({ example }: { example: (typeof EXAMPLES)[number] }) {
28
55
  const [copied, setCopied] = useState(false);
29
56
 
30
57
  const handleCopy = async () => {
31
- await Clipboard.setStringAsync(step.prompt);
58
+ await Clipboard.setStringAsync(example.prompt);
32
59
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
33
60
  setCopied(true);
34
61
  setTimeout(() => setCopied(false), 1500);
@@ -36,59 +63,66 @@ function TutorialCard({ step }: { step: (typeof STEPS)[number] }) {
36
63
 
37
64
  return (
38
65
  <Card padding={20}>
39
- <Stack gap={10}>
40
- <Row gap={10} align="center">
41
- <Text size="headline">{step.n}.</Text>
42
- <Text size="headline">{step.title}</Text>
43
- </Row>
44
- <Text size="body" color="secondary">{step.prompt}</Text>
45
- <Pressable onPress={handleCopy} style={{ alignSelf: 'flex-end', marginTop: 4 }}>
46
- <Text size="caption" color="accent">{copied ? 'Copied' : 'Copy'}</Text>
47
- </Pressable>
66
+ <Stack gap={6}>
67
+ <Text size="label" color="accent">
68
+ {example.label}
69
+ </Text>
70
+ <Text size="headline">{`"${example.prompt}"`}</Text>
71
+ {IN_DESKTOP ? null : (
72
+ <Pressable onPress={handleCopy} style={{ alignSelf: 'flex-end', marginTop: 4 }}>
73
+ <Text size="caption" color="accent">
74
+ {copied ? 'Copied' : 'Copy'}
75
+ </Text>
76
+ </Pressable>
77
+ )}
48
78
  </Stack>
49
79
  </Card>
50
80
  );
51
81
  }
52
82
 
53
83
  export default function Home() {
54
- const opacity = useSharedValue(0);
55
- const translateY = useSharedValue(12);
56
-
57
- useEffect(() => {
58
- opacity.value = withTiming(1, { duration: 600, easing: Easing.out(Easing.quad) });
59
- translateY.value = withTiming(0, { duration: 600, easing: Easing.out(Easing.quad) });
60
- }, [opacity, translateY]);
61
-
62
- const heroStyle = useAnimatedStyle(() => ({
63
- opacity: opacity.value,
64
- transform: [{ translateY: translateY.value }],
65
- }));
66
-
67
84
  return (
68
85
  <Screen scrollable>
69
86
  <Stack gap={24}>
70
- <Animated.View style={heroStyle}>
87
+ <Enter delay={0}>
88
+ <Lottie
89
+ source={require('../assets/lottie/logo-prototo.json')}
90
+ style={{ width: 56, height: 56, alignSelf: 'center' }}
91
+ />
92
+ </Enter>
93
+
94
+ <Enter delay={80}>
71
95
  <Card glass padding={24}>
72
96
  <Stack gap={8}>
73
97
  <Text size="headline">You're in.</Text>
74
98
  <Text size="body" color="secondary">
75
- {`In a terminal: cd {{APP_NAME}} && claude. Paste a prompt below.`}
99
+ {IN_DESKTOP
100
+ ? 'Type your first prompt in the terminal beside this preview, and watch it appear here.'
101
+ : `In a terminal: cd {{APP_NAME}} && claude. Then paste a prompt below.`}
76
102
  </Text>
77
103
  </Stack>
78
104
  </Card>
79
- </Animated.View>
80
-
81
- <Stack gap={12}>
82
- {STEPS.map((step) => (
83
- <TutorialCard key={step.n} step={step} />
84
- ))}
85
- </Stack>
105
+ </Enter>
86
106
 
87
- <Divider />
107
+ <Enter delay={160}>
108
+ <Stack gap={12}>
109
+ <Text size="label" color="secondary">
110
+ Try one of these
111
+ </Text>
112
+ {EXAMPLES.map((example) => (
113
+ <ExampleCard key={example.label} example={example} />
114
+ ))}
115
+ </Stack>
116
+ </Enter>
88
117
 
89
- <Text size="caption" color="secondary">
90
- Each prompt builds on the last. Prototo reads DESIGN.md before every change.
91
- </Text>
118
+ <Enter delay={240}>
119
+ <Stack gap={16}>
120
+ <Divider />
121
+ <Text size="caption" color="secondary">
122
+ Each prompt builds on the last. Prototo reads DESIGN.md before every change.
123
+ </Text>
124
+ </Stack>
125
+ </Enter>
92
126
  </Stack>
93
127
  </Screen>
94
128
  );