@btndprojects/loam-ui-native 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/dist/accordion/index.js +4 -2
  2. package/dist/adaptive/index.js +12 -2
  3. package/dist/address-block/index.js +23 -0
  4. package/dist/alert/index.js +5 -2
  5. package/dist/analyzer/index.js +13 -5
  6. package/dist/announcement/index.js +15 -5
  7. package/dist/appearance-picker/index.js +15 -0
  8. package/dist/audio-player/index.js +26 -2
  9. package/dist/avatar/index.js +2 -2
  10. package/dist/avatar-group/index.js +5 -3
  11. package/dist/badge/index.js +6 -3
  12. package/dist/banner/index.js +3 -2
  13. package/dist/board/index.js +13 -6
  14. package/dist/button/index.js +6 -3
  15. package/dist/camera-permission/index.js +19 -11
  16. package/dist/capture-result/index.js +26 -16
  17. package/dist/card/index.js +7 -3
  18. package/dist/card-form/index.js +15 -9
  19. package/dist/carousel/index.js +3 -1
  20. package/dist/chart/index.js +15 -5
  21. package/dist/charts/index.js +18 -5
  22. package/dist/chat/index.js +35 -10
  23. package/dist/checkbox/index.js +6 -4
  24. package/dist/checklist/index.js +16 -7
  25. package/dist/checkout/index.js +25 -12
  26. package/dist/chip/index.js +7 -4
  27. package/dist/chunk-2BZD3NAP.js +1110 -0
  28. package/dist/chunk-2YKVID2V.js +35 -0
  29. package/dist/{chunk-H7DWSL4O.js → chunk-36DA3IPO.js} +115 -27
  30. package/dist/chunk-3A56MIAN.js +599 -0
  31. package/dist/{chunk-AEB7KCHQ.js → chunk-3SCKCWF5.js} +39 -28
  32. package/dist/chunk-3TZNDHKO.js +187 -0
  33. package/dist/chunk-43K3BLXV.js +9 -0
  34. package/dist/chunk-46ZJIQ2Z.js +208 -0
  35. package/dist/{chunk-PMMTGBBS.js → chunk-4CZMQDQL.js} +75 -22
  36. package/dist/chunk-4QCR3K2J.js +1 -0
  37. package/dist/{chunk-E2E3I5W2.js → chunk-5DTKORLI.js} +54 -45
  38. package/dist/{chunk-5VFZRGVD.js → chunk-5J4WFXFA.js} +32 -15
  39. package/dist/{chunk-JBMLWFSJ.js → chunk-5KJ7TEQ6.js} +83 -48
  40. package/dist/chunk-5NU33TXK.js +529 -0
  41. package/dist/chunk-6KSUS7DF.js +690 -0
  42. package/dist/{chunk-3WEZCGHX.js → chunk-6RV6UW2J.js} +140 -56
  43. package/dist/chunk-6RZM7RP2.js +520 -0
  44. package/dist/{chunk-JJLPBRVZ.js → chunk-75BDCC7K.js} +2 -0
  45. package/dist/{chunk-U5NLHYV6.js → chunk-7CGJBQUA.js} +333 -22
  46. package/dist/{chunk-I4FHVKEC.js → chunk-7IVF6JLJ.js} +16 -5
  47. package/dist/chunk-7IWCGXYA.js +143 -0
  48. package/dist/chunk-7LM4ASFH.js +172 -0
  49. package/dist/chunk-7MCEJFWV.js +341 -0
  50. package/dist/{chunk-2SY63FCH.js → chunk-7NGMUVNU.js} +66 -30
  51. package/dist/chunk-7PBSIXT7.js +355 -0
  52. package/dist/chunk-7QOYGENL.js +443 -0
  53. package/dist/{chunk-TDMG26JI.js → chunk-A7SWYDE5.js} +40 -9
  54. package/dist/chunk-ADHPJALR.js +3051 -0
  55. package/dist/{chunk-ZLTO5VQI.js → chunk-ALJYZLNQ.js} +131 -61
  56. package/dist/{chunk-GDKVKNR2.js → chunk-AQ5FIG74.js} +110 -30
  57. package/dist/chunk-B2MMZZQB.js +426 -0
  58. package/dist/{chunk-QYPE5JX3.js → chunk-B7JZ3N5G.js} +142 -105
  59. package/dist/{chunk-QHTVYA65.js → chunk-BL37SW6G.js} +388 -102
  60. package/dist/chunk-BTRMA7GC.js +21 -0
  61. package/dist/{chunk-FUMHAAF3.js → chunk-C7IUWIUZ.js} +243 -92
  62. package/dist/chunk-CA3DUVYD.js +10 -0
  63. package/dist/{chunk-FYWNRX4F.js → chunk-CBCJSO2B.js} +143 -13
  64. package/dist/chunk-CHPX4MFI.js +239 -0
  65. package/dist/chunk-CLGAMIOT.js +648 -0
  66. package/dist/chunk-CUE7VML6.js +1141 -0
  67. package/dist/{chunk-D5N5QWI7.js → chunk-CVU6S2E5.js} +497 -235
  68. package/dist/{chunk-U23FYWME.js → chunk-CXPQMA6Z.js} +101 -34
  69. package/dist/{chunk-36EC2T6P.js → chunk-D2LN4I7I.js} +1 -1
  70. package/dist/{chunk-TLCIR6RG.js → chunk-D34LNBO5.js} +411 -155
  71. package/dist/{chunk-FV6HMP7W.js → chunk-D4BCQICM.js} +207 -65
  72. package/dist/chunk-EABKIIA4.js +1 -0
  73. package/dist/chunk-EICSYRAG.js +341 -0
  74. package/dist/chunk-EMWAJMAS.js +83 -0
  75. package/dist/chunk-EOLZWRV6.js +149 -0
  76. package/dist/chunk-EOQVTGB6.js +350 -0
  77. package/dist/{chunk-ZVATDZOM.js → chunk-ESGUHNB2.js} +106 -28
  78. package/dist/{chunk-46MMSDGL.js → chunk-F2KNBPKY.js} +104 -73
  79. package/dist/{chunk-KQZ3GLHR.js → chunk-G7H3NX3G.js} +40 -17
  80. package/dist/{chunk-3QMEOFW5.js → chunk-GM7YIPB7.js} +421 -207
  81. package/dist/{chunk-QHAITQIF.js → chunk-GVQQG435.js} +256 -86
  82. package/dist/chunk-HE43OMEL.js +1056 -0
  83. package/dist/{chunk-XIDTBCBA.js → chunk-HHOMFQ7R.js} +1049 -207
  84. package/dist/chunk-HNAKS4Z3.js +207 -0
  85. package/dist/chunk-HP6ST2OW.js +352 -0
  86. package/dist/chunk-HW3OWDTU.js +1 -0
  87. package/dist/{chunk-QP3KMOVF.js → chunk-HZDB44K4.js} +24 -14
  88. package/dist/chunk-I6CUP2LW.js +1155 -0
  89. package/dist/chunk-ICV4Y2WY.js +27 -0
  90. package/dist/chunk-IHSIENOE.js +3950 -0
  91. package/dist/{chunk-T5LTPKX6.js → chunk-IMMALLRU.js} +53 -25
  92. package/dist/{chunk-C4GTDLRC.js → chunk-JA6TK7H2.js} +154 -67
  93. package/dist/{chunk-EVOIGUBI.js → chunk-JAVZ5JPV.js} +43 -13
  94. package/dist/{chunk-CPTOADB2.js → chunk-JCMTP3E4.js} +527 -201
  95. package/dist/{chunk-OJQUBQWA.js → chunk-JCXFFK64.js} +73 -16
  96. package/dist/{chunk-V2OEB75Z.js → chunk-JWCVBFDU.js} +75 -35
  97. package/dist/chunk-JXRTD5RI.js +414 -0
  98. package/dist/chunk-JYIUZUIQ.js +1567 -0
  99. package/dist/chunk-K3TK3GIV.js +219 -0
  100. package/dist/chunk-KEVZIHM2.js +1511 -0
  101. package/dist/{chunk-VAOAKWUK.js → chunk-KHZDNQQD.js} +42 -8
  102. package/dist/chunk-KL4GDV4V.js +131 -0
  103. package/dist/{chunk-Q6NKEPRE.js → chunk-KNILMM4H.js} +73 -28
  104. package/dist/chunk-KRBUZV45.js +25 -0
  105. package/dist/{chunk-7BUIKDD6.js → chunk-KXLDK6HB.js} +91 -30
  106. package/dist/chunk-KZ3X7TSL.js +196 -0
  107. package/dist/{chunk-5UTRIXQ6.js → chunk-L2WMOA2V.js} +187 -43
  108. package/dist/chunk-L4D5LHCH.js +109 -0
  109. package/dist/{chunk-W63AJTM7.js → chunk-LAUJPLDB.js} +236 -71
  110. package/dist/{chunk-TN2ZNJUI.js → chunk-LGNTP5XA.js} +47 -13
  111. package/dist/chunk-LUZFCCSD.js +271 -0
  112. package/dist/{chunk-XTORZWRF.js → chunk-LXJCP4PL.js} +111 -30
  113. package/dist/chunk-MCOZJWPU.js +731 -0
  114. package/dist/{chunk-SATMR6SG.js → chunk-MG4SKWLL.js} +780 -142
  115. package/dist/{chunk-JUYWSSKW.js → chunk-MPEPTSER.js} +95 -16
  116. package/dist/chunk-MWPJ4YGP.js +1 -0
  117. package/dist/{chunk-LUMVGO2K.js → chunk-N7AX4OA4.js} +80 -29
  118. package/dist/chunk-NHSLM6SE.js +523 -0
  119. package/dist/{chunk-IT6FUXXG.js → chunk-NNJLVA7M.js} +43 -11
  120. package/dist/{chunk-TQVAIKFD.js → chunk-NZG75DGA.js} +102 -62
  121. package/dist/chunk-O76QXRBV.js +431 -0
  122. package/dist/chunk-OANH27AK.js +223 -0
  123. package/dist/chunk-OCHPK5YV.js +609 -0
  124. package/dist/chunk-OMHVEVX4.js +436 -0
  125. package/dist/{chunk-SWS5JQIF.js → chunk-ONKQUJWD.js} +187 -60
  126. package/dist/chunk-OVTK7A6T.js +615 -0
  127. package/dist/{chunk-UXGIW7OK.js → chunk-PCEFCOLN.js} +5 -1
  128. package/dist/{chunk-QIKT7LIB.js → chunk-PFLNFIVB.js} +91 -23
  129. package/dist/chunk-PHSZY37H.js +300 -0
  130. package/dist/chunk-PJLTKDVY.js +18 -0
  131. package/dist/{chunk-SHHY5OMK.js → chunk-PK7BN3MI.js} +83 -15
  132. package/dist/{chunk-YXOAPXMU.js → chunk-PWFRDRGS.js} +101 -14
  133. package/dist/{chunk-TY4MTKQJ.js → chunk-PYPMD46W.js} +332 -66
  134. package/dist/chunk-Q6CCHDI7.js +331 -0
  135. package/dist/chunk-QFA5WS2A.js +750 -0
  136. package/dist/chunk-QJMMM4RC.js +124 -0
  137. package/dist/chunk-QYWEVAM6.js +407 -0
  138. package/dist/chunk-R5T4VZ7Z.js +126 -0
  139. package/dist/chunk-R77HFLZD.js +1 -0
  140. package/dist/{chunk-LMPHCVJJ.js → chunk-RAESDNU5.js} +162 -68
  141. package/dist/chunk-RBYQUXNL.js +1 -0
  142. package/dist/chunk-RCM3DT5E.js +1 -0
  143. package/dist/chunk-RGNY56L7.js +282 -0
  144. package/dist/chunk-RIK6UX2K.js +344 -0
  145. package/dist/{chunk-5LAAAXFP.js → chunk-RJA3RXO3.js} +63 -54
  146. package/dist/chunk-RJYRJ3LD.js +1 -0
  147. package/dist/chunk-RMA3NOY4.js +599 -0
  148. package/dist/{chunk-VDPK5CY4.js → chunk-RSWKKAHK.js} +143 -61
  149. package/dist/{chunk-B7N4ASYE.js → chunk-RVOIIX3V.js} +17 -45
  150. package/dist/chunk-SA2RRBGT.js +1077 -0
  151. package/dist/{chunk-7OVVSVEZ.js → chunk-SG3J4R45.js} +440 -169
  152. package/dist/chunk-SPUK7JD4.js +238 -0
  153. package/dist/chunk-SWXV2K5N.js +434 -0
  154. package/dist/chunk-SYJEPXP6.js +1303 -0
  155. package/dist/{chunk-LKFC4CS7.js → chunk-SZLI34TA.js} +18 -9
  156. package/dist/{chunk-NBEF4EPJ.js → chunk-T77WJA74.js} +131 -16
  157. package/dist/chunk-TDWWVJA4.js +630 -0
  158. package/dist/chunk-TFHI5XDR.js +610 -0
  159. package/dist/chunk-TH4PW2WU.js +17 -0
  160. package/dist/chunk-TTQ24HQX.js +12 -0
  161. package/dist/chunk-TUACVJNG.js +12 -0
  162. package/dist/{chunk-F5B3UINI.js → chunk-TUL5RUIA.js} +171 -62
  163. package/dist/{chunk-C6APNDVA.js → chunk-TYOSA7JG.js} +63 -34
  164. package/dist/chunk-TYQTMMEV.js +1 -0
  165. package/dist/{chunk-DW3L54DQ.js → chunk-TZO6YDS7.js} +53 -12
  166. package/dist/{chunk-67IZZ2MJ.js → chunk-U3ETF22F.js} +75 -30
  167. package/dist/{chunk-EDCBJV7J.js → chunk-U6BYHD2N.js} +87 -31
  168. package/dist/{chunk-ENP3BMZU.js → chunk-UF6EJXPQ.js} +16 -1
  169. package/dist/{chunk-IPUNJMAQ.js → chunk-UNNQIPGK.js} +304 -68
  170. package/dist/chunk-UQVO6AJV.js +171 -0
  171. package/dist/{chunk-FZGAAOKS.js → chunk-VFDQAAIW.js} +138 -32
  172. package/dist/chunk-VIUE3VQT.js +1 -0
  173. package/dist/{chunk-5RENVKBQ.js → chunk-VJLSSSGC.js} +126 -30
  174. package/dist/chunk-VLMT6KYT.js +350 -0
  175. package/dist/chunk-VMHGBUGS.js +35 -0
  176. package/dist/{chunk-4IZXHVFJ.js → chunk-VQKPBYW6.js} +228 -172
  177. package/dist/{chunk-G6UESEVN.js → chunk-VSSTWLC5.js} +49 -11
  178. package/dist/{chunk-TRGFFU6A.js → chunk-VZYYZRPN.js} +866 -330
  179. package/dist/{chunk-JLC6PM5S.js → chunk-WAIJCD2V.js} +128 -54
  180. package/dist/chunk-WFMQ3WKM.js +378 -0
  181. package/dist/chunk-WJ2KDRMY.js +66 -0
  182. package/dist/chunk-WKFZJO54.js +437 -0
  183. package/dist/{chunk-QEYFDRHR.js → chunk-WVVD3XOV.js} +146 -84
  184. package/dist/chunk-XAIWMSLB.js +1 -0
  185. package/dist/chunk-XBXUBBJZ.js +98 -0
  186. package/dist/chunk-XCU7MYBJ.js +1 -0
  187. package/dist/chunk-XHE4PGCI.js +717 -0
  188. package/dist/chunk-XRSJ32M3.js +331 -0
  189. package/dist/chunk-XTCVOEAW.js +1 -0
  190. package/dist/chunk-XWGXVQF6.js +58 -0
  191. package/dist/{chunk-MKSNFG7H.js → chunk-Y3XKKO5K.js} +191 -109
  192. package/dist/chunk-Y4QNMBLO.js +533 -0
  193. package/dist/chunk-Y5H4CTKO.js +1 -0
  194. package/dist/chunk-YBQIN75A.js +403 -0
  195. package/dist/{chunk-PNF3NKB5.js → chunk-YJ5PXOX3.js} +28 -11
  196. package/dist/{chunk-M656FYNH.js → chunk-YLRSRUPS.js} +63 -49
  197. package/dist/chunk-YM6MPJ4A.js +1 -0
  198. package/dist/chunk-YV7KZENV.js +200 -0
  199. package/dist/chunk-Z7I4ZSO6.js +316 -0
  200. package/dist/{chunk-EY4DD67D.js → chunk-ZIRMWD2B.js} +199 -126
  201. package/dist/chunk-ZPMG3FWU.js +75 -0
  202. package/dist/{chunk-QRUMA22E.js → chunk-ZTXMK2G7.js} +92 -43
  203. package/dist/{chunk-2OLKPG4E.js → chunk-ZVJCWXAL.js} +189 -87
  204. package/dist/{chunk-YAAHPATV.js → chunk-ZYQ23DPT.js} +49 -34
  205. package/dist/coachmark/index.js +7 -3
  206. package/dist/code-block/index.js +4 -3
  207. package/dist/color-field/index.js +12 -4
  208. package/dist/color-picker/index.js +23 -0
  209. package/dist/commerce/index.js +27 -16
  210. package/dist/confirm/index.js +31 -0
  211. package/dist/connection-card/index.js +34 -0
  212. package/dist/connectivity/index.js +3 -2
  213. package/dist/context-menu/index.js +10 -2
  214. package/dist/copy-button/index.js +3 -2
  215. package/dist/data-grid/index.js +30 -17
  216. package/dist/date-range-picker/index.js +19 -9
  217. package/dist/description-list/index.js +4 -1
  218. package/dist/device-frame/index.js +30 -0
  219. package/dist/dialog/index.js +8 -2
  220. package/dist/divider/index.js +2 -0
  221. package/dist/empty-state/index.js +13 -2
  222. package/dist/error-summary/index.js +7 -2
  223. package/dist/fab/index.js +4 -2
  224. package/dist/file-manager/index.js +33 -17
  225. package/dist/file-upload/index.js +12 -6
  226. package/dist/filter-bar/index.js +10 -6
  227. package/dist/flow-run/index.js +19 -12
  228. package/dist/form/index.js +30 -3
  229. package/dist/gallery/index.js +5 -2
  230. package/dist/hold-to-confirm/index.js +4 -2
  231. package/dist/hover-card/index.js +18 -0
  232. package/dist/icon-tile/index.js +10 -0
  233. package/dist/image/index.js +9 -0
  234. package/dist/index.d.ts +29826 -17941
  235. package/dist/index.js +197 -141
  236. package/dist/infinite-list/index.js +4 -2
  237. package/dist/inline-edit/index.js +5 -4
  238. package/dist/input/index.js +4 -3
  239. package/dist/interactions/index.js +4 -1
  240. package/dist/invoice/index.js +18 -9
  241. package/dist/job-row/index.js +22 -0
  242. package/dist/json-inspector/index.js +20 -0
  243. package/dist/kbd/index.js +2 -2
  244. package/dist/keyboard/index.js +7 -2
  245. package/dist/level-meter/index.js +9 -0
  246. package/dist/list-row/index.js +11 -3
  247. package/dist/log-viewer/index.js +9 -2
  248. package/dist/map-view/index.js +13 -2
  249. package/dist/markdown/index.js +8 -5
  250. package/dist/metric/index.js +6 -3
  251. package/dist/metric-strip/index.js +12 -0
  252. package/dist/money/index.js +11 -4
  253. package/dist/nav-drawer/index.js +6 -2
  254. package/dist/nav-header/index.js +10 -4
  255. package/dist/navigation/index.js +29 -2
  256. package/dist/notification-center/index.js +24 -10
  257. package/dist/number-roll/index.js +3 -1
  258. package/dist/onboarding-pager/index.js +22 -0
  259. package/dist/one-handed/index.js +2 -2
  260. package/dist/os-surfaces/index.js +6 -3
  261. package/dist/otp/index.js +5 -4
  262. package/dist/page-header/index.js +2 -1
  263. package/dist/pagination/index.js +4 -2
  264. package/dist/payment-method/index.js +17 -11
  265. package/dist/payment-status/index.js +22 -14
  266. package/dist/permission/index.js +18 -2
  267. package/dist/picker/index.js +17 -6
  268. package/dist/progress/index.js +7 -2
  269. package/dist/provenance/index.js +16 -0
  270. package/dist/pull-to-refresh/index.js +2 -2
  271. package/dist/qr-code/index.js +16 -0
  272. package/dist/query-builder/index.js +30 -0
  273. package/dist/radio-group/index.js +6 -4
  274. package/dist/rating/index.js +7 -2
  275. package/dist/receivables/index.js +31 -18
  276. package/dist/recording-indicator/index.js +21 -0
  277. package/dist/refund/index.js +24 -15
  278. package/dist/repeatable-rows/index.js +19 -0
  279. package/dist/save-status/index.js +5 -3
  280. package/dist/screen/index.js +3 -0
  281. package/dist/scroll-area/index.js +1 -1
  282. package/dist/search-field/index.js +8 -3
  283. package/dist/secret-field/index.js +19 -0
  284. package/dist/section-header/index.js +4 -0
  285. package/dist/segmented-control/index.js +2 -2
  286. package/dist/sheet/index.js +9 -3
  287. package/dist/shortcuts-sheet/index.js +2 -0
  288. package/dist/sign-in-screen/index.js +25 -0
  289. package/dist/skeleton/index.js +3 -1
  290. package/dist/slider/index.js +2 -1
  291. package/dist/sortable/index.js +4 -3
  292. package/dist/spinner/index.js +2 -1
  293. package/dist/split-button/index.js +17 -5
  294. package/dist/spotlight/index.js +7 -1
  295. package/dist/stack/index.js +2 -0
  296. package/dist/stage-timeline/index.js +6 -3
  297. package/dist/status-dot/index.js +6 -2
  298. package/dist/stepper/index.js +7 -3
  299. package/dist/streaming-text/index.js +2 -1
  300. package/dist/survey/index.js +24 -14
  301. package/dist/switch/index.js +6 -2
  302. package/dist/system-state/index.js +12 -4
  303. package/dist/tab-bar/index.js +10 -2
  304. package/dist/tabs/index.js +10 -2
  305. package/dist/tags-input/index.js +8 -5
  306. package/dist/text/index.js +4 -0
  307. package/dist/textarea/index.js +5 -4
  308. package/dist/thinking/index.js +4 -1
  309. package/dist/tile-rail/index.js +10 -0
  310. package/dist/time-field/index.js +15 -7
  311. package/dist/time-range-control/index.js +26 -12
  312. package/dist/timeline/index.js +2 -2
  313. package/dist/toast/index.js +4 -2
  314. package/dist/toolbar/index.js +3 -2
  315. package/dist/tour/index.js +12 -10
  316. package/dist/transcript/index.js +28 -0
  317. package/dist/truncation-notice/index.js +2 -2
  318. package/dist/update-notice/index.js +15 -8
  319. package/dist/video-player/index.js +26 -2
  320. package/dist/viewfinder/index.js +12 -4
  321. package/dist/widget/index.js +11 -4
  322. package/dist/wizard/index.js +7 -4
  323. package/package.json +13 -13
  324. package/dist/chunk-2PKYGLQ6.js +0 -268
  325. package/dist/chunk-2SWKL3AJ.js +0 -466
  326. package/dist/chunk-2TXERVVA.js +0 -1081
  327. package/dist/chunk-3DUWIYV5.js +0 -437
  328. package/dist/chunk-3NMD74ZU.js +0 -4
  329. package/dist/chunk-4DS5ZWVC.js +0 -83
  330. package/dist/chunk-6YAQBXB3.js +0 -93
  331. package/dist/chunk-74FLKD24.js +0 -237
  332. package/dist/chunk-CMTVNF3D.js +0 -104
  333. package/dist/chunk-DN7MNQIP.js +0 -799
  334. package/dist/chunk-DVGBZ3TX.js +0 -455
  335. package/dist/chunk-F3H33DQX.js +0 -300
  336. package/dist/chunk-FAJI7BSC.js +0 -443
  337. package/dist/chunk-G3AFC24V.js +0 -60
  338. package/dist/chunk-HJLPZA4X.js +0 -369
  339. package/dist/chunk-HYEOQWT7.js +0 -647
  340. package/dist/chunk-IDM7JJMO.js +0 -110
  341. package/dist/chunk-IVKMZ7BB.js +0 -326
  342. package/dist/chunk-IXKWIFWZ.js +0 -334
  343. package/dist/chunk-JV5YOTOB.js +0 -164
  344. package/dist/chunk-KFJGFLMW.js +0 -158
  345. package/dist/chunk-LQP4KEXZ.js +0 -214
  346. package/dist/chunk-NTQ2F3AU.js +0 -257
  347. package/dist/chunk-O5V4TG3V.js +0 -329
  348. package/dist/chunk-OWAMM2DK.js +0 -280
  349. package/dist/chunk-PID3NJB7.js +0 -405
  350. package/dist/chunk-QDZGFXQD.js +0 -176
  351. package/dist/chunk-QPWDDJJ4.js +0 -385
  352. package/dist/chunk-ROCUG24E.js +0 -416
  353. package/dist/chunk-RU3ZPNL6.js +0 -1986
  354. package/dist/chunk-RXSCIO6W.js +0 -153
  355. package/dist/chunk-SKWO3SZF.js +0 -2284
  356. package/dist/chunk-UQGBTPXH.js +0 -218
  357. package/dist/chunk-UYGDJWMN.js +0 -273
  358. package/dist/chunk-W74TXYFB.js +0 -107
  359. package/dist/chunk-WRDSPUE7.js +0 -417
  360. package/dist/chunk-XBZGDDMR.js +0 -105
  361. package/dist/chunk-XN4KG3P4.js +0 -1040
  362. package/dist/chunk-XQXMLMH3.js +0 -211
  363. package/dist/chunk-ZECZOOSB.js +0 -245
  364. /package/dist/{chunk-VFCUIDRT.js → chunk-3BKLBYPK.js} +0 -0
  365. /package/dist/{chunk-GEIGMWNF.js → chunk-QNZII37O.js} +0 -0
@@ -1,2284 +0,0 @@
1
- import { useThinkingPulse, Thinking } from './chunk-W74TXYFB.js';
2
- import { Markdown } from './chunk-SWS5JQIF.js';
3
- import { CopyButton } from './chunk-C6APNDVA.js';
4
- import { Skeleton } from './chunk-PNF3NKB5.js';
5
- import { useFormFactor } from './chunk-AWKQIE5I.js';
6
- import { expandedProps } from './chunk-W23G6JLF.js';
7
- import { Button } from './chunk-E2E3I5W2.js';
8
- import { haptics } from './chunk-NY2U3PBI.js';
9
- import { useLabels } from './chunk-APCWFOZW.js';
10
- import { Icon } from './chunk-UXGIW7OK.js';
11
- import { springConfig, BREAKAWAY, resist, rubberband, useResolvedMotion } from './chunk-J64KGCGX.js';
12
- import * as React6 from 'react';
13
- import { Platform, UIManager, Animated, View, TextInput, Text, Pressable, Image, I18nManager, PanResponder, LayoutAnimation, Easing, ScrollView, AccessibilityInfo, KeyboardAvoidingView } from 'react-native';
14
- import { createStyleSheet, useStyles, useLoamTheme } from '@btndprojects/loam-theme-native';
15
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
16
-
17
- // src/chat/chat-labels.ts
18
- function formatChatDuration(ms) {
19
- if (ms < 1e3) return `${Math.round(ms)}ms`;
20
- const s = ms / 1e3;
21
- if (s < 60) return `${s < 10 ? s.toFixed(1) : Math.round(s)}s`;
22
- const m = Math.floor(s / 60);
23
- return `${m}m ${String(Math.round(s % 60)).padStart(2, "0")}s`;
24
- }
25
- function formatChatBytes(bytes) {
26
- if (bytes < 1024) return `${bytes} B`;
27
- if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
28
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
29
- }
30
- var DEFAULT_CHAT_LABELS = {
31
- chat: "Chat",
32
- messages: "Messages",
33
- emptyTitle: "How can I help?",
34
- placeholder: "Send a message\u2026",
35
- composer: "Message",
36
- assistant: "Assistant",
37
- you: "You",
38
- attachFiles: "Attach files",
39
- attachments: "Attachments",
40
- dropFiles: "Drop files to attach",
41
- send: "Send",
42
- stop: "Stop",
43
- replyingTo: (author) => `Replying to ${author}`,
44
- cancelReply: "Cancel reply",
45
- removeAttachment: (name) => `Remove ${name}`,
46
- uploading: "Uploading\u2026",
47
- uploadFailed: "Upload failed",
48
- file: "File",
49
- duration: formatChatDuration,
50
- bytes: formatChatBytes,
51
- elapsed: (s) => `${s}s`,
52
- saidBy: (name) => `${name} said`,
53
- responding: (name) => `${name} is responding\u2026`,
54
- said: (name, text) => `${name} said: ${text}`,
55
- stoppedAnnouncement: (name, text) => `${name} stopped. ${text}`,
56
- stopped: "Stopped",
57
- loadEarlier: "Load earlier messages",
58
- loadingEarlier: "Loading earlier messages",
59
- writing: "Writing\u2026",
60
- newMessages: "New messages",
61
- jumpToLatest: "Jump to latest",
62
- followUps: "Suggested follow-ups",
63
- copyMessage: "Copy message",
64
- copy: "Copy",
65
- copyResponse: "Copy response",
66
- editMessage: "Edit message",
67
- editNote: "Sending replaces this message and everything after it.",
68
- editHint: "Long-press to edit",
69
- reply: "Reply",
70
- regenerate: "Regenerate",
71
- retry: "Retry",
72
- notSent: "Not sent.",
73
- cancel: "Cancel",
74
- goodResponse: "Good response",
75
- badResponse: "Bad response",
76
- feedbackTitle: "What went wrong?",
77
- feedbackReasons: [
78
- "Not accurate",
79
- "Not helpful",
80
- "Too long",
81
- "Didn\u2019t follow instructions",
82
- "Something else"
83
- ],
84
- feedbackNote: "Tell us more (optional)",
85
- sendFeedback: "Send feedback",
86
- thanks: "Thanks for the feedback.",
87
- thinking: "Thinking\u2026",
88
- reasoning: "Reasoning",
89
- thoughtFor: (d) => `Thought for ${d}`,
90
- steps: (n) => `${n} ${n === 1 ? "step" : "steps"}`,
91
- working: (n) => `Working \xB7 ${n} ${n === 1 ? "step" : "steps"}`,
92
- usedTools: (n, failed) => `Used ${n} ${n === 1 ? "tool" : "tools"}${failed ? ` \xB7 ${failed} failed` : ""}`,
93
- toolRunning: "running",
94
- toolDone: "done",
95
- toolFailed: "failed",
96
- moreSuggestions: "More suggestions",
97
- previousSuggestions: "Previous suggestions",
98
- suggestionsPage: (p, n) => `Suggestions page ${p} of ${n}`,
99
- suggestionsPageOf: (p, n) => `Page ${p} of ${n}`,
100
- loadingSuggestions: "Loading suggestions",
101
- notUseful: "Not useful",
102
- notUsefulFor: (label) => `Not useful: ${label}`,
103
- why: (r) => `Why: ${r}`
104
- };
105
- if (Platform.OS === "android" && UIManager.setLayoutAnimationEnabledExperimental) {
106
- UIManager.setLayoutAnimationEnabledExperimental(true);
107
- }
108
- function useArrival(enabled, kind = "in", delayMs = 0) {
109
- const theme = useLoamTheme("chat");
110
- const [run] = React6.useState(enabled);
111
- const p = React6.useRef(new Animated.Value(run ? 0 : 1)).current;
112
- React6.useEffect(() => {
113
- if (!run) return;
114
- if (theme.reduceMotion) {
115
- Animated.timing(p, { toValue: 1, duration: theme.duration.fast, delay: delayMs, useNativeDriver: true }).start();
116
- return;
117
- }
118
- Animated.sequence([
119
- Animated.delay(delayMs),
120
- Animated.spring(p, { ...springConfig(theme, "ds"), toValue: 1, useNativeDriver: true })
121
- ]).start();
122
- }, [run, p, delayMs, theme]);
123
- if (!run) return void 0;
124
- if (theme.reduceMotion) return { opacity: p };
125
- const rise = 8;
126
- const scale = kind === "sent" ? 0.96 : kind === "card" ? 0.985 : 1;
127
- return {
128
- opacity: p.interpolate({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: "clamp" }),
129
- transform: [
130
- { translateY: p.interpolate({ inputRange: [0, 1], outputRange: [rise, 0] }) },
131
- { scale: p.interpolate({ inputRange: [0, 1], outputRange: [scale, 1] }) }
132
- ]
133
- };
134
- }
135
- function useAutoDisclosure(live, animate) {
136
- const theme = useLoamTheme("chat");
137
- const [open, setOpen] = React6.useState(live);
138
- const owned = React6.useRef(false);
139
- const ease = React6.useCallback(() => {
140
- if (!animate) return;
141
- const duration = theme.motion.expand.duration;
142
- LayoutAnimation.configureNext({
143
- duration,
144
- update: { type: LayoutAnimation.Types.easeInEaseOut },
145
- create: { type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.opacity, duration },
146
- delete: { type: LayoutAnimation.Types.easeInEaseOut, property: LayoutAnimation.Properties.opacity, duration }
147
- });
148
- }, [animate, theme.motion.expand.duration]);
149
- React6.useEffect(() => {
150
- if (owned.current) return;
151
- ease();
152
- setOpen(live);
153
- }, [live, ease]);
154
- const toggle = React6.useCallback(() => {
155
- owned.current = true;
156
- ease();
157
- setOpen((o) => !o);
158
- }, [ease]);
159
- return [open, toggle];
160
- }
161
- function Disclosure({
162
- open,
163
- onToggle,
164
- summary,
165
- icon,
166
- shimmer,
167
- children
168
- }) {
169
- const { styles } = useStyles(sheet);
170
- const theme = useLoamTheme("chat");
171
- const pulse = useThinkingPulse(shimmer);
172
- return /* @__PURE__ */ jsxs(View, { children: [
173
- /* @__PURE__ */ jsxs(
174
- Pressable,
175
- {
176
- role: "button",
177
- accessibilityRole: "button",
178
- accessibilityLabel: summary,
179
- ...expandedProps(open),
180
- accessibilityState: { expanded: open },
181
- onPress: onToggle,
182
- hitSlop: theme.space[2],
183
- style: styles.summary,
184
- children: [
185
- icon,
186
- /* @__PURE__ */ jsx(Animated.Text, { style: [styles.summaryText, pulse], children: summary }),
187
- /* @__PURE__ */ jsx(View, { style: { transform: [{ rotate: open ? "90deg" : "0deg" }] }, children: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: theme.icon.xs, color: theme.color.text3 }) })
188
- ]
189
- }
190
- ),
191
- open ? /* @__PURE__ */ jsx(View, { style: styles.panel, children }) : null
192
- ] });
193
- }
194
- function ReasoningView({
195
- part,
196
- live,
197
- animate,
198
- labels: labelsProp
199
- }) {
200
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
201
- const { styles } = useStyles(sheet);
202
- const theme = useLoamTheme("chat");
203
- const [open, toggle] = useAutoDisclosure(live, animate);
204
- const summary = live ? labels.thinking : part.durationMs != null ? labels.thoughtFor(labels.duration(part.durationMs)) : labels.reasoning;
205
- return /* @__PURE__ */ jsx(
206
- Disclosure,
207
- {
208
- open,
209
- onToggle: toggle,
210
- summary,
211
- shimmer: live && animate,
212
- icon: /* @__PURE__ */ jsx(Icon, { name: "brain", size: theme.icon.sm, color: theme.color.text3 }),
213
- children: /* @__PURE__ */ jsx(View, { style: styles.rail, children: /* @__PURE__ */ jsx(Text, { style: styles.reasoning, children: part.text }) })
214
- }
215
- );
216
- }
217
- function ToolMark({ status, animate }) {
218
- const theme = useLoamTheme("chat");
219
- const pop = React6.useRef(new Animated.Value(status === "running" || !animate ? 1 : 0)).current;
220
- const prev = React6.useRef(status);
221
- React6.useEffect(() => {
222
- if (prev.current === "running" && status !== "running" && animate) {
223
- pop.setValue(0);
224
- Animated.spring(pop, { toValue: 1, useNativeDriver: true, speed: 18, bounciness: 10 }).start();
225
- }
226
- prev.current = status;
227
- }, [status, animate, pop]);
228
- if (status === "running") {
229
- return /* @__PURE__ */ jsx(Thinking, { label: "", size: "sm", announce: false, motion: animate ? "full" : "none" });
230
- }
231
- return /* @__PURE__ */ jsx(Animated.View, { style: { opacity: pop, transform: [{ scale: pop.interpolate({ inputRange: [0, 1], outputRange: [0.6, 1] }) }] }, children: /* @__PURE__ */ jsx(
232
- Icon,
233
- {
234
- name: status === "error" ? "x" : "check",
235
- size: theme.icon.xs,
236
- color: status === "error" ? theme.color.danger : theme.color.good
237
- }
238
- ) });
239
- }
240
- function ToolGroupView({
241
- tools,
242
- animate,
243
- labels: labelsProp
244
- }) {
245
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
246
- const { styles } = useStyles(sheet);
247
- const theme = useLoamTheme("chat");
248
- const running = tools.some((t) => t.status === "running");
249
- const failed = tools.filter((t) => t.status === "error").length;
250
- const [open, toggle] = useAutoDisclosure(running, animate);
251
- const n = tools.length;
252
- const summary = running ? labels.working(n) : labels.usedTools(n, failed);
253
- return /* @__PURE__ */ jsx(
254
- Disclosure,
255
- {
256
- open,
257
- onToggle: toggle,
258
- summary,
259
- shimmer: running && animate,
260
- icon: /* @__PURE__ */ jsx(Icon, { name: "wrench", size: theme.icon.sm, color: theme.color.text3 }),
261
- children: /* @__PURE__ */ jsx(View, { accessibilityRole: "list", children: tools.map((tool, i) => /* @__PURE__ */ jsxs(View, { role: "listitem", style: styles.step, children: [
262
- /* @__PURE__ */ jsxs(View, { style: styles.nodeCol, children: [
263
- i > 0 ? /* @__PURE__ */ jsx(View, { style: [styles.line, styles.lineTop] }) : null,
264
- i < n - 1 ? /* @__PURE__ */ jsx(View, { style: [styles.line, styles.lineBottom] }) : null,
265
- /* @__PURE__ */ jsx(View, { style: styles.node, children: /* @__PURE__ */ jsx(Icon, { name: tool.icon ?? "wrench", size: theme.icon.xs, color: theme.color.text3 }) })
266
- ] }),
267
- /* @__PURE__ */ jsxs(View, { style: styles.stepBody, children: [
268
- /* @__PURE__ */ jsxs(View, { style: styles.stepHead, children: [
269
- /* @__PURE__ */ jsx(Text, { style: styles.stepTitle, numberOfLines: 2, children: tool.title }),
270
- /* @__PURE__ */ jsx(ToolMark, { status: tool.status, animate }),
271
- tool.durationMs != null && tool.status !== "running" ? /* @__PURE__ */ jsx(Text, { style: styles.meta, children: labels.duration(tool.durationMs) }) : null
272
- ] }),
273
- tool.detail ? /* @__PURE__ */ jsx(Text, { style: styles.meta, numberOfLines: 2, children: tool.detail }) : null
274
- ] })
275
- ] }, tool.id)) })
276
- }
277
- );
278
- }
279
- function WorkingIndicator({
280
- phase: phaseProp,
281
- startedAt,
282
- animate,
283
- labels: labelsProp
284
- }) {
285
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
286
- const phase = phaseProp ?? labels.thinking;
287
- const { styles } = useStyles(sheet);
288
- const pulse = useThinkingPulse(animate);
289
- const [now, setNow] = React6.useState(() => Date.now());
290
- React6.useEffect(() => {
291
- if (startedAt == null) return;
292
- const id = setInterval(() => setNow(Date.now()), 1e3);
293
- return () => clearInterval(id);
294
- }, [startedAt]);
295
- const elapsed = startedAt != null ? Math.max(0, Math.floor((now - startedAt) / 1e3)) : null;
296
- return /* @__PURE__ */ jsxs(View, { style: styles.working, role: "status", accessibilityLabel: phase, children: [
297
- /* @__PURE__ */ jsx(Thinking, { label: "", size: "sm", announce: false, motion: animate ? "full" : "none" }),
298
- /* @__PURE__ */ jsx(Animated.Text, { style: [styles.summaryText, pulse], children: phase }),
299
- elapsed != null && elapsed > 0 ? /* @__PURE__ */ jsxs(Text, { style: styles.meta, children: [
300
- "\xB7 ",
301
- labels.elapsed(elapsed)
302
- ] }) : null
303
- ] });
304
- }
305
- function AttachmentChip({
306
- attachment,
307
- onRemove,
308
- labels: labelsProp
309
- }) {
310
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
311
- const { styles } = useStyles(sheet);
312
- const theme = useLoamTheme("chat");
313
- const status = attachment.status ?? "done";
314
- const image = attachment.type?.startsWith("image/") && attachment.previewUrl;
315
- const sub = status === "uploading" ? labels.uploading : status === "error" ? labels.uploadFailed : attachment.size != null ? labels.bytes(attachment.size) : void 0;
316
- return /* @__PURE__ */ jsxs(View, { style: [styles.chip, status === "error" && styles.chipError], children: [
317
- image ? /* @__PURE__ */ jsx(Image, { source: { uri: attachment.previewUrl }, style: styles.thumb, accessibilityIgnoresInvertColors: true }) : /* @__PURE__ */ jsx(View, { style: styles.fileIcon, children: /* @__PURE__ */ jsx(
318
- Icon,
319
- {
320
- name: status === "uploading" ? "loader" : "file",
321
- size: theme.icon.sm,
322
- color: theme.color.text2
323
- }
324
- ) }),
325
- /* @__PURE__ */ jsxs(View, { style: styles.chipText, children: [
326
- /* @__PURE__ */ jsx(Text, { style: styles.chipName, numberOfLines: 1, children: attachment.name }),
327
- sub ? /* @__PURE__ */ jsx(Text, { style: [styles.meta, status === "error" && { color: theme.color.danger }], children: sub }) : null
328
- ] }),
329
- onRemove ? /* @__PURE__ */ jsx(
330
- Pressable,
331
- {
332
- accessibilityRole: "button",
333
- accessibilityLabel: labels.removeAttachment(attachment.name),
334
- onPress: onRemove,
335
- hitSlop: theme.space[3],
336
- style: styles.remove,
337
- children: /* @__PURE__ */ jsx(Icon, { name: "x", size: theme.icon.xs, color: theme.color.text3 })
338
- }
339
- ) : null
340
- ] });
341
- }
342
- var NODE = 24;
343
- var sheet = createStyleSheet((theme) => ({
344
- summary: {
345
- flexDirection: "row",
346
- alignItems: "center",
347
- alignSelf: "flex-start",
348
- gap: theme.space.s2,
349
- paddingVertical: theme.space[2]
350
- },
351
- summaryText: {
352
- fontFamily: theme.font.sans,
353
- fontSize: theme.type.footnote.fontSize,
354
- lineHeight: theme.type.footnote.lineHeight,
355
- color: theme.color.text2
356
- },
357
- panel: { paddingTop: theme.space.s2 },
358
- rail: {
359
- borderLeftWidth: theme.borderWidth.base,
360
- borderLeftColor: theme.color.border,
361
- paddingLeft: theme.space.s3,
362
- marginLeft: theme.space[6]
363
- },
364
- reasoning: {
365
- fontFamily: theme.font.sans,
366
- fontSize: theme.type.footnote.fontSize,
367
- lineHeight: theme.type.footnote.lineHeight,
368
- color: theme.color.text2
369
- },
370
- step: { flexDirection: "row", gap: theme.space.s3, paddingVertical: theme.space[6] },
371
- nodeCol: { width: NODE, alignItems: "center" },
372
- line: {
373
- position: "absolute",
374
- width: theme.borderWidth.base,
375
- backgroundColor: theme.color.border,
376
- left: (NODE - theme.borderWidth.base) / 2
377
- },
378
- lineTop: { top: -theme.space[6], height: theme.space[6] + theme.space[2] },
379
- lineBottom: { top: theme.space[2] + NODE, bottom: -theme.space[6] },
380
- node: {
381
- marginTop: theme.space[2],
382
- width: NODE,
383
- height: NODE,
384
- borderRadius: theme.radius.full,
385
- borderWidth: theme.borderWidth.base,
386
- borderColor: theme.color.border,
387
- backgroundColor: theme.color.surface,
388
- alignItems: "center",
389
- justifyContent: "center"
390
- },
391
- stepBody: { flex: 1, minWidth: 0, gap: theme.space[2], paddingTop: theme.space[4] },
392
- stepHead: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
393
- stepTitle: {
394
- flexShrink: 1,
395
- fontFamily: theme.font.sans,
396
- fontSize: theme.type.footnote.fontSize,
397
- lineHeight: theme.type.footnote.lineHeight,
398
- color: theme.color.text
399
- },
400
- meta: {
401
- fontFamily: theme.font.sans,
402
- fontSize: theme.type.caption1.fontSize,
403
- lineHeight: theme.type.caption1.lineHeight,
404
- color: theme.color.text3
405
- },
406
- working: { flexDirection: "row", alignItems: "center", gap: theme.space.s2, minHeight: theme.ctl.sm },
407
- chip: {
408
- flexDirection: "row",
409
- alignItems: "center",
410
- gap: theme.space.s2,
411
- maxWidth: theme.ctl.sm * 7,
412
- padding: theme.space.s1,
413
- paddingRight: theme.space.s2,
414
- borderRadius: theme.radius.md,
415
- borderWidth: theme.borderWidth.base,
416
- borderColor: theme.color.border,
417
- backgroundColor: theme.color.surface
418
- },
419
- chipError: { borderColor: theme.color.danger },
420
- thumb: { width: theme.ctl.sm, height: theme.ctl.sm, borderRadius: theme.radius.sm },
421
- fileIcon: {
422
- width: theme.ctl.sm,
423
- height: theme.ctl.sm,
424
- borderRadius: theme.radius.sm,
425
- backgroundColor: theme.color.surface2,
426
- alignItems: "center",
427
- justifyContent: "center"
428
- },
429
- chipText: { flexShrink: 1, minWidth: 0 },
430
- chipName: {
431
- fontFamily: theme.font.sans,
432
- fontSize: theme.type.footnote.fontSize,
433
- lineHeight: theme.type.footnote.lineHeight,
434
- fontWeight: theme.fontWeight.medium,
435
- color: theme.color.text
436
- },
437
- remove: { padding: theme.space[4], borderRadius: theme.radius.full }
438
- }));
439
- function ChatComposer({
440
- onSend,
441
- replyTo,
442
- onCancelReply,
443
- running = false,
444
- onStop,
445
- value: valueProp,
446
- defaultValue = "",
447
- onValueChange,
448
- placeholder: placeholderProp,
449
- label: labelProp,
450
- attachments = [],
451
- onAttachPress,
452
- onRemoveAttachment,
453
- toolbar,
454
- hint,
455
- disabled = false,
456
- inputRef,
457
- motion = "full",
458
- style,
459
- labels: labelsProp
460
- }) {
461
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
462
- const placeholder = placeholderProp ?? labels.placeholder;
463
- const label = labelProp ?? labels.composer;
464
- const { styles } = useStyles(sheet2);
465
- const theme = useLoamTheme("chat");
466
- const animate = useResolvedMotion(motion, "chat") === "full";
467
- const [inner, setInner] = React6.useState(defaultValue);
468
- const value = valueProp ?? inner;
469
- const setValue = (next) => {
470
- if (valueProp === void 0) setInner(next);
471
- onValueChange?.(next);
472
- };
473
- const [focused, setFocused] = React6.useState(false);
474
- const uploading = attachments.some((a) => a.status === "uploading");
475
- const canSend = !disabled && !running && !uploading && (value.trim() !== "" || attachments.length > 0);
476
- const showStop = running && !!onStop;
477
- const swap = React6.useRef(new Animated.Value(showStop ? 1 : 0)).current;
478
- React6.useEffect(() => {
479
- Animated.timing(swap, {
480
- toValue: showStop ? 1 : 0,
481
- duration: animate ? theme.motion.swap.duration : 0,
482
- easing: Easing.bezier(...theme.motion.swap.easing),
483
- useNativeDriver: true
484
- }).start();
485
- }, [showStop, animate, swap, theme.motion.swap]);
486
- const send = () => {
487
- if (!canSend) return;
488
- onSend(value.trim(), attachments);
489
- setValue("");
490
- };
491
- const active = showStop || canSend;
492
- return /* @__PURE__ */ jsxs(View, { style: [styles.root, style], children: [
493
- /* @__PURE__ */ jsxs(View, { style: [styles.box, focused && styles.boxFocused, disabled && styles.disabled], children: [
494
- replyTo ? /* @__PURE__ */ jsx(
495
- ReplyBar,
496
- {
497
- reply: replyTo,
498
- animate,
499
- onCancel: onCancelReply,
500
- labels
501
- },
502
- replyTo.text
503
- ) : null,
504
- attachments.length > 0 ? /* @__PURE__ */ jsx(
505
- ScrollView,
506
- {
507
- horizontal: true,
508
- showsHorizontalScrollIndicator: false,
509
- contentContainerStyle: styles.chips,
510
- children: attachments.map((a) => /* @__PURE__ */ jsx(
511
- AttachmentChip,
512
- {
513
- attachment: a,
514
- onRemove: onRemoveAttachment ? () => onRemoveAttachment(a.id) : void 0,
515
- labels
516
- },
517
- a.id
518
- ))
519
- }
520
- ) : null,
521
- /* @__PURE__ */ jsx(
522
- TextInput,
523
- {
524
- ref: inputRef,
525
- accessibilityLabel: label,
526
- placeholder,
527
- placeholderTextColor: theme.color.text3,
528
- editable: !disabled,
529
- accessibilityState: { disabled: !!disabled },
530
- "aria-disabled": disabled || void 0,
531
- multiline: true,
532
- value,
533
- onChangeText: setValue,
534
- onFocus: () => setFocused(true),
535
- onBlur: () => setFocused(false),
536
- style: styles.input
537
- }
538
- ),
539
- /* @__PURE__ */ jsxs(View, { style: styles.bar, children: [
540
- onAttachPress ? /* @__PURE__ */ jsx(
541
- Pressable,
542
- {
543
- accessibilityRole: "button",
544
- accessibilityLabel: labels.attachFiles,
545
- disabled,
546
- onPress: onAttachPress,
547
- hitSlop: theme.space[6],
548
- style: ({ pressed }) => [styles.tool, pressed && styles.toolDown],
549
- children: /* @__PURE__ */ jsx(Icon, { name: "paperclip", size: theme.icon.sm, color: theme.color.text2 })
550
- }
551
- ) : null,
552
- toolbar,
553
- /* @__PURE__ */ jsx(View, { style: styles.spacer }),
554
- /* @__PURE__ */ jsxs(
555
- Pressable,
556
- {
557
- accessibilityRole: "button",
558
- accessibilityLabel: showStop ? labels.stop : labels.send,
559
- accessibilityState: { disabled: !active },
560
- disabled: !active,
561
- onPress: showStop ? onStop : send,
562
- hitSlop: theme.space[6],
563
- style: [styles.send, !active && styles.sendIdle],
564
- children: [
565
- /* @__PURE__ */ jsx(
566
- Animated.View,
567
- {
568
- style: [
569
- styles.glyph,
570
- {
571
- opacity: swap.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }),
572
- transform: [
573
- { scale: swap.interpolate({ inputRange: [0, 1], outputRange: [1, 0.5] }) }
574
- ]
575
- }
576
- ],
577
- children: /* @__PURE__ */ jsx(
578
- Icon,
579
- {
580
- name: "arrow-up",
581
- size: theme.icon.sm,
582
- color: active ? theme.color.accentInk : theme.color.text3
583
- }
584
- )
585
- }
586
- ),
587
- /* @__PURE__ */ jsx(
588
- Animated.View,
589
- {
590
- style: [
591
- styles.glyph,
592
- {
593
- opacity: swap,
594
- transform: [
595
- { scale: swap.interpolate({ inputRange: [0, 1], outputRange: [0.5, 1] }) }
596
- ]
597
- }
598
- ],
599
- children: /* @__PURE__ */ jsx(Icon, { name: "stop", size: theme.icon.xs, color: theme.color.accentInk })
600
- }
601
- )
602
- ]
603
- }
604
- )
605
- ] })
606
- ] }),
607
- hint ? /* @__PURE__ */ jsx(Text, { style: styles.hint, children: hint }) : null
608
- ] });
609
- }
610
- function ReplyBar({
611
- reply,
612
- animate,
613
- onCancel,
614
- labels
615
- }) {
616
- const { styles } = useStyles(sheet2);
617
- const theme = useLoamTheme("chat");
618
- const arrival = useArrival(animate, "card");
619
- return /* @__PURE__ */ jsxs(
620
- Animated.View,
621
- {
622
- style: [styles.reply, arrival],
623
- role: "group",
624
- accessibilityLabel: labels.replyingTo(reply.author),
625
- children: [
626
- /* @__PURE__ */ jsx(Icon, { name: "undo", size: theme.icon.xs, color: theme.color.text3 }),
627
- /* @__PURE__ */ jsxs(View, { style: styles.replyBody, children: [
628
- /* @__PURE__ */ jsx(Text, { style: styles.replyAuthor, children: labels.replyingTo(reply.author) }),
629
- /* @__PURE__ */ jsx(Text, { style: styles.replyText, numberOfLines: 1, children: reply.text })
630
- ] }),
631
- onCancel ? /* @__PURE__ */ jsx(
632
- Pressable,
633
- {
634
- accessibilityRole: "button",
635
- accessibilityLabel: labels.cancelReply,
636
- onPress: onCancel,
637
- hitSlop: theme.space[8],
638
- style: styles.tool,
639
- children: /* @__PURE__ */ jsx(Icon, { name: "x", size: theme.icon.sm, color: theme.color.text3 })
640
- }
641
- ) : null
642
- ]
643
- }
644
- );
645
- }
646
- var sheet2 = createStyleSheet((theme) => ({
647
- root: { gap: theme.space.s2 },
648
- reply: {
649
- flexDirection: "row",
650
- alignItems: "center",
651
- gap: theme.space.s2,
652
- marginHorizontal: theme.space.s2,
653
- marginBottom: theme.space.s1,
654
- paddingStart: theme.space.s3,
655
- paddingVertical: theme.space[6],
656
- borderRadius: theme.radius.lg,
657
- borderStartWidth: theme.borderWidth.strong,
658
- borderStartColor: theme.color.accent,
659
- backgroundColor: theme.color.surface3
660
- },
661
- replyBody: { flex: 1, minWidth: 0 },
662
- replyAuthor: {
663
- fontFamily: theme.font.sans,
664
- fontSize: theme.type.caption1.fontSize,
665
- lineHeight: theme.type.caption1.lineHeight,
666
- color: theme.color.text2
667
- },
668
- replyText: {
669
- fontFamily: theme.font.sans,
670
- fontSize: theme.type.footnote.fontSize,
671
- lineHeight: theme.type.footnote.lineHeight,
672
- color: theme.color.text
673
- },
674
- box: {
675
- borderRadius: theme.radius.xl,
676
- borderWidth: theme.borderWidth.strong,
677
- borderColor: theme.color.borderStrong,
678
- backgroundColor: theme.color.surface2,
679
- paddingTop: theme.space.s2
680
- },
681
- boxFocused: { borderColor: theme.focus.ringColor, backgroundColor: theme.color.surface },
682
- disabled: { opacity: theme.state.disabledOpacity },
683
- chips: { gap: theme.space.s2, paddingHorizontal: theme.space.s3, paddingBottom: theme.space.s1 },
684
- input: {
685
- minHeight: theme.ctl.md,
686
- maxHeight: theme.type.body.lineHeight * 8,
687
- paddingHorizontal: theme.space.s4,
688
- paddingVertical: theme.space.s1,
689
- fontFamily: theme.font.sans,
690
- fontSize: theme.type.body.fontSize,
691
- lineHeight: theme.type.body.lineHeight,
692
- color: theme.color.text,
693
- textAlignVertical: "top"
694
- },
695
- bar: { flexDirection: "row", alignItems: "center", gap: theme.space.s1, padding: theme.space.s2 },
696
- spacer: { flex: 1 },
697
- tool: {
698
- width: theme.ctl.iconBtnSm,
699
- height: theme.ctl.iconBtnSm,
700
- borderRadius: theme.radius.md,
701
- alignItems: "center",
702
- justifyContent: "center"
703
- },
704
- toolDown: { backgroundColor: theme.state.pressed },
705
- send: {
706
- width: theme.ctl.iconBtnSm,
707
- height: theme.ctl.iconBtnSm,
708
- borderRadius: theme.radius.full,
709
- backgroundColor: theme.color.accent,
710
- alignItems: "center",
711
- justifyContent: "center"
712
- },
713
- sendIdle: { backgroundColor: theme.color.surface3 },
714
- glyph: { position: "absolute" },
715
- hint: {
716
- textAlign: "center",
717
- fontFamily: theme.font.sans,
718
- fontSize: theme.type.caption1.fontSize,
719
- lineHeight: theme.type.caption1.lineHeight,
720
- color: theme.color.text3
721
- }
722
- }));
723
-
724
- // src/chat/chat-types.ts
725
- function chatMessageText(message) {
726
- return message.parts.filter((p) => p.kind === "text").map((p) => p.text).join("\n\n").trim();
727
- }
728
- function AssistantMark() {
729
- const { styles } = useStyles(sheet3);
730
- const theme = useLoamTheme("chat");
731
- return /* @__PURE__ */ jsx(View, { style: styles.mark, children: /* @__PURE__ */ jsx(Icon, { name: "sparkle", size: theme.icon.sm, color: theme.color.accentOnSoft }) });
732
- }
733
- function segment(parts) {
734
- const out = [];
735
- for (const part of parts) {
736
- const last = out[out.length - 1];
737
- if (part.kind === "tool") {
738
- if (last?.kind === "tools") last.tools.push(part);
739
- else out.push({ kind: "tools", id: part.id, tools: [part] });
740
- } else out.push({ kind: "part", part });
741
- }
742
- return out;
743
- }
744
- function Action({
745
- label,
746
- icon,
747
- onPress,
748
- pressed
749
- }) {
750
- const { styles } = useStyles(sheet3);
751
- const theme = useLoamTheme("chat");
752
- return /* @__PURE__ */ jsx(
753
- Pressable,
754
- {
755
- accessibilityRole: "button",
756
- accessibilityLabel: label,
757
- accessibilityState: pressed != null ? { selected: pressed } : void 0,
758
- "aria-pressed": pressed,
759
- onPress,
760
- hitSlop: theme.space[6],
761
- style: ({ pressed: down }) => [styles.action, down && styles.actionDown],
762
- children: /* @__PURE__ */ jsx(
763
- Icon,
764
- {
765
- name: icon,
766
- size: theme.icon.sm,
767
- color: pressed ? theme.color.text : theme.color.text3
768
- }
769
- )
770
- }
771
- );
772
- }
773
- var REPLY_ARM = 56;
774
- function useSwipeReply(onReply, animate) {
775
- const theme = useLoamTheme("chat");
776
- const tx = React6.useRef(new Animated.Value(0)).current;
777
- const live = React6.useRef({ onReply, animate, theme });
778
- live.current = { onReply, animate, theme };
779
- const state = React6.useRef({ armed: false, broke: false });
780
- const sign = I18nManager.isRTL ? -1 : 1;
781
- const pan = React6.useRef(
782
- // One horizontal pull on a bubble inside a vertical list; no gesture
783
- // library is allowed on native, so `PanResponder`, as `Toast` does.
784
- // eslint-disable-next-line loam/no-pan-responder
785
- PanResponder.create({
786
- onMoveShouldSetPanResponder: (_e, g) => !!live.current.onReply && g.dx * sign > 8 && Math.abs(g.dx) > Math.abs(g.dy) * 1.5,
787
- onPanResponderGrant: () => {
788
- tx.stopAnimation();
789
- state.current = { armed: false, broke: false };
790
- },
791
- onPanResponderMove: (_e, g) => {
792
- const a = Math.max(0, g.dx * sign);
793
- const s = state.current;
794
- const armed = a >= REPLY_ARM;
795
- if (armed && !s.armed) haptics.selection();
796
- s.armed = armed;
797
- if (a < BREAKAWAY && !s.broke) return tx.setValue(sign * resist(a));
798
- const target = sign * (armed ? REPLY_ARM + rubberband(a - REPLY_ARM, REPLY_ARM * 4) : a);
799
- if (!s.broke && live.current.animate) {
800
- s.broke = true;
801
- Animated.spring(tx, {
802
- toValue: target,
803
- ...springConfig(live.current.theme, "fs"),
804
- useNativeDriver: true
805
- }).start();
806
- return;
807
- }
808
- s.broke = true;
809
- tx.setValue(target);
810
- },
811
- onPanResponderRelease: (_e, g) => {
812
- const { onReply: reply, animate: anim, theme: th } = live.current;
813
- if (anim) {
814
- Animated.spring(tx, {
815
- toValue: 0,
816
- ...springConfig(th, "fs", { velocity: g.vx }),
817
- useNativeDriver: true
818
- }).start();
819
- } else tx.setValue(0);
820
- if (state.current.armed) reply?.();
821
- state.current.armed = false;
822
- },
823
- onPanResponderTerminate: () => {
824
- state.current.armed = false;
825
- tx.setValue(0);
826
- }
827
- })
828
- ).current;
829
- const reveal = tx.interpolate({
830
- inputRange: sign > 0 ? [0, REPLY_ARM] : [-REPLY_ARM, 0],
831
- outputRange: sign > 0 ? [0, 1] : [1, 0],
832
- extrapolate: "clamp"
833
- });
834
- const cueScale = tx.interpolate({
835
- inputRange: sign > 0 ? [0, REPLY_ARM - 0.01, REPLY_ARM] : [-REPLY_ARM, -REPLY_ARM + 0.01, 0],
836
- outputRange: sign > 0 ? [0.5, 0.9, 1] : [1, 0.9, 0.5],
837
- extrapolate: "clamp"
838
- });
839
- return { tx, panHandlers: onReply ? pan.panHandlers : {}, reveal, cueScale };
840
- }
841
- function SwipeReply({
842
- onReply,
843
- animate,
844
- style,
845
- children
846
- }) {
847
- const { styles } = useStyles(sheet3);
848
- const theme = useLoamTheme("chat");
849
- const swipe = useSwipeReply(onReply, animate);
850
- return /* @__PURE__ */ jsxs(View, { style, children: [
851
- onReply ? /* @__PURE__ */ jsx(
852
- Animated.View,
853
- {
854
- pointerEvents: "none",
855
- importantForAccessibility: "no-hide-descendants",
856
- accessibilityElementsHidden: true,
857
- style: [
858
- styles.replyCue,
859
- { opacity: swipe.reveal, transform: [{ scale: swipe.cueScale }] }
860
- ],
861
- children: /* @__PURE__ */ jsx(Icon, { name: "undo", size: theme.icon.xs, color: theme.color.text2 })
862
- }
863
- ) : null,
864
- /* @__PURE__ */ jsx(
865
- Animated.View,
866
- {
867
- testID: "chat-swipe-reply",
868
- style: { transform: [{ translateX: swipe.tx }] },
869
- ...swipe.panHandlers,
870
- children
871
- }
872
- )
873
- ] });
874
- }
875
- var ChatMessageRow = React6.memo(function ChatMessageRow2(props) {
876
- return props.message.role === "user" ? /* @__PURE__ */ jsx(UserRow, { ...props }) : /* @__PURE__ */ jsx(AssistantRow, { ...props });
877
- });
878
- function UserRow({
879
- message,
880
- appearing,
881
- animate,
882
- user,
883
- running,
884
- onEdit,
885
- onRetry,
886
- onCopy,
887
- onReply,
888
- labels: labelsProp
889
- }) {
890
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
891
- const { styles } = useStyles(sheet3);
892
- const theme = useLoamTheme("chat");
893
- const arrival = useArrival(appearing && animate, "sent");
894
- const [editing, setEditing] = React6.useState(false);
895
- const text = chatMessageText(message);
896
- const [draft, setDraft] = React6.useState(text);
897
- const status = message.status ?? "done";
898
- const failed = status === "failed";
899
- const canEdit = !!onEdit && !running && status === "done" && !!text;
900
- const reply = onReply && status === "done" && !editing ? () => onReply(message) : void 0;
901
- const save = () => {
902
- const t = draft.trim();
903
- setEditing(false);
904
- if (t && t !== text) onEdit?.(message, t);
905
- };
906
- return /* @__PURE__ */ jsxs(
907
- Animated.View,
908
- {
909
- role: "group",
910
- accessibilityLabel: labels.saidBy(user?.name ?? labels.you),
911
- accessibilityActions: reply ? [{ name: "reply", label: labels.reply }] : void 0,
912
- onAccessibilityAction: reply ? (e) => e.nativeEvent.actionName === "reply" && reply() : void 0,
913
- style: [styles.userRow, arrival],
914
- children: [
915
- message.attachments?.length ? /* @__PURE__ */ jsx(View, { style: styles.userAttachments, children: message.attachments.map((a) => /* @__PURE__ */ jsx(AttachmentChip, { attachment: a, labels }, a.id)) }) : null,
916
- editing ? /* @__PURE__ */ jsxs(View, { style: styles.edit, children: [
917
- /* @__PURE__ */ jsx(
918
- TextInput,
919
- {
920
- accessibilityLabel: labels.editMessage,
921
- value: draft,
922
- onChangeText: setDraft,
923
- multiline: true,
924
- autoFocus: true,
925
- style: styles.editInput,
926
- placeholderTextColor: theme.color.text3
927
- }
928
- ),
929
- /* @__PURE__ */ jsx(Text, { style: styles.meta, children: labels.editNote }),
930
- /* @__PURE__ */ jsxs(View, { style: styles.editActions, children: [
931
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onPress: () => setEditing(false), children: labels.cancel }),
932
- /* @__PURE__ */ jsx(Button, { size: "sm", onPress: save, disabled: !draft.trim(), children: labels.send })
933
- ] })
934
- ] }) : text ? /* @__PURE__ */ jsx(SwipeReply, { onReply: reply, animate, style: styles.userSwipe, children: /* @__PURE__ */ jsx(
935
- Pressable,
936
- {
937
- accessibilityHint: canEdit ? labels.editHint : void 0,
938
- accessibilityActions: canEdit ? [{ name: "edit", label: labels.editMessage }] : void 0,
939
- onAccessibilityAction: canEdit ? (e) => {
940
- if (e.nativeEvent.actionName !== "edit") return;
941
- setDraft(text);
942
- setEditing(true);
943
- } : void 0,
944
- onLongPress: canEdit ? () => {
945
- setDraft(text);
946
- setEditing(true);
947
- } : void 0,
948
- style: [
949
- styles.bubble,
950
- styles.bubbleFill,
951
- (failed || status === "sending") && styles.faded
952
- ],
953
- children: /* @__PURE__ */ jsx(Text, { style: styles.bubbleText, selectable: true, children: text })
954
- }
955
- ) }) : null,
956
- failed ? /* @__PURE__ */ jsxs(View, { style: styles.failed, accessibilityRole: "alert", children: [
957
- /* @__PURE__ */ jsx(Icon, { name: "alert-circle", size: theme.icon.xs, color: theme.color.danger }),
958
- /* @__PURE__ */ jsx(Text, { style: [styles.meta, { color: theme.color.danger }], children: labels.notSent }),
959
- onRetry ? /* @__PURE__ */ jsx(
960
- Pressable,
961
- {
962
- accessibilityRole: "button",
963
- onPress: () => onRetry(message),
964
- hitSlop: theme.space[8],
965
- children: /* @__PURE__ */ jsx(Text, { style: styles.retry, children: labels.retry })
966
- }
967
- ) : null
968
- ] }) : !editing ? /* @__PURE__ */ jsxs(View, { style: styles.actions, children: [
969
- message.time ? /* @__PURE__ */ jsx(Text, { style: styles.meta, children: message.time }) : null,
970
- text && onCopy ? /* @__PURE__ */ jsx(
971
- CopyButton,
972
- {
973
- value: text,
974
- size: "sm",
975
- accessibilityLabel: labels.copyMessage,
976
- onCopy: () => onCopy(message, text)
977
- }
978
- ) : null,
979
- canEdit ? /* @__PURE__ */ jsx(
980
- Action,
981
- {
982
- label: labels.editMessage,
983
- icon: "edit",
984
- onPress: () => {
985
- setDraft(text);
986
- setEditing(true);
987
- }
988
- }
989
- ) : null,
990
- reply ? /* @__PURE__ */ jsx(Action, { label: labels.reply, icon: "undo", onPress: reply }) : null
991
- ] }) : null
992
- ]
993
- }
994
- );
995
- }
996
- function FeedbackPanel({
997
- reasons,
998
- animate,
999
- onSubmit,
1000
- onCancel,
1001
- labels
1002
- }) {
1003
- const { styles } = useStyles(sheet3);
1004
- const theme = useLoamTheme("chat");
1005
- const arrival = useArrival(animate, "card");
1006
- const [reason, setReason] = React6.useState();
1007
- const [note, setNote] = React6.useState("");
1008
- return /* @__PURE__ */ jsxs(
1009
- Animated.View,
1010
- {
1011
- style: [styles.feedback, arrival],
1012
- role: "group",
1013
- accessibilityLabel: labels.feedbackTitle,
1014
- children: [
1015
- /* @__PURE__ */ jsx(Text, { style: styles.feedbackTitle, children: labels.feedbackTitle }),
1016
- /* @__PURE__ */ jsx(View, { style: styles.chips, children: reasons.map((r) => {
1017
- const on = reason === r;
1018
- return /* @__PURE__ */ jsx(
1019
- Pressable,
1020
- {
1021
- accessibilityRole: "button",
1022
- accessibilityState: { selected: on },
1023
- "aria-pressed": on,
1024
- onPress: () => setReason(on ? void 0 : r),
1025
- style: [styles.reason, on && styles.reasonOn],
1026
- children: /* @__PURE__ */ jsx(Text, { style: [styles.reasonText, on && { color: theme.color.accentOnSoft }], children: r })
1027
- },
1028
- r
1029
- );
1030
- }) }),
1031
- /* @__PURE__ */ jsx(
1032
- TextInput,
1033
- {
1034
- accessibilityLabel: labels.feedbackNote,
1035
- placeholder: labels.feedbackNote,
1036
- placeholderTextColor: theme.color.text3,
1037
- value: note,
1038
- onChangeText: setNote,
1039
- multiline: true,
1040
- style: styles.note
1041
- }
1042
- ),
1043
- /* @__PURE__ */ jsxs(View, { style: styles.editActions, children: [
1044
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onPress: onCancel, children: labels.cancel }),
1045
- /* @__PURE__ */ jsx(
1046
- Button,
1047
- {
1048
- size: "sm",
1049
- disabled: !reason && !note.trim(),
1050
- onPress: () => onSubmit(reason, note.trim()),
1051
- children: labels.sendFeedback
1052
- }
1053
- )
1054
- ] })
1055
- ]
1056
- }
1057
- );
1058
- }
1059
- function AssistantRow({
1060
- message,
1061
- latest,
1062
- appearing,
1063
- animate,
1064
- assistant,
1065
- feedbackReasons,
1066
- running,
1067
- onRegenerate,
1068
- onFeedback,
1069
- onCopy,
1070
- onReply,
1071
- onLinkPress,
1072
- labels: labelsProp
1073
- }) {
1074
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
1075
- const { styles } = useStyles(sheet3);
1076
- const theme = useLoamTheme("chat");
1077
- const arrival = useArrival(appearing && animate, "in");
1078
- const status = message.status ?? "done";
1079
- const streaming = status === "streaming";
1080
- const text = chatMessageText(message);
1081
- const reply = onReply && !streaming && text ? () => onReply(message) : void 0;
1082
- const [feedback, setFeedback] = React6.useState(message.feedback);
1083
- const [asking, setAsking] = React6.useState(false);
1084
- const [thanked, setThanked] = React6.useState(false);
1085
- React6.useEffect(() => setFeedback(message.feedback), [message.feedback]);
1086
- const lastPart = message.parts[message.parts.length - 1];
1087
- const hasVisible = message.parts.some((p) => p.kind !== "text" || p.text.trim() !== "");
1088
- const showWorking = streaming && (!hasVisible || lastPart?.kind === "tool" && lastPart.status !== "running");
1089
- const rate = (value) => {
1090
- if (feedback === value) return;
1091
- setFeedback(value);
1092
- if (value === "up") {
1093
- setAsking(false);
1094
- setThanked(true);
1095
- onFeedback?.(message, { value });
1096
- } else {
1097
- setThanked(false);
1098
- setAsking(true);
1099
- }
1100
- };
1101
- return /* @__PURE__ */ jsxs(
1102
- Animated.View,
1103
- {
1104
- role: "group",
1105
- accessibilityLabel: labels.saidBy(assistant.name),
1106
- "aria-busy": streaming || void 0,
1107
- accessibilityActions: reply ? [{ name: "reply", label: labels.reply }] : void 0,
1108
- onAccessibilityAction: reply ? (e) => e.nativeEvent.actionName === "reply" && reply() : void 0,
1109
- style: [styles.assistantRow, arrival],
1110
- children: [
1111
- /* @__PURE__ */ jsx(View, { importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true, children: assistant.avatar ?? /* @__PURE__ */ jsx(AssistantMark, {}) }),
1112
- /* @__PURE__ */ jsxs(View, { style: styles.assistantBody, children: [
1113
- /* @__PURE__ */ jsx(SwipeReply, { onReply: reply, animate, children: /* @__PURE__ */ jsxs(View, { style: styles.assistantParts, children: [
1114
- segment(message.parts).map((seg) => {
1115
- if (seg.kind === "tools")
1116
- return /* @__PURE__ */ jsx(ToolGroupView, { tools: seg.tools, animate, labels }, seg.id);
1117
- const part = seg.part;
1118
- switch (part.kind) {
1119
- case "text":
1120
- return /* @__PURE__ */ jsx(
1121
- Markdown,
1122
- {
1123
- content: part.text,
1124
- streaming: streaming && part === lastPart,
1125
- motion: animate ? "full" : "none",
1126
- onLinkPress
1127
- },
1128
- part.id
1129
- );
1130
- case "reasoning":
1131
- return /* @__PURE__ */ jsx(
1132
- ReasoningView,
1133
- {
1134
- part,
1135
- live: streaming && part === lastPart,
1136
- animate,
1137
- labels
1138
- },
1139
- part.id
1140
- );
1141
- case "error":
1142
- return /* @__PURE__ */ jsxs(View, { style: styles.error, accessibilityRole: "alert", children: [
1143
- /* @__PURE__ */ jsx(
1144
- Icon,
1145
- {
1146
- name: "alert-circle",
1147
- size: theme.icon.sm,
1148
- color: theme.color.dangerOnSoft
1149
- }
1150
- ),
1151
- /* @__PURE__ */ jsx(Text, { style: styles.errorText, children: part.message })
1152
- ] }, part.id);
1153
- case "custom":
1154
- return /* @__PURE__ */ jsx(CustomPart, { animate, children: part.content }, part.id);
1155
- default:
1156
- return null;
1157
- }
1158
- }),
1159
- showWorking ? /* @__PURE__ */ jsx(
1160
- WorkingIndicator,
1161
- {
1162
- phase: message.phase,
1163
- startedAt: message.startedAt,
1164
- animate,
1165
- labels
1166
- }
1167
- ) : null
1168
- ] }) }),
1169
- !streaming ? /* @__PURE__ */ jsxs(View, { style: styles.actions, children: [
1170
- status === "stopped" ? /* @__PURE__ */ jsxs(View, { style: styles.stopped, children: [
1171
- /* @__PURE__ */ jsx(Icon, { name: "stop", size: theme.icon.xs, color: theme.color.text3 }),
1172
- /* @__PURE__ */ jsx(Text, { style: styles.meta, children: labels.stopped })
1173
- ] }) : null,
1174
- text && onCopy ? /* @__PURE__ */ jsx(
1175
- CopyButton,
1176
- {
1177
- value: text,
1178
- size: "sm",
1179
- accessibilityLabel: labels.copyResponse,
1180
- onCopy: () => onCopy(message, text)
1181
- }
1182
- ) : null,
1183
- onFeedback && text ? /* @__PURE__ */ jsxs(Fragment, { children: [
1184
- /* @__PURE__ */ jsx(
1185
- Action,
1186
- {
1187
- label: labels.goodResponse,
1188
- icon: "thumbs-up",
1189
- pressed: feedback === "up",
1190
- onPress: () => rate("up")
1191
- }
1192
- ),
1193
- /* @__PURE__ */ jsx(
1194
- Action,
1195
- {
1196
- label: labels.badResponse,
1197
- icon: "thumbs-down",
1198
- pressed: feedback === "down",
1199
- onPress: () => rate("down")
1200
- }
1201
- )
1202
- ] }) : null,
1203
- latest && onRegenerate && !running ? /* @__PURE__ */ jsx(Action, { label: labels.regenerate, icon: "refresh", onPress: () => onRegenerate(message) }) : null,
1204
- reply ? /* @__PURE__ */ jsx(Action, { label: labels.reply, icon: "undo", onPress: reply }) : null,
1205
- message.meta || message.time ? /* @__PURE__ */ jsxs(Text, { style: styles.meta, children: [
1206
- message.meta,
1207
- message.meta && message.time ? " \xB7 " : null,
1208
- message.time
1209
- ] }) : null,
1210
- thanked ? /* @__PURE__ */ jsx(Text, { style: styles.meta, accessibilityLiveRegion: "polite", role: "status", children: labels.thanks }) : null
1211
- ] }) : null,
1212
- asking ? /* @__PURE__ */ jsx(
1213
- FeedbackPanel,
1214
- {
1215
- reasons: feedbackReasons,
1216
- animate,
1217
- labels,
1218
- onCancel: () => {
1219
- setAsking(false);
1220
- setFeedback(message.feedback);
1221
- },
1222
- onSubmit: (reason, note) => {
1223
- setAsking(false);
1224
- setThanked(true);
1225
- onFeedback?.(message, { value: "down", reason, note: note || void 0 });
1226
- }
1227
- }
1228
- ) : null
1229
- ] })
1230
- ]
1231
- }
1232
- );
1233
- }
1234
- function CustomPart({ animate, children }) {
1235
- const arrival = useArrival(animate, "card");
1236
- return /* @__PURE__ */ jsx(Animated.View, { style: arrival, children });
1237
- }
1238
- var sheet3 = createStyleSheet((theme) => ({
1239
- mark: {
1240
- width: theme.ctl.sm,
1241
- height: theme.ctl.sm,
1242
- borderRadius: theme.radius.md,
1243
- backgroundColor: theme.color.accentSoft,
1244
- borderWidth: theme.borderWidth.base,
1245
- borderColor: theme.edge.accent,
1246
- alignItems: "center",
1247
- justifyContent: "center"
1248
- },
1249
- userRow: { alignItems: "flex-end", gap: theme.space[6] },
1250
- userAttachments: {
1251
- flexDirection: "row",
1252
- flexWrap: "wrap",
1253
- justifyContent: "flex-end",
1254
- gap: theme.space.s2
1255
- },
1256
- bubble: {
1257
- maxWidth: "85%",
1258
- backgroundColor: theme.color.surface3,
1259
- borderRadius: theme.radius.xl,
1260
- borderBottomRightRadius: theme.radius.xs,
1261
- paddingHorizontal: theme.space.s4,
1262
- paddingVertical: theme.space[10]
1263
- },
1264
- // Sending / failed: a hint of "not yet delivered", never `disabledOpacity`
1265
- // — at 0.38 the message itself fell to ~2:1 and could not be read back.
1266
- // Same 0.72 as the web bubble.
1267
- faded: { opacity: theme.opacity[72] },
1268
- bubbleText: {
1269
- fontFamily: theme.font.sans,
1270
- fontSize: theme.type.body.fontSize,
1271
- lineHeight: theme.type.body.lineHeight,
1272
- color: theme.color.text
1273
- },
1274
- failed: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
1275
- retry: {
1276
- fontFamily: theme.font.sans,
1277
- fontSize: theme.type.caption1.fontSize,
1278
- fontWeight: theme.fontWeight.bold,
1279
- color: theme.color.danger,
1280
- textDecorationLine: "underline"
1281
- },
1282
- edit: { alignSelf: "stretch", gap: theme.space.s2 },
1283
- editInput: {
1284
- minHeight: theme.ctl.lg * 2,
1285
- fontFamily: theme.font.sans,
1286
- fontSize: theme.type.body.fontSize,
1287
- lineHeight: theme.type.body.lineHeight,
1288
- color: theme.color.text,
1289
- backgroundColor: theme.color.surface2,
1290
- borderWidth: theme.borderWidth.strong,
1291
- borderColor: theme.focus.ringColor,
1292
- borderRadius: theme.radius.md,
1293
- padding: theme.space.s3,
1294
- textAlignVertical: "top"
1295
- },
1296
- editActions: { flexDirection: "row", justifyContent: "flex-end", gap: theme.space.s2 },
1297
- actions: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: theme.space.s1 },
1298
- action: {
1299
- width: theme.ctl.iconBtnSm,
1300
- height: theme.ctl.iconBtnSm,
1301
- borderRadius: theme.radius.md,
1302
- alignItems: "center",
1303
- justifyContent: "center"
1304
- },
1305
- actionDown: { backgroundColor: theme.state.pressed },
1306
- meta: {
1307
- fontFamily: theme.font.sans,
1308
- fontSize: theme.type.caption1.fontSize,
1309
- lineHeight: theme.type.caption1.lineHeight,
1310
- color: theme.color.text3
1311
- },
1312
- stopped: {
1313
- flexDirection: "row",
1314
- alignItems: "center",
1315
- gap: theme.space[6],
1316
- marginRight: theme.space.s2
1317
- },
1318
- assistantRow: { flexDirection: "row", gap: theme.space.s3 },
1319
- assistantBody: { flex: 1, minWidth: 0, gap: theme.space.s2 },
1320
- assistantParts: { gap: theme.space.s2 },
1321
- userSwipe: { maxWidth: "85%" },
1322
- bubbleFill: { maxWidth: "100%" },
1323
- replyCue: {
1324
- position: "absolute",
1325
- start: -theme.ctl.sm,
1326
- top: "50%",
1327
- marginTop: -theme.icon.sm,
1328
- width: theme.icon.sm * 2,
1329
- height: theme.icon.sm * 2,
1330
- borderRadius: theme.radius.full,
1331
- backgroundColor: theme.color.surface3,
1332
- alignItems: "center",
1333
- justifyContent: "center"
1334
- },
1335
- error: {
1336
- flexDirection: "row",
1337
- gap: theme.space.s2,
1338
- padding: theme.space.s3,
1339
- borderRadius: theme.radius.md,
1340
- borderWidth: theme.borderWidth.base,
1341
- borderColor: theme.edge.danger,
1342
- backgroundColor: theme.color.dangerSoft
1343
- },
1344
- errorText: {
1345
- flex: 1,
1346
- fontFamily: theme.font.sans,
1347
- fontSize: theme.type.footnote.fontSize,
1348
- lineHeight: theme.type.footnote.lineHeight,
1349
- color: theme.color.dangerOnSoft
1350
- },
1351
- feedback: {
1352
- gap: theme.space.s3,
1353
- padding: theme.space.s3,
1354
- borderRadius: theme.radius.lg,
1355
- borderWidth: theme.borderWidth.base,
1356
- borderColor: theme.color.border,
1357
- backgroundColor: theme.color.surface
1358
- },
1359
- feedbackTitle: {
1360
- fontFamily: theme.font.sans,
1361
- fontSize: theme.type.footnote.fontSize,
1362
- fontWeight: theme.fontWeight.bold,
1363
- color: theme.color.text
1364
- },
1365
- chips: { flexDirection: "row", flexWrap: "wrap", gap: theme.space.s2 },
1366
- reason: {
1367
- paddingHorizontal: theme.space.s3,
1368
- paddingVertical: theme.space[6],
1369
- borderRadius: theme.radius.full,
1370
- borderWidth: theme.borderWidth.base,
1371
- borderColor: theme.color.border
1372
- },
1373
- reasonOn: { backgroundColor: theme.color.accentSoft, borderColor: theme.edge.accent },
1374
- reasonText: {
1375
- fontFamily: theme.font.sans,
1376
- fontSize: theme.type.caption1.fontSize,
1377
- color: theme.color.text2
1378
- },
1379
- note: {
1380
- minHeight: theme.ctl.lg * 1.5,
1381
- fontFamily: theme.font.sans,
1382
- fontSize: theme.type.footnote.fontSize,
1383
- color: theme.color.text,
1384
- backgroundColor: theme.color.surface2,
1385
- borderWidth: theme.borderWidth.base,
1386
- borderColor: theme.color.borderStrong,
1387
- borderRadius: theme.radius.md,
1388
- padding: theme.space.s3,
1389
- textAlignVertical: "top"
1390
- }
1391
- }));
1392
- var keyOf = (s) => s.id ?? s.label;
1393
- function paginate(list, perPage) {
1394
- if (list.some((s) => s.page !== void 0)) {
1395
- const by = /* @__PURE__ */ new Map();
1396
- for (const s of list) {
1397
- const p = s.page ?? 0;
1398
- by.set(p, [...by.get(p) ?? [], s]);
1399
- }
1400
- return [...by.keys()].sort((a, b) => a - b).map((p) => by.get(p));
1401
- }
1402
- const size = Math.max(1, perPage);
1403
- const out = [];
1404
- for (let i = 0; i < list.length; i += size) out.push(list.slice(i, i + size));
1405
- return out;
1406
- }
1407
- function sectionsOf(page) {
1408
- const out = [];
1409
- for (const s of page) {
1410
- const hit = out.find((g) => g.title === s.group);
1411
- if (hit) hit.items.push(s);
1412
- else out.push({ title: s.group, items: [s] });
1413
- }
1414
- return out;
1415
- }
1416
- function ChatSuggestions({
1417
- suggestions,
1418
- perPage,
1419
- onPick,
1420
- onDismiss,
1421
- loading = false,
1422
- animate,
1423
- labels: labelsProp
1424
- }) {
1425
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
1426
- const { styles } = useStyles(sheet4);
1427
- const theme = useLoamTheme("chat");
1428
- const [hidden, setHidden] = React6.useState(() => /* @__PURE__ */ new Set());
1429
- const pages = React6.useMemo(
1430
- () => paginate(suggestions.filter((s) => !hidden.has(keyOf(s))), perPage),
1431
- [suggestions, hidden, perPage]
1432
- );
1433
- const [index, setIndex] = React6.useState(0);
1434
- const [visit, setVisit] = React6.useState(0);
1435
- const current = Math.min(index, Math.max(0, pages.length - 1));
1436
- const [width, setWidth] = React6.useState(0);
1437
- const scroller = React6.useRef(null);
1438
- const settle = (next) => {
1439
- if (next === current) return;
1440
- setIndex(next);
1441
- setVisit((v) => v + 1);
1442
- };
1443
- const goTo = (next) => {
1444
- const clamped = Math.max(0, Math.min(pages.length - 1, next));
1445
- scroller.current?.scrollTo({ x: clamped * width, animated: animate });
1446
- settle(clamped);
1447
- };
1448
- const onScrollEnd = (e) => {
1449
- if (width > 0) settle(Math.round(e.nativeEvent.contentOffset.x / width));
1450
- };
1451
- const dismiss = (s) => {
1452
- if (animate) {
1453
- LayoutAnimation.configureNext(
1454
- LayoutAnimation.create(theme.motion.collapse.duration, LayoutAnimation.Types.easeInEaseOut, LayoutAnimation.Properties.opacity)
1455
- );
1456
- }
1457
- setHidden((prev) => new Set(prev).add(keyOf(s)));
1458
- onDismiss?.(s);
1459
- };
1460
- if (loading) {
1461
- return (
1462
- // One "Loading" for the whole card (the SkeletonText pattern); the bones
1463
- // are decorative, or a screen reader says "Loading" a dozen times.
1464
- /* @__PURE__ */ jsx(
1465
- View,
1466
- {
1467
- style: styles.card,
1468
- accessible: true,
1469
- accessibilityRole: "image",
1470
- accessibilityLabel: labels.loadingSuggestions,
1471
- accessibilityState: { busy: true },
1472
- children: Array.from({ length: Math.min(perPage, 4) }, (_, i) => /* @__PURE__ */ jsxs(View, { style: styles.skeletonRow, children: [
1473
- /* @__PURE__ */ jsx(
1474
- Skeleton,
1475
- {
1476
- variant: "rect",
1477
- width: theme.icon.sm,
1478
- height: theme.icon.sm,
1479
- radius: "sm",
1480
- decorative: true
1481
- }
1482
- ),
1483
- /* @__PURE__ */ jsxs(View, { style: styles.rowText, children: [
1484
- /* @__PURE__ */ jsx(Skeleton, { variant: "text", width: theme.ctl.md * 4, decorative: true }),
1485
- /* @__PURE__ */ jsx(Skeleton, { variant: "text", width: theme.ctl.md * 2.5, decorative: true })
1486
- ] })
1487
- ] }, i))
1488
- }
1489
- )
1490
- );
1491
- }
1492
- if (pages.length === 0) return null;
1493
- return /* @__PURE__ */ jsxs(View, { style: styles.card, onLayout: (e) => setWidth(e.nativeEvent.layout.width - theme.space.s2 * 2), children: [
1494
- /* @__PURE__ */ jsx(
1495
- ScrollView,
1496
- {
1497
- ref: scroller,
1498
- horizontal: true,
1499
- pagingEnabled: true,
1500
- scrollEnabled: pages.length > 1,
1501
- showsHorizontalScrollIndicator: false,
1502
- onMomentumScrollEnd: onScrollEnd,
1503
- children: pages.map((page, p) => /* @__PURE__ */ jsx(
1504
- View,
1505
- {
1506
- style: width ? { width } : styles.fill,
1507
- accessibilityElementsHidden: p !== current,
1508
- importantForAccessibility: p === current ? "auto" : "no-hide-descendants",
1509
- children: /* @__PURE__ */ jsx(
1510
- Page,
1511
- {
1512
- page,
1513
- animate: animate && p === current,
1514
- onPick,
1515
- onDismiss: onDismiss ? dismiss : void 0,
1516
- labels
1517
- }
1518
- )
1519
- },
1520
- p === current ? `on-${visit}` : `off-${p}`
1521
- ))
1522
- }
1523
- ),
1524
- pages.length > 1 ? /* @__PURE__ */ jsxs(View, { style: styles.pager, role: "group", accessibilityLabel: labels.moreSuggestions, children: [
1525
- /* @__PURE__ */ jsx(PagerButton, { icon: "chevron-left", label: labels.previousSuggestions, disabled: current === 0, onPress: () => goTo(current - 1) }),
1526
- /* @__PURE__ */ jsx(View, { style: styles.dots, children: pages.map((_, p) => /* @__PURE__ */ jsx(
1527
- Dot,
1528
- {
1529
- index: p,
1530
- count: pages.length,
1531
- active: p === current,
1532
- animate,
1533
- onPress: () => goTo(p),
1534
- labels
1535
- },
1536
- p
1537
- )) }),
1538
- /* @__PURE__ */ jsx(
1539
- PagerButton,
1540
- {
1541
- icon: "chevron-right",
1542
- label: labels.moreSuggestions,
1543
- disabled: current === pages.length - 1,
1544
- onPress: () => goTo(current + 1)
1545
- }
1546
- )
1547
- ] }) : null
1548
- ] });
1549
- }
1550
- function Page({
1551
- page,
1552
- animate,
1553
- onPick,
1554
- onDismiss,
1555
- labels
1556
- }) {
1557
- const { styles } = useStyles(sheet4);
1558
- let n = 0;
1559
- return /* @__PURE__ */ jsx(View, { children: sectionsOf(page).map((section, si) => /* @__PURE__ */ jsxs(View, { style: si > 0 ? styles.sectionRule : void 0, children: [
1560
- section.title ? /* @__PURE__ */ jsx(Rise, { i: n, animate, children: /* @__PURE__ */ jsx(Text, { accessibilityRole: "header", style: styles.sectionTitle, children: section.title }) }) : null,
1561
- section.items.map((s) => /* @__PURE__ */ jsx(Rise, { i: ++n, animate, children: /* @__PURE__ */ jsx(Row, { suggestion: s, onPick, onDismiss, labels }) }, keyOf(s)))
1562
- ] }, section.title ?? `untitled-${si}`)) });
1563
- }
1564
- function Rise({ i, animate, children }) {
1565
- const theme = useLoamTheme("chat");
1566
- const style = useArrival(animate, "in", i * theme.duration.fast * 0.27);
1567
- return /* @__PURE__ */ jsx(Animated.View, { style, children });
1568
- }
1569
- function Row({
1570
- suggestion: s,
1571
- onPick,
1572
- onDismiss,
1573
- labels
1574
- }) {
1575
- const { styles } = useStyles(sheet4);
1576
- const theme = useLoamTheme("chat");
1577
- const tone = {
1578
- neutral: theme.color.text3,
1579
- accent: theme.color.accent,
1580
- good: theme.color.good,
1581
- warn: theme.color.warn,
1582
- danger: theme.color.danger
1583
- };
1584
- const swap = React6.useRef(new Animated.Value(0)).current;
1585
- const fade = React6.useRef(new Animated.Value(1)).current;
1586
- const to = (v) => Animated.timing(swap, {
1587
- toValue: v,
1588
- duration: theme.motion.swap.duration,
1589
- easing: Easing.bezier(...theme.motion.swap.easing),
1590
- useNativeDriver: true
1591
- }).start();
1592
- const lift = theme.space[3];
1593
- const leave = () => {
1594
- Animated.timing(fade, {
1595
- toValue: 0,
1596
- duration: theme.motion.exit.duration,
1597
- easing: Easing.bezier(...theme.motion.exit.easing),
1598
- useNativeDriver: true
1599
- }).start(() => onDismiss?.(s));
1600
- };
1601
- return /* @__PURE__ */ jsxs(Animated.View, { style: [styles.row, { opacity: fade }], children: [
1602
- /* @__PURE__ */ jsxs(
1603
- Pressable,
1604
- {
1605
- accessibilityRole: "button",
1606
- accessibilityLabel: s.label,
1607
- accessibilityHint: [s.description, s.reason && labels.why(s.reason)].filter(Boolean).join(". ") || void 0,
1608
- onPress: () => onPick(s),
1609
- onPressIn: s.reason ? () => to(1) : void 0,
1610
- onPressOut: s.reason ? () => to(0) : void 0,
1611
- style: ({ pressed }) => [styles.rowPress, pressed && styles.rowPressed],
1612
- children: [
1613
- /* @__PURE__ */ jsx(Icon, { name: s.icon ?? "sparkle", size: theme.icon.xs, color: tone[s.tone ?? "neutral"] }),
1614
- /* @__PURE__ */ jsxs(View, { style: styles.rowText, children: [
1615
- /* @__PURE__ */ jsx(Text, { numberOfLines: 1, style: styles.label, children: s.label }),
1616
- s.description || s.reason ? /* @__PURE__ */ jsxs(View, { children: [
1617
- /* @__PURE__ */ jsx(
1618
- Animated.Text,
1619
- {
1620
- numberOfLines: 1,
1621
- style: [
1622
- styles.meta,
1623
- s.reason && {
1624
- opacity: swap.interpolate({ inputRange: [0, 1], outputRange: [1, 0] }),
1625
- transform: [{ translateY: swap.interpolate({ inputRange: [0, 1], outputRange: [0, -lift] }) }]
1626
- }
1627
- ],
1628
- children: s.description ?? " "
1629
- }
1630
- ),
1631
- s.reason ? /* @__PURE__ */ jsx(
1632
- Animated.Text,
1633
- {
1634
- numberOfLines: 1,
1635
- importantForAccessibility: "no",
1636
- accessibilityElementsHidden: true,
1637
- style: [
1638
- styles.meta,
1639
- styles.reason,
1640
- {
1641
- opacity: swap,
1642
- transform: [{ translateY: swap.interpolate({ inputRange: [0, 1], outputRange: [lift, 0] }) }]
1643
- }
1644
- ],
1645
- children: s.reason
1646
- }
1647
- ) : null
1648
- ] }) : null
1649
- ] })
1650
- ]
1651
- }
1652
- ),
1653
- onDismiss ? /* @__PURE__ */ jsx(
1654
- Pressable,
1655
- {
1656
- accessibilityRole: "button",
1657
- accessibilityLabel: labels.notUsefulFor(s.label),
1658
- onPress: leave,
1659
- hitSlop: theme.space[8],
1660
- style: ({ pressed }) => [styles.dismiss, pressed && styles.rowPressed],
1661
- children: /* @__PURE__ */ jsx(Icon, { name: "x", size: theme.icon.xs, color: theme.color.text3 })
1662
- }
1663
- ) : null
1664
- ] });
1665
- }
1666
- function Dot({
1667
- index,
1668
- count,
1669
- active,
1670
- animate,
1671
- onPress,
1672
- labels
1673
- }) {
1674
- const { styles } = useStyles(sheet4);
1675
- const theme = useLoamTheme("chat");
1676
- const p = React6.useRef(new Animated.Value(active ? 1 : 0)).current;
1677
- React6.useEffect(() => {
1678
- Animated.timing(p, {
1679
- toValue: active ? 1 : 0,
1680
- duration: animate ? theme.motion.indicator.duration : 0,
1681
- easing: Easing.bezier(...theme.motion.indicator.easing),
1682
- useNativeDriver: false
1683
- }).start();
1684
- }, [active, animate, p, theme.motion.indicator]);
1685
- const dot = theme.space[6];
1686
- return /* @__PURE__ */ jsx(
1687
- Pressable,
1688
- {
1689
- accessibilityRole: "button",
1690
- accessibilityLabel: labels.suggestionsPage(index + 1, count),
1691
- accessibilityState: { selected: active },
1692
- onPress,
1693
- hitSlop: theme.space[8],
1694
- children: /* @__PURE__ */ jsx(
1695
- Animated.View,
1696
- {
1697
- style: [
1698
- styles.dot,
1699
- {
1700
- width: p.interpolate({ inputRange: [0, 1], outputRange: [dot, dot * 2.67] }),
1701
- backgroundColor: p.interpolate({ inputRange: [0, 1], outputRange: [theme.color.borderStrong, theme.color.accent] })
1702
- }
1703
- ]
1704
- }
1705
- )
1706
- }
1707
- );
1708
- }
1709
- function PagerButton({
1710
- icon,
1711
- label,
1712
- disabled,
1713
- onPress
1714
- }) {
1715
- const { styles } = useStyles(sheet4);
1716
- const theme = useLoamTheme("chat");
1717
- return /* @__PURE__ */ jsx(
1718
- Pressable,
1719
- {
1720
- accessibilityRole: "button",
1721
- accessibilityLabel: label,
1722
- accessibilityState: { disabled },
1723
- disabled,
1724
- onPress,
1725
- hitSlop: theme.space[8],
1726
- style: ({ pressed }) => [styles.pagerButton, pressed && styles.rowPressed, disabled && styles.disabled],
1727
- children: /* @__PURE__ */ jsx(Icon, { name: icon, size: theme.icon.sm, color: theme.color.text3 })
1728
- }
1729
- );
1730
- }
1731
- var sheet4 = createStyleSheet((theme) => ({
1732
- card: {
1733
- alignSelf: "stretch",
1734
- padding: theme.space.s2,
1735
- borderRadius: theme.radius.xl,
1736
- borderWidth: theme.borderWidth.base,
1737
- borderColor: theme.color.border,
1738
- backgroundColor: theme.color.surface,
1739
- overflow: "hidden"
1740
- },
1741
- fill: { flex: 1 },
1742
- sectionRule: {
1743
- marginTop: theme.space.s1,
1744
- paddingTop: theme.space.s1,
1745
- borderTopWidth: theme.borderWidth.base,
1746
- borderTopColor: theme.color.border
1747
- },
1748
- sectionTitle: {
1749
- paddingHorizontal: theme.space.s3,
1750
- paddingTop: theme.space.s2,
1751
- paddingBottom: theme.space.s1,
1752
- fontFamily: theme.font.sans,
1753
- fontSize: theme.type.caption1.fontSize,
1754
- lineHeight: theme.type.caption1.lineHeight,
1755
- fontWeight: theme.fontWeight.bold,
1756
- color: theme.color.text3
1757
- },
1758
- row: { flexDirection: "row", alignItems: "center", borderRadius: theme.radius.lg },
1759
- rowPress: {
1760
- flex: 1,
1761
- flexDirection: "row",
1762
- alignItems: "center",
1763
- gap: theme.space.s3,
1764
- minHeight: theme.ctl.md,
1765
- paddingHorizontal: theme.space.s3,
1766
- paddingVertical: theme.space.s2,
1767
- borderRadius: theme.radius.lg,
1768
- borderWidth: theme.borderWidth.base,
1769
- borderColor: "transparent"
1770
- },
1771
- rowPressed: { backgroundColor: theme.color.accentSoft, borderColor: theme.edge.accent },
1772
- rowText: { flex: 1, gap: theme.space[2] },
1773
- label: {
1774
- fontFamily: theme.font.sans,
1775
- fontSize: theme.type.footnote.fontSize,
1776
- lineHeight: theme.type.footnote.lineHeight,
1777
- fontWeight: theme.fontWeight.bold,
1778
- color: theme.color.text
1779
- },
1780
- meta: {
1781
- fontFamily: theme.font.sans,
1782
- fontSize: theme.type.caption1.fontSize,
1783
- lineHeight: theme.type.caption1.lineHeight,
1784
- color: theme.color.text3
1785
- },
1786
- reason: { position: "absolute", left: 0, right: 0, top: 0, color: theme.color.accent },
1787
- dismiss: {
1788
- width: theme.ctl.iconBtnSm,
1789
- height: theme.ctl.iconBtnSm,
1790
- borderRadius: theme.radius.md,
1791
- alignItems: "center",
1792
- justifyContent: "center"
1793
- },
1794
- skeletonRow: { flexDirection: "row", alignItems: "center", gap: theme.space.s3, padding: theme.space.s3 },
1795
- pager: {
1796
- flexDirection: "row",
1797
- alignItems: "center",
1798
- justifyContent: "space-between",
1799
- marginTop: theme.space.s1,
1800
- paddingTop: theme.space.s1,
1801
- borderTopWidth: theme.borderWidth.base,
1802
- borderTopColor: theme.color.border
1803
- },
1804
- pagerButton: {
1805
- width: theme.ctl.iconBtnSm,
1806
- height: theme.ctl.iconBtnSm,
1807
- borderRadius: theme.radius.md,
1808
- alignItems: "center",
1809
- justifyContent: "center"
1810
- },
1811
- disabled: { opacity: theme.state.disabledOpacity },
1812
- dots: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
1813
- dot: { height: theme.space[6], borderRadius: theme.radius.full }
1814
- }));
1815
- function useChatScroll({ threshold = 32 } = {}) {
1816
- const ref = React6.useRef(null);
1817
- const pinnedRef = React6.useRef(true);
1818
- const [pinned, setPinnedState] = React6.useState(true);
1819
- const driving = React6.useRef({ drag: false, momentum: false });
1820
- const setPinned = React6.useCallback((next) => {
1821
- if (pinnedRef.current === next) return;
1822
- pinnedRef.current = next;
1823
- setPinnedState(next);
1824
- }, []);
1825
- const scrollToBottom = React6.useCallback(
1826
- (animated = true) => {
1827
- setPinned(true);
1828
- ref.current?.scrollToEnd({ animated });
1829
- },
1830
- [setPinned]
1831
- );
1832
- const follow = React6.useCallback(() => {
1833
- if (pinnedRef.current) ref.current?.scrollToEnd({ animated: false });
1834
- }, []);
1835
- const scrollProps = React6.useMemo(
1836
- () => ({
1837
- ref,
1838
- scrollEventThrottle: 16,
1839
- onScroll: (e) => {
1840
- const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
1841
- const distance = contentSize.height - contentOffset.y - layoutMeasurement.height;
1842
- if (distance <= threshold) setPinned(true);
1843
- else if (driving.current.drag || driving.current.momentum) setPinned(false);
1844
- },
1845
- onScrollBeginDrag: () => {
1846
- driving.current.drag = true;
1847
- },
1848
- onScrollEndDrag: () => {
1849
- driving.current.drag = false;
1850
- },
1851
- onMomentumScrollBegin: () => {
1852
- driving.current.momentum = true;
1853
- },
1854
- onMomentumScrollEnd: () => {
1855
- driving.current.momentum = false;
1856
- },
1857
- onContentSizeChange: follow,
1858
- onLayout: follow
1859
- }),
1860
- [follow, setPinned, threshold]
1861
- );
1862
- return { pinned, scrollToBottom, scrollProps };
1863
- }
1864
- var CHAT_FEEDBACK_REASONS = DEFAULT_CHAT_LABELS.feedbackReasons;
1865
- function useArrivals(messages) {
1866
- const seen = React6.useRef(null);
1867
- const prevLast = React6.useRef(void 0);
1868
- if (seen.current === null) {
1869
- seen.current = new Set(messages.map((m) => m.id));
1870
- prevLast.current = messages[messages.length - 1]?.id;
1871
- }
1872
- const boundary = prevLast.current ? messages.findIndex((m) => m.id === prevLast.current) : -1;
1873
- const arriving = /* @__PURE__ */ new Set();
1874
- messages.forEach((m, i) => {
1875
- if (!seen.current.has(m.id) && i > boundary) arriving.add(m.id);
1876
- });
1877
- React6.useEffect(() => {
1878
- for (const m of messages) seen.current.add(m.id);
1879
- prevLast.current = messages[messages.length - 1]?.id;
1880
- }, [messages]);
1881
- return (id) => arriving.has(id);
1882
- }
1883
- function Staggered({
1884
- i,
1885
- animate,
1886
- children
1887
- }) {
1888
- const theme = useLoamTheme("chat");
1889
- const style = useArrival(animate, "in", i * theme.duration.fast * 0.27);
1890
- return /* @__PURE__ */ jsx(Animated.View, { style, children });
1891
- }
1892
- function Chat({
1893
- messages,
1894
- running = false,
1895
- onSend,
1896
- onStop,
1897
- onRegenerate,
1898
- onEdit,
1899
- onRetry,
1900
- onFeedback,
1901
- onCopy,
1902
- onReply,
1903
- feedbackReasons: feedbackReasonsProp,
1904
- assistant: assistantProp,
1905
- user,
1906
- suggestions,
1907
- suggestionAction = "send",
1908
- suggestionsPerPage = 4,
1909
- suggestionsLoading = false,
1910
- onDismissSuggestion,
1911
- followUps,
1912
- emptyTitle: emptyTitleProp,
1913
- emptyDescription,
1914
- emptyState,
1915
- hasOlder = false,
1916
- loadingOlder = false,
1917
- onLoadOlder,
1918
- draft: draftProp,
1919
- onDraftChange,
1920
- placeholder,
1921
- attachments,
1922
- onAttachPress,
1923
- onRemoveAttachment,
1924
- composerToolbar,
1925
- disclaimer,
1926
- disabled,
1927
- header,
1928
- onLinkPress,
1929
- keyboardOffset = 0,
1930
- label: labelProp,
1931
- motion = "full",
1932
- style,
1933
- labels: labelsProp
1934
- }) {
1935
- const labels = useLabels("chat", DEFAULT_CHAT_LABELS, labelsProp);
1936
- const assistant = React6.useMemo(
1937
- () => assistantProp ?? { name: labels.assistant },
1938
- [assistantProp, labels.assistant]
1939
- );
1940
- const feedbackReasons = feedbackReasonsProp ?? labels.feedbackReasons;
1941
- const emptyTitle = emptyTitleProp ?? labels.emptyTitle;
1942
- const label = labelProp ?? labels.chat;
1943
- const { styles } = useStyles(sheet5);
1944
- const theme = useLoamTheme("chat");
1945
- const animate = useResolvedMotion(motion, "chat") === "full";
1946
- const { pinned, scrollToBottom, scrollProps } = useChatScroll();
1947
- const { readingMeasure } = useFormFactor();
1948
- const column = {
1949
- width: "100%",
1950
- maxWidth: readingMeasure + theme.ctl.sm + theme.space.s3 + theme.space.s4 * 2,
1951
- alignSelf: "center"
1952
- };
1953
- const isArriving = useArrivals(messages);
1954
- const inputRef = React6.useRef(null);
1955
- const [innerDraft, setInnerDraft] = React6.useState("");
1956
- const draft = draftProp ?? innerDraft;
1957
- const setDraft = (next) => {
1958
- if (draftProp === void 0) setInnerDraft(next);
1959
- onDraftChange?.(next);
1960
- };
1961
- const last = messages[messages.length - 1];
1962
- const latestAssistantId = [...messages].reverse().find((m) => m.role === "assistant")?.id;
1963
- const [replyingTo, setReplyingTo] = React6.useState(null);
1964
- const reply = onReply ? (m) => {
1965
- setReplyingTo(m);
1966
- onReply(m);
1967
- inputRef.current?.focus();
1968
- } : void 0;
1969
- const send = (text, files) => {
1970
- if (replyingTo) onSend(text, files, replyingTo);
1971
- else onSend(text, files);
1972
- setReplyingTo(null);
1973
- scrollToBottom();
1974
- };
1975
- const pick = (s) => {
1976
- const prompt = s.prompt ?? s.label;
1977
- if (suggestionAction === "fill") {
1978
- setDraft(prompt);
1979
- inputRef.current?.focus();
1980
- } else send(prompt, []);
1981
- };
1982
- const [unseen, setUnseen] = React6.useState(false);
1983
- const prevCount = React6.useRef(messages.length);
1984
- React6.useEffect(() => {
1985
- if (pinned) setUnseen(false);
1986
- else if (messages.length > prevCount.current) setUnseen(true);
1987
- prevCount.current = messages.length;
1988
- }, [messages.length, pinned]);
1989
- const pillLabel = running ? labels.writing : unseen ? labels.newMessages : labels.jumpToLatest;
1990
- const pill = React6.useRef(new Animated.Value(pinned ? 0 : 1)).current;
1991
- React6.useEffect(() => {
1992
- Animated.timing(pill, {
1993
- toValue: pinned ? 0 : 1,
1994
- duration: animate ? theme.motion.enter.duration : 0,
1995
- useNativeDriver: true
1996
- }).start();
1997
- }, [pinned, animate, pill, theme.motion.enter.duration]);
1998
- const streamingId = messages.find((m) => m.status === "streaming")?.id;
1999
- const prevStreaming = React6.useRef(streamingId);
2000
- const prevRunning = React6.useRef(running);
2001
- React6.useEffect(() => {
2002
- if (running && !prevRunning.current) {
2003
- AccessibilityInfo.announceForAccessibility?.(labels.responding(assistant.name));
2004
- }
2005
- if (prevStreaming.current && prevStreaming.current !== streamingId) {
2006
- const m = messages.find((x) => x.id === prevStreaming.current);
2007
- if (m)
2008
- AccessibilityInfo.announceForAccessibility?.(labels.said(assistant.name, chatMessageText(m)));
2009
- }
2010
- prevStreaming.current = streamingId;
2011
- prevRunning.current = running;
2012
- }, [running, streamingId, messages, assistant.name, labels]);
2013
- const pendingReply = running && last?.role === "user";
2014
- const showFollowUps = !running && !!followUps?.length && last?.role === "assistant" && (last.status ?? "done") === "done";
2015
- return /* @__PURE__ */ jsxs(
2016
- KeyboardAvoidingView,
2017
- {
2018
- accessibilityLabel: label,
2019
- behavior: Platform.OS === "ios" ? "padding" : void 0,
2020
- keyboardVerticalOffset: keyboardOffset,
2021
- style: [styles.root, style],
2022
- children: [
2023
- header,
2024
- /* @__PURE__ */ jsxs(View, { style: styles.stage, children: [
2025
- /* @__PURE__ */ jsxs(
2026
- ScrollView,
2027
- {
2028
- ...scrollProps,
2029
- accessibilityLabel: labels.messages,
2030
- keyboardShouldPersistTaps: "handled",
2031
- keyboardDismissMode: "interactive",
2032
- contentContainerStyle: [styles.content, column],
2033
- onScroll: (e) => {
2034
- scrollProps.onScroll(e);
2035
- if (hasOlder && !loadingOlder && e.nativeEvent.contentOffset.y < theme.space.s8)
2036
- onLoadOlder?.();
2037
- },
2038
- children: [
2039
- hasOlder ? /* @__PURE__ */ jsx(View, { style: styles.older, children: loadingOlder ? /* @__PURE__ */ jsx(Thinking, { label: labels.loadingEarlier, size: "sm", motion }) : /* @__PURE__ */ jsx(
2040
- Pressable,
2041
- {
2042
- accessibilityRole: "button",
2043
- onPress: onLoadOlder,
2044
- hitSlop: theme.space[8],
2045
- children: /* @__PURE__ */ jsx(Text, { style: styles.olderText, children: labels.loadEarlier })
2046
- }
2047
- ) }) : null,
2048
- messages.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs(View, { style: styles.empty, children: [
2049
- /* @__PURE__ */ jsx(Staggered, { i: 0, animate, children: /* @__PURE__ */ jsxs(View, { style: styles.emptyHead, children: [
2050
- /* @__PURE__ */ jsx(AssistantMark, {}),
2051
- /* @__PURE__ */ jsx(Text, { accessibilityRole: "header", style: styles.emptyTitle, children: emptyTitle }),
2052
- emptyDescription ? /* @__PURE__ */ jsx(Text, { style: styles.emptyText, children: emptyDescription }) : null
2053
- ] }) }),
2054
- suggestionsLoading || suggestions?.length ? /* @__PURE__ */ jsx(Staggered, { i: 2, animate, children: /* @__PURE__ */ jsx(
2055
- ChatSuggestions,
2056
- {
2057
- suggestions: suggestions ?? [],
2058
- perPage: suggestionsPerPage,
2059
- loading: suggestionsLoading,
2060
- onPick: pick,
2061
- onDismiss: onDismissSuggestion,
2062
- animate,
2063
- labels
2064
- }
2065
- ) }) : null
2066
- ] }) : messages.map((m, i) => /* @__PURE__ */ jsxs(React6.Fragment, { children: [
2067
- m.day && m.day !== messages[i - 1]?.day ? /* @__PURE__ */ jsxs(View, { style: styles.day, accessibilityRole: "header", accessibilityLabel: m.day, children: [
2068
- /* @__PURE__ */ jsx(View, { style: styles.dayLine }),
2069
- /* @__PURE__ */ jsx(Text, { style: styles.dayText, children: m.day }),
2070
- /* @__PURE__ */ jsx(View, { style: styles.dayLine })
2071
- ] }) : null,
2072
- /* @__PURE__ */ jsx(
2073
- ChatMessageRow,
2074
- {
2075
- message: m,
2076
- latest: m.id === latestAssistantId,
2077
- appearing: isArriving(m.id),
2078
- animate,
2079
- assistant,
2080
- user,
2081
- feedbackReasons,
2082
- running,
2083
- onRegenerate,
2084
- onEdit,
2085
- onRetry,
2086
- onFeedback,
2087
- onCopy,
2088
- onReply: reply,
2089
- onLinkPress,
2090
- labels
2091
- }
2092
- )
2093
- ] }, m.id)),
2094
- pendingReply ? /* @__PURE__ */ jsxs(View, { style: styles.pending, children: [
2095
- assistant.avatar ?? /* @__PURE__ */ jsx(AssistantMark, {}),
2096
- /* @__PURE__ */ jsx(WorkingIndicator, { animate, labels })
2097
- ] }) : null,
2098
- showFollowUps ? /* @__PURE__ */ jsx(View, { style: styles.followUps, role: "group", accessibilityLabel: labels.followUps, children: followUps.map((s, i) => /* @__PURE__ */ jsx(Staggered, { i, animate, children: /* @__PURE__ */ jsxs(
2099
- Pressable,
2100
- {
2101
- accessibilityRole: "button",
2102
- accessibilityLabel: s.label,
2103
- onPress: () => send(s.prompt ?? s.label, []),
2104
- style: ({ pressed }) => [styles.followUp, pressed && styles.pressed],
2105
- children: [
2106
- /* @__PURE__ */ jsx(
2107
- Icon,
2108
- {
2109
- name: s.icon ?? "arrow-right",
2110
- size: theme.icon.xs,
2111
- color: theme.color.text3
2112
- }
2113
- ),
2114
- /* @__PURE__ */ jsx(Text, { style: styles.followUpText, children: s.label })
2115
- ]
2116
- }
2117
- ) }, s.id ?? s.label)) }) : null
2118
- ]
2119
- }
2120
- ),
2121
- /* @__PURE__ */ jsx(
2122
- Animated.View,
2123
- {
2124
- pointerEvents: pinned ? "none" : "box-none",
2125
- importantForAccessibility: pinned ? "no-hide-descendants" : "auto",
2126
- accessibilityElementsHidden: pinned,
2127
- style: [
2128
- styles.pillWrap,
2129
- {
2130
- opacity: pill,
2131
- transform: [
2132
- {
2133
- translateY: pill.interpolate({
2134
- inputRange: [0, 1],
2135
- outputRange: [theme.space[8], 0]
2136
- })
2137
- }
2138
- ]
2139
- }
2140
- ],
2141
- children: /* @__PURE__ */ jsxs(
2142
- Pressable,
2143
- {
2144
- accessibilityRole: "button",
2145
- accessibilityLabel: pillLabel,
2146
- onPress: () => scrollToBottom(),
2147
- style: styles.pill,
2148
- children: [
2149
- running ? /* @__PURE__ */ jsx(Thinking, { label: "", size: "sm", announce: false, motion }) : /* @__PURE__ */ jsx(Icon, { name: "arrow-down", size: theme.icon.xs, color: theme.color.text }),
2150
- /* @__PURE__ */ jsx(Text, { style: styles.pillText, children: pillLabel })
2151
- ]
2152
- }
2153
- )
2154
- }
2155
- )
2156
- ] }),
2157
- /* @__PURE__ */ jsx(
2158
- ChatComposer,
2159
- {
2160
- inputRef,
2161
- value: draft,
2162
- onValueChange: setDraft,
2163
- onSend: send,
2164
- replyTo: replyingTo ? {
2165
- author: replyingTo.role === "user" ? user?.name ?? labels.you : assistant.name,
2166
- text: chatMessageText(replyingTo)
2167
- } : null,
2168
- onCancelReply: () => setReplyingTo(null),
2169
- running,
2170
- onStop,
2171
- placeholder,
2172
- attachments,
2173
- onAttachPress,
2174
- onRemoveAttachment,
2175
- toolbar: composerToolbar,
2176
- hint: disclaimer,
2177
- disabled,
2178
- motion,
2179
- labels,
2180
- style: [styles.composer, column]
2181
- }
2182
- )
2183
- ]
2184
- }
2185
- );
2186
- }
2187
- var sheet5 = createStyleSheet((theme) => ({
2188
- root: { flex: 1, backgroundColor: theme.color.surface },
2189
- stage: { flex: 1 },
2190
- content: { flexGrow: 1, gap: theme.space.s6, padding: theme.space.s4 },
2191
- older: { alignItems: "center" },
2192
- olderText: {
2193
- fontFamily: theme.font.sans,
2194
- fontSize: theme.type.caption1.fontSize,
2195
- color: theme.color.text3
2196
- },
2197
- empty: {
2198
- flex: 1,
2199
- justifyContent: "center",
2200
- gap: theme.space.s2,
2201
- paddingVertical: theme.space.s8
2202
- },
2203
- emptyHead: { alignItems: "center", gap: theme.space.s3, marginBottom: theme.space.s4 },
2204
- emptyTitle: {
2205
- fontFamily: theme.font.display,
2206
- fontSize: theme.type.title3.fontSize,
2207
- lineHeight: theme.type.title3.lineHeight,
2208
- fontWeight: theme.fontWeight.bold,
2209
- color: theme.color.text,
2210
- textAlign: "center"
2211
- },
2212
- emptyText: {
2213
- fontFamily: theme.font.sans,
2214
- fontSize: theme.type.footnote.fontSize,
2215
- lineHeight: theme.type.footnote.lineHeight,
2216
- color: theme.color.text2,
2217
- textAlign: "center"
2218
- },
2219
- pressed: { backgroundColor: theme.state.pressed },
2220
- meta: {
2221
- fontFamily: theme.font.sans,
2222
- fontSize: theme.type.caption1.fontSize,
2223
- lineHeight: theme.type.caption1.lineHeight,
2224
- color: theme.color.text3
2225
- },
2226
- day: { flexDirection: "row", alignItems: "center", gap: theme.space.s3 },
2227
- dayLine: { flex: 1, height: theme.borderWidth.base, backgroundColor: theme.color.border },
2228
- dayText: {
2229
- fontFamily: theme.font.sans,
2230
- fontSize: theme.type.caption1.fontSize,
2231
- fontWeight: theme.fontWeight.bold,
2232
- color: theme.color.text3
2233
- },
2234
- pending: { flexDirection: "row", gap: theme.space.s3, alignItems: "center" },
2235
- followUps: {
2236
- flexDirection: "row",
2237
- flexWrap: "wrap",
2238
- gap: theme.space.s2,
2239
- paddingLeft: theme.ctl.sm + theme.space.s3
2240
- },
2241
- followUp: {
2242
- flexDirection: "row",
2243
- alignItems: "center",
2244
- gap: theme.space[6],
2245
- paddingHorizontal: theme.space.s3,
2246
- paddingVertical: theme.space.s2,
2247
- borderRadius: theme.radius.full,
2248
- borderWidth: theme.borderWidth.base,
2249
- borderColor: theme.color.border
2250
- },
2251
- followUpText: {
2252
- fontFamily: theme.font.sans,
2253
- fontSize: theme.type.footnote.fontSize,
2254
- color: theme.color.text2
2255
- },
2256
- pillWrap: {
2257
- position: "absolute",
2258
- left: 0,
2259
- right: 0,
2260
- bottom: theme.space.s3,
2261
- alignItems: "center"
2262
- },
2263
- pill: {
2264
- flexDirection: "row",
2265
- alignItems: "center",
2266
- gap: theme.space[6],
2267
- paddingHorizontal: theme.space.s3,
2268
- paddingVertical: theme.space.s2,
2269
- borderRadius: theme.radius.full,
2270
- borderWidth: theme.borderWidth.base,
2271
- borderColor: theme.color.border,
2272
- backgroundColor: theme.color.surface,
2273
- ...theme.shadow.md
2274
- },
2275
- pillText: {
2276
- fontFamily: theme.font.sans,
2277
- fontSize: theme.type.caption1.fontSize,
2278
- fontWeight: theme.fontWeight.bold,
2279
- color: theme.color.text
2280
- },
2281
- composer: { paddingHorizontal: theme.space.s4, paddingBottom: theme.space.s4 }
2282
- }));
2283
-
2284
- export { CHAT_FEEDBACK_REASONS, Chat, ChatComposer, chatMessageText };