proto-plugin 0.1.0 → 0.1.2
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/AGENTS.md +308 -29
- package/README.md +55 -13
- package/package.json +1 -1
- package/scripts/lib/host-config.example.sh +1 -1
- package/scripts/publish-package.sh +41 -0
- package/scripts/verify-prototype-preview-states.mjs +57 -0
- package/src/components/platform-ui/controls-panel-select.tsx +4 -3
- package/src/components/platform-ui/prototype-tool-dialog.tsx +117 -0
- package/src/components/platform-ui/sidebar.tsx +34 -16
- package/src/components/prototype-gallery-client.tsx +77 -37
- package/src/components/prototypes/design-exploration-variant-preview-shell.tsx +43 -0
- package/src/components/prototypes/prototype-brief-field.tsx +33 -0
- package/src/components/prototypes/prototype-change-log-panel.tsx +4 -0
- package/src/components/prototypes/prototype-comment-storage-empty-state.tsx +41 -0
- package/src/components/prototypes/prototype-create-exploration-modal.tsx +41 -64
- package/src/components/prototypes/prototype-create-prototype-modal.tsx +102 -0
- package/src/components/prototypes/prototype-create-state-modal.tsx +31 -70
- package/src/components/prototypes/prototype-default-state-map-page.tsx +4 -4
- package/src/components/prototypes/prototype-design-brief-modal.tsx +10 -10
- package/src/components/prototypes/prototype-design-brief-panel.tsx +261 -231
- package/src/components/prototypes/prototype-floating-pill.module.scss +16 -9
- package/src/components/prototypes/prototype-mobbin-gallery.tsx +18 -2
- package/src/components/prototypes/prototype-review-sidebar.tsx +11 -28
- package/src/components/prototypes/prototype-spec-panel-content.tsx +5 -5
- package/src/components/prototypes/prototype-spec-panel-empty-state.tsx +53 -0
- package/src/components/prototypes/prototype-state-canvas-overlay.module.scss +20 -47
- package/src/components/prototypes/prototype-state-canvas-overlay.tsx +4 -42
- package/src/components/prototypes/prototype-variant-explorer.tsx +32 -78
- package/src/components/prototypes/prototype-vercel-preview-modal.tsx +4 -3
- package/src/components/prototypes/state-map-annotation-tooltip.module.scss +7 -7
- package/src/components/shell/prototype-component-library-empty-state.tsx +59 -0
- package/src/components/shell/prototype-gallery-nav.tsx +124 -0
- package/src/components/shell/prototype-gallery-shell.tsx +45 -13
- package/src/components/shell/prototype-link-source-modal.tsx +100 -0
- package/src/components/ui/button.tsx +3 -1
- package/src/components/ui/dialog.tsx +43 -4
- package/src/config/create-prototype-component-library-page.tsx +74 -0
- package/src/config/create-prototype-gallery-page.tsx +26 -23
- package/src/config/create-prototype-host-app.tsx +3 -15
- package/src/config/create-prototype-site-layout.tsx +14 -1
- package/src/config/create-prototype-state-map-page.tsx +44 -0
- package/src/config/prototype-site-layout-client.tsx +20 -6
- package/src/config/with-prototype.ts +21 -0
- package/src/index.ts +54 -10
- package/src/lib/copy-text-with-attachments.ts +35 -0
- package/src/lib/format-source-directory-name.ts +12 -0
- package/src/lib/platform-ui/sidebar-interactive-cursor.ts +19 -0
- package/src/lib/pr-split/build-pr-split-plan-prompt.ts +81 -0
- package/src/lib/pr-split/pr-split-highlight.ts +3 -0
- package/src/lib/prototype-comments/core/annotation-target.ts +12 -3
- package/src/lib/prototype-comments/core/comment-capture-blocked.ts +24 -0
- package/src/lib/prototype-comments/core/screenshot.ts +68 -31
- package/src/lib/prototype-comments/core/storage.ts +3 -6
- package/src/lib/prototype-comments/core/types.ts +2 -0
- package/src/lib/prototype-comments/hooks/useLayoutShift.ts +9 -2
- package/src/lib/prototype-comments/react/CommentCaptureToolbar.tsx +45 -10
- package/src/lib/prototype-comments/react/CommentProvider.tsx +19 -0
- package/src/lib/prototype-comments/ui/CommentModeToggleButton.tsx +34 -0
- package/src/lib/prototype-comments/ui/CommentsGrid.tsx +8 -16
- package/src/lib/prototype-comments/ui/CommentsSidebar.tsx +5 -19
- package/src/lib/prototype-comments/ui/capture-styles.module.scss +5 -1
- package/src/lib/prototype-comments/ui/comments-grid-card.module.scss +0 -7
- package/src/lib/prototypes/build-design-exploration-renderers.ts +44 -0
- package/src/lib/prototypes/comment-storage-setup-prompt.ts +124 -0
- package/src/lib/prototypes/create-prototype-prompt.ts +96 -0
- package/src/lib/prototypes/create-prototype-registry.ts +5 -0
- package/src/lib/prototypes/design-exploration-types.ts +261 -10
- package/src/lib/prototypes/link-source-prompt.ts +93 -0
- package/src/lib/prototypes/populate-component-library-prompt.ts +64 -0
- package/src/lib/prototypes/prototype-config-types.ts +2 -0
- package/src/lib/prototypes/prototype-layout-classes.ts +19 -0
- package/src/lib/prototypes/prototype-preview-state-registry.ts +5 -2
- package/src/lib/prototypes/prototype-review-context.tsx +4 -4
- package/src/lib/prototypes/prototype-state-canvas-constants.ts +1 -1
- package/src/lib/prototypes/prototype-state-canvas-types.ts +0 -2
- package/src/lib/prototypes/prototype-storage-status.ts +31 -0
- package/src/lib/prototypes/sort-prototypes-by-creation-date.ts +43 -0
- package/src/lib/prototypes/use-prototype-tool-theme.tsx +1 -1
- package/src/lib/prototypes/use-toolbar-hover-panel.ts +12 -1
- package/src/lib/prototypes/variant-set-lucide-icon.tsx +24 -0
- package/src/lib/tool-portal.ts +24 -0
- package/src/lib/use-copy-to-clipboard.ts +7 -13
- package/src/server/create-host-api-route.ts +7 -16
- package/src/server/create-prototype-api-routes.ts +0 -115
- package/src/server/prototype-storage-status-route.ts +29 -0
- package/src/server.ts +0 -8
- package/src/styles/globals.css +359 -71
- package/src/lib/prototypes/design-exploration-default-storage.ts +0 -68
- package/src/lib/prototypes/design-exploration-default.ts +0 -51
- package/src/lib/prototypes/use-design-exploration-default.ts +0 -114
- package/src/server/redis/prototype-design-exploration-default.ts +0 -32
package/AGENTS.md
CHANGED
|
@@ -26,6 +26,47 @@ Host prototypes import framework from `prototype` and UI from `@/components/ui`.
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
+
## Viewport layout vs responsive UI
|
|
30
|
+
|
|
31
|
+
The review toolbar **Layout** toggle (desktop / mobile) only changes the **preview frame** — it sets `#prototype-viewport` dimensions and `data-prototype-viewport-layout` for shell chrome (device frame, footer behavior). It must **not** drive product UI differences inside prototype content.
|
|
32
|
+
|
|
33
|
+
**Host prototype content** must respond to **viewport width** only:
|
|
34
|
+
|
|
35
|
+
- Tailwind breakpoints (`sm:`, `md:`, …) or container queries for CSS
|
|
36
|
+
- `matchMedia` / resize observers when JS must branch
|
|
37
|
+
|
|
38
|
+
**Do not use** for layout or component swaps inside `src/prototypes/`:
|
|
39
|
+
|
|
40
|
+
- `prototype-desktop:` / `prototype-mobile:` variants (deprecated; do not reintroduce)
|
|
41
|
+
- `usePrototypeMobileViewportFrame()` / `usePrototypeViewportFrame()` for UI branching
|
|
42
|
+
- `review.viewportLayout` or `data-prototype-viewport-layout` in prototype components
|
|
43
|
+
|
|
44
|
+
The mobile preview frame is 390×844px; the desktop frame fills the preview stage. Breakpoints apply to the **content viewport width** — the same surface users see in production.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Tool theme vs prototype preview content
|
|
49
|
+
|
|
50
|
+
The gallery/review **light/dark mode toggle** only changes **tool chrome** (sidebar, header, gallery nav, modals). It must **never** change colors, typography, or tokens inside prototype preview content (`#prototype-viewport` / `[data-prototype-screenshot]`).
|
|
51
|
+
|
|
52
|
+
**Tool theme** is scoped to `[data-prototype-root]` via `--tool-chrome-*` and `data-prototype-comment-theme`. Tool UI uses those tokens (or Tailwind utilities whose `--color-*` values resolve from `[data-prototype-root]`).
|
|
53
|
+
|
|
54
|
+
**Prototype preview content** uses fixed **product** tokens from the synced source app. Host `globals.css` defines `--product-*` names and maps them to shadcn tokens inside `[data-prototype-screenshot]` only. Package CSS resets all semantic and `--color-*` tokens on `[data-prototype-screenshot]` so tool theme cannot cascade in.
|
|
55
|
+
|
|
56
|
+
**Do not use** for styling inside `src/prototypes/`:
|
|
57
|
+
|
|
58
|
+
- `usePrototypeToolTheme()` / `useLightTheme` / `commentTheme`
|
|
59
|
+
- `data-prototype-comment-theme` on prototype components
|
|
60
|
+
- `--tool-chrome-*` tokens in prototype JSX or CSS
|
|
61
|
+
|
|
62
|
+
**Do not** in package or host CSS:
|
|
63
|
+
|
|
64
|
+
- Bridge shadcn tokens (`--background`, `--foreground`, `--card`, …) to `--tool-chrome-*` on `[data-prototype-root]`
|
|
65
|
+
- Map host `@theme inline` `--color-*` to `var(--background)` — use self-referential `var(--color-*)` for tool chrome; product tokens belong in `[data-prototype-screenshot]` only
|
|
66
|
+
- Put product token values on `:root` without the `--product-*` prefix (tool theme would override them)
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
29
70
|
## Relationship to source app (host apps)
|
|
30
71
|
|
|
31
72
|
Host apps replicate product UI from the **source application** (linked read-only via `SOURCE_PATH` and `pnpm link-source`) **exactly** — standalone code, no runtime imports from the source monorepo.
|
|
@@ -66,7 +107,7 @@ The sync/link scripts are host-agnostic — package code names nothing host-spec
|
|
|
66
107
|
|
|
67
108
|
Default to **mock data only**.
|
|
68
109
|
|
|
69
|
-
Reference prototype: `
|
|
110
|
+
Reference prototype: `booking-flow` — `src/prototypes/booking-flow/` (or any registered slug in your host app).
|
|
70
111
|
|
|
71
112
|
---
|
|
72
113
|
|
|
@@ -79,8 +120,8 @@ Shareable UI at `/prototypes/[slug]`. Registered in `prototype.config.ts`.
|
|
|
79
120
|
### Rules (mandatory)
|
|
80
121
|
|
|
81
122
|
1. **Register** the slug in `prototype.config.ts` and add a screenshot under `public/prototypes/screenshots/`.
|
|
82
|
-
2. **Create** `src/prototypes/<slug>/component-ids.ts` — every component target id (see `
|
|
83
|
-
3. **Wire comments** with `usePrototypeComments` on the page (see `
|
|
123
|
+
2. **Create** `src/prototypes/<slug>/component-ids.ts` — every component target id (see `booking-flow/component-ids.ts`).
|
|
124
|
+
3. **Wire comments** with `usePrototypeComments(liveState, onRestore)` on the page (see `booking-flow/index.tsx`).
|
|
84
125
|
4. **Wrap the root JSX of every React component** in `PrototypeComponent` from `prototype`.
|
|
85
126
|
5. **On-page prototype controls** (state pickers, variant tabs, preview toggles) must use `PrototypeControl` or `PrototypeControlGroup`, not plain `PrototypeComponent`.
|
|
86
127
|
6. **Use ids from the registry only** — dev mode logs an error for unregistered ids.
|
|
@@ -88,9 +129,58 @@ Shareable UI at `/prototypes/[slug]`. Registered in `prototype.config.ts`.
|
|
|
88
129
|
- File's main export: kebab-case filename (`settings-section.tsx` → `settings-section`; `index.tsx` → `page`).
|
|
89
130
|
- Other functions in the same file: `{filename}.{function-kebab}`.
|
|
90
131
|
- Dynamic list items: add a prefix to `dynamicPrefixes` in `component-ids.ts`.
|
|
91
|
-
8. **Scroll container:** `PrototypeComponent` with `id="scroll-container"`
|
|
132
|
+
8. **Scroll container:** `PrototypeComponent` with `id="scroll-container"` — see **Prototype viewport fill** below.
|
|
92
133
|
9. **Do not** use `data-testid` for comment anchoring.
|
|
93
134
|
|
|
135
|
+
### Prototype viewport fill (avoid offset / clipping)
|
|
136
|
+
|
|
137
|
+
There are **two** common offset bugs — do not confuse them:
|
|
138
|
+
|
|
139
|
+
| Symptom | Cause | Fix |
|
|
140
|
+
|--------|--------|-----|
|
|
141
|
+
| Prototype preview pinned to top-left; empty space + clipping on right/bottom | Missing `h-full` fill chain inside `#prototype-viewport` | Use layout constants below on every nested wrapper |
|
|
142
|
+
| Gallery / sidebar with black gaps + white bar at bottom of window | Site shell height mismatch (`h-full` parent + `h-svh` child, or body bg showing through) | Do **not** change gallery shell height classes ad hoc — parent route wrapper owns `h-svh`; `[data-prototype-root]` fills with `h-full flex-1` |
|
|
143
|
+
|
|
144
|
+
The preview shell (`#prototype-viewport`) only auto-sizes **direct** children of `.viewportPage` (`width/height: 100%`, column flex). Every nested layer must repeat the fill chain or content shrinks to the top-left and clips on the opposite edge.
|
|
145
|
+
|
|
146
|
+
**Use the exported constants** (do not hand-roll variants):
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import {
|
|
150
|
+
PROTOTYPE_PAGE_CLASS,
|
|
151
|
+
PROTOTYPE_SCROLL_CONTAINER_CLASS,
|
|
152
|
+
PROTOTYPE_VIEW_SHELL_CLASS,
|
|
153
|
+
} from "prototype";
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Required page shell** (match `home-page` / `proto-partner-page`):
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
<PrototypeComponent
|
|
160
|
+
id="scroll-container"
|
|
161
|
+
className={cn(PROTOTYPE_SCROLL_CONTAINER_CLASS, "…")}
|
|
162
|
+
>
|
|
163
|
+
<PrototypeComponent id="page" className={cn(PROTOTYPE_PAGE_CLASS, "…")}>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Every step/view root** (including `AnimatePresence` / motion wrappers):
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
className={cn(PROTOTYPE_VIEW_SHELL_CLASS, "…")}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Put `overflow-y-auto` on the **view** that scrolls, not on `scroll-container`, unless the entire page is one scroll surface.
|
|
173
|
+
|
|
174
|
+
**Centered embed/card layouts** (public booking pages): outer view = `w-full flex-1`; inner content = `mx-auto w-full max-w-*`. Use top padding (`py-10`) for embed pages — avoid `justify-center` on a `flex-1` wrapper when content can exceed viewport height (mobile stack); it vertically centers overflow and hides content off-screen.
|
|
175
|
+
|
|
176
|
+
**Motion:** Prefer opacity-only cross-fades on page-level wrappers. Avoid horizontal `translateX` on wrappers that lack explicit `w-full` — `#prototype-viewport` uses `transform: translateZ(0)`, and transform + shrink-wrapped children produces the common top-left / clipped-right offset bug.
|
|
177
|
+
|
|
178
|
+
**Post-build layout check (mandatory before finishing):**
|
|
179
|
+
|
|
180
|
+
1. Open `/` — sidebar flush to content, no black gutter, no white strip at bottom of window.
|
|
181
|
+
2. Open the prototype at desktop width — main surface fills the preview frame, not hugging a corner.
|
|
182
|
+
3. Toggle mobile preview — tall stacked pages still scroll; nothing hidden off-screen from `justify-center`.
|
|
183
|
+
|
|
94
184
|
### Examples
|
|
95
185
|
|
|
96
186
|
```tsx
|
|
@@ -106,14 +196,27 @@ export function SettingsSection(props: SettingsSectionProps) {
|
|
|
106
196
|
```
|
|
107
197
|
|
|
108
198
|
```tsx
|
|
109
|
-
import {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
199
|
+
import { ControlsPanelSelect } from "@prototype/components/platform-ui/controls-panel-select";
|
|
200
|
+
import { usePrototypeReviewOptional } from "@prototype/lib/prototypes/prototype-review-context";
|
|
201
|
+
import { useLayoutEffect } from "react";
|
|
202
|
+
|
|
203
|
+
export function PrototypePreviewStateSelect({ stateId, onStateChange, options }) {
|
|
204
|
+
const setTweaksContent = usePrototypeReviewOptional()?.setTweaksContent;
|
|
205
|
+
|
|
206
|
+
useLayoutEffect(() => {
|
|
207
|
+
if (!setTweaksContent) return;
|
|
208
|
+
setTweaksContent(
|
|
209
|
+
<ControlsPanelSelect
|
|
210
|
+
appearance="menuList"
|
|
211
|
+
value={stateId}
|
|
212
|
+
onValueChange={onStateChange}
|
|
213
|
+
options={options}
|
|
214
|
+
/>,
|
|
215
|
+
);
|
|
216
|
+
return () => setTweaksContent(null);
|
|
217
|
+
}, [onStateChange, options, setTweaksContent, stateId]);
|
|
218
|
+
|
|
219
|
+
return null;
|
|
117
220
|
}
|
|
118
221
|
```
|
|
119
222
|
|
|
@@ -124,15 +227,115 @@ export function PrototypePreviewStateSelect(...) {
|
|
|
124
227
|
3. Add `component-ids.ts` — exhaustive static id list + `dynamicPrefixes`.
|
|
125
228
|
4. Register in `prototype.config.ts` with `componentRegistry`.
|
|
126
229
|
5. Wrap every UI component in `PrototypeComponent` / `PrototypeControl`.
|
|
127
|
-
6.
|
|
128
|
-
7.
|
|
129
|
-
|
|
230
|
+
6. Define **`liveState`** and wire `usePrototypeComments(liveState, onRestore)` — see **Live state & preview states**.
|
|
231
|
+
7. **Enumerate preview modes** from the brief and source (tabs, empty/loading/error, filters, modals, view toggles, wizard steps) and add the full preview-state stack in the **same change** as the UI:
|
|
232
|
+
- `*-preview-states.ts` (`definePreviewStateRegistry`, `createLiveStateForPreview`)
|
|
233
|
+
- `*-preview-state-select.tsx` (`ControlsPanelSelect` with `appearance="menuList"` via `setTweaksContent` — not a floating pill on the page)
|
|
234
|
+
- `*-state-canvas-config.tsx`
|
|
235
|
+
- `state-map-page.tsx` + `stateMapComponent` in `prototype.config.ts`
|
|
236
|
+
- `review.setStateCanvasConfig(...)` in `index.tsx`
|
|
237
|
+
8. Run `pnpm verify:prototype-ids` and `pnpm verify:prototype-preview-states`.
|
|
238
|
+
9. Capture screenshot; share via `pnpm share-prototype <slug>`.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Live state & preview states (mandatory)
|
|
243
|
+
|
|
244
|
+
Prototype state is a first-class tool feature — comments, share links, the review **State** map, and PR split cards all read from the same snapshot. **Do not invent parallel state systems.** Use the plugin's live-state + preview-state registry pattern on every prototype that has reviewable UI modes.
|
|
245
|
+
|
|
246
|
+
### Do this proactively
|
|
247
|
+
|
|
248
|
+
When **building or modifying** a prototype, treat preview states as part of the feature — not a follow-up:
|
|
249
|
+
|
|
250
|
+
1. **Read existing state first** — `index.tsx`, any `*-preview-states.ts`, design-exploration configs, and `pr-split-config.ts` before changing UI.
|
|
251
|
+
2. **Define a typed `TLiveState`** — one JSON-serializable object holding every field a reviewer needs to reproduce the view (step/screen, modal open, form values, toggles, selected tab, etc.).
|
|
252
|
+
3. **One `liveState` on the page** — `useState<TLiveState>(createDefaultLiveState)` (or hydrate from `readPersistedPrototypeLiveState`). Derive rendered UI from `liveState`; route interactions through setters that update it.
|
|
253
|
+
4. **Wire comments** — `usePrototypeComments(liveState, onRestore)` where `onRestore` calls `setLiveState(restored)`. This powers annotation restore and `shareState` links. Do **not** roll custom `?step=` / `?view=` query params.
|
|
254
|
+
5. **Preview states are part of the feature, not a follow-up** — for feature pages, add or update a single `definePreviewStateRegistry` in `*-preview-states.ts` (see below). Every distinct tab, empty/loading/error branch, filter mode, modal, view toggle, or wizard step reviewers should jump to gets a registry entry **in the same change** as the UI. Only skip the registry for truly static single-view pages with no reviewable modes.
|
|
255
|
+
6. **Wire the state picker into the review toolbar** — `*-preview-state-select.tsx` calls `review.setTweaksContent(...)` with `ControlsPanelSelect` using `appearance="menuList"` and `pickerOptions` from the registry. Do **not** render a floating `miniPill` select on the prototype surface (see `booking-flow-preview-state-select.tsx`).
|
|
256
|
+
7. **Wire the state map** — `*-state-canvas-config.tsx` builds `PrototypeStateCanvasConfig` from the same registry. In `index.tsx`, call `review.setStateCanvasConfig(...)` in an effect; depend on stable review callbacks, not the whole `review` object. Add a `stateMapComponent` on the prototype in `prototype.config.ts` (see `booking-flow/state-map-page.tsx`) so `/prototypes/<slug>/states` renders the map instead of the default empty page.
|
|
257
|
+
8. **Verify** — run `pnpm verify:prototype-preview-states` whenever a `*-preview-states.ts` file exists.
|
|
258
|
+
|
|
259
|
+
Optional: `usePersistPrototypeLiveState(slug, liveState)` keeps reviewer position across reloads.
|
|
260
|
+
|
|
261
|
+
### Architecture
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
| Piece | Location | Purpose |
|
|
265
|
+
| ----- | -------- | ------- |
|
|
266
|
+
| `TLiveState` + `liveState` | page `index.tsx` | Single snapshot of all preview-relevant UI |
|
|
267
|
+
| `usePrototypeComments` | page | Freeze/restore `liveState` for comments & share links |
|
|
268
|
+
| `*-preview-states.ts` | `_components/` | `definePreviewStateRegistry` — picker, map nodes, `edges`, sections |
|
|
269
|
+
| `*-preview-state-select.tsx` | `_components/` | On-page / sidebar step picker (`PrototypeControl`) |
|
|
270
|
+
| `*-state-canvas-config.tsx` | `_components/` | Wireframes + registry data for the review **State** panel |
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
Reference: `src/prototypes/booking-flow/` (`index.tsx`, `booking-flow-preview-states.ts`, `booking-flow-preview-state-select.tsx`, `booking-flow-state-canvas-config.tsx`).
|
|
274
|
+
|
|
275
|
+
### Page wiring (minimal)
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
const [liveState, setLiveState] = useState<TLiveState>(createDefaultLiveState);
|
|
279
|
+
|
|
280
|
+
const onRestore = useCallback((restored: TLiveState) => {
|
|
281
|
+
setLiveState(restored);
|
|
282
|
+
}, []);
|
|
283
|
+
|
|
284
|
+
usePrototypeComments(liveState, onRestore);
|
|
285
|
+
|
|
286
|
+
const setStateCanvasConfig = review?.setStateCanvasConfig;
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (!setStateCanvasConfig) return;
|
|
289
|
+
setStateCanvasConfig(
|
|
290
|
+
buildMyStateCanvasConfig(setStep),
|
|
291
|
+
);
|
|
292
|
+
return () => setStateCanvasConfig(null);
|
|
293
|
+
}, [setStateCanvasConfig, setStep]);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### When to add a preview-state registry entry
|
|
297
|
+
|
|
298
|
+
Add (or update) a registry state when you introduce:
|
|
299
|
+
|
|
300
|
+
- A new screen, step, wizard stage, or primary tab reviewers should open directly
|
|
301
|
+
- A modal, drawer, or overlay branch worth reviewing on its own
|
|
302
|
+
- Empty, loading, or error branches that are distinct preview modes
|
|
303
|
+
|
|
304
|
+
Skip registry entries for hover, focus, or other non-shareable micro-interactions.
|
|
305
|
+
|
|
306
|
+
### Simple prototypes without steps
|
|
307
|
+
|
|
308
|
+
If there are no navigable preview modes, `liveState` may be `{}` or hold only a few toggles — still call `usePrototypeComments`. Design explorations register separately via variant sets; do not fold exploration variants into ad-hoc `useState` without also exposing them through the exploration config.
|
|
309
|
+
|
|
310
|
+
Multi-route prototypes (e.g. `proto-partner-page`) may use Next routes for real page boundaries; in-prototype step/modal switching within a page still belongs in `liveState`.
|
|
311
|
+
|
|
312
|
+
### Anti-patterns
|
|
313
|
+
|
|
314
|
+
- Multiple independent `useState` hooks for step/modal/view **without** aggregating into `liveState`
|
|
315
|
+
- Custom URL query params for preview mode — share links use `usePrototypeComments` + encoded `shareState`
|
|
316
|
+
- Hand-rolled `<select>` / tabs for preview modes outside `ControlsPanelSelect` + the registry
|
|
317
|
+
- Floating `ControlsPanelSelect` with `appearance="miniPill"` on the prototype surface — states belong in the review toolbar **States** menu via `appearance="menuList"` and `setTweaksContent`
|
|
318
|
+
- Duplicating picker labels or state ids outside `definePreviewStateRegistry`
|
|
319
|
+
- Adding UI steps or modal branches **without** updating the registry, picker, and state map in the same change
|
|
320
|
+
- Separate state maps or pickers that are not imported from the single `*-preview-states.ts` file
|
|
321
|
+
- Passing `liveState` or the current preview mode into the state map config — the map is a **stateless** navigation surface, not a mirror of the active state
|
|
322
|
+
- Tracking `activeStateId` / selected styling / checkmarks on state map nodes
|
|
130
323
|
|
|
131
324
|
---
|
|
132
325
|
|
|
133
326
|
## Preview states & state map
|
|
134
327
|
|
|
135
|
-
Prototypes with preview modes use `definePreviewStateRegistry` in `*-preview-states.ts`. One registry drives the state picker, map nodes, `edges`, and sections.
|
|
328
|
+
Prototypes with preview modes use `definePreviewStateRegistry` in `*-preview-states.ts`. One registry drives the state picker, map nodes, `edges`, and sections. The picker, canvas config, and verification script must all import from this file — never maintain parallel state lists.
|
|
329
|
+
|
|
330
|
+
### State map behavior (mandatory)
|
|
331
|
+
|
|
332
|
+
The state map is a **stateless picker** — it must **not** reflect, highlight, or mark the current preview state.
|
|
333
|
+
|
|
334
|
+
- **No active-state association** — Do not pass the current preview mode, `liveState`, or any `activeStateId` into `PrototypeStateCanvasConfig` or `build…StateCanvasConfig`. Clicking a node navigates to that state; there is no checkmark, selected border, or `aria-current` on the map.
|
|
335
|
+
- **Follows tool theme** — The state map canvas (background, chrome, edges, section labels, node cards) uses `--tool-chrome-*` tokens and matches the gallery/review light/dark mode toggle.
|
|
336
|
+
- **`build…StateCanvasConfig(onStateSelect)`** — The builder takes only the select handler. Wire it once in `index.tsx` and `state-map-page.tsx`; do not re-build when `liveState` changes.
|
|
337
|
+
|
|
338
|
+
Reference: `src/prototypes/booking-flow/_components/booking-flow-state-canvas-config.tsx`, `state-map-page.tsx`.
|
|
136
339
|
|
|
137
340
|
### State map layout
|
|
138
341
|
|
|
@@ -142,7 +345,7 @@ When two states are connected by an `edges` entry, arrange `canvasLayout.rows` s
|
|
|
142
345
|
|
|
143
346
|
Prefer adjacent columns on the same row, or use `{ id, column }` / `startColumn` to offset nodes so vertical edges do not cut through unrelated cards in the same column.
|
|
144
347
|
|
|
145
|
-
Reference: `src/prototypes/
|
|
348
|
+
Reference: `src/prototypes/booking-flow/_components/booking-flow-preview-states.ts`.
|
|
146
349
|
|
|
147
350
|
---
|
|
148
351
|
|
|
@@ -153,11 +356,33 @@ Use when comparing **2+ UI directions** for the same user problem.
|
|
|
153
356
|
|
|
154
357
|
| Layer | Location | Owns |
|
|
155
358
|
| ------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
156
|
-
| **Tool** | `prototype` (`PrototypeVariantExplorer`, etc.) | Modal chrome, variant tabs,
|
|
157
|
-
| **Prototype** | `src/prototypes/<slug>/_components/` | Variant renderers,
|
|
359
|
+
| **Tool** | `prototype` (`PrototypeVariantExplorer`, etc.) | Modal chrome, variant tabs, Mobbin gallery, localStorage for brief fields |
|
|
360
|
+
| **Prototype** | `src/prototypes/<slug>/_components/` | Variant renderers, context JSON, Mobbin references, `component-ids.ts` |
|
|
361
|
+
|
|
158
362
|
|
|
363
|
+
Reference: `src/prototypes/proto-partner-page/_components/invite-copy-design-exploration-config.tsx` + `invite-copy-variant-toggle.tsx`.
|
|
159
364
|
|
|
160
|
-
|
|
365
|
+
### Renderers
|
|
366
|
+
|
|
367
|
+
Build one preview per option with `buildDesignExplorationRenderers`. In host prototype files (client components), import from `@prototype/...` — not the `proto-plugin` barrel, which includes server-only modules:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
import { buildDesignExplorationRenderers } from "@prototype/lib/prototypes/build-design-exploration-renderers";
|
|
371
|
+
import { DesignExplorationVariantPreviewShell } from "@prototype/components/prototypes/design-exploration-variant-preview-shell";
|
|
372
|
+
|
|
373
|
+
const renderers = buildDesignExplorationRenderers(VARIANT_OPTIONS, (variant) => (
|
|
374
|
+
<MyBlock variant={variant} />
|
|
375
|
+
), BASELINE);
|
|
376
|
+
|
|
377
|
+
// Overlay-style variants (absolute positioning on the live page):
|
|
378
|
+
const renderers = buildDesignExplorationRenderers(VARIANT_OPTIONS, (variant) => (
|
|
379
|
+
<DesignExplorationVariantPreviewShell layout={isOverlay(variant) ? "overlay" : "inline"}>
|
|
380
|
+
<MyBlock variant={variant} />
|
|
381
|
+
</DesignExplorationVariantPreviewShell>
|
|
382
|
+
), BASELINE);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Do not hand-roll renderer maps that return `null` for some variants — the helper maps every option and warns in dev when a renderer returns empty content.
|
|
161
386
|
|
|
162
387
|
### Config shape
|
|
163
388
|
|
|
@@ -167,22 +392,42 @@ Reference: `example-feature/_components/no-data-design-exploration-config.tsx` +
|
|
|
167
392
|
variantTabsIdPrefix: string;
|
|
168
393
|
storageKeyPrefix: string;
|
|
169
394
|
variant, onVariantChange,
|
|
170
|
-
options: { value, label, hint? }[];
|
|
171
|
-
rationale: Record<TVariant, { good, bad }>;
|
|
395
|
+
options: { value, label, hint? }[]; // exploration directions — newest first; exclude baseline when possible
|
|
172
396
|
renderers: Record<TVariant, ComponentType>;
|
|
173
397
|
brief: { titleDefault, descriptionDefault };
|
|
398
|
+
baseline: { value, label?, hint? }; // pre-exploration UI — always pinned at the bottom of the variant list
|
|
399
|
+
defaultVariant?: TVariant; // codebase-stored default (e.g. DEFAULT_*_VARIANT); marked "Default" in the brief
|
|
174
400
|
context?: { label, data: unknown, render?, defaultExpanded?, panelId? };
|
|
175
401
|
mobbin?: { references, title?, description?, imagePathForReference? };
|
|
176
402
|
variantsSection?: { title?, description? };
|
|
177
403
|
}
|
|
178
404
|
```
|
|
179
405
|
|
|
406
|
+
Every exploration **must** define `baseline` — the UI as it existed before the exploration was added. The design brief shows it last with an **Original** badge. Use `getDesignExplorationDisplayOptions(options, baseline)` for tabs and keyboard cycling; use `getDesignExplorationVariantOptions(options, baseline)` when listing directions in prompts.
|
|
407
|
+
|
|
408
|
+
Build renderers with `buildDesignExplorationRenderers(options, renderVariant, baseline)` so the baseline preview is included.
|
|
409
|
+
|
|
410
|
+
### Exploration icons
|
|
411
|
+
|
|
412
|
+
The review sidebar explorations menu shows a Lucide icon per variant set via `VariantSetLucideIcon` (`packages/prototype/src/lib/prototypes/variant-set-lucide-icon.tsx`). Rules match on `id` + `label` (first fit wins); unmatched sets fall back to `LayoutGrid`.
|
|
413
|
+
|
|
414
|
+
When adding a new exploration, **assign it a distinct icon**:
|
|
415
|
+
|
|
416
|
+
1. Pick a Lucide icon that reflects the exploration topic (not `LayoutGrid` unless nothing else fits).
|
|
417
|
+
2. Add a `{ test: /…/, icon: … }` entry to `VARIANT_SET_ICON_RULES`, placed **above** broad rules such as `/mobile/i`, so this exploration wins over generic matchers.
|
|
418
|
+
3. Do not reuse icons already assigned to other explorations on the same prototype.
|
|
419
|
+
4. Import the chosen icon from `lucide-react`.
|
|
420
|
+
|
|
421
|
+
The default variant lives in the codebase config (`defaultVariant`, typically `DEFAULT_*_VARIANT`) — not KV. In the brief, each non-default option shows a **Make default** button that copies a prompt (`buildSetDefaultVariantPrompt`) asking an agent to update that constant; the current default is marked **Default**.
|
|
422
|
+
|
|
180
423
|
### Iterating on variants
|
|
181
424
|
|
|
182
425
|
When asked to **modify an existing option**, add a **new variant** (do not replace in place), register ids, and update `DEFAULT_*_VARIANT`.
|
|
183
426
|
|
|
184
427
|
**Variant list order:** prepend each new entry to the top of `*_VARIANT_OPTIONS` (first item in the array). That order drives the design-brief sidebar/modal scroll view and variant tabs — newest explorations appear at the top.
|
|
185
428
|
|
|
429
|
+
In review mode, each exploration sidebar includes a brief field and **Copy prompt** button below the header. It copies `buildMoreDesignExplorationVariantsCopyText` with the typed brief, existing variants, and config path — use that to request more layout directions from an agent.
|
|
430
|
+
|
|
186
431
|
|
|
187
432
|
| Prompt intent | Action |
|
|
188
433
|
| --------------------- | ----------------------------------------------- |
|
|
@@ -203,7 +448,7 @@ Use when a prototype ships as a **sequence of small source PRs** rather than one
|
|
|
203
448
|
| **Prototype** | `src/prototypes/<slug>/_components/` | `PR_SPLIT_ENTRIES` data, `PR_SPLIT_CONFIG`, wireframe renderers, live-state URL wiring |
|
|
204
449
|
|
|
205
450
|
|
|
206
|
-
Reference: `
|
|
451
|
+
Reference: `booking-flow/_components/pr-split-config.ts` + `pr-split-wireframes.tsx` (when present) or any prototype with a spec panel.
|
|
207
452
|
|
|
208
453
|
### Host setup
|
|
209
454
|
|
|
@@ -279,15 +524,16 @@ export const PR_SPLIT_ENTRIES: MyPrSplitEntry[] = [
|
|
|
279
524
|
|
|
280
525
|
### Wireframes (`pr-split-wireframes.tsx`)
|
|
281
526
|
|
|
282
|
-
Prototype-specific schematic
|
|
527
|
+
Prototype-specific **full-page** schematic previews for each `wireframeId`. Each card must show the complete page shell (sidebar, header/toolbar, main content) — not a cropped excerpt or zoomed-in slice of the PR target. Pattern:
|
|
283
528
|
|
|
284
|
-
1. Define a `PageShell` (or layout scaffold) matching the prototype page structure.
|
|
285
|
-
2. Use low-fidelity `Wire` blocks — neutral grays for chrome, **highlighted** regions for the PR target.
|
|
529
|
+
1. Define a `PageShell` (or layout scaffold) matching the prototype's real page structure — every zone reviewers see in the live preview.
|
|
530
|
+
2. Use low-fidelity `Wire` blocks — neutral grays for chrome, **highlighted** regions for the PR target only.
|
|
286
531
|
3. Import highlight tokens from the tool: `PR_TARGET_HIGHLIGHT_BORDER`, `PR_TARGET_HIGHLIGHT_FILL`.
|
|
287
|
-
4.
|
|
288
|
-
5.
|
|
532
|
+
4. Fill the card preview: wrap the wireframe root in `PR_SPLIT_WIREFRAME_FRAME_CLASS` from `prototype` (`aspect-video w-full overflow-hidden`) so the schematic reads as a full screen, not a short strip.
|
|
533
|
+
5. Register one renderer per `wireframeId` in a `Record<WireframeId, () => ReactNode>`.
|
|
534
|
+
6. Export `PrSplitWireframe` wrapped in `PrototypeComponent` (register id in `component-ids.ts`).
|
|
289
535
|
|
|
290
|
-
Highlighted areas must match what `targetId` points to on the real prototype.
|
|
536
|
+
Highlighted areas must match what `targetId` points to on the real prototype; surrounding chrome provides context for where the change sits on the page.
|
|
291
537
|
|
|
292
538
|
### Page wiring (`index.tsx`)
|
|
293
539
|
|
|
@@ -326,6 +572,34 @@ setSpecContent(
|
|
|
326
572
|
|
|
327
573
|
---
|
|
328
574
|
|
|
575
|
+
## Consuming the review context in effects
|
|
576
|
+
|
|
577
|
+
`usePrototypeReview()` / `usePrototypeReviewOptional()` return a **new context object on most renders** — the provider's `value` memo rebuilds whenever review state changes (e.g. `variantSets`, panel open state). Several of its methods (`registerVariantSet`, `unregisterVariantSet`, etc.) also call `setState`.
|
|
578
|
+
|
|
579
|
+
**Never put the whole `review` object in an effect dependency array.** Doing so creates an infinite loop ("Maximum update depth exceeded"): the effect calls a method → state changes → `value` gets a new identity → the effect re-runs → its cleanup undoes the work → the body redoes it → repeat.
|
|
580
|
+
|
|
581
|
+
Depend on the **specific stable callbacks** instead. The context methods are wrapped in `useCallback(..., [])`, so their identities are stable across renders.
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
// ❌ loops forever — `review` identity changes whenever it registers/unregisters
|
|
585
|
+
useLayoutEffect(() => {
|
|
586
|
+
if (!review) return;
|
|
587
|
+
review.registerVariantSet(set);
|
|
588
|
+
return () => review.unregisterVariantSet(set.id);
|
|
589
|
+
}, [review]);
|
|
590
|
+
|
|
591
|
+
// ✅ stable deps — runs once, cleans up once
|
|
592
|
+
const registerVariantSet = review?.registerVariantSet;
|
|
593
|
+
const unregisterVariantSet = review?.unregisterVariantSet;
|
|
594
|
+
useLayoutEffect(() => {
|
|
595
|
+
if (!registerVariantSet || !unregisterVariantSet) return;
|
|
596
|
+
registerVariantSet(set);
|
|
597
|
+
return () => unregisterVariantSet(set.id);
|
|
598
|
+
}, [registerVariantSet, unregisterVariantSet]);
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
329
603
|
## Comment system
|
|
330
604
|
|
|
331
605
|
- Storage: Upstash Redis key `prototype-comments:<slug>`
|
|
@@ -347,4 +621,9 @@ Before finishing, confirm side-by-side with source or a screenshot:
|
|
|
347
621
|
- Importing from `source/` at runtime
|
|
348
622
|
- Reimplementing synced `@/components/ui` primitives
|
|
349
623
|
- Approximating layout dimensions or colors
|
|
624
|
+
- **Inventing custom prototype state** instead of `liveState` + `definePreviewStateRegistry` (see **Live state & preview states**)
|
|
625
|
+
- **Adding screens/modals without registering preview states** in the same change
|
|
626
|
+
- Depending on the whole `review` context object in an effect's deps (see "Consuming the review context in effects") — use the specific stable callbacks instead
|
|
627
|
+
- **`scroll-container` with `overflow-y-auto` and no `h-full` fill chain** — causes top-left offset and clipped content in the preview frame (see **Prototype viewport fill**)
|
|
628
|
+
- **Page-level motion with `translateX` on wrappers missing `w-full flex-1`** — same offset bug; use opacity-only or fill classes first
|
|
350
629
|
|
package/README.md
CHANGED
|
@@ -64,14 +64,32 @@ import { withPrototype } from "./packages/prototype/src/config/with-prototype";
|
|
|
64
64
|
export default withPrototype({ nextConfig: { /* … */ } });
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
5. **Tailwind** — scan prototype package components from the host `globals.css`:
|
|
67
|
+
5. **Tailwind** — scan prototype package components from the host `globals.css`. Register semantic `--color-*` tokens as self-referential vars so utilities like `text-muted-foreground` resolve from `[data-prototype-root]` at runtime — **do not** map `--color-foreground` / `--color-background` to product `:root` tokens (that breaks gallery contrast):
|
|
68
68
|
|
|
69
69
|
```css
|
|
70
70
|
@import "tailwindcss";
|
|
71
71
|
@source "../../packages/prototype/src/**/*.{js,ts,jsx,tsx}";
|
|
72
|
+
|
|
73
|
+
@theme inline {
|
|
74
|
+
--color-foreground: var(--color-foreground);
|
|
75
|
+
--color-muted-foreground: var(--color-muted-foreground);
|
|
76
|
+
/* …other semantic colors used by proto-plugin */
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Product tokens use separate names (e.g. `--product-background`) scoped to `[data-prototype-screenshot]`.
|
|
81
|
+
|
|
82
|
+
6. **Root layout + tool themes** — wrap routes with `createPrototypeSiteLayout()` and import package chrome CSS (dark/light themes live here, not in host `globals.css`):
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { createPrototypeSiteLayout } from "proto-plugin";
|
|
86
|
+
import "proto-plugin/styles/globals.css";
|
|
87
|
+
import "./globals.css";
|
|
88
|
+
|
|
89
|
+
const PrototypeSiteLayout = createPrototypeSiteLayout();
|
|
72
90
|
```
|
|
73
91
|
|
|
74
|
-
|
|
92
|
+
7. **tsconfig paths** (optional, for tool internal `@prototype/*` alias when developing the package):
|
|
75
93
|
|
|
76
94
|
```json
|
|
77
95
|
{
|
|
@@ -84,6 +102,18 @@ export default withPrototype({ nextConfig: { /* … */ } });
|
|
|
84
102
|
}
|
|
85
103
|
```
|
|
86
104
|
|
|
105
|
+
8. **Dev server port** — run the host on port **1985** so it stays separate from a typical source app on 3000. In the host `package.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"scripts": {
|
|
110
|
+
"dev": "next dev -p 1985"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Open [http://localhost:1985](http://localhost:1985) when developing.
|
|
116
|
+
|
|
87
117
|
## Public API
|
|
88
118
|
|
|
89
119
|
| Export | Use |
|
|
@@ -116,6 +146,8 @@ The `prototype` package uses the **host design system** — same tokens and Tail
|
|
|
116
146
|
|
|
117
147
|
Host `globals.css` must `@source` prototype package TSX for Tailwind utilities.
|
|
118
148
|
|
|
149
|
+
**Do not** implement gallery/review dark or light mode in host `globals.css`. Import `proto-plugin/styles/globals.css` in `layout.tsx` — the package defines both themes on `[data-prototype-root]` (default dark; `data-prototype-comment-theme="light"` for light). Host `globals.css` is for **product** tokens synced from the source app (prototype preview content only).
|
|
150
|
+
|
|
119
151
|
## Publish
|
|
120
152
|
|
|
121
153
|
From `packages/prototype/`:
|
|
@@ -190,21 +222,21 @@ pnpm download-mobbin-references -- --ids=uuid1,uuid2
|
|
|
190
222
|
|
|
191
223
|
## Starter prompt (for AI agents)
|
|
192
224
|
|
|
193
|
-
Copy the block below into a new agent chat to scaffold a host app from scratch. The agent should ask you for a few values up front
|
|
225
|
+
Copy the block below into a new agent chat to scaffold a host app from scratch. The agent should ask you for a few values up front — **one question at a time** — then handle the rest (including inferring what to sync from source).
|
|
194
226
|
|
|
195
227
|
````markdown
|
|
196
228
|
Set up a new Next.js host app for `proto-plugin` — a framework for shareable UI prototypes.
|
|
197
229
|
|
|
198
230
|
## Before you start — ask me for these
|
|
199
231
|
|
|
200
|
-
Do not scaffold or run commands until I answer. Ask
|
|
232
|
+
Do not scaffold or run commands until I answer all four questions. Ask **one question at a time** — wait for my reply before asking the next:
|
|
201
233
|
|
|
202
|
-
1. **Host app name** —
|
|
203
|
-
2. **Where to create it** —
|
|
204
|
-
3. **Source app path** —
|
|
205
|
-
4. **Optional env** —
|
|
234
|
+
1. **Host app name** — What should the new Next.js project directory be called? (e.g. `my-product-prototypes`)
|
|
235
|
+
2. **Where to create it** — Parent directory as an absolute path (e.g. `/Users/maayanalbert/Projects`), or confirm you want it in the current workspace.
|
|
236
|
+
3. **Source app path** — Absolute path to the real product repo whose UI we'll prototype (used for the `source/` symlink and sync).
|
|
237
|
+
4. **Optional env** — Should I configure comment/screenshot persistence now (`KV_REST_API_URL`, `KV_REST_API_TOKEN`, `BLOB_READ_WRITE_TOKEN`), or skip that for later?
|
|
206
238
|
|
|
207
|
-
|
|
239
|
+
After I answer all four, do all setup yourself.
|
|
208
240
|
|
|
209
241
|
## 1. Scaffold the app
|
|
210
242
|
|
|
@@ -229,19 +261,29 @@ Follow the package README (`node_modules/proto-plugin/README.md`) and implement
|
|
|
229
261
|
1. **`prototype.config.ts`** at the repo root — start with an empty `prototypes: []` array using `definePrototypeConfig` from `proto-plugin`.
|
|
230
262
|
2. **Gallery home** — `src/app/page.tsx` via `createPrototypeGalleryPage(prototypeConfig)`.
|
|
231
263
|
3. **Prototype route** — `src/app/prototypes/[slug]/page.tsx` via `createPrototypePage(prototypeConfig)` (export `default` + `generateStaticParams`).
|
|
232
|
-
4. **Root layout** — wrap children with `createPrototypeSiteLayout()` from `proto-plugin` in `src/app/layout.tsx
|
|
264
|
+
4. **Root layout** — wrap children with `createPrototypeSiteLayout()` from `proto-plugin` in `src/app/layout.tsx`, and import the tool chrome theme:
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { createPrototypeSiteLayout } from "proto-plugin";
|
|
268
|
+
import "proto-plugin/styles/globals.css";
|
|
269
|
+
import "./globals.css";
|
|
270
|
+
```
|
|
233
271
|
5. **API catch-all** — `src/app/api/[...path]/route.ts` using `createPrototypeApiRoute(prototypeConfig)` from `proto-plugin/server`; export `GET`, `POST`, and `PUT` handlers that call `dispatch`.
|
|
234
272
|
6. **`next.config.ts`** — wrap with `withPrototype` from `proto-plugin/with-prototype` and ensure `proto-plugin` is transpiled.
|
|
235
|
-
7. **`src/app/globals.css`** —
|
|
273
|
+
7. **`src/app/globals.css`** — Tailwind v4 + product tokens synced from source. **Do not** put tool chrome dark/light mode here (no `prefers-color-scheme`, no hardcoded gallery colors). Scan package components:
|
|
236
274
|
|
|
237
275
|
```css
|
|
276
|
+
@import "tailwindcss";
|
|
238
277
|
@source "../../node_modules/proto-plugin/src/**/*.{js,ts,jsx,tsx}";
|
|
239
278
|
```
|
|
240
279
|
|
|
241
|
-
|
|
280
|
+
Tool themes live in `proto-plugin/styles/globals.css` (import in layout). The package toggles dark/light on `[data-prototype-root]` via `data-prototype-comment-theme`.
|
|
281
|
+
|
|
282
|
+
8. **`package.json` scripts** — set dev to port 1985 and forward CLI commands to the installed package:
|
|
242
283
|
|
|
243
284
|
```json
|
|
244
285
|
{
|
|
286
|
+
"dev": "next dev -p 1985",
|
|
245
287
|
"link-source": "pnpm --dir node_modules/proto-plugin link-source",
|
|
246
288
|
"sync-from-source": "pnpm --dir node_modules/proto-plugin sync-from-source",
|
|
247
289
|
"link-source-db": "pnpm --dir node_modules/proto-plugin link-source-db",
|
|
@@ -252,7 +294,7 @@ Follow the package README (`node_modules/proto-plugin/README.md`) and implement
|
|
|
252
294
|
|
|
253
295
|
9. **`.env.example` and `.env.local`** — document and set `SOURCE_PATH` to the source app path I provided. Add comment/screenshot storage vars only if I asked for them in step 5 — the app should run without them, but comments won't persist.
|
|
254
296
|
|
|
255
|
-
Run `pnpm dev` and confirm
|
|
297
|
+
Run `pnpm dev` and confirm [http://localhost:1985](http://localhost:1985) shows the empty gallery and the app builds cleanly before continuing.
|
|
256
298
|
|
|
257
299
|
## 3. Link the source app
|
|
258
300
|
|
package/package.json
CHANGED
|
@@ -33,7 +33,7 @@ SYNC_GLOBS=(
|
|
|
33
33
|
# the source version, which the blanket copy overwrites and you must re-apply.
|
|
34
34
|
# Leave empty if the host does not diverge from source.
|
|
35
35
|
SYNC_LOCAL_EXTENSIONS=(
|
|
36
|
-
# "
|
|
36
|
+
# "src/app/globals.css (keep @source line; tool theme lives in layout via proto-plugin/styles/globals.css)"
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
# --- pnpm link-source-db ---------------------------------------------------
|