makersclaw 0.1.0 → 0.3.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 (478) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -1
  3. package/THIRD-PARTY-NOTICES.md +180 -0
  4. package/package.json +15 -8
  5. package/r/annotate.json +62 -0
  6. package/r/approval-request.json +52 -0
  7. package/r/audio-player.json +74 -0
  8. package/r/auth-form.json +55 -0
  9. package/r/board.json +55 -0
  10. package/r/calendar-view.json +105 -0
  11. package/r/cart.json +62 -0
  12. package/r/chart.json +83 -0
  13. package/r/chat.json +95 -0
  14. package/r/code-editor.json +94 -0
  15. package/r/comment-box.json +64 -0
  16. package/r/contact-section.json +53 -0
  17. package/r/core.json +341 -0
  18. package/r/csv-import.json +63 -0
  19. package/r/data-table.json +69 -0
  20. package/r/diagram.json +74 -0
  21. package/r/doc-editor.json +82 -0
  22. package/r/doc-page.json +61 -0
  23. package/r/email-render.json +73 -0
  24. package/r/faq.json +50 -0
  25. package/r/file-upload.json +54 -0
  26. package/r/flow-canvas.json +93 -0
  27. package/r/footer.json +52 -0
  28. package/r/gantt.json +70 -0
  29. package/r/job-panel.json +56 -0
  30. package/r/json-editor.json +66 -0
  31. package/r/map.json +89 -0
  32. package/r/menubar.json +52 -0
  33. package/r/mind-map.json +67 -0
  34. package/r/navigation-menu.json +52 -0
  35. package/r/order-detail.json +54 -0
  36. package/r/org-chart.json +66 -0
  37. package/r/page-builder.json +105 -0
  38. package/r/payment-methods.json +51 -0
  39. package/r/pdf-document.json +79 -0
  40. package/r/pdf-viewer.json +88 -0
  41. package/r/product-detail.json +76 -0
  42. package/r/product-grid.json +65 -0
  43. package/r/record-form.json +67 -0
  44. package/r/record-notes.json +55 -0
  45. package/r/registry.json +3612 -0
  46. package/r/search-dialog.json +51 -0
  47. package/r/settings-list.json +66 -0
  48. package/r/setup-checklist.json +51 -0
  49. package/r/share-dialog.json +52 -0
  50. package/r/sidebar.json +69 -0
  51. package/r/signature-pad.json +72 -0
  52. package/r/sketch-canvas.json +99 -0
  53. package/r/spreadsheet.json +90 -0
  54. package/r/stat-cards.json +66 -0
  55. package/r/task-list.json +63 -0
  56. package/r/task-model.json +98 -0
  57. package/r/testimonials.json +52 -0
  58. package/r/transaction-list.json +53 -0
  59. package/r/whiteboard.json +66 -0
  60. package/registry/blocks/annotate/README.md +179 -0
  61. package/registry/blocks/annotate/annotate-data.js +250 -0
  62. package/registry/blocks/annotate/annotate.jsx +646 -0
  63. package/registry/blocks/annotate/block.json +10 -0
  64. package/registry/blocks/annotate/demo.jsx +135 -0
  65. package/registry/blocks/annotate/index.js +15 -0
  66. package/registry/blocks/approval-request/README.md +116 -0
  67. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  68. package/registry/blocks/approval-request/block.json +10 -0
  69. package/registry/blocks/approval-request/demo.jsx +127 -0
  70. package/registry/blocks/approval-request/index.js +4 -0
  71. package/registry/blocks/audio-player/README.md +167 -0
  72. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  73. package/registry/blocks/audio-player/block.json +14 -0
  74. package/registry/blocks/audio-player/colors.js +109 -0
  75. package/registry/blocks/audio-player/demo.jsx +209 -0
  76. package/registry/blocks/audio-player/index.js +6 -0
  77. package/registry/blocks/audio-player/time.js +67 -0
  78. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  79. package/registry/blocks/auth-form/README.md +126 -0
  80. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  81. package/registry/blocks/auth-form/block.json +10 -0
  82. package/registry/blocks/auth-form/demo.jsx +131 -0
  83. package/registry/blocks/auth-form/index.js +3 -0
  84. package/registry/blocks/board/README.md +64 -13
  85. package/registry/blocks/board/block.json +2 -2
  86. package/registry/blocks/board/board.jsx +237 -94
  87. package/registry/blocks/board/demo.jsx +103 -24
  88. package/registry/blocks/calendar-view/README.md +180 -0
  89. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  90. package/registry/blocks/calendar-view/block.json +76 -0
  91. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  92. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  93. package/registry/blocks/calendar-view/demo.jsx +195 -0
  94. package/registry/blocks/calendar-view/draft.jsx +49 -0
  95. package/registry/blocks/calendar-view/index.js +39 -0
  96. package/registry/blocks/calendar-view/month.jsx +346 -0
  97. package/registry/blocks/calendar-view/paint.js +69 -0
  98. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  99. package/registry/blocks/calendar-view/tray.jsx +87 -0
  100. package/registry/blocks/cart/README.md +146 -0
  101. package/registry/blocks/cart/block.json +10 -0
  102. package/registry/blocks/cart/cart.jsx +597 -0
  103. package/registry/blocks/cart/demo.jsx +98 -0
  104. package/registry/blocks/cart/index.js +4 -0
  105. package/registry/blocks/cart/money.js +28 -0
  106. package/registry/blocks/chart/README.md +316 -0
  107. package/registry/blocks/chart/block.json +10 -0
  108. package/registry/blocks/chart/cartesian.jsx +642 -0
  109. package/registry/blocks/chart/core.jsx +262 -0
  110. package/registry/blocks/chart/demo.jsx +357 -0
  111. package/registry/blocks/chart/heatmap.jsx +336 -0
  112. package/registry/blocks/chart/index.js +8 -0
  113. package/registry/blocks/chart/pie.jsx +245 -0
  114. package/registry/blocks/chart/sparkline.jsx +88 -0
  115. package/registry/blocks/chat/README.md +273 -0
  116. package/registry/blocks/chat/attachment.jsx +95 -0
  117. package/registry/blocks/chat/block.json +10 -0
  118. package/registry/blocks/chat/chat.jsx +132 -0
  119. package/registry/blocks/chat/composer.jsx +364 -0
  120. package/registry/blocks/chat/demo.jsx +578 -0
  121. package/registry/blocks/chat/header.jsx +34 -0
  122. package/registry/blocks/chat/index.js +9 -0
  123. package/registry/blocks/chat/message.jsx +450 -0
  124. package/registry/blocks/chat/scroller.jsx +247 -0
  125. package/registry/blocks/chat/text.jsx +104 -0
  126. package/registry/blocks/code-editor/README.md +210 -0
  127. package/registry/blocks/code-editor/block.json +22 -0
  128. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  129. package/registry/blocks/code-editor/codemirror.js +38 -0
  130. package/registry/blocks/code-editor/demo.jsx +205 -0
  131. package/registry/blocks/code-editor/index.js +8 -0
  132. package/registry/blocks/code-editor/json.js +317 -0
  133. package/registry/blocks/code-editor/sql.js +136 -0
  134. package/registry/blocks/code-editor/text.js +101 -0
  135. package/registry/blocks/code-editor/theme.js +186 -0
  136. package/registry/blocks/comment-box/README.md +123 -0
  137. package/registry/blocks/comment-box/block.json +10 -0
  138. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  139. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  140. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  141. package/registry/blocks/comment-box/demo.jsx +141 -0
  142. package/registry/blocks/comment-box/index.js +8 -0
  143. package/registry/blocks/contact-section/README.md +152 -0
  144. package/registry/blocks/contact-section/block.json +11 -0
  145. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  146. package/registry/blocks/contact-section/demo.jsx +93 -0
  147. package/registry/blocks/contact-section/index.js +4 -0
  148. package/registry/blocks/csv-import/README.md +133 -0
  149. package/registry/blocks/csv-import/block.json +13 -0
  150. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  151. package/registry/blocks/csv-import/demo.jsx +106 -0
  152. package/registry/blocks/csv-import/import.js +200 -0
  153. package/registry/blocks/csv-import/index.js +7 -0
  154. package/registry/blocks/data-table/README.md +386 -0
  155. package/registry/blocks/data-table/block.json +10 -0
  156. package/registry/blocks/data-table/data-table.jsx +1164 -0
  157. package/registry/blocks/data-table/demo.jsx +588 -0
  158. package/registry/blocks/data-table/index.js +5 -0
  159. package/registry/blocks/data-table/model.js +169 -0
  160. package/registry/blocks/diagram/README.md +108 -0
  161. package/registry/blocks/diagram/block.json +13 -0
  162. package/registry/blocks/diagram/demo.jsx +113 -0
  163. package/registry/blocks/diagram/diagram.jsx +481 -0
  164. package/registry/blocks/diagram/index.js +7 -0
  165. package/registry/blocks/diagram/mermaid.js +52 -0
  166. package/registry/blocks/diagram/palette.js +313 -0
  167. package/registry/blocks/diagram/source.js +98 -0
  168. package/registry/blocks/doc-editor/README.md +215 -0
  169. package/registry/blocks/doc-editor/block.json +15 -0
  170. package/registry/blocks/doc-editor/demo.jsx +225 -0
  171. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  172. package/registry/blocks/doc-editor/doc.js +309 -0
  173. package/registry/blocks/doc-editor/index.js +7 -0
  174. package/registry/blocks/doc-editor/schema.jsx +56 -0
  175. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  176. package/registry/blocks/doc-editor/theme.js +82 -0
  177. package/registry/blocks/doc-page/README.md +119 -0
  178. package/registry/blocks/doc-page/autosave.js +104 -0
  179. package/registry/blocks/doc-page/block.json +10 -0
  180. package/registry/blocks/doc-page/demo.jsx +137 -0
  181. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  182. package/registry/blocks/doc-page/index.js +4 -0
  183. package/registry/blocks/email-render/README.md +151 -0
  184. package/registry/blocks/email-render/block.json +14 -0
  185. package/registry/blocks/email-render/demo.jsx +153 -0
  186. package/registry/blocks/email-render/doc-email.jsx +210 -0
  187. package/registry/blocks/email-render/email.jsx +221 -0
  188. package/registry/blocks/email-render/index.js +10 -0
  189. package/registry/blocks/email-render/render.js +50 -0
  190. package/registry/blocks/email-render/theme.js +215 -0
  191. package/registry/blocks/faq/README.md +101 -0
  192. package/registry/blocks/faq/block.json +11 -0
  193. package/registry/blocks/faq/demo.jsx +89 -0
  194. package/registry/blocks/faq/faq.jsx +220 -0
  195. package/registry/blocks/faq/index.js +4 -0
  196. package/registry/blocks/file-upload/README.md +138 -0
  197. package/registry/blocks/file-upload/block.json +10 -0
  198. package/registry/blocks/file-upload/demo.jsx +204 -0
  199. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  200. package/registry/blocks/file-upload/index.js +4 -0
  201. package/registry/blocks/flow-canvas/README.md +420 -0
  202. package/registry/blocks/flow-canvas/block.json +13 -0
  203. package/registry/blocks/flow-canvas/board.js +819 -0
  204. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  205. package/registry/blocks/flow-canvas/context.jsx +35 -0
  206. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  207. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  208. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  209. package/registry/blocks/flow-canvas/index.js +41 -0
  210. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  211. package/registry/blocks/flow-canvas/theme.js +174 -0
  212. package/registry/blocks/footer/README.md +133 -0
  213. package/registry/blocks/footer/block.json +11 -0
  214. package/registry/blocks/footer/demo.jsx +119 -0
  215. package/registry/blocks/footer/footer.jsx +406 -0
  216. package/registry/blocks/footer/index.js +4 -0
  217. package/registry/blocks/gantt/README.md +220 -0
  218. package/registry/blocks/gantt/block.json +10 -0
  219. package/registry/blocks/gantt/demo.jsx +148 -0
  220. package/registry/blocks/gantt/gantt.jsx +923 -0
  221. package/registry/blocks/gantt/index.js +7 -0
  222. package/registry/blocks/gantt/rows.jsx +468 -0
  223. package/registry/blocks/gantt/schedule.js +278 -0
  224. package/registry/blocks/job-panel/README.md +86 -2
  225. package/registry/blocks/job-panel/block.json +3 -3
  226. package/registry/blocks/job-panel/demo.jsx +230 -3
  227. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  228. package/registry/blocks/json-editor/README.md +133 -0
  229. package/registry/blocks/json-editor/block.json +10 -0
  230. package/registry/blocks/json-editor/demo.jsx +92 -0
  231. package/registry/blocks/json-editor/index.js +7 -0
  232. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  233. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  234. package/registry/blocks/json-editor/tree.js +225 -0
  235. package/registry/blocks/map/README.md +221 -0
  236. package/registry/blocks/map/block.json +14 -0
  237. package/registry/blocks/map/demo.jsx +201 -0
  238. package/registry/blocks/map/geo.js +198 -0
  239. package/registry/blocks/map/index.js +8 -0
  240. package/registry/blocks/map/list.jsx +128 -0
  241. package/registry/blocks/map/map-view.jsx +715 -0
  242. package/registry/blocks/map/maplibre.js +30 -0
  243. package/registry/blocks/map/marks.jsx +146 -0
  244. package/registry/blocks/map/theme.js +142 -0
  245. package/registry/blocks/menubar/README.md +109 -0
  246. package/registry/blocks/menubar/block.json +10 -0
  247. package/registry/blocks/menubar/demo.jsx +128 -0
  248. package/registry/blocks/menubar/index.js +4 -0
  249. package/registry/blocks/menubar/menubar.jsx +199 -0
  250. package/registry/blocks/mind-map/README.md +206 -0
  251. package/registry/blocks/mind-map/block.json +10 -0
  252. package/registry/blocks/mind-map/demo.jsx +103 -0
  253. package/registry/blocks/mind-map/index.js +31 -0
  254. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  255. package/registry/blocks/mind-map/topic.jsx +143 -0
  256. package/registry/blocks/mind-map/tree.js +645 -0
  257. package/registry/blocks/navigation-menu/README.md +124 -0
  258. package/registry/blocks/navigation-menu/block.json +10 -0
  259. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  260. package/registry/blocks/navigation-menu/index.js +4 -0
  261. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  262. package/registry/blocks/order-detail/README.md +109 -0
  263. package/registry/blocks/order-detail/block.json +10 -0
  264. package/registry/blocks/order-detail/demo.jsx +88 -0
  265. package/registry/blocks/order-detail/index.js +4 -0
  266. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  267. package/registry/blocks/org-chart/README.md +145 -0
  268. package/registry/blocks/org-chart/block.json +10 -0
  269. package/registry/blocks/org-chart/demo.jsx +94 -0
  270. package/registry/blocks/org-chart/index.js +7 -0
  271. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  272. package/registry/blocks/org-chart/org.js +245 -0
  273. package/registry/blocks/org-chart/person.jsx +96 -0
  274. package/registry/blocks/page-builder/README.md +257 -0
  275. package/registry/blocks/page-builder/block.json +14 -0
  276. package/registry/blocks/page-builder/demo.jsx +145 -0
  277. package/registry/blocks/page-builder/grapes.js +18 -0
  278. package/registry/blocks/page-builder/index.js +11 -0
  279. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  280. package/registry/blocks/page-builder/page-view.jsx +33 -0
  281. package/registry/blocks/page-builder/page.js +118 -0
  282. package/registry/blocks/page-builder/panels.jsx +588 -0
  283. package/registry/blocks/page-builder/sections.js +209 -0
  284. package/registry/blocks/page-builder/theme.js +87 -0
  285. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  286. package/registry/blocks/page-builder/tokens.js +329 -0
  287. package/registry/blocks/payment-methods/README.md +104 -0
  288. package/registry/blocks/payment-methods/block.json +10 -0
  289. package/registry/blocks/payment-methods/demo.jsx +71 -0
  290. package/registry/blocks/payment-methods/index.js +4 -0
  291. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  292. package/registry/blocks/pdf-document/README.md +213 -0
  293. package/registry/blocks/pdf-document/block.json +13 -0
  294. package/registry/blocks/pdf-document/demo.jsx +136 -0
  295. package/registry/blocks/pdf-document/index.js +33 -0
  296. package/registry/blocks/pdf-document/money.js +83 -0
  297. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  298. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  299. package/registry/blocks/pdf-document/quote.jsx +179 -0
  300. package/registry/blocks/pdf-document/theme.js +193 -0
  301. package/registry/blocks/pdf-viewer/README.md +191 -0
  302. package/registry/blocks/pdf-viewer/block.json +14 -0
  303. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  304. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  305. package/registry/blocks/pdf-viewer/index.js +8 -0
  306. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  307. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  308. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  309. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  310. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  311. package/registry/blocks/product-detail/README.md +217 -0
  312. package/registry/blocks/product-detail/block.json +10 -0
  313. package/registry/blocks/product-detail/demo.jsx +233 -0
  314. package/registry/blocks/product-detail/index.js +7 -0
  315. package/registry/blocks/product-detail/price.jsx +115 -0
  316. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  317. package/registry/blocks/product-detail/product-media.jsx +102 -0
  318. package/registry/blocks/product-detail/product-options.jsx +144 -0
  319. package/registry/blocks/product-grid/README.md +162 -0
  320. package/registry/blocks/product-grid/block.json +10 -0
  321. package/registry/blocks/product-grid/demo.jsx +255 -0
  322. package/registry/blocks/product-grid/index.js +5 -0
  323. package/registry/blocks/product-grid/product-card.jsx +232 -0
  324. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  325. package/registry/blocks/record-form/README.md +196 -0
  326. package/registry/blocks/record-form/block.json +10 -0
  327. package/registry/blocks/record-form/demo.jsx +525 -0
  328. package/registry/blocks/record-form/index.js +4 -0
  329. package/registry/blocks/record-form/record-fields.jsx +457 -0
  330. package/registry/blocks/record-form/record-form.jsx +415 -0
  331. package/registry/blocks/record-form/rules.js +81 -0
  332. package/registry/blocks/record-notes/README.md +139 -0
  333. package/registry/blocks/record-notes/block.json +10 -0
  334. package/registry/blocks/record-notes/demo.jsx +179 -0
  335. package/registry/blocks/record-notes/index.js +4 -0
  336. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  337. package/registry/blocks/search-dialog/README.md +108 -0
  338. package/registry/blocks/search-dialog/block.json +10 -0
  339. package/registry/blocks/search-dialog/demo.jsx +116 -0
  340. package/registry/blocks/search-dialog/index.js +3 -0
  341. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  342. package/registry/blocks/settings-list/README.md +171 -0
  343. package/registry/blocks/settings-list/block.json +10 -0
  344. package/registry/blocks/settings-list/demo.jsx +333 -0
  345. package/registry/blocks/settings-list/index.js +4 -0
  346. package/registry/blocks/settings-list/row-state.jsx +93 -0
  347. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  348. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  349. package/registry/blocks/setup-checklist/README.md +142 -0
  350. package/registry/blocks/setup-checklist/block.json +10 -0
  351. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  352. package/registry/blocks/setup-checklist/index.js +4 -0
  353. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  354. package/registry/blocks/share-dialog/README.md +113 -0
  355. package/registry/blocks/share-dialog/block.json +10 -0
  356. package/registry/blocks/share-dialog/demo.jsx +140 -0
  357. package/registry/blocks/share-dialog/index.js +3 -0
  358. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  359. package/registry/blocks/sidebar/README.md +190 -0
  360. package/registry/blocks/sidebar/block.json +43 -0
  361. package/registry/blocks/sidebar/demo.jsx +577 -0
  362. package/registry/blocks/sidebar/index.js +4 -0
  363. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  364. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  365. package/registry/blocks/signature-pad/README.md +137 -0
  366. package/registry/blocks/signature-pad/block.json +13 -0
  367. package/registry/blocks/signature-pad/colors.js +115 -0
  368. package/registry/blocks/signature-pad/demo.jsx +94 -0
  369. package/registry/blocks/signature-pad/index.js +6 -0
  370. package/registry/blocks/signature-pad/pad.js +7 -0
  371. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  372. package/registry/blocks/signature-pad/signature.js +68 -0
  373. package/registry/blocks/sketch-canvas/README.md +280 -0
  374. package/registry/blocks/sketch-canvas/block.json +13 -0
  375. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  376. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  377. package/registry/blocks/sketch-canvas/index.js +9 -0
  378. package/registry/blocks/sketch-canvas/palette.js +226 -0
  379. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  380. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  381. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  382. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  383. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  384. package/registry/blocks/sketch-canvas/theme.js +131 -0
  385. package/registry/blocks/spreadsheet/README.md +307 -0
  386. package/registry/blocks/spreadsheet/block.json +13 -0
  387. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  388. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  389. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  390. package/registry/blocks/spreadsheet/grid.js +387 -0
  391. package/registry/blocks/spreadsheet/index.js +34 -0
  392. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  393. package/registry/blocks/spreadsheet/types.js +473 -0
  394. package/registry/blocks/stat-cards/README.md +173 -0
  395. package/registry/blocks/stat-cards/block.json +10 -0
  396. package/registry/blocks/stat-cards/demo.jsx +257 -0
  397. package/registry/blocks/stat-cards/index.js +4 -0
  398. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  399. package/registry/blocks/stat-cards/usage.jsx +243 -0
  400. package/registry/blocks/task-list/README.md +176 -0
  401. package/registry/blocks/task-list/block.json +10 -0
  402. package/registry/blocks/task-list/demo.jsx +134 -0
  403. package/registry/blocks/task-list/index.js +5 -0
  404. package/registry/blocks/task-list/task-list.jsx +907 -0
  405. package/registry/blocks/task-model/README.md +455 -0
  406. package/registry/blocks/task-model/block.json +167 -0
  407. package/registry/blocks/task-model/changes.js +89 -0
  408. package/registry/blocks/task-model/dates.js +371 -0
  409. package/registry/blocks/task-model/demo.jsx +215 -0
  410. package/registry/blocks/task-model/dnd.jsx +240 -0
  411. package/registry/blocks/task-model/index.js +97 -0
  412. package/registry/blocks/task-model/model.js +919 -0
  413. package/registry/blocks/task-model/parts.jsx +449 -0
  414. package/registry/blocks/task-model/toolbar.jsx +271 -0
  415. package/registry/blocks/testimonials/README.md +139 -0
  416. package/registry/blocks/testimonials/block.json +11 -0
  417. package/registry/blocks/testimonials/demo.jsx +137 -0
  418. package/registry/blocks/testimonials/index.js +4 -0
  419. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  420. package/registry/blocks/transaction-list/README.md +126 -0
  421. package/registry/blocks/transaction-list/block.json +10 -0
  422. package/registry/blocks/transaction-list/demo.jsx +79 -0
  423. package/registry/blocks/transaction-list/index.js +4 -0
  424. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  425. package/registry/blocks/whiteboard/README.md +179 -0
  426. package/registry/blocks/whiteboard/block.json +10 -0
  427. package/registry/blocks/whiteboard/demo.jsx +169 -0
  428. package/registry/blocks/whiteboard/index.js +6 -0
  429. package/registry/blocks/whiteboard/templates.js +150 -0
  430. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  431. package/src/install.mjs +18 -0
  432. package/src/shadcn.mjs +171 -0
  433. package/templates/next/files/README.md +145 -11
  434. package/templates/next/files/app/globals.css +55 -4
  435. package/templates/next/files/check/run.mjs +3 -3
  436. package/templates/next/files/components/parts/fixture.jsx +1 -1
  437. package/templates/next/files/components/parts/sample.js +15 -0
  438. package/templates/next/files/components/ui/avatar.jsx +141 -0
  439. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  440. package/templates/next/files/components/ui/button-group.jsx +70 -0
  441. package/templates/next/files/components/ui/button.jsx +132 -12
  442. package/templates/next/files/components/ui/calendar.jsx +609 -0
  443. package/templates/next/files/components/ui/carousel.jsx +281 -0
  444. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  445. package/templates/next/files/components/ui/combobox.jsx +608 -0
  446. package/templates/next/files/components/ui/command.jsx +577 -0
  447. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  448. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  449. package/templates/next/files/components/ui/distribution.jsx +117 -0
  450. package/templates/next/files/components/ui/feedback.jsx +143 -0
  451. package/templates/next/files/components/ui/field.jsx +115 -3
  452. package/templates/next/files/components/ui/file-input.jsx +234 -0
  453. package/templates/next/files/components/ui/icon.jsx +130 -8
  454. package/templates/next/files/components/ui/image.jsx +143 -0
  455. package/templates/next/files/components/ui/index.js +47 -2
  456. package/templates/next/files/components/ui/input-group.jsx +213 -0
  457. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  458. package/templates/next/files/components/ui/item.jsx +206 -0
  459. package/templates/next/files/components/ui/menu.jsx +454 -82
  460. package/templates/next/files/components/ui/notice.jsx +126 -14
  461. package/templates/next/files/components/ui/pagination.jsx +156 -0
  462. package/templates/next/files/components/ui/popover.jsx +284 -0
  463. package/templates/next/files/components/ui/radio.jsx +193 -0
  464. package/templates/next/files/components/ui/rating.jsx +131 -0
  465. package/templates/next/files/components/ui/resizable.jsx +338 -0
  466. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  467. package/templates/next/files/components/ui/separator.jsx +119 -0
  468. package/templates/next/files/components/ui/sheet.jsx +96 -11
  469. package/templates/next/files/components/ui/slider.jsx +174 -0
  470. package/templates/next/files/components/ui/stat.jsx +574 -0
  471. package/templates/next/files/components/ui/steps.jsx +338 -0
  472. package/templates/next/files/components/ui/swatch.jsx +33 -0
  473. package/templates/next/files/components/ui/table.jsx +130 -0
  474. package/templates/next/files/components/ui/toast.jsx +440 -0
  475. package/templates/next/files/components/ui/toggle.jsx +195 -0
  476. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  477. package/templates/next/files/lib/sample.js +4 -12
  478. package/templates/next/template.json +264 -8
@@ -0,0 +1,649 @@
1
+ "use client";
2
+
3
+ // The canvas's chrome, all kit parts: the tool rail and its flyouts, the
4
+ // zoom bar, undo/redo with the save state, the floating toolbar over a
5
+ // selection, the connector's toolbar, the menu that follows a connector
6
+ // dropped on empty canvas, the empty state, the list view, the shortcuts
7
+ // sheet and the presentation bar. Each is a plain component driven by
8
+ // props; flow-canvas.jsx places them.
9
+ import { useEffect, useRef } from "react";
10
+ import {
11
+ Button,
12
+ Icon,
13
+ IconButton,
14
+ Kbd,
15
+ KbdGroup,
16
+ Kicker,
17
+ Menu,
18
+ MenuPanel,
19
+ Popover,
20
+ Separator,
21
+ Sheet,
22
+ Toggle,
23
+ Tooltip,
24
+ cx,
25
+ } from "../../ui";
26
+ import { ARROWS, COLOR_NAMES, LINES, SHAPES, SHAPE_NAMES, TEXT_SIZES, frameOf, describeNode, nodeRect } from "./board";
27
+ import { useCanvas } from "./context";
28
+ import { SOLID, TINT, tone } from "./theme";
29
+
30
+ /** The ⌘ key on a Mac, Ctrl elsewhere — for labels. */
31
+ export function modKey() {
32
+ if (typeof navigator === "undefined") return "Ctrl";
33
+ return /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "") ? "⌘" : "Ctrl";
34
+ }
35
+ const sc = (keys) => keys.replace("Mod", modKey());
36
+
37
+ const PANEL = "fc-chrome pointer-events-auto rounded-xl border border-border bg-popover text-popover-foreground shadow-lg";
38
+
39
+ // ── the rail ─────────────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * The tool rail: a vertical pill on the left, a bar along the bottom on a
43
+ * narrow canvas. `items` are tools ({ id, label, icon, shortcut, kind:
44
+ * "mode" | "action" }) and "|" separators.
45
+ */
46
+ export function ToolRail({ items, tool, onPick, narrow }) {
47
+ return (
48
+ <div role="toolbar" aria-label="Tools" aria-orientation={narrow ? "horizontal" : "vertical"} className={cx(PANEL, "flex gap-0.5 p-1", narrow ? "max-w-full flex-row overflow-x-auto" : "flex-col")}>
49
+ {items.map((t, i) =>
50
+ t === "|" ? (
51
+ <Separator key={`sep-${i}`} orientation={narrow ? "vertical" : "horizontal"} className={narrow ? "mx-0.5 my-1.5" : "mx-1.5 my-0.5 w-auto!"} />
52
+ ) : (
53
+ <Tooltip key={t.id} label={t.shortcut ? `${t.label} · ${t.shortcut}` : t.label} side={narrow ? "top" : "right"} delay={300}>
54
+ {t.kind === "action" ? (
55
+ <IconButton icon={t.icon} label={t.label} size={narrow ? "md" : "sm"} onClick={() => onPick(t)} aria-keyshortcuts={t.shortcut} />
56
+ ) : (
57
+ <Toggle
58
+ icon={t.icon}
59
+ label={t.label}
60
+ variant="quiet"
61
+ size={narrow ? "md" : "sm"}
62
+ pressed={tool === t.id}
63
+ onPressedChange={() => onPick(t)}
64
+ aria-keyshortcuts={t.shortcut}
65
+ />
66
+ )}
67
+ </Tooltip>
68
+ ),
69
+ )}
70
+ </div>
71
+ );
72
+ }
73
+
74
+ /** A small sticky-coloured square: the sticky palette's swatch. */
75
+ export function StickySwatch({ color, className }) {
76
+ return (
77
+ <span aria-hidden="true" className={cx("relative inline-block size-5 shrink-0 overflow-hidden rounded-sm bg-card ring-1 ring-border", className)}>
78
+ <span className={cx("absolute inset-0", tone(TINT, color, "warning"))} />
79
+ </span>
80
+ );
81
+ }
82
+
83
+ /** A full-strength colour dot. */
84
+ export function ColorDot({ color, className }) {
85
+ return <span aria-hidden="true" className={cx("inline-block size-4 shrink-0 rounded-full ring-1 ring-border", tone(SOLID, color), className)} />;
86
+ }
87
+
88
+ const colorName = (c, i) => COLOR_NAMES[c] ?? `Colour ${i + 1}`;
89
+
90
+ /** A row of colour choices (pressed = current). */
91
+ export function Palette({ colors, value, onPick, swatch = "sticky", label = "Colour" }) {
92
+ return (
93
+ <div role="group" aria-label={label} className="flex flex-wrap gap-0.5">
94
+ {colors.map((c, i) => (
95
+ <Tooltip key={c} label={colorName(c, i)} delay={300}>
96
+ <Button
97
+ variant="quiet"
98
+ size="sm"
99
+ iconOnly
100
+ aria-label={colorName(c, i)}
101
+ aria-pressed={value === c}
102
+ onClick={() => onPick(c)}
103
+ className="aria-pressed:bg-accent aria-pressed:ring-2 aria-pressed:ring-primary"
104
+ >
105
+ {swatch === "sticky" ? <StickySwatch color={c} /> : <ColorDot color={c} />}
106
+ </Button>
107
+ </Tooltip>
108
+ ))}
109
+ </div>
110
+ );
111
+ }
112
+
113
+ /** A little drawing of a shape kind, for menus and flyouts. */
114
+ export function ShapeGlyph({ kind }) {
115
+ const base = "inline-block shrink-0 border-2 border-current";
116
+ if (kind === "ellipse") return <span aria-hidden="true" className={cx(base, "h-3.5 w-5 rounded-full")} />;
117
+ if (kind === "diamond") return <span aria-hidden="true" className={cx(base, "mx-0.5 size-3.5 rotate-45 rounded-xs")} />;
118
+ if (kind === "pill") return <span aria-hidden="true" className={cx(base, "h-3 w-5 rounded-full")} />;
119
+ return <span aria-hidden="true" className={cx(base, "h-3.5 w-5 rounded-xs")} />;
120
+ }
121
+
122
+ /** What the sticky and shape tools offer while they are on: the colour, or
123
+ * the shape kind, of what the next click places. */
124
+ export function ToolFlyout({ tool, stickyColor, onStickyColor, shapeKind, onShapeKind, palette }) {
125
+ if (tool === "sticky") {
126
+ return (
127
+ <div className={cx(PANEL, "p-1")}>
128
+ <Palette colors={palette} value={stickyColor} onPick={onStickyColor} label="Sticky colour" />
129
+ </div>
130
+ );
131
+ }
132
+ if (tool === "shape") {
133
+ return (
134
+ <div role="group" aria-label="Shape" className={cx(PANEL, "flex gap-0.5 p-1")}>
135
+ {SHAPES.map((k) => (
136
+ <Tooltip key={k} label={SHAPE_NAMES[k]} delay={300}>
137
+ <Button variant="quiet" size="sm" iconOnly aria-label={SHAPE_NAMES[k]} aria-pressed={shapeKind === k} onClick={() => onShapeKind(k)} className="aria-pressed:bg-accent">
138
+ <ShapeGlyph kind={k} />
139
+ </Button>
140
+ </Tooltip>
141
+ ))}
142
+ </div>
143
+ );
144
+ }
145
+ return null;
146
+ }
147
+
148
+ // ── zoom, undo, save ─────────────────────────────────────────────────────
149
+
150
+ /** `zoom`: the zoom level to show, a node ("100%"). */
151
+ export function ZoomBar({ zoom, narrow, onZoomIn, onZoomOut, onZoomTo, onFit, onFitSelection, hasSelection, minimap, onMinimap, list, onList, snap, onSnap, onShortcuts, presentation }) {
152
+ return (
153
+ <div role="toolbar" aria-label="View" className={cx(PANEL, "flex items-center gap-0.5 p-1")}>
154
+ <Tooltip label="Zoom out" delay={300}>
155
+ <IconButton icon="minus" label="Zoom out" size="sm" onClick={onZoomOut} />
156
+ </Tooltip>
157
+ <Menu
158
+ label="Zoom options"
159
+ side="top"
160
+ align="center"
161
+ trigger={<span className="min-w-10 text-center text-xs font-medium tabular-nums">{zoom}</span>}
162
+ items={[
163
+ { label: "Zoom in", shortcut: sc("Mod+"), onSelect: onZoomIn },
164
+ { label: "Zoom out", shortcut: sc("Mod−"), onSelect: onZoomOut },
165
+ { type: "separator" },
166
+ { label: "Zoom to 50%", onSelect: () => onZoomTo(0.5) },
167
+ { label: "Zoom to 100%", shortcut: sc("Mod0"), onSelect: () => onZoomTo(1) },
168
+ { label: "Zoom to 200%", onSelect: () => onZoomTo(2) },
169
+ { type: "separator" },
170
+ { label: "Fit to screen", icon: "maximize", shortcut: "⇧1", onSelect: onFit },
171
+ { label: "Zoom to selection", shortcut: "⇧2", onSelect: onFitSelection, disabled: !hasSelection },
172
+ ]}
173
+ />
174
+ <Tooltip label="Zoom in" delay={300}>
175
+ <IconButton icon="plus" label="Zoom in" size="sm" onClick={onZoomIn} />
176
+ </Tooltip>
177
+ <Separator orientation="vertical" className="mx-0.5 my-1.5" />
178
+ <Tooltip label="Fit to screen · ⇧1" delay={300}>
179
+ <IconButton icon="maximize" label="Fit to screen" size="sm" onClick={onFit} />
180
+ </Tooltip>
181
+ {!narrow && !presentation ? (
182
+ <Tooltip label="Minimap" delay={300}>
183
+ <Toggle icon="map" label="Minimap" variant="quiet" size="sm" pressed={minimap} onPressedChange={onMinimap} />
184
+ </Tooltip>
185
+ ) : null}
186
+ <Tooltip label="List view" delay={300}>
187
+ <Toggle icon="list" label="List view" variant="quiet" size="sm" pressed={list} onPressedChange={onList} />
188
+ </Tooltip>
189
+ {!presentation ? (
190
+ <Menu
191
+ label="More view options"
192
+ side="top"
193
+ items={[
194
+ { type: "checkbox", label: "Snap to grid", checked: snap, onCheckedChange: onSnap },
195
+ { type: "separator" },
196
+ { label: "Keyboard shortcuts", icon: "keyboard", shortcut: "?", onSelect: onShortcuts },
197
+ ]}
198
+ />
199
+ ) : null}
200
+ </div>
201
+ );
202
+ }
203
+
204
+ export function HistoryBar({ canUndo, canRedo, onUndo, onRedo, save, onRetry }) {
205
+ return (
206
+ <div className="flex items-center gap-2">
207
+ <div role="toolbar" aria-label="History" className={cx(PANEL, "flex items-center gap-0.5 p-1")}>
208
+ <Tooltip label={`Undo · ${sc("ModZ")}`} delay={300}>
209
+ <IconButton icon="undo" label="Undo" size="sm" onClick={onUndo} disabled={!canUndo} />
210
+ </Tooltip>
211
+ <Tooltip label={`Redo · ⇧${sc("ModZ")}`} delay={300}>
212
+ <IconButton icon="redo" label="Redo" size="sm" onClick={onRedo} disabled={!canRedo} />
213
+ </Tooltip>
214
+ </div>
215
+ <SaveState save={save} onRetry={onRetry} />
216
+ </div>
217
+ );
218
+ }
219
+
220
+ /** "Saving…", "Saved", or why it did not save with a Retry. */
221
+ export function SaveState({ save, onRetry }) {
222
+ if (!save || save.state === "idle") return null;
223
+ if (save.state === "error") {
224
+ return (
225
+ <span role="alert" className={cx(PANEL, "flex items-center gap-1.5 py-1 pr-1 pl-2.5 text-xs text-destructive")}>
226
+ <Icon name="warning" size={14} />
227
+ <span className="max-w-48 truncate">{save.error || "Not saved"}</span>
228
+ <Button variant="quiet" size="sm" onClick={onRetry}>
229
+ Retry
230
+ </Button>
231
+ </span>
232
+ );
233
+ }
234
+ return (
235
+ <span aria-live="polite" className="fc-chrome flex items-center gap-1 rounded-full bg-background/80 px-2 py-1 text-xs text-muted-foreground">
236
+ {save.state === "saving" ? <Icon name="loader" size={12} className="animate-spin motion-reduce:animate-none" /> : <Icon name="check" size={12} />}
237
+ {save.state === "saving" ? "Saving…" : "Saved"}
238
+ </span>
239
+ );
240
+ }
241
+
242
+ // ── the floating toolbar over a selection ────────────────────────────────
243
+
244
+ const COLORABLE = new Set(["sticky", "shape", "frame", "card"]);
245
+ const TEXT_SIZE_LABEL = { sm: "Small", md: "Medium", lg: "Large", xl: "Extra large" };
246
+
247
+ /**
248
+ * Above whatever is selected: colour, shape kind, text size, align, front /
249
+ * back, duplicate, lock, delete — only what applies to the selection —
250
+ * then the block's own `extra`.
251
+ */
252
+ export function SelectionToolbar({ nodes, api, palette, extra }) {
253
+ if (!nodes.length || !api) return null;
254
+ const ids = nodes.map((n) => n.id);
255
+ const locked = nodes.every((n) => n.data?.locked);
256
+ const anyLocked = nodes.some((n) => n.data?.locked);
257
+ const colorable = nodes.filter((n) => COLORABLE.has(n.type) || n.data?.color !== undefined);
258
+ const stickies = colorable.length > 0 && colorable.every((n) => n.type === "sticky");
259
+ const colors = stickies ? palette : [...new Set([...palette, "chart-1", "success", "info", "destructive", "muted"])];
260
+ const color = colorable[0]?.data?.color ?? (colorable[0]?.type === "sticky" ? "warning" : "muted");
261
+ const shapes = nodes.filter((n) => n.type === "shape");
262
+ const sized = nodes.filter((n) => n.type === "shape" || n.type === "text");
263
+ const setData = (list, patch) => api.updateNodesData(list.map((n) => n.id), patch);
264
+ const many = nodes.length > 1;
265
+ const own = typeof extra === "function" ? extra({ nodes, edges: [] }, api) : extra;
266
+
267
+ return (
268
+ <div role="toolbar" aria-label="Selection" className={cx(PANEL, "nodrag nopan nowheel flex items-center gap-0.5 p-1")}>
269
+ {colorable.length && !locked ? (
270
+ <Popover
271
+ label="Colour"
272
+ side="top"
273
+ width="auto"
274
+ className="p-1.5"
275
+ trigger={(p) => (
276
+ <Button {...p} variant="quiet" size="sm" iconOnly aria-label="Colour">
277
+ {stickies ? <StickySwatch color={color} /> : <ColorDot color={color} />}
278
+ </Button>
279
+ )}
280
+ >
281
+ <Palette colors={colors} value={color} swatch={stickies ? "sticky" : "dot"} onPick={(c) => setData(colorable, { color: c })} />
282
+ {colorable.some((n) => n.type === "frame") ? (
283
+ <Button variant="quiet" size="sm" className="mt-1 w-full" onClick={() => setData(colorable, { color: undefined })}>
284
+ No colour
285
+ </Button>
286
+ ) : null}
287
+ </Popover>
288
+ ) : null}
289
+ {shapes.length && !locked ? (
290
+ <Menu
291
+ label="Shape"
292
+ side="top"
293
+ align="center"
294
+ trigger={<ShapeGlyph kind={shapes[0].data?.shape ?? "rect"} />}
295
+ items={[{ type: "radio", label: "Shape", value: shapes[0].data?.shape ?? "rect", onValueChange: (v) => setData(shapes, { shape: v }), options: SHAPES.map((k) => ({ value: k, label: SHAPE_NAMES[k] })) }]}
296
+ />
297
+ ) : null}
298
+ {sized.length && !locked ? (
299
+ <Menu
300
+ label="Text size"
301
+ side="top"
302
+ align="center"
303
+ trigger={<Icon name="type" size={16} />}
304
+ items={[
305
+ {
306
+ type: "radio",
307
+ label: "Text size",
308
+ value: sized[0].data?.textSize ?? "md",
309
+ onValueChange: (v) => setData(sized, { textSize: v }),
310
+ options: TEXT_SIZES.map((k) => ({ value: k, label: TEXT_SIZE_LABEL[k] })),
311
+ },
312
+ ]}
313
+ />
314
+ ) : null}
315
+ {many && !locked ? (
316
+ <Menu
317
+ label="Align"
318
+ side="top"
319
+ align="center"
320
+ trigger={<Icon name="align-center" size={16} />}
321
+ items={[
322
+ { label: "Align left", onSelect: () => api.align(ids, "left") },
323
+ { label: "Align centre", onSelect: () => api.align(ids, "center") },
324
+ { label: "Align right", onSelect: () => api.align(ids, "right") },
325
+ { type: "separator" },
326
+ { label: "Align top", onSelect: () => api.align(ids, "top") },
327
+ { label: "Align middle", onSelect: () => api.align(ids, "middle") },
328
+ { label: "Align bottom", onSelect: () => api.align(ids, "bottom") },
329
+ nodes.length > 2 && { type: "separator" },
330
+ nodes.length > 2 && { label: "Distribute horizontally", onSelect: () => api.distribute(ids, "horizontal") },
331
+ nodes.length > 2 && { label: "Distribute vertically", onSelect: () => api.distribute(ids, "vertical") },
332
+ { type: "separator" },
333
+ { label: "Tidy up (left to right)", onSelect: () => api.autoLayout({ ids, direction: "LR" }) },
334
+ { label: "Tidy up (top to bottom)", onSelect: () => api.autoLayout({ ids, direction: "TB" }) },
335
+ ]}
336
+ />
337
+ ) : null}
338
+ <Separator orientation="vertical" className="mx-0.5 my-1.5" />
339
+ <Tooltip label={`Bring to front · ${sc("Mod]")}`} delay={300}>
340
+ <IconButton icon="bring-front" label="Bring to front" size="sm" onClick={() => api.arrange(ids, "front")} />
341
+ </Tooltip>
342
+ <Tooltip label={`Send to back · ${sc("Mod[")}`} delay={300}>
343
+ <IconButton icon="send-back" label="Send to back" size="sm" onClick={() => api.arrange(ids, "back")} />
344
+ </Tooltip>
345
+ <Tooltip label={`Duplicate · ${sc("ModD")}`} delay={300}>
346
+ <IconButton icon="copy" label="Duplicate" size="sm" onClick={() => api.duplicate(ids)} />
347
+ </Tooltip>
348
+ <Tooltip label={`${anyLocked ? "Unlock" : "Lock"} · ⇧${sc("ModL")}`} delay={300}>
349
+ <IconButton icon={anyLocked ? "lock-open" : "lock"} label={anyLocked ? "Unlock" : "Lock"} size="sm" onClick={() => api.lock(ids, !anyLocked)} />
350
+ </Tooltip>
351
+ {!locked ? (
352
+ <Tooltip label="Delete · ⌫" delay={300}>
353
+ <IconButton icon="trash" label="Delete" size="sm" onClick={() => api.removeNodes(ids.filter((id) => !nodes.find((n) => n.id === id)?.data?.locked))} />
354
+ </Tooltip>
355
+ ) : null}
356
+ {own ? (
357
+ <>
358
+ <Separator orientation="vertical" className="mx-0.5 my-1.5" />
359
+ {own}
360
+ </>
361
+ ) : null}
362
+ </div>
363
+ );
364
+ }
365
+
366
+ const LINE_LABEL = { curved: "Curved", straight: "Straight", elbow: "Elbow" };
367
+ const ARROW_LABEL = { end: "Arrow at the end", both: "Arrows at both ends", none: "No arrows" };
368
+
369
+ /** Above a selected connector: its line, its arrows, its label, delete. */
370
+ export function EdgeToolbar({ id, data, label }) {
371
+ const { api } = useCanvas();
372
+ if (!api) return null;
373
+ const set = (patch) => api.updateEdge(id, { data: { ...(data ?? {}), ...patch } });
374
+ return (
375
+ <div role="toolbar" aria-label="Connector" className={cx(PANEL, "flex items-center gap-0.5 p-1")}>
376
+ <Menu
377
+ label="Line style"
378
+ side="top"
379
+ align="center"
380
+ trigger={<span className="px-1 text-xs font-medium">{LINE_LABEL[data?.line ?? "curved"]}</span>}
381
+ items={[
382
+ { type: "radio", label: "Line", value: data?.line ?? "curved", onValueChange: (v) => set({ line: v }), options: LINES.map((k) => ({ value: k, label: LINE_LABEL[k] })) },
383
+ { type: "separator" },
384
+ { type: "checkbox", label: "Dashed", checked: Boolean(data?.dashed), onCheckedChange: (v) => set({ dashed: v || undefined }) },
385
+ ]}
386
+ />
387
+ <Menu
388
+ label="Arrows"
389
+ side="top"
390
+ align="center"
391
+ trigger={<Icon name={(data?.arrow ?? "end") === "none" ? "minus" : "arrow-right"} size={16} />}
392
+ items={[{ type: "radio", label: "Arrows", value: data?.arrow ?? "end", onValueChange: (v) => set({ arrow: v }), options: ARROWS.map((k) => ({ value: k, label: ARROW_LABEL[k] })) }]}
393
+ />
394
+ <Tooltip label={label ? "Edit label" : "Add label"} delay={300}>
395
+ <IconButton icon="pencil" label={label ? "Edit label" : "Add label"} size="sm" onClick={() => api.editEdge(id)} />
396
+ </Tooltip>
397
+ <Tooltip label="Delete · ⌫" delay={300}>
398
+ <IconButton icon="trash" label="Delete connector" size="sm" onClick={() => api.removeEdges([id])} />
399
+ </Tooltip>
400
+ </div>
401
+ );
402
+ }
403
+
404
+ /**
405
+ * A point in the canvas box where a floating toolbar hangs: centred on `x`,
406
+ * its foot on `top` (above the thing), or its head on `bottom` when there
407
+ * is no room above. Positioned with left/top only — no transform — so the
408
+ * kit's menus and tooltips inside it open beside their buttons.
409
+ */
410
+ export function FloatAt({ x, top, bottom, children }) {
411
+ const below = top < 64;
412
+ return (
413
+ <div
414
+ className={cx("pointer-events-none absolute z-20 flex h-0 w-0 justify-center [&>*]:shrink-0", below ? "items-start" : "items-end")}
415
+ style={{ left: Math.round(x), top: Math.round(below ? bottom : top) }}
416
+ >
417
+ {children}
418
+ </div>
419
+ );
420
+ }
421
+
422
+ // ── the menu after a connector dropped on empty canvas ───────────────────
423
+
424
+ export function ConnectMenu({ at, items, onClose }) {
425
+ const ref = useRef(null);
426
+ useEffect(() => {
427
+ ref.current?.querySelector('[role^="menuitem"]:not([disabled])')?.focus({ preventScroll: true });
428
+ }, []);
429
+ return (
430
+ <div className="fc-chrome absolute z-20" style={{ left: at.x, top: at.y }}>
431
+ <MenuPanel
432
+ panelRef={ref}
433
+ items={items}
434
+ label="Add a connected item"
435
+ onClose={onClose}
436
+ onExit={onClose}
437
+ className="min-w-48 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg"
438
+ />
439
+ </div>
440
+ );
441
+ }
442
+
443
+ // ── empty state ──────────────────────────────────────────────────────────
444
+
445
+ export function EmptyState({ templates, onTemplate, narrow }) {
446
+ return (
447
+ <div className="pointer-events-none absolute inset-0 grid place-items-center p-6">
448
+ <div className="fc-chrome grid max-w-sm justify-items-center gap-2 text-center">
449
+ <span aria-hidden="true" className="relative mb-1 grid size-14 -rotate-6 place-items-center overflow-hidden rounded-md bg-card shadow-md">
450
+ <span className="absolute inset-0 bg-warning/35" />
451
+ <Icon name="plus" size={22} className="relative text-foreground" />
452
+ </span>
453
+ <p className="text-base font-medium">Double-click anywhere to add a sticky</p>
454
+ <p className="text-sm text-muted-foreground">
455
+ {narrow ? "Or pick a tool below." : "Or pick a tool on the left:"} <Kbd>N</Kbd> sticky, <Kbd>S</Kbd> shape, <Kbd>T</Kbd> text, <Kbd>L</Kbd> connector.
456
+ </p>
457
+ {templates?.length ? (
458
+ <div className="pointer-events-auto mt-3 grid justify-items-center gap-2">
459
+ <Kicker>Or start from</Kicker>
460
+ <div className="flex flex-wrap justify-center gap-2">
461
+ {templates.map((t) => (
462
+ <Button key={t.id} variant="secondary" size="sm" icon={t.icon} onClick={() => onTemplate(t)} title={t.description}>
463
+ {t.label}
464
+ </Button>
465
+ ))}
466
+ </div>
467
+ </div>
468
+ ) : null}
469
+ </div>
470
+ </div>
471
+ );
472
+ }
473
+
474
+ // ── list view ────────────────────────────────────────────────────────────
475
+
476
+ const TYPE_ICON = { sticky: "sticky-note", shape: "square", text: "type", frame: "frame", card: "file", image: "image" };
477
+
478
+ /** The board as a list: every frame with what it holds, every connector.
479
+ * A row goes back to the canvas with that item selected. */
480
+ export function ListView({ board, label, onPick, onClose, describe, iconOf }) {
481
+ const nodes = board.nodes;
482
+ const parent = frameOf(board);
483
+ const kids = new Map();
484
+ for (const n of nodes) {
485
+ const p = parent.get(n.id) ?? null;
486
+ if (!kids.has(p)) kids.set(p, []);
487
+ kids.get(p).push(n);
488
+ }
489
+ const order = (a, b) => {
490
+ const ra = nodeRect(a);
491
+ const rb = nodeRect(b);
492
+ return (a.type === "frame") - (b.type === "frame") || (Math.abs(ra.y - rb.y) > 24 ? ra.y - rb.y : ra.x - rb.x);
493
+ };
494
+ for (const l of kids.values()) l.sort(order);
495
+ const byId = new Map(nodes.map((n) => [n.id, n]));
496
+ const row = (n) => (
497
+ <li key={n.id}>
498
+ <Button variant="nav" size="sm" icon={iconOf?.(n) ?? TYPE_ICON[n.type] ?? "circle"} onClick={() => onPick(n.id)}>
499
+ <span className="truncate">{describeNode(n, describe)}</span>
500
+ </Button>
501
+ </li>
502
+ );
503
+ const section = (parentId, depth) =>
504
+ (kids.get(parentId) ?? []).map((n) =>
505
+ n.type === "frame" ? (
506
+ <li key={n.id} className={cx("grid gap-1", depth ? "ml-4" : "")}>
507
+ <Button variant="nav" size="sm" icon="frame" onClick={() => onPick(n.id)}>
508
+ <span className="truncate font-medium">{n.data?.title || "Untitled frame"}</span>
509
+ </Button>
510
+ <ul className="ml-4 grid gap-0.5 border-l border-border pl-2">{section(n.id, depth + 1)}</ul>
511
+ </li>
512
+ ) : (
513
+ row(n)
514
+ ),
515
+ );
516
+ return (
517
+ <div className="fc-chrome absolute inset-0 z-10 overflow-auto bg-background">
518
+ <div className="mx-auto grid max-w-2xl gap-4 p-4 pb-24 sm:p-6">
519
+ <div className="flex items-center justify-between gap-3">
520
+ <h2 className="text-base font-semibold">{label} as a list</h2>
521
+ <Button size="sm" icon="grid" onClick={onClose}>
522
+ Back to canvas
523
+ </Button>
524
+ </div>
525
+ {nodes.length ? <ul className="grid gap-0.5">{section(null, 0)}</ul> : <p className="text-sm text-muted-foreground">Nothing on the board yet.</p>}
526
+ {board.edges.length ? (
527
+ <div className="grid gap-1">
528
+ <Kicker>Connectors</Kicker>
529
+ <ul className="grid gap-1 text-sm">
530
+ {board.edges.map((e) => (
531
+ <li key={e.id} className="flex items-center gap-2 text-muted-foreground">
532
+ <span className="truncate text-foreground">{describeNode(byId.get(e.source), describe)}</span>
533
+ <Icon name={e.data?.arrow === "none" ? "minus" : "arrow-right"} size={14} className="shrink-0" />
534
+ <span className="truncate text-foreground">{describeNode(byId.get(e.target), describe)}</span>
535
+ {e.label ? <span className="shrink-0">({e.label})</span> : null}
536
+ </li>
537
+ ))}
538
+ </ul>
539
+ </div>
540
+ ) : null}
541
+ </div>
542
+ </div>
543
+ );
544
+ }
545
+
546
+ // ── shortcuts ────────────────────────────────────────────────────────────
547
+
548
+ function Row({ label, keys }) {
549
+ return (
550
+ <div className="flex items-center justify-between gap-4 py-1.5 text-sm">
551
+ <dt className="text-muted-foreground">{label}</dt>
552
+ <dd>{typeof keys === "string" ? <span className="text-xs text-muted-foreground">{keys}</span> : <KbdGroup keys={keys} />}</dd>
553
+ </div>
554
+ );
555
+ }
556
+
557
+ export function ShortcutsSheet({ open, onClose, tools }) {
558
+ const m = modKey();
559
+ const groups = [
560
+ ["Tools", tools.filter((t) => t !== "|" && t.shortcut).map((t) => [t.label, [t.shortcut]])],
561
+ [
562
+ "Edit",
563
+ [
564
+ ["Undo", [m, "Z"]],
565
+ ["Redo", ["⇧", m, "Z"]],
566
+ ["Duplicate", [m, "D"]],
567
+ ["Copy · paste", [m, "C"]],
568
+ ["Select all", [m, "A"]],
569
+ ["Delete", ["⌫"]],
570
+ ["Lock or unlock", ["⇧", m, "L"]],
571
+ ["Bring to front", [m, "]"]],
572
+ ["Send to back", [m, "["]],
573
+ ],
574
+ ],
575
+ [
576
+ "Cards",
577
+ [
578
+ ["New sticky", "Double-click the canvas"],
579
+ ["Edit words", ["↵"]],
580
+ ["Stop editing", ["Esc"]],
581
+ ["Nudge", ["←", "→"]],
582
+ ["Nudge 10px", ["⇧", "→"]],
583
+ ["Next card", ["⇥"]],
584
+ ["Actions menu", ["⇧", "F10"]],
585
+ ],
586
+ ],
587
+ [
588
+ "View",
589
+ [
590
+ ["Pan", "Scroll, or Space + drag"],
591
+ ["Zoom", "Pinch, or Ctrl + scroll"],
592
+ ["Zoom in · out", [m, "+"]],
593
+ ["Zoom to 100%", [m, "0"]],
594
+ ["Fit to screen", ["⇧", "1"]],
595
+ ["Zoom to selection", ["⇧", "2"]],
596
+ ["This sheet", ["?"]],
597
+ ],
598
+ ],
599
+ ];
600
+ return (
601
+ <Sheet open={open} onClose={onClose} title="Keyboard shortcuts" description="The board answers to these while it has focus.">
602
+ <div className="grid gap-5">
603
+ {groups.map(([title, rows]) =>
604
+ rows.length ? (
605
+ <section key={title}>
606
+ <Kicker className="mb-1">{title}</Kicker>
607
+ <dl className="divide-y divide-border">
608
+ {rows.map(([label, keys]) => (
609
+ <Row key={label} label={label} keys={keys} />
610
+ ))}
611
+ </dl>
612
+ </section>
613
+ ) : null,
614
+ )}
615
+ </div>
616
+ </Sheet>
617
+ );
618
+ }
619
+
620
+ // ── presentation ─────────────────────────────────────────────────────────
621
+
622
+ export function PresentBar({ index, count, title, onPrev, onNext, onExit }) {
623
+ return (
624
+ <div role="toolbar" aria-label="Presentation" className={cx(PANEL, "flex items-center gap-1 p-1")}>
625
+ <IconButton icon="chevron-left" label="Previous frame" size="sm" onClick={onPrev} disabled={index <= 0} />
626
+ <span aria-live="polite" className="min-w-24 px-1 text-center text-sm">
627
+ {count ? (
628
+ <>
629
+ <span className="tabular-nums text-muted-foreground">
630
+ {index + 1} / {count}
631
+ </span>{" "}
632
+ <span className="font-medium">{title}</span>
633
+ </>
634
+ ) : (
635
+ <span className="text-muted-foreground">No frames</span>
636
+ )}
637
+ </span>
638
+ <IconButton icon="chevron-right" label="Next frame" size="sm" onClick={onNext} disabled={index >= count - 1} />
639
+ {onExit ? (
640
+ <>
641
+ <Separator orientation="vertical" className="mx-0.5 my-1.5" />
642
+ <Button variant="quiet" size="sm" icon="x" onClick={onExit}>
643
+ Exit
644
+ </Button>
645
+ </>
646
+ ) : null}
647
+ </div>
648
+ );
649
+ }