@effect-tui/core 0.1.1 → 0.1.4

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 (271) hide show
  1. package/README.md +31 -11
  2. package/dist/ansi.d.ts +127 -32
  3. package/dist/ansi.d.ts.map +1 -1
  4. package/dist/ansi.js +159 -37
  5. package/dist/ansi.js.map +1 -1
  6. package/dist/colors.d.ts +139 -0
  7. package/dist/colors.d.ts.map +1 -0
  8. package/dist/colors.js +339 -0
  9. package/dist/colors.js.map +1 -0
  10. package/dist/index.d.ts +6 -10
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +13 -11
  13. package/dist/index.js.map +1 -1
  14. package/dist/keys.d.ts +21 -0
  15. package/dist/keys.d.ts.map +1 -1
  16. package/dist/keys.js +199 -58
  17. package/dist/keys.js.map +1 -1
  18. package/dist/layout/axis-helpers.d.ts +19 -0
  19. package/dist/layout/axis-helpers.d.ts.map +1 -0
  20. package/dist/layout/axis-helpers.js +19 -0
  21. package/dist/layout/axis-helpers.js.map +1 -0
  22. package/dist/output.d.ts +59 -0
  23. package/dist/output.d.ts.map +1 -0
  24. package/dist/output.js +142 -0
  25. package/dist/output.js.map +1 -0
  26. package/dist/render/buffer.d.ts.map +1 -1
  27. package/dist/render/buffer.js +6 -25
  28. package/dist/render/buffer.js.map +1 -1
  29. package/dist/render/graphemes.d.ts +15 -0
  30. package/dist/render/graphemes.d.ts.map +1 -0
  31. package/dist/render/graphemes.js +28 -0
  32. package/dist/render/graphemes.js.map +1 -0
  33. package/dist/render/measure.d.ts +1 -0
  34. package/dist/render/measure.d.ts.map +1 -1
  35. package/dist/render/measure.js +14 -36
  36. package/dist/render/measure.js.map +1 -1
  37. package/dist/render/palette.d.ts.map +1 -1
  38. package/dist/render/palette.js +26 -1
  39. package/dist/render/palette.js.map +1 -1
  40. package/dist/render/segmenter.d.ts +8 -0
  41. package/dist/render/segmenter.d.ts.map +1 -0
  42. package/dist/render/segmenter.js +23 -0
  43. package/dist/render/segmenter.js.map +1 -0
  44. package/dist/render/surface.d.ts +6 -32
  45. package/dist/render/surface.d.ts.map +1 -1
  46. package/dist/render/surface.js +11 -80
  47. package/dist/render/surface.js.map +1 -1
  48. package/dist/runtime/backend_node.d.ts.map +1 -1
  49. package/dist/runtime/backend_node.js.map +1 -1
  50. package/dist/tailwind-colors.d.ts +291 -0
  51. package/dist/tailwind-colors.d.ts.map +1 -0
  52. package/dist/tailwind-colors.js +291 -0
  53. package/dist/tailwind-colors.js.map +1 -0
  54. package/dist/types.d.ts +15 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +3 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +55 -55
  59. package/src/ansi.ts +201 -73
  60. package/src/colors.ts +468 -0
  61. package/src/index.ts +28 -14
  62. package/src/keys.ts +467 -287
  63. package/src/layout/axis-helpers.ts +33 -0
  64. package/src/output.ts +175 -0
  65. package/src/render/buffer.ts +161 -184
  66. package/src/render/graphemes.ts +34 -0
  67. package/src/render/measure.ts +15 -38
  68. package/src/render/palette.ts +98 -77
  69. package/src/render/segmenter.ts +27 -0
  70. package/src/render/surface.ts +139 -225
  71. package/src/runtime/backend_node.ts +71 -71
  72. package/src/tailwind-colors.ts +295 -0
  73. package/src/types.ts +18 -0
  74. package/dist/anim.d.ts +0 -4
  75. package/dist/anim.d.ts.map +0 -1
  76. package/dist/anim.js +0 -5
  77. package/dist/anim.js.map +0 -1
  78. package/dist/layout/linearStack.d.ts +0 -17
  79. package/dist/layout/linearStack.d.ts.map +0 -1
  80. package/dist/layout/linearStack.js +0 -86
  81. package/dist/layout/linearStack.js.map +0 -1
  82. package/dist/motion-value.d.ts +0 -58
  83. package/dist/motion-value.d.ts.map +0 -1
  84. package/dist/motion-value.js +0 -250
  85. package/dist/motion-value.js.map +0 -1
  86. package/dist/present/display.d.ts +0 -58
  87. package/dist/present/display.d.ts.map +0 -1
  88. package/dist/present/display.js +0 -168
  89. package/dist/present/display.js.map +0 -1
  90. package/dist/present/writers/fullscreen.d.ts +0 -19
  91. package/dist/present/writers/fullscreen.d.ts.map +0 -1
  92. package/dist/present/writers/fullscreen.js +0 -55
  93. package/dist/present/writers/fullscreen.js.map +0 -1
  94. package/dist/present/writers/inline.d.ts +0 -20
  95. package/dist/present/writers/inline.d.ts.map +0 -1
  96. package/dist/present/writers/inline.js +0 -92
  97. package/dist/present/writers/inline.js.map +0 -1
  98. package/dist/render/color-utils.d.ts +0 -18
  99. package/dist/render/color-utils.d.ts.map +0 -1
  100. package/dist/render/color-utils.js +0 -58
  101. package/dist/render/color-utils.js.map +0 -1
  102. package/dist/render/diff.d.ts +0 -30
  103. package/dist/render/diff.d.ts.map +0 -1
  104. package/dist/render/diff.js +0 -83
  105. package/dist/render/diff.js.map +0 -1
  106. package/dist/spring-physics.d.ts +0 -36
  107. package/dist/spring-physics.d.ts.map +0 -1
  108. package/dist/spring-physics.js +0 -113
  109. package/dist/spring-physics.js.map +0 -1
  110. package/dist/spring.d.ts +0 -73
  111. package/dist/spring.d.ts.map +0 -1
  112. package/dist/spring.js +0 -136
  113. package/dist/spring.js.map +0 -1
  114. package/dist/ui/containers/canvas.d.ts +0 -13
  115. package/dist/ui/containers/canvas.d.ts.map +0 -1
  116. package/dist/ui/containers/canvas.js +0 -16
  117. package/dist/ui/containers/canvas.js.map +0 -1
  118. package/dist/ui/containers/geometry-reader.d.ts +0 -17
  119. package/dist/ui/containers/geometry-reader.d.ts.map +0 -1
  120. package/dist/ui/containers/geometry-reader.js +0 -24
  121. package/dist/ui/containers/geometry-reader.js.map +0 -1
  122. package/dist/ui/containers/hstack.d.ts +0 -12
  123. package/dist/ui/containers/hstack.d.ts.map +0 -1
  124. package/dist/ui/containers/hstack.js +0 -28
  125. package/dist/ui/containers/hstack.js.map +0 -1
  126. package/dist/ui/containers/scroll.d.ts +0 -28
  127. package/dist/ui/containers/scroll.d.ts.map +0 -1
  128. package/dist/ui/containers/scroll.js +0 -97
  129. package/dist/ui/containers/scroll.js.map +0 -1
  130. package/dist/ui/containers/shared.d.ts +0 -12
  131. package/dist/ui/containers/shared.d.ts.map +0 -1
  132. package/dist/ui/containers/shared.js +0 -19
  133. package/dist/ui/containers/shared.js.map +0 -1
  134. package/dist/ui/containers/vstack.d.ts +0 -12
  135. package/dist/ui/containers/vstack.d.ts.map +0 -1
  136. package/dist/ui/containers/vstack.js +0 -28
  137. package/dist/ui/containers/vstack.js.map +0 -1
  138. package/dist/ui/containers/zstack.d.ts +0 -14
  139. package/dist/ui/containers/zstack.d.ts.map +0 -1
  140. package/dist/ui/containers/zstack.js +0 -36
  141. package/dist/ui/containers/zstack.js.map +0 -1
  142. package/dist/ui/core/geometry-store.d.ts +0 -22
  143. package/dist/ui/core/geometry-store.d.ts.map +0 -1
  144. package/dist/ui/core/geometry-store.js +0 -29
  145. package/dist/ui/core/geometry-store.js.map +0 -1
  146. package/dist/ui/core/geometry.d.ts +0 -34
  147. package/dist/ui/core/geometry.d.ts.map +0 -1
  148. package/dist/ui/core/geometry.js +0 -14
  149. package/dist/ui/core/geometry.js.map +0 -1
  150. package/dist/ui/core/view.d.ts +0 -25
  151. package/dist/ui/core/view.d.ts.map +0 -1
  152. package/dist/ui/core/view.js +0 -34
  153. package/dist/ui/core/view.js.map +0 -1
  154. package/dist/ui/index.d.ts +0 -44
  155. package/dist/ui/index.d.ts.map +0 -1
  156. package/dist/ui/index.js +0 -39
  157. package/dist/ui/index.js.map +0 -1
  158. package/dist/ui/inlinetext.d.ts +0 -24
  159. package/dist/ui/inlinetext.d.ts.map +0 -1
  160. package/dist/ui/inlinetext.js +0 -131
  161. package/dist/ui/inlinetext.js.map +0 -1
  162. package/dist/ui/install.d.ts +0 -22
  163. package/dist/ui/install.d.ts.map +0 -1
  164. package/dist/ui/install.js +0 -66
  165. package/dist/ui/install.js.map +0 -1
  166. package/dist/ui/markdown.d.ts +0 -40
  167. package/dist/ui/markdown.d.ts.map +0 -1
  168. package/dist/ui/markdown.js +0 -351
  169. package/dist/ui/markdown.js.map +0 -1
  170. package/dist/ui/modifiers/border.d.ts +0 -33
  171. package/dist/ui/modifiers/border.d.ts.map +0 -1
  172. package/dist/ui/modifiers/border.js +0 -82
  173. package/dist/ui/modifiers/border.js.map +0 -1
  174. package/dist/ui/modifiers/fill.d.ts +0 -14
  175. package/dist/ui/modifiers/fill.d.ts.map +0 -1
  176. package/dist/ui/modifiers/fill.js +0 -25
  177. package/dist/ui/modifiers/fill.js.map +0 -1
  178. package/dist/ui/modifiers/frame.d.ts +0 -23
  179. package/dist/ui/modifiers/frame.d.ts.map +0 -1
  180. package/dist/ui/modifiers/frame.js +0 -54
  181. package/dist/ui/modifiers/frame.js.map +0 -1
  182. package/dist/ui/modifiers/offset.d.ts +0 -15
  183. package/dist/ui/modifiers/offset.d.ts.map +0 -1
  184. package/dist/ui/modifiers/offset.js +0 -21
  185. package/dist/ui/modifiers/offset.js.map +0 -1
  186. package/dist/ui/modifiers/opacity.d.ts +0 -15
  187. package/dist/ui/modifiers/opacity.d.ts.map +0 -1
  188. package/dist/ui/modifiers/opacity.js +0 -95
  189. package/dist/ui/modifiers/opacity.js.map +0 -1
  190. package/dist/ui/modifiers/padding.d.ts +0 -20
  191. package/dist/ui/modifiers/padding.d.ts.map +0 -1
  192. package/dist/ui/modifiers/padding.js +0 -36
  193. package/dist/ui/modifiers/padding.js.map +0 -1
  194. package/dist/ui/modifiers/styled.d.ts +0 -14
  195. package/dist/ui/modifiers/styled.d.ts.map +0 -1
  196. package/dist/ui/modifiers/styled.js +0 -26
  197. package/dist/ui/modifiers/styled.js.map +0 -1
  198. package/dist/ui/primitives/rectangle.d.ts +0 -15
  199. package/dist/ui/primitives/rectangle.d.ts.map +0 -1
  200. package/dist/ui/primitives/rectangle.js +0 -23
  201. package/dist/ui/primitives/rectangle.js.map +0 -1
  202. package/dist/ui/primitives/spacer.d.ts +0 -13
  203. package/dist/ui/primitives/spacer.d.ts.map +0 -1
  204. package/dist/ui/primitives/spacer.js +0 -16
  205. package/dist/ui/primitives/spacer.js.map +0 -1
  206. package/dist/ui/primitives/text.d.ts +0 -15
  207. package/dist/ui/primitives/text.d.ts.map +0 -1
  208. package/dist/ui/primitives/text.js +0 -79
  209. package/dist/ui/primitives/text.js.map +0 -1
  210. package/dist/ui/primitives/wrapped-text.d.ts +0 -30
  211. package/dist/ui/primitives/wrapped-text.d.ts.map +0 -1
  212. package/dist/ui/primitives/wrapped-text.js +0 -117
  213. package/dist/ui/primitives/wrapped-text.js.map +0 -1
  214. package/dist/ui/shinytext.d.ts +0 -66
  215. package/dist/ui/shinytext.d.ts.map +0 -1
  216. package/dist/ui/shinytext.js +0 -99
  217. package/dist/ui/shinytext.js.map +0 -1
  218. package/dist/ui/text/layout.d.ts +0 -35
  219. package/dist/ui/text/layout.d.ts.map +0 -1
  220. package/dist/ui/text/layout.js +0 -102
  221. package/dist/ui/text/layout.js.map +0 -1
  222. package/dist/ui/textinput.d.ts +0 -140
  223. package/dist/ui/textinput.d.ts.map +0 -1
  224. package/dist/ui/textinput.js +0 -402
  225. package/dist/ui/textinput.js.map +0 -1
  226. package/dist/ui/view-constructors.d.ts +0 -72
  227. package/dist/ui/view-constructors.d.ts.map +0 -1
  228. package/dist/ui/view-constructors.js +0 -74
  229. package/dist/ui/view-constructors.js.map +0 -1
  230. package/src/anim.ts +0 -5
  231. package/src/layout/linearStack.ts +0 -115
  232. package/src/motion-value.ts +0 -335
  233. package/src/present/display.ts +0 -206
  234. package/src/present/writers/fullscreen.ts +0 -58
  235. package/src/present/writers/inline.ts +0 -101
  236. package/src/render/color-utils.ts +0 -60
  237. package/src/render/diff.ts +0 -95
  238. package/src/spring-physics.ts +0 -151
  239. package/src/spring.ts +0 -234
  240. package/src/ui/__snapshots__/wrappedtext.test.ts.snap +0 -57
  241. package/src/ui/containers/canvas.ts +0 -18
  242. package/src/ui/containers/geometry-reader.ts +0 -32
  243. package/src/ui/containers/hstack.ts +0 -33
  244. package/src/ui/containers/scroll.ts +0 -106
  245. package/src/ui/containers/shared.ts +0 -27
  246. package/src/ui/containers/vstack.ts +0 -34
  247. package/src/ui/containers/zstack.ts +0 -37
  248. package/src/ui/core/geometry-store.ts +0 -42
  249. package/src/ui/core/geometry.ts +0 -30
  250. package/src/ui/core/view.ts +0 -49
  251. package/src/ui/index.ts +0 -84
  252. package/src/ui/inlinetext.ts +0 -135
  253. package/src/ui/install.ts +0 -110
  254. package/src/ui/markdown.test.ts +0 -74
  255. package/src/ui/markdown.ts +0 -388
  256. package/src/ui/modifiers/border.ts +0 -100
  257. package/src/ui/modifiers/fill.ts +0 -28
  258. package/src/ui/modifiers/frame.ts +0 -74
  259. package/src/ui/modifiers/offset.ts +0 -23
  260. package/src/ui/modifiers/opacity.ts +0 -93
  261. package/src/ui/modifiers/padding.ts +0 -53
  262. package/src/ui/modifiers/styled.ts +0 -31
  263. package/src/ui/primitives/rectangle.ts +0 -25
  264. package/src/ui/primitives/spacer.ts +0 -18
  265. package/src/ui/primitives/text.ts +0 -85
  266. package/src/ui/primitives/wrapped-text.ts +0 -131
  267. package/src/ui/shinytext.ts +0 -159
  268. package/src/ui/text/layout.ts +0 -119
  269. package/src/ui/textinput.ts +0 -496
  270. package/src/ui/view-constructors.ts +0 -96
  271. package/src/ui/wrappedtext.test.ts +0 -138
package/src/colors.ts ADDED
@@ -0,0 +1,468 @@
1
+ // colors.ts — Comprehensive color parsing and utilities
2
+ // Supports indexed colors (0-255), RGB, hex, named colors, HSL, and alpha
3
+
4
+ import { tailwindColors, type TailwindShade, type TailwindColorFamily } from "./tailwind-colors.js"
5
+
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ // Type-safe color types
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+
10
+ /**
11
+ * Hex color string: #rgb, #rrggbb, #rgba, or #rrggbbaa
12
+ *
13
+ * Note: We use `#${string}` because enumerating all valid hex digit combinations
14
+ * (22^6 = 113M for 6-char hex) exceeds TypeScript's union type limits.
15
+ * The # prefix is validated at compile time; hex digits are validated at runtime.
16
+ */
17
+ export type HexColor = `#${string}`
18
+
19
+ /** Base ANSI terminal color names */
20
+ export type BaseColorName =
21
+ | "black"
22
+ | "red"
23
+ | "green"
24
+ | "yellow"
25
+ | "blue"
26
+ | "magenta"
27
+ | "cyan"
28
+ | "white"
29
+ | "brightBlack"
30
+ | "brightRed"
31
+ | "brightGreen"
32
+ | "brightYellow"
33
+ | "brightBlue"
34
+ | "brightMagenta"
35
+ | "brightCyan"
36
+ | "brightWhite"
37
+
38
+ /**
39
+ * Color input type - the main public type for color props.
40
+ *
41
+ * Accepts:
42
+ * - Palette index: 0-255
43
+ * - RGB object: { r, g, b }
44
+ * - Named color: "red", "brightBlue", etc. (type-checked)
45
+ * - Hex color: "#f00", "#ff0000" (# prefix required)
46
+ */
47
+ export type Color = number | { r: number; g: number; b: number } | BaseColorName | HexColor
48
+
49
+ /** RGBA color with alpha channel - used for color springs/interpolation */
50
+ export type RGBA = { r: number; g: number; b: number; a: number }
51
+
52
+ /** Color input for springs (includes alpha variants) */
53
+ export type ColorInput = string | RGBA | { r: number; g: number; b: number }
54
+
55
+ /** Standard ANSI color names mapped to 256-color indices */
56
+ export const BASE_NAMES = {
57
+ black: 0,
58
+ red: 1,
59
+ green: 2,
60
+ yellow: 3,
61
+ blue: 4,
62
+ magenta: 5,
63
+ cyan: 6,
64
+ white: 7,
65
+ brightBlack: 8,
66
+ brightRed: 9,
67
+ brightGreen: 10,
68
+ brightYellow: 11,
69
+ brightBlue: 12,
70
+ brightMagenta: 13,
71
+ brightCyan: 14,
72
+ brightWhite: 15,
73
+ } as const
74
+
75
+ // ============================================================================
76
+ // Utility functions
77
+ // ============================================================================
78
+
79
+ /** Clamp a number to 0-255 range */
80
+ export function clamp255(n: number): number {
81
+ return n < 0 ? 0 : n > 255 ? 255 : n | 0
82
+ }
83
+
84
+ /** Clamp a number to 0-1 range */
85
+ export function clamp1(n: number): number {
86
+ return n < 0 ? 0 : n > 1 ? 1 : n
87
+ }
88
+
89
+ // ============================================================================
90
+ // parseColor - For Palette/styles (returns ColorValue)
91
+ // ============================================================================
92
+
93
+ /** Internal type for resolved colors (palette index or RGB) */
94
+ type ColorValue = number | { r: number; g: number; b: number }
95
+
96
+ /**
97
+ * Parse flexible color inputs into a resolved color value.
98
+ * Supports: number (0-255), {r,g,b}, "#rgb", "#rrggbb", base names, gray0-23.
99
+ * Used by Palette and style systems.
100
+ */
101
+ export function parseColor(c: Color): ColorValue {
102
+ if (typeof c === "number") return c
103
+ if (typeof c === "object") return { r: c.r | 0, g: c.g | 0, b: c.b | 0 }
104
+ const s = String(c).trim()
105
+
106
+ // Hex color
107
+ if (s.startsWith("#")) {
108
+ const hex = s.slice(1)
109
+ if (hex.length === 3) {
110
+ // #rgb -> #rrggbb
111
+ return {
112
+ r: parseInt(hex[0] + hex[0], 16),
113
+ g: parseInt(hex[1] + hex[1], 16),
114
+ b: parseInt(hex[2] + hex[2], 16),
115
+ }
116
+ }
117
+ if (hex.length === 6) {
118
+ return {
119
+ r: parseInt(hex.slice(0, 2), 16),
120
+ g: parseInt(hex.slice(2, 4), 16),
121
+ b: parseInt(hex.slice(4, 6), 16),
122
+ }
123
+ }
124
+ }
125
+
126
+ // Named color
127
+ if (s in BASE_NAMES) return BASE_NAMES[s as keyof typeof BASE_NAMES]
128
+
129
+ // gray0-gray23
130
+ const m = /^(?:gray|grey)(\d{1,2})$/.exec(s)
131
+ if (m?.[1]) {
132
+ const n = Math.max(0, Math.min(23, parseInt(m[1], 10)))
133
+ return 232 + n
134
+ }
135
+
136
+ return 7 // default to white if unrecognized
137
+ }
138
+
139
+ // ============================================================================
140
+ // parseColorRGBA - For color springs (returns RGBA with alpha)
141
+ // ============================================================================
142
+
143
+ /**
144
+ * Parse any supported color format to RGBA (with alpha).
145
+ * Supports: hex (#rgb, #rrggbb, #rgba, #rrggbbaa), rgb(), rgba(), hsl(), hsla(), {r,g,b,a}.
146
+ * Used by color springs for interpolation.
147
+ */
148
+ export function parseColorRGBA(input: ColorInput): RGBA {
149
+ if (typeof input === "object") {
150
+ return {
151
+ r: input.r,
152
+ g: input.g,
153
+ b: input.b,
154
+ a: "a" in input ? input.a : 1,
155
+ }
156
+ }
157
+
158
+ const str = input.trim().toLowerCase()
159
+
160
+ // Hex: #rgb, #rrggbb, #rgba, #rrggbbaa
161
+ if (str.startsWith("#")) {
162
+ return parseHex(str)
163
+ }
164
+
165
+ // rgb(r, g, b) or rgba(r, g, b, a)
166
+ if (str.startsWith("rgb")) {
167
+ return parseRgb(str)
168
+ }
169
+
170
+ // hsl(h, s%, l%) or hsla(h, s%, l%, a)
171
+ if (str.startsWith("hsl")) {
172
+ return parseHsl(str)
173
+ }
174
+
175
+ // Unknown format, return black
176
+ return { r: 0, g: 0, b: 0, a: 1 }
177
+ }
178
+
179
+ function parseHex(hex: string): RGBA {
180
+ const h = hex.slice(1)
181
+
182
+ if (h.length === 3) {
183
+ return {
184
+ r: parseInt(h[0] + h[0], 16),
185
+ g: parseInt(h[1] + h[1], 16),
186
+ b: parseInt(h[2] + h[2], 16),
187
+ a: 1,
188
+ }
189
+ }
190
+
191
+ if (h.length === 4) {
192
+ return {
193
+ r: parseInt(h[0] + h[0], 16),
194
+ g: parseInt(h[1] + h[1], 16),
195
+ b: parseInt(h[2] + h[2], 16),
196
+ a: parseInt(h[3] + h[3], 16) / 255,
197
+ }
198
+ }
199
+
200
+ if (h.length === 6) {
201
+ return {
202
+ r: parseInt(h.slice(0, 2), 16),
203
+ g: parseInt(h.slice(2, 4), 16),
204
+ b: parseInt(h.slice(4, 6), 16),
205
+ a: 1,
206
+ }
207
+ }
208
+
209
+ if (h.length === 8) {
210
+ return {
211
+ r: parseInt(h.slice(0, 2), 16),
212
+ g: parseInt(h.slice(2, 4), 16),
213
+ b: parseInt(h.slice(4, 6), 16),
214
+ a: parseInt(h.slice(6, 8), 16) / 255,
215
+ }
216
+ }
217
+
218
+ return { r: 0, g: 0, b: 0, a: 1 }
219
+ }
220
+
221
+ function parseRgb(str: string): RGBA {
222
+ const match = str.match(/rgba?\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*(?:[,/]\s*([\d.]+))?\s*\)/)
223
+ if (match) {
224
+ return {
225
+ r: clamp255(parseInt(match[1], 10)),
226
+ g: clamp255(parseInt(match[2], 10)),
227
+ b: clamp255(parseInt(match[3], 10)),
228
+ a: match[4] ? clamp1(parseFloat(match[4])) : 1,
229
+ }
230
+ }
231
+ return { r: 0, g: 0, b: 0, a: 1 }
232
+ }
233
+
234
+ function parseHsl(str: string): RGBA {
235
+ const match = str.match(/hsla?\(\s*(\d+)\s*[,\s]\s*([\d.]+)%?\s*[,\s]\s*([\d.]+)%?\s*(?:[,/]\s*([\d.]+))?\s*\)/)
236
+ if (match) {
237
+ const h = parseFloat(match[1])
238
+ const s = parseFloat(match[2])
239
+ const l = parseFloat(match[3])
240
+ const a = match[4] ? clamp1(parseFloat(match[4])) : 1
241
+ return hslToRgba(h, s, l, a)
242
+ }
243
+ return { r: 0, g: 0, b: 0, a: 1 }
244
+ }
245
+
246
+ /**
247
+ * Convert HSL to RGBA.
248
+ * h: 0-360, s: 0-100, l: 0-100, a: 0-1
249
+ */
250
+ export function hslToRgba(h: number, s: number, l: number, a: number): RGBA {
251
+ // Normalize
252
+ h = ((h % 360) + 360) % 360
253
+ s = clamp1(s / 100)
254
+ l = clamp1(l / 100)
255
+
256
+ const c = (1 - Math.abs(2 * l - 1)) * s
257
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
258
+ const m = l - c / 2
259
+
260
+ let r = 0
261
+ let g = 0
262
+ let b = 0
263
+
264
+ if (h < 60) {
265
+ r = c
266
+ g = x
267
+ b = 0
268
+ } else if (h < 120) {
269
+ r = x
270
+ g = c
271
+ b = 0
272
+ } else if (h < 180) {
273
+ r = 0
274
+ g = c
275
+ b = x
276
+ } else if (h < 240) {
277
+ r = 0
278
+ g = x
279
+ b = c
280
+ } else if (h < 300) {
281
+ r = x
282
+ g = 0
283
+ b = c
284
+ } else {
285
+ r = c
286
+ g = 0
287
+ b = x
288
+ }
289
+
290
+ return {
291
+ r: Math.round((r + m) * 255),
292
+ g: Math.round((g + m) * 255),
293
+ b: Math.round((b + m) * 255),
294
+ a,
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Convert a 256-color index to RGB values (approximate xterm palette).
300
+ * 0-15: Standard ANSI, 16-231: 6x6x6 RGB cube, 232-255: Grayscale ramp
301
+ */
302
+ export function idxToRGB(idx: number): { r: number; g: number; b: number } {
303
+ if (idx < 0) idx = 0
304
+ if (idx > 255) idx = 255
305
+
306
+ // Standard ANSI colors (0-15)
307
+ if (idx < 16) {
308
+ const base: Array<[number, number, number]> = [
309
+ [0x00, 0x00, 0x00], // 0: black
310
+ [0x80, 0x00, 0x00], // 1: red
311
+ [0x00, 0x80, 0x00], // 2: green
312
+ [0x80, 0x80, 0x00], // 3: yellow
313
+ [0x00, 0x00, 0x80], // 4: blue
314
+ [0x80, 0x00, 0x80], // 5: magenta
315
+ [0x00, 0x80, 0x80], // 6: cyan
316
+ [0xc0, 0xc0, 0xc0], // 7: white
317
+ [0x80, 0x80, 0x80], // 8: bright black (gray)
318
+ [0xff, 0x00, 0x00], // 9: bright red
319
+ [0x00, 0xff, 0x00], // 10: bright green
320
+ [0xff, 0xff, 0x00], // 11: bright yellow
321
+ [0x00, 0x00, 0xff], // 12: bright blue
322
+ [0xff, 0x00, 0xff], // 13: bright magenta
323
+ [0x00, 0xff, 0xff], // 14: bright cyan
324
+ [0xff, 0xff, 0xff], // 15: bright white
325
+ ]
326
+ const [r, g, b] = base[idx] ?? [0, 0, 0]
327
+ return { r, g, b }
328
+ }
329
+
330
+ // Grayscale ramp (232-255)
331
+ if (idx >= 232) {
332
+ const n = idx - 232 // 0..23
333
+ const v = 8 + n * 10
334
+ return { r: v, g: v, b: v }
335
+ }
336
+
337
+ // 6x6x6 RGB cube (16-231)
338
+ const n = idx - 16 // 0..215
339
+ const r = Math.floor(n / 36) % 6
340
+ const g = Math.floor(n / 6) % 6
341
+ const b = n % 6
342
+ const steps = [0, 95, 135, 175, 215, 255]
343
+ return { r: steps[r] ?? 0, g: steps[g] ?? 0, b: steps[b] ?? 0 }
344
+ }
345
+
346
+ /**
347
+ * Check if a value looks like a color input.
348
+ */
349
+ export function isColorInput(value: unknown): value is ColorInput {
350
+ if (typeof value === "string") {
351
+ const s = value.trim().toLowerCase()
352
+ return s.startsWith("#") || s.startsWith("rgb") || s.startsWith("hsl")
353
+ }
354
+ if (typeof value === "object" && value !== null) {
355
+ const obj = value as Record<string, unknown>
356
+ return typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number"
357
+ }
358
+ return false
359
+ }
360
+
361
+ // ============================================================================
362
+ // Colors API - Convenience object for creating colors
363
+ // ============================================================================
364
+
365
+ type RGB = { r: number; g: number; b: number }
366
+
367
+ /** Parse hex string to RGB */
368
+ function hexToRgb(hex: string): RGB {
369
+ const h = hex.startsWith("#") ? hex.slice(1) : hex
370
+ return {
371
+ r: parseInt(h.slice(0, 2), 16),
372
+ g: parseInt(h.slice(2, 4), 16),
373
+ b: parseInt(h.slice(4, 6), 16),
374
+ }
375
+ }
376
+
377
+ /** Create a Tailwind color function for a given family */
378
+ function makeTailwindColor(family: TailwindColorFamily): (shade: TailwindShade) => RGB {
379
+ const palette = tailwindColors[family]
380
+ return (shade: TailwindShade): RGB => {
381
+ const hex = palette[shade]
382
+ return hexToRgb(hex)
383
+ }
384
+ }
385
+
386
+ type TailwindColorFn = (shade: TailwindShade) => RGB
387
+
388
+ type TailwindColors = {
389
+ slate: TailwindColorFn
390
+ gray: TailwindColorFn
391
+ zinc: TailwindColorFn
392
+ neutral: TailwindColorFn
393
+ stone: TailwindColorFn
394
+ red: TailwindColorFn
395
+ orange: TailwindColorFn
396
+ amber: TailwindColorFn
397
+ yellow: TailwindColorFn
398
+ lime: TailwindColorFn
399
+ green: TailwindColorFn
400
+ emerald: TailwindColorFn
401
+ teal: TailwindColorFn
402
+ cyan: TailwindColorFn
403
+ sky: TailwindColorFn
404
+ blue: TailwindColorFn
405
+ indigo: TailwindColorFn
406
+ violet: TailwindColorFn
407
+ purple: TailwindColorFn
408
+ fuchsia: TailwindColorFn
409
+ pink: TailwindColorFn
410
+ rose: TailwindColorFn
411
+ }
412
+
413
+ type ColorsApi = typeof BASE_NAMES & {
414
+ rgb(r: number, g: number, b: number): RGB
415
+ hex(hex: HexColor): RGB
416
+ /** Gray - 24-level grayscale palette (0-23) */
417
+ gray(level: number): number
418
+ /** Tailwind color palette */
419
+ tw: TailwindColors
420
+ }
421
+
422
+ /** Tailwind color palette under Colors.tw namespace */
423
+ const tw: TailwindColors = {
424
+ slate: makeTailwindColor("slate"),
425
+ gray: makeTailwindColor("gray"),
426
+ zinc: makeTailwindColor("zinc"),
427
+ neutral: makeTailwindColor("neutral"),
428
+ stone: makeTailwindColor("stone"),
429
+ red: makeTailwindColor("red"),
430
+ orange: makeTailwindColor("orange"),
431
+ amber: makeTailwindColor("amber"),
432
+ yellow: makeTailwindColor("yellow"),
433
+ lime: makeTailwindColor("lime"),
434
+ green: makeTailwindColor("green"),
435
+ emerald: makeTailwindColor("emerald"),
436
+ teal: makeTailwindColor("teal"),
437
+ cyan: makeTailwindColor("cyan"),
438
+ sky: makeTailwindColor("sky"),
439
+ blue: makeTailwindColor("blue"),
440
+ indigo: makeTailwindColor("indigo"),
441
+ violet: makeTailwindColor("violet"),
442
+ purple: makeTailwindColor("purple"),
443
+ fuchsia: makeTailwindColor("fuchsia"),
444
+ pink: makeTailwindColor("pink"),
445
+ rose: makeTailwindColor("rose"),
446
+ }
447
+
448
+ /** Convenience API for creating color values */
449
+ export const Colors: ColorsApi = Object.assign(
450
+ {
451
+ rgb(r: number, g: number, b: number): RGB {
452
+ return { r, g, b }
453
+ },
454
+ hex(hex: HexColor): RGB {
455
+ const result = parseColor(hex)
456
+ // parseColor returns number for named colors, but hex always returns RGB
457
+ return typeof result === "number" ? { r: 0, g: 0, b: 0 } : result
458
+ },
459
+ /** Gray - 24-level grayscale palette (0-23) */
460
+ gray(level: number): number {
461
+ const n = Math.max(0, Math.min(23, level | 0))
462
+ return 232 + n
463
+ },
464
+ /** Tailwind color palette */
465
+ tw,
466
+ },
467
+ BASE_NAMES,
468
+ ) as ColorsApi
package/src/index.ts CHANGED
@@ -1,21 +1,35 @@
1
- // Re-export core modules
1
+ // Core types
2
+ export * from "./types.js"
3
+
4
+ // Rendering primitives
2
5
  export * from "./render/surface.js"
3
6
  export * from "./render/buffer.js"
7
+ export * from "./render/palette.js"
4
8
  export * from "./render/measure.js"
5
- export * from "./anim.js"
9
+
10
+ // Colors - additional exports not in surface.js (RGBA, parseColorRGBA for springs)
11
+ export {
12
+ parseColorRGBA,
13
+ hslToRgba,
14
+ idxToRGB,
15
+ isColorInput,
16
+ clamp255,
17
+ clamp1,
18
+ type RGBA,
19
+ type ColorInput,
20
+ } from "./colors.js"
21
+
22
+ // Input handling
6
23
  export * from "./keys.js"
7
24
 
8
- // UI (canonical View and curated exports)
9
- export { View } from "./ui/index.js"
10
- export { ShinyText } from "./ui/index.js"
11
- export { TextInput, TextInputState, editTextInput } from "./ui/index.js"
12
- export { Markdown } from "./ui/index.js"
13
- export type { TextInputStateLike, TextInputStatePlain, TextInputGeom, TextInputOptions } from "./ui/index.js"
14
- export type { MarkdownOptions } from "./ui/index.js"
15
- // Geometry store (last-known layout)
16
- export { geometryStore } from "./ui/core/geometry-store.js"
17
- export type { ViewId, Rect as GeometryRect, InputGeom } from "./ui/core/geometry-store.js"
25
+ // ANSI escape sequences
26
+ export * from "./ansi.js"
27
+
28
+ // Output helpers (buffer diffing and emission)
29
+ export * from "./output.js"
30
+
31
+ // Layout utilities
32
+ export * from "./layout/axis-helpers.js"
18
33
 
19
- // Renderers (optional public API)
20
- export * from "./present/display.js"
34
+ // Terminal backend (optional)
21
35
  export type { TerminalBackend } from "./runtime/backend_node.js"