makersclaw 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/THIRD-PARTY-NOTICES.md +180 -0
  2. package/package.json +3 -2
  3. package/r/annotate.json +62 -0
  4. package/r/approval-request.json +52 -0
  5. package/r/audio-player.json +74 -0
  6. package/r/auth-form.json +55 -0
  7. package/r/board.json +10 -6
  8. package/r/calendar-view.json +105 -0
  9. package/r/cart.json +62 -0
  10. package/r/chart.json +83 -0
  11. package/r/chat.json +95 -0
  12. package/r/code-editor.json +94 -0
  13. package/r/comment-box.json +64 -0
  14. package/r/contact-section.json +53 -0
  15. package/r/core.json +205 -8
  16. package/r/csv-import.json +63 -0
  17. package/r/data-table.json +69 -0
  18. package/r/diagram.json +74 -0
  19. package/r/doc-editor.json +82 -0
  20. package/r/doc-page.json +61 -0
  21. package/r/email-render.json +73 -0
  22. package/r/faq.json +50 -0
  23. package/r/file-upload.json +54 -0
  24. package/r/flow-canvas.json +93 -0
  25. package/r/footer.json +52 -0
  26. package/r/gantt.json +70 -0
  27. package/r/job-panel.json +12 -7
  28. package/r/json-editor.json +66 -0
  29. package/r/map.json +89 -0
  30. package/r/menubar.json +52 -0
  31. package/r/mind-map.json +67 -0
  32. package/r/navigation-menu.json +52 -0
  33. package/r/order-detail.json +54 -0
  34. package/r/org-chart.json +66 -0
  35. package/r/page-builder.json +105 -0
  36. package/r/payment-methods.json +51 -0
  37. package/r/pdf-document.json +79 -0
  38. package/r/pdf-viewer.json +88 -0
  39. package/r/product-detail.json +76 -0
  40. package/r/product-grid.json +65 -0
  41. package/r/record-form.json +67 -0
  42. package/r/record-notes.json +55 -0
  43. package/r/registry.json +3411 -29
  44. package/r/search-dialog.json +51 -0
  45. package/r/settings-list.json +66 -0
  46. package/r/setup-checklist.json +51 -0
  47. package/r/share-dialog.json +52 -0
  48. package/r/sidebar.json +69 -0
  49. package/r/signature-pad.json +72 -0
  50. package/r/sketch-canvas.json +99 -0
  51. package/r/spreadsheet.json +90 -0
  52. package/r/stat-cards.json +66 -0
  53. package/r/task-list.json +63 -0
  54. package/r/task-model.json +98 -0
  55. package/r/testimonials.json +52 -0
  56. package/r/transaction-list.json +53 -0
  57. package/r/whiteboard.json +66 -0
  58. package/registry/blocks/annotate/README.md +179 -0
  59. package/registry/blocks/annotate/annotate-data.js +250 -0
  60. package/registry/blocks/annotate/annotate.jsx +646 -0
  61. package/registry/blocks/annotate/block.json +10 -0
  62. package/registry/blocks/annotate/demo.jsx +135 -0
  63. package/registry/blocks/annotate/index.js +15 -0
  64. package/registry/blocks/approval-request/README.md +116 -0
  65. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  66. package/registry/blocks/approval-request/block.json +10 -0
  67. package/registry/blocks/approval-request/demo.jsx +127 -0
  68. package/registry/blocks/approval-request/index.js +4 -0
  69. package/registry/blocks/audio-player/README.md +167 -0
  70. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  71. package/registry/blocks/audio-player/block.json +14 -0
  72. package/registry/blocks/audio-player/colors.js +109 -0
  73. package/registry/blocks/audio-player/demo.jsx +209 -0
  74. package/registry/blocks/audio-player/index.js +6 -0
  75. package/registry/blocks/audio-player/time.js +67 -0
  76. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  77. package/registry/blocks/auth-form/README.md +126 -0
  78. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  79. package/registry/blocks/auth-form/block.json +10 -0
  80. package/registry/blocks/auth-form/demo.jsx +131 -0
  81. package/registry/blocks/auth-form/index.js +3 -0
  82. package/registry/blocks/board/README.md +55 -5
  83. package/registry/blocks/board/block.json +2 -2
  84. package/registry/blocks/board/board.jsx +230 -86
  85. package/registry/blocks/board/demo.jsx +103 -24
  86. package/registry/blocks/calendar-view/README.md +180 -0
  87. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  88. package/registry/blocks/calendar-view/block.json +76 -0
  89. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  90. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  91. package/registry/blocks/calendar-view/demo.jsx +195 -0
  92. package/registry/blocks/calendar-view/draft.jsx +49 -0
  93. package/registry/blocks/calendar-view/index.js +39 -0
  94. package/registry/blocks/calendar-view/month.jsx +346 -0
  95. package/registry/blocks/calendar-view/paint.js +69 -0
  96. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  97. package/registry/blocks/calendar-view/tray.jsx +87 -0
  98. package/registry/blocks/cart/README.md +146 -0
  99. package/registry/blocks/cart/block.json +10 -0
  100. package/registry/blocks/cart/cart.jsx +597 -0
  101. package/registry/blocks/cart/demo.jsx +98 -0
  102. package/registry/blocks/cart/index.js +4 -0
  103. package/registry/blocks/cart/money.js +28 -0
  104. package/registry/blocks/chart/README.md +316 -0
  105. package/registry/blocks/chart/block.json +10 -0
  106. package/registry/blocks/chart/cartesian.jsx +642 -0
  107. package/registry/blocks/chart/core.jsx +262 -0
  108. package/registry/blocks/chart/demo.jsx +357 -0
  109. package/registry/blocks/chart/heatmap.jsx +336 -0
  110. package/registry/blocks/chart/index.js +8 -0
  111. package/registry/blocks/chart/pie.jsx +245 -0
  112. package/registry/blocks/chart/sparkline.jsx +88 -0
  113. package/registry/blocks/chat/README.md +273 -0
  114. package/registry/blocks/chat/attachment.jsx +95 -0
  115. package/registry/blocks/chat/block.json +10 -0
  116. package/registry/blocks/chat/chat.jsx +132 -0
  117. package/registry/blocks/chat/composer.jsx +364 -0
  118. package/registry/blocks/chat/demo.jsx +578 -0
  119. package/registry/blocks/chat/header.jsx +34 -0
  120. package/registry/blocks/chat/index.js +9 -0
  121. package/registry/blocks/chat/message.jsx +450 -0
  122. package/registry/blocks/chat/scroller.jsx +247 -0
  123. package/registry/blocks/chat/text.jsx +104 -0
  124. package/registry/blocks/code-editor/README.md +210 -0
  125. package/registry/blocks/code-editor/block.json +22 -0
  126. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  127. package/registry/blocks/code-editor/codemirror.js +38 -0
  128. package/registry/blocks/code-editor/demo.jsx +205 -0
  129. package/registry/blocks/code-editor/index.js +8 -0
  130. package/registry/blocks/code-editor/json.js +317 -0
  131. package/registry/blocks/code-editor/sql.js +136 -0
  132. package/registry/blocks/code-editor/text.js +101 -0
  133. package/registry/blocks/code-editor/theme.js +186 -0
  134. package/registry/blocks/comment-box/README.md +123 -0
  135. package/registry/blocks/comment-box/block.json +10 -0
  136. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  137. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  138. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  139. package/registry/blocks/comment-box/demo.jsx +141 -0
  140. package/registry/blocks/comment-box/index.js +8 -0
  141. package/registry/blocks/contact-section/README.md +152 -0
  142. package/registry/blocks/contact-section/block.json +11 -0
  143. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  144. package/registry/blocks/contact-section/demo.jsx +93 -0
  145. package/registry/blocks/contact-section/index.js +4 -0
  146. package/registry/blocks/csv-import/README.md +133 -0
  147. package/registry/blocks/csv-import/block.json +13 -0
  148. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  149. package/registry/blocks/csv-import/demo.jsx +106 -0
  150. package/registry/blocks/csv-import/import.js +200 -0
  151. package/registry/blocks/csv-import/index.js +7 -0
  152. package/registry/blocks/data-table/README.md +386 -0
  153. package/registry/blocks/data-table/block.json +10 -0
  154. package/registry/blocks/data-table/data-table.jsx +1164 -0
  155. package/registry/blocks/data-table/demo.jsx +588 -0
  156. package/registry/blocks/data-table/index.js +5 -0
  157. package/registry/blocks/data-table/model.js +169 -0
  158. package/registry/blocks/diagram/README.md +108 -0
  159. package/registry/blocks/diagram/block.json +13 -0
  160. package/registry/blocks/diagram/demo.jsx +113 -0
  161. package/registry/blocks/diagram/diagram.jsx +481 -0
  162. package/registry/blocks/diagram/index.js +7 -0
  163. package/registry/blocks/diagram/mermaid.js +52 -0
  164. package/registry/blocks/diagram/palette.js +313 -0
  165. package/registry/blocks/diagram/source.js +98 -0
  166. package/registry/blocks/doc-editor/README.md +215 -0
  167. package/registry/blocks/doc-editor/block.json +15 -0
  168. package/registry/blocks/doc-editor/demo.jsx +225 -0
  169. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  170. package/registry/blocks/doc-editor/doc.js +309 -0
  171. package/registry/blocks/doc-editor/index.js +7 -0
  172. package/registry/blocks/doc-editor/schema.jsx +56 -0
  173. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  174. package/registry/blocks/doc-editor/theme.js +82 -0
  175. package/registry/blocks/doc-page/README.md +119 -0
  176. package/registry/blocks/doc-page/autosave.js +104 -0
  177. package/registry/blocks/doc-page/block.json +10 -0
  178. package/registry/blocks/doc-page/demo.jsx +137 -0
  179. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  180. package/registry/blocks/doc-page/index.js +4 -0
  181. package/registry/blocks/email-render/README.md +151 -0
  182. package/registry/blocks/email-render/block.json +14 -0
  183. package/registry/blocks/email-render/demo.jsx +153 -0
  184. package/registry/blocks/email-render/doc-email.jsx +210 -0
  185. package/registry/blocks/email-render/email.jsx +221 -0
  186. package/registry/blocks/email-render/index.js +10 -0
  187. package/registry/blocks/email-render/render.js +50 -0
  188. package/registry/blocks/email-render/theme.js +215 -0
  189. package/registry/blocks/faq/README.md +101 -0
  190. package/registry/blocks/faq/block.json +11 -0
  191. package/registry/blocks/faq/demo.jsx +89 -0
  192. package/registry/blocks/faq/faq.jsx +220 -0
  193. package/registry/blocks/faq/index.js +4 -0
  194. package/registry/blocks/file-upload/README.md +138 -0
  195. package/registry/blocks/file-upload/block.json +10 -0
  196. package/registry/blocks/file-upload/demo.jsx +204 -0
  197. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  198. package/registry/blocks/file-upload/index.js +4 -0
  199. package/registry/blocks/flow-canvas/README.md +420 -0
  200. package/registry/blocks/flow-canvas/block.json +13 -0
  201. package/registry/blocks/flow-canvas/board.js +819 -0
  202. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  203. package/registry/blocks/flow-canvas/context.jsx +35 -0
  204. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  205. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  206. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  207. package/registry/blocks/flow-canvas/index.js +41 -0
  208. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  209. package/registry/blocks/flow-canvas/theme.js +174 -0
  210. package/registry/blocks/footer/README.md +133 -0
  211. package/registry/blocks/footer/block.json +11 -0
  212. package/registry/blocks/footer/demo.jsx +119 -0
  213. package/registry/blocks/footer/footer.jsx +406 -0
  214. package/registry/blocks/footer/index.js +4 -0
  215. package/registry/blocks/gantt/README.md +220 -0
  216. package/registry/blocks/gantt/block.json +10 -0
  217. package/registry/blocks/gantt/demo.jsx +148 -0
  218. package/registry/blocks/gantt/gantt.jsx +923 -0
  219. package/registry/blocks/gantt/index.js +7 -0
  220. package/registry/blocks/gantt/rows.jsx +468 -0
  221. package/registry/blocks/gantt/schedule.js +278 -0
  222. package/registry/blocks/job-panel/README.md +80 -0
  223. package/registry/blocks/job-panel/block.json +3 -3
  224. package/registry/blocks/job-panel/demo.jsx +230 -3
  225. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  226. package/registry/blocks/json-editor/README.md +133 -0
  227. package/registry/blocks/json-editor/block.json +10 -0
  228. package/registry/blocks/json-editor/demo.jsx +92 -0
  229. package/registry/blocks/json-editor/index.js +7 -0
  230. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  231. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  232. package/registry/blocks/json-editor/tree.js +225 -0
  233. package/registry/blocks/map/README.md +221 -0
  234. package/registry/blocks/map/block.json +14 -0
  235. package/registry/blocks/map/demo.jsx +201 -0
  236. package/registry/blocks/map/geo.js +198 -0
  237. package/registry/blocks/map/index.js +8 -0
  238. package/registry/blocks/map/list.jsx +128 -0
  239. package/registry/blocks/map/map-view.jsx +715 -0
  240. package/registry/blocks/map/maplibre.js +30 -0
  241. package/registry/blocks/map/marks.jsx +146 -0
  242. package/registry/blocks/map/theme.js +142 -0
  243. package/registry/blocks/menubar/README.md +109 -0
  244. package/registry/blocks/menubar/block.json +10 -0
  245. package/registry/blocks/menubar/demo.jsx +128 -0
  246. package/registry/blocks/menubar/index.js +4 -0
  247. package/registry/blocks/menubar/menubar.jsx +199 -0
  248. package/registry/blocks/mind-map/README.md +206 -0
  249. package/registry/blocks/mind-map/block.json +10 -0
  250. package/registry/blocks/mind-map/demo.jsx +103 -0
  251. package/registry/blocks/mind-map/index.js +31 -0
  252. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  253. package/registry/blocks/mind-map/topic.jsx +143 -0
  254. package/registry/blocks/mind-map/tree.js +645 -0
  255. package/registry/blocks/navigation-menu/README.md +124 -0
  256. package/registry/blocks/navigation-menu/block.json +10 -0
  257. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  258. package/registry/blocks/navigation-menu/index.js +4 -0
  259. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  260. package/registry/blocks/order-detail/README.md +109 -0
  261. package/registry/blocks/order-detail/block.json +10 -0
  262. package/registry/blocks/order-detail/demo.jsx +88 -0
  263. package/registry/blocks/order-detail/index.js +4 -0
  264. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  265. package/registry/blocks/org-chart/README.md +145 -0
  266. package/registry/blocks/org-chart/block.json +10 -0
  267. package/registry/blocks/org-chart/demo.jsx +94 -0
  268. package/registry/blocks/org-chart/index.js +7 -0
  269. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  270. package/registry/blocks/org-chart/org.js +245 -0
  271. package/registry/blocks/org-chart/person.jsx +96 -0
  272. package/registry/blocks/page-builder/README.md +257 -0
  273. package/registry/blocks/page-builder/block.json +14 -0
  274. package/registry/blocks/page-builder/demo.jsx +145 -0
  275. package/registry/blocks/page-builder/grapes.js +18 -0
  276. package/registry/blocks/page-builder/index.js +11 -0
  277. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  278. package/registry/blocks/page-builder/page-view.jsx +33 -0
  279. package/registry/blocks/page-builder/page.js +118 -0
  280. package/registry/blocks/page-builder/panels.jsx +588 -0
  281. package/registry/blocks/page-builder/sections.js +209 -0
  282. package/registry/blocks/page-builder/theme.js +87 -0
  283. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  284. package/registry/blocks/page-builder/tokens.js +329 -0
  285. package/registry/blocks/payment-methods/README.md +104 -0
  286. package/registry/blocks/payment-methods/block.json +10 -0
  287. package/registry/blocks/payment-methods/demo.jsx +71 -0
  288. package/registry/blocks/payment-methods/index.js +4 -0
  289. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  290. package/registry/blocks/pdf-document/README.md +213 -0
  291. package/registry/blocks/pdf-document/block.json +13 -0
  292. package/registry/blocks/pdf-document/demo.jsx +136 -0
  293. package/registry/blocks/pdf-document/index.js +33 -0
  294. package/registry/blocks/pdf-document/money.js +83 -0
  295. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  296. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  297. package/registry/blocks/pdf-document/quote.jsx +179 -0
  298. package/registry/blocks/pdf-document/theme.js +193 -0
  299. package/registry/blocks/pdf-viewer/README.md +191 -0
  300. package/registry/blocks/pdf-viewer/block.json +14 -0
  301. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  302. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  303. package/registry/blocks/pdf-viewer/index.js +8 -0
  304. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  305. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  306. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  307. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  308. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  309. package/registry/blocks/product-detail/README.md +217 -0
  310. package/registry/blocks/product-detail/block.json +10 -0
  311. package/registry/blocks/product-detail/demo.jsx +233 -0
  312. package/registry/blocks/product-detail/index.js +7 -0
  313. package/registry/blocks/product-detail/price.jsx +115 -0
  314. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  315. package/registry/blocks/product-detail/product-media.jsx +102 -0
  316. package/registry/blocks/product-detail/product-options.jsx +144 -0
  317. package/registry/blocks/product-grid/README.md +162 -0
  318. package/registry/blocks/product-grid/block.json +10 -0
  319. package/registry/blocks/product-grid/demo.jsx +255 -0
  320. package/registry/blocks/product-grid/index.js +5 -0
  321. package/registry/blocks/product-grid/product-card.jsx +232 -0
  322. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  323. package/registry/blocks/record-form/README.md +196 -0
  324. package/registry/blocks/record-form/block.json +10 -0
  325. package/registry/blocks/record-form/demo.jsx +525 -0
  326. package/registry/blocks/record-form/index.js +4 -0
  327. package/registry/blocks/record-form/record-fields.jsx +457 -0
  328. package/registry/blocks/record-form/record-form.jsx +415 -0
  329. package/registry/blocks/record-form/rules.js +81 -0
  330. package/registry/blocks/record-notes/README.md +139 -0
  331. package/registry/blocks/record-notes/block.json +10 -0
  332. package/registry/blocks/record-notes/demo.jsx +179 -0
  333. package/registry/blocks/record-notes/index.js +4 -0
  334. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  335. package/registry/blocks/search-dialog/README.md +108 -0
  336. package/registry/blocks/search-dialog/block.json +10 -0
  337. package/registry/blocks/search-dialog/demo.jsx +116 -0
  338. package/registry/blocks/search-dialog/index.js +3 -0
  339. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  340. package/registry/blocks/settings-list/README.md +171 -0
  341. package/registry/blocks/settings-list/block.json +10 -0
  342. package/registry/blocks/settings-list/demo.jsx +333 -0
  343. package/registry/blocks/settings-list/index.js +4 -0
  344. package/registry/blocks/settings-list/row-state.jsx +93 -0
  345. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  346. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  347. package/registry/blocks/setup-checklist/README.md +142 -0
  348. package/registry/blocks/setup-checklist/block.json +10 -0
  349. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  350. package/registry/blocks/setup-checklist/index.js +4 -0
  351. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  352. package/registry/blocks/share-dialog/README.md +113 -0
  353. package/registry/blocks/share-dialog/block.json +10 -0
  354. package/registry/blocks/share-dialog/demo.jsx +140 -0
  355. package/registry/blocks/share-dialog/index.js +3 -0
  356. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  357. package/registry/blocks/sidebar/README.md +190 -0
  358. package/registry/blocks/sidebar/block.json +43 -0
  359. package/registry/blocks/sidebar/demo.jsx +577 -0
  360. package/registry/blocks/sidebar/index.js +4 -0
  361. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  362. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  363. package/registry/blocks/signature-pad/README.md +137 -0
  364. package/registry/blocks/signature-pad/block.json +13 -0
  365. package/registry/blocks/signature-pad/colors.js +115 -0
  366. package/registry/blocks/signature-pad/demo.jsx +94 -0
  367. package/registry/blocks/signature-pad/index.js +6 -0
  368. package/registry/blocks/signature-pad/pad.js +7 -0
  369. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  370. package/registry/blocks/signature-pad/signature.js +68 -0
  371. package/registry/blocks/sketch-canvas/README.md +280 -0
  372. package/registry/blocks/sketch-canvas/block.json +13 -0
  373. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  374. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  375. package/registry/blocks/sketch-canvas/index.js +9 -0
  376. package/registry/blocks/sketch-canvas/palette.js +226 -0
  377. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  378. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  379. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  380. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  381. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  382. package/registry/blocks/sketch-canvas/theme.js +131 -0
  383. package/registry/blocks/spreadsheet/README.md +307 -0
  384. package/registry/blocks/spreadsheet/block.json +13 -0
  385. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  386. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  387. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  388. package/registry/blocks/spreadsheet/grid.js +387 -0
  389. package/registry/blocks/spreadsheet/index.js +34 -0
  390. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  391. package/registry/blocks/spreadsheet/types.js +473 -0
  392. package/registry/blocks/stat-cards/README.md +173 -0
  393. package/registry/blocks/stat-cards/block.json +10 -0
  394. package/registry/blocks/stat-cards/demo.jsx +257 -0
  395. package/registry/blocks/stat-cards/index.js +4 -0
  396. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  397. package/registry/blocks/stat-cards/usage.jsx +243 -0
  398. package/registry/blocks/task-list/README.md +176 -0
  399. package/registry/blocks/task-list/block.json +10 -0
  400. package/registry/blocks/task-list/demo.jsx +134 -0
  401. package/registry/blocks/task-list/index.js +5 -0
  402. package/registry/blocks/task-list/task-list.jsx +907 -0
  403. package/registry/blocks/task-model/README.md +455 -0
  404. package/registry/blocks/task-model/block.json +167 -0
  405. package/registry/blocks/task-model/changes.js +89 -0
  406. package/registry/blocks/task-model/dates.js +371 -0
  407. package/registry/blocks/task-model/demo.jsx +215 -0
  408. package/registry/blocks/task-model/dnd.jsx +240 -0
  409. package/registry/blocks/task-model/index.js +97 -0
  410. package/registry/blocks/task-model/model.js +919 -0
  411. package/registry/blocks/task-model/parts.jsx +449 -0
  412. package/registry/blocks/task-model/toolbar.jsx +271 -0
  413. package/registry/blocks/testimonials/README.md +139 -0
  414. package/registry/blocks/testimonials/block.json +11 -0
  415. package/registry/blocks/testimonials/demo.jsx +137 -0
  416. package/registry/blocks/testimonials/index.js +4 -0
  417. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  418. package/registry/blocks/transaction-list/README.md +126 -0
  419. package/registry/blocks/transaction-list/block.json +10 -0
  420. package/registry/blocks/transaction-list/demo.jsx +79 -0
  421. package/registry/blocks/transaction-list/index.js +4 -0
  422. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  423. package/registry/blocks/whiteboard/README.md +179 -0
  424. package/registry/blocks/whiteboard/block.json +10 -0
  425. package/registry/blocks/whiteboard/demo.jsx +169 -0
  426. package/registry/blocks/whiteboard/index.js +6 -0
  427. package/registry/blocks/whiteboard/templates.js +150 -0
  428. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  429. package/src/cli.mjs +19 -6
  430. package/src/install.mjs +18 -0
  431. package/src/registry.mjs +38 -4
  432. package/src/shadcn.mjs +14 -1
  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/ui/avatar.jsx +141 -0
  437. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  438. package/templates/next/files/components/ui/button-group.jsx +70 -0
  439. package/templates/next/files/components/ui/button.jsx +132 -12
  440. package/templates/next/files/components/ui/calendar.jsx +609 -0
  441. package/templates/next/files/components/ui/carousel.jsx +281 -0
  442. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  443. package/templates/next/files/components/ui/combobox.jsx +608 -0
  444. package/templates/next/files/components/ui/command.jsx +577 -0
  445. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  446. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  447. package/templates/next/files/components/ui/distribution.jsx +117 -0
  448. package/templates/next/files/components/ui/feedback.jsx +143 -0
  449. package/templates/next/files/components/ui/field.jsx +115 -3
  450. package/templates/next/files/components/ui/file-input.jsx +234 -0
  451. package/templates/next/files/components/ui/icon.jsx +130 -8
  452. package/templates/next/files/components/ui/image.jsx +143 -0
  453. package/templates/next/files/components/ui/index.js +47 -2
  454. package/templates/next/files/components/ui/input-group.jsx +213 -0
  455. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  456. package/templates/next/files/components/ui/item.jsx +206 -0
  457. package/templates/next/files/components/ui/menu.jsx +454 -82
  458. package/templates/next/files/components/ui/notice.jsx +126 -14
  459. package/templates/next/files/components/ui/pagination.jsx +156 -0
  460. package/templates/next/files/components/ui/popover.jsx +284 -0
  461. package/templates/next/files/components/ui/radio.jsx +193 -0
  462. package/templates/next/files/components/ui/rating.jsx +131 -0
  463. package/templates/next/files/components/ui/resizable.jsx +338 -0
  464. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  465. package/templates/next/files/components/ui/separator.jsx +119 -0
  466. package/templates/next/files/components/ui/sheet.jsx +96 -11
  467. package/templates/next/files/components/ui/slider.jsx +174 -0
  468. package/templates/next/files/components/ui/stat.jsx +574 -0
  469. package/templates/next/files/components/ui/steps.jsx +338 -0
  470. package/templates/next/files/components/ui/swatch.jsx +33 -0
  471. package/templates/next/files/components/ui/table.jsx +130 -0
  472. package/templates/next/files/components/ui/toast.jsx +440 -0
  473. package/templates/next/files/components/ui/toggle.jsx +195 -0
  474. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  475. package/templates/next/template.json +264 -8
@@ -0,0 +1,704 @@
1
+ "use client";
2
+
3
+ // PageBuilder — a visual page editor (GrapesJS core) in the workspace
4
+ // theme, behind one set of props.
5
+ //
6
+ // What is ours and what is GrapesJS's: GrapesJS keeps the page model, the
7
+ // canvas (an iframe), selection, drag and drop, text editing, undo and the
8
+ // HTML/CSS export. Everything around the canvas is ours, in the kit: the
9
+ // top bar (title, save state, device toggle, undo/redo, preview, Save),
10
+ // the Add / Layers panel, the Design panel (token choices only), the empty
11
+ // state with templates, and a bottom bar with sheets on a narrow screen.
12
+ // GrapesJS runs with <Canvas> from @grapesjs/react, so it creates none of
13
+ // its own panels; theme.js repaints the few things it still draws.
14
+ //
15
+ // Client only, one static bundle: the modules load with the page (they are
16
+ // safe on the server — see grapes.js); the editor is created in an effect
17
+ // after the mount guard. Until then — on the server and in the first client
18
+ // paint — the same frame shows with a skeleton.
19
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
20
+ import { Button, IconButton, Menu, Sheet, Skeleton, Tabs, TabPanel, ToggleGroup, Tooltip, cx, toast } from "../../ui";
21
+ import { BlocksProvider, Canvas, GjsEditor, grapesjs, useEditorMaybe } from "./grapes";
22
+ import { cleanCss, cleanHtml, normalizeBlock, templatePage } from "./page";
23
+ import { AddPanel, DesignPanel, LayersPanel, TemplateGrid, nameOf, topLevel, useLatest } from "./panels";
24
+ import { BASE_CSS, BASIC_BLOCKS, SECTION_BLOCKS, STARTER_TEMPLATES } from "./sections";
25
+ import { FRAME_CSS, GJS_THEME, GJS_VARS } from "./theme";
26
+ import { KEEP_TOKENS, STYLE_GROUPS, fontFaceCss, fontLinks, tokenCss } from "./tokens";
27
+
28
+ /** A ref for the builder's handle: `const page = usePageBuilder()`, then
29
+ * `<PageBuilder builderRef={page} />` and `page.current?.getPage()`. */
30
+ export function usePageBuilder() {
31
+ return useRef(null);
32
+ }
33
+
34
+ export const DEVICES = [
35
+ { id: "desktop", label: "Desktop", icon: "monitor", width: "" },
36
+ { id: "tablet", label: "Tablet", icon: "tablet", width: "768px", widthMedia: "992px" },
37
+ { id: "mobile", label: "Phone", icon: "smartphone", width: "390px", widthMedia: "480px" },
38
+ ];
39
+
40
+ /** The page as it goes out: { html, css, project }. */
41
+ export function readPage(editor) {
42
+ if (!editor) return { html: "", css: "", project: null };
43
+ return {
44
+ html: cleanHtml(editor.getHtml()),
45
+ css: cleanCss(editor.getCss({ avoidProtected: true }) ?? ""),
46
+ project: editor.getProjectData(),
47
+ };
48
+ }
49
+
50
+ function builtInBlocks(builtIns) {
51
+ if (builtIns === false || builtIns === "none") return [];
52
+ if (builtIns === "basics") return BASIC_BLOCKS;
53
+ if (builtIns === "sections") return SECTION_BLOCKS;
54
+ return [...SECTION_BLOCKS, ...BASIC_BLOCKS];
55
+ }
56
+
57
+ export function PageBuilder(props) {
58
+ const [mounted, setMounted] = useState(false);
59
+ useEffect(() => setMounted(true), []);
60
+ return mounted ? <Builder {...props} /> : <Placeholder {...props} />;
61
+ }
62
+
63
+ const ROOT = cx(
64
+ "@container relative isolate flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-background text-foreground",
65
+ KEEP_TOKENS,
66
+ );
67
+
68
+ function Placeholder({ title = "Untitled page", height = 720, className }) {
69
+ return (
70
+ <div className={cx(ROOT, className)} style={{ height }} aria-busy="true">
71
+ <div className="flex h-14 shrink-0 items-center gap-3 border-b border-border px-4">
72
+ <span className="truncate text-sm font-medium">{title}</span>
73
+ <span className="ml-auto">
74
+ <Skeleton className="h-9 w-20" />
75
+ </span>
76
+ </div>
77
+ <div className="flex min-h-0 flex-1">
78
+ <div className="hidden w-72 shrink-0 border-r border-border p-4 @5xl:block">
79
+ <Skeleton className="mb-4 h-9 w-full" />
80
+ <div className="grid grid-cols-2 gap-2">
81
+ {[0, 1, 2, 3, 4, 5].map((i) => (
82
+ <Skeleton key={i} shape="block" className="aspect-[16/10] w-full" />
83
+ ))}
84
+ </div>
85
+ </div>
86
+ <div className="grid min-w-0 flex-1 place-items-center bg-muted p-6">
87
+ <span className="sr-only">Loading the page editor…</span>
88
+ <div className="grid w-full max-w-xl gap-3" aria-hidden="true">
89
+ <Skeleton className="mx-auto h-4 w-40" />
90
+ <Skeleton className="mx-auto h-8 w-4/5" />
91
+ <Skeleton className="mx-auto h-4 w-3/5" />
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ );
97
+ }
98
+
99
+ function Builder(props) {
100
+ const { page, height = 720, className } = props;
101
+ const rootRef = useRef(null);
102
+ // Read once: GrapesJS takes its options when the editor is created. To
103
+ // open another page, change the component's `key` (or use the handle).
104
+ const [options] = useState(() => {
105
+ const opts = {
106
+ height: "100%",
107
+ width: "100%",
108
+ storageManager: false,
109
+ undoManager: { trackSelection: false },
110
+ telemetry: false,
111
+ noticeOnUnload: false,
112
+ cssIcons: "",
113
+ jsInHtml: false,
114
+ avoidInlineStyle: true,
115
+ selectorManager: { componentFirst: true },
116
+ richTextEditor: { actions: ["bold", "italic", "link"] },
117
+ deviceManager: {
118
+ default: "desktop",
119
+ devices: DEVICES.map((d) => ({ id: d.id, name: d.label, width: d.width, widthMedia: d.widthMedia ?? "" })),
120
+ },
121
+ canvas: { frameStyle: "", styles: fontLinks() },
122
+ protectedCss: "* { box-sizing: border-box; } body { margin: 0; }",
123
+ parser: { optionsHtml: { allowScripts: false, allowUnsafeAttr: false, allowUnsafeAttrValue: false } },
124
+ };
125
+ if (page?.project) opts.projectData = page.project;
126
+ else {
127
+ opts.components = cleanHtml(page?.html ?? "");
128
+ opts.style = cleanCss(page?.css ?? "");
129
+ }
130
+ return opts;
131
+ });
132
+
133
+ return (
134
+ <div ref={rootRef} className={cx(ROOT, GJS_VARS, GJS_THEME, className)} style={{ height }}>
135
+ <GjsEditor grapesjs={grapesjs} options={options} className="flex min-h-0 flex-1 flex-col">
136
+ <Shell {...props} rootRef={rootRef} />
137
+ </GjsEditor>
138
+ </div>
139
+ );
140
+ }
141
+
142
+ function Shell({
143
+ rootRef,
144
+ title = "Untitled page",
145
+ onSave,
146
+ onChange,
147
+ blocks: extraBlocks,
148
+ builtIns = true,
149
+ templates = STARTER_TEMPLATES,
150
+ styles = STYLE_GROUPS,
151
+ actions,
152
+ saveLabel = "Save",
153
+ onUploadImage,
154
+ builderRef,
155
+ }) {
156
+ const editor = useEditorMaybe();
157
+ const [ready, setReady] = useState(false);
158
+ const [rev, setRev] = useState(0);
159
+ const [selected, setSelected] = useState(null);
160
+ const [device, setDevice] = useState("desktop");
161
+ const [preview, setPreview] = useState(false);
162
+ const [dragging, setDragging] = useState(false);
163
+ const [empty, setEmpty] = useState(false);
164
+ const [startBlank, setStartBlank] = useState(false);
165
+ const [dirty, setDirty] = useState(false);
166
+ const [status, setStatus] = useState("idle"); // idle · saving · saved · error
167
+ const [history, setHistory] = useState({ undo: false, redo: false });
168
+ const [tab, setTab] = useState("add");
169
+ const [sheet, setSheet] = useState(null); // add · layers · design · templates
170
+ const saving = useRef(false);
171
+ const outline = useRef(false);
172
+
173
+ const blocks = useMemo(
174
+ () => [...builtInBlocks(builtIns), ...(extraBlocks ?? [])].map((b, i) => ({ ...normalizeBlock(b, i), level: b.level ?? (b.category === "Sections" || b.id === "section" ? "section" : "element") })),
175
+ [builtIns, extraBlocks],
176
+ );
177
+ const blockCss = useMemo(() => [BASE_CSS, ...blocks.map((b) => b.css).filter(Boolean)].join("\n"), [blocks]);
178
+ const onChangeRef = useLatest(onChange);
179
+ const onSaveRef = useLatest(onSave);
180
+
181
+ // ---- the editor's life -----------------------------------------------------
182
+
183
+ // Register (and re-register) the blocks with GrapesJS; their CSS joins
184
+ // the page's rules (only rules a page uses are exported).
185
+ useEffect(() => {
186
+ if (!editor) return;
187
+ const bm = editor.Blocks;
188
+ bm.getAll().reset([]);
189
+ for (const b of blocks) {
190
+ bm.add(b.id, { label: b.label, category: b.category, content: b.content ?? b.html, select: true, activate: false });
191
+ }
192
+ if (ready) editor.Css.addRules(blockCss);
193
+ }, [editor, blocks, blockCss, ready]);
194
+
195
+ useEffect(() => {
196
+ if (!editor) return;
197
+ const bump = () => setRev((r) => r + 1);
198
+ const sync = () => {
199
+ const um = editor.UndoManager;
200
+ setHistory({ undo: um.hasUndo(), redo: um.hasRedo() });
201
+ setDirty(editor.getDirtyCount() > 0);
202
+ setEmpty(editor.getWrapper()?.components().length === 0);
203
+ };
204
+ const onSelect = () => {
205
+ setSelected(editor.getSelected() ?? null);
206
+ bump();
207
+ };
208
+ let timer = null;
209
+ const onUpdate = () => {
210
+ bump();
211
+ sync();
212
+ if (!onChangeRef.current) return;
213
+ clearTimeout(timer);
214
+ timer = setTimeout(() => onChangeRef.current?.(readPage(editor)), 500);
215
+ };
216
+ // Picture double-click opens GrapesJS's asset modal by default; ours is
217
+ // the Design panel's picture field.
218
+ const openAssets = {
219
+ run: () => {
220
+ const c = editor.getSelected();
221
+ if (rootRef.current && rootRef.current.getBoundingClientRect().width < 1024) setSheet("design");
222
+ // The Design panel may be drawn twice (the side panel, hidden on a
223
+ // narrow screen, and the sheet): focus the one that shows.
224
+ setTimeout(() => {
225
+ const fields = rootRef.current?.ownerDocument.querySelectorAll(`[id="pb-src-${c?.cid}"]`) ?? [];
226
+ [...fields].find((el) => el.offsetParent !== null)?.focus();
227
+ }, 80);
228
+ },
229
+ };
230
+ editor.Commands.add("core:open-assets", openAssets);
231
+ editor.Commands.add("open-assets", openAssets);
232
+
233
+ const onLoad = () => {
234
+ editor.Css.addRules(blockCss);
235
+ const wrapper = editor.getWrapper();
236
+ wrapper?.set({ selectable: false, hoverable: false, highlightable: false, name: "Page" });
237
+ editor.runCommand("core:component-outline");
238
+ outline.current = true;
239
+ editor.UndoManager.clear();
240
+ editor.clearDirtyCount();
241
+ sync();
242
+ setReady(true);
243
+ };
244
+ editor.on("component:toggled", onSelect);
245
+ // "update" is GrapesJS's "the page changed" (it counts toward the dirty
246
+ // count); hover and selection changes do not fire it.
247
+ editor.on("update undo redo", onUpdate);
248
+ editor.on("component:styleUpdate component:add component:remove layer:component", bump);
249
+ editor.onReady(onLoad);
250
+ return () => {
251
+ clearTimeout(timer);
252
+ editor.off("component:toggled", onSelect);
253
+ editor.off("update undo redo", onUpdate);
254
+ editor.off("component:styleUpdate component:add component:remove layer:component", bump);
255
+ };
256
+ // eslint-disable-next-line react-hooks/exhaustive-deps
257
+ }, [editor]);
258
+
259
+ // Paint the frame in the theme: the resolved tokens, the app's fonts and
260
+ // the frame's base. Again whenever the light/dark scheme or theme changes.
261
+ useEffect(() => {
262
+ if (!editor || !ready) return;
263
+ let fonts = null;
264
+ let raf = 0;
265
+ const paint = () => {
266
+ const doc = editor.Canvas.getDocument();
267
+ if (!doc?.head) return;
268
+ fonts ??= fontFaceCss();
269
+ let el = doc.getElementById("pb-theme");
270
+ if (!el) {
271
+ el = doc.createElement("style");
272
+ el.id = "pb-theme";
273
+ doc.head.appendChild(el);
274
+ }
275
+ el.textContent = `${fonts}\n${tokenCss(rootRef.current)}\n${FRAME_CSS}`;
276
+ // A link in the page never navigates the editor's frame.
277
+ if (!doc.__pbLinks) {
278
+ doc.__pbLinks = true;
279
+ doc.addEventListener("click", (e) => e.target?.closest?.("a[href]") && e.preventDefault(), true);
280
+ }
281
+ };
282
+ const later = () => {
283
+ cancelAnimationFrame(raf);
284
+ raf = requestAnimationFrame(paint);
285
+ };
286
+ paint();
287
+ editor.on("canvas:frame:load:body", later);
288
+ const watch = new MutationObserver(later);
289
+ const opts = { attributes: true, attributeFilter: ["class", "style", "data-theme", "data-scheme"] };
290
+ watch.observe(document.documentElement, opts);
291
+ if (document.body) watch.observe(document.body, opts);
292
+ const scoped = rootRef.current?.closest?.("[data-theme]");
293
+ if (scoped && scoped !== document.documentElement) watch.observe(scoped, opts);
294
+ const mq = typeof matchMedia !== "undefined" ? matchMedia("(prefers-color-scheme: dark)") : null;
295
+ mq?.addEventListener?.("change", later);
296
+ return () => {
297
+ cancelAnimationFrame(raf);
298
+ editor.off("canvas:frame:load:body", later);
299
+ watch.disconnect();
300
+ mq?.removeEventListener?.("change", later);
301
+ };
302
+ }, [editor, ready, rootRef]);
303
+
304
+ // The canvas follows its box (panels folding, preview, the window).
305
+ useEffect(() => {
306
+ if (!editor || !ready) return;
307
+ const box = editor.Canvas.getElement()?.parentElement;
308
+ if (!box || typeof ResizeObserver === "undefined") return;
309
+ let raf = 0;
310
+ const ro = new ResizeObserver(() => {
311
+ cancelAnimationFrame(raf);
312
+ raf = requestAnimationFrame(() => editor.refresh());
313
+ });
314
+ ro.observe(box);
315
+ return () => {
316
+ cancelAnimationFrame(raf);
317
+ ro.disconnect();
318
+ };
319
+ }, [editor, ready]);
320
+
321
+ // ---- actions ---------------------------------------------------------------------
322
+
323
+ const select = useCallback(
324
+ (c) => {
325
+ if (!editor || !c) return;
326
+ editor.select(c);
327
+ editor.Canvas.scrollTo(c, { behavior: "smooth", block: "center" });
328
+ },
329
+ [editor],
330
+ );
331
+
332
+ const addBlock = useCallback(
333
+ (block) => {
334
+ if (!editor) return;
335
+ const content = block.content ?? block.html;
336
+ const wrapper = editor.getWrapper();
337
+ const sel = editor.getSelected();
338
+ let target = wrapper;
339
+ let at;
340
+ if (sel && sel !== wrapper) {
341
+ if (block.level === "section") {
342
+ at = topLevel(editor, sel).index() + 1;
343
+ } else if (!sel.is("text") && !sel.is("link") && !sel.is("image") && sel.get("droppable") !== false) {
344
+ target = sel;
345
+ } else {
346
+ target = sel.parent() ?? wrapper;
347
+ at = sel.index() + 1;
348
+ }
349
+ }
350
+ const added = target.append(content, at === undefined ? {} : { at });
351
+ setStartBlank(true);
352
+ setSheet(null);
353
+ if (added?.[0]) setTimeout(() => select(added[0]), 30);
354
+ },
355
+ [editor, select],
356
+ );
357
+
358
+ const applyTemplate = useCallback(
359
+ (template, { quiet = false } = {}) => {
360
+ if (!editor) return;
361
+ const before = editor.getProjectData();
362
+ const wasEmpty = editor.getWrapper()?.components().length === 0;
363
+ const next = templatePage(template);
364
+ if (next?.project) editor.loadProjectData(next.project);
365
+ else {
366
+ editor.setComponents(cleanHtml(next?.html ?? ""));
367
+ if (next?.css) editor.Css.addRules(cleanCss(next.css));
368
+ }
369
+ editor.Css.addRules(blockCss);
370
+ editor.UndoManager.clear();
371
+ editor.select();
372
+ setSheet(null);
373
+ setStartBlank(true);
374
+ setRev((r) => r + 1);
375
+ setEmpty(editor.getWrapper()?.components().length === 0);
376
+ if (!wasEmpty && !quiet) {
377
+ toast(`Started from “${template.label}”`, {
378
+ description: "Your previous page was replaced.",
379
+ action: {
380
+ label: "Undo",
381
+ onClick: () => {
382
+ editor.loadProjectData(before);
383
+ editor.Css.addRules(blockCss);
384
+ setRev((r) => r + 1);
385
+ },
386
+ },
387
+ });
388
+ }
389
+ },
390
+ [editor, blockCss],
391
+ );
392
+
393
+ const act = useCallback(
394
+ (what) => {
395
+ const c = editor?.getSelected();
396
+ if (!c) return;
397
+ const parent = c.parent();
398
+ const i = c.index();
399
+ if (what === "parent" && parent && parent !== editor.getWrapper()) select(parent);
400
+ else if ((what === "up" || what === "down") && parent) {
401
+ if (what === "up" ? i <= 0 : i >= parent.components().length - 1) return;
402
+ // move() takes the slot to insert before, counted before the move.
403
+ c.move(parent, { at: what === "up" ? i - 1 : i + 2 });
404
+ select(c);
405
+ } else if (what === "duplicate" && parent) {
406
+ const copy = c.clone();
407
+ parent.append(copy, { at: i + 1 });
408
+ select(copy);
409
+ } else if (what === "delete") {
410
+ const name = nameOf(editor, c);
411
+ c.remove();
412
+ editor.select();
413
+ setSheet((s) => (s === "design" ? null : s));
414
+ toast(`${name} deleted`, { action: { label: "Undo", onClick: () => editor.UndoManager.undo() } });
415
+ }
416
+ },
417
+ [editor, select],
418
+ );
419
+
420
+ const save = useCallback(async () => {
421
+ if (!editor || saving.current) return { ok: false };
422
+ saving.current = true;
423
+ setStatus("saving");
424
+ const snapshot = editor.getDirtyCount();
425
+ let res;
426
+ try {
427
+ res = onSaveRef.current ? await onSaveRef.current(readPage(editor)) : { ok: true };
428
+ } catch (e) {
429
+ res = { ok: false, error: e?.message };
430
+ }
431
+ saving.current = false;
432
+ if (res?.ok) {
433
+ // Edits made while saving stay unsaved.
434
+ if (editor.getDirtyCount() === snapshot) editor.clearDirtyCount();
435
+ setDirty(editor.getDirtyCount() > 0);
436
+ setStatus("saved");
437
+ } else {
438
+ setStatus("error");
439
+ toast.error("The page was not saved", {
440
+ description: res?.error ?? "Your changes are still here. Try again in a moment.",
441
+ action: { label: "Try again", onClick: () => saveRef.current?.() },
442
+ });
443
+ }
444
+ return res ?? { ok: false };
445
+ // eslint-disable-next-line react-hooks/exhaustive-deps
446
+ }, [editor]);
447
+ const saveRef = useLatest(save);
448
+
449
+ const pickDevice = useCallback(
450
+ (id) => {
451
+ if (!id || !editor) return;
452
+ editor.Devices.select(id);
453
+ setDevice(id);
454
+ },
455
+ [editor],
456
+ );
457
+
458
+ const togglePreview = useCallback(
459
+ (on) => {
460
+ if (!editor) return;
461
+ if (on) {
462
+ editor.select();
463
+ if (editor.Commands.isActive("core:component-outline")) editor.stopCommand("core:component-outline");
464
+ editor.getModel().stopDefault();
465
+ } else {
466
+ editor.getModel().runDefault();
467
+ if (outline.current) editor.runCommand("core:component-outline");
468
+ }
469
+ setSheet(null);
470
+ setPreview(on);
471
+ },
472
+ [editor],
473
+ );
474
+
475
+ // ⌘/Ctrl+S saves; Esc leaves preview. Undo, redo and delete are
476
+ // GrapesJS's own keymaps (⌘Z, ⇧⌘Z, Backspace), live in the frame too.
477
+ useEffect(() => {
478
+ if (!editor) return;
479
+ const onKey = (e) => {
480
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "s") {
481
+ e.preventDefault();
482
+ saveRef.current?.();
483
+ } else if (e.key === "Escape" && preview) togglePreview(false);
484
+ };
485
+ const frameWin = () => editor.Canvas.getWindow();
486
+ document.addEventListener("keydown", onKey);
487
+ let fw = null;
488
+ const attach = () => {
489
+ fw?.removeEventListener("keydown", onKey);
490
+ fw = frameWin();
491
+ fw?.addEventListener("keydown", onKey);
492
+ };
493
+ if (ready) attach();
494
+ return () => {
495
+ document.removeEventListener("keydown", onKey);
496
+ fw?.removeEventListener("keydown", onKey);
497
+ };
498
+ }, [editor, ready, preview, togglePreview, saveRef]);
499
+
500
+ // The handle.
501
+ useEffect(() => {
502
+ if (!builderRef) return;
503
+ builderRef.current = editor
504
+ ? {
505
+ editor,
506
+ getPage: () => readPage(editor),
507
+ save: () => saveRef.current?.(),
508
+ addBlock: (id) => {
509
+ const b = blocks.find((x) => x.id === id);
510
+ if (b) addBlock(b);
511
+ return Boolean(b);
512
+ },
513
+ loadTemplate: (t) => {
514
+ const tpl = typeof t === "string" ? templates.find((x) => x.id === t) : t;
515
+ if (tpl) applyTemplate(tpl);
516
+ return Boolean(tpl);
517
+ },
518
+ setPage: (p) => applyTemplate(p ?? {}, { quiet: true }),
519
+ undo: () => editor.UndoManager.undo(),
520
+ redo: () => editor.UndoManager.redo(),
521
+ setDevice: pickDevice,
522
+ preview: (on = true) => togglePreview(on),
523
+ }
524
+ : null;
525
+ }, [builderRef, editor, blocks, templates, addBlock, applyTemplate, pickDevice, togglePreview, saveRef]);
526
+
527
+ // ---- render ------------------------------------------------------------------------
528
+
529
+ const showEmpty = ready && empty && !startBlank && !dragging && !preview;
530
+ const statusText =
531
+ status === "saving" ? "Saving…" : status === "error" ? "Not saved" : dirty ? "Unsaved changes" : status === "saved" ? "All changes saved" : "No changes yet";
532
+
533
+ const addPanel = (
534
+ <AddPanel editor={editor} blocks={blocks} templates={templates} onAdd={addBlock} onDragging={setDragging} onTemplates={() => setSheet("templates")} />
535
+ );
536
+ const layersPanel = <LayersPanel editor={editor} selected={selected} rev={rev} onSelect={select} />;
537
+ const designPanel = <DesignPanel editor={editor} selected={selected} rev={rev} groups={styles} onUploadImage={onUploadImage} onAction={act} />;
538
+
539
+ const devices = (
540
+ <ToggleGroup
541
+ aria-label="Preview width"
542
+ size="sm"
543
+ value={device}
544
+ onChange={pickDevice}
545
+ items={DEVICES.map((d) => ({ value: d.id, label: d.label, icon: d.icon, iconOnly: true }))}
546
+ />
547
+ );
548
+
549
+ return (
550
+ <>
551
+ {/* top bar */}
552
+ <header className="flex h-14 shrink-0 items-center gap-2 border-b border-border px-3 @3xl:px-4">
553
+ <div className="min-w-0 flex-1">
554
+ <p className="truncate text-sm font-medium leading-tight">{title}</p>
555
+ <p className={cx("truncate text-xs leading-tight", status === "error" ? "text-destructive" : "text-muted-foreground")} aria-live="polite">
556
+ {statusText}
557
+ </p>
558
+ </div>
559
+ <div className="hidden @3xl:block">{devices}</div>
560
+ <div className="flex items-center gap-0.5">
561
+ <Tooltip label="Undo (⌘Z)">
562
+ <IconButton icon="undo" label="Undo" size="sm" disabled={!history.undo || preview} onClick={() => editor?.UndoManager.undo()} />
563
+ </Tooltip>
564
+ <Tooltip label="Redo (⇧⌘Z)">
565
+ <IconButton icon="redo" label="Redo" size="sm" disabled={!history.redo || preview} onClick={() => editor?.UndoManager.redo()} />
566
+ </Tooltip>
567
+ </div>
568
+ <span className="hidden @xl:block">
569
+ <Button variant={preview ? "primary" : "secondary"} size="sm" icon={preview ? "eye-off" : "eye"} onClick={() => togglePreview(!preview)} aria-pressed={preview}>
570
+ {preview ? "Exit preview" : "Preview"}
571
+ </Button>
572
+ </span>
573
+ <span className="@3xl:hidden">
574
+ <Menu
575
+ label="More"
576
+ items={[
577
+ { type: "radio", label: "Width", value: device, onValueChange: pickDevice, options: DEVICES.map((d) => ({ value: d.id, label: d.label })) },
578
+ { type: "separator" },
579
+ { label: preview ? "Exit preview" : "Preview", icon: "eye", onSelect: () => togglePreview(!preview) },
580
+ templates?.length ? { label: "Start from a template", icon: "file", onSelect: () => setSheet("templates") } : null,
581
+ ]}
582
+ />
583
+ </span>
584
+ {actions}
585
+ <Button variant="primary" size="sm" loading={status === "saving"} disabled={!ready || (!dirty && status !== "error")} onClick={save}>
586
+ {saveLabel}
587
+ </Button>
588
+ </header>
589
+
590
+ <div className="relative flex min-h-0 flex-1">
591
+ {/* left: add + layers */}
592
+ {!preview ? (
593
+ <aside aria-label="Blocks and layers" className="hidden w-72 shrink-0 flex-col border-r border-border bg-background @5xl:flex">
594
+ <Tabs
595
+ aria-label="Panel"
596
+ tabs={[
597
+ { key: "add", label: "Add" },
598
+ { key: "layers", label: "Layers" },
599
+ ]}
600
+ value={tab}
601
+ onChange={setTab}
602
+ className="shrink-0 px-2"
603
+ />
604
+ <TabPanel tab="add" value={tab} className="flex min-h-0 flex-1 flex-col">
605
+ {addPanel}
606
+ </TabPanel>
607
+ <TabPanel tab="layers" value={tab} className="min-h-0 flex-1 overflow-y-auto">
608
+ {layersPanel}
609
+ </TabPanel>
610
+ </aside>
611
+ ) : null}
612
+
613
+ {/* the canvas */}
614
+ <div className="relative min-w-0 flex-1 bg-muted">
615
+ <Canvas className="absolute inset-0" />
616
+ {!ready ? (
617
+ <div className="absolute inset-0 grid place-items-center bg-muted p-6" aria-hidden="true">
618
+ <div className="grid w-full max-w-xl gap-3">
619
+ <Skeleton className="mx-auto h-4 w-40" />
620
+ <Skeleton className="mx-auto h-8 w-4/5" />
621
+ <Skeleton className="mx-auto h-4 w-3/5" />
622
+ </div>
623
+ </div>
624
+ ) : null}
625
+ {showEmpty ? (
626
+ <div className="pointer-events-none absolute inset-0 grid place-items-center overflow-y-auto p-4">
627
+ <div className="pointer-events-auto grid w-full max-w-2xl gap-5 rounded-xl border border-border bg-popover p-5 text-popover-foreground shadow-sm @3xl:p-6">
628
+ <div className="grid gap-1">
629
+ <p className="text-lg font-semibold">Start your page</p>
630
+ <p className="text-sm text-muted-foreground">
631
+ {templates?.length ? "Pick a template to begin with, or start blank and add sections one at a time." : "Add a section from the Add panel, or drag one onto the page."}
632
+ </p>
633
+ </div>
634
+ {templates?.length ? <TemplateGrid templates={templates} onPick={applyTemplate} compact /> : null}
635
+ <div className="flex flex-wrap items-center gap-2">
636
+ <Button
637
+ variant={templates?.length ? "secondary" : "primary"}
638
+ size="sm"
639
+ icon="plus"
640
+ onClick={() => {
641
+ setStartBlank(true);
642
+ setTab("add");
643
+ if (rootRef.current && rootRef.current.getBoundingClientRect().width < 1024) setSheet("add");
644
+ }}
645
+ >
646
+ {templates?.length ? "Start blank" : "Add a section"}
647
+ </Button>
648
+ </div>
649
+ </div>
650
+ </div>
651
+ ) : null}
652
+ {preview ? (
653
+ <div className="pointer-events-none absolute inset-x-0 bottom-4 flex justify-center">
654
+ <Button variant="primary" size="sm" icon="eye-off" onClick={() => togglePreview(false)} className="pointer-events-auto shadow-md">
655
+ Exit preview · Esc
656
+ </Button>
657
+ </div>
658
+ ) : null}
659
+ </div>
660
+
661
+ {/* right: design */}
662
+ {!preview ? (
663
+ <aside aria-label="Design" className="hidden w-72 shrink-0 flex-col border-l border-border bg-background @5xl:flex">
664
+ <div className="flex h-11 shrink-0 items-center border-b border-border px-4">
665
+ <h2 className="text-sm font-medium">Design</h2>
666
+ </div>
667
+ <div className="min-h-0 flex-1 overflow-y-auto pt-4">{designPanel}</div>
668
+ </aside>
669
+ ) : null}
670
+ </div>
671
+
672
+ {/* narrow: a bottom bar and sheets */}
673
+ {!preview ? (
674
+ <nav aria-label="Editor panels" className="grid shrink-0 grid-cols-3 border-t border-border bg-background @5xl:hidden">
675
+ <Button variant="quiet" icon="plus" onClick={() => setSheet("add")}>
676
+ Add
677
+ </Button>
678
+ <Button variant="quiet" icon="layers" onClick={() => setSheet("layers")}>
679
+ Layers
680
+ </Button>
681
+ <Button variant="quiet" icon="palette" onClick={() => setSheet("design")} disabled={!ready}>
682
+ <span className="truncate">{selected ? `Design · ${nameOf(editor, selected)}` : "Design"}</span>
683
+ </Button>
684
+ </nav>
685
+ ) : null}
686
+
687
+ <Sheet open={sheet === "add"} onClose={() => setSheet(null)} title="Add a block" description="Tap a block to add it below the selection.">
688
+ <div className="-mx-4 -my-4 flex min-h-[60vh] flex-col pt-3">{sheet === "add" ? addPanel : null}</div>
689
+ </Sheet>
690
+ <Sheet open={sheet === "layers"} onClose={() => setSheet(null)} title="Layers">
691
+ <div className="-mx-2">{sheet === "layers" ? layersPanel : null}</div>
692
+ </Sheet>
693
+ <Sheet open={sheet === "design"} onClose={() => setSheet(null)} title="Design">
694
+ <div className="-mx-4">{sheet === "design" ? designPanel : null}</div>
695
+ </Sheet>
696
+ <Sheet open={sheet === "templates"} onClose={() => setSheet(null)} side="center" title="Start from a template" description="A template replaces the page. You can undo it right after.">
697
+ {sheet === "templates" ? <TemplateGrid templates={templates ?? []} onPick={applyTemplate} /> : null}
698
+ </Sheet>
699
+
700
+ {/* GrapesJS needs the blocks flagged custom before it is created. */}
701
+ <BlocksProvider>{() => <></>}</BlocksProvider>
702
+ </>
703
+ );
704
+ }