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/release.tsx CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  compileExtension,
9
9
  ALL_TARGETS,
10
10
  targetToFileSuffix,
11
- targetToArchiveSuffix,
12
11
  getArchiveExtension,
13
12
  type CompileTarget,
14
13
  } from './compile'
@@ -22,6 +21,7 @@ export interface ReleaseResult {
22
21
  success: boolean
23
22
  tag: string
24
23
  uploadedFiles: string[]
24
+ installUrl: string
25
25
  }
26
26
 
27
27
  export async function releaseExtension({
@@ -46,7 +46,8 @@ export async function releaseExtension({
46
46
  throw new Error(`package.json must have a "name" field`)
47
47
  }
48
48
 
49
- // Get repo name from git remote
49
+ // Get repo owner and name from git remote
50
+ let repoOwner: string
50
51
  let repoName: string
51
52
  try {
52
53
  const remoteUrl = execSync('git config --get remote.origin.url', {
@@ -54,13 +55,16 @@ export async function releaseExtension({
54
55
  encoding: 'utf-8',
55
56
  }).trim()
56
57
  // Handle both HTTPS and SSH URLs:
57
- // https://github.com/user/repo.git -> repo
58
- // git@github.com:user/repo.git -> repo
59
- const match = remoteUrl.match(/\/([^/]+?)(?:\.git)?$/) || remoteUrl.match(/:([^/]+?)(?:\.git)?$/)
58
+ // https://github.com/user/repo.git -> user/repo
59
+ // git@github.com:user/repo.git -> user/repo
60
+ const httpsMatch = remoteUrl.match(/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/)
61
+ const sshMatch = remoteUrl.match(/github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/)
62
+ const match = httpsMatch || sshMatch
60
63
  if (!match) {
61
- throw new Error(`Could not parse repo name from remote URL: ${remoteUrl}`)
64
+ throw new Error(`Could not parse owner/repo from remote URL: ${remoteUrl}`)
62
65
  }
63
- repoName = match[1].replace(/\.git$/, '')
66
+ repoOwner = match[1]
67
+ repoName = match[2].replace(/\.git$/, '')
64
68
  } catch (error: any) {
65
69
  throw new Error(`Failed to get git remote: ${error.message}`)
66
70
  }
@@ -128,8 +132,7 @@ export async function releaseExtension({
128
132
  console.log(` ✓ ${path.basename(result.outfile)}`)
129
133
 
130
134
  const archiveExt = getArchiveExtension(target)
131
- const archiveSuffix = targetToArchiveSuffix(target)
132
- const archiveName = `${repoName}-${archiveSuffix}${archiveExt}`
135
+ const archiveName = `${repoName}-${suffix.replace(/\.exe$/, '')}${archiveExt}`
133
136
  const archivePath = path.join(outputDir, archiveName)
134
137
 
135
138
  if (archiveExt === '.tar.gz') {
@@ -170,13 +173,16 @@ export async function releaseExtension({
170
173
 
171
174
 
172
175
 
176
+ const installUrl = `https://termcast.app/${repoOwner}/${repoName}/install`
173
177
  console.log(`\n✅ Release ${tag} published successfully!`)
174
178
  console.log(` ${compileResults.length} binaries uploaded`)
175
-
179
+ console.log(`\nInstall script:`)
180
+ console.log(` curl -sf ${installUrl} | bash`)
176
181
 
177
182
  return {
178
183
  success: true,
179
184
  tag,
180
185
  uploadedFiles: compileResults,
186
+ installUrl,
181
187
  }
182
188
  }
package/src/state.tsx CHANGED
@@ -2,11 +2,34 @@ import { create } from 'zustand'
2
2
  import { type ReactNode } from 'react'
3
3
  import type { RaycastPackageJson } from './package-json'
4
4
 
5
+ // Toast action keyboard shortcuts (ctrl+t for primary, ctrl+g for secondary)
6
+ export const toastPrimaryActionKey = { ctrl: true, name: 't' } as const
7
+ export const toastSecondaryActionKey = { ctrl: true, name: 'g' } as const
8
+
9
+ export type ToastStyle = 'SUCCESS' | 'FAILURE' | 'ANIMATED'
10
+
11
+ export interface ToastActionData {
12
+ title: string
13
+ onAction: () => void
14
+ }
15
+
16
+ export interface ToastData {
17
+ id: string
18
+ title: string
19
+ message?: string
20
+ style: ToastStyle
21
+ primaryAction?: ToastActionData
22
+ secondaryAction?: ToastActionData
23
+ onHide: () => void
24
+ }
25
+
5
26
  export type DialogPosition = 'center' | 'top-right' | 'bottom-right'
27
+ export type DialogStackItemType = 'actions' | undefined
6
28
 
7
29
  export interface DialogStackItem {
8
30
  element: ReactNode
9
31
  position?: DialogPosition
32
+ type?: DialogStackItemType
10
33
  }
11
34
 
12
35
  export interface NavigationStackItem {
@@ -15,29 +38,37 @@ export interface NavigationStackItem {
15
38
  }
16
39
 
17
40
  interface AppState {
18
- toast: ReactNode | null
41
+ toast: ToastData | null
42
+ toastWithPrimaryAction: boolean
19
43
  dialogStack: DialogStackItem[]
20
- // Navigation state
21
44
  navigationStack: NavigationStackItem[]
22
45
  // Dev mode state
23
46
  devElement: ReactNode | null
47
+ // used to bust cache in bun after imports. incremented on each file change
24
48
  devRebuildCount: number
25
49
  // Extension and command state
50
+ // TODO this should always be available. for compiled extensions it should be homedir/.termcast/extension-name
51
+ // there we should put data, cache, etc. the logic should be same exact as dev command extensions. simply changes the extension path
52
+ // in this same folder we should also put the binary executable, which will be added in PATH. inside a bin folder
26
53
  extensionPath: string | null
54
+ // TODO extensionPackageJson should be always defined. even for dev extensions or compiled extensions.
55
+ // it is ok to fail in functions that need it. if examples without package.json need to use this field we should move them to actual extensions inside fixtures then
27
56
  extensionPackageJson: RaycastPackageJson | null
28
57
  currentCommandName: string | null
29
58
  currentCommandArguments: Record<string, string> | null
30
- // OAuth state
31
59
  googleAccessToken?: string
32
60
  googleIdToken?: string
33
61
  // Actions state - when true, auto-execute first action instead of showing sheet
34
62
  shouldAutoExecuteFirstAction: boolean
35
63
  // First action title for footer display (set by offscreen ActionPanel)
36
64
  firstActionTitle: string
65
+ // Theme state
66
+ currentThemeName: string
37
67
  }
38
68
 
39
69
  export const useStore = create<AppState>(() => ({
40
70
  toast: null,
71
+ toastWithPrimaryAction: false,
41
72
  dialogStack: [],
42
73
  // Navigation state
43
74
  navigationStack: [],
@@ -55,4 +86,6 @@ export const useStore = create<AppState>(() => ({
55
86
  // Actions state
56
87
  shouldAutoExecuteFirstAction: false,
57
88
  firstActionTitle: '',
89
+ // Theme state
90
+ currentThemeName: 'termcast',
58
91
  }))
package/src/theme.tsx CHANGED
@@ -1,56 +1,87 @@
1
1
  import { SyntaxStyle, RGBA } from '@opentui/core'
2
+ import { getResolvedTheme, type ResolvedTheme, defaultThemeName, themeNames } from './themes'
3
+ import { useStore } from './state'
4
+ import { Cache } from './apis/cache'
2
5
 
3
- export const Theme = {
4
- // Text colors
5
- text: '#FFFFFF',
6
- textMuted: '#999999',
7
-
8
- // Background colors
9
- background: '#000000',
10
- backgroundPanel: '#1E1E1E', // Dark gray panel background
11
-
12
- // Primary/accent colors
13
- primary: '#FFC000', // Bright golden orange
14
- accent: '#FFC000', // Bright golden orange
15
-
16
- // Accessory colors (from List component)
17
- info: '#FFC000', // Orange for text accessories
18
- success: '#FFC000', // Orange for date accessories
19
- warning: '#FFC000', // Orange for tag accessories
20
- error: '#FF0000', // Red for errors
21
-
22
- // Additional UI colors
23
- border: '#333333',
24
- selectedMuted: '#AA8000', // Dimmer orange for selected values when not focused
25
- highlight: '#FFC000',
26
- selected: '#FFC000',
27
- yellow: '#FFFF00', // Yellow for icons
28
- link: '#FFC000', // Orange for links
29
-
30
- // Transparent
31
- transparent: undefined, // Use undefined for no background color
32
- } as const
33
-
34
- export const markdownSyntaxStyle = SyntaxStyle.fromStyles({
35
- default: { fg: RGBA.fromHex(Theme.text) },
36
- 'markup.heading.1': { fg: RGBA.fromHex(Theme.primary), bold: true },
37
- 'markup.heading.2': { fg: RGBA.fromHex(Theme.primary), bold: true },
38
- 'markup.heading.3': { fg: RGBA.fromHex(Theme.primary), bold: true },
39
- 'markup.heading.4': { fg: RGBA.fromHex(Theme.primary), bold: true },
40
- 'markup.heading.5': { fg: RGBA.fromHex(Theme.primary), bold: true },
41
- 'markup.heading.6': { fg: RGBA.fromHex(Theme.primary), bold: true },
42
- 'markup.heading': { fg: RGBA.fromHex(Theme.primary), bold: true },
43
- 'markup.raw.block': { fg: RGBA.fromHex(Theme.accent) },
44
- 'markup.link.url': { fg: RGBA.fromHex(Theme.link) },
45
- 'markup.link.label': { fg: RGBA.fromHex(Theme.link) },
46
- 'markup.list': { fg: RGBA.fromHex(Theme.warning) },
47
- 'markup.list.checked': { fg: RGBA.fromHex(Theme.success) },
48
- 'markup.list.unchecked': { fg: RGBA.fromHex(Theme.textMuted) },
49
- 'markup.quote': { fg: RGBA.fromHex(Theme.textMuted), italic: true },
50
- 'punctuation.special': { fg: RGBA.fromHex(Theme.textMuted) },
51
- 'punctuation.delimiter': { fg: RGBA.fromHex(Theme.textMuted) },
52
- 'string.escape': { fg: RGBA.fromHex(Theme.warning) },
53
- label: { fg: RGBA.fromHex(Theme.accent) },
6
+ // Global cache for theme persistence (no namespace = global storage)
7
+ let globalCache: Cache | null = null
8
+
9
+ function getGlobalCache(): Cache {
10
+ if (!globalCache) {
11
+ globalCache = new Cache()
12
+ }
13
+ return globalCache
14
+ }
15
+
16
+ const THEME_STORAGE_KEY = 'termcast.theme'
17
+
18
+ export function loadPersistedTheme(): string {
19
+ try {
20
+ const stored = getGlobalCache().get(THEME_STORAGE_KEY)
21
+ if (stored && themeNames.includes(stored)) {
22
+ return stored
23
+ }
24
+ } catch {
25
+ // Ignore errors on load
26
+ }
27
+ return defaultThemeName
28
+ }
29
+
30
+ export function persistTheme(name: string): void {
31
+ try {
32
+ getGlobalCache().set(THEME_STORAGE_KEY, name)
33
+ } catch {
34
+ // Ignore errors on save
35
+ }
36
+ }
37
+
38
+ // Initialize theme from persistence - call this on app startup
39
+ export function initializeTheme(): void {
40
+ const themeName = loadPersistedTheme()
41
+ useStore.setState({ currentThemeName: themeName })
42
+ }
43
+
44
+ // Proxy-based Theme object that reads from zustand state
45
+ export const Theme: ResolvedTheme = new Proxy({} as ResolvedTheme, {
46
+ get(_, prop: string) {
47
+ const themeName = useStore.getState().currentThemeName
48
+ const resolved = getResolvedTheme(themeName)
49
+ return resolved[prop as keyof ResolvedTheme]
50
+ },
51
+ })
52
+
53
+ export function getMarkdownSyntaxStyle(): SyntaxStyle {
54
+ const themeName = useStore.getState().currentThemeName
55
+ const t = getResolvedTheme(themeName)
56
+ return SyntaxStyle.fromStyles({
57
+ default: { fg: RGBA.fromHex(t.markdownText) },
58
+ 'markup.heading.1': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
59
+ 'markup.heading.2': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
60
+ 'markup.heading.3': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
61
+ 'markup.heading.4': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
62
+ 'markup.heading.5': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
63
+ 'markup.heading.6': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
64
+ 'markup.heading': { fg: RGBA.fromHex(t.markdownHeading), bold: true },
65
+ 'markup.raw.block': { fg: RGBA.fromHex(t.markdownCode) },
66
+ 'markup.link.url': { fg: RGBA.fromHex(t.markdownLink) },
67
+ 'markup.link.label': { fg: RGBA.fromHex(t.markdownLinkText) },
68
+ 'markup.list': { fg: RGBA.fromHex(t.markdownListItem) },
69
+ 'markup.list.checked': { fg: RGBA.fromHex(t.success) },
70
+ 'markup.list.unchecked': { fg: RGBA.fromHex(t.textMuted) },
71
+ 'markup.quote': { fg: RGBA.fromHex(t.markdownBlockQuote), italic: true },
72
+ 'punctuation.special': { fg: RGBA.fromHex(t.syntaxPunctuation) },
73
+ 'punctuation.delimiter': { fg: RGBA.fromHex(t.syntaxPunctuation) },
74
+ 'string.escape': { fg: RGBA.fromHex(t.syntaxString) },
75
+ label: { fg: RGBA.fromHex(t.accent) },
76
+ })
77
+ }
78
+
79
+ // For backward compatibility - some code imports markdownSyntaxStyle directly
80
+ // This is a getter that returns the current theme's syntax style
81
+ export const markdownSyntaxStyle = new Proxy({} as SyntaxStyle, {
82
+ get(_, prop: string) {
83
+ return getMarkdownSyntaxStyle()[prop as keyof SyntaxStyle]
84
+ },
54
85
  })
55
86
 
56
87
  export default Theme
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://opencode.ai/theme.json",
3
+ "defs": {
4
+ "darkBg": "#0f0f0f",
5
+ "darkBgPanel": "#15141b",
6
+ "darkBorder": "#2d2d2d",
7
+ "darkFgMuted": "#6d6d6d",
8
+ "darkFg": "#edecee",
9
+ "purple": "#a277ff",
10
+ "pink": "#f694ff",
11
+ "blue": "#82e2ff",
12
+ "red": "#ff6767",
13
+ "orange": "#ffca85",
14
+ "cyan": "#61ffca",
15
+ "green": "#9dff65"
16
+ },
17
+ "theme": {
18
+ "primary": "purple",
19
+ "secondary": "pink",
20
+ "accent": "purple",
21
+ "error": "red",
22
+ "warning": "orange",
23
+ "success": "cyan",
24
+ "info": "purple",
25
+ "text": "darkFg",
26
+ "textMuted": "darkFgMuted",
27
+ "background": "darkBg",
28
+ "backgroundPanel": "darkBgPanel",
29
+ "backgroundElement": "darkBgPanel",
30
+ "border": "darkBorder",
31
+ "borderActive": "darkFgMuted",
32
+ "borderSubtle": "darkBorder",
33
+ "diffAdded": "cyan",
34
+ "diffRemoved": "red",
35
+ "diffContext": "darkFgMuted",
36
+ "diffHunkHeader": "darkFgMuted",
37
+ "diffHighlightAdded": "cyan",
38
+ "diffHighlightRemoved": "red",
39
+ "diffAddedBg": "#354933",
40
+ "diffRemovedBg": "#3f191a",
41
+ "diffContextBg": "darkBgPanel",
42
+ "diffLineNumber": "darkBorder",
43
+ "diffAddedLineNumberBg": "#162620",
44
+ "diffRemovedLineNumberBg": "#26161a",
45
+ "markdownText": "darkFg",
46
+ "markdownHeading": "purple",
47
+ "markdownLink": "pink",
48
+ "markdownLinkText": "purple",
49
+ "markdownCode": "cyan",
50
+ "markdownBlockQuote": "darkFgMuted",
51
+ "markdownEmph": "orange",
52
+ "markdownStrong": "purple",
53
+ "markdownHorizontalRule": "darkFgMuted",
54
+ "markdownListItem": "purple",
55
+ "markdownListEnumeration": "purple",
56
+ "markdownImage": "pink",
57
+ "markdownImageText": "purple",
58
+ "markdownCodeBlock": "darkFg",
59
+ "syntaxComment": "darkFgMuted",
60
+ "syntaxKeyword": "pink",
61
+ "syntaxFunction": "purple",
62
+ "syntaxVariable": "purple",
63
+ "syntaxString": "cyan",
64
+ "syntaxNumber": "green",
65
+ "syntaxType": "purple",
66
+ "syntaxOperator": "pink",
67
+ "syntaxPunctuation": "darkFg"
68
+ }
69
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "$schema": "https://opencode.ai/theme.json",
3
+ "defs": {
4
+ "darkBg": "#0B0E14",
5
+ "darkBgAlt": "#0D1017",
6
+ "darkLine": "#11151C",
7
+ "darkPanel": "#0F131A",
8
+ "darkFg": "#BFBDB6",
9
+ "darkFgMuted": "#565B66",
10
+ "darkGutter": "#6C7380",
11
+ "darkTag": "#39BAE6",
12
+ "darkFunc": "#FFB454",
13
+ "darkEntity": "#59C2FF",
14
+ "darkString": "#AAD94C",
15
+ "darkRegexp": "#95E6CB",
16
+ "darkMarkup": "#F07178",
17
+ "darkKeyword": "#FF8F40",
18
+ "darkSpecial": "#E6B673",
19
+ "darkComment": "#ACB6BF",
20
+ "darkConstant": "#D2A6FF",
21
+ "darkOperator": "#F29668",
22
+ "darkAdded": "#7FD962",
23
+ "darkRemoved": "#F26D78",
24
+ "darkAccent": "#E6B450",
25
+ "darkError": "#D95757",
26
+ "darkIndentActive": "#6C7380"
27
+ },
28
+ "theme": {
29
+ "primary": "darkEntity",
30
+ "secondary": "darkConstant",
31
+ "accent": "darkAccent",
32
+ "error": "darkError",
33
+ "warning": "darkSpecial",
34
+ "success": "darkAdded",
35
+ "info": "darkTag",
36
+ "text": "darkFg",
37
+ "textMuted": "darkFgMuted",
38
+ "background": "darkBg",
39
+ "backgroundPanel": "darkPanel",
40
+ "backgroundElement": "darkBgAlt",
41
+ "border": "darkGutter",
42
+ "borderActive": "darkIndentActive",
43
+ "borderSubtle": "darkLine",
44
+ "diffAdded": "darkAdded",
45
+ "diffRemoved": "darkRemoved",
46
+ "diffContext": "darkComment",
47
+ "diffHunkHeader": "darkComment",
48
+ "diffHighlightAdded": "darkString",
49
+ "diffHighlightRemoved": "darkMarkup",
50
+ "diffAddedBg": "#20303b",
51
+ "diffRemovedBg": "#37222c",
52
+ "diffContextBg": "darkPanel",
53
+ "diffLineNumber": "darkGutter",
54
+ "diffAddedLineNumberBg": "#1b2b34",
55
+ "diffRemovedLineNumberBg": "#2d1f26",
56
+ "markdownText": "darkFg",
57
+ "markdownHeading": "darkConstant",
58
+ "markdownLink": "darkEntity",
59
+ "markdownLinkText": "darkTag",
60
+ "markdownCode": "darkString",
61
+ "markdownBlockQuote": "darkSpecial",
62
+ "markdownEmph": "darkSpecial",
63
+ "markdownStrong": "darkFunc",
64
+ "markdownHorizontalRule": "darkFgMuted",
65
+ "markdownListItem": "darkEntity",
66
+ "markdownListEnumeration": "darkTag",
67
+ "markdownImage": "darkEntity",
68
+ "markdownImageText": "darkTag",
69
+ "markdownCodeBlock": "darkFg",
70
+ "syntaxComment": "darkComment",
71
+ "syntaxKeyword": "darkKeyword",
72
+ "syntaxFunction": "darkFunc",
73
+ "syntaxVariable": "darkEntity",
74
+ "syntaxString": "darkString",
75
+ "syntaxNumber": "darkConstant",
76
+ "syntaxType": "darkSpecial",
77
+ "syntaxOperator": "darkOperator",
78
+ "syntaxPunctuation": "darkFg"
79
+ }
80
+ }
@@ -0,0 +1,233 @@
1
+ {
2
+ "$schema": "https://opencode.ai/theme.json",
3
+ "defs": {
4
+ "frappeRosewater": "#f2d5cf",
5
+ "frappeFlamingo": "#eebebe",
6
+ "frappePink": "#f4b8e4",
7
+ "frappeMauve": "#ca9ee6",
8
+ "frappeRed": "#e78284",
9
+ "frappeMaroon": "#ea999c",
10
+ "frappePeach": "#ef9f76",
11
+ "frappeYellow": "#e5c890",
12
+ "frappeGreen": "#a6d189",
13
+ "frappeTeal": "#81c8be",
14
+ "frappeSky": "#99d1db",
15
+ "frappeSapphire": "#85c1dc",
16
+ "frappeBlue": "#8da4e2",
17
+ "frappeLavender": "#babbf1",
18
+ "frappeText": "#c6d0f5",
19
+ "frappeSubtext1": "#b5bfe2",
20
+ "frappeSubtext0": "#a5adce",
21
+ "frappeOverlay2": "#949cb8",
22
+ "frappeOverlay1": "#838ba7",
23
+ "frappeOverlay0": "#737994",
24
+ "frappeSurface2": "#626880",
25
+ "frappeSurface1": "#51576d",
26
+ "frappeSurface0": "#414559",
27
+ "frappeBase": "#303446",
28
+ "frappeMantle": "#292c3c",
29
+ "frappeCrust": "#232634"
30
+ },
31
+ "theme": {
32
+ "primary": {
33
+ "dark": "frappeBlue",
34
+ "light": "frappeBlue"
35
+ },
36
+ "secondary": {
37
+ "dark": "frappeMauve",
38
+ "light": "frappeMauve"
39
+ },
40
+ "accent": {
41
+ "dark": "frappePink",
42
+ "light": "frappePink"
43
+ },
44
+ "error": {
45
+ "dark": "frappeRed",
46
+ "light": "frappeRed"
47
+ },
48
+ "warning": {
49
+ "dark": "frappeYellow",
50
+ "light": "frappeYellow"
51
+ },
52
+ "success": {
53
+ "dark": "frappeGreen",
54
+ "light": "frappeGreen"
55
+ },
56
+ "info": {
57
+ "dark": "frappeTeal",
58
+ "light": "frappeTeal"
59
+ },
60
+ "text": {
61
+ "dark": "frappeText",
62
+ "light": "frappeText"
63
+ },
64
+ "textMuted": {
65
+ "dark": "frappeSubtext1",
66
+ "light": "frappeSubtext1"
67
+ },
68
+ "background": {
69
+ "dark": "frappeBase",
70
+ "light": "frappeBase"
71
+ },
72
+ "backgroundPanel": {
73
+ "dark": "frappeMantle",
74
+ "light": "frappeMantle"
75
+ },
76
+ "backgroundElement": {
77
+ "dark": "frappeCrust",
78
+ "light": "frappeCrust"
79
+ },
80
+ "border": {
81
+ "dark": "frappeSurface0",
82
+ "light": "frappeSurface0"
83
+ },
84
+ "borderActive": {
85
+ "dark": "frappeSurface1",
86
+ "light": "frappeSurface1"
87
+ },
88
+ "borderSubtle": {
89
+ "dark": "frappeSurface2",
90
+ "light": "frappeSurface2"
91
+ },
92
+ "diffAdded": {
93
+ "dark": "frappeGreen",
94
+ "light": "frappeGreen"
95
+ },
96
+ "diffRemoved": {
97
+ "dark": "frappeRed",
98
+ "light": "frappeRed"
99
+ },
100
+ "diffContext": {
101
+ "dark": "frappeOverlay2",
102
+ "light": "frappeOverlay2"
103
+ },
104
+ "diffHunkHeader": {
105
+ "dark": "frappePeach",
106
+ "light": "frappePeach"
107
+ },
108
+ "diffHighlightAdded": {
109
+ "dark": "frappeGreen",
110
+ "light": "frappeGreen"
111
+ },
112
+ "diffHighlightRemoved": {
113
+ "dark": "frappeRed",
114
+ "light": "frappeRed"
115
+ },
116
+ "diffAddedBg": {
117
+ "dark": "#29342b",
118
+ "light": "#29342b"
119
+ },
120
+ "diffRemovedBg": {
121
+ "dark": "#3a2a31",
122
+ "light": "#3a2a31"
123
+ },
124
+ "diffContextBg": {
125
+ "dark": "frappeMantle",
126
+ "light": "frappeMantle"
127
+ },
128
+ "diffLineNumber": {
129
+ "dark": "frappeSurface1",
130
+ "light": "frappeSurface1"
131
+ },
132
+ "diffAddedLineNumberBg": {
133
+ "dark": "#223025",
134
+ "light": "#223025"
135
+ },
136
+ "diffRemovedLineNumberBg": {
137
+ "dark": "#2f242b",
138
+ "light": "#2f242b"
139
+ },
140
+ "markdownText": {
141
+ "dark": "frappeText",
142
+ "light": "frappeText"
143
+ },
144
+ "markdownHeading": {
145
+ "dark": "frappeMauve",
146
+ "light": "frappeMauve"
147
+ },
148
+ "markdownLink": {
149
+ "dark": "frappeBlue",
150
+ "light": "frappeBlue"
151
+ },
152
+ "markdownLinkText": {
153
+ "dark": "frappeSky",
154
+ "light": "frappeSky"
155
+ },
156
+ "markdownCode": {
157
+ "dark": "frappeGreen",
158
+ "light": "frappeGreen"
159
+ },
160
+ "markdownBlockQuote": {
161
+ "dark": "frappeYellow",
162
+ "light": "frappeYellow"
163
+ },
164
+ "markdownEmph": {
165
+ "dark": "frappeYellow",
166
+ "light": "frappeYellow"
167
+ },
168
+ "markdownStrong": {
169
+ "dark": "frappePeach",
170
+ "light": "frappePeach"
171
+ },
172
+ "markdownHorizontalRule": {
173
+ "dark": "frappeSubtext0",
174
+ "light": "frappeSubtext0"
175
+ },
176
+ "markdownListItem": {
177
+ "dark": "frappeBlue",
178
+ "light": "frappeBlue"
179
+ },
180
+ "markdownListEnumeration": {
181
+ "dark": "frappeSky",
182
+ "light": "frappeSky"
183
+ },
184
+ "markdownImage": {
185
+ "dark": "frappeBlue",
186
+ "light": "frappeBlue"
187
+ },
188
+ "markdownImageText": {
189
+ "dark": "frappeSky",
190
+ "light": "frappeSky"
191
+ },
192
+ "markdownCodeBlock": {
193
+ "dark": "frappeText",
194
+ "light": "frappeText"
195
+ },
196
+ "syntaxComment": {
197
+ "dark": "frappeOverlay2",
198
+ "light": "frappeOverlay2"
199
+ },
200
+ "syntaxKeyword": {
201
+ "dark": "frappeMauve",
202
+ "light": "frappeMauve"
203
+ },
204
+ "syntaxFunction": {
205
+ "dark": "frappeBlue",
206
+ "light": "frappeBlue"
207
+ },
208
+ "syntaxVariable": {
209
+ "dark": "frappeRed",
210
+ "light": "frappeRed"
211
+ },
212
+ "syntaxString": {
213
+ "dark": "frappeGreen",
214
+ "light": "frappeGreen"
215
+ },
216
+ "syntaxNumber": {
217
+ "dark": "frappePeach",
218
+ "light": "frappePeach"
219
+ },
220
+ "syntaxType": {
221
+ "dark": "frappeYellow",
222
+ "light": "frappeYellow"
223
+ },
224
+ "syntaxOperator": {
225
+ "dark": "frappeSky",
226
+ "light": "frappeSky"
227
+ },
228
+ "syntaxPunctuation": {
229
+ "dark": "frappeText",
230
+ "light": "frappeText"
231
+ }
232
+ }
233
+ }