create-proto 0.1.7 → 0.1.8

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.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Scaffold a new Proto prototype: `npm create proto@latest myapp`. Designer-first native iOS prototyping environment that pairs with Claude Code.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@clack/prompts": "^0.7.0",
45
- "@sherizan/proto-cli": "^0.1.7",
45
+ "@sherizan/proto-cli": "^0.1.8",
46
46
  "fs-extra": "^11.2.0",
47
47
  "qrcode-terminal": "^0.12.0",
48
48
  "validate-npm-package-name": "^5.0.1"
@@ -11,12 +11,12 @@ You're the design tool inside a Proto project. The designer prompts you in plain
11
11
  **Native iOS** — the easiest path to system-feel UI. Apple handles Liquid Glass, SF Symbols, accessibility, dynamic type:
12
12
 
13
13
  - `expo-router/unstable-native-tabs` — native `UITabBar`
14
- - `expo-router` `Stack` with `headerLargeTitle: true` + `headerTransparent: true` — native large-title nav bar
14
+ - `expo-router` `Stack` with `headerLargeTitle: true` + `title` set per route — native large-title nav bar. Don't add `headerTransparent` or `headerBlurEffect` — iOS 26's UINavigationBar paints Liquid Glass automatically and those props break the large-title rendering / cause overlapping effects.
15
15
  - `expo-symbols` `SymbolView` — SF Symbol icons
16
16
  - `@expo/ui/swift-ui` — `Button`, `Toggle`, `Form`, `Section`, etc.
17
17
  - `expo-glass-effect` `GlassView` — Liquid Glass surfaces
18
18
 
19
- **Proto primitives** in `/components/proto` — small set of themed fallbacks: `Screen`, `Stack`, `Row`, `Text`, `Card`, `Button`, `Toggle`, `Divider`, `Modal`. Read the file when you need the API.
19
+ **Proto primitives** in `/components/proto` — small set of themed fallbacks: `Screen`, `Stack`, `Row`, `Text`, `Card`, `Button`, `Toggle`, `Divider`, `Modal`. Read the file when you need the API. Card's `glass={true}` uses `expo-glass-effect`'s native iOS 26 material; on older iOS it falls back to a plain View.
20
20
 
21
21
  **Custom** — when none of the above fit, write the component you need with React Native. Put shared ones in `/components/shared/`. The designer's vision wins; the primitives are starting points, not constraints.
22
22
 
@@ -26,8 +26,7 @@ iOS system font with dynamic type defaults. Override per-size below if you need
26
26
  xs 4 · sm 8 · md 16 · lg 24 · xl 32
27
27
 
28
28
  ### Surface
29
- - iOS 26+: Apple Liquid Glass via `expo-glass-effect`
30
- - iOS < 26 + Android: system blur via `expo-blur`
29
+ Apple Liquid Glass via `expo-glass-effect` (iOS 26+ native material). On older iOS it falls back to a plain surface — Proto targets iOS 26.
31
30
 
32
31
  (Add your own tokens below as you describe what you want. Claude rewrites this file as the design system evolves.)
33
32
 
@@ -2,15 +2,14 @@ import { Stack } from 'expo-router';
2
2
 
3
3
  export default function RootLayout() {
4
4
  return (
5
- <Stack
6
- screenOptions={{
7
- headerLargeTitle: true,
8
- headerTransparent: true,
9
- headerBlurEffect: 'systemChromeMaterial',
10
- headerLargeTitleShadowVisible: false,
11
- }}
12
- >
13
- <Stack.Screen name="index" options={{ title: 'Proto' }} />
5
+ <Stack>
6
+ <Stack.Screen
7
+ name="index"
8
+ options={{
9
+ title: 'Proto',
10
+ headerLargeTitle: true,
11
+ }}
12
+ />
14
13
  </Stack>
15
14
  );
16
15
  }
@@ -1,6 +1,5 @@
1
1
  import { View } from 'react-native';
2
- import { BlurView } from 'expo-blur';
3
- import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
2
+ import { GlassView } from 'expo-glass-effect';
4
3
  import type { ReactNode } from 'react';
5
4
  import { useTheme } from './useTheme';
6
5
 
@@ -10,40 +9,33 @@ export type CardProps = {
10
9
  children?: ReactNode;
11
10
  };
12
11
 
12
+ /**
13
+ * Card surface.
14
+ *
15
+ * glass={true}: Apple Liquid Glass via expo-glass-effect's GlassView. On
16
+ * iOS 26+ this paints the real native material that refracts content behind
17
+ * it. On older iOS GlassView falls back to a plain View — no third-party
18
+ * blur. Proto targets iOS 26 and only uses Apple's native material.
19
+ *
20
+ * Plain (no glass): opaque surface with theme tokens.
21
+ */
13
22
  export function Card({ glass = false, padding, children }: CardProps) {
14
23
  const theme = useTheme();
15
24
  const pad = padding ?? theme.space.md;
16
25
 
17
26
  if (glass) {
18
- if (isLiquidGlassAvailable()) {
19
- return (
20
- <GlassView
21
- style={{
22
- borderRadius: theme.radius.card,
23
- borderWidth: 1,
24
- borderColor: theme.border.default,
25
- padding: pad,
26
- overflow: 'hidden',
27
- }}
28
- >
29
- {children}
30
- </GlassView>
31
- );
32
- }
33
27
  return (
34
- <View
28
+ <GlassView
35
29
  style={{
36
30
  borderRadius: theme.radius.card,
37
- overflow: 'hidden',
38
31
  borderWidth: 1,
39
32
  borderColor: theme.border.default,
40
- backgroundColor: theme.surface.card,
33
+ padding: pad,
34
+ overflow: 'hidden',
41
35
  }}
42
36
  >
43
- <BlurView intensity={theme.blur.card} tint="light" style={{ padding: pad }}>
44
- {children}
45
- </BlurView>
46
- </View>
37
+ {children}
38
+ </GlassView>
47
39
  );
48
40
  }
49
41
 
@@ -8,30 +8,42 @@ export type ScreenProps = {
8
8
  children?: ReactNode;
9
9
  };
10
10
 
11
+ /**
12
+ * Screen wrapper for iOS 26+.
13
+ *
14
+ * Scrollable (default): the ScrollView is the top-level element so the native
15
+ * UINavigationBar can track it for large-title scroll behavior — the big
16
+ * title shrinks to a compact inline title as content scrolls up. iOS's
17
+ * automatic content insets handle the transparent nav bar and home indicator.
18
+ *
19
+ * Non-scrollable: SafeAreaView guards bottom + side edges (no scroll to track).
20
+ *
21
+ * Background lives on the outermost element so it covers bounce / inset areas.
22
+ * For Liquid Glass surfaces inside (cards, sheets), use Card with glass={true}
23
+ * — it wraps expo-glass-effect's GlassView, iOS 26's native material.
24
+ */
11
25
  export function Screen({ scrollable = true, children }: ScreenProps) {
12
26
  const theme = useTheme();
13
- const Body = scrollable ? ScrollView : View;
27
+ const padding = theme.space.md;
28
+
29
+ if (scrollable) {
30
+ return (
31
+ <ScrollView
32
+ style={{ flex: 1, backgroundColor: theme.surface.primary }}
33
+ contentContainerStyle={{ padding, gap: padding }}
34
+ contentInsetAdjustmentBehavior="automatic"
35
+ >
36
+ {children}
37
+ </ScrollView>
38
+ );
39
+ }
40
+
14
41
  return (
15
- <View style={{ flex: 1, backgroundColor: theme.surface.primary }}>
16
- <SafeAreaView style={{ flex: 1 }} edges={['bottom', 'left', 'right']}>
17
- <Body
18
- style={{ flex: 1 }}
19
- contentContainerStyle={
20
- scrollable
21
- ? { padding: theme.space.md, gap: theme.space.md }
22
- : undefined
23
- }
24
- contentInsetAdjustmentBehavior={scrollable ? 'automatic' : undefined}
25
- >
26
- {scrollable ? (
27
- children
28
- ) : (
29
- <View style={{ flex: 1, padding: theme.space.md, gap: theme.space.md }}>
30
- {children}
31
- </View>
32
- )}
33
- </Body>
34
- </SafeAreaView>
35
- </View>
42
+ <SafeAreaView
43
+ style={{ flex: 1, backgroundColor: theme.surface.primary }}
44
+ edges={['bottom', 'left', 'right']}
45
+ >
46
+ <View style={{ flex: 1, padding, gap: padding }}>{children}</View>
47
+ </SafeAreaView>
36
48
  );
37
49
  }
@@ -4,7 +4,7 @@ export const liquidGlass: Theme = {
4
4
  surface: {
5
5
  primary: 'rgba(255, 255, 255, 0.72)',
6
6
  secondary: 'rgba(255, 255, 255, 0.48)',
7
- card: 'rgba(255, 255, 255, 0.6)',
7
+ card: '#f3f5f8',
8
8
  nav: 'rgba(255, 255, 255, 0.82)',
9
9
  },
10
10
  text: {
@@ -19,8 +19,8 @@ export const liquidGlass: Theme = {
19
19
  modal: 60,
20
20
  },
21
21
  border: {
22
- default: 'rgba(255, 255, 255, 0.4)',
23
- strong: 'rgba(255, 255, 255, 0.7)',
22
+ default: 'rgba(0, 0, 0, 0.08)',
23
+ strong: 'rgba(0, 0, 0, 0.16)',
24
24
  },
25
25
  radius: {
26
26
  card: 22,
@@ -11,7 +11,7 @@
11
11
  "dependencies": {
12
12
  "@expo/ui": "55.0.17",
13
13
  "expo": "~55.0.26",
14
- "expo-blur": "~55.0.14",
14
+ "expo-clipboard": "~8.0.7",
15
15
  "expo-glass-effect": "~55.0.11",
16
16
  "expo-haptics": "~55.0.14",
17
17
  "expo-router": "~55.0.16",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@babel/core": "^7.24.0",
29
- "@sherizan/proto-cli": "^0.1.7",
29
+ "@sherizan/proto-cli": "^0.1.8",
30
30
  "@types/react": "~19.2.15",
31
31
  "babel-preset-expo": "~55.0.22",
32
32
  "typescript": "^5.9.2"
@@ -1,4 +1,7 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, useState } from 'react';
2
+ import { Pressable } from 'react-native';
3
+ import * as Clipboard from 'expo-clipboard';
4
+ import * as Haptics from 'expo-haptics';
2
5
  import Animated, {
3
6
  useSharedValue,
4
7
  useAnimatedStyle,
@@ -20,6 +23,32 @@ const STEPS = [
20
23
  },
21
24
  ];
22
25
 
26
+ function TutorialCard({ step }: { step: (typeof STEPS)[number] }) {
27
+ const [copied, setCopied] = useState(false);
28
+
29
+ const handleCopy = async () => {
30
+ await Clipboard.setStringAsync(step.prompt);
31
+ await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
32
+ setCopied(true);
33
+ setTimeout(() => setCopied(false), 1500);
34
+ };
35
+
36
+ return (
37
+ <Card padding={20}>
38
+ <Stack gap={10}>
39
+ <Row gap={10} align="center">
40
+ <Text size="headline">{step.n}.</Text>
41
+ <Text size="headline">{step.title}</Text>
42
+ </Row>
43
+ <Text size="body" color="secondary">{step.prompt}</Text>
44
+ <Pressable onPress={handleCopy} style={{ alignSelf: 'flex-end', marginTop: 4 }}>
45
+ <Text size="caption" color="accent">{copied ? 'Copied' : 'Copy'}</Text>
46
+ </Pressable>
47
+ </Stack>
48
+ </Card>
49
+ );
50
+ }
51
+
23
52
  export default function Home() {
24
53
  const opacity = useSharedValue(0);
25
54
  const translateY = useSharedValue(12);
@@ -36,13 +65,13 @@ export default function Home() {
36
65
 
37
66
  return (
38
67
  <Screen scrollable>
39
- <Stack gap={24} padding={20}>
68
+ <Stack gap={24}>
40
69
  <Animated.View style={heroStyle}>
41
70
  <Card glass padding={24}>
42
71
  <Stack gap={8}>
43
- <Text size="title">You're in.</Text>
72
+ <Text size="headline">You're in.</Text>
44
73
  <Text size="body" color="secondary">
45
- Try these prompts in Claude Code in Auto mode (press Shift+Tab).
74
+ {`In a terminal: cd {{APP_NAME}} && claude. Paste a prompt below.`}
46
75
  </Text>
47
76
  </Stack>
48
77
  </Card>
@@ -50,17 +79,7 @@ export default function Home() {
50
79
 
51
80
  <Stack gap={12}>
52
81
  {STEPS.map((step) => (
53
- <Card key={step.n} padding={20}>
54
- <Stack gap={10}>
55
- <Row gap={10} align="center">
56
- <Text size="headline" color="accent">{step.n}.</Text>
57
- <Text size="headline">{step.title}</Text>
58
- </Row>
59
- <Text size="body" color="accent" style={{ fontFamily: 'Menlo' }}>
60
- {step.prompt}
61
- </Text>
62
- </Stack>
63
- </Card>
82
+ <TutorialCard key={step.n} step={step} />
64
83
  ))}
65
84
  </Stack>
66
85