termcast 1.3.21 → 1.3.25

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 (429) hide show
  1. package/dist/ai.d.ts +104 -0
  2. package/dist/ai.d.ts.map +1 -0
  3. package/dist/ai.js +135 -0
  4. package/dist/ai.js.map +1 -0
  5. package/dist/apis/browser-extension.d.ts +18 -0
  6. package/dist/apis/browser-extension.d.ts.map +1 -0
  7. package/dist/apis/browser-extension.js +14 -0
  8. package/dist/apis/browser-extension.js.map +1 -0
  9. package/dist/apis/localstorage.d.ts.map +1 -1
  10. package/dist/apis/localstorage.js +4 -7
  11. package/dist/apis/localstorage.js.map +1 -1
  12. package/dist/apis/oauth.d.ts.map +1 -1
  13. package/dist/apis/oauth.js +5 -1
  14. package/dist/apis/oauth.js.map +1 -1
  15. package/dist/apis/preferences.d.ts.map +1 -1
  16. package/dist/apis/preferences.js +38 -19
  17. package/dist/apis/preferences.js.map +1 -1
  18. package/dist/apis/toast.d.ts +5 -0
  19. package/dist/apis/toast.d.ts.map +1 -1
  20. package/dist/apis/toast.js +7 -43
  21. package/dist/apis/toast.js.map +1 -1
  22. package/dist/build.d.ts.map +1 -1
  23. package/dist/build.js +3 -1
  24. package/dist/build.js.map +1 -1
  25. package/dist/cache.d.ts +32 -0
  26. package/dist/cache.d.ts.map +1 -0
  27. package/dist/cache.js +205 -0
  28. package/dist/cache.js.map +1 -0
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +87 -41
  31. package/dist/cli.js.map +1 -1
  32. package/dist/clipboard.d.ts +36 -0
  33. package/dist/clipboard.d.ts.map +1 -0
  34. package/dist/clipboard.js +154 -0
  35. package/dist/clipboard.js.map +1 -0
  36. package/dist/compile.d.ts +2 -1
  37. package/dist/compile.d.ts.map +1 -1
  38. package/dist/compile.js +31 -12
  39. package/dist/compile.js.map +1 -1
  40. package/dist/components/actions.d.ts.map +1 -1
  41. package/dist/components/actions.js +56 -30
  42. package/dist/components/actions.js.map +1 -1
  43. package/dist/components/detail.d.ts.map +1 -1
  44. package/dist/components/detail.js +4 -0
  45. package/dist/components/detail.js.map +1 -1
  46. package/dist/components/dropdown.d.ts.map +1 -1
  47. package/dist/components/dropdown.js +38 -15
  48. package/dist/components/dropdown.js.map +1 -1
  49. package/dist/components/extension-preferences.d.ts.map +1 -1
  50. package/dist/components/extension-preferences.js +40 -13
  51. package/dist/components/extension-preferences.js.map +1 -1
  52. package/dist/components/form/checkbox.d.ts.map +1 -1
  53. package/dist/components/form/checkbox.js +5 -3
  54. package/dist/components/form/checkbox.js.map +1 -1
  55. package/dist/components/form/date-picker.d.ts.map +1 -1
  56. package/dist/components/form/date-picker.js +5 -3
  57. package/dist/components/form/date-picker.js.map +1 -1
  58. package/dist/components/form/description.d.ts.map +1 -1
  59. package/dist/components/form/description.js +2 -2
  60. package/dist/components/form/description.js.map +1 -1
  61. package/dist/components/form/dropdown.d.ts.map +1 -1
  62. package/dist/components/form/dropdown.js +84 -80
  63. package/dist/components/form/dropdown.js.map +1 -1
  64. package/dist/components/form/file-autocomplete.d.ts +3 -6
  65. package/dist/components/form/file-autocomplete.d.ts.map +1 -1
  66. package/dist/components/form/file-autocomplete.js +61 -66
  67. package/dist/components/form/file-autocomplete.js.map +1 -1
  68. package/dist/components/form/file-picker.d.ts.map +1 -1
  69. package/dist/components/form/file-picker.js +33 -30
  70. package/dist/components/form/file-picker.js.map +1 -1
  71. package/dist/components/form/form-end.d.ts.map +1 -1
  72. package/dist/components/form/form-end.js +21 -1
  73. package/dist/components/form/form-end.js.map +1 -1
  74. package/dist/components/form/form-type-only.d.ts +174 -0
  75. package/dist/components/form/form-type-only.d.ts.map +1 -0
  76. package/dist/components/form/form-type-only.js +2 -0
  77. package/dist/components/form/form-type-only.js.map +1 -0
  78. package/dist/components/form/index.d.ts +3 -1
  79. package/dist/components/form/index.d.ts.map +1 -1
  80. package/dist/components/form/index.js +104 -30
  81. package/dist/components/form/index.js.map +1 -1
  82. package/dist/components/form/password-field.d.ts.map +1 -1
  83. package/dist/components/form/password-field.js +5 -3
  84. package/dist/components/form/password-field.js.map +1 -1
  85. package/dist/components/form/text-area.d.ts.map +1 -1
  86. package/dist/components/form/text-area.js +5 -3
  87. package/dist/components/form/text-area.js.map +1 -1
  88. package/dist/components/form/text-field.d.ts.map +1 -1
  89. package/dist/components/form/text-field.js +6 -4
  90. package/dist/components/form/text-field.js.map +1 -1
  91. package/dist/components/form/types.d.ts +5 -0
  92. package/dist/components/form/types.d.ts.map +1 -1
  93. package/dist/components/form/use-form-handling.d.ts +4 -0
  94. package/dist/components/form/use-form-handling.d.ts.map +1 -0
  95. package/dist/components/form/use-form-handling.js +37 -0
  96. package/dist/components/form/use-form-handling.js.map +1 -0
  97. package/dist/components/form/with-left-border.d.ts +2 -1
  98. package/dist/components/form/with-left-border.d.ts.map +1 -1
  99. package/dist/components/form/with-left-border.js +27 -3
  100. package/dist/components/form/with-left-border.js.map +1 -1
  101. package/dist/components/icon.d.ts +1 -0
  102. package/dist/components/icon.d.ts.map +1 -1
  103. package/dist/components/icon.js +24 -8
  104. package/dist/components/icon.js.map +1 -1
  105. package/dist/components/list.d.ts +2 -2
  106. package/dist/components/list.d.ts.map +1 -1
  107. package/dist/components/list.js +155 -70
  108. package/dist/components/list.js.map +1 -1
  109. package/dist/components/loading-bar.d.ts.map +1 -1
  110. package/dist/components/loading-bar.js +2 -2
  111. package/dist/components/loading-bar.js.map +1 -1
  112. package/dist/components/loading-text.d.ts +8 -0
  113. package/dist/components/loading-text.d.ts.map +1 -0
  114. package/dist/components/loading-text.js +58 -0
  115. package/dist/components/loading-text.js.map +1 -0
  116. package/dist/descendants.js +1 -1
  117. package/dist/descendants.js.map +1 -1
  118. package/dist/dev-ui.d.ts +7 -0
  119. package/dist/dev-ui.d.ts.map +1 -0
  120. package/dist/dev-ui.js +118 -0
  121. package/dist/dev-ui.js.map +1 -0
  122. package/dist/environment.d.ts +63 -0
  123. package/dist/environment.d.ts.map +1 -0
  124. package/dist/environment.js +189 -0
  125. package/dist/environment.js.map +1 -0
  126. package/dist/examples/datepicker.d.ts +2 -0
  127. package/dist/examples/datepicker.d.ts.map +1 -0
  128. package/dist/examples/datepicker.js +344 -0
  129. package/dist/examples/datepicker.js.map +1 -0
  130. package/dist/examples/file-autocomplete.vitest.d.ts +2 -0
  131. package/dist/examples/file-autocomplete.vitest.d.ts.map +1 -0
  132. package/dist/examples/file-autocomplete.vitest.js +223 -0
  133. package/dist/examples/file-autocomplete.vitest.js.map +1 -0
  134. package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
  135. package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
  136. package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
  137. package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
  138. package/dist/examples/form-basic.vitest.d.ts +2 -0
  139. package/dist/examples/form-basic.vitest.d.ts.map +1 -0
  140. package/dist/examples/form-basic.vitest.js +630 -0
  141. package/dist/examples/form-basic.vitest.js.map +1 -0
  142. package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
  143. package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
  144. package/dist/examples/form-dropdown-with-sections.js +13 -0
  145. package/dist/examples/form-dropdown-with-sections.js.map +1 -0
  146. package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
  147. package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
  148. package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
  149. package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
  150. package/dist/examples/form-dropdown.vitest.d.ts +2 -0
  151. package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
  152. package/dist/examples/form-dropdown.vitest.js +854 -0
  153. package/dist/examples/form-dropdown.vitest.js.map +1 -0
  154. package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
  155. package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
  156. package/dist/examples/form-multiselect-dropdown.js +13 -0
  157. package/dist/examples/form-multiselect-dropdown.js.map +1 -0
  158. package/dist/examples/form-scroll.d.ts.map +1 -1
  159. package/dist/examples/form-scroll.js +7 -1
  160. package/dist/examples/form-scroll.js.map +1 -1
  161. package/dist/examples/form-scroll.vitest.d.ts +2 -0
  162. package/dist/examples/form-scroll.vitest.d.ts.map +1 -0
  163. package/dist/examples/form-scroll.vitest.js +211 -0
  164. package/dist/examples/form-scroll.vitest.js.map +1 -0
  165. package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
  166. package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
  167. package/dist/examples/form-tagpicker.vitest.js +736 -0
  168. package/dist/examples/form-tagpicker.vitest.js.map +1 -0
  169. package/dist/examples/internal/descendants-filtering.js +1 -1
  170. package/dist/examples/internal/descendants-filtering.js.map +1 -1
  171. package/dist/examples/internal/descendants.js +1 -1
  172. package/dist/examples/internal/descendants.js.map +1 -1
  173. package/dist/examples/internal/nested-boxes.d.ts +2 -0
  174. package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
  175. package/dist/examples/internal/nested-boxes.js +7 -0
  176. package/dist/examples/internal/nested-boxes.js.map +1 -0
  177. package/dist/examples/internal/rhf-custom-ref.js +2 -2
  178. package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
  179. package/dist/examples/internal/scrollbox-demo.js +3 -22
  180. package/dist/examples/internal/scrollbox-demo.js.map +1 -1
  181. package/dist/examples/internal/scrollbox-descendants.d.ts +2 -0
  182. package/dist/examples/internal/scrollbox-descendants.d.ts.map +1 -0
  183. package/dist/examples/internal/scrollbox-descendants.js +83 -0
  184. package/dist/examples/internal/scrollbox-descendants.js.map +1 -0
  185. package/dist/examples/internal/scrollbox-with-descendants.js +4 -8
  186. package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
  187. package/dist/examples/internal/simple-scrollbox.vitest.d.ts +2 -0
  188. package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +1 -0
  189. package/dist/examples/internal/simple-scrollbox.vitest.js +96 -0
  190. package/dist/examples/internal/simple-scrollbox.vitest.js.map +1 -0
  191. package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
  192. package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
  193. package/dist/examples/internal/unicode-square-repro.js +7 -0
  194. package/dist/examples/internal/unicode-square-repro.js.map +1 -0
  195. package/dist/examples/list-detail-metadata.d.ts +2 -0
  196. package/dist/examples/list-detail-metadata.d.ts.map +1 -0
  197. package/dist/examples/list-detail-metadata.js +8 -0
  198. package/dist/examples/list-detail-metadata.js.map +1 -0
  199. package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
  200. package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
  201. package/dist/examples/list-dropdown-default.vitest.js +234 -0
  202. package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
  203. package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
  204. package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
  205. package/dist/examples/list-fetch-data.vitest.js +111 -0
  206. package/dist/examples/list-fetch-data.vitest.js.map +1 -0
  207. package/dist/examples/list-filter-navigation.d.ts +2 -0
  208. package/dist/examples/list-filter-navigation.d.ts.map +1 -0
  209. package/dist/examples/list-filter-navigation.js +8 -0
  210. package/dist/examples/list-filter-navigation.js.map +1 -0
  211. package/dist/examples/list-scrollbox.vitest.d.ts +2 -0
  212. package/dist/examples/list-scrollbox.vitest.d.ts.map +1 -0
  213. package/dist/examples/list-scrollbox.vitest.js +93 -0
  214. package/dist/examples/list-scrollbox.vitest.js.map +1 -0
  215. package/dist/examples/list-with-detail-long.d.ts +2 -0
  216. package/dist/examples/list-with-detail-long.d.ts.map +1 -0
  217. package/dist/examples/list-with-detail-long.js +53 -0
  218. package/dist/examples/list-with-detail-long.js.map +1 -0
  219. package/dist/examples/list-with-detail.vitest.d.ts +2 -0
  220. package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
  221. package/dist/examples/list-with-detail.vitest.js +434 -0
  222. package/dist/examples/list-with-detail.vitest.js.map +1 -0
  223. package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
  224. package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
  225. package/dist/examples/list-with-dropdown.vitest.js +337 -0
  226. package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
  227. package/dist/examples/list-with-sections.js +5 -1
  228. package/dist/examples/list-with-sections.js.map +1 -1
  229. package/dist/examples/list-with-sections.vitest.d.ts +2 -0
  230. package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
  231. package/dist/examples/list-with-sections.vitest.js +601 -0
  232. package/dist/examples/list-with-sections.vitest.js.map +1 -0
  233. package/dist/examples/scrollbox-vertical-centering.d.ts +6 -0
  234. package/dist/examples/scrollbox-vertical-centering.d.ts.map +1 -0
  235. package/dist/examples/scrollbox-vertical-centering.js +17 -0
  236. package/dist/examples/scrollbox-vertical-centering.js.map +1 -0
  237. package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
  238. package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
  239. package/dist/examples/simple-file-picker.vitest.js +678 -0
  240. package/dist/examples/simple-file-picker.vitest.js.map +1 -0
  241. package/dist/examples/simple-grid.vitest.d.ts +2 -0
  242. package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
  243. package/dist/examples/simple-grid.vitest.js +521 -0
  244. package/dist/examples/simple-grid.vitest.js.map +1 -0
  245. package/dist/examples/simple-navigation.js +10 -4
  246. package/dist/examples/simple-navigation.js.map +1 -1
  247. package/dist/examples/simple-navigation.vitest.d.ts +2 -0
  248. package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
  249. package/dist/examples/simple-navigation.vitest.js +718 -0
  250. package/dist/examples/simple-navigation.vitest.js.map +1 -0
  251. package/dist/examples/store.vitest.d.ts +2 -0
  252. package/dist/examples/store.vitest.d.ts.map +1 -0
  253. package/dist/examples/store.vitest.js +69 -0
  254. package/dist/examples/store.vitest.js.map +1 -0
  255. package/dist/examples/toast-variations.d.ts +2 -0
  256. package/dist/examples/toast-variations.d.ts.map +1 -0
  257. package/dist/examples/toast-variations.js +122 -0
  258. package/dist/examples/toast-variations.js.map +1 -0
  259. package/dist/extensions/dev.d.ts +4 -2
  260. package/dist/extensions/dev.d.ts.map +1 -1
  261. package/dist/extensions/dev.js +61 -10
  262. package/dist/extensions/dev.js.map +1 -1
  263. package/dist/extensions/dev.vitest.d.ts +2 -0
  264. package/dist/extensions/dev.vitest.d.ts.map +1 -0
  265. package/dist/extensions/dev.vitest.js +197 -0
  266. package/dist/extensions/dev.vitest.js.map +1 -0
  267. package/dist/extensions/home.d.ts.map +1 -1
  268. package/dist/extensions/home.js +3 -0
  269. package/dist/extensions/home.js.map +1 -1
  270. package/dist/home-command.d.ts +8 -0
  271. package/dist/home-command.d.ts.map +1 -0
  272. package/dist/home-command.js +181 -0
  273. package/dist/home-command.js.map +1 -0
  274. package/dist/hover-repro.d.ts +2 -0
  275. package/dist/hover-repro.d.ts.map +1 -0
  276. package/dist/hover-repro.js +20 -0
  277. package/dist/hover-repro.js.map +1 -0
  278. package/dist/index.d.ts +1 -0
  279. package/dist/index.d.ts.map +1 -1
  280. package/dist/index.js +2 -0
  281. package/dist/index.js.map +1 -1
  282. package/dist/internal/dialog.d.ts +1 -0
  283. package/dist/internal/dialog.d.ts.map +1 -1
  284. package/dist/internal/dialog.js +27 -18
  285. package/dist/internal/dialog.js.map +1 -1
  286. package/dist/internal/navigation.d.ts +9 -1
  287. package/dist/internal/navigation.d.ts.map +1 -1
  288. package/dist/internal/navigation.js +5 -5
  289. package/dist/internal/navigation.js.map +1 -1
  290. package/dist/internal/offscreen.d.ts +6 -0
  291. package/dist/internal/offscreen.d.ts.map +1 -0
  292. package/dist/internal/offscreen.js +10 -0
  293. package/dist/internal/offscreen.js.map +1 -0
  294. package/dist/internal/providers.d.ts.map +1 -1
  295. package/dist/internal/providers.js +2 -2
  296. package/dist/internal/providers.js.map +1 -1
  297. package/dist/internal/scrollbox.d.ts +1 -10
  298. package/dist/internal/scrollbox.d.ts.map +1 -1
  299. package/dist/internal/scrollbox.js +2 -1
  300. package/dist/internal/scrollbox.js.map +1 -1
  301. package/dist/localstorage.d.ts +13 -0
  302. package/dist/localstorage.d.ts.map +1 -0
  303. package/dist/localstorage.js +190 -0
  304. package/dist/localstorage.js.map +1 -0
  305. package/dist/oauth.d.ts +142 -0
  306. package/dist/oauth.d.ts.map +1 -0
  307. package/dist/oauth.js +551 -0
  308. package/dist/oauth.js.map +1 -0
  309. package/dist/preferences.d.ts +23 -0
  310. package/dist/preferences.d.ts.map +1 -0
  311. package/dist/preferences.js +105 -0
  312. package/dist/preferences.js.map +1 -0
  313. package/dist/release.d.ts +1 -1
  314. package/dist/release.d.ts.map +1 -1
  315. package/dist/release.js +29 -33
  316. package/dist/release.js.map +1 -1
  317. package/dist/state.d.ts +2 -0
  318. package/dist/state.d.ts.map +1 -1
  319. package/dist/state.js +3 -0
  320. package/dist/state.js.map +1 -1
  321. package/dist/store.d.ts +21 -0
  322. package/dist/store.d.ts.map +1 -0
  323. package/dist/store.js +84 -0
  324. package/dist/store.js.map +1 -0
  325. package/dist/swift-loader.d.ts +3 -0
  326. package/dist/swift-loader.d.ts.map +1 -0
  327. package/dist/swift-loader.js +193 -0
  328. package/dist/swift-loader.js.map +1 -0
  329. package/dist/swift-runtime.d.ts +2 -0
  330. package/dist/swift-runtime.d.ts.map +1 -0
  331. package/dist/swift-runtime.js +27 -0
  332. package/dist/swift-runtime.js.map +1 -0
  333. package/dist/toast.d.ts +44 -0
  334. package/dist/toast.d.ts.map +1 -0
  335. package/dist/toast.js +221 -0
  336. package/dist/toast.js.map +1 -0
  337. package/dist/utils/file-system.d.ts +9 -0
  338. package/dist/utils/file-system.d.ts.map +1 -1
  339. package/dist/utils/file-system.js +49 -0
  340. package/dist/utils/file-system.js.map +1 -1
  341. package/dist/utils/run-command.d.ts +26 -1
  342. package/dist/utils/run-command.d.ts.map +1 -1
  343. package/dist/utils/run-command.js +53 -4
  344. package/dist/utils/run-command.js.map +1 -1
  345. package/dist/window.d.ts +12 -0
  346. package/dist/window.d.ts.map +1 -0
  347. package/dist/window.js +48 -0
  348. package/dist/window.js.map +1 -0
  349. package/package.json +12 -12
  350. package/src/apis/browser-extension.tsx +29 -0
  351. package/src/apis/localstorage.test.ts +14 -6
  352. package/src/apis/localstorage.tsx +8 -5
  353. package/src/apis/oauth.tsx +5 -1
  354. package/src/apis/preferences.tsx +48 -22
  355. package/src/apis/toast.tsx +37 -62
  356. package/src/build.test.tsx +52 -0
  357. package/src/build.tsx +3 -1
  358. package/src/cli.tsx +99 -48
  359. package/src/compile.tsx +32 -11
  360. package/src/components/actions.tsx +94 -32
  361. package/src/components/detail.tsx +4 -0
  362. package/src/components/dropdown.tsx +47 -14
  363. package/src/components/extension-preferences.tsx +44 -16
  364. package/src/components/form/checkbox.tsx +12 -6
  365. package/src/components/form/date-picker.tsx +12 -6
  366. package/src/components/form/description.tsx +7 -2
  367. package/src/components/form/dropdown.tsx +131 -119
  368. package/src/components/form/file-autocomplete.tsx +90 -108
  369. package/src/components/form/file-picker.tsx +54 -43
  370. package/src/components/form/form-end.tsx +23 -2
  371. package/src/components/form/index.tsx +168 -41
  372. package/src/components/form/password-field.tsx +12 -6
  373. package/src/components/form/text-area.tsx +13 -6
  374. package/src/components/form/text-field.tsx +13 -6
  375. package/src/components/form/types.tsx +6 -0
  376. package/src/components/form/with-left-border.tsx +41 -8
  377. package/src/components/icon.tsx +27 -8
  378. package/src/components/list.tsx +243 -101
  379. package/src/components/loading-bar.tsx +3 -2
  380. package/src/components/loading-text.tsx +79 -0
  381. package/src/descendants.tsx +1 -0
  382. package/src/examples/file-autocomplete.vitest.tsx +130 -125
  383. package/src/examples/form-basic.vitest.tsx +376 -176
  384. package/src/examples/form-dropdown.vitest.tsx +126 -126
  385. package/src/examples/form-scroll.tsx +2 -0
  386. package/src/examples/form-scroll.vitest.tsx +58 -58
  387. package/src/examples/form-tagpicker.vitest.tsx +99 -99
  388. package/src/examples/internal/descendants-filtering.tsx +1 -0
  389. package/src/examples/internal/descendants.tsx +1 -0
  390. package/src/examples/internal/rhf-custom-ref.tsx +2 -0
  391. package/src/examples/internal/scrollbox-demo.tsx +3 -27
  392. package/src/examples/internal/scrollbox-with-descendants.tsx +4 -7
  393. package/src/examples/internal/simple-scrollbox.vitest.tsx +7 -5
  394. package/src/examples/list-detail-metadata.tsx +49 -0
  395. package/src/examples/list-detail-metadata.vitest.tsx +88 -0
  396. package/src/examples/list-dropdown-default.vitest.tsx +51 -51
  397. package/src/examples/list-fetch-data.vitest.tsx +4 -4
  398. package/src/examples/list-scrollbox.vitest.tsx +73 -14
  399. package/src/examples/list-with-detail-long.tsx +70 -0
  400. package/src/examples/list-with-detail.vitest.tsx +191 -85
  401. package/src/examples/list-with-dropdown.vitest.tsx +53 -53
  402. package/src/examples/list-with-sections.tsx +1 -0
  403. package/src/examples/list-with-sections.vitest.tsx +221 -97
  404. package/src/examples/list-with-toast.vitest.tsx +16 -16
  405. package/src/examples/simple-file-picker.vitest.tsx +63 -469
  406. package/src/examples/simple-grid.vitest.tsx +238 -233
  407. package/src/examples/simple-navigation.tsx +15 -7
  408. package/src/examples/simple-navigation.vitest.tsx +130 -219
  409. package/src/examples/store.vitest.tsx +1 -1
  410. package/src/examples/swift-extension.vitest.tsx +148 -0
  411. package/src/examples/synonyms.vitest.tsx +159 -0
  412. package/src/examples/toast-variations.tsx +150 -0
  413. package/src/examples/toast-variations.vitest.tsx +370 -0
  414. package/src/extensions/dev.tsx +74 -7
  415. package/src/extensions/dev.vitest.tsx +102 -34
  416. package/src/extensions/home.tsx +6 -0
  417. package/src/index.tsx +3 -0
  418. package/src/internal/dialog.tsx +43 -30
  419. package/src/internal/navigation.tsx +3 -1
  420. package/src/internal/offscreen.tsx +15 -0
  421. package/src/internal/providers.tsx +4 -2
  422. package/src/internal/scrollbox.tsx +4 -8
  423. package/src/keyboard.test.tsx +69 -0
  424. package/src/release.tsx +32 -38
  425. package/src/state.tsx +7 -0
  426. package/src/swift-loader.tsx +239 -0
  427. package/src/swift-runtime.tsx +36 -0
  428. package/src/utils/file-system.ts +61 -0
  429. package/src/utils/run-command.tsx +82 -6
@@ -7,8 +7,10 @@ import { useFormContext, Controller } from 'react-hook-form'
7
7
  import { useFocusContext, useFormFieldDescendant } from './index'
8
8
  import { useKeyboard } from '@opentui/react'
9
9
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
10
- import { FileAutocomplete } from './file-autocomplete'
10
+ import { FileAutocompleteDialog } from './file-autocomplete'
11
11
  import { useFormNavigationHelpers } from './use-form-navigation'
12
+ import { useDialog } from 'termcast/src/internal/dialog'
13
+ import { LoadingText } from 'termcast/src/components/loading-text'
12
14
 
13
15
  export interface FilePickerProps extends FormItemProps<string[]> {
14
16
  /**
@@ -52,6 +54,7 @@ const FilePickerField = ({
52
54
  props,
53
55
  isFocused,
54
56
  setFocusedField,
57
+ isFormLoading,
55
58
  }: {
56
59
  field: any
57
60
  fieldState: any
@@ -59,12 +62,40 @@ const FilePickerField = ({
59
62
  props: FilePickerProps
60
63
  isFocused: boolean
61
64
  setFocusedField: (id: string) => void
65
+ isFormLoading: boolean
62
66
  }): any => {
63
67
  const isInFocus = useIsInFocus()
64
- const [showAutocomplete, setShowAutocomplete] = React.useState(false)
65
- const [searchTrigger, setSearchTrigger] = React.useState(0)
66
68
  const inputRef = React.useRef<TextareaRenderable>(null)
67
- const anchorRef = React.useRef<any>(null)
69
+ const dialog = useDialog()
70
+
71
+ const showAutocomplete = () => {
72
+ if (dialog.stack.length > 0) return
73
+
74
+ const handleSelect = (path: string) => {
75
+ const currentFiles = field.value || []
76
+ const newFiles =
77
+ props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
78
+ field.onChange(newFiles)
79
+ if (props.onChange) {
80
+ props.onChange(newFiles)
81
+ }
82
+ inputRef.current?.setText('')
83
+ dialog.clear()
84
+ }
85
+
86
+ dialog.push(
87
+ <FileAutocompleteDialog
88
+ onSelect={handleSelect}
89
+ onClose={() => {
90
+ dialog.clear()
91
+ }}
92
+ inputRef={inputRef}
93
+ canChooseFiles={props.canChooseFiles}
94
+ canChooseDirectories={props.canChooseDirectories}
95
+ initialDirectory={props.initialDirectory}
96
+ />
97
+ )
98
+ }
68
99
 
69
100
  // Handle Enter key and left arrow for removing last file
70
101
  useKeyboard((evt) => {
@@ -86,12 +117,11 @@ const FilePickerField = ({
86
117
  if (evt.name === 'return') {
87
118
  const inputValue = inputRef.current?.plainText || ''
88
119
  // If input is empty, show files in current directory
89
- if (!inputValue && !showAutocomplete) {
90
- setShowAutocomplete(true)
91
- setSearchTrigger((n) => n + 1)
120
+ if (!inputValue) {
121
+ showAutocomplete()
92
122
  }
93
- // If autocomplete is not visible and input has value, add the path
94
- else if (inputValue.trim() && !showAutocomplete) {
123
+ // If input has value, add the path directly
124
+ else if (inputValue.trim()) {
95
125
  const currentFiles = field.value || []
96
126
  const newFiles =
97
127
  props.allowMultipleSelection !== false
@@ -106,37 +136,30 @@ const FilePickerField = ({
106
136
  }
107
137
  })
108
138
 
109
- const handleSelectFile = (path: string) => {
110
- const currentFiles = field.value || []
111
- const newFiles =
112
- props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
113
- field.onChange(newFiles)
114
- if (props.onChange) {
115
- props.onChange(newFiles)
116
- }
117
- inputRef.current?.setText('')
118
- setShowAutocomplete(false)
119
- }
120
-
121
139
  const selectedFiles = field.value || []
122
140
 
123
141
  return (
124
142
  <box flexDirection='column'>
125
- <WithLeftBorder withDiamond isFocused={isFocused}>
126
- <text
127
- fg={isFocused ? Theme.primary : Theme.text}
143
+ <WithLeftBorder withDiamond isFocused={isFocused} isLoading={isFormLoading}>
144
+ <box
128
145
  onMouseDown={() => {
129
146
  setFocusedField(props.id)
130
147
  }}
131
148
  >
132
- {props.title || 'File Path'}
133
- </text>
149
+ <LoadingText
150
+ isLoading={isFocused && isFormLoading}
151
+ color={isFocused ? Theme.primary : Theme.text}
152
+ >
153
+ {props.title || 'File Path'}
154
+ </LoadingText>
155
+ </box>
134
156
  </WithLeftBorder>
135
157
  <WithLeftBorder isFocused={isFocused}>
136
- <box flexDirection='column' ref={anchorRef}>
158
+ <box flexDirection='column'>
137
159
  <textarea
138
160
  ref={inputRef}
139
161
  height={1}
162
+ wrapMode='none'
140
163
  keyBindings={[
141
164
  { name: 'return', action: 'submit' },
142
165
  { name: 'linefeed', action: 'submit' },
@@ -148,10 +171,7 @@ const FilePickerField = ({
148
171
  onContentChange={() => {
149
172
  const value = inputRef.current?.plainText || ''
150
173
  if (value && isFocused) {
151
- setShowAutocomplete(true)
152
- setSearchTrigger((n) => n + 1)
153
- } else if (!value) {
154
- setShowAutocomplete(false)
174
+ showAutocomplete()
155
175
  }
156
176
  }}
157
177
  />
@@ -179,24 +199,14 @@ const FilePickerField = ({
179
199
  <text fg={Theme.textMuted}>{props.info}</text>
180
200
  </WithLeftBorder>
181
201
  )}
182
- <FileAutocomplete
183
- onSelect={handleSelectFile}
184
- visible={showAutocomplete}
185
- onVisibilityChange={setShowAutocomplete}
186
- inputRef={inputRef}
187
- anchorRef={anchorRef}
188
- searchTrigger={searchTrigger}
189
- canChooseFiles={props.canChooseFiles}
190
- canChooseDirectories={props.canChooseDirectories}
191
- initialDirectory={props.initialDirectory}
192
- />
193
202
  </box>
194
203
  ) as React.ReactElement
195
204
  }
196
205
 
197
206
  export const FilePicker = (props: FilePickerProps): any => {
198
207
  const { control } = useFormContext()
199
- const { focusedField, setFocusedField } = useFocusContext()
208
+ const focusContext = useFocusContext()
209
+ const { focusedField, setFocusedField } = focusContext
200
210
  const isFocused = focusedField === props.id
201
211
  const isInFocus = useIsInFocus()
202
212
 
@@ -236,6 +246,7 @@ export const FilePicker = (props: FilePickerProps): any => {
236
246
  props={props}
237
247
  isFocused={isFocused}
238
248
  setFocusedField={setFocusedField}
249
+ isFormLoading={focusContext.isLoading}
239
250
  />
240
251
  </box>
241
252
  ) as React.ReactElement
@@ -1,6 +1,27 @@
1
- import React from 'react'
1
+ import React, { useState, useLayoutEffect } from 'react'
2
2
  import { Theme } from 'termcast/src/theme'
3
+ import { useFocusContext, useFormScrollContext } from './index'
3
4
 
4
5
  export const FormEnd = (): any => {
5
- return <text fg={Theme.text}>└</text>
6
+ const { focusedField } = useFocusContext()
7
+ const scrollContext = useFormScrollContext()
8
+ const [isLastFieldFocused, setIsLastFieldFocused] = useState(false)
9
+
10
+ useLayoutEffect(() => {
11
+ if (!scrollContext || !focusedField) {
12
+ setIsLastFieldFocused(false)
13
+ return
14
+ }
15
+ const descendants = Object.values(scrollContext.descendantsContext.map.current)
16
+ .filter((item) => item.index !== -1 && item.props?.id)
17
+ .sort((a, b) => a.index - b.index)
18
+ if (descendants.length === 0) {
19
+ setIsLastFieldFocused(false)
20
+ return
21
+ }
22
+ const lastField = descendants[descendants.length - 1]
23
+ setIsLastFieldFocused(lastField.props?.id === focusedField)
24
+ }, [focusedField])
25
+
26
+ return <text fg={isLastFieldFocused ? Theme.accent : Theme.text}>└</text>
6
27
  }
@@ -4,6 +4,7 @@ import React, {
4
4
  useContext,
5
5
  useLayoutEffect,
6
6
  useRef,
7
+ useEffect,
7
8
  } from 'react'
8
9
  import { useKeyboard } from '@opentui/react'
9
10
  import { useForm, FormProvider } from 'react-hook-form'
@@ -12,6 +13,7 @@ import { logger } from 'termcast/src/logger'
12
13
  import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
13
14
  import { useDialog } from 'termcast/src/internal/dialog'
14
15
  import { Theme } from 'termcast/src/theme'
16
+ import { useStore } from 'termcast/src/state'
15
17
  import {
16
18
  TextAttributes,
17
19
  ScrollBoxRenderable,
@@ -33,7 +35,10 @@ import {
33
35
  FormValues_2,
34
36
  FormProps_2,
35
37
  FormItemProps_2,
38
+ LinkAccessoryProps,
36
39
  } from './types'
40
+ import { LoadingBar } from 'termcast/src/components/loading-bar'
41
+ import { useNavigationPending } from 'termcast/src/internal/navigation'
37
42
  import { FORM_MAX_WIDTH } from './description'
38
43
  import { ScrollBox } from 'termcast/src/internal/scrollbox'
39
44
 
@@ -67,10 +72,11 @@ export const useFormScrollContext = () => {
67
72
  return useContext(FormScrollContext)
68
73
  }
69
74
 
70
- // Context for managing focused field
75
+ // Context for managing focused field and loading state
71
76
  interface FocusContextValue {
72
77
  focusedField: string | null
73
78
  setFocusedField: (id: string | null) => void
79
+ isLoading: boolean
74
80
  }
75
81
 
76
82
  const FocusContext = createContext<FocusContextValue | null>(null)
@@ -96,9 +102,29 @@ export const useFormSubmit = () => {
96
102
  }
97
103
 
98
104
  function FormFooter(): any {
105
+ const hasToast = useStore((s) => s.toast !== null)
106
+
107
+ const content = hasToast ? null : (
108
+ <>
109
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
110
+ ctrl ↵
111
+ </text>
112
+ <text fg={Theme.textMuted}> submit</text>
113
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
114
+ {' '}tab
115
+ </text>
116
+ <text fg={Theme.textMuted}> navigate</text>
117
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
118
+ {' '}^k
119
+ </text>
120
+ <text fg={Theme.textMuted}> actions</text>
121
+ </>
122
+ )
123
+
99
124
  return (
100
125
  <box
101
126
  border={false}
127
+ height={1}
102
128
  style={{
103
129
  paddingLeft: 1,
104
130
  paddingRight: 1,
@@ -107,16 +133,7 @@ function FormFooter(): any {
107
133
  flexDirection: 'row',
108
134
  }}
109
135
  >
110
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>ctrl ↵</text>
111
- <text fg={Theme.textMuted}> submit</text>
112
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
113
- {' '}↑↓
114
- </text>
115
- <text fg={Theme.textMuted}> navigate</text>
116
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
117
- {' '}^k
118
- </text>
119
- <text fg={Theme.textMuted}> actions</text>
136
+ {content}
120
137
  </box>
121
138
  )
122
139
  }
@@ -171,19 +188,30 @@ interface FormType {
171
188
  FilePicker: (props: FilePickerProps) => any
172
189
  Separator: () => any
173
190
  Description: (props: DescriptionProps) => any
191
+ LinkAccessory: (props: LinkAccessoryProps) => any
174
192
  }
175
193
 
176
194
  export const Form: FormType = ((props) => {
195
+ const { navigationTitle, isLoading, searchBarAccessory } = props
177
196
  const methods = useForm<FormValues>({
178
197
  // defaultValues: {},
179
198
  // mode: 'onChange',
180
199
  })
181
200
 
182
201
  const [focusedField, setFocusedFieldRaw] = useState<string | null>(null)
202
+ const navigationPending = useNavigationPending()
183
203
 
184
204
  const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
185
205
  const descendantsContext = useFormFieldDescendants()
186
206
 
207
+ // Helper to get sorted field IDs
208
+ const getFieldIds = () => {
209
+ return Object.values(descendantsContext.map.current)
210
+ .filter((item) => item.index !== -1 && item.props?.id)
211
+ .sort((a, b) => a.index - b.index)
212
+ .map((item) => item.props!.id)
213
+ }
214
+
187
215
  const scrollToField = (fieldId: string) => {
188
216
  const scrollBox = scrollBoxRef.current
189
217
  if (!scrollBox) return
@@ -197,17 +225,13 @@ export const Form: FormType = ((props) => {
197
225
 
198
226
  const contentY = scrollBox.content?.y || 0
199
227
  const viewportHeight = scrollBox.viewport?.height || 10
200
- const currentScrollTop = scrollBox.scrollTop || 0
201
228
 
202
229
  // Access current position from the BoxRenderable ref
203
230
  const itemTop = elementRef.y - contentY
204
- const itemBottom = itemTop + elementRef.height
205
231
 
206
- if (itemTop < currentScrollTop) {
207
- scrollBox.scrollTo(itemTop)
208
- } else if (itemBottom > currentScrollTop + viewportHeight) {
209
- scrollBox.scrollTo(itemBottom - viewportHeight)
210
- }
232
+ // Scroll so the top of the item is centered in the viewport
233
+ const targetScrollTop = itemTop - viewportHeight / 2
234
+ scrollBox.scrollTo(Math.max(0, targetScrollTop))
211
235
  }
212
236
 
213
237
  const setFocusedField = (id: string | null) => {
@@ -217,30 +241,82 @@ export const Form: FormType = ((props) => {
217
241
  }
218
242
  }
219
243
 
220
- // Auto-focus first field after children have registered as descendants
221
- useLayoutEffect(() => {
222
- const descendants = Object.values(descendantsContext.map.current)
223
- .filter((item) => item.index !== -1 && item.props?.id)
224
- .sort((a, b) => a.index - b.index)
225
-
226
- if (descendants.length > 0) {
227
- const firstId = descendants[0].props!.id
228
- logger.log(`focusing `, firstId)
229
- setFocusedFieldRaw(firstId)
230
- } else {
231
- logger.log(`no fields to focus in form`)
244
+ // Focus first field helper
245
+ const focusFirstField = () => {
246
+ const fieldIds = getFieldIds()
247
+ if (fieldIds.length > 0) {
248
+ logger.log(`focusing first field:`, fieldIds[0])
249
+ setFocusedField(fieldIds[0])
250
+ return true
232
251
  }
233
- }, [])
252
+ return false
253
+ }
254
+
255
+ // Focus last field helper
256
+ const focusLastField = () => {
257
+ const fieldIds = getFieldIds()
258
+ if (fieldIds.length > 0) {
259
+ logger.log(`focusing last field:`, fieldIds[fieldIds.length - 1])
260
+ setFocusedField(fieldIds[fieldIds.length - 1])
261
+ return true
262
+ }
263
+ return false
264
+ }
265
+
266
+ // Auto-focus first field when descendants become available
267
+ // Runs on every render until a field is focused (handles async loading)
268
+ useEffect(() => {
269
+ if (focusedField) return
270
+
271
+ const fieldIds = getFieldIds()
272
+ if (fieldIds.length > 0) {
273
+ logger.log(`auto-focusing first field:`, fieldIds[0])
274
+ setFocusedFieldRaw(fieldIds[0])
275
+ }
276
+ })
234
277
 
235
278
  // Get focus state and dialog
236
279
  const inFocus = useIsInFocus()
237
280
  const dialog = useDialog()
238
281
 
239
- // Handle action keys and page scrolling
282
+ // Handle action keys, tab navigation, and page scrolling
240
283
  useKeyboard((evt) => {
241
284
  // Only handle keyboard events when form is in focus
242
285
  if (!inFocus) return
243
286
 
287
+ // Tab navigation at Form level - handles case when no field is focused
288
+ // or navigates between fields
289
+ if (evt.name === 'tab') {
290
+ if (!focusedField) {
291
+ // No field focused yet - focus first or last based on shift
292
+ if (evt.shift) {
293
+ focusLastField()
294
+ } else {
295
+ focusFirstField()
296
+ }
297
+ } else {
298
+ // A field is focused - navigate to next/previous
299
+ const fieldIds = getFieldIds()
300
+ const currentIndex = fieldIds.indexOf(focusedField)
301
+ if (evt.shift) {
302
+ // Shift+Tab - go to previous
303
+ if (currentIndex > 0) {
304
+ setFocusedField(fieldIds[currentIndex - 1])
305
+ } else {
306
+ setFocusedField(fieldIds[fieldIds.length - 1])
307
+ }
308
+ } else {
309
+ // Tab - go to next
310
+ if (currentIndex < fieldIds.length - 1) {
311
+ setFocusedField(fieldIds[currentIndex + 1])
312
+ } else {
313
+ setFocusedField(fieldIds[0])
314
+ }
315
+ }
316
+ }
317
+ return
318
+ }
319
+
244
320
  // Page up/down scrolling
245
321
  if (evt.name === 'pageup' || evt.name === 'pagedown') {
246
322
  const scrollBox = scrollBoxRef.current
@@ -259,7 +335,7 @@ export const Form: FormType = ((props) => {
259
335
  }
260
336
 
261
337
  if (evt.name === 'k' && evt.ctrl && props.actions) {
262
- // Ctrl+K shows actions
338
+ // Ctrl+K shows actions (always show sheet)
263
339
  dialog.push(
264
340
  <FormSubmitContext.Provider value={submitContextValue}>
265
341
  {props.actions}
@@ -267,7 +343,8 @@ export const Form: FormType = ((props) => {
267
343
  'bottom-right',
268
344
  )
269
345
  } else if (evt.name === 'return' && evt.ctrl && props.actions) {
270
- // Ctrl+Return shows actions (form submit shortcut)
346
+ // Ctrl+Return executes first action directly
347
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
271
348
  dialog.push(
272
349
  <FormSubmitContext.Provider value={submitContextValue}>
273
350
  {props.actions}
@@ -275,7 +352,8 @@ export const Form: FormType = ((props) => {
275
352
  'bottom-right',
276
353
  )
277
354
  } else if (evt.name === 'return' && evt.meta && props.actions) {
278
- // Cmd+Return also shows actions (consistent with List)
355
+ // Cmd+Return also executes first action directly
356
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
279
357
  dialog.push(
280
358
  <FormSubmitContext.Provider value={submitContextValue}>
281
359
  {props.actions}
@@ -298,9 +376,9 @@ export const Form: FormType = ((props) => {
298
376
  <FormProvider {...methods}>
299
377
  <FormSubmitContext.Provider value={submitContextValue}>
300
378
  <FormScrollContext.Provider value={scrollContextValue}>
301
- <FocusContext.Provider value={{ focusedField, setFocusedField }}>
379
+ <FocusContext.Provider value={{ focusedField, setFocusedField, isLoading: isLoading || false }}>
302
380
  <box flexDirection='row' flexGrow={1} justifyContent='center'>
303
- <box flexDirection='column'>
381
+ <box flexGrow={0} flexDirection='column'>
304
382
  <ScrollBox
305
383
  ref={scrollBoxRef}
306
384
  flexGrow={1}
@@ -308,15 +386,40 @@ export const Form: FormType = ((props) => {
308
386
  rootOptions: {
309
387
  maxWidth: FORM_MAX_WIDTH,
310
388
  },
389
+
311
390
  contentOptions: {
312
391
  justifyContent: 'center',
313
392
  },
314
393
  }}
315
394
  >
316
- <FormFieldDescendantsProvider value={descendantsContext}>
317
- {props.children}
318
- <FormEnd />
319
- </FormFieldDescendantsProvider>
395
+ {/* Navigation header with title, loading bar, and accessory */}
396
+
397
+ {/*<box
398
+ border={false}
399
+ style={{
400
+ flexShrink: 0,
401
+ flexDirection: 'row',
402
+ paddingBottom: 1,
403
+ justifyContent: 'space-between',
404
+ alignItems: 'center',
405
+ gap: 3,
406
+ }}
407
+ >
408
+ <box overflow='hidden'>
409
+ <LoadingBar
410
+ title={navigationTitle || ''}
411
+ isLoading={isLoading || navigationPending}
412
+ />
413
+ </box>
414
+ {searchBarAccessory}
415
+ </box>*/}
416
+
417
+ <box>
418
+ <FormFieldDescendantsProvider value={descendantsContext}>
419
+ {props.children}
420
+ <FormEnd />
421
+ </FormFieldDescendantsProvider>
422
+ </box>
320
423
  </ScrollBox>
321
424
  <FormFooter />
322
425
  </box>
@@ -351,3 +454,27 @@ Form.TagPicker = TagPicker
351
454
  Form.FilePicker = FilePicker
352
455
  Form.Separator = Separator
353
456
  Form.Description = Description
457
+
458
+ // LinkAccessory component - shows a link in the navigation bar
459
+ function LinkAccessory(props: LinkAccessoryProps): any {
460
+ return (
461
+ <box
462
+ style={{
463
+ flexShrink: 0,
464
+ maxWidth: '50%',
465
+ overflow: 'hidden',
466
+ paddingRight: 1,
467
+ }}
468
+ >
469
+ <text
470
+ fg={Theme.textMuted}
471
+ attributes={TextAttributes.UNDERLINE}
472
+ wrapMode='none'
473
+ >
474
+ {props.text}
475
+ </text>
476
+ </box>
477
+ )
478
+ }
479
+
480
+ Form.LinkAccessory = LinkAccessory
@@ -6,6 +6,7 @@ import { FormItemProps, FormItemRef } from './types'
6
6
  import { Theme } from 'termcast/src/theme'
7
7
  import { WithLeftBorder } from './with-left-border'
8
8
  import { useFormNavigation } from './use-form-navigation'
9
+ import { LoadingText } from 'termcast/src/components/loading-text'
9
10
 
10
11
  export interface PasswordFieldProps extends FormItemProps<string> {
11
12
  placeholder?: string
@@ -15,7 +16,8 @@ export type PasswordFieldRef = FormItemRef
15
16
 
16
17
  export const PasswordField = (props: PasswordFieldProps): any => {
17
18
  const { control } = useFormContext()
18
- const { focusedField, setFocusedField } = useFocusContext()
19
+ const focusContext = useFocusContext()
20
+ const { focusedField, setFocusedField } = focusContext
19
21
  const isFocused = focusedField === props.id
20
22
  const elementRef = useRef<BoxRenderable>(null)
21
23
  const realValueRef = useRef(props.defaultValue || props.value || '')
@@ -38,15 +40,19 @@ export const PasswordField = (props: PasswordFieldProps): any => {
38
40
 
39
41
  return (
40
42
  <box ref={elementRef} flexDirection="column">
41
- <WithLeftBorder withDiamond isFocused={isFocused}>
42
- <text
43
- fg={isFocused ? Theme.primary : Theme.text}
43
+ <WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
44
+ <box
44
45
  onMouseDown={() => {
45
46
  setFocusedField(props.id)
46
47
  }}
47
48
  >
48
- {props.title}
49
- </text>
49
+ <LoadingText
50
+ isLoading={isFocused && focusContext.isLoading}
51
+ color={isFocused ? Theme.primary : Theme.text}
52
+ >
53
+ {props.title || ''}
54
+ </LoadingText>
55
+ </box>
50
56
  </WithLeftBorder>
51
57
  <WithLeftBorder isFocused={isFocused}>
52
58
  <input
@@ -7,6 +7,7 @@ import { Theme } from 'termcast/src/theme'
7
7
  import { WithLeftBorder } from './with-left-border'
8
8
  import { useFormNavigation } from './use-form-navigation'
9
9
  import { createTextareaFormRef } from './form-ref'
10
+ import { LoadingText } from 'termcast/src/components/loading-text'
10
11
 
11
12
  export interface TextAreaProps extends FormItemProps<string> {
12
13
  placeholder?: string
@@ -17,7 +18,8 @@ export type TextAreaRef = FormItemRef
17
18
 
18
19
  export const TextArea = (props: TextAreaProps): any => {
19
20
  const { register, formState } = useFormContext()
20
- const { focusedField, setFocusedField } = useFocusContext()
21
+ const focusContext = useFocusContext()
22
+ const { focusedField, setFocusedField } = focusContext
21
23
  const isFocused = focusedField === props.id
22
24
 
23
25
  const elementRef = useRef<BoxRenderable>(null)
@@ -58,21 +60,26 @@ export const TextArea = (props: TextAreaProps): any => {
58
60
 
59
61
  return (
60
62
  <box ref={elementRef} flexDirection="column">
61
- <WithLeftBorder withDiamond isFocused={isFocused}>
62
- <text
63
- fg={isFocused ? Theme.primary : Theme.text}
63
+ <WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
64
+ <box
64
65
  onMouseDown={() => {
65
66
  setFocusedField(props.id)
66
67
  }}
67
68
  >
68
- {props.title}
69
- </text>
69
+ <LoadingText
70
+ isLoading={isFocused && focusContext.isLoading}
71
+ color={isFocused ? Theme.primary : Theme.text}
72
+ >
73
+ {props.title || ''}
74
+ </LoadingText>
75
+ </box>
70
76
  </WithLeftBorder>
71
77
 
72
78
  <WithLeftBorder isFocused={isFocused}>
73
79
  <box flexGrow={1}>
74
80
  <textarea
75
81
  ref={handleRef}
82
+ wrapMode='none'
76
83
  initialValue={props.defaultValue || props.value || ''}
77
84
  onContentChange={handleContentChange}
78
85
  minHeight={4}