@dfosco/storyboard 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/storyboard-ui.css +1 -1
- package/dist/storyboard-ui.js +14128 -10953
- package/dist/storyboard-ui.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/dist/tiny-canvas.css +1 -1
- package/package.json +138 -128
- 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 +41 -0
- package/scaffold/src/library/_app.module.css +2 -0
- package/scaffold/src/library/create.jsx +10 -0
- package/scaffold/src/library/index.jsx +14 -0
- package/scaffold/src/library/mount.jsx +51 -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/canvas/style.css +16 -16
- package/src/core/artifact/operations.js +54 -15
- package/src/core/artifact/schemas/canvas.schema.json +7 -1
- package/src/core/autosync/server.test.js +1 -1
- package/src/core/canvas/hot-pool.js +21 -33
- package/src/core/canvas/materializer.js +39 -13
- package/src/core/canvas/selectedWidgets.js +1 -1
- package/src/core/canvas/server.js +376 -100
- package/src/core/canvas/server.test.js +472 -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/artifact.js +2 -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 +151 -23
- package/src/core/cli/dev.js +23 -35
- package/src/core/cli/index.js +21 -0
- package/src/core/cli/schemas.js +8 -4
- 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/comments/ui/comment-layout.css +14 -14
- 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 +24 -1
- package/src/core/inspector/highlighter.js +1 -1
- package/src/core/inspector/mouseMode.js +2 -2
- 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/modes/modes.css +4 -4
- package/src/core/mountStoryboardCore.js +287 -83
- package/src/core/mountStoryboardCore.test.js +198 -0
- package/src/core/rename-watcher/config.json +1 -1
- package/src/core/rename-watcher/watcher.js +330 -37
- 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/canvasInteractionStore.js +120 -0
- package/src/core/stores/canvasInteractionStore.test.js +126 -0
- package/src/core/stores/configSchema.js +64 -8
- package/src/core/stores/configSchema.test.js +3 -0
- package/src/core/stores/customerModeConfig.js +24 -3
- package/src/core/stores/customerModeConfig.test.js +46 -0
- package/src/core/stores/presentationStore.js +118 -0
- package/src/core/stores/presentationStore.test.js +114 -0
- 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/stores/uiConfig.js +10 -2
- package/src/core/styles/generateThemesCss.js +63 -0
- package/src/core/styles/tailwind.css +150 -94
- package/src/core/styles/workspace-chrome.css +13 -0
- package/src/core/tools/handlers/paletteTheme.js +11 -8
- package/src/core/ui/ActionMenuButton.jsx +2 -1
- package/src/core/ui/AutosyncMenuButton.css +7 -7
- package/src/core/ui/BranchSelect.module.css +5 -5
- package/src/core/ui/CanvasCreateMenu.jsx +1 -19
- package/src/core/ui/ChromeSlot.jsx +92 -0
- package/src/core/ui/ChromeSlot.test.jsx +140 -0
- package/src/core/ui/CoreUIBar.css +5 -5
- package/src/core/ui/CoreUIBar.jsx +170 -142
- package/src/core/ui/InspectorPanel.css +5 -5
- package/src/core/ui/InspectorPanel.jsx +9 -9
- package/src/core/ui/PwaInstallBanner.css +5 -5
- package/src/core/ui/PwaInstallBanner.jsx +5 -5
- package/src/core/ui/ThemeMenuButton.jsx +45 -30
- package/src/core/ui/core-ui-colors.css +28 -66
- package/src/core/ui/sidepanel.css +12 -12
- package/src/core/vite/server-plugin.js +13 -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 +130 -34
- 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 +51 -69
- package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
- package/src/internals/AuthModal/AuthModal.module.css +34 -34
- package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
- package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
- package/src/internals/BranchBar/BranchBar.module.css +4 -4
- package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
- package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
- package/src/internals/CommandPalette/CommandPalette.jsx +41 -29
- package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
- package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
- package/src/internals/CommandPalette/WidgetArtifactDialog.css +16 -16
- package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
- package/src/internals/CommandPalette/command-palette.css +14 -14
- 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/FlowError.module.css +5 -5
- package/src/internals/PrototypeErrorBoundary.module.css +15 -15
- package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +534 -0
- package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
- package/src/internals/Tooltip.jsx +39 -0
- package/src/internals/Viewfinder.jsx +229 -64
- package/src/internals/Viewfinder.module.css +253 -182
- package/src/internals/canvas/CanvasControls.module.css +11 -11
- package/src/internals/canvas/CanvasPage.bridge.test.jsx +11 -37
- package/src/internals/canvas/CanvasPage.jsx +358 -217
- package/src/internals/canvas/CanvasPage.module.css +20 -19
- package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
- package/src/internals/canvas/CanvasToolbar.module.css +11 -11
- package/src/internals/canvas/ConnectorLayer.jsx +39 -2
- package/src/internals/canvas/ConnectorLayer.module.css +10 -10
- package/src/internals/canvas/PageSelector.jsx +15 -1
- package/src/internals/canvas/PageSelector.module.css +41 -27
- package/src/internals/canvas/canvasApi.js +40 -5
- package/src/internals/canvas/canvasTheme.js +22 -111
- 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 +140 -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/CodePenEmbed.jsx +16 -36
- package/src/internals/canvas/widgets/CodePenEmbed.module.css +12 -12
- package/src/internals/canvas/widgets/CropOverlay.module.css +23 -23
- package/src/internals/canvas/widgets/ExpandedPane.module.css +8 -8
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
- package/src/internals/canvas/widgets/FigmaEmbed.jsx +15 -36
- package/src/internals/canvas/widgets/FigmaEmbed.module.css +15 -15
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +5 -5
- package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
- package/src/internals/canvas/widgets/ImageWidget.module.css +3 -3
- package/src/internals/canvas/widgets/LinkPreview.module.css +79 -80
- package/src/internals/canvas/widgets/MarkdownBlock.module.css +31 -31
- package/src/internals/canvas/widgets/PromptWidget.jsx +2 -16
- package/src/internals/canvas/widgets/PromptWidget.module.css +33 -33
- package/src/internals/canvas/widgets/PrototypeEmbed.jsx +92 -17
- package/src/internals/canvas/widgets/PrototypeEmbed.module.css +58 -56
- package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +118 -4
- package/src/internals/canvas/widgets/ResizeHandle.module.css +4 -4
- package/src/internals/canvas/widgets/StorySetWidget.jsx +53 -25
- package/src/internals/canvas/widgets/StoryWidget.jsx +71 -56
- package/src/internals/canvas/widgets/StoryWidget.module.css +22 -20
- package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
- package/src/internals/canvas/widgets/TerminalReadWidget.module.css +4 -4
- package/src/internals/canvas/widgets/TerminalWidget.jsx +56 -45
- package/src/internals/canvas/widgets/TerminalWidget.module.css +10 -10
- package/src/internals/canvas/widgets/TilesWidget.module.css +17 -17
- package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
- package/src/internals/canvas/widgets/WidgetChrome.module.css +76 -61
- package/src/internals/canvas/widgets/WidgetWrapper.module.css +2 -2
- package/src/internals/canvas/widgets/embedOverlay.module.css +14 -4
- package/src/internals/canvas/widgets/embedTheme.js +12 -104
- package/src/internals/canvas/widgets/expandUtils.js +19 -19
- package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
- package/src/internals/canvas/widgets/widgetConfig.js +5 -5
- 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/ComponentSetPage.module.css +14 -14
- package/src/internals/story/StoryPage.jsx +16 -25
- package/src/internals/story/StoryPage.module.css +2 -2
- package/src/internals/vite/data-plugin.js +317 -149
- 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/WebGLContextPool.jsx +0 -291
- package/src/internals/canvas/WebGLContextPool.test.jsx +0 -165
- package/src/internals/canvas/componentIsolate.jsx +0 -144
- package/src/internals/canvas/componentSetIsolate.jsx +0 -300
- package/src/internals/canvas/widgets/ComponentSetWidget.module.css +0 -89
- package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
- package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
- package/src/internals/canvas/widgets/StorySetWidget.module.css +0 -89
- package/src/primer/ThemeSync.jsx +0 -73
- package/src/primer/index.js +0 -1
|
@@ -5,12 +5,27 @@
|
|
|
5
5
|
* Formerly known as Viewfinder — renamed to match the /workspace route.
|
|
6
6
|
*/
|
|
7
7
|
import { useState, useEffect, useRef, useMemo, useCallback, useSyncExternalStore } from 'react'
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
buildPrototypeIndex,
|
|
10
|
+
listStories,
|
|
11
|
+
getStoryData,
|
|
12
|
+
BranchSelect,
|
|
13
|
+
getCustomerModeConfig,
|
|
14
|
+
getConfig,
|
|
15
|
+
isMenuHidden,
|
|
16
|
+
trackRecent,
|
|
17
|
+
subscribeToRecent,
|
|
18
|
+
getRecentSnapshot,
|
|
19
|
+
} from '../core/index.js'
|
|
20
|
+
import { buildBranchArtifactHref, mergeArtifactIndexIntoPrototypeIndex } from '../core/data/artifactIndex.js'
|
|
21
|
+
import { MarkGithubIcon, GitBranchIcon, ChevronDownIcon, ChevronRightIcon, PlusIcon, StarIcon, StarFillIcon, ThreeBarsIcon, XIcon, StackIcon, TrashIcon, ShieldLockIcon, KebabHorizontalIcon, PencilIcon, EyeClosedIcon } from '@primer/octicons-react'
|
|
10
22
|
import { Menu } from '@base-ui/react/menu'
|
|
11
23
|
import { Dialog } from '@base-ui/react/dialog'
|
|
24
|
+
import Tooltip from './Tooltip.jsx'
|
|
12
25
|
import Icon from './Icon.jsx'
|
|
13
26
|
import { useBranches } from './BranchBar/useBranches.js'
|
|
27
|
+
import { useArtifactIndex } from './hooks/useArtifactIndex.js'
|
|
28
|
+
import BranchesDropdown from './BranchesDropdown/BranchesDropdown.jsx'
|
|
14
29
|
import css from './Viewfinder.module.css'
|
|
15
30
|
|
|
16
31
|
/* ─── Theme sync: read toolbar theme from DOM and apply to Primer/BaseUI ─── */
|
|
@@ -96,8 +111,6 @@ function useGitHubUser() {
|
|
|
96
111
|
/* ─── localStorage helpers ─── */
|
|
97
112
|
|
|
98
113
|
const STARRED_KEY = 'sb-workspace-starred'
|
|
99
|
-
const RECENT_KEY = 'sb-workspace-recent'
|
|
100
|
-
const MAX_RECENT = 30
|
|
101
114
|
const GROUP_BY_FOLDERS_KEY = 'sb-workspace-group-folders'
|
|
102
115
|
const COLLAPSED_FOLDERS_KEY = 'sb-workspace-collapsed-folders'
|
|
103
116
|
|
|
@@ -122,7 +135,6 @@ function createLocalStorageStore(key, fallback) {
|
|
|
122
135
|
}
|
|
123
136
|
|
|
124
137
|
const starredStore = createLocalStorageStore(STARRED_KEY, [])
|
|
125
|
-
const recentStore = createLocalStorageStore(RECENT_KEY, [])
|
|
126
138
|
|
|
127
139
|
function useStarred() {
|
|
128
140
|
const raw = useSyncExternalStore(starredStore.subscribe, starredStore.getSnapshot)
|
|
@@ -135,15 +147,61 @@ function useStarred() {
|
|
|
135
147
|
return { starred: new Set(ids), toggle }
|
|
136
148
|
}
|
|
137
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Map a recent store entry `{type, key}` to the composite id used by the
|
|
152
|
+
* workspace `itemMap` (`proto:`, `canvas:`, `component:`).
|
|
153
|
+
*
|
|
154
|
+
* The unified recent store uses `'story'` as the canonical type for
|
|
155
|
+
* component/story artifacts (matches `core/stores/paletteProviders.js`),
|
|
156
|
+
* but the workspace's items use `'component'` for the same things.
|
|
157
|
+
*/
|
|
158
|
+
function recentEntryToItemId(entry) {
|
|
159
|
+
if (!entry || typeof entry.type !== 'string' || typeof entry.key !== 'string') return null
|
|
160
|
+
switch (entry.type) {
|
|
161
|
+
case 'prototype': return `proto:${entry.key}`
|
|
162
|
+
case 'canvas': return `canvas:${entry.key}`
|
|
163
|
+
case 'story':
|
|
164
|
+
case 'component': return `component:${entry.key}`
|
|
165
|
+
default: return `${entry.type}:${entry.key}`
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Map a workspace `item.type` value to the canonical type stored in the
|
|
171
|
+
* recent artifacts store. The workspace uses `'component'` for stories
|
|
172
|
+
* but the recent store (shared with the command palette) uses `'story'`.
|
|
173
|
+
*/
|
|
174
|
+
function itemTypeToRecentType(itemType) {
|
|
175
|
+
if (itemType === 'component') return 'story'
|
|
176
|
+
return itemType
|
|
177
|
+
}
|
|
178
|
+
|
|
138
179
|
function useRecent() {
|
|
139
|
-
const raw = useSyncExternalStore(
|
|
140
|
-
return
|
|
180
|
+
const raw = useSyncExternalStore(subscribeToRecent, getRecentSnapshot)
|
|
181
|
+
return useMemo(() => {
|
|
182
|
+
try {
|
|
183
|
+
const entries = JSON.parse(raw)
|
|
184
|
+
if (!Array.isArray(entries)) return []
|
|
185
|
+
const ids = []
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
const id = recentEntryToItemId(entry)
|
|
188
|
+
if (id) ids.push(id)
|
|
189
|
+
}
|
|
190
|
+
return ids
|
|
191
|
+
} catch {
|
|
192
|
+
return []
|
|
193
|
+
}
|
|
194
|
+
}, [raw])
|
|
141
195
|
}
|
|
142
196
|
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
197
|
+
function trackRecentItem(item) {
|
|
198
|
+
if (!item || !item.id) return
|
|
199
|
+
const colonIdx = item.id.indexOf(':')
|
|
200
|
+
if (colonIdx <= 0) return
|
|
201
|
+
const key = item.id.slice(colonIdx + 1)
|
|
202
|
+
const type = itemTypeToRecentType(item.type)
|
|
203
|
+
if (!type) return
|
|
204
|
+
trackRecent(type, key, item.name || key)
|
|
147
205
|
}
|
|
148
206
|
|
|
149
207
|
/* ─── URL helpers ─── */
|
|
@@ -440,18 +498,26 @@ function DeleteArtifactModal({ item, dirName, basePath, typeLabel, onClose, onDe
|
|
|
440
498
|
|
|
441
499
|
/* ─── Artifact Card ─── */
|
|
442
500
|
|
|
443
|
-
function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted }) {
|
|
444
|
-
const
|
|
501
|
+
function ArtifactCard({ item, basePath, branchBasePath, currentBranch, starred, onToggleStar, onItemDeleted }) {
|
|
502
|
+
const isBranchOnly = !!item.isBranchOnly
|
|
445
503
|
const isExternal = item.isExternal
|
|
504
|
+
const branchOnlyHref = isBranchOnly
|
|
505
|
+
? isExternal
|
|
506
|
+
? item.externalUrl
|
|
507
|
+
: buildBranchArtifactHref(branchBasePath || basePath, item.homeBranch?.folder, item.homeBranch?.route || item.route)
|
|
508
|
+
: null
|
|
509
|
+
const href = item.route ? withBase(basePath, item.route) : '#'
|
|
446
510
|
|
|
447
511
|
const handleClick = () => {
|
|
448
|
-
|
|
512
|
+
if (!isBranchOnly) trackRecentItem(item)
|
|
449
513
|
}
|
|
450
514
|
|
|
451
|
-
const Tag =
|
|
452
|
-
const linkProps =
|
|
453
|
-
? { href:
|
|
454
|
-
:
|
|
515
|
+
const Tag = 'a'
|
|
516
|
+
const linkProps = isBranchOnly
|
|
517
|
+
? { href: branchOnlyHref || '#', target: '_blank', rel: 'noopener noreferrer' }
|
|
518
|
+
: isExternal
|
|
519
|
+
? { href: item.externalUrl, target: '_blank', rel: 'noopener noreferrer' }
|
|
520
|
+
: { href }
|
|
455
521
|
|
|
456
522
|
const authorList = item.author
|
|
457
523
|
? (Array.isArray(item.author) ? item.author : [item.author])
|
|
@@ -463,17 +529,40 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
|
|
|
463
529
|
// Extract dirName from item.id (format: "type:dirName")
|
|
464
530
|
const dirName = item.id.split(':').slice(1).join(':')
|
|
465
531
|
const typeLabel = item.type === 'canvas' ? 'Canvas' : item.type === 'prototype' ? 'Prototype' : 'Component'
|
|
466
|
-
const canEditDelete = item.type === 'canvas' || item.type === 'prototype'
|
|
532
|
+
const canEditDelete = !isBranchOnly && (item.type === 'canvas' || item.type === 'prototype')
|
|
533
|
+
const cardClass = `${css.card}${item.isPrivate ? ' ' + css.cardPrivate : ''}${isBranchOnly ? ' ' + css.branchOnlyCard : ''}`
|
|
467
534
|
|
|
468
535
|
return (
|
|
469
536
|
<>
|
|
470
|
-
<Tag className={
|
|
537
|
+
<Tag className={cardClass} {...linkProps} onClick={handleClick}>
|
|
471
538
|
<div className={css.cardHeader}>
|
|
472
|
-
<
|
|
539
|
+
<div className={css.cardBadgeGroup}>
|
|
540
|
+
<span className={css.cardBadge}>{getTypeLabel(item.type)}</span>
|
|
541
|
+
{isBranchOnly && <span className={css.branchOnlyBadge}>Branch-only</span>}
|
|
542
|
+
{item.isPrivate && !isBranchOnly && (
|
|
543
|
+
<Tooltip text={`${typeLabel} not published, only visible to you`} direction="n">
|
|
544
|
+
<button
|
|
545
|
+
type="button"
|
|
546
|
+
className={css.cardPrivateBadge}
|
|
547
|
+
aria-label={`Private ${typeLabel.toLowerCase()}`}
|
|
548
|
+
onClick={(e) => { e.preventDefault(); e.stopPropagation() }}
|
|
549
|
+
>
|
|
550
|
+
<EyeClosedIcon size={12} />
|
|
551
|
+
</button>
|
|
552
|
+
</Tooltip>
|
|
553
|
+
)}
|
|
554
|
+
</div>
|
|
473
555
|
<div className={css.cardActions}>
|
|
474
|
-
<StarBtn active={starred} onClick={() => onToggleStar(item.id)} inline />
|
|
475
|
-
{item.flows?.length > 1 && <FlowsDropdown flows={item.flows} basePath={basePath} />}
|
|
476
|
-
{item.pages?.length > 1 && <PagesDropdown pages={item.pages} basePath={basePath} />}
|
|
556
|
+
{!isBranchOnly && <StarBtn active={starred} onClick={() => onToggleStar(item.id)} inline />}
|
|
557
|
+
{!isBranchOnly && item.flows?.length > 1 && <FlowsDropdown flows={item.flows} basePath={basePath} />}
|
|
558
|
+
{!isBranchOnly && item.pages?.length > 1 && <PagesDropdown pages={item.pages} basePath={basePath} />}
|
|
559
|
+
<BranchesDropdown
|
|
560
|
+
branches={item.branches || []}
|
|
561
|
+
branchBasePath={branchBasePath}
|
|
562
|
+
currentBranch={currentBranch}
|
|
563
|
+
homeTitle={item.name}
|
|
564
|
+
homeFolder={item.folder}
|
|
565
|
+
/>
|
|
477
566
|
{canEditDelete && (
|
|
478
567
|
<CardActionsMenu
|
|
479
568
|
typeLabel={typeLabel}
|
|
@@ -505,7 +594,7 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
|
|
|
505
594
|
</div>
|
|
506
595
|
</div>
|
|
507
596
|
</Tag>
|
|
508
|
-
{showEdit && (
|
|
597
|
+
{showEdit && !isBranchOnly && (
|
|
509
598
|
<EditArtifactModal
|
|
510
599
|
item={item}
|
|
511
600
|
dirName={dirName}
|
|
@@ -513,7 +602,7 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
|
|
|
513
602
|
onClose={() => setShowEdit(false)}
|
|
514
603
|
/>
|
|
515
604
|
)}
|
|
516
|
-
{showDelete && (
|
|
605
|
+
{showDelete && !isBranchOnly && (
|
|
517
606
|
<DeleteArtifactModal
|
|
518
607
|
item={item}
|
|
519
608
|
dirName={dirName}
|
|
@@ -609,8 +698,12 @@ function PagesDropdown({ pages, basePath }) {
|
|
|
609
698
|
<a
|
|
610
699
|
href={withBase(basePath, page.route)}
|
|
611
700
|
className={css.flowsItemLink}
|
|
701
|
+
title={page.isPrivate ? 'Local-only page — gitignored, dev-only' : undefined}
|
|
612
702
|
>
|
|
613
703
|
{page.name}
|
|
704
|
+
{page.isPrivate && (
|
|
705
|
+
<EyeClosedIcon size={12} className={css.flowsItemPrivateIcon} />
|
|
706
|
+
)}
|
|
614
707
|
</a>
|
|
615
708
|
</Menu.Item>
|
|
616
709
|
))}
|
|
@@ -623,18 +716,37 @@ function PagesDropdown({ pages, basePath }) {
|
|
|
623
716
|
|
|
624
717
|
/* ─── Folder Section ─── */
|
|
625
718
|
|
|
626
|
-
function FolderSection({ folder, collapsed, onToggle, basePath, starred, onToggleStar, onItemDeleted }) {
|
|
719
|
+
function FolderSection({ folder, collapsed, onToggle, basePath, branchBasePath, currentBranch, starred, onToggleStar, onItemDeleted }) {
|
|
720
|
+
const sectionClass = collapsed ? css.folderSectionCollapsed : css.folderSection
|
|
627
721
|
return (
|
|
628
|
-
<section className={
|
|
629
|
-
<
|
|
722
|
+
<section className={`${sectionClass}${folder.isPrivate ? ' ' + css.folderSectionPrivate : ''}`}>
|
|
723
|
+
<div
|
|
724
|
+
className={css.folderHeader}
|
|
725
|
+
role="button"
|
|
726
|
+
tabIndex={0}
|
|
727
|
+
onClick={onToggle}
|
|
728
|
+
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggle() } }}
|
|
729
|
+
>
|
|
630
730
|
<Icon name={collapsed ? 'folder' : 'folder-open'} size={16} className={css.folderIcon} />
|
|
631
731
|
<span className={css.folderName}>{folder.name}</span>
|
|
732
|
+
{folder.isPrivate && (
|
|
733
|
+
<Tooltip text="Folder not published, only visible to you" direction="n">
|
|
734
|
+
<button
|
|
735
|
+
type="button"
|
|
736
|
+
className={css.folderPrivateBadge}
|
|
737
|
+
aria-label="Private folder"
|
|
738
|
+
onClick={(e) => { e.preventDefault(); e.stopPropagation() }}
|
|
739
|
+
>
|
|
740
|
+
<EyeClosedIcon size={12} />
|
|
741
|
+
</button>
|
|
742
|
+
</Tooltip>
|
|
743
|
+
)}
|
|
632
744
|
<span className={css.folderCount}>{folder.items.length}</span>
|
|
633
745
|
<ChevronRightIcon
|
|
634
746
|
size={14}
|
|
635
747
|
className={collapsed ? css.folderChevron : css.folderChevronExpanded}
|
|
636
748
|
/>
|
|
637
|
-
</
|
|
749
|
+
</div>
|
|
638
750
|
{!collapsed && (
|
|
639
751
|
<div className={css.grid}>
|
|
640
752
|
{folder.items.map(item => (
|
|
@@ -642,6 +754,8 @@ function FolderSection({ folder, collapsed, onToggle, basePath, starred, onToggl
|
|
|
642
754
|
key={item.id}
|
|
643
755
|
item={item}
|
|
644
756
|
basePath={basePath}
|
|
757
|
+
branchBasePath={branchBasePath}
|
|
758
|
+
currentBranch={currentBranch}
|
|
645
759
|
starred={starred.has(item.id)}
|
|
646
760
|
onToggleStar={onToggleStar}
|
|
647
761
|
onItemDeleted={onItemDeleted}
|
|
@@ -878,22 +992,30 @@ function CreateForm({ type, onClose, basePath }) {
|
|
|
878
992
|
|
|
879
993
|
/* ─── Create Menu (Dropdown) ─── */
|
|
880
994
|
|
|
881
|
-
function CreateMenu({ onClose, basePath }) {
|
|
995
|
+
export function CreateMenu({ onClose, basePath }) {
|
|
882
996
|
const [activeForm, setActiveForm] = useState(null)
|
|
883
997
|
const [showMore, setShowMore] = useState(false)
|
|
884
998
|
|
|
999
|
+
// Each menu item is enabled by default; a workshop.features.create<X> flag
|
|
1000
|
+
// can opt it out per consumer. Preserves current behavior when no flags are
|
|
1001
|
+
// set, and lets a consumer hide individual artifact types by adding e.g.
|
|
1002
|
+
// { "workshop": { "features": { "createComponent": false } } }
|
|
1003
|
+
// to their storyboard.config.json.
|
|
1004
|
+
const features = getConfig('workshop')?.features || {}
|
|
1005
|
+
const isEnabled = (flag) => features[flag] !== false
|
|
1006
|
+
|
|
885
1007
|
const items = [
|
|
886
|
-
{ icon: <Icon name="canvas" size={18} />, title: 'Canvas', desc: 'Interactive board for prototypes, components, and documents' },
|
|
887
|
-
{ icon: <Icon name="prototype" size={18} />, title: 'Prototype', desc: 'Interactive page flow' },
|
|
888
|
-
{ icon: <Icon name="iconoir/-couple-solid" size={18} />, title: 'Component', desc: 'Reusable component' },
|
|
889
|
-
]
|
|
1008
|
+
{ icon: <Icon name="canvas" size={18} />, title: 'Canvas', desc: 'Interactive board for prototypes, components, and documents', flag: 'createCanvas' },
|
|
1009
|
+
{ icon: <Icon name="prototype" size={18} />, title: 'Prototype', desc: 'Interactive page flow', flag: 'createPrototype' },
|
|
1010
|
+
{ icon: <Icon name="iconoir/-couple-solid" size={18} />, title: 'Component', desc: 'Reusable component', flag: 'createComponent' },
|
|
1011
|
+
].filter(it => isEnabled(it.flag))
|
|
890
1012
|
|
|
891
1013
|
const moreItems = [
|
|
892
|
-
{ title: 'Prototype Flow', desc: 'A flow data file for a prototype', type: 'Flow' },
|
|
893
|
-
{ title: 'Prototype Page', desc: 'A new page inside a prototype', type: 'Page' },
|
|
894
|
-
{ title: 'Object', desc: 'Reusable JSON data fragment', type: 'Object' },
|
|
895
|
-
{ title: 'Record', desc: 'Collection of entries with IDs', type: 'Record' },
|
|
896
|
-
]
|
|
1014
|
+
{ title: 'Prototype Flow', desc: 'A flow data file for a prototype', type: 'Flow', flag: 'createFlow' },
|
|
1015
|
+
{ title: 'Prototype Page', desc: 'A new page inside a prototype', type: 'Page', flag: 'createPage' },
|
|
1016
|
+
{ title: 'Object', desc: 'Reusable JSON data fragment', type: 'Object', flag: 'createObject' },
|
|
1017
|
+
{ title: 'Record', desc: 'Collection of entries with IDs', type: 'Record', flag: 'createRecord' },
|
|
1018
|
+
].filter(it => isEnabled(it.flag))
|
|
897
1019
|
|
|
898
1020
|
if (activeForm) {
|
|
899
1021
|
return (
|
|
@@ -923,7 +1045,7 @@ function CreateMenu({ onClose, basePath }) {
|
|
|
923
1045
|
))}
|
|
924
1046
|
</div>
|
|
925
1047
|
|
|
926
|
-
{!showMore ? (
|
|
1048
|
+
{moreItems.length > 0 && (!showMore ? (
|
|
927
1049
|
<button className={css.moreOptionsBtn} onClick={() => setShowMore(true)}>
|
|
928
1050
|
More options <ChevronDownIcon size={12} />
|
|
929
1051
|
</button>
|
|
@@ -936,7 +1058,7 @@ function CreateMenu({ onClose, basePath }) {
|
|
|
936
1058
|
</button>
|
|
937
1059
|
))}
|
|
938
1060
|
</div>
|
|
939
|
-
)}
|
|
1061
|
+
))}
|
|
940
1062
|
|
|
941
1063
|
<CreateTip />
|
|
942
1064
|
<CreateFooter />
|
|
@@ -956,6 +1078,7 @@ const NAV_ITEMS = [
|
|
|
956
1078
|
]
|
|
957
1079
|
|
|
958
1080
|
const TAB_FILTERS = ['All', 'Recent', 'Starred']
|
|
1081
|
+
const TYPE_MAP = { prototypes: 'prototype', canvases: 'canvas', components: 'component' }
|
|
959
1082
|
|
|
960
1083
|
/* ─── Branch Navigation ─── */
|
|
961
1084
|
|
|
@@ -970,7 +1093,17 @@ function BranchNav({ basePath }) {
|
|
|
970
1093
|
if (isLocalDev) return null
|
|
971
1094
|
if (!branches || branches.length === 0) return null
|
|
972
1095
|
|
|
973
|
-
|
|
1096
|
+
// Ensure `main` is always present and sorted first. CI's `branches.json`
|
|
1097
|
+
// historically lists only `branch--*/` folders, so `main` is missing on
|
|
1098
|
+
// prod deploys — without this the <select> falls back to its first option
|
|
1099
|
+
// (often something like `0.5.0-fix-…`) when the user is actually on main.
|
|
1100
|
+
const branchNames = [
|
|
1101
|
+
'main',
|
|
1102
|
+
...branches
|
|
1103
|
+
.map(b => b.branch)
|
|
1104
|
+
.filter(b => b && b !== 'main')
|
|
1105
|
+
.sort((a, b) => a.localeCompare(b)),
|
|
1106
|
+
]
|
|
974
1107
|
|
|
975
1108
|
const navigate = async (branch) => {
|
|
976
1109
|
if (switching) return
|
|
@@ -1093,6 +1226,8 @@ function WorkspaceImpl({
|
|
|
1093
1226
|
basePath,
|
|
1094
1227
|
title = 'Storyboard',
|
|
1095
1228
|
subtitle,
|
|
1229
|
+
logo,
|
|
1230
|
+
logoIcon = 'iconoir/key-command',
|
|
1096
1231
|
showAllArtifacts = false,
|
|
1097
1232
|
showPrototypes = true,
|
|
1098
1233
|
showCanvases = true,
|
|
@@ -1100,6 +1235,8 @@ function WorkspaceImpl({
|
|
|
1100
1235
|
}) {
|
|
1101
1236
|
const themeAttrs = useToolbarTheme()
|
|
1102
1237
|
const ghUser = useGitHubUser(basePath)
|
|
1238
|
+
const { currentBranch, branchBasePath } = useBranches(basePath)
|
|
1239
|
+
const { artifactIndex } = useArtifactIndex(basePath)
|
|
1103
1240
|
const [settingsOpen, setSettingsOpen] = useState(false)
|
|
1104
1241
|
|
|
1105
1242
|
const handleRemoveToken = useCallback(() => {
|
|
@@ -1129,13 +1266,17 @@ function WorkspaceImpl({
|
|
|
1129
1266
|
const knownRoutes = useMemo(() =>
|
|
1130
1267
|
Object.keys(pageModules)
|
|
1131
1268
|
.map(p => p.replace('/src/prototypes/', '').replace('.jsx', ''))
|
|
1132
|
-
.filter(n => !n.startsWith('_') && n !== 'index' && n !== 'workspace' && n !== 'viewfinder'),
|
|
1269
|
+
.filter(n => !n.startsWith('_') && n !== 'index' && n !== 'workspace' && n !== 'viewfinder' && n !== 'create'),
|
|
1133
1270
|
[pageModules],
|
|
1134
1271
|
)
|
|
1135
1272
|
|
|
1136
1273
|
// canvasIndexTick is an intentional re-render trigger from HMR events
|
|
1137
1274
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1138
|
-
const
|
|
1275
|
+
const localPrototypeIndex = useMemo(() => buildPrototypeIndex(knownRoutes), [knownRoutes, canvasIndexTick])
|
|
1276
|
+
const prototypeIndex = useMemo(
|
|
1277
|
+
() => mergeArtifactIndexIntoPrototypeIndex(localPrototypeIndex, artifactIndex, currentBranch),
|
|
1278
|
+
[localPrototypeIndex, artifactIndex, currentBranch],
|
|
1279
|
+
)
|
|
1139
1280
|
|
|
1140
1281
|
// Build unified items list from all sources
|
|
1141
1282
|
const allItems = useMemo(() => {
|
|
@@ -1152,18 +1293,22 @@ function WorkspaceImpl({
|
|
|
1152
1293
|
: `/${proto.dirName}`
|
|
1153
1294
|
|
|
1154
1295
|
items.push({
|
|
1155
|
-
id: `proto:${proto.dirName}`,
|
|
1296
|
+
id: proto.id || `proto:${proto.dirName}`,
|
|
1156
1297
|
name: proto.name,
|
|
1157
1298
|
type: 'prototype',
|
|
1158
1299
|
author: proto.author,
|
|
1159
1300
|
gitAuthor: proto.gitAuthor,
|
|
1160
1301
|
lastModified: proto.lastModified,
|
|
1161
|
-
route,
|
|
1302
|
+
route: proto.route || route,
|
|
1162
1303
|
isExternal: proto.isExternal,
|
|
1163
1304
|
externalUrl: proto.externalUrl,
|
|
1164
1305
|
folder: proto.folder,
|
|
1165
1306
|
description: proto.description,
|
|
1166
1307
|
flows: proto.flows || [],
|
|
1308
|
+
isPrivate: !!proto.isPrivate,
|
|
1309
|
+
branches: proto.branches || [],
|
|
1310
|
+
isBranchOnly: !!proto.isBranchOnly,
|
|
1311
|
+
homeBranch: proto.homeBranch || null,
|
|
1167
1312
|
})
|
|
1168
1313
|
}
|
|
1169
1314
|
|
|
@@ -1175,18 +1320,22 @@ function WorkspaceImpl({
|
|
|
1175
1320
|
// Canvases (ungrouped + from folders)
|
|
1176
1321
|
const addCanvas = (canvas) => {
|
|
1177
1322
|
items.push({
|
|
1178
|
-
id: `canvas:${canvas.dirName}`,
|
|
1323
|
+
id: canvas.id || `canvas:${canvas.dirName}`,
|
|
1179
1324
|
name: canvas.name,
|
|
1180
1325
|
type: 'canvas',
|
|
1181
1326
|
author: canvas.author,
|
|
1182
1327
|
gitAuthor: canvas.gitAuthor,
|
|
1183
|
-
lastModified: null,
|
|
1328
|
+
lastModified: canvas.lastModified || null,
|
|
1184
1329
|
route: canvas.route,
|
|
1185
1330
|
isExternal: false,
|
|
1186
1331
|
externalUrl: null,
|
|
1187
1332
|
folder: canvas.folder,
|
|
1188
1333
|
description: canvas.description,
|
|
1189
1334
|
pages: canvas.pages || null,
|
|
1335
|
+
isPrivate: !!canvas.isPrivate,
|
|
1336
|
+
branches: canvas.branches || [],
|
|
1337
|
+
isBranchOnly: !!canvas.isBranchOnly,
|
|
1338
|
+
homeBranch: canvas.homeBranch || null,
|
|
1190
1339
|
})
|
|
1191
1340
|
}
|
|
1192
1341
|
|
|
@@ -1200,9 +1349,10 @@ function WorkspaceImpl({
|
|
|
1200
1349
|
for (const name of storyNames) {
|
|
1201
1350
|
const data = getStoryData(name)
|
|
1202
1351
|
if (!data) continue
|
|
1352
|
+
const displayName = (name.split('/').pop() || name).replace(/^~/, '')
|
|
1203
1353
|
items.push({
|
|
1204
1354
|
id: `component:${name}`,
|
|
1205
|
-
name:
|
|
1355
|
+
name: displayName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
|
|
1206
1356
|
type: 'component',
|
|
1207
1357
|
author: null,
|
|
1208
1358
|
gitAuthor: null,
|
|
@@ -1212,6 +1362,7 @@ function WorkspaceImpl({
|
|
|
1212
1362
|
externalUrl: null,
|
|
1213
1363
|
folder: null,
|
|
1214
1364
|
description: null,
|
|
1365
|
+
isPrivate: !!data._isPrivate || name.split('/').includes('drafts'),
|
|
1215
1366
|
})
|
|
1216
1367
|
}
|
|
1217
1368
|
|
|
@@ -1220,7 +1371,10 @@ function WorkspaceImpl({
|
|
|
1220
1371
|
|
|
1221
1372
|
const itemMap = useMemo(() => Object.fromEntries(allItems.map(i => [i.id, i])), [allItems])
|
|
1222
1373
|
|
|
1223
|
-
// Filter nav items based on visibility props
|
|
1374
|
+
// Filter nav items based on visibility props AND `ui.hide` config keys.
|
|
1375
|
+
// `ui.hide` entries take the form `workspace:<navId>` (e.g.
|
|
1376
|
+
// "workspace:components") and let consumers hide individual nav items
|
|
1377
|
+
// without passing component props.
|
|
1224
1378
|
const visibleNavItems = useMemo(() => {
|
|
1225
1379
|
const visibility = {
|
|
1226
1380
|
all: showAllArtifacts,
|
|
@@ -1228,7 +1382,7 @@ function WorkspaceImpl({
|
|
|
1228
1382
|
canvases: showCanvases,
|
|
1229
1383
|
components: showComponents,
|
|
1230
1384
|
}
|
|
1231
|
-
return NAV_ITEMS.filter(nav => visibility[nav.id])
|
|
1385
|
+
return NAV_ITEMS.filter(nav => visibility[nav.id] && !isMenuHidden(`workspace:${nav.id}`))
|
|
1232
1386
|
}, [showAllArtifacts, showPrototypes, showCanvases, showComponents])
|
|
1233
1387
|
|
|
1234
1388
|
// State - default to first visible nav item
|
|
@@ -1253,9 +1407,8 @@ function WorkspaceImpl({
|
|
|
1253
1407
|
const recentIds = useRecent()
|
|
1254
1408
|
|
|
1255
1409
|
// Filter by nav category
|
|
1256
|
-
const typeMap = { prototypes: 'prototype', canvases: 'canvas', components: 'component' }
|
|
1257
1410
|
const navFiltered = useMemo(() => {
|
|
1258
|
-
let filtered = activeNav === 'all' ? allItems : allItems.filter(i => i.type ===
|
|
1411
|
+
let filtered = activeNav === 'all' ? allItems : allItems.filter(i => i.type === TYPE_MAP[activeNav])
|
|
1259
1412
|
if (hiddenItems.size > 0) filtered = filtered.filter(i => !hiddenItems.has(i.id))
|
|
1260
1413
|
return filtered
|
|
1261
1414
|
}, [allItems, activeNav, hiddenItems])
|
|
@@ -1263,15 +1416,14 @@ function WorkspaceImpl({
|
|
|
1263
1416
|
// Filter by tab
|
|
1264
1417
|
const items = useMemo(() => {
|
|
1265
1418
|
if (activeTab === 'Recent') {
|
|
1266
|
-
const ordered = recentIds.map(id => itemMap[id]).filter(
|
|
1419
|
+
const ordered = recentIds.map(id => itemMap[id]).filter(item => item && !item.isBranchOnly)
|
|
1267
1420
|
if (activeNav !== 'all') {
|
|
1268
|
-
|
|
1269
|
-
return ordered.filter(i => i.type === typeMap[activeNav])
|
|
1421
|
+
return ordered.filter(i => i.type === TYPE_MAP[activeNav])
|
|
1270
1422
|
}
|
|
1271
1423
|
return ordered
|
|
1272
1424
|
}
|
|
1273
1425
|
const base = activeTab === 'Starred'
|
|
1274
|
-
? navFiltered.filter(i => starred.has(i.id))
|
|
1426
|
+
? navFiltered.filter(i => !i.isBranchOnly && starred.has(i.id))
|
|
1275
1427
|
: navFiltered
|
|
1276
1428
|
return [...base].sort((a, b) => {
|
|
1277
1429
|
const aTime = a.lastModified ? new Date(a.lastModified).getTime() : 0
|
|
@@ -1298,6 +1450,7 @@ function WorkspaceImpl({
|
|
|
1298
1450
|
const folders = Object.entries(folderItems).map(([dirName, fItems]) => ({
|
|
1299
1451
|
dirName,
|
|
1300
1452
|
name: folderMeta[dirName]?.name || dirName,
|
|
1453
|
+
isPrivate: !!folderMeta[dirName]?.isPrivate || dirName === 'drafts',
|
|
1301
1454
|
items: fItems,
|
|
1302
1455
|
}))
|
|
1303
1456
|
folders.sort((a, b) => {
|
|
@@ -1343,7 +1496,7 @@ function WorkspaceImpl({
|
|
|
1343
1496
|
}), [visibleItems])
|
|
1344
1497
|
|
|
1345
1498
|
// Starred items for sidebar
|
|
1346
|
-
const starredItems = useMemo(() => visibleItems.filter(i => starred.has(i.id)), [visibleItems, starred])
|
|
1499
|
+
const starredItems = useMemo(() => visibleItems.filter(i => !i.isBranchOnly && starred.has(i.id)), [visibleItems, starred])
|
|
1347
1500
|
|
|
1348
1501
|
return (
|
|
1349
1502
|
<div className={css.layout} {...themeAttrs}>
|
|
@@ -1357,7 +1510,9 @@ function WorkspaceImpl({
|
|
|
1357
1510
|
>
|
|
1358
1511
|
{sidebarOpen ? <XIcon size={18} /> : <ThreeBarsIcon size={18} />}
|
|
1359
1512
|
</button>
|
|
1360
|
-
|
|
1513
|
+
{logo
|
|
1514
|
+
? <div className={`${css.logo} smooth-corners`}>{logo}</div>
|
|
1515
|
+
: <div className={`${css.logo} smooth-corners`}><Icon name={logoIcon} size={22} color="#fff" /></div>}
|
|
1361
1516
|
<div>
|
|
1362
1517
|
<div className={css.appName}>{title}</div>
|
|
1363
1518
|
{subtitle && <div className={css.appSubtitle}>{subtitle}</div>}
|
|
@@ -1413,7 +1568,7 @@ function WorkspaceImpl({
|
|
|
1413
1568
|
className={css.starredItem}
|
|
1414
1569
|
href={s.isExternal ? s.externalUrl : withBase(basePath, s.route)}
|
|
1415
1570
|
{...(s.isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
|
|
1416
|
-
onClick={() =>
|
|
1571
|
+
onClick={() => trackRecentItem(s)}
|
|
1417
1572
|
>
|
|
1418
1573
|
<span className={css.starredIcon}>{getTypeIcon(s.type)}</span>
|
|
1419
1574
|
{s.name}
|
|
@@ -1499,6 +1654,8 @@ function WorkspaceImpl({
|
|
|
1499
1654
|
collapsed={collapsedFolders.has(folder.dirName)}
|
|
1500
1655
|
onToggle={() => toggleFolder(folder.dirName)}
|
|
1501
1656
|
basePath={basePath}
|
|
1657
|
+
branchBasePath={branchBasePath}
|
|
1658
|
+
currentBranch={currentBranch}
|
|
1502
1659
|
starred={starred}
|
|
1503
1660
|
onToggleStar={toggleStar}
|
|
1504
1661
|
onItemDeleted={handleItemDeleted}
|
|
@@ -1511,6 +1668,8 @@ function WorkspaceImpl({
|
|
|
1511
1668
|
key={item.id}
|
|
1512
1669
|
item={item}
|
|
1513
1670
|
basePath={basePath}
|
|
1671
|
+
branchBasePath={branchBasePath}
|
|
1672
|
+
currentBranch={currentBranch}
|
|
1514
1673
|
starred={starred.has(item.id)}
|
|
1515
1674
|
onToggleStar={toggleStar}
|
|
1516
1675
|
onItemDeleted={handleItemDeleted}
|
|
@@ -1526,6 +1685,8 @@ function WorkspaceImpl({
|
|
|
1526
1685
|
key={item.id}
|
|
1527
1686
|
item={item}
|
|
1528
1687
|
basePath={basePath}
|
|
1688
|
+
branchBasePath={branchBasePath}
|
|
1689
|
+
currentBranch={currentBranch}
|
|
1529
1690
|
starred={starred.has(item.id)}
|
|
1530
1691
|
onToggleStar={toggleStar}
|
|
1531
1692
|
onItemDeleted={handleItemDeleted}
|
|
@@ -1543,11 +1704,15 @@ function WorkspaceImpl({
|
|
|
1543
1704
|
/**
|
|
1544
1705
|
* Workspace wrapper — short-circuits to an empty page when customer mode
|
|
1545
1706
|
* has `hideHomepage` enabled, so the workspace dashboard never renders for
|
|
1546
|
-
* end-customers.
|
|
1547
|
-
*
|
|
1707
|
+
* end-customers. Also short-circuits when `canvasHomepage` is set, since
|
|
1708
|
+
* that already replaces the homepage via `history.replaceState` in
|
|
1709
|
+
* `mountStoryboardCore.applyCustomerMode` — rendering the workspace would
|
|
1710
|
+
* cause a flash before the redirect resolves. Wrapping (instead of an early
|
|
1711
|
+
* return inside WorkspaceImpl) keeps the inner component's hooks call order
|
|
1712
|
+
* stable.
|
|
1548
1713
|
*/
|
|
1549
1714
|
export default function Workspace(props) {
|
|
1550
1715
|
const cm = getCustomerModeConfig()
|
|
1551
|
-
if (cm.enabled && cm.hideHomepage) return null
|
|
1716
|
+
if (cm.enabled && (cm.hideHomepage || cm.canvasHomepage)) return null
|
|
1552
1717
|
return <WorkspaceImpl {...props} />
|
|
1553
1718
|
}
|