tuiuiu.js 1.0.4 → 1.0.6-next.4ff3fbc

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 (292) hide show
  1. package/dist/app/render-loop.d.ts +2 -0
  2. package/dist/app/render-loop.d.ts.map +1 -1
  3. package/dist/app/render-loop.js +14 -3
  4. package/dist/app/render-loop.js.map +1 -1
  5. package/dist/atoms/button.d.ts +1 -1
  6. package/dist/atoms/button.d.ts.map +1 -1
  7. package/dist/atoms/button.js +13 -11
  8. package/dist/atoms/button.js.map +1 -1
  9. package/dist/atoms/progress-bar.d.ts.map +1 -1
  10. package/dist/atoms/progress-bar.js +15 -12
  11. package/dist/atoms/progress-bar.js.map +1 -1
  12. package/dist/atoms/scrollbar.d.ts +30 -0
  13. package/dist/atoms/scrollbar.d.ts.map +1 -0
  14. package/dist/atoms/scrollbar.js +46 -0
  15. package/dist/atoms/scrollbar.js.map +1 -0
  16. package/dist/atoms/slider.js +6 -6
  17. package/dist/atoms/slider.js.map +1 -1
  18. package/dist/atoms/spinner.d.ts.map +1 -1
  19. package/dist/atoms/spinner.js +3 -2
  20. package/dist/atoms/spinner.js.map +1 -1
  21. package/dist/atoms/switch.d.ts.map +1 -1
  22. package/dist/atoms/switch.js +3 -2
  23. package/dist/atoms/switch.js.map +1 -1
  24. package/dist/atoms/text-input.d.ts.map +1 -1
  25. package/dist/atoms/text-input.js +6 -5
  26. package/dist/atoms/text-input.js.map +1 -1
  27. package/dist/atoms/timer.d.ts +1 -1
  28. package/dist/atoms/timer.js +4 -4
  29. package/dist/atoms/timer.js.map +1 -1
  30. package/dist/atoms/tooltip.d.ts +4 -4
  31. package/dist/atoms/tooltip.js +19 -19
  32. package/dist/atoms/tooltip.js.map +1 -1
  33. package/dist/core/delta-render.d.ts.map +1 -1
  34. package/dist/core/delta-render.js +17 -12
  35. package/dist/core/delta-render.js.map +1 -1
  36. package/dist/core/index.d.ts +2 -2
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/core/index.js +15 -3
  39. package/dist/core/index.js.map +1 -1
  40. package/dist/core/renderer.d.ts +4 -2
  41. package/dist/core/renderer.d.ts.map +1 -1
  42. package/dist/core/renderer.js +70 -43
  43. package/dist/core/renderer.js.map +1 -1
  44. package/dist/core/theme-loader.d.ts +89 -0
  45. package/dist/core/theme-loader.d.ts.map +1 -0
  46. package/dist/core/theme-loader.js +228 -0
  47. package/dist/core/theme-loader.js.map +1 -0
  48. package/dist/core/theme-types.d.ts +577 -0
  49. package/dist/core/theme-types.d.ts.map +1 -0
  50. package/dist/core/theme-types.js +13 -0
  51. package/dist/core/theme-types.js.map +1 -0
  52. package/dist/core/theme.d.ts +155 -236
  53. package/dist/core/theme.d.ts.map +1 -1
  54. package/dist/core/theme.js +418 -709
  55. package/dist/core/theme.js.map +1 -1
  56. package/dist/design-system/core/renderer.d.ts +14 -3
  57. package/dist/design-system/core/renderer.d.ts.map +1 -1
  58. package/dist/design-system/core/renderer.js +77 -38
  59. package/dist/design-system/core/renderer.js.map +1 -1
  60. package/dist/design-system/feedback/badge.d.ts +2 -8
  61. package/dist/design-system/feedback/badge.d.ts.map +1 -1
  62. package/dist/design-system/feedback/badge.js +32 -8
  63. package/dist/design-system/feedback/badge.js.map +1 -1
  64. package/dist/design-system/feedback/progress-bar.d.ts.map +1 -1
  65. package/dist/design-system/feedback/progress-bar.js +15 -12
  66. package/dist/design-system/feedback/progress-bar.js.map +1 -1
  67. package/dist/design-system/feedback/spinner.d.ts.map +1 -1
  68. package/dist/design-system/feedback/spinner.js +4 -3
  69. package/dist/design-system/feedback/spinner.js.map +1 -1
  70. package/dist/design-system/feedback/timer.js +3 -3
  71. package/dist/design-system/feedback/timer.js.map +1 -1
  72. package/dist/design-system/layout/app.js +6 -6
  73. package/dist/design-system/layout/app.js.map +1 -1
  74. package/dist/design-system/navigation/file-manager.js +15 -15
  75. package/dist/design-system/navigation/file-manager.js.map +1 -1
  76. package/dist/design-system/visual/big-text.js +6 -6
  77. package/dist/design-system/visual/big-text.js.map +1 -1
  78. package/dist/design-system/visual/digits.js +7 -7
  79. package/dist/design-system/visual/digits.js.map +1 -1
  80. package/dist/design-system/visual/splash-screen.d.ts.map +1 -1
  81. package/dist/design-system/visual/splash-screen.js +23 -17
  82. package/dist/design-system/visual/splash-screen.js.map +1 -1
  83. package/dist/design-system/visual/tooltip.js +16 -16
  84. package/dist/design-system/visual/tooltip.js.map +1 -1
  85. package/dist/hooks/index.d.ts +1 -1
  86. package/dist/hooks/index.d.ts.map +1 -1
  87. package/dist/hooks/index.js +1 -1
  88. package/dist/hooks/index.js.map +1 -1
  89. package/dist/hooks/types.d.ts +2 -0
  90. package/dist/hooks/types.d.ts.map +1 -1
  91. package/dist/hooks/use-app.d.ts +5 -0
  92. package/dist/hooks/use-app.d.ts.map +1 -1
  93. package/dist/hooks/use-app.js +10 -0
  94. package/dist/hooks/use-app.js.map +1 -1
  95. package/dist/index.d.ts +3 -3
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +11 -3
  98. package/dist/index.js.map +1 -1
  99. package/dist/mcp/docs-data.d.ts.map +1 -1
  100. package/dist/mcp/docs-data.js +625 -28
  101. package/dist/mcp/docs-data.js.map +1 -1
  102. package/dist/mcp/server.js +1 -1
  103. package/dist/molecules/autocomplete.js +10 -10
  104. package/dist/molecules/autocomplete.js.map +1 -1
  105. package/dist/molecules/calendar.d.ts.map +1 -1
  106. package/dist/molecules/calendar.js +13 -12
  107. package/dist/molecules/calendar.js.map +1 -1
  108. package/dist/molecules/code-block.js +21 -21
  109. package/dist/molecules/code-block.js.map +1 -1
  110. package/dist/molecules/collapsible.d.ts +9 -5
  111. package/dist/molecules/collapsible.d.ts.map +1 -1
  112. package/dist/molecules/collapsible.js +39 -7
  113. package/dist/molecules/collapsible.js.map +1 -1
  114. package/dist/molecules/data-viz/bar-chart.js +8 -8
  115. package/dist/molecules/data-viz/bar-chart.js.map +1 -1
  116. package/dist/molecules/data-viz/gauge.d.ts.map +1 -1
  117. package/dist/molecules/data-viz/gauge.js +17 -17
  118. package/dist/molecules/data-viz/gauge.js.map +1 -1
  119. package/dist/molecules/data-viz/heatmap.js +11 -11
  120. package/dist/molecules/data-viz/heatmap.js.map +1 -1
  121. package/dist/molecules/data-viz/legend.js +3 -3
  122. package/dist/molecules/data-viz/legend.js.map +1 -1
  123. package/dist/molecules/data-viz/line-chart.js +4 -4
  124. package/dist/molecules/data-viz/line-chart.js.map +1 -1
  125. package/dist/molecules/data-viz/sparkline.js +1 -1
  126. package/dist/molecules/data-viz/sparkline.js.map +1 -1
  127. package/dist/molecules/markdown.js +23 -23
  128. package/dist/molecules/markdown.js.map +1 -1
  129. package/dist/molecules/multi-select.js +9 -9
  130. package/dist/molecules/multi-select.js.map +1 -1
  131. package/dist/molecules/radio-group.js +8 -8
  132. package/dist/molecules/radio-group.js.map +1 -1
  133. package/dist/molecules/select.d.ts.map +1 -1
  134. package/dist/molecules/select.js +15 -13
  135. package/dist/molecules/select.js.map +1 -1
  136. package/dist/molecules/table.js +3 -3
  137. package/dist/molecules/table.js.map +1 -1
  138. package/dist/molecules/tabs.d.ts +6 -2
  139. package/dist/molecules/tabs.d.ts.map +1 -1
  140. package/dist/molecules/tabs.js +25 -13
  141. package/dist/molecules/tabs.js.map +1 -1
  142. package/dist/molecules/tree.js +5 -5
  143. package/dist/molecules/tree.js.map +1 -1
  144. package/dist/organisms/command-palette.d.ts.map +1 -1
  145. package/dist/organisms/command-palette.js +19 -17
  146. package/dist/organisms/command-palette.js.map +1 -1
  147. package/dist/organisms/data-table.js +5 -5
  148. package/dist/organisms/data-table.js.map +1 -1
  149. package/dist/organisms/file-browser.js +15 -15
  150. package/dist/organisms/file-browser.js.map +1 -1
  151. package/dist/organisms/index.d.ts +2 -1
  152. package/dist/organisms/index.d.ts.map +1 -1
  153. package/dist/organisms/index.js +13 -1
  154. package/dist/organisms/index.js.map +1 -1
  155. package/dist/organisms/modal.d.ts +46 -0
  156. package/dist/organisms/modal.d.ts.map +1 -1
  157. package/dist/organisms/modal.js +110 -29
  158. package/dist/organisms/modal.js.map +1 -1
  159. package/dist/organisms/scroll-area.js +6 -6
  160. package/dist/organisms/scroll-area.js.map +1 -1
  161. package/dist/organisms/scroll-list.d.ts +168 -0
  162. package/dist/organisms/scroll-list.d.ts.map +1 -0
  163. package/dist/organisms/scroll-list.js +338 -0
  164. package/dist/organisms/scroll-list.js.map +1 -0
  165. package/dist/organisms/split-panel.d.ts.map +1 -1
  166. package/dist/organisms/split-panel.js +7 -4
  167. package/dist/organisms/split-panel.js.map +1 -1
  168. package/dist/primitives/divider.d.ts.map +1 -1
  169. package/dist/primitives/divider.js +4 -3
  170. package/dist/primitives/divider.js.map +1 -1
  171. package/dist/primitives/index.d.ts +1 -0
  172. package/dist/primitives/index.d.ts.map +1 -1
  173. package/dist/primitives/index.js +1 -0
  174. package/dist/primitives/index.js.map +1 -1
  175. package/dist/primitives/nodes.d.ts.map +1 -1
  176. package/dist/primitives/nodes.js +5 -3
  177. package/dist/primitives/nodes.js.map +1 -1
  178. package/dist/primitives/scroll.d.ts +127 -0
  179. package/dist/primitives/scroll.d.ts.map +1 -0
  180. package/dist/primitives/scroll.js +202 -0
  181. package/dist/primitives/scroll.js.map +1 -0
  182. package/dist/storybook/app.d.ts.map +1 -1
  183. package/dist/storybook/app.js +218 -84
  184. package/dist/storybook/app.js.map +1 -1
  185. package/dist/storybook/components/compare-view.d.ts.map +1 -1
  186. package/dist/storybook/components/compare-view.js +25 -19
  187. package/dist/storybook/components/compare-view.js.map +1 -1
  188. package/dist/storybook/components/hotkeys-panel.d.ts +12 -0
  189. package/dist/storybook/components/hotkeys-panel.d.ts.map +1 -0
  190. package/dist/storybook/components/hotkeys-panel.js +44 -0
  191. package/dist/storybook/components/hotkeys-panel.js.map +1 -0
  192. package/dist/storybook/components/index.d.ts +6 -0
  193. package/dist/storybook/components/index.d.ts.map +1 -1
  194. package/dist/storybook/components/index.js +9 -0
  195. package/dist/storybook/components/index.js.map +1 -1
  196. package/dist/storybook/components/log-viewer.d.ts +9 -4
  197. package/dist/storybook/components/log-viewer.d.ts.map +1 -1
  198. package/dist/storybook/components/log-viewer.js +97 -35
  199. package/dist/storybook/components/log-viewer.js.map +1 -1
  200. package/dist/storybook/components/navbar.d.ts +17 -0
  201. package/dist/storybook/components/navbar.d.ts.map +1 -0
  202. package/dist/storybook/components/navbar.js +52 -0
  203. package/dist/storybook/components/navbar.js.map +1 -0
  204. package/dist/storybook/components/pressed-keys.d.ts +29 -0
  205. package/dist/storybook/components/pressed-keys.d.ts.map +1 -0
  206. package/dist/storybook/components/pressed-keys.js +148 -0
  207. package/dist/storybook/components/pressed-keys.js.map +1 -0
  208. package/dist/storybook/components/preview.d.ts.map +1 -1
  209. package/dist/storybook/components/preview.js +44 -34
  210. package/dist/storybook/components/preview.js.map +1 -1
  211. package/dist/storybook/components/search-bar.d.ts +29 -0
  212. package/dist/storybook/components/search-bar.d.ts.map +1 -0
  213. package/dist/storybook/components/search-bar.js +101 -0
  214. package/dist/storybook/components/search-bar.js.map +1 -0
  215. package/dist/storybook/components/sidebar.d.ts.map +1 -1
  216. package/dist/storybook/components/sidebar.js +27 -21
  217. package/dist/storybook/components/sidebar.js.map +1 -1
  218. package/dist/storybook/components/status-bar.d.ts +22 -0
  219. package/dist/storybook/components/status-bar.d.ts.map +1 -0
  220. package/dist/storybook/components/status-bar.js +64 -0
  221. package/dist/storybook/components/status-bar.js.map +1 -0
  222. package/dist/storybook/store.d.ts +13 -1
  223. package/dist/storybook/store.d.ts.map +1 -1
  224. package/dist/storybook/store.js +21 -1
  225. package/dist/storybook/store.js.map +1 -1
  226. package/dist/storybook/stories/apps/index.d.ts.map +1 -1
  227. package/dist/storybook/stories/apps/index.js +82 -147
  228. package/dist/storybook/stories/apps/index.js.map +1 -1
  229. package/dist/storybook/stories/atoms/index.d.ts.map +1 -1
  230. package/dist/storybook/stories/atoms/index.js +57 -52
  231. package/dist/storybook/stories/atoms/index.js.map +1 -1
  232. package/dist/storybook/stories/molecules/index.d.ts +16 -0
  233. package/dist/storybook/stories/molecules/index.d.ts.map +1 -1
  234. package/dist/storybook/stories/molecules/index.js +299 -91
  235. package/dist/storybook/stories/molecules/index.js.map +1 -1
  236. package/dist/storybook/stories/organisms/index.d.ts.map +1 -1
  237. package/dist/storybook/stories/organisms/index.js +207 -120
  238. package/dist/storybook/stories/organisms/index.js.map +1 -1
  239. package/dist/templates/app.d.ts +28 -23
  240. package/dist/templates/app.d.ts.map +1 -1
  241. package/dist/templates/app.js +104 -17
  242. package/dist/templates/app.js.map +1 -1
  243. package/dist/themes/catppuccin.theme.d.ts +8 -0
  244. package/dist/themes/catppuccin.theme.d.ts.map +1 -0
  245. package/dist/themes/catppuccin.theme.js +538 -0
  246. package/dist/themes/catppuccin.theme.js.map +1 -0
  247. package/dist/themes/dark.theme.d.ts +8 -0
  248. package/dist/themes/dark.theme.d.ts.map +1 -0
  249. package/dist/themes/dark.theme.js +502 -0
  250. package/dist/themes/dark.theme.js.map +1 -0
  251. package/dist/themes/dracula.theme.d.ts +8 -0
  252. package/dist/themes/dracula.theme.d.ts.map +1 -0
  253. package/dist/themes/dracula.theme.js +518 -0
  254. package/dist/themes/dracula.theme.js.map +1 -0
  255. package/dist/themes/gruvbox.theme.d.ts +8 -0
  256. package/dist/themes/gruvbox.theme.d.ts.map +1 -0
  257. package/dist/themes/gruvbox.theme.js +529 -0
  258. package/dist/themes/gruvbox.theme.js.map +1 -0
  259. package/dist/themes/high-contrast-dark.theme.d.ts +8 -0
  260. package/dist/themes/high-contrast-dark.theme.d.ts.map +1 -0
  261. package/dist/themes/high-contrast-dark.theme.js +503 -0
  262. package/dist/themes/high-contrast-dark.theme.js.map +1 -0
  263. package/dist/themes/index.d.ts +58 -0
  264. package/dist/themes/index.d.ts.map +1 -0
  265. package/dist/themes/index.js +75 -0
  266. package/dist/themes/index.js.map +1 -0
  267. package/dist/themes/light.theme.d.ts +8 -0
  268. package/dist/themes/light.theme.d.ts.map +1 -0
  269. package/dist/themes/light.theme.js +502 -0
  270. package/dist/themes/light.theme.js.map +1 -0
  271. package/dist/themes/monochrome.theme.d.ts +8 -0
  272. package/dist/themes/monochrome.theme.d.ts.map +1 -0
  273. package/dist/themes/monochrome.theme.js +503 -0
  274. package/dist/themes/monochrome.theme.js.map +1 -0
  275. package/dist/themes/monokai.theme.d.ts +8 -0
  276. package/dist/themes/monokai.theme.d.ts.map +1 -0
  277. package/dist/themes/monokai.theme.js +518 -0
  278. package/dist/themes/monokai.theme.js.map +1 -0
  279. package/dist/themes/nord.theme.d.ts +8 -0
  280. package/dist/themes/nord.theme.d.ts.map +1 -0
  281. package/dist/themes/nord.theme.js +527 -0
  282. package/dist/themes/nord.theme.js.map +1 -0
  283. package/dist/themes/solarized-dark.theme.d.ts +8 -0
  284. package/dist/themes/solarized-dark.theme.d.ts.map +1 -0
  285. package/dist/themes/solarized-dark.theme.js +525 -0
  286. package/dist/themes/solarized-dark.theme.js.map +1 -0
  287. package/dist/themes/tokyo-night.theme.d.ts +8 -0
  288. package/dist/themes/tokyo-night.theme.d.ts.map +1 -0
  289. package/dist/themes/tokyo-night.theme.js +529 -0
  290. package/dist/themes/tokyo-night.theme.js.map +1 -0
  291. package/dist/version.js +1 -1
  292. package/package.json +1 -1
@@ -1,470 +1,31 @@
1
1
  /**
2
2
  * Theme System - Context-based theming for Tuiuiu
3
3
  *
4
- * Provides dark/light themes with customizable colors, spacing, and styles.
5
- * Uses signals for reactive theme switching.
6
- *
7
- * Color system includes:
8
- * - Complete color palette (22 colors × 11 shades)
9
- * - Semantic colors with foreground variants
10
- * - Auto-theming support for components
4
+ * Clean, unified theme API with:
5
+ * - Direct theme structure access (no conversion layer)
6
+ * - Signal-based reactive theme switching
7
+ * - Component tokens for consistent styling
8
+ * - Background/foreground hierarchy for visual depth
11
9
  */
12
10
  import { createSignal, untrack } from '../primitives/signal.js';
13
11
  import { colors } from './colors.js';
12
+ export { getShade } from './theme-types.js';
14
13
  // =============================================================================
15
- // Built-in Themes
14
+ // Theme Imports (Direct - No Conversion)
16
15
  // =============================================================================
17
- /**
18
- * Dark theme - Default theme with dark background
19
- */
20
- export const darkTheme = {
21
- name: 'dark',
22
- colors: {
23
- // Base
24
- background: colors.slate[950],
25
- foreground: colors.slate[50],
26
- // Components
27
- card: colors.slate[900],
28
- cardForeground: colors.slate[50],
29
- popover: colors.slate[900],
30
- popoverForeground: colors.slate[50],
31
- // Semantic
32
- primary: colors.blue[500],
33
- primaryForeground: '#ffffff',
34
- secondary: colors.slate[800],
35
- secondaryForeground: colors.slate[50],
36
- muted: colors.slate[800],
37
- mutedForeground: colors.slate[400],
38
- accent: colors.slate[800],
39
- accentForeground: colors.slate[50],
40
- destructive: colors.red[500],
41
- destructiveForeground: colors.slate[50],
42
- // UI
43
- border: colors.slate[700],
44
- input: colors.slate[700],
45
- ring: colors.blue[500],
46
- // Status
47
- success: colors.green[500],
48
- successForeground: '#ffffff',
49
- warning: colors.amber[500],
50
- warningForeground: colors.slate[900],
51
- error: colors.red[500],
52
- errorForeground: '#ffffff',
53
- info: colors.cyan[500],
54
- infoForeground: '#ffffff',
55
- },
56
- spacing: {
57
- xs: 1,
58
- sm: 2,
59
- md: 4,
60
- lg: 8,
61
- xl: 16,
62
- },
63
- borderRadius: 'md',
64
- };
65
- /**
66
- * Light theme - Light background variant
67
- */
68
- export const lightTheme = {
69
- name: 'light',
70
- colors: {
71
- // Base
72
- background: '#ffffff',
73
- foreground: colors.slate[950],
74
- // Components
75
- card: '#ffffff',
76
- cardForeground: colors.slate[950],
77
- popover: '#ffffff',
78
- popoverForeground: colors.slate[950],
79
- // Semantic
80
- primary: colors.blue[600],
81
- primaryForeground: '#ffffff',
82
- secondary: colors.slate[100],
83
- secondaryForeground: colors.slate[900],
84
- muted: colors.slate[100],
85
- mutedForeground: colors.slate[500],
86
- accent: colors.slate[100],
87
- accentForeground: colors.slate[900],
88
- destructive: colors.red[600],
89
- destructiveForeground: '#ffffff',
90
- // UI
91
- border: colors.slate[200],
92
- input: colors.slate[200],
93
- ring: colors.blue[600],
94
- // Status
95
- success: colors.green[600],
96
- successForeground: '#ffffff',
97
- warning: colors.amber[600],
98
- warningForeground: colors.slate[900],
99
- error: colors.red[600],
100
- errorForeground: '#ffffff',
101
- info: colors.cyan[600],
102
- infoForeground: '#ffffff',
103
- },
104
- spacing: {
105
- xs: 1,
106
- sm: 2,
107
- md: 4,
108
- lg: 8,
109
- xl: 16,
110
- },
111
- borderRadius: 'md',
112
- };
113
- /**
114
- * High contrast dark theme - For accessibility
115
- */
116
- export const highContrastDarkTheme = {
117
- name: 'high-contrast-dark',
118
- colors: {
119
- // Base
120
- background: '#000000',
121
- foreground: '#ffffff',
122
- // Components
123
- card: colors.neutral[950],
124
- cardForeground: '#ffffff',
125
- popover: colors.neutral[950],
126
- popoverForeground: '#ffffff',
127
- // Semantic
128
- primary: colors.blue[400],
129
- primaryForeground: '#000000',
130
- secondary: colors.neutral[800],
131
- secondaryForeground: '#ffffff',
132
- muted: colors.neutral[800],
133
- mutedForeground: colors.neutral[300],
134
- accent: colors.neutral[800],
135
- accentForeground: '#ffffff',
136
- destructive: colors.red[400],
137
- destructiveForeground: '#000000',
138
- // UI
139
- border: colors.neutral[500],
140
- input: colors.neutral[600],
141
- ring: colors.blue[400],
142
- // Status
143
- success: colors.green[400],
144
- successForeground: '#000000',
145
- warning: colors.amber[400],
146
- warningForeground: '#000000',
147
- error: colors.red[400],
148
- errorForeground: '#000000',
149
- info: colors.cyan[400],
150
- infoForeground: '#000000',
151
- },
152
- spacing: {
153
- xs: 1,
154
- sm: 2,
155
- md: 4,
156
- lg: 8,
157
- xl: 16,
158
- },
159
- borderRadius: 'sm',
160
- };
161
- /**
162
- * Monochrome theme - Grayscale only
163
- */
164
- export const monochromeTheme = {
165
- name: 'monochrome',
166
- colors: {
167
- // Base
168
- background: colors.neutral[900],
169
- foreground: colors.neutral[50],
170
- // Components
171
- card: colors.neutral[800],
172
- cardForeground: colors.neutral[50],
173
- popover: colors.neutral[800],
174
- popoverForeground: colors.neutral[50],
175
- // Semantic
176
- primary: colors.neutral[300],
177
- primaryForeground: colors.neutral[900],
178
- secondary: colors.neutral[700],
179
- secondaryForeground: colors.neutral[50],
180
- muted: colors.neutral[800],
181
- mutedForeground: colors.neutral[400],
182
- accent: colors.neutral[700],
183
- accentForeground: colors.neutral[50],
184
- destructive: colors.neutral[200],
185
- destructiveForeground: colors.neutral[900],
186
- // UI
187
- border: colors.neutral[600],
188
- input: colors.neutral[700],
189
- ring: colors.neutral[400],
190
- // Status
191
- success: colors.neutral[300],
192
- successForeground: colors.neutral[900],
193
- warning: colors.neutral[400],
194
- warningForeground: colors.neutral[900],
195
- error: colors.neutral[200],
196
- errorForeground: colors.neutral[900],
197
- info: colors.neutral[300],
198
- infoForeground: colors.neutral[900],
199
- },
200
- spacing: {
201
- xs: 1,
202
- sm: 2,
203
- md: 4,
204
- lg: 8,
205
- xl: 16,
206
- },
207
- borderRadius: 'none',
208
- };
209
- /**
210
- * Monokai theme - Classic code editor theme
211
- */
212
- export const monokaiTheme = {
213
- name: 'monokai',
214
- colors: {
215
- background: '#272822',
216
- foreground: '#F8F8F2',
217
- card: '#3E3D32',
218
- cardForeground: '#F8F8F2',
219
- popover: '#3E3D32',
220
- popoverForeground: '#F8F8F2',
221
- primary: '#A6E22E',
222
- primaryForeground: '#272822',
223
- secondary: '#49483E',
224
- secondaryForeground: '#F8F8F2',
225
- muted: '#49483E',
226
- mutedForeground: '#75715E',
227
- accent: '#66D9EF',
228
- accentForeground: '#272822',
229
- destructive: '#F92672',
230
- destructiveForeground: '#F8F8F2',
231
- border: '#49483E',
232
- input: '#3E3D32',
233
- ring: '#A6E22E',
234
- success: '#A6E22E',
235
- successForeground: '#272822',
236
- warning: '#E6DB74',
237
- warningForeground: '#272822',
238
- error: '#F92672',
239
- errorForeground: '#F8F8F2',
240
- info: '#66D9EF',
241
- infoForeground: '#272822',
242
- },
243
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
244
- borderRadius: 'md',
245
- };
246
- /**
247
- * Dracula theme - Popular dark theme
248
- */
249
- export const draculaTheme = {
250
- name: 'dracula',
251
- colors: {
252
- background: '#282A36',
253
- foreground: '#F8F8F2',
254
- card: '#44475A',
255
- cardForeground: '#F8F8F2',
256
- popover: '#44475A',
257
- popoverForeground: '#F8F8F2',
258
- primary: '#BD93F9',
259
- primaryForeground: '#282A36',
260
- secondary: '#44475A',
261
- secondaryForeground: '#F8F8F2',
262
- muted: '#44475A',
263
- mutedForeground: '#6272A4',
264
- accent: '#8BE9FD',
265
- accentForeground: '#282A36',
266
- destructive: '#FF5555',
267
- destructiveForeground: '#F8F8F2',
268
- border: '#6272A4',
269
- input: '#44475A',
270
- ring: '#BD93F9',
271
- success: '#50FA7B',
272
- successForeground: '#282A36',
273
- warning: '#F1FA8C',
274
- warningForeground: '#282A36',
275
- error: '#FF5555',
276
- errorForeground: '#F8F8F2',
277
- info: '#8BE9FD',
278
- infoForeground: '#282A36',
279
- },
280
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
281
- borderRadius: 'md',
282
- };
283
- /**
284
- * Nord theme - Arctic, bluish theme
285
- */
286
- export const nordTheme = {
287
- name: 'nord',
288
- colors: {
289
- background: '#2E3440',
290
- foreground: '#ECEFF4',
291
- card: '#3B4252',
292
- cardForeground: '#ECEFF4',
293
- popover: '#3B4252',
294
- popoverForeground: '#ECEFF4',
295
- primary: '#88C0D0',
296
- primaryForeground: '#2E3440',
297
- secondary: '#434C5E',
298
- secondaryForeground: '#ECEFF4',
299
- muted: '#4C566A',
300
- mutedForeground: '#D8DEE9',
301
- accent: '#81A1C1',
302
- accentForeground: '#2E3440',
303
- destructive: '#BF616A',
304
- destructiveForeground: '#ECEFF4',
305
- border: '#4C566A',
306
- input: '#3B4252',
307
- ring: '#88C0D0',
308
- success: '#A3BE8C',
309
- successForeground: '#2E3440',
310
- warning: '#EBCB8B',
311
- warningForeground: '#2E3440',
312
- error: '#BF616A',
313
- errorForeground: '#ECEFF4',
314
- info: '#81A1C1',
315
- infoForeground: '#2E3440',
316
- },
317
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
318
- borderRadius: 'md',
319
- };
320
- /**
321
- * Solarized Dark theme
322
- */
323
- export const solarizedDarkTheme = {
324
- name: 'solarized-dark',
325
- colors: {
326
- background: '#002B36',
327
- foreground: '#839496',
328
- card: '#073642',
329
- cardForeground: '#839496',
330
- popover: '#073642',
331
- popoverForeground: '#839496',
332
- primary: '#268BD2',
333
- primaryForeground: '#FDF6E3',
334
- secondary: '#073642',
335
- secondaryForeground: '#839496',
336
- muted: '#073642',
337
- mutedForeground: '#657B83',
338
- accent: '#2AA198',
339
- accentForeground: '#FDF6E3',
340
- destructive: '#DC322F',
341
- destructiveForeground: '#FDF6E3',
342
- border: '#586E75',
343
- input: '#073642',
344
- ring: '#268BD2',
345
- success: '#859900',
346
- successForeground: '#FDF6E3',
347
- warning: '#B58900',
348
- warningForeground: '#FDF6E3',
349
- error: '#DC322F',
350
- errorForeground: '#FDF6E3',
351
- info: '#2AA198',
352
- infoForeground: '#FDF6E3',
353
- },
354
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
355
- borderRadius: 'md',
356
- };
357
- /**
358
- * Gruvbox Dark theme - Warm retro theme
359
- */
360
- export const gruvboxTheme = {
361
- name: 'gruvbox',
362
- colors: {
363
- background: '#282828',
364
- foreground: '#EBDBB2',
365
- card: '#3C3836',
366
- cardForeground: '#EBDBB2',
367
- popover: '#3C3836',
368
- popoverForeground: '#EBDBB2',
369
- primary: '#B8BB26',
370
- primaryForeground: '#282828',
371
- secondary: '#504945',
372
- secondaryForeground: '#EBDBB2',
373
- muted: '#504945',
374
- mutedForeground: '#928374',
375
- accent: '#83A598',
376
- accentForeground: '#282828',
377
- destructive: '#FB4934',
378
- destructiveForeground: '#EBDBB2',
379
- border: '#665C54',
380
- input: '#3C3836',
381
- ring: '#B8BB26',
382
- success: '#B8BB26',
383
- successForeground: '#282828',
384
- warning: '#FABD2F',
385
- warningForeground: '#282828',
386
- error: '#FB4934',
387
- errorForeground: '#EBDBB2',
388
- info: '#83A598',
389
- infoForeground: '#282828',
390
- },
391
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
392
- borderRadius: 'md',
393
- };
394
- /**
395
- * Tokyo Night theme - Modern VSCode theme
396
- */
397
- export const tokyoNightTheme = {
398
- name: 'tokyo-night',
399
- colors: {
400
- background: '#1A1B26',
401
- foreground: '#A9B1D6',
402
- card: '#24283B',
403
- cardForeground: '#A9B1D6',
404
- popover: '#24283B',
405
- popoverForeground: '#A9B1D6',
406
- primary: '#7AA2F7',
407
- primaryForeground: '#1A1B26',
408
- secondary: '#414868',
409
- secondaryForeground: '#A9B1D6',
410
- muted: '#414868',
411
- mutedForeground: '#565F89',
412
- accent: '#BB9AF7',
413
- accentForeground: '#1A1B26',
414
- destructive: '#F7768E',
415
- destructiveForeground: '#A9B1D6',
416
- border: '#414868',
417
- input: '#24283B',
418
- ring: '#7AA2F7',
419
- success: '#9ECE6A',
420
- successForeground: '#1A1B26',
421
- warning: '#E0AF68',
422
- warningForeground: '#1A1B26',
423
- error: '#F7768E',
424
- errorForeground: '#A9B1D6',
425
- info: '#7DCFFF',
426
- infoForeground: '#1A1B26',
427
- },
428
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
429
- borderRadius: 'md',
430
- };
431
- /**
432
- * Catppuccin Mocha theme - Soothing pastel theme
433
- */
434
- export const catppuccinTheme = {
435
- name: 'catppuccin',
436
- colors: {
437
- background: '#1E1E2E',
438
- foreground: '#CDD6F4',
439
- card: '#313244',
440
- cardForeground: '#CDD6F4',
441
- popover: '#313244',
442
- popoverForeground: '#CDD6F4',
443
- primary: '#CBA6F7',
444
- primaryForeground: '#1E1E2E',
445
- secondary: '#45475A',
446
- secondaryForeground: '#CDD6F4',
447
- muted: '#45475A',
448
- mutedForeground: '#6C7086',
449
- accent: '#89DCEB',
450
- accentForeground: '#1E1E2E',
451
- destructive: '#F38BA8',
452
- destructiveForeground: '#CDD6F4',
453
- border: '#6C7086',
454
- input: '#313244',
455
- ring: '#CBA6F7',
456
- success: '#A6E3A1',
457
- successForeground: '#1E1E2E',
458
- warning: '#F9E2AF',
459
- warningForeground: '#1E1E2E',
460
- error: '#F38BA8',
461
- errorForeground: '#CDD6F4',
462
- info: '#89DCEB',
463
- infoForeground: '#1E1E2E',
464
- },
465
- spacing: { xs: 1, sm: 2, md: 4, lg: 8, xl: 16 },
466
- borderRadius: 'lg',
467
- };
16
+ import { darkTheme } from '../themes/dark.theme.js';
17
+ import { lightTheme } from '../themes/light.theme.js';
18
+ import { monokaiTheme } from '../themes/monokai.theme.js';
19
+ import { draculaTheme } from '../themes/dracula.theme.js';
20
+ import { nordTheme } from '../themes/nord.theme.js';
21
+ import { solarizedDarkTheme } from '../themes/solarized-dark.theme.js';
22
+ import { gruvboxTheme } from '../themes/gruvbox.theme.js';
23
+ import { tokyoNightTheme } from '../themes/tokyo-night.theme.js';
24
+ import { catppuccinTheme } from '../themes/catppuccin.theme.js';
25
+ import { highContrastDarkTheme } from '../themes/high-contrast-dark.theme.js';
26
+ import { monochromeTheme } from '../themes/monochrome.theme.js';
27
+ // Re-export themes
28
+ export { darkTheme, lightTheme, monokaiTheme, draculaTheme, nordTheme, solarizedDarkTheme, gruvboxTheme, tokyoNightTheme, catppuccinTheme, highContrastDarkTheme, monochromeTheme, };
468
29
  // =============================================================================
469
30
  // Theme Context (Signal-based)
470
31
  // =============================================================================
@@ -473,32 +34,62 @@ const [currentTheme, setCurrentTheme] = createSignal(darkTheme);
473
34
  // Theme stack for nested themes
474
35
  const themeStack = [];
475
36
  /**
476
- * Get the current theme
37
+ * Get the current theme (reactive).
38
+ *
39
+ * Use destructuring to access specific parts:
40
+ * ```typescript
41
+ * const { palette, background, foreground, components } = useTheme()
42
+ * const buttonBg = components.button.primary.bg
43
+ * ```
477
44
  */
478
45
  export function useTheme() {
479
46
  return currentTheme();
480
47
  }
481
48
  /**
482
- * Get the current theme without tracking (for use outside effects)
49
+ * Get the current theme without tracking (for use outside effects).
483
50
  */
484
51
  export function getTheme() {
485
52
  return untrack(currentTheme);
486
53
  }
487
54
  /**
488
- * Set the global theme
55
+ * Get the current theme mode ('dark' or 'light').
56
+ */
57
+ export function useThemeMode() {
58
+ return currentTheme().mode;
59
+ }
60
+ /**
61
+ * Check if current theme is dark mode.
62
+ */
63
+ export function useIsDark() {
64
+ return currentTheme().mode === 'dark';
65
+ }
66
+ /**
67
+ * Get component tokens for a specific component.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const buttonTokens = useComponentTokens('button')
72
+ * const { primary, secondary, outline, ghost } = buttonTokens
73
+ * ```
74
+ */
75
+ export function useComponentTokens(component) {
76
+ return currentTheme().components[component];
77
+ }
78
+ /**
79
+ * Set the global theme.
489
80
  */
490
81
  export function setTheme(theme) {
491
82
  setCurrentTheme(theme);
492
83
  }
493
84
  /**
494
- * Push a theme onto the stack (for nested themes)
85
+ * Push a theme onto the stack (for nested themes).
495
86
  */
496
87
  export function pushTheme(theme) {
497
88
  themeStack.push(untrack(currentTheme));
498
89
  setCurrentTheme(theme);
499
90
  }
500
91
  /**
501
- * Pop a theme from the stack
92
+ * Pop a theme from the stack.
502
93
  */
503
94
  export function popTheme() {
504
95
  const previous = themeStack.pop();
@@ -507,66 +98,115 @@ export function popTheme() {
507
98
  }
508
99
  }
509
100
  // =============================================================================
510
- // Theme Utilities
101
+ // Theme Collection
511
102
  // =============================================================================
512
103
  /**
513
- * Create a custom theme by extending an existing theme
104
+ * All built-in themes.
514
105
  */
515
- export function createTheme(base, overrides) {
516
- return {
517
- ...base,
518
- ...overrides,
519
- colors: {
520
- ...base.colors,
521
- ...overrides.colors,
522
- },
523
- spacing: {
524
- ...base.spacing,
525
- ...overrides.spacing,
526
- },
527
- };
106
+ export const themes = {
107
+ dark: darkTheme,
108
+ light: lightTheme,
109
+ monokai: monokaiTheme,
110
+ dracula: draculaTheme,
111
+ nord: nordTheme,
112
+ solarizedDark: solarizedDarkTheme,
113
+ gruvbox: gruvboxTheme,
114
+ tokyoNight: tokyoNightTheme,
115
+ catppuccin: catppuccinTheme,
116
+ highContrastDark: highContrastDarkTheme,
117
+ monochrome: monochromeTheme,
118
+ };
119
+ /** All theme names for cycling */
120
+ export const themeNames = Object.keys(themes);
121
+ /**
122
+ * Get a theme by name.
123
+ */
124
+ export function getThemeByName(name) {
125
+ return themes[name];
528
126
  }
529
127
  /**
530
- * Get a color from the current theme
128
+ * Get next theme in cycle.
531
129
  */
532
- export function themeColor(key) {
533
- return currentTheme().colors[key];
130
+ export function getNextTheme(current) {
131
+ const currentIndex = themeNames.findIndex(name => themes[name].name === current.name);
132
+ const nextIndex = (currentIndex + 1) % themeNames.length;
133
+ return themes[themeNames[nextIndex]];
534
134
  }
535
135
  /**
536
- * Get a color pair (background + foreground) for semantic colors.
537
- * Use this for buttons, badges, navbars, and any filled components.
538
- *
539
- * @example
540
- * const { bg, fg } = themeColorPair('primary');
541
- * Box({ backgroundColor: bg },
542
- * Text({ color: fg }, 'Button')
543
- * )
136
+ * Get previous theme in cycle.
137
+ */
138
+ export function getPreviousTheme(current) {
139
+ const currentIndex = themeNames.findIndex(name => themes[name].name === current.name);
140
+ const prevIndex = (currentIndex - 1 + themeNames.length) % themeNames.length;
141
+ return themes[themeNames[prevIndex]];
142
+ }
143
+ // =============================================================================
144
+ // Theme Utilities
145
+ // =============================================================================
146
+ /**
147
+ * Create a custom theme by extending an existing theme.
544
148
  *
545
- * @example
546
- * // Destructuring in component
547
- * const colors = themeColorPair('success');
548
- * Badge({ backgroundColor: colors.bg, color: colors.fg }, 'OK')
149
+ * For deep customization, use `mergeThemes` from theme-loader.
549
150
  */
550
- export function themeColorPair(key) {
551
- const theme = currentTheme();
552
- const fgKey = `${key}Foreground`;
151
+ export function createTheme(base, overrides) {
553
152
  return {
554
- bg: theme.colors[key],
555
- fg: theme.colors[fgKey],
153
+ ...base,
154
+ ...overrides,
155
+ palette: overrides.palette ?? base.palette,
156
+ background: { ...base.background, ...overrides.background },
157
+ foreground: {
158
+ ...base.foreground,
159
+ ...overrides.foreground,
160
+ inverse: {
161
+ ...base.foreground.inverse,
162
+ ...(overrides.foreground?.inverse ?? {}),
163
+ },
164
+ },
165
+ accents: { ...base.accents, ...overrides.accents },
166
+ states: {
167
+ ...base.states,
168
+ ...overrides.states,
169
+ hover: { ...base.states.hover, ...(overrides.states?.hover ?? {}) },
170
+ active: { ...base.states.active, ...(overrides.states?.active ?? {}) },
171
+ focus: {
172
+ ...base.states.focus,
173
+ ...(overrides.states?.focus ?? {}),
174
+ ring: {
175
+ ...base.states.focus.ring,
176
+ ...(overrides.states?.focus?.ring ?? {}),
177
+ },
178
+ },
179
+ disabled: { ...base.states.disabled, ...(overrides.states?.disabled ?? {}) },
180
+ selected: { ...base.states.selected, ...(overrides.states?.selected ?? {}) },
181
+ },
182
+ borders: { ...base.borders, ...overrides.borders },
183
+ opacity: { ...base.opacity, ...overrides.opacity },
184
+ components: overrides.components ?? base.components,
556
185
  };
557
186
  }
558
187
  /**
559
- * Get a spacing value from the current theme
188
+ * Map border radius to actual characters.
560
189
  */
561
- export function themeSpacing(key) {
562
- return currentTheme().spacing[key];
190
+ export function getBorderRadiusChars(radius) {
191
+ switch (radius) {
192
+ case 'none':
193
+ case 'sm':
194
+ return { tl: '┌', tr: '┐', bl: '└', br: '┘' };
195
+ case 'md':
196
+ case 'lg':
197
+ return { tl: '╭', tr: '╮', bl: '╰', br: '╯' };
198
+ default:
199
+ return { tl: '╭', tr: '╮', bl: '╰', br: '╯' };
200
+ }
563
201
  }
202
+ // =============================================================================
203
+ // Color Utilities
204
+ // =============================================================================
564
205
  /**
565
206
  * Calculate relative luminance of a color (WCAG formula).
566
207
  * Returns value between 0 (black) and 1 (white).
567
208
  */
568
209
  function getLuminance(hex) {
569
- // Normalize hex
570
210
  let color = hex.startsWith('#') ? hex.slice(1) : hex;
571
211
  if (color.length === 3) {
572
212
  color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2];
@@ -574,7 +214,6 @@ function getLuminance(hex) {
574
214
  const r = parseInt(color.slice(0, 2), 16) / 255;
575
215
  const g = parseInt(color.slice(2, 4), 16) / 255;
576
216
  const b = parseInt(color.slice(4, 6), 16) / 255;
577
- // sRGB to linear
578
217
  const toLinear = (c) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
579
218
  return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
580
219
  }
@@ -583,20 +222,12 @@ function getLuminance(hex) {
583
222
  * Uses WCAG luminance formula for accessibility.
584
223
  *
585
224
  * @example
225
+ * ```typescript
586
226
  * getContrastColor('#3b82f6') // 'white' (blue is dark)
587
227
  * getContrastColor('#fbbf24') // 'black' (amber is light)
588
- * getContrastColor('cyan') // 'black' (cyan is light)
589
- *
590
- * @example
591
- * // With custom component
592
- * const bg = '#ff6b6b';
593
- * Box({ backgroundColor: bg },
594
- * Text({ color: getContrastColor(bg) }, 'Hello')
595
- * )
228
+ * ```
596
229
  */
597
230
  export function getContrastColor(color) {
598
- // Resolve color if it's a theme/tailwind reference
599
- const resolved = resolveColor(color);
600
231
  // Handle named colors (basic ANSI)
601
232
  const namedColorLuminance = {
602
233
  black: 0,
@@ -610,248 +241,326 @@ export function getContrastColor(color) {
610
241
  gray: 0.5,
611
242
  grey: 0.5,
612
243
  };
613
- if (resolved in namedColorLuminance) {
614
- return namedColorLuminance[resolved] > 0.5 ? 'black' : 'white';
244
+ if (color in namedColorLuminance) {
245
+ return namedColorLuminance[color] > 0.5 ? 'black' : 'white';
615
246
  }
616
247
  // For hex colors, calculate luminance
617
- if (resolved.startsWith('#')) {
618
- const luminance = getLuminance(resolved);
619
- // WCAG recommends 0.179 as threshold, we use 0.5 for terminal readability
248
+ if (color.startsWith('#')) {
249
+ const luminance = getLuminance(color);
620
250
  return luminance > 0.4 ? 'black' : 'white';
621
251
  }
622
252
  // Default to white for unknown colors (assume dark)
623
253
  return 'white';
624
254
  }
625
255
  /**
626
- * Map border radius to actual characters
256
+ * Get a Tailwind color by name and shade.
257
+ *
258
+ * @example
259
+ * ```typescript
260
+ * getColor('blue', 500) // '#3b82f6'
261
+ * getColor('gray', 100) // '#f3f4f6'
262
+ * ```
627
263
  */
628
- export function getBorderRadiusChars(radius) {
629
- switch (radius) {
630
- case 'none':
631
- case 'sm':
632
- return { tl: '┌', tr: '', bl: '└', br: '┘' };
633
- case 'md':
634
- case 'lg':
635
- return { tl: '╭', tr: '╮', bl: '╰', br: '╯' };
636
- default:
637
- return { tl: '╭', tr: '╮', bl: '╰', br: '╯' };
638
- }
264
+ export function getColor(name, shade = 500) {
265
+ const palette = colors[name];
266
+ if (!palette)
267
+ return name;
268
+ if (typeof palette === 'string')
269
+ return palette;
270
+ return palette[shade] ?? palette[500];
639
271
  }
640
- // =============================================================================
641
- // Color Resolution
642
- // =============================================================================
643
272
  /**
644
- * Resolve a color string to a hex value.
273
+ * Get a darker shade of a color.
645
274
  *
646
- * Supports multiple formats:
647
- * - Semantic names: 'primary', 'secondary', 'destructive', etc.
648
- * - Tailwind format: 'blue-500', 'gray-100', 'red-600'
649
- * - Color name only: 'blue', 'red' (uses shade 500)
650
- * - Hex values: '#3b82f6', '#fff'
651
- * - RGB values: 'rgb(59, 130, 246)'
275
+ * @param name Color name from colors.ts
276
+ * @param steps Number of shade steps to darken (1 = +100)
277
+ */
278
+ export function getDarker(name, steps = 1) {
279
+ const shade = Math.min(500 + steps * 100, 900);
280
+ return getColor(name, shade);
281
+ }
282
+ /**
283
+ * Get a lighter shade of a color.
652
284
  *
653
- * @example
654
- * resolveColor('primary') // Theme's primary color
655
- * resolveColor('blue-500') // '#3b82f6'
656
- * resolveColor('blue') // '#3b82f6' (shade 500)
657
- * resolveColor('#ff0000') // '#ff0000' (passed through)
285
+ * @param name Color name from colors.ts
286
+ * @param steps Number of shade steps to lighten (1 = -100)
658
287
  */
659
- export function resolveColor(colorInput) {
660
- const theme = untrack(currentTheme);
661
- // 1. Semantic color names from theme
662
- const semanticMap = {
663
- // Base
664
- 'background': theme.colors.background,
665
- 'foreground': theme.colors.foreground,
666
- // Components
667
- 'card': theme.colors.card,
668
- 'card-foreground': theme.colors.cardForeground,
669
- 'cardForeground': theme.colors.cardForeground,
670
- 'popover': theme.colors.popover,
671
- 'popover-foreground': theme.colors.popoverForeground,
672
- 'popoverForeground': theme.colors.popoverForeground,
673
- // Semantic
674
- 'primary': theme.colors.primary,
675
- 'primary-foreground': theme.colors.primaryForeground,
676
- 'primaryForeground': theme.colors.primaryForeground,
677
- 'secondary': theme.colors.secondary,
678
- 'secondary-foreground': theme.colors.secondaryForeground,
679
- 'secondaryForeground': theme.colors.secondaryForeground,
680
- 'muted': theme.colors.muted,
681
- 'muted-foreground': theme.colors.mutedForeground,
682
- 'mutedForeground': theme.colors.mutedForeground,
683
- 'accent': theme.colors.accent,
684
- 'accent-foreground': theme.colors.accentForeground,
685
- 'accentForeground': theme.colors.accentForeground,
686
- 'destructive': theme.colors.destructive,
687
- 'destructive-foreground': theme.colors.destructiveForeground,
688
- 'destructiveForeground': theme.colors.destructiveForeground,
689
- // UI
690
- 'border': theme.colors.border,
691
- 'input': theme.colors.input,
692
- 'ring': theme.colors.ring,
693
- // Status
694
- 'success': theme.colors.success,
695
- 'success-foreground': theme.colors.successForeground,
696
- 'successForeground': theme.colors.successForeground,
697
- 'warning': theme.colors.warning,
698
- 'warning-foreground': theme.colors.warningForeground,
699
- 'warningForeground': theme.colors.warningForeground,
700
- 'error': theme.colors.error,
701
- 'error-foreground': theme.colors.errorForeground,
702
- 'errorForeground': theme.colors.errorForeground,
703
- 'danger': theme.colors.error,
704
- 'danger-foreground': theme.colors.errorForeground,
705
- 'info': theme.colors.info,
706
- 'info-foreground': theme.colors.infoForeground,
707
- 'infoForeground': theme.colors.infoForeground,
708
- };
709
- if (colorInput in semanticMap) {
710
- return semanticMap[colorInput];
711
- }
712
- // 2. Tailwind format: "color-shade" (e.g., "blue-500", "gray-100")
713
- const tailwindMatch = colorInput.match(/^([a-z]+)-(\d+)$/);
714
- if (tailwindMatch) {
715
- const [, name, shadeStr] = tailwindMatch;
716
- const shade = parseInt(shadeStr, 10);
717
- const palette = colors[name];
718
- if (palette && typeof palette === 'object' && shade in palette) {
719
- return palette[shade];
720
- }
721
- }
722
- // 3. Color name only (uses shade 500)
723
- if (colorInput in colors) {
724
- const palette = colors[colorInput];
725
- if (typeof palette === 'object') {
726
- return palette[500];
727
- }
728
- // Base colors (white, black, transparent)
729
- if (typeof palette === 'string') {
730
- return palette;
731
- }
732
- }
733
- // 4. Return as-is (hex, rgb, named CSS color, etc.)
734
- return colorInput;
288
+ export function getLighter(name, steps = 1) {
289
+ const shade = Math.max(500 - steps * 100, 50);
290
+ return getColor(name, shade);
735
291
  }
736
292
  // =============================================================================
737
- // Theme Detection
293
+ // System Theme Detection
738
294
  // =============================================================================
739
295
  /**
740
- * Detect system color scheme preference
741
- * Note: In terminal context, this checks COLORFGBG or similar env vars
296
+ * Detect system color scheme preference.
297
+ * Checks COLORFGBG environment variable.
742
298
  */
743
299
  export function detectColorScheme() {
744
- // Check COLORFGBG environment variable (format: "fg;bg")
745
300
  const colorFgBg = process.env.COLORFGBG;
746
301
  if (colorFgBg) {
747
302
  const parts = colorFgBg.split(';');
748
303
  const bg = parseInt(parts[parts.length - 1] || '0', 10);
749
- // Background colors 0-6 and 8 are typically dark
750
- // Background colors 7 and 9-15 are typically light
751
304
  if (bg === 7 || (bg >= 9 && bg <= 15)) {
752
305
  return 'light';
753
306
  }
754
307
  return 'dark';
755
308
  }
756
- // Check for light terminal indicators
757
- const term = process.env.TERM || '';
758
309
  const termProgram = process.env.TERM_PROGRAM || '';
759
- // Some terminal programs default to light
760
310
  if (termProgram.toLowerCase().includes('apple_terminal')) {
761
311
  return 'light';
762
312
  }
763
- // Default to dark (most modern terminals)
764
313
  return 'dark';
765
314
  }
766
315
  /**
767
- * Auto-select theme based on system preference
316
+ * Auto-select theme based on system preference.
768
317
  */
769
318
  export function useSystemTheme() {
770
319
  const scheme = detectColorScheme();
771
320
  setTheme(scheme === 'light' ? lightTheme : darkTheme);
772
321
  }
773
322
  // =============================================================================
774
- // Exports
323
+ // Convenience Aliases
775
324
  // =============================================================================
776
- export const themes = {
777
- dark: darkTheme,
778
- light: lightTheme,
779
- highContrastDark: highContrastDarkTheme,
780
- monochrome: monochromeTheme,
781
- monokai: monokaiTheme,
782
- dracula: draculaTheme,
783
- nord: nordTheme,
784
- solarizedDark: solarizedDarkTheme,
785
- gruvbox: gruvboxTheme,
786
- tokyoNight: tokyoNightTheme,
787
- catppuccin: catppuccinTheme,
788
- };
789
- /** All theme names for cycling */
790
- export const themeNames = Object.keys(themes);
791
- /** Get next theme in cycle */
792
- export function getNextTheme(current) {
793
- const currentIndex = themeNames.findIndex(name => themes[name].name === current.name);
794
- const nextIndex = (currentIndex + 1) % themeNames.length;
795
- return themes[themeNames[nextIndex]];
325
+ /**
326
+ * Shorthand for common theme access patterns.
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * const { bg, fg, palette, components } = th()
331
+ * ```
332
+ */
333
+ export function th() {
334
+ return currentTheme();
335
+ }
336
+ /**
337
+ * Get background colors from current theme.
338
+ */
339
+ export function useBg() {
340
+ return currentTheme().background;
341
+ }
342
+ /**
343
+ * Get foreground colors from current theme.
344
+ */
345
+ export function useFg() {
346
+ return currentTheme().foreground;
347
+ }
348
+ /**
349
+ * Get palette from current theme.
350
+ */
351
+ export function usePalette() {
352
+ return currentTheme().palette;
353
+ }
354
+ /**
355
+ * Get accent colors from current theme.
356
+ */
357
+ export function useAccents() {
358
+ return currentTheme().accents;
359
+ }
360
+ /**
361
+ * Get state colors from current theme.
362
+ */
363
+ export function useStates() {
364
+ return currentTheme().states;
365
+ }
366
+ /**
367
+ * Get border colors from current theme.
368
+ */
369
+ export function useBorders() {
370
+ return currentTheme().borders;
371
+ }
372
+ /**
373
+ * Get opacity values from current theme.
374
+ */
375
+ export function useOpacity() {
376
+ return currentTheme().opacity;
796
377
  }
797
378
  // =============================================================================
798
- // Tailwind-style Helpers
379
+ // Color Resolution (for renderer)
799
380
  // =============================================================================
800
381
  /**
801
- * Tailwind-style color utilities.
382
+ * Semantic color name mappings.
383
+ * Maps semantic color names to theme values.
802
384
  *
803
- * @example
804
- * tw.color('blue', 500) // '#3b82f6'
805
- * tw.color('gray', 100) // '#f3f4f6'
806
- * tw.theme('primary') // Current theme's primary color
807
- * tw.darker('blue', 2) // blue-700 (500 + 2*100)
808
- * tw.lighter('blue', 2) // blue-300 (500 - 2*100)
809
- */
810
- export const tw = {
811
- /**
812
- * Get a Tailwind color by name and shade
813
- */
814
- color: (name, shade = 500) => {
815
- const palette = colors[name];
816
- if (!palette)
817
- return name;
818
- if (typeof palette === 'string')
819
- return palette;
820
- return palette[shade] ?? palette[500];
821
- },
822
- /**
823
- * Get a color from the current theme
824
- */
825
- theme: (key) => {
826
- return untrack(currentTheme).colors[key];
827
- },
828
- /**
829
- * Get a darker shade of a color (moves towards 950)
830
- * @param name Color name
831
- * @param steps Number of shade steps to darken (1 = +100)
832
- */
833
- darker: (name, steps = 1) => {
834
- const shade = Math.min(500 + steps * 100, 950);
835
- return resolveColor(`${name}-${shade}`);
836
- },
837
- /**
838
- * Get a lighter shade of a color (moves towards 50)
839
- * @param name Color name
840
- * @param steps Number of shade steps to lighten (1 = -100)
841
- */
842
- lighter: (name, steps = 1) => {
843
- const shade = Math.max(500 - steps * 100, 50);
844
- return resolveColor(`${name}-${shade}`);
845
- },
846
- /**
847
- * Get a shade relative to a base shade
848
- * @param name Color name
849
- * @param baseShade Starting shade
850
- * @param offset Offset from base (+100, -200, etc.)
851
- */
852
- shade: (name, baseShade, offset) => {
853
- const newShade = Math.max(50, Math.min(950, baseShade + offset));
854
- return resolveColor(`${name}-${newShade}`);
855
- },
385
+ * Inspired by Bootstrap/DaisyUI color patterns:
386
+ * - Base colors: primary, secondary, success, warning, danger, info
387
+ * - Foreground variants: primaryForeground (text on primary bg)
388
+ * - Background hierarchy: background, surface, card, popover, overlay
389
+ * - Text hierarchy: foreground, mutedForeground, secondaryForeground
390
+ */
391
+ const SEMANTIC_COLORS = {
392
+ // ==========================================================================
393
+ // Foreground (Text) Hierarchy
394
+ // ==========================================================================
395
+ foreground: (t) => t.foreground.primary,
396
+ 'foreground-primary': (t) => t.foreground.primary,
397
+ 'foreground-secondary': (t) => t.foreground.secondary,
398
+ 'foreground-muted': (t) => t.foreground.muted,
399
+ 'foreground-disabled': (t) => t.foreground.disabled,
400
+ mutedForeground: (t) => t.foreground.muted,
401
+ 'muted-foreground': (t) => t.foreground.muted,
402
+ disabledForeground: (t) => t.foreground.disabled,
403
+ // ==========================================================================
404
+ // Background Hierarchy
405
+ // ==========================================================================
406
+ background: (t) => t.background.base,
407
+ 'background-base': (t) => t.background.base,
408
+ 'background-subtle': (t) => t.background.subtle,
409
+ 'background-surface': (t) => t.background.surface,
410
+ 'background-raised': (t) => t.background.raised,
411
+ 'background-elevated': (t) => t.background.elevated,
412
+ surface: (t) => t.background.surface,
413
+ card: (t) => t.background.surface,
414
+ popover: (t) => t.background.popover,
415
+ overlay: (t) => t.background.overlay,
416
+ // ==========================================================================
417
+ // Semantic Colors (Bootstrap/DaisyUI-style)
418
+ // Base colors with auto-contrast foreground variants
419
+ // ==========================================================================
420
+ // Primary - Main brand color
421
+ primary: (t) => t.palette.primary[500],
422
+ 'primary-50': (t) => t.palette.primary[50],
423
+ 'primary-100': (t) => t.palette.primary[100],
424
+ 'primary-200': (t) => t.palette.primary[200],
425
+ 'primary-300': (t) => t.palette.primary[300],
426
+ 'primary-400': (t) => t.palette.primary[400],
427
+ 'primary-500': (t) => t.palette.primary[500],
428
+ 'primary-600': (t) => t.palette.primary[600],
429
+ 'primary-700': (t) => t.palette.primary[700],
430
+ 'primary-800': (t) => t.palette.primary[800],
431
+ 'primary-900': (t) => t.palette.primary[900],
432
+ primaryForeground: (t) => getContrastColor(t.palette.primary[500]), // Auto-contrast
433
+ 'primary-foreground': (t) => getContrastColor(t.palette.primary[500]),
434
+ // Secondary - Neutral/accent
435
+ secondary: (t) => t.palette.secondary[500],
436
+ 'secondary-50': (t) => t.palette.secondary[50],
437
+ 'secondary-100': (t) => t.palette.secondary[100],
438
+ 'secondary-200': (t) => t.palette.secondary[200],
439
+ 'secondary-300': (t) => t.palette.secondary[300],
440
+ 'secondary-400': (t) => t.palette.secondary[400],
441
+ 'secondary-500': (t) => t.palette.secondary[500],
442
+ 'secondary-600': (t) => t.palette.secondary[600],
443
+ 'secondary-700': (t) => t.palette.secondary[700],
444
+ 'secondary-800': (t) => t.palette.secondary[800],
445
+ 'secondary-900': (t) => t.palette.secondary[900],
446
+ secondaryForeground: (t) => getContrastColor(t.palette.secondary[500]), // Auto-contrast
447
+ 'secondary-foreground': (t) => getContrastColor(t.palette.secondary[500]),
448
+ // Success - Positive/confirmation
449
+ success: (t) => t.accents.positive,
450
+ 'success-50': (t) => t.palette.success[50],
451
+ 'success-100': (t) => t.palette.success[100],
452
+ 'success-200': (t) => t.palette.success[200],
453
+ 'success-300': (t) => t.palette.success[300],
454
+ 'success-400': (t) => t.palette.success[400],
455
+ 'success-500': (t) => t.palette.success[500],
456
+ 'success-600': (t) => t.palette.success[600],
457
+ 'success-700': (t) => t.palette.success[700],
458
+ 'success-800': (t) => t.palette.success[800],
459
+ 'success-900': (t) => t.palette.success[900],
460
+ successForeground: (t) => getContrastColor(t.accents.positive), // Auto-contrast
461
+ 'success-foreground': (t) => getContrastColor(t.accents.positive),
462
+ // Warning - Caution/attention
463
+ warning: (t) => t.accents.warning,
464
+ 'warning-50': (t) => t.palette.warning[50],
465
+ 'warning-100': (t) => t.palette.warning[100],
466
+ 'warning-200': (t) => t.palette.warning[200],
467
+ 'warning-300': (t) => t.palette.warning[300],
468
+ 'warning-400': (t) => t.palette.warning[400],
469
+ 'warning-500': (t) => t.palette.warning[500],
470
+ 'warning-600': (t) => t.palette.warning[600],
471
+ 'warning-700': (t) => t.palette.warning[700],
472
+ 'warning-800': (t) => t.palette.warning[800],
473
+ 'warning-900': (t) => t.palette.warning[900],
474
+ warningForeground: (t) => getContrastColor(t.accents.warning), // Auto-contrast
475
+ 'warning-foreground': (t) => getContrastColor(t.accents.warning),
476
+ // Danger/Error/Destructive - Critical/negative
477
+ danger: (t) => t.accents.critical,
478
+ error: (t) => t.accents.critical,
479
+ destructive: (t) => t.accents.critical,
480
+ 'danger-50': (t) => t.palette.danger[50],
481
+ 'danger-100': (t) => t.palette.danger[100],
482
+ 'danger-200': (t) => t.palette.danger[200],
483
+ 'danger-300': (t) => t.palette.danger[300],
484
+ 'danger-400': (t) => t.palette.danger[400],
485
+ 'danger-500': (t) => t.palette.danger[500],
486
+ 'danger-600': (t) => t.palette.danger[600],
487
+ 'danger-700': (t) => t.palette.danger[700],
488
+ 'danger-800': (t) => t.palette.danger[800],
489
+ 'danger-900': (t) => t.palette.danger[900],
490
+ dangerForeground: (t) => getContrastColor(t.accents.critical), // Auto-contrast
491
+ 'danger-foreground': (t) => getContrastColor(t.accents.critical),
492
+ errorForeground: (t) => getContrastColor(t.accents.critical),
493
+ destructiveForeground: (t) => getContrastColor(t.accents.critical),
494
+ // Info - Informational
495
+ info: (t) => t.accents.info,
496
+ infoForeground: (t) => getContrastColor(t.accents.info), // Auto-contrast
497
+ 'info-foreground': (t) => getContrastColor(t.accents.info),
498
+ // Muted - De-emphasized background (like Tailwind's muted)
499
+ muted: (t) => t.background.subtle,
500
+ 'muted-background': (t) => t.background.subtle,
501
+ // Accent - Highlighted elements
502
+ accent: (t) => t.palette.primary[400],
503
+ accentForeground: (t) => getContrastColor(t.palette.primary[400]), // Auto-contrast
504
+ 'accent-foreground': (t) => getContrastColor(t.palette.primary[400]),
505
+ // Neutral - Gray scale
506
+ neutral: (t) => t.palette.neutral[500],
507
+ 'neutral-50': (t) => t.palette.neutral[50],
508
+ 'neutral-100': (t) => t.palette.neutral[100],
509
+ 'neutral-200': (t) => t.palette.neutral[200],
510
+ 'neutral-300': (t) => t.palette.neutral[300],
511
+ 'neutral-400': (t) => t.palette.neutral[400],
512
+ 'neutral-500': (t) => t.palette.neutral[500],
513
+ 'neutral-600': (t) => t.palette.neutral[600],
514
+ 'neutral-700': (t) => t.palette.neutral[700],
515
+ 'neutral-800': (t) => t.palette.neutral[800],
516
+ 'neutral-900': (t) => t.palette.neutral[900],
517
+ // ==========================================================================
518
+ // Borders
519
+ // ==========================================================================
520
+ border: (t) => t.borders.default,
521
+ 'border-subtle': (t) => t.borders.subtle,
522
+ 'border-strong': (t) => t.borders.strong,
523
+ input: (t) => t.components.input.border,
524
+ ring: (t) => t.states.focus.ring.color,
856
525
  };
526
+ /**
527
+ * Resolve a color value to a hex string.
528
+ *
529
+ * Handles:
530
+ * - ANSI color names (returned as-is for the renderer)
531
+ * - Semantic theme colors (primary, success, foreground, etc.)
532
+ * - Hex colors (#rrggbb)
533
+ * - RGB colors (rgb(r,g,b))
534
+ * - Palette colors (blue-500)
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * resolveColor('primary') // '#3b82f6' (from theme)
539
+ * resolveColor('foreground') // '#f8fafc' (from theme)
540
+ * resolveColor('#ff6600') // '#ff6600' (passthrough)
541
+ * resolveColor('blue-500') // '#3b82f6' (from colors.ts)
542
+ * resolveColor('red') // 'red' (ANSI color, passthrough)
543
+ * ```
544
+ */
545
+ export function resolveColor(color) {
546
+ // Already a hex or rgb color - pass through
547
+ if (color.startsWith('#') || color.startsWith('rgb')) {
548
+ return color;
549
+ }
550
+ // Check for semantic theme color
551
+ const theme = untrack(currentTheme);
552
+ const resolver = SEMANTIC_COLORS[color];
553
+ if (resolver) {
554
+ return resolver(theme);
555
+ }
556
+ // Check for palette color (e.g., 'blue-500')
557
+ const paletteMatch = color.match(/^([a-z]+)-(\d+)$/);
558
+ if (paletteMatch) {
559
+ const [, colorName, shade] = paletteMatch;
560
+ const shadeNum = parseInt(shade, 10);
561
+ return getColor(colorName, shadeNum);
562
+ }
563
+ // Return as-is (might be ANSI color name like 'red', 'cyan', etc.)
564
+ return color;
565
+ }
857
566
  //# sourceMappingURL=theme.js.map