@dfosco/storyboard-core 4.2.0-beta.2 → 4.2.0-beta.21

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 (414) hide show
  1. package/commandpalette.config.json +109 -24
  2. package/dist/storyboard-ui.css +1 -1
  3. package/dist/storyboard-ui.js +17379 -28568
  4. package/dist/storyboard-ui.js.map +1 -1
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +5 -2
  7. package/scaffold/agents/prompt-agent.agent.md +181 -0
  8. package/scaffold/agents/terminal-agent.agent.md +351 -0
  9. package/scaffold/codex/config.toml +246 -0
  10. package/scaffold/manifest.json +5 -0
  11. package/scaffold/skills/canvas/SKILL.md +5 -4
  12. package/scaffold/skills/ship/SKILL.md +1 -1
  13. package/scaffold/storyboard.config.json +14 -1
  14. package/scaffold/toolbar.config.json +1 -1
  15. package/src/ActionMenuButton.jsx +100 -0
  16. package/src/AutosyncMenuButton.css +67 -0
  17. package/src/AutosyncMenuButton.jsx +241 -0
  18. package/src/BranchSelect.jsx +29 -0
  19. package/src/BranchSelect.module.css +30 -0
  20. package/src/CanvasAgentsMenu.jsx +87 -0
  21. package/src/CanvasCreateMenu.jsx +609 -0
  22. package/src/CanvasSnap.css +27 -0
  23. package/src/CanvasSnap.jsx +51 -0
  24. package/src/CanvasUndoRedo.css +36 -0
  25. package/src/CanvasUndoRedo.jsx +62 -0
  26. package/src/CanvasZoomControl.css +53 -0
  27. package/src/CanvasZoomControl.jsx +49 -0
  28. package/src/CanvasZoomToFit.css +18 -0
  29. package/src/CanvasZoomToFit.jsx +26 -0
  30. package/src/CommandMenu.css +8 -0
  31. package/src/CommandMenu.jsx +286 -0
  32. package/src/CommandPalette.jsx +35 -0
  33. package/src/CommandPaletteTrigger.jsx +25 -0
  34. package/src/CommentsMenuButton.jsx +38 -0
  35. package/src/CoreUIBar.css +47 -0
  36. package/src/CoreUIBar.jsx +855 -0
  37. package/src/CreateMenuButton.jsx +116 -0
  38. package/src/HideChromeTrigger.jsx +40 -0
  39. package/src/InspectorPanel.css +109 -0
  40. package/src/InspectorPanel.jsx +629 -0
  41. package/src/PwaInstallBanner.css +42 -0
  42. package/src/PwaInstallBanner.jsx +124 -0
  43. package/src/SidePanel.jsx +260 -0
  44. package/src/ThemeMenuButton.jsx +136 -0
  45. package/src/autosync/server.js +202 -5
  46. package/src/autosync/server.test.js +112 -0
  47. package/src/canvas/__tests__/agent-integration.test.js +593 -0
  48. package/src/canvas/__tests__/helpers/browser.js +95 -0
  49. package/src/canvas/__tests__/helpers/canvas-api.js +129 -0
  50. package/src/canvas/__tests__/helpers/perf.js +118 -0
  51. package/src/canvas/__tests__/helpers/setup.js +176 -0
  52. package/src/canvas/__tests__/helpers/tmux.js +130 -0
  53. package/src/canvas/__tests__/helpers/transcript.js +129 -0
  54. package/src/canvas/__tests__/terminal-integration.test.js +175 -0
  55. package/src/canvas/hot-pool.js +757 -0
  56. package/src/canvas/materializer.js +31 -0
  57. package/src/canvas/materializer.test.js +56 -0
  58. package/src/canvas/selectedWidgets.js +65 -7
  59. package/src/canvas/server.js +1801 -22
  60. package/src/canvas/server.test.js +239 -0
  61. package/src/canvas/terminal-config.js +331 -0
  62. package/src/canvas/terminal-registry.js +38 -0
  63. package/src/canvas/terminal-server.js +1037 -29
  64. package/src/canvas/writeGuard.js +51 -3
  65. package/src/canvasConfig.js +67 -1
  66. package/src/canvasConfig.test.js +79 -1
  67. package/src/cli/agent.js +85 -0
  68. package/src/cli/branch.js +232 -0
  69. package/src/cli/canvasAdd.js +59 -12
  70. package/src/cli/canvasBatch.js +98 -0
  71. package/src/cli/canvasBounds.js +1 -1
  72. package/src/cli/canvasRead.js +1 -1
  73. package/src/cli/canvasUpdate.js +179 -0
  74. package/src/cli/create.js +38 -14
  75. package/src/cli/dev.js +157 -83
  76. package/src/cli/exit.js +23 -24
  77. package/src/cli/index.js +55 -2
  78. package/src/cli/proxy.js +96 -37
  79. package/src/cli/schemas.js +22 -4
  80. package/src/cli/server.js +148 -25
  81. package/src/cli/serverUrl.js +8 -3
  82. package/src/cli/sessions.js +131 -5
  83. package/src/cli/setup.js +109 -11
  84. package/src/cli/terminal-commands.js +16 -8
  85. package/src/cli/terminal-messaging.js +231 -0
  86. package/src/cli/terminal-welcome.js +365 -33
  87. package/src/commandActions.js +1 -0
  88. package/src/commandPaletteConfig.js +9 -0
  89. package/src/comments/auth.js +2 -1
  90. package/src/comments/ui/AuthModal.jsx +114 -0
  91. package/src/comments/ui/CommentWindow.jsx +329 -0
  92. package/src/comments/ui/CommentsDrawer.jsx +102 -0
  93. package/src/comments/ui/Composer.jsx +64 -0
  94. package/src/comments/ui/authModal.test.js +1 -1
  95. package/src/comments/ui/commentWindow.js +16 -17
  96. package/src/comments/ui/commentsDrawer.js +25 -26
  97. package/src/comments/ui/composer.js +23 -24
  98. package/src/comments/ui/index.js +2 -3
  99. package/src/configSchema.js +59 -1
  100. package/src/configStore.js +161 -0
  101. package/src/core-ui-colors.css +12 -0
  102. package/src/devtools.js +17 -19
  103. package/src/devtools.test.js +18 -9
  104. package/src/featureFlags.js +12 -5
  105. package/src/fuzzySearch.test.js +10 -0
  106. package/src/index.js +14 -2
  107. package/src/lib/components/ui/alert/alert-action.jsx +11 -0
  108. package/src/lib/components/ui/alert/alert-description.jsx +11 -0
  109. package/src/lib/components/ui/alert/alert-title.jsx +11 -0
  110. package/src/lib/components/ui/alert/alert.jsx +25 -0
  111. package/src/lib/components/ui/alert/index.js +15 -15
  112. package/src/lib/components/ui/avatar/avatar-badge.jsx +22 -0
  113. package/src/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
  114. package/src/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
  115. package/src/lib/components/ui/avatar/avatar-group.jsx +19 -0
  116. package/src/lib/components/ui/avatar/avatar-image.jsx +15 -0
  117. package/src/lib/components/ui/avatar/avatar.jsx +19 -0
  118. package/src/lib/components/ui/avatar/index.js +20 -20
  119. package/src/lib/components/ui/badge/badge.jsx +31 -0
  120. package/src/lib/components/ui/badge/index.js +2 -2
  121. package/src/lib/components/ui/button/button.jsx +100 -0
  122. package/src/lib/components/ui/button/index.js +9 -9
  123. package/src/lib/components/ui/card/card-action.jsx +11 -0
  124. package/src/lib/components/ui/card/card-content.jsx +11 -0
  125. package/src/lib/components/ui/card/card-description.jsx +11 -0
  126. package/src/lib/components/ui/card/card-footer.jsx +11 -0
  127. package/src/lib/components/ui/card/card-header.jsx +19 -0
  128. package/src/lib/components/ui/card/card-title.jsx +11 -0
  129. package/src/lib/components/ui/card/card.jsx +17 -0
  130. package/src/lib/components/ui/card/index.js +23 -23
  131. package/src/lib/components/ui/checkbox/checkbox.jsx +29 -0
  132. package/src/lib/components/ui/checkbox/index.js +5 -5
  133. package/src/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
  134. package/src/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
  135. package/src/lib/components/ui/collapsible/collapsible.jsx +7 -0
  136. package/src/lib/components/ui/collapsible/index.js +11 -11
  137. package/src/lib/components/ui/dialog/dialog-close.jsx +7 -0
  138. package/src/lib/components/ui/dialog/dialog-content.jsx +34 -0
  139. package/src/lib/components/ui/dialog/dialog-description.jsx +15 -0
  140. package/src/lib/components/ui/dialog/dialog-footer.jsx +23 -0
  141. package/src/lib/components/ui/dialog/dialog-header.jsx +11 -0
  142. package/src/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
  143. package/src/lib/components/ui/dialog/dialog-portal.jsx +4 -0
  144. package/src/lib/components/ui/dialog/dialog-title.jsx +15 -0
  145. package/src/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
  146. package/src/lib/components/ui/dialog/dialog.jsx +4 -0
  147. package/src/lib/components/ui/dialog/index.js +32 -32
  148. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
  149. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
  150. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
  151. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
  152. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
  153. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
  154. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
  155. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
  156. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
  157. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
  158. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
  159. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
  160. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
  161. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
  162. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
  163. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
  164. package/src/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
  165. package/src/lib/components/ui/dropdown-menu/index.js +52 -52
  166. package/src/lib/components/ui/input/index.js +5 -5
  167. package/src/lib/components/ui/input/input.jsx +19 -0
  168. package/src/lib/components/ui/label/index.js +5 -5
  169. package/src/lib/components/ui/label/label.jsx +19 -0
  170. package/src/lib/components/ui/panel/index.js +21 -21
  171. package/src/lib/components/ui/panel/panel-body.jsx +11 -0
  172. package/src/lib/components/ui/panel/panel-close.jsx +16 -0
  173. package/src/lib/components/ui/panel/panel-content.jsx +29 -0
  174. package/src/lib/components/ui/panel/panel-footer.jsx +11 -0
  175. package/src/lib/components/ui/panel/panel-header.jsx +11 -0
  176. package/src/lib/components/ui/panel/panel-title.jsx +12 -0
  177. package/src/lib/components/ui/panel/panel.jsx +4 -0
  178. package/src/lib/components/ui/popover/index.js +26 -26
  179. package/src/lib/components/ui/popover/popover-close.jsx +7 -0
  180. package/src/lib/components/ui/popover/popover-content.jsx +22 -0
  181. package/src/lib/components/ui/popover/popover-description.jsx +11 -0
  182. package/src/lib/components/ui/popover/popover-header.jsx +11 -0
  183. package/src/lib/components/ui/popover/popover-portal.jsx +4 -0
  184. package/src/lib/components/ui/popover/popover-title.jsx +11 -0
  185. package/src/lib/components/ui/popover/popover-trigger.jsx +8 -0
  186. package/src/lib/components/ui/popover/popover.jsx +4 -0
  187. package/src/lib/components/ui/searchable-list.jsx +159 -0
  188. package/src/lib/components/ui/select/index.js +35 -35
  189. package/src/lib/components/ui/select/select-content.jsx +30 -0
  190. package/src/lib/components/ui/select/select-group-heading.jsx +17 -0
  191. package/src/lib/components/ui/select/select-group.jsx +15 -0
  192. package/src/lib/components/ui/select/select-item.jsx +26 -0
  193. package/src/lib/components/ui/select/select-label.jsx +11 -0
  194. package/src/lib/components/ui/select/select-portal.jsx +4 -0
  195. package/src/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
  196. package/src/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
  197. package/src/lib/components/ui/select/select-separator.jsx +15 -0
  198. package/src/lib/components/ui/select/select-trigger.jsx +25 -0
  199. package/src/lib/components/ui/select/select.jsx +4 -0
  200. package/src/lib/components/ui/separator/index.js +5 -5
  201. package/src/lib/components/ui/separator/separator.jsx +22 -0
  202. package/src/lib/components/ui/sheet/index.js +32 -32
  203. package/src/lib/components/ui/sheet/sheet-close.jsx +7 -0
  204. package/src/lib/components/ui/sheet/sheet-content.jsx +35 -0
  205. package/src/lib/components/ui/sheet/sheet-description.jsx +15 -0
  206. package/src/lib/components/ui/sheet/sheet-footer.jsx +11 -0
  207. package/src/lib/components/ui/sheet/sheet-header.jsx +11 -0
  208. package/src/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
  209. package/src/lib/components/ui/sheet/sheet-portal.jsx +4 -0
  210. package/src/lib/components/ui/sheet/sheet-title.jsx +15 -0
  211. package/src/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
  212. package/src/lib/components/ui/sheet/sheet.jsx +4 -0
  213. package/src/lib/components/ui/textarea/index.js +5 -5
  214. package/src/lib/components/ui/textarea/textarea.jsx +18 -0
  215. package/src/lib/components/ui/toggle/index.js +6 -9
  216. package/src/lib/components/ui/toggle/toggle.jsx +36 -0
  217. package/src/lib/components/ui/toggle-group/index.js +8 -8
  218. package/src/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
  219. package/src/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
  220. package/src/lib/components/ui/tooltip/index.js +3 -3
  221. package/src/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
  222. package/src/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
  223. package/src/lib/components/ui/tooltip/tooltip.jsx +11 -0
  224. package/src/lib/components/ui/trigger-button/index.js +3 -3
  225. package/src/lib/components/ui/trigger-button/trigger-button.css +38 -0
  226. package/src/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
  227. package/src/logger/devLogger.js +238 -0
  228. package/src/logger/devLogger.test.js +193 -0
  229. package/src/modes.test.js +4 -4
  230. package/src/mountStoryboardCore.js +123 -27
  231. package/src/paletteProviders.js +3 -0
  232. package/src/paletteProviders.test.js +2 -2
  233. package/src/server/index.js +98 -36
  234. package/src/sidepanel.css +214 -0
  235. package/src/styles/tailwind.css +1 -1
  236. package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +8 -8
  237. package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +11 -10
  238. package/src/svelte-plugin-ui/components/Icon.css +11 -0
  239. package/src/svelte-plugin-ui/components/Icon.jsx +281 -0
  240. package/src/svelte-plugin-ui/components/ModeSwitch.css +90 -0
  241. package/src/svelte-plugin-ui/components/ModeSwitch.jsx +47 -0
  242. package/src/svelte-plugin-ui/components/ToolbarShell.css +80 -0
  243. package/src/svelte-plugin-ui/components/ToolbarShell.jsx +84 -0
  244. package/src/svelte-plugin-ui/components/Viewfinder.css +412 -0
  245. package/src/svelte-plugin-ui/components/Viewfinder.jsx +512 -0
  246. package/src/svelte-plugin-ui/mount.ts +12 -16
  247. package/src/toolRegistry.js +4 -4
  248. package/src/toolbarConfigStore.js +30 -0
  249. package/src/tools/handlers/autosync.js +1 -1
  250. package/src/tools/handlers/canvasAddWidget.js +1 -1
  251. package/src/tools/handlers/canvasAgents.js +19 -0
  252. package/src/tools/handlers/canvasToolbar.js +8 -8
  253. package/src/tools/handlers/commandPalette.js +9 -0
  254. package/src/tools/handlers/comments.js +1 -1
  255. package/src/tools/handlers/create.js +1 -1
  256. package/src/tools/handlers/devtools.js +16 -0
  257. package/src/tools/handlers/devtools.test.js +38 -0
  258. package/src/tools/handlers/flows.js +1 -1
  259. package/src/tools/handlers/hideChrome.js +9 -0
  260. package/src/tools/handlers/paletteTheme.js +35 -0
  261. package/src/tools/handlers/theme.js +1 -1
  262. package/src/tools/registry.js +4 -1
  263. package/src/tools/surfaces/commandList.js +3 -3
  264. package/src/tools/surfaces/mainToolbar.js +3 -3
  265. package/src/tools/surfaces/registry.js +4 -4
  266. package/src/ui/design-modes.ts +2 -2
  267. package/src/ui/viewfinder.ts +1 -1
  268. package/src/vite/server-plugin.js +242 -60
  269. package/src/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
  270. package/src/workshop/features/createCanvas/index.js +1 -1
  271. package/src/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
  272. package/src/workshop/features/createFlow/index.js +1 -1
  273. package/src/workshop/features/createPage/CreatePageForm.jsx +304 -0
  274. package/src/workshop/features/createPage/index.js +1 -1
  275. package/src/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
  276. package/src/workshop/features/createPrototype/index.js +1 -1
  277. package/src/workshop/features/createPrototype/server.js +98 -0
  278. package/src/workshop/features/createStory/CreateStoryForm.jsx +208 -0
  279. package/src/workshop/features/createStory/index.js +1 -1
  280. package/src/workshop/ui/WorkshopPanel.jsx +98 -0
  281. package/src/workshop/ui/mount.ts +1 -1
  282. package/src/worktree/port.js +48 -0
  283. package/src/worktree/serverRegistry.js +120 -0
  284. package/toolbar.config.json +93 -42
  285. package/widgets.config.json +580 -12
  286. package/src/ActionMenuButton.svelte +0 -119
  287. package/src/AutosyncMenuButton.svelte +0 -397
  288. package/src/CanvasCreateMenu.svelte +0 -295
  289. package/src/CanvasSnap.svelte +0 -87
  290. package/src/CanvasUndoRedo.svelte +0 -108
  291. package/src/CanvasZoomControl.svelte +0 -111
  292. package/src/CanvasZoomToFit.svelte +0 -52
  293. package/src/CommandMenu.svelte +0 -249
  294. package/src/CommandPalette.svelte +0 -33
  295. package/src/CommentsMenuButton.svelte +0 -53
  296. package/src/CoreUIBar.svelte +0 -847
  297. package/src/CreateMenuButton.svelte +0 -133
  298. package/src/DocPanel.svelte +0 -299
  299. package/src/InspectorPanel.svelte +0 -745
  300. package/src/PwaInstallBanner.svelte +0 -124
  301. package/src/SidePanel.svelte +0 -480
  302. package/src/ThemeMenuButton.svelte +0 -132
  303. package/src/comments/ui/AuthModal.svelte +0 -108
  304. package/src/comments/ui/CommentWindow.svelte +0 -333
  305. package/src/comments/ui/CommentsDrawer.svelte +0 -96
  306. package/src/comments/ui/Composer.svelte +0 -65
  307. package/src/lib/components/ui/alert/alert-action.svelte +0 -19
  308. package/src/lib/components/ui/alert/alert-description.svelte +0 -22
  309. package/src/lib/components/ui/alert/alert-title.svelte +0 -22
  310. package/src/lib/components/ui/alert/alert.svelte +0 -38
  311. package/src/lib/components/ui/avatar/avatar-badge.svelte +0 -25
  312. package/src/lib/components/ui/avatar/avatar-fallback.svelte +0 -20
  313. package/src/lib/components/ui/avatar/avatar-group-count.svelte +0 -22
  314. package/src/lib/components/ui/avatar/avatar-group.svelte +0 -22
  315. package/src/lib/components/ui/avatar/avatar-image.svelte +0 -17
  316. package/src/lib/components/ui/avatar/avatar.svelte +0 -24
  317. package/src/lib/components/ui/badge/badge.svelte +0 -44
  318. package/src/lib/components/ui/button/button.svelte +0 -108
  319. package/src/lib/components/ui/card/card-action.svelte +0 -21
  320. package/src/lib/components/ui/card/card-content.svelte +0 -19
  321. package/src/lib/components/ui/card/card-description.svelte +0 -19
  322. package/src/lib/components/ui/card/card-footer.svelte +0 -18
  323. package/src/lib/components/ui/card/card-header.svelte +0 -21
  324. package/src/lib/components/ui/card/card-title.svelte +0 -14
  325. package/src/lib/components/ui/card/card.svelte +0 -21
  326. package/src/lib/components/ui/checkbox/checkbox.svelte +0 -39
  327. package/src/lib/components/ui/collapsible/collapsible-content.svelte +0 -7
  328. package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +0 -7
  329. package/src/lib/components/ui/collapsible/collapsible.svelte +0 -11
  330. package/src/lib/components/ui/dialog/dialog-close.svelte +0 -11
  331. package/src/lib/components/ui/dialog/dialog-content.svelte +0 -42
  332. package/src/lib/components/ui/dialog/dialog-description.svelte +0 -17
  333. package/src/lib/components/ui/dialog/dialog-footer.svelte +0 -29
  334. package/src/lib/components/ui/dialog/dialog-header.svelte +0 -19
  335. package/src/lib/components/ui/dialog/dialog-overlay.svelte +0 -17
  336. package/src/lib/components/ui/dialog/dialog-portal.svelte +0 -7
  337. package/src/lib/components/ui/dialog/dialog-title.svelte +0 -17
  338. package/src/lib/components/ui/dialog/dialog-trigger.svelte +0 -11
  339. package/src/lib/components/ui/dialog/dialog.svelte +0 -7
  340. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +0 -16
  341. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -40
  342. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -27
  343. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +0 -18
  344. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +0 -7
  345. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -24
  346. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +0 -20
  347. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +0 -7
  348. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +0 -16
  349. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +0 -34
  350. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -17
  351. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +0 -19
  352. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +0 -17
  353. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +0 -27
  354. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +0 -7
  355. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -7
  356. package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +0 -7
  357. package/src/lib/components/ui/input/input.svelte +0 -40
  358. package/src/lib/components/ui/label/label.svelte +0 -20
  359. package/src/lib/components/ui/panel/panel-body.svelte +0 -13
  360. package/src/lib/components/ui/panel/panel-close.svelte +0 -16
  361. package/src/lib/components/ui/panel/panel-content.svelte +0 -33
  362. package/src/lib/components/ui/panel/panel-footer.svelte +0 -13
  363. package/src/lib/components/ui/panel/panel-header.svelte +0 -16
  364. package/src/lib/components/ui/panel/panel-title.svelte +0 -14
  365. package/src/lib/components/ui/panel/panel.svelte +0 -15
  366. package/src/lib/components/ui/popover/popover-close.svelte +0 -7
  367. package/src/lib/components/ui/popover/popover-content.svelte +0 -27
  368. package/src/lib/components/ui/popover/popover-description.svelte +0 -19
  369. package/src/lib/components/ui/popover/popover-header.svelte +0 -19
  370. package/src/lib/components/ui/popover/popover-portal.svelte +0 -7
  371. package/src/lib/components/ui/popover/popover-title.svelte +0 -19
  372. package/src/lib/components/ui/popover/popover-trigger.svelte +0 -17
  373. package/src/lib/components/ui/popover/popover.svelte +0 -7
  374. package/src/lib/components/ui/select/select-content.svelte +0 -40
  375. package/src/lib/components/ui/select/select-group-heading.svelte +0 -19
  376. package/src/lib/components/ui/select/select-group.svelte +0 -17
  377. package/src/lib/components/ui/select/select-item.svelte +0 -38
  378. package/src/lib/components/ui/select/select-label.svelte +0 -18
  379. package/src/lib/components/ui/select/select-portal.svelte +0 -7
  380. package/src/lib/components/ui/select/select-scroll-down-button.svelte +0 -20
  381. package/src/lib/components/ui/select/select-scroll-up-button.svelte +0 -20
  382. package/src/lib/components/ui/select/select-separator.svelte +0 -17
  383. package/src/lib/components/ui/select/select-trigger.svelte +0 -27
  384. package/src/lib/components/ui/select/select.svelte +0 -11
  385. package/src/lib/components/ui/separator/separator.svelte +0 -23
  386. package/src/lib/components/ui/sheet/sheet-close.svelte +0 -7
  387. package/src/lib/components/ui/sheet/sheet-content.svelte +0 -43
  388. package/src/lib/components/ui/sheet/sheet-description.svelte +0 -17
  389. package/src/lib/components/ui/sheet/sheet-footer.svelte +0 -18
  390. package/src/lib/components/ui/sheet/sheet-header.svelte +0 -19
  391. package/src/lib/components/ui/sheet/sheet-overlay.svelte +0 -17
  392. package/src/lib/components/ui/sheet/sheet-portal.svelte +0 -7
  393. package/src/lib/components/ui/sheet/sheet-title.svelte +0 -17
  394. package/src/lib/components/ui/sheet/sheet-trigger.svelte +0 -7
  395. package/src/lib/components/ui/sheet/sheet.svelte +0 -7
  396. package/src/lib/components/ui/textarea/textarea.svelte +0 -21
  397. package/src/lib/components/ui/toggle/toggle.svelte +0 -45
  398. package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +0 -35
  399. package/src/lib/components/ui/toggle-group/toggle-group.svelte +0 -63
  400. package/src/lib/components/ui/tooltip/tooltip-content.svelte +0 -24
  401. package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +0 -27
  402. package/src/lib/components/ui/tooltip/tooltip.svelte +0 -9
  403. package/src/lib/components/ui/trigger-button/trigger-button.svelte +0 -106
  404. package/src/svelte-plugin-ui/components/Icon.svelte +0 -181
  405. package/src/svelte-plugin-ui/components/ModeSwitch.svelte +0 -121
  406. package/src/svelte-plugin-ui/components/ToolbarShell.svelte +0 -150
  407. package/src/svelte-plugin-ui/components/Viewfinder.svelte +0 -1001
  408. package/src/tools/handlers/docs.js +0 -11
  409. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +0 -139
  410. package/src/workshop/features/createFlow/CreateFlowForm.svelte +0 -314
  411. package/src/workshop/features/createPage/CreatePageForm.svelte +0 -249
  412. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +0 -287
  413. package/src/workshop/features/createStory/CreateStoryForm.svelte +0 -161
  414. package/src/workshop/ui/WorkshopPanel.svelte +0 -97
@@ -1,132 +0,0 @@
1
- <!--
2
- ThemeMenuButton — toolbar dropdown for switching the app color scheme.
3
-
4
- Renders a radio group of theme options (System, Light, Dark, etc.),
5
- followed by a separator and "Theme settings" submenu with sync toggles.
6
- -->
7
-
8
- <script lang="ts">
9
- import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
10
- import * as DropdownMenu from './lib/components/ui/dropdown-menu/index.js'
11
- import Icon from './svelte-plugin-ui/components/Icon.svelte'
12
- import { themeState, setTheme, THEMES, type ThemeValue, themeSyncState, setThemeSyncTarget, type ThemeSyncTargets } from './stores/themeStore.js'
13
-
14
- interface Props {
15
- config?: {
16
- ariaLabel?: string
17
- icon?: string
18
- meta?: Record<string, any>
19
- label?: string
20
- menuWidth?: string
21
- }
22
- data?: any
23
- localOnly?: boolean
24
- tabindex?: number
25
- }
26
-
27
- let { config = {}, data, localOnly, tabindex = -1 }: Props = $props()
28
-
29
- let menuOpen = $state(false)
30
- let canvasActive = $state(false)
31
-
32
- function handleSelect(value: ThemeValue) {
33
- setTheme(value)
34
- menuOpen = false
35
- }
36
-
37
- function handleSyncToggle(e: Event, target: keyof ThemeSyncTargets) {
38
- e.preventDefault()
39
- setThemeSyncTarget(target, !$themeSyncState[target])
40
- }
41
-
42
- $effect(() => {
43
- function handleCanvasMounted() {
44
- canvasActive = true
45
- }
46
- function handleCanvasUnmounted() {
47
- canvasActive = false
48
- }
49
- document.addEventListener('storyboard:canvas:mounted', handleCanvasMounted)
50
- document.addEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
51
-
52
- const state = (window as any).__storyboardCanvasBridgeState
53
- canvasActive = state?.active === true
54
- if (!canvasActive) {
55
- document.dispatchEvent(new CustomEvent('storyboard:canvas:status-request'))
56
- }
57
-
58
- return () => {
59
- document.removeEventListener('storyboard:canvas:mounted', handleCanvasMounted)
60
- document.removeEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
61
- }
62
- })
63
- </script>
64
-
65
- <DropdownMenu.Root bind:open={menuOpen}>
66
- <DropdownMenu.Trigger>
67
- {#snippet child({ props })}
68
- <TriggerButton
69
- active={menuOpen}
70
- size="icon-xl"
71
- aria-label={config.ariaLabel || 'Theme'}
72
- {tabindex}
73
- {...props}
74
- >
75
- <Icon name={config.icon || 'primer/sun'} size={16} {...(config.meta || {})} />
76
- </TriggerButton>
77
- {/snippet}
78
- </DropdownMenu.Trigger>
79
-
80
- <DropdownMenu.Content side="top" align="end" sideOffset={16} style={config.menuWidth ? `min-width: ${config.menuWidth}` : undefined} class="min-w-[200px]">
81
- {#if config.label}
82
- <DropdownMenu.Label>{config.label}</DropdownMenu.Label>
83
- {/if}
84
-
85
- <DropdownMenu.RadioGroup value={$themeState.theme}>
86
- {#each THEMES as option (option.value)}
87
- <DropdownMenu.RadioItem
88
- value={option.value}
89
- onclick={() => handleSelect(option.value)}
90
- >
91
- {option.name}
92
- </DropdownMenu.RadioItem>
93
- {/each}
94
- </DropdownMenu.RadioGroup>
95
-
96
- <DropdownMenu.Separator />
97
-
98
- <DropdownMenu.Sub>
99
- <DropdownMenu.SubTrigger>Theme settings</DropdownMenu.SubTrigger>
100
- <DropdownMenu.SubContent class="min-w-[180px]">
101
- <DropdownMenu.Label>Apply theme to</DropdownMenu.Label>
102
- {#if canvasActive}
103
- <DropdownMenu.CheckboxItem
104
- checked={$themeSyncState.canvas}
105
- onSelect={(e) => handleSyncToggle(e, 'canvas')}
106
- >
107
- Canvas
108
- </DropdownMenu.CheckboxItem>
109
- {:else}
110
- <DropdownMenu.CheckboxItem
111
- checked={$themeSyncState.prototype}
112
- onSelect={(e) => handleSyncToggle(e, 'prototype')}
113
- >
114
- Prototype
115
- </DropdownMenu.CheckboxItem>
116
- {/if}
117
- <DropdownMenu.CheckboxItem
118
- checked={$themeSyncState.toolbar}
119
- onSelect={(e) => handleSyncToggle(e, 'toolbar')}
120
- >
121
- Tools
122
- </DropdownMenu.CheckboxItem>
123
- <DropdownMenu.CheckboxItem
124
- checked={$themeSyncState.codeBoxes}
125
- onSelect={(e) => handleSyncToggle(e, 'codeBoxes')}
126
- >
127
- Code boxes
128
- </DropdownMenu.CheckboxItem>
129
- </DropdownMenu.SubContent>
130
- </DropdownMenu.Sub>
131
- </DropdownMenu.Content>
132
- </DropdownMenu.Root>
@@ -1,108 +0,0 @@
1
- <!--
2
- AuthModal — PAT entry modal for comments authentication.
3
- Uses shadcn Button, Input, Label, Alert, Avatar.
4
- -->
5
-
6
- <script lang="ts">
7
- import { onMount } from 'svelte'
8
- import { setToken, validateToken } from '../auth.js'
9
- import { getCommentsConfig } from '../config.js'
10
- import { Button } from '../../lib/components/ui/button/index.js'
11
- import { Input } from '../../lib/components/ui/input/index.js'
12
- import { Label } from '../../lib/components/ui/label/index.js'
13
- import * as Alert from '../../lib/components/ui/alert/index.js'
14
- import * as Avatar from '../../lib/components/ui/avatar/index.js'
15
-
16
- interface Props {
17
- onDone?: (user: { login: string; avatarUrl: string }) => void
18
- onClose?: () => void
19
- initialError?: string | null
20
- }
21
-
22
- let { onDone, onClose, initialError = null }: Props = $props()
23
-
24
- let token = $state('')
25
- let submitting = $state(false)
26
- let error: string | null = $state(null)
27
- let user: { login: string; avatarUrl: string } | null = $state(null)
28
- let inputEl: HTMLInputElement | null = $state(null)
29
- const commentsConfig = getCommentsConfig()
30
- const repoOwner = commentsConfig?.repo?.owner || 'github'
31
- const repoName = commentsConfig?.repo?.name || 'storyboard'
32
- const repoSlug = `${repoOwner}/${repoName}`
33
- const tokenTemplateName = 'Storyboard Comments'
34
- const tokenTemplateDescription =
35
- `Token for enabling comments on ${repoSlug} prototype. Configure as:\n\n` +
36
- `Owner: ${repoOwner}\n` +
37
- 'Expiration: 366 days (recommended)\n' +
38
- `Repository access: Only select repositories > ${repoSlug}\n` +
39
- 'Permissions: Repositories > Discussions > Access: Read and Write'
40
- const tokenCreateUrl =
41
- `https://github.com/settings/personal-access-tokens/new?` +
42
- new URLSearchParams({
43
- name: tokenTemplateName,
44
- description: tokenTemplateDescription,
45
- }).toString()
46
-
47
- onMount(() => {
48
- error = initialError
49
- inputEl?.focus()
50
- })
51
-
52
- async function submit() {
53
- const val = token.trim()
54
- if (!val) return
55
- submitting = true; error = null
56
- try { const result = await validateToken(val); setToken(val); user = result }
57
- catch (err: any) { error = err.message }
58
- finally { submitting = false }
59
- }
60
-
61
- function done() { if (user) onDone?.(user) }
62
-
63
- function handleKeydown(e: KeyboardEvent) {
64
- if (e.key === 'Enter' && !user) submit()
65
- }
66
- </script>
67
-
68
- <div class="bg-popover text-popover-foreground border border-border rounded-lg shadow-lg overflow-hidden max-w-[600px] w-full font-sans">
69
- <div class="flex items-center justify-between px-4 py-3 border-b border-border">
70
- <h2 class="text-medium font-semibold">Sign in for comments</h2>
71
- <Button variant="ghost" size="icon" onclick={onClose} aria-label="Close" class="h-7 w-7 text-muted-foreground">&#215;</Button>
72
- </div>
73
- <div class="p-4 space-y-3">
74
- {#if error}<Alert.Root variant="destructive"><Alert.Description>{error}</Alert.Description></Alert.Root>{/if}
75
- <p class="text-sm text-muted-foreground leading-relaxed">
76
- Leave comments for other users to see and respond, and react to! Storyboard comments use Discussions as a back-end and require a GitHub PAT to be enabled.
77
- </p>
78
- <p class="text-sm text-muted-foreground leading-relaxed">
79
- Create a <a class="text-primary underline" href={tokenCreateUrl} target="_blank" rel="noopener">GitHub Fine-Grained Personal Access Token</a> with the settings below to get started:
80
- </p>
81
- <div class="px-3 py-2 bg-muted border border-border rounded text-xs text-muted-foreground leading-relaxed">
82
- <div class="mb-1"><strong class="text-foreground">Fine-grained Personal Access Token</strong></div>
83
- <div>Owner: <code class="px-1 bg-background rounded font-mono text-foreground">{repoOwner}</code></div>
84
- <div>Expiration: <code class="px-1 bg-background rounded font-mono text-foreground">366 days</code> (recommended)</div>
85
- <div>Repository access: <code class="px-1 bg-background rounded font-mono text-foreground">Only select repositories &gt; {repoSlug}</code></div>
86
- <div>Permissions: <code class="px-1 bg-background rounded font-mono text-foreground">Repositories > Discussions > Access: Read and Write</code></div>
87
- </div>
88
- <div class="space-y-1">
89
- <Label for="sb-auth-token-input">Personal Access Token</Label>
90
- <Input id="sb-auth-token-input" type="password" placeholder="github_pat_\u2026 or ghp_\u2026" autocomplete="off" spellcheck="false" class="font-mono" bind:value={token} bind:ref={inputEl} onkeydown={handleKeydown} />
91
- </div>
92
- {#if user}
93
- <div class="flex items-center py-1 gap-3">
94
- <Avatar.Root class="h-10 w-10"><Avatar.Image src={user.avatarUrl} alt={user.login} /><Avatar.Fallback>{user.login[0]?.toUpperCase()}</Avatar.Fallback></Avatar.Root>
95
- <div class="text-sm"><span class="text-foreground">{user.login}</span><span class="block text-xs text-success mt-0.5">&#10003; Signed in</span></div>
96
- </div>
97
- {/if}
98
- <Alert.Root variant="warning" class="bg-amber-100 border-amber-300">
99
- <Alert.Description class="text-amber-700">
100
- ⚠️ Comments are an experimental feature and may be unstable.
101
- </Alert.Description>
102
- </Alert.Root>
103
- </div>
104
- <div class="flex items-center justify-end px-4 py-3 border-t border-border gap-2">
105
- <Button variant="outline" size="sm" onclick={onClose}>Cancel</Button>
106
- <Button size="sm" disabled={submitting} onclick={user ? done : submit}>{user ? 'Done' : (submitting ? 'Validating\u2026' : 'Sign in')}</Button>
107
- </div>
108
- </div>
@@ -1,333 +0,0 @@
1
- <!--
2
- CommentWindow — thread viewer popup showing a comment with replies and reactions.
3
- Uses shadcn-svelte Button, Textarea, Avatar, Badge, Separator.
4
- -->
5
-
6
- <script lang="ts">
7
- import { replyToComment, addReaction, removeReaction, resolveComment, unresolveComment, editComment, editReply, deleteComment } from '../api.js'
8
- import { saveDraft, getDraft, clearDraft, replyDraftKey } from '../commentDrafts.js'
9
- import { Button } from '../../lib/components/ui/button/index.js'
10
- import { Textarea } from '../../lib/components/ui/textarea/index.js'
11
- import * as Avatar from '../../lib/components/ui/avatar/index.js'
12
- import { Separator } from '../../lib/components/ui/separator/index.js'
13
- import { cn } from '../../lib/utils/index.js'
14
-
15
- const REACTION_EMOJI: Record<string, string> = {
16
- THUMBS_UP: '👍', THUMBS_DOWN: '👎', LAUGH: '😄', HOORAY: '🎉',
17
- CONFUSED: '😕', HEART: '❤️', ROCKET: '🚀', EYES: '👀',
18
- }
19
- const emojiEntries = Object.entries(REACTION_EMOJI)
20
-
21
- interface Props {
22
- comment: any
23
- discussion: any
24
- user?: any
25
- onClose?: () => void
26
- onMove?: () => void
27
- winEl?: HTMLElement
28
- }
29
-
30
- let { comment, discussion, user = null, onClose, onMove, winEl }: Props = $props()
31
-
32
- const draftKey = $derived(replyDraftKey(comment.id))
33
-
34
- const resolved = $derived(!!comment.meta?.resolved)
35
- let resolving = $state(false)
36
- let copied = $state(false)
37
- let editing = $state(false)
38
- let editText = $state('')
39
- let saving = $state(false)
40
- const commentText = $derived(comment.text ?? '')
41
- // svelte-ignore state_referenced_locally
42
- let replyText = $state(getDraft(draftKey)?.text ?? '')
43
- let submittingReply = $state(false)
44
- let editingReply = $state(-1)
45
- let editReplyText = $state('')
46
- let savingReply = $state(false)
47
- let pickerTarget: string | null = $state(null)
48
- // svelte-ignore state_referenced_locally
49
- let reactions: any[] = $state([...(comment.reactionGroups ?? [])])
50
- // svelte-ignore state_referenced_locally
51
- let replyReactions: any[][] = $state((comment.replies ?? []).map((r: any) => [...(r.reactionGroups ?? [])]))
52
- // svelte-ignore state_referenced_locally
53
- let replyTexts: string[] = $state((comment.replies ?? []).map((r: any) => r.text ?? r.body ?? ''))
54
-
55
- const replies = $derived(comment.replies ?? [])
56
- const canEdit = $derived(!!(user && comment.author?.login === user.login))
57
- const canReply = $derived(!!(user && discussion))
58
-
59
- function handleReplyBlur() {
60
- if (replyText.trim()) {
61
- saveDraft(draftKey, { type: 'reply', text: replyText })
62
- } else {
63
- clearDraft(draftKey)
64
- }
65
- }
66
-
67
- function timeAgo(dateStr: string) {
68
- return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
69
- }
70
-
71
- function emojiFor(content: string) { return REACTION_EMOJI[content] ?? content }
72
- function isReacted(content: string) { return reactions.some((r: any) => r.content === content && r.viewerHasReacted) }
73
- function isReplyReacted(ri: number, content: string) { return (replyReactions[ri] ?? []).some((r: any) => r.content === content && r.viewerHasReacted) }
74
-
75
- function pillClass(active: boolean) {
76
- return cn(
77
- 'inline-flex items-center text-xs px-2 py-0.5 rounded-full border cursor-pointer transition-colors',
78
- active ? 'border-primary bg-primary/10 text-primary' : 'border-border bg-transparent text-muted-foreground'
79
- )
80
- }
81
-
82
- function emojiPickerBtnClass(active: boolean) {
83
- return cn(
84
- 'flex items-center justify-center w-7 h-7 rounded border-none text-base cursor-pointer transition-colors',
85
- active ? 'bg-primary/10' : 'bg-transparent hover:bg-muted'
86
- )
87
- }
88
-
89
- async function toggleResolve() {
90
- resolving = true
91
- try {
92
- if (resolved) {
93
- await unresolveComment(comment.id, comment._rawBody ?? comment.body ?? '')
94
- comment.meta = { ...comment.meta }; delete comment.meta.resolved
95
- resolving = false; onMove?.()
96
- } else {
97
- await resolveComment(comment.id, comment._rawBody ?? comment.body ?? '')
98
- comment.meta = { ...comment.meta, resolved: true }; onMove?.(); onClose?.()
99
- }
100
- } catch (err) { console.error('[storyboard] Failed to toggle resolve:', err); resolving = false }
101
- }
102
-
103
- function copyLink() {
104
- const url = new URL(window.location.href); url.searchParams.set('comment', comment.id)
105
- navigator.clipboard.writeText(url.toString()).then(() => { copied = true; setTimeout(() => { copied = false }, 2000) }).catch(() => {})
106
- }
107
-
108
- async function saveEdit() {
109
- const t = editText.trim(); if (!t) return; saving = true
110
- try { await editComment(comment.id, comment._rawBody ?? comment.body ?? '', t); comment.text = t; comment._rawBody = null; editing = false } catch (err) { console.error('[storyboard] Failed to edit:', err) } finally { saving = false }
111
- }
112
-
113
- async function toggleReaction(content: string, gi?: number) {
114
- const group = gi !== undefined ? reactions[gi] : reactions.find((r: any) => r.content === content)
115
- const was = group?.viewerHasReacted ?? false
116
- if (was && group) {
117
- group.users = { totalCount: Math.max(0, (group.users?.totalCount ?? 1) - 1) }; group.viewerHasReacted = false
118
- reactions = group.users.totalCount === 0 ? reactions.filter((r: any) => r.content !== content) : [...reactions]
119
- } else if (group) {
120
- group.users = { totalCount: (group.users?.totalCount ?? 0) + 1 }; group.viewerHasReacted = true; reactions = [...reactions]
121
- } else {
122
- reactions = [...reactions, { content, users: { totalCount: 1 }, viewerHasReacted: true }]
123
- }
124
- comment.reactionGroups = reactions
125
- try { if (was) await removeReaction(comment.id, content); else await addReaction(comment.id, content) } catch {}
126
- }
127
-
128
- async function toggleReplyReaction(ri: number, content: string, rgi?: number) {
129
- const reply = replies[ri]; if (!reply) return
130
- const groups = replyReactions[ri] ?? []
131
- const group = rgi !== undefined ? groups[rgi] : groups.find((r: any) => r.content === content)
132
- const was = group?.viewerHasReacted ?? false
133
- if (was && group) {
134
- group.users = { totalCount: Math.max(0, (group.users?.totalCount ?? 1) - 1) }; group.viewerHasReacted = false
135
- if (group.users.totalCount === 0) replyReactions[ri] = groups.filter((r: any) => r.content !== content)
136
- } else if (group) {
137
- group.users = { totalCount: (group.users?.totalCount ?? 0) + 1 }; group.viewerHasReacted = true
138
- } else {
139
- groups.push({ content, users: { totalCount: 1 }, viewerHasReacted: true }); replyReactions[ri] = groups
140
- }
141
- replyReactions = [...replyReactions]; reply.reactionGroups = replyReactions[ri]
142
- try { if (was) await removeReaction(reply.id, content); else await addReaction(reply.id, content) } catch {}
143
- }
144
-
145
- async function submitReply() {
146
- const t = replyText.trim(); if (!t) return; submittingReply = true
147
- try {
148
- await replyToComment(discussion.id, comment.id, t)
149
- replyText = ''
150
- clearDraft(draftKey)
151
- onMove?.()
152
- } catch (err) { console.error('[storyboard] Reply failed:', err) } finally { submittingReply = false }
153
- }
154
-
155
- async function saveReply(ri: number) {
156
- const t = editReplyText.trim(); if (!t) return
157
- const reply = replies[ri]; if (!reply) return; savingReply = true
158
- try { await editReply(reply.id, t); reply.text = t; reply.body = t; replyTexts[ri] = t; replyTexts = [...replyTexts]; editingReply = -1 } catch (err) { console.error('[storyboard] Edit reply failed:', err) } finally { savingReply = false }
159
- }
160
-
161
- async function deleteReplyAt(ri: number) {
162
- const reply = replies[ri]; if (!reply || !confirm('Delete this reply?')) return
163
- try { await deleteComment(reply.id); onMove?.() } catch (err) { console.error('[storyboard] Delete failed:', err) }
164
- }
165
-
166
- function startDrag(e: MouseEvent) {
167
- const target = e.target as HTMLElement
168
- if (target.closest('[data-no-drag]') || !winEl) return
169
- const startX = e.clientX, startY = e.clientY, rect = winEl.getBoundingClientRect()
170
- const sx = rect.left, sy = rect.top; target.style.cursor = 'grabbing'
171
- const mv = (ev: MouseEvent) => { winEl!.style.position = 'fixed'; winEl!.style.left = `${sx + ev.clientX - startX}px`; winEl!.style.top = `${sy + ev.clientY - startY}px`; winEl!.style.transform = 'none' }
172
- const up = () => { target.style.cursor = 'grab'; document.removeEventListener('mousemove', mv); document.removeEventListener('mouseup', up) }
173
- document.addEventListener('mousemove', mv); document.addEventListener('mouseup', up); e.preventDefault()
174
- }
175
-
176
- function handleReplyKeydown(e: KeyboardEvent) {
177
- if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); submitReply() }
178
- }
179
- </script>
180
-
181
- <!-- svelte-ignore a11y_no_static_element_interactions -->
182
- <!-- svelte-ignore a11y_click_events_have_key_events -->
183
- <div class="font-sans" onclick={(e) => e.stopPropagation()}>
184
- <!-- Header -->
185
- <!-- svelte-ignore a11y_no_static_element_interactions -->
186
- <!-- svelte-ignore a11y_click_events_have_key_events -->
187
- <div class="flex items-center justify-between px-3 py-3 border-b border-border cursor-grab select-none" onmousedown={startDrag}>
188
- <div class="flex items-center gap-2">
189
- {#if comment.author?.avatarUrl}
190
- <Avatar.Root class="h-6 w-6">
191
- <Avatar.Image src={comment.author.avatarUrl} alt={comment.author?.login} />
192
- <Avatar.Fallback class="text-[10px]">{(comment.author?.login ?? '?')[0]?.toUpperCase()}</Avatar.Fallback>
193
- </Avatar.Root>
194
- {/if}
195
- <div class="flex flex-col">
196
- <span class="text-xs font-semibold">{comment.author?.login ?? 'unknown'}</span>
197
- {#if comment.createdAt}
198
- <span class="text-[11px] text-muted-foreground leading-tight">{timeAgo(comment.createdAt)}</span>
199
- {/if}
200
- </div>
201
- </div>
202
- <!-- svelte-ignore a11y_no_static_element_interactions -->
203
- <div class="flex items-center shrink-0 gap-1.5" data-no-drag onmousedown={(e) => e.stopPropagation()}>
204
- <Button variant="ghost" size="sm" class={cn('h-6 px-2 text-[11px]', resolved ? 'text-success' : 'text-muted-foreground')} disabled={resolving} onclick={toggleResolve}>
205
- {resolving ? (resolved ? 'Unresolving…' : 'Resolving…') : (resolved ? 'Resolved ✓' : 'Resolve')}
206
- </Button>
207
- <Button variant="ghost" size="sm" class={cn('h-6 px-2 text-[11px]', copied ? 'text-success' : 'text-muted-foreground')} onclick={copyLink}>
208
- {copied ? 'Copied!' : 'Copy link'}
209
- </Button>
210
- <Button variant="ghost" size="icon" class="h-6 w-6 text-muted-foreground" aria-label="Close" onclick={onClose}>×</Button>
211
- </div>
212
- </div>
213
-
214
- <!-- Body -->
215
- <div class="flex-auto overflow-y-auto px-3 pt-3 no-scrollbar">
216
- {#if !editing}
217
- <p class="text-sm leading-relaxed m-0 mb-2 break-words">{commentText}</p>
218
- {:else}
219
- <div class="mb-2">
220
- <Textarea class="min-h-[40px] max-h-[100px] text-xs mb-2" bind:value={editText} />
221
- <div class="flex justify-end gap-1">
222
- <Button variant="outline" size="sm" class="h-6 text-xs border border-input text-foreground" onclick={() => editing = false}>Cancel</Button>
223
- <Button size="sm" class="h-6 text-xs" disabled={saving} onclick={saveEdit}>{saving ? 'Saving…' : 'Save'}</Button>
224
- </div>
225
- </div>
226
- {/if}
227
-
228
- <!-- Reactions -->
229
- <div class="flex items-center flex-wrap gap-1 mb-2">
230
- {#each reactions as group, gi (group.content)}
231
- {#if (group.users?.totalCount ?? 0) > 0}
232
- <button class={pillClass(group.viewerHasReacted)}
233
- onclick={(e) => { e.stopPropagation(); toggleReaction(group.content, gi) }}>
234
- <span class="mr-1">{emojiFor(group.content)}</span><span>{group.users?.totalCount ?? 0}</span>
235
- </button>
236
- {/if}
237
- {/each}
238
- <div class="relative">
239
- <button class="inline-flex items-center text-xs px-2 py-0.5 rounded-full border border-border bg-transparent text-muted-foreground cursor-pointer hover:border-primary"
240
- onclick={(e) => { e.stopPropagation(); pickerTarget = pickerTarget === 'comment' ? null : 'comment' }}>😀 +</button>
241
- {#if pickerTarget === 'comment'}
242
- <div class="absolute bottom-full left-0 mb-1 flex gap-0.5 p-1 bg-popover border border-border rounded-lg shadow-lg z-10">
243
- {#each emojiEntries as [key, emoji] (key)}
244
- <button class={emojiPickerBtnClass(isReacted(key))}
245
- onclick={(e) => { e.stopPropagation(); toggleReaction(key); pickerTarget = null }}>{emoji}</button>
246
- {/each}
247
- </div>
248
- {/if}
249
- </div>
250
- {#if !editing && canEdit}
251
- <button class="ml-auto text-xs text-muted-foreground bg-transparent border-none cursor-pointer hover:underline" onclick={() => { editing = true; editText = commentText }}>Edit</button>
252
- {/if}
253
- </div>
254
-
255
- <!-- Replies -->
256
- {#if replies.length > 0}
257
- <Separator class="my-2" />
258
- <div class="text-[11px] font-semibold text-muted-foreground uppercase tracking-wider mb-2">{replies.length} {replies.length === 1 ? 'Reply' : 'Replies'}</div>
259
- {#each replies as reply, ri (reply.id ?? ri)}
260
- <div class="flex mb-2 gap-2">
261
- {#if reply.author?.avatarUrl}
262
- <Avatar.Root class="h-5 w-5 shrink-0">
263
- <Avatar.Image src={reply.author.avatarUrl} alt={reply.author?.login} />
264
- <Avatar.Fallback class="text-[10px]">{(reply.author?.login ?? '?')[0]?.toUpperCase()}</Avatar.Fallback>
265
- </Avatar.Root>
266
- {/if}
267
- <div class="flex-auto min-w-0">
268
- <div class="flex items-start justify-between mb-0.5">
269
- <div class="flex flex-col">
270
- <span class="text-xs font-semibold">{reply.author?.login ?? 'unknown'}</span>
271
- {#if reply.createdAt}<span class="text-[11px] text-muted-foreground leading-tight">{timeAgo(reply.createdAt)}</span>{/if}
272
- </div>
273
- {#if user && reply.author?.login === user.login}
274
- <div class="flex gap-2 ml-auto shrink-0">
275
- {#if editingReply !== ri}
276
- <button class="text-[11px] text-muted-foreground bg-transparent border-none cursor-pointer hover:underline" onclick={() => { editingReply = ri; editReplyText = replyTexts[ri] }}>Edit</button>
277
- <button class="text-[11px] text-destructive bg-transparent border-none cursor-pointer hover:underline" onclick={() => deleteReplyAt(ri)}>Delete</button>
278
- {/if}
279
- </div>
280
- {/if}
281
- </div>
282
- {#if editingReply !== ri}
283
- <p class="text-sm leading-relaxed m-0 break-words">{replyTexts[ri] ?? reply.text ?? reply.body}</p>
284
- {:else}
285
- <div>
286
- <Textarea class="min-h-[40px] max-h-[100px] text-xs mb-1" bind:value={editReplyText} />
287
- <div class="flex justify-end gap-1">
288
- <Button variant="outline" size="sm" class="h-6 text-xs border border-input text-foreground" onclick={() => editingReply = -1}>Cancel</Button>
289
- <Button size="sm" class="h-6 text-xs" disabled={savingReply} onclick={() => saveReply(ri)}>{savingReply ? 'Saving…' : 'Save'}</Button>
290
- </div>
291
- </div>
292
- {/if}
293
- <!-- Reply reactions -->
294
- <div class="flex items-center flex-wrap gap-1 mt-1">
295
- {#each (replyReactions[ri] ?? []) as rg, rgi (rg.content)}
296
- {#if (rg.users?.totalCount ?? 0) > 0}
297
- <button class={pillClass(rg.viewerHasReacted)}
298
- onclick={(e) => { e.stopPropagation(); toggleReplyReaction(ri, rg.content, rgi) }}>
299
- <span class="mr-1">{emojiFor(rg.content)}</span><span>{rg.users?.totalCount ?? 0}</span>
300
- </button>
301
- {/if}
302
- {/each}
303
- <div class="relative">
304
- <button class="inline-flex items-center text-xs px-2 py-0.5 rounded-full border border-border bg-transparent text-muted-foreground cursor-pointer"
305
- onclick={(e) => { e.stopPropagation(); pickerTarget = pickerTarget === `reply-${ri}` ? null : `reply-${ri}` }}>😀 +</button>
306
- {#if pickerTarget === `reply-${ri}`}
307
- <div class="absolute bottom-full left-0 mb-1 flex gap-0.5 p-1 bg-popover border border-border rounded-lg shadow-lg z-10">
308
- {#each emojiEntries as [key, emoji] (key)}
309
- <button class={emojiPickerBtnClass(isReplyReacted(ri, key))}
310
- onclick={(e) => { e.stopPropagation(); toggleReplyReaction(ri, key); pickerTarget = null }}>{emoji}</button>
311
- {/each}
312
- </div>
313
- {/if}
314
- </div>
315
- </div>
316
- </div>
317
- </div>
318
- {/each}
319
- {/if}
320
- </div>
321
-
322
- <!-- Reply form -->
323
- {#if canReply}
324
- <div class="border-t border-border px-3 py-3 flex flex-col">
325
- <Textarea class="min-h-[40px] max-h-[100px] text-xs mb-1" placeholder="Reply…" bind:value={replyText} onkeydown={handleReplyKeydown} onblur={handleReplyBlur} />
326
- <div class="flex justify-end mt-1">
327
- <Button size="sm" class="text-xs" disabled={!replyText.trim() || submittingReply} onclick={submitReply}>
328
- {submittingReply ? 'Posting…' : 'Reply'}
329
- </Button>
330
- </div>
331
- </div>
332
- {/if}
333
- </div>
@@ -1,96 +0,0 @@
1
- <!--
2
- CommentsDrawer — right-side panel listing all comments across all routes.
3
- Uses shadcn Avatar, Badge, Button.
4
- -->
5
-
6
- <script lang="ts">
7
- import { onMount } from 'svelte'
8
- import { listDiscussions, fetchRouteDiscussion } from '../api.js'
9
- import { getCommentsConfig } from '../config.js'
10
- import * as Avatar from '../../lib/components/ui/avatar/index.js'
11
- import { Badge } from '../../lib/components/ui/badge/index.js'
12
- import { Button } from '../../lib/components/ui/button/index.js'
13
-
14
- interface Props {
15
- onClose?: () => void
16
- onNavigate?: (route: string, commentId: string) => void
17
- }
18
- let { onClose, onNavigate }: Props = $props()
19
-
20
- interface CommentGroup { route: string; comments: any[] }
21
-
22
- let loading = $state(true)
23
- let error: string | null = $state(null)
24
- let groups: CommentGroup[] = $state([])
25
-
26
- function timeAgo(dateStr: string) {
27
- return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
28
- }
29
-
30
- onMount(async () => {
31
- try {
32
- const discussions = await listDiscussions()
33
- if (!discussions || discussions.length === 0) { loading = false; return }
34
- const basePath = getCommentsConfig()?.basePath ?? '/'
35
- const result: CommentGroup[] = []
36
- for (const disc of discussions) {
37
- const routeMatch = disc.title?.match(/^Comments:\s*(.+)$/)
38
- if (!routeMatch) continue
39
- const route = routeMatch[1]
40
- if (!route.startsWith(basePath)) continue
41
- let discussion: any
42
- try { discussion = await fetchRouteDiscussion(route) } catch { continue }
43
- if (!discussion?.comments?.length) continue
44
- result.push({ route, comments: discussion.comments })
45
- }
46
- groups = result
47
- } catch (err: any) { error = err.message } finally { loading = false }
48
- })
49
- </script>
50
-
51
- <div class="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
52
- <h2 class="text-base font-semibold">All Comments</h2>
53
- <Button variant="ghost" size="icon" class="h-7 w-7 text-muted-foreground" aria-label="Close" onclick={onClose}>&#215;</Button>
54
- </div>
55
-
56
- <div class="flex-auto overflow-y-auto">
57
- {#if loading}
58
- <div class="py-8 px-4 text-center text-sm text-muted-foreground">Loading comments&#8230;</div>
59
- {:else if error}
60
- <div class="py-8 px-4 text-center text-sm text-muted-foreground">Failed to load comments: {error}</div>
61
- {:else if groups.length === 0}
62
- <div class="py-8 px-4 text-center text-sm text-muted-foreground">No comments yet</div>
63
- {:else}
64
- {#each groups as group (group.route)}
65
- <div class="border-b border-border">
66
- <div class="flex items-center px-4 py-2 bg-muted/50 text-xs font-semibold text-muted-foreground">
67
- <code class="text-primary">{group.route}</code>
68
- <span class="ml-auto font-normal whitespace-nowrap">{group.comments.length} {group.comments.length !== 1 ? 'comments' : 'comment'}</span>
69
- </div>
70
- {#each group.comments as comment (comment.id)}
71
- <button class="flex px-4 py-2 cursor-pointer border-none bg-transparent w-full text-left font-sans hover:bg-accent/50 transition-colors"
72
- class:opacity-60={comment.meta?.resolved}
73
- onclick={() => onNavigate?.(group.route, comment.id)}>
74
- {#if comment.author?.avatarUrl}
75
- <Avatar.Root class="h-6 w-6 shrink-0 mr-2">
76
- <Avatar.Image src={comment.author.avatarUrl} alt={comment.author?.login ?? ''} />
77
- <Avatar.Fallback class="text-[10px]">{(comment.author?.login ?? '?')[0]?.toUpperCase()}</Avatar.Fallback>
78
- </Avatar.Root>
79
- {/if}
80
- <div class="flex flex-col flex-auto min-w-0 gap-0.5">
81
- <div class="flex items-center gap-1">
82
- <span class="text-xs font-semibold" class:text-foreground={!comment.meta?.resolved} class:text-muted-foreground={comment.meta?.resolved}>{comment.author?.login ?? 'unknown'}</span>
83
- {#if comment.createdAt}<span class="text-[11px] text-muted-foreground">{timeAgo(comment.createdAt)}</span>{/if}
84
- {#if comment.meta?.resolved}<Badge variant="outline" class="text-success text-[10px] px-1 py-0">Resolved</Badge>{/if}
85
- </div>
86
- <p class="text-sm leading-snug overflow-hidden whitespace-nowrap text-ellipsis m-0" class:text-foreground={!comment.meta?.resolved} class:text-muted-foreground={comment.meta?.resolved}>{(comment.text ?? '').slice(0, 100)}</p>
87
- {#if (comment.replies?.length ?? 0) > 0}
88
- <div class="text-[11px] text-muted-foreground mt-0.5">&#128172; {comment.replies.length} {comment.replies.length === 1 ? 'reply' : 'replies'}</div>
89
- {/if}
90
- </div>
91
- </button>
92
- {/each}
93
- </div>
94
- {/each}
95
- {/if}
96
- </div>