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,864 @@
1
+ "use client";
2
+
3
+ import { createContext, isValidElement, useContext, useEffect, useId, useState } from "react";
4
+ import {
5
+ Avatar,
6
+ Badge,
7
+ Button,
8
+ Collapsible,
9
+ CommandDialog,
10
+ Icon,
11
+ IconButton,
12
+ KbdGroup,
13
+ Menu,
14
+ ScrollArea,
15
+ Separator,
16
+ Sheet,
17
+ StatusDot,
18
+ Tooltip,
19
+ cx,
20
+ } from "../../ui";
21
+ import { PanelBody, guard, run } from "./sidebar-panel";
22
+
23
+ // Sidebar — an app's navigation down the left edge: the app's name (and a
24
+ // workspace switcher when there is more than one), groups of items with
25
+ // icons, counts and nested items, a custom slot, and the signed-in person
26
+ // at the foot with their menu. The block owns what a hand-drawn sidebar
27
+ // gets wrong: the active item said to a screen reader (aria-current), a
28
+ // collapse to icons that keeps every label (as a tooltip) and is
29
+ // remembered, and a phone layout where the sidebar is a Sheet behind a
30
+ // trigger instead of a column squeezing the page. The app owns the items
31
+ // (data in) and what choosing one does (href or onSelect).
32
+ //
33
+ // <SidebarLayout storageKey="crm.sidebar" sidebar={<Sidebar title="Harbor CRM" groups={NAV} user={me} />}>
34
+ // <header><SidebarTrigger /> …</header>
35
+ // …the page…
36
+ // </SidebarLayout>
37
+ //
38
+ // Beyond the column: inset and floating looks, a ⌘K search over every
39
+ // item, folding groups, a second column (the app's panel, or a parent's
40
+ // children to drill into), a primary button, and a "…" menu per item.
41
+ //
42
+ // README.md beside this file lists every prop.
43
+
44
+ const WIDE = "(min-width: 768px)";
45
+
46
+ const SidebarContext = createContext(null);
47
+
48
+ /** The layout's state, for a control of your own: { collapsed,
49
+ * setCollapsed, toggle, mobileOpen, setMobileOpen }. Null outside a
50
+ * SidebarLayout. */
51
+ export function useSidebar() {
52
+ return useContext(SidebarContext);
53
+ }
54
+
55
+ function readStored(key) {
56
+ try {
57
+ const v = window.localStorage.getItem(key);
58
+ return v === "1" ? true : v === "0" ? false : null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function writeStored(key, value) {
65
+ try {
66
+ window.localStorage.setItem(key, value ? "1" : "0");
67
+ } catch {
68
+ // private window, blocked storage: the choice lasts this visit only
69
+ }
70
+ }
71
+
72
+ export function SidebarLayout({
73
+ sidebar,
74
+ children,
75
+ collapsed,
76
+ defaultCollapsed = false,
77
+ onCollapsedChange,
78
+ storageKey,
79
+ shortcut = "b",
80
+ variant,
81
+ className,
82
+ }) {
83
+ // The look is the Sidebar's `variant` unless the layout is given one:
84
+ // inset needs the page drawn as a card, so the frame has to know.
85
+ const look = variant ?? (isValidElement(sidebar) ? sidebar.props?.variant : undefined);
86
+ const controlled = collapsed !== undefined;
87
+ const [own, setOwn] = useState(defaultCollapsed);
88
+ const [mobileOpen, setMobileOpen] = useState(false);
89
+ const isCollapsed = controlled ? collapsed : own;
90
+
91
+ // Stored after mount: the server has no storage, so the first paint is
92
+ // the default and the remembered width follows at once.
93
+ useEffect(() => {
94
+ if (!storageKey || controlled) return;
95
+ const stored = readStored(storageKey);
96
+ if (stored !== null) setOwn(stored);
97
+ }, [storageKey, controlled]);
98
+
99
+ // Widening past the phone layout closes the sheet: the column is back.
100
+ useEffect(() => {
101
+ const mq = window.matchMedia(WIDE);
102
+ const onChange = () => mq.matches && setMobileOpen(false);
103
+ mq.addEventListener("change", onChange);
104
+ return () => mq.removeEventListener("change", onChange);
105
+ }, []);
106
+
107
+ function setCollapsed(next) {
108
+ const value = typeof next === "function" ? next(isCollapsed) : Boolean(next);
109
+ if (!controlled) setOwn(value);
110
+ if (storageKey) writeStored(storageKey, value);
111
+ onCollapsedChange?.(value);
112
+ }
113
+
114
+ function toggle() {
115
+ if (window.matchMedia(WIDE).matches) setCollapsed(!isCollapsed);
116
+ else setMobileOpen((o) => !o);
117
+ }
118
+
119
+ // Cmd/Ctrl + B, as in most editors — never while typing in a field.
120
+ useEffect(() => {
121
+ if (!shortcut) return undefined;
122
+ const onKey = (e) => {
123
+ if (!(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey || e.key.toLowerCase() !== shortcut) return;
124
+ if (e.target.closest?.("input, textarea, select, [contenteditable='true']")) return;
125
+ e.preventDefault();
126
+ toggle();
127
+ };
128
+ window.addEventListener("keydown", onKey);
129
+ return () => window.removeEventListener("keydown", onKey);
130
+ });
131
+
132
+ const value = { collapsed: isCollapsed, setCollapsed, toggle, mobileOpen, setMobileOpen };
133
+ return (
134
+ <SidebarContext.Provider value={value}>
135
+ <div className={cx("flex w-full text-foreground", look === "inset" ? "bg-background md:bg-muted/40" : "bg-background", className)}>
136
+ {sidebar}
137
+ <div
138
+ className={cx(
139
+ "flex min-w-0 flex-1 flex-col",
140
+ look === "inset" && "bg-background md:my-2 md:mr-2 md:rounded-xl md:border md:border-border md:shadow-sm",
141
+ )}
142
+ >
143
+ {children}
144
+ </div>
145
+ </div>
146
+ </SidebarContext.Provider>
147
+ );
148
+ }
149
+
150
+ /** The one button that opens the sidebar on a phone and collapses it to
151
+ * icons on a wider window. Put it in the page's header. */
152
+ export function SidebarTrigger({ className }) {
153
+ const ctx = useSidebar();
154
+ if (!ctx) return null;
155
+ return (
156
+ <>
157
+ <IconButton
158
+ icon="menu"
159
+ label="Open navigation"
160
+ aria-expanded={ctx.mobileOpen}
161
+ onClick={() => ctx.setMobileOpen(true)}
162
+ className={cx("md:hidden", className)}
163
+ />
164
+ <IconButton
165
+ icon="panel-left"
166
+ label={ctx.collapsed ? "Expand sidebar" : "Collapse sidebar"}
167
+ aria-expanded={!ctx.collapsed}
168
+ onClick={() => ctx.setCollapsed(!ctx.collapsed)}
169
+ className={cx("max-md:hidden", className)}
170
+ />
171
+ </>
172
+ );
173
+ }
174
+
175
+
176
+ const keyOf = (item) => String(item.key ?? item.href ?? item.label);
177
+ const hasActive = (item) => Boolean(item.active || item.items?.some(hasActive));
178
+ const shows = (badge) => badge != null && badge !== "";
179
+
180
+ // The icon rail's active look rides on aria-current, so the state a reader
181
+ // hears is the state the eye sees. (A wide row is a nav Button, which
182
+ // paints its own aria-current; an icon button is square, not a nav row.)
183
+ const CURRENT = "aria-[current=page]:bg-accent aria-[current=page]:text-accent-foreground aria-[current=page]:font-semibold";
184
+
185
+ // The column's frame, per variant: a bordered column (today), a column on
186
+ // the frame's own ground beside a page drawn as a card (inset), or a
187
+ // rounded card held off the window's edge (floating).
188
+ const SHELLS = {
189
+ sidebar: "sticky top-0 max-h-dvh self-stretch border-r border-border bg-muted/40",
190
+ inset: "sticky top-0 max-h-dvh self-stretch",
191
+ floating: "sticky top-2 m-2 max-h-[calc(100dvh-1rem)] self-stretch overflow-hidden rounded-xl border border-border bg-card shadow-sm",
192
+ };
193
+
194
+ // A row's "…" shows on hover or focus on a wide window, always where
195
+ // there is no hover (a touch screen), and stays while its menu is open.
196
+ const REVEAL =
197
+ "opacity-0 transition-opacity motion-reduce:transition-none group-hover/row:opacity-100 group-focus-within/row:opacity-100 " +
198
+ "has-[[aria-expanded=true]]:opacity-100 [@media(hover:none)]:opacity-100";
199
+
200
+ // An href-only item chosen from the ⌘K palette: a full page load, with
201
+ // the Makersclaw app's base path when there is one.
202
+ function follow(href) {
203
+ if (typeof window === "undefined" || !href) return;
204
+ const base = /^[a-z]+:|^\/\//i.test(href) ? "" : document.documentElement.dataset.clawBasePath ?? "";
205
+ window.location.assign(base + href);
206
+ }
207
+
208
+ function focusSoon(id) {
209
+ requestAnimationFrame(() => document.getElementById(id)?.focus());
210
+ }
211
+
212
+ export function Sidebar({
213
+ groups,
214
+ items,
215
+ title,
216
+ subtitle,
217
+ logo,
218
+ switcher,
219
+ switcherLabel = "Switch workspace",
220
+ switcherPlacement = "header",
221
+ user,
222
+ userMenu,
223
+ children,
224
+ label = "Main",
225
+ variant = "sidebar",
226
+ headerActions,
227
+ search,
228
+ primaryAction,
229
+ footerActions,
230
+ footerActionsLabel = "Create",
231
+ drilldown = "inline",
232
+ panel,
233
+ className,
234
+ }) {
235
+ const ctx = useSidebar();
236
+ const uid = useId();
237
+ const collapsed = Boolean(ctx?.collapsed);
238
+ const list = (groups ?? (items ? [{ items }] : [])).filter((g) => g && g.items?.length);
239
+ const drillMode = drilldown === "panel" || drilldown === "replace" ? drilldown : "inline";
240
+ const shell = SHELLS[variant] ?? SHELLS.sidebar;
241
+ // `panel` is a node of the app's own, or { title, search, items, … }.
242
+ const panelIsNode = Boolean(panel) && (isValidElement(panel) || typeof panel !== "object" || Array.isArray(panel));
243
+
244
+ // Open parents are held here so a click on a collapsed parent's icon can
245
+ // widen the sidebar with that parent open. A parent holding the active
246
+ // item starts open.
247
+ const [open, setOpen] = useState(() => {
248
+ const out = new Set();
249
+ for (const g of list) for (const it of g.items) if (it.items?.length && hasActive(it)) out.add(keyOf(it));
250
+ return out;
251
+ });
252
+ const setParent = (key, next) =>
253
+ setOpen((s) => {
254
+ const out = new Set(s);
255
+ if (next) out.add(key);
256
+ else out.delete(key);
257
+ return out;
258
+ });
259
+
260
+ // Folding groups (group.collapsible): the ones closed, by group key.
261
+ const groupKey = (group, gi) => String(group.key ?? group.label ?? gi);
262
+ const [folded, setFolded] = useState(() => new Set(list.flatMap((g, gi) => (g.collapsible && g.defaultOpen === false ? [groupKey(g, gi)] : []))));
263
+ const setGroup = (key, isOpen) =>
264
+ setFolded((s) => {
265
+ const out = new Set(s);
266
+ if (isOpen) out.delete(key);
267
+ else out.add(key);
268
+ return out;
269
+ });
270
+
271
+ // Drill-in (drilldown="panel" | "replace"): the parent whose children
272
+ // are shown. Starts at the one holding the active item.
273
+ const [drill, setDrill] = useState(() => {
274
+ if (drillMode === "inline") return null;
275
+ for (const g of list) for (const it of g.items) if (it.items?.length && hasActive(it)) return keyOf(it);
276
+ return null;
277
+ });
278
+ const drillItem = drill == null ? null : list.flatMap((g) => g.items).find((it) => it.items?.length && keyOf(it) === drill) ?? null;
279
+
280
+ // The phone sheet's second screen: the panel.
281
+ const [sheetPanel, setSheetPanel] = useState(false);
282
+ useEffect(() => {
283
+ if (!ctx?.mobileOpen) setSheetPanel(false);
284
+ }, [ctx?.mobileOpen]);
285
+
286
+ // ⌘K search over every item.
287
+ const [searching, setSearching] = useState(false);
288
+ const searchOpts = search ? (typeof search === "object" ? search : {}) : null;
289
+ const hotkey = searchOpts ? (searchOpts.hotkey === undefined ? "k" : searchOpts.hotkey) : null;
290
+ const searchLabel = searchOpts?.label ?? "Search";
291
+
292
+ function choose(item, { sheet } = {}) {
293
+ item.onSelect?.(item);
294
+ // With a panel, choosing on the phone shows the panel (the mails in
295
+ // the folder just chosen) instead of closing the sheet.
296
+ if (sheet && panel) setSheetPanel(true);
297
+ else ctx?.setMobileOpen(false);
298
+ }
299
+
300
+ // A choice in the user menu also closes the phone sheet — through
301
+ // groups and submenus too.
302
+ const closingSheet = (entries) =>
303
+ (entries ?? []).filter(Boolean).map((item) =>
304
+ Array.isArray(item.items)
305
+ ? { ...item, items: closingSheet(item.items) }
306
+ : item.onSelect
307
+ ? {
308
+ ...item,
309
+ onSelect: () => {
310
+ ctx?.setMobileOpen(false);
311
+ item.onSelect();
312
+ },
313
+ }
314
+ : item,
315
+ );
316
+
317
+ const mark = logo ?? <Avatar name={title} shape="square" size="sm" />;
318
+
319
+ function openDrill(key, where) {
320
+ const next = drillMode === "panel" && drill === key ? null : key;
321
+ setDrill(next);
322
+ if (next == null) return;
323
+ // Focus follows: to the back row (replace, or any drill on the phone),
324
+ // or the first child in the second column.
325
+ focusSoon(drillMode === "replace" || where === "s" ? `${uid}-${where}-back` : `${uid}-drill-first`);
326
+ }
327
+
328
+ function closeDrill(where) {
329
+ const key = drill;
330
+ setDrill(null);
331
+ if (key != null) focusSoon(`${uid}-${where}-parent-${key}`);
332
+ }
333
+
334
+ // ---- one item, three ways: the wide column, the icon rail, the sheet --
335
+
336
+ function leaf(item, { sheet, id }) {
337
+ const current = item.active ? "page" : undefined;
338
+ const button = (
339
+ <Button
340
+ id={id}
341
+ variant="nav"
342
+ size={sheet ? "md" : "sm"}
343
+ href={item.disabled ? undefined : item.href}
344
+ onClick={() => choose(item, { sheet })}
345
+ disabled={item.disabled}
346
+ aria-current={current}
347
+ className={item.description ? "py-1.5" : undefined}
348
+ >
349
+ {item.hue ? (
350
+ <span className="inline-flex size-4 shrink-0 items-center justify-center">
351
+ <StatusDot hue={item.hue} size="md" />
352
+ </span>
353
+ ) : item.icon ? (
354
+ <Icon name={item.icon} className="shrink-0" />
355
+ ) : null}
356
+ {item.description ? (
357
+ <span className="grid min-w-0 flex-1 text-left leading-tight">
358
+ <span className="truncate">{item.label}</span>
359
+ <span className="truncate text-xs text-muted-foreground">{item.description}</span>
360
+ </span>
361
+ ) : (
362
+ <span className="min-w-0 flex-1 truncate text-left">{item.label}</span>
363
+ )}
364
+ {shows(item.badge) ? <Badge className="shrink-0">{item.badge}</Badge> : null}
365
+ </Button>
366
+ );
367
+ if (!item.actions?.length) return button;
368
+ return (
369
+ <div className="group/row flex min-w-0 items-center gap-0.5">
370
+ {button}
371
+ <span className={cx("shrink-0", !sheet && REVEAL)}>
372
+ <Menu items={guard(item.actions, item)} label={`Actions for ${item.label}`} size={sheet ? "md" : "sm"} />
373
+ </span>
374
+ </div>
375
+ );
376
+ }
377
+
378
+ // A parent in drill-in mode: a row that opens its children (a second
379
+ // column, or in place of the list) instead of folding them in.
380
+ function drillRow(item, { sheet, where }) {
381
+ const key = keyOf(item);
382
+ const inPanel = drillMode === "panel" && !sheet;
383
+ return (
384
+ <Button
385
+ id={`${uid}-${where}-parent-${key}`}
386
+ variant="nav"
387
+ size={sheet ? "md" : "sm"}
388
+ onClick={() => openDrill(key, where)}
389
+ disabled={item.disabled}
390
+ aria-current={hasActive(item) ? "true" : undefined}
391
+ aria-expanded={inPanel ? drill === key : undefined}
392
+ className={inPanel ? "aria-expanded:bg-accent" : undefined}
393
+ >
394
+ {item.icon ? <Icon name={item.icon} className="shrink-0" /> : null}
395
+ <span className="min-w-0 flex-1 truncate text-left">{item.label}</span>
396
+ {shows(item.badge) ? <Badge className="shrink-0">{item.badge}</Badge> : null}
397
+ <Icon name="chevron-right" className="shrink-0 text-muted-foreground" />
398
+ </Button>
399
+ );
400
+ }
401
+
402
+ function wideItem(item, opts) {
403
+ if (!item.items?.length) return leaf(item, opts);
404
+ if (drillMode !== "inline") return drillRow(item, opts);
405
+ const key = keyOf(item);
406
+ return (
407
+ <div>
408
+ <Collapsible
409
+ open={open.has(key)}
410
+ onOpenChange={(next) => setParent(key, next)}
411
+ meta={shows(item.badge) ? item.badge : undefined}
412
+ title={
413
+ <span className="flex min-w-0 items-center gap-3">
414
+ {item.icon ? <Icon name={item.icon} className="shrink-0" /> : null}
415
+ <span className="truncate">{item.label}</span>
416
+ </span>
417
+ }
418
+ className={cx(
419
+ "[&>summary]:mx-0 [&>div]:pb-1 [&>div]:pt-0.5",
420
+ opts.sheet ? "[&>summary]:px-4" : "[&>summary]:min-h-9 [&>summary]:px-3 [&>summary]:py-1",
421
+ )}
422
+ >
423
+ <ul className="ml-5 grid gap-0.5 border-l border-border pl-2">
424
+ {item.items.map((child) => (
425
+ <li key={keyOf(child)}>{leaf(child, opts)}</li>
426
+ ))}
427
+ </ul>
428
+ </Collapsible>
429
+ </div>
430
+ );
431
+ }
432
+
433
+ function iconItem(item) {
434
+ const parent = item.items?.length > 0;
435
+ const count = shows(item.badge) ? item.badge : null;
436
+ const current = item.active || (parent && hasActive(item)) ? "page" : undefined;
437
+ return (
438
+ <Tooltip label={count != null ? `${item.label} (${count})` : item.label} side="right">
439
+ <span className="relative inline-flex">
440
+ <IconButton
441
+ icon={item.icon ?? "file"}
442
+ label={item.label}
443
+ size="sm"
444
+ href={parent || item.disabled ? undefined : item.href}
445
+ disabled={item.disabled}
446
+ aria-current={current}
447
+ aria-expanded={parent && drillMode === "panel" ? drill === keyOf(item) : undefined}
448
+ onClick={() => {
449
+ if (!parent) return choose(item);
450
+ // Drill-in to a column: the rail stays narrow beside it.
451
+ if (drillMode === "panel") return openDrill(keyOf(item), "r");
452
+ // Otherwise a parent's children need labels: widen, with it open.
453
+ if (drillMode === "replace") setDrill(keyOf(item));
454
+ else setParent(keyOf(item), true);
455
+ ctx?.setCollapsed(false);
456
+ }}
457
+ className={cx(CURRENT, drillMode === "panel" && "aria-expanded:bg-accent")}
458
+ />
459
+ {count != null ? (
460
+ <span aria-hidden="true" className="pointer-events-none absolute right-1 top-1 size-1.5 rounded-full bg-primary" />
461
+ ) : null}
462
+ </span>
463
+ </Tooltip>
464
+ );
465
+ }
466
+
467
+ // A drilled parent's children in place of the list: a back row, the
468
+ // parent's name, then its children.
469
+ function replaced(item, { sheet, where }) {
470
+ const headingId = `${uid}-${where}-drill`;
471
+ return (
472
+ <div className="grid gap-0.5">
473
+ <Button id={`${uid}-${where}-back`} variant="nav" size={sheet ? "md" : "sm"} onClick={() => closeDrill(where)}>
474
+ <Icon name="arrow-left" className="shrink-0" />
475
+ <span className="min-w-0 flex-1 truncate text-left">Back</span>
476
+ </Button>
477
+ <h2 id={headingId} className="flex h-8 items-center px-3 text-xs font-medium text-muted-foreground">
478
+ {item.label}
479
+ </h2>
480
+ <ul aria-labelledby={headingId} className="grid gap-0.5">
481
+ {item.items.map((child) => (
482
+ <li key={keyOf(child)}>{leaf(child, { sheet })}</li>
483
+ ))}
484
+ </ul>
485
+ </div>
486
+ );
487
+ }
488
+
489
+ function primary({ icons, sheet }) {
490
+ if (!primaryAction) return null;
491
+ const act = () => {
492
+ ctx?.setMobileOpen(false);
493
+ return run(primaryAction.onSelect, primaryAction, primaryAction.label);
494
+ };
495
+ if (icons) {
496
+ return (
497
+ <Tooltip label={primaryAction.label} side="right">
498
+ <IconButton icon={primaryAction.icon ?? "plus"} label={primaryAction.label} size="sm" variant="secondary" href={primaryAction.href} onClick={act} />
499
+ </Tooltip>
500
+ );
501
+ }
502
+ return (
503
+ <Button variant="secondary" size={sheet ? "md" : "sm"} align="start" icon={primaryAction.icon ?? "plus"} href={primaryAction.href} onClick={act} className="w-full">
504
+ {primaryAction.label}
505
+ </Button>
506
+ );
507
+ }
508
+
509
+ function nav({ sheet }) {
510
+ const icons = collapsed && !sheet;
511
+ const where = sheet ? "s" : "r";
512
+ // Replace mode (and any drill-in on the phone): the drilled parent's
513
+ // children stand in for the list.
514
+ const drilledHere = drillItem && !icons && (drillMode === "replace" || (sheet && drillMode === "panel"));
515
+ return (
516
+ <nav aria-label={label} className={cx("grid gap-3", icons ? "justify-items-center py-2" : "px-2 py-2")}>
517
+ {primaryAction && !drilledHere ? primary({ icons, sheet }) : null}
518
+ {drilledHere
519
+ ? replaced(drillItem, { sheet, where })
520
+ : list.map((group, gi) => {
521
+ const headingId = `${uid}-${where}-${gi}`;
522
+ const gk = groupKey(group, gi);
523
+ const rows = (
524
+ <ul
525
+ aria-labelledby={group.label && !icons && !group.collapsible ? headingId : undefined}
526
+ aria-label={group.label && !icons && group.collapsible ? group.label : undefined}
527
+ className={cx("grid gap-0.5", icons && "justify-items-center")}
528
+ >
529
+ {group.items.map((item) => (
530
+ <li key={keyOf(item)}>{icons ? iconItem(item) : wideItem(item, { sheet, where })}</li>
531
+ ))}
532
+ </ul>
533
+ );
534
+ if (group.collapsible && group.label && !icons) {
535
+ return (
536
+ <Collapsible
537
+ key={gk}
538
+ open={!folded.has(gk)}
539
+ onOpenChange={(next) => setGroup(gk, next)}
540
+ meta={shows(group.badge) ? group.badge : undefined}
541
+ title={<span className="truncate text-xs font-medium text-muted-foreground">{group.label}</span>}
542
+ className={cx("[&>summary]:mx-0 [&>div]:pb-0 [&>div]:pt-0.5", sheet ? "[&>summary]:px-4" : "[&>summary]:min-h-8 [&>summary]:px-3 [&>summary]:py-1")}
543
+ >
544
+ {rows}
545
+ </Collapsible>
546
+ );
547
+ }
548
+ return (
549
+ <div key={group.key ?? group.label ?? gi} className="grid gap-0.5">
550
+ {group.label && !icons ? (
551
+ <h2 id={headingId} className="flex h-8 items-center px-3 text-xs font-medium text-muted-foreground">
552
+ {group.label}
553
+ </h2>
554
+ ) : icons && gi > 0 ? (
555
+ <Separator className="mb-2 w-6" />
556
+ ) : null}
557
+ {rows}
558
+ </div>
559
+ );
560
+ })}
561
+ {children && !icons && !drilledHere ? <div className="px-2">{children}</div> : null}
562
+ </nav>
563
+ );
564
+ }
565
+
566
+ function brand({ icons, side = "bottom" }) {
567
+ const words = (
568
+ <span className="grid min-w-0 flex-1 text-left leading-tight">
569
+ <span className="truncate text-sm font-semibold">{title}</span>
570
+ {subtitle ? <span className="truncate text-xs font-normal text-muted-foreground">{subtitle}</span> : null}
571
+ </span>
572
+ );
573
+ if (switcher?.length) {
574
+ return (
575
+ <Menu
576
+ items={switcher}
577
+ label={`${title}, ${switcherLabel}`}
578
+ side={side === "top" && icons ? "right" : side}
579
+ align={side === "top" && icons ? "end" : "start"}
580
+ className={cx("min-h-12", icons ? "px-1.5" : "w-full px-2")}
581
+ trigger={
582
+ <>
583
+ {mark}
584
+ {icons ? null : words}
585
+ {icons ? null : <Icon name="chevrons-up-down" className="shrink-0 text-muted-foreground" />}
586
+ </>
587
+ }
588
+ />
589
+ );
590
+ }
591
+ return (
592
+ <div className={cx("flex min-h-12 items-center gap-2", icons ? "justify-center" : "px-2")}>
593
+ {icons ? <Tooltip label={title} side="right">{mark}</Tooltip> : mark}
594
+ {icons ? null : words}
595
+ </div>
596
+ );
597
+ }
598
+
599
+ // The top of the column: the brand (or, with the switcher in the foot,
600
+ // only the mark), then headerActions — a bell, a trigger.
601
+ function head({ icons }) {
602
+ const footSwitch = switcherPlacement === "footer" && switcher?.length;
603
+ const top = footSwitch ? (
604
+ <div className={cx("flex min-h-12 items-center", icons ? "justify-center" : "px-2")}>
605
+ {icons ? <Tooltip label={title} side="right">{mark}</Tooltip> : mark}
606
+ </div>
607
+ ) : (
608
+ brand({ icons })
609
+ );
610
+ if (icons || !headerActions) return top;
611
+ return (
612
+ <div className="flex items-center gap-1">
613
+ <div className="min-w-0 flex-1">{top}</div>
614
+ <div className="flex shrink-0 items-center gap-1">{headerActions}</div>
615
+ </div>
616
+ );
617
+ }
618
+
619
+ function searchRow({ icons, sheet }) {
620
+ if (!searchOpts) return null;
621
+ const openIt = () => {
622
+ ctx?.setMobileOpen(false);
623
+ setSearching(true);
624
+ };
625
+ const keys = hotkey ? `Meta+${String(hotkey).toUpperCase()}` : undefined;
626
+ if (icons) {
627
+ return (
628
+ <Tooltip label={searchLabel} side="right">
629
+ <IconButton icon="search" label={searchLabel} size="sm" onClick={openIt} aria-keyshortcuts={keys} />
630
+ </Tooltip>
631
+ );
632
+ }
633
+ return (
634
+ <Button variant="secondary" size={sheet ? "md" : "sm"} align="start" onClick={openIt} aria-keyshortcuts={keys} className="w-full">
635
+ <Icon name="search" className="shrink-0 text-muted-foreground" />
636
+ <span className="min-w-0 flex-1 truncate text-left font-normal text-muted-foreground">{searchOpts.placeholder ?? `${searchLabel}…`}</span>
637
+ {hotkey && !sheet ? <KbdGroup keys={["⌘", String(hotkey).toUpperCase()]} /> : null}
638
+ </Button>
639
+ );
640
+ }
641
+
642
+ // Every item, for the palette: a group's items under its label, a
643
+ // parent's children under the parent's name.
644
+ const searchItems = searchOpts
645
+ ? list.flatMap((group, gi) =>
646
+ group.items.flatMap((item) => {
647
+ const own = (it, parent) => ({
648
+ value: `${gi}:${parent ? `${keyOf(parent)}/` : ""}${keyOf(it)}`,
649
+ label: it.label,
650
+ description: parent ? parent.label : it.description,
651
+ icon: it.hue ? <StatusDot hue={it.hue} /> : it.icon,
652
+ group: group.label ?? searchOpts.groupLabel ?? "Go to",
653
+ keywords: it.keywords,
654
+ disabled: it.disabled,
655
+ onSelect: () => {
656
+ if (it.onSelect) it.onSelect(it);
657
+ else follow(it.href);
658
+ },
659
+ });
660
+ if (!item.items?.length) return [own(item)];
661
+ return [...(item.onSelect || item.href ? [own(item)] : []), ...item.items.map((child) => own(child, item))];
662
+ }),
663
+ )
664
+ : [];
665
+
666
+ function person({ icons }) {
667
+ if (!user) return null;
668
+ const face = <Avatar name={user.name} src={user.src} size="sm" />;
669
+ const words = (
670
+ <span className="grid min-w-0 flex-1 text-left leading-tight">
671
+ <span className="truncate text-sm font-medium">{user.name}</span>
672
+ {user.email ? <span className="truncate text-xs font-normal text-muted-foreground">{user.email}</span> : null}
673
+ </span>
674
+ );
675
+ if (!userMenu?.length) {
676
+ return <div className={cx("flex min-h-12 items-center gap-2", icons ? "justify-center" : "px-2")}>{face}{icons ? null : words}</div>;
677
+ }
678
+ // Opens upward from the foot of the window (beside the rail when it is
679
+ // icons), and flips where there is no room.
680
+ return (
681
+ <Menu
682
+ side={icons ? "right" : "top"}
683
+ align={icons ? "end" : "start"}
684
+ label={`${user.name}, account menu`}
685
+ items={closingSheet(userMenu)}
686
+ className={cx("min-h-12", icons ? "px-1.5" : "w-full px-2")}
687
+ trigger={
688
+ <>
689
+ {face}
690
+ {icons ? null : words}
691
+ {icons ? null : <Icon name="chevrons-up-down" className="shrink-0 text-muted-foreground" />}
692
+ </>
693
+ }
694
+ />
695
+ );
696
+ }
697
+
698
+ // The foot: the switcher (when it lives here), the person, and the "+"
699
+ // menu of footerActions beside them.
700
+ function foot({ icons, sheet }) {
701
+ const footSwitch = switcherPlacement === "footer" && switcher?.length && !sheet;
702
+ const plus = footerActions?.length ? (
703
+ <Menu
704
+ items={closingSheet(guard(footerActions))}
705
+ label={footerActionsLabel}
706
+ side={icons ? "right" : "top"}
707
+ align="end"
708
+ size={sheet ? "md" : "sm"}
709
+ trigger={<Icon name="plus" />}
710
+ />
711
+ ) : null;
712
+ if (!user && !plus && !footSwitch) return null;
713
+ return (
714
+ <div className="grid gap-1">
715
+ {footSwitch ? brand({ icons, side: "top" }) : null}
716
+ {user || plus ? (
717
+ <div className={cx("flex items-center gap-1", icons && "flex-col")}>
718
+ {user ? <div className={cx("min-w-0", !icons && "flex-1")}>{person({ icons })}</div> : null}
719
+ {plus}
720
+ </div>
721
+ ) : null}
722
+ </div>
723
+ );
724
+ }
725
+
726
+ // The second column from 768px: a drilled parent's children
727
+ // (drilldown="panel"), else the panel.
728
+ function secondColumn() {
729
+ if (drillMode === "panel" && drillItem) {
730
+ const headingId = `${uid}-drill-title`;
731
+ return (
732
+ <div
733
+ className="flex min-h-0 flex-col"
734
+ onKeyDown={(e) => {
735
+ if (e.key === "Escape") {
736
+ e.stopPropagation();
737
+ closeDrill("r");
738
+ }
739
+ }}
740
+ >
741
+ <div className="flex min-h-14 shrink-0 items-center gap-2 border-b border-border px-3">
742
+ <h2 id={headingId} className="min-w-0 flex-1 truncate text-sm font-semibold">
743
+ {drillItem.label}
744
+ </h2>
745
+ <IconButton icon="x" label={`Close ${drillItem.label}`} size="sm" onClick={() => closeDrill("r")} />
746
+ </div>
747
+ <ScrollArea label={drillItem.label} className="min-h-0 flex-1" fade>
748
+ <ul aria-labelledby={headingId} className="grid gap-0.5 p-2">
749
+ {drillItem.items.map((child, i) => (
750
+ <li key={keyOf(child)}>{leaf(child, { sheet: false, id: i === 0 ? `${uid}-drill-first` : undefined })}</li>
751
+ ))}
752
+ </ul>
753
+ </ScrollArea>
754
+ </div>
755
+ );
756
+ }
757
+ if (!panel) return null;
758
+ if (panelIsNode) return panel;
759
+ return <PanelBody panel={panel} onChoose={() => ctx?.setMobileOpen(false)} />;
760
+ }
761
+
762
+ const inLayout = Boolean(ctx);
763
+ const second = secondColumn();
764
+ const hasFoot = Boolean(user || footerActions?.length || (switcherPlacement === "footer" && switcher?.length));
765
+ return (
766
+ <>
767
+ <div
768
+ data-sidebar=""
769
+ data-collapsed={collapsed ? "" : undefined}
770
+ data-variant={variant !== "sidebar" ? variant : undefined}
771
+ className={cx(inLayout ? "hidden md:flex" : "flex", "shrink-0", shell, className)}
772
+ >
773
+ <div
774
+ className={cx(
775
+ "flex min-h-0 shrink-0 flex-col transition-[width] duration-200 ease-out motion-reduce:transition-none",
776
+ collapsed ? "w-14" : "w-64",
777
+ )}
778
+ >
779
+ <div className={cx("shrink-0 py-2", collapsed ? "flex justify-center" : "px-2")}>{head({ icons: collapsed })}</div>
780
+ {searchOpts ? <div className={cx("shrink-0 pb-1", collapsed ? "flex justify-center" : "px-2")}>{searchRow({ icons: collapsed })}</div> : null}
781
+ <ScrollArea label={`${label} navigation`} className="flex-1" fade>
782
+ {nav({ sheet: false })}
783
+ </ScrollArea>
784
+ {hasFoot ? (
785
+ <div className={cx("shrink-0 border-t border-border py-2", collapsed ? "flex justify-center" : "px-2")}>
786
+ {foot({ icons: collapsed })}
787
+ </div>
788
+ ) : null}
789
+ </div>
790
+ {second ? (
791
+ <div className={cx("flex w-80 min-h-0 shrink-0 flex-col border-l border-border", variant === "sidebar" && "bg-background")}>{second}</div>
792
+ ) : null}
793
+ </div>
794
+ {searchOpts ? (
795
+ <CommandDialog
796
+ open={searching}
797
+ onOpenChange={setSearching}
798
+ hotkey={hotkey || undefined}
799
+ label={searchOpts.label ?? `Search ${title ?? label}`}
800
+ placeholder={searchOpts.placeholder ?? "Go to…"}
801
+ empty={searchOpts.empty ?? "Nothing by that name."}
802
+ items={searchItems}
803
+ />
804
+ ) : null}
805
+ {inLayout ? (
806
+ <Sheet
807
+ side="start"
808
+ open={ctx.mobileOpen}
809
+ onClose={() => ctx.setMobileOpen(false)}
810
+ title={title}
811
+ description={subtitle}
812
+ footer={user || footerActions?.length ? <div className="w-full">{foot({ icons: false, sheet: true })}</div> : null}
813
+ >
814
+ {ctx.mobileOpen ? (
815
+ sheetPanel && panel ? (
816
+ <div className="-mx-2 -my-2 grid gap-2">
817
+ <div className="px-2 pt-2">
818
+ <Button variant="nav" size="md" onClick={() => setSheetPanel(false)}>
819
+ <Icon name="arrow-left" className="shrink-0" />
820
+ <span className="min-w-0 flex-1 truncate text-left">Back</span>
821
+ </Button>
822
+ </div>
823
+ {panelIsNode ? panel : <PanelBody panel={panel} sheet onChoose={() => ctx.setMobileOpen(false)} />}
824
+ </div>
825
+ ) : (
826
+ <div className="-mx-2 -my-2 grid gap-2">
827
+ {headerActions ? <div className="flex flex-wrap items-center gap-1 px-2">{headerActions}</div> : null}
828
+ {searchOpts ? <div className="px-2">{searchRow({ icons: false, sheet: true })}</div> : null}
829
+ {switcher?.length ? (
830
+ // The sheet's header already names the workspace: here the
831
+ // switcher is just the way to change it.
832
+ <div className="px-2">
833
+ <Menu
834
+ items={switcher}
835
+ label={switcherLabel}
836
+ align="start"
837
+ className="min-h-11 w-full px-2"
838
+ trigger={
839
+ <>
840
+ <Icon name="chevrons-up-down" className="shrink-0 text-muted-foreground" />
841
+ <span className="min-w-0 flex-1 truncate text-left">{switcherLabel}</span>
842
+ </>
843
+ }
844
+ />
845
+ </div>
846
+ ) : null}
847
+ {panel ? (
848
+ <div className="px-2">
849
+ <Button variant="nav" size="md" onClick={() => setSheetPanel(true)}>
850
+ <Icon name="list" className="shrink-0" />
851
+ <span className="min-w-0 flex-1 truncate text-left">{(panelIsNode ? null : panel.title) ?? "Open list"}</span>
852
+ <Icon name="chevron-right" className="shrink-0 text-muted-foreground" />
853
+ </Button>
854
+ </div>
855
+ ) : null}
856
+ {nav({ sheet: true })}
857
+ </div>
858
+ )
859
+ ) : null}
860
+ </Sheet>
861
+ ) : null}
862
+ </>
863
+ );
864
+ }