termcast 1.3.21 → 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 +67 -31
  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 +22 -5
  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 +12 -10
  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 +78 -37
  345. package/src/compile.tsx +22 -5
  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
@@ -6,7 +6,7 @@ import React, {
6
6
  useRef,
7
7
  useLayoutEffect,
8
8
  } from 'react'
9
- import { BoxRenderable } from '@opentui/core'
9
+ import { BoxRenderable, ScrollBoxRenderable } from '@opentui/core'
10
10
  import { useKeyboard } from '@opentui/react'
11
11
  import {
12
12
  useFormContext,
@@ -25,6 +25,7 @@ import {
25
25
  import { WithLeftBorder } from './with-left-border'
26
26
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
27
27
  import { useFormNavigationHelpers } from './use-form-navigation'
28
+ import { LoadingText } from 'termcast/src/components/loading-text'
28
29
 
29
30
  export interface DropdownProps extends FormItemProps<string | string[]> {
30
31
  placeholder?: string
@@ -62,6 +63,7 @@ interface FormDropdownItemDescendant {
62
63
  title: string
63
64
  icon?: string
64
65
  section?: string // Track which section this item belongs to
66
+ elementRef?: BoxRenderable | null
65
67
  }
66
68
 
67
69
  const {
@@ -73,13 +75,12 @@ const {
73
75
  // Context for dropdown state
74
76
  interface FormDropdownContextValue {
75
77
  focusedIndex: number
76
- offset: number
77
- itemsPerPage: number
78
78
  isFocused: boolean
79
79
  handleSelect: (descendantId: string) => void
80
80
  field: DropdownFieldType
81
81
  props: DropdownProps
82
82
  descendantsContext: DescendantContextType<FormDropdownItemDescendant>
83
+ scrollBoxRef: React.RefObject<ScrollBoxRenderable | null>
83
84
  }
84
85
 
85
86
  // Separate context for section information
@@ -91,21 +92,20 @@ const SectionContext = createContext<SectionContextValue>({
91
92
  currentSection: undefined,
92
93
  })
93
94
 
94
- const itemsPerPage = 4
95
95
  const FormDropdownContext = createContext<FormDropdownContextValue>({
96
96
  focusedIndex: 0,
97
- offset: 0,
98
- itemsPerPage,
99
97
  isFocused: false,
100
98
  handleSelect: () => {},
101
99
  descendantsContext: {} as any,
102
100
  field: {} as DropdownFieldType,
103
101
  props: {} as DropdownProps,
102
+ scrollBoxRef: { current: null },
104
103
  })
105
104
 
106
105
  const DropdownItem = (props: DropdownItemProps) => {
107
106
  const context = useContext(FormDropdownContext)
108
107
  const sectionContext = useContext(SectionContext)
108
+ const elementRef = useRef<BoxRenderable | null>(null)
109
109
 
110
110
  // Register as descendant
111
111
  const descendant = useFormDropdownDescendant({
@@ -113,16 +113,9 @@ const DropdownItem = (props: DropdownItemProps) => {
113
113
  title: props.title,
114
114
  icon: props.icon,
115
115
  section: sectionContext.currentSection,
116
+ elementRef: elementRef.current,
116
117
  })
117
118
 
118
- // Hide items that are outside the visible range
119
- if (
120
- descendant.index < context.offset ||
121
- descendant.index >= context.offset + context.itemsPerPage
122
- ) {
123
- return null
124
- }
125
-
126
119
  const isFocused = descendant.index === context.focusedIndex
127
120
 
128
121
  // Check if this item is selected based on field value
@@ -144,54 +137,34 @@ const DropdownItem = (props: DropdownItemProps) => {
144
137
  })()
145
138
 
146
139
  return (
147
- <WithLeftBorder
148
- key={props.value}
149
- isFocused={context.isFocused}
150
- paddingLeft={0}
151
- paddingBottom={0}
152
- >
153
- <text
154
- fg={
155
- context.isFocused && isFocused
156
- ? Theme.accent
157
- : context.isFocused
158
- ? Theme.text
159
- : Theme.textMuted
160
- }
161
- onMouseDown={() => {
162
- context.handleSelect(descendant.descendantId)
163
- }}
140
+ <box ref={elementRef} key={props.value}>
141
+ <WithLeftBorder
142
+ isFocused={context.isFocused}
143
+ paddingLeft={0}
144
+ paddingBottom={0}
164
145
  >
165
- {context.isFocused && isFocused ? '› ' : ' '}
166
- {isSelected ? '●' : '○'} {props.title}
167
- </text>
168
- </WithLeftBorder>
146
+ <text
147
+ fg={
148
+ context.isFocused && isFocused
149
+ ? Theme.accent
150
+ : context.isFocused
151
+ ? Theme.text
152
+ : Theme.textMuted
153
+ }
154
+ onMouseDown={() => {
155
+ context.handleSelect(descendant.descendantId)
156
+ }}
157
+ >
158
+ {context.isFocused && isFocused ? '› ' : ' '}
159
+ {isSelected ? '●' : '○'} {props.title}
160
+ </text>
161
+ </WithLeftBorder>
162
+ </box>
169
163
  )
170
164
  }
171
165
 
172
166
  const DropdownSection = (props: DropdownSectionProps) => {
173
167
  const parentContext = useContext(FormDropdownContext)
174
- const [isVisible, setIsVisible] = useState(false)
175
-
176
- // Update visibility when offset changes
177
- useLayoutEffect(() => {
178
- if (!props.title) {
179
- setIsVisible(true)
180
- return
181
- }
182
-
183
- const items = Object.values(parentContext.descendantsContext.map.current)
184
- logger.log('DropdownSection: items', items)
185
- const hasVisibleItems = items.some((item) => {
186
- return (
187
- item.props?.section === props.title &&
188
- item.index >= parentContext.offset &&
189
- item.index < parentContext.offset + parentContext.itemsPerPage
190
- )
191
- })
192
-
193
- setIsVisible(hasVisibleItems)
194
- }, [parentContext.offset, props.title, parentContext.itemsPerPage])
195
168
 
196
169
  // Create section context value
197
170
  const sectionContextValue = useMemo(
@@ -204,7 +177,7 @@ const DropdownSection = (props: DropdownSectionProps) => {
204
177
  return (
205
178
  <SectionContext.Provider value={sectionContextValue}>
206
179
  <box flexDirection='column'>
207
- {props.title && isVisible && (
180
+ {props.title && (
208
181
  <WithLeftBorder
209
182
  paddingTop={0}
210
183
  paddingBottom={0}
@@ -232,12 +205,16 @@ const DropdownContent = ({
232
205
  }: DropdownContentProps) => {
233
206
  const descendantsContext = useFormDropdownDescendants()
234
207
  const isInFocus = useIsInFocus()
235
- const { focusedField, setFocusedField } = useFocusContext()
208
+ const focusContext = useFocusContext()
209
+ const { focusedField, setFocusedField } = focusContext
236
210
  const isFocused = focusedField === props.id
237
211
  const [focusedIndex, setFocusedIndex] = useState(0)
238
- const [offset, setOffset] = useState(0)
239
212
 
240
213
  const elementRef = useRef<BoxRenderable>(null)
214
+ const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
215
+
216
+ const typeAheadTextRef = useRef('')
217
+ const typeAheadTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
241
218
 
242
219
  // Register as form field descendant for scroll support
243
220
  useFormFieldDescendant({
@@ -246,9 +223,26 @@ const DropdownContent = ({
246
223
  })
247
224
 
248
225
  const [selectedTitles, setSelectedTitles] = useState<string[]>([])
249
- const [itemsCount, setItemsCount] = useState(0)
250
226
 
251
- const { navigateToPrevious, navigateToNext } = useFormNavigationHelpers(props.id)
227
+ const { navigateToPrevious, navigateToNext } = useFormNavigationHelpers(
228
+ props.id,
229
+ )
230
+
231
+ const scrollToItem = (item: { props?: FormDropdownItemDescendant }) => {
232
+ const scrollBox = scrollBoxRef.current
233
+ const itemElementRef = item.props?.elementRef
234
+ if (!scrollBox || !itemElementRef) return
235
+
236
+ const contentY = scrollBox.content?.y || 0
237
+ const viewportHeight = scrollBox.viewport?.height || 5
238
+
239
+ // Calculate item position relative to content
240
+ const itemTop = itemElementRef.y - contentY
241
+
242
+ // Scroll so the top of the item is centered in the viewport
243
+ const targetScrollTop = itemTop - viewportHeight / 2
244
+ scrollBox.scrollTo(Math.max(0, targetScrollTop))
245
+ }
252
246
 
253
247
  // Helper to get value for a descendantId
254
248
  const getValueForDescendantId = (
@@ -296,46 +290,26 @@ const DropdownContent = ({
296
290
  useKeyboard((evt) => {
297
291
  if (!isFocused || !isInFocus) return
298
292
 
299
- const items = Object.values(descendantsContext.map.current).filter(
300
- (item) => item.index !== -1,
301
- )
293
+ const items = Object.values(descendantsContext.map.current)
294
+ .filter((item) => item.index !== -1)
295
+ .sort((a, b) => a.index - b.index)
302
296
  const itemCount = items.length
303
297
 
304
298
  if (itemCount > 0) {
305
299
  if (evt.name === 'down') {
306
- setFocusedIndex((prev) => {
307
- const nextIndex = (prev + 1) % itemCount
308
-
309
- // Update offset only when the focused item is at the last position and there are more items
310
- const visibleEnd = offset + itemsPerPage - 1
311
- if (
312
- prev === visibleEnd &&
313
- nextIndex < itemCount &&
314
- nextIndex > prev
315
- ) {
316
- // Scroll down by one when at the last visible item
317
- setOffset(offset + 1)
318
- } else if (nextIndex < prev) {
319
- // Wrapped to beginning
320
- setOffset(0)
321
- }
322
-
323
- return nextIndex
324
- })
300
+ const nextIndex = (focusedIndex + 1) % itemCount
301
+ setFocusedIndex(nextIndex)
302
+ const nextItem = items[nextIndex]
303
+ if (nextItem) {
304
+ scrollToItem(nextItem)
305
+ }
325
306
  } else if (evt.name === 'up') {
326
- setFocusedIndex((prev) => {
327
- const nextIndex = (prev - 1 + itemCount) % itemCount
328
-
329
- // Update offset if we're going above the visible range
330
- if (nextIndex < offset) {
331
- setOffset(Math.max(0, nextIndex))
332
- } else if (nextIndex >= offset + itemsPerPage) {
333
- // Wrapped to end
334
- setOffset(Math.max(0, itemCount - itemsPerPage))
335
- }
336
-
337
- return nextIndex
338
- })
307
+ const nextIndex = (focusedIndex - 1 + itemCount) % itemCount
308
+ setFocusedIndex(nextIndex)
309
+ const nextItem = items[nextIndex]
310
+ if (nextItem) {
311
+ scrollToItem(nextItem)
312
+ }
339
313
  } else if (evt.name === 'return' || evt.name === 'space') {
340
314
  // Toggle selection of current focused item
341
315
  const entries = Object.entries(descendantsContext.map.current)
@@ -357,21 +331,41 @@ const DropdownContent = ({
357
331
  } else {
358
332
  navigateToNext()
359
333
  }
334
+ return
360
335
  }
361
- })
362
336
 
363
- // Update active item count when descendants change
364
- useLayoutEffect(() => {
365
- let itemCount = 0
366
- const entries = Object.entries(descendantsContext.map.current)
337
+ // Type-ahead search
338
+ if (
339
+ evt.name.length === 1 &&
340
+ /^[a-zA-Z0-9]$/.test(evt.name) &&
341
+ !evt.ctrl &&
342
+ !evt.meta &&
343
+ !evt.option
344
+ ) {
345
+ if (typeAheadTimeoutRef.current) {
346
+ clearTimeout(typeAheadTimeoutRef.current)
347
+ }
348
+
349
+ typeAheadTextRef.current += evt.name.toLowerCase()
350
+ const searchText = typeAheadTextRef.current
367
351
 
368
- entries.forEach(([id, item]) => {
369
- if (item.index !== -1) {
370
- itemCount++
352
+ const matchingItem =
353
+ items.find((item) => {
354
+ return item.props?.title?.toLowerCase().startsWith(searchText)
355
+ }) ||
356
+ items.find((item) => {
357
+ return item.props?.title?.toLowerCase().includes(searchText)
358
+ })
359
+
360
+ if (matchingItem) {
361
+ setFocusedIndex(matchingItem.index)
362
+ scrollToItem(matchingItem)
371
363
  }
372
- })
373
364
 
374
- setItemsCount(itemCount)
365
+ typeAheadTimeoutRef.current = setTimeout(() => {
366
+ typeAheadTextRef.current = ''
367
+ }, 300)
368
+ }
375
369
  })
376
370
 
377
371
  // Initialize selected titles from field value only once when descendants are loaded
@@ -405,27 +399,30 @@ const DropdownContent = ({
405
399
  const contextValue: FormDropdownContextValue = {
406
400
  focusedIndex,
407
401
  descendantsContext,
408
- offset,
409
- itemsPerPage,
410
402
  isFocused,
411
403
  handleSelect,
412
404
  field,
413
405
  props,
406
+ scrollBoxRef,
414
407
  }
415
408
 
416
409
  return (
417
410
  <FormDropdownDescendantsProvider value={descendantsContext}>
418
411
  <FormDropdownContext.Provider value={contextValue}>
419
412
  <box ref={elementRef} flexDirection='column'>
420
- <WithLeftBorder withDiamond isFocused={isFocused}>
421
- <text
422
- fg={isFocused ? Theme.primary : Theme.text}
413
+ <WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
414
+ <box
423
415
  onMouseDown={() => {
424
416
  setFocusedField(props.id)
425
417
  }}
426
418
  >
427
- {props.title}
428
- </text>
419
+ <LoadingText
420
+ isLoading={isFocused && focusContext.isLoading}
421
+ color={isFocused ? Theme.primary : Theme.text}
422
+ >
423
+ {props.title || ''}
424
+ </LoadingText>
425
+ </box>
429
426
  </WithLeftBorder>
430
427
  <WithLeftBorder isFocused={isFocused}>
431
428
  <text
@@ -440,12 +437,27 @@ const DropdownContent = ({
440
437
  : props.placeholder || 'Select...'}
441
438
  </text>
442
439
  </WithLeftBorder>
443
- {props.children}
444
- {itemsCount > itemsPerPage && (
445
- <WithLeftBorder isFocused={isFocused}>
446
- <text fg={Theme.textMuted}>↑↓ to see more options</text>
447
- </WithLeftBorder>
448
- )}
440
+
441
+ <scrollbox
442
+ ref={scrollBoxRef}
443
+ maxHeight={5}
444
+ flexShrink={1}
445
+ style={{
446
+ rootOptions: {
447
+ flexShrink: 1,
448
+ },
449
+ viewportOptions: {
450
+ flexShrink: 1,
451
+ },
452
+ scrollbarOptions: {
453
+ visible: false,
454
+ },
455
+ }}
456
+ >
457
+ {props.children}
458
+ </scrollbox>
459
+ <WithLeftBorder children={<box />} isFocused={isFocused} />
460
+
449
461
  {(fieldState.error || props.error) && (
450
462
  <WithLeftBorder isFocused={isFocused}>
451
463
  <text fg={Theme.error}>
@@ -1,151 +1,133 @@
1
1
  import React, { useState } from 'react'
2
2
  import { useQuery } from '@tanstack/react-query'
3
3
  import { Theme } from 'termcast/src/theme'
4
- import { TextareaRenderable, RGBA } from '@opentui/core'
5
- import { searchFiles, parsePath } from '../../utils/file-system'
4
+ import { TextareaRenderable } from '@opentui/core'
5
+ import { listAllFiles } from '../../utils/file-system'
6
6
  import { useKeyboard } from '@opentui/react'
7
7
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
8
8
 
9
- const backgroundPanel = RGBA.fromHex(Theme.backgroundPanel)
10
- const primary = RGBA.fromHex(Theme.primary)
11
- const border = RGBA.fromHex(Theme.border)
12
-
13
- export interface FileAutocompleteProps {
9
+ export interface FileAutocompleteDialogProps {
14
10
  onSelect: (path: string) => void
15
- visible: boolean
16
- onVisibilityChange: (visible: boolean) => void
11
+ onClose: () => void
17
12
  inputRef: React.RefObject<TextareaRenderable | null>
18
- anchorRef: React.RefObject<any>
19
- searchTrigger: number
20
13
  canChooseFiles?: boolean
21
14
  canChooseDirectories?: boolean
22
15
  initialDirectory?: string
23
16
  }
24
17
 
25
- export const FileAutocomplete = ({
18
+ function fuzzyMatch(text: string, query: string): boolean {
19
+ if (!query) return true
20
+ const lowerText = text.toLowerCase()
21
+ const lowerQuery = query.toLowerCase()
22
+
23
+ let queryIndex = 0
24
+ for (let i = 0; i < lowerText.length && queryIndex < lowerQuery.length; i++) {
25
+ if (lowerText[i] === lowerQuery[queryIndex]) {
26
+ queryIndex++
27
+ }
28
+ }
29
+ return queryIndex === lowerQuery.length
30
+ }
31
+
32
+ export const FileAutocompleteDialog = ({
26
33
  onSelect,
27
- visible,
28
- onVisibilityChange,
34
+ onClose,
29
35
  inputRef,
30
- anchorRef,
31
- searchTrigger,
32
36
  canChooseFiles = true,
33
37
  canChooseDirectories = false,
34
38
  initialDirectory,
35
- }: FileAutocompleteProps): any => {
39
+ }: FileAutocompleteDialogProps): any => {
36
40
  const [selectedIndex, setSelectedIndex] = useState(0)
41
+ const [filter, setFilter] = useState(() => inputRef.current?.plainText || '')
37
42
  const isInFocus = useIsInFocus()
38
43
 
39
- const inputValue = inputRef.current?.plainText || ''
40
- const defaultBasePath = initialDirectory || '.'
41
- const { basePath, prefix } = inputValue
42
- ? parsePath(inputValue)
43
- : { basePath: defaultBasePath, prefix: '' }
44
-
45
- const { data: items = [], isLoading: loading } = useQuery({
46
- queryKey: ['file-autocomplete', basePath, prefix, searchTrigger, canChooseFiles, canChooseDirectories],
44
+ const { data: allFiles = [], isLoading } = useQuery({
45
+ queryKey: ['file-list', initialDirectory, canChooseFiles, canChooseDirectories],
47
46
  queryFn: async () => {
48
- const results = await searchFiles(basePath, prefix)
49
- const filtered = results.filter((item) => {
50
- if (item.isDirectory) return true
47
+ // Always include directories in the listing, filter afterwards
48
+ const files = await listAllFiles({
49
+ basePath: initialDirectory || '.',
50
+ maxDepth: 4,
51
+ maxFiles: 500,
52
+ includeDirectories: true,
53
+ })
54
+
55
+ // Filter based on canChooseFiles/canChooseDirectories
56
+ return files.filter((f) => {
57
+ const isDir = f.endsWith('/')
58
+ if (isDir) {
59
+ return canChooseDirectories
60
+ }
51
61
  return canChooseFiles
52
62
  })
53
- setSelectedIndex(0)
54
- return filtered
55
63
  },
56
- enabled: visible,
57
64
  })
58
65
 
59
- const directoryOnlyMode = canChooseDirectories && !canChooseFiles
66
+ // Filter files based on current input
67
+ const filteredFiles = allFiles.filter((file) => fuzzyMatch(file, filter))
68
+ const visibleFiles = filteredFiles.slice(0, 10)
60
69
 
61
70
  useKeyboard((evt) => {
62
- if (!visible || !isInFocus) return
71
+ if (!isInFocus) return
63
72
 
64
73
  if (evt.name === 'up') {
65
- setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1))
74
+ setSelectedIndex((prev) => (prev > 0 ? prev - 1 : visibleFiles.length - 1))
66
75
  } else if (evt.name === 'down') {
67
- setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0))
68
- } else if (evt.name === 'escape') {
69
- onVisibilityChange(false)
70
- } else if (evt.name === 'return') {
71
- if (items[selectedIndex]) {
72
- const value = inputRef.current?.plainText || ''
73
- const selectedItem = items[selectedIndex]
74
- const newValue =
75
- value.substring(0, value.lastIndexOf('/') + 1) + selectedItem.name
76
-
77
- if (selectedItem.isDirectory) {
78
- const fullPath = newValue + '/'
79
- inputRef.current?.setText(fullPath)
80
- inputRef.current!.cursorOffset = fullPath.length
81
- } else {
82
- onSelect(newValue)
83
- onVisibilityChange(false)
84
- }
76
+ setSelectedIndex((prev) => (prev < visibleFiles.length - 1 ? prev + 1 : 0))
77
+ } else if (evt.name === 'return' || evt.name === 'tab') {
78
+ const selected = visibleFiles[selectedIndex]
79
+ if (selected) {
80
+ const path = selected.endsWith('/') ? selected.slice(0, -1) : selected
81
+ inputRef.current?.setText(path)
82
+ onSelect(path)
83
+ onClose()
85
84
  }
86
- } else if (evt.name === 'right') {
87
- if (items[selectedIndex]) {
88
- const value = inputRef.current?.plainText || ''
89
- const selectedItem = items[selectedIndex]
90
- const newValue =
91
- value.substring(0, value.lastIndexOf('/') + 1) + selectedItem.name
92
- onSelect(newValue)
93
- onVisibilityChange(false)
85
+ } else if (evt.name === 'backspace') {
86
+ if (filter.length > 0) {
87
+ const newFilter = filter.slice(0, -1)
88
+ setFilter(newFilter)
89
+ setSelectedIndex(0)
94
90
  }
91
+ } else if (evt.name && evt.name.length === 1 && !evt.ctrl && !evt.meta) {
92
+ // Single character input
93
+ const newFilter = filter + evt.name
94
+ setFilter(newFilter)
95
+ setSelectedIndex(0)
95
96
  }
96
97
  })
97
98
 
98
- if (!visible || items.length === 0) return null
99
-
100
- const anchorElement = anchorRef.current
101
- if (!anchorElement) return null
102
-
103
- const maxVisible = 8
104
- const visibleItems = items.slice(0, maxVisible)
105
- const displayHeight = visibleItems.length + 1
106
-
107
- const contentWidth = anchorElement.width - 2
108
-
109
- const hintText = directoryOnlyMode
110
- ? '↑↓ navigate ⏎ open folder → select folder esc close'
111
- : '↑↓ navigate ⏎ open/select → select esc close'
99
+ const hintText = '↑↓ navigate ⏎/tab select esc close'
112
100
 
113
101
  return (
114
- <box
115
- position='absolute'
116
- top={anchorElement.y - displayHeight - 2}
117
- left={anchorElement.x}
118
- width={anchorElement.width}
119
- height={displayHeight + 2}
120
- zIndex={1000}
121
- borderStyle='single'
122
- borderColor={border}
123
- backgroundColor={backgroundPanel}
124
- shouldFill
125
- >
126
- <box flexDirection='column' backgroundColor={backgroundPanel} shouldFill>
127
- {loading ? (
128
- <text fg={Theme.textMuted}> Loading...</text>
129
- ) : (
130
- <>
131
- {visibleItems.map((item, index) => {
132
- const icon = item.isDirectory ? '📁 ' : '📄 '
133
- const text = ' ' + icon + item.name
134
- const padded = text.padEnd(contentWidth, ' ')
135
- return (
136
- <text
137
- key={item.path}
138
- fg={index === selectedIndex ? Theme.background : Theme.text}
139
- bg={index === selectedIndex ? Theme.primary : Theme.backgroundPanel}
140
- >
141
- {padded}
142
- </text>
143
- )
144
- })}
145
- <text fg={Theme.textMuted}> {hintText}</text>
146
- </>
147
- )}
102
+ <box flexDirection='column' paddingLeft={1} paddingRight={1}>
103
+ <box flexDirection='row'>
104
+ <text fg={Theme.textMuted}>Filter: </text>
105
+ <text fg={Theme.primary}>{filter || '(type to filter)'}</text>
148
106
  </box>
107
+ <box height={1} />
108
+ {isLoading ? (
109
+ <text fg={Theme.textMuted}>Loading files...</text>
110
+ ) : visibleFiles.length === 0 ? (
111
+ <text fg={Theme.textMuted}>No files found</text>
112
+ ) : (
113
+ <>
114
+ {visibleFiles.map((file, index) => {
115
+ const isDir = file.endsWith('/')
116
+ const icon = isDir ? '📁 ' : '📄 '
117
+ return (
118
+ <text
119
+ key={file}
120
+ fg={index === selectedIndex ? Theme.background : Theme.text}
121
+ bg={index === selectedIndex ? Theme.primary : Theme.backgroundPanel}
122
+ >
123
+ {' '}{icon}{file}
124
+ </text>
125
+ )
126
+ })}
127
+ </>
128
+ )}
129
+ <box height={1} />
130
+ <text fg={Theme.textMuted}>{hintText}</text>
149
131
  </box>
150
132
  )
151
133
  }