termcast 1.3.30 → 1.3.32

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 (294) hide show
  1. package/dist/apis/cache.d.ts.map +1 -1
  2. package/dist/apis/cache.js +4 -39
  3. package/dist/apis/cache.js.map +1 -1
  4. package/dist/apis/hud.d.ts.map +1 -1
  5. package/dist/apis/hud.js +13 -31
  6. package/dist/apis/hud.js.map +1 -1
  7. package/dist/apis/localstorage.d.ts.map +1 -1
  8. package/dist/apis/localstorage.js +3 -27
  9. package/dist/apis/localstorage.js.map +1 -1
  10. package/dist/apis/toast.d.ts +16 -43
  11. package/dist/apis/toast.d.ts.map +1 -1
  12. package/dist/apis/toast.js +78 -177
  13. package/dist/apis/toast.js.map +1 -1
  14. package/dist/build.d.ts +3 -1
  15. package/dist/build.d.ts.map +1 -1
  16. package/dist/build.js +52 -2
  17. package/dist/build.js.map +1 -1
  18. package/dist/cli.d.ts +1 -0
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +206 -25
  21. package/dist/cli.js.map +1 -1
  22. package/dist/colors.d.ts.map +1 -1
  23. package/dist/colors.js +1 -0
  24. package/dist/colors.js.map +1 -1
  25. package/dist/compile.d.ts +0 -1
  26. package/dist/compile.d.ts.map +1 -1
  27. package/dist/compile.js +18 -23
  28. package/dist/compile.js.map +1 -1
  29. package/dist/components/actions.d.ts.map +1 -1
  30. package/dist/components/actions.js +30 -15
  31. package/dist/components/actions.js.map +1 -1
  32. package/dist/components/animation-tick.d.ts +12 -0
  33. package/dist/components/animation-tick.d.ts.map +1 -0
  34. package/dist/components/animation-tick.js +63 -0
  35. package/dist/components/animation-tick.js.map +1 -0
  36. package/dist/components/detail.d.ts.map +1 -1
  37. package/dist/components/detail.js +10 -13
  38. package/dist/components/detail.js.map +1 -1
  39. package/dist/components/dropdown.d.ts +1 -0
  40. package/dist/components/dropdown.d.ts.map +1 -1
  41. package/dist/components/dropdown.js +27 -26
  42. package/dist/components/dropdown.js.map +1 -1
  43. package/dist/components/extension-preferences.d.ts.map +1 -1
  44. package/dist/components/extension-preferences.js +15 -10
  45. package/dist/components/extension-preferences.js.map +1 -1
  46. package/dist/components/footer.d.ts +13 -0
  47. package/dist/components/footer.d.ts.map +1 -0
  48. package/dist/components/footer.js +106 -0
  49. package/dist/components/footer.js.map +1 -0
  50. package/dist/components/form/file-autocomplete.d.ts +19 -4
  51. package/dist/components/form/file-autocomplete.d.ts.map +1 -1
  52. package/dist/components/form/file-autocomplete.js +56 -55
  53. package/dist/components/form/file-autocomplete.js.map +1 -1
  54. package/dist/components/form/file-picker.d.ts.map +1 -1
  55. package/dist/components/form/file-picker.js +26 -15
  56. package/dist/components/form/file-picker.js.map +1 -1
  57. package/dist/components/form/index.d.ts.map +1 -1
  58. package/dist/components/form/index.js +17 -15
  59. package/dist/components/form/index.js.map +1 -1
  60. package/dist/components/form/with-left-border.d.ts.map +1 -1
  61. package/dist/components/form/with-left-border.js +4 -12
  62. package/dist/components/form/with-left-border.js.map +1 -1
  63. package/dist/components/list.d.ts.map +1 -1
  64. package/dist/components/list.js +126 -86
  65. package/dist/components/list.js.map +1 -1
  66. package/dist/components/loading-bar.d.ts.map +1 -1
  67. package/dist/components/loading-bar.js +5 -22
  68. package/dist/components/loading-bar.js.map +1 -1
  69. package/dist/components/loading-text.d.ts.map +1 -1
  70. package/dist/components/loading-text.js +3 -22
  71. package/dist/components/loading-text.js.map +1 -1
  72. package/dist/components/theme-picker.d.ts +2 -0
  73. package/dist/components/theme-picker.d.ts.map +1 -0
  74. package/dist/components/theme-picker.js +37 -0
  75. package/dist/components/theme-picker.js.map +1 -0
  76. package/dist/descendants.d.ts +6 -0
  77. package/dist/descendants.d.ts.map +1 -1
  78. package/dist/descendants.js +74 -8
  79. package/dist/descendants.js.map +1 -1
  80. package/dist/examples/internal/descendants-rerender.d.ts +14 -0
  81. package/dist/examples/internal/descendants-rerender.d.ts.map +1 -0
  82. package/dist/examples/internal/descendants-rerender.js +145 -0
  83. package/dist/examples/internal/descendants-rerender.js.map +1 -0
  84. package/dist/examples/internal/simple-dialog.js +4 -1
  85. package/dist/examples/internal/simple-dialog.js.map +1 -1
  86. package/dist/examples/internal/simple-scrollbox.js +1 -1
  87. package/dist/examples/internal/simple-scrollbox.js.map +1 -1
  88. package/dist/examples/list-with-dropdown.js +1 -1
  89. package/dist/examples/list-with-dropdown.js.map +1 -1
  90. package/dist/examples/miscellaneous.js +1 -1
  91. package/dist/examples/miscellaneous.js.map +1 -1
  92. package/dist/examples/toast-action.d.ts +2 -0
  93. package/dist/examples/toast-action.d.ts.map +1 -0
  94. package/dist/examples/toast-action.js +76 -0
  95. package/dist/examples/toast-action.js.map +1 -0
  96. package/dist/examples/toast-variations.js +38 -36
  97. package/dist/examples/toast-variations.js.map +1 -1
  98. package/dist/extensions/dev.d.ts +1 -1
  99. package/dist/extensions/dev.d.ts.map +1 -1
  100. package/dist/extensions/dev.js +62 -30
  101. package/dist/extensions/dev.js.map +1 -1
  102. package/dist/extensions/home.d.ts.map +1 -1
  103. package/dist/extensions/home.js +4 -3
  104. package/dist/extensions/home.js.map +1 -1
  105. package/dist/extensions/react-refresh-init.d.ts +5 -0
  106. package/dist/extensions/react-refresh-init.d.ts.map +1 -0
  107. package/dist/extensions/react-refresh-init.js +52 -0
  108. package/dist/extensions/react-refresh-init.js.map +1 -0
  109. package/dist/internal/date-picker-widget.js +1 -1
  110. package/dist/internal/date-picker-widget.js.map +1 -1
  111. package/dist/internal/dialog.d.ts +8 -3
  112. package/dist/internal/dialog.d.ts.map +1 -1
  113. package/dist/internal/dialog.js +37 -53
  114. package/dist/internal/dialog.js.map +1 -1
  115. package/dist/internal/navigation.d.ts +1 -0
  116. package/dist/internal/navigation.d.ts.map +1 -1
  117. package/dist/internal/navigation.js +25 -1
  118. package/dist/internal/navigation.js.map +1 -1
  119. package/dist/internal/providers.d.ts.map +1 -1
  120. package/dist/internal/providers.js +9 -197
  121. package/dist/internal/providers.js.map +1 -1
  122. package/dist/internal/scrollbox.d.ts.map +1 -1
  123. package/dist/internal/scrollbox.js +1 -0
  124. package/dist/internal/scrollbox.js.map +1 -1
  125. package/dist/release.d.ts +1 -0
  126. package/dist/release.d.ts.map +1 -1
  127. package/dist/release.js +16 -9
  128. package/dist/release.js.map +1 -1
  129. package/dist/state.d.ts +27 -1
  130. package/dist/state.d.ts.map +1 -1
  131. package/dist/state.js +6 -0
  132. package/dist/state.js.map +1 -1
  133. package/dist/theme.d.ts +6 -19
  134. package/dist/theme.d.ts.map +1 -1
  135. package/dist/theme.js +76 -45
  136. package/dist/theme.js.map +1 -1
  137. package/dist/themes/aura.json +69 -0
  138. package/dist/themes/ayu.json +80 -0
  139. package/dist/themes/catppuccin-frappe.json +233 -0
  140. package/dist/themes/catppuccin-macchiato.json +233 -0
  141. package/dist/themes/catppuccin.json +112 -0
  142. package/dist/themes/cobalt2.json +228 -0
  143. package/dist/themes/cursor.json +249 -0
  144. package/dist/themes/dracula.json +219 -0
  145. package/dist/themes/everforest.json +241 -0
  146. package/dist/themes/flexoki.json +237 -0
  147. package/dist/themes/github-light.json +56 -0
  148. package/dist/themes/github.json +241 -0
  149. package/dist/themes/gruvbox.json +95 -0
  150. package/dist/themes/kanagawa.json +77 -0
  151. package/dist/themes/lucent-orng.json +227 -0
  152. package/dist/themes/material.json +235 -0
  153. package/dist/themes/matrix.json +77 -0
  154. package/dist/themes/mercury.json +245 -0
  155. package/dist/themes/monokai.json +221 -0
  156. package/dist/themes/nightowl.json +221 -0
  157. package/dist/themes/nord.json +223 -0
  158. package/dist/themes/one-dark.json +84 -0
  159. package/dist/themes/opencode-light.json +62 -0
  160. package/dist/themes/opencode.json +245 -0
  161. package/dist/themes/orng.json +245 -0
  162. package/dist/themes/palenight.json +222 -0
  163. package/dist/themes/rosepine.json +234 -0
  164. package/dist/themes/solarized.json +223 -0
  165. package/dist/themes/synthwave84.json +226 -0
  166. package/dist/themes/termcast.json +226 -0
  167. package/dist/themes/tokyonight.json +243 -0
  168. package/dist/themes/vercel.json +255 -0
  169. package/dist/themes/vesper.json +218 -0
  170. package/dist/themes/zenburn.json +223 -0
  171. package/dist/themes.d.ts +57 -0
  172. package/dist/themes.d.ts.map +1 -0
  173. package/dist/themes.js +181 -0
  174. package/dist/themes.js.map +1 -0
  175. package/dist/utils/run-command.d.ts +2 -1
  176. package/dist/utils/run-command.d.ts.map +1 -1
  177. package/dist/utils/run-command.js +20 -10
  178. package/dist/utils/run-command.js.map +1 -1
  179. package/dist/utils.d.ts +2 -1
  180. package/dist/utils.d.ts.map +1 -1
  181. package/dist/utils.js +90 -17
  182. package/dist/utils.js.map +1 -1
  183. package/dist/watcher.d.ts +3 -0
  184. package/dist/watcher.d.ts.map +1 -0
  185. package/dist/watcher.js +16 -0
  186. package/dist/watcher.js.map +1 -0
  187. package/package.json +16 -10
  188. package/src/apis/cache.tsx +5 -44
  189. package/src/apis/hud.tsx +17 -62
  190. package/src/apis/localstorage.tsx +3 -32
  191. package/src/apis/toast.tsx +91 -275
  192. package/src/build.test.tsx +10 -0
  193. package/src/build.tsx +61 -1
  194. package/src/cli.tsx +365 -103
  195. package/src/colors.tsx +1 -0
  196. package/src/compile.tsx +21 -29
  197. package/src/compile.vitest.tsx +300 -0
  198. package/src/components/actions.tsx +64 -45
  199. package/src/components/animation-tick.tsx +85 -0
  200. package/src/components/detail.tsx +31 -35
  201. package/src/components/dropdown.tsx +32 -21
  202. package/src/components/extension-preferences.tsx +14 -10
  203. package/src/components/footer.tsx +241 -0
  204. package/src/components/form/file-autocomplete.tsx +80 -60
  205. package/src/components/form/file-picker.tsx +37 -25
  206. package/src/components/form/index.tsx +45 -41
  207. package/src/components/form/with-left-border.tsx +4 -14
  208. package/src/components/list.tsx +181 -121
  209. package/src/components/loading-bar.tsx +5 -25
  210. package/src/components/loading-text.tsx +4 -23
  211. package/src/components/theme-picker.tsx +57 -0
  212. package/src/descendants.tsx +98 -9
  213. package/src/examples/actions-dialog-layout.vitest.tsx +112 -0
  214. package/src/examples/file-autocomplete.vitest.tsx +131 -122
  215. package/src/examples/form-basic.vitest.tsx +463 -644
  216. package/src/examples/form-dropdown.vitest.tsx +553 -571
  217. package/src/examples/form-scroll.vitest.tsx +112 -102
  218. package/src/examples/form-tagpicker.vitest.tsx +364 -338
  219. package/src/examples/internal/descendants-rerender.tsx +273 -0
  220. package/src/examples/internal/descendants-rerender.vitest.tsx +194 -0
  221. package/src/examples/internal/simple-dialog.tsx +4 -4
  222. package/src/examples/internal/simple-scrollbox.tsx +2 -2
  223. package/src/examples/internal/simple-scrollbox.vitest.tsx +43 -31
  224. package/src/examples/list-detail-metadata.vitest.tsx +34 -30
  225. package/src/examples/list-dropdown-default.vitest.tsx +84 -72
  226. package/src/examples/list-empty-view.vitest.tsx +93 -0
  227. package/src/examples/list-fetch-data.vitest.tsx +36 -30
  228. package/src/examples/list-scrollbox.vitest.tsx +59 -39
  229. package/src/examples/list-with-detail.vitest.tsx +339 -314
  230. package/src/examples/list-with-dropdown.tsx +1 -0
  231. package/src/examples/list-with-dropdown.vitest.tsx +176 -150
  232. package/src/examples/list-with-sections.vitest.tsx +289 -270
  233. package/src/examples/list-with-toast.vitest.tsx +44 -44
  234. package/src/examples/miscellaneous.tsx +10 -0
  235. package/src/examples/simple-file-picker.vitest.tsx +90 -86
  236. package/src/examples/simple-grid.vitest.tsx +275 -249
  237. package/src/examples/simple-navigation.vitest.tsx +192 -168
  238. package/src/examples/store.vitest.tsx +6 -4
  239. package/src/examples/swift-extension.vitest.tsx +31 -19
  240. package/src/examples/synonyms.vitest.tsx +93 -83
  241. package/src/examples/toast-action.tsx +160 -0
  242. package/src/examples/toast-action.vitest.tsx +404 -0
  243. package/src/examples/toast-variations.tsx +58 -57
  244. package/src/examples/toast-variations.vitest.tsx +186 -166
  245. package/src/extensions/dev.tsx +74 -33
  246. package/src/extensions/dev.vitest.tsx +162 -69
  247. package/src/extensions/home.tsx +5 -6
  248. package/src/extensions/react-refresh-init.tsx +59 -0
  249. package/src/internal/date-picker-widget.tsx +1 -1
  250. package/src/internal/dialog.tsx +59 -83
  251. package/src/internal/navigation.tsx +37 -4
  252. package/src/internal/providers.tsx +27 -315
  253. package/src/internal/scrollbox.tsx +1 -0
  254. package/src/release.tsx +16 -10
  255. package/src/state.tsx +36 -3
  256. package/src/theme.tsx +82 -51
  257. package/src/themes/aura.json +69 -0
  258. package/src/themes/ayu.json +80 -0
  259. package/src/themes/catppuccin-frappe.json +233 -0
  260. package/src/themes/catppuccin-macchiato.json +233 -0
  261. package/src/themes/catppuccin.json +112 -0
  262. package/src/themes/cobalt2.json +228 -0
  263. package/src/themes/cursor.json +249 -0
  264. package/src/themes/dracula.json +219 -0
  265. package/src/themes/everforest.json +241 -0
  266. package/src/themes/flexoki.json +237 -0
  267. package/src/themes/github-light.json +56 -0
  268. package/src/themes/github.json +241 -0
  269. package/src/themes/gruvbox.json +95 -0
  270. package/src/themes/kanagawa.json +77 -0
  271. package/src/themes/lucent-orng.json +227 -0
  272. package/src/themes/material.json +235 -0
  273. package/src/themes/matrix.json +77 -0
  274. package/src/themes/mercury.json +252 -0
  275. package/src/themes/monokai.json +221 -0
  276. package/src/themes/nightowl.json +221 -0
  277. package/src/themes/nord.json +223 -0
  278. package/src/themes/one-dark.json +84 -0
  279. package/src/themes/opencode-light.json +62 -0
  280. package/src/themes/opencode.json +245 -0
  281. package/src/themes/orng.json +245 -0
  282. package/src/themes/palenight.json +222 -0
  283. package/src/themes/rosepine.json +234 -0
  284. package/src/themes/solarized.json +223 -0
  285. package/src/themes/synthwave84.json +226 -0
  286. package/src/themes/termcast.json +227 -0
  287. package/src/themes/tokyonight.json +243 -0
  288. package/src/themes/vercel.json +255 -0
  289. package/src/themes/vesper.json +218 -0
  290. package/src/themes/zenburn.json +223 -0
  291. package/src/themes.ts +291 -0
  292. package/src/utils/run-command.tsx +23 -12
  293. package/src/utils.tsx +115 -18
  294. package/src/watcher.tsx +19 -0
package/src/themes.ts ADDED
@@ -0,0 +1,291 @@
1
+ // Theme resolver for termcast
2
+ // Adapted from https://github.com/sst/opencode
3
+
4
+ import { parseColor, RGBA } from '@opentui/core'
5
+
6
+ import aura from './themes/aura.json'
7
+ import ayu from './themes/ayu.json'
8
+ import catppuccin from './themes/catppuccin.json'
9
+ import catppuccinFrappe from './themes/catppuccin-frappe.json'
10
+ import catppuccinMacchiato from './themes/catppuccin-macchiato.json'
11
+ import cobalt2 from './themes/cobalt2.json'
12
+ import cursor from './themes/cursor.json'
13
+ import dracula from './themes/dracula.json'
14
+ import everforest from './themes/everforest.json'
15
+ import flexoki from './themes/flexoki.json'
16
+ import github from './themes/github.json'
17
+ import githubLight from './themes/github-light.json'
18
+ import gruvbox from './themes/gruvbox.json'
19
+ import kanagawa from './themes/kanagawa.json'
20
+ import material from './themes/material.json'
21
+ import matrix from './themes/matrix.json'
22
+ import mercury from './themes/mercury.json'
23
+ import monokai from './themes/monokai.json'
24
+ import nightowl from './themes/nightowl.json'
25
+ import nord from './themes/nord.json'
26
+ import oneDark from './themes/one-dark.json'
27
+ import opencode from './themes/opencode.json'
28
+ import opencodeLight from './themes/opencode-light.json'
29
+ import orng from './themes/orng.json'
30
+ import palenight from './themes/palenight.json'
31
+ import rosepine from './themes/rosepine.json'
32
+ import solarized from './themes/solarized.json'
33
+ import synthwave84 from './themes/synthwave84.json'
34
+ import termcast from './themes/termcast.json'
35
+ import tokyonight from './themes/tokyonight.json'
36
+ import vercel from './themes/vercel.json'
37
+ import vesper from './themes/vesper.json'
38
+ import zenburn from './themes/zenburn.json'
39
+
40
+ type HexColor = `#${string}`
41
+ type RefName = string
42
+ type Variant = {
43
+ dark: HexColor | RefName
44
+ light: HexColor | RefName
45
+ }
46
+ type ColorValue = HexColor | RefName | Variant
47
+
48
+ interface ThemeJson {
49
+ $schema?: string
50
+ defs?: Record<string, HexColor | RefName>
51
+ theme: Record<string, ColorValue>
52
+ }
53
+
54
+ export interface ResolvedTheme {
55
+ // Text colors
56
+ text: string
57
+ textMuted: string
58
+
59
+ // Background colors
60
+ background: string
61
+ backgroundPanel: string
62
+ backgroundElement: string
63
+
64
+ // Primary/accent colors
65
+ primary: string
66
+ secondary: string
67
+ accent: string
68
+
69
+ // Semantic colors
70
+ info: string
71
+ success: string
72
+ warning: string
73
+ error: string
74
+
75
+ // Border colors
76
+ border: string
77
+ borderActive: string
78
+ borderSubtle: string
79
+
80
+ // Diff colors
81
+ diffAdded: string
82
+ diffRemoved: string
83
+ diffContext: string
84
+ diffHunkHeader: string
85
+ diffHighlightAdded: string
86
+ diffHighlightRemoved: string
87
+ diffAddedBg: string
88
+ diffRemovedBg: string
89
+ diffContextBg: string
90
+ diffLineNumber: string
91
+ diffAddedLineNumberBg: string
92
+ diffRemovedLineNumberBg: string
93
+
94
+ // Markdown colors
95
+ markdownText: string
96
+ markdownHeading: string
97
+ markdownLink: string
98
+ markdownLinkText: string
99
+ markdownCode: string
100
+ markdownBlockQuote: string
101
+ markdownEmph: string
102
+ markdownStrong: string
103
+ markdownHorizontalRule: string
104
+ markdownListItem: string
105
+ markdownListEnumeration: string
106
+ markdownImage: string
107
+ markdownImageText: string
108
+ markdownCodeBlock: string
109
+
110
+ // Syntax colors
111
+ syntaxComment: string
112
+ syntaxKeyword: string
113
+ syntaxFunction: string
114
+ syntaxVariable: string
115
+ syntaxString: string
116
+ syntaxNumber: string
117
+ syntaxType: string
118
+ syntaxOperator: string
119
+ syntaxPunctuation: string
120
+
121
+ // Transparent
122
+ transparent: undefined
123
+ }
124
+
125
+ // Note: lucent-orng excluded because it uses transparent backgrounds
126
+ const DEFAULT_THEMES: Record<string, ThemeJson> = {
127
+ aura,
128
+ ayu,
129
+ catppuccin,
130
+ 'catppuccin-frappe': catppuccinFrappe,
131
+ 'catppuccin-macchiato': catppuccinMacchiato,
132
+ cobalt2,
133
+ cursor,
134
+ dracula,
135
+ everforest,
136
+ flexoki,
137
+ github,
138
+ 'github-light': githubLight,
139
+ gruvbox,
140
+ kanagawa,
141
+ material,
142
+ matrix,
143
+ mercury,
144
+ monokai,
145
+ nightowl,
146
+ nord,
147
+ 'one-dark': oneDark,
148
+ opencode,
149
+ 'opencode-light': opencodeLight,
150
+ orng,
151
+ palenight,
152
+ rosepine,
153
+ solarized,
154
+ synthwave84,
155
+ termcast,
156
+ tokyonight,
157
+ vercel,
158
+ vesper,
159
+ zenburn,
160
+ }
161
+
162
+ function rgbaToHex(rgba: RGBA): string {
163
+ const r = Math.round(rgba.r * 255)
164
+ .toString(16)
165
+ .padStart(2, '0')
166
+ const g = Math.round(rgba.g * 255)
167
+ .toString(16)
168
+ .padStart(2, '0')
169
+ const b = Math.round(rgba.b * 255)
170
+ .toString(16)
171
+ .padStart(2, '0')
172
+ return `#${r}${g}${b}`
173
+ }
174
+
175
+ function resolveTheme(
176
+ themeJson: ThemeJson,
177
+ mode: 'dark' | 'light',
178
+ ): ResolvedTheme {
179
+ const defs = themeJson.defs ?? {}
180
+
181
+ function resolveColorToHex(c: ColorValue): string {
182
+ if (typeof c === 'string') {
183
+ if (c === 'transparent' || c === 'none') {
184
+ return '#000000'
185
+ }
186
+ if (c.startsWith('#')) {
187
+ return c
188
+ }
189
+ // Reference to defs
190
+ if (defs[c] != null) {
191
+ return resolveColorToHex(defs[c] as ColorValue)
192
+ }
193
+ // Reference to another theme property
194
+ if (themeJson.theme[c] !== undefined) {
195
+ return resolveColorToHex(themeJson.theme[c] as ColorValue)
196
+ }
197
+ // Fallback
198
+ return '#808080'
199
+ }
200
+ // Variant with dark/light
201
+ return resolveColorToHex(c[mode])
202
+ }
203
+
204
+ const t = themeJson.theme
205
+ const fallbackGray = '#808080'
206
+ const fallbackBg = '#1e1e1e'
207
+ const fallbackText = '#d4d4d4'
208
+
209
+ return {
210
+ // Text
211
+ text: resolveColorToHex(t.text ?? fallbackText),
212
+ textMuted: resolveColorToHex(t.textMuted ?? fallbackGray),
213
+
214
+ // Background
215
+ background: resolveColorToHex(t.background ?? fallbackBg),
216
+ backgroundPanel: resolveColorToHex(t.backgroundPanel ?? fallbackBg),
217
+ backgroundElement: resolveColorToHex(t.backgroundElement ?? fallbackBg),
218
+
219
+ // Primary/accent
220
+ primary: resolveColorToHex(t.primary ?? fallbackGray),
221
+ secondary: resolveColorToHex(t.secondary ?? t.primary ?? fallbackGray),
222
+ accent: resolveColorToHex(t.accent ?? t.primary ?? fallbackGray),
223
+
224
+ // Semantic
225
+ info: resolveColorToHex(t.info ?? t.primary ?? fallbackGray),
226
+ success: resolveColorToHex(t.success ?? fallbackGray),
227
+ warning: resolveColorToHex(t.warning ?? fallbackGray),
228
+ error: resolveColorToHex(t.error ?? '#ff0000'),
229
+
230
+ // Border
231
+ border: resolveColorToHex(t.border ?? fallbackGray),
232
+ borderActive: resolveColorToHex(t.borderActive ?? t.primary ?? fallbackGray),
233
+ borderSubtle: resolveColorToHex(t.borderSubtle ?? fallbackBg),
234
+
235
+ // Diff
236
+ diffAdded: resolveColorToHex(t.diffAdded ?? '#4fd6be'),
237
+ diffRemoved: resolveColorToHex(t.diffRemoved ?? '#c53b53'),
238
+ diffContext: resolveColorToHex(t.diffContext ?? fallbackGray),
239
+ diffHunkHeader: resolveColorToHex(t.diffHunkHeader ?? fallbackGray),
240
+ diffHighlightAdded: resolveColorToHex(t.diffHighlightAdded ?? '#b8db87'),
241
+ diffHighlightRemoved: resolveColorToHex(t.diffHighlightRemoved ?? '#e26a75'),
242
+ diffAddedBg: resolveColorToHex(t.diffAddedBg ?? '#1e3a1e'),
243
+ diffRemovedBg: resolveColorToHex(t.diffRemovedBg ?? '#3a1e1e'),
244
+ diffContextBg: resolveColorToHex(t.diffContextBg ?? fallbackBg),
245
+ diffLineNumber: resolveColorToHex(t.diffLineNumber ?? fallbackGray),
246
+ diffAddedLineNumberBg: resolveColorToHex(t.diffAddedLineNumberBg ?? '#1e3a1e'),
247
+ diffRemovedLineNumberBg: resolveColorToHex(t.diffRemovedLineNumberBg ?? '#3a1e1e'),
248
+
249
+ // Markdown
250
+ markdownText: resolveColorToHex(t.markdownText ?? t.text ?? fallbackText),
251
+ markdownHeading: resolveColorToHex(t.markdownHeading ?? t.primary ?? fallbackGray),
252
+ markdownLink: resolveColorToHex(t.markdownLink ?? t.primary ?? fallbackGray),
253
+ markdownLinkText: resolveColorToHex(t.markdownLinkText ?? t.primary ?? fallbackGray),
254
+ markdownCode: resolveColorToHex(t.markdownCode ?? t.primary ?? fallbackGray),
255
+ markdownBlockQuote: resolveColorToHex(t.markdownBlockQuote ?? fallbackGray),
256
+ markdownEmph: resolveColorToHex(t.markdownEmph ?? t.primary ?? fallbackGray),
257
+ markdownStrong: resolveColorToHex(t.markdownStrong ?? t.primary ?? fallbackGray),
258
+ markdownHorizontalRule: resolveColorToHex(t.markdownHorizontalRule ?? fallbackGray),
259
+ markdownListItem: resolveColorToHex(t.markdownListItem ?? t.primary ?? fallbackGray),
260
+ markdownListEnumeration: resolveColorToHex(t.markdownListEnumeration ?? t.primary ?? fallbackGray),
261
+ markdownImage: resolveColorToHex(t.markdownImage ?? t.primary ?? fallbackGray),
262
+ markdownImageText: resolveColorToHex(t.markdownImageText ?? t.primary ?? fallbackGray),
263
+ markdownCodeBlock: resolveColorToHex(t.markdownCodeBlock ?? t.text ?? fallbackText),
264
+
265
+ // Syntax
266
+ syntaxComment: resolveColorToHex(t.syntaxComment ?? fallbackGray),
267
+ syntaxKeyword: resolveColorToHex(t.syntaxKeyword ?? t.primary ?? fallbackGray),
268
+ syntaxFunction: resolveColorToHex(t.syntaxFunction ?? t.primary ?? fallbackGray),
269
+ syntaxVariable: resolveColorToHex(t.syntaxVariable ?? fallbackGray),
270
+ syntaxString: resolveColorToHex(t.syntaxString ?? t.primary ?? fallbackGray),
271
+ syntaxNumber: resolveColorToHex(t.syntaxNumber ?? t.primary ?? fallbackGray),
272
+ syntaxType: resolveColorToHex(t.syntaxType ?? t.primary ?? fallbackGray),
273
+ syntaxOperator: resolveColorToHex(t.syntaxOperator ?? t.primary ?? fallbackGray),
274
+ syntaxPunctuation: resolveColorToHex(t.syntaxPunctuation ?? t.text ?? fallbackText),
275
+
276
+ // Transparent
277
+ transparent: undefined,
278
+ }
279
+ }
280
+
281
+ export function getResolvedTheme(
282
+ name: string,
283
+ mode: 'dark' | 'light' = 'dark',
284
+ ): ResolvedTheme {
285
+ const themeJson = DEFAULT_THEMES[name] ?? DEFAULT_THEMES.termcast!
286
+ return resolveTheme(themeJson, mode)
287
+ }
288
+
289
+ export const themeNames = Object.keys(DEFAULT_THEMES).sort()
290
+
291
+ export const defaultThemeName = 'termcast'
@@ -97,8 +97,9 @@ export interface RunCommandOptions {
97
97
  extensionName: string
98
98
  packageJson?: RaycastPackageJson
99
99
  bundledPath?: string
100
- Component?: (props: LaunchProps) => any
100
+ loadComponent?: () => Promise<(props: LaunchProps) => any>
101
101
  push: (element: React.ReactNode) => void
102
+ replace?: (element: React.ReactNode) => void
102
103
  }
103
104
 
104
105
  export async function runCommand(options: RunCommandOptions): Promise<void> {
@@ -107,8 +108,9 @@ export async function runCommand(options: RunCommandOptions): Promise<void> {
107
108
  extensionName,
108
109
  packageJson,
109
110
  bundledPath,
110
- Component: BuiltInComponent,
111
+ loadComponent,
111
112
  push,
113
+ replace,
112
114
  } = options
113
115
 
114
116
  // Check if command has required preferences that are missing
@@ -116,12 +118,11 @@ export async function runCommand(options: RunCommandOptions): Promise<void> {
116
118
  command,
117
119
  extensionName,
118
120
  packageJson,
119
- hasBuiltInComponent: !!BuiltInComponent,
120
121
  })
121
122
 
122
123
  if (!prefsCheck.hasRequiredPreferences) {
123
- // TODO: Use replace instead of push to avoid stacking navigation
124
124
  // Redirect to preferences with onSubmit to run command after
125
+ // Use replace in onSubmit so the command replaces the preferences screen
125
126
  push(
126
127
  <ExtensionPreferences
127
128
  extensionName={extensionName}
@@ -131,7 +132,7 @@ export async function runCommand(options: RunCommandOptions): Promise<void> {
131
132
  : undefined
132
133
  }
133
134
  onSubmit={() => {
134
- runCommand(options)
135
+ runCommand({ ...options, push: replace || push })
135
136
  }}
136
137
  />,
137
138
  )
@@ -144,13 +145,14 @@ export async function runCommand(options: RunCommandOptions): Promise<void> {
144
145
  const needsArguments = commandArgs.length > 0 && !currentArgs
145
146
 
146
147
  if (needsArguments) {
148
+ // Use replace in onSubmit so the command replaces the arguments screen
147
149
  push(
148
150
  <CommandArguments
149
151
  arguments={commandArgs}
150
152
  commandTitle={command.title}
151
153
  onSubmit={(args) => {
152
154
  useStore.setState({ currentCommandArguments: args })
153
- runCommand(options)
155
+ runCommand({ ...options, push: replace || push })
154
156
  }}
155
157
  />,
156
158
  )
@@ -163,9 +165,20 @@ export async function runCommand(options: RunCommandOptions): Promise<void> {
163
165
  // Get the component/function to run
164
166
  let CommandComponent: ((props: LaunchProps) => any) | undefined
165
167
 
166
- if (BuiltInComponent) {
167
- CommandComponent = BuiltInComponent
168
+ if (loadComponent) {
169
+ // Lazy load the component (used by compiled extensions and built-in commands)
170
+ CommandComponent = await loadComponent()
171
+
172
+ if (!CommandComponent) {
173
+ await showToast({
174
+ style: Toast.Style.Failure,
175
+ title: 'No default export',
176
+ message: `Command file ${command.name} has no default export`,
177
+ })
178
+ return
179
+ }
168
180
  } else if (bundledPath) {
181
+ // Dynamic import with cache busting (used by dev mode)
169
182
  const state = useStore.getState()
170
183
  const devRebuildCount = state.devRebuildCount + 1
171
184
  useStore.setState({ devRebuildCount })
@@ -224,18 +237,16 @@ async function checkRequiredPreferences({
224
237
  command,
225
238
  extensionName,
226
239
  packageJson,
227
- hasBuiltInComponent,
228
240
  }: {
229
241
  command: RunnableCommand
230
242
  extensionName: string
231
243
  packageJson?: RaycastPackageJson
232
- hasBuiltInComponent: boolean
233
244
  }): Promise<{
234
245
  hasRequiredPreferences: boolean
235
246
  requiredPreferences?: 'command' | 'extension'
236
247
  }> {
237
- // Built-in commands or commands without packageJson don't have preferences
238
- if (hasBuiltInComponent || !packageJson) {
248
+ // Commands without packageJson don't have preferences
249
+ if (!packageJson) {
239
250
  return { hasRequiredPreferences: true }
240
251
  }
241
252
 
package/src/utils.tsx CHANGED
@@ -28,9 +28,10 @@ export type CommonProps = {
28
28
 
29
29
  export interface Application {
30
30
  name: string
31
- localizedName?: string
32
31
  path: string
33
32
  bundleId?: string
33
+ localizedName?: string
34
+ windowsAppId?: string
34
35
  }
35
36
 
36
37
  export type PathLike = string | Buffer | { href: string; toString(): string }
@@ -80,41 +81,137 @@ export async function getDefaultApplication(
80
81
  }
81
82
 
82
83
  export async function getFrontmostApplication(): Promise<Application> {
83
- // TODO: Implement system call to get frontmost application
84
- // For now, return Terminal as default
85
- const frontmostApp: Application = {
86
- name: 'Terminal',
87
- localizedName: 'Terminal',
88
- path: '/System/Applications/Utilities/Terminal.app',
89
- bundleId: 'com.apple.Terminal',
84
+ if (process.platform !== 'darwin') {
85
+ throw new Error('getFrontmostApplication is only supported on macOS')
86
+ }
87
+
88
+ const { execSync } = await import('node:child_process')
89
+
90
+ // Get frontmost app bundle ID
91
+ const bundleId = execSync(
92
+ `osascript -e 'tell application "System Events" to get bundle identifier of first application process whose frontmost is true'`,
93
+ { encoding: 'utf-8' },
94
+ ).trim()
95
+
96
+ // Get app path from bundle ID
97
+ const path = execSync(
98
+ `mdfind "kMDItemCFBundleIdentifier == '${bundleId}'" | head -1`,
99
+ { encoding: 'utf-8' },
100
+ ).trim()
101
+
102
+ // Get app name from path
103
+ const name = path.split('/').pop()?.replace('.app', '') || bundleId
104
+
105
+ return {
106
+ name,
107
+ localizedName: name,
108
+ path,
109
+ bundleId,
90
110
  }
91
- return Promise.resolve(frontmostApp)
92
111
  }
93
112
 
94
113
  export async function showInFinder(path: PathLike): Promise<void> {
95
- // TODO: Implement system call to show file in Finder
96
114
  const pathStr = typeof path === 'string' ? path : path.toString()
97
- console.log(`[showInFinder] Would open: ${pathStr}`)
98
- return Promise.resolve()
115
+
116
+ if (process.platform !== 'darwin') {
117
+ // On non-macOS, just open the parent directory
118
+ const { dirname } = await import('node:path')
119
+ return open(dirname(pathStr))
120
+ }
121
+
122
+ const { spawn } = await import('node:child_process')
123
+
124
+ return new Promise((resolve, reject) => {
125
+ // -R reveals the file in Finder
126
+ const child = spawn('open', ['-R', pathStr], { stdio: 'ignore' })
127
+ child.on('error', reject)
128
+ child.on('close', (code) => {
129
+ if (code === 0) {
130
+ resolve()
131
+ } else {
132
+ reject(new Error(`showInFinder failed with code ${code}`))
133
+ }
134
+ })
135
+ })
99
136
  }
100
137
 
101
138
  export async function trash(path: PathLike | PathLike[]): Promise<void> {
102
- // TODO: Implement system call to move files to trash
103
139
  const paths = Array.isArray(path) ? path : [path]
104
140
  const pathStrs = paths.map((p) => (typeof p === 'string' ? p : p.toString()))
105
- console.log(`[trash] Would trash: ${pathStrs.join(', ')}`)
106
- return Promise.resolve()
141
+
142
+ if (process.platform === 'darwin') {
143
+ const { execSync } = await import('node:child_process')
144
+
145
+ for (const filePath of pathStrs) {
146
+ // Use osascript to move to trash via Finder (proper macOS trash behavior)
147
+ execSync(
148
+ `osascript -e 'tell application "Finder" to delete POSIX file "${filePath}"'`,
149
+ { stdio: 'ignore' },
150
+ )
151
+ }
152
+ } else if (process.platform === 'win32') {
153
+ // Windows: use PowerShell to move to recycle bin
154
+ const { execSync } = await import('node:child_process')
155
+
156
+ for (const filePath of pathStrs) {
157
+ execSync(
158
+ `powershell -Command "Add-Type -AssemblyName Microsoft.VisualBasic; [Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('${filePath}', 'OnlyErrorDialogs', 'SendToRecycleBin')"`,
159
+ { stdio: 'ignore' },
160
+ )
161
+ }
162
+ } else {
163
+ // Linux: try trash-cli, fall back to gio trash
164
+ const { execSync, spawnSync } = await import('node:child_process')
165
+
166
+ // Check if trash-cli is available
167
+ const hasTrashCli = spawnSync('which', ['trash-put']).status === 0
168
+ const hasGio = spawnSync('which', ['gio']).status === 0
169
+
170
+ for (const filePath of pathStrs) {
171
+ if (hasTrashCli) {
172
+ execSync(`trash-put "${filePath}"`, { stdio: 'ignore' })
173
+ } else if (hasGio) {
174
+ execSync(`gio trash "${filePath}"`, { stdio: 'ignore' })
175
+ } else {
176
+ throw new Error(
177
+ 'No trash utility found. Install trash-cli or gio.',
178
+ )
179
+ }
180
+ }
181
+ }
107
182
  }
108
183
 
109
184
  export async function open(
110
185
  target: string,
111
186
  application?: Application | string,
112
187
  ): Promise<void> {
113
- // TODO: Implement system call to open file/URL with application
188
+ const { spawn } = await import('node:child_process')
114
189
  const appName =
115
190
  typeof application === 'string' ? application : application?.name
116
- console.log(`[open] Would open ${target}${appName ? ` with ${appName}` : ''}`)
117
- return Promise.resolve()
191
+
192
+ return new Promise((resolve, reject) => {
193
+ let cmd: string
194
+ let args: string[]
195
+
196
+ if (process.platform === 'darwin') {
197
+ // macOS
198
+ cmd = 'open'
199
+ args = appName ? ['-a', appName, target] : [target]
200
+ } else if (process.platform === 'win32') {
201
+ // Windows
202
+ cmd = 'cmd'
203
+ args = ['/c', 'start', '', target]
204
+ } else {
205
+ // Linux and others
206
+ cmd = 'xdg-open'
207
+ args = [target]
208
+ }
209
+
210
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true })
211
+ child.unref()
212
+ child.on('error', reject)
213
+ child.on('spawn', () => resolve())
214
+ })
118
215
  }
119
216
 
120
217
  export function captureException(exception: unknown): void {
@@ -0,0 +1,19 @@
1
+ // Dynamic watcher loader - only initializes when first called
2
+ // Following opencode's approach for bundling native modules in compiled binaries
3
+ // @ts-ignore - wrapper.js exists but types don't export it
4
+ import { createWrapper } from '@parcel/watcher/wrapper'
5
+ import type ParcelWatcher from '@parcel/watcher'
6
+
7
+ let _watcher: typeof ParcelWatcher | undefined
8
+
9
+ export function getWatcher(): typeof ParcelWatcher {
10
+ if (_watcher) return _watcher
11
+ // Use require with template literal so Bun can analyze and bundle the native module
12
+ // Linux requires -glibc suffix for the native binding
13
+ const suffix = process.platform === 'linux' ? '-glibc' : ''
14
+ const binding = require(
15
+ `@parcel/watcher-${process.platform}-${process.arch}${suffix}`,
16
+ )
17
+ _watcher = createWrapper(binding)
18
+ return _watcher!
19
+ }