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.
|
|
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,
|
|
13
|
+
import { Screen, Stack, Text, Card, Divider, Lottie } from '../components/proto';
|
|
13
14
|
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
|
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(
|
|
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={
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
<
|
|
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
|
-
{
|
|
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
|
-
</
|
|
80
|
-
|
|
81
|
-
<Stack gap={12}>
|
|
82
|
-
{STEPS.map((step) => (
|
|
83
|
-
<TutorialCard key={step.n} step={step} />
|
|
84
|
-
))}
|
|
85
|
-
</Stack>
|
|
105
|
+
</Enter>
|
|
86
106
|
|
|
87
|
-
<
|
|
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
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
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
|
);
|