makersclaw 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) 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/install.mjs +18 -0
  430. package/src/shadcn.mjs +8 -0
  431. package/templates/next/files/README.md +145 -11
  432. package/templates/next/files/app/globals.css +55 -4
  433. package/templates/next/files/check/run.mjs +3 -3
  434. package/templates/next/files/components/ui/avatar.jsx +141 -0
  435. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  436. package/templates/next/files/components/ui/button-group.jsx +70 -0
  437. package/templates/next/files/components/ui/button.jsx +132 -12
  438. package/templates/next/files/components/ui/calendar.jsx +609 -0
  439. package/templates/next/files/components/ui/carousel.jsx +281 -0
  440. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  441. package/templates/next/files/components/ui/combobox.jsx +608 -0
  442. package/templates/next/files/components/ui/command.jsx +577 -0
  443. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  444. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  445. package/templates/next/files/components/ui/distribution.jsx +117 -0
  446. package/templates/next/files/components/ui/feedback.jsx +143 -0
  447. package/templates/next/files/components/ui/field.jsx +115 -3
  448. package/templates/next/files/components/ui/file-input.jsx +234 -0
  449. package/templates/next/files/components/ui/icon.jsx +130 -8
  450. package/templates/next/files/components/ui/image.jsx +143 -0
  451. package/templates/next/files/components/ui/index.js +47 -2
  452. package/templates/next/files/components/ui/input-group.jsx +213 -0
  453. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  454. package/templates/next/files/components/ui/item.jsx +206 -0
  455. package/templates/next/files/components/ui/menu.jsx +454 -82
  456. package/templates/next/files/components/ui/notice.jsx +126 -14
  457. package/templates/next/files/components/ui/pagination.jsx +156 -0
  458. package/templates/next/files/components/ui/popover.jsx +284 -0
  459. package/templates/next/files/components/ui/radio.jsx +193 -0
  460. package/templates/next/files/components/ui/rating.jsx +131 -0
  461. package/templates/next/files/components/ui/resizable.jsx +338 -0
  462. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  463. package/templates/next/files/components/ui/separator.jsx +119 -0
  464. package/templates/next/files/components/ui/sheet.jsx +96 -11
  465. package/templates/next/files/components/ui/slider.jsx +174 -0
  466. package/templates/next/files/components/ui/stat.jsx +574 -0
  467. package/templates/next/files/components/ui/steps.jsx +338 -0
  468. package/templates/next/files/components/ui/swatch.jsx +33 -0
  469. package/templates/next/files/components/ui/table.jsx +130 -0
  470. package/templates/next/files/components/ui/toast.jsx +440 -0
  471. package/templates/next/files/components/ui/toggle.jsx +195 -0
  472. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  473. package/templates/next/template.json +264 -8
@@ -0,0 +1,715 @@
1
+ "use client";
2
+
3
+ // MapView — places on a map, with the list beside it, behind one set of
4
+ // props (README.md beside this file). MapLibre draws the tiles; every mark
5
+ // on them (markers, clusters, the popup) and every control is ours, in the
6
+ // kit and the workspace's tokens.
7
+ //
8
+ // Client only, one static bundle: maplibre loads with the page (see
9
+ // maplibre.js), and the map itself mounts after the first effect (the
10
+ // mount guard). Until then — on the server and in the first client paint —
11
+ // the frame, the list and a quiet placeholder are already there, so the
12
+ // page does not jump and the places can be read before the map arrives.
13
+ //
14
+ // One selection, one hover, shared by the map, the list, the popup and the
15
+ // phone's card strip. Picking in the list moves the camera to the place
16
+ // (zooming until it stands clear of its cluster); picking on the map
17
+ // scrolls the list to it. Clustering is ours (geo.js clusterPoints, in
18
+ // screen pixels at the current zoom), so a cluster is kit-drawn HTML like
19
+ // every other mark.
20
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
21
+ import { Button, ButtonGroup, Empty, Icon, IconButton, Notice, cx, toast } from "../../ui";
22
+ import { boundsOf, clusterPoints, idsKey, inBounds, isPointBounds, matches, readPosition, zoomToReveal } from "./geo";
23
+ import { ListPanel, Strip } from "./list";
24
+ import { AttributionControl, GLMap, Layer, Marker, Popup, Source, WORKER_URL, maplibregl } from "./maplibre";
25
+ import { ClusterMark, DefaultPopup, PointMark, PopupCard, StackList } from "./marks";
26
+ import { CHROME, TONES, prefersReducedMotion, resolveTones, schemeOf, styleFor, toneOrDefault } from "./theme";
27
+
28
+ /** A ref for the map's handle: `const map = useMapView()`, then
29
+ * `<MapView mapRef={map} />` and `map.current?.fitAll()`. */
30
+ export function useMapView() {
31
+ return useRef(null);
32
+ }
33
+
34
+ const WORLD = { lat: 30, lng: 0, zoom: 1.2 };
35
+ const NO_ITEMS = [];
36
+ const step = (zoom) => Math.round(zoom * 2) / 2;
37
+ const text = (v) => (typeof v === "string" || typeof v === "number" ? String(v) : "");
38
+ const defaultTitle = (item) => item?.title ?? item?.name ?? item?.label ?? item?.location?.label ?? "Untitled";
39
+ const defaultId = (item) => item?.id;
40
+
41
+ export function MapView(props) {
42
+ const [mounted, setMounted] = useState(false);
43
+ useEffect(() => setMounted(true), []);
44
+ return <Frame {...props} mounted={mounted} />;
45
+ }
46
+
47
+ function Frame({
48
+ mounted,
49
+ items = NO_ITEMS,
50
+ positionOf = readPosition,
51
+ idOf = defaultId,
52
+ titleOf = defaultTitle,
53
+ metaOf,
54
+ asideOf,
55
+ toneOf,
56
+ searchOf,
57
+ renderMarker,
58
+ renderPopup,
59
+ selectedId,
60
+ defaultSelectedId = null,
61
+ onSelect,
62
+ onMove,
63
+ onMapClick,
64
+ onViewChange,
65
+ areas,
66
+ onAreaClick,
67
+ list = "side",
68
+ search = true,
69
+ cluster = true,
70
+ clusterRadius = 48,
71
+ popup = true,
72
+ styleUrl,
73
+ scheme: schemeProp = "auto",
74
+ workerUrl = WORKER_URL,
75
+ initialView,
76
+ fitMaxZoom = 14,
77
+ maxZoom = 18,
78
+ fitOnChange = true,
79
+ scrollZoom = true,
80
+ controls = true,
81
+ height = 520,
82
+ noun = "place",
83
+ nounPlural,
84
+ emptyTitle,
85
+ emptyBody,
86
+ emptyAction,
87
+ label = "Map",
88
+ mapRef,
89
+ className,
90
+ }) {
91
+ const plural = nounPlural ?? `${noun}s`;
92
+ const rootRef = useRef(null);
93
+ const glRef = useRef(null);
94
+ const listRef = useRef(null);
95
+ const stripRef = useRef(null);
96
+ const hasList = list !== "none";
97
+
98
+ const [query, setQuery] = useState("");
99
+ const [inner, setInner] = useState(defaultSelectedId);
100
+ const selected = selectedId !== undefined ? selectedId : inner;
101
+ const [hovered, setHovered] = useState(null);
102
+ const [overrides, setOverrides] = useState({});
103
+ const [zoomStep, setZoomStep] = useState(null);
104
+ const [loaded, setLoaded] = useState(false);
105
+ const [failure, setFailure] = useState(null);
106
+ const [attempt, setAttempt] = useState(0);
107
+ const [scheme, setScheme] = useState(schemeProp === "dark" ? "dark" : "light");
108
+ const [tones, setTones] = useState({});
109
+ const [phoneView, setPhoneView] = useState("map");
110
+ const [stack, setStack] = useState(null);
111
+ const dragged = useRef(false);
112
+
113
+ // ---- the rows -------------------------------------------------------------
114
+ const rows = useMemo(
115
+ () =>
116
+ items.map((item) => {
117
+ const id = String(idOf(item));
118
+ const own = positionOf(item);
119
+ const o = overrides[id];
120
+ // An optimistic move holds until the row itself changes position.
121
+ const pos = o && samePos(o.base, own) ? o.pos : own;
122
+ const meta = metaOf?.(item) ?? null;
123
+ const title = text(titleOf(item)) || "Untitled";
124
+ return {
125
+ id,
126
+ item,
127
+ pos,
128
+ title,
129
+ meta,
130
+ aside: asideOf?.(item) ?? null,
131
+ tone: toneOrDefault(toneOf?.(item)),
132
+ words: searchOf ? text(searchOf(item)) : `${title} ${text(meta)}`,
133
+ };
134
+ }),
135
+ [items, idOf, positionOf, titleOf, metaOf, asideOf, toneOf, searchOf, overrides],
136
+ );
137
+ const byId = useMemo(() => new Map(rows.map((r) => [r.id, r])), [rows]);
138
+ const visible = useMemo(() => (query.trim() ? rows.filter((r) => matches(r.words, query)) : rows), [rows, query]);
139
+ const located = useMemo(() => visible.filter((r) => r.pos), [visible]);
140
+ const unlocated = useMemo(() => visible.filter((r) => !r.pos), [visible]);
141
+ const points = useMemo(() => located.map((r) => ({ id: r.id, lat: r.pos.lat, lng: r.pos.lng })), [located]);
142
+ const groups = useMemo(() => {
143
+ if (zoomStep == null) return [];
144
+ return cluster ? clusterPoints(points, zoomStep, clusterRadius) : points.map((p) => ({ type: "point", ...p }));
145
+ }, [points, zoomStep, cluster, clusterRadius]);
146
+ const locatedKey = useMemo(() => idsKey(points.map((p) => `${p.id}@${p.lat},${p.lng}`)), [points]);
147
+
148
+ // ---- the camera -----------------------------------------------------------
149
+ const map = () => glRef.current?.getMap?.() ?? null;
150
+ const duration = (ms) => (prefersReducedMotion() ? 0 : ms);
151
+ const padding = () => {
152
+ const w = rootRef.current?.clientWidth ?? 800;
153
+ return w < 480 ? 40 : 64;
154
+ };
155
+
156
+ const fitAll = useCallback(
157
+ ({ animate = true } = {}) => {
158
+ const m = map();
159
+ if (!m) return;
160
+ const b = boundsOf(points);
161
+ const ms = animate ? duration(700) : 0;
162
+ if (!b) {
163
+ const v = initialView ?? WORLD;
164
+ m.easeTo({ center: [v.lng, v.lat], zoom: v.zoom ?? WORLD.zoom, duration: ms });
165
+ } else if (isPointBounds(b)) {
166
+ m.easeTo({ center: [b.west, b.south], zoom: Math.max(m.getZoom(), Math.min(fitMaxZoom, 13)), duration: ms });
167
+ } else {
168
+ m.fitBounds(
169
+ [
170
+ [b.west, b.south],
171
+ [b.east, b.north],
172
+ ],
173
+ { padding: padding(), maxZoom: fitMaxZoom, duration: ms },
174
+ );
175
+ }
176
+ },
177
+ // eslint-disable-next-line react-hooks/exhaustive-deps
178
+ [points, initialView, fitMaxZoom],
179
+ );
180
+
181
+ /** Bring a place into view, clear of any cluster that hides it. */
182
+ const reveal = useCallback(
183
+ (id) => {
184
+ const m = map();
185
+ const r = byId.get(String(id));
186
+ if (!m || !r?.pos) return;
187
+ const z = m.getZoom();
188
+ const need = cluster ? zoomToReveal(points, r.id, z, { radius: clusterRadius, maxZoom: Math.min(maxZoom, fitMaxZoom + 2) }) : z;
189
+ const zoom = Math.max(z, need ?? z);
190
+ const b = m.getBounds();
191
+ const view = { west: b.getWest(), south: b.getSouth(), east: b.getEast(), north: b.getNorth() };
192
+ // Already on screen, away from the edges, and not hidden in a cluster: leave the camera be.
193
+ const inset = { west: view.west + (view.east - view.west) * 0.12, east: view.east - (view.east - view.west) * 0.12, south: view.south + (view.north - view.south) * 0.12, north: view.north - (view.north - view.south) * 0.25 };
194
+ if (zoom === z && inBounds(inset, r.pos)) return;
195
+ m.easeTo({ center: [r.pos.lng, r.pos.lat], zoom, duration: duration(600) });
196
+ },
197
+ // eslint-disable-next-line react-hooks/exhaustive-deps
198
+ [byId, points, cluster, clusterRadius, maxZoom, fitMaxZoom],
199
+ );
200
+
201
+ // ---- selection ------------------------------------------------------------
202
+ const pending = useRef({ id: undefined, reveal: false });
203
+ const select = useCallback(
204
+ (id, { reveal: move = false } = {}) => {
205
+ const next = id == null ? null : String(id);
206
+ pending.current = { id: next, reveal: move };
207
+ setStack(null);
208
+ if (selectedId === undefined) setInner(next);
209
+ onSelect?.(next, next == null ? null : byId.get(next)?.item ?? null);
210
+ },
211
+ [selectedId, onSelect, byId],
212
+ );
213
+
214
+ // A selection that changed — from the list, the handle or the parent —
215
+ // moves the camera; one made on the map itself does not.
216
+ const seen = useRef(selected);
217
+ useEffect(() => {
218
+ if (seen.current === selected) return;
219
+ seen.current = selected;
220
+ const own = pending.current.id === (selected == null ? null : String(selected));
221
+ const move = own ? pending.current.reveal : true;
222
+ pending.current = { id: undefined, reveal: false };
223
+ if (selected == null) return;
224
+ if (move && loaded) reveal(selected);
225
+ // Keep the chosen row and card in view.
226
+ const q = `[data-map-id="${cssEscape(String(selected))}"]`;
227
+ listRef.current?.querySelector(q)?.scrollIntoView({ block: "nearest", behavior: prefersReducedMotion() ? "auto" : "smooth" });
228
+ const card = stripRef.current?.querySelector(q);
229
+ if (card && stripRef.current) {
230
+ const s = stripRef.current;
231
+ s.scrollTo({ left: card.offsetLeft - (s.clientWidth - card.clientWidth) / 2, behavior: prefersReducedMotion() ? "auto" : "smooth" });
232
+ }
233
+ }, [selected, loaded, reveal]);
234
+
235
+ // Refit when the set of places on the map changes (new rows, a search).
236
+ const fittedKey = useRef(null);
237
+ useEffect(() => {
238
+ if (!loaded) return;
239
+ if (fittedKey.current === null) {
240
+ fittedKey.current = locatedKey;
241
+ return;
242
+ }
243
+ if (fittedKey.current === locatedKey) return;
244
+ fittedKey.current = locatedKey;
245
+ if (fitOnChange && points.length) fitAll();
246
+ }, [locatedKey, loaded, fitOnChange, fitAll, points.length]);
247
+
248
+ // ---- the scheme, and the tones areas are painted in ------------------------
249
+ // Read on the first effect — the same flush that mounts the map — so the
250
+ // map's first style is already the right one (no light flash in dark).
251
+ useEffect(() => {
252
+ const root = rootRef.current;
253
+ const read = () => {
254
+ const s = schemeProp === "light" || schemeProp === "dark" ? schemeProp : schemeOf(root);
255
+ setScheme(s);
256
+ if (areas?.length) setTones(resolveTones(root, TONES));
257
+ };
258
+ read();
259
+ const mo = new MutationObserver(read);
260
+ mo.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme", "class", "style"] });
261
+ const scoped = root?.parentElement?.closest("[data-theme]");
262
+ if (scoped && scoped !== document.documentElement) mo.observe(scoped, { attributes: true, attributeFilter: ["data-theme", "class", "style"] });
263
+ const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
264
+ mq?.addEventListener?.("change", read);
265
+ return () => {
266
+ mo.disconnect();
267
+ mq?.removeEventListener?.("change", read);
268
+ };
269
+ }, [schemeProp, areas?.length]);
270
+
271
+ const mapStyle = styleFor(styleUrl, scheme);
272
+
273
+ // ---- areas (territories, zones) -------------------------------------------
274
+ const areaData = useMemo(() => {
275
+ if (!areas?.length) return null;
276
+ return {
277
+ type: "FeatureCollection",
278
+ features: areas
279
+ .filter((a) => a?.geometry)
280
+ .map((a, i) => {
281
+ const tone = toneOrDefault(a.tone);
282
+ const colour = tones[tone] ?? tones.primary ?? null;
283
+ return { type: "Feature", id: i, geometry: a.geometry, properties: { id: String(a.id ?? i), label: text(a.label), paint: colour } };
284
+ })
285
+ .filter((f) => f.properties.paint),
286
+ };
287
+ }, [areas, tones]);
288
+
289
+ // ---- the handle -----------------------------------------------------------
290
+ const api = useRef({});
291
+ api.current = {
292
+ fitAll: (o) => fitAll(o),
293
+ select: (id, { reveal: move = true } = {}) => select(id, { reveal: move }),
294
+ flyTo: ({ lat, lng, zoom }) => map()?.easeTo({ center: [lng, lat], zoom: zoom ?? map()?.getZoom(), duration: duration(700) }),
295
+ fitBounds: (b, { maxZoom: mz = fitMaxZoom } = {}) =>
296
+ b &&
297
+ map()?.fitBounds(
298
+ [
299
+ [b.west, b.south],
300
+ [b.east, b.north],
301
+ ],
302
+ { padding: padding(), maxZoom: mz, duration: duration(700) },
303
+ ),
304
+ zoomIn: () => map()?.zoomIn({ duration: duration(250) }),
305
+ zoomOut: () => map()?.zoomOut({ duration: duration(250) }),
306
+ getView: () => viewOf(map()),
307
+ getMap: () => map(),
308
+ };
309
+ useImperativeHandle(
310
+ mapRef,
311
+ () => ({
312
+ fitAll: (o) => api.current.fitAll(o),
313
+ select: (id, o) => api.current.select(id, o),
314
+ flyTo: (p) => api.current.flyTo(p),
315
+ fitBounds: (b, o) => api.current.fitBounds(b, o),
316
+ zoomIn: () => api.current.zoomIn(),
317
+ zoomOut: () => api.current.zoomOut(),
318
+ getView: () => api.current.getView(),
319
+ getMap: () => api.current.getMap(),
320
+ }),
321
+ [],
322
+ );
323
+
324
+ // ---- a moved marker: optimistic, rolled back when the save fails ---------
325
+ async function moveTo(r, pos) {
326
+ const base = positionOf(r.item);
327
+ setOverrides((o) => ({ ...o, [r.id]: { pos, base } }));
328
+ let res;
329
+ try {
330
+ res = await onMove(r.item, pos);
331
+ } catch (e) {
332
+ res = { ok: false, error: e?.message };
333
+ }
334
+ if (res?.ok) return;
335
+ setOverrides((o) => {
336
+ const next = { ...o };
337
+ delete next[r.id];
338
+ return next;
339
+ });
340
+ toast.error(`${r.title} wasn’t moved`, { description: res?.error || "The new spot didn’t save, so it’s back where it was. Try again." });
341
+ }
342
+
343
+ // The map is made only once its box has a size. Made while hidden (a
344
+ // container query shows it only when the frame is wide enough), it fitted
345
+ // its first view to a 0×0 box, the camera came out invalid and no tile
346
+ // was ever asked for: "Loading the map…" for good (store-locator and
347
+ // location-map, seen in the pane 2026-09-24).
348
+ const stageRef = useRef(null);
349
+ const [sized, setSized] = useState(false);
350
+ useEffect(() => {
351
+ const el = stageRef.current;
352
+ if (!el || sized) return;
353
+ const has = () => el.clientWidth > 0 && el.clientHeight > 0;
354
+ if (has()) return setSized(true);
355
+ if (typeof ResizeObserver === "undefined") return setSized(true);
356
+ const ro = new ResizeObserver(() => has() && setSized(true));
357
+ ro.observe(el);
358
+ return () => ro.disconnect();
359
+ }, [mounted, sized]);
360
+
361
+ // ---- the first view -------------------------------------------------------
362
+ const firstView = useMemo(() => {
363
+ const b = boundsOf(points);
364
+ if (b && !isPointBounds(b)) {
365
+ return {
366
+ bounds: [
367
+ [b.west, b.south],
368
+ [b.east, b.north],
369
+ ],
370
+ fitBoundsOptions: { padding: 64, maxZoom: fitMaxZoom },
371
+ };
372
+ }
373
+ if (b) return { longitude: b.west, latitude: b.south, zoom: Math.min(fitMaxZoom, 13) };
374
+ const v = initialView ?? WORLD;
375
+ return { longitude: v.lng, latitude: v.lat, zoom: v.zoom ?? WORLD.zoom };
376
+ // Only the first render's matters: react-map-gl reads it once.
377
+ // eslint-disable-next-line react-hooks/exhaustive-deps
378
+ }, [attempt]);
379
+
380
+ // `load` can be missed: in development React mounts twice, and the event
381
+ // fired for a map the block had already let go of, so the cover stayed on
382
+ // over a map that was ready ("Loading the map…" for good, seen in the pane
383
+ // 2026-09-24). The first `idle` counts as loaded too.
384
+ const loadedRef = useRef(false);
385
+ loadedRef.current = loaded;
386
+ // And a map already loaded and idle by the time we listen fires neither
387
+ // again: ask it directly for a moment after it mounts.
388
+ useEffect(() => {
389
+ if (!mounted || !sized || loaded) return;
390
+ let tries = 0;
391
+ const t = setInterval(() => {
392
+ const m = glRef.current?.getMap?.();
393
+ if (m?.loaded?.() && !loadedRef.current) onLoad({ target: m });
394
+ if (loadedRef.current || ++tries > 40) clearInterval(t);
395
+ }, 250);
396
+ return () => clearInterval(t);
397
+ // eslint-disable-next-line react-hooks/exhaustive-deps
398
+ }, [mounted, sized, loaded, attempt]);
399
+
400
+ function onLoad(e) {
401
+ const m = e.target;
402
+ loadedRef.current = true;
403
+ m.touchZoomRotate?.disableRotation?.();
404
+ setZoomStep(step(m.getZoom()));
405
+ setLoaded(true);
406
+ setFailure(null);
407
+ if (selected != null) reveal(selected);
408
+ onViewChange?.(viewOf(m));
409
+ }
410
+
411
+ function onError(e) {
412
+ // A tile or an icon that failed is maplibre's to retry; only a map that
413
+ // never came up (the style unreachable, no WebGL) is worth a notice.
414
+ if (loaded || e?.tile || e?.sourceId || e?.source) return;
415
+ const msg = String(e?.error?.message ?? e?.message ?? "");
416
+ setFailure(/webgl/i.test(msg) ? "webgl" : "network");
417
+ }
418
+
419
+ function retry() {
420
+ setFailure(null);
421
+ setLoaded(false);
422
+ setZoomStep(null);
423
+ fittedKey.current = null;
424
+ setAttempt((a) => a + 1);
425
+ }
426
+
427
+ const selectedRow = selected == null ? null : byId.get(String(selected)) ?? null;
428
+ const popupRow = popup && selectedRow?.pos && located.includes(selectedRow) ? selectedRow : null;
429
+ const stackRows = stack ? stack.ids.map((id) => byId.get(id)).filter(Boolean) : [];
430
+ const hoveredId = hovered == null ? null : String(hovered);
431
+
432
+ const empty = (
433
+ <Empty
434
+ icon="map"
435
+ title={emptyTitle ?? `No ${plural} yet`}
436
+ body={emptyBody ?? `${cap(plural)} with an address or coordinates show up here and on the map.`}
437
+ action={emptyAction}
438
+ className="mx-1 mt-2 border-0"
439
+ />
440
+ );
441
+
442
+ return (
443
+ <div
444
+ ref={rootRef}
445
+ role="region"
446
+ aria-label={label}
447
+ onKeyDown={(e) => {
448
+ if (e.key !== "Escape" || e.defaultPrevented) return;
449
+ if (stack) setStack(null);
450
+ else if (selected != null) select(null);
451
+ }}
452
+ style={{ height }}
453
+ className={cx("@container relative flex overflow-hidden rounded-lg border border-border bg-card text-card-foreground", CHROME, className)}
454
+ >
455
+ <div className="flex min-h-0 min-w-0 flex-1 flex-col @3xl:flex-row">
456
+ {hasList ? (
457
+ <ListPanel
458
+ ref={listRef}
459
+ rows={located.map((r) => (r.id === hoveredId ? { ...r, hovered: true } : r))}
460
+ unlocated={unlocated}
461
+ total={rows.length}
462
+ query={query}
463
+ onQuery={setQuery}
464
+ search={search}
465
+ noun={noun}
466
+ plural={plural}
467
+ selected={selected == null ? null : String(selected)}
468
+ onPick={(id) => {
469
+ setPhoneView("map");
470
+ select(id, { reveal: true });
471
+ }}
472
+ onHover={setHovered}
473
+ onShowMap={() => setPhoneView("map")}
474
+ empty={empty}
475
+ className={cx("flex-1 @3xl:flex @3xl:w-80 @3xl:flex-none @3xl:border-r @3xl:border-border", phoneView === "list" ? "flex" : "hidden")}
476
+ />
477
+ ) : null}
478
+
479
+ <div ref={stageRef} className={cx("relative min-h-0 min-w-0 flex-1", hasList && phoneView === "list" && "hidden @3xl:block")}>
480
+ {mounted && sized ? (
481
+ <GLMap
482
+ key={attempt}
483
+ ref={glRef}
484
+ mapLib={maplibregl}
485
+ workerUrl={workerUrl}
486
+ initialViewState={firstView}
487
+ mapStyle={mapStyle}
488
+ maxZoom={maxZoom}
489
+ dragRotate={false}
490
+ pitchWithRotate={false}
491
+ touchPitch={false}
492
+ scrollZoom={scrollZoom}
493
+ attributionControl={false}
494
+ interactiveLayerIds={areaData && onAreaClick ? ["mc-areas-fill"] : undefined}
495
+ onLoad={onLoad}
496
+ onIdle={(e) => {
497
+ if (!loadedRef.current) onLoad(e);
498
+ }}
499
+ onError={onError}
500
+ onMove={(e) => {
501
+ const s = step(e.viewState.zoom);
502
+ if (s !== zoomStep) setZoomStep(s);
503
+ }}
504
+ onMoveEnd={(e) => onViewChange?.(viewOf(e.target))}
505
+ onClick={(e) => {
506
+ const f = e.features?.[0];
507
+ if (f && onAreaClick) {
508
+ const a = areas.find((x, i) => String(x.id ?? i) === f.properties?.id);
509
+ if (a) return onAreaClick(a);
510
+ }
511
+ if (stack) setStack(null);
512
+ else if (selected != null) select(null);
513
+ onMapClick?.({ lat: e.lngLat.lat, lng: e.lngLat.lng });
514
+ }}
515
+ style={{ width: "100%", height: "100%" }}
516
+ >
517
+ <AttributionControl compact={false} position="bottom-right" />
518
+ {areaData ? (
519
+ <Source id="mc-areas" type="geojson" data={areaData}>
520
+ <Layer id="mc-areas-fill" type="fill" filter={["in", ["geometry-type"], ["literal", ["Polygon", "MultiPolygon"]]]} paint={{ "fill-color": ["get", "paint"], "fill-opacity": 0.14 }} />
521
+ <Layer id="mc-areas-line" type="line" paint={{ "line-color": ["get", "paint"], "line-width": 1.5, "line-opacity": 0.8 }} />
522
+ </Source>
523
+ ) : null}
524
+
525
+ {groups.map((g) => {
526
+ if (g.type === "cluster") {
527
+ const holds = selected != null && g.ids.includes(String(selected));
528
+ return (
529
+ <Marker
530
+ key={g.id}
531
+ longitude={g.lng}
532
+ latitude={g.lat}
533
+ anchor="center"
534
+ onClick={(e) => {
535
+ // A native listener on the marker, so it runs before
536
+ // maplibre's own map click (which would clear the selection).
537
+ e.originalEvent.stopPropagation();
538
+ const m = map();
539
+ if (!m) return;
540
+ if (isPointBounds(g.bounds) || m.getZoom() >= maxZoom - 0.5) {
541
+ setStack(g);
542
+ return;
543
+ }
544
+ m.fitBounds(
545
+ [
546
+ [g.bounds.west, g.bounds.south],
547
+ [g.bounds.east, g.bounds.north],
548
+ ],
549
+ { padding: 96, maxZoom, duration: duration(500) },
550
+ );
551
+ }}
552
+ >
553
+ <ClusterMark count={g.count} holdsSelected={holds} label={`${g.count} ${plural} here. Zoom in`} />
554
+ </Marker>
555
+ );
556
+ }
557
+ const r = byId.get(g.id);
558
+ if (!r) return null;
559
+ const isSel = selected != null && String(selected) === r.id;
560
+ const isHover = hoveredId === r.id;
561
+ return (
562
+ <Marker
563
+ key={r.id}
564
+ longitude={g.lng}
565
+ latitude={g.lat}
566
+ anchor="center"
567
+ draggable={Boolean(onMove)}
568
+ onDragStart={() => {
569
+ dragged.current = true;
570
+ }}
571
+ onDragEnd={(e) => {
572
+ setTimeout(() => {
573
+ dragged.current = false;
574
+ }, 0);
575
+ moveTo(r, { lat: e.lngLat.lat, lng: e.lngLat.lng });
576
+ }}
577
+ onClick={(e) => {
578
+ // Native, on the marker: runs before maplibre's map click.
579
+ // A keyboard Enter on the marker's button arrives here too.
580
+ e.originalEvent.stopPropagation();
581
+ if (dragged.current) return;
582
+ select(isSel ? null : r.id);
583
+ }}
584
+ >
585
+ <PointMark
586
+ title={r.title}
587
+ tone={r.tone}
588
+ selected={isSel}
589
+ hovered={isHover}
590
+ showLabel={isHover && !(isSel && popupRow)}
591
+ focusable={!hasList}
592
+ custom={renderMarker ? renderMarker(r.item, { selected: isSel, hovered: isHover, tone: r.tone }) : null}
593
+ onHover={(on) => setHovered(on ? r.id : (h) => (h === r.id ? null : h))}
594
+ />
595
+ </Marker>
596
+ );
597
+ })}
598
+
599
+ {popupRow ? (
600
+ <Popup
601
+ key={`pop:${popupRow.id}`}
602
+ longitude={popupRow.pos.lng}
603
+ latitude={popupRow.pos.lat}
604
+ anchor="bottom"
605
+ offset={20}
606
+ closeButton={false}
607
+ closeOnClick={false}
608
+ focusAfterOpen={false}
609
+ maxWidth="none"
610
+ >
611
+ <PopupCard onClose={() => select(null)}>
612
+ {renderPopup ? (
613
+ renderPopup(popupRow.item, { close: () => select(null) })
614
+ ) : (
615
+ <DefaultPopup title={popupRow.title} meta={popupRow.meta} aside={popupRow.aside} />
616
+ )}
617
+ </PopupCard>
618
+ </Popup>
619
+ ) : null}
620
+
621
+ {stack && stackRows.length ? (
622
+ <Popup key={`stack:${stack.id}`} longitude={stack.lng} latitude={stack.lat} anchor="bottom" offset={28} closeButton={false} closeOnClick={false} focusAfterOpen={false} maxWidth="none">
623
+ <PopupCard onClose={() => setStack(null)}>
624
+ <StackList rows={stackRows} plural={plural} onPick={(id) => select(id)} />
625
+ </PopupCard>
626
+ </Popup>
627
+ ) : null}
628
+ </GLMap>
629
+ ) : null}
630
+
631
+ {!loaded && !failure ? (
632
+ <div className="pointer-events-none absolute inset-0 grid place-items-center bg-muted">
633
+ <div className="absolute inset-0 animate-pulse bg-accent/40 motion-reduce:animate-none" aria-hidden="true" />
634
+ <p className="relative flex items-center gap-2 text-sm text-muted-foreground">
635
+ <Icon name="map" />
636
+ Loading the map…
637
+ </p>
638
+ {!hasList && rows.length ? (
639
+ <ul className="sr-only">
640
+ {rows.map((r) => (
641
+ <li key={r.id}>{r.title}</li>
642
+ ))}
643
+ </ul>
644
+ ) : null}
645
+ </div>
646
+ ) : null}
647
+
648
+ {failure ? (
649
+ <div className="absolute inset-0 z-[4] grid place-items-center bg-muted p-4">
650
+ <Notice
651
+ tone="warning"
652
+ title={failure === "webgl" ? "This browser can’t draw maps" : "The map didn’t load"}
653
+ action={
654
+ failure === "webgl" ? null : (
655
+ <Button variant="secondary" size="sm" onClick={retry}>
656
+ Try again
657
+ </Button>
658
+ )
659
+ }
660
+ className="max-w-sm"
661
+ >
662
+ {failure === "webgl" ? "WebGL is turned off or not supported here." : "The map tiles couldn’t be reached. Check the connection, then try again."}
663
+ {hasList ? ` The ${plural} are still listed.` : ""}
664
+ </Notice>
665
+ </div>
666
+ ) : null}
667
+
668
+ {controls && loaded ? (
669
+ <div className="absolute right-3 top-3 z-[3] flex flex-col items-end gap-2">
670
+ <ButtonGroup orientation="vertical" size="sm" aria-label="Zoom" className="rounded-md shadow-sm">
671
+ <IconButton icon="plus" label="Zoom in" size="sm" variant="secondary" onClick={() => api.current.zoomIn()} />
672
+ <IconButton icon="minus" label="Zoom out" size="sm" variant="secondary" onClick={() => api.current.zoomOut()} />
673
+ </ButtonGroup>
674
+ <IconButton icon="maximize" label={`Show all ${plural}`} size="sm" variant="secondary" className="shadow-sm" disabled={!points.length} onClick={() => fitAll()} />
675
+ </div>
676
+ ) : null}
677
+
678
+ {hasList ? (
679
+ <div className="absolute left-3 top-3 z-[3] @3xl:hidden">
680
+ <Button variant="secondary" size="sm" icon="list" className="shadow-sm" onClick={() => setPhoneView("list")}>
681
+ List
682
+ <span className="text-muted-foreground" data-nums>
683
+ {visible.length}
684
+ </span>
685
+ </Button>
686
+ </div>
687
+ ) : null}
688
+ </div>
689
+
690
+ {hasList && located.length ? (
691
+ <Strip ref={stripRef} rows={located} selected={selected == null ? null : String(selected)} onPick={(id) => select(id, { reveal: true })} className={cx("@3xl:hidden", phoneView === "list" && "hidden")} />
692
+ ) : null}
693
+ </div>
694
+ </div>
695
+ );
696
+ }
697
+
698
+ function samePos(a, b) {
699
+ if (!a && !b) return true;
700
+ if (!a || !b) return false;
701
+ return Math.abs(a.lat - b.lat) < 1e-9 && Math.abs(a.lng - b.lng) < 1e-9;
702
+ }
703
+
704
+ function viewOf(m) {
705
+ if (!m) return null;
706
+ const c = m.getCenter();
707
+ const b = m.getBounds();
708
+ return { center: { lat: c.lat, lng: c.lng }, zoom: m.getZoom(), bounds: { west: b.getWest(), south: b.getSouth(), east: b.getEast(), north: b.getNorth() } };
709
+ }
710
+
711
+ const cap = (s) => (s ? s[0].toUpperCase() + s.slice(1) : s);
712
+
713
+ function cssEscape(s) {
714
+ return typeof CSS !== "undefined" && CSS.escape ? CSS.escape(s) : s.replace(/["\\]/g, "\\$&");
715
+ }