@dfosco/storyboard-core 3.1.2 → 3.3.0
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/storyboard-ui.css +1 -0
- package/dist/storyboard-ui.js +26298 -0
- package/dist/storyboard-ui.js.map +1 -0
- package/dist/tailwind.css +1 -1
- package/package.json +24 -19
- package/scaffold/manifest.json +35 -0
- package/scaffold/scripts/link.sh +26 -0
- package/scaffold/scripts/unlink.sh +10 -0
- package/scaffold/skills/create/SKILL.md +501 -0
- package/scaffold/skills/storyboard/SKILL.md +360 -0
- package/scaffold/skills/update-storyboard/SKILL.md +16 -0
- package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
- package/scaffold/skills/vitest/GENERATION.md +5 -0
- package/scaffold/skills/vitest/SKILL.md +52 -0
- package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
- package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
- package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
- package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
- package/scaffold/skills/vitest/references/core-cli.md +166 -0
- package/scaffold/skills/vitest/references/core-config.md +174 -0
- package/scaffold/skills/vitest/references/core-describe.md +193 -0
- package/scaffold/skills/vitest/references/core-expect.md +219 -0
- package/scaffold/skills/vitest/references/core-hooks.md +244 -0
- package/scaffold/skills/vitest/references/core-test-api.md +233 -0
- package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
- package/scaffold/skills/vitest/references/features-context.md +238 -0
- package/scaffold/skills/vitest/references/features-coverage.md +207 -0
- package/scaffold/skills/vitest/references/features-filtering.md +211 -0
- package/scaffold/skills/vitest/references/features-mocking.md +265 -0
- package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
- package/scaffold/skills/worktree/SKILL.md +51 -0
- package/scaffold/storyboard.config.json +26 -0
- package/scaffold/svelte.config.js +1 -0
- package/scaffold/toolbar.config.json +4 -0
- package/src/ActionMenuButton.svelte +1 -1
- package/src/CanvasCreateMenu.svelte +1 -1
- package/src/CoreUIBar.svelte +20 -9
- package/src/CreateMenuButton.svelte +1 -1
- package/src/InspectorPanel.svelte +144 -49
- package/src/SidePanel.svelte +10 -10
- package/src/commandActions.js +1 -1
- package/src/comments/index.js +0 -3
- package/src/devtools.js +4 -1
- package/src/index.js +5 -2
- package/src/inspector/highlighter.js +3 -4
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
- package/src/mountStoryboardCore.js +223 -0
- package/src/scaffold.js +100 -0
- package/src/stores/themeStore.ts +29 -8
- package/src/styles/tailwind.css +16 -0
- package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
- package/src/ui-entry.js +30 -0
- package/src/vite/server-plugin.js +8 -24
- package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
- package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
- package/src/workshop/features/createFlow/index.js +0 -1
- package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
- package/src/workshop/features/createPrototype/index.js +0 -1
- /package/{core-ui.config.json → toolbar.config.json} +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: storyboard
|
|
3
|
+
description: Storyboard data structuring and management system. Use when creating flow data, setting up storyboard data, creating data objects, or structuring data for a prototype page.
|
|
4
|
+
metadata:
|
|
5
|
+
author: Daniel Fosco
|
|
6
|
+
version: "2026.3.09"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Storyboard
|
|
10
|
+
|
|
11
|
+
> Triggered by: when building a new page or route in storyboard, "create flow data", "set up storyboard data", "create data objects", "create new page", "create new route", when structuring data for a prototype page
|
|
12
|
+
|
|
13
|
+
## What This Does
|
|
14
|
+
|
|
15
|
+
Guides the creation of data objects, flow files, and record collections for pages being built. Determines what data should be externalized into the Storyboard data system vs. hardcoded in the component.
|
|
16
|
+
|
|
17
|
+
## Data File Types
|
|
18
|
+
|
|
19
|
+
Storyboard uses **suffix-based naming** for data files. Files can live anywhere in the repo — a Vite plugin discovers them automatically at dev/build time.
|
|
20
|
+
|
|
21
|
+
| Suffix | Purpose | Example |
|
|
22
|
+
|--------|---------|---------|
|
|
23
|
+
| `.flow.json` | Page data context | `default.flow.json` |
|
|
24
|
+
| `.object.json` | Reusable data fragment | `jane-doe.object.json` |
|
|
25
|
+
| `.record.json` | Parameterized collection (array with `id` per entry) | `posts.record.json` |
|
|
26
|
+
|
|
27
|
+
**Rules:**
|
|
28
|
+
- Every name+suffix must be unique across the entire repo (build fails on duplicates)
|
|
29
|
+
- Files can be organized into any subdirectory structure
|
|
30
|
+
- JSONC (comments) is supported — use `.jsonc` extension if preferred
|
|
31
|
+
- `$ref` and `$global` use **name-based** references (not paths): `{ "$ref": "jane-doe" }` finds `jane-doe.object.json` anywhere
|
|
32
|
+
|
|
33
|
+
## When This Applies
|
|
34
|
+
|
|
35
|
+
- After the primer-builder skill has identified the page structure and components (Steps 1–3)
|
|
36
|
+
- Replaces the primer-builder's Step 4 (Plan Data) with a more structured approach
|
|
37
|
+
- When refactoring an existing page to use flow data
|
|
38
|
+
- When creating dynamic route pages with records
|
|
39
|
+
|
|
40
|
+
## Navigation Anti-Pattern
|
|
41
|
+
|
|
42
|
+
**DO NOT use React Router's `<Link>` component for internal navigation.** Use plain `<a href="...">` tags instead.
|
|
43
|
+
|
|
44
|
+
The storyboard hash preserver intercepts `<a>` clicks to preserve URL hash params and handle client-side routing. React Router `<Link>` bypasses this by calling `navigate()` directly, which creates a duplicate browser history entry (one from `<Link>`, one from the hash preserver's click handler). This causes a "double back" bug where the user must press back twice to actually navigate back.
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
// ❌ BAD — creates double history entries
|
|
48
|
+
<Link to="/Overview">Overview</Link>
|
|
49
|
+
|
|
50
|
+
// ✅ GOOD — intercepted by hash preserver for client-side navigation
|
|
51
|
+
<a href="/Overview">Overview</a>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## The Core Rule: What Goes in Data vs. What's Hardcoded
|
|
55
|
+
|
|
56
|
+
### Externalize as data objects
|
|
57
|
+
|
|
58
|
+
**Content and business model data** — anything that represents "what" the page displays:
|
|
59
|
+
|
|
60
|
+
- User profiles (name, avatar, bio, role)
|
|
61
|
+
- Repository / project metadata (name, description, language, stars, forks)
|
|
62
|
+
- Issue / PR items (title, state, author, labels, timestamps)
|
|
63
|
+
- Organization info (name, avatar, member count)
|
|
64
|
+
- Lists of entities (repos, issues, teams, packages, people)
|
|
65
|
+
|
|
66
|
+
**Navigation** — always externalize because it has repeated elements with many labels that benefit from easy bulk adjustment:
|
|
67
|
+
|
|
68
|
+
- Top navigation tabs (label, icon, counter, url, current state)
|
|
69
|
+
- Sidebar filter lists
|
|
70
|
+
- Settings section navigation
|
|
71
|
+
- Breadcrumb paths
|
|
72
|
+
|
|
73
|
+
**Important**: This will more usually be slotted in existing Template components from the repository, not generated code.
|
|
74
|
+
|
|
75
|
+
### Hardcode in the component
|
|
76
|
+
|
|
77
|
+
**UI chrome and microcopy** — anything that describes "how" the page works:
|
|
78
|
+
|
|
79
|
+
- Button labels: `"New repository"`, `"Save changes"`, `"Cancel"`
|
|
80
|
+
- Placeholder text: `"Find a repository..."`
|
|
81
|
+
- Section headings that are structural: `"Pinned repositories"`, `"Activity"`
|
|
82
|
+
- Empty state messages: `"No results found"`
|
|
83
|
+
- Filter dropdown labels: `"Type"`, `"Language"`, `"Sort"`
|
|
84
|
+
- Dropdown option labels: `"All"`, `"Public"`, `"Private"`
|
|
85
|
+
- Static instructional text
|
|
86
|
+
|
|
87
|
+
### Gray area — use judgment
|
|
88
|
+
|
|
89
|
+
- **Counters on tabs** — externalize if they represent real data counts; hardcode if decorative
|
|
90
|
+
- **Metadata labels** (e.g., "Updated 3 days ago") — externalize the date, hardcode the label format
|
|
91
|
+
- **Card/list item structure** — externalize the items array, hardcode the card template
|
|
92
|
+
|
|
93
|
+
## Workflow
|
|
94
|
+
|
|
95
|
+
### Step 1: Inventory the data needs
|
|
96
|
+
|
|
97
|
+
From the page description (primer-builder Step 1), list every piece of dynamic content:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Navigation:
|
|
101
|
+
- topnav: 9 tabs with icons, labels, counters, active state
|
|
102
|
+
- sidenav: 10 filter options with active state
|
|
103
|
+
|
|
104
|
+
Content:
|
|
105
|
+
- org name, avatar
|
|
106
|
+
- 6 repository items, each with: name, description, language, stars, updated date
|
|
107
|
+
- filter query string
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Step 2: Design the data objects
|
|
111
|
+
|
|
112
|
+
Create one object file per logical entity:
|
|
113
|
+
|
|
114
|
+
| Data type | File name pattern | Example |
|
|
115
|
+
|-----------|------------------|---------|
|
|
116
|
+
| User/person | `{name}.object.json` | `jane-doe.object.json` |
|
|
117
|
+
| Navigation | `{context}-navigation.object.json` | `org-navigation.object.json` |
|
|
118
|
+
| Entity list | `{entity-plural}.object.json` | `repositories.object.json` |
|
|
119
|
+
| Org/team | `{org-name}.object.json` | `primer-org.object.json` |
|
|
120
|
+
|
|
121
|
+
**Object structure rules:**
|
|
122
|
+
- **The object file's top-level structure IS the value that `$ref` resolves to.** If the flow has `"repositories": { "$ref": "repositories" }`, the object file should be a bare array `[...]`, not `{ "repositories": [...] }` — otherwise the data double-nests as `repositories.repositories`.
|
|
123
|
+
- Same rule for single-entity objects: if the flow has `"user": { "$ref": "jane-doe" }`, the object file should contain the user fields directly at root (`{ "name": "Jane", ... }`), not wrapped in `{ "user": { "name": "Jane", ... } }`.
|
|
124
|
+
- Keep objects flat where possible — avoid deep nesting
|
|
125
|
+
- Use arrays for lists of items
|
|
126
|
+
- Include all fields that the UI needs — don't make the component compute derived data
|
|
127
|
+
- Use realistic placeholder data (real GitHub avatar URLs, plausible repo names, etc.)
|
|
128
|
+
|
|
129
|
+
### Step 3: Compose the flow
|
|
130
|
+
|
|
131
|
+
Create a flow file that composes the objects:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
// default.flow.json
|
|
135
|
+
{
|
|
136
|
+
"$global": ["org-navigation"],
|
|
137
|
+
"user": { "$ref": "jane-doe" },
|
|
138
|
+
"org": {
|
|
139
|
+
"name": "my-org",
|
|
140
|
+
"avatar": "https://avatars.githubusercontent.com/u/9919?v=4"
|
|
141
|
+
},
|
|
142
|
+
"repositories": { "$ref": "repositories" }
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**flow composition rules:**
|
|
147
|
+
- Use `$global` for navigation — it merges at the root level, making nav data available at the top
|
|
148
|
+
- Use `$ref` for entity objects — keeps them reusable across flows
|
|
149
|
+
- `$ref` and `$global` use **names**, not paths: `"jane-doe"` not `"../objects/jane-doe"`
|
|
150
|
+
- Inline small, flow-specific data directly (org name, settings, filter state)
|
|
151
|
+
- Name the flow after the page/flow: `org-repos.flow.json`, `issue-detail.flow.json`
|
|
152
|
+
- Rule of thumb: a flow can be named after its corresponding page
|
|
153
|
+
|
|
154
|
+
### Step 4: Wire up the component
|
|
155
|
+
|
|
156
|
+
In the page component, use `useflowData()` with dot-notation paths:
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
import { useflowData, useflowLoading } from '../storyboard'
|
|
160
|
+
|
|
161
|
+
function ReposPage() {
|
|
162
|
+
const topnav = useflowData('topnav') // from $global navigation
|
|
163
|
+
const sidenav = useflowData('sidenav') // from $global navigation
|
|
164
|
+
const org = useflowData('org')
|
|
165
|
+
const repos = useflowData('repositories')
|
|
166
|
+
const loading = useflowLoading()
|
|
167
|
+
|
|
168
|
+
if (loading) return <Spinner />
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<Application title={org.name} topnav={topnav} sidenav={sidenav}>
|
|
172
|
+
{/* Hardcoded UI chrome */}
|
|
173
|
+
<h2>All repositories</h2>
|
|
174
|
+
<Button variant="primary">New repository</Button>
|
|
175
|
+
<TextInput placeholder="Find a repository..." />
|
|
176
|
+
|
|
177
|
+
{/* Data-driven content */}
|
|
178
|
+
{repos.map(repo => (
|
|
179
|
+
<article key={repo.name}>
|
|
180
|
+
<h3><a href={repo.url}>{repo.name}</a></h3>
|
|
181
|
+
<p>{repo.description}</p>
|
|
182
|
+
</article>
|
|
183
|
+
))}
|
|
184
|
+
</Application>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Records & Dynamic Routes
|
|
190
|
+
|
|
191
|
+
Records power **parameterized pages** — the same page template renders different content based on the URL.
|
|
192
|
+
|
|
193
|
+
### Creating a record
|
|
194
|
+
|
|
195
|
+
A record file is a **collection** — an array of entries, each with a unique `id`:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
// posts.record.json
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
"id": "welcome-to-storyboard",
|
|
202
|
+
"title": "Welcome to Storyboard",
|
|
203
|
+
"date": "2026-02-14",
|
|
204
|
+
"author": "Jane Doe",
|
|
205
|
+
"body": "..."
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "another-post",
|
|
209
|
+
"title": "Another Post",
|
|
210
|
+
"date": "2026-02-13",
|
|
211
|
+
"author": "Jane Doe",
|
|
212
|
+
"body": "..."
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Creating a dynamic route page
|
|
218
|
+
|
|
219
|
+
The filename convention `[field].jsx` determines which record field the route matches against:
|
|
220
|
+
|
|
221
|
+
- `pages/issues/[id].jsx` → `useRecord('issues')` matches `entry.id`
|
|
222
|
+
- `pages/posts/[permalink].jsx` → `useRecord('posts', 'permalink')` matches `entry.permalink`
|
|
223
|
+
|
|
224
|
+
The second argument to `useRecord` defaults to `'id'`, so `useRecord('issues')` is equivalent to `useRecord('issues', 'id')`.
|
|
225
|
+
|
|
226
|
+
In the component, use `useRecord()`:
|
|
227
|
+
|
|
228
|
+
```jsx
|
|
229
|
+
import { useRecord } from '../../storyboard'
|
|
230
|
+
|
|
231
|
+
function BlogPost() {
|
|
232
|
+
// 'posts' = record file name, 'id' = route param matched against entry.id
|
|
233
|
+
const post = useRecord('posts', 'id')
|
|
234
|
+
// URL /posts/welcome-to-storyboard → entry with id "welcome-to-storyboard"
|
|
235
|
+
|
|
236
|
+
if (!post) return <p>Post not found</p>
|
|
237
|
+
return <h1>{post.title}</h1>
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Listing all records
|
|
242
|
+
|
|
243
|
+
Use `useRecords()` for index/listing pages:
|
|
244
|
+
|
|
245
|
+
```jsx
|
|
246
|
+
import { useRecords } from '../../storyboard'
|
|
247
|
+
|
|
248
|
+
function PostsIndex() {
|
|
249
|
+
const posts = useRecords('posts')
|
|
250
|
+
return posts.map(post => (
|
|
251
|
+
<a key={post.id} href={`/posts/${post.id}`}>{post.title}</a>
|
|
252
|
+
))
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Records + flows
|
|
257
|
+
|
|
258
|
+
A page can use both a flow (for page-level data like navigation) and a record (for parameterized content). Pass `recordName` and `recordParam` to `StoryboardProvider` to merge record data under the `record` key:
|
|
259
|
+
|
|
260
|
+
```jsx
|
|
261
|
+
<StoryboardProvider recordName="posts" recordParam="slug">
|
|
262
|
+
{/* useflowData('record.title') works here */}
|
|
263
|
+
</StoryboardProvider>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### No `useState` in pages or components
|
|
267
|
+
|
|
268
|
+
All state management must happen through storyboard hooks. Storyboard state lives in the URL hash — not in React component state.
|
|
269
|
+
|
|
270
|
+
**Use these hooks instead:**
|
|
271
|
+
|
|
272
|
+
| Hook | Purpose |
|
|
273
|
+
|------|---------|
|
|
274
|
+
| `useflowData(path?)` | Read flow data by dot-notation path |
|
|
275
|
+
| `useOverride(path)` | Read/write hash-param overrides on flow or object data. Works with or without `<StoryboardProvider>`. |
|
|
276
|
+
| `useObject(name, path?)` | Load an object data file directly by name, without a flow. Supports dot-notation path and hash overrides (`object.{name}.{field}`). |
|
|
277
|
+
| `useRecord(name, param?)` | Load a single record entry matched by URL param (defaults to `'id'`) |
|
|
278
|
+
| `useRecords(name)` | Load all entries from a record collection |
|
|
279
|
+
| `useflowLoading()` | Returns true while flow is loading |
|
|
280
|
+
|
|
281
|
+
**Why:** Storyboard is a prototyping framework where all data flows through the URL hash. This makes every state change shareable via URL, inspectable in the address bar, and framework-portable. Using `useState` breaks this contract — the state becomes invisible, unshareable, and tied to React.
|
|
282
|
+
|
|
283
|
+
## Common Pitfall: Double-Nesting with `$ref`
|
|
284
|
+
|
|
285
|
+
The most frequent data bug is double-nesting. This happens when an object file wraps its data in a key that matches the flow's `$ref` key:
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
// ❌ WRONG — causes double-nesting (advisory.advisory)
|
|
289
|
+
|
|
290
|
+
// flow: { "advisory": { "$ref": "advisory" } }
|
|
291
|
+
// advisory.object.json:
|
|
292
|
+
{ "advisory": { "title": "Bug", "severity": "High" } }
|
|
293
|
+
// Result: flow.advisory = { "advisory": { "title": "Bug", ... } }
|
|
294
|
+
|
|
295
|
+
// ✅ CORRECT — object file is the raw value
|
|
296
|
+
|
|
297
|
+
// flow: { "advisory": { "$ref": "advisory" } }
|
|
298
|
+
// advisory.object.json:
|
|
299
|
+
{ "title": "Bug", "severity": "High" }
|
|
300
|
+
// Result: flow.advisory = { "title": "Bug", "severity": "High" }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Rule of thumb:** The `$ref` key in the flow IS the namespace. The object file provides the value.
|
|
304
|
+
|
|
305
|
+
## Hash Param Preservation (CRITICAL)
|
|
306
|
+
|
|
307
|
+
URL hash params are the foundation of the override system. They carry user-set and session-set values across navigations. **Never write code that drops them.**
|
|
308
|
+
|
|
309
|
+
### How it works
|
|
310
|
+
|
|
311
|
+
`installHashPreserver(router)` in `src/index.jsx` patches both `<a>` click interception and `router.navigate()` so that hash params automatically carry forward on every navigation — including programmatic `navigate('/SomePage')` calls.
|
|
312
|
+
|
|
313
|
+
### Rules
|
|
314
|
+
|
|
315
|
+
1. **Never manually strip or omit the hash.** The global preserver handles it. Plain `navigate('/Page')` works — the hash carries forward automatically.
|
|
316
|
+
2. **Never bypass the router.** Using `window.location.href = '/Page'` or `window.location.assign()` will drop the hash. Always use React Router's `navigate()` or `<Link>`.
|
|
317
|
+
3. **If a page reads overrides, it must use the hooks.** `useflowData(path)` automatically merges hash overrides. `useOverride(path)` gives read/write access.
|
|
318
|
+
4. **If a page writes overrides, downstream pages get them for free.** The Signup→Dashboard flow works because Signup writes via `useOverride`, navigation carries the hash, and Dashboard reads via `useflowData` — no manual plumbing needed.
|
|
319
|
+
5. **To intentionally clear overrides**, use `clearValue` from `useOverride` or `removeParam` from `session.js`. Never clear by navigating without the hash.
|
|
320
|
+
|
|
321
|
+
## Checklist
|
|
322
|
+
|
|
323
|
+
Before finishing data structuring, verify:
|
|
324
|
+
|
|
325
|
+
- [ ] **No double-nesting:** Object files referenced via `$ref` contain raw values, not wrapped in a key
|
|
326
|
+
- [ ] Every navigation array is in a data object (not hardcoded in the component)
|
|
327
|
+
- [ ] Every list of content items is in a data object
|
|
328
|
+
- [ ] User/org profile data is in a data object
|
|
329
|
+
- [ ] Button labels, placeholder text, and section headings are hardcoded
|
|
330
|
+
- [ ] The flow file uses `$global` for navigation and `$ref` for entities
|
|
331
|
+
- [ ] `$ref` and `$global` use **names** (not relative paths)
|
|
332
|
+
- [ ] Data files use the correct suffix: `.flow.json`, `.object.json`, `.record.json`
|
|
333
|
+
- [ ] The component uses `useflowData()` for all externalized data
|
|
334
|
+
- [ ] Shared data objects use `useObject()` when not part of a flow
|
|
335
|
+
- [ ] Dynamic route pages use `useRecord()` for parameterized content
|
|
336
|
+
- [ ] Data objects use realistic placeholder values
|
|
337
|
+
- [ ] The flow name matches the page name or flow
|
|
338
|
+
- [ ] **Hash params are never dropped** — see "Hash Param Preservation" above
|
|
339
|
+
|
|
340
|
+
## Final Step: Provide the URL
|
|
341
|
+
|
|
342
|
+
After creating the flow and wiring up the component, **always provide the full dev URL** so the user can immediately preview the page.
|
|
343
|
+
|
|
344
|
+
**Page-flow matching:** If the flow file name matches the page file name exactly (e.g. `Repositories.flow.json` for `pages/Repositories.jsx`), the flow loads automatically — no `?flow=` param needed:
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
http://localhost:1234/Repositories
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
If the flow name differs from the page name, add the `?flow=` parameter:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
http://localhost:1234/Repositories?flow=heron-silver
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
For dynamic routes, use the record entry's `id` as the URL slug:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
http://localhost:1234/posts/welcome-to-storyboard
|
|
360
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Update Storyboard Packages
|
|
2
|
+
|
|
3
|
+
Updates all `@dfosco/storyboard-*` packages together to the same version and runs the scaffold sync.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run update # Update to latest stable
|
|
9
|
+
npm run update:beta # Update to latest beta
|
|
10
|
+
npm run update:alpha # Update to latest alpha
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
1. Updates all `@dfosco/storyboard-*` packages to the specified tag
|
|
16
|
+
2. Runs `npx storyboard-scaffold` to sync skills and scripts from the latest version
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Update all @dfosco/storyboard-* packages and sync scaffold files.
|
|
3
|
+
# Run from storyboard client root: npm run update
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
TAG="${1:-latest}"
|
|
7
|
+
|
|
8
|
+
PACKAGES=(
|
|
9
|
+
"@dfosco/storyboard-core"
|
|
10
|
+
"@dfosco/storyboard-react"
|
|
11
|
+
"@dfosco/storyboard-react-primer"
|
|
12
|
+
"@dfosco/storyboard-react-reshaped"
|
|
13
|
+
"@dfosco/tiny-canvas"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
echo "Updating @dfosco/* packages to @${TAG}..."
|
|
17
|
+
for pkg in "${PACKAGES[@]}"; do
|
|
18
|
+
npm install "${pkg}@${TAG}" --save
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
echo ""
|
|
22
|
+
echo "Running scaffold sync..."
|
|
23
|
+
npx storyboard-scaffold
|
|
24
|
+
|
|
25
|
+
echo ""
|
|
26
|
+
echo "✔ All packages updated to @${TAG} and scaffold synced."
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vitest
|
|
3
|
+
description: Vitest fast unit testing framework powered by Vite with Jest-compatible API. Use when writing tests, mocking, configuring coverage, or working with test filtering and fixtures.
|
|
4
|
+
metadata:
|
|
5
|
+
author: Anthony Fu
|
|
6
|
+
version: "2026.1.28"
|
|
7
|
+
source: Generated from https://github.com/vitest-dev/vitest, scripts located at https://github.com/antfu/skills
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Vitest is a next-generation testing framework powered by Vite. It provides a Jest-compatible API with native ESM, TypeScript, and JSX support out of the box. Vitest shares the same config, transformers, resolvers, and plugins with your Vite app.
|
|
11
|
+
|
|
12
|
+
**Key Features:**
|
|
13
|
+
- Vite-native: Uses Vite's transformation pipeline for fast HMR-like test updates
|
|
14
|
+
- Jest-compatible: Drop-in replacement for most Jest test suites
|
|
15
|
+
- Smart watch mode: Only reruns affected tests based on module graph
|
|
16
|
+
- Native ESM, TypeScript, JSX support without configuration
|
|
17
|
+
- Multi-threaded workers for parallel test execution
|
|
18
|
+
- Built-in coverage via V8 or Istanbul
|
|
19
|
+
- Snapshot testing, mocking, and spy utilities
|
|
20
|
+
|
|
21
|
+
> The skill is based on Vitest 3.x, generated at 2026-01-28.
|
|
22
|
+
|
|
23
|
+
## Core
|
|
24
|
+
|
|
25
|
+
| Topic | Description | Reference |
|
|
26
|
+
|-------|-------------|-----------|
|
|
27
|
+
| Configuration | Vitest and Vite config integration, defineConfig usage | [core-config](references/core-config.md) |
|
|
28
|
+
| CLI | Command line interface, commands and options | [core-cli](references/core-cli.md) |
|
|
29
|
+
| Test API | test/it function, modifiers like skip, only, concurrent | [core-test-api](references/core-test-api.md) |
|
|
30
|
+
| Describe API | describe/suite for grouping tests and nested suites | [core-describe](references/core-describe.md) |
|
|
31
|
+
| Expect API | Assertions with toBe, toEqual, matchers and asymmetric matchers | [core-expect](references/core-expect.md) |
|
|
32
|
+
| Hooks | beforeEach, afterEach, beforeAll, afterAll, aroundEach | [core-hooks](references/core-hooks.md) |
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
| Topic | Description | Reference |
|
|
37
|
+
|-------|-------------|-----------|
|
|
38
|
+
| Mocking | Mock functions, modules, timers, dates with vi utilities | [features-mocking](references/features-mocking.md) |
|
|
39
|
+
| Snapshots | Snapshot testing with toMatchSnapshot and inline snapshots | [features-snapshots](references/features-snapshots.md) |
|
|
40
|
+
| Coverage | Code coverage with V8 or Istanbul providers | [features-coverage](references/features-coverage.md) |
|
|
41
|
+
| Test Context | Test fixtures, context.expect, test.extend for custom fixtures | [features-context](references/features-context.md) |
|
|
42
|
+
| Concurrency | Concurrent tests, parallel execution, sharding | [features-concurrency](references/features-concurrency.md) |
|
|
43
|
+
| Filtering | Filter tests by name, file patterns, tags | [features-filtering](references/features-filtering.md) |
|
|
44
|
+
|
|
45
|
+
## Advanced
|
|
46
|
+
|
|
47
|
+
| Topic | Description | Reference |
|
|
48
|
+
|-------|-------------|-----------|
|
|
49
|
+
| Vi Utilities | vi helper: mock, spyOn, fake timers, hoisted, waitFor | [advanced-vi](references/advanced-vi.md) |
|
|
50
|
+
| Environments | Test environments: node, jsdom, happy-dom, custom | [advanced-environments](references/advanced-environments.md) |
|
|
51
|
+
| Type Testing | Type-level testing with expectTypeOf and assertType | [advanced-type-testing](references/advanced-type-testing.md) |
|
|
52
|
+
| Projects | Multi-project workspaces, different configs per project | [advanced-projects](references/advanced-projects.md) |
|