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
@@ -0,0 +1,93 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "agent-pane",
4
+ "type": "registry:block",
5
+ "title": "AgentPane",
6
+ "description": "The desk's agent sidebar, as the app's own pane, in the app's theme (D27): the member's words, the agent's text as it is written, the plan, the work folded into \"Worked through N steps\" with each step opening to its facts and what came back, reads folded, retries a later call fixed, helpers, the agent's questions as one form, the one ask's yes as the `approval` block's press, what it made, the past chats with a dot on those that need you, New chat and Stop; and the desk's message box (the chat-pane block's: the words on top, + to attach and a round send under them, a stop in its place while a turn runs), files to send with the words and dictation in the box; a tool's yes pressed in place; the thread streamed as the agent writes; Open under an app or file it made. It draws what useAgentPane() returns (lib/hooks.js): the same conversation and the same rules the desk's sidebar runs, one session, two views. The app places it and chooses where; the platform supplies what it shows.",
7
+ "registryDependencies": [
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/needs-you.json",
10
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/chat-pane.json"
11
+ ],
12
+ "dependencies": [],
13
+ "files": [
14
+ {
15
+ "path": "registry/blocks/agent-pane/index.js",
16
+ "type": "registry:component",
17
+ "target": "@components/makersclaw/blocks/agent-pane/index.js",
18
+ "content": "// The AgentPane block — `makersclaw add agent-pane`. Fixed: compose it\n// through its props (README.md beside this file).\nexport { AgentPane } from \"./agent-pane\";\nexport { AgentDockContext } from \"./dock-context\";\n"
19
+ },
20
+ {
21
+ "path": "registry/blocks/agent-pane/dock-context.js",
22
+ "type": "registry:component",
23
+ "target": "@components/makersclaw/blocks/agent-pane/dock-context.js",
24
+ "content": "\"use client\";\n\n// What an AgentPane placed inside an AgentDock (blocks/agent-dock) is told:\n// `close` folds the dock (the head's close, when the app gave no onClose of\n// its own), `mode` is \"column\" or \"sheet\", and `start` is the starting line\n// a \"have the agent do it\" button asked for (openAgent). Null outside a\n// dock, where nothing changes. It is the chat-pane block's context under\n// the name the dock and the apps have always used (X5: the pane's frame\n// moved to chat-pane, and the context with it).\nexport { ChatDockContext as AgentDockContext } from \"../chat-pane\";\n"
25
+ },
26
+ {
27
+ "path": "registry/blocks/agent-pane/agent-pane.jsx",
28
+ "type": "registry:component",
29
+ "target": "@components/makersclaw/blocks/agent-pane/agent-pane.jsx",
30
+ "content": "\"use client\";\n\nimport { Button, Icon, Notice, Spinner, cx } from \"../../ui\";\nimport { ChatPane } from \"../chat-pane\";\nimport { QuestionBox } from \"../needs-you\";\nimport { Prose } from \"./prose\";\nimport { QuestionForm } from \"./questions\";\nimport { MemberWords, Message } from \"./transcript\";\n\n// AgentPane — the desk's agent sidebar, as the app's own pane (0.8.1, D27;\n// docs/AGENT-BLOCK-PLAN.md). The founder: \"the inapp component is not on\n// par with the desk agent sidebar, in terms of tool calls, questions,\n// showing information\". So this is the sidebar, drawn in the app's theme:\n// the member's words, the agent's text as it is written, the plan, the\n// work folded into \"Worked through N steps\" (each step opening to its\n// facts and what came back), helpers, several questions as one form, the\n// one ask's yes as the `approval` block's press, what it made, the past\n// chats and New chat, Stop.\n//\n// const chat = useAgentPane({ row: { kind: \"person\", id, name } });\n// <AgentPane chat={chat} title={`Ask about ${name}`} suggestions={[…]} />\n//\n// It draws the object useAgentPane() returns (lib/hooks.js) and decides\n// nothing: which lines there are and what they say is the platform's (the\n// same rules the sidebar runs), sent as data. It is the SAME conversation\n// the desk's sidebar shows: one session, two views. An app that places it\n// says so in its package (`\"agent_pane\": true` in the blueprint), and the\n// desk then draws no sidebar for the app's window.\n//\n// The frame around the lines (the head with New chat and Chats, the empty\n// state, the scroll, the desk's composer with + and the round send, a\n// starting line from the dock) is the `chat-pane` block (X5); this file is\n// what the AGENT puts in it: the lines above, and the bar of what waits.\n//\n// What waits on the member is settled here, where it was asked. The\n// agent's QUESTIONS are answered in place (`chat.answer`); its YES is the\n// `approval` block's press frame, handed in as `yes` — this pane never\n// draws an Approve of its own. A sign-in, money, or a tool approval says\n// so and opens the desk.\n\nconst STATE_LINE = {\n queued: \"Queued — it starts when the one before it ends\",\n working: \"Working on it\",\n};\n\n/** What waits on the member, as the bar under the thread says it. */\nfunction needsTitle(kind, inApp) {\n if (kind === \"yes\" || kind === \"approval\") return inApp === \"press\" ? \"Waiting for your yes\" : \"Waiting for your yes on the desk\";\n if (kind === \"question\") return inApp === \"answer\" ? \"A question for you\" : \"Waiting for your answer on the desk\";\n if (kind === \"connect\") return \"Waiting for you to connect an account on the desk\";\n if (kind === \"limit\") return \"Paused for credits — see the desk\";\n return \"Needs you on the desk\";\n}\n\n/** A claw from before the sidebar's view sends only the small turns: the\n * member's words, the agent's text, one line per step. Drawn plainly. */\nfunction LegacyTurn({ turn }) {\n return (\n <div className=\"grid min-w-0 gap-2\">\n {turn.member ? <MemberWords>{turn.member}</MemberWords> : null}\n {(turn.parts ?? []).map((p, i) =>\n p.type === \"step\" ? (\n <p key={i} className=\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\">\n {p.status === \"running\" ? <Spinner size=\"sm\" label=\"Working\" /> : <Icon name={p.status === \"failed\" ? \"warning\" : \"check\"} size={12} className=\"shrink-0\" />}\n <span className=\"min-w-0 truncate\">{p.label}</span>\n </p>\n ) : p.text?.trim() ? (\n <Prose key={i} text={p.text} />\n ) : null,\n )}\n {turn.writing ? <Prose text={`${turn.writing}▍`} /> : null}\n </div>\n );\n}\n\n/**\n * chat what useAgentPane() returns (required)\n * title the head's words (\"Ask about Pinewood\"); default \"Ask the agent\"\n * suggestions asks that fit the moment, shown before the first ask\n * empty one line before the first ask\n * placeholder the box's words\n * hint a quiet line under the box\n * yes a node drawn in the needs slot when the agent waits on a\n * yes the app can take (`chat.needs.in_app === \"press\"`):\n * the `approval` block, fed by useApproval(chat.request.id).\n * Without it that yes opens the desk\n * mark a node before the agent's turns (the app's mark), or none\n * onOpen ({ kind: \"app\", id } | { kind: \"file\", path, where, appRef })\n * the \"Open\" of something it made. Default: `chat.open`\n * (the desk opens it); with neither there is no Open\n * onClose a close in the head (a panel beside a record). Inside an\n * AgentDock it is the dock's own, unless this is given\n * recent past chats listed before the first ask (default 5; 0\n * draws none)\n * bare no head (inside a Sheet or a Card that has its own)\n * fill take the height of its container (default). false: a\n * compact pane whose thread scrolls inside a capped height\n * className layout around it. Never colour.\n */\nexport function AgentPane({\n chat,\n title = \"Ask the agent\",\n suggestions = [],\n empty,\n placeholder,\n hint,\n yes,\n mark,\n onOpen,\n onClose,\n recent = 5,\n bare = false,\n fill = true,\n className,\n}) {\n const view = chat?.view ?? null;\n const legacy = !view ? (chat?.turns ?? []) : [];\n const messages = view?.messages ?? [];\n const pending = chat?.pending && !messages.some((m) => m.role === \"user\" && m.text === chat.pending) && !legacy.some((t) => t.member === chat.pending) ? chat.pending : null;\n const needs = chat?.state === \"needs_you\" ? (chat.needs ?? { kind: \"other\", text: \"\" }) : null;\n const live = chat?.state === \"working\" || chat?.state === \"queued\" || Boolean(pending);\n const started = messages.length > 0 || legacy.length > 0 || Boolean(pending);\n const inMakersclaw = chat?.place === \"makersclaw\";\n const questions = view?.questions ?? [];\n const result = chat?.request?.status === \"done\" ? chat.request?.result?.sentence : null;\n // The thinking row: the turn runs and nothing visibly grows yet (the\n // platform says when, in the view); words just sent wait for their turn.\n const lastLegacy = legacy[legacy.length - 1];\n const starting = Boolean(pending) && !messages.some((m) => m.role === \"assistant\" && m.live);\n const thinking =\n !needs && !inMakersclaw && (pending ? true : view ? view.thinking : live && !(lastLegacy?.writing || lastLegacy?.parts?.length));\n\n const openThing = onOpen ?? (typeof chat?.open === \"function\" ? chat.open : undefined);\n const answerHere = needs?.in_app === \"answer\" && typeof chat?.answer === \"function\";\n const pressHere = needs?.in_app === \"press\" && yes != null && yes !== false;\n // A message during a question skips it and continues the same thread.\n // Approvals, account connections and credit limits keep their own gate.\n const blocked = (Boolean(needs) && needs.kind !== \"question\") || inMakersclaw;\n const closedWords = needs ? (answerHere ? \"Answer above first\" : pressHere ? \"Say yes above first\" : \"Answer on the desk first\") : \"This ask is in Makersclaw\";\n\n // What sits between the lines and the box: what waits on the member, or\n // that the agent works (its Stop is the box's, in the arrow's place), or\n // how the ask ended.\n const bar =\n needs && pressHere ? (\n <div className=\"max-h-[70%] shrink-0 overflow-y-auto border-t border-border p-2.5\">{yes}</div>\n ) : needs ? (\n <div className=\"max-h-[70%] shrink-0 overflow-y-auto border-t border-border p-2.5\">\n <div className=\"grid min-w-0 gap-2 rounded-[var(--radius)] border border-warning p-2.5\">\n <div className=\"flex min-w-0 items-start gap-2\">\n <Icon name=\"warning\" size={16} className=\"mt-px shrink-0 text-warning\" />\n <div className=\"grid min-w-0 gap-0.5\">\n <span className=\"font-medium\">\n {answerHere && questions.length > 1 ? `${questions.length} questions for you` : needsTitle(needs.kind, answerHere ? \"answer\" : needs.in_app)}\n </span>\n {needs.text && !(answerHere && questions.length > 0) ? (\n <span className={cx(\"break-words text-muted-foreground\", !answerHere && \"line-clamp-3\")}>{needs.text}</span>\n ) : null}\n </div>\n </div>\n {answerHere && questions.length > 0 ? (\n <QuestionForm key={needs.at ?? \"q\"} questions={questions} onSubmit={(answers) => chat.answer({ answers })} />\n ) : answerHere ? (\n <QuestionBox key={needs.at ?? \"q\"} options={needs.options ?? []} free={needs.free} onAnswer={(payload) => chat.answer(payload)} />\n ) : (\n <Button size=\"sm\" variant=\"primary\" className=\"justify-self-start\" onClick={() => chat.openOnDesk?.()}>\n Open on the desk <Icon name=\"external\" size={12} />\n </Button>\n )}\n </div>\n </div>\n ) : live && !inMakersclaw ? (\n <div className=\"flex shrink-0 items-center gap-2 border-t border-border px-3.5 py-1.5 text-xs text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Working\" />\n <span className=\"min-w-0 flex-1 truncate\">{STATE_LINE[chat?.state] ?? STATE_LINE.working}</span>\n </div>\n ) : result ? (\n <p className=\"flex shrink-0 items-start gap-2 border-t border-border px-3.5 py-1.5 text-xs text-muted-foreground\">\n <Icon name=\"circle-check\" size={14} className=\"mt-px shrink-0 text-success\" />\n <span className=\"min-w-0 break-words\">{result}</span>\n </p>\n ) : null;\n\n return (\n <ChatPane\n chat={chat}\n title={title}\n suggestions={suggestions}\n empty={empty}\n placeholder={placeholder}\n closedWords={closedWords}\n hint={hint}\n recent={recent}\n bare={bare}\n fill={fill}\n onClose={onClose}\n started={started || inMakersclaw}\n busy={live && !inMakersclaw}\n blocked={blocked}\n bar={bar}\n className={className}\n >\n {inMakersclaw ? (\n <Notice\n tone=\"info\"\n title=\"This ask is in Makersclaw\"\n action={\n <Button size=\"sm\" variant=\"secondary\" onClick={() => chat.openOnDesk?.()}>\n Open <Icon name=\"external\" size={12} />\n </Button>\n }\n >\n {chat.request?.title ?? \"It is followed there.\"}\n </Notice>\n ) : (\n <>\n {messages.map((m) => (\n <Message key={m.id} message={m} load={chat.callDetail} onOpen={openThing} onOpenRequest={chat.openRequest} mark={mark} />\n ))}\n {legacy.map((t) => (\n <LegacyTurn key={t.id} turn={t} />\n ))}\n {pending ? <MemberWords>{pending}</MemberWords> : null}\n {thinking ? (\n <div role=\"status\" className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Thinking\" /> {starting ? \"Starting…\" : \"Thinking…\"}\n </div>\n ) : null}\n {!live && view?.failure ? (\n <p role=\"alert\" className=\"text-xs text-destructive\">\n {view.failure.kind === \"stopped\" ? (\n <>\n <b>You stopped it.</b> <span className=\"text-muted-foreground\">Send a message to carry on.</span>\n </>\n ) : (\n <>\n <b>It stopped before finishing.</b> {view.failure.words} <span className=\"text-muted-foreground\">Send a message to carry on.</span>\n </>\n )}\n </p>\n ) : null}\n </>\n )}\n </ChatPane>\n );\n}\n"
31
+ },
32
+ {
33
+ "path": "registry/blocks/agent-pane/transcript.jsx",
34
+ "type": "registry:component",
35
+ "target": "@components/makersclaw/blocks/agent-pane/transcript.jsx",
36
+ "content": "\"use client\";\n\nimport { Button, Icon, Spinner, cx } from \"../../ui\";\nimport { Prose } from \"./prose\";\nimport { WorkBlock } from \"./work\";\n\n// The cards of a turn, as the desk's sidebar draws them (D27): the plan\n// checklist, the one-line receipt an answered card shrinks to, the\n// \"Needs your OK\" note, an app it made, everything it made, a sign-in it\n// asks for. What each holds is decided by the platform (the rules the\n// sidebar runs) and arrives as data; these draw it.\n\n/* ---- the plan -------------------------------------------------------- */\n\nconst PLAN_LABEL = { done: \"Done\", active: \"Working on it\", waiting: \"Waiting for you\", pending: \"Not started\", cancelled: \"Dropped\" };\n\nfunction PlanMark({ state }) {\n const label = PLAN_LABEL[state] ?? \"\";\n if (state === \"done\") return <Icon name=\"circle-check\" size={14} className=\"shrink-0 text-success\" label={label} />;\n if (state === \"active\") return <Spinner size=\"sm\" label={label} className=\"shrink-0 text-primary\" />;\n if (state === \"waiting\") return <Icon name=\"warning\" size={14} className=\"shrink-0 text-warning\" label={label} />;\n if (state === \"cancelled\") return <Icon name=\"circle-x\" size={14} className=\"shrink-0 text-muted-foreground\" label={label} />;\n return <Icon name=\"circle\" size={14} className=\"shrink-0 text-muted-foreground\" label={label} />;\n}\n\nfunction PlanCard({ items }) {\n return (\n <ol aria-label=\"The plan\" className=\"grid min-w-0 divide-y divide-border overflow-hidden rounded-lg border border-border bg-card\">\n {items.map((item, i) => (\n <li\n key={`${i}-${item.text}`}\n data-state={item.state}\n className={cx(\"flex min-w-0 items-start gap-2 px-2.5 py-1.5 text-xs\", item.state === \"done\" || item.state === \"cancelled\" ? \"text-muted-foreground\" : \"text-foreground\")}\n >\n <span className=\"mt-px\">\n <PlanMark state={item.state} />\n </span>\n <span className={cx(\"min-w-0 flex-1 break-words\", item.state === \"cancelled\" && \"line-through\")}>{item.text}</span>\n {item.state === \"waiting\" ? <span className=\"shrink-0 text-warning\">Waiting for you</span> : null}\n </li>\n ))}\n </ol>\n );\n}\n\n/* ---- decided cards, one line ------------------------------------------ */\n\nfunction Receipt({ block }) {\n const clip = (s) => (s.length > 60 ? `${s.slice(0, 59)}…` : s);\n const line =\n block.decision === \"approved\"\n ? `You approved · ${block.text}`\n : block.decision === \"declined\"\n ? `Not now · ${block.text}`\n : block.prompt\n ? `You answered “${clip(block.prompt)}” · ${block.text}`\n : `You answered · ${block.text}`;\n return (\n <div role=\"status\" data-decision={block.decision} className=\"flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground\">\n <Icon name={block.decision === \"declined\" ? \"x\" : \"check\"} size={12} className=\"shrink-0\" />\n <span className=\"min-w-0 truncate\" title={line}>\n {line}\n </span>\n </div>\n );\n}\n\n/** Parked tool approvals and the one ask, listed. Pressing happens in the\n * bar under the thread (the `approval` block) or on the desk. */\nfunction ApprovalNote({ block }) {\n return (\n <div role=\"group\" aria-label=\"Needs your OK\" className=\"grid min-w-0 gap-1 rounded-lg border border-warning/50 bg-card px-2.5 py-2 text-xs\">\n <span className=\"font-medium text-warning\">Needs your OK</span>\n <ul className=\"grid gap-0.5\">\n {block.items.map((item) => (\n <li key={item.id} className=\"min-w-0 break-words\">\n {item.ask}\n {item.grant ? <span className=\"block text-muted-foreground\">{item.grant}</span> : null}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\nfunction RaisedNote({ block }) {\n return (\n <div className=\"flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground\">\n <Icon name=\"warning\" size={12} className=\"shrink-0 text-warning\" />\n <span className=\"min-w-0 truncate\">{block.text} It waits on the desk.</span>\n </div>\n );\n}\n\nfunction Authorization({ block }) {\n if (block.state === \"completed\") {\n return (\n <div role=\"status\" className=\"flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground\">\n <Icon name={block.outcome === \"authorized\" ? \"check\" : \"x\"} size={12} className=\"shrink-0\" />\n <span className=\"min-w-0 truncate\">{block.outcome === \"authorized\" ? `Signed in to ${block.name}` : `The sign-in to ${block.name} didn’t finish`}</span>\n </div>\n );\n }\n return (\n <div role=\"group\" aria-label=\"Needs your OK\" className=\"grid min-w-0 gap-2 rounded-lg border border-warning/50 bg-card px-2.5 py-2 text-xs\">\n <span className=\"font-medium text-warning\">Needs you</span>\n <span>Sign in to {block.name} so the agent can carry on.</span>\n {block.url ? (\n <a\n href={block.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex w-fit items-center gap-1 rounded-md bg-primary px-2.5 py-1 text-primary-foreground hover:opacity-90\"\n >\n {block.instructions ?? \"Sign in\"}\n {block.userCode ? ` (code: ${block.userCode})` : \"\"}\n </a>\n ) : null}\n </div>\n );\n}\n\n/* ---- what it made ------------------------------------------------------ */\n\nconst APP_WORD = { building: \"Being built\", running: \"Running\", live: \"Live\", updated: \"Updated\", installed: \"Installed\" };\n\nfunction AppCard({ app, onOpen }) {\n return (\n <div data-app={app.key} className=\"flex min-w-0 items-center gap-2.5 rounded-lg border border-border bg-card px-3 py-2\">\n <Icon name=\"layers\" size={18} className=\"shrink-0 text-muted-foreground\" />\n <div className=\"grid min-w-0 flex-1\">\n <b className=\"truncate text-sm\">{app.name}</b>\n <span className=\"text-xs text-muted-foreground\">{APP_WORD[app.state] ?? app.state}</span>\n </div>\n {onOpen && app.id ? (\n <Button size=\"sm\" variant=\"secondary\" onClick={() => onOpen({ kind: \"app\", id: app.id })}>\n Open\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction FileRow({ file, onOpen }) {\n const body = (\n <>\n <span className=\"shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-[10px] font-medium text-muted-foreground\">{file.kind}</span>\n <span className=\"min-w-0 flex-1 truncate\" title={file.url ?? file.path}>\n {file.name}\n </span>\n {file.meta ? <span className=\"shrink-0 text-muted-foreground\">{file.meta}</span> : null}\n </>\n );\n const cls = \"flex w-full min-w-0 items-center gap-2 px-2.5 py-1.5 text-left text-xs\";\n // An address opens in a tab; only http(s) is one.\n if (file.where === \"web\" && file.url && /^https?:\\/\\//i.test(file.url))\n return (\n <a className={cx(cls, \"hover:bg-muted\")} href={file.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {body}\n </a>\n );\n if (onOpen && (file.where === \"workspace\" || (file.where === \"app\" && file.appRef)))\n return (\n <button type=\"button\" className={cx(cls, \"hover:bg-muted\")} onClick={() => onOpen({ kind: \"file\", path: file.path, where: file.where, appRef: file.appRef })}>\n {body}\n </button>\n );\n return <div className={cls}>{body}</div>;\n}\n\nfunction MadeList({ block, onOpen }) {\n return (\n <div className=\"grid min-w-0 gap-1.5\">\n <p className=\"text-sm\">Here’s everything I made:</p>\n <ul aria-label=\"Everything I made\" className=\"grid min-w-0 divide-y divide-border overflow-hidden rounded-lg border border-border bg-card\">\n {block.apps.map((app) => (\n <li key={`app-${app.key}`}>\n <div className=\"flex min-w-0 items-center gap-2 px-2.5 py-1.5 text-xs\">\n <span className=\"shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-[10px] font-medium text-muted-foreground\">APP</span>\n <span className=\"min-w-0 flex-1 truncate\">{app.name}</span>\n <span className=\"shrink-0 text-muted-foreground\">{APP_WORD[app.state] ?? app.state}</span>\n {onOpen && app.id ? (\n <Button size=\"sm\" variant=\"quiet\" onClick={() => onOpen({ kind: \"app\", id: app.id })}>\n Open\n </Button>\n ) : null}\n </div>\n </li>\n ))}\n {block.files.map((file) => (\n <li key={file.url ?? file.path}>\n <FileRow file={file} onOpen={onOpen} />\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\nfunction AttachedFile({ block }) {\n return (\n <span className=\"inline-flex w-fit max-w-full min-w-0 items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs\">\n <Icon name=\"paperclip\" size={12} className=\"shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">{block.name}</span>\n </span>\n );\n}\n\n/* ---- the member's words ------------------------------------------------ */\n\nexport function MemberWords({ children, files = [] }) {\n return (\n <div className=\"ml-auto grid max-w-[85%] min-w-0 gap-1.5 justify-items-end\">\n {files.map((f, i) => (\n <span key={`${f.name}-${i}`} className=\"inline-flex max-w-full min-w-0 items-center gap-1.5 rounded-md border border-border px-2 py-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>\n ))}\n {children ? <div className=\"min-w-0 whitespace-pre-wrap break-words rounded-[var(--radius)] bg-muted px-2.5 py-1.5\">{children}</div> : null}\n </div>\n );\n}\n\n/* ---- a message ---------------------------------------------------------- */\n\nfunction Block({ block, live, load, onOpen, onOpenRequest }) {\n switch (block.kind) {\n case \"text\":\n return <Prose text={block.streaming ? `${block.text}▍` : block.text} />;\n case \"plan\":\n return <PlanCard items={block.items} />;\n case \"work\":\n return <WorkBlock runs={block.runs} live={live} load={load} onOpenRequest={onOpenRequest} />;\n case \"approval\":\n return <ApprovalNote block={block} />;\n case \"receipt\":\n return <Receipt block={block} />;\n case \"raised\":\n return <RaisedNote block={block} />;\n case \"app\":\n // A card for an app only once it can be used; a building one waits.\n return block.app.state === \"building\" ? null : <AppCard app={block.app} onOpen={onOpen} />;\n case \"made\":\n return <MadeList block={block} onOpen={onOpen} />;\n case \"file\":\n return <AttachedFile block={block} />;\n case \"authorization\":\n return <Authorization block={block} />;\n default:\n return null;\n }\n}\n\n/** One message of the thread: the member's bubble, or the agent's blocks in order. */\nexport function Message({ message, load, onOpen, onOpenRequest, mark }) {\n if (message.role === \"user\") return <MemberWords files={message.files}>{message.text}</MemberWords>;\n return (\n <div data-role=\"assistant\" className=\"flex min-w-0 items-start gap-2\">\n {mark ? <span className=\"mt-0.5 shrink-0\">{mark}</span> : null}\n <div className=\"grid min-w-0 flex-1 gap-2\">\n {message.blocks.map((block) => (\n <Block key={block.key} block={block} live={message.live} load={load} onOpen={onOpen} onOpenRequest={onOpenRequest} />\n ))}\n </div>\n </div>\n );\n}\n"
37
+ },
38
+ {
39
+ "path": "registry/blocks/agent-pane/work.jsx",
40
+ "type": "registry:component",
41
+ "target": "@components/makersclaw/blocks/agent-pane/work.jsx",
42
+ "content": "\"use client\";\n\nimport { Fragment, useEffect, useRef, useState } from \"react\";\nimport { Button, Icon, Spinner, cx } from \"../../ui\";\n\n// The agent's work, as the desk's sidebar draws it (D27): the steps\n// between two things the agent said are ONE line, \"Worked through 26\n// steps\", or the step under way with a spinner; it opens to one line per\n// step — a mono tag, what happened in words, a status word — and a step\n// opens to its facts, the command or statement worth reading, what came\n// back and why it failed. Reads fold (\"Read 6 files\"), retries a later\n// call fixed fold (\"Fixed 2 issues on the way\"), the same line again is\n// one line with a count, helpers started side by side are a card.\n//\n// What the lines SAY and which lines there are is decided by the platform\n// (the rules the sidebar runs); this file only draws them, from the data\n// useAgentPane() hands over. A step's detail is asked for when it opens\n// (`load`), never carried with the line.\n\nconst plural = (n, one, many) => (n === 1 ? `1 ${one}` : `${n} ${many}`);\n\nconst STATUS_WORD = { failed: \"didn’t work\", interrupted: \"stopped\", denied: \"not allowed\" };\n\n/** Seconds since `running` went true; 0 while it is not. */\nfunction useElapsed(running) {\n const start = useRef(null);\n const [seconds, setSeconds] = useState(0);\n useEffect(() => {\n if (!running) return undefined;\n start.current ??= Date.now();\n const id = setInterval(() => setSeconds(Math.round((Date.now() - start.current) / 1000)), 1000);\n return () => clearInterval(id);\n }, [running]);\n return running ? seconds : 0;\n}\n\nfunction Tag({ children }) {\n return (\n <span className=\"shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-mono text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {children}\n </span>\n );\n}\n\nfunction Chevron({ open }) {\n return <Icon name={open ? \"chevron-down\" : \"chevron-right\"} size={12} className=\"shrink-0 text-muted-foreground\" />;\n}\n\n/** The row's button: tag, words, a word for how it ended, a chevron. */\nfunction Line({ tag, children, status, open, onToggle, running, elapsed, word, kind }) {\n return (\n <button\n type=\"button\"\n aria-expanded={open}\n data-status={status}\n onClick={onToggle}\n className={cx(\n \"flex w-full min-w-0 items-center gap-2 rounded-md px-1.5 py-1 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring\",\n status === \"failed\" || status === \"denied\" ? \"text-destructive\" : \"text-muted-foreground\",\n kind === \"fixed\" && \"text-success\",\n )}\n >\n <Tag>{tag}</Tag>\n <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n {running && elapsed >= 3 ? <span className=\"shrink-0 tabular-nums\">{elapsed}s</span> : null}\n {running ? <Spinner size=\"sm\" label=\"Working\" className=\"shrink-0\" /> : null}\n {word ? <span className=\"shrink-0\">{word}</span> : null}\n <Chevron open={open} />\n </button>\n );\n}\n\n/* ---- one step, opened ------------------------------------------------ */\n\nfunction Pre({ children }) {\n return (\n <pre className=\"max-h-48 overflow-auto rounded-md bg-muted p-2 font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-words text-foreground\">\n {children}\n </pre>\n );\n}\n\n/** What a step opens to: asked for once, when it opens. */\nfunction Detail({ id, load, reason, status }) {\n const [got, setGot] = useState(load ? { state: \"loading\" } : { state: \"none\" });\n useEffect(() => {\n if (!load) return undefined;\n let alive = true;\n Promise.resolve(load(id)).then(\n (r) => {\n if (alive) setGot(r?.ok && r.data?.call ? { state: \"ok\", call: r.data.call } : { state: \"error\" });\n },\n () => {\n if (alive) setGot({ state: \"error\" });\n },\n );\n return () => {\n alive = false;\n };\n }, [id, load]);\n\n const call = got.call;\n const facts = call?.facts ?? [];\n const empty = got.state === \"ok\" && !facts.length && !call.code && !call.result && !call.said && !reason;\n return (\n <div className=\"grid min-w-0 gap-2 rounded-md border border-border bg-card px-2.5 py-2 text-xs\">\n {status === \"failed\" && reason ? <p className=\"text-destructive\">{reason}</p> : null}\n {got.state === \"loading\" ? (\n <p className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Loading\" /> Opening this step…\n </p>\n ) : null}\n {got.state === \"error\" ? <p className=\"text-muted-foreground\">This step can’t be opened just now.</p> : null}\n {got.state === \"ok\" && call.said ? (\n <div className=\"grid gap-1\">\n <span className=\"text-[11px] text-muted-foreground\">What it said</span>\n <Pre>{call.said}</Pre>\n </div>\n ) : null}\n {facts.length ? (\n <dl className=\"grid min-w-0 grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1\">\n {facts.map((f, i) => (\n <Fragment key={`${f.label}-${i}`}>\n <dt className=\"text-muted-foreground\">{f.label}</dt>\n <dd className=\"min-w-0 break-words\">{f.value}</dd>\n </Fragment>\n ))}\n </dl>\n ) : null}\n {got.state === \"ok\" && call.code ? (\n <div className=\"grid gap-1\">\n <span className=\"text-[11px] text-muted-foreground\">{call.code.label}</span>\n <Pre>{call.code.text}</Pre>\n </div>\n ) : null}\n {got.state === \"ok\" && call.result ? (\n <div className=\"grid gap-1\">\n <span className=\"text-[11px] text-muted-foreground\">What came back</span>\n <Pre>{call.result}</Pre>\n </div>\n ) : null}\n {empty ? <p className=\"text-muted-foreground\">{status === \"running\" ? \"Still working on this.\" : \"Nothing more to show.\"}</p> : null}\n </div>\n );\n}\n\n/* ---- the rows --------------------------------------------------------- */\n\nfunction CallRow({ call, live, load, onOpenRequest }) {\n const [open, setOpen] = useState(false);\n const running = call.status === \"running\" && live;\n const elapsed = useElapsed(running);\n return (\n <div data-call={call.id} className=\"grid min-w-0 gap-1\">\n <Line\n tag={call.tag}\n status={call.status}\n open={open}\n onToggle={() => setOpen((o) => !o)}\n running={running}\n elapsed={elapsed}\n word={call.saysFailure ? null : (STATUS_WORD[call.status] ?? null)}\n >\n {call.line}\n </Line>\n {call.status === \"failed\" && call.reason && !open ? <p className=\"pl-1.5 text-[11px] text-destructive\">{call.reason}</p> : null}\n {call.openRequest && onOpenRequest ? (\n <div className=\"pl-1.5\">\n <Button size=\"sm\" variant=\"secondary\" onClick={() => onOpenRequest(call.openRequest)}>\n Open in Makersclaw <Icon name=\"external\" size={12} />\n </Button>\n </div>\n ) : null}\n {open ? <Detail id={call.id} load={load} reason={call.reason} status={call.status} /> : null}\n </div>\n );\n}\n\nfunction Sub({ children }) {\n return <div className=\"ml-3 grid min-w-0 gap-0.5 border-l border-border pl-2\">{children}</div>;\n}\n\nfunction ReadsRow({ row, live, load }) {\n const [open, setOpen] = useState(false);\n return (\n <div data-reads={row.calls.length} className=\"grid min-w-0 gap-0.5\">\n <Line tag={row.tag} status={row.status} open={open} onToggle={() => setOpen((o) => !o)} running={row.status === \"running\" && live}>\n {row.label}\n </Line>\n {open ? (\n <Sub>\n {row.calls.map((c) => (\n <CallRow key={c.id} call={c} live={live} load={load} />\n ))}\n </Sub>\n ) : null}\n </div>\n );\n}\n\nfunction RepeatRow({ row, live, load }) {\n const [open, setOpen] = useState(false);\n return (\n <div data-repeat={row.count} className=\"grid min-w-0 gap-0.5\">\n <Line\n tag={row.tag}\n status={row.status}\n open={open}\n onToggle={() => setOpen((o) => !o)}\n running={row.status === \"running\" && live}\n word={row.saysFailure ? null : (STATUS_WORD[row.status] ?? null)}\n >\n {row.line} · {row.count} times\n </Line>\n {row.status === \"failed\" && row.reason && !open ? <p className=\"pl-1.5 text-[11px] text-destructive\">{row.reason}</p> : null}\n {open ? (\n <Sub>\n {row.calls.map((c) => (\n <CallRow key={c.id} call={c} live={live} load={load} />\n ))}\n </Sub>\n ) : null}\n </div>\n );\n}\n\nfunction FixedRow({ row }) {\n const [open, setOpen] = useState(false);\n const n = row.fixes.length;\n return (\n <div data-fixed={n} className=\"grid min-w-0 gap-0.5\">\n <Line tag=\"FIXED\" kind=\"fixed\" open={open} onToggle={() => setOpen((o) => !o)}>\n {n === 1 ? \"Fixed 1 issue on the way\" : `Fixed ${n} issues on the way`}\n </Line>\n {open ? (\n <ol className=\"ml-3 grid min-w-0 gap-1.5 border-l border-border pl-2 text-xs text-muted-foreground\">\n {row.fixes.map((f) => (\n <li key={f.id} className=\"grid min-w-0 gap-0.5\">\n <span className=\"break-words\">\n {f.what}: <span className=\"text-destructive\">{f.reason}</span>\n </span>\n <span className=\"break-words\">→ {f.then}</span>\n </li>\n ))}\n </ol>\n ) : null}\n </div>\n );\n}\n\nconst HELPER_WORD = { running: \"Working\", waiting: \"Waiting for you\", done: \"Done\", failed: \"Didn’t finish\", denied: \"Not allowed\", interrupted: \"Stopped\" };\n\nfunction HelperMark({ status }) {\n if (status === \"done\") return <Icon name=\"circle-check\" size={14} className=\"shrink-0 text-success\" />;\n if (status === \"running\") return <Spinner size=\"sm\" label=\"Working\" className=\"shrink-0 text-muted-foreground\" />;\n if (status === \"failed\" || status === \"denied\") return <Icon name=\"circle-x\" size={14} className=\"shrink-0 text-destructive\" />;\n return <Icon name=\"circle\" size={14} className=\"shrink-0 text-muted-foreground\" />;\n}\n\n/** Helpers started side by side: a card, one row each; a row opens to its step. */\nfunction HelpersCard({ row, load }) {\n const [open, setOpen] = useState(null);\n return (\n <div role=\"group\" aria-label={`${row.calls.length} helpers`} data-helpers={row.calls.length} className=\"grid min-w-0 divide-y divide-border overflow-hidden rounded-lg border border-border bg-card\">\n {row.calls.map((c) => (\n <div key={c.id} className=\"grid min-w-0\">\n <button\n type=\"button\"\n aria-expanded={open === c.id}\n data-state={c.status}\n onClick={() => setOpen((o) => (o === c.id ? null : c.id))}\n className=\"flex min-w-0 items-center gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring\"\n >\n <HelperMark status={c.status} />\n <span className=\"min-w-0 flex-1 truncate\">{c.line}</span>\n <span className=\"shrink-0 text-muted-foreground\">{HELPER_WORD[c.status] ?? \"\"}</span>\n </button>\n {open === c.id ? (\n <div className=\"px-2 pb-2\">\n <Detail id={c.id} load={load} reason={null} status={c.status} />\n </div>\n ) : null}\n </div>\n ))}\n </div>\n );\n}\n\n/** The model's own reasoning is not carried: the line says it thought. */\nfunction ThoughtRow({ row }) {\n return (\n <div className=\"flex min-w-0 items-center gap-2 px-1.5 py-1 text-xs text-muted-foreground\">\n <Tag>THINK</Tag>\n <span className=\"min-w-0 flex-1 truncate\">{row.streaming ? \"Thinking it through…\" : \"Thought it through\"}</span>\n {row.streaming ? <Spinner size=\"sm\" label=\"Thinking\" className=\"shrink-0\" /> : null}\n </div>\n );\n}\n\nfunction Row({ row, live, load, onOpenRequest }) {\n switch (row.kind) {\n case \"call\":\n return <CallRow call={row} live={live} load={load} onOpenRequest={onOpenRequest} />;\n case \"reads\":\n return <ReadsRow row={row} live={live} load={load} />;\n case \"repeat\":\n return <RepeatRow row={row} live={live} load={load} />;\n case \"fixed\":\n return <FixedRow row={row} />;\n case \"helpers\":\n return <HelpersCard row={row} load={load} />;\n case \"thought\":\n return <ThoughtRow row={row} />;\n default:\n return null;\n }\n}\n\n/** A run of lines as one line, closed until opened; while a step runs the\n * line is that step with a spinner and the count so far. A failure\n * nothing fixed is counted on it, so a closed fold never hides it. */\nfunction Fold({ run, live, load, onOpenRequest }) {\n const [open, setOpen] = useState(false);\n const sum = run.summary;\n const running = sum.running && live;\n const stopped = !running && (sum.stopped || sum.running);\n const label = running ? (sum.latest ?? \"Working\") : stopped ? `Stopped after ${plural(sum.steps, \"step\", \"steps\")}` : `Worked through ${plural(sum.steps, \"step\", \"steps\")}`;\n return (\n <div className=\"grid min-w-0 gap-1\" data-open={open || undefined}>\n <button\n type=\"button\"\n aria-expanded={open}\n data-status={running ? \"running\" : stopped ? \"interrupted\" : \"done\"}\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full min-w-0 items-center gap-2 rounded-md border border-border px-2.5 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted focus-visible:outline-2 focus-visible:outline-ring\"\n >\n {running ? <Spinner size=\"sm\" label=\"Working\" className=\"shrink-0\" /> : null}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n {running ? <span className=\"shrink-0\">{plural(sum.steps, \"step\", \"steps\")}</span> : null}\n {sum.failed > 0 ? <span className=\"shrink-0 text-destructive\">{sum.failed} didn’t work</span> : null}\n <Chevron open={open} />\n </button>\n {open ? (\n <div className=\"grid min-w-0 gap-0.5 pl-1\">\n {run.rows.map((row) => (\n <Row key={row.id} row={row} live={live} load={load} onOpenRequest={onOpenRequest} />\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n\n/** The work between two things the agent said: folded into one line, with\n * a helpers card and a lone line standing as they are. */\nexport function WorkBlock({ runs, live, load, onOpenRequest }) {\n return (\n <div className=\"grid min-w-0 gap-1.5\">\n {runs.map((run) =>\n run.kind === \"fold\" ? (\n <Fold key={run.id} run={run} live={live} load={load} onOpenRequest={onOpenRequest} />\n ) : (\n <Row key={run.row.id} row={run.row} live={live} load={load} onOpenRequest={onOpenRequest} />\n ),\n )}\n </div>\n );\n}\n"
43
+ },
44
+ {
45
+ "path": "registry/blocks/agent-pane/prose.jsx",
46
+ "type": "registry:component",
47
+ "target": "@components/makersclaw/blocks/agent-pane/prose.jsx",
48
+ "content": "\"use client\";\n\nimport { Fragment } from \"react\";\nimport { cx } from \"../../ui\";\n\n// The agent's text: plain words with the markdown agents write —\n// paragraphs, lists, headings, fenced code, **bold**, `code`, [links](https://…).\n// React nodes only, never HTML. A link opens in a new tab, and only an\n// http(s) address is a link.\n\nconst INLINE = /(\\*\\*[^*]+\\*\\*|`[^`]+`|\\[[^\\]]+\\]\\((?:https?:\\/\\/)[^)\\s]+\\))/g;\n\nfunction inline(text, key) {\n const out = [];\n let last = 0;\n let i = 0;\n for (const m of String(text).matchAll(INLINE)) {\n if (m.index > last) out.push(text.slice(last, m.index));\n const tok = m[0];\n const k = `${key}-${i++}`;\n if (tok.startsWith(\"**\")) out.push(<strong key={k}>{tok.slice(2, -2)}</strong>);\n else if (tok.startsWith(\"`\")) out.push(<code key={k} className=\"rounded bg-muted px-1 font-mono text-[0.92em]\">{tok.slice(1, -1)}</code>);\n else {\n const label = tok.slice(1, tok.indexOf(\"](\"));\n const href = tok.slice(tok.indexOf(\"](\") + 2, -1);\n out.push(\n <a key={k} href={href} target=\"_blank\" rel=\"noopener noreferrer\" className=\"break-all text-primary underline underline-offset-2\">\n {label}\n </a>,\n );\n }\n last = m.index + tok.length;\n }\n if (last < text.length) out.push(text.slice(last));\n return out;\n}\n\nconst LIST_ITEM = /^\\s*(?:[-*•]|\\d+[.)])\\s+/;\n\n/** Paragraphs, lists and fenced code from the agent's text. */\nexport function Prose({ text, className }) {\n // Fenced code first: it may hold blank lines.\n const parts = String(text ?? \"\").replace(/\\r/g, \"\").split(/^```[^\\n]*\\n([\\s\\S]*?)^```[ \\t]*$/m);\n return (\n <div className={cx(\"grid min-w-0 gap-2 break-words\", className)}>\n {parts.map((part, p) => {\n if (p % 2 === 1)\n return (\n <pre key={p} className=\"max-h-72 overflow-auto rounded-md bg-muted p-2.5 font-mono text-xs leading-relaxed whitespace-pre-wrap break-words\">\n {part.replace(/\\n$/, \"\")}\n </pre>\n );\n return part\n .split(/\\n{2,}/)\n .map((block, b) => {\n const lines = block.split(\"\\n\").filter((l) => l.trim());\n if (!lines.length) return null;\n const key = `${p}-${b}`;\n if (lines.every((l) => LIST_ITEM.test(l))) {\n const ordered = /^\\s*\\d/.test(lines[0]);\n const List = ordered ? \"ol\" : \"ul\";\n return (\n <List key={key} className={cx(\"grid gap-1 pl-5\", ordered ? \"list-decimal\" : \"list-disc\")}>\n {lines.map((l, j) => (\n <li key={j}>{inline(l.replace(LIST_ITEM, \"\"), `${key}-${j}`)}</li>\n ))}\n </List>\n );\n }\n return (\n <p key={key} className=\"whitespace-pre-wrap\">\n {lines.map((l, j) => (\n <Fragment key={j}>\n {j > 0 ? \"\\n\" : null}\n {/^#{1,6}\\s/.test(l) ? <strong>{inline(l.replace(/^#{1,6}\\s+/, \"\"), `${key}-${j}`)}</strong> : inline(l, `${key}-${j}`)}\n </Fragment>\n ))}\n </p>\n );\n });\n })}\n </div>\n );\n}\n"
49
+ },
50
+ {
51
+ "path": "registry/blocks/agent-pane/questions.jsx",
52
+ "type": "registry:component",
53
+ "target": "@components/makersclaw/blocks/agent-pane/questions.jsx",
54
+ "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button, Icon, Input, cx } from \"../../ui\";\n\n// The agent's questions, as the desk's sidebar asks them (D27): every\n// question waiting is one form — its words, its options as chips, a box\n// for the member's own words where it welcomes them — and one submit\n// answers them all in a single resume, so an eight-question setup is one\n// form fill, not eight round trips. A lone question with options keeps the\n// old immediacy: a press on an option answers it.\n//\n// Fed by useAgentPane(): `questions` is chat.view.questions, `onSubmit` is\n// chat.answer({ answers }) → { ok } | { ok: false, error }. The door checks\n// each answer against the question as the session holds it.\n\nexport function QuestionForm({ questions, onSubmit, className }) {\n const [answers, setAnswers] = useState({});\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState(null);\n const [sent, setSent] = useState(false);\n if (!onSubmit || !questions?.length) return null;\n const single = questions.length === 1;\n\n const answered = (q) => {\n const a = answers[q.id];\n return Boolean(a?.optionId || a?.text?.trim());\n };\n const complete = questions.every(answered);\n // A lone question that takes its options only is answered by a press on\n // one; every other form has its submit.\n const showSend = !single || questions[0].free !== false || !(questions[0].options?.length ?? 0);\n\n async function send(list) {\n if (busy || sent) return;\n setBusy(true);\n setError(null);\n let r;\n try {\n r = await onSubmit(list);\n } catch (e) {\n r = { ok: false, error: e?.message ?? String(e) };\n }\n setBusy(false);\n if (r && r.ok === false) setError(r.error || \"That did not go through. Try again.\");\n else setSent(true);\n }\n const submit = () => {\n if (!complete) return;\n send(\n questions.map((q) => {\n const a = answers[q.id];\n return a?.optionId ? { requestId: q.id, optionId: a.optionId } : { requestId: q.id, text: a?.text?.trim() ?? \"\" };\n }),\n );\n };\n\n if (sent)\n return (\n <p role=\"status\" className={cx(\"flex items-center gap-2 text-sm text-success\", className)}>\n <Icon name=\"check\" size={14} className=\"shrink-0\" /> Sent. The agent is on it.\n </p>\n );\n\n return (\n <form\n aria-label={single ? \"A question for you\" : `${questions.length} questions for you`}\n className={cx(\"grid min-w-0 gap-3\", className)}\n onSubmit={(e) => {\n e.preventDefault();\n submit();\n }}\n >\n {questions.map((q, index) => {\n const a = answers[q.id];\n const hasOptions = (q.options?.length ?? 0) > 0;\n const free = q.free !== false || !hasOptions;\n return (\n <div key={q.id} className=\"grid min-w-0 gap-1.5\">\n <p className=\"flex min-w-0 items-start gap-2 text-sm font-medium\">\n {single ? null : (\n <span className=\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] tabular-nums text-muted-foreground\">{index + 1}</span>\n )}\n <span className=\"min-w-0 break-words\">{q.prompt}</span>\n </p>\n {hasOptions ? (\n <div className=\"flex min-w-0 flex-wrap gap-1.5\">\n {q.options.map((o) => (\n <Button\n key={o.id}\n size=\"sm\"\n variant={a?.optionId === o.id ? \"primary\" : \"secondary\"}\n aria-pressed={a?.optionId === o.id}\n disabled={busy}\n onClick={() => {\n // A lone question with options answers on the spot.\n if (single) {\n send([{ requestId: q.id, optionId: o.id }]);\n return;\n }\n setAnswers((prev) => ({ ...prev, [q.id]: prev[q.id]?.optionId === o.id ? {} : { optionId: o.id } }));\n }}\n >\n {o.label}\n </Button>\n ))}\n </div>\n ) : null}\n {free ? (\n <Input\n aria-label={hasOptions ? `Or answer “${q.prompt}” in your own words` : `Answer “${q.prompt}”`}\n placeholder={hasOptions ? \"Or answer in your own words…\" : \"Your answer…\"}\n value={a?.text ?? \"\"}\n disabled={busy}\n onChange={(e) => setAnswers((prev) => ({ ...prev, [q.id]: e.target.value ? { text: e.target.value } : {} }))}\n className=\"min-w-0\"\n />\n ) : null}\n </div>\n );\n })}\n {showSend ? (\n <Button size=\"sm\" variant=\"primary\" type=\"submit\" loading={busy} disabled={!complete || busy} className=\"justify-self-start\">\n {single ? \"Send answer\" : \"Send answers\"}\n </Button>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"text-xs text-destructive\">\n {error}\n </p>\n ) : null}\n </form>\n );\n}\n"
55
+ },
56
+ {
57
+ "path": "registry/blocks/agent-pane/composer.jsx",
58
+ "type": "registry:component",
59
+ "target": "@components/makersclaw/blocks/agent-pane/composer.jsx",
60
+ "content": "\"use client\";\n\n// The pane's composer parts — files to send with the words, and dictation —\n// are the chat-pane block's now (X5): the composer is the desk's box, and\n// this file keeps the names agent-pane has always exported.\nexport { MAX_BYTES, MAX_FILES, addFiles, filesFromTransfer, formatBytes, readFile, useDictation } from \"../chat-pane\";\n"
61
+ },
62
+ {
63
+ "path": "registry/blocks/agent-pane/README.md",
64
+ "type": "registry:file",
65
+ "target": "@components/makersclaw/blocks/agent-pane/README.md",
66
+ "content": "# AgentPane\n\nThe desk's agent sidebar, as the app's own pane, in the app's theme (0.8.1,\nD27). It replaces `agent-chat`: the founder found that chat \"not on par\nwith the desk agent sidebar, in terms of tool calls, questions, showing\ninformation\", so this is the sidebar, not a smaller chat.\n\nIt shows what the sidebar shows:\n\n- the member's words, and the agent's text as it is written;\n- the **plan**, a checklist that ticks as the agent works;\n- the **work**, folded: the steps between two things the agent said are one\n line, \"Worked through 26 steps\" (or the step running now, with a\n spinner); open it for one line per step, each with a mono tag, what it\n did in words, and a word for how it ended. **Open a step** for its facts,\n the command or statement worth reading, what came back, and why it\n failed;\n- reads folded (\"Read 6 files\"), retries a later call fixed (\"Fixed 2\n issues on the way\"), the same line again (\"Checked on the build · 5\n times\"), a failure nothing fixed with its few plain words, and a card of\n **helpers** started side by side;\n- the agent's **questions** as one form: several at once, options as\n chips, a box for the member's own words, one submit; an answered one\n shrinks to a line;\n- the one ask's **yes** as the `approval` block's press frame (`yes`);\n- what it **made**: an app, files, addresses;\n- the **past chats** (a dot on those that need you or work), New chat,\n Stop (\"Stopping…\" after the press), \"You stopped it\", \"It stopped before\n finishing\" in plain words, jump to latest.\n\nIt is the same conversation the desk's sidebar shows: one session, two\nviews. The lines and what they say are decided by the platform, with the\nrules the sidebar runs, and sent as data; this block draws them and nothing\nelse. So a new tool the platform learns to describe reads right in every\napp at once, with no change here.\n\n```jsx\nimport { AgentPane } from \"../components/blocks/agent-pane\"; // a Makersclaw app (\"./blocks/agent-pane\" from components/)\nimport { AgentPane } from \"@/components/makersclaw/blocks/agent-pane\"; // a shadcn project\n```\n\nA client component. It draws the object `useAgentPane()` returns\n(lib/hooks.js); asking stays with the hook. The Example is at the end.\n\n## The frame is `chat-pane`\n\nThe head (title, New chat, Chats, close), the empty state, the scroll, the\nmessage box and a starting line from the dock are the `chat-pane` block\n(X5): the desk's chat pane, ported. This block is what the **agent** puts in\nit: the lines (plan, folded work, questions, what it made) and the bar of what\nwaits on the member. `AgentPane` is `ChatPane` with those inside, so every\napp that draws `AgentPane` has the desk's message box: the words on top, + to\nattach and a round send under them, the microphone beside it, a round stop in\nthe send's place while a turn runs.\n\n## Follow-ups are the same conversation\n\nA message after a Stop, a finished ask or a failed turn goes on in the\nsame session and the same request (D29): the platform opens the closed\nrequest again, so there is no second card and nothing jumps. The pane\nsends every message with the thread's session (`useAgentPane` never starts\na fresh one for it) and says \"Ask a follow-up…\" once a thread exists.\nOnly a plan or a piece of one, a change the agent sent up, a counted job\nand a hand-off that never started get a new request; for those\n`chat.sameConversation` is false and the box keeps the app's own words.\n\n## Questions keep the conversation open\n\nWhile a question waits, the main composer stays available. Sending there\nskips that question and continues the same thread, including attachments.\nAnswering the question removes it immediately while the answer is sent; a\nfailed submission restores it for retry. Stale thread reads cannot bring an\naccepted question back, but a new question still appears. Approval, account\nconnection and credit-limit gates keep their existing controls.\n\n## Tell the desk\n\nAn app that places this block says so in its package: `\"agent_pane\": true`\nin `store.json`'s `seeds.blueprint` (a built app: in its blueprint). The\ndesk then draws no agent sidebar for the app's window: no column, no Agent\ntab. The pane is the conversation. Without the flag the desk keeps its\nsidebar, and the member sees two agent panes.\n\n## What the app controls\n\n- **Where it sits**: in the app's `agent-dock` (a column beside the work, the\n desk's own shape; one per app, in the shell). Inside a dock the head's close\n folds the column with no `onClose` from you. A compact card in a column of\n its own is also fine (`fill={false}`). Do not put the shell's agent in a\n modal `Sheet` that dims the work: that is what the dock replaced. `bare`\n drops the head.\n- **What it is about**: `useAgentPane({ row })`. `row` is the record on\n screen (`{ kind, id, name }`); it rides with every ask as context and the\n thread is kept for that record. `how` is one sentence for the agent only.\n The ask is `chat.send(text)`, so an Ask button, an ask bar or a chip all\n start or continue the same conversation.\n- **Its first line**: a button that says the agent will do the work calls\n `openAgent(\"Draft a project plan for me\")` (blocks/agent-dock). The pane\n inside the dock sends that line in a new chat and the agent asks only for\n what it is missing; no form first (X9).\n- **Its words**: `title`, `empty`, `suggestions`, `placeholder`, `hint`.\n- **Nothing else.** No colour props: the app's theme paints it.\n\n## Props\n\n- `chat` — what `useAgentPane()` returns. Required.\n- `title` — the head's words, and the box's label. Default `\"Ask the\n agent\"`. About a record: `\"Ask about Pinewood\"`.\n- `suggestions` — asks that fit the moment, as chips before the first ask.\n- `empty` — one line before the first ask. Say what the agent can do here,\n in the app's words.\n- `placeholder` — the box's words. Default `\"Ask anything…\"`.\n- `hint` — a quiet line under the box.\n- `yes` — a node drawn in place of the bar when the agent waits on a yes\n the app can take (`chat.needs.in_app === \"press\"`): the `approval` block,\n fed by `useApproval(chat.request.id)`. Hook it only for that need:\n `useApproval(chat.needs?.in_app === \"press\" ? chat.request?.id : null)`.\n- `mark` — a node before the agent's turns (the app's mark). Default none.\n- `onOpen` — `({ kind: \"app\", id } | { kind: \"file\", path, where, appRef })`:\n the Open of something it made. Default: `chat.open` (the desk opens it).\n- `onClose` — a close in the head. Inside an `agent-dock` it is the dock's\n own (the column folds) unless you give one.\n- `recent` — how many past chats the empty state lists before the first ask,\n with an \"All N chats\" link past that. Default `5`; `0` lists none. With no\n chats yet it says \"No chats yet. Your first message starts one.\" The head\n always has New chat and Chats.\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 thread scrolls inside it. `false`: a compact pane for a card in a column;\n once a thread starts it scrolls inside a capped height.\n- `className` — layout around it. Never colour.\n\n## useAgentPane (lib/hooks.js)\n\n```js\nconst chat = useAgentPane({ row, how, job, key });\n```\n\nIt is `useAgentThread` with the sidebar's view, plus the Chats list and a\nstep's detail. It returns everything `useAgentThread` returns (`sessionId`,\n`state`, `needs`, `pending`, `place`, `request`, `sending`, `loading`,\n`error`, `unavailable`, `send`, `stop`, `answer`, `openOnDesk`, `newChat`,\n`reload`), and:\n\n- `view` — `{ messages, thinking, failure, questions }`: the thread as the\n sidebar draws it. A claw from before the view sends none; the pane then\n draws the small turns plainly.\n- `chats` — `{ items: [{ sessionId, title, mark, at }], loading, error,\n unavailable }`, the member's own threads in this app.\n- `openSession(sessionId)` — switch to one of them.\n- `callDetail(callId)` — one step, opened: `{ facts, code, result, reason,\n said }`, asked when a member opens a step, never carried with the line.\n- `answer({ answers: [{ requestId, optionId | text }] })` — every question\n the view lists, as one resume.\n- `openRequest(requestId)` — \"Open in Makersclaw\" under a change the agent\n sent up.\n\nIt reads `GET _claw/threads/<session>?view=1` and `GET _claw/threads` (lib/\nplatform.js `thread`, `threads`), the member's own threads in this app,\nnever a teammate's, and streams a working thread (`streamThread`). It looks\nevery 1.5 s while the agent works and no stream is alive, every 6 s while\nit waits on the member, and not at all once the turn has ended or the page\nis hidden. No tool's input or output is ever sent; a step's detail is\na bounded digest.\n\n## The composer, the stream, the press, Open\n\n- **Files.** The + in the box, paste and drop attach up to 4 files and 3 MB\n together; they go with the words (`chat.send(text, { files })`, `ask({\n files })`) as the desk's attachments. A message that is queued behind\n another goes without them, and `chat.notice` says so.\n- **Voice.** A microphone button dictates into the box with the browser's\n speech recognition; it is not drawn where the browser has none.\n- **Stop.** While a turn runs and the box is empty, a round stop takes the\n send arrow's place (a message typed then waits its turn: the arrow comes\n back). \"Stopping…\" after the press.\n- **Live.** A working thread is streamed (`GET _claw/threads/<session>/\n stream`): text and steps appear as they are written. The poll looks\n rarely while the stream is alive and takes over if it ends or the claw\n has none.\n- **The yes, for a tool too.** `yes` is drawn whenever `chat.needs.in_app`\n is `\"press\"`: the one ask's yes, or one parked tool call (the card says\n what it will do and shows its command). Several calls parked together\n stay on the desk.\n- **Open.** Under an app or a file it made, Open asks the desk to open it\n (`chat.open`); `onOpen` overrides.\n\n## Outside Makersclaw\n\nAny object of the same shape works: `view` (`messages` of `{ id, role:\n\"user\", text, files }` or `{ id, role: \"assistant\", live, blocks }`), the\nfields above, and the functions.\n\n## Example\n\nA pane about the person a screen has open, beside the record.\n\n```jsx\n\"use client\";\nimport { useAgentPane } from \"../lib/hooks\";\nimport { AgentPane } from \"../components/blocks/agent-pane\";\n\nexport default function PersonAsk({ person }) {\n const chat = useAgentPane({ row: { kind: \"person\", id: person.id, name: person.name } });\n return (\n <AgentPane\n chat={chat}\n title={`Ask about ${person.name}`}\n suggestions={[\"What did we last talk about?\", \"Draft a follow-up\"]}\n />\n );\n}\n```\n\nWith the yes pressed in the approval card above the box:\n\n```jsx\n\"use client\";\nimport { useAgentPane, useApproval } from \"../lib/hooks\";\nimport { AgentPane } from \"../components/blocks/agent-pane\";\nimport { Approval } from \"../components/blocks/approval\";\n\nexport default function Ask() {\n const chat = useAgentPane();\n // Only for the one ask's yes; useApproval(null) asks nothing.\n const approval = useApproval(chat.needs?.in_app === \"press\" ? chat.request?.id : null);\n return (\n <AgentPane\n chat={chat}\n yes={<Approval approval={approval} title=\"Waiting for your yes\" onOpenDesk={chat.openOnDesk} />}\n />\n );\n}\n```\n"
67
+ }
68
+ ],
69
+ "categories": [
70
+ "chat",
71
+ "ask",
72
+ "assistant",
73
+ "conversation",
74
+ "thread",
75
+ "sidebar",
76
+ "tool calls",
77
+ "steps",
78
+ "plan",
79
+ "questions",
80
+ "stop",
81
+ "ai",
82
+ "copilot",
83
+ "inspector",
84
+ "record"
85
+ ],
86
+ "docs": "Installed to components/makersclaw/blocks/agent-pane. Import: import { AgentPane, AgentDockContext } from \"@/components/makersclaw/blocks/agent-pane\". Props and an example: components/makersclaw/blocks/agent-pane/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
87
+ "meta": {
88
+ "makersclaw": {
89
+ "version": "0.8.2",
90
+ "useWhen": "The app should hold its conversation with its agent at the level of the desk's sidebar: an Ask panel beside a record, a sheet behind an Ask button, an inspector on a home screen. It replaces agent-chat. Place it and set \"agent_pane\": true in the package's blueprint, so the desk draws no sidebar for the app's window. For work the founder commissions from a verb and watches land, use job-panel."
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "ai-action",
4
+ "type": "registry:block",
5
+ "title": "AiAction",
6
+ "description": "One model call on what the screen holds — Summarize, Shorter, Explain this score, Why did it move — with its price on the button before the press (\"Shorter · 1 cr\"), a running state, the answer in place (its text, or your render of a schema's data) with Use, Copy and Again, the refusal's own words when it fails, and a quiet \"not connected yet\" on a claw without the door. It draws what useLlmAction() returns (lib/hooks.js) and wears the dashed one-call mark, never the agent's diamond.",
7
+ "registryDependencies": [
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/agent-marks.json",
10
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/cost.json"
11
+ ],
12
+ "dependencies": [],
13
+ "files": [
14
+ {
15
+ "path": "registry/blocks/ai-action/index.js",
16
+ "type": "registry:component",
17
+ "target": "@components/makersclaw/blocks/ai-action/index.js",
18
+ "content": "// The AiAction block — `makersclaw add ai-action`. Fixed: compose it\n// through its props (README.md beside this file).\nexport { AiAction, priceWords } from \"./ai-action\";\n"
19
+ },
20
+ {
21
+ "path": "registry/blocks/ai-action/ai-action.jsx",
22
+ "type": "registry:component",
23
+ "target": "@components/makersclaw/blocks/ai-action/ai-action.jsx",
24
+ "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Button, Icon, IconButton, Spinner, cx } from \"../../ui\";\nimport { CallMark } from \"../agent-marks\";\nimport { CostTag, creditsWords } from \"../cost\";\n\n// AiAction — one model call on what the screen holds (the app kit, D15):\n// Summarize, Shorter, Explain this score, Why did it move. Its price is on\n// the button before the press (\"Shorter · 1 cr\"), the answer appears in\n// place with Use, Copy and Again, a refusal says its words, and a claw\n// without the door is a quiet \"not connected yet\".\n//\n// const shorter = useLlmAction({ input: draft, system: \"Make it shorter. Keep the facts.\" });\n// <AiAction label=\"Shorter\" action={shorter} onUse={(r) => setDraft(r.text)} />\n//\n// It draws what useLlmAction() returns (lib/hooks.js). One call is the\n// dashed CallMark, never the agent's diamond: no tools, no conversation.\n\n/** The price as words, for a screen reader and for callers: \"1 cr\", \"1–3 cr\".\n * What shows on the button is the cost block's CostTag. */\nexport function priceWords(estimate) {\n if (!estimate) return null;\n const low = creditsWords(Number(estimate.credits));\n if (low == null) return null;\n const high = creditsWords(Number(estimate.credits_max));\n return high && high !== low && Number(estimate.credits_max) > Number(estimate.credits) ? `${low}–${high} cr` : `${low} cr`;\n}\n\n/** The answer's words, when it has words: a string, { text }, or { ok, text }. */\nfunction textOf(result) {\n if (result == null) return null;\n if (typeof result === \"string\") return result;\n if (typeof result.text === \"string\") return result.text;\n return null;\n}\n\n/** Its data, when it came back shaped (a schema): { data } or an object. */\nfunction dataOf(result) {\n if (result == null || typeof result === \"string\") return undefined;\n if (\"data\" in result) return result.data;\n return typeof result.text === \"string\" ? undefined : result;\n}\n\n/**\n * label the verb (\"Shorter\", \"Explain this score\")\n * icon a kit Icon name for the button; default the call's mark\n * action what useLlmAction() returns: { estimate, estimating,\n * run(), result, loading, error, unavailable, reset() }\n * render(result) draws the answer your way (a schema's data); default\n * the text as written\n * onUse(result) puts the answer to work (replace the draft); without\n * it there is no Use\n * useLabel default \"Use\"\n * title the answer's head (default the label)\n * className layout around it. Never colour.\n */\nexport function AiAction({ label, icon, action, render, onUse, useLabel = \"Use\", title, className }) {\n const [copied, setCopied] = useState(false);\n const [used, setUsed] = useState(false);\n const timer = useRef(null);\n useEffect(() => () => clearTimeout(timer.current), []);\n\n const a = action ?? {};\n const price = priceWords(a.estimate);\n const result = a.result;\n const text = textOf(result);\n const data = dataOf(result);\n const has = result != null && (text != null ? text.trim() !== \"\" : data !== undefined);\n const busy = Boolean(a.loading);\n\n if (a.unavailable) {\n return (\n <p className={cx(\"inline-flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground\", className)}>\n <CallMark />\n <span className=\"min-w-0 break-words\">{label} is not connected yet.</span>\n </p>\n );\n }\n\n const press = () => {\n if (busy || !a.run) return;\n setUsed(false);\n a.run();\n };\n\n async function copy() {\n if (text == null) return;\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), 1600);\n } catch {\n setCopied(false);\n }\n }\n\n return (\n <div className={cx(\"@container grid grid-cols-1 min-w-0 gap-2 text-sm\", className)}>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"max-w-full justify-self-start\"\n loading={busy}\n disabled={busy || a.estimating || !a.run}\n onClick={press}\n aria-label={price ? `${label}, ${price === \"1 cr\" ? \"1 credit\" : price.replace(\" cr\", \" credits\")}` : undefined}\n >\n {icon ? <Icon name={icon} size={14} /> : <CallMark />}\n <span className=\"min-w-0 truncate\">{label}</span>\n {price || a.estimating ? (\n <>\n <span aria-hidden=\"true\">·</span>\n <CostTag credits={Number(a.estimate?.credits)} max={Number(a.estimate?.credits_max)} loading={!price} icon={false} />\n </>\n ) : null}\n </Button>\n\n {busy ? (\n <p role=\"status\" className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Asking\" /> One call to the model…\n </p>\n ) : null}\n\n {a.error && !busy ? (\n <div role=\"alert\" className=\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-destructive\">\n <Icon name=\"warning\" size={14} className=\"shrink-0\" />\n <span className=\"min-w-0 break-words\">{a.error}</span>\n {a.run ? (\n <Button variant=\"link\" size=\"inherit\" onClick={press}>\n Try again\n </Button>\n ) : null}\n </div>\n ) : null}\n\n {has && !busy ? (\n <section aria-label={`${title ?? label}: the answer`} className=\"grid grid-cols-1 min-w-0 gap-2 rounded-[var(--radius)] border border-dashed border-border bg-card p-3 text-card-foreground\">\n <div className=\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\">\n <CallMark>{title ?? label}</CallMark>\n <span className=\"shrink-0\">· 1 call</span>\n {a.reset ? <IconButton icon=\"x\" size=\"sm\" label=\"Put it away\" className=\"ml-auto -my-1\" onClick={a.reset} /> : null}\n </div>\n <div className=\"min-w-0 break-words\">\n {render ? render(result) : text != null ? <p className=\"whitespace-pre-wrap\">{text}</p> : <pre className=\"overflow-x-auto whitespace-pre-wrap text-xs\">{JSON.stringify(data, null, 2)}</pre>}\n </div>\n <div className=\"flex min-w-0 flex-wrap items-center gap-1.5\">\n {onUse ? (\n used ? (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-success\">\n <Icon name=\"check\" size={14} /> Used\n </span>\n ) : (\n <Button\n size=\"sm\"\n variant=\"primary\"\n onClick={() => {\n onUse(result);\n setUsed(true);\n }}\n >\n {useLabel}\n </Button>\n )\n ) : null}\n {text != null ? (\n <Button size=\"sm\" variant=\"quiet\" onClick={copy}>\n <Icon name={copied ? \"check\" : \"copy\"} size={14} /> {copied ? \"Copied\" : \"Copy\"}\n </Button>\n ) : null}\n {a.run ? (\n <Button size=\"sm\" variant=\"quiet\" onClick={press}>\n <Icon name=\"refresh\" size={14} /> Again\n {price ? (\n <>\n <span aria-hidden=\"true\">·</span>\n <CostTag credits={Number(a.estimate?.credits)} max={Number(a.estimate?.credits_max)} icon={false} />\n </>\n ) : null}\n </Button>\n ) : null}\n </div>\n </section>\n ) : null}\n </div>\n );\n}\n"
25
+ },
26
+ {
27
+ "path": "registry/blocks/ai-action/README.md",
28
+ "type": "registry:file",
29
+ "target": "@components/makersclaw/blocks/ai-action/README.md",
30
+ "content": "# AiAction\n\nOne model call on what the screen already holds: **Summarize**,\n**Shorter**, **Explain this score**, **Why did it move**.\n\n- The price is on the button **before** the press, drawn by the `cost`\n block's `CostTag`: \"Shorter · about 1 cr\" (the most it can use is in\n its tooltip).\n- While it runs, the button spins and the line says \"One call to the\n model…\".\n- The answer appears in place, with **Use**, **Copy** and **Again**.\n- When the platform refuses, its own words show (\"The app's weekly\n allowance is used up.\"), with Try again.\n- On a claw without the door, a quiet \"Shorter is not connected yet\".\n\nOne call is not the agent. It has no tools and no conversation, so it\nwears the dashed one-call mark (`CallMark` from `agent-marks`), never the\nagent's diamond.\n\n```jsx\nimport { AiAction } from \"../components/blocks/ai-action\"; // a Makersclaw app (\"./blocks/ai-action\" from components/)\nimport { AiAction } from \"@/components/makersclaw/blocks/ai-action\"; // a shadcn project\n```\n\nA client component. It requires `agent-marks` (laid with it).\n\n## Which hook feeds it\n\n```js\nconst shorter = useLlmAction({ input: draft, system: \"Make it shorter. Keep every fact.\" }); // lib/hooks.js\n// → { estimate: { credits, credits_max } | null, estimating, run(), result, loading, error, unavailable, reset() }\n```\n\n`useLlmAction` prices the call once per distinct input (`POST _claw/llm`\nwith `estimate: true`: checked, never sent to a model, never charged),\nand `run()` makes it. `result` is the answer: `{ text }`, or `{ data }`\nwith a `schema`.\n\n## Props\n\n- `label` — the verb, in the app's words (\"Shorter\", \"Explain this\n score\").\n- `action` — what `useLlmAction()` returns. Required.\n- `icon` — a kit `Icon` name for the button. Default the one-call mark.\n- `render(result)` — draws the answer your way: a schema's `data` as a\n list, a score's reasons. Default the text as written (a `data` without\n `render` shows as plain JSON).\n- `onUse(result)` — puts the answer to work: replace the draft, fill the\n field. Without it there is no Use. After a Use the button says \"Used\".\n- `useLabel` — default \"Use\" (\"Replace the draft\").\n- `title` — the answer's head. Default the label.\n- `className` — layout around it. Never colour.\n\n`priceWords({ credits, credits_max })` is exported for a screen reader\nand for callers: \"1 cr\", \"1–3 cr\".\n\n## Who may do what\n\nAny member who can open the app. The door holds the caps: the workspace's\nwallet, the member's monthly cap, the app's weekly allowance, 30 calls a\nminute. A refusal is shown in its own words; never retry it in a loop.\n\n## Never\n\n- **Never press without the price.** The button says it before the\n press; while it is being priced the button waits.\n- Never write the answer into a record by itself: Use is the member's\n press, and `onUse` does the write.\n- Never use it for work that needs tools (reading records, searching the\n web, sending): that is an ask to the agent.\n- Never draw the agent's diamond on it.\n\n## Example\n\nA draft with Shorter beside it.\n\n```jsx\n\"use client\";\nimport { useState } from \"react\";\nimport { Textarea } from \"../components/ui\";\nimport { useLlmAction } from \"../lib/hooks\";\nimport { AiAction } from \"../components/blocks/ai-action\";\n\nexport default function Draft({ initial }) {\n const [draft, setDraft] = useState(initial);\n const shorter = useLlmAction({ input: draft, system: \"Make it shorter. Keep every fact and the writer's voice.\" });\n return (\n <div className=\"grid gap-3\">\n <Textarea value={draft} onChange={(e) => setDraft(e.target.value)} autoGrow />\n <AiAction label=\"Shorter\" action={shorter} useLabel=\"Replace the draft\" onUse={(r) => setDraft(r.text)} />\n </div>\n );\n}\n```\n"
31
+ }
32
+ ],
33
+ "categories": [
34
+ "ai",
35
+ "llm",
36
+ "model",
37
+ "summarize",
38
+ "shorter",
39
+ "rewrite",
40
+ "explain",
41
+ "why",
42
+ "draft",
43
+ "one call",
44
+ "price",
45
+ "credits",
46
+ "generate",
47
+ "magic button"
48
+ ],
49
+ "docs": "Installed to components/makersclaw/blocks/ai-action. Import: import { AiAction, priceWords } from \"@/components/makersclaw/blocks/ai-action\". Props and an example: components/makersclaw/blocks/ai-action/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
50
+ "meta": {
51
+ "makersclaw": {
52
+ "version": "0.8.2",
53
+ "useWhen": "A screen wants a quick model answer about something it already shows, with no conversation and no tools: shorten a draft, summarize a thread, explain a score, say why a number moved, suggest a subject line. For work that reads, searches or writes records, ask the agent (ask-bar, agent-chat)."
54
+ }
55
+ }
56
+ }
package/r/annotate.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "title": "Annotate",
6
6
  "description": "Mark up a screenshot: drop, pick or paste one, then draw arrows, boxes, highlights, redaction bars and numbered notes on it (Excalidraw underneath, via sketch-canvas). A side list holds the words of each numbered note; a click on a note's number shows its pin. Starter templates (bug report, design review, walkthrough) on a fresh screenshot. The annotation is plain JSON saved through onChange / an async onSave; onSubmit hands over a PNG of the marked-up screenshot with the notes and the sketch.",
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/sketch-canvas.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/sketch-canvas.json"
10
10
  ],
11
11
  "dependencies": [],
12
12
  "files": [
@@ -55,7 +55,7 @@
55
55
  "docs": "Installed to components/makersclaw/blocks/annotate. Import: import { Annotate, ANNOTATE_TOOLS, ANNOTATE_TEMPLATES, emptyAnnotation, toAnnotation, annotationNotes, annotationToText, noteMarkers, nextNoteNumber, markerSkeleton } from \"@/components/makersclaw/blocks/annotate\". Props and an example: components/makersclaw/blocks/annotate/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": "Someone needs to point at parts of a screen and say something about each: a bug report, design feedback, a support question, a walkthrough, a request to an agent about what to change. Not a blank drawing board (whiteboard or sketch-canvas), not a wireframe (sketch-to-app), not an image upload alone (file-upload)."
60
60
  }
61
61
  }
@@ -5,7 +5,7 @@
5
5
  "title": "ApprovalRequest",
6
6
  "description": "One request waiting on a decision: what is asked for as label/value facts (an amount in large type), who asked and when, why, a status badge, and Approve / Reject (reject asks for a reason). The decision goes out through async onApprove / onReject; the block shows it working, a failure, then the outcome in place of the buttons. A card alone, or a page of detail sections beside the card.",
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": [
@@ -45,7 +45,7 @@
45
45
  "docs": "Installed to components/makersclaw/blocks/approval-request. Import: import { ApprovalRequest } from \"@/components/makersclaw/blocks/approval-request\". Props and an example: components/makersclaw/blocks/approval-request/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
46
46
  "meta": {
47
47
  "makersclaw": {
48
- "version": "0.8.0",
48
+ "version": "0.8.2",
49
49
  "useWhen": "Someone must say yes or no to one thing another person asked for: a purchase, a refund, time off, a discount, an access request, a published post. Not for a queue of many requests (a data-table with row actions) or a yes/no about the founder's own action (kit Confirm)."
50
50
  }
51
51
  }
@@ -0,0 +1,54 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "approval",
4
+ "type": "registry:block",
5
+ "title": "Approval",
6
+ "description": "The yes, given in the app. The app draws the card in its own theme — what will happen, to whom, the exact words, the cost — and under it sits the press: a small Makersclaw-origin frame, in the app's colours, that app code cannot script. It shows the server's own words for the request (the agent's question, who, from which account, the first message, the rule \"Always allow this kind\" would save) and only counts a press a person made on a card they can see. Fed by useApproval(requestId) (lib/hooks.js). If the frame cannot load or the member must sign in again, the card falls back to WaitingYes: \"Waiting for your yes on the desk\".",
7
+ "registryDependencies": [
8
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/core.json",
9
+ "https://cdn.jsdelivr.net/npm/makersclaw@0.8.2/r/needs-you.json"
10
+ ],
11
+ "dependencies": [],
12
+ "files": [
13
+ {
14
+ "path": "registry/blocks/approval/index.js",
15
+ "type": "registry:component",
16
+ "target": "@components/makersclaw/blocks/approval/index.js",
17
+ "content": "// The Approval block — `makersclaw add approval`. Fixed: compose it\n// through its props (README.md beside this file).\nexport { Approval } from \"./approval\";\n"
18
+ },
19
+ {
20
+ "path": "registry/blocks/approval/approval.jsx",
21
+ "type": "registry:component",
22
+ "target": "@components/makersclaw/blocks/approval/approval.jsx",
23
+ "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { Button, Icon, Skeleton, Spinner, cx } from \"../../ui\";\nimport { WaitingYes } from \"../needs-you\";\n\n// Approval — the yes, given in the app (the app kit, D18). The app draws\n// the card in its own theme: what will happen, to whom, the exact words,\n// the cost. The press is not the app's. Under the card sits a small page\n// of Makersclaw's, in another origin, that app code cannot script: it\n// shows the SERVER's words for the request (the agent's question, who,\n// from which account, the first message, the rule \"Always allow this\n// kind\" would save), in the app's colours, and only counts a press a\n// person made on a card they can see (in view and uncovered, or held).\n// The block draws that frame and what it says back; it never draws an\n// Approve of its own.\n//\n// const approval = useApproval(item.id); // lib/hooks.js\n// <Approval approval={approval} title=\"Send 3 follow-ups\" onOpenDesk={…}>\n// …the draft the app already shows…\n// </Approval>\n//\n// If the frame cannot do its part (it did not load in time, the member\n// must sign in again, a claw from before this door) the block falls back\n// to WaitingYes: \"Waiting for your yes on the desk\", with the desk one\n// press away. Props in, callbacks out.\n\n/** A frame that has not said how tall it wants to be. */\nconst FRAME_HEIGHT = 240;\n\n/** Why the fallback is showing, in a sentence. */\nconst WHY = {\n signin: \"Sign in again on Makersclaw to say yes here. The desk has it.\",\n blocked: \"The press could not load here. The desk has it.\",\n unavailable: \"Saying yes here needs a newer Makersclaw. The desk has it.\",\n error: \"The press could not be loaded just now. The desk has it.\",\n};\n\n/** The card's receipt, once the press has been used. */\nfunction Receipt({ icon, tone, head, children }) {\n return (\n <div role=\"status\" className=\"grid grid-cols-1 min-w-0 gap-1\">\n <p className={cx(\"flex min-w-0 items-center gap-2 font-medium\", tone)}>\n <Icon name={icon} size={16} className=\"shrink-0\" />\n <span className=\"min-w-0 break-words\">{head}</span>\n </p>\n {children ? <p className=\"text-muted-foreground\">{children}</p> : null}\n </div>\n );\n}\n\n/**\n * approval what useApproval(requestId) returns (required)\n * title what is about to happen, in the app's words (\"Send 3\n * follow-ups to the Replies list\")\n * kicker the line over it; default \"Waiting for your yes\"\n * children the app's own card content: the draft, the recipients,\n * the exact words. Drawn above the press, in the app's\n * theme\n * cost a node for the price (\"about 700 cr\": the `cost`\n * block's CostTag); shown beside the title\n * onOpenDesk() opens the desk on this ask: the press of the fallback\n * (useNeedsYou's `open(item)`); without it the fallback\n * only says where the yes is\n * onDone({ state, always }) called once when the member has said yes\n * or not now (\"approved\" or \"declined\"; `always` true when\n * \"Always allow this kind\" was saved): refresh what the\n * yes unlocks\n * className layout around it. Never colour.\n */\nexport function Approval({ approval, title, kicker = \"Waiting for your yes\", children, cost, onOpenDesk, onDone, className }) {\n const state = approval?.state ?? \"idle\";\n const spoken = useRef(null);\n useEffect(() => {\n if ((state === \"approved\" || state === \"declined\") && spoken.current !== state) {\n spoken.current = state;\n onDone?.({ state, always: Boolean(approval?.always) });\n }\n if (state !== \"approved\" && state !== \"declined\") spoken.current = null;\n }, [state, approval?.always]);\n\n if (state === \"idle\") return null;\n\n if (state === \"loading\")\n return (\n <section aria-busy=\"true\" aria-label=\"Waiting for your yes\" className={cx(\"@container grid grid-cols-1 min-w-0 gap-3 rounded-lg border border-border bg-card p-3.5\", className)}>\n <Skeleton className=\"w-1/3\" />\n <Skeleton className=\"w-3/4\" />\n <Skeleton className=\"h-16 w-full\" />\n </section>\n );\n\n // The frame cannot be used: the desk's card, with the words the app drew.\n if (approval?.fallback)\n return (\n <WaitingYes\n title={title}\n frozen={children}\n kicker={kicker}\n body={WHY[state] ?? WHY.error}\n onOpen={onOpenDesk}\n className={className}\n />\n );\n\n const done = state === \"approved\" || state === \"declined\";\n const head = (\n <div className=\"grid grid-cols-1 min-w-0 gap-1\">\n <div className=\"flex min-w-0 items-start gap-2\">\n <div className=\"grid grid-cols-1 min-w-0 flex-1 gap-1\">\n {kicker ? (\n <p className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {done ? null : <Icon name=\"shield-check\" size={12} className=\"shrink-0\" />}\n {kicker}\n </p>\n ) : null}\n {title ? <p className=\"break-words text-[15px] font-semibold leading-snug\">{title}</p> : null}\n </div>\n {cost ? <div className=\"shrink-0 pt-0.5 text-xs text-muted-foreground\">{cost}</div> : null}\n </div>\n </div>\n );\n\n return (\n <section\n aria-label={typeof title === \"string\" ? title : \"Waiting for your yes\"}\n className={cx(\"@container grid grid-cols-1 min-w-0 content-start gap-3 rounded-lg border border-border bg-card p-3.5 text-sm text-card-foreground\", className)}\n >\n {head}\n {children != null && children !== \"\" ? <div className={cx(\"min-w-0 break-words\", done && \"text-muted-foreground\")}>{children}</div> : null}\n\n {state === \"approved\" ? (\n <Receipt icon=\"check\" tone=\"text-success\" head=\"You said yes · the agent is on it\">\n {approval?.always ? \"Always allowed for this kind. It will not ask again for the same.\" : null}\n </Receipt>\n ) : state === \"declined\" ? (\n <Receipt icon=\"x\" tone=\"text-muted-foreground\" head=\"Not now. Nothing went out.\" />\n ) : state === \"gone\" ? (\n <Receipt icon=\"check\" tone=\"text-muted-foreground\" head=\"This has been answered.\" />\n ) : state === \"stale\" ? (\n <p role=\"status\" className=\"flex items-center gap-2 text-muted-foreground\">\n <Spinner size=\"sm\" label=\"Looking again\" /> This changed since the card was drawn. Looking again…\n </p>\n ) : approval?.frame ? (\n <iframe\n ref={approval.frame.ref}\n src={approval.frame.src}\n srcDoc={approval.frame.srcDoc}\n title={approval.frame.title ?? \"Approve\"}\n scrolling=\"no\"\n className=\"block w-full border-0 bg-transparent\"\n style={{ height: approval.height ?? FRAME_HEIGHT }}\n />\n ) : null}\n {state === \"ready\" && approval?.error ? <p className=\"text-xs text-destructive\">{approval.error}</p> : null}\n {state === \"gone\" && onOpenDesk ? (\n <Button size=\"sm\" variant=\"quiet\" className=\"justify-self-start\" onClick={onOpenDesk}>\n Open it on the desk\n </Button>\n ) : null}\n </section>\n );\n}\n"
24
+ },
25
+ {
26
+ "path": "registry/blocks/approval/README.md",
27
+ "type": "registry:file",
28
+ "target": "@components/makersclaw/blocks/approval/README.md",
29
+ "content": "# Approval\n\nThe yes, given in the app. The app draws the card in its own theme: what\nwill happen, to whom, the exact words, the cost. The press is not the\napp's. Under the card sits a small page of Makersclaw's, in the app's\ncolours, that app code cannot script. It shows the server's own words for\nthe request and only counts a press a person made on a card they can see.\n\n```jsx\nimport { Approval } from \"../components/blocks/approval\"; // a Makersclaw app (\"./blocks/approval\" from components/)\nimport { Approval } from \"@/components/makersclaw/blocks/approval\"; // a shadcn project\n```\n\nA client component. The Example is at the end.\n\n## Why a frame\n\nApp code is written by an agent, and an in-app Approve button could be\npressed by that same code, or by a script that got into the page. So the\nbutton that gives the yes is not in the app's page. It is a Makersclaw\npage, embedded in the card:\n\n- **Another origin.** The app cannot reach into it, read it or click it.\n- **The server's words.** It shows what Makersclaw holds for the request:\n the agent's one question, who it goes to, from which account, the first\n message in full, and the rule \"Always allow this kind\" would save. Never\n the app's text. The app's card above it shows the same thing in the\n app's own design; the frame is the one to trust.\n- **A press a person made, on a card they can see.** Where the browser\n can tell (Chrome, Edge) the Approve button works only after the card\n has been in view, uncovered and not faded, for half a second. Where it\n cannot (Safari, Firefox) Approve is press and hold, for most of a\n second. A click the page's script makes does nothing.\n- **The member's own sign-in.** The press carries the member's token from\n the desk's storage, which the app has no way to read, and the server\n checks it is the member who asked, for this app, for the question the\n card showed.\n- **The same path as the desk's Approve.** The answer goes to the agent\n as a structured answer from the member, so the audit trail, the rules\n and the Standing yes stay one.\n\n\"Always allow this kind\" is in the same card: one tick, and the server\nwrites one bounded line into the app's Standing yes (the kind: what is\nsent, to whom, from which account), shown beside the box before it is\nticked. Rules, not items: the next one of that kind goes without asking.\nAnything wider still asks.\n\n## Which hook feeds it\n\n```js\nconst approval = useApproval(item.id); // lib/hooks.js; item from useNeedsYou\n// → { approval, state, always, fallback, frame, height, loading, error, … }\n```\n\nIt reads `GET _claw/requests/:id/approval` once, for the request that\nwaits on this member's yes (`item.via === \"press\"`), and listens to the\nframe. It approves nothing.\n\n## Props\n\n- `approval` — `useApproval(...)`'s answer (required).\n- `title` — what is about to happen, in the app's words (\"Send 3\n follow-ups to the Replies list\").\n- `kicker` — the line over it. Default \"Waiting for your yes\".\n- `children` — the app's own card content: the draft, the recipients, the\n exact words. Drawn above the press, in the app's theme.\n- `cost` — a node for the price (the `cost` block's `CostTag`), beside the\n title.\n- `onOpenDesk()` — opens the desk on this ask (`needs.open(item)`): the\n press of the fallback.\n- `onDone({ state, always })` — once, when the member has said yes\n (`\"approved\"`) or not now (`\"declined\"`); `always` is true when \"Always\n allow this kind\" was saved. Refresh what the yes unlocks.\n- `className` — layout around it. Never colour.\n\n## The states\n\n`approval.state` is one of: `loading` (a skeleton) · `ready` (the frame)\n· `approved` (\"You said yes · the agent is on it\", and \"Always allowed for\nthis kind\" when it was) · `declined` (\"Not now. Nothing went out.\") ·\n`stale` (the question moved on since the card was drawn; it looks again\nby itself) · `gone` (nothing waits any more: answered elsewhere).\n\nWhen the frame cannot do its part, `approval.fallback` is true and the\nblock draws `WaitingYes` instead: \"Waiting for your yes on the desk\",\nwith **Open the desk**. That is the case when the frame did not load in\nseven seconds (`blocked`), the member must sign in again on the desk\n(`signin`), the claw is from before this door (`unavailable`) or the read\nfailed (`error`). Nothing is lost: the yes is the same yes.\n\n## Who may do what\n\n- The member who asked gives the yes. Owners and admins do not give a\n teammate's: the door refuses them (`not_your_request`).\n- Only a yes: one question, one Approve and Not now. A tool approval, a\n connect, a spend and several questions at once go to the desk.\n- A question (not a yes) is answered in place, with `QuestionBox`\n (`needs-you`), never here.\n- Topping up credits and signing in to an account stay on the desk.\n\n## Never\n\n- **Never an Approve, Yes or Send button of your own that gives the\n yes.** The app's card may have a \"Review the draft\" press; the\n approval is the frame's.\n- Never hide, cover, fade, scale or position the frame so that part of it\n is out of view: a press then does not count, by design.\n- Never put the frame in an element with `pointer-events: none`,\n `opacity` below 1 or a `transform`.\n- Never show another member's asks here.\n\n## Example\n\nThe post that waits for its yes, inside the page that already shows it.\n\n```jsx\n\"use client\";\nimport { useNeedsYou, useApproval } from \"../lib/hooks\";\nimport { Approval } from \"../components/blocks/approval\";\nimport { CostTag } from \"../components/blocks/cost\";\n\nexport function SendCard({ item, draft }) {\n const needs = useNeedsYou();\n const approval = useApproval(item.id);\n return (\n <Approval\n approval={approval}\n title={`Send ${draft.count} follow-ups to ${draft.list}`}\n cost={<CostTag credits={draft.credits} />}\n onOpenDesk={() => needs.open(item)}\n onDone={({ state }) => state === \"approved\" && refreshRows()}\n >\n <DraftList draft={draft} />\n </Approval>\n );\n}\n\n// …and in a queue of everything that waits:\n<NeedsYou\n items={needs.items}\n onOpen={needs.open}\n onAnswer={needs.answer}\n renderYes={(item) => <SendCard item={item} draft={draftFor(item)} />}\n/>\n```\n"
30
+ }
31
+ ],
32
+ "categories": [
33
+ "approval",
34
+ "approve",
35
+ "yes",
36
+ "send",
37
+ "confirm",
38
+ "needs you",
39
+ "agent",
40
+ "trust",
41
+ "always allow",
42
+ "rule",
43
+ "desk",
44
+ "publish",
45
+ "spend"
46
+ ],
47
+ "docs": "Installed to components/makersclaw/blocks/approval. Import: import { Approval } from \"@/components/makersclaw/blocks/approval\". Props and an example: components/makersclaw/blocks/approval/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
48
+ "meta": {
49
+ "makersclaw": {
50
+ "version": "0.8.2",
51
+ "useWhen": "Anywhere the agent is about to do something public or paid and waits for the member's yes — a send, a post, a purchase: put the app's own card (the draft, the recipients, the cost) inside it, give it useApproval(item.id) for the request that waits, and the member says yes without leaving the app. For the queue of everything that waits use needs-you; for the conversation, agent-chat."
52
+ }
53
+ }
54
+ }
package/r/ask-bar.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "ask-bar",
4
+ "type": "registry:block",
5
+ "title": "AskBar",
6
+ "description": "One line to ask the app's agent, with chips that fit the moment (they wrap, never run off). After a send it says where the ask stands in one line — \"Working…\", \"Waiting for you on the desk\", \"Answered\" — with an Open that shows the conversation wherever the screen keeps it. It draws what useAgentThread() returns (lib/hooks.js) and sends through it, or calls your own onAsk(text). Never an Approve: a yes is given on the desk.",
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/ask-bar/index.js",
14
+ "type": "registry:component",
15
+ "target": "@components/makersclaw/blocks/ask-bar/index.js",
16
+ "content": "// The AskBar block — `makersclaw add ask-bar`. Fixed: compose it through\n// its props (README.md beside this file).\nexport { AskBar } from \"./ask-bar\";\n"
17
+ },
18
+ {
19
+ "path": "registry/blocks/ask-bar/ask-bar.jsx",
20
+ "type": "registry:component",
21
+ "target": "@components/makersclaw/blocks/ask-bar/ask-bar.jsx",
22
+ "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button, Icon, IconButton, Input, Spinner, cx } from \"../../ui\";\n\n// AskBar — one line to ask the app's agent, with chips that fit the\n// moment (the app kit, D15). It is the door into the conversation, not\n// the conversation: after a send it says where the ask stands in one\n// line (\"Working…\", \"Waiting for you\", \"Answered\") with an\n// Open that shows the chat wherever the screen keeps it (a sheet, the\n// inspector, the agent-chat block). Fed by the object useAgentThread()\n// returns (lib/hooks.js), or by your own `onAsk(text)`.\n//\n// const chat = useAgentThread({ row: { kind: \"deal\", id, name } });\n// <AskBar chat={chat} suggestions={[\"Who should I call today?\"]} onOpen={() => setOpen(true)} />\n//\n// Never an Approve: an ask that needs the member's yes or answer says so\n// here (\"Waiting for you\") and Open shows it where the screen keeps its\n// chat, where the question is answered and the yes is given (the\n// `agent-chat` and `approval` blocks, D18/D19). A need the app cannot\n// settle (connecting an account, spending money) opens the desk.\n\n/** The thread's state as one line. */\nconst STATE = {\n queued: { words: \"Working…\", busy: true },\n working: { words: \"Working…\", busy: true },\n needs_you: { words: \"Waiting for you\", icon: \"warning\", tone: \"text-warning\" },\n done: { words: \"Answered\", icon: \"check\", tone: \"text-success\" },\n stopped: { words: \"Stopped\", icon: \"stop\", tone: \"text-muted-foreground\" },\n failed: { words: \"It didn't finish\", icon: \"warning\", tone: \"text-destructive\" },\n};\n\nfunction lastAsk(chat) {\n if (chat?.pending) return chat.pending;\n const turns = chat?.turns ?? [];\n for (let i = turns.length - 1; i >= 0; i -= 1) if (turns[i]?.member) return turns[i].member;\n return null;\n}\n\n/**\n * chat what useAgentThread() returns; asks go to chat.send(text)\n * onAsk(text) instead of `chat`: your own sender (it may be async)\n * state with onAsk: the ask's state as useAgentThread names it\n * (queued · working · needs_you · done · stopped · failed)\n * suggestions asks that fit the moment, as chips; a chip sends its words\n * placeholder the box's words (default \"Ask the agent…\")\n * label the box's name for a reader (default \"Ask the agent\")\n * onOpen() shows the conversation; without it there is no Open\n * openOnSend true: a send also calls onOpen\n * className layout around it. Never colour.\n */\nexport function AskBar({\n chat,\n onAsk,\n state: stateProp,\n suggestions = [],\n placeholder = \"Ask the agent…\",\n label = \"Ask the agent\",\n onOpen,\n openOnSend = false,\n className,\n}) {\n const [text, setText] = useState(\"\");\n const [asked, setAsked] = useState(null);\n const [sending, setSending] = useState(false);\n const [failed, setFailed] = useState(null);\n\n const state = chat ? (chat.pending && !chat.state ? \"queued\" : chat.state) : stateProp;\n const line = state ? STATE[state] : null;\n const ask = chat ? lastAsk(chat) : asked;\n const needsYou = state === \"needs_you\";\n const unavailable = Boolean(chat?.unavailable);\n const busy = sending || Boolean(chat?.sending);\n const error = chat?.error || failed;\n // Waiting on the member: Open shows the chat when the app settles it\n // there (a question to answer, a yes to press); else it goes to the desk.\n const inApp = chat?.needs?.in_app === \"answer\" || chat?.needs?.in_app === \"press\";\n const toDesk = needsYou && !(inApp && onOpen) && Boolean(chat?.openOnDesk);\n const openNow = toDesk ? () => chat.openOnDesk() : onOpen;\n\n async function send(words) {\n const t = String(words ?? text).trim();\n if (!t || busy || needsYou || unavailable) return;\n setFailed(null);\n setText(\"\");\n setAsked(t);\n if (chat?.send) chat.send(t);\n else if (onAsk) {\n setSending(true);\n try {\n const r = await onAsk(t);\n if (r && r.ok === false) setFailed(r.error || \"That ask didn't go through. Try again.\");\n } catch (e) {\n setFailed(e?.message || \"That ask didn't go through. Try again.\");\n }\n setSending(false);\n }\n if (openOnSend && onOpen) onOpen();\n }\n\n return (\n <div className={cx(\"@container grid grid-cols-1 min-w-0 gap-2 text-sm\", className)}>\n <form\n className=\"flex min-w-0 items-center gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n send();\n }}\n >\n <span className=\"relative min-w-0 flex-1\">\n <Icon name=\"sparkles\" size={16} className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\" />\n <Input\n aria-label={label}\n value={text}\n disabled={needsYou || unavailable}\n placeholder={needsYou ? (inApp ? \"Answer in the chat first\" : \"Answer on the desk first\") : placeholder}\n onChange={(e) => setText(e.target.value)}\n className=\"pl-9\"\n />\n </span>\n <IconButton icon=\"arrow-up\" label=\"Send\" variant=\"primary\" type=\"submit\" loading={busy} disabled={!text.trim() || busy || needsYou || unavailable} />\n </form>\n\n {suggestions.length && !needsYou && !unavailable ? (\n <div className=\"flex min-w-0 flex-wrap gap-1.5\" role=\"group\" aria-label=\"Suggested asks\">\n {suggestions.map((s) => (\n <button\n key={s}\n type=\"button\"\n disabled={busy}\n onClick={() => send(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 disabled:opacity-50\"\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 {line ? (\n // The state and Open on one line, the ask it is about under it,\n // cut to one line: at 320px nothing wraps into a third.\n <div role=\"status\" className=\"grid grid-cols-1 min-w-0 gap-0.5 text-xs text-muted-foreground\">\n <span className=\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5\">\n {line.busy ? <Spinner size=\"sm\" label=\"Working\" /> : <Icon name={line.icon} size={14} className={cx(\"shrink-0\", line.tone)} />}\n <span className={cx(\"font-medium\", needsYou ? \"text-foreground\" : null)}>{line.words}</span>\n {openNow ? (\n <Button variant=\"link\" size=\"inherit\" onClick={openNow} className=\"shrink-0\">\n {toDesk ? \"Open the desk\" : \"Open\"}\n </Button>\n ) : null}\n </span>\n {ask ? <span className=\"min-w-0 truncate pl-[22px]\">“{ask}”</span> : null}\n </div>\n ) : null}\n\n {error ? (\n <p role=\"alert\" className=\"text-xs text-destructive\">\n {error}\n </p>\n ) : null}\n {unavailable ? <p className=\"text-xs text-muted-foreground\">Asking from here is not connected yet.</p> : null}\n </div>\n );\n}\n"
23
+ },
24
+ {
25
+ "path": "registry/blocks/ask-bar/README.md",
26
+ "type": "registry:file",
27
+ "target": "@components/makersclaw/blocks/ask-bar/README.md",
28
+ "content": "# AskBar\n\nOne line to ask the app's agent, in the app's look. Chips under it offer\nasks that fit the moment; a chip sends its words. Long chips wrap; they\nnever run off the screen.\n\nAfter a send the bar says where the ask stands, in one line:\n\n- \"Working…\" while the agent is on it;\n- \"Waiting for you\" when it needs the member's yes or answer;\n- \"Answered\", \"Stopped\", or \"It didn't finish\".\n\nBeside the line, **Open** shows the conversation. Where it shows is the\nscreen's choice: a `Sheet` with the `agent-chat` block, the inspector,\na side column. When the agent waits on a question or a yes the app can\nsettle (`chat.needs.in_app` is `\"answer\"` or `\"press\"`), Open shows that\nchat, where the answer is given (`agent-chat`) and the yes is pressed\n(`approval`). A need the app cannot settle goes to the desk instead\n(`chat.openOnDesk()`).\n\nIt is a door into the conversation, not the conversation. The thread is\nthe same one `agent-chat` and the desk's agent column draw.\n\n```jsx\nimport { AskBar } from \"../components/blocks/ask-bar\"; // a Makersclaw app (\"./blocks/ask-bar\" from components/)\nimport { AskBar } from \"@/components/makersclaw/blocks/ask-bar\"; // a shadcn project\n```\n\nA client component. The Example is at the end.\n\n## Props\n\n- `chat` — what `useAgentThread()` returns (lib/hooks.js). A send calls\n `chat.send(text)`; the line reads `chat.state`, the last ask from\n `chat.turns` or `chat.pending`, and `chat.error`.\n- `onAsk(text)` — instead of `chat`: your own sender. It may be async; an\n answer of `{ ok: false, error }` (or a throw) shows its words under the\n bar.\n- `state` — with `onAsk` only: where your ask stands, in\n `useAgentThread`'s words (`queued` · `working` · `needs_you` · `done` ·\n `stopped` · `failed`).\n- `suggestions` — asks that fit the moment, as chips. Say them in the\n app's words (\"Who should I call today?\").\n- `placeholder` — the box's words. Default \"Ask the agent…\".\n- `label` — the box's name for a screen reader. Default \"Ask the agent\".\n- `onOpen()` — shows the conversation. Without it there is no Open.\n- `openOnSend` — `true`: a send also calls `onOpen`, so the chat opens at\n once.\n- `className` — layout around it. Never colour.\n\n## Which hook feeds it\n\n`useAgentThread({ row, how, key })` (lib/hooks.js) over\n`POST _claw/ask` and `GET _claw/threads/<session>`. Give the same `row`\nthe chat on the same screen gets, and both draw one thread.\n\n## Who may do what\n\nAny member who can open the app may ask. Each member sees only their own\nthread. Stop lives in the chat (`agent-chat`), not here.\n\n## Never\n\n- **Never an Approve.** The yes is the `approval` block's frame, which the\n app's code cannot press. The bar says \"Waiting for you\" and Open shows\n the chat that holds it.\n- Never answer the agent's question here: that is the chat's\n `QuestionBox`.\n- Never show the agent's answer in the bar. One line of state, then Open.\n\n## Outside Makersclaw\n\nAny object of the same shape works: `state`, `turns` (`[{ member }]`),\n`pending`, `error`, `unavailable`, `send(text)` and `openOnDesk()`. Or\nuse `onAsk` and `state`.\n\n## Example\n\nA home screen with the ask on top and the chat in a sheet.\n\n```jsx\n\"use client\";\nimport { useState } from \"react\";\nimport { Sheet } from \"../components/ui\";\nimport { useAgentThread } from \"../lib/hooks\";\nimport { AskBar } from \"../components/blocks/ask-bar\";\nimport { AgentChat } from \"../components/blocks/agent-chat\";\n\nexport default function Today() {\n const [open, setOpen] = useState(false);\n const chat = useAgentThread({ key: \"today\" });\n return (\n <>\n <AskBar\n chat={chat}\n suggestions={[\"Who should I call today?\", \"What changed since Friday?\"]}\n onOpen={() => setOpen(true)}\n />\n <Sheet open={open} onClose={() => setOpen(false)} title=\"Ask the agent\">\n <AgentChat chat={chat} bare />\n </Sheet>\n </>\n );\n}\n```\n"
29
+ }
30
+ ],
31
+ "categories": [
32
+ "ask",
33
+ "agent",
34
+ "ai",
35
+ "prompt",
36
+ "command",
37
+ "chips",
38
+ "suggestions",
39
+ "assistant",
40
+ "copilot",
41
+ "search bar"
42
+ ],
43
+ "docs": "Installed to components/makersclaw/blocks/ask-bar. Import: import { AskBar } from \"@/components/makersclaw/blocks/ask-bar\". Props and an example: components/makersclaw/blocks/ask-bar/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
44
+ "meta": {
45
+ "makersclaw": {
46
+ "version": "0.8.2",
47
+ "useWhen": "A home screen, a list's head or a record's timeline should take an ask in one line and keep the page in view: CRM's Today, the Ask box under a record. Pair it with agent-chat (in a Sheet or the inspector) for the conversation it opens. For the full chat in place, use agent-chat alone."
48
+ }
49
+ }
50
+ }
@@ -5,7 +5,7 @@
5
5
  "title": "AudioPlayer",
6
6
  "description": "A waveform audio player (wavesurfer.js) in the workspace theme: play, skip back and forward, speed, mute; regions (spans of the audio in a theme tone, labelled in a lane under the waveform, click to play one) and markers (pins at moments, click to jump); a hover time readout; drag across the waveform to mark a new region; add a marker at the playhead; keyboard like a video player (K, J, L, M, arrows). Time events and a handle (seek, play a range) for syncing a transcript.",
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
  "wavesurfer.js@8.0.0",
@@ -67,7 +67,7 @@
67
67
  "docs": "Installed to components/makersclaw/blocks/audio-player. Import: import { AudioPlayer, useAudioPlayer, formatTime, formatRange, spokenTime, regionAt, TONES, RATES } from \"@/components/makersclaw/blocks/audio-player\". Props and an example: components/makersclaw/blocks/audio-player/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
68
68
  "meta": {
69
69
  "makersclaw": {
70
- "version": "0.8.0",
70
+ "version": "0.8.2",
71
71
  "useWhen": "A screen plays a recording: a call or meeting recording, a voice note, a podcast clip, a voicemail, a sound file on a record. The call-review block is built on it. Not for video, not for recording audio from the microphone."
72
72
  }
73
73
  }
package/r/auth-form.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "title": "AuthForm",
6
6
  "description": "The account screens in one form: sign in (password, email only, or provider buttons for Google, GitHub, Apple and Microsoft), sign up with extra fields, a magic link, a reset link, and changing a password against a live rules checklist. The auth is yours: each submit is an async callback that returns { ok } or an error the form shows.",
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": [
@@ -48,7 +48,7 @@
48
48
  "docs": "Installed to components/makersclaw/blocks/auth-form. Import: import { AuthForm, PASSWORD_RULES } from \"@/components/makersclaw/blocks/auth-form\". Props and an example: components/makersclaw/blocks/auth-form/README.md. Makersclaw builds whole apps from these blocks: https://makersclaw.com",
49
49
  "meta": {
50
50
  "makersclaw": {
51
- "version": "0.8.0",
51
+ "version": "0.8.2",
52
52
  "useWhen": "An app needs a sign-in, sign-up, forgot-password or change-password screen, or a settings page where a signed-in person changes their password."
53
53
  }
54
54
  }