termcast 1.3.19 → 1.3.24

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 (412) 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/build.d.ts.map +1 -1
  19. package/dist/build.js +2 -1
  20. package/dist/build.js.map +1 -1
  21. package/dist/cache.d.ts +32 -0
  22. package/dist/cache.d.ts.map +1 -0
  23. package/dist/cache.js +205 -0
  24. package/dist/cache.js.map +1 -0
  25. package/dist/cli.d.ts.map +1 -1
  26. package/dist/cli.js +69 -33
  27. package/dist/cli.js.map +1 -1
  28. package/dist/clipboard.d.ts +36 -0
  29. package/dist/clipboard.d.ts.map +1 -0
  30. package/dist/clipboard.js +154 -0
  31. package/dist/clipboard.js.map +1 -0
  32. package/dist/compile.d.ts.map +1 -1
  33. package/dist/compile.js +24 -6
  34. package/dist/compile.js.map +1 -1
  35. package/dist/components/actions.d.ts.map +1 -1
  36. package/dist/components/actions.js +56 -30
  37. package/dist/components/actions.js.map +1 -1
  38. package/dist/components/detail.d.ts.map +1 -1
  39. package/dist/components/detail.js +4 -0
  40. package/dist/components/detail.js.map +1 -1
  41. package/dist/components/dropdown.d.ts.map +1 -1
  42. package/dist/components/dropdown.js +38 -15
  43. package/dist/components/dropdown.js.map +1 -1
  44. package/dist/components/extension-preferences.d.ts.map +1 -1
  45. package/dist/components/extension-preferences.js +40 -13
  46. package/dist/components/extension-preferences.js.map +1 -1
  47. package/dist/components/form/checkbox.d.ts.map +1 -1
  48. package/dist/components/form/checkbox.js +5 -3
  49. package/dist/components/form/checkbox.js.map +1 -1
  50. package/dist/components/form/date-picker.d.ts.map +1 -1
  51. package/dist/components/form/date-picker.js +5 -3
  52. package/dist/components/form/date-picker.js.map +1 -1
  53. package/dist/components/form/description.d.ts.map +1 -1
  54. package/dist/components/form/description.js +2 -2
  55. package/dist/components/form/description.js.map +1 -1
  56. package/dist/components/form/dropdown.d.ts.map +1 -1
  57. package/dist/components/form/dropdown.js +84 -80
  58. package/dist/components/form/dropdown.js.map +1 -1
  59. package/dist/components/form/file-autocomplete.d.ts +3 -6
  60. package/dist/components/form/file-autocomplete.d.ts.map +1 -1
  61. package/dist/components/form/file-autocomplete.js +61 -66
  62. package/dist/components/form/file-autocomplete.js.map +1 -1
  63. package/dist/components/form/file-picker.d.ts.map +1 -1
  64. package/dist/components/form/file-picker.js +33 -30
  65. package/dist/components/form/file-picker.js.map +1 -1
  66. package/dist/components/form/form-end.d.ts.map +1 -1
  67. package/dist/components/form/form-end.js +21 -1
  68. package/dist/components/form/form-end.js.map +1 -1
  69. package/dist/components/form/form-type-only.d.ts +174 -0
  70. package/dist/components/form/form-type-only.d.ts.map +1 -0
  71. package/dist/components/form/form-type-only.js +2 -0
  72. package/dist/components/form/form-type-only.js.map +1 -0
  73. package/dist/components/form/index.d.ts +3 -1
  74. package/dist/components/form/index.d.ts.map +1 -1
  75. package/dist/components/form/index.js +100 -28
  76. package/dist/components/form/index.js.map +1 -1
  77. package/dist/components/form/password-field.d.ts.map +1 -1
  78. package/dist/components/form/password-field.js +5 -3
  79. package/dist/components/form/password-field.js.map +1 -1
  80. package/dist/components/form/text-area.d.ts.map +1 -1
  81. package/dist/components/form/text-area.js +5 -3
  82. package/dist/components/form/text-area.js.map +1 -1
  83. package/dist/components/form/text-field.d.ts.map +1 -1
  84. package/dist/components/form/text-field.js +6 -4
  85. package/dist/components/form/text-field.js.map +1 -1
  86. package/dist/components/form/types.d.ts +5 -0
  87. package/dist/components/form/types.d.ts.map +1 -1
  88. package/dist/components/form/use-form-handling.d.ts +4 -0
  89. package/dist/components/form/use-form-handling.d.ts.map +1 -0
  90. package/dist/components/form/use-form-handling.js +37 -0
  91. package/dist/components/form/use-form-handling.js.map +1 -0
  92. package/dist/components/form/with-left-border.d.ts +2 -1
  93. package/dist/components/form/with-left-border.d.ts.map +1 -1
  94. package/dist/components/form/with-left-border.js +27 -3
  95. package/dist/components/form/with-left-border.js.map +1 -1
  96. package/dist/components/icon.d.ts +1 -0
  97. package/dist/components/icon.d.ts.map +1 -1
  98. package/dist/components/icon.js +24 -8
  99. package/dist/components/icon.js.map +1 -1
  100. package/dist/components/list.d.ts +2 -2
  101. package/dist/components/list.d.ts.map +1 -1
  102. package/dist/components/list.js +140 -62
  103. package/dist/components/list.js.map +1 -1
  104. package/dist/components/loading-bar.d.ts.map +1 -1
  105. package/dist/components/loading-bar.js +2 -2
  106. package/dist/components/loading-bar.js.map +1 -1
  107. package/dist/components/loading-text.d.ts +8 -0
  108. package/dist/components/loading-text.d.ts.map +1 -0
  109. package/dist/components/loading-text.js +58 -0
  110. package/dist/components/loading-text.js.map +1 -0
  111. package/dist/descendants.js +1 -1
  112. package/dist/descendants.js.map +1 -1
  113. package/dist/dev-ui.d.ts +7 -0
  114. package/dist/dev-ui.d.ts.map +1 -0
  115. package/dist/dev-ui.js +118 -0
  116. package/dist/dev-ui.js.map +1 -0
  117. package/dist/environment.d.ts +63 -0
  118. package/dist/environment.d.ts.map +1 -0
  119. package/dist/environment.js +189 -0
  120. package/dist/environment.js.map +1 -0
  121. package/dist/examples/datepicker.d.ts +2 -0
  122. package/dist/examples/datepicker.d.ts.map +1 -0
  123. package/dist/examples/datepicker.js +344 -0
  124. package/dist/examples/datepicker.js.map +1 -0
  125. package/dist/examples/file-autocomplete.vitest.d.ts +2 -0
  126. package/dist/examples/file-autocomplete.vitest.d.ts.map +1 -0
  127. package/dist/examples/file-autocomplete.vitest.js +223 -0
  128. package/dist/examples/file-autocomplete.vitest.js.map +1 -0
  129. package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
  130. package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
  131. package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
  132. package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
  133. package/dist/examples/form-basic.vitest.d.ts +2 -0
  134. package/dist/examples/form-basic.vitest.d.ts.map +1 -0
  135. package/dist/examples/form-basic.vitest.js +630 -0
  136. package/dist/examples/form-basic.vitest.js.map +1 -0
  137. package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
  138. package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
  139. package/dist/examples/form-dropdown-with-sections.js +13 -0
  140. package/dist/examples/form-dropdown-with-sections.js.map +1 -0
  141. package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
  142. package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
  143. package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
  144. package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
  145. package/dist/examples/form-dropdown.vitest.d.ts +2 -0
  146. package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
  147. package/dist/examples/form-dropdown.vitest.js +854 -0
  148. package/dist/examples/form-dropdown.vitest.js.map +1 -0
  149. package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
  150. package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
  151. package/dist/examples/form-multiselect-dropdown.js +13 -0
  152. package/dist/examples/form-multiselect-dropdown.js.map +1 -0
  153. package/dist/examples/form-scroll.d.ts.map +1 -1
  154. package/dist/examples/form-scroll.js +7 -1
  155. package/dist/examples/form-scroll.js.map +1 -1
  156. package/dist/examples/form-scroll.vitest.d.ts +2 -0
  157. package/dist/examples/form-scroll.vitest.d.ts.map +1 -0
  158. package/dist/examples/form-scroll.vitest.js +211 -0
  159. package/dist/examples/form-scroll.vitest.js.map +1 -0
  160. package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
  161. package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
  162. package/dist/examples/form-tagpicker.vitest.js +736 -0
  163. package/dist/examples/form-tagpicker.vitest.js.map +1 -0
  164. package/dist/examples/internal/descendants-filtering.js +1 -1
  165. package/dist/examples/internal/descendants-filtering.js.map +1 -1
  166. package/dist/examples/internal/descendants.js +1 -1
  167. package/dist/examples/internal/descendants.js.map +1 -1
  168. package/dist/examples/internal/nested-boxes.d.ts +2 -0
  169. package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
  170. package/dist/examples/internal/nested-boxes.js +7 -0
  171. package/dist/examples/internal/nested-boxes.js.map +1 -0
  172. package/dist/examples/internal/rhf-custom-ref.js +2 -2
  173. package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
  174. package/dist/examples/internal/scrollbox-demo.js +3 -22
  175. package/dist/examples/internal/scrollbox-demo.js.map +1 -1
  176. package/dist/examples/internal/scrollbox-descendants.d.ts +2 -0
  177. package/dist/examples/internal/scrollbox-descendants.d.ts.map +1 -0
  178. package/dist/examples/internal/scrollbox-descendants.js +83 -0
  179. package/dist/examples/internal/scrollbox-descendants.js.map +1 -0
  180. package/dist/examples/internal/scrollbox-with-descendants.js +4 -8
  181. package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
  182. package/dist/examples/internal/simple-scrollbox.vitest.d.ts +2 -0
  183. package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +1 -0
  184. package/dist/examples/internal/simple-scrollbox.vitest.js +96 -0
  185. package/dist/examples/internal/simple-scrollbox.vitest.js.map +1 -0
  186. package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
  187. package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
  188. package/dist/examples/internal/unicode-square-repro.js +7 -0
  189. package/dist/examples/internal/unicode-square-repro.js.map +1 -0
  190. package/dist/examples/list-detail-metadata.d.ts +2 -0
  191. package/dist/examples/list-detail-metadata.d.ts.map +1 -0
  192. package/dist/examples/list-detail-metadata.js +8 -0
  193. package/dist/examples/list-detail-metadata.js.map +1 -0
  194. package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
  195. package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
  196. package/dist/examples/list-dropdown-default.vitest.js +234 -0
  197. package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
  198. package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
  199. package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
  200. package/dist/examples/list-fetch-data.vitest.js +111 -0
  201. package/dist/examples/list-fetch-data.vitest.js.map +1 -0
  202. package/dist/examples/list-filter-navigation.d.ts +2 -0
  203. package/dist/examples/list-filter-navigation.d.ts.map +1 -0
  204. package/dist/examples/list-filter-navigation.js +8 -0
  205. package/dist/examples/list-filter-navigation.js.map +1 -0
  206. package/dist/examples/list-scrollbox.vitest.d.ts +2 -0
  207. package/dist/examples/list-scrollbox.vitest.d.ts.map +1 -0
  208. package/dist/examples/list-scrollbox.vitest.js +93 -0
  209. package/dist/examples/list-scrollbox.vitest.js.map +1 -0
  210. package/dist/examples/list-with-detail-long.d.ts +2 -0
  211. package/dist/examples/list-with-detail-long.d.ts.map +1 -0
  212. package/dist/examples/list-with-detail-long.js +53 -0
  213. package/dist/examples/list-with-detail-long.js.map +1 -0
  214. package/dist/examples/list-with-detail.vitest.d.ts +2 -0
  215. package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
  216. package/dist/examples/list-with-detail.vitest.js +434 -0
  217. package/dist/examples/list-with-detail.vitest.js.map +1 -0
  218. package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
  219. package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
  220. package/dist/examples/list-with-dropdown.vitest.js +337 -0
  221. package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
  222. package/dist/examples/list-with-sections.js +5 -1
  223. package/dist/examples/list-with-sections.js.map +1 -1
  224. package/dist/examples/list-with-sections.vitest.d.ts +2 -0
  225. package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
  226. package/dist/examples/list-with-sections.vitest.js +601 -0
  227. package/dist/examples/list-with-sections.vitest.js.map +1 -0
  228. package/dist/examples/scrollbox-vertical-centering.d.ts +6 -0
  229. package/dist/examples/scrollbox-vertical-centering.d.ts.map +1 -0
  230. package/dist/examples/scrollbox-vertical-centering.js +17 -0
  231. package/dist/examples/scrollbox-vertical-centering.js.map +1 -0
  232. package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
  233. package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
  234. package/dist/examples/simple-file-picker.vitest.js +678 -0
  235. package/dist/examples/simple-file-picker.vitest.js.map +1 -0
  236. package/dist/examples/simple-grid.vitest.d.ts +2 -0
  237. package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
  238. package/dist/examples/simple-grid.vitest.js +521 -0
  239. package/dist/examples/simple-grid.vitest.js.map +1 -0
  240. package/dist/examples/simple-navigation.js +10 -4
  241. package/dist/examples/simple-navigation.js.map +1 -1
  242. package/dist/examples/simple-navigation.vitest.d.ts +2 -0
  243. package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
  244. package/dist/examples/simple-navigation.vitest.js +718 -0
  245. package/dist/examples/simple-navigation.vitest.js.map +1 -0
  246. package/dist/examples/store.vitest.d.ts +2 -0
  247. package/dist/examples/store.vitest.d.ts.map +1 -0
  248. package/dist/examples/store.vitest.js +69 -0
  249. package/dist/examples/store.vitest.js.map +1 -0
  250. package/dist/extensions/dev.d.ts +4 -2
  251. package/dist/extensions/dev.d.ts.map +1 -1
  252. package/dist/extensions/dev.js +61 -10
  253. package/dist/extensions/dev.js.map +1 -1
  254. package/dist/extensions/dev.vitest.d.ts +2 -0
  255. package/dist/extensions/dev.vitest.d.ts.map +1 -0
  256. package/dist/extensions/dev.vitest.js +197 -0
  257. package/dist/extensions/dev.vitest.js.map +1 -0
  258. package/dist/extensions/home.d.ts.map +1 -1
  259. package/dist/extensions/home.js +3 -0
  260. package/dist/extensions/home.js.map +1 -1
  261. package/dist/home-command.d.ts +8 -0
  262. package/dist/home-command.d.ts.map +1 -0
  263. package/dist/home-command.js +181 -0
  264. package/dist/home-command.js.map +1 -0
  265. package/dist/hover-repro.d.ts +2 -0
  266. package/dist/hover-repro.d.ts.map +1 -0
  267. package/dist/hover-repro.js +20 -0
  268. package/dist/hover-repro.js.map +1 -0
  269. package/dist/index.d.ts +1 -0
  270. package/dist/index.d.ts.map +1 -1
  271. package/dist/index.js +2 -0
  272. package/dist/index.js.map +1 -1
  273. package/dist/internal/dialog.d.ts +1 -0
  274. package/dist/internal/dialog.d.ts.map +1 -1
  275. package/dist/internal/dialog.js +27 -18
  276. package/dist/internal/dialog.js.map +1 -1
  277. package/dist/internal/navigation.d.ts +9 -1
  278. package/dist/internal/navigation.d.ts.map +1 -1
  279. package/dist/internal/navigation.js +5 -5
  280. package/dist/internal/navigation.js.map +1 -1
  281. package/dist/internal/offscreen.d.ts +6 -0
  282. package/dist/internal/offscreen.d.ts.map +1 -0
  283. package/dist/internal/offscreen.js +10 -0
  284. package/dist/internal/offscreen.js.map +1 -0
  285. package/dist/internal/providers.d.ts.map +1 -1
  286. package/dist/internal/providers.js +2 -2
  287. package/dist/internal/providers.js.map +1 -1
  288. package/dist/internal/scrollbox.d.ts +1 -10
  289. package/dist/internal/scrollbox.d.ts.map +1 -1
  290. package/dist/internal/scrollbox.js +2 -1
  291. package/dist/internal/scrollbox.js.map +1 -1
  292. package/dist/localstorage.d.ts +13 -0
  293. package/dist/localstorage.d.ts.map +1 -0
  294. package/dist/localstorage.js +190 -0
  295. package/dist/localstorage.js.map +1 -0
  296. package/dist/oauth.d.ts +142 -0
  297. package/dist/oauth.d.ts.map +1 -0
  298. package/dist/oauth.js +551 -0
  299. package/dist/oauth.js.map +1 -0
  300. package/dist/preferences.d.ts +23 -0
  301. package/dist/preferences.d.ts.map +1 -0
  302. package/dist/preferences.js +105 -0
  303. package/dist/preferences.js.map +1 -0
  304. package/dist/state.d.ts +2 -0
  305. package/dist/state.d.ts.map +1 -1
  306. package/dist/state.js +3 -0
  307. package/dist/state.js.map +1 -1
  308. package/dist/store.d.ts +21 -0
  309. package/dist/store.d.ts.map +1 -0
  310. package/dist/store.js +84 -0
  311. package/dist/store.js.map +1 -0
  312. package/dist/swift-loader.d.ts +3 -0
  313. package/dist/swift-loader.d.ts.map +1 -0
  314. package/dist/swift-loader.js +193 -0
  315. package/dist/swift-loader.js.map +1 -0
  316. package/dist/swift-runtime.d.ts +2 -0
  317. package/dist/swift-runtime.d.ts.map +1 -0
  318. package/dist/swift-runtime.js +27 -0
  319. package/dist/swift-runtime.js.map +1 -0
  320. package/dist/toast.d.ts +44 -0
  321. package/dist/toast.d.ts.map +1 -0
  322. package/dist/toast.js +221 -0
  323. package/dist/toast.js.map +1 -0
  324. package/dist/utils/file-system.d.ts +9 -0
  325. package/dist/utils/file-system.d.ts.map +1 -1
  326. package/dist/utils/file-system.js +49 -0
  327. package/dist/utils/file-system.js.map +1 -1
  328. package/dist/utils/run-command.d.ts +25 -1
  329. package/dist/utils/run-command.d.ts.map +1 -1
  330. package/dist/utils/run-command.js +47 -4
  331. package/dist/utils/run-command.js.map +1 -1
  332. package/dist/window.d.ts +12 -0
  333. package/dist/window.d.ts.map +1 -0
  334. package/dist/window.js +48 -0
  335. package/dist/window.js.map +1 -0
  336. package/package.json +13 -11
  337. package/src/apis/browser-extension.tsx +29 -0
  338. package/src/apis/localstorage.test.ts +14 -6
  339. package/src/apis/localstorage.tsx +8 -5
  340. package/src/apis/oauth.tsx +5 -1
  341. package/src/apis/preferences.tsx +48 -22
  342. package/src/build.test.tsx +52 -0
  343. package/src/build.tsx +2 -1
  344. package/src/cli.tsx +80 -39
  345. package/src/compile.tsx +24 -6
  346. package/src/components/actions.tsx +94 -32
  347. package/src/components/detail.tsx +4 -0
  348. package/src/components/dropdown.tsx +47 -14
  349. package/src/components/extension-preferences.tsx +44 -16
  350. package/src/components/form/checkbox.tsx +12 -6
  351. package/src/components/form/date-picker.tsx +12 -6
  352. package/src/components/form/description.tsx +7 -2
  353. package/src/components/form/dropdown.tsx +131 -119
  354. package/src/components/form/file-autocomplete.tsx +90 -108
  355. package/src/components/form/file-picker.tsx +54 -43
  356. package/src/components/form/form-end.tsx +23 -2
  357. package/src/components/form/index.tsx +152 -34
  358. package/src/components/form/password-field.tsx +12 -6
  359. package/src/components/form/text-area.tsx +13 -6
  360. package/src/components/form/text-field.tsx +13 -6
  361. package/src/components/form/types.tsx +6 -0
  362. package/src/components/form/with-left-border.tsx +41 -8
  363. package/src/components/icon.tsx +27 -8
  364. package/src/components/list.tsx +193 -74
  365. package/src/components/loading-bar.tsx +3 -2
  366. package/src/components/loading-text.tsx +79 -0
  367. package/src/descendants.tsx +1 -0
  368. package/src/examples/file-autocomplete.vitest.tsx +130 -125
  369. package/src/examples/form-basic.vitest.tsx +376 -176
  370. package/src/examples/form-dropdown.vitest.tsx +126 -126
  371. package/src/examples/form-scroll.tsx +2 -0
  372. package/src/examples/form-scroll.vitest.tsx +58 -58
  373. package/src/examples/form-tagpicker.vitest.tsx +99 -99
  374. package/src/examples/internal/descendants-filtering.tsx +1 -0
  375. package/src/examples/internal/descendants.tsx +1 -0
  376. package/src/examples/internal/rhf-custom-ref.tsx +2 -0
  377. package/src/examples/internal/scrollbox-demo.tsx +3 -27
  378. package/src/examples/internal/scrollbox-with-descendants.tsx +4 -7
  379. package/src/examples/internal/simple-scrollbox.vitest.tsx +7 -5
  380. package/src/examples/list-detail-metadata.tsx +49 -0
  381. package/src/examples/list-detail-metadata.vitest.tsx +88 -0
  382. package/src/examples/list-dropdown-default.vitest.tsx +51 -51
  383. package/src/examples/list-fetch-data.vitest.tsx +4 -4
  384. package/src/examples/list-scrollbox.vitest.tsx +73 -14
  385. package/src/examples/list-with-detail-long.tsx +70 -0
  386. package/src/examples/list-with-detail.vitest.tsx +198 -92
  387. package/src/examples/list-with-dropdown.vitest.tsx +53 -53
  388. package/src/examples/list-with-sections.tsx +1 -0
  389. package/src/examples/list-with-sections.vitest.tsx +213 -89
  390. package/src/examples/list-with-toast.vitest.tsx +4 -4
  391. package/src/examples/simple-file-picker.vitest.tsx +61 -471
  392. package/src/examples/simple-grid.vitest.tsx +238 -233
  393. package/src/examples/simple-navigation.tsx +15 -7
  394. package/src/examples/simple-navigation.vitest.tsx +121 -210
  395. package/src/examples/store.vitest.tsx +1 -1
  396. package/src/examples/swift-extension.vitest.tsx +148 -0
  397. package/src/examples/synonyms.vitest.tsx +159 -0
  398. package/src/extensions/dev.tsx +74 -7
  399. package/src/extensions/dev.vitest.tsx +97 -31
  400. package/src/extensions/home.tsx +6 -0
  401. package/src/index.tsx +3 -0
  402. package/src/internal/dialog.tsx +43 -30
  403. package/src/internal/navigation.tsx +3 -1
  404. package/src/internal/offscreen.tsx +15 -0
  405. package/src/internal/providers.tsx +4 -2
  406. package/src/internal/scrollbox.tsx +4 -8
  407. package/src/keyboard.test.tsx +69 -0
  408. package/src/state.tsx +7 -0
  409. package/src/swift-loader.tsx +239 -0
  410. package/src/swift-runtime.tsx +36 -0
  411. package/src/utils/file-system.ts +61 -0
  412. package/src/utils/run-command.tsx +75 -6
@@ -1,12 +1,41 @@
1
1
  import React from 'react'
2
2
  import { TextAttributes } from '@opentui/core'
3
3
  import { Theme } from 'termcast/src/theme'
4
+ import { colord } from 'colord'
5
+
6
+ const spinnerFrames = [
7
+ { char: ' ', color: Theme.accent },
8
+ { char: '·', color: Theme.accent },
9
+ { char: '•', color: colord(Theme.accent).lighten(0.1).toHex() },
10
+ // { char: '●', color: colord(Theme.accent).lighten(0.2).toHex() },
11
+ ]
12
+
13
+ function Spinner(): any {
14
+ const [index, setIndex] = React.useState(0)
15
+
16
+ React.useEffect(() => {
17
+ const interval = setInterval(() => {
18
+ setIndex((i) => (i + 1) % spinnerFrames.length)
19
+ }, 200)
20
+ return () => {
21
+ clearInterval(interval)
22
+ }
23
+ }, [])
24
+
25
+ const frame = spinnerFrames[index]
26
+ return (
27
+ <text flexShrink={0} fg={frame.color}>
28
+ <b>{frame.char}</b>
29
+ </text>
30
+ )
31
+ }
4
32
 
5
33
  interface WithLeftBorderProps {
6
34
  children: React.ReactNode
7
35
  withDiamond?: boolean
8
36
  customCharacter?: { focused: string; unfocused: string }
9
37
  isFocused: boolean
38
+ isLoading?: boolean
10
39
  paddingBottom?: number
11
40
  paddingLeft?: number
12
41
  paddingTop?: number
@@ -18,21 +47,24 @@ export const WithLeftBorder = ({
18
47
  withDiamond,
19
48
  customCharacter,
20
49
  isFocused,
50
+ isLoading,
21
51
  paddingBottom = 1,
22
52
  paddingLeft = 2,
23
53
  paddingTop = 0,
24
54
  }: WithLeftBorderProps): any => {
25
55
  if (withDiamond || customCharacter) {
26
56
  const chars = customCharacter || { focused: '◆', unfocused: '◇' }
57
+ const color = isFocused ? Theme.accent : Theme.text
27
58
  return (
28
59
  <box flexDirection='row'>
29
- <text
30
- key={String(isFocused)}
31
- fg={isFocused ? Theme.accent : Theme.text}
32
- >
33
- <b>{isFocused ? chars.focused : chars.unfocused}</b>
34
- </text>
35
- <box flexGrow={1} paddingLeft={paddingLeft}>
60
+ {isFocused && isLoading ? (
61
+ <Spinner />
62
+ ) : (
63
+ <text key={String(isFocused)} flexShrink={0} fg={color}>
64
+ <b>{isFocused ? chars.focused : chars.unfocused}</b>
65
+ </text>
66
+ )}
67
+ <box flexShrink={0} flexGrow={1} paddingLeft={paddingLeft}>
36
68
  {children}
37
69
  </box>
38
70
  </box>
@@ -42,8 +74,9 @@ export const WithLeftBorder = ({
42
74
  <box
43
75
  paddingLeft={paddingLeft}
44
76
  border={['left']}
45
- borderStyle={isFocused ? 'heavy' : 'single'}
77
+ // borderStyle={isFocused ? 'heavy' : 'single'}
46
78
  borderColor={isFocused ? Theme.accent : undefined}
79
+ flexShrink={0}
47
80
  flexDirection='row'
48
81
  >
49
82
  <box paddingTop={paddingTop} paddingBottom={paddingBottom} flexGrow={1}>
@@ -6,11 +6,11 @@ const ICON_SHAPES = [
6
6
  '◆', // filled diamond
7
7
  '▲', // filled triangle up
8
8
  '▼', // filled triangle down
9
- '★', // filled star
10
- '♦', // diamond suit
11
- '♥', // heart suit
12
- '♠', // spade suit
13
- '♣', // club suit
9
+ // '★', // filled star
10
+ // '♦', // diamond suit
11
+ // '♥', // heart suit
12
+ // '♠', // spade suit
13
+ // '♣', // club suit
14
14
  ]
15
15
 
16
16
  function hashString(str: string): number {
@@ -505,13 +505,28 @@ function createIconEnum(): Record<string, string> {
505
505
  return icons
506
506
  }
507
507
 
508
- export const Icon = createIconEnum()
508
+ function createIconWithUnicode(): Record<string, string> {
509
+ const icons: Record<string, string> = {}
510
+
511
+ for (const iconId of iconIds) {
512
+ const name = iconId.replace(/-16$/, '')
513
+ const pascalName = pascalCase(name)
514
+ icons[pascalName] = getIconShape(iconId)
515
+ }
516
+
517
+ return icons
518
+ }
519
+
520
+ export const IconSource = createIconEnum()
521
+ export const Icon = createIconWithUnicode()
509
522
 
510
523
  export function getIconEmoji(icon: string): string {
524
+ // If it's an icon ID (like 'pencil-16'), convert to unicode
511
525
  if (iconIds.includes(icon)) {
512
526
  return getIconShape(icon)
513
527
  }
514
- return '?'
528
+ // Otherwise assume it's already unicode and return as-is
529
+ return icon
515
530
  }
516
531
 
517
532
  interface IconProps {
@@ -520,5 +535,9 @@ interface IconProps {
520
535
  }
521
536
 
522
537
  export function IconComponent({ source }: IconProps): any {
523
- return getIconEmoji(source)
538
+ // If it's an icon ID, convert to unicode; otherwise return as-is
539
+ if (iconIds.includes(source)) {
540
+ return getIconShape(source)
541
+ }
542
+ return source
524
543
  }
@@ -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,6 +69,8 @@ interface ActionsInterface {
67
69
  }
68
70
 
69
71
  function ListFooter(): any {
72
+ const firstActionTitle = useStore((s) => s.firstActionTitle)
73
+
70
74
  return (
71
75
  <box
72
76
  border={false}
@@ -79,10 +83,14 @@ function ListFooter(): any {
79
83
  flexDirection: 'row',
80
84
  }}
81
85
  >
82
- <text fg={Theme.text} attributes={TextAttributes.BOLD}>
83
-
84
- </text>
85
- <text fg={Theme.textMuted}> select</text>
86
+ {firstActionTitle && (
87
+ <>
88
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
89
+
90
+ </text>
91
+ <text fg={Theme.textMuted}> {firstActionTitle.toLowerCase()}</text>
92
+ </>
93
+ )}
86
94
  <text fg={Theme.text} attributes={TextAttributes.BOLD}>
87
95
  {' '}↑↓
88
96
  </text>
@@ -175,13 +183,13 @@ export interface ItemProps extends ActionsInterface, CommonProps {
175
183
  tooltip: string
176
184
  }
177
185
  accessories?: ItemAccessory[]
178
- detail?: ReactElement<DetailProps>
186
+ detail?: ReactNode
179
187
  }
180
188
 
181
189
  export interface DetailProps extends CommonProps {
182
190
  isLoading?: boolean
183
191
  markdown?: string
184
- metadata?: ReactElement<MetadataProps> | null
192
+ metadata?: ReactNode
185
193
  }
186
194
 
187
195
  export interface MetadataProps extends CommonProps {
@@ -380,7 +388,14 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
380
388
  // Wrapper function that updates search text
381
389
  const setSearchText = (value: string) => {
382
390
  setSearchTextRaw(value)
383
- setSelectedIndex(0) // Reset selection when search changes
391
+ // TODO: use flushSync when available to force descendants to update visibility
392
+ const items = Object.values(descendantsContext.map.current)
393
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
394
+ .sort((a, b) => a.index - b.index)
395
+
396
+ if (items.length > 0 && items[0]) {
397
+ setSelectedIndex(items[0].index)
398
+ }
384
399
  }
385
400
 
386
401
 
@@ -456,6 +471,7 @@ function ListDropdownDialog(props: ListDropdownDialogProps): any {
456
471
  <textarea
457
472
  ref={inputRef}
458
473
  height={1}
474
+ wrapMode='none'
459
475
  keyBindings={[
460
476
  { name: 'return', action: 'submit' },
461
477
  { name: 'linefeed', action: 'submit' },
@@ -670,7 +686,7 @@ export const List: ListType = (props) => {
670
686
  const {
671
687
  children,
672
688
  onSelectionChange,
673
- filtering = true,
689
+ filtering,
674
690
  searchText: controlledSearchText,
675
691
  onSearchTextChange,
676
692
  searchBarPlaceholder = 'Search...',
@@ -686,6 +702,7 @@ export const List: ListType = (props) => {
686
702
  const [selectedIndex, setSelectedIndex] = useState(0)
687
703
  const [isDropdownOpen, setIsDropdownOpen] = useState(false)
688
704
  const [currentDetail, setCurrentDetail] = useState<ReactNode>(null)
705
+ const [currentItemActions, setCurrentItemActions] = useState<ReactNode>(null)
689
706
  const inputRef = useRef<TextareaRenderable>(null)
690
707
  const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
691
708
  const descendantsContext = useListDescendants()
@@ -700,23 +717,35 @@ export const List: ListType = (props) => {
700
717
  useLayoutEffect(() => {
701
718
  if (controlledSearchText === undefined) return
702
719
  const textarea = inputRef.current
703
- if (textarea && textarea.plainText !== controlledSearchText) {
704
- textarea.setText(controlledSearchText)
705
- }
720
+ if (!textarea) return
721
+
722
+ // Skip if textarea already has the correct value
723
+ if (textarea.plainText === controlledSearchText) return
724
+
725
+ // Save cursor position, set text, then restore cursor (clamped to valid range)
726
+ const cursorOffset = textarea.cursorOffset
727
+ textarea.setText(controlledSearchText)
728
+ textarea.cursorOffset = Math.min(cursorOffset, controlledSearchText.length)
706
729
  }, [controlledSearchText])
707
730
 
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
731
+ // Filtering logic (matches Raycast behavior):
732
+ //
733
+ // | filtering prop | onSearchTextChange | Result |
734
+ // |----------------|-------------------|-----------------|
735
+ // | undefined | undefined | true (default) |
736
+ // | undefined | provided | false |
737
+ // | true | undefined | true |
738
+ // | true | provided | true |
739
+ // | false | undefined | false |
740
+ // | false | provided | false |
741
+ //
742
+ // Summary: filtering defaults to true, but is implicitly disabled when
743
+ // onSearchTextChange is provided (user manages filtering). Set filtering={true}
744
+ // explicitly to use built-in filtering alongside onSearchTextChange.
715
745
  const isFilteringEnabled = (() => {
716
746
  if (filtering === false) return false
717
747
  if (filtering === true) return true
718
- // filtering is undefined/not specified
719
- return !onSearchTextChange // defaults to true unless onSearchTextChange is provided
748
+ return !onSearchTextChange
720
749
  })()
721
750
 
722
751
  const openDropdown = () => {
@@ -726,8 +755,15 @@ export const List: ListType = (props) => {
726
755
  // Wrapper function that updates search text
727
756
  const setInternalSearchText = (value: string) => {
728
757
  setInternalSearchTextRaw(value)
729
- // Reset to 0 when search changes - this is expected UX behavior
730
- setSelectedIndex(0)
758
+ // TODO: use flushSync when available to force descendants to update visibility
759
+ // before querying. For now, we compute visibility inline with the new search value.
760
+ const items = Object.values(descendantsContext.map.current)
761
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
762
+ .sort((a, b) => a.index - b.index)
763
+
764
+ if (items.length > 0 && items[0]) {
765
+ setSelectedIndex(items[0].index)
766
+ }
731
767
  }
732
768
 
733
769
  const listContextValue = useMemo<ListContextValue>(
@@ -758,27 +794,37 @@ export const List: ListType = (props) => {
758
794
  if (selectedItemId !== undefined) {
759
795
  const items = Object.values(descendantsContext.map.current)
760
796
  .filter((item) => item.index !== -1)
761
- .sort((a, b) => a.index - b.index)
762
797
 
763
- const index = items.findIndex((item) => item.props?.id === selectedItemId)
764
- if (index !== -1) {
765
- setSelectedIndex(index)
798
+ const foundItem = items.find((item) => item.props?.id === selectedItemId)
799
+ if (foundItem) {
800
+ setSelectedIndex(foundItem.index)
766
801
  }
767
802
  }
768
803
  }, [selectedItemId])
769
804
 
770
- // Call onSelectionChange when selection changes
805
+ // Call onSelectionChange when selection changes and track current item's actions
771
806
  useEffect(() => {
772
- if (!onSelectionChange) return
773
-
774
807
  const items = Object.values(descendantsContext.map.current)
775
808
  .filter((item) => item.index !== -1)
776
809
  .sort((a, b) => a.index - b.index)
777
810
 
778
811
  const currentItem = items.find((item) => item.index === selectedIndex)
779
- const selectedId = currentItem?.props?.id ?? null
780
- onSelectionChange(selectedId)
781
- }, [selectedIndex])
812
+
813
+ // Track current item's actions for footer display
814
+ const actions = currentItem?.props?.actions ?? props.actions ?? null
815
+ setCurrentItemActions(actions)
816
+
817
+ // Clear first action title when there are no actions
818
+ if (!actions) {
819
+ useStore.setState({ firstActionTitle: '' })
820
+ }
821
+
822
+ // Call onSelectionChange callback if provided
823
+ if (onSelectionChange) {
824
+ const selectedId = currentItem?.props?.id ?? null
825
+ onSelectionChange(selectedId)
826
+ }
827
+ }, [selectedIndex, props.actions])
782
828
 
783
829
  const scrollToItem = (item: { props?: ListItemDescendant }) => {
784
830
  const scrollBox = scrollBoxRef.current
@@ -787,29 +833,24 @@ export const List: ListType = (props) => {
787
833
 
788
834
  const contentY = scrollBox.content?.y || 0
789
835
  const viewportHeight = scrollBox.viewport?.height || 10
790
- const currentScrollTop = scrollBox.scrollTop || 0
791
836
 
837
+ // Calculate item position relative to content
792
838
  const itemTop = elementRef.y - contentY
793
- const itemBottom = itemTop + elementRef.height
794
-
795
- const visibleTop = currentScrollTop
796
- const visibleBottom = currentScrollTop + viewportHeight
797
839
 
798
- if (itemTop < visibleTop) {
799
- scrollBox.scrollTo(itemTop)
800
- } else if (itemBottom > visibleBottom) {
801
- scrollBox.scrollTo(itemBottom - viewportHeight)
802
- }
840
+ // Scroll so the top of the item is centered in the viewport
841
+ const targetScrollTop = itemTop - viewportHeight / 2
842
+ scrollBox.scrollTo(Math.max(0, targetScrollTop))
803
843
  }
804
844
 
805
845
  const move = (direction: -1 | 1) => {
846
+ // Get all visible items
806
847
  const items = Object.values(descendantsContext.map.current)
807
848
  .filter((item) => item.index !== -1 && item.props?.visible !== false)
808
849
  .sort((a, b) => a.index - b.index)
809
850
 
810
851
  if (items.length === 0) return
811
852
 
812
-
853
+ // Find currently selected item's position in visible items
813
854
  let currentVisibleIndex = items.findIndex(
814
855
  (item) => item.index === selectedIndex,
815
856
  )
@@ -817,19 +858,19 @@ export const List: ListType = (props) => {
817
858
  // If current selection is not visible, select first visible item
818
859
  if (items[0]) {
819
860
  setSelectedIndex(items[0].index)
820
- scrollToItem(items[0])
821
861
  }
822
862
  return
823
863
  }
824
864
 
865
+ // Calculate next visible index
825
866
  let nextVisibleIndex = currentVisibleIndex + direction
826
867
  if (nextVisibleIndex < 0) nextVisibleIndex = items.length - 1
827
868
  if (nextVisibleIndex >= items.length) nextVisibleIndex = 0
828
869
 
829
870
  const nextItem = items[nextVisibleIndex]
830
871
  if (nextItem) {
831
- setSelectedIndex(nextItem.index)
832
872
  scrollToItem(nextItem)
873
+ setSelectedIndex(nextItem.index)
833
874
  }
834
875
  }
835
876
 
@@ -845,14 +886,14 @@ export const List: ListType = (props) => {
845
886
  return
846
887
  }
847
888
 
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)
889
+ // Get current item by selectedIndex (which is a descendant index)
890
+ const items = Object.values(descendantsContext.map.current)
891
+ .filter((item) => item.index !== -1)
892
+ .sort((a, b) => a.index - b.index)
893
+ const currentItem = items.find((item) => item.index === selectedIndex)
855
894
 
895
+ // Handle Ctrl+K to show actions (always show sheet)
896
+ if (evt.name === 'k' && evt.ctrl) {
856
897
  // Show current item's actions if available
857
898
  if (currentItem?.props?.actions) {
858
899
  dialog.push(currentItem.props.actions, 'bottom-right')
@@ -866,15 +907,12 @@ export const List: ListType = (props) => {
866
907
 
867
908
  if (evt.name === 'up') move(-1)
868
909
  if (evt.name === 'down') move(1)
910
+ // Handle Enter to execute first action directly
869
911
  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
912
  if (!currentItem?.props) return
876
913
 
877
914
  if (currentItem.props.actions) {
915
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
878
916
  dialog.push(currentItem.props.actions, 'bottom-right')
879
917
  }
880
918
  }
@@ -904,7 +942,10 @@ export const List: ListType = (props) => {
904
942
  border={false}
905
943
  style={{
906
944
  paddingBottom: 0,
907
- flexGrow: 1,
945
+ flexShrink: 0,
946
+ paddingLeft: 1,
947
+ paddingRight: 1,
948
+ overflow: 'hidden',
908
949
  }}
909
950
  >
910
951
  <LoadingBar
@@ -938,6 +979,7 @@ export const List: ListType = (props) => {
938
979
  <textarea
939
980
  ref={inputRef}
940
981
  height={1}
982
+ wrapMode='none'
941
983
  keyBindings={[
942
984
  { name: 'return', action: 'submit' },
943
985
  { name: 'linefeed', action: 'submit' },
@@ -985,6 +1027,11 @@ export const List: ListType = (props) => {
985
1027
 
986
1028
  {/* Footer with keyboard shortcuts or toast */}
987
1029
  <ListFooter />
1030
+
1031
+ {/* Render current item's actions offscreen to collect first action title */}
1032
+ {currentItemActions && (
1033
+ <Offscreen>{currentItemActions}</Offscreen>
1034
+ )}
988
1035
  </box>
989
1036
 
990
1037
  {/* Detail panel on the right */}
@@ -1076,7 +1123,7 @@ const ListItem: ListItemType = (props) => {
1076
1123
  elementRef: elementRef.current,
1077
1124
  })
1078
1125
 
1079
- // Get selected index from parent List context
1126
+ // Check if this item is selected
1080
1127
  const selectedIndex = listContext?.selectedIndex ?? 0
1081
1128
  const isActive = index === selectedIndex
1082
1129
 
@@ -1156,7 +1203,7 @@ const ListItemDetail: ListItemDetailType = (props) => {
1156
1203
  )}
1157
1204
 
1158
1205
  <ScrollBox
1159
- focused={true}
1206
+ focused={false}
1160
1207
  flexGrow={1}
1161
1208
  flexShrink={1}
1162
1209
  style={{
@@ -1200,8 +1247,8 @@ const ListItemDetailMetadata = (props: MetadataProps) => {
1200
1247
 
1201
1248
  const ListItemDetailMetadataLabel = (props: { title: string; text?: string; icon?: Image.ImageLike }) => {
1202
1249
  return (
1203
- <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1204
- <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1250
+ <box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
1251
+ <text fg={Theme.textMuted}>{props.title}:</text>
1205
1252
  {props.text && <text fg={Theme.text}>{props.text}</text>}
1206
1253
  </box>
1207
1254
  )
@@ -1217,8 +1264,8 @@ const ListItemDetailMetadataSeparator = () => {
1217
1264
 
1218
1265
  const ListItemDetailMetadataLink = (props: { title: string; target: string; text: string }) => {
1219
1266
  return (
1220
- <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1221
- <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1267
+ <box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
1268
+ <text fg={Theme.textMuted}>{props.title}:</text>
1222
1269
  <text fg={Theme.link}>{props.text}</text>
1223
1270
  </box>
1224
1271
  )
@@ -1568,13 +1615,21 @@ const ListSection = (props: SectionProps) => {
1568
1615
  [parentContext, props.title, searchText],
1569
1616
  )
1570
1617
 
1571
- // Hide section title when searching
1572
- const showTitle = props.title && !searchText.trim()
1618
+ const isSearching = searchText.trim().length > 0
1619
+
1620
+ const children = (
1621
+ <ListSectionContext.Provider value={sectionContextValue}>
1622
+ {props.children}
1623
+ </ListSectionContext.Provider>
1624
+ )
1625
+
1626
+ if (isSearching) {
1627
+ return children
1628
+ }
1573
1629
 
1574
1630
  return (
1575
1631
  <box style={{ marginBottom: 1 }}>
1576
- {/* Render section title if provided and not searching */}
1577
- {showTitle && (
1632
+ {props.title && (
1578
1633
  <box
1579
1634
  border={false}
1580
1635
  style={{
@@ -1586,18 +1641,82 @@ const ListSection = (props: SectionProps) => {
1586
1641
  </text>
1587
1642
  </box>
1588
1643
  )}
1589
- {/* Render children with section context */}
1590
- <ListSectionContext.Provider value={sectionContextValue}>
1591
- {props.children}
1592
- </ListSectionContext.Provider>
1644
+ {children}
1593
1645
  </box>
1594
1646
  )
1595
1647
  }
1596
1648
 
1597
1649
  List.Section = ListSection
1598
1650
  List.Dropdown = ListDropdown
1599
- List.EmptyView = (props) => {
1600
- return null
1651
+ List.EmptyView = (props: EmptyViewProps) => {
1652
+ const dialog = useDialog()
1653
+ const inFocus = useIsInFocus()
1654
+
1655
+ // Handle keyboard for actions
1656
+ useKeyboard((evt) => {
1657
+ if (!inFocus) return
1658
+
1659
+ // Handle Ctrl+K to show actions
1660
+ if (evt.name === 'k' && evt.ctrl && props.actions) {
1661
+ dialog.push(props.actions, 'bottom-right')
1662
+ return
1663
+ }
1664
+
1665
+ // Handle Enter to execute first action
1666
+ if (evt.name === 'return' && props.actions) {
1667
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
1668
+ dialog.push(props.actions, 'bottom-right')
1669
+ }
1670
+ })
1671
+
1672
+ // Get icon string from ImageLike
1673
+ const getIconString = (icon: Image.ImageLike): string | null => {
1674
+ if (typeof icon === 'string') {
1675
+ return getIconEmoji(icon)
1676
+ }
1677
+ if (icon && typeof icon === 'object' && 'source' in icon) {
1678
+ // For { source: string } or { source: { light, dark } } objects
1679
+ const source = icon.source
1680
+ if (typeof source === 'string') {
1681
+ return getIconEmoji(source)
1682
+ }
1683
+ }
1684
+ return null
1685
+ }
1686
+
1687
+ const iconEmoji = props.icon ? getIconString(props.icon) : null
1688
+
1689
+ return (
1690
+ <box
1691
+ style={{
1692
+ flexDirection: 'column',
1693
+ alignItems: 'center',
1694
+ justifyContent: 'center',
1695
+ flexGrow: 1,
1696
+ paddingTop: 2,
1697
+ paddingBottom: 2,
1698
+ paddingLeft: 2,
1699
+ paddingRight: 2,
1700
+ gap: 1,
1701
+ }}
1702
+ >
1703
+ {iconEmoji && (
1704
+ <text fg={Theme.textMuted} style={{ marginBottom: 1 }}>
1705
+ {iconEmoji}
1706
+ </text>
1707
+ )}
1708
+ {props.title && (
1709
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
1710
+ {props.title?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
1711
+ </text>
1712
+ )}
1713
+ {props.description && (
1714
+ <text fg={Theme.textMuted} wrapMode='word'>
1715
+ {props.description?.replace(/\bRaycast\b/g, 'Termcast').replace(/\braycast\b/g, 'termcast') || ''}
1716
+ </text>
1717
+ )}
1718
+ </box>
1719
+ )
1601
1720
  }
1602
1721
 
1603
1722
  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}