create-proto 0.1.7 → 0.1.9
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/README.md +8 -5
- package/dist/banner.js +7 -7
- package/dist/banner.js.map +1 -1
- package/dist/messages.js +2 -2
- package/dist/messages.js.map +1 -1
- package/package.json +2 -2
- package/template/CLAUDE.md +6 -6
- package/template/DESIGN.md +1 -2
- package/template/app/_layout.tsx +8 -9
- package/template/components/proto/Card.tsx +16 -24
- package/template/components/proto/Screen.tsx +34 -22
- package/template/components/proto/tokens/liquidGlass.ts +3 -3
- package/template/package.json +2 -2
- package/template/screens/Home.tsx +35 -16
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# create-proto
|
|
2
2
|
|
|
3
|
-
Scaffold a new
|
|
3
|
+
Scaffold a new Prototo prototype:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm create proto@latest myapp
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
> The npm package is `create-proto` (and the CLI bin is `proto`) for historical reasons. The brand and product name is **Prototo** — that's what designers see in the banner, in the iOS nav bar, and at [prototo.app](https://prototo.app).
|
|
10
|
+
|
|
11
|
+
## What Prototo is
|
|
10
12
|
|
|
11
13
|
A prompt-native design environment. The iOS Simulator is the canvas. Claude Code CLI is the design tool. The designer describes what they want; native iOS UI appears in the Simulator, with Apple's real Liquid Glass on iOS 26+.
|
|
12
14
|
|
|
@@ -14,8 +16,8 @@ No canvas. No IDE. No engineering concepts.
|
|
|
14
16
|
|
|
15
17
|
## What this command does
|
|
16
18
|
|
|
17
|
-
1. Scaffolds the project: `DESIGN.md` (design system source-of-truth), `CLAUDE.md` (Claude Code instructions for
|
|
18
|
-
2. Installs dependencies (Expo SDK 55, React Native, Reanimated 4, `@expo/ui`, `expo-glass-effect`)
|
|
19
|
+
1. Scaffolds the project: `DESIGN.md` (design system source-of-truth), `CLAUDE.md` (Claude Code instructions for Prototo-aware generation), Prototo component library, a starter Welcome screen
|
|
20
|
+
2. Installs dependencies (Expo SDK 55, React Native, Reanimated 4, `@expo/ui`, `expo-glass-effect`, `expo-clipboard`)
|
|
19
21
|
3. Auto-launches Metro and opens the iOS Simulator
|
|
20
22
|
|
|
21
23
|
Then in another terminal:
|
|
@@ -36,7 +38,8 @@ Claude reads `CLAUDE.md`, generates `app/_layout.tsx` using Apple's native `UITa
|
|
|
36
38
|
|
|
37
39
|
## Learn more
|
|
38
40
|
|
|
39
|
-
[
|
|
41
|
+
- Website: [prototo.app](https://prototo.app)
|
|
42
|
+
- Source: [github.com/sherizan/proto](https://github.com/sherizan/proto)
|
|
40
43
|
|
|
41
44
|
## License
|
|
42
45
|
|
package/dist/banner.js
CHANGED
|
@@ -2,16 +2,16 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
const ART = [
|
|
5
|
-
'██████╗ ██████╗ ██████╗ ████████╗ ██████╗ ',
|
|
6
|
-
'
|
|
7
|
-
'██████╔╝██████╔╝██║ ██║ ██║ ██║ ██║',
|
|
8
|
-
'██╔═══╝ ██╔══██╗██║ ██║ ██║ ██║ ██║',
|
|
9
|
-
'██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝',
|
|
10
|
-
'╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ',
|
|
5
|
+
'██████╗ ██████╗ ██████╗ ████████╗ ██████╗ ████████╗ ██████╗ ',
|
|
6
|
+
'██╔══██╗██╔══██╗██╔═══██╗╚══██╔══╝██╔═══██╗╚══██╔══╝██╔═══██╗',
|
|
7
|
+
'██████╔╝██████╔╝██║ ██║ ██║ ██║ ██║ ██║ ██║ ██║',
|
|
8
|
+
'██╔═══╝ ██╔══██╗██║ ██║ ██║ ██║ ██║ ██║ ██║ ██║',
|
|
9
|
+
'██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝ ██║ ╚██████╔╝',
|
|
10
|
+
'╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ',
|
|
11
11
|
].join('\n');
|
|
12
12
|
const TAGLINE = 'The prompt-native design environment for native iOS.';
|
|
13
13
|
export function renderBanner(version) {
|
|
14
|
-
return `${ART}\n\
|
|
14
|
+
return `${ART}\n\nPrototo v${version}\n${TAGLINE}\n`;
|
|
15
15
|
}
|
|
16
16
|
export function readOwnVersion() {
|
|
17
17
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
package/dist/banner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,GAAG,GAAG;IACV
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,GAAG,GAAG;IACV,+DAA+D;IAC/D,+DAA+D;IAC/D,+DAA+D;IAC/D,+DAA+D;IAC/D,+DAA+D;IAC/D,+DAA+D;CAChE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,OAAO,GAAG,sDAAsD,CAAC;AAEvE,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,GAAG,GAAG,gBAAgB,OAAO,KAAK,OAAO,IAAI,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KAC5C,CAAC;IACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;YAC3D,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACnE,OAAO,GAAG,CAAC,OAAO,CAAC;YACrB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/messages.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const messages = {
|
|
2
|
-
header: '
|
|
2
|
+
header: 'Prototo',
|
|
3
3
|
settingUp: (name, percent) => percent !== undefined ? `Setting up ${name}... ${percent}%` : `Setting up ${name}...`,
|
|
4
4
|
installed: (seconds) => `Installed in ${seconds}s`,
|
|
5
5
|
folderExists: (name) => `That folder already exists. Pick another name: npm create proto@latest <name> (currently: "${name}").`,
|
|
6
6
|
installFailedHint: (name) => `Couldn't install dependencies. Once your environment is ready: cd ${name} && pnpm install && proto start`,
|
|
7
7
|
cancelled: 'Cancelled. Folder removed.',
|
|
8
8
|
usingDefaultName: (name) => `Using name: ${name} (pass a name as the first argument to override).`,
|
|
9
|
-
bootingProto: 'Booting
|
|
9
|
+
bootingProto: 'Booting Prototo...',
|
|
10
10
|
nextSteps: (name) => `Keep this terminal running. It auto-refreshes your prototype.\n\nOpen a new terminal and run:\n cd ${name} && claude\n\nIn Claude, press Shift+Tab to switch to Auto mode.`,
|
|
11
11
|
protoCliNotFound: (name) => `Couldn't find proto-cli. Run manually: cd ${name} && npx proto start`,
|
|
12
12
|
howToRestart: (name) => `Proto stopped.\nTo restart Proto: cd ${name} && npx proto start\nTo prompt Claude: cd ${name} && claude`,
|
package/dist/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,MAAM,EAAE,SAAS;IACjB,SAAS,EAAE,CAAC,IAAY,EAAE,OAAgB,EAAE,EAAE,CAC5C,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,IAAI,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,cAAc,IAAI,KAAK;IACvF,SAAS,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,gBAAgB,OAAO,GAAG;IAC1D,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE,CAC7B,8FAA8F,IAAI,KAAK;IACzG,iBAAiB,EAAE,CAAC,IAAY,EAAE,EAAE,CAClC,qEAAqE,IAAI,iCAAiC;IAC5G,SAAS,EAAE,4BAA4B;IACvC,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CACjC,eAAe,IAAI,mDAAmD;IACxE,YAAY,EAAE,oBAAoB;IAClC,SAAS,EAAE,CAAC,IAAY,EAAE,EAAE,CAC1B,uGAAuG,IAAI,kEAAkE;IAC/K,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CACjC,6CAA6C,IAAI,qBAAqB;IACxE,YAAY,EAAE,CAAC,IAAY,EAAE,EAAE,CAC7B,wCAAwC,IAAI,6CAA6C,IAAI,YAAY;IAC3G,SAAS,EACP,yEAAyE;IAC3E,YAAY,EACV,8DAA8D;IAChE,OAAO,EAAE,gDAAgD;IACzD,aAAa,EACX,mEAAmE;CACtE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-proto",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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.
|
|
45
|
+
"@sherizan/proto-cli": "^0.1.9",
|
|
46
46
|
"fs-extra": "^11.2.0",
|
|
47
47
|
"qrcode-terminal": "^0.12.0",
|
|
48
48
|
"validate-npm-package-name": "^5.0.1"
|
package/template/CLAUDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Prototo Project — Claude Code Instructions
|
|
2
2
|
|
|
3
|
-
You're the design tool inside a
|
|
3
|
+
You're the design tool inside a Prototo project. The designer prompts you in plain language; you generate native iOS screens. The iOS Simulator is the canvas. Designers never touch files.
|
|
4
4
|
|
|
5
5
|
## Read first
|
|
6
6
|
- `DESIGN.md` — design tokens and the project's decisions
|
|
@@ -11,14 +11,14 @@ 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` + `
|
|
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
|
-
**
|
|
19
|
+
**Prototo 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
|
-
**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;
|
|
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; primitives are starting points, not constraints.
|
|
22
22
|
|
|
23
23
|
## File layout
|
|
24
24
|
|
|
@@ -27,7 +27,7 @@ You're the design tool inside a Proto project. The designer prompts you in plain
|
|
|
27
27
|
/app/_layout.tsx Stack (for native large titles) or NativeTabs (for tabs)
|
|
28
28
|
/screens/<Name>.tsx screen, PascalCase, default export
|
|
29
29
|
/components/shared/ designer-created custom components
|
|
30
|
-
/components/proto/
|
|
30
|
+
/components/proto/ Prototo primitives — read-only
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
A new screen `screens/Settings.tsx` needs:
|
package/template/DESIGN.md
CHANGED
|
@@ -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
|
-
|
|
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 — Prototo 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
|
|
package/template/app/_layout.tsx
CHANGED
|
@@ -2,15 +2,14 @@ import { Stack } from 'expo-router';
|
|
|
2
2
|
|
|
3
3
|
export default function RootLayout() {
|
|
4
4
|
return (
|
|
5
|
-
<Stack
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<Stack.Screen name="index" options={{ title: 'Proto' }} />
|
|
5
|
+
<Stack>
|
|
6
|
+
<Stack.Screen
|
|
7
|
+
name="index"
|
|
8
|
+
options={{
|
|
9
|
+
title: 'Prototo',
|
|
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 {
|
|
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
|
-
<
|
|
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
|
-
|
|
33
|
+
padding: pad,
|
|
34
|
+
overflow: 'hidden',
|
|
41
35
|
}}
|
|
42
36
|
>
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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: '
|
|
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(
|
|
23
|
-
strong: 'rgba(
|
|
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,
|
package/template/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@expo/ui": "55.0.17",
|
|
13
13
|
"expo": "~55.0.26",
|
|
14
|
-
"expo-
|
|
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.
|
|
29
|
+
"@sherizan/proto-cli": "^0.1.9",
|
|
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}
|
|
68
|
+
<Stack gap={24}>
|
|
40
69
|
<Animated.View style={heroStyle}>
|
|
41
70
|
<Card glass padding={24}>
|
|
42
71
|
<Stack gap={8}>
|
|
43
|
-
<Text size="
|
|
72
|
+
<Text size="headline">You're in.</Text>
|
|
44
73
|
<Text size="body" color="secondary">
|
|
45
|
-
|
|
74
|
+
{`In a terminal: cd {{APP_NAME}} && claude. Paste a prompt below.`}
|
|
46
75
|
</Text>
|
|
47
76
|
</Stack>
|
|
48
77
|
</Card>
|
|
@@ -50,24 +79,14 @@ export default function Home() {
|
|
|
50
79
|
|
|
51
80
|
<Stack gap={12}>
|
|
52
81
|
{STEPS.map((step) => (
|
|
53
|
-
<
|
|
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
|
|
|
67
86
|
<Divider />
|
|
68
87
|
|
|
69
88
|
<Text size="caption" color="secondary">
|
|
70
|
-
Each prompt builds on the last.
|
|
89
|
+
Each prompt builds on the last. Prototo reads DESIGN.md before every change.
|
|
71
90
|
</Text>
|
|
72
91
|
</Stack>
|
|
73
92
|
</Screen>
|