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
@@ -2,8 +2,9 @@ import React, { ReactNode, useState, useEffect } from 'react'
2
2
  import { TextAttributes } from '@opentui/core'
3
3
  import { useTerminalDimensions, useKeyboard } from '@opentui/react'
4
4
  import { colord } from 'colord'
5
- import { Theme } from 'termcast/src/theme'
5
+ import { useTheme } from 'termcast/src/theme'
6
6
  import { openInBrowser } from 'termcast/src/action-utils'
7
+ import { termcastMaxContentWidth } from 'termcast/src/utils'
7
8
  import {
8
9
  useStore,
9
10
  toastPrimaryActionKey,
@@ -12,11 +13,6 @@ import {
12
13
  } from 'termcast/src/state'
13
14
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
14
15
 
15
- /** Returns white or black foreground color based on background lightness */
16
- function getFgForBg(bgColor: string): string {
17
- return colord(bgColor).isLight() ? '#000000' : '#ffffff'
18
- }
19
-
20
16
  interface FooterProps {
21
17
  children?: ReactNode
22
18
  paddingLeft?: number
@@ -29,8 +25,12 @@ interface FooterProps {
29
25
 
30
26
  const MIN_WIDTH_FOR_POWERED_BY = 75
31
27
 
28
+ const TOAST_MARGIN = 2
29
+
32
30
  function ToastInline({ toast }: { toast: ToastData }): any {
31
+ const theme = useTheme()
33
32
  const inFocus = useIsInFocus()
33
+ const { width: terminalWidth } = useTerminalDimensions()
34
34
  const [animationFrame, setAnimationFrame] = useState(0)
35
35
 
36
36
  // Keyboard handling for toast actions
@@ -91,66 +91,76 @@ function ToastInline({ toast }: { toast: ToastData }): any {
91
91
  const getIconColor = () => {
92
92
  switch (toast.style) {
93
93
  case 'SUCCESS':
94
- return Theme.success
94
+ return theme.success
95
95
  case 'FAILURE':
96
- return Theme.error
96
+ return theme.error
97
97
  case 'ANIMATED':
98
- return Theme.primary
98
+ return theme.primary
99
99
  default:
100
- return Theme.success
100
+ return theme.success
101
101
  }
102
102
  }
103
103
 
104
- const primaryBg = Theme.primary
105
- const primaryFg = getFgForBg(primaryBg)
106
- const keysBg = colord(primaryBg).darken(0.06).toHex()
104
+ const primaryColor = theme.primary
105
+ const mutedColor = colord(primaryColor).darken(0.06).toHex()
106
+
107
+ const maxToastWidth = Math.min(terminalWidth, termcastMaxContentWidth)
108
+ const toastWidth = maxToastWidth - TOAST_MARGIN * 2
107
109
 
108
- const hasKeys = !!toast.primaryAction?.title || !!toast.secondaryAction?.title
109
110
  return (
110
111
  <box
111
112
  flexDirection='row'
112
- marginLeft={-3}
113
- marginRight={-3}
114
- flexGrow={1}
113
+ width={toastWidth}
114
+ marginLeft={-TOAST_MARGIN}
115
+ marginRight={-TOAST_MARGIN}
116
+ flexGrow={0}
117
+ flexShrink={0}
115
118
  overflow='hidden'
119
+ height={1}
116
120
  >
117
121
  {/* Title box */}
118
122
  <box
119
123
  flexDirection='row'
120
124
  flexShrink={0}
121
- backgroundColor={colord(primaryBg).lighten(0.1).toHex()}
122
- paddingLeft={3}
123
125
  paddingRight={1}
126
+ overflow='hidden'
127
+ height={1}
124
128
  >
125
- <text flexShrink={0} fg={getIconColor()}>
129
+ <text flexShrink={0} fg={getIconColor()} wrapMode='none'>
126
130
  {getIcon()}{' '}
127
131
  </text>
128
- <text flexShrink={0} fg={primaryFg} attributes={TextAttributes.BOLD}>
132
+ <text
133
+ flexShrink={1}
134
+ fg={primaryColor}
135
+ attributes={TextAttributes.BOLD}
136
+ wrapMode='none'
137
+ >
129
138
  {toast.title}
130
139
  </text>
131
140
  </box>
132
- {/* Message/description box (in the middle with keys background) */}
141
+ {/* Message/description box */}
133
142
  <box
134
143
  flexGrow={1}
135
- backgroundColor={keysBg}
144
+ flexShrink={1}
136
145
  paddingLeft={1}
137
146
  paddingRight={1}
138
147
  flexDirection='row'
139
148
  overflow='hidden'
149
+ height={1}
140
150
  >
141
- <text fg={primaryFg} wrapMode='none'>
151
+ <text fg={mutedColor} flexShrink={1} wrapMode='none'>
142
152
  {toast.message || ''}
143
153
  </text>
144
154
  </box>
145
155
  {/* Keys box (right aligned, no grow) */}
146
-
147
156
  <box
148
- backgroundColor={keysBg}
149
157
  paddingLeft={1}
150
- paddingRight={3}
158
+
151
159
  gap={1}
152
160
  flexDirection='row'
153
161
  flexShrink={0}
162
+ overflow='hidden'
163
+ height={1}
154
164
  >
155
165
  {toast.primaryAction?.title && (
156
166
  <box
@@ -160,10 +170,17 @@ function ToastInline({ toast }: { toast: ToastData }): any {
160
170
  toast.primaryAction?.onAction()
161
171
  }}
162
172
  >
163
- <text fg={primaryFg} attributes={TextAttributes.BOLD}>
173
+ <text
174
+ fg={mutedColor}
175
+ attributes={TextAttributes.BOLD}
176
+ wrapMode='none'
177
+ >
164
178
  {toast.primaryAction.title}
165
179
  </text>
166
- <text fg={primaryFg}> ctrl t</text>
180
+ <text fg={mutedColor} wrapMode='none'>
181
+ {' '}
182
+ ctrl t
183
+ </text>
167
184
  </box>
168
185
  )}
169
186
  {toast.secondaryAction?.title && (
@@ -174,10 +191,17 @@ function ToastInline({ toast }: { toast: ToastData }): any {
174
191
  toast.secondaryAction?.onAction()
175
192
  }}
176
193
  >
177
- <text fg={primaryFg} attributes={TextAttributes.BOLD}>
194
+ <text
195
+ fg={mutedColor}
196
+ attributes={TextAttributes.BOLD}
197
+ wrapMode='none'
198
+ >
178
199
  {toast.secondaryAction.title}
179
200
  </text>
180
- <text fg={primaryFg}> ctrl g</text>
201
+ <text fg={mutedColor} wrapMode='none'>
202
+ {' '}
203
+ ctrl g
204
+ </text>
181
205
  </box>
182
206
  )}
183
207
  </box>
@@ -194,6 +218,7 @@ export function Footer({
194
218
  marginTop = 1,
195
219
  hidePoweredBy = false,
196
220
  }: FooterProps): any {
221
+ const theme = useTheme()
197
222
  const { width } = useTerminalDimensions()
198
223
  const showPoweredBy = !hidePoweredBy && width >= MIN_WIDTH_FOR_POWERED_BY
199
224
  const toast = useStore((state) => state.toast)
@@ -219,7 +244,7 @@ export function Footer({
219
244
  {children}
220
245
  {showPoweredBy && (
221
246
  <box flexDirection='row' gap={1}>
222
- <text flexShrink={0} fg={Theme.textMuted}>
247
+ <text flexShrink={0} fg={theme.textMuted}>
223
248
  powered by
224
249
  </text>
225
250
  <text
@@ -227,7 +252,7 @@ export function Footer({
227
252
  onMouseDown={() => {
228
253
  openInBrowser('https://termcast.app')
229
254
  }}
230
- fg={Theme.textMuted}
255
+ fg={theme.textMuted}
231
256
  attributes={TextAttributes.BOLD}
232
257
  >
233
258
  termcast
@@ -5,8 +5,8 @@ import { useFormContext, Controller } from 'react-hook-form'
5
5
  import { useFocusContext, useFormFieldDescendant } from './index'
6
6
  import { FormItemProps, FormItemRef } from './types'
7
7
  import { logger } from 'termcast/src/logger'
8
- import { Theme } from 'termcast/src/theme'
9
- import { WithLeftBorder } from './with-left-border'
8
+ import { useTheme } from 'termcast/src/theme'
9
+ import { WithLeftBorder, TitleIndicator } from './with-left-border'
10
10
  import { useFormNavigation } from './use-form-navigation'
11
11
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
12
12
  import { LoadingText } from 'termcast/src/components/loading-text'
@@ -18,6 +18,7 @@ export interface CheckboxProps extends FormItemProps<boolean> {
18
18
  export type CheckboxRef = FormItemRef
19
19
 
20
20
  export const Checkbox = (props: CheckboxProps): any => {
21
+ const theme = useTheme()
21
22
  const { control, setValue, getValues } = useFormContext()
22
23
  const focusContext = useFocusContext()
23
24
  const { focusedField, setFocusedField } = focusContext
@@ -59,28 +60,28 @@ export const Checkbox = (props: CheckboxProps): any => {
59
60
  render={({ field, fieldState, formState }) => {
60
61
  return (
61
62
  <box ref={elementRef} flexDirection='column'>
62
- <WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
63
- <box
64
- onMouseDown={() => {
65
- // Always focus the field when clicked
66
- if (!isFocused) {
67
- setFocusedField(props.id)
68
- }
69
- // Always toggle the value when clicked
70
- handleToggle()
71
- }}
72
- >
73
- <LoadingText
74
- isLoading={isFocused && focusContext.isLoading}
75
- color={isFocused ? Theme.primary : Theme.text}
63
+ <WithLeftBorder isFocused={isFocused} paddingBottom={1}>
64
+ <TitleIndicator isFocused={isFocused} isLoading={focusContext.isLoading}>
65
+ <box
66
+ onMouseDown={() => {
67
+ // Always focus the field when clicked
68
+ if (!isFocused) {
69
+ setFocusedField(props.id)
70
+ }
71
+ // Always toggle the value when clicked
72
+ handleToggle()
73
+ }}
76
74
  >
77
- {props.title || ''}
78
- </LoadingText>
79
- </box>
80
- </WithLeftBorder>
81
- <WithLeftBorder isFocused={isFocused}>
75
+ <LoadingText
76
+ isLoading={isFocused && focusContext.isLoading}
77
+ color={isFocused ? theme.primary : theme.text}
78
+ >
79
+ {props.title || ''}
80
+ </LoadingText>
81
+ </box>
82
+ </TitleIndicator>
82
83
  <text
83
- fg={isFocused ? Theme.accent : Theme.text}
84
+ fg={isFocused ? theme.accent : theme.text}
84
85
  selectable={false}
85
86
  onMouseDown={() => {
86
87
  if (!isFocused) {
@@ -91,17 +92,14 @@ export const Checkbox = (props: CheckboxProps): any => {
91
92
  >
92
93
  {field.value ? '●' : '○'} {props.label}
93
94
  </text>
95
+ {(props.error || props.info) && <box height={1} />}
96
+ {props.error && (
97
+ <text fg={theme.error}>{props.error}</text>
98
+ )}
99
+ {props.info && (
100
+ <text fg={theme.textMuted}>{props.info}</text>
101
+ )}
94
102
  </WithLeftBorder>
95
- {props.error && (
96
- <WithLeftBorder isFocused={isFocused}>
97
- <text fg={Theme.error}>{props.error}</text>
98
- </WithLeftBorder>
99
- )}
100
- {props.info && (
101
- <WithLeftBorder isFocused={isFocused}>
102
- <text fg={Theme.textMuted}>{props.info}</text>
103
- </WithLeftBorder>
104
- )}
105
103
  </box>
106
104
  ) as React.ReactElement
107
105
  }}
@@ -4,8 +4,8 @@ import { useKeyboard } from '@opentui/react'
4
4
  import { useFormContext, Controller } from 'react-hook-form'
5
5
  import { useFocusContext, useFormFieldDescendant } from './index'
6
6
  import { FormItemProps, FormItemRef } from './types'
7
- import { Theme } from 'termcast/src/theme'
8
- import { WithLeftBorder } from './with-left-border'
7
+ import { useTheme } from 'termcast/src/theme'
8
+ import { WithLeftBorder, TitleIndicator } from './with-left-border'
9
9
  import { DatePickerWidget } from 'termcast/src/internal/date-picker-widget'
10
10
  import { useIsInFocus } from 'termcast/src/internal/focus-context'
11
11
  import { useFormNavigationHelpers } from './use-form-navigation'
@@ -30,6 +30,7 @@ interface DatePickerComponentType {
30
30
  }
31
31
 
32
32
  const DatePickerComponent = (props: DatePickerProps): any => {
33
+ const theme = useTheme()
33
34
  const { control } = useFormContext()
34
35
  const focusContext = useFocusContext()
35
36
  const { focusedField, setFocusedField } = focusContext
@@ -44,21 +45,6 @@ const DatePickerComponent = (props: DatePickerProps): any => {
44
45
  elementRef: elementRef.current,
45
46
  })
46
47
 
47
- const { navigateToPrevious, navigateToNext } = useFormNavigationHelpers(props.id)
48
-
49
- // Handle tab navigation only
50
- useKeyboard((evt) => {
51
- if (!isFocused || !isInFocus) return
52
-
53
- if (evt.name === 'tab') {
54
- if (evt.shift) {
55
- navigateToPrevious()
56
- } else {
57
- navigateToNext()
58
- }
59
- }
60
- })
61
-
62
48
  return (
63
49
  <Controller
64
50
  name={props.id}
@@ -67,21 +53,21 @@ const DatePickerComponent = (props: DatePickerProps): any => {
67
53
  render={({ field, fieldState, formState }) => {
68
54
  return (
69
55
  <box ref={elementRef} flexDirection='column'>
70
- <WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
71
- <box
72
- onMouseDown={() => {
73
- setFocusedField(props.id)
74
- }}
75
- >
76
- <LoadingText
77
- isLoading={isFocused && focusContext.isLoading}
78
- color={isFocused ? Theme.primary : Theme.text}
56
+ <WithLeftBorder isFocused={isFocused} paddingBottom={1}>
57
+ <TitleIndicator isFocused={isFocused} isLoading={focusContext.isLoading}>
58
+ <box
59
+ onMouseDown={() => {
60
+ setFocusedField(props.id)
61
+ }}
79
62
  >
80
- {props.title || ''}
81
- </LoadingText>
82
- </box>
83
- </WithLeftBorder>
84
- <WithLeftBorder isFocused={isFocused}>
63
+ <LoadingText
64
+ isLoading={isFocused && focusContext.isLoading}
65
+ color={isFocused ? theme.primary : theme.text}
66
+ >
67
+ {props.title || ''}
68
+ </LoadingText>
69
+ </box>
70
+ </TitleIndicator>
85
71
  <DatePickerWidget
86
72
  enableColors={isFocused}
87
73
  initialValue={field.value || undefined}
@@ -93,26 +79,20 @@ const DatePickerComponent = (props: DatePickerProps): any => {
93
79
  }}
94
80
  focused={isFocused}
95
81
  />
96
- </WithLeftBorder>
97
- {field.value && (
98
- <WithLeftBorder isFocused={isFocused}>
99
- <text fg={Theme.accent}>
82
+ {field.value && (
83
+ <text fg={theme.accent}>
100
84
  Selected: {field.value.toISOString().split('T')[0]}
101
85
  </text>
102
- </WithLeftBorder>
103
- )}
104
- {(fieldState.error || props.error) && (
105
- <WithLeftBorder isFocused={isFocused}>
106
- <text fg={Theme.error}>
86
+ )}
87
+ {(fieldState.error || props.error) && (
88
+ <text fg={theme.error}>
107
89
  {fieldState.error?.message || props.error}
108
90
  </text>
109
- </WithLeftBorder>
110
- )}
111
- {props.info && (
112
- <WithLeftBorder isFocused={isFocused}>
113
- <text fg={Theme.textMuted}>{props.info}</text>
114
- </WithLeftBorder>
115
- )}
91
+ )}
92
+ {props.info && (
93
+ <text fg={theme.textMuted}>{props.info}</text>
94
+ )}
95
+ </WithLeftBorder>
116
96
  </box>
117
97
  ) as React.ReactElement
118
98
  }}
@@ -1,7 +1,7 @@
1
1
  import React, { useRef, useId } from 'react'
2
2
  import { BoxRenderable, TextAttributes } from '@opentui/core'
3
- import { Theme } from 'termcast/src/theme'
4
- import { WithLeftBorder } from './with-left-border'
3
+ import { useTheme } from 'termcast/src/theme'
4
+ import { WithLeftBorder, TitleIndicator } from './with-left-border'
5
5
  import { useFocusContext, useFormFieldDescendant } from './index'
6
6
  import { useFormNavigation } from './use-form-navigation'
7
7
  import { LoadingText } from 'termcast/src/components/loading-text'
@@ -13,9 +13,10 @@ export interface DescriptionProps {
13
13
  isFormTitle?: boolean
14
14
  }
15
15
 
16
- export const FORM_MAX_WIDTH = 70
16
+ export const FORM_MAX_WIDTH = 120
17
17
 
18
18
  export const Description = (props: DescriptionProps): any => {
19
+ const theme = useTheme()
19
20
  const elementRef = useRef<BoxRenderable>(null)
20
21
  const autoId = useId()
21
22
  const id = props.id || autoId
@@ -41,22 +42,22 @@ export const Description = (props: DescriptionProps): any => {
41
42
  focusContext.setFocusedField(id)
42
43
  }}
43
44
  >
44
- {props.title && (
45
- <WithLeftBorder
46
- customCharacter={{ focused: '■', unfocused: '▪︎' }}
47
- isFocused={isFocused}
48
- isLoading={focusContext.isLoading}
49
- >
50
- <LoadingText
51
- isLoading={isFocused && focusContext.isLoading}
52
- color={isFocused ? Theme.primary : Theme.text}
45
+ <WithLeftBorder isFocused={isFocused} paddingBottom={1}>
46
+ {props.title && (
47
+ <TitleIndicator
48
+ isFocused={isFocused}
49
+ isLoading={focusContext.isLoading}
50
+ customCharacter={{ focused: '■', unfocused: '▪︎' }}
53
51
  >
54
- {props.title}
55
- </LoadingText>
56
- </WithLeftBorder>
57
- )}
58
- <WithLeftBorder isFocused={isFocused}>
59
- <text fg={Theme.textMuted}>{props.text}</text>
52
+ <LoadingText
53
+ isLoading={isFocused && focusContext.isLoading}
54
+ color={isFocused ? theme.primary : theme.text}
55
+ >
56
+ {props.title}
57
+ </LoadingText>
58
+ </TitleIndicator>
59
+ )}
60
+ <text fg={theme.textMuted}>{props.text}</text>
60
61
  </WithLeftBorder>
61
62
  </box>
62
63
  )