@dfosco/storyboard 0.7.0 → 0.8.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/assets/favicon.svg +12 -0
- package/dist/storyboard-ui.css +1 -1
- package/dist/storyboard-ui.js +13907 -10816
- package/dist/storyboard-ui.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +138 -127
- package/scaffold/AGENTS.md +9 -7
- package/scaffold/README.md +18 -0
- package/scaffold/deploy.yml +21 -0
- package/scaffold/gitignore +8 -14
- package/scaffold/preview.yml +40 -0
- package/scaffold/{manifest.json → scaffold.config.json} +73 -3
- package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
- package/scaffold/skills/canvas/SKILL.md +1 -1
- package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
- package/scaffold/skills/ship/SKILL.md +2 -2
- package/scaffold/skills/worktree/SKILL.md +1 -1
- package/scaffold/src/_prototype.jsx +17 -0
- package/scaffold/src/_story.jsx +13 -0
- package/scaffold/src/library/README.md +34 -0
- package/scaffold/src/library/_app.jsx +53 -0
- package/scaffold/src/library/_app.module.css +8 -0
- package/scaffold/src/library/create.jsx +10 -0
- package/scaffold/src/library/index.jsx +14 -0
- package/scaffold/src/library/mount.jsx +52 -0
- package/scaffold/src/library/prototypes-entry.jsx +57 -0
- package/scaffold/src/library/routes.jsx +205 -0
- package/scaffold/src/library/viewfinder.jsx +13 -0
- package/scaffold/src/library/workspace.jsx +17 -0
- package/scripts/aggregate-artifacts.mjs +257 -0
- package/src/core/artifact/operations.js +19 -13
- package/src/core/autosync/server.test.js +1 -1
- package/src/core/canvas/githubEmbeds.js +12 -0
- package/src/core/canvas/githubEmbeds.test.js +71 -2
- package/src/core/canvas/hot-pool.js +10 -11
- package/src/core/canvas/materializer.js +39 -13
- package/src/core/canvas/server.js +324 -69
- package/src/core/canvas/server.test.js +365 -2
- package/src/core/canvas/terminal-server.js +144 -57
- package/src/core/canvas/undoRedo.js +174 -0
- package/src/core/canvas/undoRedo.test.js +250 -0
- package/src/core/cli/buildManifest.js +34 -0
- package/src/core/cli/canvasRedo.js +44 -0
- package/src/core/cli/canvasUndo.js +40 -0
- package/src/core/cli/create.js +116 -18
- package/src/core/cli/dev.js +23 -35
- package/src/core/cli/index.js +21 -0
- package/src/core/cli/schemas.js +2 -3
- package/src/core/cli/serverUrl.js +24 -0
- package/src/core/cli/setup.js +14 -25
- package/src/core/cli/terminal-welcome.js +12 -6
- package/src/core/cli/updateVersion.js +42 -2
- package/src/core/cli/workleaf.js +140 -0
- package/src/core/comments/api.js +1 -1
- package/src/core/comments/queries.js +3 -0
- package/src/core/comments/ui/CommentWindow.jsx +3 -2
- package/src/core/data/artifactIndex.js +235 -0
- package/src/core/data/artifactIndex.test.js +203 -0
- package/src/core/data/artifactManifest.js +290 -0
- package/src/core/data/artifactManifest.test.js +207 -0
- package/src/core/data/viewfinder.js +47 -7
- package/src/core/index.js +6 -1
- package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
- package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
- package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
- package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
- package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
- package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
- package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
- package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
- package/src/core/lib/components/ui/dialog/index.js +4 -34
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
- package/src/core/lib/components/ui/label/label.jsx +1 -2
- package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
- package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
- package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
- package/src/core/lib/components/ui/panel/panel.jsx +11 -2
- package/src/core/lib/components/ui/separator/separator.jsx +6 -4
- package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
- package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
- package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
- package/src/core/mountStoryboardCore.js +234 -74
- package/src/core/mountStoryboardCore.test.js +174 -0
- package/src/core/rename-watcher/watcher.js +329 -36
- package/src/core/rename-watcher/watcher.test.js +253 -0
- package/src/core/scaffold/fragments.js +287 -0
- package/src/core/scaffold/fragments.test.js +401 -0
- package/src/core/scaffold/index.js +291 -0
- package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
- package/src/core/scaffold/scaffold-integration.test.js +247 -0
- package/src/core/scaffold.js +4 -97
- package/src/core/stores/configSchema.js +41 -2
- package/src/core/stores/recentArtifacts.js +49 -2
- package/src/core/stores/recentArtifacts.test.js +59 -6
- package/src/core/stores/themeBootstrap.js +264 -0
- package/src/core/stores/themeBootstrap.test.js +211 -0
- package/src/core/stores/themeStore.test.js +154 -0
- package/src/core/stores/themeStore.ts +235 -220
- package/src/core/styles/generateThemesCss.js +63 -0
- package/src/core/styles/tailwind.css +12 -4
- package/src/core/styles/workspace-chrome.css +30 -0
- package/src/core/tools/handlers/paletteTheme.js +11 -8
- package/src/core/ui/ActionMenuButton.jsx +2 -1
- package/src/core/ui/CanvasCreateMenu.jsx +1 -19
- package/src/core/ui/CoreUIBar.jsx +10 -7
- package/src/core/ui/ThemeMenuButton.jsx +45 -30
- package/src/core/vite/server-plugin.js +36 -0
- package/src/core/workleaf/cleanup.js +102 -0
- package/src/core/workleaf/create.js +199 -0
- package/src/core/workleaf/delete.js +101 -0
- package/src/core/workleaf/list.js +57 -0
- package/src/core/workleaf/merge.js +238 -0
- package/src/core/workleaf/paths.js +146 -0
- package/src/core/workleaf/paths.test.js +230 -0
- package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +28 -21
- package/src/core/workshop/features/createFlow/server.js +5 -1
- package/src/core/workshop/features/createPrototype/server.js +5 -1
- package/src/core/workshop/features/templateIndex.js +28 -1
- package/src/core/worktree/port.js +48 -5
- package/src/core/worktree/port.test.js +42 -0
- package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
- package/src/internals/ArtifactForm/ArtifactForm.module.css +48 -66
- package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
- package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
- package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
- package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
- package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
- package/src/internals/CommandPalette/CommandPalette.jsx +25 -19
- package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
- package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
- package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
- package/src/internals/CommandPalette/createHelpers.js +30 -0
- package/src/internals/CreatePage/CreatePage.jsx +57 -0
- package/src/internals/CreatePage/CreatePage.module.css +34 -0
- package/src/internals/Dialog.jsx +51 -0
- package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +533 -0
- package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
- package/src/internals/Tooltip.jsx +39 -0
- package/src/internals/Viewfinder.jsx +196 -48
- package/src/internals/Viewfinder.module.css +72 -1
- package/src/internals/canvas/CanvasPage.jsx +267 -124
- package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
- package/src/internals/canvas/PageSelector.jsx +15 -1
- package/src/internals/canvas/PageSelector.module.css +14 -0
- package/src/internals/canvas/canvasApi.js +40 -5
- package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
- package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
- package/src/internals/canvas/isolation/prototypeRoutes.jsx +131 -0
- package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
- package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
- package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
- package/src/internals/canvas/useUndoRedo.js +75 -60
- package/src/internals/canvas/useUndoRedo.test.js +46 -178
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
- package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
- package/src/internals/canvas/widgets/LinkPreview.jsx +45 -1
- package/src/internals/canvas/widgets/LinkPreview.module.css +71 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.jsx +69 -10
- package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +114 -0
- package/src/internals/canvas/widgets/StorySetWidget.jsx +8 -4
- package/src/internals/canvas/widgets/StoryWidget.jsx +21 -38
- package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
- package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
- package/src/internals/canvas/widgets/WidgetChrome.module.css +15 -0
- package/src/internals/canvas/widgets/embedTheme.js +2 -2
- package/src/internals/canvas/widgets/expandUtils.js +19 -19
- package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
- package/src/internals/context.jsx +22 -1
- package/src/internals/context.test.jsx +47 -0
- package/src/internals/hooks/useArtifactIndex.js +31 -0
- package/src/internals/hooks/useArtifactIndex.test.js +45 -0
- package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
- package/src/internals/index.js +6 -0
- package/src/internals/loadConsumerWrapper.js +60 -0
- package/src/internals/story/ComponentSetPage.jsx +75 -34
- package/src/internals/story/StoryPage.jsx +16 -25
- package/src/internals/vite/data-plugin.js +313 -148
- package/src/internals/vite/data-plugin.test.js +11 -11
- package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
- package/terminal.config.json +1 -1
- package/src/core/lib/components/ui/card/card-action.jsx +0 -11
- package/src/core/lib/components/ui/card/card-content.jsx +0 -11
- package/src/core/lib/components/ui/card/card-description.jsx +0 -11
- package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
- package/src/core/lib/components/ui/card/card-header.jsx +0 -19
- package/src/core/lib/components/ui/card/card-title.jsx +0 -11
- package/src/core/lib/components/ui/card/card.jsx +0 -17
- package/src/core/lib/components/ui/card/index.js +0 -25
- package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
- package/src/core/lib/components/ui/collapsible/index.js +0 -13
- package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
- package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
- package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
- package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
- package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
- package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
- package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
- package/src/core/lib/components/ui/popover/index.js +0 -28
- package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
- package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
- package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
- package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
- package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
- package/src/core/lib/components/ui/popover/popover.jsx +0 -4
- package/src/core/lib/components/ui/select/index.js +0 -37
- package/src/core/lib/components/ui/select/select-content.jsx +0 -30
- package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
- package/src/core/lib/components/ui/select/select-group.jsx +0 -15
- package/src/core/lib/components/ui/select/select-item.jsx +0 -26
- package/src/core/lib/components/ui/select/select-label.jsx +0 -11
- package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
- package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
- package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
- package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
- package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
- package/src/core/lib/components/ui/select/select.jsx +0 -4
- package/src/core/lib/components/ui/sheet/index.js +0 -34
- package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
- package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
- package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
- package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
- package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
- package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
- package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
- package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
- package/src/core/lib/components/ui/toggle/index.js +0 -8
- package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
- package/src/core/lib/components/ui/toggle-group/index.js +0 -10
- package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
- package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
- package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
- package/src/internals/canvas/componentIsolate.jsx +0 -144
- package/src/internals/canvas/componentSetIsolate.jsx +0 -300
- package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
- package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
- package/src/primer/ThemeSync.jsx +0 -73
- package/src/primer/index.js +0 -1
|
@@ -447,25 +447,31 @@ function buildDynamicSection(section, prefix, onNavigateToPage, onCreateAction)
|
|
|
447
447
|
if (!isLocalDev) return null
|
|
448
448
|
const isCanvasRoute = typeof window !== 'undefined' && window.location.pathname.includes('/canvas/')
|
|
449
449
|
if (!isCanvasRoute) return null
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
450
|
+
// 'story' is shown as "Component" — the StoryPicker auto-upgrades
|
|
451
|
+
// multi-export stories to a component-set widget, so we expose a single
|
|
452
|
+
// unified "Component" entry instead of separate Story / Component Set items.
|
|
453
|
+
// The deprecated 'component' widget type and the raw 'component-set' entry
|
|
454
|
+
// are both hidden in favor of this consolidated flow.
|
|
455
|
+
const hiddenTypes = new Set(['link-preview', 'image', 'figma-embed', 'codepen-embed', 'component', 'component-set', 'terminal-read', 'agent'])
|
|
456
|
+
const labelOverrides = { story: 'Component' }
|
|
457
|
+
const items = Object.entries(widgetTypes).filter(([type]) => !hiddenTypes.has(type)).map(([type, def]) => {
|
|
458
|
+
const label = labelOverrides[type] || def.label
|
|
459
|
+
return {
|
|
460
|
+
id: `create-widget:${type}`,
|
|
461
|
+
children: label,
|
|
462
|
+
index_tags: [`Add ${label}`],
|
|
463
|
+
keywords: ['add', 'widget', 'create', type, label.toLowerCase(), def.label.toLowerCase()],
|
|
464
|
+
itemType: type === 'story' ? 'component' : type,
|
|
465
|
+
onClick: () => {
|
|
466
|
+
if (WIDGET_TYPES_WITH_PICKER.has(type)) {
|
|
467
|
+
// Open the artifact picker dialog instead of creating an empty widget.
|
|
468
|
+
document.dispatchEvent(new CustomEvent('storyboard:open-widget-artifact-dialog', { detail: { type } }))
|
|
469
|
+
} else {
|
|
470
|
+
document.dispatchEvent(new CustomEvent('storyboard:canvas:add-widget', { detail: { type } }))
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
}
|
|
474
|
+
})
|
|
469
475
|
|
|
470
476
|
// Build agent submenu from canvas.agents config
|
|
471
477
|
const subPages = []
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CreateArtifactForm — shared form body for the artifact create UI.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `ArtifactForm` with the data-loading, gh-author prefill, payload
|
|
5
|
+
* shaping, and post-submit navigation logic. Used by both the in-app
|
|
6
|
+
* modal (`CreateDialog`) and the standalone full-page route
|
|
7
|
+
* (`CreatePage`). Picker mode (no `type`) is supported — ArtifactForm
|
|
8
|
+
* renders its TypeSelector and the submit handler resolves the schema
|
|
9
|
+
* from the form callback.
|
|
10
|
+
*/
|
|
11
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
12
|
+
import ArtifactForm, { ARTIFACT_SCHEMAS, isFieldVisible } from '../ArtifactForm/ArtifactForm.jsx'
|
|
13
|
+
import { resolveSchemaKey, withBase } from './createHelpers.js'
|
|
14
|
+
|
|
15
|
+
export default function CreateArtifactForm({
|
|
16
|
+
type,
|
|
17
|
+
basePath,
|
|
18
|
+
onClose,
|
|
19
|
+
hideHeader = true,
|
|
20
|
+
}) {
|
|
21
|
+
const schemaKey = resolveSchemaKey(type)
|
|
22
|
+
const isPicker = schemaKey === null
|
|
23
|
+
const fixedSchema = schemaKey ? ARTIFACT_SCHEMAS[schemaKey] : null
|
|
24
|
+
|
|
25
|
+
const [prototypes, setPrototypes] = useState([])
|
|
26
|
+
const [partials, setPartials] = useState([])
|
|
27
|
+
const [ghLogin, setGhLogin] = useState(null)
|
|
28
|
+
|
|
29
|
+
// In picker mode we don't yet know the schema, so load everything that
|
|
30
|
+
// any schema might need. For a fixed type we only load what's required.
|
|
31
|
+
const needsPrototypes = useMemo(() => {
|
|
32
|
+
if (isPicker) return true
|
|
33
|
+
return fixedSchema?.fields.some(f => f.dynamic === 'prototypes') || false
|
|
34
|
+
}, [isPicker, fixedSchema])
|
|
35
|
+
|
|
36
|
+
const needsPartials = useMemo(() => {
|
|
37
|
+
if (isPicker) return true
|
|
38
|
+
return fixedSchema?.fields.some(f => f.dynamic === 'partials') || false
|
|
39
|
+
}, [isPicker, fixedSchema])
|
|
40
|
+
|
|
41
|
+
const needsAuthor = useMemo(() => {
|
|
42
|
+
if (isPicker) return true
|
|
43
|
+
return fixedSchema?.fields.some(f => f.name === 'author') || false
|
|
44
|
+
}, [isPicker, fixedSchema])
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!needsPrototypes) return
|
|
48
|
+
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
49
|
+
fetch(`${apiBase}/_storyboard/artifact/list?type=prototype`)
|
|
50
|
+
.then(r => (r.ok ? r.json() : null))
|
|
51
|
+
.then(data => { if (data?.items) setPrototypes(data.items.map(p => p.name)) })
|
|
52
|
+
.catch(() => {})
|
|
53
|
+
}, [needsPrototypes, basePath])
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!needsPartials) return
|
|
57
|
+
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
58
|
+
fetch(`${apiBase}/_storyboard/workshop/prototypes`)
|
|
59
|
+
.then(r => (r.ok ? r.json() : null))
|
|
60
|
+
.then(data => {
|
|
61
|
+
if (!data?.partials) return
|
|
62
|
+
// Group by scope+kind: "Templates" / "Recipes" for global, "<proto> / Templates" etc. for local.
|
|
63
|
+
const opts = data.partials.map(p => {
|
|
64
|
+
const kindLabel = p.kind === 'recipe' ? 'Recipes' : 'Templates'
|
|
65
|
+
const group = p.scope === 'global'
|
|
66
|
+
? kindLabel
|
|
67
|
+
: `${p.folder ? p.folder + ' / ' : ''}${p.prototype || 'local'} / ${kindLabel}`
|
|
68
|
+
return { value: p.id, label: p.name, group }
|
|
69
|
+
})
|
|
70
|
+
setPartials(opts)
|
|
71
|
+
})
|
|
72
|
+
.catch(() => {})
|
|
73
|
+
}, [needsPartials, basePath])
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!needsAuthor) return
|
|
77
|
+
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
78
|
+
fetch(`${apiBase}/_storyboard/git-user`)
|
|
79
|
+
.then(r => (r.ok ? r.json() : null))
|
|
80
|
+
.then(data => { if (data?.login) setGhLogin(data.login) })
|
|
81
|
+
.catch(() => {})
|
|
82
|
+
}, [needsAuthor, basePath])
|
|
83
|
+
|
|
84
|
+
// Late-arriving gh login is merged into empty fields inside ArtifactForm.
|
|
85
|
+
const initialValues = useMemo(() => {
|
|
86
|
+
if (!needsAuthor || !ghLogin) return undefined
|
|
87
|
+
return { author: ghLogin }
|
|
88
|
+
}, [needsAuthor, ghLogin])
|
|
89
|
+
|
|
90
|
+
if (!isPicker && !fixedSchema) return null
|
|
91
|
+
|
|
92
|
+
async function handleSubmit({ type: t, values }) {
|
|
93
|
+
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
94
|
+
const endpoint = `${apiBase}/_storyboard/artifact/`
|
|
95
|
+
|
|
96
|
+
// In picker mode the schema is decided by the form; resolve it now.
|
|
97
|
+
const submittedSchema = ARTIFACT_SCHEMAS[t] || fixedSchema
|
|
98
|
+
if (!submittedSchema) throw new Error(`Unknown artifact type: ${t}`)
|
|
99
|
+
|
|
100
|
+
const payload = { type: t }
|
|
101
|
+
for (const field of submittedSchema.fields) {
|
|
102
|
+
// Skip UI-only fields (e.g. the `external` toggle on prototype —
|
|
103
|
+
// the server infers external mode from the presence of `url`).
|
|
104
|
+
if (field.uiOnly) continue
|
|
105
|
+
// Skip fields hidden by `showIf` so we never send a stale value
|
|
106
|
+
// for a field the user can't see (e.g. `url` when external is off).
|
|
107
|
+
if (!isFieldVisible(field, values)) continue
|
|
108
|
+
const val = values[field.name]
|
|
109
|
+
if (field.type === 'checkbox') {
|
|
110
|
+
if (val) payload[field.name] = true
|
|
111
|
+
} else if (typeof val === 'string') {
|
|
112
|
+
const trimmed = val.trim()
|
|
113
|
+
if (trimmed) payload[field.name] = trimmed
|
|
114
|
+
} else if (val != null) {
|
|
115
|
+
payload[field.name] = val
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const res = await fetch(endpoint, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
body: JSON.stringify(payload),
|
|
123
|
+
})
|
|
124
|
+
if (!res.ok) {
|
|
125
|
+
const data = await res.json().catch(() => null)
|
|
126
|
+
throw new Error(data?.error || `Request failed (${res.status})`)
|
|
127
|
+
}
|
|
128
|
+
const data = await res.json().catch(() => ({}))
|
|
129
|
+
// Navigate only if the server gave us an explicit route. Types without
|
|
130
|
+
// a route (object/record/component) just close the form.
|
|
131
|
+
if (data.route) {
|
|
132
|
+
window.location.href = withBase(basePath, data.route)
|
|
133
|
+
} else {
|
|
134
|
+
onClose?.()
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<ArtifactForm
|
|
140
|
+
type={isPicker ? undefined : schemaKey}
|
|
141
|
+
onSubmit={handleSubmit}
|
|
142
|
+
onCancel={onClose}
|
|
143
|
+
dynamicOptions={{ prototypes, partials }}
|
|
144
|
+
initialValues={initialValues}
|
|
145
|
+
hideHeader={hideHeader}
|
|
146
|
+
/>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { ARTIFACT_SCHEMAS }
|
|
@@ -1,125 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CreateDialog — Primer Dialog wrapper around
|
|
2
|
+
* CreateDialog — Primer Dialog wrapper around CreateArtifactForm.
|
|
3
3
|
*
|
|
4
|
-
* The form, fields,
|
|
5
|
-
*
|
|
6
|
-
* fetches dynamic select options (e.g. prototype list), POSTs to the
|
|
7
|
-
* artifact API, and navigates to the resulting route on success.
|
|
4
|
+
* The form, fields, validation, data-loading, and submission live in
|
|
5
|
+
* `CreateArtifactForm`. This component just supplies the modal chrome.
|
|
8
6
|
*/
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
// Map UI command-palette type ids (PascalCase) to schema keys (lowercase)
|
|
14
|
-
const TYPE_KEY_MAP = {
|
|
15
|
-
Prototype: 'prototype', Canvas: 'canvas', Component: 'component',
|
|
16
|
-
Flow: 'flow', Object: 'object', Record: 'record', Page: 'page',
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function withBase(base, route) {
|
|
20
|
-
const b = (base || '/').replace(/\/+$/, '')
|
|
21
|
-
const r = route?.startsWith('/') ? route : `/${route || ''}`
|
|
22
|
-
return b === '' || b === '/' ? r : `${b}${r}`
|
|
23
|
-
}
|
|
7
|
+
import Dialog from '../Dialog.jsx'
|
|
8
|
+
import CreateArtifactForm, { ARTIFACT_SCHEMAS } from './CreateArtifactForm.jsx'
|
|
9
|
+
import { resolveSchemaKey, TYPE_KEY_MAP } from './createHelpers.js'
|
|
24
10
|
|
|
25
11
|
export default function CreateDialog({ type, basePath, onClose }) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const [partials, setPartials] = useState([])
|
|
30
|
-
|
|
31
|
-
const needsPrototypes = useMemo(() => {
|
|
32
|
-
if (!schema) return false
|
|
33
|
-
return schema.fields.some(f => f.dynamic === 'prototypes')
|
|
34
|
-
}, [schema])
|
|
12
|
+
// `type === null`/`undefined` means "no dialog requested" — render nothing.
|
|
13
|
+
// Explicit `'Any'` (or empty string) means picker mode.
|
|
14
|
+
if (type == null) return null
|
|
35
15
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}, [schema])
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (!needsPrototypes || !schemaKey) return
|
|
43
|
-
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
44
|
-
fetch(`${apiBase}/_storyboard/artifact/list?type=prototype`)
|
|
45
|
-
.then(r => (r.ok ? r.json() : null))
|
|
46
|
-
.then(data => { if (data?.items) setPrototypes(data.items.map(p => p.name)) })
|
|
47
|
-
.catch(() => {})
|
|
48
|
-
}, [needsPrototypes, schemaKey, basePath])
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (!needsPartials || !schemaKey) return
|
|
52
|
-
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
53
|
-
fetch(`${apiBase}/_storyboard/workshop/prototypes`)
|
|
54
|
-
.then(r => (r.ok ? r.json() : null))
|
|
55
|
-
.then(data => {
|
|
56
|
-
if (!data?.partials) return
|
|
57
|
-
// Map workshop format → grouped select options.
|
|
58
|
-
// Group by scope+kind: "Templates" / "Recipes" for global, "<proto> / Templates" etc. for local.
|
|
59
|
-
const opts = data.partials.map(p => {
|
|
60
|
-
const kindLabel = p.kind === 'recipe' ? 'Recipes' : 'Templates'
|
|
61
|
-
const group = p.scope === 'global'
|
|
62
|
-
? kindLabel
|
|
63
|
-
: `${p.folder ? p.folder + ' / ' : ''}${p.prototype || 'local'} / ${kindLabel}`
|
|
64
|
-
return { value: p.id, label: p.name, group }
|
|
65
|
-
})
|
|
66
|
-
setPartials(opts)
|
|
67
|
-
})
|
|
68
|
-
.catch(() => {})
|
|
69
|
-
}, [needsPartials, schemaKey, basePath])
|
|
70
|
-
|
|
71
|
-
if (!schemaKey || !schema) return null
|
|
72
|
-
|
|
73
|
-
async function handleSubmit({ type: t, values }) {
|
|
74
|
-
const apiBase = (basePath || '/').replace(/\/+$/, '')
|
|
75
|
-
const endpoint = `${apiBase}/_storyboard/artifact/`
|
|
76
|
-
|
|
77
|
-
// Build payload — include checkboxes that are true, trimmed strings, raw selects
|
|
78
|
-
const payload = { type: t }
|
|
79
|
-
for (const field of schema.fields) {
|
|
80
|
-
const val = values[field.name]
|
|
81
|
-
if (field.type === 'checkbox') {
|
|
82
|
-
if (val) payload[field.name] = true
|
|
83
|
-
} else if (typeof val === 'string') {
|
|
84
|
-
const trimmed = val.trim()
|
|
85
|
-
if (trimmed) payload[field.name] = trimmed
|
|
86
|
-
} else if (val != null) {
|
|
87
|
-
payload[field.name] = val
|
|
88
|
-
}
|
|
89
|
-
}
|
|
16
|
+
const schemaKey = resolveSchemaKey(type)
|
|
17
|
+
const isPicker = schemaKey === null
|
|
18
|
+
const schema = schemaKey ? ARTIFACT_SCHEMAS[schemaKey] : null
|
|
90
19
|
|
|
91
|
-
|
|
92
|
-
method: 'POST',
|
|
93
|
-
headers: { 'Content-Type': 'application/json' },
|
|
94
|
-
body: JSON.stringify(payload),
|
|
95
|
-
})
|
|
96
|
-
if (!res.ok) {
|
|
97
|
-
const data = await res.json().catch(() => null)
|
|
98
|
-
throw new Error(data?.error || `Request failed (${res.status})`)
|
|
99
|
-
}
|
|
100
|
-
const data = await res.json().catch(() => ({}))
|
|
101
|
-
// Only navigate when the server gives us an explicit route. Falling back
|
|
102
|
-
// to data.path here is wrong — that's a filesystem path (e.g. "src/canvas"),
|
|
103
|
-
// not a URL route. Types without a route (object/record/component) just close.
|
|
104
|
-
if (data.route) {
|
|
105
|
-
window.location.href = withBase(basePath, data.route)
|
|
106
|
-
} else {
|
|
107
|
-
onClose?.()
|
|
108
|
-
}
|
|
109
|
-
}
|
|
20
|
+
if (!isPicker && !schema) return null
|
|
110
21
|
|
|
111
22
|
return (
|
|
112
23
|
<Dialog
|
|
113
|
-
title={`New ${schema.label}`}
|
|
114
|
-
subtitle={schema.description}
|
|
24
|
+
title={isPicker ? 'New artifact' : `New ${schema.label}`}
|
|
25
|
+
subtitle={isPicker ? 'Pick an artifact type to create.' : schema.description}
|
|
115
26
|
onClose={onClose}
|
|
116
27
|
width="large"
|
|
117
28
|
>
|
|
118
|
-
<
|
|
119
|
-
type={
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
dynamicOptions={{ prototypes, partials }}
|
|
29
|
+
<CreateArtifactForm
|
|
30
|
+
type={type}
|
|
31
|
+
basePath={basePath}
|
|
32
|
+
onClose={onClose}
|
|
123
33
|
hideHeader
|
|
124
34
|
/>
|
|
125
35
|
</Dialog>
|
|
@@ -127,4 +37,4 @@ export default function CreateDialog({ type, basePath, onClose }) {
|
|
|
127
37
|
}
|
|
128
38
|
|
|
129
39
|
// Re-export for canvas/workspace integrations
|
|
130
|
-
export { ARTIFACT_SCHEMAS as CREATE_SCHEMAS }
|
|
40
|
+
export { ARTIFACT_SCHEMAS as CREATE_SCHEMAS, TYPE_KEY_MAP }
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `storyboard:canvas:add-widget` with full props.
|
|
9
9
|
*/
|
|
10
10
|
import { useState, useMemo, useEffect, useRef } from 'react'
|
|
11
|
-
import
|
|
11
|
+
import Dialog from '../Dialog.jsx'
|
|
12
12
|
import { buildPrototypeIndex, listStories, getStoryData } from '../../core/index.js'
|
|
13
13
|
import './WidgetArtifactDialog.css'
|
|
14
14
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map UI command-palette type ids (PascalCase) to schema keys (lowercase),
|
|
3
|
+
* and normalise consumer-supplied `type` values to a schema key.
|
|
4
|
+
*
|
|
5
|
+
* Kept in its own file so React Fast Refresh stays happy in
|
|
6
|
+
* `CreateArtifactForm.jsx` and `CreateDialog.jsx` (component-only modules).
|
|
7
|
+
*/
|
|
8
|
+
export const TYPE_KEY_MAP = {
|
|
9
|
+
Prototype: 'prototype', Canvas: 'canvas', Component: 'component',
|
|
10
|
+
Flow: 'flow', Object: 'object', Record: 'record', Page: 'page',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the schema key (lowercase) for a consumer-supplied type, or
|
|
15
|
+
* `null` when no fixed type was requested (picker mode).
|
|
16
|
+
*/
|
|
17
|
+
export function resolveSchemaKey(type) {
|
|
18
|
+
if (!type || type === 'Any' || String(type).toLowerCase() === 'any') return null
|
|
19
|
+
return TYPE_KEY_MAP[type] || String(type).toLowerCase()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Prepend `base` (e.g. `/storyboard/`) to an artifact route. Used by the
|
|
24
|
+
* post-create navigation in `CreateArtifactForm`.
|
|
25
|
+
*/
|
|
26
|
+
export function withBase(base, route) {
|
|
27
|
+
const b = (base || '/').replace(/\/+$/, '')
|
|
28
|
+
const r = route?.startsWith('/') ? route : `/${route || ''}`
|
|
29
|
+
return b === '' || b === '/' ? r : `${b}${r}`
|
|
30
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CreatePage — full-page route that renders only the schema-driven
|
|
3
|
+
* artifact creation form, centered on a clean background. Used by
|
|
4
|
+
* `storyboard create --ui` to give the user a focused creation surface
|
|
5
|
+
* without the workspace/viewfinder chrome.
|
|
6
|
+
*
|
|
7
|
+
* URL contract:
|
|
8
|
+
* /create → picker (TypeSelector visible)
|
|
9
|
+
* /create?type=canvas → fixed type
|
|
10
|
+
*
|
|
11
|
+
* The component renders as a fixed full-viewport overlay so it covers
|
|
12
|
+
* the chrome (BranchBar, FeatureFlagBanner, CoreUIBar toolbar) that
|
|
13
|
+
* StoryboardProvider mounts globally — even on this route. No close
|
|
14
|
+
* button: this surface is opened by the CLI specifically as the create
|
|
15
|
+
* flow; the user closes it by completing/cancelling the form.
|
|
16
|
+
*
|
|
17
|
+
* After successful create:
|
|
18
|
+
* - If the artifact has a route (prototype, canvas, page) → navigate to it.
|
|
19
|
+
* - Otherwise → navigate back to the workspace (basePath).
|
|
20
|
+
*/
|
|
21
|
+
import { useMemo } from 'react'
|
|
22
|
+
import CreateArtifactForm from '../CommandPalette/CreateArtifactForm.jsx'
|
|
23
|
+
import styles from './CreatePage.module.css'
|
|
24
|
+
|
|
25
|
+
export default function CreatePage({ basePath }) {
|
|
26
|
+
const resolvedBase = basePath || (typeof window !== 'undefined' ? '/' : '/')
|
|
27
|
+
|
|
28
|
+
// Parse `?type=<type>` once. We don't react to changes — switching the
|
|
29
|
+
// type is handled in-form via the TypeSelector when in picker mode.
|
|
30
|
+
const type = useMemo(() => {
|
|
31
|
+
if (typeof window === 'undefined') return null
|
|
32
|
+
const params = new URLSearchParams(window.location.search)
|
|
33
|
+
return params.get('type') || null
|
|
34
|
+
}, [])
|
|
35
|
+
|
|
36
|
+
function handleClose() {
|
|
37
|
+
// Cancel = navigate back to the workspace. Hard navigation so we
|
|
38
|
+
// drop the `?type=…` query and any in-flight form state.
|
|
39
|
+
if (typeof window !== 'undefined') {
|
|
40
|
+
const target = (resolvedBase || '/').replace(/\/+$/, '/') || '/'
|
|
41
|
+
window.location.href = target
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={styles.page}>
|
|
47
|
+
<div className={styles.card}>
|
|
48
|
+
<CreateArtifactForm
|
|
49
|
+
type={type}
|
|
50
|
+
basePath={resolvedBase}
|
|
51
|
+
onClose={handleClose}
|
|
52
|
+
hideHeader={false}
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone create-only page styling.
|
|
3
|
+
*
|
|
4
|
+
* The page is a fixed full-viewport overlay so it covers the chrome
|
|
5
|
+
* (BranchBar, FeatureFlagBanner, CoreUIBar toolbar) that
|
|
6
|
+
* StoryboardProvider mounts globally on every prototype route. A high
|
|
7
|
+
* z-index keeps it above the toolbar specifically.
|
|
8
|
+
*
|
|
9
|
+
* The `.card` wrapper deliberately mirrors `.form` in ArtifactForm.module.css
|
|
10
|
+
* so the card looks identical to the in-app dialog body.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
.page {
|
|
14
|
+
position: fixed;
|
|
15
|
+
inset: 0;
|
|
16
|
+
/* Sits above the BranchBar (10001), CoreUIBar toolbar (9999), and any
|
|
17
|
+
* other dev-overlay chrome StoryboardProvider mounts. The /create
|
|
18
|
+
* route is a focused creation surface — chrome would be a distraction. */
|
|
19
|
+
z-index: 2147483646;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: flex-start;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
padding: 2rem 1.5rem 4rem;
|
|
24
|
+
background: var(--bgColor-muted, #f6f8fa);
|
|
25
|
+
overflow: auto;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.card {
|
|
29
|
+
width: 100%;
|
|
30
|
+
max-width: 480px;
|
|
31
|
+
/* Center horizontally; vertical alignment is flex-start so tall forms
|
|
32
|
+
* scroll naturally instead of clipping the top. */
|
|
33
|
+
margin: auto;
|
|
34
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog — internal adapter that exposes a Primer-style
|
|
3
|
+
* `<Dialog title subtitle onClose width height>` API on top of the
|
|
4
|
+
* framework-neutral core Dialog primitive (`core/lib/components/ui/dialog`).
|
|
5
|
+
*
|
|
6
|
+
* Lets the command palette mount its existing dialogs without pulling in
|
|
7
|
+
* `@primer/react/experimental`. See plan `.github/plans/style-separation.md`
|
|
8
|
+
* (Layer A).
|
|
9
|
+
*
|
|
10
|
+
* Semantics:
|
|
11
|
+
* - The dialog is open while mounted (caller controls visibility by
|
|
12
|
+
* conditionally rendering this component, matching Primer's behaviour).
|
|
13
|
+
* - Backdrop click + ESC fire `onClose`.
|
|
14
|
+
*/
|
|
15
|
+
import * as Dialog from '../core/lib/components/ui/dialog/index.js'
|
|
16
|
+
import css from '../core/lib/components/ui/dialog/dialog.module.css'
|
|
17
|
+
|
|
18
|
+
export default function DialogAdapter({
|
|
19
|
+
title,
|
|
20
|
+
subtitle,
|
|
21
|
+
onClose,
|
|
22
|
+
width = 'medium',
|
|
23
|
+
height = 'auto',
|
|
24
|
+
children,
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<Dialog.Root open onOpenChange={(open) => { if (!open) onClose?.() }}>
|
|
28
|
+
<Dialog.Content width={width} height={height} aria-label={typeof title === 'string' ? title : undefined}>
|
|
29
|
+
{(title || subtitle) && (
|
|
30
|
+
<div className={css.header}>
|
|
31
|
+
<div className={css.titleGroup}>
|
|
32
|
+
{title && <h2 className={css.title}>{title}</h2>}
|
|
33
|
+
{subtitle && <p className={css.subtitle}>{subtitle}</p>}
|
|
34
|
+
</div>
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
className={css.closeBtn}
|
|
38
|
+
onClick={() => onClose?.()}
|
|
39
|
+
aria-label="Close"
|
|
40
|
+
>
|
|
41
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
42
|
+
<path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.75.75 0 1 1 1.06 1.06L9.06 8l3.22 3.22a.75.75 0 1 1-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 0 1-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z" />
|
|
43
|
+
</svg>
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
<div className={css.body}>{children}</div>
|
|
48
|
+
</Dialog.Content>
|
|
49
|
+
</Dialog.Root>
|
|
50
|
+
)
|
|
51
|
+
}
|