create-proto 0.7.6 → 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.6",
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",
@@ -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":[]}
@@ -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
  );