@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.
Files changed (61) hide show
  1. package/dist/storyboard-ui.css +1 -0
  2. package/dist/storyboard-ui.js +26304 -0
  3. package/dist/storyboard-ui.js.map +1 -0
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +24 -18
  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 +23 -12
  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-consumer.js +28 -0
  44. package/src/devtools.js +4 -1
  45. package/src/index.js +8 -3
  46. package/src/inspector/highlighter.js +28 -17
  47. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  48. package/src/lib/components/ui/trigger-button/trigger-button.svelte +8 -4
  49. package/src/mountStoryboardCore.js +223 -0
  50. package/src/scaffold.js +100 -0
  51. package/src/stores/themeStore.ts +29 -8
  52. package/src/styles/tailwind.css +16 -0
  53. package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
  54. package/src/ui-entry.js +30 -0
  55. package/src/vite/server-plugin.js +8 -24
  56. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
  57. package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
  58. package/src/workshop/features/createFlow/index.js +0 -1
  59. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
  60. package/src/workshop/features/createPrototype/index.js +0 -1
  61. /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 {}
@@ -0,0 +1,4 @@
1
+ {
2
+ "_comment": "Client toolbar overrides. Deep-merged with core defaults. See @dfosco/storyboard-core/toolbar.config.json for all options.",
3
+ "menus": {}
4
+ }
@@ -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 core-ui.config.json with an "action" reference
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.
@@ -20,7 +20,7 @@
20
20
  const widgetTypes = [
21
21
  { type: 'sticky-note', label: 'Sticky Note' },
22
22
  { type: 'markdown', label: 'Markdown' },
23
- { type: 'prototype', label: 'Prototype' },
23
+ { type: 'prototype', label: 'Prototype embed' },
24
24
  ]
25
25
 
26
26
  let menuOpen = $state(false)
@@ -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 coreUIConfig from '../core-ui.config.json'
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 = (coreUIConfig as any).canvasToolbar || {}
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 : coreUIConfig.menus?.command
58
- const shortcutsConfig = (coreUIConfig as any).shortcuts || {}
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 = (coreUIConfig.menus || {}) as Record<string, any>
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 core-ui.config.json buttons.create section.
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