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
@@ -18,9 +18,11 @@ import React, {
18
18
  } from 'react'
19
19
  import { LoadingBar } from 'termcast/src/components/loading-bar'
20
20
  import { createDescendants } from 'termcast/src/descendants'
21
+ import { useStore } from 'termcast/src/state'
21
22
  import { useDialog } from 'termcast/src/internal/dialog'
22
23
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
23
24
  import { useNavigationPending } from 'termcast/src/internal/navigation'
25
+ import { Offscreen } from 'termcast/src/internal/offscreen'
24
26
  import { ScrollBox } from 'termcast/src/internal/scrollbox'
25
27
 
26
28
  import { Color, resolveColor } from 'termcast/src/colors'
@@ -67,9 +69,34 @@ interface ActionsInterface {
67
69
  }
68
70
 
69
71
  function ListFooter(): any {
72
+ const firstActionTitle = useStore((s) => s.firstActionTitle)
73
+ const hasToast = useStore((s) => s.toast !== null)
74
+
75
+ const content = hasToast ? null : (
76
+ <>
77
+ {firstActionTitle && (
78
+ <>
79
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
80
+
81
+ </text>
82
+ <text fg={Theme.textMuted}> {firstActionTitle.toLowerCase()}</text>
83
+ </>
84
+ )}
85
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
86
+ {' '}↑↓
87
+ </text>
88
+ <text fg={Theme.textMuted}> navigate</text>
89
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
90
+ {' '}^k
91
+ </text>
92
+ <text fg={Theme.textMuted}> actions</text>
93
+ </>
94
+ )
95
+
70
96
  return (
71
97
  <box
72
98
  border={false}
99
+ height={1}
73
100
  style={{
74
101
  paddingLeft: 1,
75
102
  flexShrink: 0,
@@ -79,18 +106,7 @@ function ListFooter(): any {
79
106
  flexDirection: 'row',
80
107
  }}
81
108
  >
82
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
83
-
84
- </text>
85
- <text fg={Theme.textMuted}> select</text>
86
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
87
- {' '}↑↓
88
- </text>
89
- <text fg={Theme.textMuted}> navigate</text>
90
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
91
- {' '}^k
92
- </text>
93
- <text fg={Theme.textMuted}> actions</text>
109
+ {content}
94
110
  </box>
95
111
  )
96
112
  }
@@ -175,13 +191,13 @@ export interface ItemProps extends ActionsInterface, CommonProps {
175
191
  tooltip: string
176
192
  }
177
193
  accessories?: ItemAccessory[]
178
- detail?: ReactElement<DetailProps>
194
+ detail?: ReactNode
179
195
  }
180
196
 
181
197
  export interface DetailProps extends CommonProps {
182
198
  isLoading?: boolean
183
199
  markdown?: string
184
- metadata?: ReactElement<MetadataProps> | null
200
+ metadata?: ReactNode
185
201
  }
186
202
 
187
203
  export interface MetadataProps extends CommonProps {
@@ -380,7 +396,14 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
380
396
  // Wrapper function that updates search text
381
397
  const setSearchText = (value: string) => {
382
398
  setSearchTextRaw(value)
383
- setSelectedIndex(0) // Reset selection when search changes
399
+ // TODO: use flushSync when available to force descendants to update visibility
400
+ const items = Object.values(descendantsContext.map.current)
401
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
402
+ .sort((a, b) => a.index - b.index)
403
+
404
+ if (items.length > 0 && items[0]) {
405
+ setSelectedIndex(items[0].index)
406
+ }
384
407
  }
385
408
 
386
409
 
@@ -456,6 +479,7 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
456
479
  <textarea
457
480
  ref={inputRef}
458
481
  height={1}
482
+ wrapMode='none'
459
483
  keyBindings={[
460
484
  { name: 'return', action: 'submit' },
461
485
  { name: 'linefeed', action: 'submit' },
@@ -499,30 +523,45 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
499
523
  )}
500
524
  </box>
501
525
 
502
- <box
503
- border={false}
504
- style={{
505
- paddingRight: 2,
506
- paddingLeft: 3,
507
- paddingBottom: 1,
508
- paddingTop: 1,
509
- flexDirection: 'row',
510
- }}
511
- >
512
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
513
-
514
- </text>
515
- <text fg={Theme.textMuted}> select</text>
516
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
517
- {' '}↑↓
518
- </text>
519
- <text fg={Theme.textMuted}> navigate</text>
520
- </box>
526
+ <DropdownFooter />
521
527
  </box>
522
528
  </DropdownDescendantsProvider>
523
529
  )
524
530
  }
525
531
 
532
+ function DropdownFooter(): any {
533
+ const hasToast = useStore((s) => s.toast !== null)
534
+
535
+ const content = hasToast ? null : (
536
+ <>
537
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
538
+
539
+ </text>
540
+ <text fg={Theme.textMuted}> select</text>
541
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
542
+ {' '}↑↓
543
+ </text>
544
+ <text fg={Theme.textMuted}> navigate</text>
545
+ </>
546
+ )
547
+
548
+ return (
549
+ <box
550
+ border={false}
551
+ height={1}
552
+ style={{
553
+ paddingRight: 2,
554
+ paddingLeft: 3,
555
+ paddingBottom: 1,
556
+ paddingTop: 1,
557
+ flexDirection: 'row',
558
+ }}
559
+ >
560
+ {content}
561
+ </box>
562
+ )
563
+ }
564
+
526
565
  // Render a single list item row
527
566
  function ListItemRow(props: {
528
567
  title: string
@@ -670,7 +709,7 @@ export const List: ListType = (props) => {
670
709
  const {
671
710
  children,
672
711
  onSelectionChange,
673
- filtering = true,
712
+ filtering,
674
713
  searchText: controlledSearchText,
675
714
  onSearchTextChange,
676
715
  searchBarPlaceholder = 'Search...',
@@ -686,6 +725,7 @@ export const List: ListType = (props) => {
686
725
  const [selectedIndex, setSelectedIndex] = useState(0)
687
726
  const [isDropdownOpen, setIsDropdownOpen] = useState(false)
688
727
  const [currentDetail, setCurrentDetail] = useState<ReactNode>(null)
728
+ const [currentItemActions, setCurrentItemActions] = useState<ReactNode>(null)
689
729
  const inputRef = useRef<TextareaRenderable>(null)
690
730
  const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
691
731
  const descendantsContext = useListDescendants()
@@ -700,23 +740,35 @@ export const List: ListType = (props) => {
700
740
  useLayoutEffect(() => {
701
741
  if (controlledSearchText === undefined) return
702
742
  const textarea = inputRef.current
703
- if (textarea && textarea.plainText !== controlledSearchText) {
704
- textarea.setText(controlledSearchText)
705
- }
743
+ if (!textarea) return
744
+
745
+ // Skip if textarea already has the correct value
746
+ if (textarea.plainText === controlledSearchText) return
747
+
748
+ // Save cursor position, set text, then restore cursor (clamped to valid range)
749
+ const cursorOffset = textarea.cursorOffset
750
+ textarea.setText(controlledSearchText)
751
+ textarea.cursorOffset = Math.min(cursorOffset, controlledSearchText.length)
706
752
  }, [controlledSearchText])
707
753
 
708
- // Determine if filtering is enabled
709
- // List filters automatically when:
710
- // - filtering is not specified (defaults to true) OR filtering is explicitly true
711
- // List does NOT filter automatically when:
712
- // - When filtering={false}
713
- // - When onSearchTextChange is provided (implicitly sets filtering to false)
714
- // - Unless you explicitly set filtering={true} alongside onSearchTextChange
754
+ // Filtering logic (matches Raycast behavior):
755
+ //
756
+ // | filtering prop | onSearchTextChange | Result |
757
+ // |----------------|-------------------|-----------------|
758
+ // | undefined | undefined | true (default) |
759
+ // | undefined | provided | false |
760
+ // | true | undefined | true |
761
+ // | true | provided | true |
762
+ // | false | undefined | false |
763
+ // | false | provided | false |
764
+ //
765
+ // Summary: filtering defaults to true, but is implicitly disabled when
766
+ // onSearchTextChange is provided (user manages filtering). Set filtering={true}
767
+ // explicitly to use built-in filtering alongside onSearchTextChange.
715
768
  const isFilteringEnabled = (() => {
716
769
  if (filtering === false) return false
717
770
  if (filtering === true) return true
718
- // filtering is undefined/not specified
719
- return !onSearchTextChange // defaults to true unless onSearchTextChange is provided
771
+ return !onSearchTextChange
720
772
  })()
721
773
 
722
774
  const openDropdown = () => {
@@ -726,8 +778,15 @@ export const List: ListType = (props) => {
726
778
  // Wrapper function that updates search text
727
779
  const setInternalSearchText = (value: string) => {
728
780
  setInternalSearchTextRaw(value)
729
- // Reset to 0 when search changes - this is expected UX behavior
730
- setSelectedIndex(0)
781
+ // TODO: use flushSync when available to force descendants to update visibility
782
+ // before querying. For now, we compute visibility inline with the new search value.
783
+ const items = Object.values(descendantsContext.map.current)
784
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
785
+ .sort((a, b) => a.index - b.index)
786
+
787
+ if (items.length > 0 && items[0]) {
788
+ setSelectedIndex(items[0].index)
789
+ }
731
790
  }
732
791
 
733
792
  const listContextValue = useMemo<ListContextValue>(
@@ -758,27 +817,37 @@ export const List: ListType = (props) => {
758
817
  if (selectedItemId !== undefined) {
759
818
  const items = Object.values(descendantsContext.map.current)
760
819
  .filter((item) => item.index !== -1)
761
- .sort((a, b) => a.index - b.index)
762
820
 
763
- const index = items.findIndex((item) => item.props?.id === selectedItemId)
764
- if (index !== -1) {
765
- setSelectedIndex(index)
821
+ const foundItem = items.find((item) => item.props?.id === selectedItemId)
822
+ if (foundItem) {
823
+ setSelectedIndex(foundItem.index)
766
824
  }
767
825
  }
768
826
  }, [selectedItemId])
769
827
 
770
- // Call onSelectionChange when selection changes
828
+ // Call onSelectionChange when selection changes and track current item's actions
771
829
  useEffect(() => {
772
- if (!onSelectionChange) return
773
-
774
830
  const items = Object.values(descendantsContext.map.current)
775
831
  .filter((item) => item.index !== -1)
776
832
  .sort((a, b) => a.index - b.index)
777
833
 
778
834
  const currentItem = items.find((item) => item.index === selectedIndex)
779
- const selectedId = currentItem?.props?.id ?? null
780
- onSelectionChange(selectedId)
781
- }, [selectedIndex])
835
+
836
+ // Track current item's actions for footer display
837
+ const actions = currentItem?.props?.actions ?? props.actions ?? null
838
+ setCurrentItemActions(actions)
839
+
840
+ // Clear first action title when there are no actions
841
+ if (!actions) {
842
+ useStore.setState({ firstActionTitle: '' })
843
+ }
844
+
845
+ // Call onSelectionChange callback if provided
846
+ if (onSelectionChange) {
847
+ const selectedId = currentItem?.props?.id ?? null
848
+ onSelectionChange(selectedId)
849
+ }
850
+ }, [selectedIndex, props.actions])
782
851
 
783
852
  const scrollToItem = (item: { props?: ListItemDescendant }) => {
784
853
  const scrollBox = scrollBoxRef.current
@@ -787,29 +856,24 @@ export const List: ListType = (props) => {
787
856
 
788
857
  const contentY = scrollBox.content?.y || 0
789
858
  const viewportHeight = scrollBox.viewport?.height || 10
790
- const currentScrollTop = scrollBox.scrollTop || 0
791
859
 
860
+ // Calculate item position relative to content
792
861
  const itemTop = elementRef.y - contentY
793
- const itemBottom = itemTop + elementRef.height
794
-
795
- const visibleTop = currentScrollTop
796
- const visibleBottom = currentScrollTop + viewportHeight
797
862
 
798
- if (itemTop < visibleTop) {
799
- scrollBox.scrollTo(itemTop)
800
- } else if (itemBottom > visibleBottom) {
801
- scrollBox.scrollTo(itemBottom - viewportHeight)
802
- }
863
+ // Scroll so the top of the item is centered in the viewport
864
+ const targetScrollTop = itemTop - viewportHeight / 2
865
+ scrollBox.scrollTo(Math.max(0, targetScrollTop))
803
866
  }
804
867
 
805
868
  const move = (direction: -1 | 1) => {
869
+ // Get all visible items
806
870
  const items = Object.values(descendantsContext.map.current)
807
871
  .filter((item) => item.index !== -1 && item.props?.visible !== false)
808
872
  .sort((a, b) => a.index - b.index)
809
873
 
810
874
  if (items.length === 0) return
811
875
 
812
-
876
+ // Find currently selected item's position in visible items
813
877
  let currentVisibleIndex = items.findIndex(
814
878
  (item) => item.index === selectedIndex,
815
879
  )
@@ -817,19 +881,19 @@ export const List: ListType = (props) => {
817
881
  // If current selection is not visible, select first visible item
818
882
  if (items[0]) {
819
883
  setSelectedIndex(items[0].index)
820
- scrollToItem(items[0])
821
884
  }
822
885
  return
823
886
  }
824
887
 
888
+ // Calculate next visible index
825
889
  let nextVisibleIndex = currentVisibleIndex + direction
826
890
  if (nextVisibleIndex < 0) nextVisibleIndex = items.length - 1
827
891
  if (nextVisibleIndex >= items.length) nextVisibleIndex = 0
828
892
 
829
893
  const nextItem = items[nextVisibleIndex]
830
894
  if (nextItem) {
831
- setSelectedIndex(nextItem.index)
832
895
  scrollToItem(nextItem)
896
+ setSelectedIndex(nextItem.index)
833
897
  }
834
898
  }
835
899
 
@@ -845,14 +909,14 @@ export const List: ListType = (props) => {
845
909
  return
846
910
  }
847
911
 
848
- // Handle Ctrl+K to show actions
849
- if (evt.name === 'k' && evt.ctrl) {
850
- const items = Object.values(descendantsContext.map.current)
851
- .filter((item) => item.index !== -1)
852
- .sort((a, b) => a.index - b.index)
853
-
854
- const currentItem = items.find((item) => item.index === selectedIndex)
912
+ // Get current item by selectedIndex (which is a descendant index)
913
+ const items = Object.values(descendantsContext.map.current)
914
+ .filter((item) => item.index !== -1)
915
+ .sort((a, b) => a.index - b.index)
916
+ const currentItem = items.find((item) => item.index === selectedIndex)
855
917
 
918
+ // Handle Ctrl+K to show actions (always show sheet)
919
+ if (evt.name === 'k' && evt.ctrl) {
856
920
  // Show current item's actions if available
857
921
  if (currentItem?.props?.actions) {
858
922
  dialog.push(currentItem.props.actions, 'bottom-right')
@@ -866,15 +930,12 @@ export const List: ListType = (props) => {
866
930
 
867
931
  if (evt.name === 'up') move(-1)
868
932
  if (evt.name === 'down') move(1)
933
+ // Handle Enter to execute first action directly
869
934
  if (evt.name === 'return') {
870
- const items = Object.values(descendantsContext.map.current)
871
- .filter((item) => item.index !== -1)
872
- .sort((a, b) => a.index - b.index)
873
-
874
- const currentItem = items.find((item) => item.index === selectedIndex)
875
935
  if (!currentItem?.props) return
876
936
 
877
937
  if (currentItem.props.actions) {
938
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
878
939
  dialog.push(currentItem.props.actions, 'bottom-right')
879
940
  }
880
941
  }
@@ -904,7 +965,10 @@ export const List: ListType = (props) => {
904
965
  border={false}
905
966
  style={{
906
967
  paddingBottom: 0,
907
- flexGrow: 1,
968
+ flexShrink: 0,
969
+ paddingLeft: 1,
970
+ paddingRight: 1,
971
+ overflow: 'hidden',
908
972
  }}
909
973
  >
910
974
  <LoadingBar
@@ -938,6 +1002,7 @@ export const List: ListType = (props) => {
938
1002
  <textarea
939
1003
  ref={inputRef}
940
1004
  height={1}
1005
+ wrapMode='none'
941
1006
  keyBindings={[
942
1007
  { name: 'return', action: 'submit' },
943
1008
  { name: 'linefeed', action: 'submit' },
@@ -985,6 +1050,11 @@ export const List: ListType = (props) => {
985
1050
 
986
1051
  {/* Footer with keyboard shortcuts or toast */}
987
1052
  <ListFooter />
1053
+
1054
+ {/* Render current item's actions offscreen to collect first action title */}
1055
+ {currentItemActions && (
1056
+ <Offscreen>{currentItemActions}</Offscreen>
1057
+ )}
988
1058
  </box>
989
1059
 
990
1060
  {/* Detail panel on the right */}
@@ -1076,7 +1146,7 @@ const ListItem: ListItemType = (props) => {
1076
1146
  elementRef: elementRef.current,
1077
1147
  })
1078
1148
 
1079
- // Get selected index from parent List context
1149
+ // Check if this item is selected
1080
1150
  const selectedIndex = listContext?.selectedIndex ?? 0
1081
1151
  const isActive = index === selectedIndex
1082
1152
 
@@ -1156,7 +1226,7 @@ const ListItemDetail: ListItemDetailType = (props) => {
1156
1226
  )}
1157
1227
 
1158
1228
  <ScrollBox
1159
- focused={true}
1229
+ focused={false}
1160
1230
  flexGrow={1}
1161
1231
  flexShrink={1}
1162
1232
  style={{
@@ -1200,8 +1270,8 @@ const ListItemDetailMetadata = (props: MetadataProps) => {
1200
1270
 
1201
1271
  const ListItemDetailMetadataLabel = (props: { title: string; text?: string; icon?: Image.ImageLike }) => {
1202
1272
  return (
1203
- <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1204
- <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1273
+ <box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
1274
+ <text fg={Theme.textMuted}>{props.title}:</text>
1205
1275
  {props.text && <text fg={Theme.text}>{props.text}</text>}
1206
1276
  </box>
1207
1277
  )
@@ -1217,8 +1287,8 @@ const ListItemDetailMetadataSeparator = () => {
1217
1287
 
1218
1288
  const ListItemDetailMetadataLink = (props: { title: string; target: string; text: string }) => {
1219
1289
  return (
1220
- <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1221
- <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1290
+ <box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
1291
+ <text fg={Theme.textMuted}>{props.title}:</text>
1222
1292
  <text fg={Theme.link}>{props.text}</text>
1223
1293
  </box>
1224
1294
  )
@@ -1568,13 +1638,21 @@ const ListSection = (props: SectionProps) => {
1568
1638
  [parentContext, props.title, searchText],
1569
1639
  )
1570
1640
 
1571
- // Hide section title when searching
1572
- const showTitle = props.title && !searchText.trim()
1641
+ const isSearching = searchText.trim().length > 0
1642
+
1643
+ const children = (
1644
+ <ListSectionContext.Provider value={sectionContextValue}>
1645
+ {props.children}
1646
+ </ListSectionContext.Provider>
1647
+ )
1648
+
1649
+ if (isSearching) {
1650
+ return children
1651
+ }
1573
1652
 
1574
1653
  return (
1575
1654
  <box style={{ marginBottom: 1 }}>
1576
- {/* Render section title if provided and not searching */}
1577
- {showTitle && (
1655
+ {props.title && (
1578
1656
  <box
1579
1657
  border={false}
1580
1658
  style={{
@@ -1586,18 +1664,82 @@ const ListSection = (props: SectionProps) => {
1586
1664
  </text>
1587
1665
  </box>
1588
1666
  )}
1589
- {/* Render children with section context */}
1590
- <ListSectionContext.Provider value={sectionContextValue}>
1591
- {props.children}
1592
- </ListSectionContext.Provider>
1667
+ {children}
1593
1668
  </box>
1594
1669
  )
1595
1670
  }
1596
1671
 
1597
1672
  List.Section = ListSection
1598
1673
  List.Dropdown = ListDropdown
1599
- List.EmptyView = (props) => {
1600
- return null
1674
+ List.EmptyView = (props: EmptyViewProps) => {
1675
+ const dialog = useDialog()
1676
+ const inFocus = useIsInFocus()
1677
+
1678
+ // Handle keyboard for actions
1679
+ useKeyboard((evt) => {
1680
+ if (!inFocus) return
1681
+
1682
+ // Handle Ctrl+K to show actions
1683
+ if (evt.name === 'k' && evt.ctrl && props.actions) {
1684
+ dialog.push(props.actions, 'bottom-right')
1685
+ return
1686
+ }
1687
+
1688
+ // Handle Enter to execute first action
1689
+ if (evt.name === 'return' && props.actions) {
1690
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
1691
+ dialog.push(props.actions, 'bottom-right')
1692
+ }
1693
+ })
1694
+
1695
+ // Get icon string from ImageLike
1696
+ const getIconString = (icon: Image.ImageLike): string | null => {
1697
+ if (typeof icon === 'string') {
1698
+ return getIconEmoji(icon)
1699
+ }
1700
+ if (icon && typeof icon === 'object' && 'source' in icon) {
1701
+ // For { source: string } or { source: { light, dark } } objects
1702
+ const source = icon.source
1703
+ if (typeof source === 'string') {
1704
+ return getIconEmoji(source)
1705
+ }
1706
+ }
1707
+ return null
1708
+ }
1709
+
1710
+ const iconEmoji = props.icon ? getIconString(props.icon) : null
1711
+
1712
+ return (
1713
+ <box
1714
+ style={{
1715
+ flexDirection: 'column',
1716
+ alignItems: 'center',
1717
+ justifyContent: 'center',
1718
+ flexGrow: 1,
1719
+ paddingTop: 2,
1720
+ paddingBottom: 2,
1721
+ paddingLeft: 2,
1722
+ paddingRight: 2,
1723
+ gap: 1,
1724
+ }}
1725
+ >
1726
+ {iconEmoji && (
1727
+ <text fg={Theme.textMuted} style={{ marginBottom: 1 }}>
1728
+ {iconEmoji}
1729
+ </text>
1730
+ )}
1731
+ {props.title && (
1732
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
1733
+ {props.title?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
1734
+ </text>
1735
+ )}
1736
+ {props.description && (
1737
+ <text fg={Theme.textMuted} wrapMode='word'>
1738
+ {props.description?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
1739
+ </text>
1740
+ )}
1741
+ </box>
1742
+ )
1601
1743
  }
1602
1744
 
1603
1745
  export default List
@@ -127,10 +127,11 @@ export function LoadingBar(props: LoadingBarProps): any {
127
127
  style={{
128
128
  flexDirection: 'row',
129
129
  flexGrow: 1,
130
- paddingLeft: 1,
131
- paddingRight: 1,
130
+ // paddingLeft: 1,
131
+ // paddingRight: 1,
132
132
  }}
133
133
  >
134
+
134
135
  {characters.map((char, index) => (
135
136
  <text key={index} fg={getCharacterColor(index)} flexShrink={0}>
136
137
  {char}
@@ -0,0 +1,79 @@
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import { colord } from 'colord'
3
+
4
+ interface LoadingTextProps {
5
+ children: string
6
+ isLoading?: boolean
7
+ color: string
8
+ }
9
+
10
+ /**
11
+ * Generate wave colors based on base color
12
+ * Creates a gradient from base color to a lighter version and back
13
+ * Uses a shorter wave (12 chars) compared to LoadingBar
14
+ */
15
+ function generateWaveColors(baseColor: string): string[] {
16
+ const base = colord(baseColor)
17
+ // Shorter wave for text - lighten factors from 0 to 0.5 and back
18
+ const steps = [0, 10, 20, 30, 40, 50, 40, 30, 20, 10, 0, 0]
19
+ return steps.map((percent) => base.lighten(percent / 100).toHex())
20
+ }
21
+
22
+ export function LoadingText(props: LoadingTextProps): any {
23
+ const { children, isLoading = false, color = '#FFC000' } = props
24
+ const [position, setPosition] = useState(0)
25
+ const intervalRef = useRef<NodeJS.Timeout | null>(null)
26
+
27
+ const characters = children.split('')
28
+ const waveColors = generateWaveColors(color)
29
+ const waveWidth = waveColors.length
30
+ // Add padding at the end to create a delay before the next loop
31
+ const endPadding = 10
32
+ const totalLength = characters.length + waveWidth + endPadding
33
+
34
+ useEffect(() => {
35
+ if (isLoading) {
36
+ intervalRef.current = setInterval(() => {
37
+ setPosition((prev) => (prev + 1) % totalLength)
38
+ }, 30)
39
+ } else {
40
+ if (intervalRef.current) {
41
+ clearInterval(intervalRef.current)
42
+ intervalRef.current = null
43
+ }
44
+ setPosition(0)
45
+ }
46
+
47
+ return () => {
48
+ if (intervalRef.current) {
49
+ clearInterval(intervalRef.current)
50
+ }
51
+ }
52
+ }, [isLoading, totalLength])
53
+
54
+ const getCharacterColor = (index: number): string => {
55
+ if (!isLoading) {
56
+ return color
57
+ }
58
+
59
+ const distance = position - index
60
+
61
+ // If character is within the wave (behind the current position)
62
+ if (distance >= 0 && distance < waveWidth) {
63
+ return waveColors[distance]
64
+ }
65
+
66
+ // Default to base color for characters outside the wave
67
+ return color
68
+ }
69
+
70
+ return (
71
+ <box flexDirection="row">
72
+ {characters.map((char, index) => (
73
+ <text key={index} fg={getCharacterColor(index)} flexShrink={0}>
74
+ {char}
75
+ </text>
76
+ ))}
77
+ </box>
78
+ )
79
+ }
@@ -113,6 +113,7 @@ const MenuExample = () => {
113
113
  <textarea
114
114
  ref={inputRef}
115
115
  height={1}
116
+ wrapMode='none'
116
117
  keyBindings={[
117
118
  { name: 'return', action: 'submit' },
118
119
  { name: 'linefeed', action: 'submit' },