@elaraai/e3-ui 1.0.86 → 1.0.88

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 (385) hide show
  1. package/dist/src/bind/data-indexed.d.ts +159 -0
  2. package/dist/src/bind/data-indexed.d.ts.map +1 -0
  3. package/dist/src/bind/data-indexed.js +105 -0
  4. package/dist/src/bind/data-indexed.js.map +1 -0
  5. package/dist/src/bind/data.d.ts +5 -3
  6. package/dist/src/bind/data.d.ts.map +1 -1
  7. package/dist/src/bind/data.js +6 -0
  8. package/dist/src/bind/data.js.map +1 -1
  9. package/dist/src/bind/record.d.ts +34 -23
  10. package/dist/src/bind/record.d.ts.map +1 -1
  11. package/dist/src/bind/record.js +46 -19
  12. package/dist/src/bind/record.js.map +1 -1
  13. package/dist/src/calendar/index.d.ts +2923 -0
  14. package/dist/src/calendar/index.d.ts.map +1 -0
  15. package/dist/src/calendar/index.js +135 -0
  16. package/dist/src/calendar/index.js.map +1 -0
  17. package/dist/src/decision/queue.d.ts +3 -3
  18. package/dist/src/decision/queue.d.ts.map +1 -1
  19. package/dist/src/decision/queue.js +2 -2
  20. package/dist/src/decision/queue.js.map +1 -1
  21. package/dist/src/decision/types.d.ts +4 -0
  22. package/dist/src/decision/types.d.ts.map +1 -1
  23. package/dist/src/decision/types.js +4 -0
  24. package/dist/src/decision/types.js.map +1 -1
  25. package/dist/src/flowchart/canvas.d.ts +114 -0
  26. package/dist/src/flowchart/canvas.d.ts.map +1 -0
  27. package/dist/src/flowchart/canvas.js +84 -0
  28. package/dist/src/flowchart/canvas.js.map +1 -0
  29. package/dist/src/flowchart/flowchart.d.ts +79 -0
  30. package/dist/src/flowchart/flowchart.d.ts.map +1 -0
  31. package/dist/src/flowchart/flowchart.js +9 -0
  32. package/dist/src/flowchart/flowchart.js.map +1 -0
  33. package/dist/src/flowchart/index.d.ts +305 -0
  34. package/dist/src/flowchart/index.d.ts.map +1 -0
  35. package/dist/src/flowchart/index.js +267 -0
  36. package/dist/src/flowchart/index.js.map +1 -0
  37. package/dist/src/flowchart/library.d.ts +720 -0
  38. package/dist/src/flowchart/library.d.ts.map +1 -0
  39. package/dist/src/flowchart/library.js +611 -0
  40. package/dist/src/flowchart/library.js.map +1 -0
  41. package/dist/src/flowchart/over.d.ts +234 -0
  42. package/dist/src/flowchart/over.d.ts.map +1 -0
  43. package/dist/src/flowchart/over.js +220 -0
  44. package/dist/src/flowchart/over.js.map +1 -0
  45. package/dist/src/flowchart/patch.d.ts +64 -0
  46. package/dist/src/flowchart/patch.d.ts.map +1 -0
  47. package/dist/src/flowchart/patch.js +67 -0
  48. package/dist/src/flowchart/patch.js.map +1 -0
  49. package/dist/src/flowchart/payload.d.ts +8220 -0
  50. package/dist/src/flowchart/payload.d.ts.map +1 -0
  51. package/dist/src/flowchart/payload.js +521 -0
  52. package/dist/src/flowchart/payload.js.map +1 -0
  53. package/dist/src/flowchart/types.d.ts +393 -0
  54. package/dist/src/flowchart/types.d.ts.map +1 -0
  55. package/dist/src/flowchart/types.js +216 -0
  56. package/dist/src/flowchart/types.js.map +1 -0
  57. package/dist/src/flowchart/values.d.ts +267 -0
  58. package/dist/src/flowchart/values.d.ts.map +1 -0
  59. package/dist/src/flowchart/values.js +216 -0
  60. package/dist/src/flowchart/values.js.map +1 -0
  61. package/dist/src/flowchart/window.d.ts +521 -0
  62. package/dist/src/flowchart/window.d.ts.map +1 -0
  63. package/dist/src/flowchart/window.js +69 -0
  64. package/dist/src/flowchart/window.js.map +1 -0
  65. package/dist/src/index.d.ts +25 -1
  66. package/dist/src/index.d.ts.map +1 -1
  67. package/dist/src/index.js +48 -1
  68. package/dist/src/index.js.map +1 -1
  69. package/dist/src/internal.d.ts +35 -3
  70. package/dist/src/internal.d.ts.map +1 -1
  71. package/dist/src/internal.js +72 -3
  72. package/dist/src/internal.js.map +1 -1
  73. package/dist/src/plan/assemble.d.ts +2224 -0
  74. package/dist/src/plan/assemble.d.ts.map +1 -0
  75. package/dist/src/plan/assemble.js +210 -0
  76. package/dist/src/plan/assemble.js.map +1 -0
  77. package/dist/src/plan/builders.d.ts +790 -0
  78. package/dist/src/plan/builders.d.ts.map +1 -0
  79. package/dist/src/plan/builders.js +749 -0
  80. package/dist/src/plan/builders.js.map +1 -0
  81. package/dist/src/plan/event-rows.d.ts +2565 -0
  82. package/dist/src/plan/event-rows.d.ts.map +1 -0
  83. package/dist/src/plan/event-rows.js +689 -0
  84. package/dist/src/plan/event-rows.js.map +1 -0
  85. package/dist/src/plan/factories.d.ts +458 -0
  86. package/dist/src/plan/factories.d.ts.map +1 -0
  87. package/dist/src/plan/factories.js +484 -0
  88. package/dist/src/plan/factories.js.map +1 -0
  89. package/dist/src/plan/index.d.ts +672 -0
  90. package/dist/src/plan/index.d.ts.map +1 -0
  91. package/dist/src/plan/index.js +532 -0
  92. package/dist/src/plan/index.js.map +1 -0
  93. package/dist/src/plan/ir.d.ts +2594 -0
  94. package/dist/src/plan/ir.d.ts.map +1 -0
  95. package/dist/src/plan/ir.js +78 -0
  96. package/dist/src/plan/ir.js.map +1 -0
  97. package/dist/src/plan/library.d.ts +772 -0
  98. package/dist/src/plan/library.d.ts.map +1 -0
  99. package/dist/src/plan/library.js +424 -0
  100. package/dist/src/plan/library.js.map +1 -0
  101. package/dist/src/plan/ordered-source.d.ts +27 -0
  102. package/dist/src/plan/ordered-source.d.ts.map +1 -0
  103. package/dist/src/plan/ordered-source.js +48 -0
  104. package/dist/src/plan/ordered-source.js.map +1 -0
  105. package/dist/src/plan/over.d.ts +247 -0
  106. package/dist/src/plan/over.d.ts.map +1 -0
  107. package/dist/src/plan/over.js +266 -0
  108. package/dist/src/plan/over.js.map +1 -0
  109. package/dist/src/plan/pick.d.ts +78 -0
  110. package/dist/src/plan/pick.d.ts.map +1 -0
  111. package/dist/src/plan/pick.js +146 -0
  112. package/dist/src/plan/pick.js.map +1 -0
  113. package/dist/src/plan/plan.d.ts +11611 -0
  114. package/dist/src/plan/plan.d.ts.map +1 -0
  115. package/dist/src/plan/plan.js +428 -0
  116. package/dist/src/plan/plan.js.map +1 -0
  117. package/dist/src/plan/refs.d.ts +218 -0
  118. package/dist/src/plan/refs.d.ts.map +1 -0
  119. package/dist/src/plan/refs.js +226 -0
  120. package/dist/src/plan/refs.js.map +1 -0
  121. package/dist/src/plan/root.d.ts +290 -0
  122. package/dist/src/plan/root.d.ts.map +1 -0
  123. package/dist/src/plan/root.js +463 -0
  124. package/dist/src/plan/root.js.map +1 -0
  125. package/dist/src/plan/series.d.ts +13554 -0
  126. package/dist/src/plan/series.d.ts.map +1 -0
  127. package/dist/src/plan/series.js +1921 -0
  128. package/dist/src/plan/series.js.map +1 -0
  129. package/dist/src/plan/types.d.ts +12400 -0
  130. package/dist/src/plan/types.d.ts.map +1 -0
  131. package/dist/src/plan/types.js +1335 -0
  132. package/dist/src/plan/types.js.map +1 -0
  133. package/dist/src/roster/coverage.d.ts +240 -0
  134. package/dist/src/roster/coverage.d.ts.map +1 -0
  135. package/dist/src/roster/coverage.js +109 -0
  136. package/dist/src/roster/coverage.js.map +1 -0
  137. package/dist/src/roster/index.d.ts +4028 -0
  138. package/dist/src/roster/index.d.ts.map +1 -0
  139. package/dist/src/roster/index.js +115 -0
  140. package/dist/src/roster/index.js.map +1 -0
  141. package/dist/src/roster/library.d.ts +106 -0
  142. package/dist/src/roster/library.d.ts.map +1 -0
  143. package/dist/src/roster/library.js +78 -0
  144. package/dist/src/roster/library.js.map +1 -0
  145. package/dist/src/roster/people.d.ts +50 -0
  146. package/dist/src/roster/people.d.ts.map +1 -0
  147. package/dist/src/roster/people.js +32 -0
  148. package/dist/src/roster/people.js.map +1 -0
  149. package/dist/src/roster/rules.d.ts +275 -0
  150. package/dist/src/roster/rules.d.ts.map +1 -0
  151. package/dist/src/roster/rules.js +162 -0
  152. package/dist/src/roster/rules.js.map +1 -0
  153. package/dist/src/roster/types.d.ts +672 -0
  154. package/dist/src/roster/types.d.ts.map +1 -0
  155. package/dist/src/roster/types.js +248 -0
  156. package/dist/src/roster/types.js.map +1 -0
  157. package/dist/src/roster/visibility.d.ts +66 -0
  158. package/dist/src/roster/visibility.d.ts.map +1 -0
  159. package/dist/src/roster/visibility.js +25 -0
  160. package/dist/src/roster/visibility.js.map +1 -0
  161. package/dist/src/schedule/days.d.ts +54 -0
  162. package/dist/src/schedule/days.d.ts.map +1 -0
  163. package/dist/src/schedule/days.js +68 -0
  164. package/dist/src/schedule/days.js.map +1 -0
  165. package/dist/src/schedule/events.d.ts +433 -0
  166. package/dist/src/schedule/events.d.ts.map +1 -0
  167. package/dist/src/schedule/events.js +851 -0
  168. package/dist/src/schedule/events.js.map +1 -0
  169. package/dist/src/schedule/index.d.ts +150 -0
  170. package/dist/src/schedule/index.d.ts.map +1 -0
  171. package/dist/src/schedule/index.js +132 -0
  172. package/dist/src/schedule/index.js.map +1 -0
  173. package/dist/src/schedule/library.d.ts +70 -0
  174. package/dist/src/schedule/library.d.ts.map +1 -0
  175. package/dist/src/schedule/library.js +102 -0
  176. package/dist/src/schedule/library.js.map +1 -0
  177. package/dist/src/schedule/patch.d.ts +74 -0
  178. package/dist/src/schedule/patch.d.ts.map +1 -0
  179. package/dist/src/schedule/patch.js +68 -0
  180. package/dist/src/schedule/patch.js.map +1 -0
  181. package/dist/src/schedule/record-window.d.ts +189 -0
  182. package/dist/src/schedule/record-window.d.ts.map +1 -0
  183. package/dist/src/schedule/record-window.js +95 -0
  184. package/dist/src/schedule/record-window.js.map +1 -0
  185. package/dist/src/schedule/resources.d.ts +165 -0
  186. package/dist/src/schedule/resources.d.ts.map +1 -0
  187. package/dist/src/schedule/resources.js +234 -0
  188. package/dist/src/schedule/resources.js.map +1 -0
  189. package/dist/src/schedule/templates.d.ts +47 -0
  190. package/dist/src/schedule/templates.d.ts.map +1 -0
  191. package/dist/src/schedule/templates.js +61 -0
  192. package/dist/src/schedule/templates.js.map +1 -0
  193. package/dist/src/schedule/types.d.ts +1677 -0
  194. package/dist/src/schedule/types.d.ts.map +1 -0
  195. package/dist/src/schedule/types.js +374 -0
  196. package/dist/src/schedule/types.js.map +1 -0
  197. package/dist/src/schedule/window.d.ts +154 -0
  198. package/dist/src/schedule/window.d.ts.map +1 -0
  199. package/dist/src/schedule/window.js +363 -0
  200. package/dist/src/schedule/window.js.map +1 -0
  201. package/dist/src/sheet/bridge.d.ts +385 -0
  202. package/dist/src/sheet/bridge.d.ts.map +1 -0
  203. package/dist/src/sheet/bridge.js +809 -0
  204. package/dist/src/sheet/bridge.js.map +1 -0
  205. package/dist/src/sheet/columns.d.ts +410 -0
  206. package/dist/src/sheet/columns.d.ts.map +1 -0
  207. package/dist/src/sheet/columns.js +99 -0
  208. package/dist/src/sheet/columns.js.map +1 -0
  209. package/dist/src/sheet/context-bridge.d.ts +48 -0
  210. package/dist/src/sheet/context-bridge.d.ts.map +1 -0
  211. package/dist/src/sheet/context-bridge.js +216 -0
  212. package/dist/src/sheet/context-bridge.js.map +1 -0
  213. package/dist/src/sheet/draft-bridge.d.ts +37 -0
  214. package/dist/src/sheet/draft-bridge.d.ts.map +1 -0
  215. package/dist/src/sheet/draft-bridge.js +153 -0
  216. package/dist/src/sheet/draft-bridge.js.map +1 -0
  217. package/dist/src/sheet/drafts.d.ts +14 -0
  218. package/dist/src/sheet/drafts.d.ts.map +1 -0
  219. package/dist/src/sheet/drafts.js +14 -0
  220. package/dist/src/sheet/drafts.js.map +1 -0
  221. package/dist/src/sheet/editing-bridge.d.ts +40 -0
  222. package/dist/src/sheet/editing-bridge.d.ts.map +1 -0
  223. package/dist/src/sheet/editing-bridge.js +231 -0
  224. package/dist/src/sheet/editing-bridge.js.map +1 -0
  225. package/dist/src/sheet/editing-types.d.ts +268 -0
  226. package/dist/src/sheet/editing-types.d.ts.map +1 -0
  227. package/dist/src/sheet/editing-types.js +34 -0
  228. package/dist/src/sheet/editing-types.js.map +1 -0
  229. package/dist/src/sheet/edits.d.ts +37 -0
  230. package/dist/src/sheet/edits.d.ts.map +1 -0
  231. package/dist/src/sheet/edits.js +31 -0
  232. package/dist/src/sheet/edits.js.map +1 -0
  233. package/dist/src/sheet/fields.d.ts +693 -0
  234. package/dist/src/sheet/fields.d.ts.map +1 -0
  235. package/dist/src/sheet/fields.js +280 -0
  236. package/dist/src/sheet/fields.js.map +1 -0
  237. package/dist/src/sheet/group.d.ts +165 -0
  238. package/dist/src/sheet/group.d.ts.map +1 -0
  239. package/dist/src/sheet/group.js +52 -0
  240. package/dist/src/sheet/group.js.map +1 -0
  241. package/dist/src/sheet/index.d.ts +542 -0
  242. package/dist/src/sheet/index.d.ts.map +1 -0
  243. package/dist/src/sheet/index.js +387 -0
  244. package/dist/src/sheet/index.js.map +1 -0
  245. package/dist/src/sheet/library.d.ts +284 -0
  246. package/dist/src/sheet/library.d.ts.map +1 -0
  247. package/dist/src/sheet/library.js +259 -0
  248. package/dist/src/sheet/library.js.map +1 -0
  249. package/dist/src/sheet/link.d.ts +398 -0
  250. package/dist/src/sheet/link.d.ts.map +1 -0
  251. package/dist/src/sheet/link.js +228 -0
  252. package/dist/src/sheet/link.js.map +1 -0
  253. package/dist/src/sheet/record.d.ts +75 -0
  254. package/dist/src/sheet/record.d.ts.map +1 -0
  255. package/dist/src/sheet/record.js +128 -0
  256. package/dist/src/sheet/record.js.map +1 -0
  257. package/dist/src/sheet/registers.d.ts +681 -0
  258. package/dist/src/sheet/registers.d.ts.map +1 -0
  259. package/dist/src/sheet/registers.js +671 -0
  260. package/dist/src/sheet/registers.js.map +1 -0
  261. package/dist/src/sheet/root.d.ts +329 -0
  262. package/dist/src/sheet/root.d.ts.map +1 -0
  263. package/dist/src/sheet/root.js +511 -0
  264. package/dist/src/sheet/root.js.map +1 -0
  265. package/dist/src/sheet/seed-bridge.d.ts +14 -0
  266. package/dist/src/sheet/seed-bridge.d.ts.map +1 -0
  267. package/dist/src/sheet/seed-bridge.js +51 -0
  268. package/dist/src/sheet/seed-bridge.js.map +1 -0
  269. package/dist/src/sheet/sheet.d.ts +11819 -0
  270. package/dist/src/sheet/sheet.d.ts.map +1 -0
  271. package/dist/src/sheet/sheet.js +236 -0
  272. package/dist/src/sheet/sheet.js.map +1 -0
  273. package/dist/src/sheet/sub-rows.d.ts +299 -0
  274. package/dist/src/sheet/sub-rows.d.ts.map +1 -0
  275. package/dist/src/sheet/sub-rows.js +272 -0
  276. package/dist/src/sheet/sub-rows.js.map +1 -0
  277. package/dist/src/sheet/templates.d.ts +161 -0
  278. package/dist/src/sheet/templates.d.ts.map +1 -0
  279. package/dist/src/sheet/templates.js +122 -0
  280. package/dist/src/sheet/templates.js.map +1 -0
  281. package/dist/src/sheet/transactions.d.ts +76 -0
  282. package/dist/src/sheet/transactions.d.ts.map +1 -0
  283. package/dist/src/sheet/transactions.js +40 -0
  284. package/dist/src/sheet/transactions.js.map +1 -0
  285. package/dist/src/sheet/types.d.ts +15226 -0
  286. package/dist/src/sheet/types.d.ts.map +1 -0
  287. package/dist/src/sheet/types.js +1070 -0
  288. package/dist/src/sheet/types.js.map +1 -0
  289. package/dist/src/sheet/views.d.ts +29 -0
  290. package/dist/src/sheet/views.d.ts.map +1 -0
  291. package/dist/src/sheet/views.js +43 -0
  292. package/dist/src/sheet/views.js.map +1 -0
  293. package/dist/src/studio/component.d.ts.map +1 -1
  294. package/dist/src/studio/component.js +11 -8
  295. package/dist/src/studio/component.js.map +1 -1
  296. package/dist/src/utils/derive.d.ts +134 -8
  297. package/dist/src/utils/derive.d.ts.map +1 -1
  298. package/dist/src/utils/derive.js +409 -7
  299. package/dist/src/utils/derive.js.map +1 -1
  300. package/dist/src/utils/ordered-source.d.ts +38 -0
  301. package/dist/src/utils/ordered-source.d.ts.map +1 -0
  302. package/dist/src/utils/ordered-source.js +45 -0
  303. package/dist/src/utils/ordered-source.js.map +1 -0
  304. package/dist/src/utils/row-inspector.d.ts +36 -0
  305. package/dist/src/utils/row-inspector.d.ts.map +1 -0
  306. package/dist/src/utils/row-inspector.js +49 -0
  307. package/dist/src/utils/row-inspector.js.map +1 -0
  308. package/dist/test/bind/data/data.examples.d.ts +21 -19
  309. package/dist/test/bind/data/data.examples.d.ts.map +1 -1
  310. package/dist/test/bind/data/data.examples.js +122 -122
  311. package/dist/test/bind/data/data.examples.js.map +1 -1
  312. package/dist/test/bind/record/record.examples.d.ts.map +1 -1
  313. package/dist/test/bind/record/record.examples.js +9 -9
  314. package/dist/test/bind/record/record.examples.js.map +1 -1
  315. package/dist/test/calendar/calendar-records.examples.d.ts +228 -0
  316. package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -0
  317. package/dist/test/calendar/calendar-records.examples.js +91 -0
  318. package/dist/test/calendar/calendar-records.examples.js.map +1 -0
  319. package/dist/test/calendar/calendar.examples.d.ts +776 -0
  320. package/dist/test/calendar/calendar.examples.d.ts.map +1 -0
  321. package/dist/test/calendar/calendar.examples.js +336 -0
  322. package/dist/test/calendar/calendar.examples.js.map +1 -0
  323. package/dist/test/decision/journal.examples.d.ts +28 -28
  324. package/dist/test/decision/journal.examples.d.ts.map +1 -1
  325. package/dist/test/decision/journal.examples.js +4 -4
  326. package/dist/test/decision/journal.examples.js.map +1 -1
  327. package/dist/test/decision/queue.examples.d.ts.map +1 -1
  328. package/dist/test/decision/queue.examples.js +5 -7
  329. package/dist/test/decision/queue.examples.js.map +1 -1
  330. package/dist/test/flowchart/flowchart.examples.d.ts +3713 -0
  331. package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -0
  332. package/dist/test/flowchart/flowchart.examples.js +465 -0
  333. package/dist/test/flowchart/flowchart.examples.js.map +1 -0
  334. package/dist/test/plan/plan-events.examples.d.ts +3666 -0
  335. package/dist/test/plan/plan-events.examples.d.ts.map +1 -0
  336. package/dist/test/plan/plan-events.examples.js +773 -0
  337. package/dist/test/plan/plan-events.examples.js.map +1 -0
  338. package/dist/test/plan/plan-records.examples.d.ts +3 -0
  339. package/dist/test/plan/plan-records.examples.d.ts.map +1 -0
  340. package/dist/test/plan/plan-records.examples.js +45 -0
  341. package/dist/test/plan/plan-records.examples.js.map +1 -0
  342. package/dist/test/plan/plan-windows.examples.d.ts +588 -0
  343. package/dist/test/plan/plan-windows.examples.d.ts.map +1 -0
  344. package/dist/test/plan/plan-windows.examples.js +170 -0
  345. package/dist/test/plan/plan-windows.examples.js.map +1 -0
  346. package/dist/test/plan/plan.examples.d.ts +8095 -0
  347. package/dist/test/plan/plan.examples.d.ts.map +1 -0
  348. package/dist/test/plan/plan.examples.js +2500 -0
  349. package/dist/test/plan/plan.examples.js.map +1 -0
  350. package/dist/test/roster/domain.examples.d.ts +367 -0
  351. package/dist/test/roster/domain.examples.d.ts.map +1 -0
  352. package/dist/test/roster/domain.examples.js +72 -0
  353. package/dist/test/roster/domain.examples.js.map +1 -0
  354. package/dist/test/roster/roster-records.examples.d.ts +729 -0
  355. package/dist/test/roster/roster-records.examples.d.ts.map +1 -0
  356. package/dist/test/roster/roster-records.examples.js +86 -0
  357. package/dist/test/roster/roster-records.examples.js.map +1 -0
  358. package/dist/test/roster/roster.examples.d.ts +750 -0
  359. package/dist/test/roster/roster.examples.d.ts.map +1 -0
  360. package/dist/test/roster/roster.examples.js +198 -0
  361. package/dist/test/roster/roster.examples.js.map +1 -0
  362. package/dist/test/roster/warehouse.d.ts +87 -0
  363. package/dist/test/roster/warehouse.d.ts.map +1 -0
  364. package/dist/test/roster/warehouse.js +883 -0
  365. package/dist/test/roster/warehouse.js.map +1 -0
  366. package/dist/test/sheet/sheet-link.examples.d.ts +7 -0
  367. package/dist/test/sheet/sheet-link.examples.d.ts.map +1 -0
  368. package/dist/test/sheet/sheet-link.examples.js +41 -0
  369. package/dist/test/sheet/sheet-link.examples.js.map +1 -0
  370. package/dist/test/sheet/sheet-records.examples.d.ts +421 -0
  371. package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -0
  372. package/dist/test/sheet/sheet-records.examples.js +79 -0
  373. package/dist/test/sheet/sheet-records.examples.js.map +1 -0
  374. package/dist/test/sheet/sheet-transactions.examples.d.ts +7 -0
  375. package/dist/test/sheet/sheet-transactions.examples.d.ts.map +1 -0
  376. package/dist/test/sheet/sheet-transactions.examples.js +58 -0
  377. package/dist/test/sheet/sheet-transactions.examples.js.map +1 -0
  378. package/dist/test/sheet/sheet.examples.d.ts +5562 -0
  379. package/dist/test/sheet/sheet.examples.d.ts.map +1 -0
  380. package/dist/test/sheet/sheet.examples.js +1178 -0
  381. package/dist/test/sheet/sheet.examples.js.map +1 -0
  382. package/dist/test/studio/studio.examples.d.ts.map +1 -1
  383. package/dist/test/studio/studio.examples.js +11 -11
  384. package/dist/test/studio/studio.examples.js.map +1 -1
  385. package/package.json +12 -9
@@ -0,0 +1,1178 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/e3-ui/jsx-runtime";
2
+ /**
3
+ * Copyright (c) 2025 Elara AI Pty Ltd
4
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
5
+ */
6
+ /** @jsxImportSource @elaraai/e3-ui */
7
+ import { East, ArrayType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, SetType, StringType, StructType, VariantType, example, none, some, variant, } from "@elaraai/east";
8
+ import { Badge, Box, Configurator, Field, Format, HStack, Input, Reactive, SegmentGroup, Slice, State, StatusValueType, Style, Switch, Text, UIComponentType, VStack, } from "@elaraai/east-ui";
9
+ import { Data, Record, Sheet } from "@elaraai/e3-ui";
10
+ import e3 from "@elaraai/e3";
11
+ // ============================================================================
12
+ // The Sheet's examples (#1216): the one `<Sheet>`, each in its frame — one
13
+ // toolbar holding every control the sheet has, the banners, the grid with its
14
+ // strip docked under it, the footer, and the panes it is given. The slots of
15
+ // EXAMPLES_AUTHORING.md §8, fewer and fuller (#1189):
16
+ //
17
+ // - `sheetBasic`, the smallest: a record's entries as the rows (§3.1);
18
+ // - `sheetVariants`, THE configurator: the sheet's options as axes, over the
19
+ // host's rows (`data`), committed through `onApply`;
20
+ // - `sheetStress`, two thousand rows a task makes, read only, and the lens;
21
+ // - `sheetLibrary`, a library of the author's own (§4.4);
22
+ // - `sheetWorkshop`, the flagship — the joinery workshop's orders (§3.3):
23
+ // groups, the driver and registers, the link grammar, the copilot,
24
+ // readiness, the lens and views;
25
+ // - `sheetWeeks`, one entry's rows with every column kind and its rules (§3.4);
26
+ // - `sheetBatches`, one entry's groups in the planner's order, dragged and
27
+ // moved (#1187), with their sub rows;
28
+ // - `sheetLoose`, loose rows between the groups (#846);
29
+ // - `sheetPaged`, a record read a window at a time;
30
+ // - `sheetUpkeep`, the inspector's every field kind (#1220).
31
+ //
32
+ // Its panes are optional props, and the examples show each combination:
33
+ // none (`sheetBasic`, `sheetVariants`, `sheetStress`, `sheetLoose`,
34
+ // `sheetPaged`), a library (`sheetLibrary`), an inspector (`sheetWeeks`, its
35
+ // Details the author's own; `sheetUpkeep`, its form), and both
36
+ // (`sheetWorkshop`, `sheetBatches`).
37
+ //
38
+ // Every sheet binds its rows from e3, so each runs on e3-web in the showcase
39
+ // (#1180): a record seeded with an authored literal (§2a), whose patch door
40
+ // every Save commits through, or a task's output, made where data is made.
41
+ // `State` holds only what the viewer owns: a configurator's axes, saved views,
42
+ // the week picked. The names are made up; the domain is a joinery workshop
43
+ // (decision 14).
44
+ //
45
+ // A sheet fills its parent, as a ui task's page fills the window: each
46
+ // example gives it a box of its own height. One page shows them all, so every
47
+ // sheet but the first is named (`name`), and keeps its viewer's state apart.
48
+ // ============================================================================
49
+ // ============================================================================
50
+ // sheetBasic — the smallest sheet (§3.1)
51
+ // ============================================================================
52
+ /** A job — what it is, when it starts and how many. */
53
+ export const SheetJob = StructType({ task: StringType, start: OptionType(DateTimeType), qty: OptionType(FloatType) });
54
+ /** The jobs, keyed J-0001, J-0002, …: the record's entries are the sheet's rows. */
55
+ export const sheetJobs = e3.record("sheet_jobs", DictType(StringType, SheetJob), new Map([
56
+ ["J-0001", { task: "Panel cutting", start: some(new Date("2026-10-12T00:00:00Z")), qty: some(48.0) }],
57
+ ["J-0002", { task: "Edge banding", start: some(new Date("2026-10-13T00:00:00Z")), qty: some(120.0) }],
58
+ ["J-0003", { task: "CNC routing", start: none, qty: some(48.0) }],
59
+ ["J-0004", { task: "Spray finish", start: none, qty: none }],
60
+ ]));
61
+ /** The jobs' patch door — every Save commits through it. */
62
+ export const sheetJobsPatch = e3.mutation.patch(sheetJobs);
63
+ /**
64
+ * The smallest sheet (§3.1) — the jobs an e3 record holds, in its frame: one
65
+ * row per job in key order and a blank tail for the next, three typed
66
+ * columns, every gesture a draft the history item undoes, and Save one
67
+ * commit through the record's patch door. It is given no pane.
68
+ */
69
+ export const sheetBasic = example({
70
+ keywords: ["Sheet", "basic", "BuilderFrame", "record", "Record", "Record.bind", "e3.record", "patch", "commit", "column", "date", "text", "quantity", "key order", "Reactive"],
71
+ description: "The smallest sheet — an e3 record's entries as its rows, in key order, under three typed columns, edited as drafts and saved as one commit through the record's patch door, in its frame with no pane",
72
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
73
+ const jobs = $.let(Record.bind(sheetJobs, [sheetJobsPatch]));
74
+ return (_jsx(Box, { height: "560px", children: _jsx(Sheet, { record: jobs, columns: {
75
+ task: Sheet.column.text(SheetJob, { header: "Task", width: "240px" }),
76
+ start: Sheet.column.date(SheetJob, { header: "Start", width: "96px" }),
77
+ qty: Sheet.column.quantity(SheetJob, { header: "Qty", width: "96px" }),
78
+ } }) }));
79
+ } }))),
80
+ inputs: [],
81
+ });
82
+ // ============================================================================
83
+ // sheetVariants — THE Sheet configurator
84
+ // ============================================================================
85
+ /** A job — its start, task, quantity and notes. */
86
+ export const VariantsJob = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType), notes: StringType });
87
+ /** A plan — its jobs, in the planner's order. */
88
+ export const VariantsPlan = StructType({ jobs: ArrayType(VariantsJob) });
89
+ /** The plans — the configurator's sheet edits the week's jobs. */
90
+ export const sheetVariantsPlans = e3.record("sheet_variants_plans", DictType(StringType, VariantsPlan), new Map([
91
+ ["week", { jobs: [
92
+ { id: "j1", start: some(new Date("2026-02-16T00:00:00Z")), task: "Routing", qty: some(1200.0), notes: "Nest the C-18 panels" },
93
+ { id: "j2", start: some(new Date("2026-03-09T00:00:00Z")), task: "Spraying", qty: some(250.0), notes: "" },
94
+ { id: "j3", start: none, task: "Wrapping", qty: none, notes: "" },
95
+ ] }],
96
+ ]));
97
+ /** The record's patch door — every Save commits through it. */
98
+ export const sheetVariantsPlansPatch = e3.mutation.patch(sheetVariantsPlans);
99
+ /**
100
+ * THE Sheet configurator — ONE live sheet in its frame; every axis is an
101
+ * expression-fed prop on that single instance: density, the blank tail, read
102
+ * only and the copilot switch (a fill provider that reads the switch through
103
+ * its captured bind handle). Selection (`onSelect`) and every gesture
104
+ * (`onPatch`) log to the reactive aside. Its rows are the host's (`data`):
105
+ * the week's jobs, read from an e3 record, and Save hands the checked batch
106
+ * to `onApply`, which commits it to that record. Its panes are fixed when it
107
+ * is built, so they are no axis here: it is given none. The configurator's
108
+ * axes are the viewer's own state.
109
+ */
110
+ export const sheetVariants = example({
111
+ keywords: ["Sheet", "configurator", "density", "blanks", "readOnly", "copilot", "fill", "provider", "onSelect", "onPatch", "onApply", "data", "Record", "Record.onApply", "e3.record", "bound", "Configurator", "SegmentGroup", "Switch", "Input", "Reactive", "State"],
112
+ description: "Sheet configurator — density, the blank tail, read only and the copilot switch, all expression-fed into one live sheet in its frame over the host's rows (an e3 record's week, committed through onApply); selection and edits log to the aside",
113
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
114
+ const Ctx = Sheet.Types.DraftContext(VariantsJob);
115
+ // The jobs, read from an e3 record bound with its patch door.
116
+ const plans = $.let(Record.bind(sheetVariantsPlans, [sheetVariantsPlansPatch]));
117
+ const rows = $.let(plans.read().get("week").jobs);
118
+ const onApply = $.const(Record.onApply(plans, {
119
+ entry: "week",
120
+ get: East.function([VariantsPlan], ArrayType(VariantsJob), (_$, held) => held.jobs),
121
+ set: East.function([VariantsPlan, ArrayType(VariantsJob)], VariantsPlan, (_$, _held, next) => ({ jobs: next })),
122
+ idField: "id",
123
+ }));
124
+ const densities = $.const([
125
+ variant("condensed", null), variant("compact", null), variant("comfortable", null),
126
+ ], ArrayType(Style.Types.Density));
127
+ const densityBind = $.let(State.bind([StringType], "sheet_variants_density", "compact"));
128
+ const blanksBind = $.let(State.bind([IntegerType], "sheet_variants_blanks", 18n));
129
+ const readOnlyBind = $.let(State.bind([BooleanType], "sheet_variants_readonly", false));
130
+ const copilotBind = $.let(State.bind([BooleanType], "sheet_variants_copilot", true));
131
+ const lastEventBind = $.let(State.bind([StringType], "sheet_variants_last_event", ""));
132
+ const dKey = $.let(densityBind.read());
133
+ const blanks = $.let(blanksBind.read());
134
+ const readOnly = $.let(readOnlyBind.read());
135
+ const copilotOn = $.let(copilotBind.read());
136
+ const lastEvent = $.let(lastEventBind.read());
137
+ const onDensity = $.const(East.function([StringType], NullType, ($, next) => { $(densityBind.write(next)); }));
138
+ const onBlanks = $.const(East.function([IntegerType], NullType, ($, next) => { $(blanksBind.write(next)); }));
139
+ const onReadOnly = $.const(East.function([BooleanType], NullType, ($, next) => { $(readOnlyBind.write(next)); }));
140
+ const onCopilot = $.const(East.function([BooleanType], NullType, ($, next) => { $(copilotBind.write(next)); }));
141
+ // The copilot axis — a fill that reads the switch through the
142
+ // captured bind handle, so the one instance keeps its providers.
143
+ const NoteFill = OptionType(Sheet.Types.Fill(StringType));
144
+ const phrase = $.const(East.function([Ctx], NoteFill, ($, ctx) => {
145
+ const noFill = $.const(none, NoteFill);
146
+ return ctx.row.task.match({ value: (_$, task) => copilotBind.read().and(() => task.length().greater(0n)).ifElse(() => some({ value: East.str `${task} as planned`, meta: "phrasing from the supplied task" }), () => noFill),
147
+ }, () => noFill);
148
+ }));
149
+ const onSelect = $.const(East.function([Sheet.Types.Selection], NullType, ($, sel) => {
150
+ $(lastEventBind.write(East.str `onSelect: row ${sel.rowId.match({ some: (_$, id) => id, none: (_$) => "—" })} · ${sel.key.match({ some: (_$, k) => k, none: (_$) => "—" })}`));
151
+ }));
152
+ const onPatch = $.const(East.function([Sheet.Types.PatchEvent(VariantsJob)], NullType, ($, e) => {
153
+ $(lastEventBind.write(East.str `${e.origin.getTag()} · ${e.draftChanges.length()} entries · ${e.readiness.getTag()}`));
154
+ }));
155
+ const newRow = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(VariantsJob), () => Sheet.patch(VariantsJob, { notes: "" })));
156
+ const densitySel = $.let(densities.filter((_$, v) => v.getTag().equal(dKey)).get(0n));
157
+ return (_jsx(Configurator, { controls: [
158
+ Configurator.Control("Density", dKey, _jsx(SegmentGroup, { value: dKey, onChange: onDensity, size: "sm", items: densities.map((_$, v) => SegmentGroup.Item(v.getTag(), _jsx(Text, { children: v.getTag().upperCase() }))) })),
159
+ Configurator.Control("Blank tail", East.print(blanks), _jsx(Input.Integer, { value: blanks, min: 0n, max: 40n, step: 2n, size: "sm", onChange: onBlanks })),
160
+ Configurator.Slot("Chrome", _jsxs(HStack, { gap: "5", align: "center", wrap: "wrap", children: [_jsx(Switch, { checked: readOnly, label: "Read-only", onChange: onReadOnly }), _jsx(Switch, { checked: copilotOn, label: "Copilot", onChange: onCopilot })] })),
161
+ ], preview: _jsx(Box, { width: "100%", height: "420px", children: _jsx(Sheet, { data: rows, id: "id", name: "variants", columns: {
162
+ start: Sheet.column.date(VariantsJob, { header: "Start", sub: "dd / mm / yyyy", width: "96px" }),
163
+ task: Sheet.column.text(VariantsJob, { header: "Task", width: "180px" }),
164
+ qty: Sheet.column.quantity(VariantsJob, { header: "Qty", sub: "1,200 · pcs", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
165
+ notes: Sheet.column.text(VariantsJob, { header: "Notes", sub: "free text", fill: [phrase] }),
166
+ }, density: densitySel, blanks: blanks, readOnly: readOnly, onSelect: onSelect, onPatch: onPatch, newRow: newRow, onApply: onApply }) }), aside: {
167
+ label: "Events · Reactive",
168
+ body: (_jsx(Badge, { colorPalette: "brand", variant: "outline", children: East.equal(lastEvent.length(), 0n).ifElse((_$) => "Interact with the sheet", (_$) => lastEvent) })),
169
+ }, spec: [
170
+ Configurator.Spec("Copilot", copilotOn.ifElse((_$) => "on · notes phrase", (_$) => "off")),
171
+ Configurator.Spec("Rows", East.str `${rows.length()} real`),
172
+ ] }));
173
+ } }))),
174
+ inputs: [],
175
+ });
176
+ // ============================================================================
177
+ // sheetStress — two thousand rows, and the lens over them
178
+ // ============================================================================
179
+ /** A job on the stress sheet — its start, activity, notes, work centres (a
180
+ * machine by code, or a count of a family), status and quantity. */
181
+ export const StressJob = StructType({
182
+ id: StringType, start: OptionType(DateTimeType), activity: StringType, notes: StringType,
183
+ stations: Sheet.Types.Link, status: StringType, qty: OptionType(FloatType),
184
+ });
185
+ /** How many jobs the stress sheet holds — a small authored constant;
186
+ * {@link sheetStressJobs} makes the rows. */
187
+ export const sheetStressJobCount = e3.input("sheet_stress_job_count", IntegerType, variant("value", 2000n));
188
+ /**
189
+ * The jobs, generated from their count — four a day from 5 January, the
190
+ * activities in turn: every fifth a routing run naming a count of routers,
191
+ * the rest a machine by code; every seventh urgent, every eleventh quantity
192
+ * blank.
193
+ */
194
+ export const generateStressJobs = East.function([IntegerType], ArrayType(StressJob), ($, count) => {
195
+ const activities = $.const(["Routing", "Spraying", "Wrapping", "Set-up", "Maintenance"], ArrayType(StringType));
196
+ const words = $.const(["PLANNED", "RELEASED", "COMPLETE"], ArrayType(StringType));
197
+ const codes = $.const(["R2140", "R2141", "R2145", "P3210", "A7301"], ArrayType(StringType));
198
+ const first = $.const(new Date("2026-01-05T00:00:00Z"), DateTimeType);
199
+ const noMembers = $.const([], ArrayType(Sheet.Types.Member));
200
+ const blank = $.const(none, OptionType(FloatType));
201
+ return East.Array.generate(count, StressJob, (_$, i) => ({
202
+ id: East.str `S${i}`,
203
+ start: some(first.addDays(i.divide(4n))),
204
+ activity: activities.get(i.remainder(5n)),
205
+ notes: i.remainder(7n).equal(0n).ifElse((_$2) => "urgent — inspect before delivery", (_$2) => East.str `batch ${i.add(100n)}`),
206
+ // Routing runs name a count of routers; the rest a machine by code.
207
+ stations: i.remainder(5n).equal(0n).ifElse((_$2) => East.value({ from: noMembers, to: [variant("counted", { n: i.remainder(3n).add(2n), key: "CNC router" })] }, Sheet.Types.Link), (_$2) => East.value({ from: noMembers, to: [variant("identified", { key: codes.get(i.remainder(5n)) })] }, Sheet.Types.Link)),
208
+ status: words.get(i.remainder(3n)),
209
+ qty: i.remainder(11n).equal(0n).ifElse((_$2) => blank, (_$2) => East.value(some(i.multiply(7n).toFloat().add(50.0)), OptionType(FloatType))),
210
+ }));
211
+ });
212
+ /** The task that generates the jobs — its output is what the stress sheet reads. */
213
+ export const sheetStressJobs = e3.task("sheet_stress_jobs", [sheetStressJobCount], generateStressJobs);
214
+ /**
215
+ * Stress, and the lens (§3.8) — two thousand jobs an e3 task generates, the
216
+ * host's rows (`data`), read only, every row virtualised. Search and filter
217
+ * run through the bound slice; the sheet never narrows, it draws the rows the
218
+ * narrowing leaves as collapsed context bands while hits keep their row
219
+ * numbers. The work centres are a set column searched through its display
220
+ * form (`text: r => Sheet.link.print(r.stations)`); saved views are
221
+ * slice-state snapshots evaluated live, with their context and reveals, the
222
+ * first one open, kept per viewer through a `State` bind handle. The rows
223
+ * are a task's output, made where data is made, so the sheet only reads
224
+ * them. It is given no pane.
225
+ */
226
+ export const sheetStress = example({
227
+ keywords: ["Sheet", "stress", "virtualization", "2000", "rows", "performance", "data", "readOnly", "slice", "search", "filter", "lens", "bands", "context", "reveal", "views", "State.bind", "activeView", "text", "Sheet.link.print", "set", "register", "concat", "footer", "Slice", "config", "bind", "state", "Data.bind", "bound", "e3.task", "generated", "Reactive", "State"],
228
+ description: "Two thousand jobs an e3 task generates, read only and virtualised in the sheet's frame, with the lens over them — search and filter through the bound slice drawn as context bands (hits keep their row numbers), a set column searched through its display text, and saved views kept per viewer as slice-state snapshots with their context and reveals",
229
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
230
+ const MachineType = StructType({ code: StringType, family: StringType });
231
+ // Two thousand jobs an e3 task generates — made where data is made.
232
+ const jobs = $.let(Data.bind(sheetStressJobs));
233
+ const count = $.let(jobs.read().length());
234
+ const machines = $.const([
235
+ { code: "R2140", family: "CNC router" }, { code: "R2141", family: "CNC router" }, { code: "R2145", family: "CNC router" },
236
+ { code: "P3210", family: "4-side planer" }, { code: "A7301", family: "assembly bench" },
237
+ ], ArrayType(MachineType));
238
+ // The work centres are searched through their display form — `3 x CNC router`, `A7301` — not their `.east` text.
239
+ const slice = $.let(Slice.bind("sheet_stress_slice", jobs.read(), {
240
+ fields: {
241
+ activity: { label: "Activity", hints: ["Routing", "Spraying", "Wrapping", "Set-up", "Maintenance"] },
242
+ notes: { label: "Notes" },
243
+ stations: { label: "Work centres", text: r => Sheet.link.print(r.stations) },
244
+ status: { label: "Status" },
245
+ },
246
+ search: ["activity", "notes", "stations"],
247
+ }));
248
+ const views = $.let(State.bind([ArrayType(Sheet.Types.View)], "sheet_stress_views", [
249
+ { id: "spraying", name: "SPRAYING", narrowing: Slice.state({ search: some("spraying") }), context: 1n, reveals: [], folds: new Map() },
250
+ { id: "routers", name: "ROUTERS", narrowing: Slice.state({ search: some("router") }), context: 0n, reveals: [], folds: new Map() },
251
+ { id: "urgent", name: "URGENT", narrowing: Slice.state({ search: some("urgent") }), context: 0n, reveals: [], folds: new Map() },
252
+ ]));
253
+ return (_jsx(Box, { height: "480px", children: _jsx(Sheet, { data: jobs, id: "id", name: "stress", registers: {
254
+ stations: Sheet.register.concat([
255
+ Sheet.register.members(machines, { kind: "machine", key: m => m.code, label: m => m.code, meta: m => some(m.family) }),
256
+ Sheet.register.members(machines, { kind: "family", key: m => m.family, label: m => m.family, meta: _m => some("family") }),
257
+ ]),
258
+ }, columns: {
259
+ start: Sheet.column.date(StressJob, { header: "Start", width: "96px" }),
260
+ activity: Sheet.column.text(StressJob, { header: "Activity", width: "160px" }),
261
+ notes: Sheet.column.text(StressJob, { header: "Notes", sub: "free text", width: "240px" }),
262
+ stations: Sheet.column.set(StressJob, "stations", { header: "Work centres", sub: "3 x router · machine", width: "220px",
263
+ members: [{ kind: "machine", identified: true }, { kind: "family", countable: true, resolvesTo: "machine" }] }),
264
+ status: Sheet.column.text(StressJob, { header: "Status", width: "120px" }),
265
+ qty: Sheet.column.quantity(StressJob, { header: "Qty", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
266
+ }, slice: slice, affordances: ["search", "filter"], views: views, activeView: some("spraying"), readOnly: true, footer: [{ text: East.str `${count} rows · virtualised` }] }) }));
267
+ } }))),
268
+ inputs: [],
269
+ });
270
+ // ============================================================================
271
+ // sheetLibrary — a library of the author's own (§4.4)
272
+ // ============================================================================
273
+ /** A task a job takes, and the stage of the work it belongs to. */
274
+ export const LibraryTask = StructType({ name: StringType, stage: StringType });
275
+ /**
276
+ * The jobs with a library of the author's own (§4.4, SB60): the library pane
277
+ * lists the tasks a job takes, grouped by their stage — a card dropped on a
278
+ * job sets its task — beside the columns a viewer shows and hides. It is
279
+ * given no inspector, so it has no inspector pane.
280
+ */
281
+ export const sheetLibrary = example({
282
+ keywords: ["Sheet", "BuilderFrame", "pane", "library", "Sheet.library", "Sheet.library.tab", "Sheet.library.columns", "tab", "cards", "drop", "Sheet.patch", "group"],
283
+ description: "A sheet whose library pane lists a tab of the author's own — the tasks a job takes, grouped by stage, a card dropped on a job setting its task — beside the columns",
284
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
285
+ const jobs = $.let(Record.bind(sheetJobs, [sheetJobsPatch]));
286
+ const tasks = $.let([
287
+ { name: "Panel cutting", stage: "Cutting" },
288
+ { name: "Edge banding", stage: "Cutting" },
289
+ { name: "CNC routing", stage: "Machining" },
290
+ { name: "Sanding", stage: "Finishing" },
291
+ { name: "Spray finish", stage: "Finishing" },
292
+ ], ArrayType(LibraryTask));
293
+ return (_jsx(Box, { height: "560px", children: _jsx(Sheet, { record: jobs, name: "library", columns: {
294
+ task: Sheet.column.text(SheetJob, { header: "Task", width: "240px" }),
295
+ start: Sheet.column.date(SheetJob, { header: "Start", width: "96px" }),
296
+ qty: Sheet.column.quantity(SheetJob, { header: "Qty", width: "96px" }),
297
+ }, library: [
298
+ // The tasks a job takes, by their stage: a card dropped on a job sets its task.
299
+ Sheet.library.tab(tasks, { name: "Tasks", icon: "hammer",
300
+ key: t => t.name, label: t => t.name, group: t => t.stage,
301
+ drop: t => Sheet.patch(SheetJob, { task: t.name }) }),
302
+ Sheet.library.columns(),
303
+ ] }) }));
304
+ } }))),
305
+ inputs: [],
306
+ });
307
+ // ============================================================================
308
+ // sheetWorkshop — the joinery workshop's orders (§3.3)
309
+ // ============================================================================
310
+ /** An activity — the driver's row: its unit, how many days it takes, how many
311
+ * units an hour, the family of machine it runs on (`""` for none) and the
312
+ * halves of the work centres it makes live. */
313
+ export const WorkshopActivity = StructType({
314
+ name: StringType, uom: StringType, days: IntegerType, rate: FloatType, family: StringType, sides: Sheet.Types.Sides,
315
+ });
316
+ /** A machine — its family and the bay it stands in; its code is its key. */
317
+ export const WorkshopMachine = StructType({ family: StringType, bay: StringType });
318
+ /** A status — a word of the statuses register, and its tone. */
319
+ export const WorkshopStatus = StructType({ word: StringType, tone: StatusValueType });
320
+ /** One operation of an order. */
321
+ export const WorkshopOperation = StructType({
322
+ activity: StringType, // the driver: an activity's name
323
+ start: OptionType(DateTimeType),
324
+ end: OptionType(DateTimeType),
325
+ qty: OptionType(FloatType), // in the activity's unit
326
+ machines: Sheet.Types.Link, // work centres, from → to
327
+ notes: StringType,
328
+ created_by: StringType, // no column: the inspector shows it
329
+ });
330
+ /** An order — its name, customer, due date and status, and its operations in order. */
331
+ export const WorkshopOrder = StructType({
332
+ name: StringType,
333
+ customer: StringType,
334
+ due: OptionType(DateTimeType),
335
+ status: StringType, // a word of the statuses register
336
+ ops: ArrayType(WorkshopOperation),
337
+ });
338
+ /** The activities — the driver's members, each with its unit and days. */
339
+ export const sheetWorkshopActivities = e3.input("sheet_workshop_activities", ArrayType(WorkshopActivity), variant("value", [
340
+ { name: "Panel cutting", uom: "panels", days: 1n, rate: 12.0, family: "beam saw", sides: variant("both", null) },
341
+ { name: "Edge banding", uom: "metres", days: 1n, rate: 60.0, family: "edge bander", sides: variant("both", null) },
342
+ { name: "CNC routing", uom: "panels", days: 2n, rate: 6.0, family: "CNC router", sides: variant("both", null) },
343
+ { name: "Drilling", uom: "panels", days: 1n, rate: 10.0, family: "CNC router", sides: variant("both", null) },
344
+ { name: "Sanding", uom: "panels", days: 1n, rate: 20.0, family: "", sides: variant("in", null) },
345
+ { name: "Assembly", uom: "units", days: 2n, rate: 2.0, family: "assembly bench", sides: variant("both", null) },
346
+ { name: "Spray finish", uom: "doors", days: 3n, rate: 8.0, family: "spray booth", sides: variant("both", null) },
347
+ { name: "Wrapping", uom: "units", days: 1n, rate: 10.0, family: "", sides: variant("in", null) },
348
+ { name: "Delivery", uom: "loads", days: 1n, rate: 1.0, family: "", sides: variant("from", null) },
349
+ ]));
350
+ /** The machines, keyed by code — the work centres' register. */
351
+ export const sheetWorkshopMachines = e3.record("sheet_workshop_machines", DictType(StringType, WorkshopMachine), new Map([
352
+ ["S101", { family: "beam saw", bay: "Bay 1" }],
353
+ ["S102", { family: "beam saw", bay: "Bay 1" }],
354
+ ["S103", { family: "beam saw", bay: "Bay 1" }],
355
+ ["E201", { family: "edge bander", bay: "Bay 2" }],
356
+ ["E202", { family: "edge bander", bay: "Bay 2" }],
357
+ ["R301", { family: "CNC router", bay: "Bay 3" }],
358
+ ["R302", { family: "CNC router", bay: "Bay 3" }],
359
+ ["R303", { family: "CNC router", bay: "Bay 3" }],
360
+ ["F401", { family: "spray booth", bay: "Bay 4" }],
361
+ ["F402", { family: "spray booth", bay: "Bay 4" }],
362
+ ["A701", { family: "assembly bench", bay: "Bay 7" }],
363
+ ["A702", { family: "assembly bench", bay: "Bay 7" }],
364
+ ]));
365
+ /** The orders, keyed by order number: each a group, its operations its lines. */
366
+ export const sheetWorkshopOrders = e3.record("sheet_workshop_orders", DictType(StringType, WorkshopOrder), new Map([
367
+ ["WO-2201", { name: "WO-2201 · Kitchen, oak", customer: "Quillfeather Interiors", due: some(new Date("2026-10-23T00:00:00Z")), status: "RELEASED", ops: [
368
+ { activity: "Panel cutting", start: some(new Date("2026-10-12T00:00:00Z")), end: some(new Date("2026-10-13T00:00:00Z")), qty: some(48.0),
369
+ machines: { from: [variant("identified", { key: "S101" })], to: [variant("identified", { key: "E201" })] }, notes: "Oak veneered board", created_by: "planner" },
370
+ { activity: "Edge banding", start: some(new Date("2026-10-14T00:00:00Z")), end: some(new Date("2026-10-15T00:00:00Z")), qty: some(120.0),
371
+ machines: { from: [variant("identified", { key: "E201" })], to: [variant("identified", { key: "R301" })] }, notes: "", created_by: "planner" },
372
+ { activity: "CNC routing", start: some(new Date("2026-10-15T00:00:00Z")), end: some(new Date("2026-10-17T00:00:00Z")), qty: some(48.0),
373
+ machines: { from: [variant("identified", { key: "R301" })], to: [variant("identified", { key: "A701" })] }, notes: "Hinge cups and handle slots", created_by: "planner" },
374
+ { activity: "Assembly", start: none, end: none, qty: some(12.0),
375
+ machines: { from: [], to: [variant("identified", { key: "A701" })] }, notes: "Twelve carcasses", created_by: "planner" },
376
+ { activity: "Spray finish", start: none, end: none, qty: some(24.0),
377
+ machines: { from: [], to: [variant("identified", { key: "F401" })] }, notes: "Matt lacquer", created_by: "planner" },
378
+ ] }],
379
+ ["WO-2202", { name: "WO-2202 · Wardrobes, ash", customer: "Marrowby Lettings", due: some(new Date("2026-10-30T00:00:00Z")), status: "PLANNED", ops: [
380
+ { activity: "Panel cutting", start: some(new Date("2026-10-13T00:00:00Z")), end: some(new Date("2026-10-14T00:00:00Z")), qty: some(36.0),
381
+ machines: { from: [variant("identified", { key: "S102" })], to: [variant("identified", { key: "E202" })] }, notes: "", created_by: "planner" },
382
+ { activity: "Edge banding", start: none, end: none, qty: some(90.0),
383
+ machines: { from: [variant("identified", { key: "E202" })], to: [] }, notes: "", created_by: "planner" },
384
+ { activity: "Drilling", start: none, end: none, qty: some(36.0),
385
+ machines: { from: [], to: [variant("identified", { key: "R302" })] }, notes: "Hinge and shelf pins", created_by: "planner" },
386
+ { activity: "Assembly", start: none, end: none, qty: some(6.0),
387
+ machines: { from: [], to: [variant("identified", { key: "A702" })] }, notes: "", created_by: "planner" },
388
+ ] }],
389
+ ["WO-2203", { name: "WO-2203 · Vanity unit, walnut", customer: "Tallowmere Homes", due: some(new Date("2026-11-02T00:00:00Z")), status: "PLANNED", ops: [
390
+ { activity: "Panel cutting", start: none, end: none, qty: some(12.0),
391
+ machines: { from: [], to: [variant("identified", { key: "S101" })] }, notes: "", created_by: "planner" },
392
+ { activity: "CNC routing", start: none, end: none, qty: some(12.0),
393
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "CNC router" })] }, notes: "Basin cut-out", created_by: "planner" },
394
+ { activity: "Spray finish", start: none, end: none, qty: some(4.0),
395
+ machines: { from: [], to: [variant("identified", { key: "F402" })] }, notes: "", created_by: "planner" },
396
+ { activity: "Assembly", start: none, end: none, qty: some(2.0),
397
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
398
+ ] }],
399
+ ["WO-2204", { name: "WO-2204 · Shelving, birch", customer: "Pebblecombe School", due: some(new Date("2026-10-20T00:00:00Z")), status: "ON HOLD", ops: [
400
+ { activity: "Panel cutting", start: none, end: none, qty: some(60.0),
401
+ machines: { from: [], to: [variant("counted", { n: 2n, key: "beam saw" })] }, notes: "Awaiting board delivery", created_by: "planner" },
402
+ { activity: "Edge banding", start: none, end: none, qty: some(150.0),
403
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
404
+ { activity: "Sanding", start: none, end: none, qty: some(60.0),
405
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
406
+ ] }],
407
+ ["WO-2205", { name: "WO-2205 · Office fit-out, maple", customer: "", due: none, status: "PLANNED", ops: [
408
+ { activity: "Panel cutting", start: none, end: none, qty: some(80.0),
409
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
410
+ { activity: "CNC routing", start: none, end: none, qty: some(40.0),
411
+ machines: { from: [], to: [] }, notes: "Cable ports", created_by: "planner" },
412
+ { activity: "Assembly", start: none, end: none, qty: some(10.0),
413
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
414
+ { activity: "Wrapping", start: none, end: none, qty: some(10.0),
415
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
416
+ { activity: "Delivery", start: none, end: none, qty: some(2.0),
417
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
418
+ ] }],
419
+ ["WO-2206", { name: "WO-2206 · Kitchen, painted", customer: "Orrisdale Cottages", due: some(new Date("2026-11-06T00:00:00Z")), status: "PLANNED", ops: [
420
+ { activity: "Panel cutting", start: none, end: none, qty: some(30.0),
421
+ machines: { from: [], to: [variant("identified", { key: "S103" })] }, notes: "", created_by: "planner" },
422
+ { activity: "Edge banding", start: none, end: none, qty: some(70.0),
423
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "edge bander" })] }, notes: "", created_by: "planner" },
424
+ { activity: "Spray finish", start: none, end: none, qty: some(18.0),
425
+ machines: { from: [], to: [variant("identified", { key: "F401" })] }, notes: "Primer, then two coats", created_by: "planner" },
426
+ { activity: "Assembly", start: none, end: none, qty: some(8.0),
427
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
428
+ { activity: "Wrapping", start: none, end: none, qty: some(8.0),
429
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
430
+ { activity: "Delivery", start: none, end: none, qty: some(1.0),
431
+ machines: { from: [], to: [] }, notes: "", created_by: "planner" },
432
+ ] }],
433
+ ]));
434
+ /** The orders' patch door — every Save commits through it. */
435
+ export const sheetWorkshopOrdersPatch = e3.mutation.patch(sheetWorkshopOrders);
436
+ /** The model behind the workshop's async proposer — an e3 function, a service, a notebook; the sheet needs only its types. */
437
+ const workshopRecommend = East.asyncPlatform("sheet_workshop_recommend", [Sheet.Types.DraftContext(WorkshopOrder, "ops", WorkshopActivity)], ArrayType(Sheet.Types.Proposal(WorkshopOperation)), { optional: true });
438
+ /**
439
+ * The flagship (§3.3) — the joinery workshop's orders, a grouped sheet over a
440
+ * record: each order a group, its operations its lines, the order's status and
441
+ * due date in its band. An activity driver gives each quantity its unit, the
442
+ * End fill its duration and the work centres their live halves. The work
443
+ * centres are a link cell over one register joined from three sources: the
444
+ * machines record (a Dict, its key the accessors' second argument), the bays
445
+ * (an Array with aliases) and the machine families, read off the machines
446
+ * (duplicate keys fold). Its grammar takes counts (`2 x edge bander`), locks a
447
+ * half the activity does not use, implies how many machines a quantity needs,
448
+ * and checks each machine named. The copilot fills dates, quantities, notes
449
+ * and work centres, and proposes the lines that follow — a pattern, one
450
+ * learned from the order, and an async model. A check on each order, templates
451
+ * for whole orders and single operations, the slice's search and filter with
452
+ * the work centres searched by their display text, views kept per viewer and
453
+ * a footer. Both panes: the library lists the templates, the statuses — a
454
+ * card dropped on an order's band sets its status — and the columns; the
455
+ * inspector shows every field of the selected operation, `created_by` (no
456
+ * column) read only, and the order with no customer is the batch's issue.
457
+ */
458
+ export const sheetWorkshop = example({
459
+ keywords: ["Sheet", "BuilderFrame", "record", "Record", "group", "driver", "register", "members", "concat", "aliases", "meta", "parent", "Dict", "fold", "duplicate", "link", "multiple", "sides", "locks", "arity", "check", "exists", "uom", "Format", "templates", "library", "Sheet.library", "Sheet.library.tab", "drop", "inspector", "fields", "Sheet.field", "readonly", "views", "slice", "search", "filter", "text", "Sheet.link.print", "copilot", "fill", "provider", "derive", "history", "sequence", "default", "phrase", "capacity", "propose", "proposer", "suggest", "learned", "follower", "asyncFunction", "asyncPlatform", "ready", "newGroup", "newRow", "footer", "joinery"],
460
+ description: "The flagship sheet — the workshop's orders as groups and their operations as lines; an activity driver giving units, durations and live halves; one work-centre register joined from a machines record (Dict), the bays (aliases) and the families (folded), its link grammar counting, locking, implying and checking machines; the copilot's fills (derive, history, sequence, default, phrase, capacity) and proposers (a pattern, a learned follower, an async model); an order check; order and operation templates; a library of the templates, the statuses (dropped on an order to set its status) and the columns; an inspector pane showing every field; the slice's search and filter over the link's display text; views kept per viewer; and a footer",
461
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
462
+ const orders = $.let(Record.bind(sheetWorkshopOrders, [sheetWorkshopOrdersPatch]));
463
+ const machines = $.let(Record.bind(sheetWorkshopMachines, []));
464
+ const activities = $.let(Data.bind(sheetWorkshopActivities));
465
+ const views = $.let(State.bind([ArrayType(Sheet.Types.View)], "sheet.workshop.views", []));
466
+ const statuses = $.let([
467
+ { word: "PLANNED", tone: variant("neutral", null) },
468
+ { word: "RELEASED", tone: variant("info", null) },
469
+ { word: "ON HOLD", tone: variant("warning", null) },
470
+ ], ArrayType(WorkshopStatus));
471
+ const BayType = StructType({ name: StringType, aliases: ArrayType(StringType) });
472
+ const bays = $.let([
473
+ { name: "Bay 1", aliases: ["b1", "saw bay"] },
474
+ { name: "Bay 2", aliases: ["b2", "banding bay"] },
475
+ { name: "Bay 3", aliases: ["b3", "router bay"] },
476
+ { name: "Bay 4", aliases: ["b4", "spray bay"] },
477
+ { name: "Bay 7", aliases: ["b7", "assembly bay"] },
478
+ ], ArrayType(BayType));
479
+ // Every operation of every order: what the slice searches and filters — the work centres by their display text.
480
+ const operations = $.let(orders.read().toArray((_$, o) => o.ops).flatMap((_$, ops) => ops));
481
+ const slice = $.let(Slice.bind("sheet_workshop", operations, {
482
+ fields: {
483
+ activity: { label: "Activity", hints: ["Panel cutting", "Edge banding", "CNC routing", "Drilling", "Sanding", "Assembly", "Spray finish", "Wrapping", "Delivery"] },
484
+ notes: { label: "Notes" },
485
+ machines: { label: "Work centres", text: o => Sheet.link.print(o.machines) },
486
+ },
487
+ search: ["activity", "notes", "machines"],
488
+ }));
489
+ const Ctx = Sheet.Types.DraftContext(WorkshopOrder, "ops", WorkshopActivity);
490
+ const DateFill = OptionType(Sheet.Types.Fill(DateTimeType));
491
+ const FloatFill = OptionType(Sheet.Types.Fill(FloatType));
492
+ const TextFill = OptionType(Sheet.Types.Fill(StringType));
493
+ const LinkFill = OptionType(Sheet.Types.Fill(Sheet.Types.Link));
494
+ const Counted = OptionType(Sheet.Types.Counted);
495
+ const Proposals = ArrayType(Sheet.Types.Proposal(WorkshopOperation));
496
+ // derive — End = start + the activity's days.
497
+ const endFromStart = $.const(East.function([Ctx], DateFill, ($, ctx) => {
498
+ const noFill = $.const(none, DateFill);
499
+ return ctx.row.start.match({
500
+ value: (_$, supplied) => supplied.match({
501
+ none: () => noFill,
502
+ some: (_$, start) => ctx.driver.match({
503
+ none: () => noFill,
504
+ some: (_$, a) => some({ value: start.addDays(a.days), meta: East.str `+${a.days}d · ${a.name}` }),
505
+ }),
506
+ }),
507
+ }, () => noFill);
508
+ }));
509
+ // sequence — the day after the last dated line above; else next Monday.
510
+ const nextSlot = $.const(East.function([Ctx], DateFill, ($, ctx) => {
511
+ const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("value").and(() => r.start.unwrap("value").hasTag("some"))));
512
+ return dated.length().greater(0n).ifElse(($2) => {
513
+ const last = $2.let(dated.get(dated.length().subtract(1n)));
514
+ return East.value(some({ value: last.start.unwrap("value").unwrap("some").addDays(1n), meta: "the day after the line above" }), DateFill);
515
+ }, ($2) => {
516
+ const daysToMonday = $2.let(East.value(8n, IntegerType).subtract(ctx.today.getDayOfWeek()).remainder(7n));
517
+ const monday = $2.let(ctx.today.addDays(daysToMonday.equal(0n).ifElse((_$) => 7n, (_$) => daysToMonday)));
518
+ return East.value(some({ value: monday, meta: "next Monday" }), DateFill);
519
+ });
520
+ }));
521
+ // history — the quantity of the last like operation in the order.
522
+ const lastQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
523
+ const noFill = $.const(none, FloatFill);
524
+ return ctx.row.activity.match({
525
+ value: ($, activity) => {
526
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))
527
+ .and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
528
+ return similar.size().equal(0n).ifElse(() => noFill, ($) => {
529
+ const row = $.let(similar.get(similar.size().subtract(1n)));
530
+ return some({ value: row.qty.unwrap("value").unwrap("some"), meta: "the last like operation's quantity" });
531
+ });
532
+ },
533
+ }, () => noFill);
534
+ }));
535
+ // default — a shift's worth at the activity's rate.
536
+ const shiftQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
537
+ const noFill = $.const(none, FloatFill);
538
+ return ctx.driver.match({
539
+ none: (_$) => noFill,
540
+ some: (_$, a) => East.value(some({ value: a.rate.multiply(8.0), meta: East.str `${a.rate}/h × 8 h` }), FloatFill),
541
+ });
542
+ }));
543
+ // phrase — the notes, from the supplied quantity and the activity's unit.
544
+ const phrase = $.const(East.function([Ctx], TextFill, ($, ctx) => {
545
+ const noFill = $.const(none, TextFill);
546
+ return ctx.row.qty.hasTag("value").and(() => ctx.row.qty.unwrap("value").hasTag("some")).ifElse(($) => {
547
+ const qty = $.const(ctx.row.qty.unwrap("value").unwrap("some"));
548
+ return ctx.driver.match({
549
+ none: (_$) => noFill,
550
+ some: (_$, a) => East.value(some({ value: East.str `${qty} ${a.uom} · ${a.name}`, meta: "phrasing from the supplied activity and quantity" }), TextFill),
551
+ });
552
+ }, () => noFill);
553
+ }));
554
+ // The arity rule — one machine of the activity's family for every sixty units.
555
+ const impliedMachines = $.const(East.function([Ctx], Counted, ($, ctx) => {
556
+ const noCount = $.const(none, Counted);
557
+ $.if(ctx.row.qty.hasTag("value").not(), ($) => { $.return(noCount); });
558
+ const supplied = $.const(ctx.row.qty.unwrap("value"));
559
+ $.if(supplied.hasTag("some").not(), ($) => { $.return(noCount); });
560
+ const qty = $.const(supplied.unwrap("some"));
561
+ // ⌈qty ÷ 60⌉ by hand — `toInteger` refuses a fraction.
562
+ const share = $.let(qty.divide(60.0));
563
+ const frac = $.let(share.remainder(1.0));
564
+ const needed = $.let(frac.equal(0.0).ifElse((_$) => share, (_$) => share.subtract(frac).add(1.0)).toInteger());
565
+ return ctx.driver.match({
566
+ none: (_$) => noCount,
567
+ some: (_$, a) => a.family.equal("").or(() => qty.greater(0.0).not()).ifElse((_$2) => noCount, (_$2) => East.value(some({ n: needed, key: a.family }), Counted)),
568
+ });
569
+ }));
570
+ // capacity — the machines the quantity implies, counted.
571
+ const capacity = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
572
+ const noFill = $.const(none, LinkFill);
573
+ const noMembers = $.const([], ArrayType(Sheet.Types.Member));
574
+ const implied = $.const(impliedMachines);
575
+ return implied(ctx).match({
576
+ none: (_$) => noFill,
577
+ some: (_$, c) => East.value(some({ value: { from: [variant("counted", { n: c.n, key: c.key })], to: noMembers }, meta: "capacity · from the quantity" }), LinkFill),
578
+ });
579
+ }));
580
+ // history — the work centres of the last like operation in the order.
581
+ const lastMachines = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
582
+ const noFill = $.const(none, LinkFill);
583
+ return ctx.row.activity.match({
584
+ value: ($, activity) => {
585
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity)).and(() => r.machines.hasTag("value"))));
586
+ return similar.size().equal(0n).ifElse(() => noFill, ($) => {
587
+ const link = $.let(similar.get(similar.size().subtract(1n)).machines.unwrap("value"));
588
+ return link.from.size().add(link.to.size()).equal(0n).ifElse(() => noFill, () => some({ value: link, meta: "the work centres of the last like operation" }));
589
+ });
590
+ },
591
+ }, () => noFill);
592
+ }));
593
+ // A member check — a machine on the From half runs the operation, so it is of the activity's family.
594
+ const familyOf = $.let(activities.read().toDict((_$, a) => a.name, (_$, a) => a.family));
595
+ const machineDict = $.let(machines.read());
596
+ const familyFits = $.const(East.function([Sheet.Types.CheckContext(WorkshopOrder, "ops")], OptionType(StringType), ($, c) => {
597
+ const noFlag = $.const(none, OptionType(StringType));
598
+ return c.half.match({
599
+ to: (_$) => noFlag,
600
+ from: (_$) => c.member.match({
601
+ identified: (_$2, m) => c.row.activity.match({
602
+ value: (_$3, activity) => familyOf.has(activity)
603
+ .and(() => familyOf.get(activity).equal("").not())
604
+ .and(() => machineDict.has(m.key))
605
+ .and(() => machineDict.get(m.key).family.equal(familyOf.get(activity)).not())
606
+ .ifElse((_$4) => East.value(some(East.str `${m.key} is a ${machineDict.get(m.key).family}, not a ${familyOf.get(activity)}`), OptionType(StringType)), (_$4) => noFlag),
607
+ }, (_$3) => noFlag),
608
+ }, (_$2) => noFlag),
609
+ });
610
+ }));
611
+ // A pattern — panels cut are banded, then routed.
612
+ const followUps = $.const(East.function([Ctx], Proposals, ($, ctx) => {
613
+ const empty = $.const([], Proposals);
614
+ return ctx.row.activity.hasTag("value")
615
+ .and(() => ctx.row.activity.unwrap("value").equal("Panel cutting"))
616
+ .and(() => ctx.row.end.hasTag("value"))
617
+ .and(() => ctx.row.end.unwrap("value").hasTag("some")).ifElse(($) => {
618
+ const end = $.const(ctx.row.end.unwrap("value").unwrap("some"));
619
+ return $.const([
620
+ { patch: Sheet.patch(WorkshopOperation, { activity: "Edge banding", start: some(end), end: some(end.addDays(1n)), notes: "Band the cut panels" }), meta: "edge banding after the cut" },
621
+ { patch: Sheet.patch(WorkshopOperation, { activity: "CNC routing", start: some(end.addDays(1n)), end: some(end.addDays(3n)), notes: "Route the banded panels" }), meta: "routing · end +1…+3 d" },
622
+ ], Proposals);
623
+ }, () => empty);
624
+ }));
625
+ // Learned from the order — what followed this activity in it, after how long.
626
+ const lastFollower = $.const(East.function([Ctx], Proposals, ($, ctx) => {
627
+ const empty = $.const([], Proposals);
628
+ return ctx.row.activity.hasTag("value")
629
+ .and(() => ctx.row.start.hasTag("value"))
630
+ .and(() => ctx.row.start.unwrap("value").hasTag("some")).ifElse(($) => {
631
+ const activity = $.const(ctx.row.activity.unwrap("value"));
632
+ const start = $.const(ctx.row.start.unwrap("value").unwrap("some"));
633
+ const dated = $.const(ctx.rows.filter((_$, r) => r.start.hasTag("value")
634
+ .and(() => r.start.unwrap("value").hasTag("some"))
635
+ .and(() => r.activity.hasTag("value"))));
636
+ const upper = $.const(dated.size().greater(1n).ifElse(() => dated.size().subtract(1n), () => 0n));
637
+ const pairs = $.const(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.unwrap("value").equal(activity)
638
+ .and(() => dated.get(i.add(1n)).activity.unwrap("value").equal(activity).not())));
639
+ return pairs.size().equal(0n).ifElse(() => empty, ($) => {
640
+ const index = $.const(pairs.get(pairs.size().subtract(1n)));
641
+ const from = $.const(dated.get(index));
642
+ const next = $.const(dated.get(index.add(1n)));
643
+ const gap = $.const(next.start.unwrap("value").unwrap("some").toEpochMilliseconds()
644
+ .subtract(from.start.unwrap("value").unwrap("some").toEpochMilliseconds()));
645
+ return $.const([{ patch: Sheet.patch(WorkshopOperation, { activity: next.activity.unwrap("value"), start: some(start.addMilliseconds(gap)) }),
646
+ meta: "follower learned from the order" }], Proposals);
647
+ });
648
+ }, () => empty);
649
+ }));
650
+ // A model — an ASYNC proposer; the strip shows a pending chip, and a newer context cancels the wait.
651
+ const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, ($, ctx) => {
652
+ const result = $.let([], Proposals);
653
+ $.try(($) => { $.assign(result, workshopRecommend(ctx)); }).catch(($, message) => {
654
+ // The showcase has no model behind it; any other failure still reaches the sheet's provider diagnostic.
655
+ $.if(message.notEqual("Platform function 'sheet_workshop_recommend' is not available"), ($) => { $.error(message); });
656
+ });
657
+ return result;
658
+ }));
659
+ // The operations each kind of order starts with: the group templates' lines.
660
+ const kitchen = $.let([
661
+ { activity: "Panel cutting", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
662
+ { activity: "Edge banding", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
663
+ { activity: "Assembly", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
664
+ { activity: "Spray finish", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
665
+ ], ArrayType(WorkshopOperation));
666
+ const wardrobe = $.let([
667
+ { activity: "Panel cutting", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
668
+ { activity: "Edge banding", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
669
+ { activity: "Drilling", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
670
+ { activity: "Assembly", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
671
+ ], ArrayType(WorkshopOperation));
672
+ const vanity = $.let([
673
+ { activity: "Panel cutting", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
674
+ { activity: "CNC routing", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
675
+ { activity: "Spray finish", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
676
+ { activity: "Assembly", start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "" },
677
+ ], ArrayType(WorkshopOperation));
678
+ // A new operation's and a new order's defaults, and the check every order passes before Save.
679
+ const newRow = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(WorkshopOperation), () => Sheet.patch(WorkshopOperation, { start: none, end: none, qty: none, machines: { from: [], to: [] }, notes: "", created_by: "planner" })));
680
+ const newGroup = $.const(East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(WorkshopOrder), () => Sheet.patch(WorkshopOrder, { status: "PLANNED", due: none, ops: [] })));
681
+ const readyOrder = $.const(East.function([Sheet.Types.DraftGroup(WorkshopOrder, "ops")], Sheet.Types.Readiness, ($, order) => {
682
+ $.if(order.customer.hasTag("value").and(() => order.customer.unwrap("value").length().equal(0n)), $ => {
683
+ $.return(East.value(variant("incomplete", [{ field: "customer", message: "Name the customer" }]), Sheet.Types.Readiness));
684
+ });
685
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
686
+ }));
687
+ return (_jsx(Box, { height: "760px", children: _jsx(Sheet, { record: orders, group: Sheet.group(WorkshopOrder, "ops", {
688
+ title: "name", sub: o => o.customer, noun: { singular: "order", plural: "orders" },
689
+ cells: { activity: Sheet.group.cell.enum(WorkshopOrder, "statuses", "status"),
690
+ end: Sheet.group.cell.date(WorkshopOrder, "due") },
691
+ }), driver: Sheet.driver("activity", activities.read(), { key: a => a.name, label: a => a.name, meta: a => some(a.uom) }), registers: {
692
+ machines: Sheet.register.concat([
693
+ // A Dict's key rides as the accessors' second argument.
694
+ Sheet.register.members(machines.read(), { kind: "machine", key: (_m, code) => code, label: (_m, code) => code,
695
+ meta: m => some(m.family), parent: m => some(m.bay) }),
696
+ Sheet.register.members(bays, { kind: "bay", key: b => b.name, label: b => b.name, aliases: b => b.aliases,
697
+ meta: _b => some("bay") }),
698
+ // Every machine names a family — duplicate keys fold, the first wins.
699
+ Sheet.register.members(machines.read(), { kind: "family", key: m => m.family, label: m => m.family,
700
+ meta: _m => some("family") }),
701
+ ]),
702
+ statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
703
+ }, columns: {
704
+ activity: Sheet.column.lookup(WorkshopOperation, { header: "Activity", width: "160px" }),
705
+ start: Sheet.column.date(WorkshopOperation, { header: "Start", width: "96px", fill: [nextSlot] }),
706
+ end: Sheet.column.date(WorkshopOperation, { header: "End", sub: "start + days", width: "96px",
707
+ base: "start", fill: [endFromStart] }),
708
+ qty: Sheet.column.quantity(WorkshopOperation, WorkshopActivity, { header: "Qty", sub: "unit per activity",
709
+ width: "104px", uom: a => a.uom, format: Format.Number({ maximumFractionDigits: 0n }), fill: [lastQuantity, shiftQuantity] }),
710
+ machines: Sheet.column.link(WorkshopOperation, WorkshopActivity, "machines", {
711
+ header: "Work centres", sub: "from → to · 2 x edge bander", width: "300px",
712
+ members: [{ kind: "machine", identified: true },
713
+ { kind: "bay", countable: true, resolvesTo: "machine" },
714
+ { kind: "family", countable: true, resolvesTo: "machine" }],
715
+ multiple: { forms: ["N x kind", "kind x N"], ops: ["x", "X", "*", "×"], appliesTo: "countable" },
716
+ sides: { value: a => a.sides, locks: { from: { to: "external", in: "in place" }, to: { from: "external" } } },
717
+ arity: Sheet.link.arity("from", impliedMachines),
718
+ check: [Sheet.link.check.exists(), familyFits],
719
+ fill: [lastMachines, capacity]
720
+ }),
721
+ notes: Sheet.column.text(WorkshopOperation, { header: "Notes", width: "240px", fill: [phrase] }),
722
+ }, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty", "notes", "machines"],
723
+ propose: [followUps, modelProposals, lastFollower] },
724
+ // The inspector pane: every field of an operation; created_by, which no column shows, read only.
725
+ inspector: true, fields: { created_by: Sheet.field.readonly() }, templates: {
726
+ groups: [
727
+ { key: "kitchen", name: "Kitchen order", group: "Orders",
728
+ values: Sheet.patch(WorkshopOrder, { status: "PLANNED", due: none, ops: kitchen }) },
729
+ { key: "wardrobe", name: "Wardrobe order", group: "Orders",
730
+ values: Sheet.patch(WorkshopOrder, { status: "PLANNED", due: none, ops: wardrobe }) },
731
+ { key: "vanity", name: "Vanity unit", group: "Orders",
732
+ values: Sheet.patch(WorkshopOrder, { status: "PLANNED", due: none, ops: vanity }) },
733
+ ],
734
+ rows: [
735
+ { key: "cut", name: "Panel cutting", group: "Operations",
736
+ values: Sheet.patch(WorkshopOperation, { activity: "Panel cutting",
737
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "beam saw" })] } }) },
738
+ { key: "edge", name: "Edge banding", group: "Operations",
739
+ values: Sheet.patch(WorkshopOperation, { activity: "Edge banding",
740
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "edge bander" })] } }) },
741
+ { key: "route", name: "CNC routing", group: "Operations",
742
+ values: Sheet.patch(WorkshopOperation, { activity: "CNC routing",
743
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "CNC router" })] } }) },
744
+ { key: "sand", name: "Sanding", group: "Operations",
745
+ values: Sheet.patch(WorkshopOperation, { activity: "Sanding" }) },
746
+ { key: "spray", name: "Spray finish", group: "Operations",
747
+ values: Sheet.patch(WorkshopOperation, { activity: "Spray finish",
748
+ machines: { from: [], to: [variant("counted", { n: 1n, key: "spray booth" })] } }) },
749
+ { key: "assemble", name: "Assembly", group: "Operations",
750
+ values: Sheet.patch(WorkshopOperation, { activity: "Assembly" }) },
751
+ { key: "wrap", name: "Wrapping", group: "Dispatch",
752
+ values: Sheet.patch(WorkshopOperation, { activity: "Wrapping" }) },
753
+ { key: "deliver", name: "Delivery", group: "Dispatch",
754
+ values: Sheet.patch(WorkshopOperation, { activity: "Delivery" }) },
755
+ ],
756
+ }, library: [
757
+ Sheet.library.rows(),
758
+ // The statuses an order takes: a card dropped on an order's band sets its status.
759
+ Sheet.library.tab(statuses, { name: "Statuses", icon: "flag",
760
+ key: s => s.word, label: s => s.word, drop: s => Sheet.patch(WorkshopOrder, { status: s.word }) }),
761
+ Sheet.library.columns(),
762
+ ], newRow: newRow, newGroup: newGroup, ready: { group: readyOrder }, slice: slice, affordances: ["search", "filter"], views: views, footer: [{ text: East.str `${operations.size()} operations` }], name: "workshop" }) }));
763
+ } }))),
764
+ inputs: [],
765
+ });
766
+ // ============================================================================
767
+ // sheetWeeks — one entry's rows (§3.4)
768
+ // ============================================================================
769
+ /** A plan row — what it is, when it starts (read at its own level) and when it
770
+ * really started, how many, its set-ups, the site it runs at, its status, the
771
+ * order code the ERP stamps on release, and the machines it runs on (a set
772
+ * of codes); `id` identifies it. */
773
+ export const WeekRow = StructType({
774
+ id: StringType, task: StringType, start: OptionType(DateTimeType), qty: OptionType(FloatType),
775
+ setups: OptionType(IntegerType), site: StringType, status: StringType, code: StringType,
776
+ level: Sheet.Types.DateLevel, started: OptionType(DateTimeType), machines: Sheet.Types.Link,
777
+ });
778
+ /** One week's plan — when it starts, and its rows in the planner's order. */
779
+ export const WeekPlan = StructType({ starts: DateTimeType, rows: ArrayType(WeekRow) });
780
+ /** The plans, one per week, keyed 2026-W42, 2026-W43, … */
781
+ export const sheetWeekPlans = e3.record("sheet_week_plans", DictType(StringType, WeekPlan), new Map([
782
+ ["2026-W42", { starts: new Date("2026-10-12T00:00:00Z"), rows: [
783
+ { id: "w42-1", task: "Cut the kitchen carcasses", start: some(new Date("2026-10-12T00:00:00Z")), qty: some(48.0), setups: some(2n), site: "Machine shop",
784
+ status: "PLANNED", code: "", level: variant("day", null), started: none,
785
+ machines: { from: [], to: [variant("range", { from: "S101", to: "S103" })] } },
786
+ { id: "w42-2", task: "Band the carcass edges", start: some(new Date("2026-10-13T00:00:00Z")), qty: some(120.0), setups: some(1n), site: "Machine shop",
787
+ status: "RELEASED", code: "WO-2201", level: variant("day", null), started: some(new Date("2026-10-13T07:30:00Z")),
788
+ machines: { from: [], to: [variant("identified", { key: "E201" })] } },
789
+ { id: "w42-3", task: "Route the door panels", start: none, qty: some(24.0), setups: none, site: "Bench room",
790
+ status: "PLANNED", code: "", level: variant("week", null), started: none,
791
+ machines: { from: [], to: [] } },
792
+ ] }],
793
+ ["2026-W43", { starts: new Date("2026-10-19T00:00:00Z"), rows: [
794
+ { id: "w43-1", task: "Assemble the wardrobes", start: some(new Date("2026-10-19T00:00:00Z")), qty: some(6.0), setups: none, site: "Fitting shop",
795
+ status: "PLANNED", code: "", level: variant("day", null), started: none,
796
+ machines: { from: [], to: [variant("identified", { key: "A701" })] } },
797
+ { id: "w43-2", task: "Spray the vanity doors", start: none, qty: some(4.0), setups: none, site: "Spray shop",
798
+ status: "PLANNED", code: "", level: variant("time", null), started: none,
799
+ machines: { from: [], to: [] } },
800
+ ] }],
801
+ ]));
802
+ /** The plans' patch door — every Save commits through it. */
803
+ export const sheetWeekPlansPatch = e3.mutation.patch(sheetWeekPlans);
804
+ /**
805
+ * One entry's rows (§3.4) — the week the viewer picks, its rows in the
806
+ * planner's order; each week keeps its own drafts until Save or Discard, and
807
+ * a week the record does not hold opens empty and read-only. Every column
808
+ * kind the workshop does not draw, with its rules: a date read at each row's
809
+ * own level, with the actual start once one is recorded; an integer; a
810
+ * reference to the sites; a status whose menu an `options` rule narrows until
811
+ * the row has an order code, the code its detail; the code itself stamped by
812
+ * the ERP, the row it is on owned upstream; and the machines a set of codes,
813
+ * runs of which are offered and printed as one range. A row is ready once its
814
+ * quantity is positive. Every gesture is journalled with its provenance
815
+ * (`onPatch`), and a new row's id is minted from a counter (`newRowId`). Its
816
+ * one pane is the inspector, its Details the author's own (SB58): a row's
817
+ * quantity on a slider, written back through `update` as one transaction —
818
+ * while a row is still missing a field, the sheet's own form shows instead.
819
+ */
820
+ export const sheetWeeks = example({
821
+ keywords: ["Sheet", "BuilderFrame", "record", "entry", "one entry", "rows", "id", "week", "State", "SegmentGroup", "drafts per entry", "inspector", "update", "Field.Slider", "column", "date", "text", "quantity", "integer", "reference", "enum", "set", "stamped", "owned", "register", "options", "level", "actual", "detail", "ranged", "DateLevel", "week", "day", "range", "time", "rule", "ready", "row", "Readiness", "completeness", "validation", "onPatch", "PatchEvent", "provenance", "source", "newRowId", "newRow", "write-back"],
822
+ description: "A sheet over one entry's rows — the week the viewer picks, its rows in the planner's order, each week its own drafts until Save or Discard — with every column kind and its rules (a date at each row's level with its actual, an integer, a reference, an enum narrowed by an options rule with a detail, a stamped code owning its row, a set of machines in ranges), a row rule, a journal of every gesture, ids minted by a counter, and its own Details in the inspector pane, a row's quantity on a slider written back as one transaction",
823
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
824
+ const plans = $.let(Record.bind(sheetWeekPlans, [sheetWeekPlansPatch]));
825
+ // The week the viewer picked, the journal and the id counter: a viewer's own state.
826
+ const week = $.let(State.bind([StringType], "sheet.weeks.week", "2026-W42"));
827
+ const log = $.let(State.bind([ArrayType(StringType)], "sheet.weeks.journal", []));
828
+ const counter = $.let(State.bind([IntegerType], "sheet.weeks.counter", 1n));
829
+ const weeks = $.let(["2026-W42", "2026-W43", "2026-W44"], ArrayType(StringType));
830
+ const pick = $.const(East.function([StringType], NullType, ($, w) => { $(week.write(w)); }));
831
+ const StatusType = StructType({ word: StringType, tone: StatusValueType });
832
+ const statuses = $.let([
833
+ { word: "PLANNED", tone: variant("neutral", null) }, { word: "RELEASED", tone: variant("info", null) },
834
+ { word: "IN PROGRESS", tone: variant("warning", null) }, { word: "COMPLETE", tone: variant("success", null) },
835
+ ], ArrayType(StatusType));
836
+ const sites = $.let(["Machine shop", "Bench room", "Fitting shop", "Spray shop"], ArrayType(StringType));
837
+ const machines = $.let(["S101", "S102", "S103", "E201", "E202", "R301", "R302", "R303", "A701", "A702"], ArrayType(StringType));
838
+ const Ctx = Sheet.Types.DraftContext(WeekRow);
839
+ // Until a row has an order code it may only be planned or released.
840
+ const statusOptions = $.const(East.function([Ctx], OptionType(ArrayType(StringType)), ($, ctx) => {
841
+ const whole = $.const(none, OptionType(ArrayType(StringType)));
842
+ const early = $.const(some(["PLANNED", "RELEASED"]), OptionType(ArrayType(StringType)));
843
+ return ctx.row.code.match({ value: (_$2, code) => code.length().equal(0n).ifElse(() => early, () => whole) }, () => early);
844
+ }));
845
+ // A row is ready once its quantity is positive.
846
+ const readyRow = $.const(East.function([Sheet.Types.Draft(WeekRow), Ctx], Sheet.Types.Readiness, ($, row) => {
847
+ $.if(row.qty.hasTag("value").and(() => row.qty.unwrap("value").match({ some: (_$2, q) => q.lessEqual(0.0), none: () => false })), $ => {
848
+ $.return(East.value(variant("incomplete", [{ field: "qty", message: "Quantity must be positive" }]), Sheet.Types.Readiness));
849
+ });
850
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
851
+ }));
852
+ // Every gesture, journalled once with where it came from.
853
+ const onPatch = $.const(East.function([Sheet.Types.PatchEvent(WeekRow)], NullType, ($, e) => {
854
+ $(log.write(log.read().concat([East.str `${e.origin.getTag()} · ${e.label} · ${e.readiness.getTag()}`])));
855
+ }));
856
+ // The host mints a new row's id.
857
+ const newRowId = $.const(East.function([], StringType, ($) => {
858
+ const n = $.let(counter.read());
859
+ $(counter.write(n.add(1n)));
860
+ return East.str `new-${n}`;
861
+ }));
862
+ const newRow = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(WeekRow), () => Sheet.patch(WeekRow, {
863
+ setups: none, site: "", status: "PLANNED", code: "", level: variant("day", null), started: none, machines: { from: [], to: [] },
864
+ })));
865
+ // A row's own Details: its task, and its quantity on a slider — released, the edited row goes back through `update`.
866
+ const inspect = $.const(East.function([WeekRow, FunctionType([WeekRow], NullType)], UIComponentType, ($, row, update) => {
867
+ const qty = $.let(row.qty.match({ none: () => 0.0, some: (_$, q) => q }));
868
+ const setQty = $.const(East.function([FloatType], NullType, ($2, next) => {
869
+ // East has no struct spread: the row, rebuilt with its new quantity.
870
+ const edited = $2.const({
871
+ id: row.id, task: row.task, start: row.start, qty: some(next), setups: row.setups, site: row.site,
872
+ status: row.status, code: row.code, level: row.level, started: row.started, machines: row.machines,
873
+ }, WeekRow);
874
+ $2(update(edited));
875
+ }));
876
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { children: row.task }), _jsx(Field.Slider, { label: "Quantity", value: qty, min: 0, max: 200, step: 1, helperText: "Released, it is one step the history undoes", onChangeEnd: setQty })] }));
877
+ }));
878
+ const entries = $.let(log.read());
879
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(SegmentGroup, { value: week.read(), onChange: pick, size: "sm", items: weeks.map((_$, w) => SegmentGroup.Item(w, _jsx(Text, { children: w }))) }), _jsx(Box, { height: "480px", children: _jsx(Sheet, { record: plans, entry: { key: week.read(), rows: "rows", id: "id" }, name: "weeks", inspector: inspect, owned: r => r.code.length().greater(0n), registers: {
880
+ statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
881
+ sites: Sheet.register.members(sites, { kind: "site", key: s => s, label: s => s }),
882
+ machines: Sheet.register.members(machines, { kind: "machine", key: m => m, label: m => m }),
883
+ }, columns: {
884
+ task: Sheet.column.text(WeekRow, { header: "Task", width: "260px" }),
885
+ start: Sheet.column.date(WeekRow, { header: "Start", sub: "at the row's level", width: "168px", level: r => r.level, actual: r => r.started }),
886
+ qty: Sheet.column.quantity(WeekRow, { header: "Qty", width: "96px" }),
887
+ setups: Sheet.column.integer(WeekRow, { header: "Set-ups", width: "72px" }),
888
+ site: Sheet.column.reference(WeekRow, "sites", { header: "Site", width: "128px" }),
889
+ status: Sheet.column.enum(WeekRow, "statuses", { header: "Status", width: "132px", options: statusOptions, detail: r => r.code }),
890
+ code: Sheet.column.stamped(WeekRow, { header: "Order code", sub: "stamped on release", owner: "ERP", width: "112px" }),
891
+ machines: Sheet.column.set(WeekRow, "machines", { header: "Machines", sub: "S101-03 · a run", width: "200px",
892
+ members: [{ kind: "machine", identified: true, ranged: true }] }),
893
+ }, ready: { row: readyRow }, onPatch: onPatch, newRow: newRow, newRowId: newRowId }) }), _jsx(Text.MonoLabel, { children: East.str `EDITS · ${entries.length()}` }), entries.map((_$, l) => _jsx(Text, { textStyle: "caption", color: "fg.muted", children: l }))] }));
894
+ } }))),
895
+ inputs: [],
896
+ });
897
+ // ============================================================================
898
+ // sheetBatches — one entry's groups, in the planner's order (#1187)
899
+ // ============================================================================
900
+ /** A part a step works — shown as a sub row: its code, its name, its materials, and where it is done. */
901
+ export const BatchPart = StructType({ id: StringType, code: StringType, name: StringType, materials: ArrayType(StringType), station: OptionType(StringType) });
902
+ /** A booking a step makes — the labour or the equipment it claims, shown as a sub row arm by arm. */
903
+ export const BatchBooking = VariantType({
904
+ labour: StructType({ team: StringType, people: IntegerType, hours: FloatType }),
905
+ equipment: StructType({ resource: StringType }),
906
+ });
907
+ /** A batch's step — what is done to it, and how many, with the parts it works and the bookings it makes (no columns: its sub rows). */
908
+ export const BatchStep = StructType({ task: StringType, qty: OptionType(FloatType), parts: ArrayType(BatchPart), bookings: ArrayType(BatchBooking) });
909
+ /** A batch — its id, its name, and its steps in order. */
910
+ export const Batch = StructType({ id: StringType, name: StringType, steps: ArrayType(BatchStep) });
911
+ /** One day's batches, in the planner's order. */
912
+ export const BatchDay = StructType({ batches: ArrayType(Batch) });
913
+ /** The days, keyed by date. */
914
+ export const sheetBatchDays = e3.record("sheet_batch_days", DictType(StringType, BatchDay), new Map([
915
+ ["2026-10-12", { batches: [
916
+ { id: "B-101", name: "Doors, oak", steps: [
917
+ { task: "Cut doors", qty: some(12.0), parts: [
918
+ { id: "B-101-1", code: "CUT", name: "Cut the door blanks", materials: ["Oak veneered board × 6"], station: some("S101") },
919
+ ], bookings: [variant("labour", { team: "Cutting", people: 1n, hours: 3.0 })] },
920
+ { task: "Band doors", qty: some(48.0), parts: [], bookings: [variant("equipment", { resource: "Edge bander E201" })] },
921
+ { task: "Spray doors", qty: some(12.0), parts: [
922
+ { id: "B-101-3", code: "SPR", name: "Seal and lacquer", materials: ["Sealer", "Matt lacquer"], station: none },
923
+ ], bookings: [] },
924
+ ] },
925
+ { id: "B-102", name: "Carcasses, birch", steps: [
926
+ { task: "Cut carcasses", qty: some(8.0), parts: [], bookings: [] },
927
+ { task: "Drill carcasses", qty: some(8.0), parts: [], bookings: [variant("labour", { team: "Machining", people: 2n, hours: 2.5 })] },
928
+ ] },
929
+ { id: "B-103", name: "Shelves, ash", steps: [
930
+ { task: "Cut shelves", qty: some(20.0), parts: [], bookings: [] },
931
+ { task: "Sand shelves", qty: some(20.0), parts: [], bookings: [] },
932
+ ] },
933
+ ] }],
934
+ ]));
935
+ /** The days' patch door — every Save commits through it. */
936
+ export const sheetBatchDaysPatch = e3.mutation.patch(sheetBatchDays);
937
+ /**
938
+ * One day's batches, in the planner's order (#1187, SB39, SB40, SB44) — a
939
+ * grouped sheet over one entry's groups, each batch a group of its steps. A
940
+ * template dragged from the library lands on the seam it is dropped on — a
941
+ * step into the batch under the pointer, a whole batch between two — and a
942
+ * grip moves a step within its batch or into another, or a whole batch to
943
+ * another place in the day. A batch needs a name before Save, and a new step
944
+ * and a new batch start from their defaults. A step's own records hang under
945
+ * it as sub rows, read only, sharing none of its columns: the parts it works
946
+ * (a struct source — chips and labelled facets, a `none` facet dropping out)
947
+ * and its bookings (a variant source matched arm by arm). Its chevron opens
948
+ * them. Both panes: the library holds the templates and the columns, and the
949
+ * inspector a step's form, which leaves the sub rows' fields out.
950
+ */
951
+ export const sheetBatches = example({
952
+ keywords: ["Sheet", "BuilderFrame", "drag", "drop", "move", "grip", "seam", "templates", "group", "grouped", "entry", "order", "Sheet.library.rows", "children", "newGroup", "newRow", "Patch", "defaults", "completeness", "fold", "undo", "redo", "ready", "subRows", "subRow", "sub rows", "SubRow", "Facet", "chips", "facets", "lead", "detail", "noun", "variant", "match", "read-only", "inspector", "Sheet.field", "hidden"],
953
+ description: "A sheet over one day's batches in the planner's order — templates dragged from the library onto a seam, steps and whole batches moved to another seam by their grips, a batch named before Save, new steps and batches from their defaults, and each step's parts and bookings as read-only sub rows under it, declared per array field like columns, beside a library and an inspector pane",
954
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
955
+ const days = $.let(Record.bind(sheetBatchDays, [sheetBatchDaysPatch]));
956
+ // The steps a finishing batch starts with: the batch template's lines.
957
+ const finishing = $.let([
958
+ { task: "Sand", qty: none, parts: [], bookings: [] },
959
+ { task: "Seal", qty: none, parts: [], bookings: [] },
960
+ { task: "Spray", qty: none, parts: [], bookings: [] },
961
+ ], ArrayType(BatchStep));
962
+ // A batch needs a name before Save.
963
+ const readyBatch = $.const(East.function([Sheet.Types.DraftGroup(Batch, "steps")], Sheet.Types.Readiness, ($, batch) => {
964
+ $.if(batch.name.hasTag("value").and(() => batch.name.unwrap("value").length().equal(0n)), $ => {
965
+ $.return(East.value(variant("incomplete", [{ field: "name", message: "Name the batch" }]), Sheet.Types.Readiness));
966
+ });
967
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
968
+ }));
969
+ const newStep = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(BatchStep), () => Sheet.patch(BatchStep, { qty: none, parts: [], bookings: [] })));
970
+ const newBatch = $.const(East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(Batch), () => Sheet.patch(Batch, { steps: [] })));
971
+ return (_jsx(Box, { height: "560px", children: _jsx(Sheet, { record: days, entry: { key: "2026-10-12", rows: "batches", id: "id" }, group: Sheet.group(Batch, "steps", { title: "name", noun: { singular: "batch", plural: "batches" } }), name: "batches", columns: {
972
+ task: Sheet.column.text(BatchStep, { header: "Step", width: "240px" }),
973
+ qty: Sheet.column.quantity(BatchStep, { header: "Qty", width: "96px" }),
974
+ }, subRows: Sheet.subRows(BatchStep, {
975
+ parts: (p) => Sheet.subRow({
976
+ code: p.code,
977
+ name: p.name,
978
+ chips: p.materials,
979
+ facets: { station: p.station }, // a none drops out
980
+ id: p.id,
981
+ }),
982
+ bookings: (b) => b.match({
983
+ labour: (_$2, l) => Sheet.subRow({ code: "LABOUR", name: East.str `${l.team} · ${l.people} people · ${l.hours} hours` }),
984
+ equipment: (_$2, e) => Sheet.subRow({ code: "EQUIPMENT", name: e.resource }),
985
+ }),
986
+ }),
987
+ // The sub rows show a step's parts and bookings; the inspector's form leaves them out.
988
+ inspector: true, fields: { parts: Sheet.field.hidden(), bookings: Sheet.field.hidden() }, templates: {
989
+ groups: [{ key: "finishing", name: "Finishing batch", group: "Batches",
990
+ values: Sheet.patch(Batch, { name: "Finishing", steps: finishing }) }],
991
+ rows: [{ key: "sand", name: "Sand", group: "Steps", values: Sheet.patch(BatchStep, { task: "Sand", qty: none }) },
992
+ { key: "seal", name: "Seal", group: "Steps", values: Sheet.patch(BatchStep, { task: "Seal", qty: none }) }],
993
+ }, library: [Sheet.library.rows(), Sheet.library.columns()], ready: { group: readyBatch }, newRow: newStep, newGroup: newBatch }) }));
994
+ } }))),
995
+ inputs: [],
996
+ });
997
+ // ============================================================================
998
+ // sheetLoose — loose rows between the groups (#846)
999
+ // ============================================================================
1000
+ /** A task — a package's line, or a loose task that belongs to no package; `id` identifies a loose one. */
1001
+ export const LooseTask = StructType({ id: StringType, task: StringType, qty: OptionType(FloatType), notes: StringType });
1002
+ /** A work package — its name and its tasks. */
1003
+ export const LoosePackage = StructType({ id: StringType, name: StringType, tasks: ArrayType(LooseTask) });
1004
+ /** An entry of the week — a package with its tasks, or a loose task. */
1005
+ export const LooseEntry = Sheet.Types.Entry(LoosePackage, "tasks");
1006
+ /** A week's work — its entries, in the planner's order. */
1007
+ export const LooseWork = StructType({ entries: ArrayType(LooseEntry) });
1008
+ /** The work, one entry per week. */
1009
+ export const sheetLooseWork = e3.record("sheet_loose_work", DictType(StringType, LooseWork), new Map([
1010
+ ["2026-W42", { entries: [
1011
+ variant("row", { id: "brief", task: "Check the drawings", qty: none, notes: "Before any cutting" }),
1012
+ variant("group", { id: "doors", name: "Kitchen doors", tasks: [
1013
+ { id: "doors-1", task: "Cut door blanks", qty: some(24.0), notes: "Oak veneered board" },
1014
+ { id: "doors-2", task: "Inspect the blanks", qty: some(4.0), notes: "Before finishing" },
1015
+ ] }),
1016
+ variant("row", { id: "handover", task: "Hand over to finishing", qty: none, notes: "" }),
1017
+ variant("group", { id: "finish", name: "Door finishing", tasks: [
1018
+ { id: "finish-1", task: "Spray the doors", qty: some(24.0), notes: "Matt lacquer" },
1019
+ ] }),
1020
+ ] }],
1021
+ ]));
1022
+ /** The work's patch door — every Save commits through it. */
1023
+ export const sheetLooseWorkPatch = e3.mutation.patch(sheetLooseWork);
1024
+ /**
1025
+ * Loose rows between the groups (#846) — the week's entries are
1026
+ * `Sheet.Types.Entry(LoosePackage, "tasks")`: a work package with its
1027
+ * tasks, or a task that belongs to no package. A loose task draws as a plain
1028
+ * row, numbered in the packages' sequence; the seam above a package's band,
1029
+ * or beside a loose task, inserts a loose task, and a task's seam inserts a
1030
+ * task into its package. `id` names a field of both types: a loose task is an
1031
+ * entry, identified like a package, and a new task's id is minted. A loose
1032
+ * task's grip moves it between the entries, as a package's moves the package.
1033
+ * It is given no pane.
1034
+ */
1035
+ export const sheetLoose = example({
1036
+ keywords: ["Sheet", "BuilderFrame", "group", "grouped", "loose", "ungrouped", "Entry", "Types.Entry", "variant", "entries", "insert", "move", "newRow", "newGroup", "noun", "entry", "record"],
1037
+ description: "A sheet over one week's entries — work packages and loose tasks: a loose task is a plain row numbered in the packages' sequence, inserted at the seam above a package or beside another loose task, and moved between the entries by its grip",
1038
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1039
+ const work = $.let(Record.bind(sheetLooseWork, [sheetLooseWorkPatch]));
1040
+ const newTask = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(LooseTask), () => Sheet.patch(LooseTask, { qty: none, notes: "" })));
1041
+ const newPackage = $.const(East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(LoosePackage), () => Sheet.patch(LoosePackage, { tasks: [] })));
1042
+ return (_jsx(Box, { height: "480px", children: _jsx(Sheet, { record: work, entry: { key: "2026-W42", rows: "entries", id: "id" }, group: Sheet.group(LoosePackage, "tasks", { title: "name", noun: { singular: "package", plural: "packages" } }), name: "loose", columns: {
1043
+ task: Sheet.column.text(LooseTask, { header: "Task", width: "240px" }),
1044
+ qty: Sheet.column.quantity(LooseTask, { header: "Qty", width: "96px" }),
1045
+ notes: Sheet.column.text(LooseTask, { header: "Notes", width: "260px" }),
1046
+ }, newRow: newTask, newGroup: newPackage }) }));
1047
+ } }))),
1048
+ inputs: [],
1049
+ });
1050
+ // ============================================================================
1051
+ // sheetPaged — a record read a window at a time (§3.4)
1052
+ // ============================================================================
1053
+ /**
1054
+ * A record read a window at a time (§3.4) — the jobs, paged in key order: the
1055
+ * key search seeks a job by its key, and the lens narrows the loaded rows. Its
1056
+ * `edits` let rows be added — from a gutter seam, the selection strip, or
1057
+ * Alt+Insert — and never removed: a job leaves through the record's own
1058
+ * process, not the sheet. It is given no pane.
1059
+ */
1060
+ export const sheetPaged = example({
1061
+ keywords: ["Sheet", "BuilderFrame", "record", "window", "Data.bindPaged", "paged", "key order", "key search", "edits", "insertRows", "removeRows", "insert", "before", "after", "Undo"],
1062
+ description: "A sheet over a large record, read a window at a time in key order — Data.bindPaged over the same record its Save commits to — whose edits add rows (gutter seams, the selection strip, Alt+Insert) and never remove one",
1063
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1064
+ const jobs = $.let(Record.bind(sheetJobs, [sheetJobsPatch]));
1065
+ const page = $.let(Data.bindPaged(sheetJobs));
1066
+ return (_jsx(Box, { height: "560px", children: _jsx(Sheet, { record: jobs, window: page, name: "paged", edits: { insertRows: true, removeRows: false }, columns: {
1067
+ task: Sheet.column.text(SheetJob, { header: "Task", width: "240px" }),
1068
+ start: Sheet.column.date(SheetJob, { header: "Start", width: "96px" }),
1069
+ qty: Sheet.column.quantity(SheetJob, { header: "Qty", width: "96px" }),
1070
+ } }) }));
1071
+ } }))),
1072
+ inputs: [],
1073
+ });
1074
+ // ============================================================================
1075
+ // sheetUpkeep — the inspector's every field kind (#1220)
1076
+ // ============================================================================
1077
+ /** A machine's family — the kind of machine it is. */
1078
+ export const UpkeepFamily = VariantType({ beam_saw: NullType, edge_bander: NullType, cnc_router: NullType, spray_booth: NullType });
1079
+ /** One check of a service — what is checked, and whether it was done. */
1080
+ export const UpkeepCheck = StructType({ text: StringType, done: BooleanType });
1081
+ /** Who supplies a machine's parts. */
1082
+ export const UpkeepSupplier = StructType({ name: StringType, phone: StringType });
1083
+ /** A machine's upkeep — one field of each kind the inspector's form edits. */
1084
+ export const MachineUpkeep = StructType({
1085
+ name: StringType, // a text column
1086
+ bay: StringType, // a reference column: a key of the bays
1087
+ hours: IntegerType, // an integer column: a number with its unit and bounds
1088
+ family: UpkeepFamily, // a select of the variant's cases
1089
+ wear: OptionType(FloatType), // a number with its unit, bounds and step, cleared to none
1090
+ crew: OptionType(StringType), // a text, emptied to none
1091
+ tickets: SetType(StringType), // tags, the tickets offered as they are typed
1092
+ checks: ArrayType(UpkeepCheck), // a checklist
1093
+ supplier: UpkeepSupplier, // a nested struct: its fields under its name
1094
+ in_service: BooleanType, // a checkbox
1095
+ guard_checked: OptionType(BooleanType), // a checkbox, cleared to none: not recorded
1096
+ serviced: DateTimeType, // a date and a time
1097
+ next_service: OptionType(DateTimeType), // a date and a time, set or cleared
1098
+ serial: StringType, // a stamped column: read only
1099
+ });
1100
+ /** The machines' upkeep, keyed by machine code. */
1101
+ export const sheetMachineUpkeep = e3.record("sheet_machine_upkeep", DictType(StringType, MachineUpkeep), new Map([
1102
+ ["S101", { name: "Beam saw", bay: "Bay 1", hours: 1240n, family: variant("beam_saw", null), wear: some(35.5), crew: some("Cutting crew"),
1103
+ tickets: new Set(["Panel saw ticket", "Dust extraction"]),
1104
+ checks: [{ text: "Blade sharpened", done: true }, { text: "Fence squared", done: true }, { text: "Extraction cleaned", done: false }],
1105
+ supplier: { name: "Kestrelmoor Saw Works", phone: "01632 960 118" },
1106
+ in_service: true, guard_checked: some(true),
1107
+ serviced: new Date("2026-09-28T07:30:00Z"), next_service: some(new Date("2026-11-09T07:30:00Z")), serial: "BS-4410-0193" }],
1108
+ ["E201", { name: "Edge bander", bay: "Bay 2", hours: 860n, family: variant("edge_bander", null), wear: none, crew: none,
1109
+ tickets: new Set(), checks: [], supplier: { name: "", phone: "" },
1110
+ in_service: true, guard_checked: none,
1111
+ serviced: new Date("2026-08-17T06:00:00Z"), next_service: none, serial: "EB-2207-0457" }],
1112
+ ["R301", { name: "CNC router", bay: "Bay 3", hours: 2210n, family: variant("cnc_router", null), wear: some(62.0), crew: some("Machining crew"),
1113
+ tickets: new Set(["Router ticket"]),
1114
+ checks: [{ text: "Spindle greased", done: true }, { text: "Bed vacuum tested", done: false }],
1115
+ supplier: { name: "Brindlecote Tooling", phone: "01632 960 342" },
1116
+ in_service: false, guard_checked: some(false),
1117
+ serviced: new Date("2026-07-06T13:15:00Z"), next_service: none, serial: "CR-5120-0088" }],
1118
+ ["F401", { name: "Spray booth", bay: "Bay 4", hours: 540n, family: variant("spray_booth", null), wear: none, crew: some("Finishing crew"),
1119
+ tickets: new Set(["Spray ticket", "First aid"]),
1120
+ checks: [{ text: "Filters changed", done: true }],
1121
+ supplier: { name: "Fennimarsh Coatings", phone: "01632 960 775" },
1122
+ in_service: true, guard_checked: some(true),
1123
+ serviced: new Date("2026-10-01T08:00:00Z"), next_service: some(new Date("2026-12-01T08:00:00Z")), serial: "SB-0391-0017" }],
1124
+ ]));
1125
+ /** The upkeep's patch door — every Save commits through it. */
1126
+ export const sheetMachineUpkeepPatch = e3.mutation.patch(sheetMachineUpkeep);
1127
+ /**
1128
+ * The inspector's every field kind (#1220) — the workshop's machines' upkeep,
1129
+ * a record keyed by machine code. The grid shows four columns: a machine's
1130
+ * name, its bay, the hours it has run and its serial, stamped by its maker.
1131
+ * The inspector pane shows every field of the selected machine through
1132
+ * `Fields`' form: a column's field through its column's kind, a field no
1133
+ * column shows by its type, each hinted with `Sheet.field.*` where its type
1134
+ * cannot say enough — a number with its unit, bounds and step, a select of a
1135
+ * variant's cases, a reference to the bays, a text emptied to none, tags
1136
+ * offered from a list, a checklist, a nested struct's fields under its name,
1137
+ * a checkbox, a date and a time, and Options set and cleared. The hinted
1138
+ * fields lead, in hint order, and the rest follow in declared order. A
1139
+ * machine out of service needs its next service booked before Save. It is
1140
+ * given no library.
1141
+ */
1142
+ export const sheetUpkeep = example({
1143
+ keywords: ["Sheet", "BuilderFrame", "inspector", "fields", "Sheet.field", "Fields", "form", "text", "placeholder", "number", "unit", "min", "max", "step", "select", "labels", "reference", "register", "checkbox", "datetime", "Option", "Set", "Clear", "tags", "options", "checklist", "nested", "struct", "stamped", "readonly", "integer", "ready", "row", "Readiness", "record"],
1144
+ description: "A sheet whose inspector pane edits one field of every kind over the workshop's machines' upkeep — a number with its unit and bounds, a select of a variant's cases, a reference, a text emptied to none, tags, a checklist, a nested struct's fields under its name, a checkbox, a date and a time, Options set and cleared, and a stamped code read only — the hinted fields leading, with a row rule's issue",
1145
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1146
+ const upkeep = $.let(Record.bind(sheetMachineUpkeep, [sheetMachineUpkeepPatch]));
1147
+ const bays = $.let(["Bay 1", "Bay 2", "Bay 3", "Bay 4", "Bay 7"], ArrayType(StringType));
1148
+ // A machine out of service needs its next service booked.
1149
+ const readyMachine = $.const(East.function([Sheet.Types.Draft(MachineUpkeep), Sheet.Types.DraftContext(MachineUpkeep)], Sheet.Types.Readiness, ($, row) => {
1150
+ const out = $.const(row.in_service.match({ value: (_$2, on) => on.not() }, () => false));
1151
+ const unbooked = $.const(row.next_service.match({ value: (_$2, next) => next.hasTag("none") }, () => false));
1152
+ $.if(out.and(() => unbooked), $ => {
1153
+ $.return(East.value(variant("incomplete", [{ field: "next_service", message: "Out of service — book its next service" }]), Sheet.Types.Readiness));
1154
+ });
1155
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
1156
+ }));
1157
+ return (_jsx(Box, { height: "640px", children: _jsx(Sheet, { record: upkeep, name: "upkeep", registers: { bays: Sheet.register.members(bays, { kind: "bay", key: b => b, label: b => b }) }, columns: {
1158
+ name: Sheet.column.text(MachineUpkeep, { header: "Machine", width: "200px" }),
1159
+ bay: Sheet.column.reference(MachineUpkeep, "bays", { header: "Bay", width: "96px" }),
1160
+ hours: Sheet.column.integer(MachineUpkeep, { header: "Hours", sub: "run since new", width: "96px" }),
1161
+ serial: Sheet.column.stamped(MachineUpkeep, { header: "Serial", sub: "on the maker's plate", owner: "Maker", width: "140px" }),
1162
+ },
1163
+ // The inspector pane: every field of a machine — the hinted ones first, in this order.
1164
+ inspector: true, fields: {
1165
+ name: Sheet.field.text({ placeholder: "Its name on the floor" }),
1166
+ bay: Sheet.field.reference({ of: "bays", help: "Where it stands" }),
1167
+ hours: Sheet.field.number({ unit: "h", min: 0n, step: 10n }),
1168
+ family: Sheet.field.select({ labels: { beam_saw: "Beam saw", edge_bander: "Edge bander", cnc_router: "CNC router", spray_booth: "Spray booth" } }),
1169
+ wear: Sheet.field.number({ label: "Blade wear", unit: "%", min: 0, max: 100, step: 0.5 }),
1170
+ crew: Sheet.field.text({ placeholder: "Which crew runs it" }),
1171
+ tickets: Sheet.field.tags({ options: ["Panel saw ticket", "Router ticket", "Spray ticket", "Dust extraction", "First aid"] }),
1172
+ checks: Sheet.field.checklist({ help: "Signed off at the last service" }),
1173
+ supplier: { name: Sheet.field.text({ placeholder: "Who supplies its parts" }), phone: Sheet.field.text({ placeholder: "01632 960 000" }) },
1174
+ }, ready: { row: readyMachine } }) }));
1175
+ } }))),
1176
+ inputs: [],
1177
+ });
1178
+ //# sourceMappingURL=sheet.examples.js.map