@dfosco/storyboard 0.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/commandpalette.config.json +152 -0
  2. package/dist/storyboard-ui.css +1 -0
  3. package/dist/storyboard-ui.js +21328 -0
  4. package/dist/storyboard-ui.js.map +1 -0
  5. package/dist/tailwind.css +2 -0
  6. package/dist/tiny-canvas.css +1 -0
  7. package/dist/tiny-canvas.js +389 -0
  8. package/package.json +121 -0
  9. package/paste.config.json +67 -0
  10. package/scaffold/AGENTS.md +432 -0
  11. package/scaffold/agents/prompt-agent.agent.md +181 -0
  12. package/scaffold/agents/terminal-agent.agent.md +351 -0
  13. package/scaffold/codex/config.toml +246 -0
  14. package/scaffold/deploy.yml +103 -0
  15. package/scaffold/githooks/pre-push +114 -0
  16. package/scaffold/gitignore +64 -0
  17. package/scaffold/manifest.json +56 -0
  18. package/scaffold/preview.yml +181 -0
  19. package/scaffold/scripts/link.sh +26 -0
  20. package/scaffold/scripts/unlink.sh +10 -0
  21. package/scaffold/skills/agent-browser/SKILL.md +260 -0
  22. package/scaffold/skills/canvas/SKILL.md +364 -0
  23. package/scaffold/skills/create/SKILL.md +501 -0
  24. package/scaffold/skills/ship/SKILL.md +237 -0
  25. package/scaffold/skills/storyboard/SKILL.md +360 -0
  26. package/scaffold/skills/update-storyboard/SKILL.md +16 -0
  27. package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
  28. package/scaffold/skills/vitest/GENERATION.md +5 -0
  29. package/scaffold/skills/vitest/SKILL.md +52 -0
  30. package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
  31. package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
  32. package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
  33. package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
  34. package/scaffold/skills/vitest/references/core-cli.md +166 -0
  35. package/scaffold/skills/vitest/references/core-config.md +174 -0
  36. package/scaffold/skills/vitest/references/core-describe.md +193 -0
  37. package/scaffold/skills/vitest/references/core-expect.md +219 -0
  38. package/scaffold/skills/vitest/references/core-hooks.md +244 -0
  39. package/scaffold/skills/vitest/references/core-test-api.md +233 -0
  40. package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
  41. package/scaffold/skills/vitest/references/features-context.md +238 -0
  42. package/scaffold/skills/vitest/references/features-coverage.md +207 -0
  43. package/scaffold/skills/vitest/references/features-filtering.md +211 -0
  44. package/scaffold/skills/vitest/references/features-mocking.md +265 -0
  45. package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
  46. package/scaffold/skills/worktree/SKILL.md +93 -0
  47. package/scaffold/storyboard.config.json +44 -0
  48. package/src/canvas/Canvas.jsx +78 -0
  49. package/src/canvas/Draggable.jsx +235 -0
  50. package/src/canvas/index.d.ts +41 -0
  51. package/src/canvas/index.js +6 -0
  52. package/src/canvas/style.css +118 -0
  53. package/src/canvas/useResetCanvas.js +17 -0
  54. package/src/canvas/utils.js +136 -0
  55. package/src/core/assets/fonts/IoskeleyMono-Bold.woff2 +0 -0
  56. package/src/core/assets/fonts/IoskeleyMono-Italic.woff2 +0 -0
  57. package/src/core/assets/fonts/IoskeleyMono-Medium.woff2 +0 -0
  58. package/src/core/assets/fonts/IoskeleyMono-Regular.woff2 +0 -0
  59. package/src/core/assets/fonts/IoskeleyMono-SemiBold.woff2 +0 -0
  60. package/src/core/autosync/server.js +714 -0
  61. package/src/core/autosync/server.test.js +158 -0
  62. package/src/core/canvas/__tests__/agent-integration.test.js +596 -0
  63. package/src/core/canvas/__tests__/helpers/browser.js +95 -0
  64. package/src/core/canvas/__tests__/helpers/canvas-api.js +129 -0
  65. package/src/core/canvas/__tests__/helpers/perf.js +118 -0
  66. package/src/core/canvas/__tests__/helpers/setup.js +176 -0
  67. package/src/core/canvas/__tests__/helpers/tmux.js +130 -0
  68. package/src/core/canvas/__tests__/helpers/transcript.js +132 -0
  69. package/src/core/canvas/__tests__/terminal-integration.test.js +177 -0
  70. package/src/core/canvas/collision.js +292 -0
  71. package/src/core/canvas/collision.test.js +371 -0
  72. package/src/core/canvas/compact.js +83 -0
  73. package/src/core/canvas/deriveCanvasId.test.js +40 -0
  74. package/src/core/canvas/githubEmbeds.js +527 -0
  75. package/src/core/canvas/githubEmbeds.test.js +302 -0
  76. package/src/core/canvas/hot-pool.js +766 -0
  77. package/src/core/canvas/identity.js +107 -0
  78. package/src/core/canvas/identity.test.js +100 -0
  79. package/src/core/canvas/materializer.js +259 -0
  80. package/src/core/canvas/materializer.test.js +356 -0
  81. package/src/core/canvas/selectedWidgets.js +270 -0
  82. package/src/core/canvas/selectedWidgets.test.js +321 -0
  83. package/src/core/canvas/server.js +3134 -0
  84. package/src/core/canvas/server.test.js +379 -0
  85. package/src/core/canvas/terminal-config.js +330 -0
  86. package/src/core/canvas/terminal-registry.js +465 -0
  87. package/src/core/canvas/terminal-server.js +1436 -0
  88. package/src/core/canvas/writeGuard.js +53 -0
  89. package/src/core/cli/agent.js +85 -0
  90. package/src/core/cli/branch.js +386 -0
  91. package/src/core/cli/canvasAdd.js +241 -0
  92. package/src/core/cli/canvasBatch.js +98 -0
  93. package/src/core/cli/canvasBounds.js +160 -0
  94. package/src/core/cli/canvasRead.js +236 -0
  95. package/src/core/cli/canvasUpdate.js +179 -0
  96. package/src/core/cli/code.js +67 -0
  97. package/src/core/cli/compact.js +62 -0
  98. package/src/core/cli/create.js +674 -0
  99. package/src/core/cli/dev-helpers.js +53 -0
  100. package/src/core/cli/dev-helpers.test.js +53 -0
  101. package/src/core/cli/dev.js +430 -0
  102. package/src/core/cli/exit.js +38 -0
  103. package/src/core/cli/flags.js +174 -0
  104. package/src/core/cli/flags.test.js +155 -0
  105. package/src/core/cli/index.js +233 -0
  106. package/src/core/cli/intro.js +37 -0
  107. package/src/core/cli/proxy.js +319 -0
  108. package/src/core/cli/proxy.test.js +63 -0
  109. package/src/core/cli/schemas.js +223 -0
  110. package/src/core/cli/server.js +192 -0
  111. package/src/core/cli/serverUrl.js +61 -0
  112. package/src/core/cli/sessions.js +459 -0
  113. package/src/core/cli/setup.js +404 -0
  114. package/src/core/cli/terminal-commands.js +287 -0
  115. package/src/core/cli/terminal-messaging.js +231 -0
  116. package/src/core/cli/terminal-welcome.js +515 -0
  117. package/src/core/cli/updateVersion.js +124 -0
  118. package/src/core/comments/api.js +284 -0
  119. package/src/core/comments/api.test.js +282 -0
  120. package/src/core/comments/auth.js +151 -0
  121. package/src/core/comments/auth.test.js +167 -0
  122. package/src/core/comments/commentCache.js +109 -0
  123. package/src/core/comments/commentCache.test.js +48 -0
  124. package/src/core/comments/commentDrafts.js +68 -0
  125. package/src/core/comments/commentMode.js +63 -0
  126. package/src/core/comments/commentMode.test.js +90 -0
  127. package/src/core/comments/config.js +47 -0
  128. package/src/core/comments/config.test.js +77 -0
  129. package/src/core/comments/graphql.js +65 -0
  130. package/src/core/comments/graphql.test.js +95 -0
  131. package/src/core/comments/index.js +42 -0
  132. package/src/core/comments/metadata.js +52 -0
  133. package/src/core/comments/metadata.test.js +110 -0
  134. package/src/core/comments/queries.js +245 -0
  135. package/src/core/comments/ui/AuthModal.jsx +114 -0
  136. package/src/core/comments/ui/CommentOverlay.js +52 -0
  137. package/src/core/comments/ui/CommentWindow.jsx +329 -0
  138. package/src/core/comments/ui/CommentsDrawer.jsx +102 -0
  139. package/src/core/comments/ui/Composer.jsx +64 -0
  140. package/src/core/comments/ui/authModal.js +66 -0
  141. package/src/core/comments/ui/authModal.test.js +76 -0
  142. package/src/core/comments/ui/comment-cursor-dark.svg +1 -0
  143. package/src/core/comments/ui/comment-cursor.svg +1 -0
  144. package/src/core/comments/ui/comment-layout.css +142 -0
  145. package/src/core/comments/ui/commentWindow.js +121 -0
  146. package/src/core/comments/ui/comments.css +242 -0
  147. package/src/core/comments/ui/commentsDrawer.js +84 -0
  148. package/src/core/comments/ui/composer.js +136 -0
  149. package/src/core/comments/ui/index.js +14 -0
  150. package/src/core/comments/ui/mount.js +687 -0
  151. package/src/core/comments/ui/mount.test.js +336 -0
  152. package/src/core/data/dotPath.js +53 -0
  153. package/src/core/data/dotPath.test.js +114 -0
  154. package/src/core/data/loader.js +409 -0
  155. package/src/core/data/loader.test.js +599 -0
  156. package/src/core/data/viewfinder.js +363 -0
  157. package/src/core/data/viewfinder.test.js +456 -0
  158. package/src/core/devtools/devtools-consumer.js +28 -0
  159. package/src/core/devtools/devtools.js +144 -0
  160. package/src/core/devtools/devtools.test.js +75 -0
  161. package/src/core/devtools/sceneDebug.js +112 -0
  162. package/src/core/devtools/sceneDebug.test.js +141 -0
  163. package/src/core/index.js +124 -0
  164. package/src/core/inspector/fiberWalker.js +239 -0
  165. package/src/core/inspector/highlighter.js +275 -0
  166. package/src/core/inspector/mouseMode.js +259 -0
  167. package/src/core/lib/components/ui/alert/alert-action.jsx +11 -0
  168. package/src/core/lib/components/ui/alert/alert-description.jsx +11 -0
  169. package/src/core/lib/components/ui/alert/alert-title.jsx +11 -0
  170. package/src/core/lib/components/ui/alert/alert.jsx +25 -0
  171. package/src/core/lib/components/ui/alert/index.js +17 -0
  172. package/src/core/lib/components/ui/avatar/avatar-badge.jsx +22 -0
  173. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
  174. package/src/core/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
  175. package/src/core/lib/components/ui/avatar/avatar-group.jsx +19 -0
  176. package/src/core/lib/components/ui/avatar/avatar-image.jsx +15 -0
  177. package/src/core/lib/components/ui/avatar/avatar.jsx +19 -0
  178. package/src/core/lib/components/ui/avatar/index.js +22 -0
  179. package/src/core/lib/components/ui/badge/badge.jsx +31 -0
  180. package/src/core/lib/components/ui/badge/index.js +2 -0
  181. package/src/core/lib/components/ui/button/button.jsx +100 -0
  182. package/src/core/lib/components/ui/button/index.js +12 -0
  183. package/src/core/lib/components/ui/card/card-action.jsx +11 -0
  184. package/src/core/lib/components/ui/card/card-content.jsx +11 -0
  185. package/src/core/lib/components/ui/card/card-description.jsx +11 -0
  186. package/src/core/lib/components/ui/card/card-footer.jsx +11 -0
  187. package/src/core/lib/components/ui/card/card-header.jsx +19 -0
  188. package/src/core/lib/components/ui/card/card-title.jsx +11 -0
  189. package/src/core/lib/components/ui/card/card.jsx +17 -0
  190. package/src/core/lib/components/ui/card/index.js +25 -0
  191. package/src/core/lib/components/ui/checkbox/checkbox.jsx +29 -0
  192. package/src/core/lib/components/ui/checkbox/index.js +6 -0
  193. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
  194. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
  195. package/src/core/lib/components/ui/collapsible/collapsible.jsx +7 -0
  196. package/src/core/lib/components/ui/collapsible/index.js +13 -0
  197. package/src/core/lib/components/ui/dialog/dialog-close.jsx +7 -0
  198. package/src/core/lib/components/ui/dialog/dialog-content.jsx +34 -0
  199. package/src/core/lib/components/ui/dialog/dialog-description.jsx +15 -0
  200. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +23 -0
  201. package/src/core/lib/components/ui/dialog/dialog-header.jsx +11 -0
  202. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
  203. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +4 -0
  204. package/src/core/lib/components/ui/dialog/dialog-title.jsx +15 -0
  205. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
  206. package/src/core/lib/components/ui/dialog/dialog.jsx +4 -0
  207. package/src/core/lib/components/ui/dialog/index.js +34 -0
  208. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
  209. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
  210. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
  211. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
  212. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
  213. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
  214. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
  215. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
  216. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
  217. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
  218. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
  219. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
  220. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
  221. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
  222. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
  223. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
  224. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
  225. package/src/core/lib/components/ui/dropdown-menu/index.js +54 -0
  226. package/src/core/lib/components/ui/input/index.js +7 -0
  227. package/src/core/lib/components/ui/input/input.jsx +19 -0
  228. package/src/core/lib/components/ui/label/index.js +7 -0
  229. package/src/core/lib/components/ui/label/label.jsx +19 -0
  230. package/src/core/lib/components/ui/panel/index.js +24 -0
  231. package/src/core/lib/components/ui/panel/panel-body.jsx +11 -0
  232. package/src/core/lib/components/ui/panel/panel-close.jsx +16 -0
  233. package/src/core/lib/components/ui/panel/panel-content.jsx +29 -0
  234. package/src/core/lib/components/ui/panel/panel-footer.jsx +11 -0
  235. package/src/core/lib/components/ui/panel/panel-header.jsx +11 -0
  236. package/src/core/lib/components/ui/panel/panel-title.jsx +12 -0
  237. package/src/core/lib/components/ui/panel/panel.jsx +4 -0
  238. package/src/core/lib/components/ui/popover/index.js +28 -0
  239. package/src/core/lib/components/ui/popover/popover-close.jsx +7 -0
  240. package/src/core/lib/components/ui/popover/popover-content.jsx +22 -0
  241. package/src/core/lib/components/ui/popover/popover-description.jsx +11 -0
  242. package/src/core/lib/components/ui/popover/popover-header.jsx +11 -0
  243. package/src/core/lib/components/ui/popover/popover-portal.jsx +4 -0
  244. package/src/core/lib/components/ui/popover/popover-title.jsx +11 -0
  245. package/src/core/lib/components/ui/popover/popover-trigger.jsx +8 -0
  246. package/src/core/lib/components/ui/popover/popover.jsx +4 -0
  247. package/src/core/lib/components/ui/searchable-list.jsx +160 -0
  248. package/src/core/lib/components/ui/select/index.js +37 -0
  249. package/src/core/lib/components/ui/select/select-content.jsx +30 -0
  250. package/src/core/lib/components/ui/select/select-group-heading.jsx +17 -0
  251. package/src/core/lib/components/ui/select/select-group.jsx +15 -0
  252. package/src/core/lib/components/ui/select/select-item.jsx +26 -0
  253. package/src/core/lib/components/ui/select/select-label.jsx +11 -0
  254. package/src/core/lib/components/ui/select/select-portal.jsx +4 -0
  255. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
  256. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
  257. package/src/core/lib/components/ui/select/select-separator.jsx +15 -0
  258. package/src/core/lib/components/ui/select/select-trigger.jsx +25 -0
  259. package/src/core/lib/components/ui/select/select.jsx +4 -0
  260. package/src/core/lib/components/ui/separator/index.js +7 -0
  261. package/src/core/lib/components/ui/separator/separator.jsx +22 -0
  262. package/src/core/lib/components/ui/sheet/index.js +34 -0
  263. package/src/core/lib/components/ui/sheet/sheet-close.jsx +7 -0
  264. package/src/core/lib/components/ui/sheet/sheet-content.jsx +35 -0
  265. package/src/core/lib/components/ui/sheet/sheet-description.jsx +15 -0
  266. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +11 -0
  267. package/src/core/lib/components/ui/sheet/sheet-header.jsx +11 -0
  268. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
  269. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +4 -0
  270. package/src/core/lib/components/ui/sheet/sheet-title.jsx +15 -0
  271. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
  272. package/src/core/lib/components/ui/sheet/sheet.jsx +4 -0
  273. package/src/core/lib/components/ui/textarea/index.js +7 -0
  274. package/src/core/lib/components/ui/textarea/textarea.jsx +18 -0
  275. package/src/core/lib/components/ui/toggle/index.js +8 -0
  276. package/src/core/lib/components/ui/toggle/toggle.jsx +36 -0
  277. package/src/core/lib/components/ui/toggle-group/index.js +10 -0
  278. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
  279. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
  280. package/src/core/lib/components/ui/tooltip/index.js +3 -0
  281. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
  282. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
  283. package/src/core/lib/components/ui/tooltip/tooltip.jsx +11 -0
  284. package/src/core/lib/components/ui/trigger-button/index.js +6 -0
  285. package/src/core/lib/components/ui/trigger-button/trigger-button.css +38 -0
  286. package/src/core/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
  287. package/src/core/lib/utils/index.js +6 -0
  288. package/src/core/logger/devLogger.js +238 -0
  289. package/src/core/logger/devLogger.test.js +193 -0
  290. package/src/core/modes/modes.css +98 -0
  291. package/src/core/modes/modes.js +492 -0
  292. package/src/core/modes/modes.test.js +562 -0
  293. package/src/core/mountStoryboardCore.js +478 -0
  294. package/src/core/rename-watcher/config.json +23 -0
  295. package/src/core/rename-watcher/watcher.js +531 -0
  296. package/src/core/scaffold.js +100 -0
  297. package/src/core/server/index.js +391 -0
  298. package/src/core/session/bodyClasses.js +128 -0
  299. package/src/core/session/bodyClasses.test.js +192 -0
  300. package/src/core/session/hashSubscribe.js +19 -0
  301. package/src/core/session/hashSubscribe.test.js +62 -0
  302. package/src/core/session/hideMode.js +424 -0
  303. package/src/core/session/hideMode.test.js +268 -0
  304. package/src/core/session/interceptHideParams.js +35 -0
  305. package/src/core/session/interceptHideParams.test.js +90 -0
  306. package/src/core/session/localStorage.js +134 -0
  307. package/src/core/session/localStorage.test.js +148 -0
  308. package/src/core/session/session.js +76 -0
  309. package/src/core/session/session.test.js +91 -0
  310. package/src/core/stores/canvasConfig.js +134 -0
  311. package/src/core/stores/canvasConfig.test.js +120 -0
  312. package/src/core/stores/commandActions.js +284 -0
  313. package/src/core/stores/commandPaletteConfig.js +31 -0
  314. package/src/core/stores/configSchema.js +232 -0
  315. package/src/core/stores/configSchema.test.js +72 -0
  316. package/src/core/stores/configStore.js +161 -0
  317. package/src/core/stores/customerModeConfig.js +30 -0
  318. package/src/core/stores/featureFlags.js +127 -0
  319. package/src/core/stores/paletteProviders.js +360 -0
  320. package/src/core/stores/paletteProviders.test.js +186 -0
  321. package/src/core/stores/plugins.js +40 -0
  322. package/src/core/stores/plugins.test.js +68 -0
  323. package/src/core/stores/recentArtifacts.js +68 -0
  324. package/src/core/stores/recentArtifacts.test.js +71 -0
  325. package/src/core/stores/sidePanelStore.ts +143 -0
  326. package/src/core/stores/themeStore.ts +291 -0
  327. package/src/core/stores/toolRegistry.js +227 -0
  328. package/src/core/stores/toolStateStore.js +183 -0
  329. package/src/core/stores/toolStateStore.test.js +220 -0
  330. package/src/core/stores/toolbarConfigStore.js +165 -0
  331. package/src/core/stores/uiConfig.js +64 -0
  332. package/src/core/stores/uiConfig.test.js +63 -0
  333. package/src/core/styles/tailwind.css +204 -0
  334. package/src/core/tools/handlers/autosync.js +12 -0
  335. package/src/core/tools/handlers/canvasAddWidget.js +11 -0
  336. package/src/core/tools/handlers/canvasAgents.js +20 -0
  337. package/src/core/tools/handlers/canvasToolbar.js +56 -0
  338. package/src/core/tools/handlers/commandPalette.js +9 -0
  339. package/src/core/tools/handlers/comments.js +16 -0
  340. package/src/core/tools/handlers/create.js +39 -0
  341. package/src/core/tools/handlers/devtools.js +122 -0
  342. package/src/core/tools/handlers/devtools.test.js +87 -0
  343. package/src/core/tools/handlers/featureFlags.js +21 -0
  344. package/src/core/tools/handlers/flows.js +68 -0
  345. package/src/core/tools/handlers/hideChrome.js +9 -0
  346. package/src/core/tools/handlers/hideToolbars.js +25 -0
  347. package/src/core/tools/handlers/inspector.js +19 -0
  348. package/src/core/tools/handlers/paletteTheme.js +35 -0
  349. package/src/core/tools/handlers/theme.js +9 -0
  350. package/src/core/tools/registry.js +26 -0
  351. package/src/core/tools/surfaces/canvasToolbar.js +10 -0
  352. package/src/core/tools/surfaces/commandList.js +10 -0
  353. package/src/core/tools/surfaces/mainToolbar.js +11 -0
  354. package/src/core/tools/surfaces/registry.js +19 -0
  355. package/src/core/ui/ActionMenuButton.jsx +114 -0
  356. package/src/core/ui/AutosyncMenuButton.css +67 -0
  357. package/src/core/ui/AutosyncMenuButton.jsx +242 -0
  358. package/src/core/ui/BranchSelect.jsx +29 -0
  359. package/src/core/ui/BranchSelect.module.css +30 -0
  360. package/src/core/ui/CanvasAgentsMenu.jsx +89 -0
  361. package/src/core/ui/CanvasCreateMenu.jsx +611 -0
  362. package/src/core/ui/CanvasSnap.css +27 -0
  363. package/src/core/ui/CanvasSnap.jsx +51 -0
  364. package/src/core/ui/CanvasUndoRedo.css +36 -0
  365. package/src/core/ui/CanvasUndoRedo.jsx +62 -0
  366. package/src/core/ui/CanvasZoomControl.css +53 -0
  367. package/src/core/ui/CanvasZoomControl.jsx +49 -0
  368. package/src/core/ui/CanvasZoomToFit.css +18 -0
  369. package/src/core/ui/CanvasZoomToFit.jsx +26 -0
  370. package/src/core/ui/CommandMenu.css +8 -0
  371. package/src/core/ui/CommandMenu.jsx +287 -0
  372. package/src/core/ui/CommandPalette.jsx +35 -0
  373. package/src/core/ui/CommandPaletteTrigger.jsx +25 -0
  374. package/src/core/ui/CommentsMenuButton.jsx +40 -0
  375. package/src/core/ui/CoreUIBar.css +47 -0
  376. package/src/core/ui/CoreUIBar.jsx +905 -0
  377. package/src/core/ui/CreateMenuButton.jsx +117 -0
  378. package/src/core/ui/HideChromeTrigger.jsx +48 -0
  379. package/src/core/ui/Icon.jsx +279 -0
  380. package/src/core/ui/InspectorPanel.css +109 -0
  381. package/src/core/ui/InspectorPanel.jsx +632 -0
  382. package/src/core/ui/PwaInstallBanner.css +42 -0
  383. package/src/core/ui/PwaInstallBanner.jsx +124 -0
  384. package/src/core/ui/SidePanel.jsx +261 -0
  385. package/src/core/ui/ThemeMenuButton.jsx +139 -0
  386. package/src/core/ui/core-ui-colors.css +129 -0
  387. package/src/core/ui/design-modes.ts +7 -0
  388. package/src/core/ui/sidepanel.css +301 -0
  389. package/src/core/ui/viewfinder.ts +7 -0
  390. package/src/core/ui-entry.js +30 -0
  391. package/src/core/utils/fuzzySearch.js +117 -0
  392. package/src/core/utils/fuzzySearch.test.js +119 -0
  393. package/src/core/utils/mobileViewport.js +57 -0
  394. package/src/core/utils/mobileViewport.test.js +68 -0
  395. package/src/core/utils/prodMode.js +38 -0
  396. package/src/core/utils/smoothCorners.js +20 -0
  397. package/src/core/vite/docs-handler.js +155 -0
  398. package/src/core/vite/server-plugin.js +797 -0
  399. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
  400. package/src/core/workshop/features/createCanvas/index.js +14 -0
  401. package/src/core/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
  402. package/src/core/workshop/features/createFlow/index.js +19 -0
  403. package/src/core/workshop/features/createFlow/server.js +663 -0
  404. package/src/core/workshop/features/createPage/CreatePageForm.jsx +304 -0
  405. package/src/core/workshop/features/createPage/index.js +11 -0
  406. package/src/core/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
  407. package/src/core/workshop/features/createPrototype/index.js +19 -0
  408. package/src/core/workshop/features/createPrototype/server.js +433 -0
  409. package/src/core/workshop/features/createStory/CreateStoryForm.jsx +208 -0
  410. package/src/core/workshop/features/createStory/index.js +14 -0
  411. package/src/core/workshop/features/registry-server.js +22 -0
  412. package/src/core/workshop/features/registry.js +28 -0
  413. package/src/core/workshop/features/templateIndex.js +155 -0
  414. package/src/core/workshop/ui/WorkshopPanel.jsx +98 -0
  415. package/src/core/workshop/ui/mount.ts +6 -0
  416. package/src/core/worktree/port.js +268 -0
  417. package/src/core/worktree/port.test.js +222 -0
  418. package/src/core/worktree/serverRegistry.js +120 -0
  419. package/src/internals/AuthModal/AuthModal.jsx +132 -0
  420. package/src/internals/AuthModal/AuthModal.module.css +221 -0
  421. package/src/internals/BranchBar/BranchBar.jsx +87 -0
  422. package/src/internals/BranchBar/BranchBar.module.css +247 -0
  423. package/src/internals/BranchBar/useBranches.js +93 -0
  424. package/src/internals/BranchBar/useBranches.test.js +68 -0
  425. package/src/internals/CommandPalette/CommandPalette.jsx +1361 -0
  426. package/src/internals/CommandPalette/CreateDialog.jsx +219 -0
  427. package/src/internals/CommandPalette/command-palette.css +180 -0
  428. package/src/internals/FlowError.module.css +30 -0
  429. package/src/internals/Icon.jsx +279 -0
  430. package/src/internals/StoryboardContext.js +3 -0
  431. package/src/internals/Viewfinder.jsx +1479 -0
  432. package/src/internals/Viewfinder.module.css +1540 -0
  433. package/src/internals/Workspace.jsx +7 -0
  434. package/src/internals/__mocks__/virtual-storyboard-data-index.js +4 -0
  435. package/src/internals/canvas/CanvasControls.jsx +112 -0
  436. package/src/internals/canvas/CanvasControls.module.css +135 -0
  437. package/src/internals/canvas/CanvasPage.bridge.test.jsx +387 -0
  438. package/src/internals/canvas/CanvasPage.dragdrop.test.jsx +350 -0
  439. package/src/internals/canvas/CanvasPage.jsx +3092 -0
  440. package/src/internals/canvas/CanvasPage.module.css +187 -0
  441. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +358 -0
  442. package/src/internals/canvas/CanvasToolbar.jsx +73 -0
  443. package/src/internals/canvas/CanvasToolbar.module.css +92 -0
  444. package/src/internals/canvas/ComponentErrorBoundary.jsx +50 -0
  445. package/src/internals/canvas/ConnectorLayer.jsx +208 -0
  446. package/src/internals/canvas/ConnectorLayer.module.css +129 -0
  447. package/src/internals/canvas/MarqueeOverlay.jsx +20 -0
  448. package/src/internals/canvas/PageSelector.jsx +587 -0
  449. package/src/internals/canvas/PageSelector.module.css +261 -0
  450. package/src/internals/canvas/PageSelector.test.jsx +113 -0
  451. package/src/internals/canvas/WebGLContextPool.jsx +292 -0
  452. package/src/internals/canvas/WebGLContextPool.test.jsx +165 -0
  453. package/src/internals/canvas/canvasApi.js +164 -0
  454. package/src/internals/canvas/canvasReloadGuard.js +37 -0
  455. package/src/internals/canvas/canvasReloadGuard.test.js +27 -0
  456. package/src/internals/canvas/canvasTheme.js +118 -0
  457. package/src/internals/canvas/componentIsolate.jsx +165 -0
  458. package/src/internals/canvas/componentSetIsolate.jsx +257 -0
  459. package/src/internals/canvas/computeCanvasBounds.test.js +121 -0
  460. package/src/internals/canvas/connectorGeometry.js +132 -0
  461. package/src/internals/canvas/hotPoolDevLogs.js +25 -0
  462. package/src/internals/canvas/textSelection.js +10 -0
  463. package/src/internals/canvas/textSelection.test.js +26 -0
  464. package/src/internals/canvas/useCanvas.js +126 -0
  465. package/src/internals/canvas/useCanvas.test.js +26 -0
  466. package/src/internals/canvas/useMarqueeSelect.js +213 -0
  467. package/src/internals/canvas/useMarqueeSelect.test.js +78 -0
  468. package/src/internals/canvas/useUndoRedo.js +86 -0
  469. package/src/internals/canvas/useUndoRedo.test.js +231 -0
  470. package/src/internals/canvas/widgets/CodePenEmbed.jsx +293 -0
  471. package/src/internals/canvas/widgets/CodePenEmbed.module.css +161 -0
  472. package/src/internals/canvas/widgets/ComponentSetWidget.jsx +2 -0
  473. package/src/internals/canvas/widgets/ComponentSetWidget.module.css +89 -0
  474. package/src/internals/canvas/widgets/ComponentWidget.jsx +14 -0
  475. package/src/internals/canvas/widgets/ComponentWidget.module.css +0 -0
  476. package/src/internals/canvas/widgets/CropOverlay.jsx +179 -0
  477. package/src/internals/canvas/widgets/CropOverlay.module.css +154 -0
  478. package/src/internals/canvas/widgets/ExpandedPane.jsx +474 -0
  479. package/src/internals/canvas/widgets/ExpandedPane.module.css +179 -0
  480. package/src/internals/canvas/widgets/ExpandedPane.test.jsx +240 -0
  481. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +111 -0
  482. package/src/internals/canvas/widgets/ExpandedPaneTopBar.module.css +59 -0
  483. package/src/internals/canvas/widgets/ExpandedPaneTopBar.test.jsx +45 -0
  484. package/src/internals/canvas/widgets/FigmaEmbed.jsx +296 -0
  485. package/src/internals/canvas/widgets/FigmaEmbed.module.css +222 -0
  486. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +151 -0
  487. package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +83 -0
  488. package/src/internals/canvas/widgets/ImageWidget.jsx +287 -0
  489. package/src/internals/canvas/widgets/ImageWidget.module.css +81 -0
  490. package/src/internals/canvas/widgets/LinkPreview.jsx +439 -0
  491. package/src/internals/canvas/widgets/LinkPreview.module.css +585 -0
  492. package/src/internals/canvas/widgets/LinkPreview.test.jsx +193 -0
  493. package/src/internals/canvas/widgets/MarkdownBlock.jsx +354 -0
  494. package/src/internals/canvas/widgets/MarkdownBlock.module.css +377 -0
  495. package/src/internals/canvas/widgets/MarkdownBlock.test.jsx +92 -0
  496. package/src/internals/canvas/widgets/PromptWidget.jsx +428 -0
  497. package/src/internals/canvas/widgets/PromptWidget.module.css +273 -0
  498. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +463 -0
  499. package/src/internals/canvas/widgets/PrototypeEmbed.module.css +579 -0
  500. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +10 -0
  501. package/src/internals/canvas/widgets/ResizeHandle.jsx +67 -0
  502. package/src/internals/canvas/widgets/ResizeHandle.module.css +29 -0
  503. package/src/internals/canvas/widgets/StickyNote.jsx +92 -0
  504. package/src/internals/canvas/widgets/StickyNote.module.css +70 -0
  505. package/src/internals/canvas/widgets/StickyNote.test.jsx +116 -0
  506. package/src/internals/canvas/widgets/StorySetWidget.jsx +208 -0
  507. package/src/internals/canvas/widgets/StorySetWidget.module.css +89 -0
  508. package/src/internals/canvas/widgets/StoryWidget.jsx +334 -0
  509. package/src/internals/canvas/widgets/StoryWidget.module.css +211 -0
  510. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +146 -0
  511. package/src/internals/canvas/widgets/TerminalReadWidget.module.css +94 -0
  512. package/src/internals/canvas/widgets/TerminalWidget.jsx +704 -0
  513. package/src/internals/canvas/widgets/TerminalWidget.module.css +444 -0
  514. package/src/internals/canvas/widgets/TilesWidget.jsx +300 -0
  515. package/src/internals/canvas/widgets/TilesWidget.module.css +133 -0
  516. package/src/internals/canvas/widgets/WidgetChrome.jsx +580 -0
  517. package/src/internals/canvas/widgets/WidgetChrome.module.css +421 -0
  518. package/src/internals/canvas/widgets/WidgetWrapper.jsx +15 -0
  519. package/src/internals/canvas/widgets/WidgetWrapper.module.css +25 -0
  520. package/src/internals/canvas/widgets/codepenUrl.js +75 -0
  521. package/src/internals/canvas/widgets/codepenUrl.test.js +76 -0
  522. package/src/internals/canvas/widgets/embedInteraction.test.jsx +173 -0
  523. package/src/internals/canvas/widgets/embedOverlay.module.css +35 -0
  524. package/src/internals/canvas/widgets/embedTheme.js +148 -0
  525. package/src/internals/canvas/widgets/expandUtils.js +559 -0
  526. package/src/internals/canvas/widgets/expandUtils.test.js +155 -0
  527. package/src/internals/canvas/widgets/figmaUrl.js +118 -0
  528. package/src/internals/canvas/widgets/figmaUrl.test.js +139 -0
  529. package/src/internals/canvas/widgets/githubUrl.js +82 -0
  530. package/src/internals/canvas/widgets/githubUrl.test.js +74 -0
  531. package/src/internals/canvas/widgets/iframeDevLogs.js +49 -0
  532. package/src/internals/canvas/widgets/iframeDevLogs.test.jsx +81 -0
  533. package/src/internals/canvas/widgets/index.js +42 -0
  534. package/src/internals/canvas/widgets/pasteRules.js +295 -0
  535. package/src/internals/canvas/widgets/pasteRules.test.js +474 -0
  536. package/src/internals/canvas/widgets/snapshotDisplay.test.jsx +211 -0
  537. package/src/internals/canvas/widgets/tilePool.js +23 -0
  538. package/src/internals/canvas/widgets/tiles/diagonal-bl.png +0 -0
  539. package/src/internals/canvas/widgets/tiles/diagonal-br.png +0 -0
  540. package/src/internals/canvas/widgets/tiles/diagonal-tl.png +0 -0
  541. package/src/internals/canvas/widgets/tiles/leaf.png +0 -0
  542. package/src/internals/canvas/widgets/tiles/quarter-tl.png +0 -0
  543. package/src/internals/canvas/widgets/tiles/quarter-tr.png +0 -0
  544. package/src/internals/canvas/widgets/tiles/solid-a.png +0 -0
  545. package/src/internals/canvas/widgets/tiles/solid-b.png +0 -0
  546. package/src/internals/canvas/widgets/widgetConfig.js +291 -0
  547. package/src/internals/canvas/widgets/widgetConfig.test.js +68 -0
  548. package/src/internals/canvas/widgets/widgetIcons.jsx +190 -0
  549. package/src/internals/canvas/widgets/widgetProps.js +133 -0
  550. package/src/internals/context/FormContext.js +13 -0
  551. package/src/internals/context/FormContext.test.js +48 -0
  552. package/src/internals/context.jsx +481 -0
  553. package/src/internals/context.test.jsx +296 -0
  554. package/src/internals/hashPreserver.js +73 -0
  555. package/src/internals/hashPreserver.test.js +107 -0
  556. package/src/internals/hooks/useConfig.js +14 -0
  557. package/src/internals/hooks/useFeatureFlag.js +14 -0
  558. package/src/internals/hooks/useFlows.js +50 -0
  559. package/src/internals/hooks/useFlows.test.js +134 -0
  560. package/src/internals/hooks/useHideMode.js +31 -0
  561. package/src/internals/hooks/useHideMode.test.js +43 -0
  562. package/src/internals/hooks/useLocalStorage.js +57 -0
  563. package/src/internals/hooks/useLocalStorage.test.js +75 -0
  564. package/src/internals/hooks/useMode.js +43 -0
  565. package/src/internals/hooks/useObject.js +101 -0
  566. package/src/internals/hooks/useObject.test.js +74 -0
  567. package/src/internals/hooks/useOverride.js +84 -0
  568. package/src/internals/hooks/useOverride.test.js +71 -0
  569. package/src/internals/hooks/usePrototypeReloadGuard.js +64 -0
  570. package/src/internals/hooks/useRecord.js +158 -0
  571. package/src/internals/hooks/useRecord.test.js +221 -0
  572. package/src/internals/hooks/useScene.js +38 -0
  573. package/src/internals/hooks/useScene.test.js +66 -0
  574. package/src/internals/hooks/useSceneData.js +108 -0
  575. package/src/internals/hooks/useSceneData.test.js +136 -0
  576. package/src/internals/hooks/useSession.js +4 -0
  577. package/src/internals/hooks/useSession.test.js +8 -0
  578. package/src/internals/hooks/useThemeState.js +61 -0
  579. package/src/internals/hooks/useThemeState.test.js +66 -0
  580. package/src/internals/hooks/useUndoRedo.js +28 -0
  581. package/src/internals/hooks/useUndoRedo.test.js +64 -0
  582. package/src/internals/index.js +58 -0
  583. package/src/internals/story/ComponentSetPage.jsx +198 -0
  584. package/src/internals/story/ComponentSetPage.module.css +129 -0
  585. package/src/internals/story/StoryPage.jsx +147 -0
  586. package/src/internals/story/StoryPage.module.css +18 -0
  587. package/src/internals/test-utils.js +45 -0
  588. package/src/internals/vite/data-plugin.js +1508 -0
  589. package/src/internals/vite/data-plugin.test.js +1223 -0
  590. package/src/test-utils.js +44 -0
  591. package/toolbar.config.json +271 -0
  592. package/widgets.config.json +1537 -0
@@ -0,0 +1,687 @@
1
+ /**
2
+ * Mount the comments system — keyboard shortcut, cursor overlay, click-to-comment.
3
+ *
4
+ * Call mountComments() once at app startup (after initCommentsConfig).
5
+ */
6
+
7
+ import { getCommentsConfig, isCommentsEnabled } from '../config.js'
8
+ import { isAuthenticated, getCachedUser } from '../auth.js'
9
+ import { toggleCommentMode, setCommentMode, isCommentModeActive, subscribeToCommentMode } from '../commentMode.js'
10
+ import { fetchRouteCommentsSummary, fetchCommentDetail, moveComment, createComment } from '../api.js'
11
+ import { getCachedComments, setCachedComments, clearCachedComments, savePendingComment, getPendingComments, removePendingComment } from '../commentCache.js'
12
+ import { showComposer } from './composer.js'
13
+ import { openAuthModal } from './authModal.js'
14
+ import { showCommentWindow, closeCommentWindow } from './commentWindow.js'
15
+
16
+ const INVALID_PAT_ERROR_MESSAGE = 'GitHub PAT is invalid or expired. Please sign in again.'
17
+ const TOKEN_ACCESS_ERROR_MESSAGE =
18
+ `Token doesn't have access to repository discussions. ` +
19
+ 'Fine-grained tokens need "Discussions: Read and write". ' +
20
+ 'Classic tokens need the "repo" scope.'
21
+
22
+ let banner = null
23
+ let overlay = null
24
+ let activeComposer = null
25
+ let renderedPins = []
26
+ let cachedDiscussion = null
27
+ const CANVAS_SCROLL_SELECTOR = '[data-storyboard-canvas-scroll]'
28
+ const CANVAS_ZOOM_SELECTOR = '[data-storyboard-canvas-zoom]'
29
+ const CANVAS_SURFACE_SELECTOR = '.tc-canvas'
30
+
31
+ function esc(str) {
32
+ const d = document.createElement('div')
33
+ d.textContent = str ?? ''
34
+ return d.innerHTML
35
+ }
36
+
37
+ function roundPct(value) {
38
+ return Math.round(value * 10) / 10
39
+ }
40
+
41
+ function parseScale(transform) {
42
+ if (!transform || transform === 'none') return 1
43
+ const scaleMatch = transform.match(/scale\(([^)]+)\)/)
44
+ if (scaleMatch) {
45
+ const parsed = Number.parseFloat(scaleMatch[1])
46
+ if (Number.isFinite(parsed) && parsed > 0) return parsed
47
+ }
48
+ const matrixMatch = transform.match(/matrix\(([^)]+)\)/)
49
+ if (matrixMatch) {
50
+ const first = Number.parseFloat(matrixMatch[1].split(',')[0])
51
+ if (Number.isFinite(first) && first > 0) return first
52
+ }
53
+ return 1
54
+ }
55
+
56
+ function getCanvasContext() {
57
+ const scrollEl = document.querySelector(CANVAS_SCROLL_SELECTOR)
58
+ const zoomEl = document.querySelector(CANVAS_ZOOM_SELECTOR)
59
+ const canvasEl = (zoomEl && zoomEl.querySelector(CANVAS_SURFACE_SELECTOR)) || null
60
+ if (!scrollEl || !zoomEl || !canvasEl) return null
61
+ const scale = parseScale(zoomEl.style.transform || getComputedStyle(zoomEl).transform)
62
+ const width = canvasEl.offsetWidth || canvasEl.clientWidth
63
+ const height = canvasEl.offsetHeight || canvasEl.clientHeight
64
+ if (!width || !height) return null
65
+ return { scrollEl, scale, width, height }
66
+ }
67
+
68
+ function getAnchorPosition(xPct, yPct) {
69
+ const canvas = getCanvasContext()
70
+ if (!canvas) {
71
+ return {
72
+ left: `${xPct}%`,
73
+ top: `${yPct}%`,
74
+ canvas: false,
75
+ }
76
+ }
77
+
78
+ const rect = canvas.scrollEl.getBoundingClientRect()
79
+ const canvasX = (xPct / 100) * canvas.width
80
+ const canvasY = (yPct / 100) * canvas.height
81
+ const left = rect.left + (canvasX * canvas.scale) - canvas.scrollEl.scrollLeft
82
+ const top = rect.top + (canvasY * canvas.scale) - canvas.scrollEl.scrollTop
83
+
84
+ return {
85
+ left: `${left}px`,
86
+ top: `${top}px`,
87
+ canvas: true,
88
+ }
89
+ }
90
+
91
+ function getPercentFromPointer(clientX, clientY) {
92
+ const canvas = getCanvasContext()
93
+ if (canvas) {
94
+ const rect = canvas.scrollEl.getBoundingClientRect()
95
+ const canvasX = (clientX - rect.left + canvas.scrollEl.scrollLeft) / canvas.scale
96
+ const canvasY = (clientY - rect.top + canvas.scrollEl.scrollTop) / canvas.scale
97
+ const xPct = roundPct((canvasX / canvas.width) * 100)
98
+ const yPct = roundPct((canvasY / canvas.height) * 100)
99
+ return { xPct, yPct, canvas: true }
100
+ }
101
+
102
+ const xPct = roundPct((clientX / window.innerWidth) * 100)
103
+ const docHeight = document.documentElement.scrollHeight
104
+ const yPct = roundPct(((clientY + window.scrollY) / docHeight) * 100)
105
+ return { xPct, yPct, canvas: false }
106
+ }
107
+
108
+ function syncOverlayCoordinateSpace() {
109
+ if (!overlay) return
110
+ if (getCanvasContext()) {
111
+ overlay.style.position = 'fixed'
112
+ overlay.style.width = '100vw'
113
+ overlay.style.height = '100vh'
114
+ } else {
115
+ overlay.style.position = 'absolute'
116
+ overlay.style.width = ''
117
+ overlay.style.height = ''
118
+ }
119
+ }
120
+
121
+ function ensureOverlay() {
122
+ if (overlay) return overlay
123
+
124
+ overlay = document.createElement('div')
125
+ overlay.className = 'sb-comment-overlay'
126
+ document.body.appendChild(overlay)
127
+ syncOverlayCoordinateSpace()
128
+
129
+ // Click handler for placing comments lives on the overlay itself
130
+ overlay.addEventListener('click', (e) => {
131
+ if (!isCommentModeActive()) return
132
+ if (e.target.closest('.sb-composer') || e.target.closest('.sb-comment-pin') || e.target.closest('.sb-comment-window')) return
133
+ handleOverlayClick(e)
134
+ })
135
+ // Keep canvas scroll usable while comment mode is active.
136
+ overlay.addEventListener('wheel', (e) => {
137
+ if (!isCommentModeActive()) return
138
+ const target = e.target
139
+ if (
140
+ target instanceof Element &&
141
+ (target.closest('.sb-composer') || target.closest('.sb-comment-pin') || target.closest('.sb-comment-window'))
142
+ ) {
143
+ return
144
+ }
145
+ const canvas = getCanvasContext()
146
+ if (!canvas) return
147
+ if (typeof canvas.scrollEl.scrollBy === 'function') {
148
+ canvas.scrollEl.scrollBy({ left: e.deltaX, top: e.deltaY, behavior: 'auto' })
149
+ } else {
150
+ canvas.scrollEl.scrollLeft += e.deltaX
151
+ canvas.scrollEl.scrollTop += e.deltaY
152
+ }
153
+ e.preventDefault()
154
+ }, { passive: false })
155
+
156
+ return overlay
157
+ }
158
+
159
+ function showBanner() {
160
+ if (banner) return
161
+ banner = document.createElement('div')
162
+ banner.className = 'sb-banner fixed flex items-center pe-none sans-serif sb-shadow'
163
+ banner.innerHTML = `
164
+ Comment mode — click to place a comment. Press
165
+ <kbd class="sb-kbd">C</kbd> or
166
+ <kbd class="sb-kbd">Esc</kbd> to exit.
167
+ `
168
+ document.body.appendChild(banner)
169
+ }
170
+
171
+ function hideBanner() {
172
+ if (!banner) return
173
+ banner.remove()
174
+ banner = null
175
+ }
176
+
177
+ function getCurrentRoute() {
178
+ return window.location.pathname
179
+ }
180
+
181
+ function getAuthErrorMessage(err) {
182
+ const message = typeof err === 'string'
183
+ ? err
184
+ : (typeof err?.message === 'string' ? err.message : String(err ?? ''))
185
+
186
+ if (message.includes('invalid or expired')) {
187
+ return INVALID_PAT_ERROR_MESSAGE
188
+ }
189
+
190
+ if (message.includes('Not authenticated — no GitHub PAT found')) {
191
+ return 'Not authenticated — no GitHub PAT found. Please sign in.'
192
+ }
193
+
194
+ if (
195
+ message.includes('Resource not accessible by personal access token') ||
196
+ message.includes('insufficient') ||
197
+ message.includes("doesn't have access")
198
+ ) {
199
+ return TOKEN_ACCESS_ERROR_MESSAGE
200
+ }
201
+
202
+ if (message.includes('Could not resolve to a Repository with the name')) {
203
+ const config = getCommentsConfig()
204
+ const repo = config?.repo?.owner && config?.repo?.name
205
+ ? `${config.repo.owner}/${config.repo.name}`
206
+ : 'the configured repository'
207
+
208
+ return `Token cannot access repository \`${repo}\`. Please set the PAT repository access to \`${repo}\` and include Discussions read/write (classic tokens need repo scope).`
209
+ }
210
+
211
+ return null
212
+ }
213
+
214
+ async function promptReauthForAuthError(err) {
215
+ const errorMessage = getAuthErrorMessage(err)
216
+ if (!errorMessage) return false
217
+
218
+ setCommentMode(false)
219
+ openAuthModal({ initialError: errorMessage })
220
+ return true
221
+ }
222
+
223
+ function clearPins() {
224
+ for (const pin of renderedPins) pin.remove()
225
+ renderedPins = []
226
+ }
227
+
228
+ function reloadComments() {
229
+ clearCachedComments(getCurrentRoute())
230
+ loadAndRenderComments()
231
+ }
232
+
233
+ /**
234
+ * Render an optimistic pin immediately after the user submits a comment.
235
+ * Returns callbacks to mark it as succeeded or failed.
236
+ */
237
+ function renderOptimisticPin(ov, xPct, yPct, text, user) {
238
+ const pendingId = `pending-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
239
+ const pin = document.createElement('div')
240
+ pin.className = 'sb-comment-pin sb-comment-pin-pending absolute br-100 sb-bg pointer sb-shadow pe-auto overflow-hidden'
241
+ const anchor = getAnchorPosition(xPct, yPct)
242
+ pin.style.left = anchor.left
243
+ pin.style.top = anchor.top
244
+ pin.title = `${user?.login ?? 'you'}: ${text.slice(0, 80)}`
245
+
246
+ pin.innerHTML = user?.avatarUrl
247
+ ? `<img class="br-100 db sb-pin-img" src="${esc(user.avatarUrl)}" alt="${esc(user.login)}" draggable="false" />`
248
+ : ''
249
+
250
+ ov.appendChild(pin)
251
+ renderedPins.push(pin)
252
+
253
+ return {
254
+ pendingId,
255
+ succeed: () => {
256
+ pin.classList.remove('sb-comment-pin-pending')
257
+ },
258
+ fail: () => {
259
+ pin.classList.remove('sb-comment-pin-pending')
260
+ pin.classList.add('sb-comment-pin-failed')
261
+ pin.title = `⚠ Failed to post — click to retry: ${text.slice(0, 60)}`
262
+
263
+ // Save to localStorage for persistence
264
+ const route = getCurrentRoute()
265
+ savePendingComment(route, { id: pendingId, x: xPct, y: yPct, text, author: user })
266
+
267
+ // Click to retry
268
+ pin.addEventListener('click', async (e) => {
269
+ e.stopPropagation()
270
+ pin.classList.remove('sb-comment-pin-failed')
271
+ pin.classList.add('sb-comment-pin-pending')
272
+ pin.title = 'Retrying…'
273
+ try {
274
+ await createComment(route, xPct, yPct, text)
275
+ removePendingComment(route, pendingId)
276
+ pin.classList.remove('sb-comment-pin-pending')
277
+ reloadComments()
278
+ } catch (err) {
279
+ if (await promptReauthForAuthError(err)) return
280
+ pin.classList.remove('sb-comment-pin-pending')
281
+ pin.classList.add('sb-comment-pin-failed')
282
+ pin.title = `⚠ Failed to post — click to retry: ${text.slice(0, 60)}`
283
+ }
284
+ })
285
+ },
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Render pins for pending (failed) comments from localStorage.
291
+ */
292
+ function renderPendingPins(ov) {
293
+ const route = getCurrentRoute()
294
+ const pending = getPendingComments(route)
295
+ for (const p of pending) {
296
+ const pin = document.createElement('div')
297
+ pin.className = 'sb-comment-pin sb-comment-pin-failed absolute br-100 sb-bg pointer sb-shadow pe-auto overflow-hidden'
298
+ const anchor = getAnchorPosition(p.x, p.y)
299
+ pin.style.left = anchor.left
300
+ pin.style.top = anchor.top
301
+ pin.title = `⚠ Failed to post — click to retry: ${p.text?.slice(0, 60) ?? ''}`
302
+
303
+ pin.innerHTML = p.author?.avatarUrl
304
+ ? `<img class="br-100 db sb-pin-img" src="${esc(p.author.avatarUrl)}" alt="${esc(p.author.login)}" draggable="false" />`
305
+ : ''
306
+
307
+ pin.addEventListener('click', async (e) => {
308
+ e.stopPropagation()
309
+ pin.classList.remove('sb-comment-pin-failed')
310
+ pin.classList.add('sb-comment-pin-pending')
311
+ pin.title = 'Retrying…'
312
+ try {
313
+ await createComment(route, p.x, p.y, p.text)
314
+ removePendingComment(route, p.id)
315
+ pin.remove()
316
+ reloadComments()
317
+ } catch (err) {
318
+ if (await promptReauthForAuthError(err)) return
319
+ pin.classList.remove('sb-comment-pin-pending')
320
+ pin.classList.add('sb-comment-pin-failed')
321
+ pin.title = `⚠ Failed to post — click to retry: ${p.text?.slice(0, 60) ?? ''}`
322
+ }
323
+ })
324
+
325
+ ov.appendChild(pin)
326
+ renderedPins.push(pin)
327
+ }
328
+ }
329
+
330
+ function renderPin(ov, comment, index) {
331
+ const hue = Math.round((index * 137.5) % 360)
332
+ const pin = document.createElement('div')
333
+ pin.className = 'sb-comment-pin absolute br-100 sb-bg pointer sb-shadow pe-auto overflow-hidden'
334
+ const startAnchor = getAnchorPosition(comment.meta?.x ?? 0, comment.meta?.y ?? 0)
335
+ pin.style.left = startAnchor.left
336
+ pin.style.top = startAnchor.top
337
+ pin.style.setProperty('--pin-hue', String(hue))
338
+
339
+ if (comment.meta?.resolved) pin.setAttribute('data-resolved', 'true')
340
+ pin.title = `${comment.author?.login ?? 'unknown'}: ${comment.text?.slice(0, 80) ?? ''}`
341
+
342
+ pin.innerHTML = comment.author?.avatarUrl
343
+ ? `<img class="br-100 db sb-pin-img" src="${esc(comment.author.avatarUrl)}" alt="${esc(comment.author.login)}" draggable="false" />`
344
+ : ''
345
+
346
+ pin._commentId = comment.id
347
+ comment._rawBody = comment.body
348
+
349
+ let dragged = false
350
+
351
+ pin.addEventListener('mousedown', (e) => {
352
+ if (e.button !== 0) return
353
+ dragged = false
354
+ const startX = e.clientX
355
+ const startY = e.clientY
356
+ const startCoords = getPercentFromPointer(e.clientX, e.clientY)
357
+ const startLeftPct = startCoords.xPct
358
+ const startTopPct = startCoords.yPct
359
+ let lastCoords = { xPct: startLeftPct, yPct: startTopPct }
360
+
361
+ const onMove = (ev) => {
362
+ const dx = ev.clientX - startX
363
+ const dy = ev.clientY - startY
364
+ if (!dragged && Math.abs(dx) < 4 && Math.abs(dy) < 4) return
365
+ dragged = true
366
+
367
+ if (startCoords.canvas) {
368
+ lastCoords = getPercentFromPointer(ev.clientX, ev.clientY)
369
+ pin.style.left = `${ev.clientX}px`
370
+ pin.style.top = `${ev.clientY}px`
371
+ } else {
372
+ const xPct = roundPct(startLeftPct + (dx / window.innerWidth) * 100)
373
+ const docHeight = document.documentElement.scrollHeight
374
+ const yPct = roundPct(startTopPct + (dy / docHeight) * 100)
375
+ lastCoords = { xPct, yPct }
376
+ pin.style.left = `${xPct}%`
377
+ pin.style.top = `${yPct}%`
378
+ }
379
+ }
380
+
381
+ const onUp = async (ev) => {
382
+ document.removeEventListener('mousemove', onMove)
383
+ document.removeEventListener('mouseup', onUp)
384
+ if (!dragged) return
385
+
386
+ let xPct = lastCoords.xPct
387
+ let yPct = lastCoords.yPct
388
+ if (!startCoords.canvas) {
389
+ const dx = ev.clientX - startX
390
+ const dy = ev.clientY - startY
391
+ xPct = roundPct(startLeftPct + (dx / window.innerWidth) * 100)
392
+ const docHeight = document.documentElement.scrollHeight
393
+ yPct = roundPct(startTopPct + (dy / docHeight) * 100)
394
+ }
395
+ comment.meta = { ...comment.meta, x: xPct, y: yPct }
396
+
397
+ try {
398
+ await moveComment(comment.id, comment._rawBody ?? comment.body ?? '', xPct, yPct)
399
+ comment._rawBody = null
400
+ clearCachedComments(getCurrentRoute())
401
+ } catch (err) {
402
+ if (await promptReauthForAuthError(err)) return
403
+ console.error('[storyboard] Failed to move pin:', err)
404
+ }
405
+ }
406
+
407
+ document.addEventListener('mousemove', onMove)
408
+ document.addEventListener('mouseup', onUp)
409
+ e.preventDefault()
410
+ })
411
+
412
+ pin.addEventListener('click', async (e) => {
413
+ e.stopPropagation()
414
+ if (dragged) return
415
+ if (activeComposer) {
416
+ activeComposer.destroy()
417
+ activeComposer = null
418
+ }
419
+ // Lazy-load full comment detail (replies, reactions, createdAt)
420
+ try {
421
+ const detail = await fetchCommentDetail(comment.id)
422
+ if (detail) {
423
+ detail._rawBody = detail.body
424
+ showCommentWindow(ov, detail, cachedDiscussion, {
425
+ getAnchorPosition,
426
+ onClose: () => {},
427
+ onMove: () => reloadComments(),
428
+ })
429
+ }
430
+ } catch (err) {
431
+ console.warn('[storyboard] Could not load comment detail:', err.message)
432
+ // Fall back to summary data
433
+ showCommentWindow(ov, comment, cachedDiscussion, {
434
+ getAnchorPosition,
435
+ onClose: () => {},
436
+ onMove: () => reloadComments(),
437
+ })
438
+ }
439
+ })
440
+
441
+ ov.appendChild(pin)
442
+ renderedPins.push(pin)
443
+ return pin
444
+ }
445
+
446
+ function renderCachedPins() {
447
+ if (!cachedDiscussion?.comments?.length) return
448
+ const ov = ensureOverlay()
449
+ clearPins()
450
+ cachedDiscussion.comments.forEach((comment, i) => {
451
+ if (comment.meta?.x != null && comment.meta?.y != null) {
452
+ renderPin(ov, comment, i)
453
+ }
454
+ })
455
+ renderPendingPins(ov)
456
+ }
457
+
458
+ async function loadAndRenderComments() {
459
+ if (!isAuthenticated()) return
460
+ const ov = ensureOverlay()
461
+ const route = getCurrentRoute()
462
+
463
+ // 1. Render from cache — skip API if cache is fresh
464
+ const cached = getCachedComments(route)
465
+ if (cached) {
466
+ cachedDiscussion = cached
467
+ renderCachedPins()
468
+ autoOpenCommentFromUrl(ensureOverlay(), cached)
469
+ return
470
+ }
471
+
472
+ // 2. Cache miss/expired — fetch lightweight summary
473
+ try {
474
+ const discussion = await fetchRouteCommentsSummary(route)
475
+ cachedDiscussion = discussion
476
+ if (discussion) {
477
+ setCachedComments(route, discussion)
478
+ }
479
+ clearPins()
480
+ if (!discussion?.comments?.length) {
481
+ renderPendingPins(ov)
482
+ return
483
+ }
484
+
485
+ discussion.comments.forEach((comment, i) => {
486
+ if (comment.meta?.x != null && comment.meta?.y != null) {
487
+ renderPin(ov, comment, i)
488
+ }
489
+ })
490
+ renderPendingPins(ov)
491
+
492
+ autoOpenCommentFromUrl(ov, discussion)
493
+ } catch (err) {
494
+ if (await promptReauthForAuthError(err)) return
495
+ console.warn('[storyboard] Could not load comments:', err.message)
496
+ }
497
+ }
498
+
499
+ async function autoOpenCommentFromUrl(ov, discussion) {
500
+ const commentId = new URLSearchParams(window.location.search).get('comment')
501
+ if (!commentId || !discussion?.comments?.length) return
502
+
503
+ const comment = discussion.comments.find(c => c.id === commentId)
504
+ if (!comment) return
505
+
506
+ if (comment.meta?.y != null) {
507
+ const canvas = getCanvasContext()
508
+ if (canvas) {
509
+ const canvasY = (comment.meta.y / 100) * canvas.height
510
+ const yPx = canvasY * canvas.scale
511
+ const viewTop = canvas.scrollEl.scrollTop
512
+ const viewBottom = viewTop + canvas.scrollEl.clientHeight
513
+ if (yPx < viewTop || yPx > viewBottom) {
514
+ const scrollTarget = Math.max(0, yPx - canvas.scrollEl.clientHeight / 3)
515
+ canvas.scrollEl.scrollTo({ top: scrollTarget, behavior: 'smooth' })
516
+ }
517
+ } else {
518
+ const docHeight = document.documentElement.scrollHeight
519
+ const yPx = (comment.meta.y / 100) * docHeight
520
+ const viewTop = window.scrollY
521
+ const viewBottom = viewTop + window.innerHeight
522
+ if (yPx < viewTop || yPx > viewBottom) {
523
+ const scrollTarget = Math.max(0, yPx - window.innerHeight / 3)
524
+ window.scrollTo({ top: scrollTarget, behavior: 'smooth' })
525
+ }
526
+ }
527
+ }
528
+
529
+ // Lazy-load full detail before opening window
530
+ try {
531
+ const detail = await fetchCommentDetail(commentId)
532
+ if (detail) {
533
+ detail._rawBody = detail.body
534
+ showCommentWindow(ov, detail, discussion, {
535
+ getAnchorPosition,
536
+ onClose: () => {},
537
+ onMove: () => reloadComments(),
538
+ })
539
+ return
540
+ }
541
+ } catch (err) {
542
+ if (await promptReauthForAuthError(err)) return
543
+ console.warn('[storyboard] Could not load comment detail:', err.message)
544
+ }
545
+
546
+ // Fallback to summary data
547
+ comment._rawBody = comment.body
548
+ showCommentWindow(ov, comment, discussion, {
549
+ getAnchorPosition,
550
+ onClose: () => {},
551
+ onMove: () => reloadComments(),
552
+ })
553
+ }
554
+
555
+ function handleOverlayClick(e) {
556
+ if (!isCommentModeActive()) return
557
+ if (e.target.closest('.sb-composer') || e.target.closest('.sb-comment-pin') || e.target.closest('.sb-comment-window')) return
558
+
559
+ closeCommentWindow()
560
+
561
+ const { xPct, yPct } = getPercentFromPointer(e.clientX, e.clientY)
562
+
563
+ // Move existing composer instead of destroying and recreating
564
+ if (activeComposer) {
565
+ activeComposer.moveTo(xPct, yPct)
566
+ return
567
+ }
568
+
569
+ const ov = ensureOverlay()
570
+ const route = getCurrentRoute()
571
+ activeComposer = showComposer(ov, xPct, yPct, route, {
572
+ getAnchorPosition,
573
+ onCancel: () => { activeComposer = null },
574
+ onSubmitOptimistic: (text, x, y) => {
575
+ activeComposer = null
576
+ const user = getCachedUser()
577
+ const opt = renderOptimisticPin(ov, x, y, text, user)
578
+ // Fire API call in background
579
+ createComment(route, x, y, text)
580
+ .then(() => {
581
+ opt.succeed()
582
+ reloadComments()
583
+ })
584
+ .catch(async (err) => {
585
+ console.error('[storyboard] Failed to post comment:', err)
586
+ if (await promptReauthForAuthError(err)) return
587
+ opt.fail()
588
+ })
589
+ },
590
+ })
591
+ }
592
+
593
+ function setBodyCommentMode(active) {
594
+ if (active) {
595
+ document.body.classList.add('sb-comment-mode')
596
+ showBanner()
597
+ ensureOverlay()
598
+ syncOverlayCoordinateSpace()
599
+ renderCachedPins()
600
+ loadAndRenderComments()
601
+ } else {
602
+ document.body.classList.remove('sb-comment-mode')
603
+ hideBanner()
604
+ if (activeComposer) {
605
+ activeComposer.destroy()
606
+ activeComposer = null
607
+ }
608
+ closeCommentWindow()
609
+ clearPins()
610
+ if (overlay) {
611
+ overlay.remove()
612
+ overlay = null
613
+ }
614
+ }
615
+ }
616
+
617
+ let _mounted = false
618
+
619
+ /**
620
+ * Mount the comments system — registers keyboard shortcuts, cursor overlay, and click handler.
621
+ * Initializes Alpine.js for comments UI components.
622
+ * Safe to call multiple times (idempotent).
623
+ */
624
+ export function mountComments() {
625
+ if (_mounted) return
626
+ _mounted = true
627
+
628
+ subscribeToCommentMode(setBodyCommentMode)
629
+ window.addEventListener('popstate', () => {
630
+ if (isCommentModeActive()) {
631
+ setCommentMode(false)
632
+ }
633
+ })
634
+
635
+ document.addEventListener('storyboard:canvas:mounted', () => {
636
+ syncOverlayCoordinateSpace()
637
+ if (isCommentModeActive()) renderCachedPins()
638
+ })
639
+ document.addEventListener('storyboard:canvas:unmounted', () => {
640
+ syncOverlayCoordinateSpace()
641
+ if (isCommentModeActive()) renderCachedPins()
642
+ })
643
+ document.addEventListener('storyboard:canvas:zoom-changed', () => {
644
+ syncOverlayCoordinateSpace()
645
+ if (isCommentModeActive()) renderCachedPins()
646
+ })
647
+ document.addEventListener('scroll', (e) => {
648
+ const target = e.target
649
+ if (!(target instanceof Element)) return
650
+ if (!target.matches(CANVAS_SCROLL_SELECTOR)) return
651
+ if (!isCommentModeActive()) return
652
+ renderCachedPins()
653
+ }, true)
654
+
655
+ window.addEventListener('keydown', (e) => {
656
+ const tag = e.target.tagName
657
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || e.target.isContentEditable) {
658
+ return
659
+ }
660
+
661
+ if (e.key === 'c' && !e.metaKey && !e.ctrlKey && !e.altKey) {
662
+ if (!isCommentsEnabled()) return
663
+ e.preventDefault()
664
+
665
+ if (!isCommentModeActive() && !isAuthenticated()) {
666
+ openAuthModal()
667
+ return
668
+ }
669
+
670
+ toggleCommentMode()
671
+ }
672
+
673
+ if (e.key === 'Escape') {
674
+ if (isCommentModeActive()) {
675
+ e.preventDefault()
676
+ setCommentMode(false)
677
+ }
678
+ }
679
+ })
680
+
681
+ if (isCommentsEnabled() && isAuthenticated()) {
682
+ const commentId = new URLSearchParams(window.location.search).get('comment')
683
+ if (commentId) {
684
+ setCommentMode(true)
685
+ }
686
+ }
687
+ }