@elaraai/east-ui 1.0.80 → 1.0.82

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 (353) hide show
  1. package/dist/src/charts/chart/index.d.ts +4 -0
  2. package/dist/src/charts/chart/index.d.ts.map +1 -1
  3. package/dist/src/charts/spec/index.d.ts +8 -0
  4. package/dist/src/charts/spec/index.d.ts.map +1 -1
  5. package/dist/src/collections/deck/index.d.ts +8 -0
  6. package/dist/src/collections/deck/index.d.ts.map +1 -1
  7. package/dist/src/collections/index.d.ts +2 -2
  8. package/dist/src/collections/index.d.ts.map +1 -1
  9. package/dist/src/collections/index.js.map +1 -1
  10. package/dist/src/collections/library/index.d.ts +4 -0
  11. package/dist/src/collections/library/index.d.ts.map +1 -1
  12. package/dist/src/collections/library/types.d.ts +4 -0
  13. package/dist/src/collections/library/types.d.ts.map +1 -1
  14. package/dist/src/collections/matrix/index.d.ts +56 -19
  15. package/dist/src/collections/matrix/index.d.ts.map +1 -1
  16. package/dist/src/collections/matrix/index.js +38 -24
  17. package/dist/src/collections/matrix/index.js.map +1 -1
  18. package/dist/src/collections/plan/assemble.d.ts +655 -2113
  19. package/dist/src/collections/plan/assemble.d.ts.map +1 -1
  20. package/dist/src/collections/plan/assemble.js +159 -136
  21. package/dist/src/collections/plan/assemble.js.map +1 -1
  22. package/dist/src/collections/plan/builders.d.ts +193 -40
  23. package/dist/src/collections/plan/builders.d.ts.map +1 -1
  24. package/dist/src/collections/plan/builders.js +162 -32
  25. package/dist/src/collections/plan/builders.js.map +1 -1
  26. package/dist/src/collections/plan/factories.d.ts +290 -116
  27. package/dist/src/collections/plan/factories.d.ts.map +1 -1
  28. package/dist/src/collections/plan/factories.js +281 -156
  29. package/dist/src/collections/plan/factories.js.map +1 -1
  30. package/dist/src/collections/plan/index.d.ts +111 -58
  31. package/dist/src/collections/plan/index.d.ts.map +1 -1
  32. package/dist/src/collections/plan/index.js +62 -31
  33. package/dist/src/collections/plan/index.js.map +1 -1
  34. package/dist/src/collections/plan/ir.d.ts +759 -1769
  35. package/dist/src/collections/plan/ir.d.ts.map +1 -1
  36. package/dist/src/collections/plan/ir.js +51 -27
  37. package/dist/src/collections/plan/ir.js.map +1 -1
  38. package/dist/src/collections/plan/pick.d.ts +89 -71
  39. package/dist/src/collections/plan/pick.d.ts.map +1 -1
  40. package/dist/src/collections/plan/pick.js +101 -86
  41. package/dist/src/collections/plan/pick.js.map +1 -1
  42. package/dist/src/collections/plan/root.d.ts +164 -77
  43. package/dist/src/collections/plan/root.d.ts.map +1 -1
  44. package/dist/src/collections/plan/root.js +350 -42
  45. package/dist/src/collections/plan/root.js.map +1 -1
  46. package/dist/src/collections/plan/series.d.ts +12524 -10527
  47. package/dist/src/collections/plan/series.d.ts.map +1 -1
  48. package/dist/src/collections/plan/series.js +1596 -298
  49. package/dist/src/collections/plan/series.js.map +1 -1
  50. package/dist/src/collections/plan/types.d.ts +6115 -2802
  51. package/dist/src/collections/plan/types.d.ts.map +1 -1
  52. package/dist/src/collections/plan/types.js +575 -170
  53. package/dist/src/collections/plan/types.js.map +1 -1
  54. package/dist/src/collections/sheet/bridge.d.ts +154 -43
  55. package/dist/src/collections/sheet/bridge.d.ts.map +1 -1
  56. package/dist/src/collections/sheet/bridge.js +383 -240
  57. package/dist/src/collections/sheet/bridge.js.map +1 -1
  58. package/dist/src/collections/sheet/columns.d.ts +66 -16
  59. package/dist/src/collections/sheet/columns.d.ts.map +1 -1
  60. package/dist/src/collections/sheet/columns.js +25 -25
  61. package/dist/src/collections/sheet/columns.js.map +1 -1
  62. package/dist/src/collections/sheet/context-bridge.d.ts +48 -0
  63. package/dist/src/collections/sheet/context-bridge.d.ts.map +1 -0
  64. package/dist/src/collections/sheet/context-bridge.js +216 -0
  65. package/dist/src/collections/sheet/context-bridge.js.map +1 -0
  66. package/dist/src/collections/sheet/draft-bridge.d.ts +37 -0
  67. package/dist/src/collections/sheet/draft-bridge.d.ts.map +1 -0
  68. package/dist/src/collections/sheet/draft-bridge.js +153 -0
  69. package/dist/src/collections/sheet/draft-bridge.js.map +1 -0
  70. package/dist/src/collections/sheet/drafts.d.ts +14 -0
  71. package/dist/src/collections/sheet/drafts.d.ts.map +1 -0
  72. package/dist/src/collections/sheet/drafts.js +14 -0
  73. package/dist/src/collections/sheet/drafts.js.map +1 -0
  74. package/dist/src/collections/sheet/editing-bridge.d.ts +36 -0
  75. package/dist/src/collections/sheet/editing-bridge.d.ts.map +1 -0
  76. package/dist/src/collections/sheet/editing-bridge.js +196 -0
  77. package/dist/src/collections/sheet/editing-bridge.js.map +1 -0
  78. package/dist/src/collections/sheet/editing-types.d.ts +268 -0
  79. package/dist/src/collections/sheet/editing-types.d.ts.map +1 -0
  80. package/dist/src/collections/sheet/editing-types.js +34 -0
  81. package/dist/src/collections/sheet/editing-types.js.map +1 -0
  82. package/dist/src/collections/sheet/edits.d.ts +37 -0
  83. package/dist/src/collections/sheet/edits.d.ts.map +1 -0
  84. package/dist/src/collections/sheet/edits.js +31 -0
  85. package/dist/src/collections/sheet/edits.js.map +1 -0
  86. package/dist/src/collections/sheet/group.d.ts +165 -0
  87. package/dist/src/collections/sheet/group.d.ts.map +1 -0
  88. package/dist/src/collections/sheet/group.js +52 -0
  89. package/dist/src/collections/sheet/group.js.map +1 -0
  90. package/dist/src/collections/sheet/index.d.ts +171 -27
  91. package/dist/src/collections/sheet/index.d.ts.map +1 -1
  92. package/dist/src/collections/sheet/index.js +99 -17
  93. package/dist/src/collections/sheet/index.js.map +1 -1
  94. package/dist/src/collections/sheet/link.d.ts +22 -16
  95. package/dist/src/collections/sheet/link.d.ts.map +1 -1
  96. package/dist/src/collections/sheet/link.js +18 -12
  97. package/dist/src/collections/sheet/link.js.map +1 -1
  98. package/dist/src/collections/sheet/registers.d.ts +152 -5
  99. package/dist/src/collections/sheet/registers.d.ts.map +1 -1
  100. package/dist/src/collections/sheet/registers.js +154 -5
  101. package/dist/src/collections/sheet/registers.js.map +1 -1
  102. package/dist/src/collections/sheet/root.d.ts +175 -26
  103. package/dist/src/collections/sheet/root.d.ts.map +1 -1
  104. package/dist/src/collections/sheet/root.js +150 -26
  105. package/dist/src/collections/sheet/root.js.map +1 -1
  106. package/dist/src/collections/sheet/seed-bridge.d.ts +14 -0
  107. package/dist/src/collections/sheet/seed-bridge.d.ts.map +1 -0
  108. package/dist/src/collections/sheet/seed-bridge.js +51 -0
  109. package/dist/src/collections/sheet/seed-bridge.js.map +1 -0
  110. package/dist/src/collections/sheet/sub-rows.d.ts +249 -0
  111. package/dist/src/collections/sheet/sub-rows.d.ts.map +1 -0
  112. package/dist/src/collections/sheet/sub-rows.js +222 -0
  113. package/dist/src/collections/sheet/sub-rows.js.map +1 -0
  114. package/dist/src/collections/sheet/transactions.d.ts +76 -0
  115. package/dist/src/collections/sheet/transactions.d.ts.map +1 -0
  116. package/dist/src/collections/sheet/transactions.js +40 -0
  117. package/dist/src/collections/sheet/transactions.js.map +1 -0
  118. package/dist/src/collections/sheet/types.d.ts +12034 -3651
  119. package/dist/src/collections/sheet/types.d.ts.map +1 -1
  120. package/dist/src/collections/sheet/types.js +365 -48
  121. package/dist/src/collections/sheet/types.js.map +1 -1
  122. package/dist/src/collections/table/index.d.ts +98 -123
  123. package/dist/src/collections/table/index.d.ts.map +1 -1
  124. package/dist/src/collections/table/index.js +97 -111
  125. package/dist/src/collections/table/index.js.map +1 -1
  126. package/dist/src/collections/table/types.d.ts +87 -31
  127. package/dist/src/collections/table/types.d.ts.map +1 -1
  128. package/dist/src/collections/table/types.js +51 -26
  129. package/dist/src/collections/table/types.js.map +1 -1
  130. package/dist/src/collections/value-tree/flatten.d.ts.map +1 -1
  131. package/dist/src/collections/value-tree/flatten.js +4 -1
  132. package/dist/src/collections/value-tree/flatten.js.map +1 -1
  133. package/dist/src/component.d.ts +5075 -4079
  134. package/dist/src/component.d.ts.map +1 -1
  135. package/dist/src/component.js +46 -46
  136. package/dist/src/component.js.map +1 -1
  137. package/dist/src/contracts/approval.d.ts +10 -8
  138. package/dist/src/contracts/approval.d.ts.map +1 -1
  139. package/dist/src/contracts/approval.js +10 -8
  140. package/dist/src/contracts/approval.js.map +1 -1
  141. package/dist/src/contracts/drag.d.ts +11 -9
  142. package/dist/src/contracts/drag.d.ts.map +1 -1
  143. package/dist/src/contracts/drag.js +28 -21
  144. package/dist/src/contracts/drag.js.map +1 -1
  145. package/dist/src/contracts/editing.d.ts +1283 -0
  146. package/dist/src/contracts/editing.d.ts.map +1 -0
  147. package/dist/src/contracts/editing.js +711 -0
  148. package/dist/src/contracts/editing.js.map +1 -0
  149. package/dist/src/contracts/index.d.ts +2 -1
  150. package/dist/src/contracts/index.d.ts.map +1 -1
  151. package/dist/src/contracts/index.js +2 -1
  152. package/dist/src/contracts/index.js.map +1 -1
  153. package/dist/src/contracts/pick.d.ts +6 -7
  154. package/dist/src/contracts/pick.d.ts.map +1 -1
  155. package/dist/src/contracts/pick.js +6 -7
  156. package/dist/src/contracts/pick.js.map +1 -1
  157. package/dist/src/contracts/review.d.ts +3 -1
  158. package/dist/src/contracts/review.d.ts.map +1 -1
  159. package/dist/src/contracts/review.js +3 -1
  160. package/dist/src/contracts/review.js.map +1 -1
  161. package/dist/src/contracts/source.d.ts +270 -80
  162. package/dist/src/contracts/source.d.ts.map +1 -1
  163. package/dist/src/contracts/source.js +357 -58
  164. package/dist/src/contracts/source.js.map +1 -1
  165. package/dist/src/display/chip-rail/index.d.ts.map +1 -1
  166. package/dist/src/display/chip-rail/index.js +10 -10
  167. package/dist/src/display/chip-rail/index.js.map +1 -1
  168. package/dist/src/extension.d.ts +2 -1
  169. package/dist/src/extension.d.ts.map +1 -1
  170. package/dist/src/extension.js +2 -1
  171. package/dist/src/extension.js.map +1 -1
  172. package/dist/src/format/index.d.ts +1 -154
  173. package/dist/src/format/index.d.ts.map +1 -1
  174. package/dist/src/format/types.d.ts +35 -23
  175. package/dist/src/format/types.d.ts.map +1 -1
  176. package/dist/src/format/types.js +16 -14
  177. package/dist/src/format/types.js.map +1 -1
  178. package/dist/src/forms/field/index.js +4 -4
  179. package/dist/src/forms/field/index.js.map +1 -1
  180. package/dist/src/forms/file-upload/index.js +16 -16
  181. package/dist/src/forms/file-upload/index.js.map +1 -1
  182. package/dist/src/index.d.ts +4 -2
  183. package/dist/src/index.d.ts.map +1 -1
  184. package/dist/src/index.js +12 -5
  185. package/dist/src/index.js.map +1 -1
  186. package/dist/src/internal.d.ts +8 -3
  187. package/dist/src/internal.d.ts.map +1 -1
  188. package/dist/src/internal.js +11 -3
  189. package/dist/src/internal.js.map +1 -1
  190. package/dist/src/layout/box/index.d.ts.map +1 -1
  191. package/dist/src/layout/box/index.js +47 -47
  192. package/dist/src/layout/box/index.js.map +1 -1
  193. package/dist/src/layout/flex/index.d.ts.map +1 -1
  194. package/dist/src/layout/flex/index.js +47 -47
  195. package/dist/src/layout/flex/index.js.map +1 -1
  196. package/dist/src/layout/grid/index.js +11 -11
  197. package/dist/src/layout/grid/index.js.map +1 -1
  198. package/dist/src/layout/scroll-area/index.d.ts.map +1 -1
  199. package/dist/src/layout/scroll-area/index.js +8 -8
  200. package/dist/src/layout/scroll-area/index.js.map +1 -1
  201. package/dist/src/layout/separator/index.d.ts.map +1 -1
  202. package/dist/src/layout/separator/index.js +6 -6
  203. package/dist/src/layout/separator/index.js.map +1 -1
  204. package/dist/src/layout/stack/index.d.ts.map +1 -1
  205. package/dist/src/layout/stack/index.js +46 -46
  206. package/dist/src/layout/stack/index.js.map +1 -1
  207. package/dist/src/layout/sticky/index.d.ts.map +1 -1
  208. package/dist/src/layout/sticky/index.js +8 -8
  209. package/dist/src/layout/sticky/index.js.map +1 -1
  210. package/dist/src/overlays/action-bar/index.d.ts.map +1 -1
  211. package/dist/src/overlays/action-bar/index.js +8 -8
  212. package/dist/src/overlays/action-bar/index.js.map +1 -1
  213. package/dist/src/platform/index.d.ts +1 -1
  214. package/dist/src/platform/index.d.ts.map +1 -1
  215. package/dist/src/platform/index.js +1 -1
  216. package/dist/src/platform/index.js.map +1 -1
  217. package/dist/src/platform/slice/impl.d.ts +62 -51
  218. package/dist/src/platform/slice/impl.d.ts.map +1 -1
  219. package/dist/src/platform/slice/impl.js +155 -72
  220. package/dist/src/platform/slice/impl.js.map +1 -1
  221. package/dist/src/platform/slice/index.d.ts +3744 -2226
  222. package/dist/src/platform/slice/index.d.ts.map +1 -1
  223. package/dist/src/platform/slice/index.js +43 -1
  224. package/dist/src/platform/slice/index.js.map +1 -1
  225. package/dist/src/runtime/collections/deck.d.ts +4 -0
  226. package/dist/src/runtime/collections/deck.d.ts.map +1 -1
  227. package/dist/src/runtime/collections/matrix.d.ts +10 -9
  228. package/dist/src/runtime/collections/matrix.d.ts.map +1 -1
  229. package/dist/src/runtime/collections/matrix.js +11 -6
  230. package/dist/src/runtime/collections/matrix.js.map +1 -1
  231. package/dist/src/runtime/collections/plan.d.ts +148 -65
  232. package/dist/src/runtime/collections/plan.d.ts.map +1 -1
  233. package/dist/src/runtime/collections/plan.js +155 -67
  234. package/dist/src/runtime/collections/plan.js.map +1 -1
  235. package/dist/src/runtime/collections/sheet.d.ts +56 -23
  236. package/dist/src/runtime/collections/sheet.d.ts.map +1 -1
  237. package/dist/src/runtime/collections/sheet.js +30 -22
  238. package/dist/src/runtime/collections/sheet.js.map +1 -1
  239. package/dist/src/runtime/collections/table.d.ts +18 -11
  240. package/dist/src/runtime/collections/table.d.ts.map +1 -1
  241. package/dist/src/runtime/collections/table.js +7 -4
  242. package/dist/src/runtime/collections/table.js.map +1 -1
  243. package/dist/src/shared/reify.d.ts +4 -46
  244. package/dist/src/shared/reify.d.ts.map +1 -1
  245. package/dist/src/shared/reify.js +4 -61
  246. package/dist/src/shared/reify.js.map +1 -1
  247. package/dist/src/shared/tree.d.ts +88 -0
  248. package/dist/src/shared/tree.d.ts.map +1 -0
  249. package/dist/src/shared/tree.js +134 -0
  250. package/dist/src/shared/tree.js.map +1 -0
  251. package/dist/src/slice/breakdown/index.d.ts +4 -0
  252. package/dist/src/slice/breakdown/index.d.ts.map +1 -1
  253. package/dist/src/slice/breakdown/types.d.ts +4 -0
  254. package/dist/src/slice/breakdown/types.d.ts.map +1 -1
  255. package/dist/src/slice/cohort/index.d.ts +24 -4
  256. package/dist/src/slice/cohort/index.d.ts.map +1 -1
  257. package/dist/src/slice/cohort/index.js +8 -4
  258. package/dist/src/slice/cohort/index.js.map +1 -1
  259. package/dist/src/slice/cohort/types.d.ts +11 -0
  260. package/dist/src/slice/cohort/types.d.ts.map +1 -1
  261. package/dist/src/slice/cohort/types.js +7 -0
  262. package/dist/src/slice/cohort/types.js.map +1 -1
  263. package/dist/src/slice/filter/index.d.ts +4 -0
  264. package/dist/src/slice/filter/index.d.ts.map +1 -1
  265. package/dist/src/slice/filter/types.d.ts +4 -0
  266. package/dist/src/slice/filter/types.d.ts.map +1 -1
  267. package/dist/src/slice/index.d.ts +736 -0
  268. package/dist/src/slice/index.d.ts.map +1 -1
  269. package/dist/src/slice/legend/index.d.ts +4 -0
  270. package/dist/src/slice/legend/index.d.ts.map +1 -1
  271. package/dist/src/slice/legend/types.d.ts +4 -0
  272. package/dist/src/slice/legend/types.d.ts.map +1 -1
  273. package/dist/src/slice/rail/index.d.ts +8 -4
  274. package/dist/src/slice/rail/index.d.ts.map +1 -1
  275. package/dist/src/slice/rail/index.js.map +1 -1
  276. package/dist/src/slice/range/index.d.ts +4 -0
  277. package/dist/src/slice/range/index.d.ts.map +1 -1
  278. package/dist/src/slice/range/types.d.ts +4 -0
  279. package/dist/src/slice/range/types.d.ts.map +1 -1
  280. package/dist/src/slice/search/index.d.ts +4 -0
  281. package/dist/src/slice/search/index.d.ts.map +1 -1
  282. package/dist/src/slice/search/types.d.ts +4 -0
  283. package/dist/src/slice/search/types.d.ts.map +1 -1
  284. package/dist/src/slice/summary/index.d.ts +4 -0
  285. package/dist/src/slice/summary/index.d.ts.map +1 -1
  286. package/dist/src/slice/summary/types.d.ts +4 -0
  287. package/dist/src/slice/summary/types.d.ts.map +1 -1
  288. package/dist/src/typography/code/index.js +1 -1
  289. package/dist/src/typography/code/index.js.map +1 -1
  290. package/dist/src/typography/code-block/index.js +7 -7
  291. package/dist/src/typography/code-block/index.js.map +1 -1
  292. package/dist/src/typography/heading/index.js +2 -2
  293. package/dist/src/typography/heading/index.js.map +1 -1
  294. package/dist/src/typography/list/index.js +1 -1
  295. package/dist/src/typography/list/index.js.map +1 -1
  296. package/dist/src/typography/mark/index.js +1 -1
  297. package/dist/src/typography/mark/index.js.map +1 -1
  298. package/dist/src/typography/note/index.js +1 -1
  299. package/dist/src/typography/note/index.js.map +1 -1
  300. package/dist/src/typography/numeric/index.d.ts +3 -308
  301. package/dist/src/typography/numeric/index.d.ts.map +1 -1
  302. package/dist/src/typography/numeric/index.js +4 -4
  303. package/dist/src/typography/numeric/index.js.map +1 -1
  304. package/dist/src/typography/numeric/types.d.ts +1 -154
  305. package/dist/src/typography/numeric/types.d.ts.map +1 -1
  306. package/dist/src/typography/text/index.js +1 -1
  307. package/dist/src/typography/text/index.js.map +1 -1
  308. package/dist/test/collections/matrix.examples.d.ts +7 -3
  309. package/dist/test/collections/matrix.examples.d.ts.map +1 -1
  310. package/dist/test/collections/matrix.examples.js +55 -24
  311. package/dist/test/collections/matrix.examples.js.map +1 -1
  312. package/dist/test/collections/paged-source.examples.d.ts +4 -0
  313. package/dist/test/collections/paged-source.examples.d.ts.map +1 -1
  314. package/dist/test/collections/paged-source.examples.js +98 -13
  315. package/dist/test/collections/paged-source.examples.js.map +1 -1
  316. package/dist/test/collections/plan.examples.d.ts +101 -26
  317. package/dist/test/collections/plan.examples.d.ts.map +1 -1
  318. package/dist/test/collections/plan.examples.js +1481 -755
  319. package/dist/test/collections/plan.examples.js.map +1 -1
  320. package/dist/test/collections/sheet-link.examples.d.ts +7 -0
  321. package/dist/test/collections/sheet-link.examples.d.ts.map +1 -0
  322. package/dist/test/collections/sheet-link.examples.js +41 -0
  323. package/dist/test/collections/sheet-link.examples.js.map +1 -0
  324. package/dist/test/collections/sheet-transactions.examples.d.ts +7 -0
  325. package/dist/test/collections/sheet-transactions.examples.d.ts.map +1 -0
  326. package/dist/test/collections/sheet-transactions.examples.js +58 -0
  327. package/dist/test/collections/sheet-transactions.examples.js.map +1 -0
  328. package/dist/test/collections/sheet.examples.d.ts +52 -5
  329. package/dist/test/collections/sheet.examples.d.ts.map +1 -1
  330. package/dist/test/collections/sheet.examples.js +500 -158
  331. package/dist/test/collections/sheet.examples.js.map +1 -1
  332. package/dist/test/collections/table.examples.d.ts +31 -3
  333. package/dist/test/collections/table.examples.d.ts.map +1 -1
  334. package/dist/test/collections/table.examples.js +188 -28
  335. package/dist/test/collections/table.examples.js.map +1 -1
  336. package/dist/test/contracts/editing.examples.d.ts +7 -0
  337. package/dist/test/contracts/editing.examples.d.ts.map +1 -0
  338. package/dist/test/contracts/editing.examples.js +58 -0
  339. package/dist/test/contracts/editing.examples.js.map +1 -0
  340. package/dist/test/layout/splitter.examples.d.ts.map +1 -1
  341. package/dist/test/layout/splitter.examples.js +3 -1
  342. package/dist/test/layout/splitter.examples.js.map +1 -1
  343. package/dist/test/platform/slice.examples.d.ts.map +1 -1
  344. package/dist/test/platform/slice.examples.js +1 -0
  345. package/dist/test/platform/slice.examples.js.map +1 -1
  346. package/dist/test/slice/slice.examples.d.ts.map +1 -1
  347. package/dist/test/slice/slice.examples.js +5 -2
  348. package/dist/test/slice/slice.examples.js.map +1 -1
  349. package/package.json +4 -3
  350. package/dist/src/collections/plan/data-forms.d.ts +0 -332
  351. package/dist/src/collections/plan/data-forms.d.ts.map +0 -1
  352. package/dist/src/collections/plan/data-forms.js +0 -212
  353. package/dist/src/collections/plan/data-forms.js.map +0 -1
@@ -4,13 +4,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/east-ui/jsx-runtime";
4
4
  * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
5
5
  */
6
6
  /** @jsxImportSource @elaraai/east-ui */
7
- import { East, ArrayType, BooleanType, DateTimeType, DictType, FloatType, IntegerType, NullType, OptionType, StringType, StructType, example, none, some, variant, } from "@elaraai/east";
7
+ import { East, ArrayType, BooleanType, DateTimeType, DictType, FloatType, IntegerType, NullType, OptionType, StringType, StructType, VariantType, example, none, some, variant, } from "@elaraai/east";
8
8
  import { Badge, Box, Configurator, Format, HStack, Input, Paged, Reactive, SegmentGroup, Sheet, Slice, State, Status, Style, Switch, Text, UIComponentType, VStack, } from "@elaraai/east-ui";
9
9
  // ============================================================================
10
10
  // The Sheet corpus — the five slots of EXAMPLES_AUTHORING.md §8 (Sheet Spec.md §8 P1):
11
11
  // `sheetBasic` · `sheetVariants` (THE configurator) · `sheetPlan` (the flagship)
12
12
  // · the behavioural isolates `sheetCopilot` / `sheetLens` / `sheetWriteBack` /
13
- // `sheetPaged` · `sheetStress`. Every example is self-contained — types,
13
+ // `sheetGrouped` / `sheetPaged` / `sheetReadiness` / `sheetInsertion` /
14
+ // `sheetSubRows` / `sheetRules` / `sheetRegisters` / `sheetLoose` · `sheetStress`. Every example is self-contained — types,
14
15
  // fixtures and constructors inside the body, bulk data derived East-side —
15
16
  // and the fixtures are the prototype's synthetic registers and rows: a
16
17
  // discrete manufacturing plant (machines on lines, work orders moving parts
@@ -29,7 +30,7 @@ const ACTIVITY_TYPE = StructType({
29
30
  name: StringType, uom: StringType, rate: FloatType, fte: IntegerType, days: IntegerType, sides: Sheet.Types.Sides,
30
31
  });
31
32
  /** The model behind an async proposer — an e3 function, a service, a notebook; the Sheet only needs the types. */
32
- const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.Context(PLAN_ROW_TYPE, ACTIVITY_TYPE)], ArrayType(Sheet.Types.Proposal(PLAN_ROW_TYPE)), { optional: true });
33
+ const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.DraftContext(PLAN_ROW_TYPE, ACTIVITY_TYPE)], ArrayType(Sheet.Types.Proposal(PLAN_ROW_TYPE)), { optional: true });
33
34
  /**
34
35
  * The smallest sheet (§3.1) — three typed columns over the host's structs,
35
36
  * `onUpdate` receiving the whole collection with the edit applied (the
@@ -37,7 +38,7 @@ const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.Co
37
38
  */
38
39
  export const sheetBasic = example({
39
40
  keywords: ["Sheet", "Root", "basic", "column", "date", "text", "quantity", "onUpdate", "State", "Reactive", "id"],
40
- description: "The smallest sheet — three typed columns over the host's structs, the whole collection written back on every edit",
41
+ description: "The smallest sheet — three typed columns over the host's structs, draft changes reviewed together and the whole collection written once on Apply",
41
42
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
42
43
  const JobType = StructType({
43
44
  id: StringType,
@@ -48,8 +49,8 @@ export const sheetBasic = example({
48
49
  const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_basic_jobs", [
49
50
  { id: "j1", start: none, task: "Machining", qty: none },
50
51
  ]));
51
- return (_jsx(Sheet, { data: jobs.read(), id: "id", columns: {
52
- start: Sheet.column.date(JobType, { header: "Start", sub: "d/m · fri · +3d" }),
52
+ return (_jsx(Sheet, { data: jobs, id: "id", columns: {
53
+ start: Sheet.column.date(JobType, { header: "Start", sub: "dd / mm / yyyy" }),
53
54
  task: Sheet.column.text(JobType, { header: "Task" }),
54
55
  qty: Sheet.column.quantity(JobType, { header: "Qty" }), // no driver on this sheet — the two-argument form
55
56
  }, onUpdate: jobs.write }));
@@ -64,11 +65,11 @@ export const sheetBasic = example({
64
65
  * log to the reactive aside.
65
66
  */
66
67
  export const sheetVariants = example({
67
- keywords: ["Sheet", "Root", "configurator", "density", "blanks", "readOnly", "height", "fill", "scroll", "#320", "copilot", "fill", "provider", "onSelect", "onEdit", "Configurator", "SegmentGroup", "Switch", "Input", "Reactive", "State"],
68
+ keywords: ["Sheet", "Root", "configurator", "density", "blanks", "readOnly", "height", "fill", "scroll", "#320", "copilot", "fill", "provider", "onSelect", "onPatch", "Configurator", "SegmentGroup", "Switch", "Input", "Reactive", "State"],
68
69
  description: "Sheet configurator — density, the blank tail, read-only, size mode (auto / scroll / fill) and the copilot switch all expression-fed into one live sheet; selection and edits log to the aside",
69
70
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
70
71
  const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType), notes: StringType });
71
- const Ctx = Sheet.Types.Context(JobType);
72
+ const Ctx = Sheet.Types.DraftContext(JobType);
72
73
  const rows = $.let(State.bind([ArrayType(JobType)], "sheet_variants_rows", [
73
74
  { id: "j1", start: some(new Date("2026-02-16T00:00:00Z")), task: "Machining", qty: some(1200.0), notes: "Rough the P-40 blanks" },
74
75
  { id: "j2", start: some(new Date("2026-03-09T00:00:00Z")), task: "Painting", qty: some(250.0), notes: "" },
@@ -100,17 +101,14 @@ export const sheetVariants = example({
100
101
  const NoteFill = OptionType(Sheet.Types.Fill(StringType));
101
102
  const phrase = $.const(East.function([Ctx], NoteFill, ($, ctx) => {
102
103
  const noFill = $.const(none, NoteFill);
103
- return copilotBind.read().and(() => ctx.row.task.length().greater(0n)).ifElse((_$) => East.value(some({ value: East.str `${ctx.row.task} as planned`, meta: "phrasing from the task" }), NoteFill), (_$) => noFill);
104
+ 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),
105
+ }, () => noFill);
104
106
  }));
105
107
  const onSelect = $.const(East.function([Sheet.Types.Selection], NullType, ($, sel) => {
106
108
  $(lastEventBind.write(East.str `onSelect: row ${sel.rowId.match({ some: (_$, id) => id, none: (_$) => "—" })} · ${sel.key.match({ some: (_$, k) => k, none: (_$) => "—" })}`));
107
109
  }));
108
- const onEdit = $.const(East.function([Sheet.Types.Edit(JobType)], NullType, ($, e) => {
109
- $(lastEventBind.write(e.match({
110
- commit: (_$, c) => East.str `commit: ${c.rowId} · ${c.key} · ${c.source.getTag()}`,
111
- insert: (_$, i) => East.str `insert: ${i.row.id}`,
112
- remove: (_$, r) => East.str `remove: ${r.rowIds.length()} rows`,
113
- })));
110
+ const onPatch = $.const(East.function([Sheet.Types.PatchEvent(JobType)], NullType, ($, e) => {
111
+ $(lastEventBind.write(East.str `${e.origin.getTag()} · ${e.draftChanges.length()} entries · ${e.readiness.getTag()}`));
114
112
  }));
115
113
  const densitySel = $.let(densities.filter((_$, v) => v.getTag().equal(dKey)).get(0n));
116
114
  // Size mode — an empty height reads as unbounded, so ONE sheet
@@ -122,12 +120,12 @@ export const sheetVariants = example({
122
120
  Configurator.Control("Size", sKey, _jsx(SegmentGroup, { value: sKey, onChange: onSize, size: "sm", items: sizeModes.map((_$, m) => SegmentGroup.Item(m, _jsx(Text, { children: m.upperCase() }))) })),
123
121
  Configurator.Control("Blank tail", East.print(blanks), _jsx(Input.Integer, { value: blanks, min: 0n, max: 40n, step: 2n, size: "sm", onChange: onBlanks })),
124
122
  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 })] })),
125
- ], preview: _jsx(Box, { width: "100%", height: boxHeight, overflow: "hidden", children: _jsx(Sheet, { data: rows.read(), id: "id", columns: {
126
- start: Sheet.column.date(JobType, { header: "Start", sub: "d/m · fri · +3d", width: "96px" }),
123
+ ], preview: _jsx(Box, { width: "100%", height: boxHeight, overflow: "hidden", children: _jsx(Sheet, { data: rows, id: "id", columns: {
124
+ start: Sheet.column.date(JobType, { header: "Start", sub: "dd / mm / yyyy", width: "96px" }),
127
125
  task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
128
- qty: Sheet.column.quantity(JobType, { header: "Qty", sub: "1,200 · 1.2k · pcs", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
126
+ qty: Sheet.column.quantity(JobType, { header: "Qty", sub: "1,200 · pcs", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
129
127
  notes: Sheet.column.text(JobType, { header: "Notes", sub: "free text", fill: [phrase] }),
130
- }, density: densitySel, blanks: blanks, readOnly: readOnly, onSelect: onSelect, onEdit: onEdit, onUpdate: rows.write, style: { height: sheetHeight } }) }), aside: {
128
+ }, density: densitySel, blanks: blanks, readOnly: readOnly, onSelect: onSelect, onPatch: onPatch, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "" })), onUpdate: rows.write, style: { height: sheetHeight } }) }), aside: {
131
129
  label: "Events · Reactive",
132
130
  body: (_jsx(Badge, { colorPalette: "brand", variant: "outline", children: East.equal(lastEvent.length(), 0n).ifElse((_$) => "Interact with the sheet", (_$) => lastEvent) })),
133
131
  }, spec: [
@@ -156,7 +154,7 @@ export const sheetPlan = example({
156
154
  const LineType = StructType({ code: StringType, name: StringType, machines: IntegerType, aliases: ArrayType(StringType) });
157
155
  const FamilyType = StructType({ name: StringType, aliases: ArrayType(StringType) });
158
156
  const StatusType = StructType({ word: StringType, tone: Status.Types.Value });
159
- const Ctx = Sheet.Types.Context(PlanRowType, ActivityType);
157
+ const Ctx = Sheet.Types.DraftContext(PlanRowType, ActivityType);
160
158
  const Proposals = ArrayType(Sheet.Types.Proposal(PlanRowType));
161
159
  // The synthetic registers — a discrete manufacturing plant: machines on
162
160
  // lines, work orders moving parts between them.
@@ -247,7 +245,10 @@ export const sheetPlan = example({
247
245
  // The arity rule (§3.4) — how many machines the To half should hold, from the quantity.
248
246
  const impliedStations = $.const(East.function([Ctx], OptionType(Sheet.Types.Counted), ($, ctx) => {
249
247
  const noCount = $.const(none, OptionType(Sheet.Types.Counted));
250
- const qty = $.let(ctx.row.qty.match({ some: (_$, v) => v, none: (_$) => 0.0 }));
248
+ $.if(ctx.row.qty.hasTag("value").not(), ($) => { $.return(noCount); });
249
+ const supplied = $.const(ctx.row.qty.unwrap("value"));
250
+ $.if(supplied.hasTag("some").not(), ($) => { $.return(noCount); });
251
+ const qty = $.const(supplied.unwrap("some"));
251
252
  const piecesPerMachine = $.const(300.0);
252
253
  // ⌈qty ÷ 300⌉ and round(qty) by hand — `toInteger` refuses a fraction.
253
254
  const share = $.let(qty.divide(piecesPerMachine));
@@ -266,12 +267,12 @@ export const sheetPlan = example({
266
267
  const siteMatches = $.const(East.function([CheckCtx], OptionType(StringType), ($, c) => {
267
268
  const noFlag = $.const(none, OptionType(StringType));
268
269
  const site = $.let(c.half.match({ from: (_$) => c.row.fromSite, to: (_$) => c.row.toSite }));
269
- return c.member.match({
270
- identified: (_$, m) => site.equal("").not()
270
+ return site.hasTag("value").ifElse(() => c.member.match({
271
+ identified: (_$, m) => site.unwrap("value").equal("").not()
271
272
  .and(() => machineSites.has(m.key))
272
- .and(() => machineSites.get(m.key).equal(site).not())
273
- .ifElse((_$2) => East.value(some(East.str `${m.key} is not at ${site}`), OptionType(StringType)), (_$2) => noFlag),
274
- }, (_$) => noFlag);
273
+ .and(() => machineSites.get(m.key).equal(site.unwrap("value")).not())
274
+ .ifElse((_$2) => East.value(some(East.str `${m.key} is not at ${site.unwrap("value")}`), OptionType(StringType)), (_$2) => noFlag),
275
+ }, (_$) => noFlag), () => noFlag);
275
276
  }));
276
277
  // The fills (§3.5) — derive, history, sequence, default, phrase, capacity — as author functions.
277
278
  const DateFill = OptionType(Sheet.Types.Fill(DateTimeType));
@@ -281,38 +282,52 @@ export const sheetPlan = example({
281
282
  const endFromStart = $.const(East.function([Ctx], DateFill, ($, ctx) => {
282
283
  const noFill = $.const(none, DateFill);
283
284
  return ctx.row.start.match({
284
- none: (_$) => noFill,
285
- some: (_$, start) => ctx.driver.match({
286
- none: (_$2) => noFill,
287
- some: (_$2, d) => East.value(some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }), DateFill),
285
+ value: (_$, supplied) => supplied.match({
286
+ none: () => noFill,
287
+ some: (_$, start) => ctx.driver.match({
288
+ none: () => noFill,
289
+ some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
290
+ }),
288
291
  }),
289
- });
292
+ }, () => noFill);
290
293
  }));
291
- const lastSimilar = $.const(East.function([Ctx], OptionType(PlanRowType), ($, ctx) => {
292
- const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.equal(ctx.row.activity)));
293
- return similar.length().equal(0n).ifElse((_$) => East.value(none, OptionType(PlanRowType)), (_$) => East.value(some(similar.get(similar.length().subtract(1n))), OptionType(PlanRowType)));
294
+ const lastSimilar = $.const(East.function([Ctx], OptionType(Sheet.Types.Draft(PlanRowType)), ($, ctx) => {
295
+ const noRow = $.const(none, OptionType(Sheet.Types.Draft(PlanRowType)));
296
+ return ctx.row.activity.match({
297
+ value: ($, activity) => {
298
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))));
299
+ return similar.size().equal(0n).ifElse(() => noRow, () => some(similar.get(similar.size().subtract(1n))));
300
+ },
301
+ }, () => noRow);
294
302
  }));
295
303
  const lastQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
296
304
  const noFill = $.const(none, FloatFill);
297
- const similar = $.const(lastSimilar);
298
- return similar(ctx).match({
299
- none: (_$) => noFill,
300
- some: (_$, r) => r.qty.match({ none: (_$2) => noFill, some: (_$2, v) => East.value(some({ value: v, meta: East.str `like ${r.id}` }), FloatFill) }),
301
- });
305
+ return ctx.row.activity.match({
306
+ value: ($, activity) => {
307
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))
308
+ .and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
309
+ return similar.size().equal(0n).ifElse(() => noFill, ($) => {
310
+ const row = $.let(similar.get(similar.size().subtract(1n)));
311
+ return some({ value: row.qty.unwrap("value").unwrap("some"), meta: "last supplied quantity for this activity" });
312
+ });
313
+ },
314
+ }, () => noFill);
302
315
  }));
303
316
  const lastStations = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
304
317
  const noFill = $.const(none, LinkFill);
305
318
  const similar = $.const(lastSimilar);
306
319
  return similar(ctx).match({
307
- none: (_$) => noFill,
308
- some: (_$, r) => r.stations.from.length().add(r.stations.to.length()).equal(0n).ifElse((_$2) => noFill, (_$2) => East.value(some({ value: r.stations, meta: East.str `same stations as ${r.id}` }), LinkFill)),
320
+ none: () => noFill,
321
+ some: (_$, r) => r.stations.match({
322
+ value: (_$, stations) => stations.from.size().add(stations.to.size()).equal(0n).ifElse(() => noFill, () => some({ value: stations, meta: "same stations as the last similar row" })),
323
+ }, () => noFill),
309
324
  });
310
325
  }));
311
326
  const nextSlot = $.const(East.function([Ctx], DateFill, ($, ctx) => {
312
- const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("some")));
327
+ const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("value").and(() => r.start.unwrap("value").hasTag("some"))));
313
328
  return dated.length().greater(0n).ifElse(($2) => {
314
329
  const last = $2.let(dated.get(dated.length().subtract(1n)));
315
- return East.value(some({ value: last.start.unwrap("some").addDays(7n), meta: East.str `week after ${last.id}` }), DateFill);
330
+ return East.value(some({ value: last.start.unwrap("value").unwrap("some").addDays(7n), meta: East.str `week after ${last.id.match({ value: (_$, id) => id }, () => "previous row")}` }), DateFill);
316
331
  }, ($2) => {
317
332
  const daysToMonday = $2.let(East.value(8n, IntegerType).subtract(ctx.today.getDayOfWeek()).remainder(7n));
318
333
  const monday = $2.let(ctx.today.addDays(daysToMonday.equal(0n).ifElse((_$) => 7n, (_$) => daysToMonday)));
@@ -328,11 +343,12 @@ export const sheetPlan = example({
328
343
  }));
329
344
  const phrase = $.const(East.function([Ctx], TextFill, ($, ctx) => {
330
345
  const noFill = $.const(none, TextFill);
331
- const n = $.let(ctx.row.qty.match({
332
- some: ($2, v) => { const k = $2.let(v.add(0.5)); return k.subtract(k.remainder(1.0)).toInteger(); }, // round by hand — `toInteger` refuses a fraction
333
- none: (_$) => 0n,
334
- }));
335
- return ctx.row.activity.startsWith("Machining").ifElse((_$) => East.value(some({ value: East.str `Machine ${n} P-40 blanks`, meta: "phrasing from past machining runs" }), TextFill), (_$) => ctx.row.activity.startsWith("Painting").ifElse((_$2) => East.value(some({ value: East.str `Paint ${n} P-40 housings`, meta: "phrasing from past painting runs" }), TextFill), (_$2) => noFill));
346
+ return ctx.row.activity.hasTag("value").and(() => ctx.row.qty.hasTag("value"))
347
+ .and(() => ctx.row.qty.unwrap("value").hasTag("some")).ifElse(($) => {
348
+ const activity = $.const(ctx.row.activity.unwrap("value"));
349
+ const qty = $.const(ctx.row.qty.unwrap("value").unwrap("some"));
350
+ return activity.startsWith("Machining").ifElse(() => some({ value: East.str `Machine ${qty} P-40 blanks`, meta: "phrasing from supplied activity and quantity" }), () => activity.startsWith("Painting").ifElse(() => some({ value: East.str `Paint ${qty} P-40 housings`, meta: "phrasing from supplied activity and quantity" }), () => noFill));
351
+ }, () => noFill);
336
352
  }));
337
353
  const countedByQuantity = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
338
354
  const noFill = $.const(none, LinkFill);
@@ -346,39 +362,52 @@ export const sheetPlan = example({
346
362
  // The proposers (§3.6) — a domain pattern, a learned follower, and an ASYNC model call.
347
363
  const roughingFollowUps = $.const(East.function([Ctx], Proposals, ($, ctx) => {
348
364
  const empty = $.const([], Proposals);
349
- const implied = $.const(impliedStations);
350
- const n = $.let(implied(ctx).match({ some: (_$, c) => c.n, none: (_$) => 1n }));
351
- return ctx.row.activity.equal("Machining - Roughing").and(() => ctx.row.end.hasTag("some")).ifElse(($2) => {
352
- const endAt = $2.let(ctx.row.end.unwrap("some"));
353
- return $2.const([
354
- { patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: ctx.row.start, end: ctx.row.end, qty: some(n.toFloat()), notes: East.str `Inspect ${n} roughing lots` }),
355
- meta: "inspection · same days" },
356
- { patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(endAt.addDays(3n)), end: some(endAt.addDays(7n)), qty: ctx.row.qty, notes: "Finish the roughed blanks" }),
357
- meta: "finishing · end +3…+7 d" },
365
+ return ctx.row.activity.hasTag("value")
366
+ .and(() => ctx.row.activity.unwrap("value").equal("Machining - Roughing"))
367
+ .and(() => ctx.row.end.hasTag("value"))
368
+ .and(() => ctx.row.end.unwrap("value").hasTag("some")).ifElse(($) => {
369
+ const end = $.const(ctx.row.end.unwrap("value").unwrap("some"));
370
+ return $.const([
371
+ { patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: some(end), end: some(end), qty: some(4.0), notes: "Inspect 4 lots" }), meta: "inspection at the supplied end" },
372
+ { patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(end.addDays(3n)), end: some(end.addDays(7n)), notes: "Finish the roughed blanks" }), meta: "finishing · end +3…+7 d" },
358
373
  ], Proposals);
359
- }, (_$2) => empty);
374
+ }, () => empty);
360
375
  }));
361
376
  const lastFollower = $.const(East.function([Ctx], Proposals, ($, ctx) => {
362
377
  const empty = $.const([], Proposals);
363
- const dated = $.let(ctx.rows.filter((_$, r) => r.start.hasTag("some").and(() => r.activity.equal("").not())));
364
- const n = $.let(dated.length());
365
- const upper = $.let(n.greater(1n).ifElse((_$) => n.subtract(1n), (_$) => 0n));
366
- const pairs = $.let(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.equal(ctx.row.activity).and(() => dated.get(i.add(1n)).activity.equal(ctx.row.activity).not())));
367
- return pairs.length().equal(0n).or(() => ctx.row.activity.equal("")).or(() => ctx.row.start.hasTag("some").not()).ifElse((_$) => empty, ($2) => {
368
- const i = $2.let(pairs.get(pairs.length().subtract(1n)));
369
- const from = $2.let(dated.get(i));
370
- const next = $2.let(dated.get(i.add(1n)));
371
- const gapMs = $2.let(next.start.unwrap("some").toEpochMilliseconds().subtract(from.start.unwrap("some").toEpochMilliseconds()));
372
- const at = $2.let(ctx.row.start.unwrap("some").addMilliseconds(gapMs));
373
- return $2.const([{
374
- patch: Sheet.patch(PlanRowType, { activity: next.activity, start: some(at), qty: next.qty, notes: next.notes }),
375
- meta: East.str `${next.activity} followed ${ctx.row.activity} last time · +${gapMs.divide(86400000n)}d`,
376
- }], Proposals);
378
+ return ctx.row.activity.hasTag("value")
379
+ .and(() => ctx.row.start.hasTag("value"))
380
+ .and(() => ctx.row.start.unwrap("value").hasTag("some")).ifElse(($) => {
381
+ const activity = $.const(ctx.row.activity.unwrap("value"));
382
+ const start = $.const(ctx.row.start.unwrap("value").unwrap("some"));
383
+ const dated = $.const(ctx.rows.filter((_$, r) => r.start.hasTag("value")
384
+ .and(() => r.start.unwrap("value").hasTag("some"))
385
+ .and(() => r.activity.hasTag("value"))));
386
+ const upper = $.const(dated.size().greater(1n).ifElse(() => dated.size().subtract(1n), () => 0n));
387
+ const pairs = $.const(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.unwrap("value").equal(activity)
388
+ .and(() => dated.get(i.add(1n)).activity.unwrap("value").equal(activity).not())));
389
+ return pairs.size().equal(0n).ifElse(() => empty, ($) => {
390
+ const index = $.const(pairs.get(pairs.size().subtract(1n)));
391
+ const from = $.const(dated.get(index));
392
+ const next = $.const(dated.get(index.add(1n)));
393
+ const gap = $.const(next.start.unwrap("value").unwrap("some").toEpochMilliseconds()
394
+ .subtract(from.start.unwrap("value").unwrap("some").toEpochMilliseconds()));
395
+ return $.const([{ patch: Sheet.patch(PlanRowType, { activity: next.activity.unwrap("value"), start: some(start.addMilliseconds(gap)) }),
396
+ meta: "follower learned from supplied activities and dates" }], Proposals);
397
+ });
398
+ }, () => empty);
399
+ }));
400
+ const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, ($, ctx) => {
401
+ const result = $.let([], Proposals);
402
+ $.try(($) => { $.assign(result, planRecommend(ctx)); }).catch(($, message) => {
403
+ // The standalone showcase has no model backend. Other failures
404
+ // still reach the Sheet's provider diagnostic.
405
+ $.if(message.notEqual("Platform function 'sheet_plan_recommend' is not available"), ($) => { $.error(message); });
377
406
  });
407
+ return result;
378
408
  }));
379
- const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, (_$, ctx) => planRecommend(ctx)));
380
409
  const planned = $.let(rows.filter((_$, r) => r.activity.length().greater(0n)).length());
381
- return (_jsx(Sheet, { data: rows, id: "id", owned: r => r.orderCode.length().greater(0n).or(() => r.status.equal("CANCELLED")), driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), registers: {
410
+ return (_jsx(Sheet, { data: plan, id: "id", owned: r => r.orderCode.length().greater(0n).or(() => r.status.equal("CANCELLED")), driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), registers: {
382
411
  stations: Sheet.register.concat([
383
412
  Sheet.register.members(machines, { kind: "machine", key: m => m.code, label: m => m.code,
384
413
  meta: m => some(m.family), parent: m => some(m.line) }),
@@ -391,7 +420,7 @@ export const sheetPlan = example({
391
420
  sites: Sheet.register.members(sites, { kind: "site", key: s => s, label: s => s }),
392
421
  statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
393
422
  }, columns: {
394
- start: Sheet.column.date(PlanRowType, { header: "Start", sub: "d/m · fri · +3d", width: "96px", fill: [nextSlot] }),
423
+ start: Sheet.column.date(PlanRowType, { header: "Start", sub: "dd / mm / yyyy", width: "96px", fill: [nextSlot] }),
395
424
  end: Sheet.column.date(PlanRowType, { header: "End", sub: "4d = start+4", width: "96px", base: "start", fill: [endFromStart] }),
396
425
  activity: Sheet.column.lookup(PlanRowType, { header: "Activity", sub: "activity register", width: "214px" }),
397
426
  qty: Sheet.column.quantity(PlanRowType, ActivityType, {
@@ -416,7 +445,9 @@ export const sheetPlan = example({
416
445
  orderCode: Sheet.column.stamped(PlanRowType, { header: "Order code", sub: "stamped on release", owner: "ERP", width: "104px" }),
417
446
  status: Sheet.column.enum(PlanRowType, "statuses", { header: "Status", sub: "erp", width: "124px" }),
418
447
  }, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty", "notes", "stations"],
419
- propose: [roughingFollowUps, modelProposals, lastFollower] }, slice: slice, affordances: ["search", "filter"], views: views.read(), onViewsChange: views.write, onUpdate: plan.write, footer: [{ text: East.str `${planned} planned` }], style: { height: "fill" } }));
448
+ propose: [roughingFollowUps, modelProposals, lastFollower] }, slice: slice, affordances: ["search", "filter"], views: views.read(), onViewsChange: views.write, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(PlanRowType), () => Sheet.patch(PlanRowType, {
449
+ notes: "", stations: { from: [], to: [] }, fromSite: "", toSite: "", orderCode: "", status: "PLANNED",
450
+ })), onUpdate: plan.write, footer: [{ text: East.str `${planned} planned` }], style: { height: "fill" } }));
420
451
  } }))),
421
452
  inputs: [],
422
453
  });
@@ -426,10 +457,10 @@ export const sheetPlan = example({
426
457
  * similar row's quantity), sequence (a week after the last dated row), default
427
458
  * (eight hours at the driver's rate), a domain pattern proposer, a learned
428
459
  * follower, and an ASYNC proposer behind a platform function; every take
429
- * logs its provenance through `onEdit`.
460
+ * logs its provenance through `onPatch`.
430
461
  */
431
462
  export const sheetCopilot = example({
432
- keywords: ["Sheet", "Root", "copilot", "fill", "provider", "propose", "proposer", "suggest", "derive", "history", "sequence", "default", "learned", "follower", "asyncFunction", "asyncPlatform", "pending", "latest wins", "provenance", "onEdit", "source", "Context", "Fill", "Patch", "Proposal", "Reactive", "State"],
463
+ keywords: ["Sheet", "Root", "copilot", "fill", "provider", "propose", "proposer", "suggest", "derive", "history", "sequence", "default", "learned", "follower", "asyncFunction", "asyncPlatform", "pending", "latest wins", "provenance", "onPatch", "source", "Context", "Fill", "Patch", "Proposal", "Reactive", "State"],
433
464
  description: "The copilot in isolation — derive, history, sequence and default fills, a pattern proposer, a learned follower and an async model proposer, all author East functions over the typed context; takes log their provenance",
434
465
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
435
466
  const PlanRowType = StructType({
@@ -438,7 +469,7 @@ export const sheetCopilot = example({
438
469
  fromSite: StringType, toSite: StringType, orderCode: StringType, status: StringType,
439
470
  });
440
471
  const ActivityType = StructType({ name: StringType, uom: StringType, rate: FloatType, fte: IntegerType, days: IntegerType, sides: Sheet.Types.Sides });
441
- const Ctx = Sheet.Types.Context(PlanRowType, ActivityType);
472
+ const Ctx = Sheet.Types.DraftContext(PlanRowType, ActivityType);
442
473
  const Proposals = ArrayType(Sheet.Types.Proposal(PlanRowType));
443
474
  const activities = $.const([
444
475
  { name: "Machining", uom: "pcs", rate: 60.0, fte: 2n, days: 4n, sides: variant("both", null) },
@@ -459,28 +490,35 @@ export const sheetCopilot = example({
459
490
  const endFromStart = $.const(East.function([Ctx], DateFill, ($, ctx) => {
460
491
  const noFill = $.const(none, DateFill);
461
492
  return ctx.row.start.match({
462
- none: (_$) => noFill,
463
- some: (_$, start) => ctx.driver.match({
464
- none: (_$2) => noFill,
465
- some: (_$2, d) => East.value(some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }), DateFill),
493
+ value: (_$, supplied) => supplied.match({
494
+ none: () => noFill,
495
+ some: (_$, start) => ctx.driver.match({
496
+ none: () => noFill,
497
+ some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
498
+ }),
466
499
  }),
467
- });
500
+ }, () => noFill);
468
501
  }));
469
502
  // history — the last row above with this activity.
470
503
  const lastQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
471
504
  const noFill = $.const(none, FloatFill);
472
- const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.equal(ctx.row.activity)));
473
- return similar.length().equal(0n).ifElse((_$) => noFill, ($2) => {
474
- const r = $2.let(similar.get(similar.length().subtract(1n)));
475
- return r.qty.match({ none: (_$) => noFill, some: (_$, v) => East.value(some({ value: v, meta: East.str `like ${r.id}` }), FloatFill) });
476
- });
505
+ return ctx.row.activity.match({
506
+ value: ($, activity) => {
507
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))
508
+ .and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
509
+ return similar.size().equal(0n).ifElse(() => noFill, ($) => {
510
+ const row = $.let(similar.get(similar.size().subtract(1n)));
511
+ return some({ value: row.qty.unwrap("value").unwrap("some"), meta: "last supplied quantity for this activity" });
512
+ });
513
+ },
514
+ }, () => noFill);
477
515
  }));
478
516
  // sequence — a week after the nearest dated row above; else next Monday.
479
517
  const nextSlot = $.const(East.function([Ctx], DateFill, ($, ctx) => {
480
- const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("some")));
518
+ const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("value").and(() => r.start.unwrap("value").hasTag("some"))));
481
519
  return dated.length().greater(0n).ifElse(($2) => {
482
520
  const last = $2.let(dated.get(dated.length().subtract(1n)));
483
- return East.value(some({ value: last.start.unwrap("some").addDays(7n), meta: East.str `week after ${last.id}` }), DateFill);
521
+ return East.value(some({ value: last.start.unwrap("value").unwrap("some").addDays(7n), meta: East.str `week after ${last.id.match({ value: (_$, id) => id }, () => "previous row")}` }), DateFill);
484
522
  }, ($2) => {
485
523
  const daysToMonday = $2.let(East.value(8n, IntegerType).subtract(ctx.today.getDayOfWeek()).remainder(7n));
486
524
  const monday = $2.let(ctx.today.addDays(daysToMonday.equal(0n).ifElse((_$) => 7n, (_$) => daysToMonday)));
@@ -498,54 +536,66 @@ export const sheetCopilot = example({
498
536
  // A domain pattern — a roughing run is followed by an inspection and a finishing run.
499
537
  const roughingFollowUps = $.const(East.function([Ctx], Proposals, ($, ctx) => {
500
538
  const empty = $.const([], Proposals);
501
- return ctx.row.activity.equal("Machining - Roughing").and(() => ctx.row.end.hasTag("some")).ifElse(($2) => {
502
- const endAt = $2.let(ctx.row.end.unwrap("some"));
503
- return $2.const([
504
- { patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: ctx.row.start, end: ctx.row.end, qty: some(4.0), notes: "Inspect 4 lots" }),
505
- meta: "inspection · same days" },
506
- { patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(endAt.addDays(3n)), end: some(endAt.addDays(7n)), qty: ctx.row.qty, notes: "Finish the roughed blanks" }),
507
- meta: "finishing · end +3…+7 d" },
539
+ return ctx.row.activity.hasTag("value")
540
+ .and(() => ctx.row.activity.unwrap("value").equal("Machining - Roughing"))
541
+ .and(() => ctx.row.end.hasTag("value"))
542
+ .and(() => ctx.row.end.unwrap("value").hasTag("some")).ifElse(($) => {
543
+ const end = $.const(ctx.row.end.unwrap("value").unwrap("some"));
544
+ return $.const([
545
+ { patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: some(end), end: some(end), qty: some(4.0), notes: "Inspect 4 lots" }), meta: "inspection at the supplied end" },
546
+ { patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(end.addDays(3n)), end: some(end.addDays(7n)), notes: "Finish the roughed blanks" }), meta: "finishing · end +3…+7 d" },
508
547
  ], Proposals);
509
- }, (_$2) => empty);
548
+ }, () => empty);
510
549
  }));
511
550
  // Learned from the sheet — what followed this activity last time, after how long.
512
551
  const lastFollower = $.const(East.function([Ctx], Proposals, ($, ctx) => {
513
552
  const empty = $.const([], Proposals);
514
- const dated = $.let(ctx.rows.filter((_$, r) => r.start.hasTag("some").and(() => r.activity.equal("").not())));
515
- const n = $.let(dated.length());
516
- const upper = $.let(n.greater(1n).ifElse((_$) => n.subtract(1n), (_$) => 0n));
517
- const pairs = $.let(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.equal(ctx.row.activity).and(() => dated.get(i.add(1n)).activity.equal(ctx.row.activity).not())));
518
- return pairs.length().equal(0n).or(() => ctx.row.activity.equal("")).or(() => ctx.row.start.hasTag("some").not()).ifElse((_$) => empty, ($2) => {
519
- const i = $2.let(pairs.get(pairs.length().subtract(1n)));
520
- const from = $2.let(dated.get(i));
521
- const next = $2.let(dated.get(i.add(1n)));
522
- const gapMs = $2.let(next.start.unwrap("some").toEpochMilliseconds().subtract(from.start.unwrap("some").toEpochMilliseconds()));
523
- const at = $2.let(ctx.row.start.unwrap("some").addMilliseconds(gapMs));
524
- return $2.const([{
525
- patch: Sheet.patch(PlanRowType, { activity: next.activity, start: some(at), qty: next.qty, notes: next.notes }),
526
- meta: East.str `${next.activity} followed ${ctx.row.activity} last time · +${gapMs.divide(86400000n)}d`,
527
- }], Proposals);
528
- });
553
+ return ctx.row.activity.hasTag("value")
554
+ .and(() => ctx.row.start.hasTag("value"))
555
+ .and(() => ctx.row.start.unwrap("value").hasTag("some")).ifElse(($) => {
556
+ const activity = $.const(ctx.row.activity.unwrap("value"));
557
+ const start = $.const(ctx.row.start.unwrap("value").unwrap("some"));
558
+ const dated = $.const(ctx.rows.filter((_$, r) => r.start.hasTag("value")
559
+ .and(() => r.start.unwrap("value").hasTag("some"))
560
+ .and(() => r.activity.hasTag("value"))));
561
+ const upper = $.const(dated.size().greater(1n).ifElse(() => dated.size().subtract(1n), () => 0n));
562
+ const pairs = $.const(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.unwrap("value").equal(activity)
563
+ .and(() => dated.get(i.add(1n)).activity.unwrap("value").equal(activity).not())));
564
+ return pairs.size().equal(0n).ifElse(() => empty, ($) => {
565
+ const index = $.const(pairs.get(pairs.size().subtract(1n)));
566
+ const from = $.const(dated.get(index));
567
+ const next = $.const(dated.get(index.add(1n)));
568
+ const gap = $.const(next.start.unwrap("value").unwrap("some").toEpochMilliseconds()
569
+ .subtract(from.start.unwrap("value").unwrap("some").toEpochMilliseconds()));
570
+ return $.const([{ patch: Sheet.patch(PlanRowType, { activity: next.activity.unwrap("value"), start: some(start.addMilliseconds(gap)) }),
571
+ meta: "follower learned from supplied activities and dates" }], Proposals);
572
+ });
573
+ }, () => empty);
529
574
  }));
530
575
  // A model — an ASYNC proposer; the strip shows a pending chip and a newer context cancels the wait.
531
- const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, (_$, ctx) => planRecommend(ctx)));
532
- // Every take carries its provenance through `onEdit` — the log measures copilot uptake.
533
- const onEdit = $.const(East.function([Sheet.Types.Edit(PlanRowType)], NullType, ($, e) => {
534
- const line = $.let(e.match({
535
- commit: (_$, c) => East.str `${c.source.getTag()} · ${c.rowId} · ${c.key}`,
536
- insert: (_$, i) => East.str `${i.source.getTag()} · new row ${i.row.id}`,
537
- remove: (_$, r) => East.str `removed ${r.rowIds.length()}`,
538
- }));
539
- $(log.write(log.read().concat([line])));
576
+ const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, ($, ctx) => {
577
+ const result = $.let([], Proposals);
578
+ $.try(($) => { $.assign(result, planRecommend(ctx)); }).catch(($, message) => {
579
+ // The standalone showcase has no model backend. Other failures
580
+ // still reach the Sheet's provider diagnostic.
581
+ $.if(message.notEqual("Platform function 'sheet_plan_recommend' is not available"), ($) => { $.error(message); });
582
+ });
583
+ return result;
584
+ }));
585
+ // Every gesture logs once, including incomplete drafts and undo.
586
+ const onPatch = $.const(East.function([Sheet.Types.PatchEvent(PlanRowType)], NullType, ($, e) => {
587
+ $(log.write(log.read().concat([East.str `${e.origin.getTag()} · ${e.label} · ${e.draftChanges.length()} entries`])));
540
588
  }));
541
589
  const entries = $.let(log.read());
542
- return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: plan.read(), id: "id", driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), columns: {
590
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: plan, id: "id", driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), columns: {
543
591
  start: Sheet.column.date(PlanRowType, { header: "Start", sub: "sequence", width: "96px", fill: [nextSlot] }),
544
592
  end: Sheet.column.date(PlanRowType, { header: "End", sub: "derive", width: "96px", base: "start", fill: [endFromStart] }),
545
593
  activity: Sheet.column.lookup(PlanRowType, { header: "Activity", sub: "activity register", width: "214px" }),
546
594
  qty: Sheet.column.quantity(PlanRowType, ActivityType, { header: "Qty", sub: "history · default", width: "112px", uom: d => d.uom, fill: [lastQuantity, shiftQuantity] }),
547
595
  notes: Sheet.column.text(PlanRowType, { header: "Notes", width: "260px" }),
548
- }, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty"], propose: [roughingFollowUps, modelProposals, lastFollower] }, onEdit: onEdit, onUpdate: plan.write, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `COPILOT LOG · ${entries.length()} edits · ${entries.filter((_$, l) => l.startsWith("typed").not()).length()} from the copilot` })] }));
596
+ }, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty"], propose: [roughingFollowUps, modelProposals, lastFollower] }, onPatch: onPatch, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(PlanRowType), () => Sheet.patch(PlanRowType, {
597
+ notes: "", stations: { from: [], to: [] }, fromSite: "", toSite: "", orderCode: "", status: "PLANNED",
598
+ })), onUpdate: plan.write, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `COPILOT LOG · ${entries.length()} edits · ${entries.filter((_$, l) => l.startsWith("typed").not()).length()} from the copilot` })] }));
549
599
  } }))),
550
600
  inputs: [],
551
601
  });
@@ -592,9 +642,9 @@ export const sheetLens = example({
592
642
  }));
593
643
  const slice = $.let(Slice.bind([JobType], "sheet_lens_slice", cfg, Slice.state(), rows, none));
594
644
  const views = $.let(State.bind([ArrayType(Sheet.Types.View)], "sheet_lens_views", [
595
- { id: "painting", name: "PAINTING", narrowing: Slice.state({ search: some("painting") }), context: 1n, reveals: [] },
596
- { id: "lathes", name: "LATHES", narrowing: Slice.state({ search: some("lathe") }), context: 0n, reveals: [] },
597
- { id: "urgent", name: "URGENT", narrowing: Slice.state({ search: some("urgent") }), context: 0n, reveals: [] },
645
+ { id: "painting", name: "PAINTING", narrowing: Slice.state({ search: some("painting") }), context: 1n, reveals: [], folds: new Map() },
646
+ { id: "lathes", name: "LATHES", narrowing: Slice.state({ search: some("lathe") }), context: 0n, reveals: [], folds: new Map() },
647
+ { id: "urgent", name: "URGENT", narrowing: Slice.state({ search: some("urgent") }), context: 0n, reveals: [], folds: new Map() },
598
648
  ]));
599
649
  return (_jsx(Sheet, { data: rows, id: "id", registers: {
600
650
  stations: Sheet.register.concat([
@@ -614,13 +664,13 @@ export const sheetLens = example({
614
664
  inputs: [],
615
665
  });
616
666
  /**
617
- * Write-back (§3.7) — the raw `onEdit` channel observing every commit,
618
- * insert and remove with its provenance, beside `onUpdate` writing the whole
619
- * collection; a staged bind holds changes until they are applied.
667
+ * Write-back (§3.7) — onPatch journals one complete gesture, while the
668
+ * live onUpdate adapter checks and applies the composed batch once. The
669
+ * Sheet holds local drafts until Apply changes is pressed.
620
670
  */
621
671
  export const sheetWriteBack = example({
622
- keywords: ["Sheet", "Root", "onEdit", "onUpdate", "write-back", "commit", "insert", "remove", "source", "provenance", "Edit", "staged", "newRowId", "Reactive", "State"],
623
- description: "Write-back — the raw onEdit event channel logging commit / insert / remove with provenance, beside onUpdate writing the whole collection, and a host-minted row id",
672
+ keywords: ["Sheet", "Root", "onPatch", "onUpdate", "write-back", "commit", "insert", "remove", "source", "provenance", "PatchEvent", "staged", "newRowId", "Reactive", "State"],
673
+ description: "Write-back — one onPatch event per gesture with provenance and readiness, beside onUpdate applying a checked batch to the live collection",
624
674
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
625
675
  const JobType = StructType({ id: StringType, task: StringType, qty: OptionType(FloatType), createdBy: StringType });
626
676
  const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_writeback_rows", [
@@ -629,15 +679,10 @@ export const sheetWriteBack = example({
629
679
  ]));
630
680
  const log = $.let(State.bind([ArrayType(StringType)], "sheet_writeback_log", []));
631
681
  const counter = $.let(State.bind([IntegerType], "sheet_writeback_counter", 3n));
632
- // The raw channel — the row AFTER the commit, typed over the host's
633
- // struct: `createdBy` has no column, and still reads its real value.
634
- const onEdit = $.const(East.function([Sheet.Types.Edit(JobType)], NullType, ($, e) => {
635
- const line = $.let(e.match({
636
- commit: (_$, c) => East.str `commit ${c.rowId}.${c.key} (${c.source.getTag()}) · ${c.row.task} · by ${c.row.createdBy}`,
637
- insert: (_$, i) => East.str `insert ${i.row.id} after ${i.afterRowId.match({ some: (_$2, a) => a, none: (_$2) => "end" })}`,
638
- remove: (_$, r) => East.str `remove ${r.rowIds.stringJoin(", ")}`,
639
- }));
640
- $(log.write(log.read().concat([line])));
682
+ // Draft patches include hidden fields without inventing their values.
683
+ // This observer journals gestures; the live adapter applies ready batches.
684
+ const onPatch = $.const(East.function([Sheet.Types.PatchEvent(JobType)], NullType, ($, e) => {
685
+ $(log.write(log.read().concat([East.str `${e.origin.getTag()} · ${e.label} · ${e.readiness.getTag()}`])));
641
686
  }));
642
687
  // The host mints ids for inserted rows.
643
688
  const newRowId = $.const(East.function([], StringType, ($) => {
@@ -646,10 +691,48 @@ export const sheetWriteBack = example({
646
691
  return East.str `j${n}`;
647
692
  }));
648
693
  const entries = $.let(log.read());
649
- return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: jobs.read(), id: "id", columns: {
694
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: jobs, id: "id", columns: {
650
695
  task: Sheet.column.text(JobType, { header: "Task", width: "200px" }),
651
696
  qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px" }),
652
- }, onEdit: onEdit, onUpdate: jobs.write, newRowId: newRowId, blanks: 6n }), _jsx(Text.MonoLabel, { children: East.str `EDITS · ${entries.length()}` }), entries.map((_$, l) => _jsx(Text, { textStyle: "caption", color: "fg.muted", children: l }))] }));
697
+ }, onPatch: onPatch, onUpdate: jobs.write, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { createdBy: "planner" })), newRowId: newRowId, blanks: 6n }), _jsx(Text.MonoLabel, { children: East.str `EDITS · ${entries.length()}` }), entries.map((_$, l) => _jsx(Text, { textStyle: "caption", color: "fg.muted", children: l }))] }));
698
+ } }))),
699
+ inputs: [],
700
+ });
701
+ /**
702
+ * Grouped editing — named work packages with ordered child rows. Explicit
703
+ * constructors supply hidden metadata; the schema determines completeness.
704
+ */
705
+ export const sheetGrouped = example({
706
+ keywords: ["Sheet", "group", "grouped", "children", "newGroup", "newRow", "Patch", "defaults", "completeness", "fold", "undo", "redo", "onUpdate", "Reactive", "State"],
707
+ description: "Grouped work packages — edit names and child rows, fold groups, create drafts with explicit defaults, then apply or undo the batch",
708
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
709
+ const JobType = StructType({ task: StringType, qty: OptionType(FloatType), notes: StringType, createdBy: StringType });
710
+ const PackageType = StructType({ id: StringType, name: StringType, owner: StringType, jobs: ArrayType(JobType) });
711
+ const packages = $.let(State.bind([ArrayType(PackageType)], "sheet_grouped_packages", [
712
+ { id: "roughing", name: "P-40 · Roughing", owner: "planner", jobs: [
713
+ { task: "Machine blanks", qty: some(1200.0), notes: "Four CNC lathes", createdBy: "planner" },
714
+ { task: "Inspect lots", qty: some(4.0), notes: "Check before finishing", createdBy: "planner" },
715
+ ] },
716
+ { id: "finishing", name: "P-40 · Finishing", owner: "planner", jobs: [
717
+ { task: "Finish housings", qty: some(1200.0), notes: "After inspection", createdBy: "planner" },
718
+ { task: "Pack for assembly", qty: some(100.0), notes: "Twelve per carton", createdBy: "planner" },
719
+ ] },
720
+ ]));
721
+ const applied = $.let(packages.read());
722
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Edit a package name or task. Use the gutter + to insert a task, or the stacked-rows + button to create a package at a group boundary. Apply changes saves the batch; Undo and Redo retain each gesture." }), _jsx(Sheet, { data: packages, id: "id", group: Sheet.group(PackageType, "jobs", { title: "name" }), ready: { group: East.function([Sheet.Types.DraftGroup(PackageType, "jobs")], Sheet.Types.Readiness, ($, group) => {
723
+ $.if(group.name.hasTag("value").and(() => group.name.unwrap("value").length().equal(0n)), $ => {
724
+ $.return(East.value(variant("incomplete", [{ field: "name", message: "Name the work package" }]), Sheet.Types.Readiness));
725
+ });
726
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
727
+ }) }, columns: {
728
+ task: Sheet.column.text(JobType, { header: "Task", width: "240px" }),
729
+ qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px" }),
730
+ notes: Sheet.column.text(JobType, { header: "Notes", width: "300px" }),
731
+ }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, {
732
+ notes: "", createdBy: "planner",
733
+ })), newGroup: East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(PackageType), () => Sheet.patch(PackageType, {
734
+ owner: "planner", jobs: [],
735
+ })), onUpdate: packages.write, style: { height: "440px" } }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${applied.length()} packages · ${applied.map((_$, p) => p.jobs.length()).sum()} tasks` })] }));
653
736
  } }))),
654
737
  inputs: [],
655
738
  });
@@ -657,11 +740,11 @@ export const sheetWriteBack = example({
657
740
  * The paged arm (§3.13) — the same tag over a windowed source: windows land
658
741
  * as the planner scrolls, the footer carries the transport line, the blank
659
742
  * tail appears once the source is exhausted, search is a key search over
660
- * `seek`, and edits go through `onEdit` to the host's journal.
743
+ * `seek`, and the immutable snapshot is read-only. A mutable source needs an authoritative onApply callback.
661
744
  */
662
745
  export const sheetPaged = example({
663
- keywords: ["Sheet", "Root", "paged", "Paged", "of", "window", "page", "seek", "key search", "transport", "partial", "exhausted", "onEdit", "journal", "row-source", "Reactive", "State"],
664
- description: "A paged sheet — the same tag over a Paged.of source keyed by id: windows land on scroll, the footer counts elements, key search seeks the source, and edits go to the host's journal through onEdit",
746
+ keywords: ["Sheet", "Root", "paged", "Paged", "of", "window", "page", "seek", "key search", "transport", "partial", "exhausted", "onPatch", "journal", "row-source", "Reactive", "State"],
747
+ description: "A paged sheet — the same tag over a Paged.of source keyed by id: windows land on scroll, the footer counts elements, key search seeks the source, and its immutable snapshot stays read-only",
665
748
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
666
749
  const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType) });
667
750
  // Six hundred rows behind a source that windows them — keyed by id,
@@ -673,16 +756,11 @@ export const sheetPaged = example({
673
756
  qty: some(i.multiply(15n).toFloat().add(180.0)),
674
757
  })), ArrayType(JobType));
675
758
  const source = $.const(Paged.of("sheet_paged_jobs", rows, { key: r => r.id })); // Data.bindPaged(planInput) in e3-ui
676
- const edits = $.let(State.bind([ArrayType(Sheet.Types.Edit(JobType))], "sheet_paged_edits", []));
677
- const applyEdit = $.const(East.function([Sheet.Types.Edit(JobType)], NullType, ($, e) => {
678
- $(edits.write(edits.read().concat([e]))); // the host's edit journal, replayed server-side
679
- }));
680
- const journal = $.let(edits.read());
681
759
  return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: source, id: "id", columns: {
682
760
  start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
683
761
  task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
684
762
  qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
685
- }, onEdit: applyEdit, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `JOURNAL · ${journal.length()} edits` })] }));
763
+ }, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: "Immutable snapshot \u00B7 key search and paging" })] }));
686
764
  } }))),
687
765
  inputs: [],
688
766
  });
@@ -695,7 +773,7 @@ export const sheetStress = example({
695
773
  description: "Two thousand rows in one sheet — virtualised rows, typed columns, and a history fill that reads the whole resident sheet",
696
774
  fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
697
775
  const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType), notes: StringType });
698
- const Ctx = Sheet.Types.Context(JobType);
776
+ const Ctx = Sheet.Types.DraftContext(JobType);
699
777
  const tasks = $.const(["Machining", "Painting", "Packaging", "Changeover", "Maintenance", "Receiving", "Shipping"], ArrayType(StringType));
700
778
  const rows = $.let(State.bind([ArrayType(JobType)], "sheet_stress_rows", East.Array.generate(2000n, JobType, (_$, i) => ({
701
779
  id: East.str `S${i}`,
@@ -707,18 +785,282 @@ export const sheetStress = example({
707
785
  const FloatFill = OptionType(Sheet.Types.Fill(FloatType));
708
786
  const lastQty = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
709
787
  const noFill = $.const(none, FloatFill);
710
- const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.task.equal(ctx.row.task).and(() => r.qty.hasTag("some"))));
788
+ const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.task.hasTag("value").and(() => ctx.row.task.hasTag("value")).and(() => r.task.unwrap("value").equal(ctx.row.task.unwrap("value"))).and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
711
789
  return similar.length().equal(0n).ifElse((_$) => noFill, ($2) => {
712
790
  const r = $2.let(similar.get(similar.length().subtract(1n)));
713
- return East.value(some({ value: r.qty.unwrap("some"), meta: East.str `like ${r.id}` }), FloatFill);
791
+ return East.value(some({ value: r.qty.unwrap("value").unwrap("some"), meta: East.str `like ${r.id.match({ value: (_$, id) => id }, () => "previous row")}` }), FloatFill);
714
792
  });
715
793
  }));
716
- return (_jsx(Sheet, { data: rows.read(), id: "id", columns: {
794
+ return (_jsx(Sheet, { data: rows, id: "id", columns: {
717
795
  start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
718
796
  task: Sheet.column.text(JobType, { header: "Task", width: "160px" }),
719
797
  qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }), fill: [lastQty] }),
720
798
  notes: Sheet.column.text(JobType, { header: "Notes", width: "240px" }),
721
- }, onUpdate: rows.write, footer: [{ text: "2000 rows · virtualised" }], style: { height: "480px" } }));
799
+ }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "" })), onUpdate: rows.write, footer: [{ text: "2000 rows · virtualised" }], style: { height: "480px" } }));
800
+ } }))),
801
+ inputs: [],
802
+ });
803
+ /** Schema completeness plus an optional business rule, evaluated on typed drafts. */
804
+ export const sheetReadiness = example({
805
+ keywords: ["Sheet", "ready", "row", "Readiness", "Draft", "DraftContext", "completeness", "validation", "Apply", "onUpdate", "Reactive", "State"],
806
+ description: "Draft readiness — quantities must be positive before Apply; missing required values and invalid text are checked automatically",
807
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
808
+ const JobType = StructType({ id: StringType, task: StringType, qty: IntegerType, note: OptionType(StringType), createdBy: StringType });
809
+ const jobs = $.const(State.bind([ArrayType(JobType)], "sheet_readiness_jobs", [
810
+ { id: "inspection", task: "Inspect lots", qty: 4n, note: none, createdBy: "planner" },
811
+ ]));
812
+ const readyRow = $.const(East.function([Sheet.Types.Draft(JobType), Sheet.Types.DraftContext(JobType)], Sheet.Types.Readiness, ($, row) => {
813
+ $.if(row.qty.hasTag("value").and(() => row.qty.unwrap("value").lessEqual(0n)), $ => {
814
+ $.return(East.value(variant("incomplete", [{ field: "qty", message: "Quantity must be positive" }]), Sheet.Types.Readiness));
815
+ });
816
+ return East.value(variant("ready", null), Sheet.Types.Readiness);
817
+ }));
818
+ const saved = $.const(jobs.read());
819
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Set Qty to 0: Apply stays disabled. Enter a positive quantity to save. Required fields are checked automatically; Notes may stay blank." }), _jsx(Sheet, { data: jobs, id: "id", columns: { task: Sheet.column.text(JobType), qty: Sheet.column.integer(JobType), note: Sheet.column.text(JobType) }, ready: { row: readyRow }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { createdBy: "planner" })), onUpdate: jobs.write, blanks: 2n }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.length()} rows · ${saved.map((_$, row) => row.qty).sum()} total quantity` })] }));
820
+ } }))),
821
+ inputs: [],
822
+ });
823
+ /** Row insertion and capability limits over a live collection. */
824
+ export const sheetInsertion = example({
825
+ keywords: ["Sheet", "edits", "insertRows", "removeRows", "insert", "newRow", "before", "after", "Undo", "State"],
826
+ description: "Insert rows with the gutter buttons, selection strip or Alt+Insert; existing-row deletion is disabled while new drafts can be discarded",
827
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
828
+ const RowType = StructType({ id: StringType, task: StringType, qty: IntegerType, createdBy: StringType });
829
+ const jobs = $.const(State.bind([ArrayType(RowType)], "sheet_insertion_jobs", [
830
+ { id: "rough", task: "Rough machining", qty: 120n, createdBy: "planner" },
831
+ { id: "inspect", task: "Inspect lots", qty: 4n, createdBy: "planner" },
832
+ { id: "finish", task: "Finish housings", qty: 120n, createdBy: "planner" },
833
+ ]));
834
+ const saved = $.const(jobs.read());
835
+ return _jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Hover or focus a gutter to insert before a row. Select a row marker for Insert above/below, or use Alt+Insert and Alt+Shift+Insert. Name the draft and Apply to save it." }), _jsx(Sheet, { data: jobs, id: "id", columns: { task: Sheet.column.text(RowType), qty: Sheet.column.integer(RowType) }, edits: { insertRows: true, removeRows: false, moveRows: "none" }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(RowType), () => Sheet.patch(RowType, { qty: 1n, createdBy: "planner" })), onUpdate: jobs.write, blanks: 2n }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.map((_$, row) => row.task).stringJoin(" → ")}` })] });
836
+ } }))),
837
+ inputs: [],
838
+ });
839
+ /**
840
+ * Sub rows (#844) — read-only rows under each line that share none of its
841
+ * columns. `Sheet.subRows` is keyed like `columns`: each key names an array
842
+ * field of the line type and maps one element to a sub row — a struct source
843
+ * with chips and labelled facets (a `none` facet drops out), and a variant
844
+ * source matched arm by arm. The group's noun is the host's word.
845
+ */
846
+ export const sheetSubRows = example({
847
+ keywords: ["Sheet", "subRows", "subRow", "sub rows", "SubRow", "Facet", "chips", "facets", "lead", "detail", "group", "noun", "variant", "match", "read-only", "Reactive", "State"],
848
+ description: "Sub rows under each line — operations with chips and facets, bookings matched from a variant — declared per array field like columns, under groups the host calls orders",
849
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
850
+ const OperationType = StructType({
851
+ id: StringType, code: StringType, name: StringType, materials: ArrayType(StringType),
852
+ station: OptionType(StringType), by: OptionType(StringType),
853
+ });
854
+ const BookingType = VariantType({
855
+ space: StructType({ area: StringType, units: IntegerType }),
856
+ labour: StructType({ team: StringType, people: IntegerType, hours: FloatType }),
857
+ equipment: StructType({ resource: StringType }),
858
+ });
859
+ const JobType = StructType({
860
+ task: StringType, qty: OptionType(FloatType), notes: StringType,
861
+ operations: ArrayType(OperationType), // no column — shown as sub rows
862
+ bookings: ArrayType(BookingType), // no column — shown as sub rows
863
+ });
864
+ const OrderType = StructType({ id: StringType, name: StringType, jobs: ArrayType(JobType) });
865
+ const orders = $.let(State.bind([ArrayType(OrderType)], "sheet_subrows_orders", [
866
+ { id: "wo-1042", name: "WO-1042 · Frames", jobs: [
867
+ { task: "Assemble frames", qty: some(40.0), notes: "Two benches", operations: [
868
+ { id: "WO-1042-1", code: "CUT", name: "Cut rails to length", materials: ["Rail stock × 80"], station: some("Saw 2"), by: none },
869
+ { id: "WO-1042-2", code: "ASM", name: "Assemble frame", materials: ["M6 bolts × 12", "Frame kit"], station: some("Bench 7"), by: some("Assembly") },
870
+ ], bookings: [
871
+ variant("labour", { team: "Assembly", people: 2n, hours: 12.0 }),
872
+ variant("equipment", { resource: "Torque driver" }),
873
+ ] },
874
+ { task: "Inspect frames", qty: some(40.0), notes: "", operations: [], bookings: [
875
+ variant("space", { area: "Test bay", units: 2n }),
876
+ ] },
877
+ ] },
878
+ { id: "wo-1043", name: "WO-1043 · Housings", jobs: [
879
+ { task: "Paint housings", qty: some(250.0), notes: "Primer first", operations: [
880
+ { id: "WO-1043-1", code: "PNT", name: "Prime and paint", materials: ["Primer", "Topcoat"], station: none, by: none },
881
+ ], bookings: [] },
882
+ ] },
883
+ ]));
884
+ return (_jsx(Sheet, { data: orders, id: "id", group: Sheet.group(OrderType, "jobs", { title: "name", noun: { singular: "order", plural: "orders" } }), columns: {
885
+ task: Sheet.column.text(JobType, { header: "Task", width: "220px" }),
886
+ qty: Sheet.column.quantity(JobType, { header: "Qty", width: "96px" }),
887
+ notes: Sheet.column.text(JobType, { header: "Notes", width: "240px" }),
888
+ }, subRows: Sheet.subRows(JobType, {
889
+ operations: (op) => Sheet.subRow({
890
+ code: op.code,
891
+ name: op.name,
892
+ chips: op.materials,
893
+ facets: { station: op.station, by: op.by }, // a none drops out
894
+ id: op.id,
895
+ }),
896
+ bookings: (b) => b.match({
897
+ space: (_$2, s) => Sheet.subRow({ code: "CLAIM", name: East.str `${s.area} · ${s.units} units` }),
898
+ labour: (_$2, l) => Sheet.subRow({ code: "LABOUR", name: East.str `${l.team} · ${l.people} people · ${l.hours} person-hours` }),
899
+ equipment: (_$2, e) => Sheet.subRow({ code: "EQUIPMENT", name: e.resource }),
900
+ }),
901
+ }), newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "", operations: [], bookings: [] })), newGroup: East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(OrderType), () => Sheet.patch(OrderType, { jobs: [] })), onUpdate: orders.write, style: { height: "420px" } }));
902
+ } }))),
903
+ inputs: [],
904
+ });
905
+ /**
906
+ * Column rules (#844) — what a column offers, how deep a date reads, and
907
+ * what a cell says beyond its value: an `options` rule narrows the status
908
+ * menu until the row has an order code; the start date reads at the row's
909
+ * own `level` and prints the `actual` start once one is recorded; the status
910
+ * cell's `detail` is the order code; machines are a `ranged` kind, so runs
911
+ * of consecutive codes are offered and printed as one range.
912
+ */
913
+ export const sheetRules = example({
914
+ keywords: ["Sheet", "options", "level", "actual", "detail", "ranged", "DateLevel", "week", "day", "range", "time", "enum", "date", "set", "members", "rule", "Reactive", "State"],
915
+ description: "Column rules — an options rule narrowing an enum's menu, a date read at each row's level with its actual start, a status detail, and a ranged machine kind",
916
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
917
+ const JobType = StructType({
918
+ id: StringType, task: StringType, start: OptionType(DateTimeType), level: Sheet.Types.DateLevel,
919
+ started: OptionType(DateTimeType), status: StringType, orderCode: StringType, machines: Sheet.Types.Link,
920
+ });
921
+ const Ctx = Sheet.Types.DraftContext(JobType);
922
+ const StatusType = StructType({ word: StringType, tone: Status.Types.Value });
923
+ const statuses = $.const([
924
+ { word: "PLANNED", tone: variant("neutral", null) }, { word: "RELEASED", tone: variant("info", null) },
925
+ { word: "IN PROGRESS", tone: variant("warning", null) }, { word: "COMPLETE", tone: variant("success", null) },
926
+ ], ArrayType(StatusType));
927
+ const machines = $.const(["M2140", "M2141", "M2142", "M2143", "M3210", "M3211"], ArrayType(StringType));
928
+ const noMembers = $.const([], ArrayType(Sheet.Types.Member));
929
+ const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_rules_jobs", [
930
+ { id: "j1", task: "Rough blanks", start: some(new Date("2026-02-16T00:00:00Z")), level: variant("week", null), started: none, status: "PLANNED", orderCode: "", machines: { from: [], to: [] } },
931
+ { id: "j2", task: "Finish blanks", start: some(new Date("2026-02-18T00:00:00Z")), level: variant("day", null), started: some(new Date("2026-02-19T07:30:00Z")), status: "IN PROGRESS", orderCode: "WO-26001", machines: { from: [], to: [variant("range", { from: "M2140", to: "M2143" })] } },
932
+ { id: "j3", task: "Changeover", start: some(new Date("2026-02-19T14:00:00Z")), level: variant("time", null), started: none, status: "RELEASED", orderCode: "WO-26002", machines: { from: [], to: [] } },
933
+ ]));
934
+ // Until a row has an order code it may only be planned or released.
935
+ const statusOptions = $.const(East.function([Ctx], OptionType(ArrayType(StringType)), ($, ctx) => {
936
+ const whole = $.const(none, OptionType(ArrayType(StringType)));
937
+ const early = $.const(some(["PLANNED", "RELEASED"]), OptionType(ArrayType(StringType)));
938
+ return ctx.row.orderCode.match({ value: (_$2, code) => code.length().equal(0n).ifElse(() => early, () => whole) }, () => early);
939
+ }));
940
+ return (_jsx(Sheet, { data: jobs, id: "id", registers: {
941
+ statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
942
+ machines: Sheet.register.members(machines, { kind: "machine", key: m => m, label: m => m }),
943
+ }, columns: {
944
+ task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
945
+ start: Sheet.column.date(JobType, { header: "Start", sub: "at the row's level", width: "168px", level: r => r.level, actual: r => r.started }),
946
+ status: Sheet.column.enum(JobType, "statuses", { header: "Status", width: "132px", options: statusOptions, detail: r => r.orderCode }),
947
+ machines: Sheet.column.set(JobType, "machines", { header: "Machines", sub: "M2140-43 · a run", width: "220px",
948
+ members: [{ kind: "machine", identified: true, ranged: true }] }),
949
+ }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, {
950
+ level: variant("day", null), started: none, orderCode: "", machines: { from: noMembers, to: noMembers },
951
+ })), onUpdate: jobs.write, blanks: 4n }));
952
+ } }))),
953
+ inputs: [],
954
+ });
955
+ /**
956
+ * Registers and the driver (§3.3) — a register is the grammar's vocabulary,
957
+ * projected from the host's rows by accessors: machines from an Array with a
958
+ * meta and a parent, lines from a Dict whose key rides as the accessors'
959
+ * second argument, and the machine families read off the same machines (the
960
+ * lathes fold into one member), joined into the one register the Machines
961
+ * column resolves against. The driver is the `lookup` column whose member
962
+ * decides what the row does: its row gives the quantity its unit and the End
963
+ * fill its duration.
964
+ */
965
+ export const sheetRegisters = example({
966
+ keywords: ["Sheet", "register", "members", "concat", "driver", "lookup", "set", "kind", "key", "label", "aliases", "meta", "parent", "Dict", "fold", "duplicate", "uom", "fill", "DraftContext", "Reactive", "State"],
967
+ description: "Registers and the driver — members projected from Array and Dict rows (meta, parent, aliases, duplicate keys folded) and joined into one register a set column resolves against, beside a driver whose row gives each quantity its unit and a fill its duration",
968
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
969
+ const JobType = StructType({
970
+ id: StringType, activity: StringType, start: OptionType(DateTimeType), end: OptionType(DateTimeType),
971
+ qty: OptionType(FloatType), machines: Sheet.Types.Link,
972
+ });
973
+ const ActivityType = StructType({ name: StringType, uom: StringType, days: IntegerType });
974
+ const MachineType = StructType({ code: StringType, family: StringType, line: StringType });
975
+ const LineType = StructType({ name: StringType, aliases: ArrayType(StringType) });
976
+ const activities = $.const([
977
+ { name: "Machining", uom: "pcs", days: 4n },
978
+ { name: "Inspection", uom: "lots", days: 1n },
979
+ ], ArrayType(ActivityType));
980
+ const machines = $.const([
981
+ { code: "M2140", family: "CNC lathe", line: "L2" },
982
+ { code: "M2141", family: "CNC lathe", line: "L2" },
983
+ { code: "M3210", family: "5-axis mill", line: "L3" },
984
+ ], ArrayType(MachineType));
985
+ const lines = $.const(new Map([
986
+ ["L2", { name: "Line 2", aliases: ["l2", "line 2"] }],
987
+ ["L3", { name: "Line 3", aliases: ["l3", "line 3"] }],
988
+ ]), DictType(StringType, LineType));
989
+ const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_registers_jobs", [
990
+ { id: "j1", activity: "Machining", start: some(new Date("2026-02-16T00:00:00Z")), end: none, qty: some(1200.0),
991
+ machines: { from: [], to: [variant("counted", { n: 2n, key: "CNC lathe" })] } },
992
+ ]));
993
+ // The driver's row reaches a fill typed: End = Start + the activity's days.
994
+ const DateFill = OptionType(Sheet.Types.Fill(DateTimeType));
995
+ const endFromStart = $.const(East.function([Sheet.Types.DraftContext(JobType, ActivityType)], DateFill, ($, ctx) => {
996
+ const noFill = $.const(none, DateFill);
997
+ return ctx.row.start.match({
998
+ value: (_$, supplied) => supplied.match({
999
+ none: () => noFill,
1000
+ some: (_$, start) => ctx.driver.match({
1001
+ none: () => noFill,
1002
+ some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
1003
+ }),
1004
+ }),
1005
+ }, () => noFill);
1006
+ }));
1007
+ const newJob = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { machines: { from: [], to: [] } })));
1008
+ return (_jsx(Sheet, { data: jobs, id: "id", driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name, meta: a => some(a.uom) }), registers: {
1009
+ machines: Sheet.register.concat([
1010
+ Sheet.register.members(machines, { kind: "machine", key: m => m.code, label: m => m.code,
1011
+ meta: m => some(m.family), parent: m => some(m.line) }),
1012
+ // A Dict's key rides as the accessors' second argument.
1013
+ Sheet.register.members(lines, { kind: "line", key: (_l, code) => code, label: l => l.name, aliases: l => l.aliases }),
1014
+ // Both lathes name one family — duplicate keys fold, the first wins.
1015
+ Sheet.register.members(machines, { kind: "family", key: m => m.family, label: m => m.family, meta: _m => some("family") }),
1016
+ ]),
1017
+ }, columns: {
1018
+ activity: Sheet.column.lookup(JobType, { header: "Activity", width: "140px" }),
1019
+ start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
1020
+ end: Sheet.column.date(JobType, { header: "End", sub: "start + days", width: "96px", base: "start", fill: [endFromStart] }),
1021
+ qty: Sheet.column.quantity(JobType, ActivityType, { header: "Qty", sub: "uom per activity", width: "112px", uom: d => d.uom }),
1022
+ machines: Sheet.column.set(JobType, "machines", { header: "Machines", sub: "M2140 · 2 x lathe · line 2", width: "240px",
1023
+ members: [{ kind: "machine", identified: true }, { kind: "line", countable: true, resolvesTo: "machine" },
1024
+ { kind: "family", countable: true, resolvesTo: "machine" }] }),
1025
+ }, newRow: newJob, onUpdate: jobs.write }));
1026
+ } }))),
1027
+ inputs: [],
1028
+ });
1029
+ /**
1030
+ * Loose rows between the groups (#846) — the source holds entries
1031
+ * `Sheet.Types.Entry(PackageType, "tasks")`: a work package with its tasks,
1032
+ * or a task that belongs to no package. A loose task draws as a plain row,
1033
+ * numbered in the packages' sequence; the seam above a package's band, or
1034
+ * beside a loose task, inserts a loose task, and a task's seam inserts a task
1035
+ * into its package. `id` names a field of both types: a loose task is an
1036
+ * entry, identified like a package.
1037
+ */
1038
+ export const sheetLoose = example({
1039
+ keywords: ["Sheet", "group", "grouped", "loose", "ungrouped", "Entry", "Types.Entry", "variant", "entries", "insert", "newRow", "newGroup", "noun", "onUpdate", "Reactive", "State"],
1040
+ description: "Loose rows between the groups — entries of 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",
1041
+ fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
1042
+ const TaskType = StructType({ id: StringType, task: StringType, qty: OptionType(FloatType), notes: StringType });
1043
+ const PackageType = StructType({ id: StringType, name: StringType, tasks: ArrayType(TaskType) });
1044
+ const Entry = Sheet.Types.Entry(PackageType, "tasks");
1045
+ const entries = $.let(State.bind([ArrayType(Entry)], "sheet_loose_entries", [
1046
+ variant("row", { id: "brief", task: "Review the drawings", qty: none, notes: "Before any machining" }),
1047
+ variant("group", { id: "roughing", name: "P-40 · Roughing", tasks: [
1048
+ { id: "rough-1", task: "Machine blanks", qty: some(1200.0), notes: "Four CNC lathes" },
1049
+ { id: "rough-2", task: "Inspect lots", qty: some(4.0), notes: "Check before finishing" },
1050
+ ] }),
1051
+ variant("row", { id: "handover", task: "Hand over to finishing", qty: none, notes: "" }),
1052
+ variant("group", { id: "finishing", name: "P-40 · Finishing", tasks: [
1053
+ { id: "finish-1", task: "Finish housings", qty: some(1200.0), notes: "After inspection" },
1054
+ ] }),
1055
+ ]));
1056
+ const saved = $.let(entries.read());
1057
+ const newTask = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(TaskType), () => Sheet.patch(TaskType, { notes: "" })));
1058
+ const newPackage = $.const(East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(PackageType), () => Sheet.patch(PackageType, { tasks: [] })));
1059
+ return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Hover the seam above a package, or beside a loose task, to insert a loose task; a task's seam inserts a task into its package. Apply changes saves the batch." }), _jsx(Sheet, { data: entries, id: "id", group: Sheet.group(PackageType, "tasks", { title: "name", noun: { singular: "package", plural: "packages" } }), columns: {
1060
+ task: Sheet.column.text(TaskType, { header: "Task", width: "240px" }),
1061
+ qty: Sheet.column.quantity(TaskType, { header: "Qty", width: "112px" }),
1062
+ notes: Sheet.column.text(TaskType, { header: "Notes", width: "280px" }),
1063
+ }, newRow: newTask, newGroup: newPackage, onUpdate: entries.write, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.filter((_$, e) => e.hasTag("group")).length()} packages · ${saved.filter((_$, e) => e.hasTag("row")).length()} loose tasks` })] }));
722
1064
  } }))),
723
1065
  inputs: [],
724
1066
  });