@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.
Files changed (59) hide show
  1. package/dist/storyboard-ui.css +1 -0
  2. package/dist/storyboard-ui.js +26298 -0
  3. package/dist/storyboard-ui.js.map +1 -0
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +24 -19
  6. package/scaffold/manifest.json +35 -0
  7. package/scaffold/scripts/link.sh +26 -0
  8. package/scaffold/scripts/unlink.sh +10 -0
  9. package/scaffold/skills/create/SKILL.md +501 -0
  10. package/scaffold/skills/storyboard/SKILL.md +360 -0
  11. package/scaffold/skills/update-storyboard/SKILL.md +16 -0
  12. package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
  13. package/scaffold/skills/vitest/GENERATION.md +5 -0
  14. package/scaffold/skills/vitest/SKILL.md +52 -0
  15. package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
  16. package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
  17. package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
  18. package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
  19. package/scaffold/skills/vitest/references/core-cli.md +166 -0
  20. package/scaffold/skills/vitest/references/core-config.md +174 -0
  21. package/scaffold/skills/vitest/references/core-describe.md +193 -0
  22. package/scaffold/skills/vitest/references/core-expect.md +219 -0
  23. package/scaffold/skills/vitest/references/core-hooks.md +244 -0
  24. package/scaffold/skills/vitest/references/core-test-api.md +233 -0
  25. package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
  26. package/scaffold/skills/vitest/references/features-context.md +238 -0
  27. package/scaffold/skills/vitest/references/features-coverage.md +207 -0
  28. package/scaffold/skills/vitest/references/features-filtering.md +211 -0
  29. package/scaffold/skills/vitest/references/features-mocking.md +265 -0
  30. package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
  31. package/scaffold/skills/worktree/SKILL.md +51 -0
  32. package/scaffold/storyboard.config.json +26 -0
  33. package/scaffold/svelte.config.js +1 -0
  34. package/scaffold/toolbar.config.json +4 -0
  35. package/src/ActionMenuButton.svelte +1 -1
  36. package/src/CanvasCreateMenu.svelte +1 -1
  37. package/src/CoreUIBar.svelte +20 -9
  38. package/src/CreateMenuButton.svelte +1 -1
  39. package/src/InspectorPanel.svelte +144 -49
  40. package/src/SidePanel.svelte +10 -10
  41. package/src/commandActions.js +1 -1
  42. package/src/comments/index.js +0 -3
  43. package/src/devtools.js +4 -1
  44. package/src/index.js +5 -2
  45. package/src/inspector/highlighter.js +3 -4
  46. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  47. package/src/mountStoryboardCore.js +223 -0
  48. package/src/scaffold.js +100 -0
  49. package/src/stores/themeStore.ts +29 -8
  50. package/src/styles/tailwind.css +16 -0
  51. package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
  52. package/src/ui-entry.js +30 -0
  53. package/src/vite/server-plugin.js +8 -24
  54. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
  55. package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
  56. package/src/workshop/features/createFlow/index.js +0 -1
  57. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
  58. package/src/workshop/features/createPrototype/index.js +0 -1
  59. /package/{core-ui.config.json → toolbar.config.json} +0 -0
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: vitest-configuration
3
+ description: Configure Vitest with vite.config.ts or vitest.config.ts
4
+ ---
5
+
6
+ # Configuration
7
+
8
+ Vitest reads configuration from `vitest.config.ts` or `vite.config.ts`. It shares the same config format as Vite.
9
+
10
+ ## Basic Setup
11
+
12
+ ```ts
13
+ // vitest.config.ts
14
+ import { defineConfig } from 'vitest/config'
15
+
16
+ export default defineConfig({
17
+ test: {
18
+ // test options
19
+ },
20
+ })
21
+ ```
22
+
23
+ ## Using with Existing Vite Config
24
+
25
+ Add Vitest types reference and use the `test` property:
26
+
27
+ ```ts
28
+ // vite.config.ts
29
+ /// <reference types="vitest/config" />
30
+ import { defineConfig } from 'vite'
31
+
32
+ export default defineConfig({
33
+ test: {
34
+ globals: true,
35
+ environment: 'jsdom',
36
+ },
37
+ })
38
+ ```
39
+
40
+ ## Merging Configs
41
+
42
+ If you have separate config files, use `mergeConfig`:
43
+
44
+ ```ts
45
+ // vitest.config.ts
46
+ import { defineConfig, mergeConfig } from 'vitest/config'
47
+ import viteConfig from './vite.config'
48
+
49
+ export default mergeConfig(viteConfig, defineConfig({
50
+ test: {
51
+ environment: 'jsdom',
52
+ },
53
+ }))
54
+ ```
55
+
56
+ ## Common Options
57
+
58
+ ```ts
59
+ defineConfig({
60
+ test: {
61
+ // Enable global APIs (describe, it, expect) without imports
62
+ globals: true,
63
+
64
+ // Test environment: 'node', 'jsdom', 'happy-dom'
65
+ environment: 'node',
66
+
67
+ // Setup files to run before each test file
68
+ setupFiles: ['./tests/setup.ts'],
69
+
70
+ // Include patterns for test files
71
+ include: ['**/*.{test,spec}.{js,ts,jsx,tsx}'],
72
+
73
+ // Exclude patterns
74
+ exclude: ['**/node_modules/**', '**/dist/**'],
75
+
76
+ // Test timeout in ms
77
+ testTimeout: 5000,
78
+
79
+ // Hook timeout in ms
80
+ hookTimeout: 10000,
81
+
82
+ // Enable watch mode by default
83
+ watch: true,
84
+
85
+ // Coverage configuration
86
+ coverage: {
87
+ provider: 'v8', // or 'istanbul'
88
+ reporter: ['text', 'html'],
89
+ include: ['src/**/*.ts'],
90
+ },
91
+
92
+ // Run tests in isolation (each file in separate process)
93
+ isolate: true,
94
+
95
+ // Pool for running tests: 'threads', 'forks', 'vmThreads'
96
+ pool: 'threads',
97
+
98
+ // Number of threads/processes
99
+ poolOptions: {
100
+ threads: {
101
+ maxThreads: 4,
102
+ minThreads: 1,
103
+ },
104
+ },
105
+
106
+ // Automatically clear mocks between tests
107
+ clearMocks: true,
108
+
109
+ // Restore mocks between tests
110
+ restoreMocks: true,
111
+
112
+ // Retry failed tests
113
+ retry: 0,
114
+
115
+ // Stop after first failure
116
+ bail: 0,
117
+ },
118
+ })
119
+ ```
120
+
121
+ ## Conditional Configuration
122
+
123
+ Use `mode` or `process.env.VITEST` for test-specific config:
124
+
125
+ ```ts
126
+ export default defineConfig(({ mode }) => ({
127
+ plugins: mode === 'test' ? [] : [myPlugin()],
128
+ test: {
129
+ // test options
130
+ },
131
+ }))
132
+ ```
133
+
134
+ ## Projects (Monorepos)
135
+
136
+ Run different configurations in the same Vitest process:
137
+
138
+ ```ts
139
+ defineConfig({
140
+ test: {
141
+ projects: [
142
+ 'packages/*',
143
+ {
144
+ test: {
145
+ name: 'unit',
146
+ include: ['tests/unit/**/*.test.ts'],
147
+ environment: 'node',
148
+ },
149
+ },
150
+ {
151
+ test: {
152
+ name: 'integration',
153
+ include: ['tests/integration/**/*.test.ts'],
154
+ environment: 'jsdom',
155
+ },
156
+ },
157
+ ],
158
+ },
159
+ })
160
+ ```
161
+
162
+ ## Key Points
163
+
164
+ - Vitest uses Vite's transformation pipeline - same `resolve.alias`, plugins work
165
+ - `vitest.config.ts` takes priority over `vite.config.ts`
166
+ - Use `--config` flag to specify a custom config path
167
+ - `process.env.VITEST` is set to `true` when running tests
168
+ - Test config uses `test` property, rest is Vite config
169
+
170
+ <!--
171
+ Source references:
172
+ - https://vitest.dev/guide/#configuring-vitest
173
+ - https://vitest.dev/config/
174
+ -->
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: describe-api
3
+ description: describe/suite for grouping tests into logical blocks
4
+ ---
5
+
6
+ # Describe API
7
+
8
+ Group related tests into suites for organization and shared setup.
9
+
10
+ ## Basic Usage
11
+
12
+ ```ts
13
+ import { describe, expect, test } from 'vitest'
14
+
15
+ describe('Math', () => {
16
+ test('adds numbers', () => {
17
+ expect(1 + 1).toBe(2)
18
+ })
19
+
20
+ test('subtracts numbers', () => {
21
+ expect(3 - 1).toBe(2)
22
+ })
23
+ })
24
+
25
+ // Alias: suite
26
+ import { suite } from 'vitest'
27
+ suite('equivalent to describe', () => {})
28
+ ```
29
+
30
+ ## Nested Suites
31
+
32
+ ```ts
33
+ describe('User', () => {
34
+ describe('when logged in', () => {
35
+ test('shows dashboard', () => {})
36
+ test('can update profile', () => {})
37
+ })
38
+
39
+ describe('when logged out', () => {
40
+ test('shows login page', () => {})
41
+ })
42
+ })
43
+ ```
44
+
45
+ ## Suite Options
46
+
47
+ ```ts
48
+ // All tests inherit options
49
+ describe('slow tests', { timeout: 30_000 }, () => {
50
+ test('test 1', () => {}) // 30s timeout
51
+ test('test 2', () => {}) // 30s timeout
52
+ })
53
+ ```
54
+
55
+ ## Suite Modifiers
56
+
57
+ ### Skip Suites
58
+
59
+ ```ts
60
+ describe.skip('skipped suite', () => {
61
+ test('wont run', () => {})
62
+ })
63
+
64
+ // Conditional
65
+ describe.skipIf(process.env.CI)('not in CI', () => {})
66
+ describe.runIf(!process.env.CI)('only local', () => {})
67
+ ```
68
+
69
+ ### Focus Suites
70
+
71
+ ```ts
72
+ describe.only('only this suite runs', () => {
73
+ test('runs', () => {})
74
+ })
75
+ ```
76
+
77
+ ### Todo Suites
78
+
79
+ ```ts
80
+ describe.todo('implement later')
81
+ ```
82
+
83
+ ### Concurrent Suites
84
+
85
+ ```ts
86
+ // All tests run in parallel
87
+ describe.concurrent('parallel tests', () => {
88
+ test('test 1', async ({ expect }) => {})
89
+ test('test 2', async ({ expect }) => {})
90
+ })
91
+ ```
92
+
93
+ ### Sequential in Concurrent
94
+
95
+ ```ts
96
+ describe.concurrent('parallel', () => {
97
+ test('concurrent 1', async () => {})
98
+
99
+ describe.sequential('must be sequential', () => {
100
+ test('step 1', async () => {})
101
+ test('step 2', async () => {})
102
+ })
103
+ })
104
+ ```
105
+
106
+ ### Shuffle Tests
107
+
108
+ ```ts
109
+ describe.shuffle('random order', () => {
110
+ test('test 1', () => {})
111
+ test('test 2', () => {})
112
+ test('test 3', () => {})
113
+ })
114
+
115
+ // Or with option
116
+ describe('random', { shuffle: true }, () => {})
117
+ ```
118
+
119
+ ## Parameterized Suites
120
+
121
+ ### describe.each
122
+
123
+ ```ts
124
+ describe.each([
125
+ { name: 'Chrome', version: 100 },
126
+ { name: 'Firefox', version: 90 },
127
+ ])('$name browser', ({ name, version }) => {
128
+ test('has version', () => {
129
+ expect(version).toBeGreaterThan(0)
130
+ })
131
+ })
132
+ ```
133
+
134
+ ### describe.for
135
+
136
+ ```ts
137
+ describe.for([
138
+ ['Chrome', 100],
139
+ ['Firefox', 90],
140
+ ])('%s browser', ([name, version]) => {
141
+ test('has version', () => {
142
+ expect(version).toBeGreaterThan(0)
143
+ })
144
+ })
145
+ ```
146
+
147
+ ## Hooks in Suites
148
+
149
+ ```ts
150
+ describe('Database', () => {
151
+ let db
152
+
153
+ beforeAll(async () => {
154
+ db = await createDb()
155
+ })
156
+
157
+ afterAll(async () => {
158
+ await db.close()
159
+ })
160
+
161
+ beforeEach(async () => {
162
+ await db.clear()
163
+ })
164
+
165
+ test('insert works', async () => {
166
+ await db.insert({ name: 'test' })
167
+ expect(await db.count()).toBe(1)
168
+ })
169
+ })
170
+ ```
171
+
172
+ ## Modifier Combinations
173
+
174
+ All modifiers can be chained:
175
+
176
+ ```ts
177
+ describe.skip.concurrent('skipped concurrent', () => {})
178
+ describe.only.shuffle('only and shuffled', () => {})
179
+ describe.concurrent.skip('equivalent', () => {})
180
+ ```
181
+
182
+ ## Key Points
183
+
184
+ - Top-level tests belong to an implicit file suite
185
+ - Nested suites inherit parent's options (timeout, retry, etc.)
186
+ - Hooks are scoped to their suite and nested suites
187
+ - Use `describe.concurrent` with context's `expect` for snapshots
188
+ - Shuffle order depends on `sequence.seed` config
189
+
190
+ <!--
191
+ Source references:
192
+ - https://vitest.dev/api/describe.html
193
+ -->
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: expect-api
3
+ description: Assertions with matchers, asymmetric matchers, and custom matchers
4
+ ---
5
+
6
+ # Expect API
7
+
8
+ Vitest uses Chai assertions with Jest-compatible API.
9
+
10
+ ## Basic Assertions
11
+
12
+ ```ts
13
+ import { expect, test } from 'vitest'
14
+
15
+ test('assertions', () => {
16
+ // Equality
17
+ expect(1 + 1).toBe(2) // Strict equality (===)
18
+ expect({ a: 1 }).toEqual({ a: 1 }) // Deep equality
19
+
20
+ // Truthiness
21
+ expect(true).toBeTruthy()
22
+ expect(false).toBeFalsy()
23
+ expect(null).toBeNull()
24
+ expect(undefined).toBeUndefined()
25
+ expect('value').toBeDefined()
26
+
27
+ // Numbers
28
+ expect(10).toBeGreaterThan(5)
29
+ expect(10).toBeGreaterThanOrEqual(10)
30
+ expect(5).toBeLessThan(10)
31
+ expect(0.1 + 0.2).toBeCloseTo(0.3, 5)
32
+
33
+ // Strings
34
+ expect('hello world').toMatch(/world/)
35
+ expect('hello').toContain('ell')
36
+
37
+ // Arrays
38
+ expect([1, 2, 3]).toContain(2)
39
+ expect([{ a: 1 }]).toContainEqual({ a: 1 })
40
+ expect([1, 2, 3]).toHaveLength(3)
41
+
42
+ // Objects
43
+ expect({ a: 1, b: 2 }).toHaveProperty('a')
44
+ expect({ a: 1, b: 2 }).toHaveProperty('a', 1)
45
+ expect({ a: { b: 1 } }).toHaveProperty('a.b', 1)
46
+ expect({ a: 1 }).toMatchObject({ a: 1 })
47
+
48
+ // Types
49
+ expect('string').toBeTypeOf('string')
50
+ expect(new Date()).toBeInstanceOf(Date)
51
+ })
52
+ ```
53
+
54
+ ## Negation
55
+
56
+ ```ts
57
+ expect(1).not.toBe(2)
58
+ expect({ a: 1 }).not.toEqual({ a: 2 })
59
+ ```
60
+
61
+ ## Error Assertions
62
+
63
+ ```ts
64
+ // Sync errors - wrap in function
65
+ expect(() => throwError()).toThrow()
66
+ expect(() => throwError()).toThrow('message')
67
+ expect(() => throwError()).toThrow(/pattern/)
68
+ expect(() => throwError()).toThrow(CustomError)
69
+
70
+ // Async errors - use rejects
71
+ await expect(asyncThrow()).rejects.toThrow('error')
72
+ ```
73
+
74
+ ## Promise Assertions
75
+
76
+ ```ts
77
+ // Resolves
78
+ await expect(Promise.resolve(1)).resolves.toBe(1)
79
+ await expect(fetchData()).resolves.toEqual({ data: true })
80
+
81
+ // Rejects
82
+ await expect(Promise.reject('error')).rejects.toBe('error')
83
+ await expect(failingFetch()).rejects.toThrow()
84
+ ```
85
+
86
+ ## Spy/Mock Assertions
87
+
88
+ ```ts
89
+ const fn = vi.fn()
90
+ fn('arg1', 'arg2')
91
+ fn('arg3')
92
+
93
+ expect(fn).toHaveBeenCalled()
94
+ expect(fn).toHaveBeenCalledTimes(2)
95
+ expect(fn).toHaveBeenCalledWith('arg1', 'arg2')
96
+ expect(fn).toHaveBeenLastCalledWith('arg3')
97
+ expect(fn).toHaveBeenNthCalledWith(1, 'arg1', 'arg2')
98
+
99
+ expect(fn).toHaveReturned()
100
+ expect(fn).toHaveReturnedWith(value)
101
+ ```
102
+
103
+ ## Asymmetric Matchers
104
+
105
+ Use inside `toEqual`, `toHaveBeenCalledWith`, etc:
106
+
107
+ ```ts
108
+ expect({ id: 1, name: 'test' }).toEqual({
109
+ id: expect.any(Number),
110
+ name: expect.any(String),
111
+ })
112
+
113
+ expect({ a: 1, b: 2, c: 3 }).toEqual(
114
+ expect.objectContaining({ a: 1 })
115
+ )
116
+
117
+ expect([1, 2, 3, 4]).toEqual(
118
+ expect.arrayContaining([1, 3])
119
+ )
120
+
121
+ expect('hello world').toEqual(
122
+ expect.stringContaining('world')
123
+ )
124
+
125
+ expect('hello world').toEqual(
126
+ expect.stringMatching(/world$/)
127
+ )
128
+
129
+ expect({ value: null }).toEqual({
130
+ value: expect.anything() // Matches anything except null/undefined
131
+ })
132
+
133
+ // Negate with expect.not
134
+ expect([1, 2]).toEqual(
135
+ expect.not.arrayContaining([3])
136
+ )
137
+ ```
138
+
139
+ ## Soft Assertions
140
+
141
+ Continue test after failure:
142
+
143
+ ```ts
144
+ expect.soft(1).toBe(2) // Marks test failed but continues
145
+ expect.soft(2).toBe(3) // Also runs
146
+ // All failures reported at end
147
+ ```
148
+
149
+ ## Poll Assertions
150
+
151
+ Retry until passes:
152
+
153
+ ```ts
154
+ await expect.poll(() => fetchStatus()).toBe('ready')
155
+
156
+ await expect.poll(
157
+ () => document.querySelector('.element'),
158
+ { interval: 100, timeout: 5000 }
159
+ ).toBeTruthy()
160
+ ```
161
+
162
+ ## Assertion Count
163
+
164
+ ```ts
165
+ test('async assertions', async () => {
166
+ expect.assertions(2) // Exactly 2 assertions must run
167
+
168
+ await doAsync((data) => {
169
+ expect(data).toBeDefined()
170
+ expect(data.id).toBe(1)
171
+ })
172
+ })
173
+
174
+ test('at least one', () => {
175
+ expect.hasAssertions() // At least 1 assertion must run
176
+ })
177
+ ```
178
+
179
+ ## Extending Matchers
180
+
181
+ ```ts
182
+ expect.extend({
183
+ toBeWithinRange(received, floor, ceiling) {
184
+ const pass = received >= floor && received <= ceiling
185
+ return {
186
+ pass,
187
+ message: () =>
188
+ `expected ${received} to be within range ${floor} - ${ceiling}`,
189
+ }
190
+ },
191
+ })
192
+
193
+ test('custom matcher', () => {
194
+ expect(100).toBeWithinRange(90, 110)
195
+ })
196
+ ```
197
+
198
+ ## Snapshot Assertions
199
+
200
+ ```ts
201
+ expect(data).toMatchSnapshot()
202
+ expect(data).toMatchInlineSnapshot(`{ "id": 1 }`)
203
+ await expect(result).toMatchFileSnapshot('./expected.json')
204
+
205
+ expect(() => throw new Error('fail')).toThrowErrorMatchingSnapshot()
206
+ ```
207
+
208
+ ## Key Points
209
+
210
+ - Use `toBe` for primitives, `toEqual` for objects/arrays
211
+ - `toStrictEqual` checks undefined properties and array sparseness
212
+ - Always `await` async assertions (`resolves`, `rejects`, `poll`)
213
+ - Use context's `expect` in concurrent tests for correct tracking
214
+ - `toThrow` requires wrapping sync code in a function
215
+
216
+ <!--
217
+ Source references:
218
+ - https://vitest.dev/api/expect.html
219
+ -->