@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.
Files changed (258) hide show
  1. package/assets/favicon.svg +12 -0
  2. package/dist/storyboard-ui.css +1 -1
  3. package/dist/storyboard-ui.js +13907 -10816
  4. package/dist/storyboard-ui.js.map +1 -1
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +138 -127
  7. package/scaffold/AGENTS.md +9 -7
  8. package/scaffold/README.md +18 -0
  9. package/scaffold/deploy.yml +21 -0
  10. package/scaffold/gitignore +8 -14
  11. package/scaffold/preview.yml +40 -0
  12. package/scaffold/{manifest.json → scaffold.config.json} +73 -3
  13. package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
  14. package/scaffold/skills/canvas/SKILL.md +1 -1
  15. package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
  16. package/scaffold/skills/ship/SKILL.md +2 -2
  17. package/scaffold/skills/worktree/SKILL.md +1 -1
  18. package/scaffold/src/_prototype.jsx +17 -0
  19. package/scaffold/src/_story.jsx +13 -0
  20. package/scaffold/src/library/README.md +34 -0
  21. package/scaffold/src/library/_app.jsx +53 -0
  22. package/scaffold/src/library/_app.module.css +8 -0
  23. package/scaffold/src/library/create.jsx +10 -0
  24. package/scaffold/src/library/index.jsx +14 -0
  25. package/scaffold/src/library/mount.jsx +52 -0
  26. package/scaffold/src/library/prototypes-entry.jsx +57 -0
  27. package/scaffold/src/library/routes.jsx +205 -0
  28. package/scaffold/src/library/viewfinder.jsx +13 -0
  29. package/scaffold/src/library/workspace.jsx +17 -0
  30. package/scripts/aggregate-artifacts.mjs +257 -0
  31. package/src/core/artifact/operations.js +19 -13
  32. package/src/core/autosync/server.test.js +1 -1
  33. package/src/core/canvas/githubEmbeds.js +12 -0
  34. package/src/core/canvas/githubEmbeds.test.js +71 -2
  35. package/src/core/canvas/hot-pool.js +10 -11
  36. package/src/core/canvas/materializer.js +39 -13
  37. package/src/core/canvas/server.js +324 -69
  38. package/src/core/canvas/server.test.js +365 -2
  39. package/src/core/canvas/terminal-server.js +144 -57
  40. package/src/core/canvas/undoRedo.js +174 -0
  41. package/src/core/canvas/undoRedo.test.js +250 -0
  42. package/src/core/cli/buildManifest.js +34 -0
  43. package/src/core/cli/canvasRedo.js +44 -0
  44. package/src/core/cli/canvasUndo.js +40 -0
  45. package/src/core/cli/create.js +116 -18
  46. package/src/core/cli/dev.js +23 -35
  47. package/src/core/cli/index.js +21 -0
  48. package/src/core/cli/schemas.js +2 -3
  49. package/src/core/cli/serverUrl.js +24 -0
  50. package/src/core/cli/setup.js +14 -25
  51. package/src/core/cli/terminal-welcome.js +12 -6
  52. package/src/core/cli/updateVersion.js +42 -2
  53. package/src/core/cli/workleaf.js +140 -0
  54. package/src/core/comments/api.js +1 -1
  55. package/src/core/comments/queries.js +3 -0
  56. package/src/core/comments/ui/CommentWindow.jsx +3 -2
  57. package/src/core/data/artifactIndex.js +235 -0
  58. package/src/core/data/artifactIndex.test.js +203 -0
  59. package/src/core/data/artifactManifest.js +290 -0
  60. package/src/core/data/artifactManifest.test.js +207 -0
  61. package/src/core/data/viewfinder.js +47 -7
  62. package/src/core/index.js +6 -1
  63. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
  64. package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
  65. package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
  66. package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
  67. package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
  68. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
  69. package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
  70. package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
  71. package/src/core/lib/components/ui/dialog/index.js +4 -34
  72. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
  73. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
  74. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
  75. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
  76. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
  77. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
  78. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
  79. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
  80. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
  81. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
  82. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
  83. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
  84. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
  85. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
  86. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
  87. package/src/core/lib/components/ui/label/label.jsx +1 -2
  88. package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
  89. package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
  90. package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
  91. package/src/core/lib/components/ui/panel/panel.jsx +11 -2
  92. package/src/core/lib/components/ui/separator/separator.jsx +6 -4
  93. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
  94. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
  95. package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
  96. package/src/core/mountStoryboardCore.js +234 -74
  97. package/src/core/mountStoryboardCore.test.js +174 -0
  98. package/src/core/rename-watcher/watcher.js +329 -36
  99. package/src/core/rename-watcher/watcher.test.js +253 -0
  100. package/src/core/scaffold/fragments.js +287 -0
  101. package/src/core/scaffold/fragments.test.js +401 -0
  102. package/src/core/scaffold/index.js +291 -0
  103. package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
  104. package/src/core/scaffold/scaffold-integration.test.js +247 -0
  105. package/src/core/scaffold.js +4 -97
  106. package/src/core/stores/configSchema.js +41 -2
  107. package/src/core/stores/recentArtifacts.js +49 -2
  108. package/src/core/stores/recentArtifacts.test.js +59 -6
  109. package/src/core/stores/themeBootstrap.js +264 -0
  110. package/src/core/stores/themeBootstrap.test.js +211 -0
  111. package/src/core/stores/themeStore.test.js +154 -0
  112. package/src/core/stores/themeStore.ts +235 -220
  113. package/src/core/styles/generateThemesCss.js +63 -0
  114. package/src/core/styles/tailwind.css +12 -4
  115. package/src/core/styles/workspace-chrome.css +30 -0
  116. package/src/core/tools/handlers/paletteTheme.js +11 -8
  117. package/src/core/ui/ActionMenuButton.jsx +2 -1
  118. package/src/core/ui/CanvasCreateMenu.jsx +1 -19
  119. package/src/core/ui/CoreUIBar.jsx +10 -7
  120. package/src/core/ui/ThemeMenuButton.jsx +45 -30
  121. package/src/core/vite/server-plugin.js +36 -0
  122. package/src/core/workleaf/cleanup.js +102 -0
  123. package/src/core/workleaf/create.js +199 -0
  124. package/src/core/workleaf/delete.js +101 -0
  125. package/src/core/workleaf/list.js +57 -0
  126. package/src/core/workleaf/merge.js +238 -0
  127. package/src/core/workleaf/paths.js +146 -0
  128. package/src/core/workleaf/paths.test.js +230 -0
  129. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +28 -21
  130. package/src/core/workshop/features/createFlow/server.js +5 -1
  131. package/src/core/workshop/features/createPrototype/server.js +5 -1
  132. package/src/core/workshop/features/templateIndex.js +28 -1
  133. package/src/core/worktree/port.js +48 -5
  134. package/src/core/worktree/port.test.js +42 -0
  135. package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
  136. package/src/internals/ArtifactForm/ArtifactForm.module.css +48 -66
  137. package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
  138. package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
  139. package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
  140. package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
  141. package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
  142. package/src/internals/CommandPalette/CommandPalette.jsx +25 -19
  143. package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
  144. package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
  145. package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
  146. package/src/internals/CommandPalette/createHelpers.js +30 -0
  147. package/src/internals/CreatePage/CreatePage.jsx +57 -0
  148. package/src/internals/CreatePage/CreatePage.module.css +34 -0
  149. package/src/internals/Dialog.jsx +51 -0
  150. package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +533 -0
  151. package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
  152. package/src/internals/Tooltip.jsx +39 -0
  153. package/src/internals/Viewfinder.jsx +196 -48
  154. package/src/internals/Viewfinder.module.css +72 -1
  155. package/src/internals/canvas/CanvasPage.jsx +267 -124
  156. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
  157. package/src/internals/canvas/PageSelector.jsx +15 -1
  158. package/src/internals/canvas/PageSelector.module.css +14 -0
  159. package/src/internals/canvas/canvasApi.js +40 -5
  160. package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
  161. package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
  162. package/src/internals/canvas/isolation/prototypeRoutes.jsx +131 -0
  163. package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
  164. package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
  165. package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
  166. package/src/internals/canvas/useUndoRedo.js +75 -60
  167. package/src/internals/canvas/useUndoRedo.test.js +46 -178
  168. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
  169. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
  170. package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
  171. package/src/internals/canvas/widgets/LinkPreview.jsx +45 -1
  172. package/src/internals/canvas/widgets/LinkPreview.module.css +71 -0
  173. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +69 -10
  174. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +114 -0
  175. package/src/internals/canvas/widgets/StorySetWidget.jsx +8 -4
  176. package/src/internals/canvas/widgets/StoryWidget.jsx +21 -38
  177. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
  178. package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
  179. package/src/internals/canvas/widgets/WidgetChrome.module.css +15 -0
  180. package/src/internals/canvas/widgets/embedTheme.js +2 -2
  181. package/src/internals/canvas/widgets/expandUtils.js +19 -19
  182. package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
  183. package/src/internals/context.jsx +22 -1
  184. package/src/internals/context.test.jsx +47 -0
  185. package/src/internals/hooks/useArtifactIndex.js +31 -0
  186. package/src/internals/hooks/useArtifactIndex.test.js +45 -0
  187. package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
  188. package/src/internals/index.js +6 -0
  189. package/src/internals/loadConsumerWrapper.js +60 -0
  190. package/src/internals/story/ComponentSetPage.jsx +75 -34
  191. package/src/internals/story/StoryPage.jsx +16 -25
  192. package/src/internals/vite/data-plugin.js +313 -148
  193. package/src/internals/vite/data-plugin.test.js +11 -11
  194. package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
  195. package/terminal.config.json +1 -1
  196. package/src/core/lib/components/ui/card/card-action.jsx +0 -11
  197. package/src/core/lib/components/ui/card/card-content.jsx +0 -11
  198. package/src/core/lib/components/ui/card/card-description.jsx +0 -11
  199. package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
  200. package/src/core/lib/components/ui/card/card-header.jsx +0 -19
  201. package/src/core/lib/components/ui/card/card-title.jsx +0 -11
  202. package/src/core/lib/components/ui/card/card.jsx +0 -17
  203. package/src/core/lib/components/ui/card/index.js +0 -25
  204. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
  205. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
  206. package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
  207. package/src/core/lib/components/ui/collapsible/index.js +0 -13
  208. package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
  209. package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
  210. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
  211. package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
  212. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
  213. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
  214. package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
  215. package/src/core/lib/components/ui/popover/index.js +0 -28
  216. package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
  217. package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
  218. package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
  219. package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
  220. package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
  221. package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
  222. package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
  223. package/src/core/lib/components/ui/popover/popover.jsx +0 -4
  224. package/src/core/lib/components/ui/select/index.js +0 -37
  225. package/src/core/lib/components/ui/select/select-content.jsx +0 -30
  226. package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
  227. package/src/core/lib/components/ui/select/select-group.jsx +0 -15
  228. package/src/core/lib/components/ui/select/select-item.jsx +0 -26
  229. package/src/core/lib/components/ui/select/select-label.jsx +0 -11
  230. package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
  231. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
  232. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
  233. package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
  234. package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
  235. package/src/core/lib/components/ui/select/select.jsx +0 -4
  236. package/src/core/lib/components/ui/sheet/index.js +0 -34
  237. package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
  238. package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
  239. package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
  240. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
  241. package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
  242. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
  243. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
  244. package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
  245. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
  246. package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
  247. package/src/core/lib/components/ui/toggle/index.js +0 -8
  248. package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
  249. package/src/core/lib/components/ui/toggle-group/index.js +0 -10
  250. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
  251. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
  252. package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
  253. package/src/internals/canvas/componentIsolate.jsx +0 -144
  254. package/src/internals/canvas/componentSetIsolate.jsx +0 -300
  255. package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
  256. package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
  257. package/src/primer/ThemeSync.jsx +0 -73
  258. package/src/primer/index.js +0 -1
@@ -5,12 +5,25 @@
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 { buildPrototypeIndex, listStories, getStoryData, BranchSelect, getCustomerModeConfig } from '../core/index.js'
9
- import { MarkGithubIcon, GitBranchIcon, ChevronDownIcon, ChevronRightIcon, PlusIcon, StarIcon, StarFillIcon, ThreeBarsIcon, XIcon, StackIcon, TrashIcon, ShieldLockIcon, KebabHorizontalIcon, PencilIcon } from '@primer/octicons-react'
8
+ import {
9
+ buildPrototypeIndex,
10
+ listStories,
11
+ getStoryData,
12
+ BranchSelect,
13
+ getCustomerModeConfig,
14
+ trackRecent,
15
+ subscribeToRecent,
16
+ getRecentSnapshot,
17
+ } from '../core/index.js'
18
+ import { buildBranchArtifactHref, mergeArtifactIndexIntoPrototypeIndex } from '../core/data/artifactIndex.js'
19
+ import { MarkGithubIcon, GitBranchIcon, ChevronDownIcon, ChevronRightIcon, PlusIcon, StarIcon, StarFillIcon, ThreeBarsIcon, XIcon, StackIcon, TrashIcon, ShieldLockIcon, KebabHorizontalIcon, PencilIcon, EyeClosedIcon } from '@primer/octicons-react'
10
20
  import { Menu } from '@base-ui/react/menu'
11
21
  import { Dialog } from '@base-ui/react/dialog'
22
+ import Tooltip from './Tooltip.jsx'
12
23
  import Icon from './Icon.jsx'
13
24
  import { useBranches } from './BranchBar/useBranches.js'
25
+ import { useArtifactIndex } from './hooks/useArtifactIndex.js'
26
+ import BranchesDropdown from './BranchesDropdown/BranchesDropdown.jsx'
14
27
  import css from './Viewfinder.module.css'
15
28
 
16
29
  /* ─── Theme sync: read toolbar theme from DOM and apply to Primer/BaseUI ─── */
@@ -96,8 +109,6 @@ function useGitHubUser() {
96
109
  /* ─── localStorage helpers ─── */
97
110
 
98
111
  const STARRED_KEY = 'sb-workspace-starred'
99
- const RECENT_KEY = 'sb-workspace-recent'
100
- const MAX_RECENT = 30
101
112
  const GROUP_BY_FOLDERS_KEY = 'sb-workspace-group-folders'
102
113
  const COLLAPSED_FOLDERS_KEY = 'sb-workspace-collapsed-folders'
103
114
 
@@ -122,7 +133,6 @@ function createLocalStorageStore(key, fallback) {
122
133
  }
123
134
 
124
135
  const starredStore = createLocalStorageStore(STARRED_KEY, [])
125
- const recentStore = createLocalStorageStore(RECENT_KEY, [])
126
136
 
127
137
  function useStarred() {
128
138
  const raw = useSyncExternalStore(starredStore.subscribe, starredStore.getSnapshot)
@@ -135,15 +145,61 @@ function useStarred() {
135
145
  return { starred: new Set(ids), toggle }
136
146
  }
137
147
 
148
+ /**
149
+ * Map a recent store entry `{type, key}` to the composite id used by the
150
+ * workspace `itemMap` (`proto:`, `canvas:`, `component:`).
151
+ *
152
+ * The unified recent store uses `'story'` as the canonical type for
153
+ * component/story artifacts (matches `core/stores/paletteProviders.js`),
154
+ * but the workspace's items use `'component'` for the same things.
155
+ */
156
+ function recentEntryToItemId(entry) {
157
+ if (!entry || typeof entry.type !== 'string' || typeof entry.key !== 'string') return null
158
+ switch (entry.type) {
159
+ case 'prototype': return `proto:${entry.key}`
160
+ case 'canvas': return `canvas:${entry.key}`
161
+ case 'story':
162
+ case 'component': return `component:${entry.key}`
163
+ default: return `${entry.type}:${entry.key}`
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Map a workspace `item.type` value to the canonical type stored in the
169
+ * recent artifacts store. The workspace uses `'component'` for stories
170
+ * but the recent store (shared with the command palette) uses `'story'`.
171
+ */
172
+ function itemTypeToRecentType(itemType) {
173
+ if (itemType === 'component') return 'story'
174
+ return itemType
175
+ }
176
+
138
177
  function useRecent() {
139
- const raw = useSyncExternalStore(recentStore.subscribe, recentStore.getSnapshot)
140
- return JSON.parse(raw)
178
+ const raw = useSyncExternalStore(subscribeToRecent, getRecentSnapshot)
179
+ return useMemo(() => {
180
+ try {
181
+ const entries = JSON.parse(raw)
182
+ if (!Array.isArray(entries)) return []
183
+ const ids = []
184
+ for (const entry of entries) {
185
+ const id = recentEntryToItemId(entry)
186
+ if (id) ids.push(id)
187
+ }
188
+ return ids
189
+ } catch {
190
+ return []
191
+ }
192
+ }, [raw])
141
193
  }
142
194
 
143
- function trackRecent(id) {
144
- const current = readJSON(RECENT_KEY, [])
145
- const next = [id, ...current.filter(x => x !== id)].slice(0, MAX_RECENT)
146
- writeJSON(RECENT_KEY, next)
195
+ function trackRecentItem(item) {
196
+ if (!item || !item.id) return
197
+ const colonIdx = item.id.indexOf(':')
198
+ if (colonIdx <= 0) return
199
+ const key = item.id.slice(colonIdx + 1)
200
+ const type = itemTypeToRecentType(item.type)
201
+ if (!type) return
202
+ trackRecent(type, key, item.name || key)
147
203
  }
148
204
 
149
205
  /* ─── URL helpers ─── */
@@ -440,18 +496,26 @@ function DeleteArtifactModal({ item, dirName, basePath, typeLabel, onClose, onDe
440
496
 
441
497
  /* ─── Artifact Card ─── */
442
498
 
443
- function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted }) {
444
- const href = item.route ? withBase(basePath, item.route) : '#'
499
+ function ArtifactCard({ item, basePath, branchBasePath, currentBranch, starred, onToggleStar, onItemDeleted }) {
500
+ const isBranchOnly = !!item.isBranchOnly
445
501
  const isExternal = item.isExternal
502
+ const branchOnlyHref = isBranchOnly
503
+ ? isExternal
504
+ ? item.externalUrl
505
+ : buildBranchArtifactHref(branchBasePath || basePath, item.homeBranch?.folder, item.homeBranch?.route || item.route)
506
+ : null
507
+ const href = item.route ? withBase(basePath, item.route) : '#'
446
508
 
447
509
  const handleClick = () => {
448
- trackRecent(item.id)
510
+ if (!isBranchOnly) trackRecentItem(item)
449
511
  }
450
512
 
451
- const Tag = isExternal ? 'a' : 'a'
452
- const linkProps = isExternal
453
- ? { href: item.externalUrl, target: '_blank', rel: 'noopener noreferrer' }
454
- : { href }
513
+ const Tag = 'a'
514
+ const linkProps = isBranchOnly
515
+ ? { href: branchOnlyHref || '#', target: '_blank', rel: 'noopener noreferrer' }
516
+ : isExternal
517
+ ? { href: item.externalUrl, target: '_blank', rel: 'noopener noreferrer' }
518
+ : { href }
455
519
 
456
520
  const authorList = item.author
457
521
  ? (Array.isArray(item.author) ? item.author : [item.author])
@@ -463,17 +527,40 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
463
527
  // Extract dirName from item.id (format: "type:dirName")
464
528
  const dirName = item.id.split(':').slice(1).join(':')
465
529
  const typeLabel = item.type === 'canvas' ? 'Canvas' : item.type === 'prototype' ? 'Prototype' : 'Component'
466
- const canEditDelete = item.type === 'canvas' || item.type === 'prototype'
530
+ const canEditDelete = !isBranchOnly && (item.type === 'canvas' || item.type === 'prototype')
531
+ const cardClass = `${css.card}${item.isPrivate ? ' ' + css.cardPrivate : ''}${isBranchOnly ? ' ' + css.branchOnlyCard : ''}`
467
532
 
468
533
  return (
469
534
  <>
470
- <Tag className={css.card} {...linkProps} onClick={handleClick}>
535
+ <Tag className={cardClass} {...linkProps} onClick={handleClick}>
471
536
  <div className={css.cardHeader}>
472
- <span className={css.cardBadge}>{getTypeLabel(item.type)}</span>
537
+ <div className={css.cardBadgeGroup}>
538
+ <span className={css.cardBadge}>{getTypeLabel(item.type)}</span>
539
+ {isBranchOnly && <span className={css.branchOnlyBadge}>Branch-only</span>}
540
+ {item.isPrivate && !isBranchOnly && (
541
+ <Tooltip text={`${typeLabel} not published, only visible to you`} direction="n">
542
+ <button
543
+ type="button"
544
+ className={css.cardPrivateBadge}
545
+ aria-label={`Private ${typeLabel.toLowerCase()}`}
546
+ onClick={(e) => { e.preventDefault(); e.stopPropagation() }}
547
+ >
548
+ <EyeClosedIcon size={12} />
549
+ </button>
550
+ </Tooltip>
551
+ )}
552
+ </div>
473
553
  <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} />}
554
+ {!isBranchOnly && <StarBtn active={starred} onClick={() => onToggleStar(item.id)} inline />}
555
+ {!isBranchOnly && item.flows?.length > 1 && <FlowsDropdown flows={item.flows} basePath={basePath} />}
556
+ {!isBranchOnly && item.pages?.length > 1 && <PagesDropdown pages={item.pages} basePath={basePath} />}
557
+ <BranchesDropdown
558
+ branches={item.branches || []}
559
+ branchBasePath={branchBasePath}
560
+ currentBranch={currentBranch}
561
+ homeTitle={item.name}
562
+ homeFolder={item.folder}
563
+ />
477
564
  {canEditDelete && (
478
565
  <CardActionsMenu
479
566
  typeLabel={typeLabel}
@@ -505,7 +592,7 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
505
592
  </div>
506
593
  </div>
507
594
  </Tag>
508
- {showEdit && (
595
+ {showEdit && !isBranchOnly && (
509
596
  <EditArtifactModal
510
597
  item={item}
511
598
  dirName={dirName}
@@ -513,7 +600,7 @@ function ArtifactCard({ item, basePath, starred, onToggleStar, onItemDeleted })
513
600
  onClose={() => setShowEdit(false)}
514
601
  />
515
602
  )}
516
- {showDelete && (
603
+ {showDelete && !isBranchOnly && (
517
604
  <DeleteArtifactModal
518
605
  item={item}
519
606
  dirName={dirName}
@@ -609,8 +696,12 @@ function PagesDropdown({ pages, basePath }) {
609
696
  <a
610
697
  href={withBase(basePath, page.route)}
611
698
  className={css.flowsItemLink}
699
+ title={page.isPrivate ? 'Local-only page — gitignored, dev-only' : undefined}
612
700
  >
613
701
  {page.name}
702
+ {page.isPrivate && (
703
+ <EyeClosedIcon size={12} className={css.flowsItemPrivateIcon} />
704
+ )}
614
705
  </a>
615
706
  </Menu.Item>
616
707
  ))}
@@ -623,18 +714,37 @@ function PagesDropdown({ pages, basePath }) {
623
714
 
624
715
  /* ─── Folder Section ─── */
625
716
 
626
- function FolderSection({ folder, collapsed, onToggle, basePath, starred, onToggleStar, onItemDeleted }) {
717
+ function FolderSection({ folder, collapsed, onToggle, basePath, branchBasePath, currentBranch, starred, onToggleStar, onItemDeleted }) {
718
+ const sectionClass = collapsed ? css.folderSectionCollapsed : css.folderSection
627
719
  return (
628
- <section className={collapsed ? css.folderSectionCollapsed : css.folderSection}>
629
- <button className={css.folderHeader} onClick={onToggle}>
720
+ <section className={`${sectionClass}${folder.isPrivate ? ' ' + css.folderSectionPrivate : ''}`}>
721
+ <div
722
+ className={css.folderHeader}
723
+ role="button"
724
+ tabIndex={0}
725
+ onClick={onToggle}
726
+ onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggle() } }}
727
+ >
630
728
  <Icon name={collapsed ? 'folder' : 'folder-open'} size={16} className={css.folderIcon} />
631
729
  <span className={css.folderName}>{folder.name}</span>
730
+ {folder.isPrivate && (
731
+ <Tooltip text="Folder not published, only visible to you" direction="n">
732
+ <button
733
+ type="button"
734
+ className={css.folderPrivateBadge}
735
+ aria-label="Private folder"
736
+ onClick={(e) => { e.preventDefault(); e.stopPropagation() }}
737
+ >
738
+ <EyeClosedIcon size={12} />
739
+ </button>
740
+ </Tooltip>
741
+ )}
632
742
  <span className={css.folderCount}>{folder.items.length}</span>
633
743
  <ChevronRightIcon
634
744
  size={14}
635
745
  className={collapsed ? css.folderChevron : css.folderChevronExpanded}
636
746
  />
637
- </button>
747
+ </div>
638
748
  {!collapsed && (
639
749
  <div className={css.grid}>
640
750
  {folder.items.map(item => (
@@ -642,6 +752,8 @@ function FolderSection({ folder, collapsed, onToggle, basePath, starred, onToggl
642
752
  key={item.id}
643
753
  item={item}
644
754
  basePath={basePath}
755
+ branchBasePath={branchBasePath}
756
+ currentBranch={currentBranch}
645
757
  starred={starred.has(item.id)}
646
758
  onToggleStar={onToggleStar}
647
759
  onItemDeleted={onItemDeleted}
@@ -878,7 +990,7 @@ function CreateForm({ type, onClose, basePath }) {
878
990
 
879
991
  /* ─── Create Menu (Dropdown) ─── */
880
992
 
881
- function CreateMenu({ onClose, basePath }) {
993
+ export function CreateMenu({ onClose, basePath }) {
882
994
  const [activeForm, setActiveForm] = useState(null)
883
995
  const [showMore, setShowMore] = useState(false)
884
996
 
@@ -956,6 +1068,7 @@ const NAV_ITEMS = [
956
1068
  ]
957
1069
 
958
1070
  const TAB_FILTERS = ['All', 'Recent', 'Starred']
1071
+ const TYPE_MAP = { prototypes: 'prototype', canvases: 'canvas', components: 'component' }
959
1072
 
960
1073
  /* ─── Branch Navigation ─── */
961
1074
 
@@ -970,7 +1083,17 @@ function BranchNav({ basePath }) {
970
1083
  if (isLocalDev) return null
971
1084
  if (!branches || branches.length === 0) return null
972
1085
 
973
- const branchNames = branches.map(b => b.branch)
1086
+ // Ensure `main` is always present and sorted first. CI's `branches.json`
1087
+ // historically lists only `branch--*/` folders, so `main` is missing on
1088
+ // prod deploys — without this the <select> falls back to its first option
1089
+ // (often something like `0.5.0-fix-…`) when the user is actually on main.
1090
+ const branchNames = [
1091
+ 'main',
1092
+ ...branches
1093
+ .map(b => b.branch)
1094
+ .filter(b => b && b !== 'main')
1095
+ .sort((a, b) => a.localeCompare(b)),
1096
+ ]
974
1097
 
975
1098
  const navigate = async (branch) => {
976
1099
  if (switching) return
@@ -1093,6 +1216,8 @@ function WorkspaceImpl({
1093
1216
  basePath,
1094
1217
  title = 'Storyboard',
1095
1218
  subtitle,
1219
+ logo,
1220
+ logoIcon = 'iconoir/key-command',
1096
1221
  showAllArtifacts = false,
1097
1222
  showPrototypes = true,
1098
1223
  showCanvases = true,
@@ -1100,6 +1225,8 @@ function WorkspaceImpl({
1100
1225
  }) {
1101
1226
  const themeAttrs = useToolbarTheme()
1102
1227
  const ghUser = useGitHubUser(basePath)
1228
+ const { currentBranch, branchBasePath } = useBranches(basePath)
1229
+ const { artifactIndex } = useArtifactIndex(basePath)
1103
1230
  const [settingsOpen, setSettingsOpen] = useState(false)
1104
1231
 
1105
1232
  const handleRemoveToken = useCallback(() => {
@@ -1129,13 +1256,17 @@ function WorkspaceImpl({
1129
1256
  const knownRoutes = useMemo(() =>
1130
1257
  Object.keys(pageModules)
1131
1258
  .map(p => p.replace('/src/prototypes/', '').replace('.jsx', ''))
1132
- .filter(n => !n.startsWith('_') && n !== 'index' && n !== 'workspace' && n !== 'viewfinder'),
1259
+ .filter(n => !n.startsWith('_') && n !== 'index' && n !== 'workspace' && n !== 'viewfinder' && n !== 'create'),
1133
1260
  [pageModules],
1134
1261
  )
1135
1262
 
1136
1263
  // canvasIndexTick is an intentional re-render trigger from HMR events
1137
1264
  // eslint-disable-next-line react-hooks/exhaustive-deps
1138
- const prototypeIndex = useMemo(() => buildPrototypeIndex(knownRoutes), [knownRoutes, canvasIndexTick])
1265
+ const localPrototypeIndex = useMemo(() => buildPrototypeIndex(knownRoutes), [knownRoutes, canvasIndexTick])
1266
+ const prototypeIndex = useMemo(
1267
+ () => mergeArtifactIndexIntoPrototypeIndex(localPrototypeIndex, artifactIndex, currentBranch),
1268
+ [localPrototypeIndex, artifactIndex, currentBranch],
1269
+ )
1139
1270
 
1140
1271
  // Build unified items list from all sources
1141
1272
  const allItems = useMemo(() => {
@@ -1152,18 +1283,22 @@ function WorkspaceImpl({
1152
1283
  : `/${proto.dirName}`
1153
1284
 
1154
1285
  items.push({
1155
- id: `proto:${proto.dirName}`,
1286
+ id: proto.id || `proto:${proto.dirName}`,
1156
1287
  name: proto.name,
1157
1288
  type: 'prototype',
1158
1289
  author: proto.author,
1159
1290
  gitAuthor: proto.gitAuthor,
1160
1291
  lastModified: proto.lastModified,
1161
- route,
1292
+ route: proto.route || route,
1162
1293
  isExternal: proto.isExternal,
1163
1294
  externalUrl: proto.externalUrl,
1164
1295
  folder: proto.folder,
1165
1296
  description: proto.description,
1166
1297
  flows: proto.flows || [],
1298
+ isPrivate: !!proto.isPrivate,
1299
+ branches: proto.branches || [],
1300
+ isBranchOnly: !!proto.isBranchOnly,
1301
+ homeBranch: proto.homeBranch || null,
1167
1302
  })
1168
1303
  }
1169
1304
 
@@ -1175,18 +1310,22 @@ function WorkspaceImpl({
1175
1310
  // Canvases (ungrouped + from folders)
1176
1311
  const addCanvas = (canvas) => {
1177
1312
  items.push({
1178
- id: `canvas:${canvas.dirName}`,
1313
+ id: canvas.id || `canvas:${canvas.dirName}`,
1179
1314
  name: canvas.name,
1180
1315
  type: 'canvas',
1181
1316
  author: canvas.author,
1182
1317
  gitAuthor: canvas.gitAuthor,
1183
- lastModified: null,
1318
+ lastModified: canvas.lastModified || null,
1184
1319
  route: canvas.route,
1185
1320
  isExternal: false,
1186
1321
  externalUrl: null,
1187
1322
  folder: canvas.folder,
1188
1323
  description: canvas.description,
1189
1324
  pages: canvas.pages || null,
1325
+ isPrivate: !!canvas.isPrivate,
1326
+ branches: canvas.branches || [],
1327
+ isBranchOnly: !!canvas.isBranchOnly,
1328
+ homeBranch: canvas.homeBranch || null,
1190
1329
  })
1191
1330
  }
1192
1331
 
@@ -1200,9 +1339,10 @@ function WorkspaceImpl({
1200
1339
  for (const name of storyNames) {
1201
1340
  const data = getStoryData(name)
1202
1341
  if (!data) continue
1342
+ const displayName = (name.split('/').pop() || name).replace(/^~/, '')
1203
1343
  items.push({
1204
1344
  id: `component:${name}`,
1205
- name: name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
1345
+ name: displayName.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' '),
1206
1346
  type: 'component',
1207
1347
  author: null,
1208
1348
  gitAuthor: null,
@@ -1212,6 +1352,7 @@ function WorkspaceImpl({
1212
1352
  externalUrl: null,
1213
1353
  folder: null,
1214
1354
  description: null,
1355
+ isPrivate: !!data._isPrivate || name.split('/').includes('drafts'),
1215
1356
  })
1216
1357
  }
1217
1358
 
@@ -1253,9 +1394,8 @@ function WorkspaceImpl({
1253
1394
  const recentIds = useRecent()
1254
1395
 
1255
1396
  // Filter by nav category
1256
- const typeMap = { prototypes: 'prototype', canvases: 'canvas', components: 'component' }
1257
1397
  const navFiltered = useMemo(() => {
1258
- let filtered = activeNav === 'all' ? allItems : allItems.filter(i => i.type === typeMap[activeNav])
1398
+ let filtered = activeNav === 'all' ? allItems : allItems.filter(i => i.type === TYPE_MAP[activeNav])
1259
1399
  if (hiddenItems.size > 0) filtered = filtered.filter(i => !hiddenItems.has(i.id))
1260
1400
  return filtered
1261
1401
  }, [allItems, activeNav, hiddenItems])
@@ -1263,15 +1403,14 @@ function WorkspaceImpl({
1263
1403
  // Filter by tab
1264
1404
  const items = useMemo(() => {
1265
1405
  if (activeTab === 'Recent') {
1266
- const ordered = recentIds.map(id => itemMap[id]).filter(Boolean)
1406
+ const ordered = recentIds.map(id => itemMap[id]).filter(item => item && !item.isBranchOnly)
1267
1407
  if (activeNav !== 'all') {
1268
- const typeMap = { prototypes: 'prototype', canvases: 'canvas', components: 'component' }
1269
- return ordered.filter(i => i.type === typeMap[activeNav])
1408
+ return ordered.filter(i => i.type === TYPE_MAP[activeNav])
1270
1409
  }
1271
1410
  return ordered
1272
1411
  }
1273
1412
  const base = activeTab === 'Starred'
1274
- ? navFiltered.filter(i => starred.has(i.id))
1413
+ ? navFiltered.filter(i => !i.isBranchOnly && starred.has(i.id))
1275
1414
  : navFiltered
1276
1415
  return [...base].sort((a, b) => {
1277
1416
  const aTime = a.lastModified ? new Date(a.lastModified).getTime() : 0
@@ -1298,6 +1437,7 @@ function WorkspaceImpl({
1298
1437
  const folders = Object.entries(folderItems).map(([dirName, fItems]) => ({
1299
1438
  dirName,
1300
1439
  name: folderMeta[dirName]?.name || dirName,
1440
+ isPrivate: !!folderMeta[dirName]?.isPrivate || dirName === 'drafts',
1301
1441
  items: fItems,
1302
1442
  }))
1303
1443
  folders.sort((a, b) => {
@@ -1343,7 +1483,7 @@ function WorkspaceImpl({
1343
1483
  }), [visibleItems])
1344
1484
 
1345
1485
  // Starred items for sidebar
1346
- const starredItems = useMemo(() => visibleItems.filter(i => starred.has(i.id)), [visibleItems, starred])
1486
+ const starredItems = useMemo(() => visibleItems.filter(i => !i.isBranchOnly && starred.has(i.id)), [visibleItems, starred])
1347
1487
 
1348
1488
  return (
1349
1489
  <div className={css.layout} {...themeAttrs}>
@@ -1357,7 +1497,9 @@ function WorkspaceImpl({
1357
1497
  >
1358
1498
  {sidebarOpen ? <XIcon size={18} /> : <ThreeBarsIcon size={18} />}
1359
1499
  </button>
1360
- <div className={`${css.logo} smooth-corners`}><Icon name="iconoir/key-command" size={22} color="#fff" /></div>
1500
+ {logo
1501
+ ? <div className={`${css.logo} smooth-corners`}>{logo}</div>
1502
+ : <div className={`${css.logo} smooth-corners`}><Icon name={logoIcon} size={22} color="#fff" /></div>}
1361
1503
  <div>
1362
1504
  <div className={css.appName}>{title}</div>
1363
1505
  {subtitle && <div className={css.appSubtitle}>{subtitle}</div>}
@@ -1413,7 +1555,7 @@ function WorkspaceImpl({
1413
1555
  className={css.starredItem}
1414
1556
  href={s.isExternal ? s.externalUrl : withBase(basePath, s.route)}
1415
1557
  {...(s.isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
1416
- onClick={() => trackRecent(s.id)}
1558
+ onClick={() => trackRecentItem(s)}
1417
1559
  >
1418
1560
  <span className={css.starredIcon}>{getTypeIcon(s.type)}</span>
1419
1561
  {s.name}
@@ -1499,6 +1641,8 @@ function WorkspaceImpl({
1499
1641
  collapsed={collapsedFolders.has(folder.dirName)}
1500
1642
  onToggle={() => toggleFolder(folder.dirName)}
1501
1643
  basePath={basePath}
1644
+ branchBasePath={branchBasePath}
1645
+ currentBranch={currentBranch}
1502
1646
  starred={starred}
1503
1647
  onToggleStar={toggleStar}
1504
1648
  onItemDeleted={handleItemDeleted}
@@ -1511,6 +1655,8 @@ function WorkspaceImpl({
1511
1655
  key={item.id}
1512
1656
  item={item}
1513
1657
  basePath={basePath}
1658
+ branchBasePath={branchBasePath}
1659
+ currentBranch={currentBranch}
1514
1660
  starred={starred.has(item.id)}
1515
1661
  onToggleStar={toggleStar}
1516
1662
  onItemDeleted={handleItemDeleted}
@@ -1526,6 +1672,8 @@ function WorkspaceImpl({
1526
1672
  key={item.id}
1527
1673
  item={item}
1528
1674
  basePath={basePath}
1675
+ branchBasePath={branchBasePath}
1676
+ currentBranch={currentBranch}
1529
1677
  starred={starred.has(item.id)}
1530
1678
  onToggleStar={toggleStar}
1531
1679
  onItemDeleted={handleItemDeleted}
@@ -642,6 +642,23 @@
642
642
  color: inherit;
643
643
  }
644
644
 
645
+ .cardPrivate {
646
+ opacity: 0.8;
647
+ }
648
+
649
+ .cardPrivate:hover {
650
+ opacity: 1;
651
+ }
652
+
653
+ .branchOnlyCard {
654
+ background: var(--bgColor-muted, #f6f8fa);
655
+ border-color: var(--borderColor-muted, #d8dee4);
656
+ }
657
+
658
+ .branchOnlyCard:hover {
659
+ border-color: var(--borderColor-accent-muted, #54aeff66);
660
+ }
661
+
645
662
  .cardThumb {
646
663
  height: 140px;
647
664
  background: var(--bgColor-muted, #f5f5f5);
@@ -663,6 +680,33 @@
663
680
  color: var(--fgColor-muted, #555);
664
681
  }
665
682
 
683
+ .branchOnlyBadge {
684
+ padding: 3px 8px;
685
+ border-radius: 999px;
686
+ font-size: 11px;
687
+ font-weight: 700;
688
+ text-transform: uppercase;
689
+ letter-spacing: 0.4px;
690
+ background: var(--bgColor-accent-muted, #ddf4ff);
691
+ color: var(--fgColor-accent, #0969da);
692
+ }
693
+
694
+ .cardPrivateBadge {
695
+ display: inline-flex;
696
+ align-items: center;
697
+ justify-content: center;
698
+ color: var(--fgColor-muted, #656d76);
699
+ background: none;
700
+ border: none;
701
+ padding: 2px;
702
+ }
703
+
704
+ .cardBadgeGroup {
705
+ display: flex;
706
+ align-items: center;
707
+ gap: 6px;
708
+ }
709
+
666
710
  /* Icon buttons (star, flows, etc.) */
667
711
 
668
712
  .iconBtn {
@@ -1036,6 +1080,25 @@
1036
1080
  flex: 1;
1037
1081
  }
1038
1082
 
1083
+ .folderSectionPrivate {
1084
+ opacity: 0.8;
1085
+ }
1086
+
1087
+ .folderSectionPrivate:hover {
1088
+ opacity: 1;
1089
+ }
1090
+
1091
+ .folderPrivateBadge {
1092
+ display: inline-flex;
1093
+ align-items: center;
1094
+ justify-content: center;
1095
+ color: var(--fgColor-muted, #656d76);
1096
+ background: none;
1097
+ border: none;
1098
+ padding: 2px;
1099
+ margin-right: 6px;
1100
+ }
1101
+
1039
1102
  .folderCount {
1040
1103
  font-size: 14px;
1041
1104
  font-weight: 400;
@@ -1119,11 +1182,19 @@
1119
1182
  }
1120
1183
 
1121
1184
  .flowsItemLink {
1122
- display: block;
1185
+ display: flex;
1186
+ align-items: center;
1187
+ gap: 6px;
1123
1188
  color: inherit;
1124
1189
  text-decoration: none;
1125
1190
  }
1126
1191
 
1192
+ .flowsItemPrivateIcon {
1193
+ color: var(--fgColor-muted, #656d76);
1194
+ flex-shrink: 0;
1195
+ margin-left: auto;
1196
+ }
1197
+
1127
1198
  /* Avatar stack */
1128
1199
 
1129
1200
  .avatarStack {