makersclaw 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/README.md +17 -4
  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 +4 -4
  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 +5 -5
  26. package/r/chat-pane.json +84 -0
  27. package/r/chat.json +3 -3
  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 +18 -6
  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 +3 -3
  46. package/r/flow-editor.json +79 -0
  47. package/r/footer.json +3 -3
  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 +4 -4
  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 +4 -4
  60. package/r/pace-bar.json +69 -0
  61. package/r/page-builder.json +3 -3
  62. package/r/payment-methods.json +2 -2
  63. package/r/pdf-document.json +2 -2
  64. package/r/pdf-viewer.json +3 -3
  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 +4089 -1510
  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 +3 -3
  82. package/r/signature-pad.json +2 -2
  83. package/r/sketch-canvas.json +3 -3
  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 +4 -4
  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 +225 -0
  143. package/registry/blocks/agent-pane/agent-pane.jsx +244 -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/annotate/README.md +14 -11
  159. package/registry/blocks/approval/README.md +145 -0
  160. package/registry/blocks/approval/approval.jsx +163 -0
  161. package/registry/blocks/approval/block.json +31 -0
  162. package/registry/blocks/approval/demo.jsx +121 -0
  163. package/registry/blocks/approval/index.js +3 -0
  164. package/registry/blocks/ask-bar/README.md +105 -0
  165. package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
  166. package/registry/blocks/ask-bar/block.json +26 -0
  167. package/registry/blocks/ask-bar/demo.jsx +78 -0
  168. package/registry/blocks/ask-bar/index.js +3 -0
  169. package/registry/blocks/automation-editor/README.md +291 -0
  170. package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
  171. package/registry/blocks/automation-editor/block.json +39 -0
  172. package/registry/blocks/automation-editor/demo.jsx +284 -0
  173. package/registry/blocks/automation-editor/index.js +4 -0
  174. package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
  175. package/registry/blocks/automation-list/README.md +129 -0
  176. package/registry/blocks/automation-list/automation-list.jsx +476 -0
  177. package/registry/blocks/automation-list/block.json +35 -0
  178. package/registry/blocks/automation-list/demo.jsx +67 -0
  179. package/registry/blocks/automation-list/index.js +3 -0
  180. package/registry/blocks/automation-model/README.md +109 -0
  181. package/registry/blocks/automation-model/block.json +135 -0
  182. package/registry/blocks/automation-model/catalog.js +256 -0
  183. package/registry/blocks/automation-model/check.js +161 -0
  184. package/registry/blocks/automation-model/demo.jsx +42 -0
  185. package/registry/blocks/automation-model/edit.js +262 -0
  186. package/registry/blocks/automation-model/fixes.js +287 -0
  187. package/registry/blocks/automation-model/glyph.jsx +13 -0
  188. package/registry/blocks/automation-model/graph.js +410 -0
  189. package/registry/blocks/automation-model/index.js +33 -0
  190. package/registry/blocks/automation-model/overlay.js +175 -0
  191. package/registry/blocks/automation-model/parts.jsx +94 -0
  192. package/registry/blocks/automation-model/schedule.js +53 -0
  193. package/registry/blocks/automation-model/templates.js +128 -0
  194. package/registry/blocks/automation-model/values.js +29 -0
  195. package/registry/blocks/automation-model/words.js +439 -0
  196. package/registry/blocks/automations-screen/README.md +207 -0
  197. package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
  198. package/registry/blocks/automations-screen/block.json +47 -0
  199. package/registry/blocks/automations-screen/demo.jsx +364 -0
  200. package/registry/blocks/automations-screen/index.js +4 -0
  201. package/registry/blocks/automations-screen/screen-model.js +129 -0
  202. package/registry/blocks/chart/cartesian.jsx +1 -1
  203. package/registry/blocks/chart/core.jsx +33 -23
  204. package/registry/blocks/chart/pie.jsx +1 -1
  205. package/registry/blocks/chat/README.md +4 -1
  206. package/registry/blocks/chat/demo.jsx +2 -2
  207. package/registry/blocks/chat-pane/README.md +143 -0
  208. package/registry/blocks/chat-pane/block.json +48 -0
  209. package/registry/blocks/chat-pane/chat-attach.js +166 -0
  210. package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
  211. package/registry/blocks/chat-pane/chat-context.js +17 -0
  212. package/registry/blocks/chat-pane/chat-model.js +43 -0
  213. package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
  214. package/registry/blocks/chat-pane/chats-list.jsx +52 -0
  215. package/registry/blocks/chat-pane/demo.jsx +103 -0
  216. package/registry/blocks/chat-pane/index.js +9 -0
  217. package/registry/blocks/connect-card/README.md +99 -0
  218. package/registry/blocks/connect-card/block.json +10 -0
  219. package/registry/blocks/connect-card/connect-card.jsx +103 -0
  220. package/registry/blocks/connect-card/demo.jsx +49 -0
  221. package/registry/blocks/connect-card/index.js +3 -0
  222. package/registry/blocks/cost/README.md +115 -0
  223. package/registry/blocks/cost/block.json +10 -0
  224. package/registry/blocks/cost/cost.jsx +176 -0
  225. package/registry/blocks/cost/demo.jsx +66 -0
  226. package/registry/blocks/cost/index.js +3 -0
  227. package/registry/blocks/data-picker/README.md +81 -0
  228. package/registry/blocks/data-picker/block.json +38 -0
  229. package/registry/blocks/data-picker/data-picker.jsx +210 -0
  230. package/registry/blocks/data-picker/demo.jsx +34 -0
  231. package/registry/blocks/data-picker/fields.js +89 -0
  232. package/registry/blocks/data-picker/index.js +4 -0
  233. package/registry/blocks/data-table/demo.jsx +1 -1
  234. package/registry/blocks/first-run/README.md +118 -0
  235. package/registry/blocks/first-run/block.json +11 -0
  236. package/registry/blocks/first-run/demo.jsx +88 -0
  237. package/registry/blocks/first-run/first-run-model.js +51 -0
  238. package/registry/blocks/first-run/first-run.jsx +156 -0
  239. package/registry/blocks/first-run/index.js +5 -0
  240. package/registry/blocks/first-run/probe-rules.js +26 -0
  241. package/registry/blocks/fix-note/README.md +79 -0
  242. package/registry/blocks/fix-note/block.json +35 -0
  243. package/registry/blocks/fix-note/demo.jsx +30 -0
  244. package/registry/blocks/fix-note/fix-note.jsx +235 -0
  245. package/registry/blocks/fix-note/index.js +3 -0
  246. package/registry/blocks/flow-canvas/README.md +15 -8
  247. package/registry/blocks/flow-editor/README.md +108 -0
  248. package/registry/blocks/flow-editor/block.json +44 -0
  249. package/registry/blocks/flow-editor/demo.jsx +115 -0
  250. package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
  251. package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
  252. package/registry/blocks/flow-editor/index.js +6 -0
  253. package/registry/blocks/flow-editor/minimap.jsx +78 -0
  254. package/registry/blocks/flow-editor/outline.jsx +134 -0
  255. package/registry/blocks/footer/README.md +3 -2
  256. package/registry/blocks/library-shelf/README.md +117 -0
  257. package/registry/blocks/library-shelf/block.json +28 -0
  258. package/registry/blocks/library-shelf/demo.jsx +68 -0
  259. package/registry/blocks/library-shelf/index.js +3 -0
  260. package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
  261. package/registry/blocks/library-shelf/probe-rules.js +15 -0
  262. package/registry/blocks/margin-notes/README.md +222 -0
  263. package/registry/blocks/margin-notes/block.json +10 -0
  264. package/registry/blocks/margin-notes/demo.jsx +144 -0
  265. package/registry/blocks/margin-notes/index.js +20 -0
  266. package/registry/blocks/margin-notes/margin-model.js +224 -0
  267. package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
  268. package/registry/blocks/mind-map/README.md +4 -2
  269. package/registry/blocks/needs-you/README.md +177 -0
  270. package/registry/blocks/needs-you/block.json +34 -0
  271. package/registry/blocks/needs-you/demo.jsx +143 -0
  272. package/registry/blocks/needs-you/index.js +3 -0
  273. package/registry/blocks/needs-you/needs-you.jsx +441 -0
  274. package/registry/blocks/org-chart/README.md +4 -2
  275. package/registry/blocks/pace-bar/README.md +230 -0
  276. package/registry/blocks/pace-bar/block.json +10 -0
  277. package/registry/blocks/pace-bar/demo.jsx +255 -0
  278. package/registry/blocks/pace-bar/index.js +22 -0
  279. package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
  280. package/registry/blocks/pace-bar/pace-model.js +288 -0
  281. package/registry/blocks/page-builder/README.md +1 -1
  282. package/registry/blocks/page-builder/demo.jsx +3 -3
  283. package/registry/blocks/pdf-viewer/README.md +4 -1
  284. package/registry/blocks/record-grid/README.md +100 -0
  285. package/registry/blocks/record-grid/block.json +28 -0
  286. package/registry/blocks/record-grid/demo.jsx +43 -0
  287. package/registry/blocks/record-grid/index.js +3 -0
  288. package/registry/blocks/record-grid/record-grid.jsx +251 -0
  289. package/registry/blocks/run-history/README.md +138 -0
  290. package/registry/blocks/run-history/block.json +10 -0
  291. package/registry/blocks/run-history/demo.jsx +74 -0
  292. package/registry/blocks/run-history/index.js +3 -0
  293. package/registry/blocks/run-history/run-history.jsx +218 -0
  294. package/registry/blocks/run-view/README.md +174 -0
  295. package/registry/blocks/run-view/block.json +50 -0
  296. package/registry/blocks/run-view/demo.jsx +286 -0
  297. package/registry/blocks/run-view/index.js +5 -0
  298. package/registry/blocks/run-view/run-controls.jsx +266 -0
  299. package/registry/blocks/run-view/run-view.jsx +236 -0
  300. package/registry/blocks/run-view/runs-list.jsx +99 -0
  301. package/registry/blocks/schedule-card/README.md +121 -0
  302. package/registry/blocks/schedule-card/block.json +10 -0
  303. package/registry/blocks/schedule-card/demo.jsx +82 -0
  304. package/registry/blocks/schedule-card/index.js +3 -0
  305. package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
  306. package/registry/blocks/schedule-engine/README.md +137 -0
  307. package/registry/blocks/schedule-engine/block.json +10 -0
  308. package/registry/blocks/schedule-engine/demo.jsx +82 -0
  309. package/registry/blocks/schedule-engine/engine.js +769 -0
  310. package/registry/blocks/schedule-engine/index.js +9 -0
  311. package/registry/blocks/schedule-engine/ops.js +71 -0
  312. package/registry/blocks/schedule-engine/place.js +294 -0
  313. package/registry/blocks/schedule-engine/tree.js +79 -0
  314. package/registry/blocks/schedule-engine/waits.js +80 -0
  315. package/registry/blocks/schedule-gantt/README.md +159 -0
  316. package/registry/blocks/schedule-gantt/block.json +10 -0
  317. package/registry/blocks/schedule-gantt/demo.jsx +223 -0
  318. package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
  319. package/registry/blocks/schedule-gantt/geometry.js +176 -0
  320. package/registry/blocks/schedule-gantt/index.js +26 -0
  321. package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
  322. package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
  323. package/registry/blocks/schedule-gantt/track.jsx +190 -0
  324. package/registry/blocks/schedule-ruler/README.md +148 -0
  325. package/registry/blocks/schedule-ruler/block.json +10 -0
  326. package/registry/blocks/schedule-ruler/demo.jsx +181 -0
  327. package/registry/blocks/schedule-ruler/index.js +21 -0
  328. package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
  329. package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
  330. package/registry/blocks/sidebar/README.md +23 -13
  331. package/registry/blocks/sketch-canvas/README.md +15 -9
  332. package/registry/blocks/source-receipt/README.md +97 -0
  333. package/registry/blocks/source-receipt/block.json +30 -0
  334. package/registry/blocks/source-receipt/demo.jsx +71 -0
  335. package/registry/blocks/source-receipt/index.js +3 -0
  336. package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
  337. package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
  338. package/registry/blocks/step-picker/README.md +78 -0
  339. package/registry/blocks/step-picker/block.json +31 -0
  340. package/registry/blocks/step-picker/demo.jsx +23 -0
  341. package/registry/blocks/step-picker/index.js +3 -0
  342. package/registry/blocks/step-picker/step-picker.jsx +144 -0
  343. package/registry/blocks/step-settings/README.md +100 -0
  344. package/registry/blocks/step-settings/block.json +44 -0
  345. package/registry/blocks/step-settings/demo.jsx +93 -0
  346. package/registry/blocks/step-settings/fields.jsx +58 -0
  347. package/registry/blocks/step-settings/flow-settings.jsx +115 -0
  348. package/registry/blocks/step-settings/index.js +5 -0
  349. package/registry/blocks/step-settings/step-settings.jsx +290 -0
  350. package/registry/blocks/suggestion/README.md +171 -0
  351. package/registry/blocks/suggestion/block.json +40 -0
  352. package/registry/blocks/suggestion/demo.jsx +91 -0
  353. package/registry/blocks/suggestion/index.js +3 -0
  354. package/registry/blocks/suggestion/suggestion.jsx +436 -0
  355. package/registry/blocks/sum-bar/README.md +122 -0
  356. package/registry/blocks/sum-bar/block.json +10 -0
  357. package/registry/blocks/sum-bar/demo.jsx +110 -0
  358. package/registry/blocks/sum-bar/index.js +5 -0
  359. package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
  360. package/registry/blocks/sum-bar/sum-model.js +126 -0
  361. package/registry/blocks/tier-map/README.md +217 -0
  362. package/registry/blocks/tier-map/block.json +10 -0
  363. package/registry/blocks/tier-map/demo.jsx +222 -0
  364. package/registry/blocks/tier-map/index.js +24 -0
  365. package/registry/blocks/tier-map/tier-map.jsx +574 -0
  366. package/registry/blocks/tier-map/tier-model.js +318 -0
  367. package/registry/blocks/time-grid/README.md +305 -0
  368. package/registry/blocks/time-grid/block.json +10 -0
  369. package/registry/blocks/time-grid/demo.jsx +279 -0
  370. package/registry/blocks/time-grid/grid-model.js +322 -0
  371. package/registry/blocks/time-grid/index.js +25 -0
  372. package/registry/blocks/time-grid/time-grid.jsx +1325 -0
  373. package/registry/blocks/time-strip/README.md +141 -0
  374. package/registry/blocks/time-strip/block.json +10 -0
  375. package/registry/blocks/time-strip/demo.jsx +129 -0
  376. package/registry/blocks/time-strip/index.js +19 -0
  377. package/registry/blocks/time-strip/strip-model.js +178 -0
  378. package/registry/blocks/time-strip/time-strip.jsx +260 -0
  379. package/registry/blocks/top-bar/README.md +149 -0
  380. package/registry/blocks/top-bar/block.json +35 -0
  381. package/registry/blocks/top-bar/demo.jsx +78 -0
  382. package/registry/blocks/top-bar/index.js +5 -0
  383. package/registry/blocks/top-bar/probe-rules.js +74 -0
  384. package/registry/blocks/top-bar/top-bar-model.js +152 -0
  385. package/registry/blocks/top-bar/top-bar.jsx +244 -0
  386. package/registry/blocks/trigger-picker/README.md +98 -0
  387. package/registry/blocks/trigger-picker/block.json +38 -0
  388. package/registry/blocks/trigger-picker/demo.jsx +59 -0
  389. package/registry/blocks/trigger-picker/index.js +3 -0
  390. package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
  391. package/registry/blocks/whiteboard/README.md +20 -14
  392. package/registry/blocks/workflow-view/README.md +123 -0
  393. package/registry/blocks/workflow-view/block.json +10 -0
  394. package/registry/blocks/workflow-view/demo.jsx +90 -0
  395. package/registry/blocks/workflow-view/index.js +3 -0
  396. package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
  397. package/src/cli.mjs +2 -2
  398. package/src/install.mjs +100 -22
  399. package/src/registry.mjs +37 -3
  400. package/templates/next/browser/lib/data.js +6 -1
  401. package/templates/next/files/README.md +909 -26
  402. package/templates/next/files/app/shell.jsx +54 -37
  403. package/templates/next/files/check/automation-format.ts +2103 -0
  404. package/templates/next/files/check/automation-package.ts +142 -0
  405. package/templates/next/files/check/run.mjs +1043 -12
  406. package/templates/next/files/components/ui/field.jsx +17 -4
  407. package/templates/next/files/components/ui/icon.jsx +13 -0
  408. package/templates/next/files/components/ui/index.js +3 -1
  409. package/templates/next/files/components/ui/layout.jsx +382 -0
  410. package/templates/next/files/components/ui/notice.jsx +70 -0
  411. package/templates/next/files/components/ui/page.jsx +124 -0
  412. package/templates/next/files/components/ui/stat.jsx +22 -7
  413. package/templates/next/files/components/ui/toast.jsx +6 -3
  414. package/templates/next/files/lib/automations.js +660 -0
  415. package/templates/next/files/lib/claw.js +244 -5
  416. package/templates/next/files/lib/door.js +92 -0
  417. package/templates/next/files/lib/hooks.js +1694 -6
  418. package/templates/next/files/lib/platform.js +611 -14
  419. package/templates/next/template.json +22 -3
@@ -0,0 +1,548 @@
1
+ "use client";
2
+
3
+ import { Button, Empty, Icon, Notice, Skeleton, cx } from "../../ui";
4
+ import {
5
+ dayName,
6
+ dayNumber,
7
+ gapOf,
8
+ gapWords,
9
+ gateOf,
10
+ axisOf,
11
+ frameOf,
12
+ labelPct,
13
+ layoutLanes,
14
+ placeLabel,
15
+ readMilestones,
16
+ readSegments,
17
+ shortDay,
18
+ summaryOf,
19
+ } from "./ruler-model";
20
+
21
+ // ScheduleRuler: a dated chain drawn to scale, like a surveyor's chain.
22
+ // A baseline of days (a tick each Monday, a taller one on the 1st); the
23
+ // segments of the chain as bars with their names; a gate post at the end of
24
+ // a segment, built from its exit criteria as pips; milestone diamonds; the
25
+ // target as a solid line and the forecast as a dashed one, and between them
26
+ // the gap hatched: green when the forecast lands early (slack), red when it
27
+ // lands late (an overrun), with its size in working days ("+3 d"). Today is
28
+ // a line in the accent.
29
+ //
30
+ // GapRuler is the target against the forecast and nothing else, for a
31
+ // sentence to sit beside. ScheduleStrip is the chain small, one row, for a
32
+ // row in a list.
33
+ //
34
+ // Positions are percentages of the chain's width: nothing measures the page,
35
+ // so the server's HTML and the browser agree. The chain is never narrower
36
+ // than `minWidth`; a container narrower than that scrolls it sideways and,
37
+ // under 36rem of its own width, also lists the chain in words below it.
38
+ // Props in, one callback out (`onOpen`); it imports only React and the kit.
39
+ // README.md beside this file lists every prop.
40
+
41
+ const AXIS = 30;
42
+ const MILE_H = 20;
43
+ const GHOST_H = 14;
44
+ const GATE_H = 38;
45
+ const FLAG_H = 22;
46
+
47
+ // The hatch is drawn in currentColor, so a text-* token paints it.
48
+ const HATCH = { backgroundImage: "repeating-linear-gradient(135deg, currentColor 0 1.5px, transparent 1.5px 7px)" };
49
+ const HATCH_FINE = { backgroundImage: "repeating-linear-gradient(135deg, currentColor 0 1.2px, transparent 1.2px 5px)" };
50
+
51
+ // A segment's status: done (solid, success) · active (solid ink) · planned
52
+ // (an ink outline) · late (solid accent: past its gate) · draft (dashed,
53
+ // breathing: still being written).
54
+ const TONE = {
55
+ done: "bg-success text-background border border-success",
56
+ active: "bg-foreground text-background border border-foreground",
57
+ planned: "bg-background text-foreground border border-foreground",
58
+ late: "bg-primary text-primary-foreground border border-primary",
59
+ draft: "border-[1.5px] border-dashed border-muted-foreground bg-background text-muted-foreground motion-safe:animate-pulse",
60
+ };
61
+ const STRIP = {
62
+ done: "bg-success",
63
+ active: "bg-foreground",
64
+ late: "bg-primary",
65
+ planned: "border border-foreground bg-background",
66
+ draft: "border border-dashed border-muted-foreground",
67
+ };
68
+ const STATUS_WORDS = { done: "done", active: "in progress", planned: "planned", late: "late", draft: "draft" };
69
+
70
+ const GAP_TEXT = { over: "text-destructive", slack: "text-success", on: "text-foreground" };
71
+ const GAP_LINE = { over: "border-destructive", slack: "border-success", on: "border-foreground" };
72
+
73
+ const WORDS = {
74
+ target: "Target",
75
+ forecast: "Forecast",
76
+ today: "Today",
77
+ on: "On target",
78
+ over: (n) => `+${n} d`,
79
+ slack: (n) => `${n} d slack`,
80
+ undated: (n) => `${n} without dates, left off`,
81
+ early: "Earlier plan",
82
+ };
83
+
84
+ const pct = (n) => `${n}%`;
85
+
86
+ /** The things every form of the ruler reads the same way. */
87
+ function readProps({ segments, milestones, target, forecast, today, count, workweek }) {
88
+ const { segs, skipped } = readSegments(segments);
89
+ const miles = readMilestones(milestones);
90
+ const tgt = dayNumber(target);
91
+ const t = dayNumber(today);
92
+ // forecast: undefined → the chain's own last day; null or "" → none.
93
+ const derived = forecast === undefined && segs.length ? Math.max(...segs.map((p) => p.e)) : null;
94
+ const fc = forecast === undefined ? derived : dayNumber(forecast);
95
+ const gap = gapOf({ target: tgt, forecast: fc, count, workweek });
96
+ return { segs, skipped, miles, tgt, fc, t, gap };
97
+ }
98
+
99
+ /**
100
+ * The full chain.
101
+ *
102
+ * Props: see README.md. In short: `segments` [{ id, label, start, end,
103
+ * status, sub, gate }], `milestones` [{ id, date, label, done }], `target`,
104
+ * `forecast`, `today`, `ghost`, `count`, `words`, `compact`, `minWidth`,
105
+ * `onOpen`, `loading`, `error`, `onRetry`, `empty`, `emptyAction`.
106
+ */
107
+ export function ScheduleRuler({
108
+ segments,
109
+ milestones,
110
+ start,
111
+ target,
112
+ forecast,
113
+ today,
114
+ ghost,
115
+ count = "working",
116
+ workweek,
117
+ words: wordsIn,
118
+ compact = false,
119
+ minWidth = 560,
120
+ fitToday = true,
121
+ from,
122
+ to,
123
+ onOpen,
124
+ loading = false,
125
+ error,
126
+ onRetry,
127
+ empty,
128
+ emptyAction,
129
+ label = "Schedule",
130
+ className,
131
+ }) {
132
+ const words = { ...WORDS, ...wordsIn };
133
+
134
+ if (loading) {
135
+ return (
136
+ <div aria-busy="true" aria-label={`${label}, loading`} className={cx("@container grid min-w-0 gap-3", className)}>
137
+ <Skeleton shape="rect" className="h-4 w-full" />
138
+ <Skeleton shape="rect" className="h-12 w-full" />
139
+ <Skeleton shape="rect" className="h-4 w-2/3" />
140
+ </div>
141
+ );
142
+ }
143
+ if (error) {
144
+ return (
145
+ <div className={cx("@container min-w-0", className)}>
146
+ <Notice
147
+ tone="danger"
148
+ title={`${label} did not load`}
149
+ action={onRetry ? <Button size="sm" onClick={onRetry}>Try again</Button> : null}
150
+ >
151
+ {typeof error === "string" ? error : "Something went wrong reading the dates."}
152
+ </Notice>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ const { segs, skipped, miles, tgt, fc, t, gap } = readProps({ segments, milestones, target, forecast, today, count, workweek });
158
+ const startDay = dayNumber(start);
159
+ const ghosts = (ghost ?? [])
160
+ .map((g) => ({ s: dayNumber(g?.start), e: dayNumber(g?.end) }))
161
+ .filter((g) => g.s != null && g.e != null && g.e >= g.s);
162
+ const days = [
163
+ startDay,
164
+ ...segs.flatMap((p) => [p.s, p.e + 1]),
165
+ ...miles.flatMap((m) => [m.day, m.day + 1]),
166
+ tgt != null ? tgt + 1 : null,
167
+ fc != null ? fc + 1 : null,
168
+ ...ghosts.flatMap((g) => [g.s, g.e + 1]),
169
+ fitToday && t != null ? t : null,
170
+ ].filter((d) => d != null);
171
+ const frame = segs.length || miles.length || tgt != null ? frameOf({ days, from: dayNumber(from), to: dayNumber(to) }) : null;
172
+
173
+ if (!frame) {
174
+ return (
175
+ <div className={cx("@container min-w-0", className)}>
176
+ <Empty
177
+ icon="calendar"
178
+ title={empty ?? "Nothing is dated yet"}
179
+ body={skipped ? words.undated(skipped) : null}
180
+ action={emptyAction}
181
+ />
182
+ </div>
183
+ );
184
+ }
185
+
186
+ const { r0, r1, x } = frame;
187
+ const axis = axisOf(r0, r1);
188
+ const inWindow = (d) => d != null && d >= r0 && d <= r1;
189
+ const tIn = inWindow(t) ? t : null;
190
+
191
+ // ---- rows, top to bottom, in px
192
+ const mileItems = miles.map((m) => {
193
+ const text = `${m.label} ${shortDay(m.day)}`;
194
+ const w = labelPct(text.length, minWidth, 26);
195
+ const px = x(m.day) + (100 / (r1 - r0)) / 2;
196
+ return { id: m.id, m, px, ...placeLabel({ x: px, width: w, prefer: m.side ?? "right" }) };
197
+ });
198
+ const mileLay = layoutLanes(mileItems);
199
+ const mileZone = miles.length ? mileLay.count * MILE_H + 6 : 0;
200
+ const ghostZone = ghosts.length ? GHOST_H + 4 : 0;
201
+ const bandTop = AXIS + mileZone + ghostZone + 8;
202
+ const bandH = compact ? 36 : 48;
203
+ const bandBottom = bandTop + bandH;
204
+
205
+ const gateItems = compact
206
+ ? []
207
+ : segs
208
+ .map((p) => ({ p, g: gateOf(p.gate, p.e, t) }))
209
+ .filter((r) => r.g)
210
+ .map(({ p, g }) => {
211
+ const gx = x(g.day + 1);
212
+ // The date and the count share a line; the pips (12px each) are the other.
213
+ const chars = Math.max(shortDay(g.day).length + (g.total ? 5 : 0), g.total * 1.7);
214
+ return { id: p.id, p, g, gx, ...placeLabel({ x: gx, width: labelPct(chars, minWidth, 8), prefer: "left" }) };
215
+ });
216
+ const gateLay = layoutLanes(gateItems);
217
+ const gateZone = gateItems.length ? 8 + gateLay.count * GATE_H : 0;
218
+
219
+ const gapTop = bandBottom + gateZone + 4;
220
+ const gapRow = gap ? 20 : 0;
221
+
222
+ const flags = [];
223
+ if (tgt != null) flags.push({ id: "target", day: tgt + 1, text: `${words.target} ${dayName(tgt)}`, kind: "target" });
224
+ if (fc != null && gap?.state !== "on" && (tgt != null || forecast !== undefined)) flags.push({ id: "forecast", day: fc + 1, text: `${words.forecast} ${dayName(fc)}`, kind: "forecast" });
225
+ if (tIn != null) flags.push({ id: "today", day: tIn, text: `${words.today} ${shortDay(tIn)}`, kind: "today" });
226
+ const flagItems = compact
227
+ ? []
228
+ : flags.map((f) => {
229
+ const fx = x(f.day);
230
+ return { ...f, fx, ...placeLabel({ x: fx, width: labelPct(f.text.length, minWidth, 18), prefer: "right" }) };
231
+ });
232
+ const flagLay = layoutLanes(flagItems);
233
+ const flagTop = gapTop + gapRow + 6;
234
+ const flagZone = flagItems.length ? flagLay.count * FLAG_H : 0;
235
+ const height = (compact ? bandBottom + 6 + gapRow : flagTop + flagZone) + 4;
236
+ const bottomOf = (lane) => flagTop + lane * FLAG_H + 18;
237
+ // A line runs down to its own flag; with no flag drawn (compact) it stops under the band.
238
+ const lineBottom = (id) => (flagLay.lanes[id] != null ? bottomOf(flagLay.lanes[id]) : bandBottom + 8);
239
+
240
+ const summary = summaryOf({
241
+ label,
242
+ segs: segs.map((p) => ({ ...p, status: STATUS_WORDS[p.status] ?? p.status, gate: gateOf(p.gate, p.e, t) })),
243
+ milestones: miles,
244
+ target: tgt,
245
+ forecast: fc,
246
+ gap,
247
+ today: t,
248
+ words,
249
+ });
250
+ const open = (kind, item) => (onOpen ? () => onOpen({ kind, id: item.id, item }) : undefined);
251
+
252
+ return (
253
+ <div role="group" aria-label={label} className={cx("@container grid min-w-0 gap-3", className)}>
254
+ <p className="sr-only">{summary}</p>
255
+ <div role="region" aria-label={`${label}, scrolls sideways`} tabIndex={0} className="overflow-x-auto rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring">
256
+ <div className="relative w-full" style={{ minWidth, height }}>
257
+ {/* the axis */}
258
+ <span aria-hidden="true" className="absolute inset-x-0 h-px bg-foreground" style={{ top: AXIS - 4 }} />
259
+ {axis.weeks.map((d) => (
260
+ <span key={`w${d}`} aria-hidden="true" className="absolute h-[5px] w-px bg-foreground" style={{ left: pct(x(d)), top: AXIS - 9 }} />
261
+ ))}
262
+ {axis.months.map((m) => (
263
+ <span key={`m${m.day}`} aria-hidden="true">
264
+ {m.tick ? <span className="absolute h-[17px] w-px bg-foreground" style={{ left: pct(x(m.day)), top: AXIS - 21 }} /> : null}
265
+ <span className="absolute top-0 whitespace-nowrap pl-1.5 font-mono text-xs uppercase tracking-[0.06em] text-muted-foreground" style={{ left: pct(x(m.day)) }}>
266
+ {m.label}
267
+ </span>
268
+ </span>
269
+ ))}
270
+
271
+ {/* milestones */}
272
+ {mileItems.map((m) => {
273
+ const lane = mileLay.lanes[m.id];
274
+ const top = AXIS + 4 + lane * MILE_H;
275
+ const Mark = onOpen ? "button" : "span";
276
+ return (
277
+ <Mark
278
+ key={m.id}
279
+ {...(onOpen ? { type: "button", onClick: open("milestone", m.m), "aria-label": `${m.m.label}${m.m.done ? ", done" : ""}, ${dayName(m.m.day)}` } : { "aria-hidden": "true" })}
280
+ className={cx("absolute flex h-[18px] items-center gap-1.5 whitespace-nowrap rounded-sm bg-background pr-1 text-left font-mono text-xs outline-none", onOpen && "focus-visible:ring-2 focus-visible:ring-ring")}
281
+ style={m.side === "right" ? { left: `calc(${pct(m.px)} - 6px)`, top } : { right: `calc(${pct(100 - m.px)} - 6px)`, top, flexDirection: "row-reverse" }}
282
+ >
283
+ <span aria-hidden="true" className={cx("block size-[11px] shrink-0 rotate-45 border-[1.5px] border-foreground", m.m.done ? "bg-foreground" : "bg-background")} />
284
+ <span className="font-sans text-xs font-medium text-foreground">{m.m.label}</span>
285
+ <span className="text-muted-foreground">{shortDay(m.m.day)}</span>
286
+ </Mark>
287
+ );
288
+ })}
289
+
290
+ {/* where the chain sat before */}
291
+ {ghosts.map((g, i) => (
292
+ <span
293
+ key={`g${i}`}
294
+ aria-hidden="true"
295
+ title={words.early}
296
+ className="absolute border border-dashed border-muted-foreground"
297
+ style={{ left: pct(x(g.s)), width: `calc(${x(g.e + 1) - x(g.s)}% - 3px)`, top: bandTop - GHOST_H - 3, height: GHOST_H - 4 }}
298
+ />
299
+ ))}
300
+
301
+ {/* the segments */}
302
+ {segs.map((p, i) => {
303
+ const x0 = x(p.s);
304
+ const x1 = x(p.e + 1);
305
+ const Seg = onOpen ? "button" : "span";
306
+ const status = STATUS_WORDS[p.status] ?? p.status ?? "planned";
307
+ return (
308
+ <Seg
309
+ key={p.id}
310
+ {...(onOpen
311
+ ? { type: "button", onClick: open("segment", p), "aria-label": `${p.label}, ${status}, ${shortDay(p.s)} to ${shortDay(p.e)}` }
312
+ : { "aria-hidden": "true" })}
313
+ title={`${p.label}, ${status}, ${shortDay(p.s)} to ${shortDay(p.e)}`}
314
+ className={cx(
315
+ "absolute flex min-w-[3px] flex-col justify-between overflow-hidden px-2.5 py-2 text-left outline-none",
316
+ TONE[p.status] ?? TONE.planned,
317
+ onOpen && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
318
+ )}
319
+ style={{ left: pct(x0), width: `calc(${x1 - x0}% - 3px)`, top: bandTop, height: bandH }}
320
+ >
321
+ <span className="truncate text-sm font-semibold leading-tight">{p.label}</span>
322
+ {compact ? null : <span className="truncate font-mono text-xs leading-tight">{p.sub ?? i + 1}</span>}
323
+ </Seg>
324
+ );
325
+ })}
326
+
327
+ {/* the gap: slack in green, an overrun in red, hatched */}
328
+ {gap && gap.state !== "on" ? (
329
+ <span
330
+ aria-hidden="true"
331
+ className={cx("absolute border-2 border-current bg-background/70", GAP_TEXT[gap.state])}
332
+ style={{ ...HATCH, left: `calc(${x(gap.from)}% + 1px)`, width: `max(3px, calc(${x(gap.to + 1) - x(gap.from)}% - 3px))`, top: bandTop, height: bandH }}
333
+ />
334
+ ) : null}
335
+ {gap ? (
336
+ <span
337
+ aria-hidden="true"
338
+ className={cx("absolute whitespace-nowrap font-mono text-xs font-semibold", GAP_TEXT[gap.state])}
339
+ style={
340
+ gap.state === "on"
341
+ ? { left: pct(x(gap.target + 1)), top: gapTop, transform: x(gap.target + 1) > 78 ? "translateX(-100%)" : undefined }
342
+ : x(gap.from) > 72
343
+ ? { right: pct(100 - x(gap.to + 1)), top: gapTop }
344
+ : { left: pct(x(gap.from)), top: gapTop }
345
+ }
346
+ >
347
+ {gapWords(gap, words)}
348
+ </span>
349
+ ) : null}
350
+
351
+ {/* gate posts */}
352
+ {gateItems.map(({ id, p, g, gx, side }) => {
353
+ const lane = gateLay.lanes[id];
354
+ const top = bandBottom + 8 + lane * GATE_H;
355
+ const stateTone = g.state === "overdue" ? "text-destructive" : "text-muted-foreground";
356
+ const Post = onOpen && g.total ? "button" : "span";
357
+ const said = `${p.label} gate, ${shortDay(g.day)}${g.total ? `, ${g.met} of ${g.total} criteria met` : ""}${g.state === "overdue" ? ", overdue" : ""}`;
358
+ return (
359
+ <span key={`gate-${id}`}>
360
+ <span aria-hidden="true" className="absolute w-px bg-foreground" style={{ left: `calc(${pct(gx)} - 3.5px)`, top: bandBottom, height: 8 + lane * GATE_H }} />
361
+ <span
362
+ aria-hidden="true"
363
+ className={cx("absolute size-[9px] border border-foreground", g.state === "met" ? "bg-success border-success" : "bg-background")}
364
+ style={{ left: `calc(${pct(gx)} - 8px)`, top: bandBottom - 4 }}
365
+ />
366
+ <Post
367
+ {...(Post === "button" ? { type: "button", onClick: open("gate", p), "aria-label": said } : { "aria-hidden": "true" })}
368
+ className={cx(
369
+ "absolute grid gap-1 rounded-sm bg-background px-1 text-left outline-none",
370
+ Post === "button" && "focus-visible:ring-2 focus-visible:ring-ring",
371
+ side === "left" ? "justify-items-end" : "justify-items-start",
372
+ )}
373
+ style={side === "left" ? { right: `calc(${pct(100 - gx)} + 6px)`, top } : { left: `calc(${pct(gx)} + 3px)`, top }}
374
+ >
375
+ <span className={cx("font-mono text-xs leading-none", stateTone)}>
376
+ {shortDay(g.day)}
377
+ {g.total ? <span className="ml-1.5 tabular-nums">{g.met}/{g.total}</span> : null}
378
+ </span>
379
+ {g.total ? (
380
+ <span className="flex items-center gap-[3px]">
381
+ {g.pips.map((pip, k) => (
382
+ <span
383
+ key={pip.id ?? k}
384
+ title={`${pip.label ?? "Criterion"}${pip.met ? ", met" : ", not met"}`}
385
+ className={cx("block size-[9px] rounded-full border", pip.met ? "border-success bg-success" : "border-foreground bg-background")}
386
+ />
387
+ ))}
388
+ </span>
389
+ ) : null}
390
+ </Post>
391
+ </span>
392
+ );
393
+ })}
394
+
395
+ {/* the target, the forecast, today */}
396
+ {tgt != null ? (
397
+ <span aria-hidden="true" className="absolute w-0.5 bg-foreground" style={{ left: `calc(${pct(x(tgt + 1))} - 2px)`, top: AXIS - 14, height: lineBottom("target") - (AXIS - 14) }} />
398
+ ) : null}
399
+ {fc != null && gap?.state !== "on" && flags.some((f) => f.id === "forecast") ? (
400
+ <span
401
+ aria-hidden="true"
402
+ className={cx("absolute w-0 border-l-2 border-dashed", gap ? GAP_LINE[gap.state] : "border-foreground")}
403
+ style={{ left: `calc(${pct(x(fc + 1))} - 2px)`, top: AXIS - 14, height: lineBottom("forecast") - (AXIS - 14) }}
404
+ />
405
+ ) : null}
406
+ {tIn != null ? (
407
+ <span aria-hidden="true" className="absolute w-0.5 bg-primary" style={{ left: `calc(${pct(x(tIn))} - 1px)`, top: AXIS - 4, height: (compact ? bandBottom + 6 : lineBottom("today")) - (AXIS - 4) }} />
408
+ ) : null}
409
+ {flagItems.map((f) => {
410
+ const lane = flagLay.lanes[f.id];
411
+ const top = flagTop + lane * FLAG_H;
412
+ const place = f.side === "left" ? { right: `calc(${pct(100 - f.fx)} + 6px)` } : { left: `calc(${pct(f.fx)} + 6px)` };
413
+ const tone = f.kind === "forecast" ? (gap ? GAP_TEXT[gap.state] : "text-foreground") : "text-foreground";
414
+ return f.kind === "today" ? (
415
+ <span
416
+ key={f.id}
417
+ aria-hidden="true"
418
+ className="absolute whitespace-nowrap bg-primary px-1.5 py-0.5 font-mono text-xs font-medium text-primary-foreground"
419
+ style={f.side === "left" ? { right: `calc(${pct(100 - f.fx)} - 1px)`, top } : { left: `calc(${pct(f.fx)} - 1px)`, top }}
420
+ >
421
+ {f.text}
422
+ </span>
423
+ ) : (
424
+ <span key={f.id} aria-hidden="true" className={cx("absolute flex items-center gap-1 whitespace-nowrap bg-background px-1 font-mono text-xs font-medium", tone)} style={{ ...place, top }}>
425
+ <Icon name="flag" size={12} className="shrink-0" />
426
+ {f.text}
427
+ </span>
428
+ );
429
+ })}
430
+ </div>
431
+ </div>
432
+
433
+ {skipped ? <p className="text-xs text-muted-foreground">{words.undated(skipped)}</p> : null}
434
+
435
+ {/* under 36rem the same chain in words, because a scrolled picture hides its ends */}
436
+ <ol aria-hidden="true" className="grid min-w-0 gap-1.5 @xl:hidden">
437
+ {segs.map((p, i) => {
438
+ const g = gateOf(p.gate, p.e, t);
439
+ return (
440
+ <li key={p.id} className="flex min-w-0 items-baseline justify-between gap-3 border-t border-border pt-1.5 text-sm">
441
+ <span className="min-w-0 truncate font-medium">{i + 1}. {p.label}</span>
442
+ <span className="shrink-0 text-right font-mono text-xs tabular-nums text-muted-foreground">
443
+ {shortDay(p.s)} to {shortDay(p.e)}
444
+ {g?.total ? ` · ${g.met}/${g.total} met` : ""}
445
+ </span>
446
+ </li>
447
+ );
448
+ })}
449
+ {miles.map((m) => (
450
+ <li key={m.id} className="flex min-w-0 items-baseline justify-between gap-3 border-t border-border pt-1.5 text-sm">
451
+ <span className="min-w-0 truncate font-medium">{m.label}</span>
452
+ <span className="shrink-0 font-mono text-xs tabular-nums text-muted-foreground">{shortDay(m.day)}{m.done ? " · done" : ""}</span>
453
+ </li>
454
+ ))}
455
+ {tgt != null || gap ? (
456
+ <li className="flex min-w-0 flex-col gap-0.5 border-t border-border pt-1.5 font-mono text-xs tabular-nums">
457
+ {tgt != null ? <span className="text-foreground">{words.target} {dayName(tgt)}</span> : null}
458
+ {fc != null && gap?.state !== "on" ? <span className={gap ? GAP_TEXT[gap.state] : "text-foreground"}>{words.forecast} {dayName(fc)}</span> : null}
459
+ {gap ? <span className={cx("font-semibold", GAP_TEXT[gap.state])}>{gapWords(gap, words)}</span> : null}
460
+ </li>
461
+ ) : null}
462
+ </ol>
463
+ </div>
464
+ );
465
+ }
466
+
467
+ /**
468
+ * The target against the forecast, drawn and nothing else: a dashed line
469
+ * for the target, a solid bar for the forecast, the gap between them
470
+ * hatched (green early, red late) and its size in words underneath. For a
471
+ * sentence to sit beside ("Public launch lands Tue 3 Nov").
472
+ */
473
+ export function GapRuler({ target, forecast, count = "working", workweek, words: wordsIn, label, width = 380, className }) {
474
+ const words = { ...WORDS, ...wordsIn };
475
+ const gap = gapOf({ target, forecast, count, workweek });
476
+ if (!gap) return null;
477
+ const lo = Math.min(gap.target, gap.forecast);
478
+ const hi = Math.max(gap.target, gap.forecast);
479
+ const total = Math.max(hi - lo + 6, 14);
480
+ const r0 = lo - Math.floor((total - (hi - lo)) / 2);
481
+ const at = (d) => ((d - r0) / total) * 100;
482
+ const tx = at(gap.target);
483
+ const fx = at(gap.forecast);
484
+ const a = Math.min(tx, fx);
485
+ const b = Math.max(tx, fx);
486
+ const tone = GAP_TEXT[gap.state];
487
+ const earlierIsTarget = gap.target <= gap.forecast;
488
+ const said = label ?? gapWords(gap, words);
489
+ return (
490
+ <div
491
+ role="img"
492
+ aria-label={`${words.target} ${dayName(gap.target)}, ${words.forecast.toLowerCase()} ${dayName(gap.forecast)}: ${said}`}
493
+ className={cx("@container relative h-[92px] w-full min-w-0", className)}
494
+ style={{ maxWidth: width }}
495
+ >
496
+ <span aria-hidden="true" className="absolute inset-x-0 top-[46px] h-px bg-foreground" />
497
+ {gap.state !== "on" ? (
498
+ <span aria-hidden="true" className={cx("absolute top-[34px] h-6 border-y-2 border-current", tone)} style={{ ...HATCH, left: pct(a), width: pct(b - a) }} />
499
+ ) : null}
500
+ <span aria-hidden="true" className="absolute top-6 h-11 w-0 border-l-[1.5px] border-dashed border-foreground" style={{ left: pct(tx) }} />
501
+ <span aria-hidden="true" className={cx("absolute top-5 h-[52px] w-[3px] -translate-x-px bg-current", tone)} style={{ left: pct(fx) }} />
502
+ <span aria-hidden="true" className={cx("absolute top-0 whitespace-nowrap font-mono text-xs leading-tight text-muted-foreground", earlierIsTarget ? "-translate-x-full pr-1.5 text-right" : "pl-1.5")} style={{ left: pct(tx) }}>
503
+ {words.target.toUpperCase()}
504
+ <br />
505
+ {shortDay(gap.target)}
506
+ </span>
507
+ <span aria-hidden="true" className={cx("absolute top-0 whitespace-nowrap font-mono text-xs font-semibold leading-tight", tone, earlierIsTarget ? "pl-1.5" : "-translate-x-full pr-1.5 text-right")} style={{ left: pct(fx) }}>
508
+ {words.forecast.toUpperCase()}
509
+ <br />
510
+ {shortDay(gap.forecast)}
511
+ </span>
512
+ <span aria-hidden="true" className={cx("absolute top-[72px] whitespace-nowrap font-mono text-xs font-semibold", tone)} style={{ left: pct((a + b) / 2), transform: "translateX(-50%)" }}>
513
+ {said}
514
+ </span>
515
+ </div>
516
+ );
517
+ }
518
+
519
+ /**
520
+ * The chain small: one row of bars with the gap hatched, the target in ink
521
+ * and today in the accent. For a row in a list; put the numbers in words
522
+ * beside it.
523
+ */
524
+ export function ScheduleStrip({ segments, target, forecast, today, count = "working", workweek, height = 16, label = "Schedule", className }) {
525
+ const { segs, tgt, fc, t, gap } = readProps({ segments, target, forecast, today, count, workweek });
526
+ const days = [...segs.flatMap((p) => [p.s, p.e + 1]), tgt != null ? tgt + 1 : null, fc != null ? fc + 1 : null].filter((d) => d != null);
527
+ const frame = frameOf({ days, padBefore: 0, padAfter: 1 });
528
+ if (!frame || (!segs.length && tgt == null)) return <div aria-hidden="true" className={cx("w-full border-b border-border", className)} style={{ height: height + 6 }} />;
529
+ const { x, r0, r1 } = frame;
530
+ const summary = segs.map((p) => `${p.label} ${STATUS_WORDS[p.status] ?? p.status ?? "planned"}`).join(", ");
531
+ return (
532
+ <div
533
+ role="img"
534
+ aria-label={`${label}: ${summary || "no dated items"}${gap ? `; ${gapWords(gap, { ...WORDS, over: (n) => `${n} days over`, slack: (n) => `${n} days of slack` })}` : ""}`}
535
+ className={cx("relative w-full min-w-0 border-b border-border", className)}
536
+ style={{ height: height + 6 }}
537
+ >
538
+ {segs.map((p) => (
539
+ <span key={p.id} aria-hidden="true" className={cx("absolute top-0", STRIP[p.status] ?? STRIP.planned)} style={{ left: pct(x(p.s)), width: `calc(${x(p.e + 1) - x(p.s)}% - 2px)`, height }} />
540
+ ))}
541
+ {gap && gap.state !== "on" ? (
542
+ <span aria-hidden="true" className={cx("absolute top-0 border border-current bg-background/70", GAP_TEXT[gap.state])} style={{ ...HATCH_FINE, left: pct(x(gap.from)), width: `max(2px, calc(${x(gap.to + 1) - x(gap.from)}% - 1px))`, height }} />
543
+ ) : null}
544
+ {tgt != null ? <span aria-hidden="true" className="absolute -top-1 w-0.5 bg-foreground" style={{ left: `calc(${pct(x(tgt + 1))} - 1px)`, height: height + 10 }} /> : null}
545
+ {t != null && t >= r0 && t <= r1 ? <span aria-hidden="true" className="absolute w-0.5 bg-primary" style={{ left: `calc(${pct(x(t))} - 1px)`, top: -5, height: height + 11 }} /> : null}
546
+ </div>
547
+ );
548
+ }
@@ -57,8 +57,11 @@ true) is where it starts. The icon rail shows every group unfolded.
57
57
  - `shortcut` — Cmd/Ctrl + this key toggles it; default `"b"`, `null` for none.
58
58
  - `variant` — `"inset"` draws the page as a card on the frame's ground.
59
59
  Default: the Sidebar's own `variant`, so you rarely pass it here.
60
- - `className` — the frame's height and edges (`min-h-dvh` in a shell).
61
- Never colour.
60
+ - `className` — the frame's edges, and its height from its parent: in a
61
+ shell it sits in the kit's `AppLayout contentMode="fill"` with
62
+ `min-h-0 flex-1` (the Example). The kit's `AppLayout` owns the
63
+ window's height; `min-h-dvh` or `h-dvh` here is refused by `npm run
64
+ check`. Never colour.
62
65
 
63
66
  ### Sidebar
64
67
 
@@ -138,6 +141,7 @@ The header button: opens the sheet on a phone, collapses to icons from
138
141
  // app/shell.jsx — the app's shell, the surfaces as the sidebar's items
139
142
  "use client";
140
143
  import { usePathname } from "next/navigation";
144
+ import { AppLayout } from "../components/ui";
141
145
  import { Sidebar, SidebarLayout, SidebarTrigger } from "../components/blocks/sidebar";
142
146
 
143
147
  const ICONS = { inbox: "inbox", deals: "dollar", contacts: "users", reports: "chart" };
@@ -153,18 +157,24 @@ export default function Shell({ name, surfaces, children }) {
153
157
  active: s.key === activeKey,
154
158
  badge: s.built ? undefined : "planned",
155
159
  }));
160
+ // The outer AppLayout owns the window; the inner one (it fills its
161
+ // slot) is the content that scrolls, back to its top on a new route.
156
162
  return (
157
- <SidebarLayout
158
- storageKey={`${name}.sidebar`}
159
- className="min-h-dvh"
160
- sidebar={<Sidebar title={name} items={items} label={`${name} surfaces`} />}
161
- >
162
- <header className="flex min-h-14 items-center gap-2 border-b border-border px-2 md:hidden">
163
- <SidebarTrigger />
164
- <span className="font-semibold">{name}</span>
165
- </header>
166
- <main className="min-w-0 flex-1 p-4 sm:p-6">{children}</main>
167
- </SidebarLayout>
163
+ <AppLayout contentMode="fill">
164
+ <SidebarLayout
165
+ storageKey={`${name}.sidebar`}
166
+ className="min-h-0 flex-1"
167
+ sidebar={<Sidebar title={name} items={items} label={`${name} surfaces`} />}
168
+ >
169
+ <header className="flex min-h-14 items-center gap-2 border-b border-border px-2 md:hidden">
170
+ <SidebarTrigger />
171
+ <span className="font-semibold">{name}</span>
172
+ </header>
173
+ <AppLayout contentClassName="p-4 sm:p-6" scrollKey={pathname}>
174
+ {children}
175
+ </AppLayout>
176
+ </SidebarLayout>
177
+ </AppLayout>
168
178
  );
169
179
  }
170
180
  ```
@@ -130,8 +130,11 @@ A sketch is plain JSON. Store it as it comes out of `onChange` / `onSave`
130
130
  - `onReady(handle)` — once, when the canvas has mounted.
131
131
  - `label` — the accessible name of the canvas region (default "Sketch").
132
132
  - `className` — layout: width, margin, height. The default height is
133
- `h-[32rem]`; a height class of yours (`h-dvh`, `h-full`, `min-h-…`,
134
- `flex-1`) replaces it. Never colour.
133
+ `h-[32rem]`; a height class of yours (`h-full`, `min-h-…`, `min-h-0
134
+ flex-1`) replaces it. A canvas that is the whole surface sits in an
135
+ `AppLayout contentMode="fill"` from the kit with `min-h-0 flex-1` (the
136
+ Example): never `h-dvh` or a `vh` height, which `npm run check`
137
+ refuses. Never colour.
135
138
 
136
139
  ### The handle
137
140
 
@@ -220,6 +223,7 @@ A whiteboard that saves itself:
220
223
  import { useSearchParams } from "next/navigation";
221
224
  import { useRow } from "../lib/data";
222
225
  import { updateRow } from "../lib/actions";
226
+ import { AppLayout } from "../components/ui";
223
227
  import { SketchCanvas } from "../components/blocks/sketch-canvas";
224
228
 
225
229
  export default function Board() {
@@ -227,13 +231,15 @@ export default function Board() {
227
231
  const { row: board, loading } = useRow({ source: "boards", id });
228
232
  if (loading || !board) return null;
229
233
  return (
230
- <SketchCanvas
231
- key={board.id}
232
- label={board.title}
233
- defaultValue={board.sketch} // jsonb, may be null; read once per board
234
- onSave={(sketch) => updateRow({ source: "boards", id: board.id, patch: { sketch } })}
235
- className="h-dvh rounded-none border-0"
236
- />
234
+ <AppLayout contentMode="fill">
235
+ <SketchCanvas
236
+ key={board.id}
237
+ label={board.title}
238
+ defaultValue={board.sketch} // jsonb, may be null; read once per board
239
+ onSave={(sketch) => updateRow({ source: "boards", id: board.id, patch: { sketch } })}
240
+ className="min-h-0 flex-1 rounded-none border-0"
241
+ />
242
+ </AppLayout>
237
243
  );
238
244
  }
239
245
  ```