makersclaw 0.8.0 → 0.8.2

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 (401) hide show
  1. package/README.md +9 -3
  2. package/package.json +1 -1
  3. package/r/activity-log.json +52 -0
  4. package/r/agent-changes.json +51 -0
  5. package/r/agent-chat.json +53 -0
  6. package/r/agent-column.json +85 -0
  7. package/r/agent-dock.json +57 -0
  8. package/r/agent-kit.json +49 -0
  9. package/r/agent-marks.json +49 -0
  10. package/r/agent-pane.json +93 -0
  11. package/r/ai-action.json +56 -0
  12. package/r/annotate.json +3 -3
  13. package/r/approval-request.json +2 -2
  14. package/r/approval.json +54 -0
  15. package/r/ask-bar.json +50 -0
  16. package/r/audio-player.json +2 -2
  17. package/r/auth-form.json +2 -2
  18. package/r/automation-editor.json +66 -0
  19. package/r/automation-list.json +57 -0
  20. package/r/automation-model.json +118 -0
  21. package/r/automations-screen.json +71 -0
  22. package/r/board.json +2 -2
  23. package/r/calendar-view.json +3 -3
  24. package/r/cart.json +2 -2
  25. package/r/chart.json +2 -2
  26. package/r/chat-pane.json +84 -0
  27. package/r/chat.json +2 -2
  28. package/r/code-editor.json +2 -2
  29. package/r/comment-box.json +3 -3
  30. package/r/connect-card.json +51 -0
  31. package/r/contact-section.json +2 -2
  32. package/r/core.json +13 -7
  33. package/r/cost.json +51 -0
  34. package/r/csv-import.json +3 -3
  35. package/r/data-picker.json +60 -0
  36. package/r/data-table.json +2 -2
  37. package/r/diagram.json +2 -2
  38. package/r/doc-editor.json +2 -2
  39. package/r/doc-page.json +3 -3
  40. package/r/email-render.json +2 -2
  41. package/r/faq.json +2 -2
  42. package/r/file-upload.json +2 -2
  43. package/r/first-run.json +63 -0
  44. package/r/fix-note.json +57 -0
  45. package/r/flow-canvas.json +2 -2
  46. package/r/flow-editor.json +79 -0
  47. package/r/footer.json +2 -2
  48. package/r/gantt.json +3 -3
  49. package/r/job-panel.json +2 -2
  50. package/r/json-editor.json +3 -3
  51. package/r/library-shelf.json +56 -0
  52. package/r/map.json +2 -2
  53. package/r/margin-notes.json +67 -0
  54. package/r/menubar.json +2 -2
  55. package/r/mind-map.json +3 -3
  56. package/r/navigation-menu.json +2 -2
  57. package/r/needs-you.json +55 -0
  58. package/r/order-detail.json +2 -2
  59. package/r/org-chart.json +3 -3
  60. package/r/pace-bar.json +69 -0
  61. package/r/page-builder.json +2 -2
  62. package/r/payment-methods.json +2 -2
  63. package/r/pdf-document.json +2 -2
  64. package/r/pdf-viewer.json +2 -2
  65. package/r/product-detail.json +2 -2
  66. package/r/product-grid.json +3 -3
  67. package/r/record-form.json +2 -2
  68. package/r/record-grid.json +52 -0
  69. package/r/record-notes.json +4 -4
  70. package/r/registry.json +4086 -1512
  71. package/r/run-history.json +52 -0
  72. package/r/run-view.json +80 -0
  73. package/r/schedule-card.json +54 -0
  74. package/r/schedule-engine.json +79 -0
  75. package/r/schedule-gantt.json +83 -0
  76. package/r/schedule-ruler.json +64 -0
  77. package/r/search-dialog.json +2 -2
  78. package/r/settings-list.json +2 -2
  79. package/r/setup-checklist.json +2 -2
  80. package/r/share-dialog.json +2 -2
  81. package/r/sidebar.json +2 -2
  82. package/r/signature-pad.json +2 -2
  83. package/r/sketch-canvas.json +2 -2
  84. package/r/source-receipt.json +52 -0
  85. package/r/spreadsheet.json +3 -3
  86. package/r/stat-cards.json +3 -3
  87. package/r/step-picker.json +53 -0
  88. package/r/step-settings.json +72 -0
  89. package/r/suggestion.json +58 -0
  90. package/r/sum-bar.json +64 -0
  91. package/r/task-list.json +3 -3
  92. package/r/task-model.json +2 -2
  93. package/r/testimonials.json +2 -2
  94. package/r/tier-map.json +67 -0
  95. package/r/time-grid.json +73 -0
  96. package/r/time-strip.json +64 -0
  97. package/r/top-bar.json +66 -0
  98. package/r/transaction-list.json +2 -2
  99. package/r/trigger-picker.json +61 -0
  100. package/r/whiteboard.json +3 -3
  101. package/r/workflow-view.json +55 -0
  102. package/registry/blocks/activity-log/README.md +112 -0
  103. package/registry/blocks/activity-log/activity-log.jsx +351 -0
  104. package/registry/blocks/activity-log/block.json +33 -0
  105. package/registry/blocks/activity-log/demo.jsx +48 -0
  106. package/registry/blocks/activity-log/index.js +4 -0
  107. package/registry/blocks/agent-changes/README.md +94 -0
  108. package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
  109. package/registry/blocks/agent-changes/block.json +28 -0
  110. package/registry/blocks/agent-changes/demo.jsx +39 -0
  111. package/registry/blocks/agent-changes/index.js +3 -0
  112. package/registry/blocks/agent-chat/README.md +145 -0
  113. package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
  114. package/registry/blocks/agent-chat/block.json +30 -0
  115. package/registry/blocks/agent-chat/demo.jsx +165 -0
  116. package/registry/blocks/agent-chat/index.js +3 -0
  117. package/registry/blocks/agent-column/README.md +197 -0
  118. package/registry/blocks/agent-column/agent-column.jsx +492 -0
  119. package/registry/blocks/agent-column/block.json +67 -0
  120. package/registry/blocks/agent-column/column-picker.jsx +163 -0
  121. package/registry/blocks/agent-column/demo.jsx +50 -0
  122. package/registry/blocks/agent-column/index.js +7 -0
  123. package/registry/blocks/agent-column/model.js +164 -0
  124. package/registry/blocks/agent-column/receipt.jsx +483 -0
  125. package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
  126. package/registry/blocks/agent-dock/README.md +210 -0
  127. package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
  128. package/registry/blocks/agent-dock/block.json +47 -0
  129. package/registry/blocks/agent-dock/demo.jsx +153 -0
  130. package/registry/blocks/agent-dock/dock-model.js +118 -0
  131. package/registry/blocks/agent-dock/index.js +5 -0
  132. package/registry/blocks/agent-kit/README.md +73 -0
  133. package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
  134. package/registry/blocks/agent-kit/block.json +10 -0
  135. package/registry/blocks/agent-kit/demo.jsx +35 -0
  136. package/registry/blocks/agent-kit/index.js +3 -0
  137. package/registry/blocks/agent-marks/README.md +80 -0
  138. package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
  139. package/registry/blocks/agent-marks/block.json +27 -0
  140. package/registry/blocks/agent-marks/demo.jsx +49 -0
  141. package/registry/blocks/agent-marks/index.js +3 -0
  142. package/registry/blocks/agent-pane/README.md +234 -0
  143. package/registry/blocks/agent-pane/agent-pane.jsx +246 -0
  144. package/registry/blocks/agent-pane/block.json +41 -0
  145. package/registry/blocks/agent-pane/composer.jsx +6 -0
  146. package/registry/blocks/agent-pane/demo.jsx +233 -0
  147. package/registry/blocks/agent-pane/dock-context.js +10 -0
  148. package/registry/blocks/agent-pane/index.js +4 -0
  149. package/registry/blocks/agent-pane/prose.jsx +84 -0
  150. package/registry/blocks/agent-pane/questions.jsx +134 -0
  151. package/registry/blocks/agent-pane/transcript.jsx +268 -0
  152. package/registry/blocks/agent-pane/work.jsx +368 -0
  153. package/registry/blocks/ai-action/README.md +94 -0
  154. package/registry/blocks/ai-action/ai-action.jsx +188 -0
  155. package/registry/blocks/ai-action/block.json +34 -0
  156. package/registry/blocks/ai-action/demo.jsx +93 -0
  157. package/registry/blocks/ai-action/index.js +3 -0
  158. package/registry/blocks/approval/README.md +145 -0
  159. package/registry/blocks/approval/approval.jsx +163 -0
  160. package/registry/blocks/approval/block.json +31 -0
  161. package/registry/blocks/approval/demo.jsx +121 -0
  162. package/registry/blocks/approval/index.js +3 -0
  163. package/registry/blocks/ask-bar/README.md +105 -0
  164. package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
  165. package/registry/blocks/ask-bar/block.json +26 -0
  166. package/registry/blocks/ask-bar/demo.jsx +78 -0
  167. package/registry/blocks/ask-bar/index.js +3 -0
  168. package/registry/blocks/automation-editor/README.md +291 -0
  169. package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
  170. package/registry/blocks/automation-editor/block.json +39 -0
  171. package/registry/blocks/automation-editor/demo.jsx +284 -0
  172. package/registry/blocks/automation-editor/index.js +4 -0
  173. package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
  174. package/registry/blocks/automation-list/README.md +129 -0
  175. package/registry/blocks/automation-list/automation-list.jsx +476 -0
  176. package/registry/blocks/automation-list/block.json +35 -0
  177. package/registry/blocks/automation-list/demo.jsx +67 -0
  178. package/registry/blocks/automation-list/index.js +3 -0
  179. package/registry/blocks/automation-model/README.md +109 -0
  180. package/registry/blocks/automation-model/block.json +135 -0
  181. package/registry/blocks/automation-model/catalog.js +256 -0
  182. package/registry/blocks/automation-model/check.js +161 -0
  183. package/registry/blocks/automation-model/demo.jsx +42 -0
  184. package/registry/blocks/automation-model/edit.js +262 -0
  185. package/registry/blocks/automation-model/fixes.js +287 -0
  186. package/registry/blocks/automation-model/glyph.jsx +13 -0
  187. package/registry/blocks/automation-model/graph.js +410 -0
  188. package/registry/blocks/automation-model/index.js +33 -0
  189. package/registry/blocks/automation-model/overlay.js +175 -0
  190. package/registry/blocks/automation-model/parts.jsx +94 -0
  191. package/registry/blocks/automation-model/schedule.js +53 -0
  192. package/registry/blocks/automation-model/templates.js +128 -0
  193. package/registry/blocks/automation-model/values.js +29 -0
  194. package/registry/blocks/automation-model/words.js +439 -0
  195. package/registry/blocks/automations-screen/README.md +207 -0
  196. package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
  197. package/registry/blocks/automations-screen/block.json +47 -0
  198. package/registry/blocks/automations-screen/demo.jsx +364 -0
  199. package/registry/blocks/automations-screen/index.js +4 -0
  200. package/registry/blocks/automations-screen/screen-model.js +129 -0
  201. package/registry/blocks/chat-pane/README.md +143 -0
  202. package/registry/blocks/chat-pane/block.json +48 -0
  203. package/registry/blocks/chat-pane/chat-attach.js +166 -0
  204. package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
  205. package/registry/blocks/chat-pane/chat-context.js +17 -0
  206. package/registry/blocks/chat-pane/chat-model.js +43 -0
  207. package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
  208. package/registry/blocks/chat-pane/chats-list.jsx +52 -0
  209. package/registry/blocks/chat-pane/demo.jsx +103 -0
  210. package/registry/blocks/chat-pane/index.js +9 -0
  211. package/registry/blocks/connect-card/README.md +99 -0
  212. package/registry/blocks/connect-card/block.json +10 -0
  213. package/registry/blocks/connect-card/connect-card.jsx +103 -0
  214. package/registry/blocks/connect-card/demo.jsx +49 -0
  215. package/registry/blocks/connect-card/index.js +3 -0
  216. package/registry/blocks/cost/README.md +115 -0
  217. package/registry/blocks/cost/block.json +10 -0
  218. package/registry/blocks/cost/cost.jsx +176 -0
  219. package/registry/blocks/cost/demo.jsx +66 -0
  220. package/registry/blocks/cost/index.js +3 -0
  221. package/registry/blocks/data-picker/README.md +81 -0
  222. package/registry/blocks/data-picker/block.json +38 -0
  223. package/registry/blocks/data-picker/data-picker.jsx +210 -0
  224. package/registry/blocks/data-picker/demo.jsx +34 -0
  225. package/registry/blocks/data-picker/fields.js +89 -0
  226. package/registry/blocks/data-picker/index.js +4 -0
  227. package/registry/blocks/data-table/demo.jsx +1 -1
  228. package/registry/blocks/first-run/README.md +118 -0
  229. package/registry/blocks/first-run/block.json +11 -0
  230. package/registry/blocks/first-run/demo.jsx +88 -0
  231. package/registry/blocks/first-run/first-run-model.js +51 -0
  232. package/registry/blocks/first-run/first-run.jsx +156 -0
  233. package/registry/blocks/first-run/index.js +5 -0
  234. package/registry/blocks/first-run/probe-rules.js +26 -0
  235. package/registry/blocks/fix-note/README.md +79 -0
  236. package/registry/blocks/fix-note/block.json +35 -0
  237. package/registry/blocks/fix-note/demo.jsx +30 -0
  238. package/registry/blocks/fix-note/fix-note.jsx +235 -0
  239. package/registry/blocks/fix-note/index.js +3 -0
  240. package/registry/blocks/flow-editor/README.md +108 -0
  241. package/registry/blocks/flow-editor/block.json +44 -0
  242. package/registry/blocks/flow-editor/demo.jsx +115 -0
  243. package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
  244. package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
  245. package/registry/blocks/flow-editor/index.js +6 -0
  246. package/registry/blocks/flow-editor/minimap.jsx +78 -0
  247. package/registry/blocks/flow-editor/outline.jsx +134 -0
  248. package/registry/blocks/library-shelf/README.md +117 -0
  249. package/registry/blocks/library-shelf/block.json +28 -0
  250. package/registry/blocks/library-shelf/demo.jsx +68 -0
  251. package/registry/blocks/library-shelf/index.js +3 -0
  252. package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
  253. package/registry/blocks/library-shelf/probe-rules.js +15 -0
  254. package/registry/blocks/margin-notes/README.md +222 -0
  255. package/registry/blocks/margin-notes/block.json +10 -0
  256. package/registry/blocks/margin-notes/demo.jsx +144 -0
  257. package/registry/blocks/margin-notes/index.js +20 -0
  258. package/registry/blocks/margin-notes/margin-model.js +224 -0
  259. package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
  260. package/registry/blocks/needs-you/README.md +177 -0
  261. package/registry/blocks/needs-you/block.json +34 -0
  262. package/registry/blocks/needs-you/demo.jsx +143 -0
  263. package/registry/blocks/needs-you/index.js +3 -0
  264. package/registry/blocks/needs-you/needs-you.jsx +441 -0
  265. package/registry/blocks/pace-bar/README.md +230 -0
  266. package/registry/blocks/pace-bar/block.json +10 -0
  267. package/registry/blocks/pace-bar/demo.jsx +255 -0
  268. package/registry/blocks/pace-bar/index.js +22 -0
  269. package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
  270. package/registry/blocks/pace-bar/pace-model.js +288 -0
  271. package/registry/blocks/record-grid/README.md +100 -0
  272. package/registry/blocks/record-grid/block.json +28 -0
  273. package/registry/blocks/record-grid/demo.jsx +43 -0
  274. package/registry/blocks/record-grid/index.js +3 -0
  275. package/registry/blocks/record-grid/record-grid.jsx +251 -0
  276. package/registry/blocks/run-history/README.md +138 -0
  277. package/registry/blocks/run-history/block.json +10 -0
  278. package/registry/blocks/run-history/demo.jsx +74 -0
  279. package/registry/blocks/run-history/index.js +3 -0
  280. package/registry/blocks/run-history/run-history.jsx +218 -0
  281. package/registry/blocks/run-view/README.md +174 -0
  282. package/registry/blocks/run-view/block.json +50 -0
  283. package/registry/blocks/run-view/demo.jsx +286 -0
  284. package/registry/blocks/run-view/index.js +5 -0
  285. package/registry/blocks/run-view/run-controls.jsx +266 -0
  286. package/registry/blocks/run-view/run-view.jsx +236 -0
  287. package/registry/blocks/run-view/runs-list.jsx +99 -0
  288. package/registry/blocks/schedule-card/README.md +121 -0
  289. package/registry/blocks/schedule-card/block.json +10 -0
  290. package/registry/blocks/schedule-card/demo.jsx +82 -0
  291. package/registry/blocks/schedule-card/index.js +3 -0
  292. package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
  293. package/registry/blocks/schedule-engine/README.md +137 -0
  294. package/registry/blocks/schedule-engine/block.json +10 -0
  295. package/registry/blocks/schedule-engine/demo.jsx +82 -0
  296. package/registry/blocks/schedule-engine/engine.js +769 -0
  297. package/registry/blocks/schedule-engine/index.js +9 -0
  298. package/registry/blocks/schedule-engine/ops.js +71 -0
  299. package/registry/blocks/schedule-engine/place.js +294 -0
  300. package/registry/blocks/schedule-engine/tree.js +79 -0
  301. package/registry/blocks/schedule-engine/waits.js +80 -0
  302. package/registry/blocks/schedule-gantt/README.md +159 -0
  303. package/registry/blocks/schedule-gantt/block.json +10 -0
  304. package/registry/blocks/schedule-gantt/demo.jsx +223 -0
  305. package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
  306. package/registry/blocks/schedule-gantt/geometry.js +176 -0
  307. package/registry/blocks/schedule-gantt/index.js +26 -0
  308. package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
  309. package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
  310. package/registry/blocks/schedule-gantt/track.jsx +190 -0
  311. package/registry/blocks/schedule-ruler/README.md +148 -0
  312. package/registry/blocks/schedule-ruler/block.json +10 -0
  313. package/registry/blocks/schedule-ruler/demo.jsx +181 -0
  314. package/registry/blocks/schedule-ruler/index.js +21 -0
  315. package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
  316. package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
  317. package/registry/blocks/source-receipt/README.md +97 -0
  318. package/registry/blocks/source-receipt/block.json +30 -0
  319. package/registry/blocks/source-receipt/demo.jsx +71 -0
  320. package/registry/blocks/source-receipt/index.js +3 -0
  321. package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
  322. package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
  323. package/registry/blocks/step-picker/README.md +78 -0
  324. package/registry/blocks/step-picker/block.json +31 -0
  325. package/registry/blocks/step-picker/demo.jsx +23 -0
  326. package/registry/blocks/step-picker/index.js +3 -0
  327. package/registry/blocks/step-picker/step-picker.jsx +144 -0
  328. package/registry/blocks/step-settings/README.md +100 -0
  329. package/registry/blocks/step-settings/block.json +44 -0
  330. package/registry/blocks/step-settings/demo.jsx +93 -0
  331. package/registry/blocks/step-settings/fields.jsx +58 -0
  332. package/registry/blocks/step-settings/flow-settings.jsx +115 -0
  333. package/registry/blocks/step-settings/index.js +5 -0
  334. package/registry/blocks/step-settings/step-settings.jsx +290 -0
  335. package/registry/blocks/suggestion/README.md +171 -0
  336. package/registry/blocks/suggestion/block.json +40 -0
  337. package/registry/blocks/suggestion/demo.jsx +91 -0
  338. package/registry/blocks/suggestion/index.js +3 -0
  339. package/registry/blocks/suggestion/suggestion.jsx +436 -0
  340. package/registry/blocks/sum-bar/README.md +122 -0
  341. package/registry/blocks/sum-bar/block.json +10 -0
  342. package/registry/blocks/sum-bar/demo.jsx +110 -0
  343. package/registry/blocks/sum-bar/index.js +5 -0
  344. package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
  345. package/registry/blocks/sum-bar/sum-model.js +126 -0
  346. package/registry/blocks/tier-map/README.md +217 -0
  347. package/registry/blocks/tier-map/block.json +10 -0
  348. package/registry/blocks/tier-map/demo.jsx +222 -0
  349. package/registry/blocks/tier-map/index.js +24 -0
  350. package/registry/blocks/tier-map/tier-map.jsx +574 -0
  351. package/registry/blocks/tier-map/tier-model.js +318 -0
  352. package/registry/blocks/time-grid/README.md +305 -0
  353. package/registry/blocks/time-grid/block.json +10 -0
  354. package/registry/blocks/time-grid/demo.jsx +279 -0
  355. package/registry/blocks/time-grid/grid-model.js +322 -0
  356. package/registry/blocks/time-grid/index.js +25 -0
  357. package/registry/blocks/time-grid/time-grid.jsx +1325 -0
  358. package/registry/blocks/time-strip/README.md +141 -0
  359. package/registry/blocks/time-strip/block.json +10 -0
  360. package/registry/blocks/time-strip/demo.jsx +129 -0
  361. package/registry/blocks/time-strip/index.js +19 -0
  362. package/registry/blocks/time-strip/strip-model.js +178 -0
  363. package/registry/blocks/time-strip/time-strip.jsx +260 -0
  364. package/registry/blocks/top-bar/README.md +149 -0
  365. package/registry/blocks/top-bar/block.json +35 -0
  366. package/registry/blocks/top-bar/demo.jsx +78 -0
  367. package/registry/blocks/top-bar/index.js +5 -0
  368. package/registry/blocks/top-bar/probe-rules.js +74 -0
  369. package/registry/blocks/top-bar/top-bar-model.js +152 -0
  370. package/registry/blocks/top-bar/top-bar.jsx +244 -0
  371. package/registry/blocks/trigger-picker/README.md +98 -0
  372. package/registry/blocks/trigger-picker/block.json +38 -0
  373. package/registry/blocks/trigger-picker/demo.jsx +59 -0
  374. package/registry/blocks/trigger-picker/index.js +3 -0
  375. package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
  376. package/registry/blocks/workflow-view/README.md +123 -0
  377. package/registry/blocks/workflow-view/block.json +10 -0
  378. package/registry/blocks/workflow-view/demo.jsx +90 -0
  379. package/registry/blocks/workflow-view/index.js +3 -0
  380. package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
  381. package/src/install.mjs +77 -16
  382. package/src/registry.mjs +3 -0
  383. package/templates/next/browser/lib/data.js +6 -1
  384. package/templates/next/files/README.md +627 -24
  385. package/templates/next/files/check/automation-format.ts +2103 -0
  386. package/templates/next/files/check/automation-package.ts +142 -0
  387. package/templates/next/files/check/run.mjs +968 -9
  388. package/templates/next/files/components/ui/field.jsx +17 -4
  389. package/templates/next/files/components/ui/icon.jsx +13 -0
  390. package/templates/next/files/components/ui/index.js +2 -1
  391. package/templates/next/files/components/ui/layout.jsx +77 -2
  392. package/templates/next/files/components/ui/notice.jsx +70 -0
  393. package/templates/next/files/components/ui/page.jsx +124 -0
  394. package/templates/next/files/components/ui/stat.jsx +22 -7
  395. package/templates/next/files/components/ui/toast.jsx +6 -3
  396. package/templates/next/files/lib/automations.js +660 -0
  397. package/templates/next/files/lib/claw.js +241 -5
  398. package/templates/next/files/lib/door.js +92 -0
  399. package/templates/next/files/lib/hooks.js +1677 -8
  400. package/templates/next/files/lib/platform.js +529 -21
  401. package/templates/next/template.json +13 -3
package/r/cart.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Cart",
6
6
  "description": "A shopping cart: line items with picture, option, quantity and remove, a summary (subtotal, discount code, shipping, tax, total) and Checkout. step=\"checkout\" is the checkout page: contact and delivery details beside the order summary, ending at onPay, which hands off to Paddle. No card fields. Empty and loading states built in.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -55,7 +55,7 @@
55
55
  "docs": "Installed to components/makersclaw/blocks/cart. Import: import { Cart } from \"@/components/makersclaw/blocks/cart\". Props and an example: components/makersclaw/blocks/cart/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
56
56
  "meta": {
57
57
  "makersclaw": {
58
- "version": "0.8.0",
58
+ "version": "0.8.2",
59
59
  "useWhen": "An app that sells things: the basket before paying, and the page that collects the buyer's email and address before the payment provider takes over. Not for a subscription plan picker (a pricing table) or for an order already placed (order-detail)."
60
60
  }
61
61
  }
package/r/chart.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Chart",
6
6
  "description": "Line, area (stacked), bar (grouped or stacked, columns or bars, value labels at the ends, a track behind), a muted or dashed series for a previous period to compare against, pie and donut charts, sparklines and heatmaps (a grid shaded by value, with a scale legend), drawn in SVG (the heatmap as an HTML table) with the workspace's chart colours: round ticks, gridlines, a tooltip and crosshair under the pointer or the arrow keys, a legend that hides a series, and a data table for screen readers.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -76,7 +76,7 @@
76
76
  "docs": "Installed to components/makersclaw/blocks/chart. Import: import { LineChart, AreaChart, BarChart, PieChart, Sparkline, ChartLegend, HeatmapChart, ScaleLegend, scaleStep, scaleShade } from \"@/components/makersclaw/blocks/chart\". Props and an example: components/makersclaw/blocks/chart/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
77
77
  "meta": {
78
78
  "makersclaw": {
79
- "version": "0.8.0",
79
+ "version": "0.8.2",
80
80
  "useWhen": "Numbers the founder should see as a shape: revenue by month, deals by stage, pipeline by source, a KPI's trend. Not for a single figure with no history (a stat), exact lookups (a table), or records on a timeline (a calendar)."
81
81
  }
82
82
  }
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "chat-pane",
4
+ "type": "registry:block",
5
+ "title": "ChatPane",
6
+ "description": "The desk's chat pane as the app's own, in the app's theme: the chrome around a conversation. A head (title, New chat, Chats, close) with the past chats dropping over the chat; before the first ask one line, the recent chats and the suggestions; the conversation, kept at the newest words until the member scrolls up, with a jump to latest; a bar for what waits on the member; and the desk's composer: ONE rounded box with the words on top and under them a row, + (attach) at the left and the microphone and a round arrow to send at the right, the arrow a stop while a turn runs, a quiet hint under the box. Files by + or paste or drop, dictation, the box closed while something waits. A starting line handed down by the dock (openAgent) is sent as the member's first words in a fresh chat, or put in the box. It draws what useAgentPane() returns (lib/hooks.js) and decides nothing about the lines of the conversation: those are its children (agent-pane draws the agent's).",
7
+ "registryDependencies": [
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
+ ],
10
+ "dependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "registry/blocks/chat-pane/index.js",
14
+ "type": "registry:component",
15
+ "target": "@components/makersclaw/blocks/chat-pane/index.js",
16
+ "content": "// The ChatPane block — `makersclaw add chat-pane`. Fixed: compose it\n// through its props (README.md beside this file). The model's helpers are\n// plain functions; they work on the server too.\nexport { ChatPane } from \"./chat-pane\";\nexport { ChatComposer } from \"./chat-composer\";\nexport { ChatsList } from \"./chats-list\";\nexport { ChatDockContext } from \"./chat-context\";\nexport { ago, startStep } from \"./chat-model\";\nexport { MAX_BYTES, MAX_FILES, addFiles, filesFromTransfer, formatBytes, readFile, useDictation } from \"./chat-attach\";\n"
17
+ },
18
+ {
19
+ "path": "registry/blocks/chat-pane/chat-pane.jsx",
20
+ "type": "registry:component",
21
+ "target": "@components/makersclaw/blocks/chat-pane/chat-pane.jsx",
22
+ "content": "\"use client\";\n\nimport { useContext, useEffect, useRef, useState } from \"react\";\nimport { Button, Icon, IconButton, Spinner, cx } from \"../../ui\";\nimport { addFiles, filesFromTransfer, readFile, useDictation } from \"./chat-attach\";\nimport { ChatDockContext } from \"./chat-context\";\nimport { ChatComposer } from \"./chat-composer\";\nimport { ChatsList } from \"./chats-list\";\nimport { startStep } from \"./chat-model\";\n\n// ChatPane — the desk's chat pane (claw's web/src/os/windows/artifact/\n// ChatPane.tsx and the composer of its EveChat), as the app's own, in the\n// app's theme (0.8.1, X5; the founder: \"add claw's ChatPane as a block in\n// the makersclaw template\"). It is the chrome around a conversation:\n//\n// head the title, New chat, Chats (the list drops over the chat,\n// the desk's column mode), close\n// empty before the first ask: one line, the past chats, the\n// suggestions\n// scroll the conversation, which stays at the newest words until the\n// member scrolls up, with a jump to latest\n// bar what waits on the member, or says the agent is working\n// composer ONE box: the words on top; under them + (attach) at the\n// left, the microphone and a round arrow at the right, the\n// arrow a stop while a turn runs; a quiet hint under it\n//\n// It is not the conversation's lines: those are the children. agent-pane\n// draws the agent's (the plan, the work folded, the questions, what it\n// made); any lines will do. Nothing here asks the agent anything: `chat`\n// is an object of the shape useAgentPane() returns (lib/hooks.js), and the\n// pane calls its send, stop, newChat and openSession.\n//\n// A \"have the agent do it\" button reaches the pane through the dock:\n// openAgent({ ask }) (blocks/agent-dock) hands the pane a starting line,\n// which it sends as the member's first words in a fresh chat (or puts in\n// the box). The agent asks only for what it is missing, in the chat.\n//\n// chat the object useAgentPane() returns (required)\n// title the head's words, and the box's label (default \"Ask the agent\")\n// suggestions asks that fit the moment, before the first ask\n// empty one line before the first ask\n// placeholder the box's words before the first ask; once there is a\n// conversation it says \"Ask a follow-up…\" (D29) unless\n// `chat.sameConversation` is false (the next message starts a\n// new request, and the box keeps the app's own words)\n// closedWords the box's words while `blocked` (\"Answer above first\")\n// hint a quiet line under the box\n// recent past chats listed before the first ask (default 5; 0 none)\n// bare no head (inside a Sheet or a Card that has its own)\n// fill take the container's height (default); false: compact,\n// the conversation scrolls inside a capped height\n// onClose a close in the head; inside an AgentDock it is the dock's\n// started there is a conversation to draw (else the empty state)\n// busy a turn runs: the arrow is a stop while the box is empty\n// blocked something waits on the member: the whole box is closed\n// bar a node between the conversation and the box\n// children the conversation's lines\n// className layout around it. Never colour.\nexport function ChatPane({\n chat,\n title = \"Ask the agent\",\n suggestions = [],\n empty,\n placeholder,\n closedWords = \"Not available right now\",\n hint,\n recent = 5,\n bare = false,\n fill = true,\n onClose: onCloseProp,\n started = false,\n busy = false,\n blocked = false,\n bar,\n className,\n children,\n}) {\n const dock = useContext(ChatDockContext);\n const onClose = onCloseProp ?? dock?.close;\n const [text, setText] = useState(\"\");\n const [listOpen, setListOpen] = useState(false);\n const [stopping, setStopping] = useState(false);\n const [atBottom, setAtBottom] = useState(true);\n const [files, setFiles] = useState([]);\n const [attachError, setAttachError] = useState(null);\n const [encoding, setEncoding] = useState(false);\n const [dragOver, setDragOver] = useState(false);\n const fileInput = useRef(null);\n const textarea = useRef(null);\n const dragDepth = useRef(0);\n const listRef = useRef(null);\n const rootRef = useRef(null);\n const stick = useRef(true);\n const chatRef = useRef(chat);\n chatRef.current = chat;\n\n const chats = chat?.chats && !chat.chats.unavailable ? chat.chats : null;\n const canNew = typeof chat?.newChat === \"function\";\n const canStop = busy && !blocked && Boolean(chat?.request?.id) && Boolean(chat?.stop);\n\n useEffect(() => {\n if (!busy) setStopping(false);\n }, [busy]);\n\n // Dictation adds finished phrases to the box.\n const dictation = useDictation((words) => setText((t) => (t.trim() ? `${t.trimEnd()} ${words}` : words)));\n\n const attach = (incoming) => {\n const r = addFiles(files, incoming);\n setFiles(r.files);\n setAttachError(r.error);\n };\n\n // The Chats list closes on a press outside it and on Escape.\n useEffect(() => {\n if (!listOpen) return undefined;\n const onDown = (e) => {\n if (!rootRef.current?.querySelector(\"[data-chats]\")?.contains(e.target) && !e.target.closest?.(\"[data-chats-toggle]\")) setListOpen(false);\n };\n const onKey = (e) => {\n if (e.key === \"Escape\") setListOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n document.addEventListener(\"keydown\", onKey);\n return () => {\n document.removeEventListener(\"mousedown\", onDown);\n document.removeEventListener(\"keydown\", onKey);\n };\n }, [listOpen]);\n\n // The newest words stay in view while the agent writes — until the\n // member scrolls up to read, which stops it following.\n useEffect(() => {\n const el = listRef.current;\n if (el && stick.current) el.scrollTop = el.scrollHeight;\n }, [chat?.view, chat?.turns, chat?.pending, chat?.state, chat?.needs?.at, started]);\n const onScroll = () => {\n const el = listRef.current;\n if (!el) return;\n const near = el.scrollHeight - el.scrollTop - el.clientHeight < 48;\n stick.current = near;\n setAtBottom(near);\n };\n const jump = () => {\n const el = listRef.current;\n if (el) el.scrollTo({ top: el.scrollHeight, behavior: \"smooth\" });\n };\n\n const submit = async (words) => {\n const typed = String(words ?? text).trim();\n const sending = words === undefined ? files : [];\n // Files without words still go: the agent is told what they are.\n const t = typed || (sending.length ? `Attached: ${sending.map((f) => f.name).join(\", \")}` : \"\");\n if (!t || !chat?.send || encoding) return;\n dictation.stop();\n stick.current = true;\n if (sending.length === 0) {\n setText(\"\");\n chat.send(t);\n return;\n }\n setEncoding(true);\n try {\n const read = await Promise.all(sending.map(readFile));\n setText(\"\");\n setFiles([]);\n setAttachError(null);\n chat.send(t, { files: read });\n } catch {\n setAttachError(\"A file could not be read. Try attaching it again.\");\n } finally {\n setEncoding(false);\n }\n };\n const stop = () => {\n if (stopping || !chat?.stop) return;\n setStopping(true);\n chat.stop();\n };\n\n // A starting line from the dock (openAgent). Fresh: a new conversation\n // first, then the line sent as its first words, so the agent asks only\n // for what it is missing. Or prefilled for the member to finish. The pane\n // tells the dock it took it; a pane that has not mounted yet takes it when\n // it does.\n const start = dock?.start ?? null;\n const takenRef = useRef(null);\n const newedRef = useRef(null);\n useEffect(() => {\n if (!start) return;\n const step = startStep({\n start,\n handled: takenRef.current,\n sessionId: chat?.sessionId ?? null,\n blocked,\n canNew,\n newed: newedRef.current === start.nonce,\n });\n if (step === \"none\" || step === \"wait\") return;\n if (step === \"new-chat\") {\n newedRef.current = start.nonce;\n setListOpen(false);\n chatRef.current?.newChat?.();\n return;\n }\n takenRef.current = start.nonce;\n if (step === \"send\") {\n stick.current = true;\n chatRef.current?.send?.(String(start.ask).trim());\n } else {\n // Words already in the box are the member's: the line goes under them.\n setText((t) => (t.trim() ? `${t.trimEnd()}\\n${start.ask}` : String(start.ask)));\n requestAnimationFrame(() => {\n const el = textarea.current;\n if (!el || el.disabled) return;\n el.focus({ preventScroll: true });\n el.setSelectionRange(el.value.length, el.value.length);\n });\n }\n dock?.taken?.(start.nonce);\n }, [start, chat?.sessionId, blocked, canNew, dock]);\n\n const followUp = started && chat?.sameConversation !== false;\n const words = blocked ? closedWords : followUp ? \"Ask a follow-up…\" : (placeholder ?? \"Ask anything…\");\n\n return (\n <div\n ref={rootRef}\n className={cx(\"@container relative flex min-w-0 flex-col bg-background text-sm text-foreground\", fill && \"h-full min-h-0\", className)}\n onDragEnter={(e) => {\n if (!e.dataTransfer?.types?.includes(\"Files\") || blocked) return;\n e.preventDefault();\n dragDepth.current += 1;\n setDragOver(true);\n }}\n onDragLeave={(e) => {\n if (!e.dataTransfer?.types?.includes(\"Files\")) return;\n dragDepth.current = Math.max(0, dragDepth.current - 1);\n if (dragDepth.current === 0) setDragOver(false);\n }}\n onDragOver={(e) => {\n if (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n }}\n onDrop={(e) => {\n if (!e.dataTransfer?.types?.includes(\"Files\")) return;\n e.preventDefault();\n dragDepth.current = 0;\n setDragOver(false);\n if (!blocked) attach(filesFromTransfer(e.dataTransfer));\n }}\n >\n {dragOver ? (\n <div className=\"pointer-events-none absolute inset-0 z-20 grid place-items-center rounded-[var(--radius)] border-2 border-dashed border-primary bg-background/80 text-sm font-medium\">\n Drop files to attach\n </div>\n ) : null}\n {!bare ? (\n <div className=\"flex h-10 shrink-0 items-center gap-1 border-b border-border pl-3.5 pr-2 font-semibold\">\n <Icon name=\"sparkles\" size={16} className=\"shrink-0 text-primary\" />\n <span className=\"min-w-0 flex-1 truncate\">{title}</span>\n {canNew ? (\n <IconButton\n icon=\"plus\"\n size=\"sm\"\n label=\"New chat\"\n onClick={() => {\n setListOpen(false);\n chat.newChat();\n }}\n />\n ) : null}\n {chats ? (\n <Button size=\"sm\" variant=\"quiet\" data-chats-toggle=\"\" aria-expanded={listOpen} aria-label=\"Chats\" onClick={() => setListOpen((v) => !v)}>\n <Icon name=\"history\" size={14} />\n <span className=\"hidden @[16rem]:inline\">Chats</span>\n {chats.items.length > 0 ? <span className=\"text-xs text-muted-foreground\">{chats.items.length}</span> : null}\n </Button>\n ) : null}\n {onClose ? <IconButton icon=\"x\" size=\"sm\" label=\"Close\" onClick={onClose} /> : null}\n </div>\n ) : null}\n\n {listOpen && chats ? (\n <div data-chats=\"\" className=\"absolute inset-x-0 z-10 max-h-72 overflow-y-auto border-b border-border bg-popover text-popover-foreground shadow-md\" style={{ top: bare ? 0 : \"2.5rem\" }}>\n <ChatsList\n chats={chats}\n currentId={chat?.sessionId}\n onPick={(id) => {\n setListOpen(false);\n chat.openSession?.(id);\n }}\n />\n </div>\n ) : null}\n\n <div className={cx(\"relative\", fill ? \"min-h-0 flex-1\" : \"\")}>\n <div\n ref={listRef}\n onScroll={onScroll}\n role=\"log\"\n aria-live=\"polite\"\n aria-label={title}\n className={cx(\"grid min-w-0 content-start gap-3 overflow-y-auto overscroll-contain p-3.5\", fill ? \"h-full min-h-0\" : started ? \"max-h-96\" : \"\")}\n >\n {started ? (\n children\n ) : (\n <>\n {empty ? <p className=\"text-muted-foreground\">{empty}</p> : null}\n {chat?.loading ? (\n <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Loading\" /> Opening the chat…\n </div>\n ) : null}\n {chats && recent > 0 && !chat?.loading ? (\n <section aria-label=\"Recent chats\" className=\"grid min-w-0 gap-0.5\">\n <h3 className=\"text-xs font-medium text-muted-foreground\">Recent chats</h3>\n <div className=\"-mx-3.5\">\n <ChatsList chats={{ ...chats, items: chats.items.slice(0, recent) }} currentId={chat?.sessionId} onPick={(id) => chat.openSession?.(id)} />\n </div>\n {chats.items.length > recent ? (\n <Button variant=\"link\" size=\"inherit\" className=\"justify-self-start text-xs\" onClick={() => setListOpen(true)}>\n All {chats.items.length} chats\n </Button>\n ) : null}\n </section>\n ) : null}\n {suggestions.length ? (\n <div className=\"flex min-w-0 flex-wrap gap-1.5\">\n {suggestions.map((s) => (\n <button\n key={s}\n type=\"button\"\n onClick={() => submit(s)}\n className=\"flex max-w-full min-w-0 items-start gap-1.5 rounded-[var(--radius)] border border-border px-2.5 py-1 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring\"\n >\n <Icon name=\"sparkles\" size={12} className=\"mt-0.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 break-words\">{s}</span>\n </button>\n ))}\n </div>\n ) : null}\n </>\n )}\n </div>\n {!atBottom && started ? (\n <IconButton icon=\"arrow-down\" size=\"sm\" label=\"Jump to latest\" onClick={jump} className=\"absolute bottom-2 right-3 shadow-md\" />\n ) : null}\n </div>\n\n {bar}\n {chat?.error ? <p className=\"shrink-0 px-3.5 pt-2 text-xs text-destructive\">{chat.error}</p> : null}\n {chat?.notice ? (\n <p role=\"status\" className=\"shrink-0 px-3.5 pt-2 text-xs text-muted-foreground\">\n {chat.notice}\n </p>\n ) : null}\n {chat?.unavailable ? (\n <p className=\"shrink-0 px-3.5 pt-2 text-xs text-muted-foreground\">\n This chat needs a newer Makersclaw.{\" \"}\n {chat.request ? (\n <Button variant=\"link\" size=\"inherit\" onClick={() => chat.openOnDesk?.()}>\n Open it on the desk\n </Button>\n ) : null}\n </p>\n ) : null}\n {attachError || dictation.error ? (\n <p role=\"alert\" className=\"shrink-0 px-3.5 pt-2 text-xs text-destructive\">\n {attachError ?? dictation.error}\n </p>\n ) : null}\n\n <ChatComposer\n className=\"shrink-0 px-3 pb-3 pt-2\"\n label={title}\n value={text}\n onChange={setText}\n onSubmit={() => submit()}\n onStop={stop}\n onAttach={attach}\n files={files}\n onRemoveFile={(i) => setFiles((all) => all.filter((_, j) => j !== i))}\n busy={busy}\n canStop={canStop}\n stopping={stopping}\n blocked={blocked}\n sending={Boolean(chat?.sending)}\n encoding={encoding}\n dictation={dictation}\n placeholder={words}\n hint={hint}\n textareaRef={textarea}\n fileInputRef={fileInput}\n />\n </div>\n );\n}\n"
23
+ },
24
+ {
25
+ "path": "registry/blocks/chat-pane/chat-composer.jsx",
26
+ "type": "registry:component",
27
+ "target": "@components/makersclaw/blocks/chat-pane/chat-composer.jsx",
28
+ "content": "\"use client\";\n\nimport { useEffect } from \"react\";\nimport { Icon, cx } from \"../../ui\";\nimport { filesFromTransfer, formatBytes } from \"./chat-attach\";\n\n// ChatComposer — the desk's message box (claw's ChatComposer, as the app\n// draws it): ONE rounded box. The words on top, full width, growing with\n// what is typed; under them a row with + (attach) at the left, and at the\n// right the microphone and a round arrow to send. While a turn runs, a round\n// stop takes the arrow's place. A quiet hint can sit under the box.\n//\n// It draws the box and nothing else: the words, the files and the dictation\n// are the pane's (so a drop anywhere on the pane, a prefilled line and a\n// sent suggestion all reach the same box).\n//\n// value, onChange the words (controlled)\n// onSubmit() Enter, or the arrow\n// onStop() the stop (shown while `busy` and `canStop`)\n// onAttach(File[]) the + and a paste\n// files, onRemoveFile(i) what is attached, as chips inside the box\n// busy a turn runs: the arrow is a stop while the box is empty\n// canStop, stopping the stop can be pressed / has been\n// blocked something waits on the member: the whole box is closed\n// sending, encoding the arrow waits\n// dictation useDictation()'s object (no microphone where the\n// browser has none)\n// placeholder, label the box's words, and its name for a screen reader\n// hint a quiet line under the box\n// textareaRef the pane's handle on the box (focus, caret)\n\nconst ROUND = \"inline-flex size-8 shrink-0 items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-card disabled:cursor-not-allowed disabled:opacity-40\";\n\nexport function ChatComposer({\n value,\n onChange,\n onSubmit,\n onStop,\n onAttach,\n files = [],\n onRemoveFile,\n busy = false,\n canStop = false,\n stopping = false,\n blocked = false,\n sending = false,\n encoding = false,\n dictation,\n placeholder,\n label = \"Message\",\n hint,\n textareaRef,\n fileInputRef,\n className,\n}) {\n const empty = !value.trim() && files.length === 0;\n const stopShown = busy && canStop && empty;\n\n // The box grows with its words, up to its max-h; a hidden box measures 0\n // and is left natural (pinning 0px would stick).\n useEffect(() => {\n const el = textareaRef?.current;\n if (!el) return;\n el.style.height = \"auto\";\n if (el.scrollHeight > 0) el.style.height = `${el.scrollHeight}px`;\n }, [value, textareaRef]);\n\n return (\n <div className={cx(\"grid min-w-0 gap-1.5\", className)}>\n <form\n data-chat-composer=\"\"\n className=\"grid min-w-0 gap-2 rounded-xl border border-border bg-card px-3 pb-2 pt-2.5 text-card-foreground shadow-sm transition-colors focus-within:border-ring motion-reduce:transition-none\"\n onSubmit={(e) => {\n e.preventDefault();\n onSubmit?.();\n }}\n >\n {files.length > 0 ? (\n <ul aria-label=\"Files to send\" className=\"flex min-w-0 flex-wrap gap-1.5\">\n {files.map((f, i) => (\n <li key={`${f.name}-${i}`} className=\"inline-flex max-w-full min-w-0 items-center gap-1.5 rounded-md border border-border py-0.5 pl-2 pr-1 text-xs\">\n <Icon name=\"paperclip\" size={12} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">{f.name}</span>\n <span className=\"shrink-0 text-muted-foreground\">{formatBytes(f.size)}</span>\n <button\n type=\"button\"\n aria-label={`Remove ${f.name}`}\n title={`Remove ${f.name}`}\n onClick={() => onRemoveFile?.(i)}\n className=\"inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon name=\"x\" size={12} />\n </button>\n </li>\n ))}\n </ul>\n ) : null}\n\n <textarea\n ref={textareaRef}\n aria-label={label}\n rows={1}\n placeholder={placeholder}\n value={value}\n disabled={blocked}\n onChange={(e) => onChange?.(e.target.value)}\n onPaste={(e) => {\n const pasted = filesFromTransfer(e.clipboardData);\n if (pasted.length > 0) {\n e.preventDefault();\n onAttach?.(pasted);\n }\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.nativeEvent.isComposing) {\n e.preventDefault();\n onSubmit?.();\n }\n }}\n className=\"block max-h-40 min-h-6 w-full min-w-0 resize-none overflow-y-auto bg-transparent text-sm leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed\"\n />\n\n {dictation?.listening ? (\n <p role=\"status\" className=\"flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground\">\n <span aria-hidden=\"true\" className=\"size-1.5 shrink-0 animate-pulse rounded-full bg-destructive motion-reduce:animate-none\" />\n Listening…\n {dictation.interim ? <span className=\"min-w-0 truncate italic\">{dictation.interim}</span> : null}\n </p>\n ) : null}\n\n <div className=\"flex items-center gap-1.5\">\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n hidden\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={(e) => {\n onAttach?.(Array.from(e.target.files ?? []));\n e.target.value = \"\";\n }}\n />\n <button\n type=\"button\"\n aria-label=\"Attach a file\"\n title=\"Attach a file\"\n disabled={blocked || encoding}\n onClick={() => fileInputRef?.current?.click()}\n className={cx(ROUND, \"rounded-lg border border-border text-muted-foreground hover:bg-accent hover:text-foreground\")}\n >\n <Icon name=\"plus\" size={16} />\n </button>\n <span className=\"flex-1\" />\n {dictation?.supported ? (\n <button\n type=\"button\"\n aria-label={dictation.listening ? \"Stop dictating\" : \"Dictate\"}\n title={dictation.listening ? \"Stop dictating\" : \"Dictate\"}\n aria-pressed={dictation.listening}\n disabled={blocked}\n onClick={dictation.toggle}\n className={cx(ROUND, \"rounded-lg hover:bg-accent\", dictation.listening ? \"text-destructive\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon name=\"mic\" size={16} />\n </button>\n ) : null}\n {stopShown ? (\n <button\n type=\"button\"\n aria-label={stopping ? \"Stopping…\" : \"Stop\"}\n title={stopping ? \"Stopping…\" : \"Stop\"}\n disabled={stopping}\n onClick={onStop}\n className={cx(ROUND, \"rounded-full bg-primary text-primary-foreground hover:opacity-90\")}\n >\n <Icon name=\"stop\" size={12} />\n </button>\n ) : (\n <button\n type=\"submit\"\n aria-label=\"Send\"\n title=\"Send\"\n disabled={blocked || encoding || sending || empty}\n className={cx(ROUND, \"rounded-full bg-primary text-primary-foreground hover:opacity-90\")}\n >\n <Icon name=\"arrow-up\" size={16} />\n </button>\n )}\n </div>\n </form>\n {hint ? <p className=\"px-1 text-xs text-muted-foreground\">{hint}</p> : null}\n </div>\n );\n}\n"
29
+ },
30
+ {
31
+ "path": "registry/blocks/chat-pane/chats-list.jsx",
32
+ "type": "registry:component",
33
+ "target": "@components/makersclaw/blocks/chat-pane/chats-list.jsx",
34
+ "content": "\"use client\";\n\nimport { cx } from \"../../ui\";\nimport { ago } from \"./chat-model\";\n\n/**\n * The member's past chats, newest first, one line each: the title, a dot on\n * one that needs them (warning) or is working (info), and its age. The\n * pane's Chats overlay and its empty state both draw this.\n *\n * chats { items: [{ sessionId, title, mark: \"needs\" | \"working\" | null, at }],\n * loading, error }\n * currentId the open chat's session\n * onPick (sessionId) => void\n */\nexport function ChatsList({ chats, currentId, onPick }) {\n if (chats.items.length === 0)\n return (\n <p className=\"px-3.5 py-3 text-xs text-muted-foreground\">{chats.loading ? \"Loading chats…\" : chats.error ? \"The chats did not load.\" : \"No chats yet. Your first message starts one.\"}</p>\n );\n return (\n <nav aria-label=\"Past chats\" className=\"grid\">\n {chats.items.map((c) => (\n <button\n key={c.sessionId}\n type=\"button\"\n aria-current={c.sessionId === currentId ? \"true\" : undefined}\n onClick={() => onPick(c.sessionId)}\n className={cx(\n \"flex min-w-0 items-center gap-2 px-3.5 py-2 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring\",\n c.sessionId === currentId && \"bg-muted font-medium\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\" title={c.title}>\n {c.title}\n </span>\n {c.mark ? (\n <span\n role=\"img\"\n aria-label={c.mark === \"needs\" ? \"Needs you\" : \"Working\"}\n title={c.mark === \"needs\" ? \"Needs you\" : \"Working\"}\n className={cx(\"size-2 shrink-0 rounded-full\", c.mark === \"needs\" ? \"bg-warning\" : \"animate-pulse bg-info\")}\n />\n ) : null}\n <time className=\"shrink-0 text-muted-foreground\" dateTime={c.at ?? undefined}>\n {ago(c.at)}\n </time>\n </button>\n ))}\n </nav>\n );\n}\n"
35
+ },
36
+ {
37
+ "path": "registry/blocks/chat-pane/chat-context.js",
38
+ "type": "registry:component",
39
+ "target": "@components/makersclaw/blocks/chat-pane/chat-context.js",
40
+ "content": "\"use client\";\n\nimport { createContext } from \"react\";\n\n// What a ChatPane placed inside an AgentDock (blocks/agent-dock) is told:\n//\n// close folds the dock (the head's close, when the app gave no onClose)\n// mode \"column\" or \"sheet\"\n// start the starting line a \"have the agent do it\" button asked for\n// (openAgent({ ask }), blocks/agent-dock), or null:\n// { ask, send, fresh, nonce }\n// taken (nonce) => void — the pane took it; the dock drops it\n//\n// Null outside a dock, where nothing changes. It lives here, beside the\n// pane, so the pane never imports the dock; agent-pane re-exports it as\n// AgentDockContext, which is the name the dock and apps have always used.\nexport const ChatDockContext = createContext(null);\n"
41
+ },
42
+ {
43
+ "path": "registry/blocks/chat-pane/chat-model.js",
44
+ "type": "registry:component",
45
+ "target": "@components/makersclaw/blocks/chat-pane/chat-model.js",
46
+ "content": "// chat-pane: the rules that need no React, as plain functions (they work on\n// the server too, and test/chat-pane.test.mjs holds them).\n\n/** \"now\", \"5m\", \"3h\", \"2d\", else the date: a past chat's age. */\nexport function ago(iso, now = Date.now()) {\n const t = iso ? Date.parse(iso) : Number.NaN;\n if (!Number.isFinite(t)) return \"\";\n const s = Math.max(0, Math.round((now - t) / 1000));\n if (s < 60) return \"now\";\n if (s < 3600) return `${Math.floor(s / 60)}m`;\n if (s < 86400) return `${Math.floor(s / 3600)}h`;\n if (s < 7 * 86400) return `${Math.floor(s / 86400)}d`;\n return new Date(t).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" });\n}\n\n/**\n * What the pane does next with a starting line the dock holds (openAgent in\n * blocks/agent-dock). A \"have the agent do it\" button does not open a form:\n * it hands the agent its first line, and the agent asks only for what it is\n * missing, in the chat.\n *\n * start { ask, send, fresh, nonce } or null\n * handled the nonce this pane already took, or null\n * sessionId the conversation open now (null: a fresh one)\n * blocked the box is closed (something waits on the member)\n * canNew the chat can start a new conversation (chat.newChat)\n * newed newChat was already called for this nonce\n *\n * → \"none\" nothing to do\n * \"new-chat\" start a fresh conversation first (fresh, and one is open)\n * \"wait\" the fresh conversation has not replaced the old one yet\n * \"send\" send the line as the member's first words\n * \"prefill\" put the line in the box for the member to finish (send is\n * false, or the box is closed and cannot send)\n */\nexport function startStep({ start, handled = null, sessionId = null, blocked = false, canNew = true, newed = false } = {}) {\n if (!start || !String(start.ask ?? \"\").trim()) return \"none\";\n if (handled !== null && handled === start.nonce) return \"none\";\n if (start.send === false || blocked) return \"prefill\";\n const fresh = start.fresh !== false && canNew;\n if (fresh && sessionId) return newed ? \"wait\" : \"new-chat\";\n return \"send\";\n}\n"
47
+ },
48
+ {
49
+ "path": "registry/blocks/chat-pane/chat-attach.js",
50
+ "type": "registry:component",
51
+ "target": "@components/makersclaw/blocks/chat-pane/chat-attach.js",
52
+ "content": "\"use client\";\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\n\n// The composer's parts (0.8.1, D27 phase 4): files to send with the words,\n// and dictation. Both are the browser's own — nothing here reads a door. The\n// platform checks the files again (agent-app app-attachments.ts) and the\n// limits below are its. They lived in agent-pane/composer.jsx until the\n// chat-pane block (X5); that file now re-exports them from here.\n\n/** At most this many files, and this many bytes together, per message. */\nexport const MAX_FILES = 4;\nexport const MAX_BYTES = 3 * 1024 * 1024;\n\nexport function formatBytes(bytes) {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Add `incoming` files to `current`, within the limits.\n * → { files, error } `error` is words for the member, or null\n */\nexport function addFiles(current, incoming) {\n const files = [...current];\n let error = null;\n let total = files.reduce((n, f) => n + f.size, 0);\n for (const file of incoming) {\n if (files.length >= MAX_FILES) {\n error = `Send at most ${MAX_FILES} files at a time.`;\n break;\n }\n if (file.size === 0) {\n error = `${file.name} is empty.`;\n continue;\n }\n if (total + file.size > MAX_BYTES) {\n error = `Files can be up to ${formatBytes(MAX_BYTES)} together here. Attach a smaller one, or attach it in the Makersclaw window.`;\n continue;\n }\n total += file.size;\n files.push(file);\n }\n return { files, error };\n}\n\n/** A File as the ask takes it: { name, type, data } with a base64 data URL. */\nexport function readFile(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve({ name: file.name, type: file.type || \"application/octet-stream\", data: String(reader.result) });\n reader.onerror = () => reject(reader.error ?? new Error(\"read failed\"));\n reader.readAsDataURL(file);\n });\n}\n\n/** Files off a paste or a drop. A pasted image is named \"image.png\" by the\n * browser; stamp it so several stay apart in the thread. */\nexport function filesFromTransfer(dt) {\n if (!dt) return [];\n const out = [];\n for (const item of Array.from(dt.items ?? [])) {\n if (item.kind !== \"file\") continue;\n const file = item.getAsFile();\n if (!file) continue;\n out.push(\n file.name === \"image.png\" && file.type.startsWith(\"image/\")\n ? new File([file], `pasted-${new Date().toISOString().replace(/[:.]/g, \"-\")}.png`, { type: file.type })\n : file,\n );\n }\n return out;\n}\n\nfunction recognitionCtor() {\n if (typeof window === \"undefined\") return null;\n return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null;\n}\n\n/**\n * Dictation: the microphone to text through the browser's speech\n * recognition (the desk's composer does the same). `onText(words)` hears\n * each finished phrase. `supported` is false where the browser has none,\n * and the pane draws no microphone. `error` says when the microphone is\n * refused (a frame the desk has not allowed it in, or the member said no).\n */\nexport function useDictation(onText) {\n const [supported] = useState(() => recognitionCtor() !== null);\n const [listening, setListening] = useState(false);\n const [interim, setInterim] = useState(\"\");\n const [error, setError] = useState(null);\n const recRef = useRef(null);\n const wantRef = useRef(false);\n const onTextRef = useRef(onText);\n useLayoutEffect(() => {\n onTextRef.current = onText;\n }, [onText]);\n\n const stop = useCallback(() => {\n wantRef.current = false;\n recRef.current?.stop();\n recRef.current = null;\n setListening(false);\n setInterim(\"\");\n }, []);\n\n const start = useCallback(() => {\n const Ctor = recognitionCtor();\n if (!Ctor || recRef.current) return;\n setError(null);\n const rec = new Ctor();\n rec.continuous = true;\n rec.interimResults = true;\n rec.lang = (typeof navigator !== \"undefined\" && navigator.language) || \"en-US\";\n rec.onresult = (event) => {\n let interimText = \"\";\n for (let i = event.resultIndex; i < event.results.length; i += 1) {\n const result = event.results[i];\n const transcript = result[0]?.transcript ?? \"\";\n if (result.isFinal) {\n if (transcript.trim()) onTextRef.current(transcript.trim());\n } else {\n interimText += transcript;\n }\n }\n setInterim(interimText);\n };\n rec.onend = () => {\n recRef.current = null;\n setInterim(\"\");\n if (wantRef.current) {\n // Silence ends a recognition; while the toggle is on, listen again.\n try {\n rec.start();\n recRef.current = rec;\n return;\n } catch {\n wantRef.current = false;\n }\n }\n setListening(false);\n };\n rec.onerror = (event) => {\n if (event.error === \"not-allowed\" || event.error === \"service-not-allowed\") {\n wantRef.current = false;\n setListening(false);\n setError(\"The microphone is not allowed here. Allow it for this site, or type instead.\");\n }\n };\n wantRef.current = true;\n try {\n rec.start();\n } catch {\n wantRef.current = false;\n setError(\"The microphone could not start.\");\n return;\n }\n recRef.current = rec;\n setListening(true);\n }, []);\n\n const toggle = useCallback(() => (listening ? stop() : start()), [listening, start, stop]);\n useEffect(() => stop, [stop]);\n return { supported, listening, interim, error, toggle, stop };\n}\n"
53
+ },
54
+ {
55
+ "path": "registry/blocks/chat-pane/README.md",
56
+ "type": "registry:file",
57
+ "target": "@components/makersclaw/blocks/chat-pane/README.md",
58
+ "content": "# ChatPane\n\nThe desk's chat pane, as the app's own, in the app's theme (0.8.1). The\nfounder, after reviewing the app agent's message box next to the\nAutomations window's: \"add claw's ChatPane as a block in the makersclaw\ntemplate\". This is that port: claw's `web/src/os/windows/artifact/ChatPane`\nand the composer of its `EveChat`, in JSX, with the kit's `Icon` and\n`Button` and the app's tokens, talking to the agent through the app's own\nhook (`useAgentPane`, `lib/hooks.js`) instead of `eve/client`.\n\nIt is the **chrome around a conversation**:\n\n- **The head**: the title, **New chat**, **Chats** (the past chats drop over\n the conversation, newest first, with a dot on one that needs you or is\n working), and a close.\n- **The empty state**: before the first ask, one line, the recent chats\n (\"No chats yet. Your first message starts one.\" when there are none), and\n the app's suggestions.\n- **The conversation**: its lines are the children. It stays at the newest\n words until the member scrolls up, then offers a jump to latest.\n- **The bar** between the conversation and the box: what waits on the member,\n or that the agent is working.\n- **The message box**: the desk's composer. **One rounded box.** The words\n on top, full width, growing with what is typed. Under them a row: **+**\n (attach) at the left, and at the right the **microphone** and a **round\n arrow** to send. **While a turn runs, a round stop takes the arrow's\n place** (once the member types, the arrow comes back: a message sent then\n waits its turn, as before). A quiet `hint` can sit under the box.\n- **Files** by the + button, by paste and by a drop anywhere on the pane (up\n to 4 files and 3 MB together); **dictation** where the browser has speech\n recognition; the box **closed** while something waits on the member, with\n the reason in its placeholder.\n- **A starting line** from the dock: `openAgent({ ask })` (blocks/agent-dock).\n The pane sends it as the member's first words in a new chat, or puts it\n in the box. See \"Have the agent do it\" below.\n\n```jsx\nimport { ChatPane } from \"../components/blocks/chat-pane\"; // a Makersclaw app (\"./blocks/chat-pane\" from components/)\nimport { ChatPane } from \"@/components/makersclaw/blocks/chat-pane\"; // a shadcn project\n```\n\nA client component. It needs the kit and nothing else; it is not tied to the\nagent's lines. **For the agent's conversation use `agent-pane`**, which\ndraws the plan, the work folded into \"Worked through N steps\", the\nquestions, the yes and what the agent made, inside this pane. Use `ChatPane`\nitself for lines of your own in the desk's frame.\n\n## What was ported, and what was not\n\nPorted: the head (New chat, Chats), the past chats list and its marks, the\nempty state, the scroll that follows, the composer (its layout, the\nattachments, the dictation, the send and stop), the closed box, and the\nhand-off seam in a form the app needs (the starting line).\n\nNot ported, because the app has no such thing: the desk's **rail** beside the\nchat (an app's agent lives in the dock, a column; the Chats overlay is the\ncolumn mode's), the **binding** (headers, scope, the session pointer: the\napp's `useAgentPane` holds those), **`openSession`/`pendingLine`** (the desk's\nhand-off of a request into the agent's chat; the app's starting line does\nthat job), **`chatMarks` \"In Makersclaw\"** (an ask made in Makersclaw shows\nas a pointer in the conversation instead) and the wallet gate (the platform\nrefuses a turn with no credits and says so).\n\n## Props\n\n- `chat` — what `useAgentPane()` returns. Required. The pane calls its\n `send(text, { files })`, `stop()`, `newChat()` and `openSession(id)` and\n reads `sessionId`, `request`, `chats`, `view`/`turns`/`pending`/`state`\n (to follow the newest words), `sameConversation`, `sending`, `loading`,\n `error`, `notice`, `unavailable`.\n- `title` — the head's words, and the box's label. Default `\"Ask the agent\"`.\n- `suggestions` — asks that fit the moment, as chips before the first ask.\n- `empty` — one line before the first ask.\n- `placeholder` — the box's words before the first ask. Once there is a\n conversation it says \"Ask a follow-up…\" unless `chat.sameConversation` is\n false.\n- `closedWords` — the box's words while `blocked` (\"Answer above first\").\n- `hint` — a quiet line under the box (\"Enter sends · Shift+Enter is a new\n line\").\n- `recent` — past chats listed before the first ask. Default `5`; `0` none.\n- `bare` — no head, inside a `Sheet` or a `Card` that has its own.\n- `fill` — default `true`: the pane takes its container's height and the\n conversation scrolls inside it. `false`: compact, a capped height.\n- `onClose` — a close in the head. Inside an `agent-dock` it is the dock's.\n- `started` — there is a conversation to draw (else the empty state).\n- `busy` — a turn runs: the arrow is a stop while the box is empty.\n- `blocked` — something waits on the member: the whole box is closed.\n- `bar` — a node between the conversation and the box.\n- `children` — the conversation's lines.\n- `className` — layout around it. Never colour.\n\n`ChatComposer` is also exported, for a box of your own around the same\nframe: `value`, `onChange`, `onSubmit`, `onStop`, `onAttach`, `files`,\n`onRemoveFile`, `busy`, `canStop`, `stopping`, `blocked`, `sending`,\n`encoding`, `dictation` (`useDictation()`), `placeholder`, `label`, `hint`,\n`textareaRef`, `fileInputRef`. `ChatsList` draws the past chats.\n\n## Have the agent do it\n\nA button that says the agent will do the work does not open a form. It calls\n`openAgent(\"Draft a project plan for me\")` (blocks/agent-dock). The dock\nopens, tells this pane the line through `ChatDockContext`, and the pane:\n\n1. starts a new chat (if one is open; `fresh: false` carries on in it),\n2. sends the line as the member's first words, once,\n3. and tells the dock it took it.\n\nWith `send: false` it puts the line in the box instead, with the cursor at\nits end (words the member already typed stay, and the line goes under them). A closed box (something waits on the member) always takes the line\nas a prefill. The pure rule is `startStep(...)` in `chat-model.js`.\n\n## The model (plain functions)\n\n`ago(iso)`, `startStep({ start, handled, sessionId, blocked, canNew, newed })`,\nand the composer's parts: `addFiles`, `filesFromTransfer`, `readFile`,\n`formatBytes`, `MAX_FILES`, `MAX_BYTES`, `useDictation`.\n\n## Example\n\n```jsx\n\"use client\";\nimport { useAgentPane } from \"../lib/hooks\";\nimport { ChatPane } from \"../components/blocks/chat-pane\";\n\nexport default function Notes() {\n const chat = useAgentPane({ key: \"notes\" });\n const messages = chat.view?.messages ?? [];\n return (\n <ChatPane\n chat={chat}\n title=\"Ask about your notes\"\n suggestions={[\"Summarise this week\"]}\n hint=\"Enter sends · Shift+Enter is a new line\"\n started={messages.length > 0}\n busy={chat.state === \"working\"}\n >\n {messages.map((m) => (\n <p key={m.id}>{m.text}</p>\n ))}\n </ChatPane>\n );\n}\n```\n"
59
+ }
60
+ ],
61
+ "categories": [
62
+ "chat",
63
+ "composer",
64
+ "message box",
65
+ "ask",
66
+ "assistant",
67
+ "conversation",
68
+ "new chat",
69
+ "chats",
70
+ "send",
71
+ "stop",
72
+ "attach",
73
+ "dictate",
74
+ "copilot",
75
+ "sidebar"
76
+ ],
77
+ "docs": "Installed to components/makersclaw/blocks/chat-pane. Import: import { ChatPane, ChatComposer, ChatsList, ChatDockContext, ago, startStep, MAX_BYTES, MAX_FILES, addFiles, filesFromTransfer, formatBytes, readFile, useDictation } from \"@/components/makersclaw/blocks/chat-pane\". Props and an example: components/makersclaw/blocks/chat-pane/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
78
+ "meta": {
79
+ "makersclaw": {
80
+ "version": "0.8.2",
81
+ "useWhen": "Any app chat with the agent: the pane inside an agent-dock, a compact card beside a record, a sheet. Use agent-pane when the conversation is the agent's (it puts the plan, the work, the questions and the yes inside this pane); use chat-pane itself to draw your own lines in the desk's frame, or just the composer (ChatComposer). Not for a chat between people (chat) or an agent run that is commissioned from a verb and watched (job-panel)."
82
+ }
83
+ }
84
+ }
package/r/chat.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "Chat",
6
6
  "description": "The parts of a chat: messages with avatars, times, grouping, sending/failed/streaming states, hover actions, thumbs up/down feedback, in-place editing and a folding reasoning section; a scroller that sticks to the bottom with day separators and older-message loading; a composer (a box or an \"ask anything\" pill) with attachments, a + menu, tools, suggestion chips, Enter to send and Stop; a header; and ChatThread, which composes them from messages + onSend.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -88,7 +88,7 @@
88
88
  "docs": "Installed to components/makersclaw/blocks/chat. Import: import { ChatThread, ChatHeader, Message, Bubble, Attachment, MessageScroller, Composer } from \"@/components/makersclaw/blocks/chat\". Props and an example: components/makersclaw/blocks/chat/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
89
89
  "meta": {
90
90
  "makersclaw": {
91
- "version": "0.8.0",
91
+ "version": "0.8.2",
92
92
  "useWhen": "A conversation lives inside the app's own screen: a support inbox reply pane, a customer-facing assistant, comments between teammates on a record, a thread per object. Not the app window's Agent tab (the OS draws that) and not a one-off job the founder commissions (job-panel)."
93
93
  }
94
94
  }
@@ -5,7 +5,7 @@
5
5
  "title": "CodeEditor",
6
6
  "description": "A code editor (CodeMirror 6) in the workspace theme: SQL with the database's tables and columns in autocomplete, JSON checked as you type with plain-words errors, or plain text; line numbers, problems underlined in the text with a dot in the gutter, find and replace, folding, and a run hook on Cmd/Ctrl+Enter that runs the selection or the statement under the cursor. A quiet status line shows problems, the cursor, the mode and a Run button. Helpers split SQL into statements and check or format JSON anywhere, the server included.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
9
  ],
10
10
  "dependencies": [
11
11
  "@codemirror/state@6.7.6",
@@ -87,7 +87,7 @@
87
87
  "docs": "Installed to components/makersclaw/blocks/code-editor. Import: import { CodeEditor, useCodeEditor, LANGUAGES, SQL_DIALECTS, splitStatements, statementAt, sqlNamespace, checkJson, formatJson, stringifyJson, lineCol, offsetOf, normalizeDiagnostics } from \"@/components/makersclaw/blocks/code-editor\". Props and an example: components/makersclaw/blocks/code-editor/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
88
88
  "meta": {
89
89
  "makersclaw": {
90
- "version": "0.8.0",
90
+ "version": "0.8.2",
91
91
  "useWhen": "A screen edits code or structured text: a SQL console, a query in a report, a JSON config or payload, a webhook body, a template, a script. json-editor and sql-console are built on it. Not for prose (doc-editor), one line of text (the kit's Input), or showing a diagram (diagram)."
92
92
  }
93
93
  }
@@ -5,8 +5,8 @@
5
5
  "title": "CommentBox",
6
6
  "description": "A minimal rich comment field (DocEditor in minimal mode): bold, italic, links and lists from the selection toolbar or markdown shortcuts, @mentions as chips, Cmd/Ctrl+Enter to post. It posts through an async onPost(doc) that answers { ok }, keeps the draft and says why when it fails, and clears when it lands. With `comments` it draws the thread above it: avatar, name, relative time, the rich body. Also exports the composer alone, the read-only body renderer and the timestamp.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json",
9
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/doc-editor.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/doc-editor.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -57,7 +57,7 @@
57
57
  "docs": "Installed to components/makersclaw/blocks/comment-box. Import: import { CommentBox, CommentComposer, CommentBody, CommentTime } from \"@/components/makersclaw/blocks/comment-box\". Props and an example: components/makersclaw/blocks/comment-box/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
58
58
  "meta": {
59
59
  "makersclaw": {
60
- "version": "0.8.0",
60
+ "version": "0.8.2",
61
61
  "useWhen": "People comment on something in the app: a task, a design, a document, an approval, a support ticket; a reply field with @mentions under a thread. Not dated notes kept on a record with edit and delete (record-notes), not a conversation with an assistant (chat), not a long document (doc-editor, doc-page)."
62
62
  }
63
63
  }
@@ -0,0 +1,51 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "connect-card",
4
+ "type": "registry:block",
5
+ "title": "ConnectCard",
6
+ "description": "The accounts the app declares, as rows like the window's Settings › Connections: what each is for, its kind (Mail, Calendar), needed or optional, live or not connected as a mark and a word, which accounts meet it, and Connect or Change. The press goes out as onConnect(key): useConnections() (lib/hooks.js) opens the desk's connections panel for that need, listing every provider of the kind (no agent turn), or hands out the link in a new tab outside the desk, then watches until the account is live. Signing in stays the member's, at the provider.",
7
+ "registryDependencies": [
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
+ ],
10
+ "dependencies": [],
11
+ "files": [
12
+ {
13
+ "path": "registry/blocks/connect-card/index.js",
14
+ "type": "registry:component",
15
+ "target": "@components/makersclaw/blocks/connect-card/index.js",
16
+ "content": "// The ConnectCard block — `makersclaw add connect-card`. Fixed: compose\n// it through its props (README.md beside this file).\nexport { ConnectCard, ConnectRow } from \"./connect-card\";\n"
17
+ },
18
+ {
19
+ "path": "registry/blocks/connect-card/connect-card.jsx",
20
+ "type": "registry:component",
21
+ "target": "@components/makersclaw/blocks/connect-card/connect-card.jsx",
22
+ "content": "\"use client\";\n\n// The app's declared accounts as rows, the way the window's Settings ›\n// Connections draws them: what each is for, live or not connected, which\n// accounts meet it, and Connect (or Change). Props in, callbacks out: the\n// press goes out as onConnect(key), which useConnections turns into the\n// desk's connections panel (every provider of the kind; no agent turn),\n// or a link in a new tab outside the desk. Signing in stays the member's,\n// at the provider. Tokens only; live or not is a mark AND a word. Its own\n// width decides its layout (@container): under about 384 px the state and\n// the button go under the words.\n\nimport { Button, Icon, cx } from \"../../ui\";\n\n/** Live or not, as a pill: a mark and a word. */\nfunction StatePill({ connected, waiting }) {\n const look = connected\n ? { icon: \"circle-check\", word: \"Live\", tone: \"text-success border-success\" }\n : waiting\n ? { icon: \"loader\", word: \"Waiting for sign-in\", tone: \"text-info border-info\" }\n : { icon: \"circle-dashed\", word: \"Not connected\", tone: \"text-muted-foreground border-border\" };\n return (\n <span className={cx(\"inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-[11.5px]\", look.tone)}>\n <Icon name={look.icon} size={12} className={waiting && !connected ? \"animate-spin motion-reduce:animate-none\" : undefined} />\n {look.word}\n </span>\n );\n}\n\n/**\n * One declared account.\n *\n * connection { key, label, required, connected, accounts, category_label }\n * (a row of useConnections' `connections`)\n * onConnect(key) Connect / Change; leave it out and no button is drawn\n * connecting this account is being connected: the pill waits\n * connectLabel, changeLabel the button's words (default \"Connect\", \"Change\")\n */\nexport function ConnectRow({ connection, onConnect, connecting = false, connectLabel = \"Connect\", changeLabel = \"Change\", className }) {\n const c = connection ?? {};\n const accounts = Array.isArray(c.accounts) ? c.accounts.filter(Boolean) : [];\n const meta = [c.category_label, c.required === true ? \"Needed\" : c.required === false ? \"Optional\" : null].filter(Boolean).join(\" · \");\n return (\n <li className={cx(\"grid min-w-0 gap-2 px-3.5 py-3 @sm:grid-cols-[minmax(0,1fr)_auto] @sm:items-center @sm:gap-3\", className)}>\n <div className=\"grid min-w-0 gap-0.5\">\n <span className=\"line-clamp-2 text-[13px] text-card-foreground\">{c.label}</span>\n {meta ? <span className=\"truncate text-xs text-muted-foreground\">{meta}</span> : null}\n {c.connected && accounts.length ? (\n <span className=\"truncate text-xs text-muted-foreground\" title={accounts.join(\", \")}>\n {accounts.length === 1 ? accounts[0] : `${accounts[0]} and ${accounts.length - 1} more`}\n </span>\n ) : null}\n </div>\n <div className=\"flex min-w-0 flex-wrap items-center gap-2 @sm:flex-nowrap @sm:justify-end\">\n <StatePill connected={Boolean(c.connected)} waiting={connecting} />\n {onConnect ? (\n <Button size=\"sm\" variant={c.connected ? \"quiet\" : \"secondary\"} onClick={() => onConnect(c.key)} disabled={connecting && !c.connected}>\n {c.connected ? changeLabel : connectLabel}\n </Button>\n ) : null}\n </div>\n </li>\n );\n}\n\n/**\n * The app's accounts on a card.\n *\n * connections useConnections' `connections`\n * onConnect(key) useConnections' `connect`\n * connecting the key being connected (useConnections' `connecting`)\n * title the card's head (default \"Accounts it uses\"); null for none\n * description one line under it: what the app does with them\n * empty what the card says with no accounts (null draws nothing)\n */\nexport function ConnectCard({ connections = [], onConnect, connecting = null, title = \"Accounts it uses\", description, empty = \"This app connects to no accounts.\", className }) {\n const list = Array.isArray(connections) ? connections : [];\n if (!list.length && empty === null) return null;\n return (\n <section aria-label={title ?? \"Accounts\"} className={cx(\"@container grid min-w-0 overflow-hidden rounded-[var(--radius)] border border-border bg-card text-card-foreground\", className)}>\n {title || description ? (\n <header className=\"grid gap-0.5 border-b border-border px-3.5 py-3\">\n {title ? (\n <span className=\"flex min-w-0 items-center gap-2\">\n <Icon name=\"link\" size={14} className=\"shrink-0 text-muted-foreground\" />\n <b className=\"truncate text-sm font-semibold\">{title}</b>\n </span>\n ) : null}\n {description ? <span className=\"text-xs text-muted-foreground\">{description}</span> : null}\n </header>\n ) : null}\n {list.length ? (\n <ul className=\"m-0 grid list-none divide-y divide-border p-0\">\n {list.map((c) => (\n <ConnectRow key={c.key} connection={c} onConnect={onConnect} connecting={connecting === c.key} />\n ))}\n </ul>\n ) : (\n <p className=\"m-0 px-3.5 py-3 text-[13px] text-muted-foreground\">{empty}</p>\n )}\n </section>\n );\n}\n"
23
+ },
24
+ {
25
+ "path": "registry/blocks/connect-card/README.md",
26
+ "type": "registry:file",
27
+ "target": "@components/makersclaw/blocks/connect-card/README.md",
28
+ "content": "# ConnectCard\n\nThe accounts the app declares, as rows the member already knows from the\nwindow's Settings › Connections: what each is for, its kind (Mail,\nCalendar), needed or optional, **live** or **not connected** (a mark and\na word), which accounts meet it, and **Connect** or **Change**. Part of\nthe agent kit (0.8.1): the member connects where the need is felt,\ninside the app.\n\n```jsx\nimport { ConnectCard, ConnectRow } from \"../components/blocks/connect-card\"; // a Makersclaw app\nimport { ConnectCard, ConnectRow } from \"@/components/makersclaw/blocks/connect-card\"; // a shadcn project\n```\n\nA client component. Its own width decides its layout: under about\n384 px each row's state and button go under its words.\n\n## What Connect does\n\nThe block only calls `onConnect(key)`. `useConnections().connect` then:\n\n1. Inside the desk, asks it to open its connections panel for that\n need (`claw:open-connections`). The panel lists every provider of the\n need's kind — a \"mail\" need shows Gmail, Outlook, Zoho Mail — and the\n member signs in there. No agent turn, no chat.\n2. Outside the desk (the app on its own host), fetches the link (`POST\n _claw/connect`) and opens it in a new tab.\n3. Either way, looks every 3 s for two minutes until the account is\n live; `connecting` is its key meanwhile, and the row says \"Waiting\n for sign-in\".\n\n## Props\n\n### ConnectCard\n\n- `connections` — useConnections' `connections`: `[{ key, label,\n required, connected, accounts, category, category_label }]`.\n- `onConnect(key)` — useConnections' `connect`. Leave it out and no\n button is drawn (a read-only list).\n- `connecting` — the key being connected (useConnections'\n `connecting`).\n- `title` — the head (default \"Accounts it uses\"); `null` for none.\n `description` — one line under it: what the app does with them.\n- `empty` — what it says with no accounts; `null` draws nothing.\n- `className` — layout around it. Never colour.\n\n### ConnectRow\n\nOne row, for a list of your own (an empty state, a setup step):\n`connection`, `onConnect(key)`, `connecting` (boolean), `connectLabel`\n(\"Connect\"), `changeLabel` (\"Change\"). Put it inside a `<ul>`, in an\nelement with `@container` so its layout follows its width.\n\n## useConnections (lib/hooks.js)\n\n```js\nconst { connections, connecting, connect } = useConnections();\n```\n\nIt reads `GET _claw/connect` once, then only while an account is being\nconnected, and again when the page comes back. `connect(key)` resolves\n`{ opened: \"desk\" | \"tab\" | false, connected?, error? }`. On a claw from\nbefore the door it is `unavailable`: draw the card with `empty`, never\nan error.\n\n## Who may do what\n\nAny member who opens the app sees the accounts and may connect one: the\nsign-in is theirs, at the provider, as on the desk. Which accounts an\napp may use is the app's declaration, not this block's.\n\n## Never\n\n- Never a sign-in form, a password or a key field: the member signs in\n at the provider, on the desk's panel or the link's page.\n- Never an agent turn to connect an account (\"Ask the agent to connect\n Gmail\"): Connect opens the panel.\n- Never live or not by colour alone.\n\n## Example\n\n```jsx\n\"use client\";\nimport { useConnections } from \"../lib/hooks\";\nimport { ConnectCard } from \"../components/blocks/connect-card\";\n\nexport default function WhatItReads() {\n const { connections, connecting, connect } = useConnections();\n return (\n <ConnectCard\n title=\"What the CRM reads\"\n description=\"Your mail and calendar, to build each person's timeline.\"\n connections={connections}\n connecting={connecting}\n onConnect={connect}\n />\n );\n}\n```\n"
29
+ }
30
+ ],
31
+ "categories": [
32
+ "connect",
33
+ "connections",
34
+ "accounts",
35
+ "integrations",
36
+ "oauth",
37
+ "mailbox",
38
+ "calendar",
39
+ "sign in",
40
+ "setup",
41
+ "live",
42
+ "kit"
43
+ ],
44
+ "docs": "Installed to components/makersclaw/blocks/connect-card. Import: import { ConnectCard, ConnectRow } from \"@/components/makersclaw/blocks/connect-card\". Props and an example: components/makersclaw/blocks/connect-card/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
45
+ "meta": {
46
+ "makersclaw": {
47
+ "version": "0.8.2",
48
+ "useWhen": "An app that reads or writes through an outside account shows what it uses and lets the member connect it where the need is felt: a CRM's \"What the CRM reads\" (mailbox, calendar), an inbox app's accounts, a setup step, the empty state of a screen that needs a mailbox. For a first-run checklist use setup-checklist; for the app's own settings page use settings-list."
49
+ }
50
+ }
51
+ }
@@ -5,7 +5,7 @@
5
5
  "title": "Contact Section",
6
6
  "description": "A public \"contact us\" section: a form (name, email, message; optional phone, company, subject, a topic as chips or radios, a consent box) and/or the ways to reach you (email, phone, address, hours, offices). The form alone, beside an info panel (plain or filled), or with info cards above or beside it; an optional picture beside or behind the form. Validates in place, submits through an async onSubmit that returns { ok }, and shows the failure or a sent state.",
7
7
  "registryDependencies": [
8
- "https://cdn.jsdelivr.net/npm/makersclaw@0.8.0/r/core.json"
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json"
9
9
  ],
10
10
  "dependencies": [],
11
11
  "files": [
@@ -46,7 +46,7 @@
46
46
  "docs": "Installed to components/makersclaw/blocks/contact-section. Import: import { ContactSection } from \"@/components/makersclaw/blocks/contact-section\". Props and an example: components/makersclaw/blocks/contact-section/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
47
47
  "meta": {
48
48
  "makersclaw": {
49
- "version": "0.8.0",
49
+ "version": "0.8.2",
50
50
  "useWhen": "The contact page or contact section of a public site: sales enquiries, support requests, a studio's \"start a project\" form. Not for a signed-in user's support inbox or ticket thread, or a general-purpose record form (use a form of your own from the kit)."
51
51
  }
52
52
  }