@dfosco/storyboard-core 3.2.0 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/storyboard-ui.css +1 -0
- package/dist/storyboard-ui.js +26304 -0
- package/dist/storyboard-ui.js.map +1 -0
- package/dist/tailwind.css +1 -1
- package/package.json +24 -18
- 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 +23 -12
- 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-consumer.js +28 -0
- package/src/devtools.js +4 -1
- package/src/index.js +8 -3
- package/src/inspector/highlighter.js +28 -17
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
- package/src/lib/components/ui/trigger-button/trigger-button.svelte +8 -4
- 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,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mocking
|
|
3
|
+
description: Mock functions, modules, timers, and dates with vi utilities
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Mocking
|
|
7
|
+
|
|
8
|
+
## Mock Functions
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { expect, vi } from 'vitest'
|
|
12
|
+
|
|
13
|
+
// Create mock function
|
|
14
|
+
const fn = vi.fn()
|
|
15
|
+
fn('hello')
|
|
16
|
+
|
|
17
|
+
expect(fn).toHaveBeenCalled()
|
|
18
|
+
expect(fn).toHaveBeenCalledWith('hello')
|
|
19
|
+
|
|
20
|
+
// With implementation
|
|
21
|
+
const add = vi.fn((a, b) => a + b)
|
|
22
|
+
expect(add(1, 2)).toBe(3)
|
|
23
|
+
|
|
24
|
+
// Mock return values
|
|
25
|
+
fn.mockReturnValue(42)
|
|
26
|
+
fn.mockReturnValueOnce(1).mockReturnValueOnce(2)
|
|
27
|
+
fn.mockResolvedValue({ data: true })
|
|
28
|
+
fn.mockRejectedValue(new Error('fail'))
|
|
29
|
+
|
|
30
|
+
// Mock implementation
|
|
31
|
+
fn.mockImplementation((x) => x * 2)
|
|
32
|
+
fn.mockImplementationOnce(() => 'first call')
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Spying on Objects
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const cart = {
|
|
39
|
+
getTotal: () => 100,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const spy = vi.spyOn(cart, 'getTotal')
|
|
43
|
+
cart.getTotal()
|
|
44
|
+
|
|
45
|
+
expect(spy).toHaveBeenCalled()
|
|
46
|
+
|
|
47
|
+
// Mock implementation
|
|
48
|
+
spy.mockReturnValue(200)
|
|
49
|
+
expect(cart.getTotal()).toBe(200)
|
|
50
|
+
|
|
51
|
+
// Restore original
|
|
52
|
+
spy.mockRestore()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Module Mocking
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// vi.mock is hoisted to top of file
|
|
59
|
+
vi.mock('./api', () => ({
|
|
60
|
+
fetchUser: vi.fn(() => ({ id: 1, name: 'Mock' })),
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
import { fetchUser } from './api'
|
|
64
|
+
|
|
65
|
+
test('mocked module', () => {
|
|
66
|
+
expect(fetchUser()).toEqual({ id: 1, name: 'Mock' })
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Partial Mock
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
vi.mock('./utils', async (importOriginal) => {
|
|
74
|
+
const actual = await importOriginal()
|
|
75
|
+
return {
|
|
76
|
+
...actual,
|
|
77
|
+
specificFunction: vi.fn(),
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Auto-mock with Spy
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// Keep implementation but spy on calls
|
|
86
|
+
vi.mock('./calculator', { spy: true })
|
|
87
|
+
|
|
88
|
+
import { add } from './calculator'
|
|
89
|
+
|
|
90
|
+
test('spy on module', () => {
|
|
91
|
+
const result = add(1, 2) // Real implementation
|
|
92
|
+
expect(result).toBe(3)
|
|
93
|
+
expect(add).toHaveBeenCalledWith(1, 2)
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Manual Mocks (__mocks__)
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
src/
|
|
101
|
+
__mocks__/
|
|
102
|
+
axios.ts # Mocks 'axios'
|
|
103
|
+
api/
|
|
104
|
+
__mocks__/
|
|
105
|
+
client.ts # Mocks './client'
|
|
106
|
+
client.ts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// Just call vi.mock with no factory
|
|
111
|
+
vi.mock('axios')
|
|
112
|
+
vi.mock('./api/client')
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Dynamic Mocking (vi.doMock)
|
|
116
|
+
|
|
117
|
+
Not hoisted - use for dynamic imports:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
test('dynamic mock', async () => {
|
|
121
|
+
vi.doMock('./config', () => ({
|
|
122
|
+
apiUrl: 'http://test.local',
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
const { apiUrl } = await import('./config')
|
|
126
|
+
expect(apiUrl).toBe('http://test.local')
|
|
127
|
+
|
|
128
|
+
vi.doUnmock('./config')
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Mock Timers
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { afterEach, beforeEach, vi } from 'vitest'
|
|
136
|
+
|
|
137
|
+
beforeEach(() => {
|
|
138
|
+
vi.useFakeTimers()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
afterEach(() => {
|
|
142
|
+
vi.useRealTimers()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('timers', () => {
|
|
146
|
+
const fn = vi.fn()
|
|
147
|
+
setTimeout(fn, 1000)
|
|
148
|
+
|
|
149
|
+
expect(fn).not.toHaveBeenCalled()
|
|
150
|
+
|
|
151
|
+
vi.advanceTimersByTime(1000)
|
|
152
|
+
expect(fn).toHaveBeenCalled()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Other timer methods
|
|
156
|
+
vi.runAllTimers() // Run all pending timers
|
|
157
|
+
vi.runOnlyPendingTimers() // Run only currently pending
|
|
158
|
+
vi.advanceTimersToNextTimer() // Advance to next timer
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Async Timer Methods
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
test('async timers', async () => {
|
|
165
|
+
vi.useFakeTimers()
|
|
166
|
+
|
|
167
|
+
let resolved = false
|
|
168
|
+
setTimeout(() => Promise.resolve().then(() => { resolved = true }), 100)
|
|
169
|
+
|
|
170
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
171
|
+
expect(resolved).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Mock Dates
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
vi.setSystemTime(new Date('2024-01-01'))
|
|
179
|
+
expect(new Date().getFullYear()).toBe(2024)
|
|
180
|
+
|
|
181
|
+
vi.useRealTimers() // Restore
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Mock Globals
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
vi.stubGlobal('fetch', vi.fn(() =>
|
|
188
|
+
Promise.resolve({ json: () => ({ data: 'mock' }) })
|
|
189
|
+
))
|
|
190
|
+
|
|
191
|
+
// Restore
|
|
192
|
+
vi.unstubAllGlobals()
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Mock Environment Variables
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
vi.stubEnv('API_KEY', 'test-key')
|
|
199
|
+
expect(import.meta.env.API_KEY).toBe('test-key')
|
|
200
|
+
|
|
201
|
+
// Restore
|
|
202
|
+
vi.unstubAllEnvs()
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Clearing Mocks
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const fn = vi.fn()
|
|
209
|
+
fn()
|
|
210
|
+
|
|
211
|
+
fn.mockClear() // Clear call history
|
|
212
|
+
fn.mockReset() // Clear history + implementation
|
|
213
|
+
fn.mockRestore() // Restore original (for spies)
|
|
214
|
+
|
|
215
|
+
// Global
|
|
216
|
+
vi.clearAllMocks()
|
|
217
|
+
vi.resetAllMocks()
|
|
218
|
+
vi.restoreAllMocks()
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Config Auto-Reset
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// vitest.config.ts
|
|
225
|
+
defineConfig({
|
|
226
|
+
test: {
|
|
227
|
+
clearMocks: true, // Clear before each test
|
|
228
|
+
mockReset: true, // Reset before each test
|
|
229
|
+
restoreMocks: true, // Restore after each test
|
|
230
|
+
unstubEnvs: true, // Restore env vars
|
|
231
|
+
unstubGlobals: true, // Restore globals
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Hoisted Variables for Mocks
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const mockFn = vi.hoisted(() => vi.fn())
|
|
240
|
+
|
|
241
|
+
vi.mock('./module', () => ({
|
|
242
|
+
getData: mockFn,
|
|
243
|
+
}))
|
|
244
|
+
|
|
245
|
+
import { getData } from './module'
|
|
246
|
+
|
|
247
|
+
test('hoisted mock', () => {
|
|
248
|
+
mockFn.mockReturnValue('test')
|
|
249
|
+
expect(getData()).toBe('test')
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Key Points
|
|
254
|
+
|
|
255
|
+
- `vi.mock` is hoisted - called before imports
|
|
256
|
+
- Use `vi.doMock` for dynamic, non-hoisted mocking
|
|
257
|
+
- Always restore mocks to avoid test pollution
|
|
258
|
+
- Use `{ spy: true }` to keep implementation but track calls
|
|
259
|
+
- `vi.hoisted` lets you reference variables in mock factories
|
|
260
|
+
|
|
261
|
+
<!--
|
|
262
|
+
Source references:
|
|
263
|
+
- https://vitest.dev/guide/mocking.html
|
|
264
|
+
- https://vitest.dev/api/vi.html
|
|
265
|
+
-->
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: snapshot-testing
|
|
3
|
+
description: Snapshot testing with file, inline, and file snapshots
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Snapshot Testing
|
|
7
|
+
|
|
8
|
+
Snapshot tests capture output and compare against stored references.
|
|
9
|
+
|
|
10
|
+
## Basic Snapshot
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { expect, test } from 'vitest'
|
|
14
|
+
|
|
15
|
+
test('snapshot', () => {
|
|
16
|
+
const result = generateOutput()
|
|
17
|
+
expect(result).toMatchSnapshot()
|
|
18
|
+
})
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
First run creates `.snap` file:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// __snapshots__/test.spec.ts.snap
|
|
25
|
+
exports['snapshot 1'] = `
|
|
26
|
+
{
|
|
27
|
+
"id": 1,
|
|
28
|
+
"name": "test"
|
|
29
|
+
}
|
|
30
|
+
`
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Inline Snapshots
|
|
34
|
+
|
|
35
|
+
Stored directly in test file:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
test('inline snapshot', () => {
|
|
39
|
+
const data = { foo: 'bar' }
|
|
40
|
+
expect(data).toMatchInlineSnapshot()
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Vitest updates the test file:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
test('inline snapshot', () => {
|
|
48
|
+
const data = { foo: 'bar' }
|
|
49
|
+
expect(data).toMatchInlineSnapshot(`
|
|
50
|
+
{
|
|
51
|
+
"foo": "bar",
|
|
52
|
+
}
|
|
53
|
+
`)
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## File Snapshots
|
|
58
|
+
|
|
59
|
+
Compare against explicit file:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
test('render html', async () => {
|
|
63
|
+
const html = renderComponent()
|
|
64
|
+
await expect(html).toMatchFileSnapshot('./expected/component.html')
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Snapshot Hints
|
|
69
|
+
|
|
70
|
+
Add descriptive hints:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
test('multiple snapshots', () => {
|
|
74
|
+
expect(header).toMatchSnapshot('header')
|
|
75
|
+
expect(body).toMatchSnapshot('body content')
|
|
76
|
+
expect(footer).toMatchSnapshot('footer')
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Object Shape Matching
|
|
81
|
+
|
|
82
|
+
Match partial structure:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
test('shape snapshot', () => {
|
|
86
|
+
const data = {
|
|
87
|
+
id: Math.random(),
|
|
88
|
+
created: new Date(),
|
|
89
|
+
name: 'test'
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
expect(data).toMatchSnapshot({
|
|
93
|
+
id: expect.any(Number),
|
|
94
|
+
created: expect.any(Date),
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Error Snapshots
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
test('error message', () => {
|
|
103
|
+
expect(() => {
|
|
104
|
+
throw new Error('Something went wrong')
|
|
105
|
+
}).toThrowErrorMatchingSnapshot()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('inline error', () => {
|
|
109
|
+
expect(() => {
|
|
110
|
+
throw new Error('Bad input')
|
|
111
|
+
}).toThrowErrorMatchingInlineSnapshot(`[Error: Bad input]`)
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Updating Snapshots
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Update all snapshots
|
|
119
|
+
vitest -u
|
|
120
|
+
vitest --update
|
|
121
|
+
|
|
122
|
+
# In watch mode, press 'u' to update failed snapshots
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Custom Serializers
|
|
126
|
+
|
|
127
|
+
Add custom snapshot formatting:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
expect.addSnapshotSerializer({
|
|
131
|
+
test(val) {
|
|
132
|
+
return val && typeof val.toJSON === 'function'
|
|
133
|
+
},
|
|
134
|
+
serialize(val, config, indentation, depth, refs, printer) {
|
|
135
|
+
return printer(val.toJSON(), config, indentation, depth, refs)
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Or via config:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// vitest.config.ts
|
|
144
|
+
defineConfig({
|
|
145
|
+
test: {
|
|
146
|
+
snapshotSerializers: ['./my-serializer.ts'],
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Snapshot Format Options
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
defineConfig({
|
|
155
|
+
test: {
|
|
156
|
+
snapshotFormat: {
|
|
157
|
+
printBasicPrototype: false, // Don't print Array/Object prototypes
|
|
158
|
+
escapeString: false,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Concurrent Test Snapshots
|
|
165
|
+
|
|
166
|
+
Use context's expect:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
test.concurrent('concurrent 1', async ({ expect }) => {
|
|
170
|
+
expect(await getData()).toMatchSnapshot()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test.concurrent('concurrent 2', async ({ expect }) => {
|
|
174
|
+
expect(await getOther()).toMatchSnapshot()
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Snapshot File Location
|
|
179
|
+
|
|
180
|
+
Default: `__snapshots__/<test-file>.snap`
|
|
181
|
+
|
|
182
|
+
Customize:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
defineConfig({
|
|
186
|
+
test: {
|
|
187
|
+
resolveSnapshotPath: (testPath, snapExtension) => {
|
|
188
|
+
return testPath.replace('__tests__', '__snapshots__') + snapExtension
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Key Points
|
|
195
|
+
|
|
196
|
+
- Commit snapshot files to version control
|
|
197
|
+
- Review snapshot changes in code review
|
|
198
|
+
- Use hints for multiple snapshots in one test
|
|
199
|
+
- Use `toMatchFileSnapshot` for large outputs (HTML, JSON)
|
|
200
|
+
- Inline snapshots auto-update in test file
|
|
201
|
+
- Use context's `expect` for concurrent tests
|
|
202
|
+
|
|
203
|
+
<!--
|
|
204
|
+
Source references:
|
|
205
|
+
- https://vitest.dev/guide/snapshot.html
|
|
206
|
+
- https://vitest.dev/api/expect.html#tomatchsnapshot
|
|
207
|
+
-->
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Worktree Skill
|
|
2
|
+
|
|
3
|
+
> Triggered by: "create worktree", "new worktree", "worktree for", "worktree"
|
|
4
|
+
|
|
5
|
+
## What This Does
|
|
6
|
+
|
|
7
|
+
Creates a git worktree for a given branch name inside `.worktrees/` and switches into it.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## How to Execute
|
|
12
|
+
|
|
13
|
+
When the user asks for a worktree named `<branch-name>`:
|
|
14
|
+
|
|
15
|
+
### Step 1: Create the worktree
|
|
16
|
+
|
|
17
|
+
If the branch already exists locally or on the remote:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git worktree add .worktrees/<branch-name> <branch-name>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If the branch does NOT exist yet, create it from the current HEAD:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git worktree add .worktrees/<branch-name> -b <branch-name>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Step 2: Change into the worktree directory
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd .worktrees/<branch-name>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
All subsequent commands in the session should run from this directory.
|
|
36
|
+
|
|
37
|
+
### Step 3: Confirm
|
|
38
|
+
|
|
39
|
+
Print the current working directory and branch to confirm:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pwd && git branch --show-current
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Notes
|
|
48
|
+
|
|
49
|
+
- Worktrees live in `.worktrees/` at the repo root — this directory is already gitignored.
|
|
50
|
+
- The branch name comes from the user's request (e.g., "create worktree comments-redo" → branch is `comments-redo`).
|
|
51
|
+
- If the worktree already exists, inform the user and `cd` into it instead of recreating it.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repository": {
|
|
3
|
+
"owner": "",
|
|
4
|
+
"name": ""
|
|
5
|
+
},
|
|
6
|
+
"modes": {
|
|
7
|
+
"enabled": false
|
|
8
|
+
},
|
|
9
|
+
"comments": {
|
|
10
|
+
"discussions": {
|
|
11
|
+
"category": "Comments"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"plugins": {
|
|
15
|
+
"devtools": true
|
|
16
|
+
},
|
|
17
|
+
"workshop": {
|
|
18
|
+
"enabled": false,
|
|
19
|
+
"features": {
|
|
20
|
+
"createPrototype": true,
|
|
21
|
+
"createFlow": true,
|
|
22
|
+
"createCanvas": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"featureFlags": {}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Renders a standalone toolbar button whose items come from the
|
|
5
5
|
command action registry (via getActionChildren). This is the
|
|
6
6
|
standalone equivalent of CommandMenu's submenu rendering — any
|
|
7
|
-
menu declared in
|
|
7
|
+
menu declared in toolbar.config.json with an "action" reference
|
|
8
8
|
gets rendered by this component.
|
|
9
9
|
|
|
10
10
|
Supports child types: default, toggle, radio.
|
package/src/CoreUIBar.svelte
CHANGED
|
@@ -17,13 +17,16 @@
|
|
|
17
17
|
import * as Tooltip from './lib/components/ui/tooltip/index.js'
|
|
18
18
|
import Icon from './svelte-plugin-ui/components/Icon.svelte'
|
|
19
19
|
import { modeState } from './svelte-plugin-ui/stores/modeStore.js'
|
|
20
|
-
import { sidePanelState, togglePanel } from './stores/sidePanelStore.js'
|
|
20
|
+
import { sidePanelState, togglePanel, openPanel } from './stores/sidePanelStore.js'
|
|
21
21
|
import { initCommandActions, registerCommandAction, getActionChildren, isExcludedByRoute, setRoutingBasePath } from './commandActions.js'
|
|
22
22
|
import { isMenuHidden } from './uiConfig.js'
|
|
23
|
-
import
|
|
23
|
+
import defaultToolbarConfig from '../toolbar.config.json'
|
|
24
24
|
|
|
25
|
-
interface Props { basePath?: string }
|
|
26
|
-
let { basePath = '/' }: Props = $props()
|
|
25
|
+
interface Props { basePath?: string; toolbarConfig?: any }
|
|
26
|
+
let { basePath = '/', toolbarConfig }: Props = $props()
|
|
27
|
+
|
|
28
|
+
// Use provided config (merged by mountStoryboardCore) or fall back to defaults
|
|
29
|
+
const config = $derived(toolbarConfig || defaultToolbarConfig)
|
|
27
30
|
|
|
28
31
|
let visible = $state(true)
|
|
29
32
|
// Hide the entire toolbar when loaded inside a prototype embed iframe
|
|
@@ -45,7 +48,7 @@
|
|
|
45
48
|
let canvasActive = $state(false)
|
|
46
49
|
let activeCanvasName = $state('')
|
|
47
50
|
let canvasZoom = $state(100)
|
|
48
|
-
const canvasToolbarConfig = (
|
|
51
|
+
const canvasToolbarConfig = $derived((config as any).canvasToolbar || {})
|
|
49
52
|
|
|
50
53
|
const ZOOM_STEP = 10
|
|
51
54
|
const ZOOM_MIN = 25
|
|
@@ -54,18 +57,18 @@
|
|
|
54
57
|
// Roving tabindex: only one button in the toolbar is tabbable at a time
|
|
55
58
|
let activeToolbarIndex = $state(-1)
|
|
56
59
|
|
|
57
|
-
const commandMenuConfig = isMenuHidden('command') ? null :
|
|
58
|
-
const shortcutsConfig = (
|
|
60
|
+
const commandMenuConfig = $derived(isMenuHidden('command') ? null : config.menus?.command)
|
|
61
|
+
const shortcutsConfig = $derived((config as any).shortcuts || {})
|
|
59
62
|
|
|
60
63
|
// Build ordered menu list from JSON key order (excluding command, which is always rightmost)
|
|
61
|
-
const allMenus = (
|
|
62
|
-
const orderedMenus = Object.entries(allMenus)
|
|
64
|
+
const allMenus = $derived((config.menus || {}) as Record<string, any>)
|
|
65
|
+
const orderedMenus = $derived(Object.entries(allMenus)
|
|
63
66
|
.filter(([key]) => key !== 'command')
|
|
64
67
|
.filter(([key]) => !isMenuHidden(key))
|
|
65
|
-
.map(([key, menu]) => ({ key, ...menu }))
|
|
68
|
+
.map(([key, menu]) => ({ key, ...menu })))
|
|
66
69
|
|
|
67
70
|
// Discover menus with sidepanel property
|
|
68
|
-
const sidepanelMenus = orderedMenus.filter(menu => menu.sidepanel)
|
|
71
|
+
const sidepanelMenus = $derived(orderedMenus.filter(menu => menu.sidepanel))
|
|
69
72
|
|
|
70
73
|
function menuVisibleInMode(menu: any, mode: string): boolean {
|
|
71
74
|
if (!menu?.modes) return false
|
|
@@ -215,6 +218,14 @@
|
|
|
215
218
|
})
|
|
216
219
|
}
|
|
217
220
|
|
|
221
|
+
// Auto-open inspector panel if ?inspect= param is in the URL
|
|
222
|
+
try {
|
|
223
|
+
const inspectParam = new URL(window.location.href).searchParams.get('inspect')
|
|
224
|
+
if (inspectParam) {
|
|
225
|
+
openPanel('inspector')
|
|
226
|
+
}
|
|
227
|
+
} catch {}
|
|
228
|
+
|
|
218
229
|
// Register devtools submenu (show flow info, reset params, hide mode, logout)
|
|
219
230
|
{
|
|
220
231
|
let loader: any = null
|
|
@@ -552,7 +563,7 @@
|
|
|
552
563
|
</div>
|
|
553
564
|
{/if}
|
|
554
565
|
|
|
555
|
-
{#if SidePanel}
|
|
566
|
+
{#if !isEmbed && SidePanel}
|
|
556
567
|
<SidePanel onClose={() => focusToolbarItem(activeToolbarIndex < 0 ? toolbarItemCount - 1 : activeToolbarIndex)} />
|
|
557
568
|
{/if}
|
|
558
569
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
CreateMenuButton — config-driven trigger for create/workshop features.
|
|
3
3
|
Appears to the left of the command button in develop (inspect) mode.
|
|
4
|
-
Menu label and items are defined by
|
|
4
|
+
Menu label and items are defined by toolbar.config.json buttons.create section.
|
|
5
5
|
|
|
6
6
|
Icon options (set in config):
|
|
7
7
|
- "icon": octicon name (e.g. "plus", "beaker") — rendered via Octicon.svelte
|