@mistriwork/mistri 1.0.0-rc.12 → 1.0.0-rc.13

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 (221) hide show
  1. package/README.md +4 -3
  2. package/dist/client/assets/LayoutBuilder-MPzkVU2e.js.map +1 -1
  3. package/dist/client/assets/index-D2dHI6hR.js.map +1 -1
  4. package/dist/client/assets/shell-TreN_uLs.js.map +1 -1
  5. package/dist/docs/access-model.md +1 -1
  6. package/dist/docs/build-and-deploy.md +0 -1
  7. package/dist/docs/cli.md +1 -1
  8. package/dist/docs/glossary.md +3 -3
  9. package/dist/docs/query.md +9 -21
  10. package/dist/docs/status.md +3 -3
  11. package/dist/embed/react/chunks/LayoutBuilder-Cbxa_wve.js.map +1 -1
  12. package/dist/embed/react/chunks/app-0UZ1Re1k.js.map +1 -1
  13. package/dist/embed/react/chunks/page-Bx4o1kpQ.js.map +1 -1
  14. package/dist/embed/react/chunks/palette-Bb7Lf6eo.js.map +1 -1
  15. package/dist/embed/react/chunks/shell-DZRACiB9.js.map +1 -1
  16. package/dist/embed/react/chunks/write-BQO4CFc9.js.map +1 -1
  17. package/dist/embed/react/index.js +1 -1
  18. package/dist/server/build/dockerfile.js +1 -1
  19. package/dist/server/cli/usage.js +2 -2
  20. package/dist/ui/index.js +2 -2
  21. package/dist/ui/page.js +2 -2
  22. package/dist/ui/{shared-N4Ej8yRD.js → shared-1PD7TOTJ.js} +5 -5
  23. package/dist/ui/{shared-N4Ej8yRD.js.map → shared-1PD7TOTJ.js.map} +1 -1
  24. package/dist/ui/{shared-D5m86-4o.js → shared-1gMnOu9J.js} +6 -6
  25. package/dist/ui/{shared-D5m86-4o.js.map → shared-1gMnOu9J.js.map} +1 -1
  26. package/dist/ui/{shared-Ctaj8mR2.js → shared-5RQMi4BR.js} +3 -3
  27. package/dist/ui/{shared-Ctaj8mR2.js.map → shared-5RQMi4BR.js.map} +1 -1
  28. package/dist/ui/{shared-DpLeIATZ.js → shared-B-BUF9fx.js} +2 -2
  29. package/dist/ui/{shared-DpLeIATZ.js.map → shared-B-BUF9fx.js.map} +1 -1
  30. package/dist/ui/{shared-DWYnjItP.js → shared-B-MhiPIN.js} +9 -9
  31. package/dist/ui/{shared-DWYnjItP.js.map → shared-B-MhiPIN.js.map} +1 -1
  32. package/dist/ui/{shared-0fNuhSbG.js → shared-B07qIMlE.js} +5 -5
  33. package/dist/ui/{shared-0fNuhSbG.js.map → shared-B07qIMlE.js.map} +1 -1
  34. package/dist/ui/{shared-lYtMadRU.js → shared-B1SAS_J-.js} +3 -3
  35. package/dist/ui/{shared-lYtMadRU.js.map → shared-B1SAS_J-.js.map} +1 -1
  36. package/dist/ui/{shared-CpI0Y8M5.js → shared-B4jgU8h9.js} +6 -6
  37. package/dist/ui/{shared-CpI0Y8M5.js.map → shared-B4jgU8h9.js.map} +1 -1
  38. package/dist/ui/{shared-Cxu1c35l.js → shared-B5aBCUi7.js} +30 -30
  39. package/dist/ui/{shared-Cxu1c35l.js.map → shared-B5aBCUi7.js.map} +1 -1
  40. package/dist/ui/{shared-CAe-US1q.js → shared-B9H7_Pji.js} +5 -5
  41. package/dist/ui/{shared-CAe-US1q.js.map → shared-B9H7_Pji.js.map} +1 -1
  42. package/dist/ui/{shared-Q2bgBaYo.js → shared-BAXr20S-.js} +4 -4
  43. package/dist/ui/{shared-Q2bgBaYo.js.map → shared-BAXr20S-.js.map} +1 -1
  44. package/dist/ui/{shared-CyEefFFb.js → shared-BDDUzH-f.js} +19 -19
  45. package/dist/ui/{shared-CyEefFFb.js.map → shared-BDDUzH-f.js.map} +1 -1
  46. package/dist/ui/{shared-BdhPI6dq.js → shared-BGmkeDeW.js} +3 -3
  47. package/dist/ui/{shared-BdhPI6dq.js.map → shared-BGmkeDeW.js.map} +1 -1
  48. package/dist/ui/{shared-DONIpq2q.js → shared-BHCQ60yN.js} +2 -2
  49. package/dist/ui/{shared-DONIpq2q.js.map → shared-BHCQ60yN.js.map} +1 -1
  50. package/dist/ui/{shared-CNmaAv64.js → shared-BHsjawAG.js} +8 -8
  51. package/dist/ui/{shared-CNmaAv64.js.map → shared-BHsjawAG.js.map} +1 -1
  52. package/dist/ui/{shared-DgjU9kOy.js → shared-BIQuK_nq.js} +4 -4
  53. package/dist/ui/{shared-DgjU9kOy.js.map → shared-BIQuK_nq.js.map} +1 -1
  54. package/dist/ui/{shared-icSQAj21.js → shared-BOe4W2FI.js} +3 -3
  55. package/dist/ui/{shared-icSQAj21.js.map → shared-BOe4W2FI.js.map} +1 -1
  56. package/dist/ui/{shared-BlxoOJSk.js → shared-BPpC08ip.js} +3 -3
  57. package/dist/ui/{shared-BlxoOJSk.js.map → shared-BPpC08ip.js.map} +1 -1
  58. package/dist/ui/{shared-BtiFL37J.js → shared-BdA3LPCB.js} +2 -2
  59. package/dist/ui/{shared-BtiFL37J.js.map → shared-BdA3LPCB.js.map} +1 -1
  60. package/dist/ui/{shared-CvR94vn4.js → shared-Be3nbWsd.js} +11 -11
  61. package/dist/ui/{shared-CvR94vn4.js.map → shared-Be3nbWsd.js.map} +1 -1
  62. package/dist/ui/{shared-Bl_nF_IH.js → shared-Bf30ZLoS.js} +8 -4
  63. package/dist/ui/shared-Bf30ZLoS.js.map +1 -0
  64. package/dist/ui/{shared-DijrJ_V-.js → shared-BguP_BaR.js} +7 -7
  65. package/dist/ui/{shared-DijrJ_V-.js.map → shared-BguP_BaR.js.map} +1 -1
  66. package/dist/ui/{shared-DPAVoWY0.js → shared-BoLbc2WD.js} +17 -2
  67. package/dist/ui/{shared-DPAVoWY0.js.map → shared-BoLbc2WD.js.map} +1 -1
  68. package/dist/ui/{shared-BkdBTYwx.js → shared-BttyOW4W.js} +3 -3
  69. package/dist/ui/{shared-BkdBTYwx.js.map → shared-BttyOW4W.js.map} +1 -1
  70. package/dist/ui/{shared-CQWLz8uT.js → shared-BumxsBlA.js} +10 -10
  71. package/dist/ui/{shared-CQWLz8uT.js.map → shared-BumxsBlA.js.map} +1 -1
  72. package/dist/ui/{shared-Dzu7tFiC.js → shared-Bw73ktBk.js} +3 -3
  73. package/dist/ui/{shared-Dzu7tFiC.js.map → shared-Bw73ktBk.js.map} +1 -1
  74. package/dist/ui/{shared-BhAYErSx.js → shared-C1B0GH0e.js} +4 -4
  75. package/dist/ui/{shared-BhAYErSx.js.map → shared-C1B0GH0e.js.map} +1 -1
  76. package/dist/ui/{shared-BQJQcGgj.js → shared-C2FsyYr7.js} +4 -4
  77. package/dist/ui/{shared-BQJQcGgj.js.map → shared-C2FsyYr7.js.map} +1 -1
  78. package/dist/ui/{shared-B9lw2Ryh.js → shared-C2RU1qjv.js} +4 -4
  79. package/dist/ui/{shared-B9lw2Ryh.js.map → shared-C2RU1qjv.js.map} +1 -1
  80. package/dist/ui/{shared-DQYNI9wO.js → shared-C42f07hJ.js} +5 -5
  81. package/dist/ui/{shared-DQYNI9wO.js.map → shared-C42f07hJ.js.map} +1 -1
  82. package/dist/ui/{shared-CqCZ8wMY.js → shared-C4LkPCU4.js} +3 -3
  83. package/dist/ui/{shared-CqCZ8wMY.js.map → shared-C4LkPCU4.js.map} +1 -1
  84. package/dist/ui/{shared-BhtpgOpa.js → shared-C4wSfz6k.js} +9 -9
  85. package/dist/ui/{shared-BhtpgOpa.js.map → shared-C4wSfz6k.js.map} +1 -1
  86. package/dist/ui/{shared-BHc2Izkj.js → shared-C9DZtSFH.js} +2 -2
  87. package/dist/ui/{shared-BHc2Izkj.js.map → shared-C9DZtSFH.js.map} +1 -1
  88. package/dist/ui/{shared-DW1YuiNS.js → shared-CA52ClGo.js} +2 -2
  89. package/dist/ui/{shared-DW1YuiNS.js.map → shared-CA52ClGo.js.map} +1 -1
  90. package/dist/ui/{shared-BbSbilkh.js → shared-CBciTwm6.js} +9 -9
  91. package/dist/ui/{shared-BbSbilkh.js.map → shared-CBciTwm6.js.map} +1 -1
  92. package/dist/ui/{shared-C-J32eBn.js → shared-CL3RLKDA.js} +16 -16
  93. package/dist/ui/{shared-C-J32eBn.js.map → shared-CL3RLKDA.js.map} +1 -1
  94. package/dist/ui/{shared-Ck8S4OxW.js → shared-CLq3-_hW.js} +3 -3
  95. package/dist/ui/{shared-Ck8S4OxW.js.map → shared-CLq3-_hW.js.map} +1 -1
  96. package/dist/ui/{shared-Q5WefT7G.js → shared-CMjK-Ucd.js} +3 -3
  97. package/dist/ui/{shared-Q5WefT7G.js.map → shared-CMjK-Ucd.js.map} +1 -1
  98. package/dist/ui/{shared-D3MAuEA3.js → shared-CPjpSFGh.js} +2 -2
  99. package/dist/ui/{shared-D3MAuEA3.js.map → shared-CPjpSFGh.js.map} +1 -1
  100. package/dist/ui/{shared-VshXGwXP.js → shared-CSPhENq3.js} +2 -2
  101. package/dist/ui/{shared-VshXGwXP.js.map → shared-CSPhENq3.js.map} +1 -1
  102. package/dist/ui/{shared-DJrJaE73.js → shared-CZJE4Bmp.js} +3 -3
  103. package/dist/ui/{shared-DJrJaE73.js.map → shared-CZJE4Bmp.js.map} +1 -1
  104. package/dist/ui/{shared-D7ONOwI7.js → shared-C_BhUL2I.js} +7 -7
  105. package/dist/ui/{shared-D7ONOwI7.js.map → shared-C_BhUL2I.js.map} +1 -1
  106. package/dist/ui/{shared-j-2peBIB.js → shared-CbeujwdU.js} +4 -4
  107. package/dist/ui/{shared-j-2peBIB.js.map → shared-CbeujwdU.js.map} +1 -1
  108. package/dist/ui/{shared-D5GrOEa2.js → shared-Cbf-o4Dt.js} +4 -4
  109. package/dist/ui/{shared-D5GrOEa2.js.map → shared-Cbf-o4Dt.js.map} +1 -1
  110. package/dist/ui/{shared-HwiPIajr.js → shared-CdNmeNHh.js} +3 -3
  111. package/dist/ui/{shared-HwiPIajr.js.map → shared-CdNmeNHh.js.map} +1 -1
  112. package/dist/ui/{shared-ZCFt-CPb.js → shared-CdQOMD8J.js} +3 -3
  113. package/dist/ui/{shared-ZCFt-CPb.js.map → shared-CdQOMD8J.js.map} +1 -1
  114. package/dist/ui/{shared-ClhXSGGK.js → shared-CdiPYlTh.js} +2 -2
  115. package/dist/ui/{shared-ClhXSGGK.js.map → shared-CdiPYlTh.js.map} +1 -1
  116. package/dist/ui/{shared-DA8_pVRZ.js → shared-CepLAhwm.js} +2 -2
  117. package/dist/ui/{shared-DA8_pVRZ.js.map → shared-CepLAhwm.js.map} +1 -1
  118. package/dist/ui/{shared-BrYKjrnM.js → shared-Ch82h_Fu.js} +6 -6
  119. package/dist/ui/{shared-BrYKjrnM.js.map → shared-Ch82h_Fu.js.map} +1 -1
  120. package/dist/ui/{shared-C4UrE8Fb.js → shared-Chp88all.js} +3 -3
  121. package/dist/ui/{shared-C4UrE8Fb.js.map → shared-Chp88all.js.map} +1 -1
  122. package/dist/ui/{shared-CWa80DTO.js → shared-Ci16s2rN.js} +9 -9
  123. package/dist/ui/{shared-CWa80DTO.js.map → shared-Ci16s2rN.js.map} +1 -1
  124. package/dist/ui/{shared-tksTLD6Q.js → shared-CiqNkoE1.js} +5 -5
  125. package/dist/ui/{shared-tksTLD6Q.js.map → shared-CiqNkoE1.js.map} +1 -1
  126. package/dist/ui/{shared-yt7DjSrq.js → shared-CpMhOq8F.js} +2 -2
  127. package/dist/ui/{shared-yt7DjSrq.js.map → shared-CpMhOq8F.js.map} +1 -1
  128. package/dist/ui/{shared-Dm0z5WUl.js → shared-CrWv5JKH.js} +6 -6
  129. package/dist/ui/{shared-Dm0z5WUl.js.map → shared-CrWv5JKH.js.map} +1 -1
  130. package/dist/ui/{shared-CQL3FGHc.js → shared-Csfi3Pti.js} +2 -2
  131. package/dist/ui/{shared-CQL3FGHc.js.map → shared-Csfi3Pti.js.map} +1 -1
  132. package/dist/ui/{shared-BKNRbm-V.js → shared-CuZt26rm.js} +2 -2
  133. package/dist/ui/{shared-BKNRbm-V.js.map → shared-CuZt26rm.js.map} +1 -1
  134. package/dist/ui/{shared-Guq3upiR.js → shared-Cws_z-TS.js} +2 -2
  135. package/dist/ui/{shared-Guq3upiR.js.map → shared-Cws_z-TS.js.map} +1 -1
  136. package/dist/ui/{shared-Bw_qZeIj.js → shared-D1KWuCg6.js} +2 -2
  137. package/dist/ui/{shared-Bw_qZeIj.js.map → shared-D1KWuCg6.js.map} +1 -1
  138. package/dist/ui/{shared-CuCZKwXW.js → shared-D66ju_Cb.js} +23 -23
  139. package/dist/ui/{shared-CuCZKwXW.js.map → shared-D66ju_Cb.js.map} +1 -1
  140. package/dist/ui/{shared-DYfX7NSM.js → shared-DGaQZXnw.js} +2 -2
  141. package/dist/ui/{shared-DYfX7NSM.js.map → shared-DGaQZXnw.js.map} +1 -1
  142. package/dist/ui/{shared-B3Hp8tOv.js → shared-DHy41sET.js} +6 -6
  143. package/dist/ui/{shared-B3Hp8tOv.js.map → shared-DHy41sET.js.map} +1 -1
  144. package/dist/ui/{shared-CXyd7IKi.js → shared-DKEOMQ6l.js} +2 -2
  145. package/dist/ui/{shared-CXyd7IKi.js.map → shared-DKEOMQ6l.js.map} +1 -1
  146. package/dist/ui/{shared-Ccmc8YO0.js → shared-DSUICuaJ.js} +33 -33
  147. package/dist/ui/{shared-Ccmc8YO0.js.map → shared-DSUICuaJ.js.map} +1 -1
  148. package/dist/ui/{shared-CL2iw_js.js → shared-DUPMRoO0.js} +13 -13
  149. package/dist/ui/{shared-CL2iw_js.js.map → shared-DUPMRoO0.js.map} +1 -1
  150. package/dist/ui/{shared-Dtp4eRul.js → shared-DVfdm6ah.js} +2 -2
  151. package/dist/ui/{shared-Dtp4eRul.js.map → shared-DVfdm6ah.js.map} +1 -1
  152. package/dist/ui/{shared-BJ5Ex32G.js → shared-DXM7d7lJ.js} +3 -3
  153. package/dist/ui/{shared-BJ5Ex32G.js.map → shared-DXM7d7lJ.js.map} +1 -1
  154. package/dist/ui/{shared-BFr6ld0e.js → shared-DXbMlcev.js} +2 -2
  155. package/dist/ui/{shared-BFr6ld0e.js.map → shared-DXbMlcev.js.map} +1 -1
  156. package/dist/ui/{shared-BUp3jv-N.js → shared-D_SkPtJ9.js} +6 -6
  157. package/dist/ui/{shared-BUp3jv-N.js.map → shared-D_SkPtJ9.js.map} +1 -1
  158. package/dist/ui/{shared-CdNomVzH.js → shared-Dd21k2ic.js} +4 -4
  159. package/dist/ui/{shared-CdNomVzH.js.map → shared-Dd21k2ic.js.map} +1 -1
  160. package/dist/ui/{shared-gRMbxmpu.js → shared-Dh0nHtLZ.js} +8 -8
  161. package/dist/ui/{shared-gRMbxmpu.js.map → shared-Dh0nHtLZ.js.map} +1 -1
  162. package/dist/ui/{shared-BKGJgjog.js → shared-DkOdSpSK.js} +3 -3
  163. package/dist/ui/{shared-BKGJgjog.js.map → shared-DkOdSpSK.js.map} +1 -1
  164. package/dist/ui/{shared-BMsh72Uq.js → shared-Dkph5ckT.js} +2 -2
  165. package/dist/ui/{shared-BMsh72Uq.js.map → shared-Dkph5ckT.js.map} +1 -1
  166. package/dist/ui/{shared-Be1HpDOB.js → shared-Dl1_Nx--.js} +3 -3
  167. package/dist/ui/{shared-Be1HpDOB.js.map → shared-Dl1_Nx--.js.map} +1 -1
  168. package/dist/ui/{shared-BQIZWOoJ.js → shared-Dm0IGbAf.js} +2 -2
  169. package/dist/ui/{shared-BQIZWOoJ.js.map → shared-Dm0IGbAf.js.map} +1 -1
  170. package/dist/ui/{shared-DSUtcU5S.js → shared-DnIkTpPr.js} +5 -5
  171. package/dist/ui/{shared-DSUtcU5S.js.map → shared-DnIkTpPr.js.map} +1 -1
  172. package/dist/ui/{shared-DsxGbYlV.js → shared-Docl_0tq.js} +3 -3
  173. package/dist/ui/{shared-DsxGbYlV.js.map → shared-Docl_0tq.js.map} +1 -1
  174. package/dist/ui/{shared-DZskrV26.js → shared-DprrHC72.js} +2 -2
  175. package/dist/ui/{shared-DZskrV26.js.map → shared-DprrHC72.js.map} +1 -1
  176. package/dist/ui/{shared-Bmw43Hwf.js → shared-DyKsJTZs.js} +2 -2
  177. package/dist/ui/{shared-Bmw43Hwf.js.map → shared-DyKsJTZs.js.map} +1 -1
  178. package/dist/ui/{shared-BLG88Pya.js → shared-FO7vfI3F.js} +4 -4
  179. package/dist/ui/{shared-BLG88Pya.js.map → shared-FO7vfI3F.js.map} +1 -1
  180. package/dist/ui/{shared-DLSd80D1.js → shared-HalAVHjv.js} +3 -3
  181. package/dist/ui/{shared-DLSd80D1.js.map → shared-HalAVHjv.js.map} +1 -1
  182. package/dist/ui/{shared-D1mMBLdj.js → shared-JvcI-LWp.js} +15 -15
  183. package/dist/ui/{shared-D1mMBLdj.js.map → shared-JvcI-LWp.js.map} +1 -1
  184. package/dist/ui/{shared-C6Hv_4O0.js → shared-K7Ym27yg.js} +2 -2
  185. package/dist/ui/{shared-C6Hv_4O0.js.map → shared-K7Ym27yg.js.map} +1 -1
  186. package/dist/ui/{shared-B39ln7oi.js → shared-SzlnwfJO.js} +3 -3
  187. package/dist/ui/{shared-B39ln7oi.js.map → shared-SzlnwfJO.js.map} +1 -1
  188. package/dist/ui/{shared-DgPn_Jll.js → shared-TrGbd1FY.js} +3 -3
  189. package/dist/ui/{shared-DgPn_Jll.js.map → shared-TrGbd1FY.js.map} +1 -1
  190. package/dist/ui/{shared-DrFb-XtA.js → shared-WhbuObEW.js} +2 -2
  191. package/dist/ui/{shared-DrFb-XtA.js.map → shared-WhbuObEW.js.map} +1 -1
  192. package/dist/ui/{shared-DiFQljcq.js → shared-XcSKDp6N.js} +11 -11
  193. package/dist/ui/{shared-DiFQljcq.js.map → shared-XcSKDp6N.js.map} +1 -1
  194. package/dist/ui/{shared-CMAg58rY.js → shared-Z4uBJU6I.js} +7 -6
  195. package/dist/ui/shared-Z4uBJU6I.js.map +1 -0
  196. package/dist/ui/{shared-BnUptuUp.js → shared-bAgeiRa5.js} +2 -2
  197. package/dist/ui/{shared-BnUptuUp.js.map → shared-bAgeiRa5.js.map} +1 -1
  198. package/dist/ui/{shared-BxMvSjey.js → shared-cgXG7OgZ.js} +5 -5
  199. package/dist/ui/{shared-BxMvSjey.js.map → shared-cgXG7OgZ.js.map} +1 -1
  200. package/dist/ui/{shared-u4CG2kQJ.js → shared-eZ0SB_zm.js} +2 -2
  201. package/dist/ui/{shared-u4CG2kQJ.js.map → shared-eZ0SB_zm.js.map} +1 -1
  202. package/dist/ui/{shared-L90bWW8e.js → shared-frKFb79C.js} +2 -2
  203. package/dist/ui/{shared-L90bWW8e.js.map → shared-frKFb79C.js.map} +1 -1
  204. package/dist/ui/{shared-Dd34nhkw.js → shared-hAayNSRA.js} +3 -3
  205. package/dist/ui/{shared-Dd34nhkw.js.map → shared-hAayNSRA.js.map} +1 -1
  206. package/dist/ui/{shared-DzC64cpp.js → shared-j_J16p2B.js} +2 -2
  207. package/dist/ui/{shared-DzC64cpp.js.map → shared-j_J16p2B.js.map} +1 -1
  208. package/dist/ui/{shared-DX7HRIV-.js → shared-tqpl5N3g.js} +6 -6
  209. package/dist/ui/{shared-DX7HRIV-.js.map → shared-tqpl5N3g.js.map} +1 -1
  210. package/dist/ui/{shared-BS5dBMpA.js → shared-y-6_ZDts.js} +2 -2
  211. package/dist/ui/{shared-BS5dBMpA.js.map → shared-y-6_ZDts.js.map} +1 -1
  212. package/dist/ui/{shared-Cv5v_MP1.js → shared-zG6e1Rhz.js} +13 -13
  213. package/dist/ui/{shared-Cv5v_MP1.js.map → shared-zG6e1Rhz.js.map} +1 -1
  214. package/dist/ui/testing.js +1 -1
  215. package/package.json +4 -4
  216. package/dist/docs/cli-browser.md +0 -232
  217. package/dist/docs/database-browser.md +0 -115
  218. package/dist/docs/keyboard.md +0 -63
  219. package/dist/docs/mcp.md +0 -86
  220. package/dist/ui/shared-Bl_nF_IH.js.map +0 -1
  221. package/dist/ui/shared-CMAg58rY.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"shared-BQJQcGgj.js","names":[],"sources":["../../../kit/src/client/components/suggestModel.ts","../../../kit/src/client/components/SuggestBox.tsx"],"sourcesContent":["/**\n * What a search box suggests as the reader types: the distinct values of a\n * few of the list's own columns that contain what was typed, under the\n * list's standing query, counted by the server through the reader's grants\n * as a facet's values are. Kept apart from SuggestBox so the rules are\n * tested without a browser.\n */\nimport { andQuery, quoteValue } from '@mistriwork/engine/shared/query.js';\n\n/** About a screenful: a suggestion list is a nudge, not a second list. */\nexport const SUGGEST_MOST = 8;\n/** Long enough that a word typed at speed is one question, short enough to feel like an answer. */\nexport const SUGGEST_WAIT_MS = 200;\n\nexport interface Suggestion {\n value: string;\n count: number;\n}\n\n/** The breakdown one column's suggestions are read by. */\nexport function suggestText(table: string, column: string, typed: string, standing = ''): string {\n const condition = `${column} contains ${quoteValue(typed)}`;\n return `${table} ${andQuery(standing, condition)} count by ${column} sort count desc`.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * The columns' answers as one list: each value once, whatever its case or\n * column; a value that starts with what was typed before one that merely\n * contains it, then the commoner first, then by name; at most SUGGEST_MOST.\n */\nexport function rankSuggestions(answers: ReadonlyArray<ReadonlyArray<Suggestion>>, typed: string, most = SUGGEST_MOST): Suggestion[] {\n const needle = typed.trim().toLowerCase();\n const byValue = new Map<string, Suggestion>();\n for (const answer of answers)\n for (const suggestion of answer) {\n const value = suggestion.value.trim();\n if (!value) continue;\n const key = value.toLowerCase();\n const known = byValue.get(key);\n if (!known || suggestion.count > known.count) byValue.set(key, { value, count: suggestion.count });\n }\n const leads = (s: Suggestion) => (s.value.toLowerCase().startsWith(needle) ? 0 : 1);\n return [...byValue.values()].sort((a, b) => leads(a) - leads(b) || b.count - a.count || a.value.localeCompare(b.value)).slice(0, most);\n}\n\n/** One breakdown reply's rows as suggestions: the key first, the count last. */\nexport function suggestionsOf(reply: { columns?: string[]; rows?: Array<Record<string, unknown>>; errors?: unknown[] }): Suggestion[] {\n const keys = reply.columns ?? [];\n if (reply.errors?.length || keys.length < 2) return [];\n return (reply.rows ?? []).flatMap((row) => {\n const value = row[keys[0]];\n const count = Number(row[keys[keys.length - 1]]);\n return value == null || value === '' || !Number.isFinite(count) ? [] : [{ value: String(value), count }];\n });\n}\n","/**\n * A search box that suggests: the box a list's text filter already is, with\n * a short list of the values its columns hold that contain what was typed\n * (suggestModel.ts). Loaded on demand (onDemand.tsx) by the boxes that offer\n * it — the rail's text facet, the bar's search, a landing's hero search —\n * which draw their plain box until it arrives, so the first paint carries\n * none of it.\n *\n * Enter is still the box's own: nothing is lit until the reader arrows into\n * the list, so Enter searches for what was typed, and a lit or clicked\n * suggestion fills the box and searches for it.\n */\nimport { useEffect, useRef, useState, type InputHTMLAttributes, type Ref } from 'react';\nimport { keepPreviousData, useQuery } from '@tanstack/react-query';\nimport { state } from '../core.js';\nimport { batchedBreakdown } from '../breakdownBatch.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { SUGGEST_MOST, SUGGEST_WAIT_MS, rankSuggestions, suggestText, suggestionsOf, type Suggestion } from './suggestModel.js';\n\ninterface SuggestBoxProps {\n /** The list's table, by id. */\n table: string;\n /** The columns whose values are suggested. */\n columns: readonly string[];\n /** The list's standing query: a suggestion is a value the list can show. */\n standing?: string;\n text: string;\n onText: (text: string) => void;\n /** A suggestion taken: the box already says it. */\n onPick: (value: string) => void;\n inputProps: InputHTMLAttributes<HTMLInputElement>;\n inputRef?: Ref<HTMLInputElement>;\n}\n\nexport function SuggestBox({ table, columns, standing = '', text, onText, onPick, inputProps, inputRef }: SuggestBoxProps) {\n const typed = text.trim();\n const [asked, setAsked] = useState(typed);\n useEffect(() => {\n const wait = setTimeout(() => setAsked(typed), SUGGEST_WAIT_MS);\n return () => clearTimeout(wait);\n }, [typed]);\n const [wanted, setWanted] = useState(false);\n const lit = useRef<Suggestion | undefined>(undefined);\n const answer = useQuery<Suggestion[]>({\n /* In the list's query family, so a write that refreshes the rows refreshes what is suggested. */\n queryKey: ['run', state.activeConnection ?? '', 'suggest', table, columns.join(','), standing, asked],\n queryFn: async () =>\n rankSuggestions(\n await Promise.all(\n columns.map(async (column) =>\n suggestionsOf(await batchedBreakdown({ text: suggestText(table, column, asked, standing), limit: SUGGEST_MOST })),\n ),\n ),\n asked,\n ),\n enabled: asked !== '',\n staleTime: 60_000,\n placeholderData: keepPreviousData,\n retry: false,\n });\n /* What was typed, said back, is no suggestion. */\n const items = typed === '' ? [] : (answer.data ?? []).filter((item) => item.value.toLowerCase() !== typed.toLowerCase());\n const { onKeyDown, ...rest } = inputProps;\n return (\n <Typeahead<Suggestion>\n text={text}\n onText={(next) => {\n onText(next);\n setWanted(true);\n }}\n open={wanted && items.length > 0}\n onOpenChange={setWanted}\n items={items}\n itemKey={(item) => item.value}\n itemLabel={(item) => item.value}\n itemClassName={(_item, highlighted) => `dd-option${highlighted ? ' active' : ''}`}\n renderItem={(item) => <span className=\"dd-label\">{item.value}</span>}\n onPick={(item) => {\n setWanted(false);\n onText(item.value);\n onPick(item.value);\n }}\n inputRef={inputRef}\n autoHighlight={false}\n onHighlight={(item) => {\n lit.current = item;\n }}\n inputProps={{\n ...rest,\n autoComplete: 'off',\n onKeyDown: (e) => {\n /* A lit suggestion is the list's to take on Enter; the box searches only for what was typed. */\n if (e.key === 'Enter' && wanted && lit.current) return;\n onKeyDown?.(e);\n },\n }}\n popupClassName=\"dd-popup\"\n listClassName=\"dd-list\"\n fit=\"min-width\"\n />\n );\n}\n"],"mappings":";;;;;;;;AAoBA,SAAgB,YAAY,OAAe,QAAgB,OAAe,WAAW,IAAY;CAC/F,MAAM,YAAY,GAAG,OAAO,YAAY,WAAW,KAAK;CACxD,OAAO,GAAG,MAAM,GAAG,SAAS,UAAU,SAAS,EAAE,YAAY,OAAO,kBAAkB,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;AAClH;;;;;;AAOA,SAAgB,gBAAgB,SAAmD,OAAe,OAAA,GAAmC;CACnI,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,YAAY;CACxC,MAAM,0BAAU,IAAI,IAAwB;CAC5C,KAAK,MAAM,UAAU,SACnB,KAAK,MAAM,cAAc,QAAQ;EAC/B,MAAM,QAAQ,WAAW,MAAM,KAAK;EACpC,IAAI,CAAC,OAAO;EACZ,MAAM,MAAM,MAAM,YAAY;EAC9B,MAAM,QAAQ,QAAQ,IAAI,GAAG;EAC7B,IAAI,CAAC,SAAS,WAAW,QAAQ,MAAM,OAAO,QAAQ,IAAI,KAAK;GAAE;GAAO,OAAO,WAAW;EAAM,CAAC;CACnG;CACF,MAAM,SAAS,MAAmB,EAAE,MAAM,YAAY,CAAC,CAAC,WAAW,MAAM,IAAI,IAAI;CACjF,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI;AACvI;;AAGA,SAAgB,cAAc,OAAwG;CACpI,MAAM,OAAO,MAAM,WAAW,CAAC;CAC/B,IAAI,MAAM,QAAQ,UAAU,KAAK,SAAS,GAAG,OAAO,CAAC;CACrD,QAAQ,MAAM,QAAQ,CAAC,EAAA,CAAG,SAAS,QAAQ;EACzC,MAAM,QAAQ,IAAI,KAAK;EACvB,MAAM,QAAQ,OAAO,IAAI,KAAK,KAAK,SAAS,GAAG;EAC/C,OAAO,SAAS,QAAQ,UAAU,MAAM,CAAC,OAAO,SAAS,KAAK,IAAI,CAAC,IAAI,CAAC;GAAE,OAAO,OAAO,KAAK;GAAG;EAAM,CAAC;CACzG,CAAC;AACH;;;;;;;;;;;;;;ACpBA,SAAgB,WAAW,EAAE,OAAO,SAAS,WAAW,IAAI,MAAM,QAAQ,QAAQ,YAAY,YAA6B;CACzH,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,gBAAgB;EACd,MAAM,OAAO,iBAAiB,SAAS,KAAK,GAAA,GAAkB;EAC9D,aAAa,aAAa,IAAI;CAChC,GAAG,CAAC,KAAK,CAAC;CACV,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,MAAM,OAA+B,KAAA,CAAS;CACpD,MAAM,SAAS,SAAuB;EAEpC,UAAU;GAAC;GAAO,MAAM,oBAAoB;GAAI;GAAW;GAAO,QAAQ,KAAK,GAAG;GAAG;GAAU;EAAK;EACpG,SAAS,YACP,gBACE,MAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,WACjB,cAAc,MAAM,iBAAiB;GAAE,MAAM,YAAY,OAAO,QAAQ,OAAO,QAAQ;GAAG,OAAA;EAAoB,CAAC,CAAC,CAClH,CACF,GACA,KACF;EACF,SAAS,UAAU;EACnB,WAAW;EACX,iBAAiB;EACjB,OAAO;CACT,CAAC;CAED,MAAM,QAAQ,UAAU,KAAK,CAAC,KAAK,OAAO,QAAQ,CAAC,EAAA,CAAG,QAAQ,SAAS,KAAK,MAAM,YAAY,MAAM,MAAM,YAAY,CAAC;CACvH,MAAM,EAAE,WAAW,GAAG,SAAS;CAC/B,OACE,oBAAC,WAAD;EACQ;EACN,SAAS,SAAS;GAChB,OAAO,IAAI;GACX,UAAU,IAAI;EAChB;EACA,MAAM,UAAU,MAAM,SAAS;EAC/B,cAAc;EACP;EACP,UAAU,SAAS,KAAK;EACxB,YAAY,SAAS,KAAK;EAC1B,gBAAgB,OAAO,gBAAgB,YAAY,cAAc,YAAY;EAC7E,aAAa,SAAS,oBAAC,QAAD;GAAM,WAAU;GAAY,UAAA,KAAK;EAAY,CAAA;EACnE,SAAS,SAAS;GAChB,UAAU,KAAK;GACf,OAAO,KAAK,KAAK;GACjB,OAAO,KAAK,KAAK;EACnB;EACU;EACV,eAAe;EACf,cAAc,SAAS;GACrB,IAAI,UAAU;EAChB;EACA,YAAY;GACV,GAAG;GACH,cAAc;GACd,YAAY,MAAM;IAEhB,IAAI,EAAE,QAAQ,WAAW,UAAU,IAAI,SAAS;IAChD,YAAY,CAAC;GACf;EACF;EACA,gBAAe;EACf,eAAc;EACd,KAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"shared-C2FsyYr7.js","names":[],"sources":["../../../kit/src/client/components/suggestModel.ts","../../../kit/src/client/components/SuggestBox.tsx"],"sourcesContent":["/**\n * What a search box suggests as the reader types: the distinct values of a\n * few of the list's own columns that contain what was typed, under the\n * list's standing query, counted by the server through the reader's grants\n * as a facet's values are. Kept apart from SuggestBox so the rules are\n * tested without a browser.\n */\nimport { andQuery, quoteValue } from '@mistriwork/engine/shared/query.js';\n\n/** About a screenful: a suggestion list is a nudge, not a second list. */\nexport const SUGGEST_MOST = 8;\n/** Long enough that a word typed at speed is one question, short enough to feel like an answer. */\nexport const SUGGEST_WAIT_MS = 200;\n\nexport interface Suggestion {\n value: string;\n count: number;\n}\n\n/** The breakdown one column's suggestions are read by. */\nexport function suggestText(table: string, column: string, typed: string, standing = ''): string {\n const condition = `${column} contains ${quoteValue(typed)}`;\n return `${table} ${andQuery(standing, condition)} count by ${column} sort count desc`.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * The columns' answers as one list: each value once, whatever its case or\n * column; a value that starts with what was typed before one that merely\n * contains it, then the commoner first, then by name; at most SUGGEST_MOST.\n */\nexport function rankSuggestions(answers: ReadonlyArray<ReadonlyArray<Suggestion>>, typed: string, most = SUGGEST_MOST): Suggestion[] {\n const needle = typed.trim().toLowerCase();\n const byValue = new Map<string, Suggestion>();\n for (const answer of answers)\n for (const suggestion of answer) {\n const value = suggestion.value.trim();\n if (!value) continue;\n const key = value.toLowerCase();\n const known = byValue.get(key);\n if (!known || suggestion.count > known.count) byValue.set(key, { value, count: suggestion.count });\n }\n const leads = (s: Suggestion) => (s.value.toLowerCase().startsWith(needle) ? 0 : 1);\n return [...byValue.values()].sort((a, b) => leads(a) - leads(b) || b.count - a.count || a.value.localeCompare(b.value)).slice(0, most);\n}\n\n/** One breakdown reply's rows as suggestions: the key first, the count last. */\nexport function suggestionsOf(reply: { columns?: string[]; rows?: Array<Record<string, unknown>>; errors?: unknown[] }): Suggestion[] {\n const keys = reply.columns ?? [];\n if (reply.errors?.length || keys.length < 2) return [];\n return (reply.rows ?? []).flatMap((row) => {\n const value = row[keys[0]];\n const count = Number(row[keys[keys.length - 1]]);\n return value == null || value === '' || !Number.isFinite(count) ? [] : [{ value: String(value), count }];\n });\n}\n","/**\n * A search box that suggests: the box a list's text filter already is, with\n * a short list of the values its columns hold that contain what was typed\n * (suggestModel.ts). Loaded on demand (onDemand.tsx) by the boxes that offer\n * it — the rail's text facet, the bar's search, a landing's hero search —\n * which draw their plain box until it arrives, so the first paint carries\n * none of it.\n *\n * Enter is still the box's own: nothing is lit until the reader arrows into\n * the list, so Enter searches for what was typed, and a lit or clicked\n * suggestion fills the box and searches for it.\n */\nimport { useEffect, useRef, useState, type InputHTMLAttributes, type Ref } from 'react';\nimport { keepPreviousData, useQuery } from '@tanstack/react-query';\nimport { state } from '../core.js';\nimport { batchedBreakdown } from '../breakdownBatch.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { SUGGEST_MOST, SUGGEST_WAIT_MS, rankSuggestions, suggestText, suggestionsOf, type Suggestion } from './suggestModel.js';\n\ninterface SuggestBoxProps {\n /** The list's table, by id. */\n table: string;\n /** The columns whose values are suggested. */\n columns: readonly string[];\n /** The list's standing query: a suggestion is a value the list can show. */\n standing?: string;\n text: string;\n onText: (text: string) => void;\n /** A suggestion taken: the box already says it. */\n onPick: (value: string) => void;\n inputProps: InputHTMLAttributes<HTMLInputElement>;\n inputRef?: Ref<HTMLInputElement>;\n}\n\nexport function SuggestBox({ table, columns, standing = '', text, onText, onPick, inputProps, inputRef }: SuggestBoxProps) {\n const typed = text.trim();\n const [asked, setAsked] = useState(typed);\n useEffect(() => {\n const wait = setTimeout(() => setAsked(typed), SUGGEST_WAIT_MS);\n return () => clearTimeout(wait);\n }, [typed]);\n const [wanted, setWanted] = useState(false);\n const lit = useRef<Suggestion | undefined>(undefined);\n const answer = useQuery<Suggestion[]>({\n /* In the list's query family, so a write that refreshes the rows refreshes what is suggested. */\n queryKey: ['run', state.activeConnection ?? '', 'suggest', table, columns.join(','), standing, asked],\n queryFn: async () =>\n rankSuggestions(\n await Promise.all(\n columns.map(async (column) =>\n suggestionsOf(await batchedBreakdown({ text: suggestText(table, column, asked, standing), limit: SUGGEST_MOST })),\n ),\n ),\n asked,\n ),\n enabled: asked !== '',\n staleTime: 60_000,\n placeholderData: keepPreviousData,\n retry: false,\n });\n /* What was typed, said back, is no suggestion. */\n const items = typed === '' ? [] : (answer.data ?? []).filter((item) => item.value.toLowerCase() !== typed.toLowerCase());\n const { onKeyDown, ...rest } = inputProps;\n return (\n <Typeahead<Suggestion>\n text={text}\n onText={(next) => {\n onText(next);\n setWanted(true);\n }}\n open={wanted && items.length > 0}\n onOpenChange={setWanted}\n items={items}\n itemKey={(item) => item.value}\n itemLabel={(item) => item.value}\n itemClassName={(_item, highlighted) => `dd-option${highlighted ? ' active' : ''}`}\n renderItem={(item) => <span className=\"dd-label\">{item.value}</span>}\n onPick={(item) => {\n setWanted(false);\n onText(item.value);\n onPick(item.value);\n }}\n inputRef={inputRef}\n autoHighlight={false}\n onHighlight={(item) => {\n lit.current = item;\n }}\n inputProps={{\n ...rest,\n autoComplete: 'off',\n onKeyDown: (e) => {\n /* A lit suggestion is the list's to take on Enter; the box searches only for what was typed. */\n if (e.key === 'Enter' && wanted && lit.current) return;\n onKeyDown?.(e);\n },\n }}\n popupClassName=\"dd-popup\"\n listClassName=\"dd-list\"\n fit=\"min-width\"\n />\n );\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { D as subscribeSessionIdentity, E as sessionIdentity, it as useQuery } from "./shared-poc11xxo.js";
2
2
  import { F as primaryKey, gt as defineMessages, rn as shell, ut as humanName } from "./shared-DJZjSi3J.js";
3
- import { z as Typeahead } from "./shared-CNmaAv64.js";
4
- import { L as state, T as recordLabel, i as api, j as rowLabel, m as labelPath, st as ownedBy } from "./shared-DPAVoWY0.js";
5
- import { g as pageBridge, m as componentSlot } from "./shared-C4UrE8Fb.js";
3
+ import { z as Typeahead } from "./shared-BHsjawAG.js";
4
+ import { L as state, T as recordLabel, i as api, j as rowLabel, m as labelPath, st as ownedBy } from "./shared-BoLbc2WD.js";
5
+ import { g as pageBridge, m as componentSlot } from "./shared-Chp88all.js";
6
6
  import { u as resourceOf } from "./shared-DpVb48sM.js";
7
7
  import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
8
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -410,4 +410,4 @@ function InputSlot({ component, fallback, ...props }) {
410
410
  }
411
411
  export { offeredChoices as i, columnInput as n, RecordPicker as r, InputSlot as t };
412
412
 
413
- //# sourceMappingURL=shared-B9lw2Ryh.js.map
413
+ //# sourceMappingURL=shared-C2RU1qjv.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-B9lw2Ryh.js","names":["_Fragment"],"sources":["../../../kit/src/client/components/pickerStaleness.ts","../../../kit/src/client/copy/forms/components/RecordPicker.ts","../../../kit/src/client/components/RecordPicker.tsx","../../../kit/src/client/components/inputRenderers.tsx"],"sourcesContent":["/**\n * What a server-searched list may offer while its answer is on the way.\n *\n * The record picker, a command's reference field and the palette's record\n * hits all ask the server a pause after typing, so for a beat — the pause,\n * then the request — the rows on screen answer earlier text (on first open,\n * the empty search). The first row is lit as you type, and offered whole it\n * is what a quick Enter or click takes: a record the box never named (an\n * empty search's first issue took a new comment on CI). So until the answer\n * for what the box says lands, a list keeps only the rows whose name\n * contains that text, by the same case-blind `contains` the search runs.\n * Those stay under the pointer — the last answer is not blanked, so nothing\n * flickers — and nothing else can be chosen. Folding case here can only be\n * as strict as the server or stricter (an accent-blind collation finds more),\n * so a stale row is hidden early, never offered wrongly.\n *\n * `answered` also says whether \"nothing matches\" may be said yet: an empty\n * filtered list is not the server's answer.\n */\nexport function offeredChoices<T>({\n typed,\n asked,\n placeholder = false,\n rows,\n label,\n}: {\n /** What the box says now. */\n typed: string;\n /** The text the rows on screen were asked for. */\n asked: string;\n /** Rows kept from an earlier question (Query's `isPlaceholderData`): whatever `asked` is, they answer something else. */\n placeholder?: boolean;\n rows: T[] | undefined;\n label: (row: T) => string;\n}): { choices: T[]; answered: boolean } {\n const needle = typed.trim();\n const answered = asked === needle && !placeholder;\n if (answered) return { choices: rows ?? [], answered };\n const folded = needle.toLowerCase();\n return { choices: (rows ?? []).filter((row) => label(row).toLowerCase().includes(folded)), answered };\n}\n","/** What RecordPicker says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n remove: 'Remove {shownLabel}',\n choose: 'Choose {name}',\n search: 'Search {name}…',\n nothingToChoose: 'Nothing to choose from.',\n});\n","/**\n * A record picker: choose a row of another table by its name.\n *\n * A foreign key is a number that means a record, and a form asking for the\n * number is asking the person to know the database by heart. This asks for\n * the name — type a few letters, pick from what matches — and keeps the id\n * as the value. The search is the query language's own `contains` on the\n * target's naming column, bounded, one request per pause in typing and\n * cached by Query, so the same letters twice cost one request. The list is\n * the product's listbox, drawn by the page and not by the OS, with the\n * keyboard contract a listbox owes: arrows move, Enter chooses, Escape\n * closes, Tab leaves.\n *\n * `multiple` keeps every choice as a chip and reports an array — the shape\n * a filter's `in (…)` wants. A single picker reports one id or ''.\n */\nimport { ownedBy } from '../requestIdentity.js';\nimport { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, type ReactNode, type Ref } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, labelPath, recordLabel, rowLabel, state } from '../core.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { offeredChoices } from './pickerStaleness.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { shell } from '../shellMode.js';\nimport { resourceOf } from './appRooms.js';\nimport { sessionIdentity, subscribeSessionIdentity } from '../sessionIdentity.js';\nimport { say } from '../copy/forms/components/RecordPicker.js';\n\nconst LIMIT = 20;\n\ninterface Choice {\n value: string;\n label: string;\n /** The row the choice was read from, where the picker draws more of it than its name (`face`). */\n row?: Record<string, unknown>;\n}\n\n/** More of each choice than its name: the columns it is read with, and how a choice is drawn from them (FormLines.tsx). */\ninterface PickerFace {\n columns: readonly string[];\n /** One choice, beside every row the list holds now, so what the choices share (their photos) is asked once. */\n draw: (row: Record<string, unknown>, label: string, rows: readonly Record<string, unknown>[]) => ReactNode;\n}\n\nexport interface RecordPickerEditing {\n onStateChange(state: { unresolvedReference: boolean; open: boolean }): void;\n onCancel?: () => void;\n popupRef?: Ref<HTMLDivElement>;\n}\n\ninterface Common {\n table: TableMeta;\n ariaLabel?: string;\n placeholder?: string;\n /** In an app, the placeholder speaks the table's human name. */\n app?: boolean;\n /** The box, for a form that needs to put focus on this control. */\n inputRef?: Ref<HTMLInputElement>;\n /** A reference may point to a unique natural key rather than the primary key. */\n valueColumn?: string;\n /** Leave the key off each choice: where a name is the whole of a record (a tag), the id beside it is noise. */\n hideKey?: boolean;\n /** Only the rows this condition (the query language, over the table) admits: a New form's narrowed reference. */\n where?: string;\n disabled?: boolean;\n /** The field is wrong: the box says so (`aria-invalid`), as every other input does. */\n invalid?: boolean;\n /** What explains the field, by id: the error beside it. */\n describedBy?: string;\n face?: PickerFace;\n}\n\nexport type RecordPickerProps = Common &\n (\n | {\n multiple?: false;\n value: string;\n onChange: (value: string) => void;\n editing?: RecordPickerEditing;\n /** The chosen record's name where the caller already draws it (a grid's cell): the box says it from the\n first paint, where it would say the key until the name was fetched. */\n known?: Choice;\n }\n | {\n multiple: true;\n value: string[];\n onChange: (value: string[]) => void;\n editing?: never;\n }\n );\n\n/** The rows whose name contains the text, twenty at a time, every value bound. */\nasync function searchRecords(\n table: TableMeta,\n text: string,\n valueColumn = primaryKey(table)[0],\n connection = state.activeConnection,\n narrowed = '',\n more: readonly string[] = [],\n): Promise<Choice[]> {\n const naming = labelPath(table) ?? valueColumn;\n const pk = valueColumn;\n if (!pk) return [];\n const where = [narrowed, text ? `${naming} contains ${JSON.stringify(text)}` : ''].filter(Boolean).join(' and ');\n const q = [table.id, where, `show ${[...new Set([naming, pk, ...primaryKey(table), ...more])].join(', ')}`, `sort ${naming}`]\n .filter(Boolean)\n .join(' ');\n const data = await api('/api/run', {\n q,\n limit: LIMIT,\n offset: 0,\n count: 'none',\n ...ownedBy(connection),\n });\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((r) => ({\n value: String(r[pk]),\n label: recordLabel(table) === undefined ? String(r[naming] ?? r[pk]) : rowLabel(table, r),\n ...(more.length ? { row: r } : {}),\n }));\n}\n\nexport function RecordPicker(props: RecordPickerProps) {\n return props.editing ? <EditingRecordPicker {...props} /> : <RecordPickerControl {...props} />;\n}\n\nfunction EditingRecordPicker(props: RecordPickerProps) {\n const identity = useSyncExternalStore(subscribeSessionIdentity, sessionIdentity, sessionIdentity);\n // Replacement owners start with a fresh interaction and isolated query answers.\n const scope = JSON.stringify([\n identity.epoch,\n state.activeConnection,\n props.table.id,\n props.valueColumn ?? primaryKey(props.table)[0],\n labelPath(props.table),\n ]);\n return <RecordPickerControl key={scope} {...props} scope={scope} />;\n}\n\nfunction RecordPickerControl(props: RecordPickerProps & { scope?: string }) {\n const { table, ariaLabel, placeholder, app, inputRef } = props;\n const editing = props.editing;\n const connection = state.activeConnection;\n const valueColumn = props.valueColumn ?? primaryKey(table)[0];\n const ids = props.multiple === true ? props.value : props.value ? [props.value] : [];\n /* Labels for what is chosen: known once a name has been picked, the id\n itself until then (a preset value arrives as an id alone). */\n const [labels, setLabels] = useState<Map<string, string>>(new Map());\n /* A value's name: fetched, else the one the caller draws for it (`known`), else the value itself. */\n const known = props.multiple !== true ? props.known : undefined;\n const nameOf = (value: string) => labels.get(value) ?? (known?.value === value ? known.label : undefined) ?? value;\n const [legacyText, setText] = useState(() => (props.multiple !== true && props.value ? nameOf(props.value) : ''));\n // Even an empty search is unresolved until the user chooses or explicitly clears.\n const [search, setSearch] = useState<string | null>(null);\n const text = editing && typeof props.value === 'string' ? (search ?? (props.value ? nameOf(props.value) : '')) : legacyText;\n /* What the person typed, for the stale-rows rule below. An editable field\n shows its record's label until someone types (`search` is null); that\n label names the record already chosen, not a search, so it must not\n narrow what a browsing person can pick. */\n const typedSearch = editing && typeof props.value === 'string' ? (search ?? '') : legacyText;\n const [asked, setAsked] = useState('');\n const [open, setOpen] = useState(false);\n const signature = JSON.stringify(ids);\n const previous = useRef(signature);\n const reported = useRef<string | null>(null);\n const [editingValue, setEditingValue] = useState(signature);\n if (editing && editingValue !== signature) {\n setEditingValue(signature);\n if (reported.current !== signature) {\n setSearch(null);\n setOpen(false);\n setAsked('');\n setLabels(new Map());\n }\n reported.current = null;\n }\n const unresolvedReference = Boolean(editing && search !== null);\n const notified = useRef<{\n unresolvedReference: boolean;\n open: boolean;\n } | null>(null);\n useLayoutEffect(() => {\n if (editing && (notified.current?.unresolvedReference !== unresolvedReference || notified.current?.open !== open)) {\n notified.current = { unresolvedReference, open };\n editing.onStateChange(notified.current);\n }\n });\n const wrap = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n useEffect(() => {\n setLabels(new Map());\n }, [connection, table.id, valueColumn]);\n\n const setInput = (node: HTMLInputElement | null) => {\n input.current = node;\n if (typeof inputRef === 'function') inputRef(node);\n else if (inputRef) inputRef.current = node;\n };\n\n // One request per pause in typing.\n useEffect(() => {\n const timer = setTimeout(() => setAsked(text.trim()), 150);\n return () => clearTimeout(timer);\n }, [text]);\n\n const results = useQuery({\n queryKey: [\n 'picker',\n connection,\n table.id,\n valueColumn,\n asked,\n props.scope,\n editing ? signature : undefined,\n labelPath(table),\n recordLabel(table),\n props.where ?? '',\n props.face?.columns.join(),\n ],\n queryFn: () => searchRecords(table, asked, valueColumn, connection, props.where, props.face?.columns),\n enabled: open && !props.disabled,\n /* The last answer stays on screen while the next is fetched: a list\n that empties between keystrokes flickers, and an option someone is\n about to click must not be unmounted under the pointer. */\n placeholderData: (data, previous) =>\n previous &&\n previous.queryKey[1] === connection &&\n previous.queryKey[2] === table.id &&\n previous.queryKey[3] === valueColumn &&\n previous.queryKey[5] === props.scope &&\n (!editing || previous.queryKey[6] === signature) &&\n previous.queryKey[7] === labelPath(table) &&\n previous.queryKey[8] === recordLabel(table) &&\n previous.queryKey[9] === (props.where ?? '')\n ? data\n : undefined,\n });\n /* A list still answering earlier text offers only the rows the box's text\n also names — its lit first row is otherwise a record nobody named. */\n const { choices: options, answered } = offeredChoices({\n typed: typedSearch,\n asked,\n placeholder: results.isPlaceholderData,\n rows: results.data,\n label: (o) => o.label,\n });\n\n const selected = useQuery<Choice[]>({\n queryKey: ['picker-labels', connection, table.id, valueColumn, ids, props.scope, labelPath(table), recordLabel(table)],\n enabled: Boolean(valueColumn && ids.length),\n queryFn: async () => {\n const naming = labelPath(table) ?? valueColumn;\n const columns = [...new Set([valueColumn, naming, ...primaryKey(table)])];\n /* A walked name and a label template (`$label`) are paths, which a view reads and a plain query does not. */\n const data =\n naming.includes('.') || naming.startsWith('$')\n ? await api('/api/view/run', {\n ...ownedBy(connection),\n view: {\n id: `picker:${table.id}`,\n name: '',\n base: table.id,\n columns: columns.map((path) => ({ path })),\n filter: {\n groups: [[{ path: valueColumn, op: 'in', value: ids }]],\n },\n limit: ids.length,\n },\n })\n : await api('/api/query', {\n ...ownedBy(connection),\n table: table.id,\n columns,\n filter: {\n groups: [[{ column: valueColumn, op: 'in', value: ids }]],\n },\n limit: ids.length,\n offset: 0,\n });\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((row) => ({\n value: String(row[valueColumn]),\n label: recordLabel(table) === undefined ? String(row[naming] ?? row[valueColumn]) : rowLabel(table, row),\n }));\n },\n });\n useEffect(() => {\n if (selected.data) setLabels((known) => new Map([...known, ...selected.data!.map((choice) => [choice.value, choice.label] as const)]));\n }, [selected.data]);\n useEffect(() => {\n if (editing) return;\n const changed = previous.current !== signature;\n previous.current = signature;\n const ownChange = reported.current === signature;\n if (ownChange) reported.current = null;\n if (props.multiple !== true && ((!open && !ownChange) || (changed && !ownChange))) {\n setText(props.value ? nameOf(props.value) : '');\n }\n }, [signature, labels, open]);\n\n const report = (next: string[]) => {\n reported.current = JSON.stringify(next);\n if (props.multiple === true) props.onChange(next);\n else props.onChange(next[0] ?? '');\n };\n\n const pick = (choice: Choice) => {\n setLabels((m) => new Map(m).set(choice.value, choice.label));\n if (props.multiple) {\n report(ids.includes(choice.value) ? ids.filter((v) => v !== choice.value) : [...ids, choice.value]);\n setText('');\n } else {\n setSearch(null);\n report([choice.value]);\n setText(choice.label);\n setOpen(false);\n }\n };\n\n const remove = (value: string) => {\n report(ids.filter((v) => v !== value));\n input.current?.focus();\n };\n\n const clear = () => {\n setSearch(null);\n report([]);\n setText('');\n input.current?.focus();\n };\n\n const shownLabel = (value: string) => labels.get(value) ?? value;\n\n return (\n <div\n className={`picker${props.multiple ? ' picker-multi' : ''}`}\n ref={wrap}\n onKeyDownCapture={(e) => {\n if (editing && open && e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n editing.onCancel?.();\n setSearch(null);\n setOpen(false);\n }\n }}\n >\n <span className=\"picker-chips\">\n {props.multiple &&\n ids.map((value) => (\n <span key={value} className=\"picker-chip\">\n <span>{shownLabel(value)}</span>\n <button\n type=\"button\"\n className=\"picker-chip-x\"\n aria-label={say('remove', { shownLabel: shownLabel(value) })}\n onClick={() => remove(value)}\n >\n ×\n </button>\n </span>\n ))}\n </span>\n <Typeahead\n text={text}\n onText={(next) => {\n if (editing) setSearch(next);\n else setText(next);\n setOpen(true);\n if (!editing && !props.multiple && ids.length) report([]);\n }}\n open={open}\n onOpenChange={setOpen}\n items={options}\n itemKey={(o) => o.value}\n itemLabel={(o) => o.label}\n itemClassName={(o, highlighted) => `picker-option${highlighted ? ' active' : ''}${ids.includes(o.value) ? ' chosen' : ''}`}\n renderItem={(o) =>\n props.face && o.row ? (\n props.face.draw(\n o.row,\n o.label,\n options.flatMap((each) => (each.row ? [each.row] : [])),\n )\n ) : (\n <>\n <span className=\"picker-option-label\">{o.label}</span>\n {!props.hideKey && o.label !== o.value && <span className=\"picker-option-detail\">{o.value}</span>}\n </>\n )\n }\n onPick={pick}\n inputRef={setInput}\n popupRef={editing?.popupRef}\n anchorRef={wrap}\n fit=\"width\"\n inputProps={{\n type: 'text',\n className: 'picker-input',\n 'aria-label': ariaLabel ?? say('choose', { name: humanName(table.name) }),\n /* In an App the resource's own noun (\"lead\"), not the table's name (\"crm_lead\"). */\n placeholder:\n placeholder ??\n (app\n ? say('search', {\n name: (resourceOf(shell().app(), table.id)?.noun?.one ?? humanName(table.name)).toLowerCase(),\n })\n : `→ ${table.name}`),\n autoComplete: 'off',\n spellCheck: false,\n ...(props.disabled ? { disabled: true } : {}),\n ...(props.invalid ? { 'aria-invalid': true } : {}),\n ...(props.describedBy ? { 'aria-describedby': props.describedBy } : {}),\n /* Backspace on an empty box takes the last chip off. */\n onKeyDown: (e) => {\n // Base UI clears text on closed-popup Escape. Let the editor receive\n // that key without manufacturing a new search or selection first.\n if (editing && e.key === 'Escape') {\n (e as typeof e & { preventBaseUIHandler?: () => void }).preventBaseUIHandler?.();\n }\n if (e.key === 'Backspace' && props.multiple && !text && ids.length) report(ids.slice(0, -1));\n },\n }}\n popupClassName=\"picker-popup\"\n listClassName=\"picker-list\"\n empty={results.isPending || !answered ? undefined : text ? say('common.nothingMatches') : say('nothingToChoose')}\n emptyClassName=\"picker-empty\"\n />\n <button\n type=\"button\"\n className=\"picker-clear\"\n aria-label={say('common.clear')}\n hidden={ids.length === 0 && !unresolvedReference}\n onClick={clear}\n >\n ×\n </button>\n </div>\n );\n}\n","/**\n * An input component where a form draws a field: a record's column in its\n * edit and New forms (`resources.<name>.inputs`), a command's input in an\n * action's or a signed-in form's command form (`inputs` beside the command).\n * The author's component draws the control and hands back a value through\n * `onChange`; the form keeps it, says its problems beside it and submits it,\n * and what the value must be is still the form's rules and the server's to\n * judge — a component cannot make a value valid.\n *\n * Until the module has arrived the platform's own control stands in its\n * place, so a form never waits on it; one that throws is the platform's\n * control from then on, said once in the console. Small and in the first\n * chunk beside the value renderers, since the forms use it; the loading\n * itself is the shell's (shellMode.ts `components`: in an App, the SDK's\n * chunk, componentLoader.ts), and the slot is the kit's one\n * (componentSlot.tsx).\n */\nimport type { ReactNode } from 'react';\nimport { pageBridge } from '../uiBridge.js';\nimport { shell } from '../shellMode.js';\nimport { componentSlot } from './componentSlot.js';\n\n/** What an input component receives: `InputProps` in packages/mistri/schema/ui.d.ts. */\ninterface InputComponentProps {\n /** The form's value: a column's text (null for none) in a record's form, the field's value in a command's. */\n value: string | number | boolean | null;\n onChange(value: string | number | boolean | null): void;\n /** The control's id, which the field's label points at, and what describes it (the hint, the problem). */\n id: string;\n describedBy?: string;\n invalid: boolean;\n disabled: boolean;\n required: boolean;\n /** The field's caption, for a control that names itself. */\n label: string;\n /** The column, or the command's input, it stands for. */\n field: { name: string; resource?: string };\n /** Which form: a record's edit, a New form, or a command's. */\n context: 'edit' | 'new' | 'command';\n}\n\n/* The platform's own inputs for a display rule's formats — stars, a slider, a color, a time, a month, a year, a JSON document — are\n named like this and loaded like an author's, from one chunk of their own (builtInInputs.tsx). */\nconst BUILT_IN = 'mistri:';\n\nconst Slot = componentSlot<InputComponentProps>({\n className: 'custom-input',\n load: (id) => (id.startsWith(BUILT_IN) ? shell().components.builtIn(id.slice(BUILT_IN.length)) : shell().components.load(id)),\n couldNotLoad: (id, error) => console.error(`the input \"${id}\" could not be loaded; its field is the platform's control.`, error),\n threw: (id, error) => console.error(`the input \"${id}\" threw; its field is the platform's control.`, error),\n});\n\ntype RuleLike = { format?: string; slider?: unknown; scan?: unknown; signature?: unknown };\n\n/** The formats a field is edited in an input of the platform's own for. */\nconst OWN_INPUTS = new Set(['rating', 'color', 'time', 'month', 'year', 'json']);\n\n/** The App's input component for a resource's column, where it declares one (`resources.<name>.inputs`), else the\n platform's own for the column's display rule, where it has one (a rating's stars, a slider, a color). */\nexport function columnInput(tableId: string, column: string): string | undefined {\n const resources = (\n pageBridge.activeApp() as {\n resources?: Record<string, { inputs?: Record<string, { id: string }>; display?: Record<string, RuleLike> }>;\n } | null\n )?.resources;\n const input = resources?.[tableId]?.inputs;\n if (input && Object.hasOwn(input, column)) return input[column].id;\n const display = resources?.[tableId]?.display;\n const rule = display && Object.hasOwn(display, column) ? display[column] : undefined;\n if (rule?.slider) return `${BUILT_IN}slider`;\n if (rule?.scan) return `${BUILT_IN}scan`;\n /* A signature whose file resource did not resolve here is a plain box: there is nowhere to keep the drawing. */\n if (rule?.signature && typeof rule.signature === 'object') return `${BUILT_IN}signature`;\n return rule?.format && OWN_INPUTS.has(rule.format) ? `${BUILT_IN}${rule.format}` : undefined;\n}\n\n/** One field's control, drawn by the input component `id` once it is here, and by `fallback` until then (or for good, if it cannot be). */\nexport function InputSlot({ component, fallback, ...props }: InputComponentProps & { component: string; fallback: ReactNode }) {\n return <Slot id={component} fallback={fallback} props={props} />;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-C2RU1qjv.js","names":["_Fragment"],"sources":["../../../kit/src/client/components/pickerStaleness.ts","../../../kit/src/client/copy/forms/components/RecordPicker.ts","../../../kit/src/client/components/RecordPicker.tsx","../../../kit/src/client/components/inputRenderers.tsx"],"sourcesContent":["/**\n * What a server-searched list may offer while its answer is on the way.\n *\n * The record picker, a command's reference field and the palette's record\n * hits all ask the server a pause after typing, so for a beat — the pause,\n * then the request — the rows on screen answer earlier text (on first open,\n * the empty search). The first row is lit as you type, and offered whole it\n * is what a quick Enter or click takes: a record the box never named (an\n * empty search's first issue took a new comment on CI). So until the answer\n * for what the box says lands, a list keeps only the rows whose name\n * contains that text, by the same case-blind `contains` the search runs.\n * Those stay under the pointer — the last answer is not blanked, so nothing\n * flickers — and nothing else can be chosen. Folding case here can only be\n * as strict as the server or stricter (an accent-blind collation finds more),\n * so a stale row is hidden early, never offered wrongly.\n *\n * `answered` also says whether \"nothing matches\" may be said yet: an empty\n * filtered list is not the server's answer.\n */\nexport function offeredChoices<T>({\n typed,\n asked,\n placeholder = false,\n rows,\n label,\n}: {\n /** What the box says now. */\n typed: string;\n /** The text the rows on screen were asked for. */\n asked: string;\n /** Rows kept from an earlier question (Query's `isPlaceholderData`): whatever `asked` is, they answer something else. */\n placeholder?: boolean;\n rows: T[] | undefined;\n label: (row: T) => string;\n}): { choices: T[]; answered: boolean } {\n const needle = typed.trim();\n const answered = asked === needle && !placeholder;\n if (answered) return { choices: rows ?? [], answered };\n const folded = needle.toLowerCase();\n return { choices: (rows ?? []).filter((row) => label(row).toLowerCase().includes(folded)), answered };\n}\n","/** What RecordPicker says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n remove: 'Remove {shownLabel}',\n choose: 'Choose {name}',\n search: 'Search {name}…',\n nothingToChoose: 'Nothing to choose from.',\n});\n","/**\n * A record picker: choose a row of another table by its name.\n *\n * A foreign key is a number that means a record, and a form asking for the\n * number is asking the person to know the database by heart. This asks for\n * the name — type a few letters, pick from what matches — and keeps the id\n * as the value. The search is the query language's own `contains` on the\n * target's naming column, bounded, one request per pause in typing and\n * cached by Query, so the same letters twice cost one request. The list is\n * the product's listbox, drawn by the page and not by the OS, with the\n * keyboard contract a listbox owes: arrows move, Enter chooses, Escape\n * closes, Tab leaves.\n *\n * `multiple` keeps every choice as a chip and reports an array — the shape\n * a filter's `in (…)` wants. A single picker reports one id or ''.\n */\nimport { ownedBy } from '../requestIdentity.js';\nimport { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, type ReactNode, type Ref } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, labelPath, recordLabel, rowLabel, state } from '../core.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { offeredChoices } from './pickerStaleness.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { shell } from '../shellMode.js';\nimport { resourceOf } from './appRooms.js';\nimport { sessionIdentity, subscribeSessionIdentity } from '../sessionIdentity.js';\nimport { say } from '../copy/forms/components/RecordPicker.js';\n\nconst LIMIT = 20;\n\ninterface Choice {\n value: string;\n label: string;\n /** The row the choice was read from, where the picker draws more of it than its name (`face`). */\n row?: Record<string, unknown>;\n}\n\n/** More of each choice than its name: the columns it is read with, and how a choice is drawn from them (FormLines.tsx). */\ninterface PickerFace {\n columns: readonly string[];\n /** One choice, beside every row the list holds now, so what the choices share (their photos) is asked once. */\n draw: (row: Record<string, unknown>, label: string, rows: readonly Record<string, unknown>[]) => ReactNode;\n}\n\nexport interface RecordPickerEditing {\n onStateChange(state: { unresolvedReference: boolean; open: boolean }): void;\n onCancel?: () => void;\n popupRef?: Ref<HTMLDivElement>;\n}\n\ninterface Common {\n table: TableMeta;\n ariaLabel?: string;\n placeholder?: string;\n /** In an app, the placeholder speaks the table's human name. */\n app?: boolean;\n /** The box, for a form that needs to put focus on this control. */\n inputRef?: Ref<HTMLInputElement>;\n /** A reference may point to a unique natural key rather than the primary key. */\n valueColumn?: string;\n /** Leave the key off each choice: where a name is the whole of a record (a tag), the id beside it is noise. */\n hideKey?: boolean;\n /** Only the rows this condition (the query language, over the table) admits: a New form's narrowed reference. */\n where?: string;\n disabled?: boolean;\n /** The field is wrong: the box says so (`aria-invalid`), as every other input does. */\n invalid?: boolean;\n /** What explains the field, by id: the error beside it. */\n describedBy?: string;\n face?: PickerFace;\n}\n\nexport type RecordPickerProps = Common &\n (\n | {\n multiple?: false;\n value: string;\n onChange: (value: string) => void;\n editing?: RecordPickerEditing;\n /** The chosen record's name where the caller already draws it (a grid's cell): the box says it from the\n first paint, where it would say the key until the name was fetched. */\n known?: Choice;\n }\n | {\n multiple: true;\n value: string[];\n onChange: (value: string[]) => void;\n editing?: never;\n }\n );\n\n/** The rows whose name contains the text, twenty at a time, every value bound. */\nasync function searchRecords(\n table: TableMeta,\n text: string,\n valueColumn = primaryKey(table)[0],\n connection = state.activeConnection,\n narrowed = '',\n more: readonly string[] = [],\n): Promise<Choice[]> {\n const naming = labelPath(table) ?? valueColumn;\n const pk = valueColumn;\n if (!pk) return [];\n const where = [narrowed, text ? `${naming} contains ${JSON.stringify(text)}` : ''].filter(Boolean).join(' and ');\n const q = [table.id, where, `show ${[...new Set([naming, pk, ...primaryKey(table), ...more])].join(', ')}`, `sort ${naming}`]\n .filter(Boolean)\n .join(' ');\n const data = await api('/api/run', {\n q,\n limit: LIMIT,\n offset: 0,\n count: 'none',\n ...ownedBy(connection),\n });\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((r) => ({\n value: String(r[pk]),\n label: recordLabel(table) === undefined ? String(r[naming] ?? r[pk]) : rowLabel(table, r),\n ...(more.length ? { row: r } : {}),\n }));\n}\n\nexport function RecordPicker(props: RecordPickerProps) {\n return props.editing ? <EditingRecordPicker {...props} /> : <RecordPickerControl {...props} />;\n}\n\nfunction EditingRecordPicker(props: RecordPickerProps) {\n const identity = useSyncExternalStore(subscribeSessionIdentity, sessionIdentity, sessionIdentity);\n // Replacement owners start with a fresh interaction and isolated query answers.\n const scope = JSON.stringify([\n identity.epoch,\n state.activeConnection,\n props.table.id,\n props.valueColumn ?? primaryKey(props.table)[0],\n labelPath(props.table),\n ]);\n return <RecordPickerControl key={scope} {...props} scope={scope} />;\n}\n\nfunction RecordPickerControl(props: RecordPickerProps & { scope?: string }) {\n const { table, ariaLabel, placeholder, app, inputRef } = props;\n const editing = props.editing;\n const connection = state.activeConnection;\n const valueColumn = props.valueColumn ?? primaryKey(table)[0];\n const ids = props.multiple === true ? props.value : props.value ? [props.value] : [];\n /* Labels for what is chosen: known once a name has been picked, the id\n itself until then (a preset value arrives as an id alone). */\n const [labels, setLabels] = useState<Map<string, string>>(new Map());\n /* A value's name: fetched, else the one the caller draws for it (`known`), else the value itself. */\n const known = props.multiple !== true ? props.known : undefined;\n const nameOf = (value: string) => labels.get(value) ?? (known?.value === value ? known.label : undefined) ?? value;\n const [legacyText, setText] = useState(() => (props.multiple !== true && props.value ? nameOf(props.value) : ''));\n // Even an empty search is unresolved until the user chooses or explicitly clears.\n const [search, setSearch] = useState<string | null>(null);\n const text = editing && typeof props.value === 'string' ? (search ?? (props.value ? nameOf(props.value) : '')) : legacyText;\n /* What the person typed, for the stale-rows rule below. An editable field\n shows its record's label until someone types (`search` is null); that\n label names the record already chosen, not a search, so it must not\n narrow what a browsing person can pick. */\n const typedSearch = editing && typeof props.value === 'string' ? (search ?? '') : legacyText;\n const [asked, setAsked] = useState('');\n const [open, setOpen] = useState(false);\n const signature = JSON.stringify(ids);\n const previous = useRef(signature);\n const reported = useRef<string | null>(null);\n const [editingValue, setEditingValue] = useState(signature);\n if (editing && editingValue !== signature) {\n setEditingValue(signature);\n if (reported.current !== signature) {\n setSearch(null);\n setOpen(false);\n setAsked('');\n setLabels(new Map());\n }\n reported.current = null;\n }\n const unresolvedReference = Boolean(editing && search !== null);\n const notified = useRef<{\n unresolvedReference: boolean;\n open: boolean;\n } | null>(null);\n useLayoutEffect(() => {\n if (editing && (notified.current?.unresolvedReference !== unresolvedReference || notified.current?.open !== open)) {\n notified.current = { unresolvedReference, open };\n editing.onStateChange(notified.current);\n }\n });\n const wrap = useRef<HTMLDivElement>(null);\n const input = useRef<HTMLInputElement>(null);\n useEffect(() => {\n setLabels(new Map());\n }, [connection, table.id, valueColumn]);\n\n const setInput = (node: HTMLInputElement | null) => {\n input.current = node;\n if (typeof inputRef === 'function') inputRef(node);\n else if (inputRef) inputRef.current = node;\n };\n\n // One request per pause in typing.\n useEffect(() => {\n const timer = setTimeout(() => setAsked(text.trim()), 150);\n return () => clearTimeout(timer);\n }, [text]);\n\n const results = useQuery({\n queryKey: [\n 'picker',\n connection,\n table.id,\n valueColumn,\n asked,\n props.scope,\n editing ? signature : undefined,\n labelPath(table),\n recordLabel(table),\n props.where ?? '',\n props.face?.columns.join(),\n ],\n queryFn: () => searchRecords(table, asked, valueColumn, connection, props.where, props.face?.columns),\n enabled: open && !props.disabled,\n /* The last answer stays on screen while the next is fetched: a list\n that empties between keystrokes flickers, and an option someone is\n about to click must not be unmounted under the pointer. */\n placeholderData: (data, previous) =>\n previous &&\n previous.queryKey[1] === connection &&\n previous.queryKey[2] === table.id &&\n previous.queryKey[3] === valueColumn &&\n previous.queryKey[5] === props.scope &&\n (!editing || previous.queryKey[6] === signature) &&\n previous.queryKey[7] === labelPath(table) &&\n previous.queryKey[8] === recordLabel(table) &&\n previous.queryKey[9] === (props.where ?? '')\n ? data\n : undefined,\n });\n /* A list still answering earlier text offers only the rows the box's text\n also names — its lit first row is otherwise a record nobody named. */\n const { choices: options, answered } = offeredChoices({\n typed: typedSearch,\n asked,\n placeholder: results.isPlaceholderData,\n rows: results.data,\n label: (o) => o.label,\n });\n\n const selected = useQuery<Choice[]>({\n queryKey: ['picker-labels', connection, table.id, valueColumn, ids, props.scope, labelPath(table), recordLabel(table)],\n enabled: Boolean(valueColumn && ids.length),\n queryFn: async () => {\n const naming = labelPath(table) ?? valueColumn;\n const columns = [...new Set([valueColumn, naming, ...primaryKey(table)])];\n /* A walked name and a label template (`$label`) are paths, which a view reads and a plain query does not. */\n const data =\n naming.includes('.') || naming.startsWith('$')\n ? await api('/api/view/run', {\n ...ownedBy(connection),\n view: {\n id: `picker:${table.id}`,\n name: '',\n base: table.id,\n columns: columns.map((path) => ({ path })),\n filter: {\n groups: [[{ path: valueColumn, op: 'in', value: ids }]],\n },\n limit: ids.length,\n },\n })\n : await api('/api/query', {\n ...ownedBy(connection),\n table: table.id,\n columns,\n filter: {\n groups: [[{ column: valueColumn, op: 'in', value: ids }]],\n },\n limit: ids.length,\n offset: 0,\n });\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((row) => ({\n value: String(row[valueColumn]),\n label: recordLabel(table) === undefined ? String(row[naming] ?? row[valueColumn]) : rowLabel(table, row),\n }));\n },\n });\n useEffect(() => {\n if (selected.data) setLabels((known) => new Map([...known, ...selected.data!.map((choice) => [choice.value, choice.label] as const)]));\n }, [selected.data]);\n useEffect(() => {\n if (editing) return;\n const changed = previous.current !== signature;\n previous.current = signature;\n const ownChange = reported.current === signature;\n if (ownChange) reported.current = null;\n if (props.multiple !== true && ((!open && !ownChange) || (changed && !ownChange))) {\n setText(props.value ? nameOf(props.value) : '');\n }\n }, [signature, labels, open]);\n\n const report = (next: string[]) => {\n reported.current = JSON.stringify(next);\n if (props.multiple === true) props.onChange(next);\n else props.onChange(next[0] ?? '');\n };\n\n const pick = (choice: Choice) => {\n setLabels((m) => new Map(m).set(choice.value, choice.label));\n if (props.multiple) {\n report(ids.includes(choice.value) ? ids.filter((v) => v !== choice.value) : [...ids, choice.value]);\n setText('');\n } else {\n setSearch(null);\n report([choice.value]);\n setText(choice.label);\n setOpen(false);\n }\n };\n\n const remove = (value: string) => {\n report(ids.filter((v) => v !== value));\n input.current?.focus();\n };\n\n const clear = () => {\n setSearch(null);\n report([]);\n setText('');\n input.current?.focus();\n };\n\n const shownLabel = (value: string) => labels.get(value) ?? value;\n\n return (\n <div\n className={`picker${props.multiple ? ' picker-multi' : ''}`}\n ref={wrap}\n onKeyDownCapture={(e) => {\n if (editing && open && e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n editing.onCancel?.();\n setSearch(null);\n setOpen(false);\n }\n }}\n >\n <span className=\"picker-chips\">\n {props.multiple &&\n ids.map((value) => (\n <span key={value} className=\"picker-chip\">\n <span>{shownLabel(value)}</span>\n <button\n type=\"button\"\n className=\"picker-chip-x\"\n aria-label={say('remove', { shownLabel: shownLabel(value) })}\n onClick={() => remove(value)}\n >\n ×\n </button>\n </span>\n ))}\n </span>\n <Typeahead\n text={text}\n onText={(next) => {\n if (editing) setSearch(next);\n else setText(next);\n setOpen(true);\n if (!editing && !props.multiple && ids.length) report([]);\n }}\n open={open}\n onOpenChange={setOpen}\n items={options}\n itemKey={(o) => o.value}\n itemLabel={(o) => o.label}\n itemClassName={(o, highlighted) => `picker-option${highlighted ? ' active' : ''}${ids.includes(o.value) ? ' chosen' : ''}`}\n renderItem={(o) =>\n props.face && o.row ? (\n props.face.draw(\n o.row,\n o.label,\n options.flatMap((each) => (each.row ? [each.row] : [])),\n )\n ) : (\n <>\n <span className=\"picker-option-label\">{o.label}</span>\n {!props.hideKey && o.label !== o.value && <span className=\"picker-option-detail\">{o.value}</span>}\n </>\n )\n }\n onPick={pick}\n inputRef={setInput}\n popupRef={editing?.popupRef}\n anchorRef={wrap}\n fit=\"width\"\n inputProps={{\n type: 'text',\n className: 'picker-input',\n 'aria-label': ariaLabel ?? say('choose', { name: humanName(table.name) }),\n /* In an App the resource's own noun (\"lead\"), not the table's name (\"crm_lead\"). */\n placeholder:\n placeholder ??\n (app\n ? say('search', {\n name: (resourceOf(shell().app(), table.id)?.noun?.one ?? humanName(table.name)).toLowerCase(),\n })\n : `→ ${table.name}`),\n autoComplete: 'off',\n spellCheck: false,\n ...(props.disabled ? { disabled: true } : {}),\n ...(props.invalid ? { 'aria-invalid': true } : {}),\n ...(props.describedBy ? { 'aria-describedby': props.describedBy } : {}),\n /* Backspace on an empty box takes the last chip off. */\n onKeyDown: (e) => {\n // Base UI clears text on closed-popup Escape. Let the editor receive\n // that key without manufacturing a new search or selection first.\n if (editing && e.key === 'Escape') {\n (e as typeof e & { preventBaseUIHandler?: () => void }).preventBaseUIHandler?.();\n }\n if (e.key === 'Backspace' && props.multiple && !text && ids.length) report(ids.slice(0, -1));\n },\n }}\n popupClassName=\"picker-popup\"\n listClassName=\"picker-list\"\n empty={results.isPending || !answered ? undefined : text ? say('common.nothingMatches') : say('nothingToChoose')}\n emptyClassName=\"picker-empty\"\n />\n <button\n type=\"button\"\n className=\"picker-clear\"\n aria-label={say('common.clear')}\n hidden={ids.length === 0 && !unresolvedReference}\n onClick={clear}\n >\n ×\n </button>\n </div>\n );\n}\n","/**\n * An input component where a form draws a field: a record's column in its\n * edit and New forms (`resources.<name>.inputs`), a command's input in an\n * action's or a signed-in form's command form (`inputs` beside the command).\n * The author's component draws the control and hands back a value through\n * `onChange`; the form keeps it, says its problems beside it and submits it,\n * and what the value must be is still the form's rules and the server's to\n * judge — a component cannot make a value valid.\n *\n * Until the module has arrived the platform's own control stands in its\n * place, so a form never waits on it; one that throws is the platform's\n * control from then on, said once in the console. Small and in the first\n * chunk beside the value renderers, since the forms use it; the loading\n * itself is the shell's (shellMode.ts `components`: in an App, the SDK's\n * chunk, componentLoader.ts), and the slot is the kit's one\n * (componentSlot.tsx).\n */\nimport type { ReactNode } from 'react';\nimport { pageBridge } from '../uiBridge.js';\nimport { shell } from '../shellMode.js';\nimport { componentSlot } from './componentSlot.js';\n\n/** What an input component receives: `InputProps` in packages/mistri/schema/ui.d.ts. */\ninterface InputComponentProps {\n /** The form's value: a column's text (null for none) in a record's form, the field's value in a command's. */\n value: string | number | boolean | null;\n onChange(value: string | number | boolean | null): void;\n /** The control's id, which the field's label points at, and what describes it (the hint, the problem). */\n id: string;\n describedBy?: string;\n invalid: boolean;\n disabled: boolean;\n required: boolean;\n /** The field's caption, for a control that names itself. */\n label: string;\n /** The column, or the command's input, it stands for. */\n field: { name: string; resource?: string };\n /** Which form: a record's edit, a New form, or a command's. */\n context: 'edit' | 'new' | 'command';\n}\n\n/* The platform's own inputs for a display rule's formats — stars, a slider, a color, a time, a month, a year, a JSON document — are\n named like this and loaded like an author's, from one chunk of their own (builtInInputs.tsx). */\nconst BUILT_IN = 'mistri:';\n\nconst Slot = componentSlot<InputComponentProps>({\n className: 'custom-input',\n load: (id) => (id.startsWith(BUILT_IN) ? shell().components.builtIn(id.slice(BUILT_IN.length)) : shell().components.load(id)),\n couldNotLoad: (id, error) => console.error(`the input \"${id}\" could not be loaded; its field is the platform's control.`, error),\n threw: (id, error) => console.error(`the input \"${id}\" threw; its field is the platform's control.`, error),\n});\n\ntype RuleLike = { format?: string; slider?: unknown; scan?: unknown; signature?: unknown };\n\n/** The formats a field is edited in an input of the platform's own for. */\nconst OWN_INPUTS = new Set(['rating', 'color', 'time', 'month', 'year', 'json']);\n\n/** The App's input component for a resource's column, where it declares one (`resources.<name>.inputs`), else the\n platform's own for the column's display rule, where it has one (a rating's stars, a slider, a color). */\nexport function columnInput(tableId: string, column: string): string | undefined {\n const resources = (\n pageBridge.activeApp() as {\n resources?: Record<string, { inputs?: Record<string, { id: string }>; display?: Record<string, RuleLike> }>;\n } | null\n )?.resources;\n const input = resources?.[tableId]?.inputs;\n if (input && Object.hasOwn(input, column)) return input[column].id;\n const display = resources?.[tableId]?.display;\n const rule = display && Object.hasOwn(display, column) ? display[column] : undefined;\n if (rule?.slider) return `${BUILT_IN}slider`;\n if (rule?.scan) return `${BUILT_IN}scan`;\n /* A signature whose file resource did not resolve here is a plain box: there is nowhere to keep the drawing. */\n if (rule?.signature && typeof rule.signature === 'object') return `${BUILT_IN}signature`;\n return rule?.format && OWN_INPUTS.has(rule.format) ? `${BUILT_IN}${rule.format}` : undefined;\n}\n\n/** One field's control, drawn by the input component `id` once it is here, and by `fallback` until then (or for good, if it cannot be). */\nexport function InputSlot({ component, fallback, ...props }: InputComponentProps & { component: string; fallback: ReactNode }) {\n return <Slot id={component} fallback={fallback} props={props} />;\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { L as splitClauseTail, N as andQuery, it as useQuery, nt as Button, x as fetchServer } from "./shared-poc11xxo.js";
2
2
  import { R as tableNamed, gt as defineMessages } from "./shared-DJZjSi3J.js";
3
- import { z as Typeahead } from "./shared-CNmaAv64.js";
4
- import { L as state, i as api, s as columnDisplay, st as ownedBy } from "./shared-DPAVoWY0.js";
3
+ import { z as Typeahead } from "./shared-BHsjawAG.js";
4
+ import { L as state, i as api, s as columnDisplay, st as ownedBy } from "./shared-BoLbc2WD.js";
5
5
  import { n as readsAsName } from "./shared-gtzTT5Z7.js";
6
- import { i as offeredChoices } from "./shared-B9lw2Ryh.js";
7
- import { p as say$1 } from "./shared-D1mMBLdj.js";
6
+ import { i as offeredChoices } from "./shared-C2RU1qjv.js";
7
+ import { p as say$1 } from "./shared-JvcI-LWp.js";
8
8
  import { d as knownReferenceName, f as rememberReferenceName, i as sayInput, t as answered } from "./shared-CxovRZz5.js";
9
9
  import { Fragment, useEffect, useRef, useState } from "react";
10
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -323,4 +323,4 @@ async function commandChoices(field, text, source) {
323
323
  }
324
324
  export { CommandReferenceField, ReferenceName, ReviewNamed, commandChoices, readReferenceName, run };
325
325
 
326
- //# sourceMappingURL=shared-DQYNI9wO.js.map
326
+ //# sourceMappingURL=shared-C42f07hJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-DQYNI9wO.js","names":["_Fragment","sayField"],"sources":["../../src/client/copy/forms/components/CommandReference.ts","../../src/client/components/CommandReference.tsx"],"sourcesContent":["/** What CommandReference says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n choicesCouldNotBeLoaded: 'Choices could not be loaded. Try again.',\n loadingChoices: 'Loading choices…',\n noMatchingChoices: 'No matching choices.',\n loadingName: 'Loading…',\n nameCouldNotBeLoaded: 'Its name could not be loaded.',\n tryAgain: 'Try again',\n noLongerAvailable: 'No longer available. Change this answer.',\n});\n","/**\n * A command's reference input as a picker over its resource: a part loaded on\n * demand (CommandFields draws it through `onDemand`), so a public form's first\n * page carries neither this nor the combobox it is drawn with. A signed-in\n * reader reads its choices through their connection; a public form's visitor\n * through the App's signed-out read, which answers only what `auth.public`\n * opens (server forms/savedFormChecks.ts, [public-form-picker]).\n */\nimport { Fragment, useEffect, useRef, useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnDisplay, state } from '@mistriwork/kit/client/core.js';\nimport { fetchServer } from '@mistriwork/kit/client/transport.js';\nimport { ownedBy } from '@mistriwork/kit/client/requestIdentity.js';\nimport type { CommandValue } from '../commandState.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { Button } from '@mistriwork/ui/Button.js';\nimport { offeredChoices } from '@mistriwork/kit/client/components/pickerStaleness.js';\nimport { readsAsName } from '@mistriwork/kit/client/components/Dropdown.js';\nimport { say as sayField } from '../copy/forms/components/CommandFields.js';\nimport { say } from '../copy/forms/components/CommandReference.js';\nimport { andQuery, splitClauseTail } from '@mistriwork/engine/shared/query.js';\nimport { tableNamed, type DatabaseSchema } from '@mistriwork/engine/shared/schema.js';\nimport { knownReferenceName, rememberReferenceName } from './referenceNames.js';\nimport { answered, sayInput } from './reviewValues.js';\nimport type { ReviewSource } from './ReviewValue.js';\nimport type { AppCommandField } from '../../shared/command.js';\n/* The type only: a runtime import back into CommandFields, which loads this file on demand, would be a file loop. */\nimport type { ReferenceField } from './CommandFields.js';\n\n/** Where a picker reads: a signed-in reader's connection, or a public form's App, read as its signed-out visitor. */\nexport type ChoiceSource = string | { visitor: { app: string; via?: string } };\n\n/* A visitor's read is a plain fetch, as the form's submit is: no session plumbing, the App and its intake address in the URL. */\nexport async function run(\n source: ChoiceSource,\n body: { q: string; limit: number; offset: number; count: 'none' },\n): Promise<{ rows?: unknown; errors?: unknown[] }> {\n if (typeof source === 'string') return api('/api/run', { ...ownedBy(source), ...body });\n const { app, via } = source.visitor;\n const query = new URLSearchParams({ app, ...(via ? { via } : {}) });\n const res = await fetchServer(`/api/run?${query}`, {\n method: 'POST',\n credentials: 'same-origin',\n signal: AbortSignal.timeout(15_000),\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(say('choicesCouldNotBeLoaded'));\n return res.json();\n}\n\n/** What a reference's record is called, as the reader may read it: its label, and the second line that tells two of\n one name apart where the App's display shape gives one. */\nexport interface ReferenceName {\n label: string;\n detail?: string;\n}\n\n/**\n * The column that tells two records of one name apart: the `subtitle` the App's display gives the reference's label\n * column on its resource (`display: { name: { subtitle: 'last4' } }`), when this reader's schema holds that column. A\n * visitor's read is the key and the label alone, the columns a public form's picker is checked for\n * (server forms/savedFormChecks.ts, [public-form-picker]).\n */\nfunction referenceDetail(field: ReferenceField, source: ChoiceSource): string | undefined {\n if (typeof source !== 'string' || !state.schema) return undefined;\n const table = tableNamed(state.schema as DatabaseSchema, field.from);\n if (!table) return undefined;\n const subtitle = (columnDisplay(table.id, field.labelColumn) as { subtitle?: unknown } | null)?.subtitle;\n return typeof subtitle === 'string' &&\n subtitle !== field.labelColumn &&\n subtitle !== field.by &&\n table.columns.some((column) => column.name === subtitle)\n ? subtitle\n : undefined;\n}\n\nconst shown = (field: ReferenceField, detail: string | undefined) =>\n `show ${[...new Set([field.by, field.labelColumn, ...(detail ? [detail] : [])])].join(', ')}`;\nconst said = (value: unknown) => (value === undefined || value === null || value === '' ? undefined : String(value));\n\n/**\n * The name of the record a reference holds, read by its key the way its picker reads its choices: the same resource,\n * through the same door (the reader's connection, or the visitor's signed-out read), under the field's own filter, so\n * a key the picker would not offer — a row this reader can no longer read, or one the filter leaves out — has no name\n * (null). Never a name the page was handed: the key is what is sent, and what the server judges.\n */\nexport async function readReferenceName(\n field: ReferenceField,\n value: string | number,\n source: ChoiceSource,\n): Promise<ReferenceName | null> {\n const detail = referenceDetail(field, source);\n const key = `${field.by} = ${typeof value === 'number' ? value : JSON.stringify(value)}`;\n const q = [field.from, andQuery(splitClauseTail(field.query ?? '').head, key), shown(field, detail)].join(' ');\n const data = await run(source, { q, limit: 1, offset: 0, count: 'none' });\n if (data.errors?.length || !Array.isArray(data.rows)) throw new Error(say('choicesCouldNotBeLoaded'));\n const row = data.rows[0] as Record<string, unknown> | undefined;\n const label = said(row?.[field.labelColumn]);\n if (!row || label === undefined) return null;\n const second = detail ? said(row[detail]) : undefined;\n return { label, ...(second ? { detail: second } : {}) };\n}\n\n/**\n * A reference's answer where a form reads its answers back (a review): its record's name, read again as it is drawn,\n * so a choice made before a permission changed is not named from what the page remembers. Until the name is read the\n * answer says so; a name that cannot be read, or a record no longer offered, is said as that — never as its key.\n */\nexport function ReferenceName({\n field,\n value,\n source,\n identityEpoch,\n}: {\n field: ReferenceField;\n value: string | number;\n source: ChoiceSource;\n identityEpoch: number;\n}) {\n const name = useQuery({\n queryKey: ['reference-review-name', identityEpoch, source, field.from, field.by, field.labelColumn, field.query ?? '', value],\n retry: false,\n gcTime: 0,\n staleTime: 0,\n refetchOnMount: 'always',\n queryFn: () => readReferenceName(field, value, source),\n });\n if (name.isPending) return <span aria-busy=\"true\">{say('loadingName')}</span>;\n if (name.isError)\n return (\n <span>\n {say('nameCouldNotBeLoaded')}{' '}\n <Button intent=\"quiet\" size=\"compact\" onClick={() => void name.refetch()}>\n {say('tryAgain')}\n </Button>\n </span>\n );\n if (!name.data) return <span>{say('noLongerAvailable')}</span>;\n return (\n <>\n {name.data.label}\n {name.data.detail && <span className=\"review-detail\"> · {name.data.detail}</span>}\n </>\n );\n}\n\n/** One member's answer on a review, a reference by its record's name. */\nfunction Said({ field, value, from }: { field: AppCommandField; value: unknown; from: ReviewSource }) {\n if (field.type === 'reference' && (typeof value === 'string' || typeof value === 'number') && value !== '')\n return <ReferenceName field={field} value={value} source={from.source} identityEpoch={from.identityEpoch} />;\n return <>{sayInput(field, value)}</>;\n}\n\n/**\n * A review's answer that holds a reference (ReviewValue.tsx): the reference, or a group with reference members, said\n * as reviewLines says a group — a line per member, or per row of a repeating group — each reference by its name.\n * Here, with the picker, so a form's first page carries none of it.\n */\nexport function ReviewNamed({ field, value, from }: { field: AppCommandField; value: unknown; from: ReviewSource }) {\n if (field.type !== 'group') return <Said field={field} value={value} from={from} />;\n type Part = { member: AppCommandField; held: unknown };\n const partsOf = (row: unknown): Part[] =>\n field.fields\n .map((member) => ({ member, held: (row as Record<string, unknown> | undefined)?.[member.name] }))\n .filter(({ held }) => answered(held));\n const lines: Part[][] = field.repeat\n ? (Array.isArray(value) ? value : []).map(partsOf).filter((parts) => parts.length)\n : partsOf(value).map((part) => [part]);\n if (!lines.length) return <>—</>;\n const line = (parts: Part[]) =>\n parts.map(({ member, held }, index) => (\n <Fragment key={member.name}>\n {index > 0 && ', '}\n {field.fields.length > 1 && `${member.label}: `}\n <Said field={member} value={held} from={from} />\n </Fragment>\n ));\n if (lines.length === 1) return <>{line(lines[0])}</>;\n return (\n <ul className=\"review-lines\">\n {lines.map((parts, index) => (\n <li key={index}>{line(parts)}</li>\n ))}\n </ul>\n );\n}\n\n/** A reference input as a picker over its resource: the command modal's, and a signed-in command form's. */\nexport function CommandReferenceField({\n field,\n id,\n describedBy,\n invalid,\n connection,\n identityEpoch,\n value,\n disabled,\n onChange,\n isCurrent,\n}: {\n field: ReferenceField;\n id: string;\n describedBy?: string;\n invalid?: boolean;\n connection: ChoiceSource;\n identityEpoch: number;\n value?: CommandValue;\n disabled: boolean;\n onChange: (value: string | number) => void;\n isCurrent: () => boolean;\n}) {\n /* The box says the record's name: one this picker read for the same key before (its step drawn again, Back from the\n review) at once, else the key until the name below is read. */\n const nameOf = (held: CommandValue | undefined) =>\n typeof held === 'string' || typeof held === 'number' ? knownReferenceName(field, connection, identityEpoch, held) : undefined;\n const [text, setText] = useState(value === undefined ? '' : (nameOf(value) ?? String(value)));\n /* The value the box's words stand for, and the last one it handed up itself. A value that changes from outside —\n the command's starting values, which prepare answers after the picker is drawn — is said afresh: seeded once, at\n mount, the box kept its placeholder over a chosen chair. */\n const [seeded, setSeeded] = useState(value);\n const own = useRef(value);\n if (value !== seeded) {\n setSeeded(value);\n if (value !== own.current) setText(value === undefined || value === '' ? '' : (nameOf(value) ?? String(value)));\n own.current = value;\n }\n const [asked, setAsked] = useState('');\n const [open, setOpen] = useState(false);\n useEffect(() => {\n const timer = setTimeout(() => setAsked(text.trim()), 150);\n return () => clearTimeout(timer);\n }, [text]);\n /* A value the form started with is a key; the box says the record's name, as it does once one is picked. */\n const held = typeof value === 'string' || typeof value === 'number' ? value : undefined;\n const named = useQuery({\n queryKey: ['command-choice-label', identityEpoch, connection, field.from, field.by, field.labelColumn, field.query ?? '', held],\n retry: false,\n enabled: held !== undefined && held !== '' && isCurrent(),\n queryFn: async () => {\n const read = await readReferenceName(field, held!, connection);\n if (read) rememberReferenceName(field, connection, identityEpoch, held!, read.label);\n return read?.label ?? null;\n },\n });\n useEffect(() => {\n if (named.data && held !== undefined && text === String(held)) setText(named.data);\n }, [named.data, held, text]);\n const choices = useQuery({\n queryKey: ['command-choices', identityEpoch, connection, field, asked],\n retry: false,\n enabled: open && !disabled && isCurrent(),\n queryFn: async () => {\n if (!isCurrent()) throw new Error(sayField('common.thisCommandFormIsNoLonger'));\n const result = await commandChoices(field, asked, connection);\n if (!isCurrent()) throw new Error(sayField('common.thisCommandFormIsNoLonger'));\n return result;\n },\n });\n /* For the pause before the typed search is asked, the list answers earlier\n text with its first row lit: offer only the rows the box's text names, so\n Enter cannot put a reviewer nobody typed into the command. */\n const { choices: offered, answered } = offeredChoices({\n typed: text,\n asked,\n placeholder: choices.isPlaceholderData,\n rows: choices.data,\n label: (choice) => choice.label,\n });\n return (\n <div className=\"picker\">\n <Typeahead\n text={text}\n onText={(next) => {\n setText(next);\n own.current = '';\n onChange('');\n }}\n open={open && !disabled}\n onOpenChange={setOpen}\n items={offered}\n itemKey={(choice) => `${typeof choice.value}:${choice.value}`}\n itemLabel={(choice) => choice.label}\n itemClassName={(_choice, highlighted) => `picker-option${highlighted ? ' active' : ''}`}\n renderItem={(choice) => (\n <>\n <span className=\"picker-option-label\">{choice.label}</span>\n {/* Two records of one name are told apart by the second line the App's display gives them. */}\n {choice.detail && <span className=\"picker-option-detail\">{choice.detail}</span>}\n </>\n )}\n onPick={(choice) => {\n if (isCurrent()) {\n rememberReferenceName(field, connection, identityEpoch, choice.value, choice.label);\n own.current = choice.value;\n onChange(choice.value);\n setText(choice.label);\n setOpen(false);\n }\n }}\n inputProps={{\n /* Named by the field's <label for> (components/Field.tsx), \"(required)\" and all, as a textbox is. */\n id,\n 'aria-describedby': describedBy,\n 'aria-invalid': invalid || undefined,\n required: field.required,\n disabled,\n className: 'picker-input',\n placeholder: readsAsName(field.label)\n ? sayField('choosePlaceholder', { label: field.label.toLowerCase() })\n : sayField('chooseOne'),\n }}\n fit=\"width\"\n popupClassName=\"picker-popup\"\n listClassName=\"picker-list\"\n emptyClassName=\"picker-empty\"\n empty={\n choices.isError\n ? say('choicesCouldNotBeLoaded')\n : choices.isFetching || !answered\n ? say('loadingChoices')\n : say('noMatchingChoices')\n }\n />\n </div>\n );\n}\n\ninterface Choice {\n value: string | number;\n label: string;\n detail?: string;\n}\nexport async function commandChoices(field: ReferenceField, text: string, source: ChoiceSource): Promise<Choice[]> {\n // Authored filters constrain eligibility; this picker owns projection, ordering and page size.\n const condition = andQuery(splitClauseTail(field.query ?? '').head, text ? `${field.labelColumn} contains ${JSON.stringify(text)}` : '');\n const detail = referenceDetail(field, source);\n const q = [field.from, condition, shown(field, detail), `sort ${field.labelColumn}`].filter(Boolean).join(' ');\n const data = await run(source, { q, limit: 20, offset: 0, count: 'none' });\n if (data.errors?.length || !Array.isArray(data.rows)) throw new Error(say('choicesCouldNotBeLoaded'));\n return data.rows.slice(0, 20).flatMap((row: Record<string, unknown>) => {\n const value = row[field.by];\n return typeof value === 'string' || (typeof value === 'number' && Number.isSafeInteger(value))\n ? [{ value, label: String(row[field.labelColumn] ?? value), ...(detail && said(row[detail]) ? { detail: said(row[detail])! } : {}) }]\n : [];\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-C42f07hJ.js","names":["_Fragment","sayField"],"sources":["../../src/client/copy/forms/components/CommandReference.ts","../../src/client/components/CommandReference.tsx"],"sourcesContent":["/** What CommandReference says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n choicesCouldNotBeLoaded: 'Choices could not be loaded. Try again.',\n loadingChoices: 'Loading choices…',\n noMatchingChoices: 'No matching choices.',\n loadingName: 'Loading…',\n nameCouldNotBeLoaded: 'Its name could not be loaded.',\n tryAgain: 'Try again',\n noLongerAvailable: 'No longer available. Change this answer.',\n});\n","/**\n * A command's reference input as a picker over its resource: a part loaded on\n * demand (CommandFields draws it through `onDemand`), so a public form's first\n * page carries neither this nor the combobox it is drawn with. A signed-in\n * reader reads its choices through their connection; a public form's visitor\n * through the App's signed-out read, which answers only what `auth.public`\n * opens (server forms/savedFormChecks.ts, [public-form-picker]).\n */\nimport { Fragment, useEffect, useRef, useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnDisplay, state } from '@mistriwork/kit/client/core.js';\nimport { fetchServer } from '@mistriwork/kit/client/transport.js';\nimport { ownedBy } from '@mistriwork/kit/client/requestIdentity.js';\nimport type { CommandValue } from '../commandState.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { Button } from '@mistriwork/ui/Button.js';\nimport { offeredChoices } from '@mistriwork/kit/client/components/pickerStaleness.js';\nimport { readsAsName } from '@mistriwork/kit/client/components/Dropdown.js';\nimport { say as sayField } from '../copy/forms/components/CommandFields.js';\nimport { say } from '../copy/forms/components/CommandReference.js';\nimport { andQuery, splitClauseTail } from '@mistriwork/engine/shared/query.js';\nimport { tableNamed, type DatabaseSchema } from '@mistriwork/engine/shared/schema.js';\nimport { knownReferenceName, rememberReferenceName } from './referenceNames.js';\nimport { answered, sayInput } from './reviewValues.js';\nimport type { ReviewSource } from './ReviewValue.js';\nimport type { AppCommandField } from '../../shared/command.js';\n/* The type only: a runtime import back into CommandFields, which loads this file on demand, would be a file loop. */\nimport type { ReferenceField } from './CommandFields.js';\n\n/** Where a picker reads: a signed-in reader's connection, or a public form's App, read as its signed-out visitor. */\nexport type ChoiceSource = string | { visitor: { app: string; via?: string } };\n\n/* A visitor's read is a plain fetch, as the form's submit is: no session plumbing, the App and its intake address in the URL. */\nexport async function run(\n source: ChoiceSource,\n body: { q: string; limit: number; offset: number; count: 'none' },\n): Promise<{ rows?: unknown; errors?: unknown[] }> {\n if (typeof source === 'string') return api('/api/run', { ...ownedBy(source), ...body });\n const { app, via } = source.visitor;\n const query = new URLSearchParams({ app, ...(via ? { via } : {}) });\n const res = await fetchServer(`/api/run?${query}`, {\n method: 'POST',\n credentials: 'same-origin',\n signal: AbortSignal.timeout(15_000),\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(say('choicesCouldNotBeLoaded'));\n return res.json();\n}\n\n/** What a reference's record is called, as the reader may read it: its label, and the second line that tells two of\n one name apart where the App's display shape gives one. */\nexport interface ReferenceName {\n label: string;\n detail?: string;\n}\n\n/**\n * The column that tells two records of one name apart: the `subtitle` the App's display gives the reference's label\n * column on its resource (`display: { name: { subtitle: 'last4' } }`), when this reader's schema holds that column. A\n * visitor's read is the key and the label alone, the columns a public form's picker is checked for\n * (server forms/savedFormChecks.ts, [public-form-picker]).\n */\nfunction referenceDetail(field: ReferenceField, source: ChoiceSource): string | undefined {\n if (typeof source !== 'string' || !state.schema) return undefined;\n const table = tableNamed(state.schema as DatabaseSchema, field.from);\n if (!table) return undefined;\n const subtitle = (columnDisplay(table.id, field.labelColumn) as { subtitle?: unknown } | null)?.subtitle;\n return typeof subtitle === 'string' &&\n subtitle !== field.labelColumn &&\n subtitle !== field.by &&\n table.columns.some((column) => column.name === subtitle)\n ? subtitle\n : undefined;\n}\n\nconst shown = (field: ReferenceField, detail: string | undefined) =>\n `show ${[...new Set([field.by, field.labelColumn, ...(detail ? [detail] : [])])].join(', ')}`;\nconst said = (value: unknown) => (value === undefined || value === null || value === '' ? undefined : String(value));\n\n/**\n * The name of the record a reference holds, read by its key the way its picker reads its choices: the same resource,\n * through the same door (the reader's connection, or the visitor's signed-out read), under the field's own filter, so\n * a key the picker would not offer — a row this reader can no longer read, or one the filter leaves out — has no name\n * (null). Never a name the page was handed: the key is what is sent, and what the server judges.\n */\nexport async function readReferenceName(\n field: ReferenceField,\n value: string | number,\n source: ChoiceSource,\n): Promise<ReferenceName | null> {\n const detail = referenceDetail(field, source);\n const key = `${field.by} = ${typeof value === 'number' ? value : JSON.stringify(value)}`;\n const q = [field.from, andQuery(splitClauseTail(field.query ?? '').head, key), shown(field, detail)].join(' ');\n const data = await run(source, { q, limit: 1, offset: 0, count: 'none' });\n if (data.errors?.length || !Array.isArray(data.rows)) throw new Error(say('choicesCouldNotBeLoaded'));\n const row = data.rows[0] as Record<string, unknown> | undefined;\n const label = said(row?.[field.labelColumn]);\n if (!row || label === undefined) return null;\n const second = detail ? said(row[detail]) : undefined;\n return { label, ...(second ? { detail: second } : {}) };\n}\n\n/**\n * A reference's answer where a form reads its answers back (a review): its record's name, read again as it is drawn,\n * so a choice made before a permission changed is not named from what the page remembers. Until the name is read the\n * answer says so; a name that cannot be read, or a record no longer offered, is said as that — never as its key.\n */\nexport function ReferenceName({\n field,\n value,\n source,\n identityEpoch,\n}: {\n field: ReferenceField;\n value: string | number;\n source: ChoiceSource;\n identityEpoch: number;\n}) {\n const name = useQuery({\n queryKey: ['reference-review-name', identityEpoch, source, field.from, field.by, field.labelColumn, field.query ?? '', value],\n retry: false,\n gcTime: 0,\n staleTime: 0,\n refetchOnMount: 'always',\n queryFn: () => readReferenceName(field, value, source),\n });\n if (name.isPending) return <span aria-busy=\"true\">{say('loadingName')}</span>;\n if (name.isError)\n return (\n <span>\n {say('nameCouldNotBeLoaded')}{' '}\n <Button intent=\"quiet\" size=\"compact\" onClick={() => void name.refetch()}>\n {say('tryAgain')}\n </Button>\n </span>\n );\n if (!name.data) return <span>{say('noLongerAvailable')}</span>;\n return (\n <>\n {name.data.label}\n {name.data.detail && <span className=\"review-detail\"> · {name.data.detail}</span>}\n </>\n );\n}\n\n/** One member's answer on a review, a reference by its record's name. */\nfunction Said({ field, value, from }: { field: AppCommandField; value: unknown; from: ReviewSource }) {\n if (field.type === 'reference' && (typeof value === 'string' || typeof value === 'number') && value !== '')\n return <ReferenceName field={field} value={value} source={from.source} identityEpoch={from.identityEpoch} />;\n return <>{sayInput(field, value)}</>;\n}\n\n/**\n * A review's answer that holds a reference (ReviewValue.tsx): the reference, or a group with reference members, said\n * as reviewLines says a group — a line per member, or per row of a repeating group — each reference by its name.\n * Here, with the picker, so a form's first page carries none of it.\n */\nexport function ReviewNamed({ field, value, from }: { field: AppCommandField; value: unknown; from: ReviewSource }) {\n if (field.type !== 'group') return <Said field={field} value={value} from={from} />;\n type Part = { member: AppCommandField; held: unknown };\n const partsOf = (row: unknown): Part[] =>\n field.fields\n .map((member) => ({ member, held: (row as Record<string, unknown> | undefined)?.[member.name] }))\n .filter(({ held }) => answered(held));\n const lines: Part[][] = field.repeat\n ? (Array.isArray(value) ? value : []).map(partsOf).filter((parts) => parts.length)\n : partsOf(value).map((part) => [part]);\n if (!lines.length) return <>—</>;\n const line = (parts: Part[]) =>\n parts.map(({ member, held }, index) => (\n <Fragment key={member.name}>\n {index > 0 && ', '}\n {field.fields.length > 1 && `${member.label}: `}\n <Said field={member} value={held} from={from} />\n </Fragment>\n ));\n if (lines.length === 1) return <>{line(lines[0])}</>;\n return (\n <ul className=\"review-lines\">\n {lines.map((parts, index) => (\n <li key={index}>{line(parts)}</li>\n ))}\n </ul>\n );\n}\n\n/** A reference input as a picker over its resource: the command modal's, and a signed-in command form's. */\nexport function CommandReferenceField({\n field,\n id,\n describedBy,\n invalid,\n connection,\n identityEpoch,\n value,\n disabled,\n onChange,\n isCurrent,\n}: {\n field: ReferenceField;\n id: string;\n describedBy?: string;\n invalid?: boolean;\n connection: ChoiceSource;\n identityEpoch: number;\n value?: CommandValue;\n disabled: boolean;\n onChange: (value: string | number) => void;\n isCurrent: () => boolean;\n}) {\n /* The box says the record's name: one this picker read for the same key before (its step drawn again, Back from the\n review) at once, else the key until the name below is read. */\n const nameOf = (held: CommandValue | undefined) =>\n typeof held === 'string' || typeof held === 'number' ? knownReferenceName(field, connection, identityEpoch, held) : undefined;\n const [text, setText] = useState(value === undefined ? '' : (nameOf(value) ?? String(value)));\n /* The value the box's words stand for, and the last one it handed up itself. A value that changes from outside —\n the command's starting values, which prepare answers after the picker is drawn — is said afresh: seeded once, at\n mount, the box kept its placeholder over a chosen chair. */\n const [seeded, setSeeded] = useState(value);\n const own = useRef(value);\n if (value !== seeded) {\n setSeeded(value);\n if (value !== own.current) setText(value === undefined || value === '' ? '' : (nameOf(value) ?? String(value)));\n own.current = value;\n }\n const [asked, setAsked] = useState('');\n const [open, setOpen] = useState(false);\n useEffect(() => {\n const timer = setTimeout(() => setAsked(text.trim()), 150);\n return () => clearTimeout(timer);\n }, [text]);\n /* A value the form started with is a key; the box says the record's name, as it does once one is picked. */\n const held = typeof value === 'string' || typeof value === 'number' ? value : undefined;\n const named = useQuery({\n queryKey: ['command-choice-label', identityEpoch, connection, field.from, field.by, field.labelColumn, field.query ?? '', held],\n retry: false,\n enabled: held !== undefined && held !== '' && isCurrent(),\n queryFn: async () => {\n const read = await readReferenceName(field, held!, connection);\n if (read) rememberReferenceName(field, connection, identityEpoch, held!, read.label);\n return read?.label ?? null;\n },\n });\n useEffect(() => {\n if (named.data && held !== undefined && text === String(held)) setText(named.data);\n }, [named.data, held, text]);\n const choices = useQuery({\n queryKey: ['command-choices', identityEpoch, connection, field, asked],\n retry: false,\n enabled: open && !disabled && isCurrent(),\n queryFn: async () => {\n if (!isCurrent()) throw new Error(sayField('common.thisCommandFormIsNoLonger'));\n const result = await commandChoices(field, asked, connection);\n if (!isCurrent()) throw new Error(sayField('common.thisCommandFormIsNoLonger'));\n return result;\n },\n });\n /* For the pause before the typed search is asked, the list answers earlier\n text with its first row lit: offer only the rows the box's text names, so\n Enter cannot put a reviewer nobody typed into the command. */\n const { choices: offered, answered } = offeredChoices({\n typed: text,\n asked,\n placeholder: choices.isPlaceholderData,\n rows: choices.data,\n label: (choice) => choice.label,\n });\n return (\n <div className=\"picker\">\n <Typeahead\n text={text}\n onText={(next) => {\n setText(next);\n own.current = '';\n onChange('');\n }}\n open={open && !disabled}\n onOpenChange={setOpen}\n items={offered}\n itemKey={(choice) => `${typeof choice.value}:${choice.value}`}\n itemLabel={(choice) => choice.label}\n itemClassName={(_choice, highlighted) => `picker-option${highlighted ? ' active' : ''}`}\n renderItem={(choice) => (\n <>\n <span className=\"picker-option-label\">{choice.label}</span>\n {/* Two records of one name are told apart by the second line the App's display gives them. */}\n {choice.detail && <span className=\"picker-option-detail\">{choice.detail}</span>}\n </>\n )}\n onPick={(choice) => {\n if (isCurrent()) {\n rememberReferenceName(field, connection, identityEpoch, choice.value, choice.label);\n own.current = choice.value;\n onChange(choice.value);\n setText(choice.label);\n setOpen(false);\n }\n }}\n inputProps={{\n /* Named by the field's <label for> (components/Field.tsx), \"(required)\" and all, as a textbox is. */\n id,\n 'aria-describedby': describedBy,\n 'aria-invalid': invalid || undefined,\n required: field.required,\n disabled,\n className: 'picker-input',\n placeholder: readsAsName(field.label)\n ? sayField('choosePlaceholder', { label: field.label.toLowerCase() })\n : sayField('chooseOne'),\n }}\n fit=\"width\"\n popupClassName=\"picker-popup\"\n listClassName=\"picker-list\"\n emptyClassName=\"picker-empty\"\n empty={\n choices.isError\n ? say('choicesCouldNotBeLoaded')\n : choices.isFetching || !answered\n ? say('loadingChoices')\n : say('noMatchingChoices')\n }\n />\n </div>\n );\n}\n\ninterface Choice {\n value: string | number;\n label: string;\n detail?: string;\n}\nexport async function commandChoices(field: ReferenceField, text: string, source: ChoiceSource): Promise<Choice[]> {\n // Authored filters constrain eligibility; this picker owns projection, ordering and page size.\n const condition = andQuery(splitClauseTail(field.query ?? '').head, text ? `${field.labelColumn} contains ${JSON.stringify(text)}` : '');\n const detail = referenceDetail(field, source);\n const q = [field.from, condition, shown(field, detail), `sort ${field.labelColumn}`].filter(Boolean).join(' ');\n const data = await run(source, { q, limit: 20, offset: 0, count: 'none' });\n if (data.errors?.length || !Array.isArray(data.rows)) throw new Error(say('choicesCouldNotBeLoaded'));\n return data.rows.slice(0, 20).flatMap((row: Record<string, unknown>) => {\n const value = row[field.by];\n return typeof value === 'string' || (typeof value === 'number' && Number.isSafeInteger(value))\n ? [{ value, label: String(row[field.labelColumn] ?? value), ...(detail && said(row[detail]) ? { detail: said(row[detail])! } : {}) }]\n : [];\n });\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { gt as defineMessages, nt as richTextImageAddress } from "./shared-DJZjSi3J.js";
2
- import { i as api } from "./shared-DPAVoWY0.js";
3
- import { g as pageBridge } from "./shared-C4UrE8Fb.js";
2
+ import { i as api } from "./shared-BoLbc2WD.js";
3
+ import { g as pageBridge } from "./shared-Chp88all.js";
4
4
  import { useEffect, useId, useRef, useState } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
6
  // packages/mistri/src/client/copy/record/components/builtInInputs.ts
@@ -565,4 +565,4 @@ function builtInInput(name) {
565
565
  }
566
566
  export { builtInInput };
567
567
 
568
- //# sourceMappingURL=shared-CqCZ8wMY.js.map
568
+ //# sourceMappingURL=shared-C4LkPCU4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-CqCZ8wMY.js","names":["_Fragment"],"sources":["../../src/client/copy/record/components/builtInInputs.ts","../../src/client/components/builtInInputs.tsx"],"sourcesContent":["/** What builtInInputs says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n stars: '{stars} of {outOf} stars',\n pickColor: 'Pick {label}',\n jsonProblem: 'Not valid JSON: {problem}',\n formatJson: 'Format',\n scan: 'Scan',\n stopScanning: 'Stop scanning',\n cameraView: 'Camera: hold the code in view',\n cameraRefused: 'The camera could not be opened. Type the code instead.',\n signHere: 'Sign here: {label}',\n typedSignature: '{label}, typed',\n typeYourName: 'Type your full name',\n keptSignature: '{label}: the kept signature',\n undoStroke: 'Undo',\n clearSignature: 'Clear',\n typeInstead: 'Type instead',\n drawInstead: 'Draw instead',\n useSignature: 'Use this signature',\n keepingSignature: 'Keeping…',\n keepPrevious: 'Keep the previous one',\n signAgain: 'Sign again',\n removeSignature: 'Remove',\n signatureUnread: 'The signature could not be kept. Try again.',\n});\n","/**\n * The platform's own inputs for the display formats that are edited as\n * something other than a box of text: a rating's stars, a slider along a\n * number's range, a color, a time of day, a month, a year, a JSON\n * document, a code the camera reads and a signature.\n *\n * Each is an input component like an author's (inputRenderers.tsx): it\n * stands in a record's edit form, its New form and a grid cell's editor,\n * hands the form a value through `onChange`, and is fetched the first time a\n * field needs it, so no first page carries it. Until it is here the plain box\n * stands in its place, and the form's rules and the server still judge what\n * it hands back.\n *\n * The native pickers do the hard parts — a color, a time, a month — so a\n * keyboard and a screen reader get the browser's own control; the stars are\n * a radio group, one tab stop, the arrow keys moving the pick.\n */\nimport { useEffect, useId, useRef, useState, type ComponentType, type KeyboardEvent, type PointerEvent } from 'react';\nimport { api } from '@mistriwork/kit/client/core.js';\nimport { pageBridge } from '@mistriwork/kit/client/uiBridge.js';\nimport { richTextImageAddress } from '@mistriwork/engine/shared/richText.js';\nimport { say } from '../copy/record/components/builtInInputs.js';\nimport './builtInInputs.css';\n\ninterface InputProps {\n value: string | number | boolean | null;\n onChange(value: string | number | boolean | null): void;\n id: string;\n describedBy?: string;\n invalid: boolean;\n disabled: boolean;\n required: boolean;\n label: string;\n field: { name: string; resource?: string };\n}\n\ntype Rule = { outOf?: number; slider?: { min: number; max: number; step?: number }; signature?: true | { table: string; key: string } };\n\n/** The column's display rule, as the App's payload carries it. */\nfunction ruleOf(field: InputProps['field']): Rule {\n const resources = (pageBridge.activeApp() as { resources?: Record<string, { display?: Record<string, Rule> }> } | null)?.resources;\n const display = field.resource ? resources?.[field.resource]?.display : undefined;\n return display && Object.hasOwn(display, field.name) ? display[field.name] : {};\n}\n\nconst text = (value: InputProps['value']) => (value === null || value === undefined ? '' : String(value));\n\n/** Stars as a radio group: a click picks one, picking the one already picked clears it where the field may be empty. */\nfunction RatingInput({ value, onChange, id, describedBy, invalid, disabled, required, label, field }: InputProps) {\n const outOf = ruleOf(field).outOf ?? 5;\n const picked = Math.max(0, Math.min(outOf, Math.round(Number(value) || 0)));\n const name = useId();\n const pick = (n: number) => onChange(n === picked && !required ? null : n);\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const step =\n event.key === 'ArrowRight' || event.key === 'ArrowUp' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowDown' ? -1 : 0;\n if (!step) return;\n event.preventDefault();\n const next = Math.max(required ? 1 : 0, Math.min(outOf, picked + step));\n onChange(next === 0 ? null : next);\n };\n return (\n <div\n id={id}\n className=\"rating-input\"\n role=\"radiogroup\"\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n aria-required={required || undefined}\n onKeyDown={onKeyDown}\n >\n {Array.from({ length: outOf }, (_, index) => {\n const n = index + 1;\n return (\n <button\n key={n}\n type=\"button\"\n role=\"radio\"\n name={name}\n className={n <= picked ? 'rating-input-star is-on' : 'rating-input-star'}\n aria-checked={n === picked}\n aria-label={say('stars', { stars: n, outOf })}\n tabIndex={n === (picked || 1) ? 0 : -1}\n disabled={disabled}\n onClick={() => pick(n)}\n >\n ★\n </button>\n );\n })}\n </div>\n );\n}\n\n/** A range with its figure beside it, the figure live as the thumb moves. */\nfunction SliderInput({ value, onChange, id, describedBy, invalid, disabled, required, label, field }: InputProps) {\n const { min = 0, max = 100, step = 1 } = ruleOf(field).slider ?? {};\n const shown = text(value);\n return (\n <span className=\"slider-input\">\n <input\n id={id}\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={shown === '' ? min : shown}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(Number(event.target.value))}\n />\n <output htmlFor={id} className=\"slider-input-figure num-value\">\n {shown === '' ? '—' : shown}\n </output>\n </span>\n );\n}\n\n/** The system's color picker, its code beside it as a box of its own for a reader who has one to paste. */\nfunction ColorInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const shown = text(value);\n const valid = /^#[0-9a-f]{6}$/i.test(shown);\n return (\n <span className=\"color-input\">\n <input\n type=\"color\"\n className=\"color-input-picker\"\n value={valid ? shown.toLowerCase() : '#000000'}\n aria-label={say('pickColor', { label })}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value)}\n />\n <input\n id={id}\n className=\"color-input-code\"\n type=\"text\"\n value={shown}\n placeholder=\"#1d6a5a\"\n spellCheck={false}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value.trim() || null)}\n />\n </span>\n );\n}\n\n/** One native picker: a time of day, a month, a year (a number box, since there is no year picker). */\nfunction nativeInput(type: 'time' | 'month' | 'year'): ComponentType<InputProps> {\n function NativeInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const raw = text(value);\n /* A stored time keeps its seconds (\"14:30:00\") and a month may be a date (\"2026-10-01\"): the picker takes the part it shows. */\n const shown = type === 'time' ? raw.slice(0, 5) : type === 'month' ? raw.slice(0, 7) : raw;\n return (\n <input\n id={id}\n className={`native-input ${type}-input`}\n type={type === 'year' ? 'number' : type}\n {...(type === 'year' ? { min: 1, max: 9999, step: 1, inputMode: 'numeric' as const } : {})}\n value={shown}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => {\n const next = event.target.value;\n onChange(next === '' ? null : type === 'year' ? Number(next) : next);\n }}\n />\n );\n }\n return NativeInput;\n}\n\n/** Where a JSON text stops parsing, said as the parser says it, or null for a document that reads. */\nexport function jsonProblem(source: string): string | null {\n if (!source.trim()) return null;\n try {\n JSON.parse(source);\n return null;\n } catch (error) {\n return (error as Error).message;\n }\n}\n\n/** A stored document as text to edit: a string as it is, an object (a Postgres jsonb) laid out two spaces deep. */\nconst jsonText = (value: unknown) =>\n value === null || value === undefined ? '' : typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);\n\n/**\n * A JSON document in a box of its own: monospaced, tab-free, its parse problem\n * said under it while it is typed, and a Format button that lays a document\n * that reads out two spaces deep. The text is the value, so what was typed is\n * what is kept until it reads; the server refuses a document that does not\n * (appValidation.ts), whatever control sent it.\n */\nfunction JsonInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const shown = jsonText(value);\n const [problem, setProblem] = useState(() => jsonProblem(shown));\n const problemId = useId();\n const rows = Math.min(16, Math.max(4, shown.split('\\n').length + 1));\n const change = (next: string) => {\n setProblem(jsonProblem(next));\n onChange(next.trim() ? next : null);\n };\n return (\n <span className=\"json-input\">\n <textarea\n id={id}\n className=\"json-input-text\"\n value={shown}\n rows={rows}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n aria-label={label}\n aria-describedby={[describedBy, problem ? problemId : ''].filter(Boolean).join(' ') || undefined}\n aria-invalid={invalid || Boolean(problem) || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => change(event.target.value)}\n />\n <span className=\"json-input-foot\">\n <span id={problemId} className=\"json-input-problem\" aria-live=\"polite\">\n {problem ? say('jsonProblem', { problem }) : ''}\n </span>\n <button\n type=\"button\"\n className=\"btn btn-quiet json-input-format\"\n disabled={disabled || Boolean(problem) || !shown.trim()}\n onClick={() => change(JSON.stringify(JSON.parse(shown), null, 2))}\n >\n {say('formatJson')}\n </button>\n </span>\n </span>\n );\n}\n\n/** The browser's code reader where it has one (Chromium on Android and macOS, Safari 17+): absent elsewhere. */\ntype CodeReader = { detect(source: HTMLVideoElement): Promise<Array<{ rawValue?: string }>> };\nconst codeReader = (): (new () => CodeReader) | undefined =>\n typeof window === 'undefined' || !navigator.mediaDevices?.getUserMedia\n ? undefined\n : (window as unknown as { BarcodeDetector?: new () => CodeReader }).BarcodeDetector;\n\n/** The camera, looking for a code five times a second until it reads one; the stream stops when it closes. */\nfunction Camera({ onRead, onFail }: { onRead(code: string): void; onFail(): void }) {\n const video = useRef<HTMLVideoElement>(null);\n useEffect(() => {\n const Reader = codeReader();\n let stream: MediaStream | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n void navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }).then(\n async (opened) => {\n stream = opened;\n if (stopped || !video.current || !Reader) return opened.getTracks().forEach((track) => track.stop());\n video.current.srcObject = opened;\n await video.current.play().catch(() => undefined);\n const reader = new Reader();\n const look = async () => {\n if (stopped || !video.current) return;\n const found = await reader.detect(video.current).catch(() => []);\n const code = found.find((item) => item.rawValue)?.rawValue;\n if (code) onRead(code);\n else timer = setTimeout(look, 200);\n };\n void look();\n },\n () => !stopped && onFail(),\n );\n return () => {\n stopped = true;\n clearTimeout(timer);\n stream?.getTracks().forEach((track) => track.stop());\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n return <video ref={video} className=\"scan-input-video\" muted playsInline aria-label={say('cameraView')} />;\n}\n\n/**\n * A code typed, or read by the camera where the browser reads codes: a box\n * that always takes typing, so the form never waits on a camera, and beside\n * it a Scan button only where there is something to scan with. A read code\n * replaces the box's text, which can still be corrected by hand.\n */\nfunction ScanInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const [open, setOpen] = useState(false);\n const [refused, setRefused] = useState(false);\n const can = Boolean(codeReader());\n return (\n <span className=\"scan-input\">\n <span className=\"scan-input-row\">\n <input\n id={id}\n type=\"text\"\n className=\"scan-input-text\"\n value={text(value)}\n autoComplete=\"off\"\n spellCheck={false}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value === '' ? null : event.target.value)}\n />\n {can && (\n <button\n type=\"button\"\n className=\"btn btn-quiet scan-input-button\"\n aria-pressed={open}\n disabled={disabled}\n onClick={() => {\n setRefused(false);\n setOpen(!open);\n }}\n >\n {say(open ? 'stopScanning' : 'scan')}\n </button>\n )}\n </span>\n {open && (\n <Camera\n onRead={(code) => {\n onChange(code);\n setOpen(false);\n }}\n onFail={() => {\n setRefused(true);\n setOpen(false);\n }}\n />\n )}\n {refused && (\n <span className=\"scan-input-note\" role=\"status\">\n {say('cameraRefused')}\n </span>\n )}\n </span>\n );\n}\n\n/** A canvas's picture as base64, for the file route. */\nasync function pngOf(canvas: HTMLCanvasElement): Promise<string> {\n const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, 'image/png'));\n if (!blob) throw new Error(say('signatureUnread'));\n const bytes = new Uint8Array(await blob.arrayBuffer());\n let out = '';\n for (let i = 0; i < bytes.length; i += 0x8000) out += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n return btoa(out);\n}\n\ntype Stroke = Array<[number, number]>;\n/** The pad's drawing size in CSS pixels; the canvas is this times the screen's density, so a line stays sharp. */\nconst PAD = { wide: 480, high: 150 };\n\n/**\n * A signature: drawn with a finger, a pen or the mouse (each stroke undone in\n * turn, or all cleared), or a name typed where drawing is not possible, and\n * kept with \"Use this signature\" as a PNG row of the file resource the column\n * points at; the form holds that row's key. A kept signature is shown as its\n * picture with \"Sign again\" beside it. Nothing is stored until it is kept.\n */\nfunction SignatureInput({ value, onChange, id, describedBy, invalid, disabled, label, field }: InputProps) {\n const rule = ruleOf(field).signature;\n const keeps = rule && typeof rule === 'object' ? rule : undefined;\n const canvas = useRef<HTMLCanvasElement>(null);\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [typed, setTyped] = useState<string | null>(null);\n const [signing, setSigning] = useState(value === null || value === '');\n const [state, setState] = useState<'idle' | 'keeping' | 'failed'>('idle');\n const drawing = useRef<Stroke | null>(null);\n const again = useRef<HTMLButtonElement>(null);\n const kept = useRef(false);\n /* The button that kept the drawing is gone once it is kept: the focus moves to Sign again, inside the field, so a\n form that saves when the focus leaves the field saves when the reader moves on, not before. */\n useEffect(() => {\n if (kept.current && again.current) {\n kept.current = false;\n again.current.focus();\n }\n });\n const empty = typed === null ? strokes.length === 0 : !typed.trim();\n\n /* The whole drawing is laid again from its strokes on every change: an undo is a stroke left out. */\n useEffect(() => {\n const element = canvas.current;\n const context = element?.getContext('2d');\n if (!element || !context) return;\n const density = window.devicePixelRatio || 1;\n element.width = PAD.wide * density;\n element.height = PAD.high * density;\n context.setTransform(density, 0, 0, density, 0, 0);\n context.clearRect(0, 0, PAD.wide, PAD.high);\n // Ink on paper: the pad is white in every look, so the kept picture reads wherever it is shown.\n context.strokeStyle = context.fillStyle = '#111827';\n context.lineWidth = 2.4;\n context.lineCap = context.lineJoin = 'round';\n if (typed !== null) {\n context.font = 'italic 40px Georgia, \"Times New Roman\", serif';\n context.textBaseline = 'middle';\n context.fillText(typed, 24, PAD.high / 2, PAD.wide - 48);\n return;\n }\n for (const stroke of [...strokes, ...(drawing.current ? [drawing.current] : [])]) {\n context.beginPath();\n stroke.forEach(([x, y], i) => (i ? context.lineTo(x, y) : context.moveTo(x, y)));\n if (stroke.length === 1) context.lineTo(stroke[0][0] + 0.1, stroke[0][1]);\n context.stroke();\n }\n });\n\n const at = (event: PointerEvent<HTMLCanvasElement>): [number, number] => {\n const box = event.currentTarget.getBoundingClientRect();\n // screen-px: the pointer's ruler, mapped onto the pad's own units by the ratio.\n return [((event.clientX - box.left) / (box.width || 1)) * PAD.wide, ((event.clientY - box.top) / (box.height || 1)) * PAD.high];\n };\n const [, redraw] = useState(0);\n\n const keep = async () => {\n if (!keeps || !canvas.current) return;\n setState('keeping');\n try {\n const reply = (await api('/api/file/upload', {\n table: keeps.table,\n values: {},\n file: { name: 'signature.png', type: 'image/png', data: await pngOf(canvas.current) },\n })) as { key?: Record<string, string | number>; errors?: unknown[]; error?: unknown };\n const stored = reply.key?.[keeps.key];\n if (stored === undefined || reply.errors || reply.error) throw new Error(say('signatureUnread'));\n onChange(typeof stored === 'number' ? stored : String(stored));\n kept.current = true;\n setSigning(false);\n setStrokes([]);\n setTyped(null);\n setState('idle');\n } catch {\n setState('failed');\n }\n };\n\n if (!signing && value !== null && value !== '' && keeps)\n return (\n <span className=\"signature-input\">\n <img\n className=\"signature-input-kept\"\n src={richTextImageAddress(keeps.table, { [keeps.key]: typeof value === 'number' ? value : String(value) })}\n alt={say('keptSignature', { label })}\n />\n <span className=\"signature-input-tools\">\n <button ref={again} type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => setSigning(true)}>\n {say('signAgain')}\n </button>\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => onChange(null)}>\n {say('removeSignature')}\n </button>\n </span>\n </span>\n );\n return (\n <span className=\"signature-input\" aria-describedby={describedBy}>\n {typed === null ? (\n <canvas\n id={id}\n ref={canvas}\n className=\"signature-input-pad\"\n /* Focusable by the pointer, not by Tab: a press on the pad keeps the focus inside the field, so a form that\n closes its editor when the focus leaves keeps it open while a signature is drawn. Keyboards type instead. */\n tabIndex={-1}\n role=\"img\"\n aria-label={say('signHere', { label })}\n aria-invalid={invalid || undefined}\n onPointerDown={(event) => {\n if (disabled) return;\n event.currentTarget.setPointerCapture?.(event.pointerId);\n drawing.current = [at(event)];\n redraw((n) => n + 1);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(at(event));\n redraw((n) => n + 1);\n }}\n onPointerUp={() => {\n // Taken before it is let go: the update runs after this handler has cleared it.\n const done = drawing.current;\n drawing.current = null;\n if (done) setStrokes((all) => [...all, done]);\n }}\n onPointerCancel={() => {\n drawing.current = null;\n redraw((n) => n + 1);\n }}\n />\n ) : (\n <>\n <input\n id={id}\n type=\"text\"\n className=\"signature-input-name\"\n value={typed}\n autoComplete=\"name\"\n placeholder={say('typeYourName')}\n aria-label={say('typedSignature', { label })}\n aria-invalid={invalid || undefined}\n disabled={disabled}\n onChange={(event) => setTyped(event.target.value)}\n />\n <canvas ref={canvas} className=\"signature-input-pad\" aria-hidden=\"true\" />\n </>\n )}\n <span className=\"signature-input-tools\">\n {typed === null && (\n <>\n <button\n type=\"button\"\n className=\"btn btn-quiet\"\n disabled={disabled || !strokes.length}\n onClick={() => setStrokes((all) => all.slice(0, -1))}\n >\n {say('undoStroke')}\n </button>\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled || !strokes.length} onClick={() => setStrokes([])}>\n {say('clearSignature')}\n </button>\n </>\n )}\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => setTyped(typed === null ? '' : null)}>\n {say(typed === null ? 'typeInstead' : 'drawInstead')}\n </button>\n <button\n type=\"button\"\n className=\"btn signature-input-keep\"\n disabled={disabled || empty || !keeps || state === 'keeping'}\n onClick={() => void keep()}\n >\n {say(state === 'keeping' ? 'keepingSignature' : 'useSignature')}\n </button>\n {value !== null && value !== '' && (\n <button type=\"button\" className=\"btn btn-quiet\" onClick={() => setSigning(false)}>\n {say('keepPrevious')}\n </button>\n )}\n </span>\n {state === 'failed' && (\n <span className=\"signature-input-problem\" role=\"alert\">\n {say('signatureUnread')}\n </span>\n )}\n </span>\n );\n}\n\nconst INPUTS: Record<string, ComponentType<InputProps>> = {\n json: JsonInput,\n scan: ScanInput,\n signature: SignatureInput,\n rating: RatingInput,\n slider: SliderInput,\n color: ColorInput,\n time: nativeInput('time'),\n month: nativeInput('month'),\n year: nativeInput('year'),\n};\n\n/** The platform's input for a display format, by name; null for a name it has none for, so the plain box stays. */\nexport function builtInInput(name: string): ComponentType<InputProps> | null {\n return Object.hasOwn(INPUTS, name) ? INPUTS[name] : null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-C4LkPCU4.js","names":["_Fragment"],"sources":["../../src/client/copy/record/components/builtInInputs.ts","../../src/client/components/builtInInputs.tsx"],"sourcesContent":["/** What builtInInputs says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n stars: '{stars} of {outOf} stars',\n pickColor: 'Pick {label}',\n jsonProblem: 'Not valid JSON: {problem}',\n formatJson: 'Format',\n scan: 'Scan',\n stopScanning: 'Stop scanning',\n cameraView: 'Camera: hold the code in view',\n cameraRefused: 'The camera could not be opened. Type the code instead.',\n signHere: 'Sign here: {label}',\n typedSignature: '{label}, typed',\n typeYourName: 'Type your full name',\n keptSignature: '{label}: the kept signature',\n undoStroke: 'Undo',\n clearSignature: 'Clear',\n typeInstead: 'Type instead',\n drawInstead: 'Draw instead',\n useSignature: 'Use this signature',\n keepingSignature: 'Keeping…',\n keepPrevious: 'Keep the previous one',\n signAgain: 'Sign again',\n removeSignature: 'Remove',\n signatureUnread: 'The signature could not be kept. Try again.',\n});\n","/**\n * The platform's own inputs for the display formats that are edited as\n * something other than a box of text: a rating's stars, a slider along a\n * number's range, a color, a time of day, a month, a year, a JSON\n * document, a code the camera reads and a signature.\n *\n * Each is an input component like an author's (inputRenderers.tsx): it\n * stands in a record's edit form, its New form and a grid cell's editor,\n * hands the form a value through `onChange`, and is fetched the first time a\n * field needs it, so no first page carries it. Until it is here the plain box\n * stands in its place, and the form's rules and the server still judge what\n * it hands back.\n *\n * The native pickers do the hard parts — a color, a time, a month — so a\n * keyboard and a screen reader get the browser's own control; the stars are\n * a radio group, one tab stop, the arrow keys moving the pick.\n */\nimport { useEffect, useId, useRef, useState, type ComponentType, type KeyboardEvent, type PointerEvent } from 'react';\nimport { api } from '@mistriwork/kit/client/core.js';\nimport { pageBridge } from '@mistriwork/kit/client/uiBridge.js';\nimport { richTextImageAddress } from '@mistriwork/engine/shared/richText.js';\nimport { say } from '../copy/record/components/builtInInputs.js';\nimport './builtInInputs.css';\n\ninterface InputProps {\n value: string | number | boolean | null;\n onChange(value: string | number | boolean | null): void;\n id: string;\n describedBy?: string;\n invalid: boolean;\n disabled: boolean;\n required: boolean;\n label: string;\n field: { name: string; resource?: string };\n}\n\ntype Rule = { outOf?: number; slider?: { min: number; max: number; step?: number }; signature?: true | { table: string; key: string } };\n\n/** The column's display rule, as the App's payload carries it. */\nfunction ruleOf(field: InputProps['field']): Rule {\n const resources = (pageBridge.activeApp() as { resources?: Record<string, { display?: Record<string, Rule> }> } | null)?.resources;\n const display = field.resource ? resources?.[field.resource]?.display : undefined;\n return display && Object.hasOwn(display, field.name) ? display[field.name] : {};\n}\n\nconst text = (value: InputProps['value']) => (value === null || value === undefined ? '' : String(value));\n\n/** Stars as a radio group: a click picks one, picking the one already picked clears it where the field may be empty. */\nfunction RatingInput({ value, onChange, id, describedBy, invalid, disabled, required, label, field }: InputProps) {\n const outOf = ruleOf(field).outOf ?? 5;\n const picked = Math.max(0, Math.min(outOf, Math.round(Number(value) || 0)));\n const name = useId();\n const pick = (n: number) => onChange(n === picked && !required ? null : n);\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const step =\n event.key === 'ArrowRight' || event.key === 'ArrowUp' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowDown' ? -1 : 0;\n if (!step) return;\n event.preventDefault();\n const next = Math.max(required ? 1 : 0, Math.min(outOf, picked + step));\n onChange(next === 0 ? null : next);\n };\n return (\n <div\n id={id}\n className=\"rating-input\"\n role=\"radiogroup\"\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n aria-required={required || undefined}\n onKeyDown={onKeyDown}\n >\n {Array.from({ length: outOf }, (_, index) => {\n const n = index + 1;\n return (\n <button\n key={n}\n type=\"button\"\n role=\"radio\"\n name={name}\n className={n <= picked ? 'rating-input-star is-on' : 'rating-input-star'}\n aria-checked={n === picked}\n aria-label={say('stars', { stars: n, outOf })}\n tabIndex={n === (picked || 1) ? 0 : -1}\n disabled={disabled}\n onClick={() => pick(n)}\n >\n ★\n </button>\n );\n })}\n </div>\n );\n}\n\n/** A range with its figure beside it, the figure live as the thumb moves. */\nfunction SliderInput({ value, onChange, id, describedBy, invalid, disabled, required, label, field }: InputProps) {\n const { min = 0, max = 100, step = 1 } = ruleOf(field).slider ?? {};\n const shown = text(value);\n return (\n <span className=\"slider-input\">\n <input\n id={id}\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={shown === '' ? min : shown}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(Number(event.target.value))}\n />\n <output htmlFor={id} className=\"slider-input-figure num-value\">\n {shown === '' ? '—' : shown}\n </output>\n </span>\n );\n}\n\n/** The system's color picker, its code beside it as a box of its own for a reader who has one to paste. */\nfunction ColorInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const shown = text(value);\n const valid = /^#[0-9a-f]{6}$/i.test(shown);\n return (\n <span className=\"color-input\">\n <input\n type=\"color\"\n className=\"color-input-picker\"\n value={valid ? shown.toLowerCase() : '#000000'}\n aria-label={say('pickColor', { label })}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value)}\n />\n <input\n id={id}\n className=\"color-input-code\"\n type=\"text\"\n value={shown}\n placeholder=\"#1d6a5a\"\n spellCheck={false}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value.trim() || null)}\n />\n </span>\n );\n}\n\n/** One native picker: a time of day, a month, a year (a number box, since there is no year picker). */\nfunction nativeInput(type: 'time' | 'month' | 'year'): ComponentType<InputProps> {\n function NativeInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const raw = text(value);\n /* A stored time keeps its seconds (\"14:30:00\") and a month may be a date (\"2026-10-01\"): the picker takes the part it shows. */\n const shown = type === 'time' ? raw.slice(0, 5) : type === 'month' ? raw.slice(0, 7) : raw;\n return (\n <input\n id={id}\n className={`native-input ${type}-input`}\n type={type === 'year' ? 'number' : type}\n {...(type === 'year' ? { min: 1, max: 9999, step: 1, inputMode: 'numeric' as const } : {})}\n value={shown}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => {\n const next = event.target.value;\n onChange(next === '' ? null : type === 'year' ? Number(next) : next);\n }}\n />\n );\n }\n return NativeInput;\n}\n\n/** Where a JSON text stops parsing, said as the parser says it, or null for a document that reads. */\nexport function jsonProblem(source: string): string | null {\n if (!source.trim()) return null;\n try {\n JSON.parse(source);\n return null;\n } catch (error) {\n return (error as Error).message;\n }\n}\n\n/** A stored document as text to edit: a string as it is, an object (a Postgres jsonb) laid out two spaces deep. */\nconst jsonText = (value: unknown) =>\n value === null || value === undefined ? '' : typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);\n\n/**\n * A JSON document in a box of its own: monospaced, tab-free, its parse problem\n * said under it while it is typed, and a Format button that lays a document\n * that reads out two spaces deep. The text is the value, so what was typed is\n * what is kept until it reads; the server refuses a document that does not\n * (appValidation.ts), whatever control sent it.\n */\nfunction JsonInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const shown = jsonText(value);\n const [problem, setProblem] = useState(() => jsonProblem(shown));\n const problemId = useId();\n const rows = Math.min(16, Math.max(4, shown.split('\\n').length + 1));\n const change = (next: string) => {\n setProblem(jsonProblem(next));\n onChange(next.trim() ? next : null);\n };\n return (\n <span className=\"json-input\">\n <textarea\n id={id}\n className=\"json-input-text\"\n value={shown}\n rows={rows}\n spellCheck={false}\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n aria-label={label}\n aria-describedby={[describedBy, problem ? problemId : ''].filter(Boolean).join(' ') || undefined}\n aria-invalid={invalid || Boolean(problem) || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => change(event.target.value)}\n />\n <span className=\"json-input-foot\">\n <span id={problemId} className=\"json-input-problem\" aria-live=\"polite\">\n {problem ? say('jsonProblem', { problem }) : ''}\n </span>\n <button\n type=\"button\"\n className=\"btn btn-quiet json-input-format\"\n disabled={disabled || Boolean(problem) || !shown.trim()}\n onClick={() => change(JSON.stringify(JSON.parse(shown), null, 2))}\n >\n {say('formatJson')}\n </button>\n </span>\n </span>\n );\n}\n\n/** The browser's code reader where it has one (Chromium on Android and macOS, Safari 17+): absent elsewhere. */\ntype CodeReader = { detect(source: HTMLVideoElement): Promise<Array<{ rawValue?: string }>> };\nconst codeReader = (): (new () => CodeReader) | undefined =>\n typeof window === 'undefined' || !navigator.mediaDevices?.getUserMedia\n ? undefined\n : (window as unknown as { BarcodeDetector?: new () => CodeReader }).BarcodeDetector;\n\n/** The camera, looking for a code five times a second until it reads one; the stream stops when it closes. */\nfunction Camera({ onRead, onFail }: { onRead(code: string): void; onFail(): void }) {\n const video = useRef<HTMLVideoElement>(null);\n useEffect(() => {\n const Reader = codeReader();\n let stream: MediaStream | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n void navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } }).then(\n async (opened) => {\n stream = opened;\n if (stopped || !video.current || !Reader) return opened.getTracks().forEach((track) => track.stop());\n video.current.srcObject = opened;\n await video.current.play().catch(() => undefined);\n const reader = new Reader();\n const look = async () => {\n if (stopped || !video.current) return;\n const found = await reader.detect(video.current).catch(() => []);\n const code = found.find((item) => item.rawValue)?.rawValue;\n if (code) onRead(code);\n else timer = setTimeout(look, 200);\n };\n void look();\n },\n () => !stopped && onFail(),\n );\n return () => {\n stopped = true;\n clearTimeout(timer);\n stream?.getTracks().forEach((track) => track.stop());\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n return <video ref={video} className=\"scan-input-video\" muted playsInline aria-label={say('cameraView')} />;\n}\n\n/**\n * A code typed, or read by the camera where the browser reads codes: a box\n * that always takes typing, so the form never waits on a camera, and beside\n * it a Scan button only where there is something to scan with. A read code\n * replaces the box's text, which can still be corrected by hand.\n */\nfunction ScanInput({ value, onChange, id, describedBy, invalid, disabled, required, label }: InputProps) {\n const [open, setOpen] = useState(false);\n const [refused, setRefused] = useState(false);\n const can = Boolean(codeReader());\n return (\n <span className=\"scan-input\">\n <span className=\"scan-input-row\">\n <input\n id={id}\n type=\"text\"\n className=\"scan-input-text\"\n value={text(value)}\n autoComplete=\"off\"\n spellCheck={false}\n aria-label={label}\n aria-describedby={describedBy}\n aria-invalid={invalid || undefined}\n required={required}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value === '' ? null : event.target.value)}\n />\n {can && (\n <button\n type=\"button\"\n className=\"btn btn-quiet scan-input-button\"\n aria-pressed={open}\n disabled={disabled}\n onClick={() => {\n setRefused(false);\n setOpen(!open);\n }}\n >\n {say(open ? 'stopScanning' : 'scan')}\n </button>\n )}\n </span>\n {open && (\n <Camera\n onRead={(code) => {\n onChange(code);\n setOpen(false);\n }}\n onFail={() => {\n setRefused(true);\n setOpen(false);\n }}\n />\n )}\n {refused && (\n <span className=\"scan-input-note\" role=\"status\">\n {say('cameraRefused')}\n </span>\n )}\n </span>\n );\n}\n\n/** A canvas's picture as base64, for the file route. */\nasync function pngOf(canvas: HTMLCanvasElement): Promise<string> {\n const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, 'image/png'));\n if (!blob) throw new Error(say('signatureUnread'));\n const bytes = new Uint8Array(await blob.arrayBuffer());\n let out = '';\n for (let i = 0; i < bytes.length; i += 0x8000) out += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n return btoa(out);\n}\n\ntype Stroke = Array<[number, number]>;\n/** The pad's drawing size in CSS pixels; the canvas is this times the screen's density, so a line stays sharp. */\nconst PAD = { wide: 480, high: 150 };\n\n/**\n * A signature: drawn with a finger, a pen or the mouse (each stroke undone in\n * turn, or all cleared), or a name typed where drawing is not possible, and\n * kept with \"Use this signature\" as a PNG row of the file resource the column\n * points at; the form holds that row's key. A kept signature is shown as its\n * picture with \"Sign again\" beside it. Nothing is stored until it is kept.\n */\nfunction SignatureInput({ value, onChange, id, describedBy, invalid, disabled, label, field }: InputProps) {\n const rule = ruleOf(field).signature;\n const keeps = rule && typeof rule === 'object' ? rule : undefined;\n const canvas = useRef<HTMLCanvasElement>(null);\n const [strokes, setStrokes] = useState<Stroke[]>([]);\n const [typed, setTyped] = useState<string | null>(null);\n const [signing, setSigning] = useState(value === null || value === '');\n const [state, setState] = useState<'idle' | 'keeping' | 'failed'>('idle');\n const drawing = useRef<Stroke | null>(null);\n const again = useRef<HTMLButtonElement>(null);\n const kept = useRef(false);\n /* The button that kept the drawing is gone once it is kept: the focus moves to Sign again, inside the field, so a\n form that saves when the focus leaves the field saves when the reader moves on, not before. */\n useEffect(() => {\n if (kept.current && again.current) {\n kept.current = false;\n again.current.focus();\n }\n });\n const empty = typed === null ? strokes.length === 0 : !typed.trim();\n\n /* The whole drawing is laid again from its strokes on every change: an undo is a stroke left out. */\n useEffect(() => {\n const element = canvas.current;\n const context = element?.getContext('2d');\n if (!element || !context) return;\n const density = window.devicePixelRatio || 1;\n element.width = PAD.wide * density;\n element.height = PAD.high * density;\n context.setTransform(density, 0, 0, density, 0, 0);\n context.clearRect(0, 0, PAD.wide, PAD.high);\n // Ink on paper: the pad is white in every look, so the kept picture reads wherever it is shown.\n context.strokeStyle = context.fillStyle = '#111827';\n context.lineWidth = 2.4;\n context.lineCap = context.lineJoin = 'round';\n if (typed !== null) {\n context.font = 'italic 40px Georgia, \"Times New Roman\", serif';\n context.textBaseline = 'middle';\n context.fillText(typed, 24, PAD.high / 2, PAD.wide - 48);\n return;\n }\n for (const stroke of [...strokes, ...(drawing.current ? [drawing.current] : [])]) {\n context.beginPath();\n stroke.forEach(([x, y], i) => (i ? context.lineTo(x, y) : context.moveTo(x, y)));\n if (stroke.length === 1) context.lineTo(stroke[0][0] + 0.1, stroke[0][1]);\n context.stroke();\n }\n });\n\n const at = (event: PointerEvent<HTMLCanvasElement>): [number, number] => {\n const box = event.currentTarget.getBoundingClientRect();\n // screen-px: the pointer's ruler, mapped onto the pad's own units by the ratio.\n return [((event.clientX - box.left) / (box.width || 1)) * PAD.wide, ((event.clientY - box.top) / (box.height || 1)) * PAD.high];\n };\n const [, redraw] = useState(0);\n\n const keep = async () => {\n if (!keeps || !canvas.current) return;\n setState('keeping');\n try {\n const reply = (await api('/api/file/upload', {\n table: keeps.table,\n values: {},\n file: { name: 'signature.png', type: 'image/png', data: await pngOf(canvas.current) },\n })) as { key?: Record<string, string | number>; errors?: unknown[]; error?: unknown };\n const stored = reply.key?.[keeps.key];\n if (stored === undefined || reply.errors || reply.error) throw new Error(say('signatureUnread'));\n onChange(typeof stored === 'number' ? stored : String(stored));\n kept.current = true;\n setSigning(false);\n setStrokes([]);\n setTyped(null);\n setState('idle');\n } catch {\n setState('failed');\n }\n };\n\n if (!signing && value !== null && value !== '' && keeps)\n return (\n <span className=\"signature-input\">\n <img\n className=\"signature-input-kept\"\n src={richTextImageAddress(keeps.table, { [keeps.key]: typeof value === 'number' ? value : String(value) })}\n alt={say('keptSignature', { label })}\n />\n <span className=\"signature-input-tools\">\n <button ref={again} type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => setSigning(true)}>\n {say('signAgain')}\n </button>\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => onChange(null)}>\n {say('removeSignature')}\n </button>\n </span>\n </span>\n );\n return (\n <span className=\"signature-input\" aria-describedby={describedBy}>\n {typed === null ? (\n <canvas\n id={id}\n ref={canvas}\n className=\"signature-input-pad\"\n /* Focusable by the pointer, not by Tab: a press on the pad keeps the focus inside the field, so a form that\n closes its editor when the focus leaves keeps it open while a signature is drawn. Keyboards type instead. */\n tabIndex={-1}\n role=\"img\"\n aria-label={say('signHere', { label })}\n aria-invalid={invalid || undefined}\n onPointerDown={(event) => {\n if (disabled) return;\n event.currentTarget.setPointerCapture?.(event.pointerId);\n drawing.current = [at(event)];\n redraw((n) => n + 1);\n }}\n onPointerMove={(event) => {\n if (!drawing.current) return;\n drawing.current.push(at(event));\n redraw((n) => n + 1);\n }}\n onPointerUp={() => {\n // Taken before it is let go: the update runs after this handler has cleared it.\n const done = drawing.current;\n drawing.current = null;\n if (done) setStrokes((all) => [...all, done]);\n }}\n onPointerCancel={() => {\n drawing.current = null;\n redraw((n) => n + 1);\n }}\n />\n ) : (\n <>\n <input\n id={id}\n type=\"text\"\n className=\"signature-input-name\"\n value={typed}\n autoComplete=\"name\"\n placeholder={say('typeYourName')}\n aria-label={say('typedSignature', { label })}\n aria-invalid={invalid || undefined}\n disabled={disabled}\n onChange={(event) => setTyped(event.target.value)}\n />\n <canvas ref={canvas} className=\"signature-input-pad\" aria-hidden=\"true\" />\n </>\n )}\n <span className=\"signature-input-tools\">\n {typed === null && (\n <>\n <button\n type=\"button\"\n className=\"btn btn-quiet\"\n disabled={disabled || !strokes.length}\n onClick={() => setStrokes((all) => all.slice(0, -1))}\n >\n {say('undoStroke')}\n </button>\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled || !strokes.length} onClick={() => setStrokes([])}>\n {say('clearSignature')}\n </button>\n </>\n )}\n <button type=\"button\" className=\"btn btn-quiet\" disabled={disabled} onClick={() => setTyped(typed === null ? '' : null)}>\n {say(typed === null ? 'typeInstead' : 'drawInstead')}\n </button>\n <button\n type=\"button\"\n className=\"btn signature-input-keep\"\n disabled={disabled || empty || !keeps || state === 'keeping'}\n onClick={() => void keep()}\n >\n {say(state === 'keeping' ? 'keepingSignature' : 'useSignature')}\n </button>\n {value !== null && value !== '' && (\n <button type=\"button\" className=\"btn btn-quiet\" onClick={() => setSigning(false)}>\n {say('keepPrevious')}\n </button>\n )}\n </span>\n {state === 'failed' && (\n <span className=\"signature-input-problem\" role=\"alert\">\n {say('signatureUnread')}\n </span>\n )}\n </span>\n );\n}\n\nconst INPUTS: Record<string, ComponentType<InputProps>> = {\n json: JsonInput,\n scan: ScanInput,\n signature: SignatureInput,\n rating: RatingInput,\n slider: SliderInput,\n color: ColorInput,\n time: nativeInput('time'),\n month: nativeInput('month'),\n year: nativeInput('year'),\n};\n\n/** The platform's input for a display format, by name; null for a name it has none for, so the plain box stays. */\nexport function builtInInput(name: string): ComponentType<InputProps> | null {\n return Object.hasOwn(INPUTS, name) ? INPUTS[name] : null;\n}\n"],"mappings":"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"}