@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
|
@@ -583,3 +583,74 @@
|
|
|
583
583
|
.expandedUrl:hover {
|
|
584
584
|
text-decoration: underline;
|
|
585
585
|
}
|
|
586
|
+
|
|
587
|
+
/* ── PR metadata (state badge + branch refs + diff stats) ──────────── */
|
|
588
|
+
|
|
589
|
+
.prMeta {
|
|
590
|
+
display: flex;
|
|
591
|
+
align-items: center;
|
|
592
|
+
gap: 8px;
|
|
593
|
+
flex-wrap: wrap;
|
|
594
|
+
font-size: 12px;
|
|
595
|
+
color: var(--fgColor-muted, #656d76);
|
|
596
|
+
min-width: 0;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.prStateBadge {
|
|
600
|
+
display: inline-flex;
|
|
601
|
+
align-items: center;
|
|
602
|
+
gap: 6px;
|
|
603
|
+
padding: 5px 10px 5px 9px;
|
|
604
|
+
border-radius: 2em;
|
|
605
|
+
font-size: 12px;
|
|
606
|
+
font-weight: 500;
|
|
607
|
+
line-height: 1;
|
|
608
|
+
color: var(--fgColor-onEmphasis, #ffffff);
|
|
609
|
+
white-space: nowrap;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.prStateOpen { background: var(--bgColor-open-emphasis, #1f883d); }
|
|
613
|
+
.prStateMerged { background: var(--bgColor-done-emphasis, #8250df); }
|
|
614
|
+
.prStateClosed { background: var(--bgColor-closed-emphasis, #cf222e); }
|
|
615
|
+
.prStateDraft {
|
|
616
|
+
background: var(--bgColor-neutral-emphasis, #59636e);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.prBranchRef {
|
|
620
|
+
display: inline-flex;
|
|
621
|
+
align-items: center;
|
|
622
|
+
gap: 4px;
|
|
623
|
+
padding: 2px 6px;
|
|
624
|
+
border-radius: 4px;
|
|
625
|
+
background: var(--bgColor-muted, #f6f8fa);
|
|
626
|
+
color: var(--fgColor-default, #1f2328);
|
|
627
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
628
|
+
font-size: 11px;
|
|
629
|
+
max-width: 240px;
|
|
630
|
+
overflow: hidden;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.prBranchName {
|
|
634
|
+
overflow: hidden;
|
|
635
|
+
text-overflow: ellipsis;
|
|
636
|
+
white-space: nowrap;
|
|
637
|
+
max-width: 96px;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.prBranchArrow {
|
|
641
|
+
color: var(--fgColor-muted, #656d76);
|
|
642
|
+
font-family: -apple-system, system-ui, sans-serif;
|
|
643
|
+
flex-shrink: 0;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.prDiffStat {
|
|
647
|
+
display: inline-flex;
|
|
648
|
+
align-items: center;
|
|
649
|
+
gap: 4px;
|
|
650
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
651
|
+
font-size: 12px;
|
|
652
|
+
font-weight: 600;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.prDiffAdd { color: var(--fgColor-success, #1a7f37); }
|
|
656
|
+
.prDiffDel { color: var(--fgColor-danger, #cf222e); }
|
|
@@ -7,6 +7,7 @@ import { readProp, prototypeEmbedSchema } from './widgetProps.js'
|
|
|
7
7
|
import { getEmbedChromeVars } from './embedTheme.js'
|
|
8
8
|
import { useIframeDevLogs } from './iframeDevLogs.js'
|
|
9
9
|
import { findAllConnectedSplitTargets, getSplitPaneLabel, buildPaneForWidget, buildSplitLayout } from './expandUtils.js'
|
|
10
|
+
import { normalizeLegacyEmbedSrc } from './normalizeLegacyEmbedSrc.js'
|
|
10
11
|
import ExpandedPane from './ExpandedPane.jsx'
|
|
11
12
|
import styles from './PrototypeEmbed.module.css'
|
|
12
13
|
import overlayStyles from './embedOverlay.module.css'
|
|
@@ -46,21 +47,76 @@ function resolveCanvasThemeFromStorage() {
|
|
|
46
47
|
const HEADER_HEIGHT = 37
|
|
47
48
|
|
|
48
49
|
export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdate, resizable }, ref) {
|
|
49
|
-
const
|
|
50
|
+
const rawStoredSrc = readProp(props, 'src', prototypeEmbedSchema)
|
|
51
|
+
const src = normalizeLegacyEmbedSrc(rawStoredSrc)
|
|
50
52
|
const width = readProp(props, 'width', prototypeEmbedSchema) || 800
|
|
51
53
|
const height = readProp(props, 'height', prototypeEmbedSchema) || 600
|
|
52
54
|
const zoom = readProp(props, 'zoom', prototypeEmbedSchema) || 100
|
|
53
55
|
const label = readProp(props, 'label', prototypeEmbedSchema) || src
|
|
54
56
|
|
|
57
|
+
// Self-healing write-back: if normalization changed the src, persist the
|
|
58
|
+
// clean form so the canvas JSONL stops carrying the broken URL. Without
|
|
59
|
+
// this, every render keeps recovering at runtime but the file stays
|
|
60
|
+
// poisoned and any consumer on an older library still sees the bug.
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!onUpdate) return
|
|
63
|
+
if (typeof rawStoredSrc !== 'string' || rawStoredSrc === src) return
|
|
64
|
+
onUpdate({ src })
|
|
65
|
+
}, [rawStoredSrc, src, onUpdate])
|
|
66
|
+
|
|
55
67
|
const basePath = (import.meta.env.BASE_URL || '/').replace(/\/$/, '')
|
|
56
68
|
const baseSegment = basePath.replace(/^\//, '')
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
// Two URLs are derived from `src`:
|
|
70
|
+
// - rawSrc — the iframe URL. In DEV this routes through the isolated
|
|
71
|
+
// prototypes.html entry so a broken prototype's transform/HMR errors
|
|
72
|
+
// stay inside the iframe (see .agents/plans/vite-isolation.md):
|
|
73
|
+
// /MyProto/SignupForm becomes prototypes.html/MyProto/SignupForm.
|
|
74
|
+
// Routing happens in the *pathname* (createBrowserRouter), not in the
|
|
75
|
+
// hash, so the hash and search stay free for storyboard URL state
|
|
76
|
+
// (`?flow=...` and `#key=value` overrides). In PROD it loads the
|
|
77
|
+
// prototype path directly through the canvas SPA (prototypes.html is
|
|
78
|
+
// a build-time isolation artifact that must not leak into deployed URLs).
|
|
79
|
+
// - externalSrc — the URL used by "Open in new tab". Always direct
|
|
80
|
+
// (`${basePath}/<protoPath>`), never prototypes.html, even in dev —
|
|
81
|
+
// opening prototypes.html/... in a fresh tab is a leaky surprise
|
|
82
|
+
// for users navigating from the canvas.
|
|
83
|
+
// External http(s) URLs are left alone in both cases. basePath already
|
|
84
|
+
// carries the /branch--xxx/ prefix on branch deploys, so both work for
|
|
85
|
+
// main and branch deploys alike.
|
|
86
|
+
const { rawSrc, externalSrc } = useMemo(() => {
|
|
87
|
+
if (!src) return { rawSrc: '', externalSrc: '' }
|
|
88
|
+
if (/^https?:\/\//.test(src)) return { rawSrc: src, externalSrc: src }
|
|
60
89
|
const cleaned = src.replace(/^\/branch--[^/]+/, '')
|
|
61
|
-
|
|
62
|
-
if (baseSegment && cleaned.startsWith(
|
|
63
|
-
|
|
90
|
+
let normalized
|
|
91
|
+
if (baseSegment && cleaned.startsWith(basePath)) normalized = cleaned
|
|
92
|
+
else if (baseSegment && cleaned.startsWith(baseSegment)) normalized = `/${cleaned}`
|
|
93
|
+
else normalized = `${basePath}${cleaned}`
|
|
94
|
+
// Strip basePath so we can split path/query/hash cleanly and
|
|
95
|
+
// re-anchor for whichever mode we're in. Any pre-existing #hash on
|
|
96
|
+
// the original src is preserved.
|
|
97
|
+
const withoutBase = baseSegment && normalized.startsWith(basePath)
|
|
98
|
+
? normalized.slice(basePath.length) || '/'
|
|
99
|
+
: normalized
|
|
100
|
+
const hashIdx = withoutBase.indexOf('#')
|
|
101
|
+
const innerHash = hashIdx >= 0 ? withoutBase.slice(hashIdx + 1) : ''
|
|
102
|
+
const pathAndQuery = hashIdx >= 0 ? withoutBase.slice(0, hashIdx) : withoutBase
|
|
103
|
+
const routePath = pathAndQuery.startsWith('/') ? pathAndQuery : `/${pathAndQuery}`
|
|
104
|
+
const suffix = innerHash ? `#${innerHash}` : ''
|
|
105
|
+
// Direct path through the canvas SPA — used in prod for the iframe and
|
|
106
|
+
// always for "Open in new tab".
|
|
107
|
+
const directUrl = `${basePath}${routePath}${suffix}`
|
|
108
|
+
if (import.meta.env.PROD) {
|
|
109
|
+
return { rawSrc: directUrl, externalSrc: directUrl }
|
|
110
|
+
}
|
|
111
|
+
// Dev iframe: prototypes.html with the route as a sub-path. The library
|
|
112
|
+
// middleware (`data-plugin.js`) serves the same HTML shell for
|
|
113
|
+
// `/prototypes.html/*` so a hard reload / open-in-new-tab works. The
|
|
114
|
+
// prototype name (first path segment) narrows getRoutesForProto so a
|
|
115
|
+
// broken sibling prototype can't poison the lazy() chain. Hash and
|
|
116
|
+
// search stay attached so storyboard URL state (`#key=value` overrides,
|
|
117
|
+
// `?flow=name`) survives the redirect through the isolation entry.
|
|
118
|
+
const iframeUrl = `${basePath}/prototypes.html${routePath}${suffix}`
|
|
119
|
+
return { rawSrc: iframeUrl, externalSrc: directUrl }
|
|
64
120
|
}, [src, basePath, baseSegment])
|
|
65
121
|
|
|
66
122
|
const scale = zoom / 100
|
|
@@ -268,7 +324,10 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
268
324
|
function handleMessage(e) {
|
|
269
325
|
if (e.source !== iframeRef.current?.contentWindow) return
|
|
270
326
|
if (e.data?.type !== 'storyboard:embed:navigate') return
|
|
271
|
-
|
|
327
|
+
// Defensive: older consumer embeds reported `/prototypes.html#/route`
|
|
328
|
+
// here (loader path + hash). Normalize to the inner route so we never
|
|
329
|
+
// persist the broken form.
|
|
330
|
+
const newSrc = normalizeLegacyEmbedSrc(e.data.src)
|
|
272
331
|
if (newSrc && newSrc !== src) {
|
|
273
332
|
const originalSrc = readProp(props, 'originalSrc', prototypeEmbedSchema)
|
|
274
333
|
onUpdate?.({ src: newSrc, originalSrc: originalSrc || src })
|
|
@@ -295,7 +354,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
295
354
|
} else if (actionId === 'split-screen') {
|
|
296
355
|
setExpandMode('split')
|
|
297
356
|
} else if (actionId === 'open-external') {
|
|
298
|
-
if (
|
|
357
|
+
if (externalSrc) window.open(externalSrc, '_blank', 'noopener')
|
|
299
358
|
} else if (actionId === 'refresh-frame') {
|
|
300
359
|
const iframe = iframeRef.current
|
|
301
360
|
if (iframe) {
|
|
@@ -310,7 +369,7 @@ export default forwardRef(function PrototypeEmbed({ id: widgetId, props, onUpdat
|
|
|
310
369
|
onUpdate?.({ zoom: Math.max(25, zoom - step) })
|
|
311
370
|
}
|
|
312
371
|
},
|
|
313
|
-
}), [
|
|
372
|
+
}), [externalSrc, zoom, onUpdate])
|
|
314
373
|
|
|
315
374
|
function handlePickRoute(route) {
|
|
316
375
|
onUpdate?.({ src: route })
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import { getEmbedChromeVars } from './embedTheme.js'
|
|
3
|
+
import { normalizeLegacyEmbedSrc } from './normalizeLegacyEmbedSrc.js'
|
|
3
4
|
|
|
4
5
|
describe('getEmbedChromeVars', () => {
|
|
5
6
|
it('follows toolbar theme variants for embed edit chrome', () => {
|
|
@@ -8,3 +9,116 @@ describe('getEmbedChromeVars', () => {
|
|
|
8
9
|
expect(getEmbedChromeVars('dark_dimmed')['--bgColor-default']).toBe('#212830')
|
|
9
10
|
})
|
|
10
11
|
})
|
|
12
|
+
|
|
13
|
+
describe('normalizeLegacyEmbedSrc', () => {
|
|
14
|
+
it('passes through clean canvas-app routes unchanged', () => {
|
|
15
|
+
expect(normalizeLegacyEmbedSrc('/cq-org-enablement')).toBe('/cq-org-enablement')
|
|
16
|
+
expect(normalizeLegacyEmbedSrc('/MyProto/SignupForm')).toBe('/MyProto/SignupForm')
|
|
17
|
+
expect(normalizeLegacyEmbedSrc('/cq-org-enablement#x=1')).toBe('/cq-org-enablement#x=1')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('passes through external http(s) URLs unchanged', () => {
|
|
21
|
+
expect(normalizeLegacyEmbedSrc('https://example.com/foo')).toBe('https://example.com/foo')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('passes through empty/falsy values', () => {
|
|
25
|
+
expect(normalizeLegacyEmbedSrc('')).toBe('')
|
|
26
|
+
expect(normalizeLegacyEmbedSrc(null)).toBe(null)
|
|
27
|
+
expect(normalizeLegacyEmbedSrc(undefined)).toBe(undefined)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('legacy hash-form: /prototypes.html#/<route>', () => {
|
|
31
|
+
it('strips loader prefix and keeps inner route', () => {
|
|
32
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html#/cq-org-enablement')).toBe('/cq-org-enablement')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('keeps query strings inside inner', () => {
|
|
36
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html#/cq-org-enablement?cqConfirmOpen=true'))
|
|
37
|
+
.toBe('/cq-org-enablement?cqConfirmOpen=true')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('handles non-root basePath', () => {
|
|
41
|
+
expect(normalizeLegacyEmbedSrc('/storyboard/prototypes.html#/MyProto')).toBe('/MyProto')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('returns "/" for empty inner', () => {
|
|
45
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html#')).toBe('/')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('prefixes "/" if inner missing leading slash', () => {
|
|
49
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html#MyProto')).toBe('/MyProto')
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('legacy URLSearchParams-clobbered form (real-world 0.6.13/0.6.14 bug)', () => {
|
|
54
|
+
// Real bug: storyboard's session.writeHash did URLSearchParams.toString()
|
|
55
|
+
// on the entire hash, which URL-encoded the route slash to %2F and turned
|
|
56
|
+
// /MyProto into a key with an empty value. Real persisted srcs from the
|
|
57
|
+
// wild look like /prototypes.html#%2Fcq-org-enablement=&cqConfirmOpen=true
|
|
58
|
+
it('recovers route from URLSearchParams-mangled hash', () => {
|
|
59
|
+
expect(
|
|
60
|
+
normalizeLegacyEmbedSrc('/prototypes.html#%2Fcq-org-enablement=&cqConfirmOpen=true'),
|
|
61
|
+
).toBe('/cq-org-enablement#cqConfirmOpen=true')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('recovers route with no surviving storyboard state', () => {
|
|
65
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html#%2Fcq-org-enablement=')).toBe('/cq-org-enablement')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('recovers route with multiple storyboard params', () => {
|
|
69
|
+
expect(
|
|
70
|
+
normalizeLegacyEmbedSrc(
|
|
71
|
+
'/prototypes.html#%2Fcq-org-enablement=&cqEnabled=null&cqFlashDismissed=null&cqConfirmOpen=true',
|
|
72
|
+
),
|
|
73
|
+
).toBe('/cq-org-enablement#cqEnabled=null&cqFlashDismissed=null&cqConfirmOpen=true')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('recovers nested route', () => {
|
|
77
|
+
expect(
|
|
78
|
+
normalizeLegacyEmbedSrc('/prototypes.html#%2FMyProto%2FSignupForm=&x=1'),
|
|
79
|
+
).toBe('/MyProto/SignupForm#x=1')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('handles non-root basePath', () => {
|
|
83
|
+
expect(
|
|
84
|
+
normalizeLegacyEmbedSrc('/storyboard/prototypes.html#%2FMyProto=&x=1'),
|
|
85
|
+
).toBe('/MyProto#x=1')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('defensive path-form: /prototypes.html/<route>', () => {
|
|
90
|
+
it('strips loader prefix', () => {
|
|
91
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html/MyProto')).toBe('/MyProto')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('handles nested routes', () => {
|
|
95
|
+
expect(normalizeLegacyEmbedSrc('/prototypes.html/MyProto/SignupForm')).toBe('/MyProto/SignupForm')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('handles non-root basePath', () => {
|
|
99
|
+
expect(normalizeLegacyEmbedSrc('/storyboard/prototypes.html/MyProto')).toBe('/MyProto')
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('all-encompassing regression — no legacy shape can produce a /prototypes.html src', () => {
|
|
104
|
+
const cases = [
|
|
105
|
+
'/prototypes.html#/foo',
|
|
106
|
+
'/prototypes.html#/foo?bar=1',
|
|
107
|
+
'/prototypes.html#',
|
|
108
|
+
'/prototypes.html#%2Ffoo=&bar=1',
|
|
109
|
+
'/prototypes.html#%2Ffoo%2Fbar=&x=1',
|
|
110
|
+
'/prototypes.html/foo',
|
|
111
|
+
'/prototypes.html/foo/bar',
|
|
112
|
+
'/storyboard/prototypes.html#/foo',
|
|
113
|
+
'/storyboard/prototypes.html#%2Ffoo=&x=1',
|
|
114
|
+
'/storyboard/prototypes.html/foo',
|
|
115
|
+
]
|
|
116
|
+
for (const input of cases) {
|
|
117
|
+
it(`recovers ${input}`, () => {
|
|
118
|
+
const result = normalizeLegacyEmbedSrc(input)
|
|
119
|
+
expect(result).not.toMatch(/prototypes\.html/)
|
|
120
|
+
expect(result.startsWith('/')).toBe(true)
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -31,18 +31,22 @@ function resolveStorySetUrl(storyId, layout, selected, density, theme) {
|
|
|
31
31
|
const story = getStoryData(storyId)
|
|
32
32
|
if (!story?._storyModule) return ''
|
|
33
33
|
const base = (import.meta.env.BASE_URL || '/').replace(/\/$/, '')
|
|
34
|
+
|
|
35
|
+
// Route through the shared iframe-isolation entry in dev so a broken
|
|
36
|
+
// .story.jsx can't poison the canvas SPA's module graph. The real story
|
|
37
|
+
// route handles it in prod (no isolation middleware in deployed builds).
|
|
34
38
|
const params = new URLSearchParams()
|
|
35
|
-
// Route via the real story page (works in dev AND prod). The dev-only
|
|
36
|
-
// `_storyboard/canvas/isolate-set` middleware doesn't exist in deployed
|
|
37
|
-
// builds, so we mount ComponentSetPage at the story's route with
|
|
38
|
-
// `_sb_component_set` instead. `_sb_embed` keeps the canvas chrome off.
|
|
39
39
|
params.set('_sb_embed', '')
|
|
40
40
|
params.set('_sb_component_set', '')
|
|
41
41
|
if (layout) params.set('layout', layout)
|
|
42
42
|
if (selected) params.set('selected', selected)
|
|
43
43
|
if (density) params.set('density', density)
|
|
44
44
|
if (theme) params.set('theme', theme)
|
|
45
|
+
|
|
45
46
|
const route = story._route || `/components/${storyId}`
|
|
47
|
+
if (import.meta.env.DEV) {
|
|
48
|
+
return `${base}/stories.html${route}?${params}`
|
|
49
|
+
}
|
|
46
50
|
return `${base}${route}?${params}`
|
|
47
51
|
}
|
|
48
52
|
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import { forwardRef, useImperativeHandle, useRef, useCallback, useState, useEffect, useMemo } from 'react'
|
|
12
12
|
import { getStoryData } from '../../../core/index.js'
|
|
13
13
|
import { useThemeState, useThemeSyncTargets } from '../../hooks/useThemeState.js'
|
|
14
|
-
import { getConfig } from '../../../core/stores/configStore.js'
|
|
15
14
|
import { createInspectorHighlighter } from '../../../core/inspector/highlighter.js'
|
|
16
15
|
import Icon from '../../Icon.jsx'
|
|
17
16
|
import WidgetWrapper from './WidgetWrapper.jsx'
|
|
@@ -20,7 +19,6 @@ import { useIframeDevLogs } from './iframeDevLogs.js'
|
|
|
20
19
|
import { findAllConnectedSplitTargets, getSplitPaneLabel, buildPaneForWidget, buildSplitLayout, buildSecondaryIframeUrl } from './expandUtils.js'
|
|
21
20
|
import { useExpandOverride } from './useExpandOverride.js'
|
|
22
21
|
import ExpandedPane from './ExpandedPane.jsx'
|
|
23
|
-
import InlineStoryRenderer from './InlineStoryRenderer.jsx'
|
|
24
22
|
import styles from './StoryWidget.module.css'
|
|
25
23
|
import overlayStyles from './embedOverlay.module.css'
|
|
26
24
|
|
|
@@ -28,30 +26,24 @@ function ComponentIcon({ size = 36 }) {
|
|
|
28
26
|
return <Icon name="iconoir/keyframe" size={size} />
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
function isInlineStoriesEnabled() {
|
|
32
|
-
try {
|
|
33
|
-
if (typeof window !== 'undefined') {
|
|
34
|
-
const params = new URLSearchParams(window.location.search)
|
|
35
|
-
if (params.has('_sb_inline_stories')) {
|
|
36
|
-
const v = params.get('_sb_inline_stories')
|
|
37
|
-
return v !== '0' && v !== 'false'
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
} catch { /* */ }
|
|
41
|
-
try {
|
|
42
|
-
return Boolean(getConfig('canvas')?.inlineStories)
|
|
43
|
-
} catch { return false }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
29
|
function resolveStoryUrl(storyId, exportName, theme) {
|
|
47
30
|
const story = getStoryData(storyId)
|
|
48
31
|
if (!story?._storyModule) return ''
|
|
49
32
|
const base = (import.meta.env.BASE_URL || '/').replace(/\/$/, '')
|
|
33
|
+
|
|
34
|
+
// In dev, route through the isolated `stories.html` entry so a broken
|
|
35
|
+
// .story.jsx's transform/HMR errors stay inside the iframe (the canvas
|
|
36
|
+
// chrome can't be poisoned by a sibling story's module graph). In prod
|
|
37
|
+
// there is no isolation middleware — the real SPA route handles it.
|
|
50
38
|
const params = new URLSearchParams()
|
|
51
|
-
params.set('module', story._storyModule)
|
|
52
39
|
if (exportName) params.set('export', exportName)
|
|
40
|
+
params.set('_sb_embed', '')
|
|
53
41
|
if (theme) params.set('theme', theme)
|
|
54
|
-
|
|
42
|
+
|
|
43
|
+
if (import.meta.env.DEV) {
|
|
44
|
+
return `${base}/stories.html${story._route || `/components/${storyId}`}?${params}`
|
|
45
|
+
}
|
|
46
|
+
return `${base}${story._route || `/components/${storyId}`}?${params}`
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
const _storySourcesCache = {}
|
|
@@ -205,15 +197,10 @@ export default forwardRef(function StoryWidget({ id: widgetId, props, onUpdate,
|
|
|
205
197
|
[storyId, exportName, storyIndexKey, effectiveTheme],
|
|
206
198
|
)
|
|
207
199
|
|
|
208
|
-
const inlineEnabled = isInlineStoriesEnabled()
|
|
209
|
-
|
|
210
|
-
// When paused and not interactive, freeze the iframe src to prevent reloads
|
|
211
|
-
const effectiveSrc = iframeSrc
|
|
212
|
-
|
|
213
200
|
useIframeDevLogs({
|
|
214
201
|
widget: 'StoryWidget',
|
|
215
|
-
loaded:
|
|
216
|
-
src:
|
|
202
|
+
loaded: interactive && !showCode && Boolean(iframeSrc),
|
|
203
|
+
src: iframeSrc,
|
|
217
204
|
})
|
|
218
205
|
|
|
219
206
|
const displayName = exportName ? `${storyId} / ${exportName}` : storyId
|
|
@@ -231,7 +218,7 @@ export default forwardRef(function StoryWidget({ id: widgetId, props, onUpdate,
|
|
|
231
218
|
)
|
|
232
219
|
}
|
|
233
220
|
|
|
234
|
-
if (!
|
|
221
|
+
if (!iframeSrc) {
|
|
235
222
|
return (
|
|
236
223
|
<WidgetWrapper>
|
|
237
224
|
<div className={styles.container} ref={containerRef}>
|
|
@@ -279,17 +266,13 @@ export default forwardRef(function StoryWidget({ id: widgetId, props, onUpdate,
|
|
|
279
266
|
) : (
|
|
280
267
|
<>
|
|
281
268
|
<div className={styles.content}>
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
title={displayName}
|
|
290
|
-
onLoad={(e) => e.target.blur()}
|
|
291
|
-
/>
|
|
292
|
-
)}
|
|
269
|
+
<iframe
|
|
270
|
+
ref={iframeRef}
|
|
271
|
+
src={iframeSrc}
|
|
272
|
+
className={styles.iframe}
|
|
273
|
+
title={displayName}
|
|
274
|
+
onLoad={(e) => e.target.blur()}
|
|
275
|
+
/>
|
|
293
276
|
</div>
|
|
294
277
|
|
|
295
278
|
{!interactive && (
|
|
@@ -55,26 +55,24 @@ export default function TerminalReadWidget({ id, props }) {
|
|
|
55
55
|
const canvasId = getCanvasId()
|
|
56
56
|
if (!canvasId) { setFailed(true); return }
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`${baseUrl}_storyboard/terminal-snapshots/${id}.snapshot.json`,
|
|
68
|
-
`${baseUrl}_storyboard/terminal-snapshots/${canvasId.replace(/\//g, '--')}/${id}.json`,
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
for (const url of urls) {
|
|
58
|
+
const indexUrl = `${baseUrl}_storyboard/terminal-snapshots/agents.snapshot.json`
|
|
59
|
+
const legacyUrl = `${baseUrl}_storyboard/terminal-snapshots/${canvasId.replace(/\//g, '--')}/${id}.json`
|
|
60
|
+
const restUrl = `${baseUrl}_storyboard/canvas/terminal-snapshot/${id}`
|
|
61
|
+
|
|
62
|
+
const tryUrls = isProduction()
|
|
63
|
+
? [{ url: indexUrl, fromIndex: true }, { url: legacyUrl, fromIndex: false }]
|
|
64
|
+
: [{ url: restUrl, fromIndex: false }, { url: indexUrl, fromIndex: true }, { url: legacyUrl, fromIndex: false }]
|
|
65
|
+
|
|
66
|
+
for (const { url, fromIndex } of tryUrls) {
|
|
72
67
|
try {
|
|
73
68
|
const res = await fetch(url)
|
|
74
69
|
if (!res.ok) continue
|
|
75
70
|
const data = await res.json()
|
|
76
71
|
if (cancelled) return
|
|
77
|
-
const
|
|
72
|
+
const entry = fromIndex ? data?.agents?.[id] : data
|
|
73
|
+
if (!entry) continue
|
|
74
|
+
const text = entry.paneContent || entry.content || entry.output || ''
|
|
75
|
+
if (!text) continue
|
|
78
76
|
setContent(text)
|
|
79
77
|
|
|
80
78
|
const converter = await getConverter()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useCallback, useRef, useEffect, useSyncExternalStore } from 'react'
|
|
2
|
-
import
|
|
2
|
+
import Tooltip from '../../Tooltip.jsx'
|
|
3
3
|
import { CheckIcon } from '@primer/octicons-react'
|
|
4
4
|
import { getConnectorConfig, getInteractGate } from './widgetConfig.js'
|
|
5
5
|
import { ICON_REGISTRY } from './widgetIcons.jsx'
|
|
@@ -149,6 +149,21 @@
|
|
|
149
149
|
top: calc(100% + 10px);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/* Invisible hit-area that bridges the 10px gap between the widget and the
|
|
153
|
+
toolbar. Keeps the parent .chromeContainer in :hover (and React mouseenter)
|
|
154
|
+
when the pointer crosses the padding, and — crucially — stops the pointer
|
|
155
|
+
from falling through to widgets stacked underneath, so the toolbar of an
|
|
156
|
+
overlapping widget on top stays interactive. */
|
|
157
|
+
.toolbar::before {
|
|
158
|
+
content: '';
|
|
159
|
+
position: absolute;
|
|
160
|
+
left: 0;
|
|
161
|
+
right: 0;
|
|
162
|
+
bottom: 100%;
|
|
163
|
+
height: 10px;
|
|
164
|
+
pointer-events: auto;
|
|
165
|
+
}
|
|
166
|
+
|
|
152
167
|
/* Trigger dot — positioned in the toolbar, visible at rest */
|
|
153
168
|
.triggerDot {
|
|
154
169
|
width: 6px;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Resolve the effective canvas theme from the core theme store.
|
|
3
3
|
* Respects the canvas-specific theme sync toggle.
|
|
4
4
|
*/
|
|
5
|
-
import { getTheme,
|
|
5
|
+
import { getTheme, getSurfaceSync } from '../../../core/index.js'
|
|
6
6
|
|
|
7
7
|
function resolveSystem() {
|
|
8
8
|
if (typeof window === 'undefined') return 'light'
|
|
@@ -10,7 +10,7 @@ function resolveSystem() {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function resolveCanvasTheme() {
|
|
13
|
-
const sync =
|
|
13
|
+
const sync = getSurfaceSync()
|
|
14
14
|
if (!sync.canvas) return 'light'
|
|
15
15
|
const theme = getTheme()
|
|
16
16
|
return theme === 'system' ? resolveSystem() : theme
|
|
@@ -498,31 +498,31 @@ export function buildSecondaryIframeUrl(widget) {
|
|
|
498
498
|
const exportName = widget.props?.exportName
|
|
499
499
|
if (!storyId) return null
|
|
500
500
|
const storyData = getStoryData(storyId)
|
|
501
|
-
if (storyData?._storyModule)
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
return
|
|
501
|
+
if (!storyData?._storyModule) return null
|
|
502
|
+
const route = storyData._route || `/components/${storyId}`
|
|
503
|
+
const params = new URLSearchParams()
|
|
504
|
+
if (exportName) params.set('export', exportName)
|
|
505
|
+
params.set('_sb_embed', '')
|
|
506
|
+
// Dev: route through the shared isolation entry. Prod: direct SPA route.
|
|
507
|
+
if (import.meta.env.DEV) return `${baseClean}/stories.html${route}?${params}`
|
|
508
|
+
return `${baseClean}${route}?${params}`
|
|
508
509
|
}
|
|
509
510
|
|
|
510
511
|
if (widget.type === 'component-set') {
|
|
511
512
|
const storyId = widget.props?.storyId
|
|
512
513
|
if (!storyId) return null
|
|
513
514
|
const storyData = getStoryData(storyId)
|
|
514
|
-
if (storyData?._storyModule)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
return null
|
|
515
|
+
if (!storyData?._storyModule) return null
|
|
516
|
+
const params = new URLSearchParams()
|
|
517
|
+
params.set('_sb_embed', '')
|
|
518
|
+
params.set('_sb_component_set', '')
|
|
519
|
+
const layout = widget.props?.layout
|
|
520
|
+
if (layout) params.set('layout', layout)
|
|
521
|
+
const selected = widget.props?.selected
|
|
522
|
+
if (selected) params.set('selected', selected)
|
|
523
|
+
const route = storyData._route || `/components/${storyId}`
|
|
524
|
+
if (import.meta.env.DEV) return `${baseClean}/stories.html${route}?${params}`
|
|
525
|
+
return `${baseClean}${route}?${params}`
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
return null
|