create-proto 0.6.0 → 0.6.1
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/dist/messages.js +1 -1
- package/dist/messages.js.map +1 -1
- package/package.json +1 -1
- package/template/.mcp.json +8 -0
- package/template/CLAUDE.md +16 -6
- package/template/README.md +1 -0
- package/template/components/proto/Modal.tsx +13 -19
- package/template/components/proto/ProtoConfigContext.tsx +29 -0
- package/template/components/proto/index.ts +1 -0
- package/template/components/proto/useTheme.ts +8 -7
package/dist/messages.js
CHANGED
|
@@ -9,7 +9,7 @@ export const messages = {
|
|
|
9
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
|
-
howToRestart: (name) => `Proto stopped.\nTo restart Proto: cd ${name} && npx proto start\nTo prompt Claude: cd ${name} && claude`,
|
|
12
|
+
howToRestart: (name) => `Proto stopped.\nTo restart Proto: cd ${name} && npx proto start\nTo share a link: cd ${name} && npx proto share\nTo prompt Claude: cd ${name} && claude`,
|
|
13
13
|
noNetwork: "Couldn't reach the package registry. Check your internet and try again.",
|
|
14
14
|
noPermission: "Don't have permission to write here. Try a different folder.",
|
|
15
15
|
noSpace: 'Out of disk space. Free some up and try again.',
|
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,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;
|
|
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,4CAA4C,IAAI,6CAA6C,IAAI,YAAY;IAC3J,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.6.
|
|
3
|
+
"version": "0.6.1",
|
|
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",
|
package/template/CLAUDE.md
CHANGED
|
@@ -76,6 +76,19 @@ You can't see the Simulator by default, so after any visual change, look at it:
|
|
|
76
76
|
|
|
77
77
|
Do this especially after layout, color, typography, or spacing changes. If `proto shot` reports no preview is running, the designer needs to run `proto start` first.
|
|
78
78
|
|
|
79
|
+
## Proto MCP tools
|
|
80
|
+
|
|
81
|
+
When the designer runs `proto start`, a local MCP server (`prototo`) connects automatically — no setup. It gives you two tools that close the feedback loop, so you can see what you built instead of asking the designer to relay it.
|
|
82
|
+
|
|
83
|
+
**After every screen write:**
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
2. Call `get_simulator_screenshot` — it returns what the prototype actually renders right now. Inspect it for the same defects as above.
|
|
87
|
+
|
|
88
|
+
Never assume a screen rendered correctly — check the screenshot. Never ask the designer to describe an error you can catch with `compile_check`. If a tool says the Simulator isn't running, the designer needs to run `proto start` first.
|
|
89
|
+
|
|
90
|
+
(`get_simulator_screenshot` is the automated form of the `proto shot` loop above — prefer the tool when it's available.)
|
|
91
|
+
|
|
79
92
|
## Mock vs real data
|
|
80
93
|
|
|
81
94
|
When a screen shows placeholder numbers that aren't yet wired to a real source, wrap them in `mock()` from `../components/proto` — `const conditions = mock({ wave: '0.8m' })`. It returns the value unchanged, so nothing breaks; it just makes stubbed data obvious and greppable so fake numbers never ship believing they're real. When you wire the value to a live source (a `fetch`), drop the `mock()` wrapper. (Don't use code comments to mark mock data — generated screens stay comment-free.)
|
|
@@ -106,14 +119,11 @@ When the designer says "use real data", follow this shape so every screen handle
|
|
|
106
119
|
|
|
107
120
|
**Keyless APIs** (no key, CORS-open, good for prototypes): Open-Meteo (weather/marine/air), REST Countries, Open Library, PokéAPI, Art Institute of Chicago, TheMealDB, Wikipedia REST. Prefer these so "make it real" stays a one-prompt step. If a source needs a key, tell the designer that key goes in `proto.config.js`, nowhere else.
|
|
108
121
|
|
|
109
|
-
##
|
|
110
|
-
|
|
111
|
-
Everything here runs at full fidelity on the designer's device. But when a prototype is **shared**, it renders from a lightweight description (a manifest) inside a tiny native viewer — so a shared prototype can only show what that viewer already contains. Two tiers, on purpose:
|
|
122
|
+
## Sharing your prototype
|
|
112
123
|
|
|
113
|
-
|
|
114
|
-
- **Local-only fidelity** is everything richer: gestures, `motion`/`gestures`/`canvas`/`svg`/`lottie`, live data fetching, anything with custom logic. Great on-device; it just won't appear in a shared link.
|
|
124
|
+
Run `proto share` to publish the prototype and get a `prototo.app/p/<token>` link. Anyone can open it in a browser — no install — and see the **real prototype running**, streamed live from a cloud iPhone. Full fidelity: gestures, `motion`/`gestures`/`canvas`/`svg`/`lottie`, live data, and custom logic all show up in the shared link exactly as they run on the designer's device. There's nothing to dumb down — build whatever the designer asks for and it all shares.
|
|
115
125
|
|
|
116
|
-
|
|
126
|
+
Two limits worth knowing: the prototype must be built on the **current Prototo** (an older one won't stream — re-scaffold if prompted), and **real-hardware features** (camera, GPS) are simulated on the cloud device rather than reading a real sensor.
|
|
117
127
|
|
|
118
128
|
## When modifying
|
|
119
129
|
|
package/template/README.md
CHANGED
|
@@ -5,4 +5,5 @@ Built with Prototo.
|
|
|
5
5
|
Before running `proto start` on iPhone, install Prototo from the App Store.
|
|
6
6
|
|
|
7
7
|
Run `proto start` to preview on the iOS Simulator (auto) or your iPhone (scan QR).
|
|
8
|
+
Run `proto share` to get a link anyone can open in their browser, no install needed.
|
|
8
9
|
Run `proto add "describe a screen"` to generate a new screen.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Modal as RNModal,
|
|
1
|
+
import { Modal as RNModal, View } from 'react-native';
|
|
2
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
2
3
|
import type { ReactNode } from 'react';
|
|
3
4
|
import { useTheme } from './useTheme';
|
|
4
5
|
import { Text } from './Text';
|
|
@@ -10,34 +11,27 @@ export type ModalProps = {
|
|
|
10
11
|
children?: ReactNode;
|
|
11
12
|
};
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Modal presented as iOS 26's native page sheet (UISheetPresentationController):
|
|
16
|
+
* native spring-in, backdrop dim, the screen behind receding, and swipe-to-dismiss
|
|
17
|
+
* — no hand-rolled overlay. onClose fires on both the swipe-dismiss (onRequestClose)
|
|
18
|
+
* and programmatic close.
|
|
19
|
+
*/
|
|
13
20
|
export function Modal({ title, visible, onClose, children }: ModalProps) {
|
|
14
21
|
const theme = useTheme();
|
|
15
22
|
return (
|
|
16
23
|
<RNModal
|
|
17
24
|
visible={visible}
|
|
18
|
-
transparent
|
|
19
25
|
animationType="slide"
|
|
26
|
+
presentationStyle="pageSheet"
|
|
20
27
|
onRequestClose={onClose}
|
|
21
28
|
>
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
style={{ flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.4)' }}
|
|
25
|
-
>
|
|
26
|
-
<Pressable
|
|
27
|
-
onPress={() => {}}
|
|
28
|
-
style={{
|
|
29
|
-
marginTop: 'auto',
|
|
30
|
-
backgroundColor: theme.surface.primary,
|
|
31
|
-
borderTopLeftRadius: theme.radius.modal,
|
|
32
|
-
borderTopRightRadius: theme.radius.modal,
|
|
33
|
-
padding: theme.space.lg,
|
|
34
|
-
gap: theme.space.md,
|
|
35
|
-
}}
|
|
36
|
-
>
|
|
29
|
+
<SafeAreaView style={{ flex: 1, backgroundColor: theme.surface.primary }} edges={['bottom']}>
|
|
30
|
+
<View style={{ flex: 1, padding: theme.space.lg, gap: theme.space.md }}>
|
|
37
31
|
<Text size="headline">{title}</Text>
|
|
38
32
|
{children}
|
|
39
|
-
</
|
|
40
|
-
</
|
|
33
|
+
</View>
|
|
34
|
+
</SafeAreaView>
|
|
41
35
|
</RNModal>
|
|
42
36
|
);
|
|
43
37
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode, createContext, useContext } from 'react';
|
|
2
|
+
import defaultConfigModule from '../../proto.config.js';
|
|
3
|
+
import type { ProtoConfig } from './types';
|
|
4
|
+
|
|
5
|
+
// The static `proto.config.js` at the project root is the default. Scaffolded
|
|
6
|
+
// projects render screens directly (no provider) and fall back to it, so their
|
|
7
|
+
// behaviour is unchanged. The manifest renderer wraps its tree in
|
|
8
|
+
// <ProtoConfigProvider config={manifest.app}> to drive theme from a manifest.
|
|
9
|
+
const defaultConfig = defaultConfigModule as ProtoConfig;
|
|
10
|
+
|
|
11
|
+
const ProtoConfigContext = createContext<ProtoConfig | null>(null);
|
|
12
|
+
|
|
13
|
+
export function ProtoConfigProvider({
|
|
14
|
+
config,
|
|
15
|
+
children,
|
|
16
|
+
}: {
|
|
17
|
+
config?: ProtoConfig;
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<ProtoConfigContext.Provider value={config ?? defaultConfig}>
|
|
22
|
+
{children}
|
|
23
|
+
</ProtoConfigContext.Provider>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useProtoConfig(): ProtoConfig {
|
|
28
|
+
return useContext(ProtoConfigContext) ?? defaultConfig;
|
|
29
|
+
}
|
|
@@ -8,6 +8,7 @@ export { Toggle, type ToggleProps } from './Toggle';
|
|
|
8
8
|
export { Divider } from './Divider';
|
|
9
9
|
export { Modal, type ModalProps } from './Modal';
|
|
10
10
|
export { useTheme, useAccent } from './useTheme';
|
|
11
|
+
export { ProtoConfigProvider, useProtoConfig } from './ProtoConfigContext';
|
|
11
12
|
export { mock } from './mock';
|
|
12
13
|
export { a11y } from './a11y';
|
|
13
14
|
export type { Theme, ThemeName, ThemeOverrides, ProtoConfig } from './types';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useColorScheme } from 'react-native';
|
|
2
|
-
import
|
|
2
|
+
import { useProtoConfig } from './ProtoConfigContext';
|
|
3
3
|
import { base, baseDark } from './tokens/base';
|
|
4
4
|
import { liquidGlass, liquidGlassDark } from './tokens/liquidGlass';
|
|
5
5
|
import { materialYou, materialYouDark } from './tokens/materialYou';
|
|
6
|
-
import type {
|
|
6
|
+
import type { Theme, ThemeName, ThemeOverrides } from './types';
|
|
7
7
|
|
|
8
8
|
const lightThemes: Record<ThemeName, Theme> = {
|
|
9
9
|
liquidGlass,
|
|
@@ -29,13 +29,14 @@ function mergeTheme(base: Theme, overrides?: ThemeOverrides): Theme {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const cfg = config as ProtoConfig;
|
|
33
|
-
|
|
34
32
|
// A hook — it reads the system colour scheme so screens re-render when the device
|
|
35
33
|
// switches between light and dark. Call it during render, like any hook.
|
|
36
|
-
//
|
|
37
|
-
//
|
|
34
|
+
// Config comes from the nearest <ProtoConfigProvider> (the manifest renderer sets
|
|
35
|
+
// one) or, with no provider, the project's static `proto.config.js`. A scheme can
|
|
36
|
+
// be pinned with `colorScheme: 'light' | 'dark'`; the default ('system') follows
|
|
37
|
+
// the device.
|
|
38
38
|
export function useTheme(): Theme {
|
|
39
|
+
const cfg = useProtoConfig();
|
|
39
40
|
const systemScheme = useColorScheme();
|
|
40
41
|
const preference = cfg.colorScheme ?? 'system';
|
|
41
42
|
const isDark = preference === 'dark' || (preference === 'system' && systemScheme === 'dark');
|
|
@@ -47,5 +48,5 @@ export function useTheme(): Theme {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export function useAccent(): string {
|
|
50
|
-
return
|
|
51
|
+
return useProtoConfig().accentColor ?? '#007AFF';
|
|
51
52
|
}
|