termcast 1.3.21 → 1.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (429) hide show
  1. package/dist/ai.d.ts +104 -0
  2. package/dist/ai.d.ts.map +1 -0
  3. package/dist/ai.js +135 -0
  4. package/dist/ai.js.map +1 -0
  5. package/dist/apis/browser-extension.d.ts +18 -0
  6. package/dist/apis/browser-extension.d.ts.map +1 -0
  7. package/dist/apis/browser-extension.js +14 -0
  8. package/dist/apis/browser-extension.js.map +1 -0
  9. package/dist/apis/localstorage.d.ts.map +1 -1
  10. package/dist/apis/localstorage.js +4 -7
  11. package/dist/apis/localstorage.js.map +1 -1
  12. package/dist/apis/oauth.d.ts.map +1 -1
  13. package/dist/apis/oauth.js +5 -1
  14. package/dist/apis/oauth.js.map +1 -1
  15. package/dist/apis/preferences.d.ts.map +1 -1
  16. package/dist/apis/preferences.js +38 -19
  17. package/dist/apis/preferences.js.map +1 -1
  18. package/dist/apis/toast.d.ts +5 -0
  19. package/dist/apis/toast.d.ts.map +1 -1
  20. package/dist/apis/toast.js +7 -43
  21. package/dist/apis/toast.js.map +1 -1
  22. package/dist/build.d.ts.map +1 -1
  23. package/dist/build.js +3 -1
  24. package/dist/build.js.map +1 -1
  25. package/dist/cache.d.ts +32 -0
  26. package/dist/cache.d.ts.map +1 -0
  27. package/dist/cache.js +205 -0
  28. package/dist/cache.js.map +1 -0
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +87 -41
  31. package/dist/cli.js.map +1 -1
  32. package/dist/clipboard.d.ts +36 -0
  33. package/dist/clipboard.d.ts.map +1 -0
  34. package/dist/clipboard.js +154 -0
  35. package/dist/clipboard.js.map +1 -0
  36. package/dist/compile.d.ts +2 -1
  37. package/dist/compile.d.ts.map +1 -1
  38. package/dist/compile.js +31 -12
  39. package/dist/compile.js.map +1 -1
  40. package/dist/components/actions.d.ts.map +1 -1
  41. package/dist/components/actions.js +56 -30
  42. package/dist/components/actions.js.map +1 -1
  43. package/dist/components/detail.d.ts.map +1 -1
  44. package/dist/components/detail.js +4 -0
  45. package/dist/components/detail.js.map +1 -1
  46. package/dist/components/dropdown.d.ts.map +1 -1
  47. package/dist/components/dropdown.js +38 -15
  48. package/dist/components/dropdown.js.map +1 -1
  49. package/dist/components/extension-preferences.d.ts.map +1 -1
  50. package/dist/components/extension-preferences.js +40 -13
  51. package/dist/components/extension-preferences.js.map +1 -1
  52. package/dist/components/form/checkbox.d.ts.map +1 -1
  53. package/dist/components/form/checkbox.js +5 -3
  54. package/dist/components/form/checkbox.js.map +1 -1
  55. package/dist/components/form/date-picker.d.ts.map +1 -1
  56. package/dist/components/form/date-picker.js +5 -3
  57. package/dist/components/form/date-picker.js.map +1 -1
  58. package/dist/components/form/description.d.ts.map +1 -1
  59. package/dist/components/form/description.js +2 -2
  60. package/dist/components/form/description.js.map +1 -1
  61. package/dist/components/form/dropdown.d.ts.map +1 -1
  62. package/dist/components/form/dropdown.js +84 -80
  63. package/dist/components/form/dropdown.js.map +1 -1
  64. package/dist/components/form/file-autocomplete.d.ts +3 -6
  65. package/dist/components/form/file-autocomplete.d.ts.map +1 -1
  66. package/dist/components/form/file-autocomplete.js +61 -66
  67. package/dist/components/form/file-autocomplete.js.map +1 -1
  68. package/dist/components/form/file-picker.d.ts.map +1 -1
  69. package/dist/components/form/file-picker.js +33 -30
  70. package/dist/components/form/file-picker.js.map +1 -1
  71. package/dist/components/form/form-end.d.ts.map +1 -1
  72. package/dist/components/form/form-end.js +21 -1
  73. package/dist/components/form/form-end.js.map +1 -1
  74. package/dist/components/form/form-type-only.d.ts +174 -0
  75. package/dist/components/form/form-type-only.d.ts.map +1 -0
  76. package/dist/components/form/form-type-only.js +2 -0
  77. package/dist/components/form/form-type-only.js.map +1 -0
  78. package/dist/components/form/index.d.ts +3 -1
  79. package/dist/components/form/index.d.ts.map +1 -1
  80. package/dist/components/form/index.js +104 -30
  81. package/dist/components/form/index.js.map +1 -1
  82. package/dist/components/form/password-field.d.ts.map +1 -1
  83. package/dist/components/form/password-field.js +5 -3
  84. package/dist/components/form/password-field.js.map +1 -1
  85. package/dist/components/form/text-area.d.ts.map +1 -1
  86. package/dist/components/form/text-area.js +5 -3
  87. package/dist/components/form/text-area.js.map +1 -1
  88. package/dist/components/form/text-field.d.ts.map +1 -1
  89. package/dist/components/form/text-field.js +6 -4
  90. package/dist/components/form/text-field.js.map +1 -1
  91. package/dist/components/form/types.d.ts +5 -0
  92. package/dist/components/form/types.d.ts.map +1 -1
  93. package/dist/components/form/use-form-handling.d.ts +4 -0
  94. package/dist/components/form/use-form-handling.d.ts.map +1 -0
  95. package/dist/components/form/use-form-handling.js +37 -0
  96. package/dist/components/form/use-form-handling.js.map +1 -0
  97. package/dist/components/form/with-left-border.d.ts +2 -1
  98. package/dist/components/form/with-left-border.d.ts.map +1 -1
  99. package/dist/components/form/with-left-border.js +27 -3
  100. package/dist/components/form/with-left-border.js.map +1 -1
  101. package/dist/components/icon.d.ts +1 -0
  102. package/dist/components/icon.d.ts.map +1 -1
  103. package/dist/components/icon.js +24 -8
  104. package/dist/components/icon.js.map +1 -1
  105. package/dist/components/list.d.ts +2 -2
  106. package/dist/components/list.d.ts.map +1 -1
  107. package/dist/components/list.js +155 -70
  108. package/dist/components/list.js.map +1 -1
  109. package/dist/components/loading-bar.d.ts.map +1 -1
  110. package/dist/components/loading-bar.js +2 -2
  111. package/dist/components/loading-bar.js.map +1 -1
  112. package/dist/components/loading-text.d.ts +8 -0
  113. package/dist/components/loading-text.d.ts.map +1 -0
  114. package/dist/components/loading-text.js +58 -0
  115. package/dist/components/loading-text.js.map +1 -0
  116. package/dist/descendants.js +1 -1
  117. package/dist/descendants.js.map +1 -1
  118. package/dist/dev-ui.d.ts +7 -0
  119. package/dist/dev-ui.d.ts.map +1 -0
  120. package/dist/dev-ui.js +118 -0
  121. package/dist/dev-ui.js.map +1 -0
  122. package/dist/environment.d.ts +63 -0
  123. package/dist/environment.d.ts.map +1 -0
  124. package/dist/environment.js +189 -0
  125. package/dist/environment.js.map +1 -0
  126. package/dist/examples/datepicker.d.ts +2 -0
  127. package/dist/examples/datepicker.d.ts.map +1 -0
  128. package/dist/examples/datepicker.js +344 -0
  129. package/dist/examples/datepicker.js.map +1 -0
  130. package/dist/examples/file-autocomplete.vitest.d.ts +2 -0
  131. package/dist/examples/file-autocomplete.vitest.d.ts.map +1 -0
  132. package/dist/examples/file-autocomplete.vitest.js +223 -0
  133. package/dist/examples/file-autocomplete.vitest.js.map +1 -0
  134. package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
  135. package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
  136. package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
  137. package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
  138. package/dist/examples/form-basic.vitest.d.ts +2 -0
  139. package/dist/examples/form-basic.vitest.d.ts.map +1 -0
  140. package/dist/examples/form-basic.vitest.js +630 -0
  141. package/dist/examples/form-basic.vitest.js.map +1 -0
  142. package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
  143. package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
  144. package/dist/examples/form-dropdown-with-sections.js +13 -0
  145. package/dist/examples/form-dropdown-with-sections.js.map +1 -0
  146. package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
  147. package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
  148. package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
  149. package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
  150. package/dist/examples/form-dropdown.vitest.d.ts +2 -0
  151. package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
  152. package/dist/examples/form-dropdown.vitest.js +854 -0
  153. package/dist/examples/form-dropdown.vitest.js.map +1 -0
  154. package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
  155. package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
  156. package/dist/examples/form-multiselect-dropdown.js +13 -0
  157. package/dist/examples/form-multiselect-dropdown.js.map +1 -0
  158. package/dist/examples/form-scroll.d.ts.map +1 -1
  159. package/dist/examples/form-scroll.js +7 -1
  160. package/dist/examples/form-scroll.js.map +1 -1
  161. package/dist/examples/form-scroll.vitest.d.ts +2 -0
  162. package/dist/examples/form-scroll.vitest.d.ts.map +1 -0
  163. package/dist/examples/form-scroll.vitest.js +211 -0
  164. package/dist/examples/form-scroll.vitest.js.map +1 -0
  165. package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
  166. package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
  167. package/dist/examples/form-tagpicker.vitest.js +736 -0
  168. package/dist/examples/form-tagpicker.vitest.js.map +1 -0
  169. package/dist/examples/internal/descendants-filtering.js +1 -1
  170. package/dist/examples/internal/descendants-filtering.js.map +1 -1
  171. package/dist/examples/internal/descendants.js +1 -1
  172. package/dist/examples/internal/descendants.js.map +1 -1
  173. package/dist/examples/internal/nested-boxes.d.ts +2 -0
  174. package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
  175. package/dist/examples/internal/nested-boxes.js +7 -0
  176. package/dist/examples/internal/nested-boxes.js.map +1 -0
  177. package/dist/examples/internal/rhf-custom-ref.js +2 -2
  178. package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
  179. package/dist/examples/internal/scrollbox-demo.js +3 -22
  180. package/dist/examples/internal/scrollbox-demo.js.map +1 -1
  181. package/dist/examples/internal/scrollbox-descendants.d.ts +2 -0
  182. package/dist/examples/internal/scrollbox-descendants.d.ts.map +1 -0
  183. package/dist/examples/internal/scrollbox-descendants.js +83 -0
  184. package/dist/examples/internal/scrollbox-descendants.js.map +1 -0
  185. package/dist/examples/internal/scrollbox-with-descendants.js +4 -8
  186. package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
  187. package/dist/examples/internal/simple-scrollbox.vitest.d.ts +2 -0
  188. package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +1 -0
  189. package/dist/examples/internal/simple-scrollbox.vitest.js +96 -0
  190. package/dist/examples/internal/simple-scrollbox.vitest.js.map +1 -0
  191. package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
  192. package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
  193. package/dist/examples/internal/unicode-square-repro.js +7 -0
  194. package/dist/examples/internal/unicode-square-repro.js.map +1 -0
  195. package/dist/examples/list-detail-metadata.d.ts +2 -0
  196. package/dist/examples/list-detail-metadata.d.ts.map +1 -0
  197. package/dist/examples/list-detail-metadata.js +8 -0
  198. package/dist/examples/list-detail-metadata.js.map +1 -0
  199. package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
  200. package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
  201. package/dist/examples/list-dropdown-default.vitest.js +234 -0
  202. package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
  203. package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
  204. package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
  205. package/dist/examples/list-fetch-data.vitest.js +111 -0
  206. package/dist/examples/list-fetch-data.vitest.js.map +1 -0
  207. package/dist/examples/list-filter-navigation.d.ts +2 -0
  208. package/dist/examples/list-filter-navigation.d.ts.map +1 -0
  209. package/dist/examples/list-filter-navigation.js +8 -0
  210. package/dist/examples/list-filter-navigation.js.map +1 -0
  211. package/dist/examples/list-scrollbox.vitest.d.ts +2 -0
  212. package/dist/examples/list-scrollbox.vitest.d.ts.map +1 -0
  213. package/dist/examples/list-scrollbox.vitest.js +93 -0
  214. package/dist/examples/list-scrollbox.vitest.js.map +1 -0
  215. package/dist/examples/list-with-detail-long.d.ts +2 -0
  216. package/dist/examples/list-with-detail-long.d.ts.map +1 -0
  217. package/dist/examples/list-with-detail-long.js +53 -0
  218. package/dist/examples/list-with-detail-long.js.map +1 -0
  219. package/dist/examples/list-with-detail.vitest.d.ts +2 -0
  220. package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
  221. package/dist/examples/list-with-detail.vitest.js +434 -0
  222. package/dist/examples/list-with-detail.vitest.js.map +1 -0
  223. package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
  224. package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
  225. package/dist/examples/list-with-dropdown.vitest.js +337 -0
  226. package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
  227. package/dist/examples/list-with-sections.js +5 -1
  228. package/dist/examples/list-with-sections.js.map +1 -1
  229. package/dist/examples/list-with-sections.vitest.d.ts +2 -0
  230. package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
  231. package/dist/examples/list-with-sections.vitest.js +601 -0
  232. package/dist/examples/list-with-sections.vitest.js.map +1 -0
  233. package/dist/examples/scrollbox-vertical-centering.d.ts +6 -0
  234. package/dist/examples/scrollbox-vertical-centering.d.ts.map +1 -0
  235. package/dist/examples/scrollbox-vertical-centering.js +17 -0
  236. package/dist/examples/scrollbox-vertical-centering.js.map +1 -0
  237. package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
  238. package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
  239. package/dist/examples/simple-file-picker.vitest.js +678 -0
  240. package/dist/examples/simple-file-picker.vitest.js.map +1 -0
  241. package/dist/examples/simple-grid.vitest.d.ts +2 -0
  242. package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
  243. package/dist/examples/simple-grid.vitest.js +521 -0
  244. package/dist/examples/simple-grid.vitest.js.map +1 -0
  245. package/dist/examples/simple-navigation.js +10 -4
  246. package/dist/examples/simple-navigation.js.map +1 -1
  247. package/dist/examples/simple-navigation.vitest.d.ts +2 -0
  248. package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
  249. package/dist/examples/simple-navigation.vitest.js +718 -0
  250. package/dist/examples/simple-navigation.vitest.js.map +1 -0
  251. package/dist/examples/store.vitest.d.ts +2 -0
  252. package/dist/examples/store.vitest.d.ts.map +1 -0
  253. package/dist/examples/store.vitest.js +69 -0
  254. package/dist/examples/store.vitest.js.map +1 -0
  255. package/dist/examples/toast-variations.d.ts +2 -0
  256. package/dist/examples/toast-variations.d.ts.map +1 -0
  257. package/dist/examples/toast-variations.js +122 -0
  258. package/dist/examples/toast-variations.js.map +1 -0
  259. package/dist/extensions/dev.d.ts +4 -2
  260. package/dist/extensions/dev.d.ts.map +1 -1
  261. package/dist/extensions/dev.js +61 -10
  262. package/dist/extensions/dev.js.map +1 -1
  263. package/dist/extensions/dev.vitest.d.ts +2 -0
  264. package/dist/extensions/dev.vitest.d.ts.map +1 -0
  265. package/dist/extensions/dev.vitest.js +197 -0
  266. package/dist/extensions/dev.vitest.js.map +1 -0
  267. package/dist/extensions/home.d.ts.map +1 -1
  268. package/dist/extensions/home.js +3 -0
  269. package/dist/extensions/home.js.map +1 -1
  270. package/dist/home-command.d.ts +8 -0
  271. package/dist/home-command.d.ts.map +1 -0
  272. package/dist/home-command.js +181 -0
  273. package/dist/home-command.js.map +1 -0
  274. package/dist/hover-repro.d.ts +2 -0
  275. package/dist/hover-repro.d.ts.map +1 -0
  276. package/dist/hover-repro.js +20 -0
  277. package/dist/hover-repro.js.map +1 -0
  278. package/dist/index.d.ts +1 -0
  279. package/dist/index.d.ts.map +1 -1
  280. package/dist/index.js +2 -0
  281. package/dist/index.js.map +1 -1
  282. package/dist/internal/dialog.d.ts +1 -0
  283. package/dist/internal/dialog.d.ts.map +1 -1
  284. package/dist/internal/dialog.js +27 -18
  285. package/dist/internal/dialog.js.map +1 -1
  286. package/dist/internal/navigation.d.ts +9 -1
  287. package/dist/internal/navigation.d.ts.map +1 -1
  288. package/dist/internal/navigation.js +5 -5
  289. package/dist/internal/navigation.js.map +1 -1
  290. package/dist/internal/offscreen.d.ts +6 -0
  291. package/dist/internal/offscreen.d.ts.map +1 -0
  292. package/dist/internal/offscreen.js +10 -0
  293. package/dist/internal/offscreen.js.map +1 -0
  294. package/dist/internal/providers.d.ts.map +1 -1
  295. package/dist/internal/providers.js +2 -2
  296. package/dist/internal/providers.js.map +1 -1
  297. package/dist/internal/scrollbox.d.ts +1 -10
  298. package/dist/internal/scrollbox.d.ts.map +1 -1
  299. package/dist/internal/scrollbox.js +2 -1
  300. package/dist/internal/scrollbox.js.map +1 -1
  301. package/dist/localstorage.d.ts +13 -0
  302. package/dist/localstorage.d.ts.map +1 -0
  303. package/dist/localstorage.js +190 -0
  304. package/dist/localstorage.js.map +1 -0
  305. package/dist/oauth.d.ts +142 -0
  306. package/dist/oauth.d.ts.map +1 -0
  307. package/dist/oauth.js +551 -0
  308. package/dist/oauth.js.map +1 -0
  309. package/dist/preferences.d.ts +23 -0
  310. package/dist/preferences.d.ts.map +1 -0
  311. package/dist/preferences.js +105 -0
  312. package/dist/preferences.js.map +1 -0
  313. package/dist/release.d.ts +1 -1
  314. package/dist/release.d.ts.map +1 -1
  315. package/dist/release.js +29 -33
  316. package/dist/release.js.map +1 -1
  317. package/dist/state.d.ts +2 -0
  318. package/dist/state.d.ts.map +1 -1
  319. package/dist/state.js +3 -0
  320. package/dist/state.js.map +1 -1
  321. package/dist/store.d.ts +21 -0
  322. package/dist/store.d.ts.map +1 -0
  323. package/dist/store.js +84 -0
  324. package/dist/store.js.map +1 -0
  325. package/dist/swift-loader.d.ts +3 -0
  326. package/dist/swift-loader.d.ts.map +1 -0
  327. package/dist/swift-loader.js +193 -0
  328. package/dist/swift-loader.js.map +1 -0
  329. package/dist/swift-runtime.d.ts +2 -0
  330. package/dist/swift-runtime.d.ts.map +1 -0
  331. package/dist/swift-runtime.js +27 -0
  332. package/dist/swift-runtime.js.map +1 -0
  333. package/dist/toast.d.ts +44 -0
  334. package/dist/toast.d.ts.map +1 -0
  335. package/dist/toast.js +221 -0
  336. package/dist/toast.js.map +1 -0
  337. package/dist/utils/file-system.d.ts +9 -0
  338. package/dist/utils/file-system.d.ts.map +1 -1
  339. package/dist/utils/file-system.js +49 -0
  340. package/dist/utils/file-system.js.map +1 -1
  341. package/dist/utils/run-command.d.ts +26 -1
  342. package/dist/utils/run-command.d.ts.map +1 -1
  343. package/dist/utils/run-command.js +53 -4
  344. package/dist/utils/run-command.js.map +1 -1
  345. package/dist/window.d.ts +12 -0
  346. package/dist/window.d.ts.map +1 -0
  347. package/dist/window.js +48 -0
  348. package/dist/window.js.map +1 -0
  349. package/package.json +12 -12
  350. package/src/apis/browser-extension.tsx +29 -0
  351. package/src/apis/localstorage.test.ts +14 -6
  352. package/src/apis/localstorage.tsx +8 -5
  353. package/src/apis/oauth.tsx +5 -1
  354. package/src/apis/preferences.tsx +48 -22
  355. package/src/apis/toast.tsx +37 -62
  356. package/src/build.test.tsx +52 -0
  357. package/src/build.tsx +3 -1
  358. package/src/cli.tsx +99 -48
  359. package/src/compile.tsx +32 -11
  360. package/src/components/actions.tsx +94 -32
  361. package/src/components/detail.tsx +4 -0
  362. package/src/components/dropdown.tsx +47 -14
  363. package/src/components/extension-preferences.tsx +44 -16
  364. package/src/components/form/checkbox.tsx +12 -6
  365. package/src/components/form/date-picker.tsx +12 -6
  366. package/src/components/form/description.tsx +7 -2
  367. package/src/components/form/dropdown.tsx +131 -119
  368. package/src/components/form/file-autocomplete.tsx +90 -108
  369. package/src/components/form/file-picker.tsx +54 -43
  370. package/src/components/form/form-end.tsx +23 -2
  371. package/src/components/form/index.tsx +168 -41
  372. package/src/components/form/password-field.tsx +12 -6
  373. package/src/components/form/text-area.tsx +13 -6
  374. package/src/components/form/text-field.tsx +13 -6
  375. package/src/components/form/types.tsx +6 -0
  376. package/src/components/form/with-left-border.tsx +41 -8
  377. package/src/components/icon.tsx +27 -8
  378. package/src/components/list.tsx +243 -101
  379. package/src/components/loading-bar.tsx +3 -2
  380. package/src/components/loading-text.tsx +79 -0
  381. package/src/descendants.tsx +1 -0
  382. package/src/examples/file-autocomplete.vitest.tsx +130 -125
  383. package/src/examples/form-basic.vitest.tsx +376 -176
  384. package/src/examples/form-dropdown.vitest.tsx +126 -126
  385. package/src/examples/form-scroll.tsx +2 -0
  386. package/src/examples/form-scroll.vitest.tsx +58 -58
  387. package/src/examples/form-tagpicker.vitest.tsx +99 -99
  388. package/src/examples/internal/descendants-filtering.tsx +1 -0
  389. package/src/examples/internal/descendants.tsx +1 -0
  390. package/src/examples/internal/rhf-custom-ref.tsx +2 -0
  391. package/src/examples/internal/scrollbox-demo.tsx +3 -27
  392. package/src/examples/internal/scrollbox-with-descendants.tsx +4 -7
  393. package/src/examples/internal/simple-scrollbox.vitest.tsx +7 -5
  394. package/src/examples/list-detail-metadata.tsx +49 -0
  395. package/src/examples/list-detail-metadata.vitest.tsx +88 -0
  396. package/src/examples/list-dropdown-default.vitest.tsx +51 -51
  397. package/src/examples/list-fetch-data.vitest.tsx +4 -4
  398. package/src/examples/list-scrollbox.vitest.tsx +73 -14
  399. package/src/examples/list-with-detail-long.tsx +70 -0
  400. package/src/examples/list-with-detail.vitest.tsx +191 -85
  401. package/src/examples/list-with-dropdown.vitest.tsx +53 -53
  402. package/src/examples/list-with-sections.tsx +1 -0
  403. package/src/examples/list-with-sections.vitest.tsx +221 -97
  404. package/src/examples/list-with-toast.vitest.tsx +16 -16
  405. package/src/examples/simple-file-picker.vitest.tsx +63 -469
  406. package/src/examples/simple-grid.vitest.tsx +238 -233
  407. package/src/examples/simple-navigation.tsx +15 -7
  408. package/src/examples/simple-navigation.vitest.tsx +130 -219
  409. package/src/examples/store.vitest.tsx +1 -1
  410. package/src/examples/swift-extension.vitest.tsx +148 -0
  411. package/src/examples/synonyms.vitest.tsx +159 -0
  412. package/src/examples/toast-variations.tsx +150 -0
  413. package/src/examples/toast-variations.vitest.tsx +370 -0
  414. package/src/extensions/dev.tsx +74 -7
  415. package/src/extensions/dev.vitest.tsx +102 -34
  416. package/src/extensions/home.tsx +6 -0
  417. package/src/index.tsx +3 -0
  418. package/src/internal/dialog.tsx +43 -30
  419. package/src/internal/navigation.tsx +3 -1
  420. package/src/internal/offscreen.tsx +15 -0
  421. package/src/internal/providers.tsx +4 -2
  422. package/src/internal/scrollbox.tsx +4 -8
  423. package/src/keyboard.test.tsx +69 -0
  424. package/src/release.tsx +32 -38
  425. package/src/state.tsx +7 -0
  426. package/src/swift-loader.tsx +239 -0
  427. package/src/swift-runtime.tsx +36 -0
  428. package/src/utils/file-system.ts +61 -0
  429. package/src/utils/run-command.tsx +82 -6
package/src/cli.tsx CHANGED
@@ -2,10 +2,11 @@ import fs from 'node:fs'
2
2
  import path from 'node:path'
3
3
  import { execSync, spawn } from 'node:child_process'
4
4
  import { cac } from 'cac'
5
- import chokidar from 'chokidar'
5
+ import * as watcher from '@parcel/watcher'
6
6
  import { buildExtensionCommands } from './build'
7
7
  import { logger } from './logger'
8
8
  import { installExtension } from './utils'
9
+ import { searchStoreListings } from './store-api/search'
9
10
  import './globals'
10
11
  import { startDevMode, triggerRebuild } from './extensions/dev'
11
12
  import { compileExtension } from './compile'
@@ -66,13 +67,22 @@ checkForUpdates()
66
67
 
67
68
  cli
68
69
  .command('dev [path]', 'Run the extension in the current working directory')
69
- .action(async (extensionPath, options) => {
70
+ .action(async (rawExtensionPath, options) => {
70
71
  try {
71
- extensionPath = path.resolve(extensionPath || process.cwd())
72
+ // Check if the provided arg looks like a path (contains / or . or is existing dir)
73
+ const looksLikePath = rawExtensionPath && (
74
+ rawExtensionPath.includes('/') ||
75
+ rawExtensionPath.startsWith('.') ||
76
+ fs.existsSync(rawExtensionPath)
77
+ )
78
+ const extensionPath = path.resolve(looksLikePath ? rawExtensionPath : process.cwd())
72
79
  let isBuilding = false
73
80
 
74
81
  // Start dev mode with initial render
75
- await startDevMode({ extensionPath })
82
+ // Skip args up to and including "dev" subcommand, plus path if it looks like one
83
+ const devIndex = process.argv.findIndex((arg) => arg === 'dev')
84
+ const skipArgv = devIndex - 1 + (looksLikePath ? 1 : 0)
85
+ await startDevMode({ extensionPath, skipArgv })
76
86
 
77
87
  logger.log(`dev mode started`)
78
88
  // Only watch if running in a TTY (interactive terminal)
@@ -83,38 +93,18 @@ cli
83
93
 
84
94
  console.log('\nWatching for file changes...')
85
95
 
86
- // Watch entire extension directory
87
- const watcher = chokidar.watch(extensionPath, {
88
- persistent: true,
89
- ignoreInitial: true,
90
- awaitWriteFinish: {
91
- stabilityThreshold: 300,
92
- pollInterval: 100,
93
- },
94
- })
95
-
96
+ // Watch entire extension directory using @parcel/watcher
96
97
  const ignoredPatterns = [
97
- 'node_modules',
98
- '.termcast-bundle',
99
- '.git',
100
- 'app.log',
101
- 'dist',
102
- 'build',
98
+ '**/node_modules/**',
99
+ '**/.termcast-bundle/**',
100
+ '**/.git/**',
101
+ '**/.build/**', // Swift build output
102
+ '**/*.log',
103
+ '**/dist/**',
104
+ '**/build/**',
103
105
  ]
104
106
 
105
- const shouldIgnore = (filePath: string) => {
106
- const relativePath = path.relative(extensionPath, filePath)
107
- return ignoredPatterns.some(
108
- (pattern) =>
109
- relativePath.includes(pattern) || filePath.endsWith('.log'),
110
- )
111
- }
112
-
113
107
  const rebuild = async (filePath: string) => {
114
- if (shouldIgnore(filePath)) {
115
- return
116
- }
117
-
118
108
  if (isBuilding) {
119
109
  logger.log('Build already in progress, skipping...')
120
110
  return
@@ -133,11 +123,28 @@ cli
133
123
  }
134
124
  }
135
125
 
136
- watcher
137
- .on('change', rebuild)
138
- .on('add', rebuild)
139
- .on('unlink', rebuild)
140
- .on('error', (error) => logger.error('Watcher error:', error))
126
+ const subscription = await watcher.subscribe(
127
+ extensionPath,
128
+ (err, events) => {
129
+ if (err) {
130
+ logger.error('Watcher error:', err)
131
+ return
132
+ }
133
+ // Trigger rebuild for any event (create, update, delete)
134
+ if (events.length > 0) {
135
+ rebuild(events[0].path)
136
+ }
137
+ },
138
+ { ignore: ignoredPatterns }
139
+ )
140
+
141
+ // Clean up watcher on exit signals
142
+ const cleanup = async () => {
143
+ await subscription.unsubscribe()
144
+ process.exit(0)
145
+ }
146
+ process.on('SIGINT', cleanup)
147
+ process.on('SIGTERM', cleanup)
141
148
  } catch (e: any) {
142
149
  console.error('Failed to start dev mode:', e?.message || e)
143
150
  logger.error(e)
@@ -215,7 +222,7 @@ cli
215
222
  single: options.single,
216
223
  })
217
224
 
218
- console.log(`\nRelease complete: v${result.version}`)
225
+ console.log(`\nRelease complete: ${result.tag}`)
219
226
  console.log(`Uploaded ${result.uploadedFiles.length} binaries`)
220
227
  } catch (error: any) {
221
228
  console.error('Release failed:', error.message)
@@ -365,17 +372,54 @@ cli
365
372
  }
366
373
  })
367
374
 
375
+ cli
376
+ .command('raycast-search <query>', 'Search for extensions in the Raycast store')
377
+ .option('-n, --limit <number>', 'Number of results to show', { default: '10' })
378
+ .action(async (query: string, options: { limit: string }) => {
379
+ try {
380
+ const limit = parseInt(options.limit, 10)
381
+ const result = await searchStoreListings({ query, perPage: limit })
382
+
383
+ if (result.data.length === 0) {
384
+ console.log(`No extensions found for "${query}"`)
385
+ process.exit(0)
386
+ }
387
+
388
+ console.log(`Found ${result.data.length} extensions for "${query}":\n`)
389
+
390
+ for (const ext of result.data) {
391
+ const downloads = ext.download_count.toLocaleString()
392
+ const commands = ext.commands.map((c) => c.name).join(', ')
393
+ console.log(` ${ext.name}`)
394
+ console.log(` Path: extensions/${ext.relative_path.replace('extensions/', '')}`)
395
+ console.log(` Downloads: ${downloads}`)
396
+ console.log(` Commands: ${commands || 'none'}`)
397
+ console.log(` Description: ${ext.description.slice(0, 100)}${ext.description.length > 100 ? '...' : ''}`)
398
+ console.log()
399
+ }
400
+
401
+ console.log(`Download with: termcast download <extension-name>`)
402
+ process.exit(0)
403
+ } catch (error: any) {
404
+ console.error('Search failed:', error.message)
405
+ process.exit(1)
406
+ }
407
+ })
408
+
368
409
  cli
369
410
  .command('download <extensionName>', 'Download extension from Raycast extensions repo')
370
411
  .option('-o, --output <path>', 'Output directory', { default: '.' })
371
- .action(async (extensionName: string, options: { output: string }) => {
412
+ .option('--no-dir', 'Put files directly in output directory instead of creating extension subdirectory')
413
+ .action(async (extensionName: string, options: { output: string; dir: boolean }) => {
372
414
  try {
373
415
  const destPath = path.resolve(options.output)
374
- const extensionDir = path.join(destPath, extensionName)
416
+ // When --no-dir is passed, dir is false; put files directly in destPath
417
+ const extensionDir = options.dir ? path.join(destPath, extensionName) : destPath
418
+ const tempCloneDir = path.join(destPath, `.tmp-${extensionName}-${Date.now()}`)
375
419
 
376
420
  console.log(`Downloading extension '${extensionName}' from raycast/extensions...`)
377
421
 
378
- if (fs.existsSync(extensionDir)) {
422
+ if (options.dir && fs.existsSync(extensionDir)) {
379
423
  console.log(`Removing existing directory: ${extensionDir}`)
380
424
  fs.rmSync(extensionDir, { recursive: true, force: true })
381
425
  }
@@ -383,7 +427,8 @@ cli
383
427
  fs.mkdirSync(destPath, { recursive: true })
384
428
 
385
429
  const repoUrl = 'https://github.com/raycast/extensions.git'
386
- const cloneCmd = `git clone -n --depth=1 --filter=tree:0 "${repoUrl}" "${extensionName}"`
430
+ const cloneDirName = path.basename(tempCloneDir)
431
+ const cloneCmd = `git clone -n --depth=1 --filter=tree:0 "${repoUrl}" "${cloneDirName}"`
387
432
  console.log(`Running: ${cloneCmd}`)
388
433
  try {
389
434
  execSync(cloneCmd, {
@@ -399,11 +444,12 @@ cli
399
444
  console.log(`Running: ${sparseCmd}`)
400
445
  try {
401
446
  execSync(sparseCmd, {
402
- cwd: extensionDir,
447
+ cwd: tempCloneDir,
403
448
  stdio: 'inherit',
404
449
  })
405
450
  } catch (error) {
406
451
  console.error(`Failed to set sparse-checkout`)
452
+ fs.rmSync(tempCloneDir, { recursive: true, force: true })
407
453
  process.exit(1)
408
454
  }
409
455
 
@@ -411,22 +457,27 @@ cli
411
457
  console.log(`Running: ${checkoutCmd}`)
412
458
  try {
413
459
  execSync(checkoutCmd, {
414
- cwd: extensionDir,
460
+ cwd: tempCloneDir,
415
461
  stdio: 'inherit',
416
462
  })
417
463
  } catch (error) {
418
464
  console.error(`Failed to checkout files`)
465
+ fs.rmSync(tempCloneDir, { recursive: true, force: true })
419
466
  process.exit(1)
420
467
  }
421
468
 
422
- const extensionPath = path.join(extensionDir, 'extensions', extensionName)
469
+ const extensionPath = path.join(tempCloneDir, 'extensions', extensionName)
423
470
 
424
471
  if (!fs.existsSync(extensionPath)) {
425
472
  console.error(`Extension '${extensionName}' not found in raycast/extensions repo`)
426
- fs.rmSync(extensionDir, { recursive: true, force: true })
473
+ fs.rmSync(tempCloneDir, { recursive: true, force: true })
427
474
  process.exit(1)
428
475
  }
429
476
 
477
+ // Move files to final destination
478
+ if (options.dir) {
479
+ fs.mkdirSync(extensionDir, { recursive: true })
480
+ }
430
481
  const filesToMove = fs.readdirSync(extensionPath)
431
482
  for (const file of filesToMove) {
432
483
  const src = path.join(extensionPath, file)
@@ -434,8 +485,8 @@ cli
434
485
  fs.renameSync(src, dest)
435
486
  }
436
487
 
437
- fs.rmSync(path.join(extensionDir, 'extensions'), { recursive: true, force: true })
438
- fs.rmSync(path.join(extensionDir, '.git'), { recursive: true, force: true })
488
+ // Clean up temp clone directory
489
+ fs.rmSync(tempCloneDir, { recursive: true, force: true })
439
490
 
440
491
  console.log(`\nInstalling dependencies...`)
441
492
  execSync('npm install', {
package/src/compile.tsx CHANGED
@@ -3,6 +3,7 @@ import path from 'node:path'
3
3
  import type { BunPlugin } from 'bun'
4
4
  import { logger } from './logger'
5
5
  import { getCommandsWithFiles } from './package-json'
6
+ import { swiftLoaderPlugin } from './swift-loader'
6
7
 
7
8
  const raycastAliasPlugin: BunPlugin = {
8
9
  name: 'raycast-to-termcast',
@@ -53,6 +54,7 @@ ${commandsArray}
53
54
  await startCompiledExtension({
54
55
  extensionPath: ${JSON.stringify(extensionPath)},
55
56
  compiledCommands,
57
+ skipArgv: 0,
56
58
  });
57
59
  }
58
60
 
@@ -103,15 +105,15 @@ export function getArchiveExtension(target: CompileTarget): string {
103
105
  export const ALL_TARGETS: CompileTarget[] = [
104
106
  { os: 'linux', arch: 'arm64' },
105
107
  { os: 'linux', arch: 'x64' },
106
- { os: 'linux', arch: 'x64', avx2: false },
107
- { os: 'linux', arch: 'arm64', abi: 'musl' },
108
- { os: 'linux', arch: 'x64', abi: 'musl' },
109
- { os: 'linux', arch: 'x64', abi: 'musl', avx2: false },
108
+ // { os: 'linux', arch: 'x64', avx2: false },
109
+ // { os: 'linux', arch: 'arm64', abi: 'musl' },
110
+ // { os: 'linux', arch: 'x64', abi: 'musl' },
111
+ // { os: 'linux', arch: 'x64', abi: 'musl', avx2: false },
110
112
  { os: 'darwin', arch: 'arm64' },
111
113
  { os: 'darwin', arch: 'x64' },
112
- { os: 'darwin', arch: 'x64', avx2: false },
114
+ // { os: 'darwin', arch: 'x64', avx2: false },
113
115
  { os: 'win32', arch: 'x64' },
114
- { os: 'win32', arch: 'x64', avx2: false },
116
+ // { os: 'win32', arch: 'x64', avx2: false },
115
117
  ]
116
118
 
117
119
  export function getCurrentTarget(): CompileTarget {
@@ -125,6 +127,7 @@ export interface CompileOptions {
125
127
  outfile?: string
126
128
  minify?: boolean
127
129
  target?: CompileTarget
130
+ version?: string
128
131
  }
129
132
 
130
133
  export interface CompileResult {
@@ -137,6 +140,7 @@ export async function compileExtension({
137
140
  outfile,
138
141
  minify = false,
139
142
  target,
143
+ version,
140
144
  }: CompileOptions): Promise<CompileResult> {
141
145
  const resolvedPath = path.resolve(extensionPath)
142
146
 
@@ -162,6 +166,7 @@ export async function compileExtension({
162
166
  if (!fs.existsSync(bundleDir)) {
163
167
  fs.mkdirSync(bundleDir, { recursive: true })
164
168
  }
169
+ fs.writeFileSync(path.join(bundleDir, '.gitignore'), '*\n')
165
170
 
166
171
  const entryCode = generateEntryCode({
167
172
  extensionPath: resolvedPath,
@@ -184,18 +189,34 @@ export async function compileExtension({
184
189
  entrypoints: [tempEntryPath],
185
190
  target: bunTarget as 'bun',
186
191
  minify,
192
+ // bytecode: true, // TODO need to wait for opentui to support this removing top level await
193
+ sourcemap: 'external',
187
194
  compile: {
188
195
  outfile: defaultOutfile,
189
196
  },
190
- plugins: [raycastAliasPlugin],
197
+ define: { 'process.env.VERSION': JSON.stringify(version || '') },
198
+ plugins: [raycastAliasPlugin, swiftLoaderPlugin],
191
199
  throw: false,
192
200
  } as Parameters<typeof Bun.build>[0])
193
201
 
194
202
  if (!result.success) {
195
- const errorMessage = result.logs
196
- .map((log: any) => log.message || String(log))
197
- .join('\n')
198
- throw new Error(`Compile failed: ${errorMessage || 'Unknown error'}`)
203
+ const errorDetails = result.logs.map((log: any) => {
204
+ const parts = [log.message || String(log)]
205
+ if (log.position) {
206
+ parts.push(` at ${log.position.file}:${log.position.line}:${log.position.column}`)
207
+ }
208
+ if (log.notes) {
209
+ for (const note of log.notes) {
210
+ parts.push(` note: ${note.text}`)
211
+ if (note.location) {
212
+ parts.push(` at ${note.location.file}:${note.location.line}`)
213
+ }
214
+ }
215
+ }
216
+ return parts.join('\n')
217
+ })
218
+ logger.log('Compile errors:', JSON.stringify(result.logs, null, 2))
219
+ throw new Error(`Compile failed: ${errorDetails.join('\n\n') || 'Unknown error'}`)
199
220
  }
200
221
 
201
222
  logger.log('Build outputs:', result.outputs?.map((o) => o.path))
@@ -17,8 +17,12 @@ import {
17
17
  moveToTrash,
18
18
  } from 'termcast/src/action-utils'
19
19
  import { useDialog } from 'termcast/src/internal/dialog'
20
+ import { useNavigation } from 'termcast/src/internal/navigation'
20
21
  import { Dropdown } from 'termcast/src/components/dropdown'
22
+ import { ExtensionPreferences } from 'termcast/src/components/extension-preferences'
23
+ import { useStore } from 'termcast/src/state'
21
24
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
25
+ import { useIsOffscreen } from 'termcast/src/internal/offscreen'
22
26
  import { CommonProps } from 'termcast/src/utils'
23
27
  import type {
24
28
  KeyboardShortcut,
@@ -180,7 +184,7 @@ const Action: ActionType = (props) => {
180
184
 
181
185
  const isDestructive = props.style === ActionStyle.Destructive
182
186
 
183
- // Render as Dropdown.Item
187
+ // Render as Dropdown.Item (handles offscreen check internally)
184
188
  return (
185
189
  <Dropdown.Item
186
190
  title={props.title}
@@ -210,7 +214,7 @@ Action.Push = (props) => {
210
214
  },
211
215
  })
212
216
 
213
- // Render as Dropdown.Item
217
+ // Render as Dropdown.Item (handles offscreen check internally)
214
218
  return (
215
219
  <Dropdown.Item
216
220
  title={props.title}
@@ -243,7 +247,7 @@ Action.CopyToClipboard = (props) => {
243
247
  },
244
248
  })
245
249
 
246
- // Render as Dropdown.Item
250
+ // Render as Dropdown.Item (handles offscreen check internally)
247
251
  return (
248
252
  <Dropdown.Item
249
253
  title={props.title}
@@ -270,7 +274,7 @@ Action.OpenInBrowser = (props) => {
270
274
  },
271
275
  })
272
276
 
273
- // Render as Dropdown.Item
277
+ // Render as Dropdown.Item (handles offscreen check internally)
274
278
  return (
275
279
  <Dropdown.Item
276
280
  title={props.title}
@@ -298,7 +302,7 @@ Action.Open = (props) => {
298
302
  },
299
303
  })
300
304
 
301
- // Render as Dropdown.Item
305
+ // Render as Dropdown.Item (handles offscreen check internally)
302
306
  return (
303
307
  <Dropdown.Item
304
308
  title={props.title}
@@ -328,7 +332,7 @@ Action.Paste = (props) => {
328
332
  },
329
333
  })
330
334
 
331
- // Render as Dropdown.Item
335
+ // Render as Dropdown.Item (handles offscreen check internally)
332
336
  return (
333
337
  <Dropdown.Item
334
338
  title={props.title}
@@ -355,7 +359,7 @@ Action.ShowInFinder = (props) => {
355
359
  },
356
360
  })
357
361
 
358
- // Render as Dropdown.Item
362
+ // Render as Dropdown.Item (handles offscreen check internally)
359
363
  return (
360
364
  <Dropdown.Item
361
365
  title={props.title || 'Show in Finder'}
@@ -382,7 +386,7 @@ Action.OpenWith = (props) => {
382
386
  },
383
387
  })
384
388
 
385
- // Render as Dropdown.Item
389
+ // Render as Dropdown.Item (handles offscreen check internally)
386
390
  return (
387
391
  <Dropdown.Item
388
392
  title={props.title || `Open with ${props.application}`}
@@ -413,7 +417,7 @@ Action.Trash = (props) => {
413
417
  },
414
418
  })
415
419
 
416
- // Render as Dropdown.Item
420
+ // Render as Dropdown.Item (handles offscreen check internally)
417
421
  return (
418
422
  <Dropdown.Item
419
423
  title={props.title || 'Move to Trash'}
@@ -448,7 +452,7 @@ Action.SubmitForm = (props) => {
448
452
  },
449
453
  })
450
454
 
451
- // Render as Dropdown.Item
455
+ // Render as Dropdown.Item (handles offscreen check internally)
452
456
  return (
453
457
  <Dropdown.Item
454
458
  title={props.title || 'Submit'}
@@ -480,7 +484,7 @@ Action.CreateSnippet = (props) => {
480
484
  },
481
485
  })
482
486
 
483
- // Render as Dropdown.Item
487
+ // Render as Dropdown.Item (handles offscreen check internally)
484
488
  return (
485
489
  <Dropdown.Item
486
490
  title={props.title || 'Create Snippet'}
@@ -510,7 +514,7 @@ Action.CreateQuicklink = (props) => {
510
514
  },
511
515
  })
512
516
 
513
- // Render as Dropdown.Item
517
+ // Render as Dropdown.Item (handles offscreen check internally)
514
518
  return (
515
519
  <Dropdown.Item
516
520
  title={props.title || 'Create Quicklink'}
@@ -540,7 +544,7 @@ Action.ToggleQuickLook = (props) => {
540
544
  },
541
545
  })
542
546
 
543
- // Render as Dropdown.Item
547
+ // Render as Dropdown.Item (handles offscreen check internally)
544
548
  return (
545
549
  <Dropdown.Item
546
550
  title={props.title || 'Quick Look'}
@@ -552,8 +556,6 @@ Action.ToggleQuickLook = (props) => {
552
556
  }
553
557
 
554
558
  Action.PickDate = (props) => {
555
- const dialog = useDialog()
556
-
557
559
  // Register as descendant with execute function
558
560
  useActionDescendant({
559
561
  title: props.title || 'Pick Date',
@@ -570,7 +572,7 @@ Action.PickDate = (props) => {
570
572
  },
571
573
  })
572
574
 
573
- // Render as Dropdown.Item
575
+ // Render as Dropdown.Item (handles offscreen check internally)
574
576
  return (
575
577
  <Dropdown.Item
576
578
  title={props.title || 'Pick Date'}
@@ -631,31 +633,55 @@ function formatShortcut(
631
633
  const ActionPanel: ActionPanelType = (props) => {
632
634
  const { children, title } = props
633
635
  const dialog = useDialog()
636
+ const { push } = useNavigation()
634
637
  const inFocus = useIsInFocus()
638
+ const isOffscreen = useIsOffscreen()
635
639
  const descendantsContext = useActionDescendants()
636
640
 
641
+ // Get extension and command info for configure actions
642
+ const extensionPackageJson = useStore((state) => state.extensionPackageJson)
643
+ const currentCommandName = useStore((state) => state.currentCommandName)
644
+
645
+ const hasExtensionPrefs =
646
+ (extensionPackageJson?.preferences?.length ?? 0) > 0
647
+ const currentCommand = extensionPackageJson?.commands?.find(
648
+ (c) => c.name === currentCommandName,
649
+ )
650
+ const hasCommandPrefs = (currentCommand?.preferences?.length ?? 0) > 0
651
+
637
652
  // Create context value
638
653
  const contextValue = useMemo<ActionPanelContextValue>(
639
654
  () => ({ currentSection: undefined }),
640
655
  [],
641
656
  )
642
657
 
643
- // prevent showing actions if no dialog is shown
644
- if (!dialog.stack.length) return null
645
- // if (!inFocus) return
646
-
647
- // Check if there's only one action and execute it immediately
658
+ // Auto-execute first action if flag is set (triggered by enter/ctrl+enter)
659
+ // Also report first action title when rendered offscreen
648
660
  useLayoutEffect(() => {
649
661
  const allActions = Object.values(descendantsContext.map.current)
650
662
  .filter((item: any) => item.index !== -1)
651
663
  .map((item: any) => item.props as ActionDescendant)
652
664
 
653
- if (allActions.length === 1) {
654
- logger.log(`Auto-executing single action: ${allActions[0].title}`)
665
+ // When offscreen, just report first action title for footer display
666
+ if (isOffscreen) {
667
+ useStore.setState({ firstActionTitle: allActions[0]?.title ?? '' })
668
+ return
669
+ }
670
+
671
+ const shouldExecute = useStore.getState().shouldAutoExecuteFirstAction
672
+ useStore.setState({ shouldAutoExecuteFirstAction: false })
673
+
674
+ if (!shouldExecute) return
675
+
676
+ if (allActions[0]) {
677
+ logger.log(`Auto-executing first action: ${allActions[0].title}`)
655
678
  dialog.clear()
656
679
  allActions[0].execute()
657
680
  }
658
- }, [descendantsContext.map, dialog])
681
+ }, [descendantsContext.map, dialog, isOffscreen])
682
+
683
+ // prevent showing actions if no dialog is shown (must be after hooks)
684
+ if (!dialog.stack.length && !isOffscreen) return null
659
685
 
660
686
  // ActionPanel renders as Dropdown with children
661
687
  return (
@@ -680,6 +706,38 @@ const ActionPanel: ActionPanelType = (props) => {
680
706
  }}
681
707
  >
682
708
  {children}
709
+ {(hasExtensionPrefs || hasCommandPrefs) && (
710
+ <ActionPanel.Section title="Settings">
711
+ {hasExtensionPrefs && (
712
+ <Action
713
+ title="Configure Extension..."
714
+ shortcut={{ modifiers: ['cmd', 'shift'], key: ',' }}
715
+ onAction={() => {
716
+ dialog.clear()
717
+ push(
718
+ <ExtensionPreferences
719
+ extensionName={extensionPackageJson!.name}
720
+ />,
721
+ )
722
+ }}
723
+ />
724
+ )}
725
+ {hasCommandPrefs && (
726
+ <Action
727
+ title="Configure Command..."
728
+ onAction={() => {
729
+ dialog.clear()
730
+ push(
731
+ <ExtensionPreferences
732
+ extensionName={extensionPackageJson!.name}
733
+ commandName={currentCommandName!}
734
+ />,
735
+ )
736
+ }}
737
+ />
738
+ )}
739
+ </ActionPanel.Section>
740
+ )}
683
741
  </Dropdown>
684
742
  </ActionPanelContext.Provider>
685
743
  </ActionDescendantsProvider>
@@ -698,11 +756,13 @@ ActionPanel.Section = (props) => {
698
756
  [parentContext, props.title],
699
757
  )
700
758
 
701
- // Section provides context to its children
759
+ // Section renders Dropdown.Section and provides context to its children
702
760
  return (
703
- <ActionPanelContext.Provider value={sectionContextValue}>
704
- {props.children}
705
- </ActionPanelContext.Provider>
761
+ <Dropdown.Section title={props.title}>
762
+ <ActionPanelContext.Provider value={sectionContextValue}>
763
+ {props.children}
764
+ </ActionPanelContext.Provider>
765
+ </Dropdown.Section>
706
766
  )
707
767
  }
708
768
 
@@ -718,11 +778,13 @@ ActionPanel.Submenu = (props) => {
718
778
  [parentContext, props.title],
719
779
  )
720
780
 
721
- // Submenu provides context to its children
781
+ // Submenu renders Dropdown.Section and provides context to its children
722
782
  return (
723
- <ActionPanelContext.Provider value={submenuContextValue}>
724
- {props.children}
725
- </ActionPanelContext.Provider>
783
+ <Dropdown.Section title={props.title}>
784
+ <ActionPanelContext.Provider value={submenuContextValue}>
785
+ {props.children}
786
+ </ActionPanelContext.Provider>
787
+ </Dropdown.Section>
726
788
  )
727
789
  }
728
790
 
@@ -9,6 +9,7 @@ import { Color, resolveColor } from 'termcast/src/colors'
9
9
 
10
10
  import { useDialog } from 'termcast/src/internal/dialog'
11
11
  import { ScrollBox } from 'termcast/src/internal/scrollbox'
12
+ import { useStore } from 'termcast/src/state'
12
13
 
13
14
  interface ActionsInterface {
14
15
  actions?: ReactNode
@@ -277,8 +278,11 @@ const Detail: DetailType = (props) => {
277
278
  if (!inFocus) return
278
279
 
279
280
  if (evt.name === 'k' && evt.ctrl && actions) {
281
+ // Ctrl+K shows actions (always show sheet)
280
282
  dialog.push(actions, 'bottom-right')
281
283
  } else if (evt.name === 'return' && actions) {
284
+ // Enter executes first action directly
285
+ useStore.setState({ shouldAutoExecuteFirstAction: true })
282
286
  dialog.push(actions, 'bottom-right')
283
287
  }
284
288
  })