termcast 1.3.32 → 1.3.34

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 (327) hide show
  1. package/dist/action-utils.d.ts.map +1 -1
  2. package/dist/action-utils.js +8 -0
  3. package/dist/action-utils.js.map +1 -1
  4. package/dist/apis/cache.d.ts +1 -2
  5. package/dist/apis/cache.d.ts.map +1 -1
  6. package/dist/apis/cache.js +138 -54
  7. package/dist/apis/cache.js.map +1 -1
  8. package/dist/apis/clipboard.d.ts.map +1 -1
  9. package/dist/apis/clipboard.js +4 -0
  10. package/dist/apis/clipboard.js.map +1 -1
  11. package/dist/apis/oauth.d.ts.map +1 -1
  12. package/dist/apis/oauth.js +31 -4
  13. package/dist/apis/oauth.js.map +1 -1
  14. package/dist/build.d.ts +0 -1
  15. package/dist/build.d.ts.map +1 -1
  16. package/dist/build.js +30 -51
  17. package/dist/build.js.map +1 -1
  18. package/dist/cli.js +31 -14
  19. package/dist/cli.js.map +1 -1
  20. package/dist/compile.d.ts.map +1 -1
  21. package/dist/compile.js +5 -1
  22. package/dist/compile.js.map +1 -1
  23. package/dist/components/actions.d.ts +14 -0
  24. package/dist/components/actions.d.ts.map +1 -1
  25. package/dist/components/actions.js +151 -59
  26. package/dist/components/actions.js.map +1 -1
  27. package/dist/components/alert.d.ts.map +1 -1
  28. package/dist/components/alert.js +6 -5
  29. package/dist/components/alert.js.map +1 -1
  30. package/dist/components/animation-tick.d.ts +1 -1
  31. package/dist/components/animation-tick.js +1 -1
  32. package/dist/components/animation-tick.js.map +1 -1
  33. package/dist/components/detail.d.ts +5 -31
  34. package/dist/components/detail.d.ts.map +1 -1
  35. package/dist/components/detail.js +36 -52
  36. package/dist/components/detail.js.map +1 -1
  37. package/dist/components/dropdown.d.ts +1 -1
  38. package/dist/components/dropdown.d.ts.map +1 -1
  39. package/dist/components/dropdown.js +50 -22
  40. package/dist/components/dropdown.js.map +1 -1
  41. package/dist/components/footer.d.ts.map +1 -1
  42. package/dist/components/footer.js +19 -18
  43. package/dist/components/footer.js.map +1 -1
  44. package/dist/components/form/checkbox.d.ts.map +1 -1
  45. package/dist/components/form/checkbox.js +12 -11
  46. package/dist/components/form/checkbox.js.map +1 -1
  47. package/dist/components/form/date-picker.d.ts.map +1 -1
  48. package/dist/components/form/date-picker.js +7 -22
  49. package/dist/components/form/date-picker.js.map +1 -1
  50. package/dist/components/form/description.d.ts +1 -1
  51. package/dist/components/form/description.d.ts.map +1 -1
  52. package/dist/components/form/description.js +6 -5
  53. package/dist/components/form/description.js.map +1 -1
  54. package/dist/components/form/dropdown.d.ts.map +1 -1
  55. package/dist/components/form/dropdown.js +53 -50
  56. package/dist/components/form/dropdown.js.map +1 -1
  57. package/dist/components/form/file-autocomplete.d.ts.map +1 -1
  58. package/dist/components/form/file-autocomplete.js +5 -4
  59. package/dist/components/form/file-autocomplete.js.map +1 -1
  60. package/dist/components/form/file-picker.d.ts.map +1 -1
  61. package/dist/components/form/file-picker.js +23 -22
  62. package/dist/components/form/file-picker.js.map +1 -1
  63. package/dist/components/form/form-end.d.ts.map +1 -1
  64. package/dist/components/form/form-end.js +6 -4
  65. package/dist/components/form/form-end.js.map +1 -1
  66. package/dist/components/form/form-field-wrapper.d.ts +15 -0
  67. package/dist/components/form/form-field-wrapper.d.ts.map +1 -0
  68. package/dist/components/form/form-field-wrapper.js +29 -0
  69. package/dist/components/form/form-field-wrapper.js.map +1 -0
  70. package/dist/components/form/index.d.ts.map +1 -1
  71. package/dist/components/form/index.js +31 -30
  72. package/dist/components/form/index.js.map +1 -1
  73. package/dist/components/form/password-field.d.ts.map +1 -1
  74. package/dist/components/form/password-field.js +7 -6
  75. package/dist/components/form/password-field.js.map +1 -1
  76. package/dist/components/form/separator.d.ts.map +1 -1
  77. package/dist/components/form/separator.js +3 -2
  78. package/dist/components/form/separator.js.map +1 -1
  79. package/dist/components/form/tagpicker.d.ts.map +1 -1
  80. package/dist/components/form/tagpicker.js +2 -1
  81. package/dist/components/form/tagpicker.js.map +1 -1
  82. package/dist/components/form/text-area.d.ts.map +1 -1
  83. package/dist/components/form/text-area.js +7 -6
  84. package/dist/components/form/text-area.js.map +1 -1
  85. package/dist/components/form/text-field.d.ts.map +1 -1
  86. package/dist/components/form/text-field.js +7 -6
  87. package/dist/components/form/text-field.js.map +1 -1
  88. package/dist/components/form/use-form-navigation.d.ts.map +1 -1
  89. package/dist/components/form/use-form-navigation.js +4 -4
  90. package/dist/components/form/use-form-navigation.js.map +1 -1
  91. package/dist/components/form/with-left-border.d.ts +15 -0
  92. package/dist/components/form/with-left-border.d.ts.map +1 -1
  93. package/dist/components/form/with-left-border.js +21 -9
  94. package/dist/components/form/with-left-border.js.map +1 -1
  95. package/dist/components/icon.d.ts +14 -0
  96. package/dist/components/icon.d.ts.map +1 -1
  97. package/dist/components/icon.js +60 -0
  98. package/dist/components/icon.js.map +1 -1
  99. package/dist/components/image.d.ts +47 -2
  100. package/dist/components/image.d.ts.map +1 -1
  101. package/dist/components/image.js +46 -7
  102. package/dist/components/image.js.map +1 -1
  103. package/dist/components/list.d.ts +5 -0
  104. package/dist/components/list.d.ts.map +1 -1
  105. package/dist/components/list.js +188 -132
  106. package/dist/components/list.js.map +1 -1
  107. package/dist/components/loading-bar.d.ts.map +1 -1
  108. package/dist/components/loading-bar.js +4 -3
  109. package/dist/components/loading-bar.js.map +1 -1
  110. package/dist/components/metadata.d.ts +70 -0
  111. package/dist/components/metadata.d.ts.map +1 -0
  112. package/dist/components/metadata.js +82 -0
  113. package/dist/components/metadata.js.map +1 -0
  114. package/dist/components/theme-picker.d.ts.map +1 -1
  115. package/dist/components/theme-picker.js +3 -2
  116. package/dist/components/theme-picker.js.map +1 -1
  117. package/dist/descendants-v2.d.ts +60 -0
  118. package/dist/descendants-v2.d.ts.map +1 -0
  119. package/dist/descendants-v2.js +144 -0
  120. package/dist/descendants-v2.js.map +1 -0
  121. package/dist/examples/actions-context.d.ts +2 -0
  122. package/dist/examples/actions-context.d.ts.map +1 -0
  123. package/dist/examples/actions-context.js +33 -0
  124. package/dist/examples/actions-context.js.map +1 -0
  125. package/dist/examples/form-basic.d.ts.map +1 -1
  126. package/dist/examples/form-basic.js +1 -1
  127. package/dist/examples/form-basic.js.map +1 -1
  128. package/dist/examples/form-dropdown.js +1 -1
  129. package/dist/examples/form-dropdown.js.map +1 -1
  130. package/dist/examples/internal/custom-action-renderables.d.ts +70 -0
  131. package/dist/examples/internal/custom-action-renderables.d.ts.map +1 -0
  132. package/dist/examples/internal/custom-action-renderables.js +163 -0
  133. package/dist/examples/internal/custom-action-renderables.js.map +1 -0
  134. package/dist/examples/internal/custom-dropdown.d.ts +99 -0
  135. package/dist/examples/internal/custom-dropdown.d.ts.map +1 -0
  136. package/dist/examples/internal/custom-dropdown.js +270 -0
  137. package/dist/examples/internal/custom-dropdown.js.map +1 -0
  138. package/dist/examples/internal/custom-renderable-form.d.ts +43 -0
  139. package/dist/examples/internal/custom-renderable-form.d.ts.map +1 -0
  140. package/dist/examples/internal/custom-renderable-form.js +284 -0
  141. package/dist/examples/internal/custom-renderable-form.js.map +1 -0
  142. package/dist/examples/internal/custom-renderable-list-default-search.d.ts +2 -0
  143. package/dist/examples/internal/custom-renderable-list-default-search.d.ts.map +1 -0
  144. package/dist/examples/internal/custom-renderable-list-default-search.js +16 -0
  145. package/dist/examples/internal/custom-renderable-list-default-search.js.map +1 -0
  146. package/dist/examples/internal/custom-renderable-list-v2-default-search.d.ts +2 -0
  147. package/dist/examples/internal/custom-renderable-list-v2-default-search.d.ts.map +1 -0
  148. package/dist/examples/internal/custom-renderable-list-v2-default-search.js +24 -0
  149. package/dist/examples/internal/custom-renderable-list-v2-default-search.js.map +1 -0
  150. package/dist/examples/internal/custom-renderable-list-v2.d.ts +189 -0
  151. package/dist/examples/internal/custom-renderable-list-v2.d.ts.map +1 -0
  152. package/dist/examples/internal/custom-renderable-list-v2.js +708 -0
  153. package/dist/examples/internal/custom-renderable-list-v2.js.map +1 -0
  154. package/dist/examples/internal/custom-renderable-list.d.ts +72 -0
  155. package/dist/examples/internal/custom-renderable-list.d.ts.map +1 -0
  156. package/dist/examples/internal/custom-renderable-list.js +544 -0
  157. package/dist/examples/internal/custom-renderable-list.js.map +1 -0
  158. package/dist/examples/internal/rhf-custom-ref.js +5 -4
  159. package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
  160. package/dist/examples/internal/scrollbox-with-descendants.js +4 -2
  161. package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
  162. package/dist/examples/list-controlled-search.d.ts +2 -0
  163. package/dist/examples/list-controlled-search.d.ts.map +1 -0
  164. package/dist/examples/list-controlled-search.js +12 -0
  165. package/dist/examples/list-controlled-search.js.map +1 -0
  166. package/dist/examples/list-detail-metadata.js +1 -1
  167. package/dist/examples/list-detail-metadata.js.map +1 -1
  168. package/dist/examples/simple-image-mask.d.ts +8 -0
  169. package/dist/examples/simple-image-mask.d.ts.map +1 -0
  170. package/dist/examples/simple-image-mask.js +12 -0
  171. package/dist/examples/simple-image-mask.js.map +1 -0
  172. package/dist/examples/toast-variations.js +1 -1
  173. package/dist/examples/toast-variations.js.map +1 -1
  174. package/dist/extensions/dev.d.ts.map +1 -1
  175. package/dist/extensions/dev.js +3 -2
  176. package/dist/extensions/dev.js.map +1 -1
  177. package/dist/extensions/react-refresh-init.d.ts.map +1 -1
  178. package/dist/extensions/react-refresh-init.js +4 -3
  179. package/dist/extensions/react-refresh-init.js.map +1 -1
  180. package/dist/index.d.ts +3 -2
  181. package/dist/index.d.ts.map +1 -1
  182. package/dist/index.js +1 -1
  183. package/dist/index.js.map +1 -1
  184. package/dist/internal/date-picker-widget.d.ts.map +1 -1
  185. package/dist/internal/date-picker-widget.js +2 -1
  186. package/dist/internal/date-picker-widget.js.map +1 -1
  187. package/dist/internal/dialog.d.ts +6 -0
  188. package/dist/internal/dialog.d.ts.map +1 -1
  189. package/dist/internal/dialog.js +59 -18
  190. package/dist/internal/dialog.js.map +1 -1
  191. package/dist/internal/navigation.d.ts.map +1 -1
  192. package/dist/internal/navigation.js +8 -1
  193. package/dist/internal/navigation.js.map +1 -1
  194. package/dist/internal/offscreen.d.ts +3 -0
  195. package/dist/internal/offscreen.d.ts.map +1 -1
  196. package/dist/internal/offscreen.js +5 -0
  197. package/dist/internal/offscreen.js.map +1 -1
  198. package/dist/internal/providers.d.ts.map +1 -1
  199. package/dist/internal/providers.js +20 -3
  200. package/dist/internal/providers.js.map +1 -1
  201. package/dist/internal/scrollbox.d.ts.map +1 -1
  202. package/dist/internal/scrollbox.js +3 -2
  203. package/dist/internal/scrollbox.js.map +1 -1
  204. package/dist/logger.d.ts.map +1 -1
  205. package/dist/logger.js +4 -0
  206. package/dist/logger.js.map +1 -1
  207. package/dist/preload.js +5 -17
  208. package/dist/preload.js.map +1 -1
  209. package/dist/state.d.ts +4 -0
  210. package/dist/state.d.ts.map +1 -1
  211. package/dist/state.js +4 -0
  212. package/dist/state.js.map +1 -1
  213. package/dist/test-border-overlay.d.ts +2 -0
  214. package/dist/test-border-overlay.d.ts.map +1 -0
  215. package/dist/test-border-overlay.js +7 -0
  216. package/dist/test-border-overlay.js.map +1 -0
  217. package/dist/test-layout-2.d.ts +2 -0
  218. package/dist/test-layout-2.d.ts.map +1 -0
  219. package/dist/test-layout-2.js +5 -0
  220. package/dist/test-layout-2.js.map +1 -0
  221. package/dist/test-layout.d.ts +2 -0
  222. package/dist/test-layout.d.ts.map +1 -0
  223. package/dist/test-layout.js +7 -0
  224. package/dist/test-layout.js.map +1 -0
  225. package/dist/theme.d.ts +1 -2
  226. package/dist/theme.d.ts.map +1 -1
  227. package/dist/theme.js +5 -9
  228. package/dist/theme.js.map +1 -1
  229. package/dist/utils/run-command.d.ts +1 -1
  230. package/dist/utils/run-command.d.ts.map +1 -1
  231. package/dist/utils/run-command.js +27 -7
  232. package/dist/utils/run-command.js.map +1 -1
  233. package/dist/utils.d.ts +1 -0
  234. package/dist/utils.d.ts.map +1 -1
  235. package/dist/utils.js +44 -23
  236. package/dist/utils.js.map +1 -1
  237. package/dist/watcher.d.ts.map +1 -1
  238. package/dist/watcher.js +24 -4
  239. package/dist/watcher.js.map +1 -1
  240. package/package.json +14 -12
  241. package/src/action-utils.tsx +10 -0
  242. package/src/apis/cache.test.ts +35 -3
  243. package/src/apis/cache.tsx +184 -59
  244. package/src/apis/clipboard.tsx +5 -0
  245. package/src/apis/oauth.tsx +33 -4
  246. package/src/build.tsx +35 -58
  247. package/src/cli.tsx +156 -134
  248. package/src/compile.tsx +6 -3
  249. package/src/compile.vitest.tsx +33 -15
  250. package/src/components/actions.tsx +230 -99
  251. package/src/components/alert.tsx +11 -10
  252. package/src/components/animation-tick.tsx +1 -1
  253. package/src/components/detail.tsx +56 -151
  254. package/src/components/dropdown.tsx +70 -36
  255. package/src/components/footer.tsx +58 -33
  256. package/src/components/form/checkbox.tsx +30 -32
  257. package/src/components/form/date-picker.tsx +27 -47
  258. package/src/components/form/description.tsx +19 -18
  259. package/src/components/form/dropdown.tsx +95 -103
  260. package/src/components/form/file-autocomplete.tsx +9 -8
  261. package/src/components/form/file-picker.tsx +46 -46
  262. package/src/components/form/form-end.tsx +6 -4
  263. package/src/components/form/index.tsx +38 -48
  264. package/src/components/form/password-field.tsx +25 -27
  265. package/src/components/form/separator.tsx +3 -2
  266. package/src/components/form/tagpicker.tsx +2 -1
  267. package/src/components/form/text-area.tsx +25 -30
  268. package/src/components/form/text-field.tsx +25 -27
  269. package/src/components/form/use-form-navigation.tsx +4 -5
  270. package/src/components/form/with-left-border.tsx +48 -10
  271. package/src/components/icon.tsx +69 -0
  272. package/src/components/image.tsx +60 -7
  273. package/src/components/list.tsx +270 -202
  274. package/src/components/loading-bar.tsx +4 -3
  275. package/src/components/metadata.tsx +217 -0
  276. package/src/components/theme-picker.tsx +3 -2
  277. package/src/examples/actions-context.tsx +63 -0
  278. package/src/examples/actions-context.vitest.tsx +110 -0
  279. package/src/examples/actions-dialog-layout.vitest.tsx +2 -1
  280. package/src/examples/file-autocomplete.vitest.tsx +15 -15
  281. package/src/examples/form-basic.tsx +12 -0
  282. package/src/examples/form-basic.vitest.tsx +74 -74
  283. package/src/examples/form-dropdown.tsx +8 -0
  284. package/src/examples/form-dropdown.vitest.tsx +364 -421
  285. package/src/examples/form-tagpicker.vitest.tsx +56 -54
  286. package/src/examples/github.vitest.tsx +252 -0
  287. package/src/examples/internal/rhf-custom-ref.tsx +16 -15
  288. package/src/examples/internal/scrollbox-with-descendants.tsx +4 -2
  289. package/src/examples/internal/simple-dialog.tsx +1 -1
  290. package/src/examples/internal/simple-scrollbox.vitest.tsx +14 -9
  291. package/src/examples/list-controlled-search.tsx +28 -0
  292. package/src/examples/list-controlled-search.vitest.tsx +49 -0
  293. package/src/examples/list-detail-metadata.tsx +8 -5
  294. package/src/examples/list-detail-metadata.vitest.tsx +22 -22
  295. package/src/examples/list-dropdown-default.vitest.tsx +12 -12
  296. package/src/examples/list-scrollbox.vitest.tsx +52 -38
  297. package/src/examples/list-with-detail.vitest.tsx +45 -41
  298. package/src/examples/list-with-dropdown.vitest.tsx +5 -5
  299. package/src/examples/list-with-sections.vitest.tsx +65 -12
  300. package/src/examples/list-with-toast.vitest.tsx +4 -4
  301. package/src/examples/simple-file-picker.vitest.tsx +12 -12
  302. package/src/examples/simple-grid.vitest.tsx +53 -53
  303. package/src/examples/simple-image-mask.tsx +58 -0
  304. package/src/examples/simple-navigation.vitest.tsx +19 -19
  305. package/src/examples/store.vitest.tsx +1 -1
  306. package/src/examples/swift-extension.vitest.tsx +4 -2
  307. package/src/examples/synonyms.vitest.tsx +31 -9
  308. package/src/examples/toast-action.vitest.tsx +8 -8
  309. package/src/examples/toast-variations.tsx +1 -1
  310. package/src/examples/toast-variations.vitest.tsx +69 -134
  311. package/src/extensions/dev.tsx +3 -2
  312. package/src/extensions/dev.vitest.tsx +65 -28
  313. package/src/extensions/react-refresh-init.tsx +4 -3
  314. package/src/index.tsx +3 -1
  315. package/src/internal/date-picker-widget.tsx +2 -1
  316. package/src/internal/dialog.tsx +100 -28
  317. package/src/internal/navigation.tsx +8 -1
  318. package/src/internal/offscreen.tsx +10 -0
  319. package/src/internal/providers.tsx +34 -8
  320. package/src/internal/scrollbox.tsx +4 -2
  321. package/src/logger.tsx +4 -0
  322. package/src/preload.tsx +5 -17
  323. package/src/state.tsx +12 -0
  324. package/src/theme.tsx +6 -9
  325. package/src/utils/run-command.tsx +32 -8
  326. package/src/utils.tsx +58 -23
  327. package/src/watcher.tsx +26 -6
package/src/cli.tsx CHANGED
@@ -7,7 +7,7 @@ import './extensions/react-refresh-init'
7
7
  import fs from 'node:fs'
8
8
  import path from 'node:path'
9
9
  import { execSync, spawn } from 'node:child_process'
10
- import { cac } from 'cac'
10
+ import { goke } from 'goke'
11
11
  import { getWatcher } from './watcher'
12
12
  import { buildExtensionCommands } from './build'
13
13
  import { logger } from './logger'
@@ -21,7 +21,7 @@ import { runHomeCommand } from './extensions/home'
21
21
  import { showToast, Toast } from './apis/toast'
22
22
  import packageJson from '../package.json'
23
23
 
24
- const cli = cac('termcast')
24
+ const cli = goke('termcast')
25
25
 
26
26
  // Auto-update check
27
27
  async function checkForUpdates() {
@@ -75,139 +75,149 @@ async function checkForUpdates() {
75
75
  // TODO: re-enable auto-update check once install script temp dir issue is fixed
76
76
  // checkForUpdates()
77
77
 
78
- cli
79
- .command('dev [path]', 'Run the extension in the current working directory')
80
- .action(async (rawExtensionPath, options) => {
81
- try {
82
- // Check if the provided arg looks like a path (contains / or . or is existing dir)
83
- const looksLikePath =
84
- rawExtensionPath &&
85
- (rawExtensionPath.includes('/') ||
86
- rawExtensionPath.startsWith('.') ||
87
- fs.existsSync(rawExtensionPath))
88
- const extensionPath = path.resolve(
89
- looksLikePath ? rawExtensionPath : process.cwd(),
78
+ // Shared dev mode action used by both 'dev' and '' commands
79
+ async function runDevAction(rawExtensionPath?: string) {
80
+ try {
81
+ // Check if the provided arg looks like a path (contains / or . or is existing dir)
82
+ const looksLikePath =
83
+ rawExtensionPath &&
84
+ (rawExtensionPath.includes('/') ||
85
+ rawExtensionPath.startsWith('.') ||
86
+ fs.existsSync(rawExtensionPath))
87
+ const extensionPath = path.resolve(
88
+ looksLikePath ? rawExtensionPath : process.cwd(),
89
+ )
90
+ let isBuilding = false
91
+
92
+ // Start dev mode with initial render
93
+ // Skip args up to and including "dev" subcommand, plus path if it looks like one
94
+ const devIndex = process.argv.findIndex((arg) => arg === 'dev')
95
+ const skipArgv = devIndex === -1 ? 0 : devIndex - 1 + (looksLikePath ? 1 : 0)
96
+ await startDevMode({ extensionPath, skipArgv })
97
+
98
+ logger.log(`dev mode started`)
99
+ // Only watch if running in a TTY (interactive terminal)
100
+ if (!process.stdout.isTTY) {
101
+ console.log('Not running in interactive terminal, watching disabled')
102
+ return
103
+ }
104
+
105
+ console.log('\nWatching for file changes...')
106
+
107
+ // Watch entire extension directory using @parcel/watcher
108
+ // Single source of truth for ignored patterns
109
+ const IGNORED_DIRS = [
110
+ 'node_modules',
111
+ '.termcast-bundle',
112
+ '.git',
113
+ '.build', // Swift build output
114
+ '.cache',
115
+ 'tmp',
116
+ '.tmp',
117
+ 'dist',
118
+ 'build',
119
+ ]
120
+ const IGNORED_EXTENSIONS = ['.log', '.db', '.sqlite']
121
+
122
+ // Glob patterns for @parcel/watcher (matched against relative paths using micromatch)
123
+ const ignoredPatterns = [
124
+ ...IGNORED_DIRS.map((dir) => `**/${dir}/**`),
125
+ ...IGNORED_EXTENSIONS.map((ext) => `**/*${ext}`),
126
+ // SQLite creates .db-wal and .db-shm alongside .db
127
+ '**/*.db-*',
128
+ '**/*.sqlite-*',
129
+ ]
130
+
131
+ // Backup filter for files that should never trigger rebuild
132
+ // This catches cases where @parcel/watcher ignore doesn't work as expected
133
+ const shouldIgnoreFile = (filePath: string): boolean => {
134
+ const relativePath = path.relative(extensionPath, filePath)
135
+ // Ignore files outside the extension directory
136
+ if (relativePath.startsWith('..')) {
137
+ return true
138
+ }
139
+ // Check if path contains any ignored directory
140
+ const hasIgnoredDir = IGNORED_DIRS.some(
141
+ (dir) =>
142
+ relativePath.includes(`/${dir}/`) ||
143
+ relativePath.startsWith(`${dir}/`),
90
144
  )
91
- let isBuilding = false
92
-
93
- // Start dev mode with initial render
94
- // Skip args up to and including "dev" subcommand, plus path if it looks like one
95
- const devIndex = process.argv.findIndex((arg) => arg === 'dev')
96
- const skipArgv = devIndex - 1 + (looksLikePath ? 1 : 0)
97
- await startDevMode({ extensionPath, skipArgv })
98
-
99
- logger.log(`dev mode started`)
100
- // Only watch if running in a TTY (interactive terminal)
101
- if (!process.stdout.isTTY) {
102
- console.log('Not running in interactive terminal, watching disabled')
145
+ if (hasIgnoredDir) {
146
+ return true
147
+ }
148
+ // Check if file has ignored extension
149
+ if (IGNORED_EXTENSIONS.some((ext) => relativePath.endsWith(ext))) {
150
+ return true
151
+ }
152
+ // Also catch .db-* and .sqlite-* patterns
153
+ if (/\.db-|\.sqlite-/.test(relativePath)) {
154
+ return true
155
+ }
156
+ return false
157
+ }
158
+
159
+ const rebuild = async (filePath: string) => {
160
+ if (isBuilding) {
161
+ logger.log('Build already in progress, skipping...')
103
162
  return
104
163
  }
105
164
 
106
- console.log('\nWatching for file changes...')
107
-
108
- // Watch entire extension directory using @parcel/watcher
109
- // Single source of truth for ignored patterns
110
- const IGNORED_DIRS = [
111
- 'node_modules',
112
- '.termcast-bundle',
113
- '.git',
114
- '.build', // Swift build output
115
- '.cache',
116
- 'tmp',
117
- '.tmp',
118
- 'dist',
119
- 'build',
120
- ]
121
- const IGNORED_EXTENSIONS = ['.log', '.db', '.sqlite']
122
-
123
- // Glob patterns for @parcel/watcher (matched against relative paths using micromatch)
124
- const ignoredPatterns = [
125
- ...IGNORED_DIRS.map((dir) => `**/${dir}/**`),
126
- ...IGNORED_EXTENSIONS.map((ext) => `**/*${ext}`),
127
- // SQLite creates .db-wal and .db-shm alongside .db
128
- '**/*.db-*',
129
- '**/*.sqlite-*',
130
- ]
131
-
132
- // Backup filter for files that should never trigger rebuild
133
- // This catches cases where @parcel/watcher ignore doesn't work as expected
134
- const shouldIgnoreFile = (filePath: string): boolean => {
135
- const relativePath = path.relative(extensionPath, filePath)
136
- // Ignore files outside the extension directory
137
- if (relativePath.startsWith('..')) {
138
- return true
139
- }
140
- // Check if path contains any ignored directory
141
- const hasIgnoredDir = IGNORED_DIRS.some(
142
- (dir) =>
143
- relativePath.includes(`/${dir}/`) ||
144
- relativePath.startsWith(`${dir}/`),
145
- )
146
- if (hasIgnoredDir) {
147
- return true
148
- }
149
- // Check if file has ignored extension
150
- if (IGNORED_EXTENSIONS.some((ext) => relativePath.endsWith(ext))) {
151
- return true
152
- }
153
- // Also catch .db-* and .sqlite-* patterns
154
- if (/\.db-|\.sqlite-/.test(relativePath)) {
155
- return true
156
- }
157
- return false
165
+ isBuilding = true
166
+ logger.log('File changed, rebuilding...')
167
+ logger.log(filePath)
168
+ try {
169
+ await triggerRebuild({ extensionPath })
170
+ logger.log('Rebuild complete')
171
+ } catch (error: any) {
172
+ logger.error('Failed to trigger rebuild:', error.message)
173
+ } finally {
174
+ isBuilding = false
158
175
  }
176
+ }
159
177
 
160
- const rebuild = async (filePath: string) => {
161
- if (isBuilding) {
162
- logger.log('Build already in progress, skipping...')
178
+ const subscription = await getWatcher().subscribe(
179
+ extensionPath,
180
+ (err, events) => {
181
+ if (err) {
182
+ logger.error('Watcher error:', err)
163
183
  return
164
184
  }
165
185
 
166
- isBuilding = true
167
- logger.log('File changed, rebuilding...')
168
- logger.log(filePath)
169
- try {
170
- await triggerRebuild({ extensionPath })
171
- logger.log('Rebuild complete')
172
- } catch (error: any) {
173
- logger.error('Failed to trigger rebuild:', error.message)
174
- } finally {
175
- isBuilding = false
176
- }
177
- }
186
+ // Filter out events for files that should be ignored
187
+ const relevantEvents = events.filter(
188
+ (event) => !shouldIgnoreFile(event.path),
189
+ )
178
190
 
179
- const subscription = await getWatcher().subscribe(
180
- extensionPath,
181
- (err, events) => {
182
- if (err) {
183
- logger.error('Watcher error:', err)
184
- return
185
- }
191
+ if (relevantEvents.length > 0) {
192
+ rebuild(relevantEvents[0].path)
193
+ }
194
+ },
195
+ { ignore: ignoredPatterns },
196
+ )
186
197
 
187
- // Filter out events for files that should be ignored
188
- const relevantEvents = events.filter(
189
- (event) => !shouldIgnoreFile(event.path),
190
- )
198
+ // Clean up watcher on exit signals
199
+ const cleanup = async () => {
200
+ await subscription.unsubscribe()
201
+ process.exit(0)
202
+ }
203
+ process.on('SIGINT', cleanup)
204
+ process.on('SIGTERM', cleanup)
205
+ } catch (e: any) {
206
+ console.error('Failed to start dev mode:', e?.message || e)
207
+ logger.error(e)
208
+ process.exit(1)
209
+ }
210
+ }
191
211
 
192
- if (relevantEvents.length > 0) {
193
- rebuild(relevantEvents[0].path)
194
- }
195
- },
196
- { ignore: ignoredPatterns },
197
- )
212
+ // Default command (no args) is an alias for dev
213
+ cli.command('', 'Run dev mode in current directory').action(async () => {
214
+ await runDevAction()
215
+ })
198
216
 
199
- // Clean up watcher on exit signals
200
- const cleanup = async () => {
201
- await subscription.unsubscribe()
202
- process.exit(0)
203
- }
204
- process.on('SIGINT', cleanup)
205
- process.on('SIGTERM', cleanup)
206
- } catch (e: any) {
207
- console.error('Failed to start dev mode:', e?.message || e)
208
- logger.error(e)
209
- process.exit(1)
210
- }
217
+ cli
218
+ .command('dev [path]', 'Run the extension in the current working directory')
219
+ .action(async (rawExtensionPath) => {
220
+ await runDevAction(rawExtensionPath)
211
221
  })
212
222
 
213
223
  cli
@@ -525,12 +535,10 @@ cli
525
535
  'raycast-search <query>',
526
536
  'Search for extensions in the Raycast store',
527
537
  )
528
- .option('-n, --limit <number>', 'Number of results to show', {
529
- default: '10',
530
- })
531
- .action(async (query: string, options: { limit: string }) => {
538
+ .option('-n, --limit [number]', 'Number of results to show (default: 10)')
539
+ .action(async (query: string, options: { limit?: string }) => {
532
540
  try {
533
- const limit = parseInt(options.limit, 10)
541
+ const limit = parseInt(options.limit || '10', 10)
534
542
  const result = await searchStoreListings({ query, perPage: limit })
535
543
 
536
544
  if (result.data.length === 0) {
@@ -568,7 +576,7 @@ cli
568
576
  'raycast-download <extensionName>',
569
577
  'Download extension from Raycast extensions repo',
570
578
  )
571
- .option('-o, --output <path>', 'Output directory', { default: '.' })
579
+ .option('-o, --output [path]', 'Output directory (default: .)')
572
580
  .option(
573
581
  '--no-dir',
574
582
  'Put files directly in output directory instead of creating extension subdirectory',
@@ -576,10 +584,10 @@ cli
576
584
  .action(
577
585
  async (
578
586
  extensionName: string,
579
- options: { output: string; dir: boolean },
587
+ options: { output?: string; dir: boolean },
580
588
  ) => {
581
589
  try {
582
- const destPath = path.resolve(options.output)
590
+ const destPath = path.resolve(options.output || '.')
583
591
  // When --no-dir is passed, dir is false; put files directly in destPath
584
592
  const extensionDir = options.dir
585
593
  ? path.join(destPath, extensionName)
@@ -685,8 +693,18 @@ cli
685
693
  )
686
694
 
687
695
  cli
688
- .command('new <name>', 'Create a new termcast extension')
696
+ .command('new [name]', 'Create a new termcast extension')
689
697
  .action(async (name: string) => {
698
+ if (!name) {
699
+ console.log('Usage: termcast new <extension-name>\n')
700
+ console.log('Create a new termcast extension with the given name.\n')
701
+ console.log('Example:')
702
+ console.log(' termcast new my-extension')
703
+ console.log(' cd my-extension')
704
+ console.log(' termcast dev')
705
+ process.exit(0)
706
+ }
707
+
690
708
  try {
691
709
  const targetDir = path.resolve(name)
692
710
 
@@ -768,9 +786,13 @@ cli
768
786
  }
769
787
  })
770
788
 
771
- cli.command('', 'List and run installed extensions').action(async () => {
772
- await runHomeCommand()
773
- })
789
+ cli
790
+ .command('legacy-raycast-store', 'List and run installed extensions')
791
+ .action(async () => {
792
+ await runHomeCommand()
793
+ })
794
+
795
+
774
796
 
775
797
  cli.help()
776
798
  cli.version('0.1.0')
package/src/compile.tsx CHANGED
@@ -12,6 +12,10 @@ const raycastAliasPlugin: BunPlugin = {
12
12
  path: require.resolve('termcast/src/index'),
13
13
  }))
14
14
 
15
+ build.onResolve({ filter: /@raycast\/utils/ }, () => ({
16
+ path: require.resolve('@termcast/utils'),
17
+ }))
18
+
15
19
  build.onResolve({ filter: /^termcast/ }, (args) => ({
16
20
  path: require.resolve(args.path),
17
21
  }))
@@ -25,8 +29,6 @@ const raycastAliasPlugin: BunPlugin = {
25
29
  build.onResolve({ filter: /^react\/jsx-dev-runtime$/ }, () => ({
26
30
  path: require.resolve('react/jsx-dev-runtime'),
27
31
  }))
28
-
29
-
30
32
  },
31
33
  }
32
34
 
@@ -215,8 +217,9 @@ export async function compileExtension({
215
217
  const result = await Bun.build({
216
218
  entrypoints: [tempEntryPath],
217
219
  target: bunTarget as 'bun',
220
+ format: 'esm',
218
221
  minify,
219
- // bytecode: true, // TODO need to wait for opentui to support this removing top level await
222
+ bytecode: true,
220
223
  sourcemap: 'external',
221
224
  compile: {
222
225
  outfile: defaultOutfile,
@@ -6,8 +6,14 @@ import { execSync } from 'node:child_process'
6
6
 
7
7
  const fixtureDir = path.resolve(__dirname, '../fixtures/simple-extension')
8
8
  const distDir = path.join(fixtureDir, 'dist')
9
+ const bundleDir = path.join(fixtureDir, '.termcast-bundle')
9
10
  const executablePath = path.join(distDir, 'simple-extension')
10
11
 
12
+ const singleErrorFixtureDir = path.resolve(__dirname, '../fixtures/single-error-extension')
13
+ const singleErrorDistDir = path.join(singleErrorFixtureDir, 'dist')
14
+ const singleErrorBundleDir = path.join(singleErrorFixtureDir, '.termcast-bundle')
15
+ const singleErrorExecutablePath = path.join(singleErrorDistDir, 'single-error-extension')
16
+
11
17
  let session: Session
12
18
 
13
19
  afterEach(() => {
@@ -38,6 +44,22 @@ beforeAll(() => {
38
44
  if (fs.existsSync(executablePath)) {
39
45
  fs.unlinkSync(executablePath)
40
46
  }
47
+ if (fs.existsSync(distDir)) {
48
+ fs.rmSync(distDir, { recursive: true, force: true })
49
+ }
50
+ if (fs.existsSync(bundleDir)) {
51
+ fs.rmSync(bundleDir, { recursive: true, force: true })
52
+ }
53
+
54
+ if (fs.existsSync(singleErrorExecutablePath)) {
55
+ fs.unlinkSync(singleErrorExecutablePath)
56
+ }
57
+ if (fs.existsSync(singleErrorDistDir)) {
58
+ fs.rmSync(singleErrorDistDir, { recursive: true, force: true })
59
+ }
60
+ if (fs.existsSync(singleErrorBundleDir)) {
61
+ fs.rmSync(singleErrorBundleDir, { recursive: true, force: true })
62
+ }
41
63
  })
42
64
 
43
65
 
@@ -70,11 +92,11 @@ test('compile extension and run executable', async () => {
70
92
  ›List Items Displays a simple list with some ite view ▀
71
93
  Search Items Search and filter through a list o view
72
94
  Google Oauth view
73
- usePromise Demo Shows how to use the usePromise view
95
+ usePromise Demo Shows how to use the usePromise view
96
+ Show State Shows the current application state view ▼
74
97
 
75
98
 
76
99
  ↵ run command ↑↓ navigate ^k actions
77
-
78
100
  "
79
101
  `)
80
102
  }, 60000)
@@ -114,13 +136,13 @@ test('compiled executable can run command', async () => {
114
136
 
115
137
  Items ▲
116
138
  ›▲ First Item This is the first item █
117
- ▲ Second Item This is the second item
139
+ ▲ Second Item This is the second item
118
140
  ▲ Third Item This is the third item
119
- ▲ Fourth Item This is the fourth item
120
-
141
+ ▲ Fourth Item This is the fourth item
142
+ ▲ Fifth Item This is the fifth item ▼
121
143
 
122
- ✓ Copied to Clipboard First Item
123
144
 
145
+ ✓ Copied to Clipboard First Item
124
146
  "
125
147
  `)
126
148
  }, 60000)
@@ -170,13 +192,13 @@ test('compiled executable can navigate back', async () => {
170
192
 
171
193
  Items ▲
172
194
  ›▲ First Item This is the first item █
173
- ▲ Second Item This is the second item
195
+ ▲ Second Item This is the second item
174
196
  ▲ Third Item This is the third item
175
- ▲ Fourth Item This is the fourth item
197
+ ▲ Fourth Item This is the fourth item
198
+ ▲ Fifth Item This is the fifth item ▼
176
199
 
177
200
 
178
201
  ↵ copy item title ↑↓ navigate ^k actions
179
-
180
202
  "
181
203
  `)
182
204
  }, 60000)
@@ -237,11 +259,6 @@ test('compiled executable shows error when command throws at root scope', async
237
259
  `)
238
260
  }, 60000)
239
261
 
240
- // Test for single-command extension with root-level error
241
- const singleErrorFixtureDir = path.resolve(__dirname, '../fixtures/single-error-extension')
242
- const singleErrorDistDir = path.join(singleErrorFixtureDir, 'dist')
243
- const singleErrorExecutablePath = path.join(singleErrorDistDir, 'single-error-extension')
244
-
245
262
  function ensureSingleErrorCompiled() {
246
263
  if (!fs.existsSync(singleErrorExecutablePath)) {
247
264
  if (fs.existsSync(singleErrorDistDir)) {
@@ -273,6 +290,8 @@ test('single command extension shows error when command throws at root scope', a
273
290
  await session.waitIdle()
274
291
 
275
292
  const errorSnapshot = await session.text()
293
+ expect(errorSnapshot).not.toContain('Failed to load native binding')
294
+ expect(errorSnapshot).not.toContain('@swc/core/binding.js')
276
295
  expect(errorSnapshot).toMatchInlineSnapshot(`
277
296
  "
278
297
 
@@ -297,4 +316,3 @@ test('single command extension shows error when command throws at root scope', a
297
316
  "
298
317
  `)
299
318
  }, 60000)
300
-