termcast 1.3.9

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 (699) hide show
  1. package/dist/action-utils.d.ts +25 -0
  2. package/dist/action-utils.d.ts.map +1 -0
  3. package/dist/action-utils.js +209 -0
  4. package/dist/action-utils.js.map +1 -0
  5. package/dist/ai.d.ts +104 -0
  6. package/dist/ai.d.ts.map +1 -0
  7. package/dist/ai.js +135 -0
  8. package/dist/ai.js.map +1 -0
  9. package/dist/apis/ai.d.ts +104 -0
  10. package/dist/apis/ai.d.ts.map +1 -0
  11. package/dist/apis/ai.js +135 -0
  12. package/dist/apis/ai.js.map +1 -0
  13. package/dist/apis/cache.d.ts +84 -0
  14. package/dist/apis/cache.d.ts.map +1 -0
  15. package/dist/apis/cache.js +307 -0
  16. package/dist/apis/cache.js.map +1 -0
  17. package/dist/apis/cache.test.d.ts +2 -0
  18. package/dist/apis/cache.test.d.ts.map +1 -0
  19. package/dist/apis/cache.test.js +246 -0
  20. package/dist/apis/cache.test.js.map +1 -0
  21. package/dist/apis/clipboard.d.ts +36 -0
  22. package/dist/apis/clipboard.d.ts.map +1 -0
  23. package/dist/apis/clipboard.js +154 -0
  24. package/dist/apis/clipboard.js.map +1 -0
  25. package/dist/apis/environment.d.ts +63 -0
  26. package/dist/apis/environment.d.ts.map +1 -0
  27. package/dist/apis/environment.js +189 -0
  28. package/dist/apis/environment.js.map +1 -0
  29. package/dist/apis/hud.d.ts +7 -0
  30. package/dist/apis/hud.d.ts.map +1 -0
  31. package/dist/apis/hud.js +45 -0
  32. package/dist/apis/hud.js.map +1 -0
  33. package/dist/apis/localstorage.d.ts +13 -0
  34. package/dist/apis/localstorage.d.ts.map +1 -0
  35. package/dist/apis/localstorage.js +190 -0
  36. package/dist/apis/localstorage.js.map +1 -0
  37. package/dist/apis/localstorage.test.d.ts +2 -0
  38. package/dist/apis/localstorage.test.d.ts.map +1 -0
  39. package/dist/apis/localstorage.test.js +131 -0
  40. package/dist/apis/localstorage.test.js.map +1 -0
  41. package/dist/apis/oauth.d.ts +142 -0
  42. package/dist/apis/oauth.d.ts.map +1 -0
  43. package/dist/apis/oauth.js +551 -0
  44. package/dist/apis/oauth.js.map +1 -0
  45. package/dist/apis/preferences.d.ts +23 -0
  46. package/dist/apis/preferences.d.ts.map +1 -0
  47. package/dist/apis/preferences.js +105 -0
  48. package/dist/apis/preferences.js.map +1 -0
  49. package/dist/apis/toast.d.ts +81 -0
  50. package/dist/apis/toast.d.ts.map +1 -0
  51. package/dist/apis/toast.js +275 -0
  52. package/dist/apis/toast.js.map +1 -0
  53. package/dist/apis/toast.test.d.ts +2 -0
  54. package/dist/apis/toast.test.d.ts.map +1 -0
  55. package/dist/apis/toast.test.js +67 -0
  56. package/dist/apis/toast.test.js.map +1 -0
  57. package/dist/apis/window.d.ts +12 -0
  58. package/dist/apis/window.d.ts.map +1 -0
  59. package/dist/apis/window.js +47 -0
  60. package/dist/apis/window.js.map +1 -0
  61. package/dist/build.d.ts +15 -0
  62. package/dist/build.d.ts.map +1 -0
  63. package/dist/build.js +213 -0
  64. package/dist/build.js.map +1 -0
  65. package/dist/build.test.d.ts +2 -0
  66. package/dist/build.test.d.ts.map +1 -0
  67. package/dist/build.test.js +73 -0
  68. package/dist/build.test.js.map +1 -0
  69. package/dist/cache.d.ts +32 -0
  70. package/dist/cache.d.ts.map +1 -0
  71. package/dist/cache.js +205 -0
  72. package/dist/cache.js.map +1 -0
  73. package/dist/cache.test.d.ts +2 -0
  74. package/dist/cache.test.d.ts.map +1 -0
  75. package/dist/cache.test.js +246 -0
  76. package/dist/cache.test.js.map +1 -0
  77. package/dist/cli.d.ts +2 -0
  78. package/dist/cli.d.ts.map +1 -0
  79. package/dist/cli.js +278 -0
  80. package/dist/cli.js.map +1 -0
  81. package/dist/clipboard.d.ts +36 -0
  82. package/dist/clipboard.d.ts.map +1 -0
  83. package/dist/clipboard.js +154 -0
  84. package/dist/clipboard.js.map +1 -0
  85. package/dist/colors.d.ts +15 -0
  86. package/dist/colors.d.ts.map +1 -0
  87. package/dist/colors.js +13 -0
  88. package/dist/colors.js.map +1 -0
  89. package/dist/components/actions.d.ts +120 -0
  90. package/dist/components/actions.d.ts.map +1 -0
  91. package/dist/components/actions.js +371 -0
  92. package/dist/components/actions.js.map +1 -0
  93. package/dist/components/alert.d.ts +25 -0
  94. package/dist/components/alert.d.ts.map +1 -0
  95. package/dist/components/alert.js +99 -0
  96. package/dist/components/alert.js.map +1 -0
  97. package/dist/components/detail.d.ts +65 -0
  98. package/dist/components/detail.d.ts.map +1 -0
  99. package/dist/components/detail.js +147 -0
  100. package/dist/components/detail.js.map +1 -0
  101. package/dist/components/dropdown.d.ts +40 -0
  102. package/dist/components/dropdown.d.ts.map +1 -0
  103. package/dist/components/dropdown.js +202 -0
  104. package/dist/components/dropdown.js.map +1 -0
  105. package/dist/components/extension-preferences.d.ts +8 -0
  106. package/dist/components/extension-preferences.d.ts.map +1 -0
  107. package/dist/components/extension-preferences.js +139 -0
  108. package/dist/components/extension-preferences.js.map +1 -0
  109. package/dist/components/form/assign-components.d.ts +2 -0
  110. package/dist/components/form/assign-components.d.ts.map +1 -0
  111. package/dist/components/form/assign-components.js +22 -0
  112. package/dist/components/form/assign-components.js.map +1 -0
  113. package/dist/components/form/checkbox.d.ts +7 -0
  114. package/dist/components/form/checkbox.d.ts.map +1 -0
  115. package/dist/components/form/checkbox.js +46 -0
  116. package/dist/components/form/checkbox.js.map +1 -0
  117. package/dist/components/form/date-picker.d.ts +18 -0
  118. package/dist/components/form/date-picker.d.ts.map +1 -0
  119. package/dist/components/form/date-picker.js +69 -0
  120. package/dist/components/form/date-picker.js.map +1 -0
  121. package/dist/components/form/description.d.ts +7 -0
  122. package/dist/components/form/description.d.ts.map +1 -0
  123. package/dist/components/form/description.js +8 -0
  124. package/dist/components/form/description.js.map +1 -0
  125. package/dist/components/form/dropdown.d.ts +25 -0
  126. package/dist/components/form/dropdown.d.ts.map +1 -0
  127. package/dist/components/form/dropdown.js +286 -0
  128. package/dist/components/form/dropdown.js.map +1 -0
  129. package/dist/components/form/file-autocomplete.d.ts +12 -0
  130. package/dist/components/form/file-autocomplete.d.ts.map +1 -0
  131. package/dist/components/form/file-autocomplete.js +84 -0
  132. package/dist/components/form/file-autocomplete.js.map +1 -0
  133. package/dist/components/form/file-picker.d.ts +31 -0
  134. package/dist/components/form/file-picker.d.ts.map +1 -0
  135. package/dist/components/form/file-picker.js +113 -0
  136. package/dist/components/form/file-picker.js.map +1 -0
  137. package/dist/components/form/form-end.d.ts +2 -0
  138. package/dist/components/form/form-end.d.ts.map +1 -0
  139. package/dist/components/form/form-end.js +6 -0
  140. package/dist/components/form/form-end.js.map +1 -0
  141. package/dist/components/form/form-type-only.d.ts +174 -0
  142. package/dist/components/form/form-type-only.d.ts.map +1 -0
  143. package/dist/components/form/form-type-only.js +2 -0
  144. package/dist/components/form/form-type-only.js.map +1 -0
  145. package/dist/components/form/index.d.ts +46 -0
  146. package/dist/components/form/index.d.ts.map +1 -0
  147. package/dist/components/form/index.js +106 -0
  148. package/dist/components/form/index.js.map +1 -0
  149. package/dist/components/form/password-field.d.ts +7 -0
  150. package/dist/components/form/password-field.d.ts.map +1 -0
  151. package/dist/components/form/password-field.js +34 -0
  152. package/dist/components/form/password-field.js.map +1 -0
  153. package/dist/components/form/separator.d.ts +2 -0
  154. package/dist/components/form/separator.d.ts.map +1 -0
  155. package/dist/components/form/separator.js +8 -0
  156. package/dist/components/form/separator.js.map +1 -0
  157. package/dist/components/form/tagpicker.d.ts +134 -0
  158. package/dist/components/form/tagpicker.d.ts.map +1 -0
  159. package/dist/components/form/tagpicker.js +79 -0
  160. package/dist/components/form/tagpicker.js.map +1 -0
  161. package/dist/components/form/text-area.d.ts +8 -0
  162. package/dist/components/form/text-area.d.ts.map +1 -0
  163. package/dist/components/form/text-area.js +26 -0
  164. package/dist/components/form/text-area.js.map +1 -0
  165. package/dist/components/form/text-field.d.ts +7 -0
  166. package/dist/components/form/text-field.d.ts.map +1 -0
  167. package/dist/components/form/text-field.js +28 -0
  168. package/dist/components/form/text-field.js.map +1 -0
  169. package/dist/components/form/types.d.ts +43 -0
  170. package/dist/components/form/types.d.ts.map +1 -0
  171. package/dist/components/form/types.js +2 -0
  172. package/dist/components/form/types.js.map +1 -0
  173. package/dist/components/form/use-form-handling.d.ts +4 -0
  174. package/dist/components/form/use-form-handling.d.ts.map +1 -0
  175. package/dist/components/form/use-form-handling.js +37 -0
  176. package/dist/components/form/use-form-handling.js.map +1 -0
  177. package/dist/components/form/use-form-navigation.d.ts +8 -0
  178. package/dist/components/form/use-form-navigation.d.ts.map +1 -0
  179. package/dist/components/form/use-form-navigation.js +58 -0
  180. package/dist/components/form/use-form-navigation.js.map +1 -0
  181. package/dist/components/form/with-left-border.d.ts +17 -0
  182. package/dist/components/form/with-left-border.d.ts.map +1 -0
  183. package/dist/components/form/with-left-border.js +10 -0
  184. package/dist/components/form/with-left-border.js.map +1 -0
  185. package/dist/components/icon.d.ts +9 -0
  186. package/dist/components/icon.d.ts.map +1 -0
  187. package/dist/components/icon.js +485 -0
  188. package/dist/components/icon.js.map +1 -0
  189. package/dist/components/image.d.ts +19 -0
  190. package/dist/components/image.d.ts.map +1 -0
  191. package/dist/components/image.js +32 -0
  192. package/dist/components/image.js.map +1 -0
  193. package/dist/components/list.d.ts +234 -0
  194. package/dist/components/list.d.ts.map +1 -0
  195. package/dist/components/list.js +667 -0
  196. package/dist/components/list.js.map +1 -0
  197. package/dist/components/loading-bar.d.ts +8 -0
  198. package/dist/components/loading-bar.d.ts.map +1 -0
  199. package/dist/components/loading-bar.js +107 -0
  200. package/dist/components/loading-bar.js.map +1 -0
  201. package/dist/components/menubar-extra.d.ts +68 -0
  202. package/dist/components/menubar-extra.d.ts.map +1 -0
  203. package/dist/components/menubar-extra.js +39 -0
  204. package/dist/components/menubar-extra.js.map +1 -0
  205. package/dist/descendants.d.ts +25 -0
  206. package/dist/descendants.d.ts.map +1 -0
  207. package/dist/descendants.js +81 -0
  208. package/dist/descendants.js.map +1 -0
  209. package/dist/dev-ui.d.ts +7 -0
  210. package/dist/dev-ui.d.ts.map +1 -0
  211. package/dist/dev-ui.js +118 -0
  212. package/dist/dev-ui.js.map +1 -0
  213. package/dist/e2e-node.d.ts +63 -0
  214. package/dist/e2e-node.d.ts.map +1 -0
  215. package/dist/e2e-node.js +255 -0
  216. package/dist/e2e-node.js.map +1 -0
  217. package/dist/e2e.d.ts +39 -0
  218. package/dist/e2e.d.ts.map +1 -0
  219. package/dist/e2e.js +127 -0
  220. package/dist/e2e.js.map +1 -0
  221. package/dist/environment.d.ts +63 -0
  222. package/dist/environment.d.ts.map +1 -0
  223. package/dist/environment.js +189 -0
  224. package/dist/environment.js.map +1 -0
  225. package/dist/examples/action-show-in-finder.d.ts +2 -0
  226. package/dist/examples/action-show-in-finder.d.ts.map +1 -0
  227. package/dist/examples/action-show-in-finder.js +13 -0
  228. package/dist/examples/action-show-in-finder.js.map +1 -0
  229. package/dist/examples/datepicker.d.ts +2 -0
  230. package/dist/examples/datepicker.d.ts.map +1 -0
  231. package/dist/examples/datepicker.js +344 -0
  232. package/dist/examples/datepicker.js.map +1 -0
  233. package/dist/examples/environment-test.d.ts +2 -0
  234. package/dist/examples/environment-test.d.ts.map +1 -0
  235. package/dist/examples/environment-test.js +28 -0
  236. package/dist/examples/environment-test.js.map +1 -0
  237. package/dist/examples/error-boundary.d.ts +6 -0
  238. package/dist/examples/error-boundary.d.ts.map +1 -0
  239. package/dist/examples/error-boundary.js +67 -0
  240. package/dist/examples/error-boundary.js.map +1 -0
  241. package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
  242. package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
  243. package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
  244. package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
  245. package/dist/examples/form-basic.d.ts +2 -0
  246. package/dist/examples/form-basic.d.ts.map +1 -0
  247. package/dist/examples/form-basic.js +21 -0
  248. package/dist/examples/form-basic.js.map +1 -0
  249. package/dist/examples/form-basic.vitest.d.ts +2 -0
  250. package/dist/examples/form-basic.vitest.d.ts.map +1 -0
  251. package/dist/examples/form-basic.vitest.js +995 -0
  252. package/dist/examples/form-basic.vitest.js.map +1 -0
  253. package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
  254. package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
  255. package/dist/examples/form-dropdown-with-sections.js +13 -0
  256. package/dist/examples/form-dropdown-with-sections.js.map +1 -0
  257. package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
  258. package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
  259. package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
  260. package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
  261. package/dist/examples/form-dropdown.d.ts +2 -0
  262. package/dist/examples/form-dropdown.d.ts.map +1 -0
  263. package/dist/examples/form-dropdown.js +13 -0
  264. package/dist/examples/form-dropdown.js.map +1 -0
  265. package/dist/examples/form-dropdown.vitest.d.ts +2 -0
  266. package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
  267. package/dist/examples/form-dropdown.vitest.js +722 -0
  268. package/dist/examples/form-dropdown.vitest.js.map +1 -0
  269. package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
  270. package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
  271. package/dist/examples/form-multiselect-dropdown.js +13 -0
  272. package/dist/examples/form-multiselect-dropdown.js.map +1 -0
  273. package/dist/examples/form-tagpicker.d.ts +2 -0
  274. package/dist/examples/form-tagpicker.d.ts.map +1 -0
  275. package/dist/examples/form-tagpicker.js +13 -0
  276. package/dist/examples/form-tagpicker.js.map +1 -0
  277. package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
  278. package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
  279. package/dist/examples/form-tagpicker.vitest.js +491 -0
  280. package/dist/examples/form-tagpicker.vitest.js.map +1 -0
  281. package/dist/examples/internal/descendants-filtering.d.ts +2 -0
  282. package/dist/examples/internal/descendants-filtering.d.ts.map +1 -0
  283. package/dist/examples/internal/descendants-filtering.js +144 -0
  284. package/dist/examples/internal/descendants-filtering.js.map +1 -0
  285. package/dist/examples/internal/descendants.d.ts +2 -0
  286. package/dist/examples/internal/descendants.d.ts.map +1 -0
  287. package/dist/examples/internal/descendants.js +134 -0
  288. package/dist/examples/internal/descendants.js.map +1 -0
  289. package/dist/examples/internal/nested-boxes.d.ts +2 -0
  290. package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
  291. package/dist/examples/internal/nested-boxes.js +7 -0
  292. package/dist/examples/internal/nested-boxes.js.map +1 -0
  293. package/dist/examples/internal/scrollbox-demo.d.ts +2 -0
  294. package/dist/examples/internal/scrollbox-demo.d.ts.map +1 -0
  295. package/dist/examples/internal/scrollbox-demo.js +104 -0
  296. package/dist/examples/internal/scrollbox-demo.js.map +1 -0
  297. package/dist/examples/internal/simple-dialog.d.ts +2 -0
  298. package/dist/examples/internal/simple-dialog.d.ts.map +1 -0
  299. package/dist/examples/internal/simple-dialog.js +43 -0
  300. package/dist/examples/internal/simple-dialog.js.map +1 -0
  301. package/dist/examples/internal/text-stacking.d.ts +2 -0
  302. package/dist/examples/internal/text-stacking.d.ts.map +1 -0
  303. package/dist/examples/internal/text-stacking.js +53 -0
  304. package/dist/examples/internal/text-stacking.js.map +1 -0
  305. package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
  306. package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
  307. package/dist/examples/internal/unicode-square-repro.js +7 -0
  308. package/dist/examples/internal/unicode-square-repro.js.map +1 -0
  309. package/dist/examples/list-dropdown-default.d.ts +2 -0
  310. package/dist/examples/list-dropdown-default.d.ts.map +1 -0
  311. package/dist/examples/list-dropdown-default.js +14 -0
  312. package/dist/examples/list-dropdown-default.js.map +1 -0
  313. package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
  314. package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
  315. package/dist/examples/list-dropdown-default.vitest.js +164 -0
  316. package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
  317. package/dist/examples/list-fetch-data.d.ts +2 -0
  318. package/dist/examples/list-fetch-data.d.ts.map +1 -0
  319. package/dist/examples/list-fetch-data.js +87 -0
  320. package/dist/examples/list-fetch-data.js.map +1 -0
  321. package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
  322. package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
  323. package/dist/examples/list-fetch-data.vitest.js +103 -0
  324. package/dist/examples/list-fetch-data.vitest.js.map +1 -0
  325. package/dist/examples/list-filter-navigation.d.ts +2 -0
  326. package/dist/examples/list-filter-navigation.d.ts.map +1 -0
  327. package/dist/examples/list-filter-navigation.js +8 -0
  328. package/dist/examples/list-filter-navigation.js.map +1 -0
  329. package/dist/examples/list-with-detail.d.ts +2 -0
  330. package/dist/examples/list-with-detail.d.ts.map +1 -0
  331. package/dist/examples/list-with-detail.js +94 -0
  332. package/dist/examples/list-with-detail.js.map +1 -0
  333. package/dist/examples/list-with-detail.vitest.d.ts +2 -0
  334. package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
  335. package/dist/examples/list-with-detail.vitest.js +438 -0
  336. package/dist/examples/list-with-detail.vitest.js.map +1 -0
  337. package/dist/examples/list-with-dropdown.d.ts +2 -0
  338. package/dist/examples/list-with-dropdown.d.ts.map +1 -0
  339. package/dist/examples/list-with-dropdown.js +43 -0
  340. package/dist/examples/list-with-dropdown.js.map +1 -0
  341. package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
  342. package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
  343. package/dist/examples/list-with-dropdown.vitest.js +297 -0
  344. package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
  345. package/dist/examples/list-with-sections.d.ts +2 -0
  346. package/dist/examples/list-with-sections.d.ts.map +1 -0
  347. package/dist/examples/list-with-sections.js +67 -0
  348. package/dist/examples/list-with-sections.js.map +1 -0
  349. package/dist/examples/list-with-sections.vitest.d.ts +2 -0
  350. package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
  351. package/dist/examples/list-with-sections.vitest.js +441 -0
  352. package/dist/examples/list-with-sections.vitest.js.map +1 -0
  353. package/dist/examples/miscellaneous.d.ts +2 -0
  354. package/dist/examples/miscellaneous.d.ts.map +1 -0
  355. package/dist/examples/miscellaneous.js +313 -0
  356. package/dist/examples/miscellaneous.js.map +1 -0
  357. package/dist/examples/nested-navigation.d.ts +2 -0
  358. package/dist/examples/nested-navigation.d.ts.map +1 -0
  359. package/dist/examples/nested-navigation.js +35 -0
  360. package/dist/examples/nested-navigation.js.map +1 -0
  361. package/dist/examples/preferences-test.d.ts +2 -0
  362. package/dist/examples/preferences-test.d.ts.map +1 -0
  363. package/dist/examples/preferences-test.js +43 -0
  364. package/dist/examples/preferences-test.js.map +1 -0
  365. package/dist/examples/simple-dropdown.d.ts +2 -0
  366. package/dist/examples/simple-dropdown.d.ts.map +1 -0
  367. package/dist/examples/simple-dropdown.js +15 -0
  368. package/dist/examples/simple-dropdown.js.map +1 -0
  369. package/dist/examples/simple-file-picker.d.ts +2 -0
  370. package/dist/examples/simple-file-picker.d.ts.map +1 -0
  371. package/dist/examples/simple-file-picker.js +21 -0
  372. package/dist/examples/simple-file-picker.js.map +1 -0
  373. package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
  374. package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
  375. package/dist/examples/simple-file-picker.vitest.js +277 -0
  376. package/dist/examples/simple-file-picker.vitest.js.map +1 -0
  377. package/dist/examples/simple-grid.d.ts +2 -0
  378. package/dist/examples/simple-grid.d.ts.map +1 -0
  379. package/dist/examples/simple-grid.js +51 -0
  380. package/dist/examples/simple-grid.js.map +1 -0
  381. package/dist/examples/simple-grid.vitest.d.ts +2 -0
  382. package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
  383. package/dist/examples/simple-grid.vitest.js +498 -0
  384. package/dist/examples/simple-grid.vitest.js.map +1 -0
  385. package/dist/examples/simple-hud.d.ts +2 -0
  386. package/dist/examples/simple-hud.d.ts.map +1 -0
  387. package/dist/examples/simple-hud.js +18 -0
  388. package/dist/examples/simple-hud.js.map +1 -0
  389. package/dist/examples/simple-list-search.d.ts +2 -0
  390. package/dist/examples/simple-list-search.d.ts.map +1 -0
  391. package/dist/examples/simple-list-search.js +26 -0
  392. package/dist/examples/simple-list-search.js.map +1 -0
  393. package/dist/examples/simple-list.d.ts +2 -0
  394. package/dist/examples/simple-list.d.ts.map +1 -0
  395. package/dist/examples/simple-list.js +50 -0
  396. package/dist/examples/simple-list.js.map +1 -0
  397. package/dist/examples/simple-navigation.d.ts +2 -0
  398. package/dist/examples/simple-navigation.d.ts.map +1 -0
  399. package/dist/examples/simple-navigation.js +36 -0
  400. package/dist/examples/simple-navigation.js.map +1 -0
  401. package/dist/examples/simple-navigation.vitest.d.ts +2 -0
  402. package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
  403. package/dist/examples/simple-navigation.vitest.js +522 -0
  404. package/dist/examples/simple-navigation.vitest.js.map +1 -0
  405. package/dist/examples/store.d.ts +2 -0
  406. package/dist/examples/store.d.ts.map +1 -0
  407. package/dist/examples/store.js +5 -0
  408. package/dist/examples/store.js.map +1 -0
  409. package/dist/examples/store.vitest.d.ts +2 -0
  410. package/dist/examples/store.vitest.d.ts.map +1 -0
  411. package/dist/examples/store.vitest.js +52 -0
  412. package/dist/examples/store.vitest.js.map +1 -0
  413. package/dist/examples/tanstack-demo.d.ts +2 -0
  414. package/dist/examples/tanstack-demo.d.ts.map +1 -0
  415. package/dist/examples/tanstack-demo.js +51 -0
  416. package/dist/examples/tanstack-demo.js.map +1 -0
  417. package/dist/examples/use-promise-demo.d.ts +2 -0
  418. package/dist/examples/use-promise-demo.d.ts.map +1 -0
  419. package/dist/examples/use-promise-demo.js +45 -0
  420. package/dist/examples/use-promise-demo.js.map +1 -0
  421. package/dist/extensions/dev.d.ts +7 -0
  422. package/dist/extensions/dev.d.ts.map +1 -0
  423. package/dist/extensions/dev.js +124 -0
  424. package/dist/extensions/dev.js.map +1 -0
  425. package/dist/extensions/home.d.ts +8 -0
  426. package/dist/extensions/home.d.ts.map +1 -0
  427. package/dist/extensions/home.js +184 -0
  428. package/dist/extensions/home.js.map +1 -0
  429. package/dist/extensions/store.d.ts +6 -0
  430. package/dist/extensions/store.d.ts.map +1 -0
  431. package/dist/extensions/store.js +203 -0
  432. package/dist/extensions/store.js.map +1 -0
  433. package/dist/globals.d.ts +9 -0
  434. package/dist/globals.d.ts.map +1 -0
  435. package/dist/globals.js +21 -0
  436. package/dist/globals.js.map +1 -0
  437. package/dist/home-command.d.ts +8 -0
  438. package/dist/home-command.d.ts.map +1 -0
  439. package/dist/home-command.js +181 -0
  440. package/dist/home-command.js.map +1 -0
  441. package/dist/hooks/hooks.test.d.ts +2 -0
  442. package/dist/hooks/hooks.test.d.ts.map +1 -0
  443. package/dist/hooks/hooks.test.js +37 -0
  444. package/dist/hooks/hooks.test.js.map +1 -0
  445. package/dist/hooks/index.d.ts +6 -0
  446. package/dist/hooks/index.d.ts.map +1 -0
  447. package/dist/hooks/index.js +6 -0
  448. package/dist/hooks/index.js.map +1 -0
  449. package/dist/hooks/use-action-panel.d.ts +16 -0
  450. package/dist/hooks/use-action-panel.d.ts.map +1 -0
  451. package/dist/hooks/use-action-panel.js +19 -0
  452. package/dist/hooks/use-action-panel.js.map +1 -0
  453. package/dist/hooks/use-id.d.ts +9 -0
  454. package/dist/hooks/use-id.d.ts.map +1 -0
  455. package/dist/hooks/use-id.js +17 -0
  456. package/dist/hooks/use-id.js.map +1 -0
  457. package/dist/hooks/use-unstable-ai.d.ts +9 -0
  458. package/dist/hooks/use-unstable-ai.d.ts.map +1 -0
  459. package/dist/hooks/use-unstable-ai.js +13 -0
  460. package/dist/hooks/use-unstable-ai.js.map +1 -0
  461. package/dist/hooks.d.ts +10 -0
  462. package/dist/hooks.d.ts.map +1 -0
  463. package/dist/hooks.js +20 -0
  464. package/dist/hooks.js.map +1 -0
  465. package/dist/hover-repro.d.ts +2 -0
  466. package/dist/hover-repro.d.ts.map +1 -0
  467. package/dist/hover-repro.js +20 -0
  468. package/dist/hover-repro.js.map +1 -0
  469. package/dist/index.d.ts +58 -0
  470. package/dist/index.d.ts.map +1 -0
  471. package/dist/index.js +71 -0
  472. package/dist/index.js.map +1 -0
  473. package/dist/internal/date-picker-widget.d.ts +9 -0
  474. package/dist/internal/date-picker-widget.d.ts.map +1 -0
  475. package/dist/internal/date-picker-widget.js +380 -0
  476. package/dist/internal/date-picker-widget.js.map +1 -0
  477. package/dist/internal/dialog.d.ts +21 -0
  478. package/dist/internal/dialog.d.ts.map +1 -0
  479. package/dist/internal/dialog.js +122 -0
  480. package/dist/internal/dialog.js.map +1 -0
  481. package/dist/internal/error-handler.d.ts +2 -0
  482. package/dist/internal/error-handler.d.ts.map +1 -0
  483. package/dist/internal/error-handler.js +29 -0
  484. package/dist/internal/error-handler.js.map +1 -0
  485. package/dist/internal/focus-context.d.ts +10 -0
  486. package/dist/internal/focus-context.d.ts.map +1 -0
  487. package/dist/internal/focus-context.js +12 -0
  488. package/dist/internal/focus-context.js.map +1 -0
  489. package/dist/internal/navigation.d.ts +18 -0
  490. package/dist/internal/navigation.d.ts.map +1 -0
  491. package/dist/internal/navigation.js +83 -0
  492. package/dist/internal/navigation.js.map +1 -0
  493. package/dist/internal/providers.d.ts +8 -0
  494. package/dist/internal/providers.d.ts.map +1 -0
  495. package/dist/internal/providers.js +262 -0
  496. package/dist/internal/providers.js.map +1 -0
  497. package/dist/localstorage.d.ts +13 -0
  498. package/dist/localstorage.d.ts.map +1 -0
  499. package/dist/localstorage.js +190 -0
  500. package/dist/localstorage.js.map +1 -0
  501. package/dist/localstorage.test.d.ts +2 -0
  502. package/dist/localstorage.test.d.ts.map +1 -0
  503. package/dist/localstorage.test.js +131 -0
  504. package/dist/localstorage.test.js.map +1 -0
  505. package/dist/logger.d.ts +7 -0
  506. package/dist/logger.d.ts.map +1 -0
  507. package/dist/logger.js +70 -0
  508. package/dist/logger.js.map +1 -0
  509. package/dist/oauth.d.ts +142 -0
  510. package/dist/oauth.d.ts.map +1 -0
  511. package/dist/oauth.js +551 -0
  512. package/dist/oauth.js.map +1 -0
  513. package/dist/package-json.d.ts +84 -0
  514. package/dist/package-json.d.ts.map +1 -0
  515. package/dist/package-json.js +77 -0
  516. package/dist/package-json.js.map +1 -0
  517. package/dist/preferences.d.ts +23 -0
  518. package/dist/preferences.d.ts.map +1 -0
  519. package/dist/preferences.js +105 -0
  520. package/dist/preferences.js.map +1 -0
  521. package/dist/preload.d.ts +2 -0
  522. package/dist/preload.d.ts.map +1 -0
  523. package/dist/preload.js +28 -0
  524. package/dist/preload.js.map +1 -0
  525. package/dist/state.d.ts +26 -0
  526. package/dist/state.d.ts.map +1 -0
  527. package/dist/state.js +18 -0
  528. package/dist/state.js.map +1 -0
  529. package/dist/store-api/download.d.ts +8 -0
  530. package/dist/store-api/download.d.ts.map +1 -0
  531. package/dist/store-api/download.js +37 -0
  532. package/dist/store-api/download.js.map +1 -0
  533. package/dist/store-api/download.test.d.ts +2 -0
  534. package/dist/store-api/download.test.d.ts.map +1 -0
  535. package/dist/store-api/download.test.js +36 -0
  536. package/dist/store-api/download.test.js.map +1 -0
  537. package/dist/store-api/extension.d.ts +87 -0
  538. package/dist/store-api/extension.d.ts.map +1 -0
  539. package/dist/store-api/extension.js +23 -0
  540. package/dist/store-api/extension.js.map +1 -0
  541. package/dist/store-api/extension.test.d.ts +2 -0
  542. package/dist/store-api/extension.test.d.ts.map +1 -0
  543. package/dist/store-api/extension.test.js +22 -0
  544. package/dist/store-api/extension.test.js.map +1 -0
  545. package/dist/store-api/search.d.ts +101 -0
  546. package/dist/store-api/search.d.ts.map +1 -0
  547. package/dist/store-api/search.js +29 -0
  548. package/dist/store-api/search.js.map +1 -0
  549. package/dist/store-api/search.test.d.ts +2 -0
  550. package/dist/store-api/search.test.d.ts.map +1 -0
  551. package/dist/store-api/search.test.js +45 -0
  552. package/dist/store-api/search.test.js.map +1 -0
  553. package/dist/store.d.ts +21 -0
  554. package/dist/store.d.ts.map +1 -0
  555. package/dist/store.js +84 -0
  556. package/dist/store.js.map +1 -0
  557. package/dist/theme.d.ts +20 -0
  558. package/dist/theme.d.ts.map +1 -0
  559. package/dist/theme.js +26 -0
  560. package/dist/theme.js.map +1 -0
  561. package/dist/toast.d.ts +44 -0
  562. package/dist/toast.d.ts.map +1 -0
  563. package/dist/toast.js +221 -0
  564. package/dist/toast.js.map +1 -0
  565. package/dist/utils/file-system.d.ts +11 -0
  566. package/dist/utils/file-system.d.ts.map +1 -0
  567. package/dist/utils/file-system.js +66 -0
  568. package/dist/utils/file-system.js.map +1 -0
  569. package/dist/utils.d.ts +234 -0
  570. package/dist/utils.d.ts.map +1 -0
  571. package/dist/utils.js +473 -0
  572. package/dist/utils.js.map +1 -0
  573. package/dist/utils.test.d.ts +2 -0
  574. package/dist/utils.test.d.ts.map +1 -0
  575. package/dist/utils.test.js +152 -0
  576. package/dist/utils.test.js.map +1 -0
  577. package/dist/window.d.ts +12 -0
  578. package/dist/window.d.ts.map +1 -0
  579. package/dist/window.js +48 -0
  580. package/dist/window.js.map +1 -0
  581. package/package.json +56 -0
  582. package/src/action-utils.tsx +207 -0
  583. package/src/apis/ai.tsx +177 -0
  584. package/src/apis/cache.test.ts +311 -0
  585. package/src/apis/cache.tsx +394 -0
  586. package/src/apis/clipboard.tsx +200 -0
  587. package/src/apis/environment.tsx +239 -0
  588. package/src/apis/hud.tsx +86 -0
  589. package/src/apis/localstorage.test.ts +164 -0
  590. package/src/apis/localstorage.tsx +215 -0
  591. package/src/apis/oauth.tsx +744 -0
  592. package/src/apis/preferences.tsx +140 -0
  593. package/src/apis/toast.tsx +388 -0
  594. package/src/apis/window.tsx +61 -0
  595. package/src/build.test.tsx +78 -0
  596. package/src/build.tsx +273 -0
  597. package/src/cli.tsx +328 -0
  598. package/src/colors.tsx +15 -0
  599. package/src/components/actions.tsx +718 -0
  600. package/src/components/alert.tsx +205 -0
  601. package/src/components/detail.tsx +359 -0
  602. package/src/components/dropdown.tsx +438 -0
  603. package/src/components/extension-preferences.tsx +269 -0
  604. package/src/components/form/assign-components.tsx +22 -0
  605. package/src/components/form/checkbox.tsx +96 -0
  606. package/src/components/form/date-picker.tsx +125 -0
  607. package/src/components/form/description.tsx +30 -0
  608. package/src/components/form/dropdown.tsx +512 -0
  609. package/src/components/form/file-autocomplete.tsx +141 -0
  610. package/src/components/form/file-picker.tsx +233 -0
  611. package/src/components/form/form-end.tsx +6 -0
  612. package/src/components/form/index.tsx +242 -0
  613. package/src/components/form/password-field.tsx +87 -0
  614. package/src/components/form/separator.tsx +15 -0
  615. package/src/components/form/tagpicker.tsx +245 -0
  616. package/src/components/form/text-area.tsx +82 -0
  617. package/src/components/form/text-field.tsx +82 -0
  618. package/src/components/form/types.tsx +49 -0
  619. package/src/components/form/use-form-navigation.tsx +65 -0
  620. package/src/components/form/with-left-border.tsx +53 -0
  621. package/src/components/icon.tsx +496 -0
  622. package/src/components/image.tsx +54 -0
  623. package/src/components/list.tsx +1564 -0
  624. package/src/components/loading-bar.tsx +141 -0
  625. package/src/components/menubar-extra.tsx +181 -0
  626. package/src/descendants.tsx +134 -0
  627. package/src/e2e-node.tsx +303 -0
  628. package/src/e2e.tsx +147 -0
  629. package/src/examples/action-show-in-finder.tsx +80 -0
  630. package/src/examples/environment-test.tsx +50 -0
  631. package/src/examples/error-boundary.tsx +217 -0
  632. package/src/examples/form-basic.tsx +122 -0
  633. package/src/examples/form-basic.vitest.tsx +1035 -0
  634. package/src/examples/form-dropdown.tsx +102 -0
  635. package/src/examples/form-dropdown.vitest.tsx +758 -0
  636. package/src/examples/form-tagpicker.tsx +66 -0
  637. package/src/examples/form-tagpicker.vitest.tsx +523 -0
  638. package/src/examples/internal/descendants-filtering.tsx +223 -0
  639. package/src/examples/internal/descendants.tsx +208 -0
  640. package/src/examples/internal/scrollbox-demo.tsx +149 -0
  641. package/src/examples/internal/simple-dialog.tsx +124 -0
  642. package/src/examples/internal/text-stacking.tsx +90 -0
  643. package/src/examples/list-dropdown-default.tsx +69 -0
  644. package/src/examples/list-dropdown-default.vitest.tsx +187 -0
  645. package/src/examples/list-fetch-data.tsx +123 -0
  646. package/src/examples/list-fetch-data.vitest.tsx +110 -0
  647. package/src/examples/list-with-detail.tsx +161 -0
  648. package/src/examples/list-with-detail.vitest.tsx +468 -0
  649. package/src/examples/list-with-dropdown.tsx +97 -0
  650. package/src/examples/list-with-dropdown.vitest.tsx +324 -0
  651. package/src/examples/list-with-sections.tsx +196 -0
  652. package/src/examples/list-with-sections.vitest.tsx +479 -0
  653. package/src/examples/miscellaneous.tsx +780 -0
  654. package/src/examples/nested-navigation.tsx +118 -0
  655. package/src/examples/preferences-test.tsx +82 -0
  656. package/src/examples/simple-dropdown.tsx +95 -0
  657. package/src/examples/simple-file-picker.tsx +75 -0
  658. package/src/examples/simple-file-picker.vitest.tsx +306 -0
  659. package/src/examples/simple-grid.tsx +149 -0
  660. package/src/examples/simple-grid.vitest.tsx +535 -0
  661. package/src/examples/simple-hud.tsx +60 -0
  662. package/src/examples/simple-list-search.tsx +93 -0
  663. package/src/examples/simple-list.tsx +149 -0
  664. package/src/examples/simple-navigation.tsx +89 -0
  665. package/src/examples/simple-navigation.vitest.tsx +571 -0
  666. package/src/examples/store.tsx +4 -0
  667. package/src/examples/store.vitest.tsx +59 -0
  668. package/src/examples/tanstack-demo.tsx +104 -0
  669. package/src/examples/use-promise-demo.tsx +96 -0
  670. package/src/extensions/dev.tsx +215 -0
  671. package/src/extensions/home.tsx +332 -0
  672. package/src/extensions/store.tsx +375 -0
  673. package/src/globals.ts +34 -0
  674. package/src/hooks/index.tsx +8 -0
  675. package/src/hooks/use-action-panel.tsx +28 -0
  676. package/src/hooks/use-id.tsx +19 -0
  677. package/src/hooks/use-unstable-ai.tsx +15 -0
  678. package/src/hooks.tsx +22 -0
  679. package/src/index.tsx +240 -0
  680. package/src/internal/date-picker-widget.tsx +500 -0
  681. package/src/internal/dialog.tsx +202 -0
  682. package/src/internal/error-handler.tsx +32 -0
  683. package/src/internal/focus-context.tsx +23 -0
  684. package/src/internal/navigation.tsx +149 -0
  685. package/src/internal/providers.tsx +430 -0
  686. package/src/logger.tsx +84 -0
  687. package/src/package-json.tsx +197 -0
  688. package/src/preload.tsx +32 -0
  689. package/src/state.tsx +49 -0
  690. package/src/store-api/download.test.tsx +38 -0
  691. package/src/store-api/download.tsx +52 -0
  692. package/src/store-api/extension.test.tsx +24 -0
  693. package/src/store-api/extension.tsx +123 -0
  694. package/src/store-api/search.test.tsx +50 -0
  695. package/src/store-api/search.tsx +146 -0
  696. package/src/theme.tsx +31 -0
  697. package/src/utils/file-system.ts +87 -0
  698. package/src/utils.test.tsx +204 -0
  699. package/src/utils.tsx +657 -0
@@ -0,0 +1,1564 @@
1
+ import React, {
2
+ ReactNode,
3
+ ReactElement,
4
+ Children,
5
+ isValidElement,
6
+ useState,
7
+ useEffect,
8
+ useRef,
9
+ Fragment,
10
+ useMemo,
11
+ useLayoutEffect,
12
+ createContext,
13
+ useContext,
14
+ } from 'react'
15
+ import { TextAttributes } from '@opentui/core'
16
+ import { useKeyboard } from '@opentui/react'
17
+ import { logger } from 'termcast/src/logger'
18
+ import { Theme } from 'termcast/src/theme'
19
+ import { Action, ActionPanel } from 'termcast/src/components/actions'
20
+ import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
21
+ import { CommonProps } from 'termcast/src/utils'
22
+ import { useStore } from 'termcast/src/state'
23
+ import { useDialog } from 'termcast/src/internal/dialog'
24
+ import { createDescendants } from 'termcast/src/descendants'
25
+ import { LoadingBar } from 'termcast/src/components/loading-bar'
26
+ import { useNavigationPending } from 'termcast/src/internal/navigation'
27
+
28
+ interface ActionsInterface {
29
+ actions?: ReactNode
30
+ }
31
+
32
+ function ListFooter(): any {
33
+ const toast = useStore((state) => state.toast)
34
+
35
+ if (toast) {
36
+ return (
37
+ <box
38
+ border={false}
39
+ style={{
40
+ paddingLeft: 1,
41
+ paddingRight: 1,
42
+ paddingTop: 1,
43
+ marginTop: 1,
44
+ }}
45
+ >
46
+ {toast}
47
+ </box>
48
+ )
49
+ }
50
+
51
+ return (
52
+ <box
53
+ border={false}
54
+ style={{
55
+ paddingLeft: 1,
56
+ paddingRight: 1,
57
+ paddingTop: 1,
58
+ marginTop: 1,
59
+ flexDirection: 'row',
60
+ }}
61
+ >
62
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
63
+
64
+ </text>
65
+ <text fg={Theme.textMuted}> select</text>
66
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
67
+ {' '}↑↓
68
+ </text>
69
+ <text fg={Theme.textMuted}> navigate</text>
70
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
71
+ {' '}^k
72
+ </text>
73
+ <text fg={Theme.textMuted}> actions</text>
74
+ </box>
75
+ )
76
+ }
77
+
78
+ interface NavigationChildInterface {
79
+ navigationTitle?: string
80
+ isLoading?: boolean
81
+ }
82
+
83
+ interface SearchBarInterface {
84
+ filtering?: boolean | { keepSectionOrder: boolean }
85
+ isLoading?: boolean
86
+ onSearchTextChange?: (newValue: string) => void
87
+ searchBarPlaceholder?: string
88
+ throttle?: boolean
89
+ }
90
+
91
+ interface PaginationInterface {
92
+ pagination?: {
93
+ pageSize: number
94
+ hasMore: boolean
95
+ onLoadMore: () => void
96
+ }
97
+ }
98
+
99
+ export type Color = string
100
+
101
+ export namespace Image {
102
+ export type ImageLike = string
103
+ }
104
+
105
+ export type ItemAccessory =
106
+ | {
107
+ text?:
108
+ | string
109
+ | null
110
+ | {
111
+ value: string | null
112
+ color?: Color
113
+ }
114
+ }
115
+ | {
116
+ date?:
117
+ | Date
118
+ | null
119
+ | {
120
+ value: Date | null
121
+ color?: Color
122
+ }
123
+ }
124
+ | {
125
+ tag?:
126
+ | string
127
+ | {
128
+ value: string
129
+ color?: Color
130
+ }
131
+ }
132
+ | {
133
+ icon?: Image.ImageLike | null
134
+ text?: string | null
135
+ tooltip?: string | null
136
+ }
137
+
138
+ export interface ItemProps extends ActionsInterface, CommonProps {
139
+ id?: string
140
+ title:
141
+ | string
142
+ | {
143
+ value: string
144
+ tooltip?: string | null
145
+ }
146
+ subtitle?:
147
+ | string
148
+ | {
149
+ value?: string | null
150
+ tooltip?: string | null
151
+ }
152
+ keywords?: string[]
153
+ icon?:
154
+ | Image.ImageLike
155
+ | {
156
+ value: Image.ImageLike | null
157
+ tooltip: string
158
+ }
159
+ accessories?: ItemAccessory[]
160
+ detail?: ReactElement<DetailProps>
161
+ }
162
+
163
+ export interface DetailProps extends CommonProps {
164
+ isLoading?: boolean
165
+ markdown?: string
166
+ metadata?: ReactElement<MetadataProps> | null
167
+ }
168
+
169
+ export interface MetadataProps extends CommonProps {
170
+ children?: ReactNode
171
+ }
172
+
173
+ export interface DropdownItemProps extends CommonProps {
174
+ value: string
175
+ title: string
176
+ icon?: Image.ImageLike | null
177
+ keywords?: string[]
178
+ }
179
+
180
+ export interface DropdownSectionProps extends CommonProps {
181
+ children?: ReactNode
182
+ title?: string
183
+ }
184
+
185
+ export interface DropdownProps extends SearchBarInterface, CommonProps {
186
+ id?: string
187
+ tooltip: string
188
+ placeholder?: string
189
+ storeValue?: boolean
190
+ value?: string
191
+ defaultValue?: string
192
+ onChange?: (newValue: string) => void
193
+ children?: ReactNode
194
+ }
195
+
196
+ export interface SectionProps extends CommonProps {
197
+ children?: ReactNode
198
+ id?: string
199
+ title?: string
200
+ subtitle?: string
201
+ }
202
+
203
+ export interface ListProps
204
+ extends ActionsInterface,
205
+ NavigationChildInterface,
206
+ SearchBarInterface,
207
+ PaginationInterface,
208
+ CommonProps {
209
+ actions?: ReactNode
210
+ children?: ReactNode
211
+ onSelectionChange?: (id: string | null) => void
212
+ searchBarAccessory?: ReactElement<DropdownProps> | null
213
+ searchText?: string
214
+ enableFiltering?: boolean
215
+ searchBarPlaceholder?: string
216
+ selectedItemId?: string
217
+ isShowingDetail?: boolean
218
+ }
219
+
220
+ interface ListType {
221
+ (props: ListProps): any
222
+ Item: ListItemType
223
+ Section: (props: SectionProps) => any
224
+ Dropdown: ListDropdownType
225
+ EmptyView: (props: EmptyViewProps) => any
226
+ }
227
+
228
+ interface ListItemType {
229
+ (props: ItemProps): any
230
+ Detail: ListItemDetailType
231
+ }
232
+
233
+ interface ListItemDetailType {
234
+ (props: DetailProps): any
235
+ Metadata: ListItemDetailMetadataType
236
+ }
237
+
238
+ interface ListItemDetailMetadataType {
239
+ (props: MetadataProps): any
240
+ Label: (props: { title: string; text?: string; icon?: Image.ImageLike }) => any
241
+ Separator: () => any
242
+ Link: (props: { title: string; target: string; text: string }) => any
243
+ TagList: ListItemDetailMetadataTagListType
244
+ }
245
+
246
+ interface ListItemDetailMetadataTagListType {
247
+ (props: { title: string; children: ReactNode }): any
248
+ Item: (props: { text?: string; color?: Color; icon?: Image.ImageLike; onAction?: () => void }) => any
249
+ }
250
+
251
+ interface ListDropdownType {
252
+ (props: DropdownProps): any
253
+ Item: (props: DropdownItemProps) => any
254
+ Section: (props: DropdownSectionProps) => any
255
+ }
256
+
257
+ interface EmptyViewProps extends ActionsInterface, CommonProps {
258
+ icon?: Image.ImageLike
259
+ title?: string
260
+ description?: string
261
+ }
262
+
263
+ // List context for passing data to dropdown
264
+ interface ListContextValue {
265
+ isDropdownOpen: boolean
266
+ setIsDropdownOpen: (value: boolean) => void
267
+ openDropdown: () => void
268
+ selectedIndex: number
269
+ setSelectedIndex?: (index: number) => void
270
+ searchText: string
271
+ isFiltering: boolean
272
+ setCurrentDetail?: (detail: ReactNode) => void
273
+ isShowingDetail?: boolean
274
+ }
275
+
276
+ const ListContext = createContext<ListContextValue | undefined>(undefined)
277
+
278
+ // Helper function to determine if an item should be visible based on search
279
+ function shouldItemBeVisible(
280
+ searchQuery: string,
281
+ props: {
282
+ title: string
283
+ subtitle?: string
284
+ keywords?: string[]
285
+ },
286
+ ): boolean {
287
+ // If no search query, show all items
288
+ if (!searchQuery.trim()) return true
289
+
290
+ const needle = searchQuery.toLowerCase().trim()
291
+ const searchableText = [
292
+ props.title,
293
+ props.subtitle,
294
+ ...(props.keywords || []),
295
+ ]
296
+ .filter(Boolean)
297
+ .join(' ')
298
+ .toLowerCase()
299
+
300
+ return searchableText.includes(needle)
301
+ }
302
+
303
+ // Create descendants for List items
304
+ interface ListItemDescendant {
305
+ id?: string
306
+ title: string
307
+ subtitle?: string
308
+ keywords?: string[]
309
+ actions?: ReactNode
310
+ visible?: boolean
311
+ detail?: ReactNode
312
+ }
313
+
314
+ const {
315
+ DescendantsProvider: ListDescendantsProvider,
316
+ useDescendants: useListDescendants,
317
+ useDescendant: useListItemDescendant,
318
+ } = createDescendants<ListItemDescendant>()
319
+
320
+ // Create descendants for Dropdown items
321
+ interface DropdownItemDescendant {
322
+ value: string
323
+ title: string
324
+ section?: string
325
+ visible?: boolean
326
+ }
327
+
328
+ const {
329
+ DescendantsProvider: DropdownDescendantsProvider,
330
+ useDescendants: useDropdownDescendants,
331
+ useDescendant: useDropdownItemDescendant,
332
+ } = createDescendants<DropdownItemDescendant>()
333
+
334
+ // Dropdown context for passing data to dropdown items
335
+ interface DropdownContextValue {
336
+ currentSection?: string
337
+ selectedIndex?: number
338
+ setSelectedIndex?: (index: number) => void
339
+ currentValue?: string
340
+ searchText?: string
341
+ onChange?: (value: string) => void
342
+ isFiltering?: boolean
343
+ }
344
+
345
+ const DropdownContext = createContext<DropdownContextValue | undefined>(
346
+ undefined,
347
+ )
348
+
349
+ // Dropdown dialog component
350
+ interface ListDropdownDialogProps extends DropdownProps {
351
+ onCancel: () => void
352
+ children?: ReactNode
353
+ }
354
+
355
+ function ListDropdownDialog(props: ListDropdownDialogProps): any {
356
+ const [searchText, setSearchTextRaw] = useState('')
357
+ const [selectedIndex, setSelectedIndex] = useState(0)
358
+ const inputRef = useRef<any>(null)
359
+ const descendantsContext = useDropdownDescendants()
360
+
361
+ // Wrapper function that updates search text
362
+ const setSearchText = (value: string) => {
363
+ setSearchTextRaw(value)
364
+ setSelectedIndex(0) // Reset selection when search changes
365
+ }
366
+
367
+ const move = (direction: -1 | 1) => {
368
+ // Get all visible items
369
+ const items = Object.values(descendantsContext.map.current)
370
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
371
+ .sort((a, b) => a.index - b.index)
372
+
373
+ if (items.length === 0) return
374
+
375
+ // Find currently selected item's position in visible items
376
+ let currentVisibleIndex = items.findIndex(
377
+ (item) => item.index === selectedIndex,
378
+ )
379
+ if (currentVisibleIndex === -1) {
380
+ // If current selection is not visible, select first visible item
381
+ if (items[0]) {
382
+ setSelectedIndex(items[0].index)
383
+ }
384
+ return
385
+ }
386
+
387
+ // Calculate next visible index
388
+ let nextVisibleIndex = currentVisibleIndex + direction
389
+ if (nextVisibleIndex < 0) nextVisibleIndex = items.length - 1
390
+ if (nextVisibleIndex >= items.length) nextVisibleIndex = 0
391
+
392
+ const nextItem = items[nextVisibleIndex]
393
+ if (nextItem) {
394
+ setSelectedIndex(nextItem.index)
395
+ }
396
+ }
397
+
398
+ const inFocus = useIsInFocus()
399
+
400
+ useKeyboard((evt) => {
401
+ if (!inFocus) return
402
+
403
+ if (evt.name === 'escape') {
404
+ props.onCancel()
405
+ }
406
+ if (evt.name === 'up') move(-1)
407
+ if (evt.name === 'down') move(1)
408
+ if (evt.name === 'return') {
409
+ const items = Object.values(descendantsContext.map.current)
410
+ .filter((item) => item.index !== -1)
411
+ .sort((a, b) => a.index - b.index)
412
+
413
+ const currentItem = items.find((item) => item.index === selectedIndex)
414
+ if (currentItem?.props) {
415
+ props.onChange?.((currentItem.props as DropdownItemDescendant).value)
416
+ }
417
+ }
418
+ })
419
+
420
+ return (
421
+ <DropdownDescendantsProvider value={descendantsContext}>
422
+ <box>
423
+ <box style={{ paddingLeft: 2, paddingRight: 2 }}>
424
+ <box style={{ paddingLeft: 1, paddingRight: 1 }}>
425
+ {/* Header */}
426
+ <box
427
+ style={{
428
+ flexDirection: 'row',
429
+ justifyContent: 'space-between',
430
+ }}
431
+ >
432
+ <text attributes={TextAttributes.BOLD}>{props.tooltip}</text>
433
+ <text fg={Theme.textMuted}>esc</text>
434
+ </box>
435
+ <box style={{ paddingTop: 1, paddingBottom: 1 }}>
436
+ <input
437
+ ref={inputRef}
438
+ onInput={setSearchText}
439
+ placeholder={props.placeholder || 'Search...'}
440
+ focused={inFocus}
441
+ value={searchText}
442
+ focusedBackgroundColor={Theme.backgroundPanel}
443
+ cursorColor={Theme.primary}
444
+ focusedTextColor={Theme.textMuted}
445
+ />
446
+ </box>
447
+ </box>
448
+
449
+ {/* Items list - children will render themselves */}
450
+ <box style={{ paddingBottom: 1 }}>
451
+ <DropdownContext.Provider
452
+ value={{
453
+ currentSection: undefined,
454
+ selectedIndex,
455
+ setSelectedIndex,
456
+ currentValue: props.value,
457
+ searchText,
458
+ isFiltering: true, // Dropdown always has filtering enabled
459
+ onChange: (value: string) => {
460
+ props.onChange?.(value)
461
+ },
462
+ }}
463
+ >
464
+ {props.children}
465
+ </DropdownContext.Provider>
466
+ </box>
467
+ {props.isLoading && (
468
+ <box style={{ paddingLeft: 1 }}>
469
+ <text fg={Theme.textMuted}>Loading...</text>
470
+ </box>
471
+ )}
472
+ </box>
473
+
474
+ <box
475
+ border={false}
476
+ style={{
477
+ paddingRight: 2,
478
+ paddingLeft: 3,
479
+ paddingBottom: 1,
480
+ paddingTop: 1,
481
+ flexDirection: 'row',
482
+ }}
483
+ >
484
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
485
+
486
+ </text>
487
+ <text fg={Theme.textMuted}> select</text>
488
+ <text fg={Theme.text} attributes={TextAttributes.BOLD}>
489
+ {' '}↑↓
490
+ </text>
491
+ <text fg={Theme.textMuted}> navigate</text>
492
+ </box>
493
+ </box>
494
+ </DropdownDescendantsProvider>
495
+ )
496
+ }
497
+
498
+ // Render a single list item row
499
+ function ListItemRow(props: {
500
+ title: string
501
+ subtitle?: string
502
+ accessories?: ItemAccessory[]
503
+ active?: boolean
504
+ isShowingDetail?: boolean
505
+ onMouseDown?: () => void
506
+ index?: number
507
+ }) {
508
+ const { title, subtitle, accessories, active } = props
509
+ const [isHovered, setIsHovered] = useState(false)
510
+
511
+ // Format accessories for display
512
+ const accessoryElements: ReactNode[] = []
513
+ if (accessories) {
514
+ accessories.forEach((accessory) => {
515
+ if ('text' in accessory && accessory.text) {
516
+ const textValue =
517
+ typeof accessory.text === 'string'
518
+ ? accessory.text
519
+ : accessory.text?.value
520
+ if (textValue) {
521
+ accessoryElements.push(
522
+ <text
523
+ key={`text-${textValue}`}
524
+ fg={active ? Theme.background : Theme.info}
525
+ >
526
+ {textValue}
527
+ </text>,
528
+ )
529
+ }
530
+ }
531
+ if ('tag' in accessory && accessory.tag) {
532
+ const tagValue =
533
+ typeof accessory.tag === 'string'
534
+ ? accessory.tag
535
+ : accessory.tag?.value
536
+ if (tagValue) {
537
+ accessoryElements.push(
538
+ <text
539
+ key={`tag-${tagValue}`}
540
+ fg={active ? Theme.background : Theme.warning}
541
+ >
542
+ [{tagValue}]
543
+ </text>,
544
+ )
545
+ }
546
+ }
547
+ })
548
+ }
549
+
550
+ return (
551
+ <box
552
+ style={{
553
+ flexDirection: 'row',
554
+ justifyContent: 'space-between',
555
+ backgroundColor: active
556
+ ? Theme.primary
557
+ : isHovered
558
+ ? Theme.backgroundPanel
559
+ : undefined,
560
+ paddingLeft: active ? 0 : 1,
561
+ paddingRight: 1,
562
+ }}
563
+ border={false}
564
+ onMouseMove={() => setIsHovered(true)}
565
+ onMouseOut={() => setIsHovered(false)}
566
+ onMouseDown={props.onMouseDown}
567
+ >
568
+ <box style={{ flexDirection: 'row', flexGrow: 1, flexShrink: 1 }}>
569
+ {active && (
570
+ <text fg={Theme.textMuted} selectable={false}>
571
+
572
+ </text>
573
+ )}
574
+ <text
575
+ fg={active ? Theme.background : Theme.text}
576
+ attributes={active ? TextAttributes.BOLD : undefined}
577
+ selectable={false}
578
+ >
579
+ {title}
580
+ </text>
581
+ {subtitle && (
582
+ <text
583
+ fg={active ? Theme.background : Theme.textMuted}
584
+ selectable={false}
585
+ >
586
+ {' '}
587
+ {subtitle}
588
+ </text>
589
+ )}
590
+ </box>
591
+ {accessoryElements.length > 0 && (
592
+ <box style={{ flexDirection: 'row' }}>
593
+ {accessoryElements.map((elem, i) => (
594
+ <box key={i} style={{ flexDirection: 'row' }}>
595
+ {i > 0 && <text> </text>}
596
+ {elem}
597
+ </box>
598
+ ))}
599
+ </box>
600
+ )}
601
+ {/*{active && <text fg={Theme.textMuted}>‹</text>}*/}
602
+ </box>
603
+ )
604
+ }
605
+
606
+ export const List: ListType = (props) => {
607
+ const {
608
+ children,
609
+ onSelectionChange,
610
+ filtering = true,
611
+ searchText: controlledSearchText,
612
+ onSearchTextChange,
613
+ searchBarPlaceholder = 'Search...',
614
+ isLoading,
615
+ navigationTitle,
616
+ isShowingDetail,
617
+ selectedItemId,
618
+ searchBarAccessory,
619
+ ...otherProps
620
+ } = props
621
+
622
+ const [internalSearchText, setInternalSearchTextRaw] = useState('')
623
+ const [selectedIndex, setSelectedIndex] = useState(0)
624
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false)
625
+ const [currentDetail, setCurrentDetail] = useState<ReactNode>(null)
626
+ const inputRef = useRef<any>(null)
627
+ const descendantsContext = useListDescendants()
628
+ const navigationPending = useNavigationPending()
629
+
630
+ const searchText =
631
+ controlledSearchText !== undefined
632
+ ? controlledSearchText
633
+ : internalSearchText
634
+
635
+ // Determine if filtering is enabled
636
+ // List filters automatically when:
637
+ // - filtering is not specified (defaults to true) OR filtering is explicitly true
638
+ // List does NOT filter automatically when:
639
+ // - When filtering={false}
640
+ // - When onSearchTextChange is provided (implicitly sets filtering to false)
641
+ // - Unless you explicitly set filtering={true} alongside onSearchTextChange
642
+ const isFilteringEnabled = (() => {
643
+ if (filtering === false) return false
644
+ if (filtering === true) return true
645
+ // filtering is undefined/not specified
646
+ return !onSearchTextChange // defaults to true unless onSearchTextChange is provided
647
+ })()
648
+
649
+ const openDropdown = () => {
650
+ setIsDropdownOpen(true)
651
+ }
652
+
653
+ // Wrapper function that updates search text
654
+ const setInternalSearchText = (value: string) => {
655
+ setInternalSearchTextRaw(value)
656
+ // Reset to 0 when search changes - this is expected UX behavior
657
+ setSelectedIndex(0)
658
+ }
659
+
660
+ const listContextValue = useMemo<ListContextValue>(
661
+ () => ({
662
+ isDropdownOpen,
663
+ setIsDropdownOpen,
664
+ openDropdown,
665
+ selectedIndex,
666
+ setSelectedIndex,
667
+ searchText,
668
+ isFiltering: isFilteringEnabled,
669
+ setCurrentDetail,
670
+ isShowingDetail,
671
+ }),
672
+ [isDropdownOpen, selectedIndex, searchText, isFilteringEnabled, isShowingDetail],
673
+ )
674
+
675
+ // Clear detail when detail view is hidden
676
+ useEffect(() => {
677
+ if (!isShowingDetail) {
678
+ setCurrentDetail(null)
679
+ }
680
+ }, [isShowingDetail])
681
+
682
+ // Handle selectedItemId prop changes
683
+ useEffect(() => {
684
+ // Only update selection if selectedItemId is explicitly provided
685
+ if (selectedItemId !== undefined) {
686
+ const items = Object.values(descendantsContext.map.current)
687
+ .filter((item) => item.index !== -1)
688
+ .sort((a, b) => a.index - b.index)
689
+
690
+ const index = items.findIndex((item) => item.props?.id === selectedItemId)
691
+ if (index !== -1) {
692
+ setSelectedIndex(index)
693
+ }
694
+ }
695
+ }, [selectedItemId])
696
+
697
+ const move = (direction: -1 | 1) => {
698
+ // Get all visible items
699
+ const items = Object.values(descendantsContext.map.current)
700
+ .filter((item) => item.index !== -1 && item.props?.visible !== false)
701
+ .sort((a, b) => a.index - b.index)
702
+
703
+ if (items.length === 0) return
704
+
705
+ // Find currently selected item's position in visible items
706
+ let currentVisibleIndex = items.findIndex(
707
+ (item) => item.index === selectedIndex,
708
+ )
709
+ if (currentVisibleIndex === -1) {
710
+ // If current selection is not visible, select first visible item
711
+ if (items[0]) {
712
+ setSelectedIndex(items[0].index)
713
+ }
714
+ return
715
+ }
716
+
717
+ // Calculate next visible index
718
+ let nextVisibleIndex = currentVisibleIndex + direction
719
+ if (nextVisibleIndex < 0) nextVisibleIndex = items.length - 1
720
+ if (nextVisibleIndex >= items.length) nextVisibleIndex = 0
721
+
722
+ const nextItem = items[nextVisibleIndex]
723
+ if (nextItem) {
724
+ setSelectedIndex(nextItem.index)
725
+ }
726
+ }
727
+
728
+ // Handle keyboard navigation
729
+ const inFocus = useIsInFocus()
730
+ const dialog = useDialog()
731
+
732
+ useKeyboard((evt) => {
733
+ if (!inFocus) return
734
+
735
+ // Handle Ctrl+P for dropdown
736
+ if (evt.ctrl && evt.name === 'p' && searchBarAccessory && !isDropdownOpen) {
737
+ openDropdown()
738
+ return
739
+ }
740
+
741
+ // Handle Ctrl+K to show actions
742
+ if (evt.name === 'k' && evt.ctrl) {
743
+ const items = Object.values(descendantsContext.map.current)
744
+ .filter((item) => item.index !== -1)
745
+ .sort((a, b) => a.index - b.index)
746
+
747
+ const currentItem = items.find((item) => item.index === selectedIndex)
748
+
749
+ // Show current item's actions if available
750
+ if (currentItem?.props?.actions) {
751
+ dialog.push(currentItem.props.actions, 'bottom-right')
752
+ }
753
+ // Otherwise show List's own actions
754
+ else if (props.actions) {
755
+ dialog.push(props.actions, 'bottom-right')
756
+ }
757
+ return
758
+ }
759
+
760
+ if (evt.name === 'up') move(-1)
761
+ if (evt.name === 'down') move(1)
762
+ if (evt.name === 'return') {
763
+ const items = Object.values(descendantsContext.map.current)
764
+ .filter((item) => item.index !== -1)
765
+ .sort((a, b) => a.index - b.index)
766
+
767
+ const currentItem = items.find((item) => item.index === selectedIndex)
768
+ if (!currentItem?.props) return
769
+
770
+ if (currentItem.props.actions) {
771
+ dialog.push(currentItem.props.actions, 'bottom-right')
772
+ }
773
+ }
774
+ })
775
+
776
+ const handleSearchChange = (newValue: string) => {
777
+ if (!inFocus) return
778
+
779
+ // Always call onSearchTextChange if provided
780
+ if (onSearchTextChange) {
781
+ onSearchTextChange(newValue)
782
+ }
783
+
784
+ if (controlledSearchText === undefined) {
785
+ setInternalSearchText(newValue)
786
+ }
787
+ }
788
+
789
+ return (
790
+ <ListContext.Provider value={listContextValue}>
791
+ <ListDescendantsProvider value={descendantsContext}>
792
+ <box style={{ flexDirection: 'column', flexGrow: 1 }}>
793
+ {/* Cannot mount focused actions here - would need to be handled differently */}
794
+
795
+ {navigationTitle && (
796
+ <box
797
+ border={false}
798
+ style={{
799
+ paddingBottom: 0,
800
+ flexGrow: 1,
801
+ }}
802
+ >
803
+ <LoadingBar
804
+ title={navigationTitle}
805
+ isLoading={isLoading || navigationPending}
806
+ />
807
+ </box>
808
+ )}
809
+
810
+ {/* Search bar with optional dropdown accessory */}
811
+ <box>
812
+ <box
813
+ border={false}
814
+ style={{
815
+ paddingLeft: 1,
816
+ paddingRight: 1,
817
+ marginTop: 1,
818
+ marginBottom: 1,
819
+ flexDirection: 'row',
820
+ justifyContent: 'space-between',
821
+
822
+ alignItems: 'center',
823
+ }}
824
+ >
825
+ <box
826
+ style={{
827
+ flexGrow: 1,
828
+ flexDirection: 'column',
829
+ flexShrink: 1,
830
+ }}
831
+ >
832
+ <input
833
+ ref={inputRef}
834
+ placeholder={searchBarPlaceholder}
835
+ focused={inFocus && !isDropdownOpen}
836
+ value={searchText}
837
+ onInput={handleSearchChange}
838
+ focusedBackgroundColor={Theme.backgroundPanel}
839
+ cursorColor={Theme.primary}
840
+ focusedTextColor={Theme.text}
841
+ />
842
+ </box>
843
+ {searchBarAccessory}
844
+ </box>
845
+ </box>
846
+
847
+ {/* Main content area with optional detail view */}
848
+ <box style={{ flexDirection: 'row', flexGrow: 1 }}>
849
+ {/* List content - render children which will register themselves */}
850
+ <box style={{ marginTop: 1, width: isShowingDetail ? '50%' : '100%', flexGrow: isShowingDetail ? 0 : 1 }}>
851
+ <>
852
+ {/* Render children - they will register as descendants */}
853
+ <ListItemsRenderer>{children}</ListItemsRenderer>
854
+
855
+ {/* Footer with keyboard shortcuts or toast */}
856
+ <ListFooter />
857
+ </>
858
+ </box>
859
+
860
+ {/* Detail panel on the right */}
861
+ {isShowingDetail && currentDetail && (
862
+ <box
863
+ style={{
864
+ marginTop: 1,
865
+ width: '50%',
866
+ paddingLeft: 1,
867
+ paddingRight: 1,
868
+ }}
869
+ border={['left']}
870
+ borderStyle='single'
871
+ borderColor={Theme.border}
872
+ >
873
+ {currentDetail}
874
+ </box>
875
+ )}
876
+ </box>
877
+ </box>
878
+ </ListDescendantsProvider>
879
+ </ListContext.Provider>
880
+ )
881
+ }
882
+
883
+ // Component to render list items and sections
884
+ function ListItemsRenderer(props: { children?: ReactNode }): any {
885
+ const { children } = props
886
+ const listContext = useContext(ListContext)
887
+ const searchText = listContext?.searchText || ''
888
+
889
+ // Pass search text down via context
890
+ return (
891
+ <ListSectionContext.Provider value={{ searchText }}>
892
+ {children}
893
+ </ListSectionContext.Provider>
894
+ )
895
+ }
896
+
897
+ // Context for passing section state to items
898
+ interface ListSectionContextValue {
899
+ sectionTitle?: string
900
+ searchText?: string
901
+ }
902
+
903
+ const ListSectionContext = createContext<ListSectionContextValue>({})
904
+
905
+ const ListItem: ListItemType = (props) => {
906
+ const listSectionContext = useContext(ListSectionContext)
907
+ const { sectionTitle } = listSectionContext
908
+ const listContext = useContext(ListContext)
909
+ const dialog = useDialog()
910
+
911
+ // Extract text values for descendant registration
912
+ const titleText =
913
+ typeof props.title === 'string' ? props.title : props.title.value
914
+ const subtitleText = props.subtitle
915
+ ? typeof props.subtitle === 'string'
916
+ ? props.subtitle
917
+ : props.subtitle.value || ''
918
+ : undefined
919
+
920
+ // Check if this item is visible based on search
921
+ const isFiltering = listContext?.isFiltering ?? false
922
+ const searchText = listContext?.searchText ?? ''
923
+
924
+ const isVisible =
925
+ !isFiltering ||
926
+ shouldItemBeVisible(searchText, {
927
+ title: titleText,
928
+ subtitle: subtitleText,
929
+ keywords: [...(props.keywords || []), sectionTitle].filter(
930
+ Boolean,
931
+ ) as string[],
932
+ })
933
+
934
+ // Register as descendant with all searchable data
935
+ const { index } = useListItemDescendant({
936
+ id: props.id,
937
+ title: titleText,
938
+ subtitle: subtitleText,
939
+ keywords: [...(props.keywords || []), sectionTitle].filter(
940
+ Boolean,
941
+ ) as string[],
942
+ actions: props.actions,
943
+ visible: isVisible,
944
+ detail: props.detail,
945
+ })
946
+
947
+ // Get selected index from parent List context
948
+ const selectedIndex = listContext?.selectedIndex ?? 0
949
+ const isActive = index === selectedIndex
950
+
951
+ // Update detail when this item becomes active or detail prop changes
952
+ useEffect(() => {
953
+ if (isActive && listContext?.isShowingDetail && listContext?.setCurrentDetail) {
954
+ listContext.setCurrentDetail(props.detail || null)
955
+ }
956
+ }, [isActive, props.detail, listContext?.isShowingDetail, listContext?.setCurrentDetail])
957
+
958
+ // Don't render if not visible
959
+ if (!isVisible) return null
960
+
961
+ // Handle mouse click on item
962
+ const handleMouseDown = () => {
963
+ if (listContext && index !== -1) {
964
+ // If clicking on already selected item, show actions (like pressing Enter)
965
+ if (isActive && props.actions) {
966
+ dialog.push(props.actions, 'bottom-right')
967
+ } else if (listContext.setSelectedIndex) {
968
+ // Otherwise just select the item
969
+ listContext.setSelectedIndex(index)
970
+ }
971
+ }
972
+ }
973
+
974
+ // Don't show accessories if we're showing detail
975
+ const showAccessories = !props.detail && props.accessories
976
+
977
+ // Render the item row directly
978
+ return (
979
+ <ListItemRow
980
+ title={titleText}
981
+ subtitle={subtitleText}
982
+ accessories={showAccessories ? props.accessories : undefined}
983
+ active={isActive}
984
+ isShowingDetail={props.detail !== undefined}
985
+ onMouseDown={handleMouseDown}
986
+ index={index}
987
+ />
988
+ )
989
+ }
990
+
991
+ const ListItemDetail: ListItemDetailType = (props) => {
992
+ const { isLoading, markdown, metadata } = props
993
+
994
+ return (
995
+ <box style={{ flexDirection: 'column', flexGrow: 1 }}>
996
+ {isLoading && (
997
+ <box style={{ paddingBottom: 1 }}>
998
+ <text fg={Theme.textMuted}>Loading...</text>
999
+ </box>
1000
+ )}
1001
+
1002
+ {markdown && (
1003
+ <box style={{ flexGrow: 1, flexShrink: 1, overflow: 'scroll' }}>
1004
+ <text>{markdown}</text>
1005
+ </box>
1006
+ )}
1007
+
1008
+ {metadata && (
1009
+ <box
1010
+ style={{ paddingTop: 1 }}
1011
+ border={['top']}
1012
+ borderStyle='single'
1013
+ borderColor={Theme.border}
1014
+ >
1015
+ {metadata}
1016
+ </box>
1017
+ )}
1018
+ </box>
1019
+ )
1020
+ }
1021
+
1022
+ const ListItemDetailMetadata = (props: MetadataProps) => {
1023
+ return (
1024
+ <box style={{ flexDirection: 'column' }}>
1025
+ {props.children}
1026
+ </box>
1027
+ )
1028
+ }
1029
+
1030
+ const ListItemDetailMetadataLabel = (props: { title: string; text?: string; icon?: Image.ImageLike }) => {
1031
+ return (
1032
+ <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1033
+ <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1034
+ {props.text && <text fg={Theme.text}>{props.text}</text>}
1035
+ </box>
1036
+ )
1037
+ }
1038
+
1039
+ const ListItemDetailMetadataSeparator = () => {
1040
+ return (
1041
+ <box style={{ paddingBottom: 0.5 }}>
1042
+ <text fg={Theme.border}>─────────────────</text>
1043
+ </box>
1044
+ )
1045
+ }
1046
+
1047
+ const ListItemDetailMetadataLink = (props: { title: string; target: string; text: string }) => {
1048
+ return (
1049
+ <box style={{ flexDirection: 'row', paddingBottom: 0.5 }}>
1050
+ <text fg={Theme.textMuted} style={{ minWidth: 15 }}>{props.title}:</text>
1051
+ <text fg={Theme.link}>{props.text}</text>
1052
+ </box>
1053
+ )
1054
+ }
1055
+
1056
+ const ListItemDetailMetadataTagList = (props: { title: string; children: ReactNode }) => {
1057
+ return (
1058
+ <box style={{ flexDirection: 'column', paddingBottom: 0.5 }}>
1059
+ <text fg={Theme.textMuted}>{props.title}:</text>
1060
+ <box style={{ flexDirection: 'row', paddingLeft: 1 }}>
1061
+ {props.children}
1062
+ </box>
1063
+ </box>
1064
+ )
1065
+ }
1066
+
1067
+ const ListItemDetailMetadataTagListItem = (props: { text?: string; color?: Color; icon?: Image.ImageLike; onAction?: () => void }) => {
1068
+ return (
1069
+ <box style={{ paddingRight: 1 }}>
1070
+ <text fg={props.color || Theme.accent}>[{props.text}]</text>
1071
+ </box>
1072
+ )
1073
+ }
1074
+
1075
+ ListItemDetail.Metadata = ListItemDetailMetadata as any
1076
+ ListItemDetailMetadata.Label = ListItemDetailMetadataLabel as any
1077
+ ListItemDetailMetadata.Separator = ListItemDetailMetadataSeparator as any
1078
+ ListItemDetailMetadata.Link = ListItemDetailMetadataLink as any
1079
+ ListItemDetailMetadata.TagList = ListItemDetailMetadataTagList as any
1080
+ ListItemDetailMetadataTagList.Item = ListItemDetailMetadataTagListItem as any
1081
+
1082
+ ListItem.Detail = ListItemDetail
1083
+
1084
+ const ListDropdown: ListDropdownType = (props) => {
1085
+ const listContext = useContext(ListContext)
1086
+ const [isHovered, setIsHovered] = useState(false)
1087
+
1088
+ // If not inside a List, just render nothing (for type safety)
1089
+ if (!listContext) {
1090
+ return null
1091
+ }
1092
+
1093
+ const { isDropdownOpen, setIsDropdownOpen } = listContext
1094
+ // Store both value and title together
1095
+ const [dropdownState, setDropdownState] = useState<{
1096
+ value: string
1097
+ title: string
1098
+ }>(() => {
1099
+ const initialValue = props.value || props.defaultValue || ''
1100
+ return { value: initialValue, title: initialValue || 'All' }
1101
+ })
1102
+ const descendantsContext = useDropdownDescendants()
1103
+ const dialog = useDialog()
1104
+ const inFocus = useIsInFocus()
1105
+
1106
+ // Update value and find its title
1107
+ useLayoutEffect(() => {
1108
+ const valueToUse =
1109
+ props.value !== undefined ? props.value : dropdownState.value
1110
+
1111
+ // If no value is set and we have descendants, use the first item
1112
+ if (!valueToUse && !props.value && !props.defaultValue) {
1113
+ const items = Object.values(descendantsContext.map.current)
1114
+ .filter((item) => item.index !== -1)
1115
+ .sort((a, b) => a.index - b.index)
1116
+
1117
+ if (items.length > 0) {
1118
+ const firstItem = items[0].props as DropdownItemDescendant
1119
+ setDropdownState({ value: firstItem.value, title: firstItem.title })
1120
+ return
1121
+ }
1122
+ }
1123
+
1124
+ if (!valueToUse) return
1125
+
1126
+ // Try to find the title for this value
1127
+ let title = valueToUse
1128
+ for (const item of Object.values(descendantsContext.map.current)) {
1129
+ const itemProps = item.props as DropdownItemDescendant
1130
+ if (itemProps.value === valueToUse) {
1131
+ title = itemProps.title
1132
+ break
1133
+ }
1134
+ }
1135
+
1136
+ // Only update if something changed
1137
+ if (dropdownState.value !== valueToUse || dropdownState.title !== title) {
1138
+ setDropdownState({ value: valueToUse, title })
1139
+ }
1140
+ }, [props.value]) // Run when props.value changes and on mount
1141
+
1142
+ const dropdownContextValue = useMemo<DropdownContextValue>(
1143
+ () => ({
1144
+ currentSection: undefined,
1145
+ }),
1146
+ [],
1147
+ )
1148
+
1149
+ // Open dropdown dialog when triggered
1150
+ useEffect(() => {
1151
+ if (isDropdownOpen && !dialog.stack.length) {
1152
+ // Pass the children to the dialog to render them there
1153
+ dialog.push(
1154
+ <ListDropdownDialog
1155
+ {...props}
1156
+ value={dropdownState.value}
1157
+ onChange={(newValue) => {
1158
+ // Find the title for this value
1159
+ let title = newValue
1160
+ for (const item of Object.values(descendantsContext.map.current)) {
1161
+ const itemProps = item.props as DropdownItemDescendant
1162
+ if (itemProps.value === newValue) {
1163
+ title = itemProps.title
1164
+ break
1165
+ }
1166
+ }
1167
+ setDropdownState({ value: newValue, title })
1168
+ setIsDropdownOpen(false)
1169
+ dialog.clear()
1170
+ if (props.onChange) {
1171
+ props.onChange(newValue)
1172
+ }
1173
+ // TODO: Handle storeValue to persist the value
1174
+ }}
1175
+ onCancel={() => {
1176
+ setIsDropdownOpen(false)
1177
+ dialog.clear()
1178
+ }}
1179
+ >
1180
+ {props.children}
1181
+ </ListDropdownDialog>,
1182
+ 'top-right',
1183
+ )
1184
+ }
1185
+ }, [isDropdownOpen, props.children])
1186
+
1187
+ // Display the title from our state
1188
+ const displayValue = dropdownState.title || 'All'
1189
+
1190
+ return (
1191
+ <DropdownDescendantsProvider value={descendantsContext}>
1192
+ <DropdownContext.Provider value={dropdownContextValue}>
1193
+ {/* Render children to collect items - they return null anyway */}
1194
+ {props.children}
1195
+ {/* Render dropdown UI */}
1196
+ <box
1197
+ key={dropdownState.value}
1198
+ style={{
1199
+ paddingTop: 1,
1200
+ paddingLeft: 2,
1201
+ // minWidth: value.length + 4,
1202
+ flexDirection: 'row',
1203
+ flexShrink: 0,
1204
+ backgroundColor: isHovered ? Theme.backgroundPanel : undefined,
1205
+ }}
1206
+ onMouseMove={() => setIsHovered(true)}
1207
+ onMouseOut={() => setIsHovered(false)}
1208
+ onMouseDown={() => {
1209
+ // Open dropdown when clicked
1210
+ if (!isDropdownOpen) {
1211
+ listContext.openDropdown()
1212
+ }
1213
+ }}
1214
+ >
1215
+ {/*<text >^p </text>*/}
1216
+ <text
1217
+ fg={isHovered ? Theme.text : Theme.textMuted}
1218
+ selectable={false}
1219
+ >
1220
+ {displayValue}
1221
+ </text>
1222
+ <text
1223
+ fg={isHovered ? Theme.text : Theme.textMuted}
1224
+ selectable={false}
1225
+ >
1226
+ {' '}
1227
+
1228
+ </text>
1229
+ </box>
1230
+ </DropdownContext.Provider>
1231
+ </DropdownDescendantsProvider>
1232
+ )
1233
+ }
1234
+
1235
+ ListDropdown.Item = (props) => {
1236
+ const dropdownContext = useContext(DropdownContext)
1237
+ const [isHovered, setIsHovered] = useState(false)
1238
+
1239
+ // If not inside a Dropdown, just render nothing
1240
+ if (!dropdownContext) {
1241
+ return null
1242
+ }
1243
+
1244
+ const {
1245
+ currentSection,
1246
+ selectedIndex,
1247
+ currentValue,
1248
+ setSelectedIndex,
1249
+ onChange,
1250
+ searchText,
1251
+ isFiltering,
1252
+ } = dropdownContext
1253
+
1254
+ // Check if this item is visible based on search
1255
+ const isVisible =
1256
+ !isFiltering ||
1257
+ !searchText ||
1258
+ shouldItemBeVisible(searchText, {
1259
+ title: props.title,
1260
+ keywords: currentSection ? [currentSection] : [],
1261
+ })
1262
+
1263
+ // Register as descendant
1264
+ const { index } = useDropdownItemDescendant({
1265
+ value: props.value,
1266
+ title: props.title,
1267
+ section: currentSection,
1268
+ visible: isVisible,
1269
+ })
1270
+
1271
+ // Don't render if not visible
1272
+ if (!isVisible) return null
1273
+
1274
+ // If we're in the dialog, render the item
1275
+ if (selectedIndex !== undefined) {
1276
+ const isActive = selectedIndex === index
1277
+ const isCurrent = props.value === currentValue
1278
+
1279
+ const handleMouseMove = () => {
1280
+ setIsHovered(true)
1281
+ // Update selected index on hover
1282
+ if (setSelectedIndex && index !== selectedIndex) {
1283
+ setSelectedIndex(index)
1284
+ }
1285
+ }
1286
+
1287
+ const handleMouseDown = () => {
1288
+ // Trigger selection on click
1289
+ if (onChange) {
1290
+ onChange(props.value)
1291
+ }
1292
+ }
1293
+
1294
+ return (
1295
+ <box
1296
+ style={{
1297
+ flexDirection: 'row',
1298
+ backgroundColor: isActive
1299
+ ? Theme.primary
1300
+ : isHovered
1301
+ ? Theme.backgroundPanel
1302
+ : undefined,
1303
+ paddingLeft: isActive ? 0 : 1,
1304
+ paddingRight: 1,
1305
+ justifyContent: 'space-between',
1306
+ }}
1307
+ border={false}
1308
+ onMouseMove={handleMouseMove}
1309
+ onMouseOut={() => setIsHovered(false)}
1310
+ onMouseDown={handleMouseDown}
1311
+ >
1312
+ <box style={{ flexDirection: 'row' }}>
1313
+ {isActive && (
1314
+ <text fg={Theme.background} selectable={false}>
1315
+ ›{''}
1316
+ </text>
1317
+ )}
1318
+ <text
1319
+ fg={
1320
+ isActive
1321
+ ? Theme.background
1322
+ : isCurrent
1323
+ ? Theme.primary
1324
+ : Theme.text
1325
+ }
1326
+ attributes={isActive ? TextAttributes.BOLD : undefined}
1327
+ selectable={false}
1328
+ >
1329
+ {props.title}
1330
+ </text>
1331
+ </box>
1332
+ </box>
1333
+ )
1334
+ }
1335
+
1336
+ return null
1337
+ }
1338
+
1339
+ ListDropdown.Section = (props) => {
1340
+ const parentContext = useContext(DropdownContext)
1341
+
1342
+ // If not inside a Dropdown, just render nothing
1343
+ if (!parentContext) {
1344
+ return null
1345
+ }
1346
+
1347
+ // Create a new context with the section name
1348
+ const sectionContextValue = useMemo<DropdownContextValue>(
1349
+ () => ({
1350
+ ...parentContext,
1351
+ currentSection: props.title,
1352
+ }),
1353
+ [parentContext, props.title],
1354
+ )
1355
+
1356
+ // Hide section title when searching
1357
+ const showTitle =
1358
+ parentContext.selectedIndex !== undefined &&
1359
+ props.title &&
1360
+ !parentContext.searchText?.trim()
1361
+
1362
+ return (
1363
+ <>
1364
+ {/* Render section title if we're in the dialog and not searching */}
1365
+ {showTitle && (
1366
+ <box style={{ paddingTop: 1, paddingLeft: 1 }}>
1367
+ <text fg={Theme.accent} attributes={TextAttributes.BOLD}>
1368
+ {props.title}
1369
+ </text>
1370
+ </box>
1371
+ )}
1372
+ <DropdownContext.Provider value={sectionContextValue}>
1373
+ {props.children}
1374
+ </DropdownContext.Provider>
1375
+ </>
1376
+ )
1377
+ }
1378
+
1379
+ List.Item = ListItem
1380
+ const ListSection = (props: SectionProps) => {
1381
+ const parentContext = useContext(ListSectionContext)
1382
+ const listContext = useContext(ListContext)
1383
+ const searchText = listContext?.searchText || ''
1384
+
1385
+ // Create new context with section title and search text
1386
+ const sectionContextValue = useMemo(
1387
+ () => ({
1388
+ ...parentContext,
1389
+ sectionTitle: props.title,
1390
+ searchText,
1391
+ }),
1392
+ [parentContext, props.title, searchText],
1393
+ )
1394
+
1395
+ // Hide section title when searching
1396
+ const showTitle = props.title && !searchText.trim()
1397
+
1398
+ return (
1399
+ <>
1400
+ {/* Render section title if provided and not searching */}
1401
+ {showTitle && (
1402
+ <box
1403
+ border={false}
1404
+ style={{
1405
+ paddingLeft: 1,
1406
+ paddingTop: 1,
1407
+ }}
1408
+ >
1409
+ <text fg={Theme.accent} attributes={TextAttributes.BOLD}>
1410
+ {props.title}
1411
+ </text>
1412
+ </box>
1413
+ )}
1414
+ {/* Render children with section context */}
1415
+ <ListSectionContext.Provider value={sectionContextValue}>
1416
+ {props.children}
1417
+ </ListSectionContext.Provider>
1418
+ </>
1419
+ )
1420
+ }
1421
+
1422
+ List.Section = ListSection
1423
+ List.Dropdown = ListDropdown
1424
+ List.EmptyView = (props) => {
1425
+ return null
1426
+ }
1427
+
1428
+ export default List
1429
+
1430
+ // Grid Component Implementation
1431
+ export interface GridInset {
1432
+ bottom?: number
1433
+ left?: number
1434
+ right?: number
1435
+ top?: number
1436
+ }
1437
+
1438
+ export interface GridItemProps extends ActionsInterface, CommonProps {
1439
+ id?: string
1440
+ content:
1441
+ | Image.ImageLike
1442
+ | {
1443
+ value: Image.ImageLike
1444
+ tooltip?: string | null
1445
+ }
1446
+ title:
1447
+ | string
1448
+ | {
1449
+ value: string
1450
+ tooltip?: string | null
1451
+ }
1452
+ subtitle?:
1453
+ | string
1454
+ | {
1455
+ value?: string | null
1456
+ tooltip?: string | null
1457
+ }
1458
+ keywords?: string[]
1459
+ getDetailMarkdown?: () =>
1460
+ | { markdown: string; metadata?: ReactElement<MetadataProps> }
1461
+ | Promise<{ markdown: string; metadata?: ReactElement<MetadataProps> }>
1462
+ }
1463
+
1464
+ export interface GridProps
1465
+ extends ActionsInterface,
1466
+ NavigationChildInterface,
1467
+ SearchBarInterface,
1468
+ PaginationInterface,
1469
+ CommonProps {
1470
+ actions?: ReactNode
1471
+ aspectRatio?: '1' | '3/2' | '2/3' | '4/3' | '3/4' | '16/9' | '9/16'
1472
+ children?: ReactNode
1473
+ columns?: number
1474
+ fit?: 'contain' | 'fill'
1475
+ inset?: GridInset
1476
+ navigationTitle?: string
1477
+ onSelectionChange?: (id: string | null) => void
1478
+ searchBarAccessory?: ReactElement<DropdownProps> | null
1479
+ searchText?: string
1480
+ enableFiltering?: boolean
1481
+ searchBarPlaceholder?: string
1482
+ selectedItemId?: string
1483
+ }
1484
+
1485
+ export interface GridSectionProps extends CommonProps {
1486
+ children?: ReactNode
1487
+ id?: string
1488
+ title?: string
1489
+ subtitle?: string
1490
+ aspectRatio?: '1' | '3/2' | '2/3' | '4/3' | '3/4' | '16/9' | '9/16'
1491
+ columns?: number
1492
+ fit?: 'contain' | 'fill'
1493
+ inset?: GridInset
1494
+ }
1495
+
1496
+ interface GridType {
1497
+ (props: GridProps): any
1498
+ Item: (props: GridItemProps) => any
1499
+ Section: (props: GridSectionProps) => any
1500
+ Dropdown: ListDropdownType
1501
+ EmptyView: (props: EmptyViewProps) => any
1502
+ Inset: {
1503
+ Small: GridInset
1504
+ Medium: GridInset
1505
+ Large: GridInset
1506
+ }
1507
+ }
1508
+
1509
+ // Grid uses List internally with a different visual representation
1510
+ export const Grid: GridType = (props) => {
1511
+ // Grid is essentially List with grid layout
1512
+ // We'll reuse the List component but with grid-specific styling
1513
+ const {
1514
+ columns = 5,
1515
+ aspectRatio = '1',
1516
+ fit = 'contain',
1517
+ inset,
1518
+ ...listProps
1519
+ } = props
1520
+
1521
+ return <List {...listProps} />
1522
+ }
1523
+
1524
+ // Grid.Item maps to List.Item but with content instead of icon
1525
+ Grid.Item = (props: GridItemProps) => {
1526
+ const { content, getDetailMarkdown, ...itemProps } = props
1527
+
1528
+ // Extract image value and tooltip
1529
+ const imageValue = typeof content === 'string' ? content : content?.value
1530
+ const imageTooltip =
1531
+ typeof content === 'object' ? content?.tooltip : undefined
1532
+
1533
+ // Convert Grid.Item props to List.Item props
1534
+ const listItemProps: ItemProps = {
1535
+ ...itemProps,
1536
+ // Grid items don't have accessories in Raycast
1537
+ accessories: undefined,
1538
+ // Use content as icon for now (in a real implementation, this would be rendered differently)
1539
+ icon: imageValue,
1540
+ }
1541
+
1542
+ return <List.Item {...listItemProps} />
1543
+ }
1544
+
1545
+ // Grid.Section maps to List.Section with grid-specific props
1546
+ Grid.Section = (props: GridSectionProps) => {
1547
+ const { columns, aspectRatio, fit, inset, ...sectionProps } = props
1548
+
1549
+ // Pass through to List.Section
1550
+ return <List.Section {...sectionProps} />
1551
+ }
1552
+
1553
+ // Reuse List's Dropdown
1554
+ Grid.Dropdown = List.Dropdown
1555
+
1556
+ // Reuse List's EmptyView
1557
+ Grid.EmptyView = List.EmptyView
1558
+
1559
+ // Grid Inset presets
1560
+ Grid.Inset = {
1561
+ Small: { top: 0, right: 0, bottom: 0, left: 0 },
1562
+ Medium: { top: 8, right: 8, bottom: 8, left: 8 },
1563
+ Large: { top: 16, right: 16, bottom: 16, left: 16 },
1564
+ }